diff -ruN qmail-1.03-factory/EXTTODO qmail-1.03-7.10/EXTTODO
--- qmail-1.03-factory/EXTTODO	1969-12-31 19:00:00.000000000 -0500
+++ qmail-1.03-7.10/EXTTODO	2010-02-14 22:53:32.000000000 -0500
@@ -0,0 +1,114 @@
+EXTTODO by Claudio Jeker <jeker@n-r-g.com> and 
+Andre Oppermann <opi@nrg4u.com>
+(c) 1998,1999,2000,2001,2002 Internet Business Solutions Ltd.
+
+The EXTTODO patch is a part of the qmail-ldap patch.
+This patches for qmail come with NO WARRANTY.
+
+These patches are under the BSD license.
+
+RELEASE: 5. Jan. 2003
+
+EXTTODO:
+======================
+
+TOC:
+ WHAT DOES IT DO
+ INSTALL
+ CONFIG FILES
+ SETUP
+ BIG PICTURE
+
+NEWS:
+ 
+ This is the first release of the EXTTODO patch.
+
+================================================================================
+
+WHAT DOES IT DO
+
+ The exttodo patch addresses a problem known as the silly qmail (queue)
+ problem. This problem is found only on system with high injection rates.
+
+ qmail with a big local and remote concurrency could deliver a tremendous 
+ amount of messages but normally this can not be achieved because qmail-send
+ becomes a bottleneck on those high volumes servers.
+ qmail-send preprocesses all new messages before distributing them for local
+ or remote delivering. In one run qmail-send does one todo run but has the 
+ ability to close multiple jobs. Because of this layout qmail-send can not 
+ feed all the new available (local/remote) delivery slots and therefor it is 
+ not possible to achieve the maximum throughput.
+ This would be a minor problem if one qmail-send run could be done in extreme
+ short time but because of many file system calls (fsync and (un)link) a todo
+ run is expensive and throttles the throughput.
+
+ The exttodo patch tries to solve the problem by moving the todo routine into 
+ an external program. This reduces the run time in qmail-send.
+
+ exttodo adds a new program to qmail called qmail-todo. qmail-todo prepares
+ incoming messages for local and remote delivering (by creating info/<messid>
+ local/<messid> and remote/<messid> and removing todo/<messid>). See also
+ INTERNALS. As next qmail-todo transmits the <messid> to qmail-send which will
+ add this message into the priority queue which schedules the message for 
+ delivery. 
+
+INSTALL
+
+ To enable the exttodo patch you need to define EXTERNAL_TODO while compiling
+ qmail(-ldap) this can be done with the -D flag of cc (e.g. cc -DEXTERNAL_TODO).
+
+ NOTE: the exttodo patch can also be used on qmail systems without the 
+ qmail-ldap patch.
+ 
+================================================================================
+
+CONFIG FILES
+
+ No additional control files are used or needed.
+
+================================================================================
+
+SETUP
+
+ qmail-todo will be started by qmail-start and therefor no additional setup
+ is needed.
+
+ To verify that exttodo is running just check if qmail-todo is running.
+
+================================================================================
+
+BIG PICTURE
+
+               +-------+   +-------+
+               | clean |   | clean |
+               +--0-1--+   +--0-1--+       +-----------+
+         trigger  ^ |         ^ |        +->0,1 lspawn |
+            |     | v         | v       /  +-----------+
+ +-------+  v  +--2-3--+   +--5-6--+   /
+ |       |  |  |       0<--7     1,2<-+
+ | queue |--+--| todo  |   | send  |
+ |       |  |  |       1-->8     3,4<-+
+ +-------+     +-------+   +---0---+   \
+                               |        \  +-----------+
+                               v         +->0,1 rspwan |
+                           +---0---+       +-----------+
+                           | logger|
+                           +-------+
+
+Communication between qmail-send and qmail-todo
+
+todo -> send:
+   D[LRB]<mesgid>\0
+          Start delivery for new message with id <messid>.
+          the character L, R or B defines the type
+          of delivery, local, remote or both respectively.
+   L<string>\0
+          Dump string to the logger without adding additional \n or similar.
+send -> todo:
+   H      Got a SIGHUP reread ~/control/locals and ~/control/virtualdomains
+   X      Quit ASAP.
+
+qmail-todo sends "\0" terminated messages whereas qmail-send just send one
+character to qmail-todo.
+
+
diff -ruN qmail-1.03-factory/EXTTODO-INFO qmail-1.03-7.10/EXTTODO-INFO
--- qmail-1.03-factory/EXTTODO-INFO	1969-12-31 19:00:00.000000000 -0500
+++ qmail-1.03-7.10/EXTTODO-INFO	2010-02-14 22:53:32.000000000 -0500
@@ -0,0 +1,11 @@
+Files modified:
+Makefile
+EXTTODO
+FILES
+TARGETS
+qmail-send.c
+qmail-todo.c
+qmail-start.c
+hier.c
+install-big.c
+
diff -ruN qmail-1.03-factory/FILES qmail-1.03-7.10/FILES
--- qmail-1.03-factory/FILES	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/FILES	2010-02-14 22:53:32.000000000 -0500
@@ -135,6 +135,8 @@
 dnsip.c
 dnsmxip.c
 dnsptr.c
+dnstxt.c
+spfquery.c
 hostname.c
 ipmeprint.c
 tcp-env.c
@@ -335,13 +337,16 @@
 byte.h
 byte_chr.c
 byte_copy.c
+byte_cspn.c
 byte_cr.c
 byte_diff.c
 byte_rchr.c
+byte_rcspn.c
 byte_zero.c
 str.h
 str_chr.c
 str_cpy.c
+str_cpyb.c
 str_diff.c
 str_diffn.c
 str_len.c
@@ -401,6 +406,8 @@
 date822fmt.c
 dns.h
 dns.c
+spf.h
+spf.c
 trylsock.c
 tryrsolv.c
 ip.h
@@ -431,3 +438,4 @@
 tcp-environ.5
 constmap.h
 constmap.c
+qmail-todo.c
diff -ruN qmail-1.03-factory/Makefile qmail-1.03-7.10/Makefile
--- qmail-1.03-factory/Makefile	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/Makefile	2010-02-14 22:53:32.000000000 -0500
@@ -1,5 +1,7 @@
 # Don't edit Makefile! Use conf-* for configuration.
 
+DEFINES=-DEXTERNAL_TODO # use to enable external todo
+
 SHELL=/bin/sh
 
 default: it
@@ -136,6 +138,10 @@
 compile auto_usera.c
 	./compile auto_usera.c
 
+base64.o: \
+compile base64.c base64.h stralloc.h substdio.h str.h
+	./compile base64.c
+
 binm1: \
 binm1.sh conf-qmail
 	cat binm1.sh \
@@ -203,6 +209,10 @@
 compile byte_cr.c byte.h
 	./compile byte_cr.c
 
+byte_cspn.o: \
+compile byte_cspn.c byte.h
+	./compile byte_cspn.c
+
 byte_diff.o: \
 compile byte_diff.c byte.h
 	./compile byte_diff.c
@@ -211,6 +221,10 @@
 compile byte_rchr.c byte.h
 	./compile byte_rchr.c
 
+byte_rcspn.o: \
+compile byte_rcspn.c byte.h
+	./compile byte_rcspn.c
+
 byte_zero.o: \
 compile byte_zero.c byte.h
 	./compile byte_zero.c
@@ -393,84 +407,96 @@
 	rm -f trydrent.o
 
 dns.lib: \
-tryrsolv.c compile load socket.lib dns.o ipalloc.o ip.o stralloc.a \
-alloc.a error.a fs.a str.a
+tryrsolv.c compile load socket.lib dns.o ipalloc.o strsalloc.o ip.o \
+stralloc.a alloc.a error.a fs.a str.a
 	( ( ./compile tryrsolv.c && ./load tryrsolv dns.o \
-	ipalloc.o ip.o stralloc.a alloc.a error.a fs.a str.a \
+	ipalloc.o strsalloc.o ip.o stralloc.a alloc.a error.a fs.a str.a \
 	-lresolv `cat socket.lib` ) >/dev/null 2>&1 \
 	&& echo -lresolv || exit 0 ) > dns.lib
 	rm -f tryrsolv.o tryrsolv
 
 dns.o: \
-compile dns.c ip.h ipalloc.h ip.h gen_alloc.h fmt.h alloc.h str.h \
-stralloc.h gen_alloc.h dns.h case.h
+compile dns.c ip.h ipalloc.h strsalloc.h gen_alloc.h fmt.h alloc.h \
+str.h stralloc.h dns.h case.h
 	./compile dns.c
 
 dnscname: \
-load dnscname.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \
+load dnscname.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \
 substdio.a error.a str.a fs.a dns.lib socket.lib
-	./load dnscname dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \
+	./load dnscname dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a \
 	alloc.a substdio.a error.a str.a fs.a  `cat dns.lib` `cat \
 	socket.lib`
 
 dnscname.o: \
-compile dnscname.c substdio.h subfd.h substdio.h stralloc.h \
+compile dnscname.c substdio.h subfd.h stralloc.h \
 gen_alloc.h dns.h dnsdoe.h readwrite.h exit.h
 	./compile dnscname.c
 
 dnsdoe.o: \
-compile dnsdoe.c substdio.h subfd.h substdio.h exit.h dns.h dnsdoe.h
+compile dnsdoe.c substdio.h subfd.h exit.h dns.h dnsdoe.h
 	./compile dnsdoe.c
 
 dnsfq: \
-load dnsfq.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \
+load dnsfq.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \
 substdio.a error.a str.a fs.a dns.lib socket.lib
-	./load dnsfq dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \
+	./load dnsfq dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a \
 	alloc.a substdio.a error.a str.a fs.a  `cat dns.lib` `cat \
 	socket.lib`
 
 dnsfq.o: \
-compile dnsfq.c substdio.h subfd.h substdio.h stralloc.h gen_alloc.h \
-dns.h dnsdoe.h ip.h ipalloc.h ip.h gen_alloc.h exit.h
+compile dnsfq.c substdio.h subfd.h stralloc.h gen_alloc.h \
+dns.h dnsdoe.h ip.h ipalloc.h strsalloc.h exit.h
 	./compile dnsfq.c
 
 dnsip: \
-load dnsip.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \
+load dnsip.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \
 substdio.a error.a str.a fs.a dns.lib socket.lib
-	./load dnsip dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \
+	./load dnsip dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a \
 	alloc.a substdio.a error.a str.a fs.a  `cat dns.lib` `cat \
 	socket.lib`
 
 dnsip.o: \
-compile dnsip.c substdio.h subfd.h substdio.h stralloc.h gen_alloc.h \
-dns.h dnsdoe.h ip.h ipalloc.h ip.h gen_alloc.h exit.h
+compile dnsip.c substdio.h subfd.h stralloc.h gen_alloc.h \
+dns.h dnsdoe.h ip.h ipalloc.h strsalloc.h exit.h
 	./compile dnsip.c
 
 dnsmxip: \
-load dnsmxip.o dns.o dnsdoe.o ip.o ipalloc.o now.o stralloc.a alloc.a \
-substdio.a error.a str.a fs.a dns.lib socket.lib
-	./load dnsmxip dns.o dnsdoe.o ip.o ipalloc.o now.o \
+load dnsmxip.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o now.o stralloc.a \
+alloc.a substdio.a error.a str.a fs.a dns.lib socket.lib
+	./load dnsmxip dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o now.o \
 	stralloc.a alloc.a substdio.a error.a str.a fs.a  `cat \
 	dns.lib` `cat socket.lib`
 
 dnsmxip.o: \
-compile dnsmxip.c substdio.h subfd.h substdio.h stralloc.h \
-gen_alloc.h fmt.h dns.h dnsdoe.h ip.h ipalloc.h ip.h gen_alloc.h \
+compile dnsmxip.c substdio.h subfd.h stralloc.h \
+gen_alloc.h fmt.h dns.h dnsdoe.h ip.h ipalloc.h strsalloc.h \
 now.h datetime.h exit.h
 	./compile dnsmxip.c
 
 dnsptr: \
-load dnsptr.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \
+load dnsptr.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \
 substdio.a error.a str.a fs.a dns.lib socket.lib
-	./load dnsptr dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \
+	./load dnsptr dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a \
 	alloc.a substdio.a error.a str.a fs.a  `cat dns.lib` `cat \
 	socket.lib`
 
 dnsptr.o: \
-compile dnsptr.c substdio.h subfd.h substdio.h stralloc.h gen_alloc.h \
+compile dnsptr.c substdio.h subfd.h stralloc.h gen_alloc.h \
 str.h scan.h dns.h dnsdoe.h ip.h exit.h
 	./compile dnsptr.c
 
+dnstxt: \
+load dnstxt.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \
+substdio.a error.a str.a fs.a dns.lib socket.lib
+	./load dnstxt dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a \
+	alloc.a substdio.a error.a str.a fs.a  `cat dns.lib` `cat \
+	socket.lib`
+
+dnstxt.o: \
+compile dnstxt.c substdio.h subfd.h stralloc.h gen_alloc.h \
+str.h scan.h dns.h dnsdoe.h ip.h exit.h
+	./compile dnstxt.c
+
 dot-qmail.0: \
 dot-qmail.5
 	nroff -man dot-qmail.5 > dot-qmail.0
@@ -703,7 +729,7 @@
 
 hier.o: \
 compile hier.c auto_qmail.h auto_split.h auto_uids.h fmt.h fifo.h
-	./compile hier.c
+	./compile $(DEFINES) hier.c
 
 home: \
 home.sh conf-qmail
@@ -755,7 +781,7 @@
 install-big.o: \
 compile install-big.c auto_qmail.h auto_split.h auto_uids.h fmt.h \
 fifo.h
-	./compile install-big.c
+	./compile $(DEFINES) install-big.c
 
 install.o: \
 compile install.c substdio.h strerr.h error.h open.h readwrite.h \
@@ -777,24 +803,24 @@
 	./compile ip.c
 
 ipalloc.o: \
-compile ipalloc.c alloc.h gen_allocdefs.h ip.h ipalloc.h ip.h \
+compile ipalloc.c alloc.h gen_allocdefs.h ip.h ipalloc.h \
 gen_alloc.h
 	./compile ipalloc.c
 
 ipme.o: \
-compile ipme.c hassalen.h byte.h ip.h ipalloc.h ip.h gen_alloc.h \
-stralloc.h gen_alloc.h ipme.h ip.h ipalloc.h
+compile ipme.c hassalen.h byte.h ip.h ipalloc.h strsalloc.h ip.h gen_alloc.h \
+stralloc.h gen_alloc.h ipme.h ip.h ipalloc.h strsalloc.h
 	./compile ipme.c
 
 ipmeprint: \
-load ipmeprint.o ipme.o ip.o ipalloc.o stralloc.a alloc.a substdio.a \
-error.a str.a fs.a socket.lib
-	./load ipmeprint ipme.o ip.o ipalloc.o stralloc.a alloc.a \
-	substdio.a error.a str.a fs.a  `cat socket.lib`
+load ipmeprint.o ipme.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \
+substdio.a error.a str.a fs.a socket.lib
+	./load ipmeprint ipme.o ip.o ipalloc.o strsalloc.o stralloc.a \
+	alloc.a substdio.a error.a str.a fs.a `cat socket.lib`
 
 ipmeprint.o: \
 compile ipmeprint.c subfd.h substdio.h substdio.h ip.h ipme.h ip.h \
-ipalloc.h ip.h gen_alloc.h exit.h
+ipalloc.h strsalloc.h ip.h gen_alloc.h exit.h
 	./compile ipmeprint.c
 
 it: \
@@ -804,11 +830,11 @@
 qmail-pw2u qmail-qread qmail-qstat qmail-tcpto qmail-tcpok \
 qmail-pop3d qmail-popup qmail-qmqpc qmail-qmqpd qmail-qmtpd \
 qmail-smtpd sendmail tcp-env qmail-newmrh config config-fast dnscname \
-dnsptr dnsip dnsmxip dnsfq hostname ipmeprint qreceipt qsmhook qbiff \
+dnsptr dnsip dnsmxip dnsfq dnstxt hostname ipmeprint qreceipt qsmhook qbiff \
 forward preline condredirect bouncesaying except maildirmake \
 maildir2mbox maildirwatch qail elq pinq idedit install-big install \
 instcheck home home+df proc proc+df binm1 binm1+df binm2 binm2+df \
-binm3 binm3+df
+binm3 binm3+df spfquery qmail-todo
 
 load: \
 make-load warn-auto.sh systype
@@ -1333,10 +1359,10 @@
 
 qmail-qmqpc: \
 load qmail-qmqpc.o slurpclose.o timeoutread.o timeoutwrite.o \
-timeoutconn.o ip.o control.o auto_qmail.o sig.a ndelay.a open.a \
+timeoutconn.o constmap.o case.a ip.o control.o auto_qmail.o sig.a ndelay.a open.a \
 getln.a substdio.a stralloc.a alloc.a error.a str.a fs.a socket.lib
 	./load qmail-qmqpc slurpclose.o timeoutread.o \
-	timeoutwrite.o timeoutconn.o ip.o control.o auto_qmail.o \
+	timeoutwrite.o timeoutconn.o constmap.o case.a ip.o control.o auto_qmail.o \
 	sig.a ndelay.a open.a getln.a substdio.a stralloc.a alloc.a \
 	error.a str.a fs.a  `cat socket.lib`
 
@@ -1419,13 +1445,14 @@
 	nroff -man qmail-qstat.8 > qmail-qstat.0
 
 qmail-queue: \
-load qmail-queue.o triggerpull.o fmtqfn.o now.o date822fmt.o \
-datetime.a seek.a ndelay.a open.a sig.a alloc.a substdio.a error.a \
-str.a fs.a auto_qmail.o auto_split.o auto_uids.o
+load qmail-queue.o triggerpull.o fmtqfn.o now.o date822fmt.o qregex.o \
+datetime.a seek.a case.a ndelay.a open.a sig.a getln.a stralloc.a alloc.a \
+substdio.a error.a control.o constmap.o str.a fs.a auto_qmail.o \
+auto_split.o auto_uids.o
 	./load qmail-queue triggerpull.o fmtqfn.o now.o \
-	date822fmt.o datetime.a seek.a ndelay.a open.a sig.a \
-	alloc.a substdio.a error.a str.a fs.a auto_qmail.o \
-	auto_split.o auto_uids.o 
+	date822fmt.o qregex.o control.o constmap.o datetime.a case.a seek.a \
+	ndelay.a open.a sig.a getln.a stralloc.a alloc.a substdio.a error.a \
+	str.a fs.a auto_qmail.o auto_split.o auto_uids.o 
 
 qmail-queue.0: \
 qmail-queue.8
@@ -1439,14 +1466,16 @@
 
 qmail-remote: \
 load qmail-remote.o control.o constmap.o timeoutread.o timeoutwrite.o \
-timeoutconn.o tcpto.o now.o dns.o ip.o ipalloc.o ipme.o quote.o \
+timeoutconn.o tcpto.o now.o dns.o ip.o ipalloc.o strsalloc.o ipme.o quote.o \
 ndelay.a case.a sig.a open.a lock.a seek.a getln.a stralloc.a alloc.a \
-substdio.a error.a str.a fs.a auto_qmail.o dns.lib socket.lib
+substdio.a error.a str.a fs.a auto_qmail.o base64.o qregex.o dns.lib \
+socket.lib
 	./load qmail-remote control.o constmap.o timeoutread.o \
 	timeoutwrite.o timeoutconn.o tcpto.o now.o dns.o ip.o \
-	ipalloc.o ipme.o quote.o ndelay.a case.a sig.a open.a \
+	ipalloc.o strsalloc.o ipme.o quote.o ndelay.a case.a sig.a open.a \
 	lock.a seek.a getln.a stralloc.a alloc.a substdio.a error.a \
-	str.a fs.a auto_qmail.o  `cat dns.lib` `cat socket.lib`
+	str.a fs.a auto_qmail.o base64.o qregex.o `cat dns.lib` \
+	`cat socket.lib` -lssl -lcrypto
 
 qmail-remote.0: \
 qmail-remote.8
@@ -1455,7 +1484,7 @@
 qmail-remote.o: \
 compile qmail-remote.c sig.h stralloc.h gen_alloc.h substdio.h \
 subfd.h substdio.h scan.h case.h error.h auto_qmail.h control.h dns.h \
-alloc.h quote.h ip.h ipalloc.h ip.h gen_alloc.h ipme.h ip.h ipalloc.h \
+alloc.h quote.h ip.h ipalloc.h strsalloc.h ip.h gen_alloc.h ipme.h ip.h ipalloc.h strsalloc.h \
 gen_alloc.h gen_allocdefs.h str.h now.h datetime.h exit.h constmap.h \
 tcpto.h readwrite.h timeoutconn.h timeoutread.h timeoutwrite.h
 	./compile qmail-remote.c
@@ -1483,12 +1512,12 @@
 trigger.o fmtqfn.o quote.o now.o readsubdir.o qmail.o date822fmt.o \
 datetime.a case.a ndelay.a getln.a wait.a seek.a fd.a sig.a open.a \
 lock.a stralloc.a alloc.a substdio.a error.a str.a fs.a auto_qmail.o \
-auto_split.o
+auto_split.o env.a
 	./load qmail-send qsutil.o control.o constmap.o newfield.o \
 	prioq.o trigger.o fmtqfn.o quote.o now.o readsubdir.o \
 	qmail.o date822fmt.o datetime.a case.a ndelay.a getln.a \
 	wait.a seek.a fd.a sig.a open.a lock.a stralloc.a alloc.a \
-	substdio.a error.a str.a fs.a auto_qmail.o auto_split.o 
+	substdio.a error.a str.a fs.a auto_qmail.o auto_split.o env.a
 
 qmail-send.0: \
 qmail-send.8
@@ -1509,7 +1538,7 @@
 scan.h case.h auto_qmail.h trigger.h newfield.h stralloc.h quote.h \
 qmail.h substdio.h qsutil.h prioq.h datetime.h gen_alloc.h constmap.h \
 fmtqfn.h readsubdir.h direntry.h
-	./compile qmail-send.c
+	./compile $(DEFINES) qmail-send.c
 
 qmail-showctl: \
 load qmail-showctl.o auto_uids.o control.o open.a getln.a stralloc.a \
@@ -1528,21 +1557,23 @@
 compile qmail-showctl.c substdio.h subfd.h substdio.h exit.h fmt.h \
 str.h control.h constmap.h stralloc.h gen_alloc.h direntry.h \
 auto_uids.h auto_qmail.h auto_break.h auto_patrn.h auto_spawn.h \
-auto_split.h
+auto_split.h spf.h
 	./compile qmail-showctl.c
 
 qmail-smtpd: \
-load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \
-timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \
-date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \
-open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \
-fs.a auto_qmail.o socket.lib
-	./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \
-	timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \
-	received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \
-	datetime.a getln.a open.a sig.a case.a env.a stralloc.a \
-	alloc.a substdio.a error.a str.a fs.a auto_qmail.o  `cat \
-	socket.lib`
+load qmail-smtpd.o qregex.o rcpthosts.o commands.o timeoutread.o \
+timeoutwrite.o ip.o ipme.o ipalloc.o strsalloc.o control.o constmap.o \
+received.o date822fmt.o now.o qmail.o spf.o dns.o cdb.a fd.a wait.a \
+datetime.a getln.a open.a sig.a case.a env.a stralloc.a alloc.a strerr.a \
+substdio.a error.a str.a fs.a auto_qmail.o base64.o socket.lib dns.lib \
+auto_break.o
+	./load qmail-smtpd qregex.o rcpthosts.o commands.o timeoutread.o \
+	timeoutwrite.o ip.o ipme.o ipalloc.o strsalloc.o control.o \
+	constmap.o received.o date822fmt.o now.o qmail.o spf.o dns.o cdb.a \
+	fd.a wait.a datetime.a getln.a open.a sig.a case.a env.a stralloc.a \
+	alloc.a strerr.a substdio.a error.a str.a fs.a auto_qmail.o \
+	auto_break.o base64.o `cat socket.lib` `cat dns.lib` \
+	-lssl -lcrypto -lcrypt
 
 qmail-smtpd.0: \
 qmail-smtpd.8
@@ -1551,9 +1582,10 @@
 qmail-smtpd.o: \
 compile qmail-smtpd.c sig.h readwrite.h stralloc.h gen_alloc.h \
 substdio.h alloc.h auto_qmail.h control.h received.h constmap.h \
-error.h ipme.h ip.h ipalloc.h ip.h gen_alloc.h ip.h qmail.h \
+error.h ipme.h ip.h ipalloc.h strsalloc.h ip.h gen_alloc.h ip.h qmail.h \
 substdio.h str.h fmt.h scan.h byte.h case.h env.h now.h datetime.h \
-exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h
+exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h spf.h \
+cdb.h uint32.h auto_break.h
 	./compile qmail-smtpd.c
 
 qmail-start: \
@@ -1574,7 +1606,7 @@
 
 qmail-start.o: \
 compile qmail-start.c fd.h prot.h exit.h fork.h auto_uids.h
-	./compile qmail-start.c
+	./compile $(DEFINES) qmail-start.c
 
 qmail-tcpok: \
 load qmail-tcpok.o open.a lock.a strerr.a substdio.a error.a str.a \
@@ -1606,6 +1638,20 @@
 fmt.h ip.h lock.h error.h exit.h datetime.h now.h datetime.h
 	./compile qmail-tcpto.c
 
+qmail-todo: \
+load qmail-todo.o control.o constmap.o trigger.o fmtqfn.o now.o \
+readsubdir.o case.a ndelay.a getln.a sig.a open.a stralloc.a alloc.a \
+substdio.a error.a str.a fs.a auto_qmail.o auto_split.o
+	./load qmail-todo control.o constmap.o trigger.o fmtqfn.o now.o \
+	readsubdir.o case.a ndelay.a getln.a sig.a open.a stralloc.a \
+	alloc.a substdio.a error.a str.a fs.a auto_qmail.o auto_split.o
+
+qmail-todo.o: \
+compile alloc.h auto_qmail.h byte.h constmap.h control.h direntry.h error.h \
+exit.h fmt.h fmtqfn.h getln.h open.h ndelay.h now.h readsubdir.h readwrite.h \
+scan.h select.h str.h stralloc.h substdio.h trigger.h
+	./compile $(DEFINES) qmail-todo.c
+
 qmail-upq: \
 warn-auto.sh qmail-upq.sh conf-qmail conf-break conf-split
 	cat warn-auto.sh qmail-upq.sh \
@@ -1681,6 +1727,10 @@
 constmap.h stralloc.h gen_alloc.h rcpthosts.h
 	./compile rcpthosts.c
 
+qregex.o: \
+compile qregex.c qregex.h
+	./compile qregex.c
+
 readsubdir.o: \
 compile readsubdir.c readsubdir.h direntry.h fmt.h scan.h str.h \
 auto_split.h
@@ -1779,7 +1829,7 @@
 qmail-qread.c qmail-qstat.sh qmail-queue.c qmail-remote.c \
 qmail-rspawn.c qmail-send.c qmail-showctl.c qmail-smtpd.c \
 qmail-start.c qmail-tcpok.c qmail-tcpto.c spawn.c dnscname.c dnsfq.c \
-dnsip.c dnsmxip.c dnsptr.c hostname.c ipmeprint.c tcp-env.c \
+dnsip.c dnsmxip.c dnsptr.c dnstxt.c hostname.c ipmeprint.c tcp-env.c \
 sendmail.c qreceipt.c qsmhook.c qbiff.c forward.c preline.c predate.c \
 except.c bouncesaying.c condredirect.c maildirmake.c maildir2mbox.c \
 maildirwatch.c splogger.c qail.sh elq.sh pinq.sh qmail-upq.sh \
@@ -1813,8 +1863,9 @@
 trywaitp.c sig.h sig_alarm.c sig_block.c sig_catch.c sig_pause.c \
 sig_pipe.c sig_child.c sig_term.c sig_hup.c sig_misc.c sig_bug.c \
 trysgact.c trysgprm.c env.3 env.h env.c envread.c byte.h byte_chr.c \
-byte_copy.c byte_cr.c byte_diff.c byte_rchr.c byte_zero.c str.h \
-str_chr.c str_cpy.c str_diff.c str_diffn.c str_len.c str_rchr.c \
+byte_copy.c byte_cr.c byte_cspn.c byte_diff.c byte_rchr.c byte_rcspn.c \
+byte_zero.c str.h spf.c spf.h spfquery.c \
+str_chr.c str_cpy.c str_cpyb.c str_diff.c str_diffn.c str_len.c str_rchr.c \
 str_start.c lock.h lock_ex.c lock_exnb.c lock_un.c tryflock.c getln.3 \
 getln.h getln.c getln2.3 getln2.c sgetopt.3 sgetopt.h sgetopt.c \
 subgetopt.3 subgetopt.h subgetopt.c error.3 error_str.3 error_temp.3 \
@@ -1824,7 +1875,7 @@
 headerbody.h headerbody.c token822.h token822.c control.h control.c \
 datetime.3 datetime.h datetime.c datetime_un.c prioq.h prioq.c \
 date822fmt.h date822fmt.c dns.h dns.c trylsock.c tryrsolv.c ip.h ip.c \
-ipalloc.h ipalloc.c select.h1 select.h2 trysysel.c ndelay.h ndelay.c \
+ipalloc.h strsalloc.h ipalloc.c select.h1 select.h2 trysysel.c ndelay.h ndelay.c \
 ndelay_off.c direntry.3 direntry.h1 direntry.h2 trydrent.c prot.h \
 prot.c chkshsgr.c warn-shsgr tryshsgr.c ipme.h ipme.c trysalen.c \
 maildir.5 maildir.h maildir.c tcp-environ.5 constmap.h constmap.c
@@ -1897,6 +1948,24 @@
 	./chkspawn
 	./compile spawn.c
 
+spf.o: \
+compile spf.c stralloc.h gen_alloc.h alloc.h ipme.h ip.h ipalloc.h \
+strsalloc.h str.h fmt.h scan.h byte.h now.h case.h qregex.h
+	./compile spf.c
+
+spfquery: \
+load spfquery.o spf.o ip.o ipme.o ipalloc.o strsalloc.o now.o dns.o env.o \
+datetime.a stralloc.a alloc.a str.a substdio.a error.a fs.a case.a dns.lib \
+socket.lib envread.o qregex.o
+	./load spfquery spf.o ip.o ipme.o ipalloc.o strsalloc.o \
+	now.o dns.o env.o datetime.a stralloc.a alloc.a str.a substdio.a \
+	case.a error.a fs.a `cat dns.lib` `cat socket.lib` envread.o qregex.o
+
+spfquery.o: \
+compile spfquery.c substdio.h subfd.h stralloc.h gen_alloc.h alloc.h \
+spf.h exit.h
+	./compile spfquery.c
+
 splogger: \
 load splogger.o substdio.a error.a str.a fs.a syslog.lib socket.lib
 	./load splogger substdio.a error.a str.a fs.a  `cat \
@@ -1912,12 +1981,12 @@
 	./compile splogger.c
 
 str.a: \
-makelib str_len.o str_diff.o str_diffn.o str_cpy.o str_chr.o \
-str_rchr.o str_start.o byte_chr.o byte_rchr.o byte_diff.o byte_copy.o \
-byte_cr.o byte_zero.o
-	./makelib str.a str_len.o str_diff.o str_diffn.o str_cpy.o \
-	str_chr.o str_rchr.o str_start.o byte_chr.o byte_rchr.o \
-	byte_diff.o byte_copy.o byte_cr.o byte_zero.o
+makelib str_len.o str_diff.o str_diffn.o str_cpy.o str_cpyb.o str_chr.o \
+str_rchr.o str_start.o byte_chr.o byte_rchr.o byte_cspn.o byte_rcspn.o \
+byte_diff.o byte_copy.o byte_cr.o byte_zero.o
+	./makelib str.a str_len.o str_diff.o str_diffn.o str_cpy.o str_cpyb.o \
+	str_chr.o str_rchr.o str_start.o byte_chr.o byte_rchr.o byte_cspn.o \
+	byte_rcspn.o byte_diff.o byte_copy.o byte_cr.o byte_zero.o
 
 str_chr.o: \
 compile str_chr.c str.h
@@ -1927,6 +1996,10 @@
 compile str_cpy.c str.h
 	./compile str_cpy.c
 
+str_cpyb.o: \
+compile str_cpyb.c str.h
+	./compile str_cpyb.c
+
 str_diff.o: \
 compile str_diff.c str.h
 	./compile str_diff.c
@@ -2006,6 +2079,11 @@
 compile strerr_sys.c error.h strerr.h
 	./compile strerr_sys.c
 
+strsalloc.o: \
+compile strsalloc.c alloc.h gen_allocdefs.h stralloc.h strsalloc.h \
+gen_alloc.h
+	./compile strsalloc.c
+
 subfderr.o: \
 compile subfderr.c readwrite.h substdio.h subfd.h substdio.h
 	./compile subfderr.c
@@ -2066,11 +2144,13 @@
 
 tcp-env: \
 load tcp-env.o dns.o remoteinfo.o timeoutread.o timeoutwrite.o \
-timeoutconn.o ip.o ipalloc.o case.a ndelay.a sig.a env.a getopt.a \
-stralloc.a alloc.a substdio.a error.a str.a fs.a dns.lib socket.lib
+constmap.o control.o open.a getln.a \
+timeoutconn.o ip.o ipalloc.o strsalloc.o case.a ndelay.a sig.a env.a \
+getopt.a stralloc.a alloc.a substdio.a error.a str.a fs.a dns.lib socket.lib
 	./load tcp-env dns.o remoteinfo.o timeoutread.o \
-	timeoutwrite.o timeoutconn.o ip.o ipalloc.o case.a ndelay.a \
-	sig.a env.a getopt.a stralloc.a alloc.a substdio.a error.a \
+	constmap.o control.o open.a getln.a \
+	timeoutwrite.o timeoutconn.o ip.o ipalloc.o strsalloc.o case.a \
+	ndelay.a sig.a env.a getopt.a stralloc.a alloc.a substdio.a error.a \
 	str.a fs.a  `cat dns.lib` `cat socket.lib`
 
 tcp-env.0: \
@@ -2139,3 +2219,23 @@
 wait_pid.o: \
 compile wait_pid.c error.h haswaitp.h
 	./compile wait_pid.c
+
+cert:
+	openssl req -new -x509 -nodes \
+	-out /var/qmail/control/servercert.pem -days 366 \
+	-keyout /var/qmail/control/servercert.pem
+	chmod 640 /var/qmail/control/servercert.pem
+	chown qmaild.qmail /var/qmail/control/servercert.pem
+	ln -s /var/qmail/control/servercert.pem /var/qmail/control/clientcert.pem
+
+cert-req:
+	openssl req -new -nodes \
+	-out req.pem \
+	-keyout /var/qmail/control/servercert.pem
+	chmod 640 /var/qmail/control/servercert.pem
+	chown qmaild.qmail /var/qmail/control/servercert.pem
+	ln -s /var/qmail/control/servercert.pem /var/qmail/control/clientcert.pem
+	@echo
+	@echo "Send req.pem to your CA to obtain signed_req.pem, and do:"
+	@echo "cat signed_req.pem >> /var/qmail/control/servercert.pem"
+
diff -ruN qmail-1.03-factory/README.auth qmail-1.03-7.10/README.auth
--- qmail-1.03-factory/README.auth	1969-12-31 19:00:00.000000000 -0500
+++ qmail-1.03-7.10/README.auth	2010-02-14 22:53:32.000000000 -0500
@@ -0,0 +1,175 @@
+*** Warning! Cuidado! Vorsicht! ***
+===================================
+*** Version 0.30 of the patch changes the arguments which must be
+*** passed to qmail-smtpd.  If you are upgrading from a previous
+*** version of the patch, take care to ensure your invocation of
+*** qmail-smtpd uses the correct arguments.  Otherwise, your server
+*** may run as an open relay!
+===================================
+*** Warning! Cuidado! Vorsicht! ***
+
+
+This patch adds ESMTP AUTH authentication protocol support to
+qmail-1.03.  It's originally based on Mrs. Brisby's smtp-auth patch
+with many enhancements from Krzysztof Dabrowski <brush@elysium.pl>.
+
+Beginning with version 0.30, the patch was completely rewritten to
+use only djb's string functions by Eric M. Johnston <emj@postal.net>.
+
+You can always get the newest version from:
+http://members.elysium.pl/brush/qmail-smtpd-auth/
+
+To use all of it's functionality you will also have to obtain and
+install Krzysztof's cmd5checkpw utility available at:
+http://members.elysium.pl/brush/cmd5checkpw/
+
+If you need more information about SMTP-AUTH itself and the
+client/server support and configuration, visit:
+http://members.elysium.pl/brush/smtp-auth/
+
+---
+
+Detailed patch information:
+
+This patch adds the ESMTP AUTH option to qmail-1.03, allowing the
+LOGIN, PLAIN, and CRAM-MD5 AUTH types. An appropriate checkpassword
+tool is necessary to support the authentication.  See
+http://cr.yp.to/checkpwd.html for more information on the interface.
+Note that the checkpassword tool should support all of the AUTH types
+advertised by qmail-smtpd.
+
+As reflected in the modified qmail-smtpd(8) man page, qmail-smtpd
+must be invoked with three arguments: hostname, checkprogram, and
+subprogram.  If these arguments are missing, qmail-smtpd will still
+advertise availability of AUTH, but will fail with a permanent error
+when AUTH is used.
+
+hostname is simply used to form the CRAM-MD5 challenge.  qmail-smtpd
+invokes checkprogram, feeding it the username and password, in the
+case of LOGIN or PLAIN, or the username, challenge, and response, in
+the case of CRAM-MD5.  If the user is permitted, checkprogram invokes
+subprogram, which just has to exit with a status of 0 for the user to
+be authenticated.  Otherwise, checkprogram exits with a non-zero
+status.  subprogram can usually be /usr/bin/true (or /bin/true,
+depending on your flavor of OS).
+
+If the user is successfully authenticated, the RELAYCLIENT
+environment variable is effectively set for the SMTP session, and
+the TCPREMOTEINFO environment variable is set to the authenticated
+username, overriding any value that tcpserver may have set.  The
+value of TCPREMOTEINFO is reflected in a Received header.
+
+
+How to install it:
+
+Simply patch your qmail-1.03 distribution with the included patch
+file and recompile & install like usual.
+
+The steps to do this are as follows (assuming your virgin
+qmail-1.03 install is in "../qmail-1.03"):
+
+  cp README.auth base64.c base64.h ../qmail-1.03
+  patch -d ../qmail-1.03 < auth.patch
+
+Install qmail normally, with the exception of the new arguments
+to qmail-smtpd described elsewhere in this file.
+
+Also obtain, unpack, compile and install the cmd5checkpw utility
+(or some other checkpassword utility) and add a sample account to
+/etc/poppasswd file.  This file must be readable by the qmail-smtpd
+user, usually qmaild.
+
+
+How to use it:
+
+*** Warning: In version 0.30 the arguments have changed from
+*** previous versions of qmail-smtpd-auth.  Take care to make sure
+*** you update your startup scripts if updating!
+
+If you're running qmail-smtpd from inetd, you'll want to do the
+following:
+
+smtp stream tcp nowait qmaild /var/qmail/bin/tcp-env tcp-env \
+/var/qmail/bin/qmail-smtpd mail.acme.com /bin/cmd5checkpw /bin/true
+
+Replace mail.acme.com with your hostname.  The second argument to
+qmail-smtpd is your checkpassword utility (preferably cmd5checkpw
+or some alternative that can handle CRAM-MD5).  The third argument
+is the executable that the checkpassword utility execs when
+authentication is successful.  (Note that the location of "true"
+is OS dependent: you may need /usr/bin/true.)
+
+Invocations using tcpserver will require analagous changes.  Give
+your inetd a kill -HUP or restart tcpserver and away you go.
+
+
+Caveats:
+
+Please note that as authentication needs vary wildly across
+installations, no effort has been made to make this patch work ``out
+of the box.''  You'll have to procure or develop your own
+checkpassword program.  Also note that CRAM-MD5 will require you to
+keep plaintext passwords.  You'll probably want to disable this AUTH
+type if you're just using /etc/passwd (keeping in mind that PLAIN and
+LOGIN aren't quite as safe over the wire) -- just undefine AUTHCRAM
+in qmail-smtpd.
+
+Krzysztof Dabrowski's cmd5checkpw tool used as an example in this
+document supports the three AUTH types included in this patch.
+It's available at http://www.elysium.pl/members/brush/cmd5checkpw/.
+
+This patch has been generated against the stock qmail 1.03
+distribution.  The results of combining this patch with others are
+unknown.
+
+
+Features:
+
+This patch supports the following auth methods: LOGIN, PLAIN and
+CRAM-MD5.
+
+
+Compatibility:
+
+The following MUA's are confirmed to work with this patch:
+
+Eudora 4.2.2		-	CRAM-MD5
+Eudora 5.0.2 		- 	CRAM-MD5
+The Bat 1.39		-	LOGIN & CRAM-MD5
+Outlook Express 4	- 	LOGIN
+Outlook Express 5	-	LOGIN
+Outlook 2000 		- 	LOGIN
+Netscape 4.x		-	LOGIN & PLAIN
+Netscape 4.0x		-	LOGIN
+Pegasus Mail 3.1x	-	CRAM-MD5
+
+
+Various compatibility issues:
+
+Testing with Pegasus Mail 3.1 revealed that it requires the new style
+(RFC recommended) greeting message.  Both styles are now enabled to
+maintain the highest degree of compatibility with various clients.
+This fix was suggested by David Harris <David.Harris@pmail.gen.nz>,
+the developer of Pegasus Mail.
+
+
+Acknowledgments:
+
+This patch is based on work by Krzysztof Dabrowski at
+http://members.elysium.pl/brush/qmail-smtpd-auth/ and ``Mrs. Brisby''
+at http://www.nimh.org/hacks/qmail-smtpd.c which has been further
+developed by Eric M. Johnston <emj@postal.net>.
+
+---
+
+THIS SOFTWARE IS IN THE PUBLIC DOMAIN, IS PROVIDED BY THE AUTHOR
+``AS IS,'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff -ruN qmail-1.03-factory/README.qregex qmail-1.03-7.10/README.qregex
--- qmail-1.03-factory/README.qregex	1969-12-31 19:00:00.000000000 -0500
+++ qmail-1.03-7.10/README.qregex	2010-02-14 22:53:32.000000000 -0500
@@ -0,0 +1,203 @@
+QREGEX (v2) 20060423 - README April 23, 2006
+A Regular Expression matching patch for qmail 1.03 and netqmail
+
+
+OVERVIEW:
+
+qregex adds the ability to match address evelopes via Regular Expressions (REs)
+in the qmail-smtpd process. It has the abiltiy to match `helo/ehlo` (host name),
+`mail from` (envelope sender), and `rcpt to` (envelope recipient) commands.
+It follows all the base rules that are set out with qmail (ie using control
+files) so it makes for easy integretion into an existing setup (see the
+install instructions for more info). The v2 is specified because qregex was
+re-written to better conform to the security guarantee set forth by the author
+of qmail. The original version used stdio.h and stdlib.h for reading the
+control files whereas v2 now uses all stralloc functions which are much more
+regulated against buffer overruns and the like.
+See: http://cr.yp.to/qmail/guarantee.html
+
+
+FEATURES:
+
+Features of qregex include:
+
+1. Performs pattern matching on envelope senders and envelope
+   recipients against REs in the badmailfrom and badrcptto control
+   files. Two additional control files, badmailfromnorelay and
+   badrcpttonorelay, are used for pattern matching when the 
+   RELAYCLIENT environment variable is not set.
+
+2. Performs pattern matching on the helo/ehlo host name. Setting the
+   NOBADHELO environment variable prevents the host name from being
+   compared to the patterns in the badhelo control file.
+
+3. Matches to patterns are logged. Setting the LOGREGEX environment
+   variable causes the matched regex pattern to be included in the log. 
+
+4. Matching is case insensitive.
+
+5. qregex ignores empty envelope senders. An empty envelope sender is not
+   compared to the patterns in the badmailfrom and badmailfromnorelay
+   control files and is always accepted.
+
+
+PLATFORMS:
+
+qregex has been built and tested on the following platforms. I'm sure it won't
+have any problems on any platform that qmail will run on (providing they have
+a regex interface) but if you run into problems let me know.
+
+	- OpenBSD 3.x
+	- FreeBSD 4.x, 5.x
+	- Mandrake Linux 9.x
+	- SuSE Linux 8.x
+
+
+
+INSTALLATION INSTRUCTIONS:
+
+Installation is very simple, there is only one requirement. You need to use the
+GNU version of the patch utility (http://www.gnu.org/software/patch/patch.html).
+(For Solaris 8 users it is installed as 'gpatch')
+
+- If this is a new setup.
+Unpack the qmail archive, cd into the qmail-1.03 directory and run
+"patch < /path/to/qregex-<version>.patch". Follow the instructions as per the
+included qmail INSTALL file. Once you are done come back to this file and read
+the section on the control files.
+
+If you are using netqmail, then unpack the netqmail archive. Run the collate.sh
+script and cd into the resulting netqmail-<version> directory. From there, run
+"patch < /path/to/qregex-<version>.patch". Complete the netqmail installation
+normally. Once you are done, come back to this file and read the section on the
+control files.
+
+- If this is an existing setup.
+FIRST: create your control files (see below).
+cd into your existing qmail or netqmail source directory. Run
+"patch < /path/to/qregex-<version>.patch" then "make qmail-smtpd". Now run
+./qmail-smtpd and test your new rules to make sure they work as expected.
+
+Install the new binary by cd'ing to /var/qmail/bin and as root (in one command)
+copy the existing binary to 'qmail-smtpd.old' and copy the new binary from the
+source directory to 'qmail-smtpd'.
+(ex. cp qmail-smtpd qmail-smtpd.old && cp ~/qmail-1.03/qmail-smtpd qmail-smtpd)
+
+You can also optionally just run "make setup check" as it will install the
+updated documentation and man pages provided with this patch. Stopping qmail
+before doing the "make setup check" is always a good idea.
+
+
+LOGGING:
+
+qregex will log matches to the patterns in the various control files. Log
+messages will take these three forms depending on which control file was
+matched:
+
+badhelo
+qmail-smtpd: badhelo: <host> at <remote IP>
+
+badmailfrom and badmailfromnorelay
+qmail-smtpd: badmailfrom: <sender address> at <remote IP>
+
+badrcptto and badrcpttonorelay
+qmail-smtpd: badrcptto: <rcpt address> at <remote IP>
+
+When the LOGREGEX environment variable is set, the matched pattern will
+be included in the log. Log messages will have the regex pattern appended
+to them. For example, a badhelo log message will look like this:
+
+qmail-smtpd: badhelo: <host> at <remote IP> matches pattern: <regex>
+
+
+CONTROL FILES:
+
+qregex provides you with five control files. None of these control files
+is mandatory and you can use them in any combination you choose in your setup.
+
+The "control/badmailfrom" and "control/badrcptto" files contain your REs for
+matching against the 'mail from' (envelope sender) and 'rcpt to' (envelope
+recipient) smtp commands respectively.
+The "control/badmailfromnorelay" and "control/badrcptonorelay" match against
+the same commands but are read only when the RELAYCLIENT environment variable
+is not set.
+The "control/badhelo" file matches against the 'helo/ehlo' smtp command.
+
+If you prefer you can symlink the badmailfrom and badrcptto control files
+(ln -s badmailfrom badrcptto) and maintain fewer sets of rules. Beware
+this might cause problems in certain setups.
+        
+	Here's an example "badhelo" file.
+	-----------------------------------
+	# block host strings with no dot (not a FQDN)
+	!\.
+	-----------------------------------
+	
+	An example "badmailfrom" file.
+	-----------------------------------
+	# this will drop everything containing the string
+	# bad.domain.com or Bad.Domain.Com or BAD.domain.COM
+	bad\.domain\.com
+	# force users to fully qualify themselves
+	# (i.e. deny "user", accept "user@domain")
+	!@
+	-----------------------------------
+
+	And "badrcptto" (a little more interesting)
+	-----------------------------------
+	# must not contain invalid characters, brakets or multiple @'s
+	[!%#:*^(){}]
+	@.*@
+	-----------------------------------
+
+You can use the non-RE character '!' to start an RE as a signal to qregex to
+negate the action. As used above in the badmailfrom file, by negating the '@'
+symbol qregex will signal qmail-smtpd to deny the 'mail from' command whenever
+the address doesn't contain an @ symbol. When used inside a bracket expression,
+the '!' character looses this special meaning. This is shown in the badrcptto
+example.
+
+The norelay control files follow the same rules as the other control files but
+are intended to address two specific scenarios.
+The badmailfromnorelay file can be used to block mail trying to spoof a domain
+hosted on your mail server. It prevents a mail client that is not allowed to
+relay email through your server from using one of your hosted domains as its
+envelope sender.
+The badrcpttonorelay file can be used to create email addresses that cannot
+receive mail from any source not allowed to relay email through your server.
+This is handy for creating email addresses for use only within your own 
+domain(s) that can't receive spam from the world at large.
+
+
+INTERNALS:
+
+qregex (or regexmatch as the function is called) will be called during the
+`helo/ehlo`, `rcpt to` and `mail from` handling routines in "qmail-smtpd.c".
+When called, it will read the proper control file then one by one compile and
+execute the regex on the string passed into qmail-smtpd. If the regex matches
+it returns TRUE (1) and the qmail-smtpd process will deny the user the ability
+to continue. If you change anything and think it betters this patch please
+send me a new diff file so I can take a peek.
+
+
+CONTACT:
+qregex is maintained by:
+	Andrew St. Jean
+	andrew@arda.homeunix.net
+	www.arda.homeunix.net/store/qmail/
+
+Contributers to qregex:
+	Jeremy Kitchen	
+	kitchen at scriptkitchen dot com
+	http://www.scriptkitchen.com/qmail
+
+	Alex Pleiner
+	alex@zeitform.de
+	zeitform Internet Dienste
+	http://www.zeitform.de/
+
+	Thanos Massias
+
+Original qregex patch written by:
+	Evan Borgstrom
+	evan at unixpimps dot org
diff -ruN qmail-1.03-factory/README.tap qmail-1.03-7.10/README.tap
--- qmail-1.03-factory/README.tap	1969-12-31 19:00:00.000000000 -0500
+++ qmail-1.03-7.10/README.tap	2010-02-14 22:53:32.000000000 -0500
@@ -0,0 +1,28 @@
+qmail provides the ability to make a copy of each email that flows through the system.
+This is done using the QUEUE_EXTRA code. See qmail FAQ #8.2
+
+The qmail tap patch adds additional functionality:
+1) Specify which email addresses to tap using a regex style control file. With the
+   regex function, you can specify full domains or individual email addresses.
+
+2) Specify which email address to send the emails to.
+
+3) Qmail does not need to be restated to when the taps control file is changed.
+
+The regex match is applied to both the to and from email addresses. So email
+sent to or from the addresses will be copied. Matching is case insensitive.
+If there are multiple matches, the first match is used.
+
+The queue tap patch adds a new control file:
+
+/var/qmail/control/taps
+Contains a regex style list of addresses to tap and the email
+address of where you want the copy sent to.
+
+Examples:
+a) To tap a whole domain add a line like:
+.*@domain.com:joe@example.com
+
+
+b) To tap an individual email address add a line like:
+user@domain.com:other@example.com
diff -ruN qmail-1.03-factory/TARGETS qmail-1.03-7.10/TARGETS
--- qmail-1.03-factory/TARGETS	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/TARGETS	2010-02-14 22:53:32.000000000 -0500
@@ -100,11 +100,14 @@
 str_diff.o
 str_diffn.o
 str_cpy.o
+str_cpyb.o
 str_chr.o
 str_rchr.o
 str_start.o
 byte_chr.o
 byte_rchr.o
+byte_cspn.o
+byte_rcspn.o
 byte_diff.o
 byte_copy.o
 byte_cr.o
@@ -171,8 +174,10 @@
 timeoutconn.o
 tcpto.o
 dns.o
+spf.o
 ip.o
 ipalloc.o
+strsalloc.o
 hassalen.h
 ipme.o
 ndelay.o
@@ -212,6 +217,9 @@
 headerbody.o
 hfield.o
 token822.o
+spf.o
+spfquery.o
+spfquery
 qmail-inject
 predate.o
 predate
@@ -250,8 +258,10 @@
 qmail-qmtpd.o
 rcpthosts.o
 qmail-qmtpd
+base64.o
 qmail-smtpd.o
 qmail-smtpd
+qregex.o
 sendmail.o
 sendmail
 tcp-env.o
@@ -270,6 +280,8 @@
 dnsip
 dnsmxip.o
 dnsmxip
+dnstxt.o
+dnstxt
 dnsfq.o
 dnsfq
 hostname.o
@@ -385,3 +397,5 @@
 man
 setup
 check
+qmail-todo.o
+qmail-todo
diff -ruN qmail-1.03-factory/base64.c qmail-1.03-7.10/base64.c
--- qmail-1.03-factory/base64.c	1969-12-31 19:00:00.000000000 -0500
+++ qmail-1.03-7.10/base64.c	2010-02-14 22:53:32.000000000 -0500
@@ -0,0 +1,123 @@
+#include "base64.h"
+#include "stralloc.h"
+#include "substdio.h"
+#include "str.h"
+
+static char *b64alpha =
+  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+#define B64PAD '='
+
+/* returns 0 ok, 1 illegal, -1 problem */
+
+int b64decode(in,l,out)
+const unsigned char *in;
+int l;
+stralloc *out; /* not null terminated */
+{
+  int p = 0;
+  int n;
+  unsigned int x;
+  int i, j;
+  char *s;
+  unsigned char b[3];
+
+  if (l == 0)
+  {
+    if (!stralloc_copys(out,"")) return -1;
+    return 0;
+  }
+
+  while(in[l-1] == B64PAD) {
+    p ++;
+    l--;
+  }
+
+  n = (l + p) / 4;
+  i = (n * 3) - p;
+  if (!stralloc_ready(out,i)) return -1;
+  out->len = i;
+  s = out->s;
+
+  for(i = 0; i < n - 1 ; i++) {
+    x = 0;
+    for(j = 0; j < 4; j++) {
+      if(in[j] >= 'A' && in[j] <= 'Z')
+        x = (x << 6) + (unsigned int)(in[j] - 'A' + 0);
+      else if(in[j] >= 'a' && in[j] <= 'z')
+        x = (x << 6) + (unsigned int)(in[j] - 'a' + 26);
+      else if(in[j] >= '0' && in[j] <= '9')
+        x = (x << 6) + (unsigned int)(in[j] - '0' + 52);
+      else if(in[j] == '+')
+        x = (x << 6) + 62;
+      else if(in[j] == '/')
+        x = (x << 6) + 63;
+      else if(in[j] == '=')
+        x = (x << 6);
+    }
+
+    s[2] = (unsigned char)(x & 255); x >>= 8;
+    s[1] = (unsigned char)(x & 255); x >>= 8;
+    s[0] = (unsigned char)(x & 255); x >>= 8;
+    s += 3; in += 4;
+  }
+
+  x = 0;
+  for(j = 0; j < 4; j++) {
+    if(in[j] >= 'A' && in[j] <= 'Z')
+      x = (x << 6) + (unsigned int)(in[j] - 'A' + 0);
+    else if(in[j] >= 'a' && in[j] <= 'z')
+      x = (x << 6) + (unsigned int)(in[j] - 'a' + 26);
+    else if(in[j] >= '0' && in[j] <= '9')
+      x = (x << 6) + (unsigned int)(in[j] - '0' + 52);
+    else if(in[j] == '+')
+      x = (x << 6) + 62;
+    else if(in[j] == '/')
+      x = (x << 6) + 63;
+    else if(in[j] == '=')
+      x = (x << 6);
+  }
+
+  b[2] = (unsigned char)(x & 255); x >>= 8;
+  b[1] = (unsigned char)(x & 255); x >>= 8;
+  b[0] = (unsigned char)(x & 255); x >>= 8;
+
+  for(i = 0; i < 3 - p; i++)
+    s[i] = b[i];
+
+  return 0;
+}
+
+int b64encode(in,out)
+stralloc *in;
+stralloc *out; /* not null terminated */
+{
+  unsigned char a, b, c;
+  int i;
+  char *s;
+
+  if (in->len == 0)
+  {
+    if (!stralloc_copys(out,"")) return -1;
+    return 0;
+  }
+
+  if (!stralloc_ready(out,in->len / 3 * 4 + 4)) return -1;
+  s = out->s;
+
+  for (i = 0;i < in->len;i += 3) {
+    a = in->s[i];
+    b = i + 1 < in->len ? in->s[i + 1] : 0;
+    c = i + 2 < in->len ? in->s[i + 2] : 0;
+
+    *s++ = b64alpha[a >> 2];
+    *s++ = b64alpha[((a & 3 ) << 4) | (b >> 4)];
+
+    if (i + 1 >= in->len) *s++ = B64PAD;
+    else *s++ = b64alpha[((b & 15) << 2) | (c >> 6)];
+
+    if (i + 2 >= in->len) *s++ = B64PAD;
+    else *s++ = b64alpha[c & 63];
+  }
+  out->len = s - out->s;
+  return 0;
+}
diff -ruN qmail-1.03-factory/base64.h qmail-1.03-7.10/base64.h
--- qmail-1.03-factory/base64.h	1969-12-31 19:00:00.000000000 -0500
+++ qmail-1.03-7.10/base64.h	2010-02-14 22:53:32.000000000 -0500
@@ -0,0 +1,7 @@
+#ifndef BASE64_H
+#define BASE64_H
+
+extern int b64decode();
+extern int b64encode();
+
+#endif
diff -ruN qmail-1.03-factory/byte.h qmail-1.03-7.10/byte.h
--- qmail-1.03-factory/byte.h	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/byte.h	2010-02-14 22:53:32.000000000 -0500
@@ -3,6 +3,8 @@
 
 extern unsigned int byte_chr();
 extern unsigned int byte_rchr();
+extern unsigned int byte_cspn();
+extern unsigned int byte_rcspn();
 extern void byte_copy();
 extern void byte_copyr();
 extern int byte_diff();
diff -ruN qmail-1.03-factory/byte_cspn.c qmail-1.03-7.10/byte_cspn.c
--- qmail-1.03-factory/byte_cspn.c	1969-12-31 19:00:00.000000000 -0500
+++ qmail-1.03-7.10/byte_cspn.c	2010-02-14 22:53:32.000000000 -0500
@@ -0,0 +1,11 @@
+#include "byte.h"
+
+unsigned int byte_cspn(s,n,c)
+register char *s;
+register unsigned int n;
+register char *c;
+{
+  while(*c)
+    n = byte_chr(s,n,*c++);
+  return n;
+}
diff -ruN qmail-1.03-factory/byte_rcspn.c qmail-1.03-7.10/byte_rcspn.c
--- qmail-1.03-factory/byte_rcspn.c	1969-12-31 19:00:00.000000000 -0500
+++ qmail-1.03-7.10/byte_rcspn.c	2010-02-14 22:53:32.000000000 -0500
@@ -0,0 +1,17 @@
+#include "byte.h"
+
+unsigned int byte_rcspn(s,n,c)
+register char *s;
+register unsigned int n;
+register char *c;
+{
+  unsigned int ret,pos,i;
+
+  for(ret = n,pos = 0;*c;++c) {
+    i = byte_rchr(s + pos,n - pos,*c) + pos;
+    if (i < n) ret = pos = i;
+  }
+
+  return ret;
+}
+
diff -ruN qmail-1.03-factory/chkspawn.c qmail-1.03-7.10/chkspawn.c
--- qmail-1.03-factory/chkspawn.c	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/chkspawn.c	2010-02-14 22:53:32.000000000 -0500
@@ -22,8 +22,8 @@
     _exit(1);
   }
 
-  if (auto_spawn > 255) {
-    substdio_puts(subfderr,"Oops. You have set conf-spawn higher than 255.\n");
+  if (auto_spawn > 65000) {
+    substdio_puts(subfderr,"Oops. You have set conf-spawn higher than 65000.\n");
     substdio_flush(subfderr);
     _exit(1);
   }
diff -ruN qmail-1.03-factory/conf-cc qmail-1.03-7.10/conf-cc
--- qmail-1.03-factory/conf-cc	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/conf-cc	2010-02-14 22:53:32.000000000 -0500
@@ -1,3 +1,3 @@
-cc -O2
+cc -O2 -DTLS -DEXTERNAL_TODO -I/usr/local/ssl/include -I/usr/kerberos/include
 
 This will be used to compile .c files.
diff -ruN qmail-1.03-factory/conf-spawn qmail-1.03-7.10/conf-spawn
--- qmail-1.03-factory/conf-spawn	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/conf-spawn	2010-02-14 22:53:32.000000000 -0500
@@ -1,5 +1,7 @@
 120
 
-This is a silent concurrency limit. You can't set it above 255. On some
-systems you can't set it above 125. qmail will refuse to compile if the
-limit is too high.
+This is a silent concurrency limit. You can't set it above 65000. Many
+systems have a "hidden limit" of 509, because a single process cannot
+have more than 1023 handles open at once, and each concurrent delivery
+uses two handles. If you set it any higher than your system's "hidden
+limit", qmail will refuse to compile.
diff -ruN qmail-1.03-factory/date822fmt.c qmail-1.03-7.10/date822fmt.c
--- qmail-1.03-factory/date822fmt.c	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/date822fmt.c	2010-02-14 22:53:32.000000000 -0500
@@ -1,3 +1,4 @@
+#include <time.h>
 #include "datetime.h"
 #include "fmt.h"
 #include "date822fmt.h"
@@ -12,18 +13,51 @@
 {
   unsigned int i;
   unsigned int len;
+  time_t now;
+  datetime_sec utc;
+  datetime_sec local;
+  struct tm *tm;
+  struct datetime new_dt;
+  int minutes;
+
+  utc = datetime_untai(dt);
+  now = (time_t)utc;
+  tm = localtime(&now);
+  new_dt.year = tm->tm_year;
+  new_dt.mon = tm->tm_mon;
+  new_dt.mday = tm->tm_mday;
+  new_dt.hour = tm->tm_hour;
+  new_dt.min = tm->tm_min;
+  new_dt.sec = tm->tm_sec;
+  local = datetime_untai(&new_dt);
+
   len = 0;
-  i = fmt_uint(s,dt->mday); len += i; if (s) s += i;
+  i = fmt_uint(s,new_dt.mday); len += i; if (s) s += i;
   i = fmt_str(s," "); len += i; if (s) s += i;
-  i = fmt_str(s,montab[dt->mon]); len += i; if (s) s += i;
+  i = fmt_str(s,montab[new_dt.mon]); len += i; if (s) s += i;
   i = fmt_str(s," "); len += i; if (s) s += i;
-  i = fmt_uint(s,dt->year + 1900); len += i; if (s) s += i;
+  i = fmt_uint(s,new_dt.year + 1900); len += i; if (s) s += i;
   i = fmt_str(s," "); len += i; if (s) s += i;
-  i = fmt_uint0(s,dt->hour,2); len += i; if (s) s += i;
+  i = fmt_uint0(s,new_dt.hour,2); len += i; if (s) s += i;
   i = fmt_str(s,":"); len += i; if (s) s += i;
-  i = fmt_uint0(s,dt->min,2); len += i; if (s) s += i;
+  i = fmt_uint0(s,new_dt.min,2); len += i; if (s) s += i;
   i = fmt_str(s,":"); len += i; if (s) s += i;
-  i = fmt_uint0(s,dt->sec,2); len += i; if (s) s += i;
-  i = fmt_str(s," -0000\n"); len += i; if (s) s += i;
+  i = fmt_uint0(s,new_dt.sec,2); len += i; if (s) s += i;
+
+  if (local < utc) {
+    minutes = (utc - local + 30) / 60;
+    i = fmt_str(s," -"); len += i; if (s) s += i;
+    i = fmt_uint0(s,minutes / 60,2); len += i; if (s) s += i;
+    i = fmt_uint0(s,minutes % 60,2); len += i; if (s) s += i;
+  }
+  else {
+    minutes = (local - utc + 30) / 60;
+    i = fmt_str(s," +"); len += i; if (s) s += i;
+    i = fmt_uint0(s,minutes / 60,2); len += i; if (s) s += i;
+    i = fmt_uint0(s,minutes % 60,2); len += i; if (s) s += i;
+  }
+
+  i = fmt_str(s,"\n"); len += i; if (s) s += i;
+
   return len;
 }
diff -ruN qmail-1.03-factory/dns.c qmail-1.03-7.10/dns.c
--- qmail-1.03-factory/dns.c	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/dns.c	2010-02-14 22:53:32.000000000 -0500
@@ -11,6 +11,7 @@
 extern int h_errno;
 #include "ip.h"
 #include "ipalloc.h"
+#include "strsalloc.h"
 #include "fmt.h"
 #include "alloc.h"
 #include "str.h"
@@ -21,14 +22,17 @@
 static unsigned short getshort(c) unsigned char *c;
 { unsigned short u; u = c[0]; return (u << 8) + c[1]; }
 
-static union { HEADER hdr; unsigned char buf[PACKETSZ]; } response;
+static struct { unsigned char *buf; } response;
+static int responsebuflen = 0;
 static int responselen;
 static unsigned char *responseend;
 static unsigned char *responsepos;
+static u_long saveresoptions;
 
 static int numanswers;
 static char name[MAXDNAME];
 static struct ip_address ip;
+static stralloc txt = {0};
 unsigned short pref;
 
 static stralloc glue = {0};
@@ -45,18 +49,33 @@
  errno = 0;
  if (!stralloc_copy(&glue,domain)) return DNS_MEM;
  if (!stralloc_0(&glue)) return DNS_MEM;
- responselen = lookup(glue.s,C_IN,type,response.buf,sizeof(response));
+ if (!responsebuflen)
+  if (response.buf = (unsigned char *)alloc(PACKETSZ+1))
+   responsebuflen = PACKETSZ+1;
+  else return DNS_MEM;
+
+ responselen = lookup(glue.s,C_IN,type,response.buf,responsebuflen);
+ if ((responselen >= responsebuflen) ||
+     (responselen > 0 && (((HEADER *)response.buf)->tc)))
+  {
+   if (responsebuflen < 65536)
+    if (alloc_re(&response.buf, responsebuflen, 65536))
+     responsebuflen = 65536;
+    else return DNS_MEM;
+    saveresoptions = _res.options;
+    _res.options |= RES_USEVC;
+    responselen = lookup(glue.s,C_IN,type,response.buf,responsebuflen);
+    _res.options = saveresoptions;
+  }
  if (responselen <= 0)
   {
    if (errno == ECONNREFUSED) return DNS_SOFT;
    if (h_errno == TRY_AGAIN) return DNS_SOFT;
    return DNS_HARD;
   }
- if (responselen >= sizeof(response))
-   responselen = sizeof(response);
  responseend = response.buf + responselen;
  responsepos = response.buf + sizeof(HEADER);
- n = ntohs(response.hdr.qdcount);
+ n = ntohs(((HEADER *)response.buf)->qdcount);
  while (n-- > 0)
   {
    i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME);
@@ -66,7 +85,7 @@
    if (i < QFIXEDSZ) return DNS_SOFT;
    responsepos += QFIXEDSZ;
   }
- numanswers = ntohs(response.hdr.ancount);
+ numanswers = ntohs(((HEADER *)response.buf)->ancount);
  return 0;
 }
 
@@ -179,6 +198,49 @@
  return 0;
 }
 
+static int findtxt(wanttype)
+int wanttype;
+{
+ unsigned short rrtype;
+ unsigned short rrdlen;
+ int i;
+
+ if (numanswers <= 0) return 2;
+ --numanswers;
+ if (responsepos == responseend) return DNS_SOFT;
+
+ i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME);
+ if (i < 0) return DNS_SOFT;
+ responsepos += i;
+
+ i = responseend - responsepos;
+ if (i < 4 + 3 * 2) return DNS_SOFT;
+   
+ rrtype = getshort(responsepos);
+ rrdlen = getshort(responsepos + 8);
+ responsepos += 10;
+
+ if (rrtype == wanttype)
+  {
+   unsigned short txtpos;
+   unsigned char txtlen;
+
+   txt.len = 0;
+   for (txtpos = 0;txtpos < rrdlen;txtpos += txtlen)
+    {
+     txtlen = responsepos[txtpos++];
+     if (txtlen > rrdlen-txtpos) txtlen = rrdlen-txtpos;
+     if (!stralloc_catb(&txt,&responsepos[txtpos],txtlen)) return DNS_MEM;
+    }
+
+   responsepos += rrdlen;
+   return 1;
+ }
+
+ responsepos += rrdlen;
+ return 0;
+}
+
 void dns_init(flagsearch)
 int flagsearch;
 {
@@ -196,7 +258,7 @@
    if (!sa->len) return loop;
    if (sa->s[sa->len - 1] == ']') return loop;
    if (sa->s[sa->len - 1] == '.') { --sa->len; continue; }
-   switch(resolve(sa,T_ANY))
+   switch(resolve(sa,T_CNAME))
     {
      case DNS_MEM: return DNS_MEM;
      case DNS_SOFT: return DNS_SOFT;
@@ -237,15 +299,18 @@
  return len;
 }
 
-int dns_ptr(sa,ip)
-stralloc *sa;
+static int dns_ptrplus(ssa,ip)
+strsalloc *ssa;
 struct ip_address *ip;
 {
+ stralloc sa = {0};
  int r;
 
- if (!stralloc_ready(sa,iaafmt((char *) 0,ip))) return DNS_MEM;
- sa->len = iaafmt(sa->s,ip);
- switch(resolve(sa,T_PTR))
+ if (!stralloc_ready(&sa,iaafmt((char *) 0,ip))) return DNS_MEM;
+ sa.len = iaafmt(sa.s,ip);
+ r = resolve(&sa,T_PTR);
+ alloc_free(sa.s);
+ switch(r)
   {
    case DNS_MEM: return DNS_MEM;
    case DNS_SOFT: return DNS_SOFT;
@@ -256,13 +321,35 @@
    if (r == DNS_SOFT) return DNS_SOFT;
    if (r == 1)
     {
-     if (!stralloc_copys(sa,name)) return DNS_MEM;
-     return 0;
+     stralloc sa2 = {0};
+     if (!stralloc_copys(&sa2,name)) return DNS_MEM;
+     if (!strsalloc_append(ssa,&sa2)) return DNS_MEM;
     }
   }
+ if (ssa->len) return 0;
  return DNS_HARD;
 }
 
+int dns_ptr(ssa,ip)
+strsalloc *ssa;
+struct ip_address *ip;
+{
+ int r;
+ int j;
+
+ if (!strsalloc_readyplus(ssa,0)) return DNS_MEM;
+ ssa->len = 0;
+ r = dns_ptrplus(ssa,ip);
+ if (r < 0)
+  {
+   for (j = 0;j < ssa->len;++j)
+    alloc_free(ssa->sa[j].s);
+   ssa->len = 0;
+  }
+ return r;
+}
+
+
 static int dns_ipplus(ia,sa,pref)
 ipalloc *ia;
 stralloc *sa;
@@ -270,6 +357,14 @@
 {
  int r;
  struct ip_mx ix;
+#ifdef TLS
+ stralloc fqdn = {0};
+
+ if (!stralloc_copy(&fqdn,sa)) return DNS_MEM;
+ if (!stralloc_0(&fqdn)) return DNS_MEM;
+ ix.fqdn = fqdn.s;
+ alloc_free(fqdn);
+#endif
 
  if (!stralloc_copy(&glue,sa)) return DNS_MEM;
  if (!stralloc_0(&glue)) return DNS_MEM;
@@ -330,6 +425,9 @@
    ix.pref = 0;
    if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)])
     {
+#ifdef TLS
+     ix.fqdn = NULL;
+#endif
      if (!ipalloc_append(ia,&ix)) return DNS_MEM;
      return 0;
     }
@@ -398,3 +496,49 @@
  alloc_free(mx);
  return flagsoft;
 }
+
+
+static int dns_txtplus(ssa,sa)
+strsalloc *ssa;
+stralloc *sa;
+{
+ int r;
+
+ switch(resolve(sa,T_TXT)) 
+  {
+   case DNS_MEM: return DNS_MEM;
+   case DNS_SOFT: return DNS_SOFT;
+   case DNS_HARD: return DNS_HARD;
+  }
+ while ((r = findtxt(T_TXT)) != 2)
+  {
+   if (r == DNS_SOFT) return DNS_SOFT;
+   if (r == 1)
+    {
+     stralloc sa = {0};
+     if (!stralloc_copy(&sa,&txt)) return DNS_MEM;
+     if (!strsalloc_append(ssa,&sa)) return DNS_MEM;
+    }
+  }
+ if (ssa->len) return 0;
+ return DNS_HARD;
+}
+
+int dns_txt(ssa,sa)
+strsalloc *ssa;
+stralloc *sa;
+{
+ int r;
+ int j;
+
+ if (!strsalloc_readyplus(ssa,0)) return DNS_MEM;
+ ssa->len = 0;
+ r = dns_txtplus(ssa,sa);
+ if (r < 0)
+  {
+   for (j = 0;j < ssa->len;++j)
+    alloc_free(ssa->sa[j].s);
+   ssa->len = 0;
+  }
+ return r;
+}
diff -ruN qmail-1.03-factory/dns.h qmail-1.03-7.10/dns.h
--- qmail-1.03-factory/dns.h	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/dns.h	2010-02-14 22:53:32.000000000 -0500
@@ -10,5 +10,6 @@
 int dns_mxip();
 int dns_ip();
 int dns_ptr();
+int dns_txt();
 
 #endif
diff -ruN qmail-1.03-factory/dnsfq.c qmail-1.03-7.10/dnsfq.c
--- qmail-1.03-factory/dnsfq.c	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/dnsfq.c	2010-02-14 22:53:32.000000000 -0500
@@ -5,15 +5,19 @@
 #include "dnsdoe.h"
 #include "ip.h"
 #include "ipalloc.h"
+#include "strsalloc.h"
 #include "exit.h"
 
 stralloc sa = {0};
+strsalloc ssa = {0};
 ipalloc ia = {0};
 
 void main(argc,argv)
 int argc;
 char **argv;
 {
+ int j;
+
  if (!argv[1]) _exit(100);
 
  if (!stralloc_copys(&sa,argv[1]))
@@ -25,8 +29,11 @@
   {
    substdio_putsflush(subfderr,"no IP addresses\n"); _exit(100);
   }
- dnsdoe(dns_ptr(&sa,&ia.ix[0].ip));
- substdio_putflush(subfdout,sa.s,sa.len);
- substdio_putsflush(subfdout,"\n");
+ dnsdoe(dns_ptr(&ssa,&ia.ix[0].ip));
+ for(j = 0;j < ssa.len;++j)
+  {
+   substdio_putflush(subfdout,ssa.sa[j].s,ssa.sa[j].len);
+   substdio_putsflush(subfdout,"\n");
+  }
  _exit(0);
 }
diff -ruN qmail-1.03-factory/dnsptr.c qmail-1.03-7.10/dnsptr.c
--- qmail-1.03-factory/dnsptr.c	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/dnsptr.c	2010-02-14 22:53:32.000000000 -0500
@@ -6,22 +6,28 @@
 #include "dns.h"
 #include "dnsdoe.h"
 #include "ip.h"
+#include "strsalloc.h"
 #include "exit.h"
 
-stralloc sa = {0};
+strsalloc ssa = {0};
 struct ip_address ip;
 
 void main(argc,argv)
 int argc;
 char **argv;
 {
+ int j;
+
  if (!argv[1]) _exit(100);
 
  ip_scan(argv[1],&ip);
 
  dns_init(0);
- dnsdoe(dns_ptr(&sa,&ip));
- substdio_putflush(subfdout,sa.s,sa.len);
- substdio_putsflush(subfdout,"\n");
+ dnsdoe(dns_ptr(&ssa,&ip));
+ for(j = 0;j < ssa.len;++j)
+  {
+   substdio_putflush(subfdout,ssa.sa[j].s,ssa.sa[j].len);
+   substdio_putsflush(subfdout,"\n");
+  }
  _exit(0);
 }
diff -ruN qmail-1.03-factory/dnstxt.c qmail-1.03-7.10/dnstxt.c
--- qmail-1.03-factory/dnstxt.c	1969-12-31 19:00:00.000000000 -0500
+++ qmail-1.03-7.10/dnstxt.c	2010-02-14 22:53:32.000000000 -0500
@@ -0,0 +1,32 @@
+#include "substdio.h"
+#include "subfd.h"
+#include "stralloc.h"
+#include "str.h"
+#include "scan.h"
+#include "dns.h"
+#include "dnsdoe.h"
+#include "strsalloc.h"
+#include "exit.h"
+
+strsalloc ssa = {0};
+stralloc sa = {0};
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ int j;
+
+ if (!argv[1]) _exit(100);
+
+ if (!stralloc_copys(&sa, argv[1]))
+  { substdio_putsflush(subfderr,"out of memory\n"); _exit(111); }
+ dns_init(0);
+ dnsdoe(dns_txt(&ssa,&sa));
+ for (j = 0;j < ssa.len;++j)
+  {
+   substdio_put(subfdout,ssa.sa[j].s,ssa.sa[j].len);
+   substdio_putsflush(subfdout,"\n");
+  }
+ _exit(0);
+}
diff -ruN qmail-1.03-factory/error.c qmail-1.03-7.10/error.c
--- qmail-1.03-factory/error.c	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/error.c	2010-02-14 22:53:32.000000000 -0500
@@ -93,3 +93,10 @@
 #else
 -13;
 #endif
+
+int error_dquot =
+#ifdef EDQUOT
+EDQUOT;
+#else
+-14;
+#endif
diff -ruN qmail-1.03-factory/error.h qmail-1.03-7.10/error.h
--- qmail-1.03-factory/error.h	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/error.h	2010-02-14 22:53:32.000000000 -0500
@@ -1,7 +1,8 @@
 #ifndef ERROR_H
 #define ERROR_H
 
-extern int errno;
+/* extern int errno; */
+#include <errno.h>
 
 extern int error_intr;
 extern int error_nomem;
@@ -16,6 +17,7 @@
 extern int error_pipe;
 extern int error_perm;
 extern int error_acces;
+extern int error_dquot;
 
 extern char *error_str();
 extern int error_temp();
diff -ruN qmail-1.03-factory/hier.c qmail-1.03-7.10/hier.c
--- qmail-1.03-factory/hier.c	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/hier.c	2010-02-14 22:53:32.000000000 -0500
@@ -76,6 +76,7 @@
   c(auto_qmail,"boot","binm3+df",auto_uido,auto_gidq,0755);
 
   c(auto_qmail,"doc","FAQ",auto_uido,auto_gidq,0644);
+  c(auto_qmail,"doc","README.qregex",auto_uido,auto_gidq,0644);
   c(auto_qmail,"doc","UPGRADE",auto_uido,auto_gidq,0644);
   c(auto_qmail,"doc","SENDMAIL",auto_uido,auto_gidq,0644);
   c(auto_qmail,"doc","INSTALL",auto_uido,auto_gidq,0644);
@@ -108,6 +109,9 @@
   c(auto_qmail,"bin","qmail-rspawn",auto_uido,auto_gidq,0711);
   c(auto_qmail,"bin","qmail-clean",auto_uido,auto_gidq,0711);
   c(auto_qmail,"bin","qmail-send",auto_uido,auto_gidq,0711);
+#ifdef EXTERNAL_TODO
+  c(auto_qmail,"bin","qmail-todo",auto_uido,auto_gidq,0711);
+#endif
   c(auto_qmail,"bin","splogger",auto_uido,auto_gidq,0711);
   c(auto_qmail,"bin","qmail-newu",auto_uido,auto_gidq,0700);
   c(auto_qmail,"bin","qmail-newmrh",auto_uido,auto_gidq,0700);
diff -ruN qmail-1.03-factory/install-big.c qmail-1.03-7.10/install-big.c
--- qmail-1.03-factory/install-big.c	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/install-big.c	2010-02-14 22:53:32.000000000 -0500
@@ -76,6 +76,7 @@
   c(auto_qmail,"boot","binm3+df",auto_uido,auto_gidq,0755);
 
   c(auto_qmail,"doc","FAQ",auto_uido,auto_gidq,0644);
+  c(auto_qmail,"doc","README.qregex",auto_uido,auto_gidq,0644);
   c(auto_qmail,"doc","UPGRADE",auto_uido,auto_gidq,0644);
   c(auto_qmail,"doc","SENDMAIL",auto_uido,auto_gidq,0644);
   c(auto_qmail,"doc","INSTALL",auto_uido,auto_gidq,0644);
@@ -108,6 +109,9 @@
   c(auto_qmail,"bin","qmail-rspawn",auto_uido,auto_gidq,0711);
   c(auto_qmail,"bin","qmail-clean",auto_uido,auto_gidq,0711);
   c(auto_qmail,"bin","qmail-send",auto_uido,auto_gidq,0711);
+#ifdef EXTERNAL_TODO
+  c(auto_qmail,"bin","qmail-todo",auto_uido,auto_gidq,0711);
+#endif
   c(auto_qmail,"bin","splogger",auto_uido,auto_gidq,0711);
   c(auto_qmail,"bin","qmail-newu",auto_uido,auto_gidq,0700);
   c(auto_qmail,"bin","qmail-newmrh",auto_uido,auto_gidq,0700);
diff -ruN qmail-1.03-factory/ipalloc.h qmail-1.03-7.10/ipalloc.h
--- qmail-1.03-factory/ipalloc.h	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/ipalloc.h	2010-02-14 22:53:32.000000000 -0500
@@ -3,7 +3,12 @@
 
 #include "ip.h"
 
+#ifdef TLS
+#include "stralloc.h"
+struct ip_mx { struct ip_address ip; int pref; char *fqdn; } ;
+#else
 struct ip_mx { struct ip_address ip; int pref; } ;
+#endif
 
 #include "gen_alloc.h"
 
diff -ruN qmail-1.03-factory/ipme.c qmail-1.03-7.10/ipme.c
--- qmail-1.03-factory/ipme.c	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/ipme.c	2010-02-14 22:53:32.000000000 -0500
@@ -46,9 +46,14 @@
   ipme.len = 0;
   ix.pref = 0;
  
+  /* 0.0.0.0 is a special address which always refers to 
+   * "this host, this network", according to RFC 1122, Sec. 3.2.1.3a.
+  */
+  byte_copy(&ix.ip,4,"\0\0\0\0");
+  if (!ipalloc_append(&ipme,&ix)) { return 0; }
   if ((s = socket(AF_INET,SOCK_STREAM,0)) == -1) return -1;
  
-  len = 256;
+  len = 8192; /* any value big enough to get all the interfaces in one read is good */
   for (;;) {
     if (!stralloc_ready(&buf,len)) { close(s); return 0; }
     buf.len = 0;
@@ -60,7 +65,7 @@
         break;
       }
     if (len > 200000) { close(s); return -1; }
-    len += 100 + (len >> 2);
+    len *= 2;
   }
   x = buf.s;
   while (x < buf.s + buf.len) {
diff -ruN qmail-1.03-factory/make-makelib.sh qmail-1.03-7.10/make-makelib.sh
--- qmail-1.03-factory/make-makelib.sh	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/make-makelib.sh	2010-02-14 22:53:32.000000000 -0500
@@ -3,6 +3,9 @@
 echo 'ar cr "$main" ${1+"$@"}'
 
 case "$1" in
+darwin-*)
+  echo 'ranlib -c "$main"'
+  ;;
 sunos-5.*) ;;
 unix_sv*) ;;
 irix64-*) ;;
diff -ruN qmail-1.03-factory/qmail-control.9 qmail-1.03-7.10/qmail-control.9
--- qmail-1.03-factory/qmail-control.9	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/qmail-control.9	2010-02-14 22:53:32.000000000 -0500
@@ -20,7 +20,11 @@
 
 Comments are allowed
 in
+.IR badhelo ,
 .IR badmailfrom ,
+.IR badmailfromnorelay ,
+.IR badrcpto ,
+.IR badrcptonorelay ,
 .IR locals ,
 .IR percenthack ,
 .IR qmqpservers ,
@@ -40,7 +44,11 @@
 .ta 5c 10c
 control	default	used by
 
+.I badhelo	\fR(none)	\fRqmail-smtpd
 .I badmailfrom	\fR(none)	\fRqmail-smtpd
+.I badmailfromnorelay	\fR(none)	\fRqmail-smtpd
+.I badrcptto	\fR(none)	\fRqmail-smtpd
+.I badrcpttonorelay	\fR(none)	\fRqmail-smtpd
 .I bouncefrom	\fRMAILER-DAEMON	\fRqmail-send
 .I bouncehost	\fIme	\fRqmail-send
 .I concurrencylocal	\fR10	\fRqmail-send
@@ -55,6 +63,7 @@
 .I idhost	\fIme	\fRqmail-inject
 .I localiphost	\fIme	\fRqmail-smtpd
 .I locals	\fIme	\fRqmail-send
+.I mfcheck	\fR0	\fRqmail-smtpd
 .I morercpthosts	\fR(none)	\fRqmail-smtpd
 .I percenthack	\fR(none)	\fRqmail-send
 .I plusdomain	\fIme	\fRqmail-inject
@@ -63,6 +72,11 @@
 .I rcpthosts	\fR(none)	\fRqmail-smtpd
 .I smtpgreeting	\fIme	\fRqmail-smtpd
 .I smtproutes	\fR(none)	\fRqmail-remote
+.I spfbehavior	\fR0	\fRqmail-smtpd
+.I spfexp	\fR(default)	\fRqmail-smtpd
+.I spfguess	\fR(none)	\fRqmail-smtpd
+.I spfrules	\fR(none)	\fRqmail-smtpd
+.I taps		\fR(none)	\fRqmail-queue
 .I timeoutconnect	\fR60	\fRqmail-remote
 .I timeoutremote	\fR1200	\fRqmail-remote
 .I timeoutsmtpd	\fR1200	\fRqmail-smtpd
@@ -72,6 +86,7 @@
 .SH "SEE ALSO"
 qmail-inject(8),
 qmail-qmqpc(8),
+qmail-queue(8),
 qmail-remote(8),
 qmail-send(8),
 qmail-showctl(8),
diff -ruN qmail-1.03-factory/qmail-local.c qmail-1.03-7.10/qmail-local.c
--- qmail-1.03-factory/qmail-local.c	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/qmail-local.c	2010-02-14 22:53:32.000000000 -0500
@@ -41,6 +41,9 @@
 void temp_qmail(fn) char *fn;
 { strerr_die5x(111,"Unable to open ",fn,": ",error_str(errno),". (#4.3.0)"); }
 
+char *overquota = 
+	"Recipient's mailbox is full, message returned to sender. (#5.2.2)";
+
 int flagdoit;
 int flag99;
 
@@ -110,7 +113,12 @@
 
  alarm(86400);
  fd = open_excl(fntmptph);
- if (fd == -1) _exit(1);
+ if (fd == -1) {
+   if (errno == error_dquot)
+     _exit(5);
+   else
+     _exit(1);
+ }
 
  substdio_fdbuf(&ss,read,0,buf,sizeof(buf));
  substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf));
@@ -131,7 +139,12 @@
    /* if it was error_exist, almost certainly successful; i hate NFS */
  tryunlinktmp(); _exit(0);
 
- fail: tryunlinktmp(); _exit(1);
+ fail: 
+   if (errno == error_dquot) {
+     tryunlinktmp(); _exit(5);
+   } else {
+     tryunlinktmp(); _exit(1);
+   }
 }
 
 /* end child process */
@@ -162,6 +175,7 @@
    case 2: strerr_die1x(111,"Unable to chdir to maildir. (#4.2.1)");
    case 3: strerr_die1x(111,"Timeout on maildir delivery. (#4.3.0)");
    case 4: strerr_die1x(111,"Unable to read message. (#4.3.0)");
+   case 5: strerr_die1x(100,overquota);
    default: strerr_die1x(111,"Temporary error on maildir delivery. (#4.3.0)");
   }
 }
@@ -221,7 +235,12 @@
  return;
 
  writeerrs:
- strerr_warn5("Unable to write ",fn,": ",error_str(errno),". (#4.3.0)",0);
+ if (errno == error_dquot) { 
+   if (flaglocked) seek_trunc(fd,pos);
+   close(fd);
+   strerr_die1x(100,overquota);
+ } else
+   strerr_warn5("Unable to write ",fn,": ",error_str(errno),". (#4.3.0)",0);
  if (flaglocked) seek_trunc(fd,pos);
  close(fd);
  _exit(111);
@@ -645,7 +664,7 @@
     {
      cmds.s[j] = 0;
      k = j;
-     while ((k > i) && (cmds.s[k - 1] == ' ') || (cmds.s[k - 1] == '\t'))
+     while ((k > i) && ((cmds.s[k - 1] == ' ') || (cmds.s[k - 1] == '\t')))
        cmds.s[--k] = 0;
      switch(cmds.s[i])
       {
diff -ruN qmail-1.03-factory/qmail-qmtpd.c qmail-1.03-7.10/qmail-qmtpd.c
--- qmail-1.03-factory/qmail-qmtpd.c	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/qmail-qmtpd.c	2010-02-14 22:53:32.000000000 -0500
@@ -45,6 +45,8 @@
   for (;;) {
     substdio_get(&ssin,&ch,1);
     if (ch == ':') return len;
+    /* trap non-numeric input in netstring: */
+    if ((ch < '0') || (ch > '9')) badproto();
     if (len > 200000000) resources();
     len = 10 * len + (ch - '0');
   }
@@ -193,6 +195,8 @@
         substdio_get(&ssin,&ch,1);
         --biglen;
         if (ch == ':') break;
+        /* trap non-numeric input in netstring: */
+        if ((ch < '0') || (ch > '9')) badproto();
         if (len > 200000000) resources();
         len = 10 * len + (ch - '0');
       }
diff -ruN qmail-1.03-factory/qmail-queue.8 qmail-1.03-7.10/qmail-queue.8
--- qmail-1.03-factory/qmail-queue.8	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/qmail-queue.8	2010-02-14 22:53:32.000000000 -0500
@@ -40,6 +40,12 @@
 However, the recipients probably expect to see a proper header,
 as described in
 .BR qmail-header(5) .
+.SH "CONTROL FILES"
+.TP 5
+.I taps
+Should contain regex syntax of email addresses to tap and
+the associated email address to send the copy to. The two
+fields should be separated by a colon.
 .SH "FILESYSTEM RESTRICTIONS"
 .B qmail-queue
 imposes two constraints on the queue structure:
diff -ruN qmail-1.03-factory/qmail-queue.c qmail-1.03-7.10/qmail-queue.c
--- qmail-1.03-factory/qmail-queue.c	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/qmail-queue.c	2010-02-14 22:53:32.000000000 -0500
@@ -16,6 +16,8 @@
 #include "auto_uids.h"
 #include "date822fmt.h"
 #include "fmtqfn.h"
+#include "stralloc.h"
+#include "constmap.h"
 
 #define DEATH 86400 /* 24 hours; _must_ be below q-s's OSSIFIED (36 hours) */
 #define ADDR 1003
@@ -25,6 +27,13 @@
 char outbuf[256];
 struct substdio ssout;
 
+int tapok = 0;
+stralloc tap = {0};
+struct constmap maptap;
+stralloc chkaddr = {0};
+int tapped;
+stralloc tapaddr = {0};
+
 datetime_sec starttime;
 struct datetime dt;
 unsigned long mypid;
@@ -159,6 +168,11 @@
  sig_blocknone();
  umask(033);
  if (chdir(auto_qmail) == -1) die(61);
+
+ tapok = control_readfile(&tap,"control/taps",0);
+ if (tapok == -1) die(65);
+ if (!constmap_init(&maptap,tap.s,tap.len,0)) die(65);
+
  if (chdir("queue") == -1) die(62);
 
  mypid = getpid();
@@ -219,14 +233,28 @@
  if (substdio_get(&ssin,&ch,1) < 1) die_read();
  if (ch != 'F') die(91);
  if (substdio_bput(&ssout,&ch,1) == -1) die_write();
+ stralloc_0(&chkaddr);
  for (len = 0;len < ADDR;++len)
   {
+   if ( len == 1 ) stralloc_copyb(&chkaddr, &ch,1);
+   else if ( len > 1 ) stralloc_catb(&chkaddr, &ch,1);
    if (substdio_get(&ssin,&ch,1) < 1) die_read();
    if (substdio_put(&ssout,&ch,1) == -1) die_write();
    if (!ch) break;
   }
  if (len >= ADDR) die(11);
 
+ /* check the from address */
+ stralloc_0(&chkaddr);
+ if (tapped == 0 && tapcheck()==1 ) {
+   tapped = 1;
+   if ( tapaddr.len > 0 ) {
+     if (substdio_bput(&ssout,"T",1) == -1) die_write();
+     if (substdio_bput(&ssout,tapaddr.s,tapaddr.len) == -1) die_write();
+     if (substdio_bput(&ssout,"",1) == -1) die_write();
+   }
+ }
+
  if (substdio_bput(&ssout,QUEUE_EXTRA,QUEUE_EXTRALEN) == -1) die_write();
 
  for (;;)
@@ -237,10 +265,24 @@
    if (substdio_bput(&ssout,&ch,1) == -1) die_write();
    for (len = 0;len < ADDR;++len)
     {
+     if ( len == 1 ) stralloc_copyb(&chkaddr, &ch,1);
+     else if ( len > 1 ) stralloc_catb(&chkaddr, &ch,1);
      if (substdio_get(&ssin,&ch,1) < 1) die_read();
      if (substdio_bput(&ssout,&ch,1) == -1) die_write();
      if (!ch) break;
     }
+
+    /* check the to address */
+    stralloc_0(&chkaddr);
+    if (tapped == 0 && tapcheck()==1 ) {
+      tapped = 1;
+      if ( tapaddr.len > 0 ) {
+        if (substdio_bput(&ssout,"T",1) == -1) die_write();
+        if (substdio_bput(&ssout,tapaddr.s,tapaddr.len) == -1) die_write();
+        if (substdio_bput(&ssout,"",1) == -1) die_write();
+       }
+     }
+
    if (len >= ADDR) die(11);
   }
 
@@ -252,3 +294,42 @@
  triggerpull();
  die(0);
 }
+
+int tapcheck()
+{
+  int i = 0;
+  int j = 0;
+  int x = 0;
+  int negate = 0;
+  stralloc curregex = {0};
+  char tmpbuf[200];
+
+  while (j < tap.len) {
+    i = j;
+    while ((tap.s[i] != ':') && (i < tap.len)) i++;
+    if (tap.s[j] == '!') {
+      negate = 1;
+      j++;
+    }
+    stralloc_copys(&tapaddr, &tap.s[i+1]);
+
+    stralloc_copyb(&curregex,tap.s + j,(i - j));
+    stralloc_0(&curregex);
+    x = matchregex(chkaddr.s, curregex.s, tmpbuf);
+
+    while ((tap.s[i] != '\0') && (i < tap.len)) i++;
+  
+    if ((negate) && (x == 0)) {
+      return 1;
+    }
+    if (!(negate) && (x > 0)) {
+      return 1;
+    }
+    j = i + 1;
+    negate = 0;
+
+
+  }
+  return 0;
+}
+
diff -ruN qmail-1.03-factory/qmail-remote.8 qmail-1.03-7.10/qmail-remote.8
--- qmail-1.03-factory/qmail-remote.8	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/qmail-remote.8	2010-02-14 22:53:32.000000000 -0500
@@ -127,8 +127,15 @@
 .I smtproutes
 Artificial SMTP routes.
 Each route has the form
-.IR domain\fB:\fIrelay ,
-without any extra spaces.
+
+.EX
+   \fIdomain\fB:\fIrelay user pass
+.EE
+
+without any extra spaces between
+.I domain
+and
+.I relay.
 If
 .I domain
 matches
@@ -174,6 +181,35 @@
 any other address is artificially routed to
 .BR heaven.af.mil .
 
+.I user
+and
+.I pass
+may be empty; if present they will be used as the userid and password 
+for an AUTH command when connecting to the remote SMTP server. Note that
+in order to be used, both
+.I user
+and
+.I pass
+must be present, and must be separated from the prior items on the 
+line by exactly one space. This means that
+.I user
+cannot contain any spaces, although
+.I pass
+may contain spaces, and in fact if any "extra" spaces are present at the 
+end of the line, they will be considered part of the password. (You have 
+been warned.)
+
+Note that
+.B qmail-remote
+will not send AUTH commands over a non-secured connection (i.e. if a 
+STARTTLS command has not been successfully sent.) This is done on 
+purpose, to prevent your server from sending a password across the 
+internet in clear text. If
+.I user
+starts with a "-" character, the "-" will be removed and this security 
+check will be bypassed. Think of this as qmail-remote's version of the
+ALLOW_INSECURE_AUTH variable used by qmail-smtpd.
+
 The
 .B qmail
 system does not protect you if you create an artificial
diff -ruN qmail-1.03-factory/qmail-remote.c qmail-1.03-7.10/qmail-remote.c
--- qmail-1.03-factory/qmail-remote.c	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/qmail-remote.c	2010-02-14 22:54:43.000000000 -0500
@@ -26,14 +26,29 @@
 #include "tcpto.h"
 #include "readwrite.h"
 #include "timeoutconn.h"
+#include "qregex.h"
+#ifndef TLS
 #include "timeoutread.h"
 #include "timeoutwrite.h"
+#endif
+
+#ifdef TLS
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <openssl/ssl.h>
+SSL *ssl = NULL;
+
+stralloc tlsclientciphers = {0};
+int can_tls = 1;
+#endif
 
 #define HUGESMTPTEXT 5000
 
 #define PORT_SMTP 25 /* silly rabbit, /etc/services is for users */
 unsigned long port = PORT_SMTP;
 
+int allow_insecure_auth = 0;
+
 GEN_ALLOC_typedef(saa,stralloc,sa,len,a)
 GEN_ALLOC_readyplus(saa,stralloc,sa,len,a,i,n,x,10,saa_readyplus)
 static stralloc sauninit = {0};
@@ -43,6 +58,9 @@
 struct constmap maproutes;
 stralloc host = {0};
 stralloc sender = {0};
+stralloc auth_smtp_user = {0};
+stralloc auth_smtp_pass = {0};
+stralloc auth_smtp_plain = {0};
 
 saa reciplist = {0};
 
@@ -107,17 +125,94 @@
 int smtpfd;
 int timeout = 1200;
 
+#ifdef TLS
+int flagtimedout = 0;
+void sigalrm()
+{
+ flagtimedout = 1;
+}
+
+int ssl_timeoutread(timeout,fd,buf,n) int timeout; int fd; char *buf; int n;
+{
+ int r; int saveerrno;
+ if (flagtimedout) { errno = error_timeout; return -1; }
+ alarm(timeout);
+ if (ssl) {
+   while(((r = SSL_read(ssl,buf,n)) <= 0)
+         && (SSL_get_error(ssl, r) == SSL_ERROR_WANT_READ));
+   if (SSL_get_error(ssl, r) != SSL_ERROR_NONE)
+    {char buf[1024];
+ 
+     out("ZTLS connection to "); outhost(); out(" died: ");
+     SSL_load_error_strings();
+     out(ERR_error_string(ERR_get_error(), buf)); out("\n");
+     SSL_shutdown(ssl);
+     zerodie();
+    }
+ }else r = read(fd,buf,n);
+ saveerrno = errno;
+ alarm(0);
+ if (flagtimedout) { errno = error_timeout; return -1; }
+ errno = saveerrno;
+ return r;
+}
+
+int ssl_timeoutwrite(timeout,fd,buf,n) int timeout; int fd; char *buf; int n;
+{
+ int r; int saveerrno;
+ if (flagtimedout) { errno = error_timeout; return -1; }
+ alarm(timeout);
+ if (ssl) {
+   while(((r = SSL_write(ssl,buf,n)) <= 0)
+         && (SSL_get_error(ssl, r) == SSL_ERROR_WANT_WRITE));
+   if (SSL_get_error(ssl, r) != SSL_ERROR_NONE)
+    {char buf[1024];
+
+     out("ZTLS connection to "); outhost(); out(" died: ");
+     SSL_load_error_strings();
+     out(ERR_error_string(ERR_get_error(), buf)); out("\n");
+     SSL_shutdown(ssl);
+     zerodie();
+    }
+ }else r = write(fd,buf,n);
+ saveerrno = errno;
+ alarm(0);
+ if (flagtimedout) { errno = error_timeout; return -1; }
+ errno = saveerrno;
+ return r;
+}
+
+static int client_cert_cb(SSL *s,X509 **x509, EVP_PKEY **pkey)
+{
+ out("ZTLS found no client cert in control/clientcert.pem\n");
+ zerodie(NULL,NULL);
+}
+
+static int verify_cb(int ok, X509_STORE_CTX * ctx)
+{
+  return (1);
+}
+#endif 
+
 int saferead(fd,buf,len) int fd; char *buf; int len;
 {
   int r;
+#ifdef TLS
+  r = ssl_timeoutread(timeout,smtpfd,buf,len);
+#else
   r = timeoutread(timeout,smtpfd,buf,len);
+#endif
   if (r <= 0) dropped();
   return r;
 }
 int safewrite(fd,buf,len) int fd; char *buf; int len;
 {
   int r;
+#ifdef TLS
+  r = ssl_timeoutwrite(timeout,smtpfd,buf,len);
+#else
   r = timeoutwrite(timeout,smtpfd,buf,len);
+#endif
   if (r <= 0) dropped();
   return r;
 }
@@ -186,6 +281,34 @@
   out(append);
   out(".\n");
   outsmtptext();
+
+/* TAG */
+#if defined(TLS) && defined(DEBUG)
+#define ONELINE_NAME(X) X509_NAME_oneline(X,NULL,0)
+
+ if(ssl){
+ X509 *peer;
+
+  out("STARTTLS proto="); out(SSL_get_version(ssl));
+  out("; cipher="); out(SSL_CIPHER_get_name(SSL_get_current_cipher(ssl)));
+
+  /* we want certificate details */
+  peer=SSL_get_peer_certificate(ssl);
+  if (peer != NULL) {
+   char *str;
+
+   str=ONELINE_NAME(X509_get_subject_name(peer));
+   out("; subject="); out(str);
+   OPENSSL_free(str);
+   str=ONELINE_NAME(X509_get_issuer_name(peer));
+   out("; issuer="); out(str);
+   OPENSSL_free(str);
+   X509_free(peer);
+  }
+  out(";\n");
+ }
+#endif
+
   zerodie();
 }
 
@@ -215,25 +338,236 @@
 }
 
 stralloc recip = {0};
+stralloc xuser = {0} ;
+
+int xtext(sa,s,len)
+stralloc *sa;
+char *s;
+int len;
+{
+  int i;
+
+  if(!stralloc_copys(sa,"")) temp_nomem();
+  
+  for (i = 0; i < len; i++) {
+    if (s[i] == '=') {
+      if (!stralloc_cats(sa,"+3D")) temp_nomem();
+    } else if (s[i] == '+') {  
+        if (!stralloc_cats(sa,"+2B")) temp_nomem(); 
+    } else if ((int) s[i] < 33 || (int) s[i] > 126) {
+        if (!stralloc_cats(sa,"+3F")) temp_nomem(); /* ok. not correct */
+    } else if (!stralloc_catb(sa,s+i,1)) {
+        temp_nomem();
+    }
+  }
+
+  return sa->len;
+}
+
 
+#ifdef TLS
+void smtp(fqdn)
+char *fqdn;
+#else
 void smtp()
+#endif
 {
   unsigned long code;
   int flagbother;
+  int try_plain=0;
+  int try_login=0;
+  int authd=0;
   int i;
+  stralloc slop = {0};
+#ifdef TLS
+  int needtlsauth = 0;
+  SSL_CTX *ctx;
+  int saveerrno, r;
+
+  stralloc servercert = {0};
+  struct stat st;
+  if(fqdn){
+   if(!stralloc_copys(&servercert, "control/tlshosts/")) temp_nomem();
+   if(!stralloc_catb(&servercert, fqdn, str_len(fqdn))) temp_nomem();
+   if(!stralloc_catb(&servercert, ".pem", 4)) temp_nomem();
+   if(!stralloc_0(&servercert)) temp_nomem();
+   if (stat(servercert.s,&st) == 0)  needtlsauth = 1;
+  }
+#endif
+
+  code = smtpcode();
+  if (code >= 400 && code < 600) return; /* try next MX, see RFC 2821 */
+  if (code != 220) quit("ZConnected to "," but greeting failed");
  
-  if (smtpcode() != 220) quit("ZConnected to "," but greeting failed");
- 
-  substdio_puts(&smtpto,"HELO ");
+  substdio_puts(&smtpto,"EHLO ");
   substdio_put(&smtpto,helohost.s,helohost.len);
   substdio_puts(&smtpto,"\r\n");
   substdio_flush(&smtpto);
-  if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected");
- 
-  substdio_puts(&smtpto,"MAIL FROM:<");
-  substdio_put(&smtpto,sender.s,sender.len);
-  substdio_puts(&smtpto,">\r\n");
-  substdio_flush(&smtpto);
+  if (smtpcode() != 250){
+   substdio_puts(&smtpto,"HELO ");
+   substdio_put(&smtpto,helohost.s,helohost.len);
+   substdio_puts(&smtpto,"\r\n");
+   substdio_flush(&smtpto);
+   if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected");
+  }
+
+#ifdef TLS
+  if(can_tls) {
+  i = 0; 
+  while((i += str_chr(smtptext.s+i,'\n') + 1) && (i+12 < smtptext.len) &&
+        str_diffn(smtptext.s+i+4,"STARTTLS\n",9));
+  if (i+12 < smtptext.len)
+   {
+    substdio_puts(&smtpto,"STARTTLS\r\n");
+    substdio_flush(&smtpto);
+    if (smtpcode() == 220)
+     {
+      SSL_library_init();
+      if(!(ctx=SSL_CTX_new(SSLv23_client_method())))
+       {char buf[1024];
+
+        out("ZTLS not available: error initializing ctx: ");
+        SSL_load_error_strings();
+        out(ERR_error_string(ERR_get_error(), buf));
+        out("\n");
+        SSL_shutdown(ssl);
+        zerodie();
+      }
+      if((stat("control/clientcert.pem", &st) == 0) &&
+         ((SSL_CTX_use_RSAPrivateKey_file(ctx, "control/clientcert.pem", SSL_FILETYPE_PEM) <= 0) ||
+         (SSL_CTX_use_certificate_chain_file(ctx, "control/clientcert.pem") <= 0) ||
+         (SSL_CTX_check_private_key(ctx) <= 0)))
+        /* if there is a cert and it is bad, I fail
+           if there is no cert, I leave it to the other side to complain */
+        SSL_CTX_set_client_cert_cb(ctx, client_cert_cb);
+ 
+      /*SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1);*/
+      SSL_CTX_set_cipher_list(ctx,tlsclientciphers.s);
+ 
+      if (needtlsauth){
+        if (!SSL_CTX_load_verify_locations(ctx, servercert.s, NULL))
+          {out("ZTLS unable to load "); out(servercert.s); out("\n");
+           zerodie();}
+        SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_cb);
+      }
+     
+      if(!(ssl=SSL_new(ctx)))
+        {char buf[1024];
+
+         out("ZTLS not available: error initializing ssl: "); 
+         SSL_load_error_strings();
+         out(ERR_error_string(ERR_get_error(), buf));
+         out("\n");
+         SSL_shutdown(ssl);
+         zerodie();
+        }
+      SSL_set_fd(ssl,smtpfd);
+
+      alarm(timeout);
+      r = SSL_connect(ssl); saveerrno = errno;
+      alarm(0); 
+      if (flagtimedout) 
+       {out("ZTLS not available: connect timed out\n");
+        zerodie();}
+      errno = saveerrno;
+      if (r<=0)
+        {char buf[1024];
+
+         out("ZTLS not available: connect failed: ");
+         SSL_load_error_strings();
+         out(ERR_error_string(ERR_get_error(), buf));
+         out("\n");
+         SSL_shutdown(ssl);
+         zerodie();
+        }
+      if (needtlsauth)
+       /* should also check alternate names */
+       {char commonName[256];
+
+        if ((r=SSL_get_verify_result(ssl)) != X509_V_OK)
+         {out("ZTLS unable to verify server with ");
+          out(servercert.s); out(": ");
+          out(X509_verify_cert_error_string(r)); out("\n");
+          zerodie();
+         }
+        X509_NAME_get_text_by_NID(X509_get_subject_name(
+                                   SSL_get_peer_certificate(ssl)),
+                                   NID_commonName, commonName, 256);
+        if (strcasecmp(fqdn,commonName)){
+         out("ZTLS connection to "); out(fqdn);
+         out(" wanted, certificate for "); out(commonName);
+         out(" received\n");
+         zerodie();}
+        }
+
+      substdio_puts(&smtpto,"EHLO ");
+      substdio_put(&smtpto,helohost.s,helohost.len);
+      substdio_puts(&smtpto,"\r\n");
+      substdio_flush(&smtpto);
+
+      if (smtpcode() != 250)
+       {
+        quit("ZTLS connected to "," but my name was rejected");
+       }
+     } 
+   }
+  if ((!ssl) && needtlsauth)
+   {out("ZNo TLS achieved while "); out(servercert.s); out(" exists.\n");
+    quit();}
+  } /* ends if(can_tls) block */
+#endif
+
+  if (allow_insecure_auth || ssl) {
+    stralloc_0(&smtptext);
+    if(auth_smtp_user.len && auth_smtp_pass.len) {
+      try_plain=matchregex(smtptext.s,"AUTH[^\n]+PLAIN");
+      try_login=matchregex(smtptext.s,"AUTH[^\n]+LOGIN");
+    }
+  }
+
+  if (try_plain>0) {
+    if (!stralloc_0(&auth_smtp_plain)) temp_nomem();
+    if (!stralloc_cat(&auth_smtp_plain,&auth_smtp_user)) temp_nomem();
+    if (!stralloc_0(&auth_smtp_plain)) temp_nomem();
+    if (!stralloc_cat(&auth_smtp_plain,&auth_smtp_pass)) temp_nomem();
+    if (b64encode(&auth_smtp_plain,&slop)) temp_nomem();
+    substdio_puts(&smtpto,"AUTH PLAIN ");
+    substdio_put(&smtpto,slop.s,slop.len);
+    substdio_puts(&smtpto,"\r\n");
+    substdio_flush(&smtpto);
+    if (smtpcode() != 235) quit("ZConnected to "," but AUTH PLAIN was rejected");
+    authd=1;
+  }
+  if ((!authd) && (try_login>0)) {
+    substdio_puts(&smtpto,"AUTH LOGIN\r\n");
+    substdio_flush(&smtpto);
+    if (smtpcode() != 334) quit("ZConnected to "," but AUTH LOGIN command was rejected");
+    if (b64encode(&auth_smtp_user,&slop) < 0) temp_nomem();
+    substdio_put(&smtpto,slop.s,slop.len);
+    substdio_puts(&smtpto,"\r\n");
+    substdio_flush(&smtpto);
+    if (smtpcode() != 334) quit("ZConnected to "," but AUTH LOGIN username was rejected");
+    if (b64encode(&auth_smtp_pass,&slop) < 0) temp_nomem();
+    substdio_put(&smtpto,slop.s,slop.len);
+    substdio_puts(&smtpto,"\r\n");
+    substdio_flush(&smtpto);
+    if (smtpcode() != 235) quit("ZConnected to "," but AUTH LOGIN password was rejected");
+    authd=1;
+  }
+  if (authd) {
+    if (!xtext(&xuser,auth_smtp_user.s,auth_smtp_user.len)) temp_nomem();
+    substdio_puts(&smtpto,"MAIL FROM:<");
+    substdio_put(&smtpto,sender.s,sender.len);
+    substdio_puts(&smtpto,"> AUTH=");
+    substdio_put(&smtpto,xuser.s,xuser.len);
+    substdio_puts(&smtpto,"\r\n");
+    substdio_flush(&smtpto);
+  } else {
+    substdio_puts(&smtpto,"MAIL FROM:<");
+    substdio_put(&smtpto,sender.s,sender.len);
+    substdio_puts(&smtpto,">\r\n");
+    substdio_flush(&smtpto);
+  }
   code = smtpcode();
   if (code >= 500) quit("DConnected to "," but sender was rejected");
   if (code >= 400) quit("ZConnected to "," but sender was rejected");
@@ -324,6 +658,11 @@
     case 1:
       if (!constmap_init(&maproutes,routes.s,routes.len,1)) temp_nomem(); break;
   }
+#ifdef TLS
+  if (control_rldef(&tlsclientciphers,"control/tlsclientciphers",0,"DEFAULT") != 1)
+    temp_control();
+  if(!stralloc_0(&tlsclientciphers)) temp_nomem();
+#endif
 }
 
 void main(argc,argv)
@@ -331,22 +670,34 @@
 char **argv;
 {
   static ipalloc ip = {0};
-  int i;
+  int i,j;
   unsigned long random;
   char **recips;
   unsigned long prefme;
   int flagallaliases;
   int flagalias;
   char *relayhost;
- 
+#ifdef TLS
+  int zfd;
+
+  sig_alarmcatch(sigalrm);
+#endif
+
   sig_pipeignore();
   if (argc < 4) perm_usage();
   if (chdir(auto_qmail) == -1) temp_chdir();
   getcontrols();
- 
+
+#ifdef TLS
+  if(-1==(zfd=open("control/clientcert.pem",O_RDONLY))) can_tls=0;
+  else close(zfd);
+#endif 
  
   if (!stralloc_copys(&host,argv[1])) temp_nomem();
  
+  if (!stralloc_copys(&auth_smtp_user,"")) temp_nomem();
+  if (!stralloc_copys(&auth_smtp_pass,"")) temp_nomem();
+
   relayhost = 0;
   for (i = 0;i <= host.len;++i)
     if ((i == 0) || (i == host.len) || (host.s[i] == '.'))
@@ -355,6 +706,21 @@
   if (relayhost && !*relayhost) relayhost = 0;
  
   if (relayhost) {
+    i = str_chr(relayhost,' ');
+    if (relayhost[i]) {
+      relayhost[i]=0;
+      if ('-' == relayhost[i+1]) {
+        allow_insecure_auth=1;
+        can_tls=0;
+        i++;
+      }
+      j = str_chr(relayhost + i + 1,' ');
+      if (relayhost[j]) {
+	relayhost[i + j + 1] = 0;
+        if (!stralloc_copys(&auth_smtp_user,relayhost + i + 1)) temp_nomem();
+	if (!stralloc_copys(&auth_smtp_pass,relayhost + i + j + 2)) temp_nomem();
+      }
+    }
     i = str_chr(relayhost,':');
     if (relayhost[i]) {
       scan_ulong(relayhost + i + 1,&port);
@@ -365,7 +731,9 @@
 
 
   addrmangle(&sender,argv[2],&flagalias,0);
- 
+  stralloc_0(&sender);
+  sender.len --; 
+
   if (!saa_readyplus(&reciplist,0)) temp_nomem();
   if (ipme_init() != 1) temp_oserr();
  
@@ -413,11 +781,18 @@
  
     smtpfd = socket(AF_INET,SOCK_STREAM,0);
     if (smtpfd == -1) temp_oserr();
- 
+
+    bind_by_sender ( smtpfd , sender.s , 1 ) ;
+    bind_by_remoteip ( smtpfd , &ip.ix[i].ip , 0 ) ;
+
     if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0) {
       tcpto_err(&ip.ix[i].ip,0);
       partner = ip.ix[i].ip;
-      smtp(); /* does not return */
+#ifdef TLS
+      smtp(ip.ix[i].fqdn); /* does not return */
+#else
+      smtp(); /* only returns when the next MX is to be tried */
+#endif
     }
     tcpto_err(&ip.ix[i].ip,errno == error_timeout);
     close(smtpfd);
diff -ruN qmail-1.03-factory/qmail-send.c qmail-1.03-7.10/qmail-send.c
--- qmail-1.03-factory/qmail-send.c	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/qmail-send.c	2010-02-14 22:53:32.000000000 -0500
@@ -44,6 +44,8 @@
 
 int lifetime = 604800;
 
+int bouncemaxbytes = 0;
+
 stralloc percenthack = {0};
 struct constmap mappercenthack;
 stralloc locals = {0};
@@ -262,6 +264,8 @@
  while (!stralloc_copys(&comm_buf[c],"")) nomem();
  ch = delnum;
  while (!stralloc_append(&comm_buf[c],&ch)) nomem();
+ ch = delnum >> 8;
+ while (!stralloc_append(&comm_buf[c],&ch)) nomem();
  fnmake_split(id);
  while (!stralloc_cats(&comm_buf[c],fn.s)) nomem();
  while (!stralloc_0(&comm_buf[c])) nomem();
@@ -740,9 +744,26 @@
      qmail_fail(&qqt);
    else
     {
-     substdio_fdbuf(&ssread,read,fd,inbuf,sizeof(inbuf));
-     while ((r = substdio_get(&ssread,buf,sizeof(buf))) > 0)
-       qmail_put(&qqt,buf,r);
+     if(bouncemaxbytes)
+     {
+       int bytestogo = bouncemaxbytes;
+       int bytestoget = (bytestogo < sizeof buf) ? bytestogo : sizeof buf;
+       substdio_fdbuf(&ssread,read,fd,inbuf,sizeof(inbuf));
+       while (bytestoget > 0 && (r = substdio_get(&ssread,buf,bytestoget)) > 0) {
+         qmail_put(&qqt,buf,r);
+         bytestogo -= bytestoget;
+         bytestoget = (bytestogo < sizeof buf) ? bytestogo : sizeof buf;
+       }
+       if (r > 0) {
+         qmail_puts(&qqt,"\n\n--- End of message stripped.\n");
+       }
+     }
+     else
+     {
+       substdio_fdbuf(&ssread,read,fd,inbuf,sizeof(inbuf));
+       while ((r = substdio_get(&ssread,buf,sizeof(buf))) > 0)
+         qmail_put(&qqt,buf,r);
+     }
      close(fd);
      if (r == -1)
        qmail_fail(&qqt);
@@ -906,41 +927,42 @@
      dline[c].len = REPORTMAX;
      /* qmail-lspawn and qmail-rspawn are responsible for keeping it short */
      /* but from a security point of view, we don't trust rspawn */
-   if (!ch && (dline[c].len > 1))
+   if (!ch && (dline[c].len > 2))
     {
      delnum = (unsigned int) (unsigned char) dline[c].s[0];
+     delnum += (unsigned int) ((unsigned int) dline[c].s[1]) << 8;
      if ((delnum < 0) || (delnum >= concurrency[c]) || !d[c][delnum].used)
        log1("warning: internal error: delivery report out of range\n");
      else
       {
        strnum3[fmt_ulong(strnum3,d[c][delnum].delid)] = 0;
-       if (dline[c].s[1] == 'Z')
+       if (dline[c].s[2] == 'Z')
 	 if (jo[d[c][delnum].j].flagdying)
 	  {
-	   dline[c].s[1] = 'D';
+	   dline[c].s[2] = 'D';
 	   --dline[c].len;
 	   while (!stralloc_cats(&dline[c],"I'm not going to try again; this message has been in the queue too long.\n")) nomem();
 	   while (!stralloc_0(&dline[c])) nomem();
 	  }
-       switch(dline[c].s[1])
+       switch(dline[c].s[2])
 	{
 	 case 'K':
 	   log3("delivery ",strnum3,": success: ");
-	   logsafe(dline[c].s + 2);
+	   logsafe(dline[c].s + 3);
 	   log1("\n");
 	   markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos);
 	   --jo[d[c][delnum].j].numtodo;
 	   break;
 	 case 'Z':
 	   log3("delivery ",strnum3,": deferral: ");
-	   logsafe(dline[c].s + 2);
+	   logsafe(dline[c].s + 3);
 	   log1("\n");
 	   break;
 	 case 'D':
 	   log3("delivery ",strnum3,": failure: ");
-	   logsafe(dline[c].s + 2);
+	   logsafe(dline[c].s + 3);
 	   log1("\n");
-	   addbounce(jo[d[c][delnum].j].id,d[c][delnum].recip.s,dline[c].s + 2);
+	   addbounce(jo[d[c][delnum].j].id,d[c][delnum].recip.s,dline[c].s + 3);
 	   markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos);
 	   --jo[d[c][delnum].j].numtodo;
 	   break;
@@ -1215,6 +1237,7 @@
 
 /* this file is too long ---------------------------------------------- TODO */
 
+#ifndef EXTERNAL_TODO
 datetime_sec nexttodorun;
 DIR *tododir; /* if 0, have to opendir again */
 stralloc todoline = {0};
@@ -1438,10 +1461,148 @@
    if (fdchan[c] != -1) close(fdchan[c]);
 }
 
+#endif
+
+/* this file is too long ------------------------------------- EXTERNAL TODO */
+
+#ifdef EXTERNAL_TODO
+stralloc todoline = {0};
+char todobuf[2048];
+int todofdin;
+int todofdout;
+int flagtodoalive;
+
+void tododied() { log1("alert: oh no! lost qmail-todo connection! dying...\n");
+ flagexitasap = 1; flagtodoalive = 0; }
+
+void todo_init()
+{
+  todofdout = 7;
+  todofdin = 8;
+  flagtodoalive = 1;
+  /* sync with external todo */
+  if (write(todofdout, "S", 1) != 1) tododied();
+  
+  return;
+}
+
+void todo_selprep(nfds,rfds,wakeup)
+int *nfds;
+fd_set *rfds;
+datetime_sec *wakeup;
+{
+  if (flagexitasap) {
+    if (flagtodoalive) {
+      write(todofdout, "X", 1);
+    }
+  }
+  if (flagtodoalive) {
+    FD_SET(todofdin,rfds);
+    if (*nfds <= todofdin)
+      *nfds = todofdin + 1;
+  }
+}
+
+void todo_del(char* s)
+{
+ int flagchan[CHANNELS];
+ struct prioq_elt pe;
+ unsigned long id;
+ unsigned int len;
+ int c;
+
+ for (c = 0;c < CHANNELS;++c) flagchan[c] = 0;
+ switch(*s++) {
+  case 'L':
+    flagchan[0] = 1;
+    break;
+  case 'R':
+    flagchan[1] = 1;
+    break;
+  case 'B':
+    flagchan[0] = 1;
+    flagchan[1] = 1;
+    break;
+  case 'X':
+    break;
+  default:
+    log1("warning: qmail-send unable to understand qmail-todo\n");
+    return;
+ }
+ 
+ len = scan_ulong(s,&id);
+ if (!len || s[len]) {
+  log1("warning: qmail-send unable to understand qmail-todo\n");
+  return;
+ }
+
+ pe.id = id; pe.dt = now();
+ for (c = 0;c < CHANNELS;++c)
+   if (flagchan[c])
+     while (!prioq_insert(&pqchan[c],&pe)) nomem();
+
+ for (c = 0;c < CHANNELS;++c) if (flagchan[c]) break;
+ if (c == CHANNELS)
+   while (!prioq_insert(&pqdone,&pe)) nomem();
+
+ return;
+}
+
+void todo_do(rfds)
+fd_set *rfds;
+{
+  int r;
+  char ch;
+  int i;
+  
+  if (!flagtodoalive) return;
+  if (!FD_ISSET(todofdin,rfds)) return;
+
+  r = read(todofdin,todobuf,sizeof(todobuf));
+  if (r == -1) return;
+  if (r == 0) {
+    if (flagexitasap)
+      flagtodoalive = 0;
+    else
+      tododied();
+    return;
+  }
+  for (i = 0;i < r;++i) {
+    ch = todobuf[i];
+    while (!stralloc_append(&todoline,&ch)) nomem();
+    if (todoline.len > REPORTMAX)
+      todoline.len = REPORTMAX;
+      /* qmail-todo is responsible for keeping it short */
+    if (!ch && (todoline.len > 1)) {
+      switch (todoline.s[0]) {
+	case 'D':
+	  if (flagexitasap) break;
+	  todo_del(todoline.s + 1);
+	  break;
+	case 'L':
+	  log1(todoline.s + 1);
+	  break;
+	case 'X':
+	  if (flagexitasap)
+	    flagtodoalive = 0;
+	  else
+	    tododied();
+	  break;
+	default:
+	  log1("warning: qmail-send unable to understand qmail-todo: report mangled\n");
+	  break;
+      }
+      todoline.len = 0;
+    }
+  }
+}
+
+#endif
 
 /* this file is too long ---------------------------------------------- MAIN */
 
 int getcontrols() { if (control_init() == -1) return 0;
+ if (control_readint(&bouncemaxbytes,"control/bouncemaxbytes") == -1) return 0;   
  if (control_readint(&lifetime,"control/queuelifetime") == -1) return 0;
  if (control_readint(&concurrency[0],"control/concurrencylocal") == -1) return 0;
  if (control_readint(&concurrency[1],"control/concurrencyremote") == -1) return 0;
@@ -1504,6 +1665,9 @@
    log1("alert: unable to reread controls: unable to switch to home directory\n");
    return;
   }
+#ifdef EXTERNAL_TODO
+ write(todofdout, "H", 1);
+#endif
  regetcontrols();
  while (chdir("queue") == -1)
   {
@@ -1544,7 +1708,7 @@
  numjobs = 0;
  for (c = 0;c < CHANNELS;++c)
   {
-   char ch;
+   char ch, ch1;
    int u;
    int r;
    do
@@ -1552,7 +1716,13 @@
    while ((r == -1) && (errno == error_intr));
    if (r < 1)
     { log1("alert: cannot start: hath the daemon spawn no fire?\n"); _exit(111); }
+   do
+     r = read(chanfdin[c],&ch1,1);
+   while ((r == -1) && (errno == error_intr));
+   if (r < 1)
+    { log1("alert: cannot start: hath the daemon spawn no fire?\n"); _exit(111); }
    u = (unsigned int) (unsigned char) ch;
+   u += (unsigned int) ((unsigned char) ch1) << 8;
    if (concurrency[c] > u) concurrency[c] = u;
    numjobs += concurrency[c];
   }
@@ -1568,8 +1738,12 @@
  todo_init();
  cleanup_init();
 
+#ifdef EXTERNAL_TODO
+ while (!flagexitasap || !del_canexit() || flagtodoalive)
+#else
  while (!flagexitasap || !del_canexit())
-  {
+#endif
+ {
    recent = now();
 
    if (flagrunasap) { flagrunasap = 0; pqrun(); }
diff -ruN qmail-1.03-factory/qmail-showctl.c qmail-1.03-7.10/qmail-showctl.c
--- qmail-1.03-factory/qmail-showctl.c	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/qmail-showctl.c	2010-02-14 22:53:32.000000000 -0500
@@ -15,6 +15,7 @@
 #include "auto_patrn.h"
 #include "auto_spawn.h"
 #include "auto_split.h"
+#include "spf.h"
 
 stralloc me = {0};
 int meok;
@@ -214,7 +215,12 @@
     _exit(111);
   }
 
-  do_lst("badmailfrom","Any MAIL FROM is allowed.",""," not accepted in MAIL FROM.");
+  do_lst("badhelo","Any HELO host name is allowed.",""," HELO host name denied if it matches this pattern.");
+  do_lst("badmailfrom","Any MAIL FROM is allowed.",""," MAIL FROM denied if it matches this pattern.");
+  do_lst("badmailfromnorelay","Any MAIL FROM is allowed.",""," MAIL FROM denied if it matches this pattern and RELAYCLIENT is not set.");
+  do_lst("badrcptto","No RCPT TO are specifically denied.",""," RCPT TO denied if it matches this pattern.");
+  do_lst("badrcpttonorelay","No RCPT TO are specifically denied.","",
+    " RCPT TO denied if it matches this pattern, RELAYCLIENT is not set, and client has not done a successful AUTH.");
   do_str("bouncefrom",0,"MAILER-DAEMON","Bounce user name is ");
   do_str("bouncehost",1,"bouncehost","Bounce host name is ");
   do_int("concurrencylocal","10","Local concurrency is ","");
@@ -257,6 +263,10 @@
 
   do_str("smtpgreeting",1,"smtpgreeting","SMTP greeting: 220 ");
   do_lst("smtproutes","No artificial SMTP routes.","SMTP route: ","");
+  do_int("spfbehavior","0","The SPF behavior is ","");
+  do_str("spfexp",0,SPF_DEFEXP,"The SPF default explanation is: 550 ");
+  do_str("spfguess",0,"","The guess SPF rules are: ");
+  do_str("spfrules",0,"","The local SPF rules are: ");
   do_int("timeoutconnect","60","SMTP client connection timeout is "," seconds");
   do_int("timeoutremote","1200","SMTP client data timeout is "," seconds");
   do_int("timeoutsmtpd","1200","SMTP server data timeout is "," seconds");
@@ -267,7 +277,11 @@
     if (str_equal(d->d_name,"..")) continue;
     if (str_equal(d->d_name,"bouncefrom")) continue;
     if (str_equal(d->d_name,"bouncehost")) continue;
+    if (str_equal(d->d_name,"badhelo")) continue;
     if (str_equal(d->d_name,"badmailfrom")) continue;
+    if (str_equal(d->d_name,"badmailfromnorelay")) continue;
+    if (str_equal(d->d_name,"badrcptto")) continue;
+    if (str_equal(d->d_name,"badrcpttonorelay")) continue;
     if (str_equal(d->d_name,"bouncefrom")) continue;
     if (str_equal(d->d_name,"bouncehost")) continue;
     if (str_equal(d->d_name,"concurrencylocal")) continue;
@@ -292,6 +306,10 @@
     if (str_equal(d->d_name,"rcpthosts")) continue;
     if (str_equal(d->d_name,"smtpgreeting")) continue;
     if (str_equal(d->d_name,"smtproutes")) continue;
+    if (str_equal(d->d_name,"spfbehavior")) continue;
+    if (str_equal(d->d_name,"spfexp")) continue;
+    if (str_equal(d->d_name,"spfguess")) continue;
+    if (str_equal(d->d_name,"spfrules")) continue;
     if (str_equal(d->d_name,"timeoutconnect")) continue;
     if (str_equal(d->d_name,"timeoutremote")) continue;
     if (str_equal(d->d_name,"timeoutsmtpd")) continue;
diff -ruN qmail-1.03-factory/qmail-smtpd.8 qmail-1.03-7.10/qmail-smtpd.8
--- qmail-1.03-factory/qmail-smtpd.8	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/qmail-smtpd.8	2010-02-14 23:12:05.000000000 -0500
@@ -3,6 +3,11 @@
 qmail-smtpd \- receive mail via SMTP
 .SH SYNOPSIS
 .B qmail-smtpd
+[
+.I hostname
+.I checkprogram
+.I subprogram
+]
 .SH DESCRIPTION
 .B qmail-smtpd
 receives mail messages via the Simple Mail Transfer Protocol (SMTP)
@@ -23,7 +28,36 @@
 header fields.
 
 .B qmail-smtpd
-supports ESMTP, including the 8BITMIME and PIPELINING options.
+has been modified to include many capabilities which are not present in
+a standard qmail installation. The modifications are briefly described
+below, or you can visit the web page listed at the end of this document
+for a more complete description of how this version of qmail-smtpd differs
+from the standard qmail-smtpd.
+
+.B qmail-smtpd
+supports ESMTP, including the 8BITMIME, PIPELINING, and AUTH options.
+
+.B qmail-smtpd
+can accept LOGIN, PLAIN, and optionally CRAM-MD5 AUTH types.  It invokes
+.IR checkprogram ,
+which reads on file descriptor 3 the username, a 0 byte, the password
+or CRAM-MD5 response derived from
+.IR hostname ,
+another 0 byte, a CRAM-MD5 challenge (if applicable to the AUTH type),
+and a final 0 byte.
+.I checkprogram
+invokes
+.I subprogram
+upon successful authentication, which should in turn return 0 to
+.BR qmail-smtpd ,
+effectively setting the environment variables RELAYCLIENT and TCPREMOTEINFO
+(any supplied value replaced with the authenticated username).
+.B qmail-smtpd
+will reject the authentication attempt if it receives a nonzero return
+value from
+.I checkprogram
+or
+.IR subprogram .
 .SH TRANSPARENCY
 .B qmail-smtpd
 converts the SMTP newline convention into the UNIX newline convention
@@ -37,11 +71,30 @@
 even though such messages violate the SMTP protocol.
 .SH "CONTROL FILES"
 .TP 5
+.I badhelo
+Unacceptable HELO/EHLO host names.
+.B qmail-smtpd
+will reject every recipient address for a message if
+the host name is listed in, 
+or matches a POSIX regular expression pattern listed in,
+.IR badhelo .
+If the 
+.B NOBADHELO 
+environment variable is set, then the contents of 
+.IR badhelo 
+will be ignored.
+If the
+.B LOGREGEX
+environment variable is set, then the log entry generated by a match
+will include the regular expression which was matched.
+For more information, please have a look at doc/README.qregex.
+.TP 5
 .I badmailfrom
 Unacceptable envelope sender addresses.
 .B qmail-smtpd
 will reject every recipient address for a message
-if the envelope sender address is listed in
+if the envelope sender address is listed in, or matches a POSIX regular expression
+pattern listed in,
 .IR badmailfrom .
 A line in
 .I badmailfrom
@@ -49,6 +102,41 @@
 .BR @\fIhost ,
 meaning every address at
 .IR host .
+If the
+.B LOGREGEX
+environment variable is set, then the log entry generated by a match
+will include the regular expression which was matched.
+For more information, please have a look at doc/README.qregex.
+.TP 5
+.I badmailfromnorelay
+Functions the same as the
+.IR badmailfrom
+control file but is read only if the 
+.B RELAYCLIENT 
+environment variable is not set.
+For more information, please have a look at doc/README.qregex.
+.TP 5
+.I badrcptto
+Unacceptable envelope recipient addresses.
+.B qmail-smtpd
+will reject every recipient address for a message if the recipient address
+is listed in,
+or matches a POSIX regular expression pattern listed in,
+.IR badrcptto .
+If the
+.B LOGREGEX
+environment variable is set, then the log entry generated by a match
+will include the regular expression which was matched.
+For more information, please have a look at doc/README.qregex.
+.TP 5
+.I badrcpttonorelay
+Functions the same as the
+.IR badrcptto
+control file but is read only if the
+.B RELAYCLIENT
+environment variable is not set (and the client has not done a successful
+AUTH command.)
+For more information, please have a look at doc/README.qregex.
 .TP 5
 .I databytes
 Maximum number of bytes allowed in a message,
@@ -76,6 +164,9 @@
 .B DATABYTES
 is set, it overrides
 .IR databytes .
+In addition, the environment variable is re-checked after a successful 
+AUTH command, which makes it possible to change its value using an 
+AUTH_SET_DATABYTES environment variable.
 .TP 5
 .I localiphost
 Replacement host name for local IP addresses.
@@ -97,6 +188,32 @@
 This is done before
 .IR rcpthosts .
 .TP 5
+.I maxrcpt
+If this file exists and contains a non-zero value, qmail-smtpd will
+not accept more than this number of recipients for any single
+message.
+
+This value may be overridden using the
+.B MAXRCPT
+environment variable, which may itself be changed in the event of
+a successful AUTH command using an
+.B AUTH_SET_MAXRCPT
+environment variable.
+.TP 5
+.I mfcheck
+If this file exists and contains a non-zero value, the MAIL FROM
+argument will be checked to make sure that the domain portion has
+an MX record. This can help prevent spammers using bogus return
+addresses. If the value is greater than 1, the results of this check
+will be logged.
+
+If the environment variable
+.B MFCHECK
+is set, its value overrides the value in this file. In addition, the 
+environment variable is re-checked after a successful AUTH command, 
+which makes it possible to change its value using an AUTH_SET_MFCHECK 
+environment variable.
+.TP 5
 .I morercpthosts
 Extra allowed RCPT domains.
 If
@@ -163,12 +280,584 @@
 The first word of
 .I smtpgreeting
 should be the current host's name.
+
+Note that this value can be overridden by setting an
+.B SMTPGREETING environment variable.
+.TP 5
+.I spfbehavior
+Set to a value between 1 and 6 to enable SPF checks; 0 to disable.
+1 selects 'annotate-only' mode, where
+.B qmail-smtpd
+will annotate incoming email with
+.B Received-SPF
+fields, but will not reject any messages.  2 will produce temporary
+failures on DNS lookup problems so you can make sure you always have
+meaningful Received-SPF headers.  3 selects 'reject' mode,
+where incoming mail will be rejected if the SPF record says 'fail'.  4
+selects a more stricter rejection mode, which is like 'reject' mode,
+except that incoming mail will also be rejected when the SPF record
+says 'softfail'.  5 will also reject when the SPF record says 'neutral',
+and 6 if no SPF records are available at all (or a syntax error was
+encountered). The contents of this file are overridden by the value of
+the
+.B SPFBEHAVIOR
+environment variable, if set.
+Default: 0.
+
+Note that the environment variable is re-checked after a successful AUTH
+command, which makes it possible to change its value using an
+AUTH_SET_SPFBEHAVIOR environment variable.
+.TP 5
+.I spfexp
+You can add a line with a an SPF explanation that will be shown to the
+sender in case of a reject. It will override the default one. You can
+use SPF macro expansion.
+.TP 5
+.I spfguess
+You can add a line with SPF rules that will be checked if a sender
+domain doesn't have a SPF record. The local rules will also be used
+in this case.
+.TP 5
+.I spfrules
+You can add a line with SPF rules that will be checked before other SPF
+rules would fail.  This can be used to always allow certain machines to
+send certain mails.
+.TP 5
+.I servercert.pem
+This file should contain a PEM-encoded key and certificate which will be
+used in order to create the server end of an SSL connection when processing
+the STARTTLS command. This file should be readable by
+.B qmail-smtpd
+but not readable to the entire world.
+
+Note that this filename may be overridden by setting a
+.B TLS_SERVER_CERT environment variable.
 .TP 5
 .I timeoutsmtpd
 Number of seconds
 .B qmail-smtpd
 will wait for each new buffer of data from the remote SMTP client.
 Default: 1200.
+.TP 5
+.SH "ENVIRONMENT VARIABLES"
+There are several environment variables which may be set in order to
+modify how qmail-smtpd works.
+.TP 5
+.I ALLOW_CRAM
+The CRAM-MD5 authentication method is not normally supported, because it
+requires the server to have a list of plain-text passwords.
+.B This is a bad thing.
+However, if you understand the problem and are willing to accept the risk
+that somebody cracking into your server might get all of your mailbox
+passwords very easily, you can set this variable to a non-zero value and
+.B qmail-smtpd
+will advertise and support CRAM-MD5 as an authentication method.
+
+This option also requires that your checkpassword program know how to
+process a CRAM authentication request. The
+.B vchkpw
+program, part of the vpopmail package (version 5.4 and above) is able 
+to handle CRAM-MD5 if you compile it with the option to store plain-text 
+passwords. Earlier versions of vpopmail had a bug where it was reading 
+the challenge and response in the wrong order, because the original SMTP 
+AUTH patch sent the values in the wrong order. The 
+.B 6c
+version of the combined patch (described at the end of this document) fixed
+the order.
+.TP 5
+.I ALLOW_INSECURE_AUTH
+The AUTH command is not normally advertised in response to the EHLO
+command, or accepted from the client, unless the connection is known to be
+secure (because STARTTLS has been successfully processed, or because the
+.B SSL
+environment variable has a non-zero value.) Setting
+.B ALLOW_INSECURE_AUTH
+to a non-zero value will make
+.B qmail-smtpd
+advertise the AUTH capabilities in the EHLO response, and accept the
+AUTH command from the client, regardless of whether or not the connection
+is secure.
+
+.B This is a very bad idea.
+This allows your users to send their passwords across the Internet in
+plain text when they AUTH in order to relay outbound mail. Anybody with
+a packet sniffer could get your user's password, and then have their way
+with the user's mailbox, or anything else where the user may be using
+the same password. They would also be able to use that password to use
+your server as a relay for spam...
+.TP 5
+.I AUTH_CDB
+If present, this variable contains the name of a cdb file which contains
+valid userid/password combinations to satisfy the AUTH command. Each key
+in this file should be a valid system userid, virtual email address, or
+other value which passes for a "userid" in the AUTH command. The value
+linked to each key should be the ENCRYPTED password for that userid.
+
+Note that the passwords in this file MUST BE ENCRYPTED using an encryption
+or hashing method supported by your system's
+.B crypt(3)
+function. This is almost always the same set of encryption methods used
+for the system's password database.
+.TP 5
+.I AUTH_SET_ and AUTH_UNSET_
+When the first DATA command is sent during a session, if any environment
+variables with names like AUTH_SET_{something} exist, a corresponding 
+environment variable called {something} will be created with the value 
+from the AUTH_SET_{something} variable, or if such a variable already 
+exists, the existing value will be replaced with the new value. In 
+either case, the AUTH_SET_{something} variable will then be deleted.
+
+At the same time, if any environment variables with names like
+AUTH_UNSET_{something} exist, any existing environment variable called
+{something} will be deleted from the environment, and then the 
+AUTH_UNSET_{something} variable will be deleted.
+
+These types of variables can be useful in situations where changes need 
+to be made to the environment when a user successfully sends an AUTH 
+command, in order to modify the behavior of a program called through the
+.B QMAILQUEUE
+mechanism (see below.)
+.TP 5
+.I DENY_TLS
+Setting this variable to a non-zero value will cause qmail-smtpd to not
+support the STARTTLS command under any circumstances.
+
+This value is internally set to 1 if the
+.B servercert.pem
+control file (or whatever filename the
+.B TLS_SERVER_CERT
+variable points to) is not present or not readable.
+.TP 5
+.I DROP_PRE_GREET
+Many spammers will try to send commands to SMTP servers before the server
+has sent its inital greeting, even though this violates RFC 821. Setting
+this variable to a non-zero value will cause
+.b qmail-smtpd
+to pause for one second before sending the initial greeting, and drop any
+client connection which tries to send commands before the greeting has
+been sent.
+
+Note that if you also use the
+.B GREETDELAY
+variable, the one second delay that DROP_PRE_GREET uses is taken from that
+number- so the value of GREETDELAY will be the total used by both features.
+.TP 5
+.I FORCE_TLS
+Setting this variable to a non-zero value will cause
+.B qmail-smtpd
+not to accept the MAIL FROM command unless STARTTLS has been successfully
+processed. This makes it possible to create an SMTP server which can only
+be used by authorized users.
+
+.B WARNING: do not do this for a standard port 25 SMTP service
+which handles incoming mail for any real domains, or you will prevent the
+domain from being able to receive any mail unless it happens to come from
+one of your authorized users.
+
+This value is internally set to zero if the
+.B SSL
+environment variable is set to a non-zero value.
+
+If you try to combine
+.B FORCE_TLS
+with
+.B DENY_TLS
+(either by manually setting the
+.B DENY_TLS
+variable, or by not having a readable
+.B servercert.pem
+control file) the client will receive an error message and
+.B qmail-smtpd
+will exit without accepting any message from the client.
+.TP 5
+.I GREETDELAY
+Many spammers use programs which will give up on an SMTP server if
+the server answers but doesn't send its initial banner in a timely
+manner. However, legitimate mail servers will wait for the banner.
+If you set this variable to a non-zero value,
+.B qmail-smtpd
+will pause that many seconds before sending the banner.
+
+Note that if you also use the
+.B DROP_PRE_GREET
+variable, the extra one-second delay that DROP_PRE_GREET introduces
+is taken from this total, so that if both variables are used, the
+value of GREETDELAY is the total delay introduced by both features.
+.TP 5
+.I LOGREGEX
+The badhelo, badmailfrom, badmailfromnorelay, badrcptto, and
+badrcpttonorelay control files contain regular expressions which will
+cause HELO, MAIL FROM, or RCPT TO commands whose arguments match a
+pattern listed in the file to be rejected.
+
+If this variable exists, the log messages generated by these mecahnisms
+will include the regular expression which was matched.
+.TP 5
+.I MAXRCPT
+If this variable is set, its value will override the value from the
+.B maxrcpt
+control file. It sets the maximum number of recipients for any one
+message.
+
+This variable is re-checked after a successful AUTH command, which
+makes it possible to change its value using an AUTH_SET_MAXRCPT
+environment variable.
+.TP 5
+.I MFCHECK
+If this variable is set and has a non-zero value, the MAIL FROM
+argument will be checked to make sure that the domain portion has
+an MX record. This can help prevent spammers using bogus return
+addresses. Note that this is the same check which is done if the
+.b mfcheck
+control file contains a non-zero value, although the value in the
+environment variable overrides the value in the file.
+
+If the value is greater than 1, the results of this check will be
+logged.
+
+This variable is re-checked after a successful AUTH command, which
+makes it possible to change its value using an AUTH_SET_MFCHECK
+environment variable.
+.TP 5
+.I PASSWORD_EXPIRES
+If this variable is set, it is expected to contain a unix time() value (the
+number of seconds since 1970-01-01 00:00:00 GMT.) If the current time is
+equal to or greater than this value, any AUTH command will fail with an
+error message saying that the password is expired.
+
+Logically, this variable should only be used on a per-user basis. The
+AUTH_CDB patch allows you to create environment variables which are
+created when a specific user sends a successful AUTH command. Otherwise,
+there is currently no other way to set this on a per-user basis.
+.TP 5
+.I QMAILQUEUE
+If this variable is set, it should point to a program which will be 
+executed instead of the
+.B qmail-queue
+program, in order to add a received message to the queue. This program 
+should read its input and return the values exactly as the original
+.B qmail-queue
+program does. This version of the patch adds two additional return 
+values which are not present in the original
+.B qmail-queue
+program:
+.RS 5
+.TP 5
+.B 1
+Tells the remote SMTP client "Your spam has been ignored", with the same 
+status code as a successful delivery. This tells the remote SMTP client 
+that the message was delivered, while in fact the message was not added 
+to the queue. This can be used in cases where you know the message is 
+SPAM and you know that if you refuse the message, the remote SMTP client 
+is only going to keep trying over and over.
+.TP 5
+.B 32
+Tells the remote SMTP client "we do not accept SPAM", with a status code 
+indicating permanent refusal of the message (i.e. a "hard error".) This 
+can be used if you know that the message is SPAM and want this message 
+to show up in the remote server's log (instead of the standard "mail 
+server permanently rejected message" caused by return value 31.)
+.RE
+.TP 5
+.I RCPTCHECK
+If this variable exists, it should contain the full pathname to a
+program which
+.B qmail-smtpd
+will run after receiving each
+.B RCPT
+command.
+
+No arguments are passed on the command line; before running the program,
+.B qmail-smtpd
+will store the recipient address sent by the client in the
+.B RECIPIENT
+environment variable, and the sender address (from the
+.B MAIL
+command) in the
+.B SENDER
+variable. Any other environment variables which were present when
+.B qmail-smtpd
+started, as possibly modified by the
+.B AUTH_SET
+mechanism, will be present as well. Note that this includes the
+.B TCPREMOTEIP
+variable, set by tcpserver, which will contain the client's IP address.
+
+The exit code of the program determines whether or not
+.B qmail-smtpd
+will accept the command. The possible values are:
+.RS 5
+.TP 5
+.B 100
+The recipient is not valid. The client receives a 553 error.
+.TP 5
+.B 111
+Temporary problem verifying the recipient. The client receives a 421
+error code and the connection is closed.
+.TP 5
+.B 120
+The program could not execute correctly. The client receives a 421 error
+code and the connection is closed.
+.TP 5
+.B anything else
+The recipient is valid.
+
+.RE
+.RS 5
+Anything the program may send to its standard out or standard error
+channel will be sent to the SMTP service's logs, just as if
+.B qmail-smtpd
+had printed it. This allows your program to log what it's doing.
+
+This variable may be modified using the
+.B AUTH_SET
+mechanism if you want to bypass recipient checking, or use a different
+program, for clients who have sent a valid AUTH command. However, I
+recommend you write your program to check the
+.B SMTP_AUTH_USER
+variable instead- if a valid AUTH command has been sent, this variable
+will contain the userid from that command.
+
+This functionality (calling an external program in response to an AUTH
+command) comes from Jay Soffian's RCPTCHECK patch, and is more fully
+documented on his web site:
+.IP
+http://www.soffian.org/downloads/qmail/qmail-smtpd-doc.html
+
+.RE
+.TP 5
+.I RELAYCLIENT
+If this variable exists when
+.B qmail-smtpd
+starts, the remote SMTP client will be allowed to relay mail, ignoring 
+the
+.B rcpthosts
+and
+.B morercpthosts.cdb
+files. This is normally only done for IP addresses which belong to you 
+or your clients, and which could not be used by anybody who should not 
+be authorized to relay. Some sites don't use this at all, preferring to 
+grant relay access only to users who send a successful AUTH command.
+.TP 5
+.I RELAYREJ
+If this variable exists and has a non-zero value,
+.B qmail-smtpd
+will do a sanity check each recipient address, to ensure that the
+address does not contain multiple "@" characters, or that it does not
+contain any "%" or "!" characters before the "@" character.
+
+This check is only useful when dealing with broken relay testers that
+try to use these characters to trigger old sendmail bugs and force your
+server to be an open relay. Without these checks, qmail-smtpd may accept
+messages but then not be able to deliver them- however some broken relay
+testers consider this to be evidence of an open relay.
+
+This check is normally not needed, and in fact it can break some types
+of gateway systems (such as SMTP-to-UUCP.) However, some people seem to
+get a warm fuzzy feeling if they know that it's being done, so I've
+included it for their benefit.
+.B Please don't use it.
+
+This variable is re-checked after a successful AUTH command, which
+makes it possible to change its value using an AUTH_SET_RELAYREJ
+environment variable.
+.TP 5
+.I REQUIRE_AUTH
+Setting this variable to a non-zero value will cause
+.B qmail-smtpd
+to not accept the MAIL FROM command until the user has successfully
+authenticated (using the AUTH command.) Note that this may be combined
+with
+.B FORCE_TLS
+to create a secure server which your users can use to relay mail.
+
+If you try to set this without properly specifying an authentication
+program on the command line, the client will receive an error message and
+.B qmail-smtpd
+will exit without accepting any message from the client.
+.TP 5
+.I SMTP_AUTH_USER
+When the remote SMTP client sends a successful AUTH command, the
+.B SMTP_AUTH_USER
+variable will be set to the userid from the credentials. This variable 
+can be tested by any scripts called through the
+.B QMAILQUEUE
+mechanism to determine whether or not the remote SMTP client has sent a 
+successful AUTH command or not.
+.TP 5
+.I QMAILSMTPD_HELP_VERSION
+If this variable is set to a non-zero value, the output of the HELP command
+will include the version and URL of the jms1 combined patch you are using.
+
+Note that this variable is re-checked after a successful AUTH command,
+which makes it possible to change its value using an
+AUTH_SET_QMAILSMTPD_HELP_VERSION environment variable.
+.TP 5
+.I QMAILSMTPD_LOG_MAIL
+If this variable is set to a non-zero value, qmail-smtpd will log the sender
+addresses specified in all successful MAIL FROM commands received from the
+client.
+.TP 5
+.I QMAILSMTPD_LOG_RCPT
+If this variable is set to a non-zero value, qmail-smtpd will log the
+recipient addresses specified in all successful RCPT TO commands received
+from the client.
+.TP 5
+.I SMTPGREETING
+This variable overrides the value stored in the
+.B smtpgreeting
+control file (see above.)
+.TP 5
+.I SPFBEHAVIOR
+This variable overrides the value stored in the
+.B spfbehavior
+control file (see above.)
+
+Note that this variable is re-checked after a successful AUTH command,
+which makes it possible to change its value using an
+AUTH_SET_SPFBEHAVIOR environment variable.
+.TP 5
+.I SPF_BLOCK_PLUS_ALL
+Setting this variable to a non-zero value will cause any "+all" term 
+found in a domain's SPF record to be interpreted as if it said
+"-all". This can be useful if you have a problem with spammers who have 
+discovered that by creating an SPF record with "+all" in it, they can 
+basically "walk around" any SPF-based filtering you may be doing.
+
+This variable is checked during the SPF check, which happens after the 
+client sends a MAIL FROM command. This makes it possible to change its 
+value using an AUTH_SET_SPF_BLOCK_PLUS_ALL environment variable.
+.TP 5
+.I SPF_LOG
+Setting this variable to a non-zero value will cause the result of every
+SPF check to be logged. The log entries will be identical to the
+"Received-SPF" header which is added to each incoming message.
+
+This variable is re-checked after a successful AUTH command, which
+makes it possible to change its value using an AUTH_SET_SPF_LOG
+environment variable.
+.TP 5
+.I SSL
+This variable should be set to a non-zero value if the SMTP service is
+running
+.B qmail-smtpd
+under an SSL-secured socket. This tells
+.B qmail-smtpd
+that the connection is secure, which enables the AUTH command and disables
+the STARTTLS command.
+.TP 5
+.I TCPREMOTEINFO
+This variable is normally set by
+.B tcpserver
+if run without the -R option, and will contain the data (if any) 
+returned by the
+.B identd
+service on the remote SMTP client's IP address. A successful AUTH 
+command will add or replace this value with the userid which was part of 
+the credentials. The data is the same as what you would find in the
+.B SMTP_AUTH_USER
+variable, with the exception that this variable may exist without a 
+successful AUTH command.
+.TP 5
+.I TLS_SERVER_CERT
+This variable contains the name of a file, which contains a PEM-encoded
+private key and certificate used to encrypt the connection if a STARTTLS
+command is received. If this variable is not present, the default name
+.B control/servercert.pem
+will be used.
+
+Note that if you wish to set up a service which will not accept the
+STARTTLS command at all, you may either remove whatever file this variable
+(or the default value) points to, or you can create a
+.B DENY_TLS
+environment variable with a non-zero value.
+.TP 5
+.I VALIDRCPTTO_CDB
+If this variable is present and not empty, qmail-smtpd will expect it to
+contain the name of a cdb file. qmail-smtpd will check the argument of every
+RCPT command against this file. It searches the file for a key which matches
+the RCPT command. If a key is not found, the RCPT command will be rejected.
+If a key is found, the RCPT command may be accepted or rejected, depending
+on the value (if any) attached to that key.
+
+qmail-smtpd searches first for a key which matches the argument to the
+RCPT command. If the address sent by the client is not found, and if the
+mailbox portion of the address (the part to the left of the "@" sign)
+contains one or more of qmail's "ext" character (usually "-", ASCII 45)
+it will search for any appropriate entries of the form
+.B user-default@domain
+going from more to less specific, with
+.B @domain
+as a final resort.
+
+For example, if the address sent is
+.B user-one-two-three@domain,
+it will search for the following items:
+
+.EX
+   user-one-two-three@domain
+   user-one-two-default@domain
+   user-one-default@domain
+   user-default@domain
+   @domain
+.EE
+
+The first key key it finds while searching is what will be used.
+
+Once an appropriate key is found, the value (if any) attached to that key
+is checked. If the key has no value (as will be the case if you are using
+an older version of the validrcptto.cdb patch) then the address will be
+accepted. If there is a value attached, and if the first byte of that
+value is a hyphen (i.e. "-", ASCII 45) then the address will be rejected.
+Values other than "-" are reserved for future versions of this patch and
+should not be used.
+
+In order to prevent "harvesting" attacks,
+.B qmail-smtpd
+counts how many invalid recipients have been attempted in each connection,
+and when a certain limit is reached,
+.B qmail-smtpd
+will forcibly disconnect the client. This limit has a default 10, and can be
+changed or disabled with the
+.B VALIDRCPTTO_LIMIT
+environment variable.
+
+This variable is re-checked after a successful AUTH command, which makes
+it possible to change its value using an AUTH_SET_VALIDRCPTTO_CDB
+or AUTH_UNSET_VALIDRCPTTO_CDB environment variable.
+.TP 5
+.I VALIDRCPTTO_LIMIT
+This variable sets the limit of how many invalid RCPT TO commands will be
+treated as "soft errors". When this limit is reached,
+.B qmail-smtpd
+will forcibly disconnect from the client. The default limit is 10. Setting
+this value to zero will disable the counting entirely, making it possible
+for a spammer to "harvest" the valid email addresses on your server.
+
+This variable is re-checked after a successful AUTH command, which makes
+it possible to change its value using an AUTH_SET_VALIDRCPTTO_LIMIT
+environment variable.
+.TP 5
+.I VALIDRCPTTO_LOG
+Setting this variable to 1 will cause
+.B qmail-smtpd
+to log each RCPT TO command received from a client, along with what entry
+from the
+.B validrcptto.cdb
+control file matched the address entered. If no match was found, that will
+be logged as well. If a client is forcibly disconnected for trying more than
+.B VALIDRCPTTO_LIMIT
+invalid recipients, that will be logged as well.
+
+Setting this variable to 2 will cause
+.B qmail-smtpd
+to also log everything it searches for within the
+.B validrcptto.cdb
+file. This can be useful when debugging a problem.
+
+This variable is re-checked after a successful AUTH command, which makes
+it possible to change its value using an AUTH_SET_VALIDRCPTTO_LOG
+environment variable.
 .SH "SEE ALSO"
 tcp-env(1),
 tcp-environ(5),
@@ -177,3 +866,10 @@
 qmail-newmrh(8),
 qmail-queue(8),
 qmail-remote(8)
+.SH "HISTORY"
+See
+.B http://qmail.jms1.net/patches/combined-details.shtml
+for more detailed information about the differences between this instance
+of qmail and the stock qmail. This man page is current as of the
+.B 7.10
+version of the combined patch file.
diff -ruN qmail-1.03-factory/qmail-smtpd.c qmail-1.03-7.10/qmail-smtpd.c
--- qmail-1.03-factory/qmail-smtpd.c	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/qmail-smtpd.c	2010-02-14 22:54:53.000000000 -0500
@@ -1,5 +1,7 @@
 #include "sig.h"
+#ifndef TLS
 #include "readwrite.h"
+#endif
 #include "stralloc.h"
 #include "substdio.h"
 #include "alloc.h"
@@ -20,18 +22,124 @@
 #include "now.h"
 #include "exit.h"
 #include "rcpthosts.h"
+#ifndef TLS
 #include "timeoutread.h"
 #include "timeoutwrite.h"
+#endif
 #include "commands.h"
+#include "qregex.h"
+#include "strerr.h"
+#include "wait.h"
+#include "fd.h"
+#include "dns.h"
+#include "spf.h"
+#include "cdb.h"
+#include "auto_break.h"
+
+#define BMCHECK_BMF 0
+#define BMCHECK_BMFNR 1
+#define BMCHECK_BRT 2
+#define BMCHECK_BRTNR 3
+#define BMCHECK_BHELO 4
+
+#define _XOPEN_SOURCE
+#include <unistd.h>
+#ifdef TLS
+#include <openssl/ssl.h>
+SSL *ssl = NULL;
+
+stralloc clientcert = {0};
+stralloc tlsserverciphers = {0};
+#endif
 
 #define MAXHOPS 100
 unsigned int databytes = 0;
+unsigned int greetdelay = 0;
+unsigned int drop_pre_greet = 0;
+unsigned int mfchk = 0;
 int timeout = 1200;
+int rcptcounter = 0;
+int maxrcpt = -1;
+unsigned int spfbehavior = 0;
+int useauth = 0;
+int useauth_cl = 0;
+int useauth_cdb = 0;
+int auth_cdb_fd = -1;
+char *auth_cdb_file;
+static stralloc authcdb_vars = {0};
+int usecram = 0;
+unsigned int essl = 0;
+char unique[FMT_ULONG + FMT_ULONG + 3];
+static stralloc authin = {0};
+static stralloc user = {0};
+static stralloc pass = {0};
+static stralloc chal = {0};
+static stralloc slop = {0};
+char *hostname;
+char **childargs;
+substdio ssup;
+char upbuf[128];
+int authd = 0;
+unsigned int allow_insecure_auth = 0;
+unsigned int require_auth = 0;
+char pid_buf[FMT_ULONG];
+stralloc title = {0};
+int log_mail = 0;
+int log_rcpt = 0;
+unsigned long pw_expire = 0;
+char rcptcheck_err[1024];
+
+#ifdef TLS
+unsigned int force_tls = 0;
+unsigned int deny_tls = 0;
+int flagtimedout = 0;
+char *servercert = "control/servercert.pem";
+void sigalrm()
+{
+ flagtimedout = 1;
+}
+
+int ssl_timeoutread(timeout,fd,buf,n) int timeout; int fd; char *buf; int n;
+{
+ int r; int saveerrno;
+  if (flagtimedout) { errno = error_timeout; return -1; }
+  alarm(timeout);
+  if (ssl) {
+    while(((r = SSL_read(ssl,buf,n)) <= 0)
+          && (SSL_get_error(ssl, r) == SSL_ERROR_WANT_READ));
+  }else r = read(fd,buf,n);
+ saveerrno = errno;
+ alarm(0);
+ if (flagtimedout) { errno = error_timeout; return -1; }
+ errno = saveerrno;
+ return r;
+}
+
+int ssl_timeoutwrite(timeout,fd,buf,n) int timeout; int fd; char *buf; int n;
+{
+ int r; int saveerrno;
+ if (flagtimedout) { errno = error_timeout; return -1; }
+ alarm(timeout);
+ if (ssl) {
+    while(((r = SSL_write(ssl,buf,n)) <= 0)
+          && (SSL_get_error(ssl, r) == SSL_ERROR_WANT_WRITE));
+ }else r = write(fd,buf,n);
+ saveerrno = errno;
+ alarm(0);
+ if (flagtimedout) { errno = error_timeout; return -1; }
+ errno = saveerrno;
+ return r;
+}
+#endif
 
 int safewrite(fd,buf,len) int fd; char *buf; int len;
 {
   int r;
+#ifdef TLS
+  r = ssl_timeoutwrite(timeout,fd,buf,len);
+#else
   r = timeoutwrite(timeout,fd,buf,len);
+#endif
   if (r <= 0) _exit(1);
   return r;
 }
@@ -47,20 +155,59 @@
 void die_nomem() { out("421 out of memory (#4.3.0)\r\n"); flush(); _exit(1); }
 void die_control() { out("421 unable to read controls (#4.3.0)\r\n"); flush(); _exit(1); }
 void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); }
+void die_fork() { out("421 unable to fork (#4.3.0)\r\n"); flush(); _exit(1); }
+void die_rcpt() { out("421 unable to verify recipient (#4.3.0)\r\n"); flush(); _exit(1); }
+void die_rcpt2() { out("421 unable to execute recipient check (#4.3.0)\r\n"); flush(); _exit(1); }
 void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); }
-
-void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); }
+void die_cannot_auth() { out("421 REQUIRE_AUTH set without valid AUTH program.\r\n"); flush(); _exit(1); }
+void die_cannot_cram() { out("421 ALLOW_CRAM not available\r\n"); flush(); _exit(1); }
+void die_auth_cdb() { out("421 cannot read AUTH_CDB file\r\n"); flush(); _exit(1); }
+void die_pre_greet() { out("554 SMTP protocol violation\r\n"); flush(); _exit(1); }
+
+void err_bmf() { out("553 sorry, your envelope sender has been denied (#5.7.1)\r\n"); }
+void err_brt() { out("553 sorry, your envelope recipient has been denied (#5.7.1)\r\n"); }
+void err_bhelo() { out("553 sorry, your HELO host name has been denied (#5.7.1)\r\n"); }
+void err_hmf() { out("553 sorry, your envelope sender domain must exist (#5.7.1)\r\n"); }
+void err_smf() { out("451 DNS temporary failure (#4.3.0)\r\n"); }
 void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); }
+#ifdef TLS
+void err_nogwcert() { out("553 no valid cert for gatewaying (#5.7.1)\r\n"); }
+void err_tlsfirst() { out("503 STARTTLS first (#5.5.1)\r\n"); }
+void die_forcedenytls() { out("421 FORCE_TLS and DENY_TLS both found (#4.3.0)\r\n"); flush(); _exit(1); }
+#endif
 void err_unimpl() { out("502 unimplemented (#5.5.1)\r\n"); }
 void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); }
+void err_relay() { out("553 we don't relay (#5.7.1)\r\n"); }
 void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); }
+void err_seenmail() { out("503 only one MAIL command allowed (#5.5.1)\r\n"); }
 void err_wantrcpt() { out("503 RCPT first (#5.5.1)\r\n"); }
 void err_noop() { out("250 ok\r\n"); }
 void err_vrfy() { out("252 send some mail, i'll try my best\r\n"); }
 void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); }
+void err_badrcpt() { out("553 sorry, no mailbox here by that name. (#5.1.1)\r\n"); }
+
+int err_child() { out("454 oops, problem with child and I can't auth (#4.3.0)\r\n"); return -1; }
+int err_fork() { out("454 oops, child won't start and I can't auth (#4.3.0)\r\n"); return -1; }
+int err_pipe() { out("454 oops, unable to open pipe and I can't auth (#4.3.0)\r\n"); return -1; }
+int err_write() { out("454 oops, unable to write pipe and I can't auth (#4.3.0)\r\n"); return -1; }
+void err_authd() { out("503 you're already authenticated (#5.5.0)\r\n"); }
+void err_authmail() { out("503 no auth during mail transaction (#5.5.0)\r\n"); }
+void err_noauthavail() { out("503 auth not available (#5.3.3)\r\n"); }
+int err_noauthtype() { out("504 auth type not available (#5.5.1)\r\n"); return -1; }
+int err_authabrt() { out("501 auth exchange cancelled (#5.0.0)\r\n"); return -1; }
+int err_input() { out("501 malformed auth input (#5.5.4)\r\n"); return -1; }
+int err_authfirst() { out("503 AUTH first (#5.5.1)\r\n"); }
+int err_authmethod() { out("454 oops, unknown AUTH back-end (#4.3.0)\r\n"); return -1; }
 
+void err_vrt() { out("553 sorry, this recipient is not in my validrcptto list (#5.7.1)\r\n"); }
+void die_vrt() { out("421 too many invalid addresses, goodbye (#4.3.0)\r\n"); flush(); _exit(1); }
 
 stralloc greeting = {0};
+stralloc spflocal = {0};
+stralloc spfguess = {0};
+stralloc spfexp = {0};
+int spf_log = 0;
+int help_version = 0;
 
 void smtp_greet(code) char *code;
 {
@@ -69,7 +216,11 @@
 }
 void smtp_help()
 {
-  out("214 qmail home page: http://pobox.com/~djb/qmail.html\r\n");
+  if(help_version)
+    out("214-qmail home page: http://pobox.com/~djb/qmail.html\r\n"
+        "214 jms1 combined patch v7.10 http://qmail.jms1.net/patches/combined.shtml\r\n");
+  else
+    out("214 qmail home page: http://pobox.com/~djb/qmail.html\r\n");
 }
 void smtp_quit()
 {
@@ -81,6 +232,15 @@
 char *remoteinfo;
 char *local;
 char *relayclient;
+static char *rcptcheck[2] = { 0, 0 };
+#ifdef TLS
+char *tlsciphers;
+#endif
+
+void err_size() {
+  out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n");
+  strerr_warn4(title.s,"DATABYTES exceeded [",remoteip,"]",0);
+}
 
 stralloc helohost = {0};
 char *fakehelo; /* pointer into helohost, or 0 */
@@ -93,35 +253,159 @@
 
 int liphostok = 0;
 stralloc liphost = {0};
+
 int bmfok = 0;
 stralloc bmf = {0};
-struct constmap mapbmf;
+
+int bmfnrok = 0;
+stralloc bmfnr = {0};
+
+int brtok = 0;
+stralloc brt = {0};
+
+int brtnrok = 0;
+stralloc brtnr = {0};
+
+int bhelook = 0;
+stralloc bhelo = {0};
+
+int vrtfd = -1;
+int vrtcount = 0;
+int vrtlimit = 10;
+int vrtlog_do = 0;
+
+int relayrej = 0;
+
+void readenv()
+{
+  char *x;
+  unsigned long u;
+
+  x = env_get("MFCHECK");
+  if (x) { scan_ulong(x,&u); mfchk = u; }
+
+  x = env_get("MAXRCPT");
+  if (x) { scan_ulong(x,&u); maxrcpt = u; }
+
+  /* RFC 2821 section 4.5.3.1 "recipients buffer" */
+  if(maxrcpt<1) maxrcpt=-1;
+  else if(maxrcpt<100) maxrcpt=100 ;
+
+  x = env_get("DATABYTES");
+  if (x) { scan_ulong(x,&u); databytes = u; }
+  if (!(databytes + 1)) --databytes;
+
+  x = env_get("SPFBEHAVIOR");
+  if (x) { scan_ulong(x,&u); spfbehavior = u; }
+
+  x = env_get("VALIDRCPTTO_LIMIT");
+  if(x) { scan_ulong(x,&u); vrtlimit = (int) u; }
+
+  x = env_get("VALIDRCPTTO_LOG");
+  if(x) { scan_ulong(x,&u); vrtlog_do = (int) u; }
+
+  x = env_get("SPF_LOG");
+  if(x) { scan_ulong(x,&u); spf_log = (int) u; }
+
+  x = env_get("RELAYREJ");
+  if(x) { scan_ulong(x,&u); relayrej = (int) u; }
+
+  x = env_get("VALIDRCPTTO_CDB");
+  if(x) {
+    if (-1 != vrtfd) { close(vrtfd); vrtfd = -1; }
+    if(*x) {
+      vrtfd = open_read(x);
+      if (-1 == vrtfd) die_control();
+    }
+  }
+  else if (-1 != vrtfd) { close(vrtfd); vrtfd = -1; }
+
+  x = env_get("QMAILSMTPD_LOG_MAIL");
+  if(x) { scan_ulong(x,&u); log_mail = (int) u; }
+
+  x = env_get("QMAILSMTPD_LOG_RCPT");
+  if(x) { scan_ulong(x,&u); log_rcpt = (int) u; }
+
+  x = env_get("QMAILSMTPD_HELP_VERSION");
+  if(x) { scan_ulong(x,&u); help_version = (int) u; }
+
+  rcptcheck[0] = env_get("RCPTCHECK");
+}
+
+int logregex = 0;
+stralloc matchedregex = {0};
 
 void setup()
 {
   char *x;
   unsigned long u;
+#ifdef TLS
+  char *tlsciphers;
+#endif
  
   if (control_init() == -1) die_control();
   if (control_rldef(&greeting,"control/smtpgreeting",1,(char *) 0) != 1)
     die_control();
+  x=env_get("SMTPGREETING");
+  if(x) { if(!stralloc_copys(&greeting,x)) die_nomem(); }
   liphostok = control_rldef(&liphost,"control/localiphost",1,(char *) 0);
   if (liphostok == -1) die_control();
   if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control();
   if (timeout <= 0) timeout = 1;
+  if (control_readint(&maxrcpt,"control/maxrcpt") == -1) die_control();
 
   if (rcpthosts_init() == -1) die_control();
 
+  if (control_readint(&mfchk,"control/mfcheck") == -1) die_control();
+
   bmfok = control_readfile(&bmf,"control/badmailfrom",0);
   if (bmfok == -1) die_control();
-  if (bmfok)
-    if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem();
+
+  bmfnrok = control_readfile(&bmfnr,"control/badmailfromnorelay",0);
+  if (bmfnrok == -1) die_control();
+
+  brtok = control_readfile(&brt,"control/badrcptto",0);
+  if (brtok == -1) die_control();
+
+  brtnrok = control_readfile(&brtnr,"control/badrcpttonorelay",0);
+  if (brtnrok == -1) die_control();
+
+  bhelook = control_readfile(&bhelo,"control/badhelo",0);
+  if(bhelook == -1) die_control() ;
+  if(env_get("NOBADHELO")) bhelook = 0;
+
+  if(env_get("LOGREGEX")) logregex = 1;
+
+  auth_cdb_file = env_get("AUTH_CDB");
+  if(auth_cdb_file) {
+    if ( ! access(auth_cdb_file,R_OK) ) { useauth_cdb = 1; }
+    else die_auth_cdb() ;
+  }
+
+  if (useauth_cdb) {
+    auth_cdb_fd = open_read(auth_cdb_file);
+    if(-1 == auth_cdb_fd) die_control();
+  }
  
   if (control_readint(&databytes,"control/databytes") == -1) die_control();
-  x = env_get("DATABYTES");
-  if (x) { scan_ulong(x,&u); databytes = u; }
-  if (!(databytes + 1)) --databytes;
  
+  if (control_readint(&spfbehavior,"control/spfbehavior") == -1)
+    die_control();
+
+  if (control_readline(&spflocal,"control/spfrules") == -1) die_control();
+  if (spflocal.len && !stralloc_0(&spflocal)) die_nomem();
+  if (control_readline(&spfguess,"control/spfguess") == -1) die_control();
+  if (spfguess.len && !stralloc_0(&spfguess)) die_nomem();
+  if (control_rldef(&spfexp,"control/spfexp",0,SPF_DEFEXP) == -1)
+    die_control();
+  if (!stralloc_0(&spfexp)) die_nomem();
+
+  x = env_get("GREETDELAY");
+  if(x) { scan_ulong(x,&u); greetdelay = u; }
+
+  x = env_get("DROP_PRE_GREET");
+  if(x) { scan_ulong(x,&u); drop_pre_greet = u; }
+
   remoteip = env_get("TCPREMOTEIP");
   if (!remoteip) remoteip = "unknown";
   local = env_get("TCPLOCALHOST");
@@ -131,6 +415,20 @@
   if (!remotehost) remotehost = "unknown";
   remoteinfo = env_get("TCPREMOTEINFO");
   relayclient = env_get("RELAYCLIENT");
+#ifdef TLS
+  if (tlsciphers = env_get("TLSCIPHERS")){
+    if (!stralloc_copys(&tlsserverciphers,tlsciphers)) die_nomem();
+  }
+  else {
+    if (control_rldef(&tlsserverciphers,"control/tlsserverciphers",0,"DEFAULT") != 1)
+      die_control();
+  }
+  if (!stralloc_0(&tlsserverciphers)) die_nomem();
+
+  x=env_get("TLS_SERVER_CERT");
+  if(x) servercert=x;
+#endif
+  readenv();
   dohelo(remotehost);
 }
 
@@ -197,14 +495,213 @@
   return 1;
 }
 
-int bmfcheck()
+int sizelimit(arg) /* modified SIZELIMIT function by Erwin Hoffmann (tx Uwe Ohse) */
+char *arg;   
 {
+  int i;
+  long r;
+  char terminator;
+  unsigned long sizebytes = 0;
+
+  terminator = '>';
+  i = str_chr(arg,'<');
+  if (arg[i])
+    arg += i + 1;
+  else {
+    terminator = ' ';
+    arg += str_chr(arg,':');
+    if (*arg == ':') ++arg;
+    while (*arg == ' ') ++arg;
+  }
+
+  arg += str_chr(arg,terminator);
+  if (*arg && terminator == '>' ) ++arg; /* end of adddress */
+
+  while (*++arg) {
+    i = str_chr(arg,'=');
+    arg[i] = 0;
+    if (case_equals(arg,"SIZE")) {
+      arg += i;
+      while (*++arg && *arg > 47 && *arg < 58) {
+        sizebytes *= 10;
+        sizebytes += *arg - 48; }
+      r = databytes - sizebytes;
+      if (r < 0) return 0;
+    }
+    else
+      ++arg;
+  }
+  return 1;
+}
+
+int bmcheck(which) int which;
+{
+  int i = 0;
+  int j = 0;
+  int x = 0;
+  int negate = 0;
+  static stralloc bmb = {0};
+  static stralloc curregex = {0};
+
+  if (which == BMCHECK_BMF) {
+    if (!stralloc_copy(&bmb,&bmf)) die_nomem();
+  } else if (which == BMCHECK_BMFNR) {
+    if (!stralloc_copy(&bmb,&bmfnr)) die_nomem();
+  } else if (which == BMCHECK_BRT) {
+    if (!stralloc_copy(&bmb,&brt)) die_nomem();
+  } else if (which == BMCHECK_BRTNR) {
+    if (!stralloc_copy(&bmb,&brtnr)) die_nomem();
+  } else if (which == BMCHECK_BHELO) {
+    if (!stralloc_copy(&bmb,&bhelo)) die_nomem();
+  } else {
+    die_control();
+  }
+
+  while (j < bmb.len) {
+    i = j;
+    while ((bmb.s[i] != '\0') && (i < bmb.len)) i++;
+    if (bmb.s[j] == '!') {
+      negate = 1;
+      j++;
+    }
+    if (!stralloc_copyb(&curregex,bmb.s + j,(i - j))) die_nomem();
+    if (!stralloc_0(&curregex)) die_nomem();
+    if (which == BMCHECK_BHELO) {
+      x = matchregex(helohost.s, curregex.s);
+    } else {
+      x = matchregex(addr.s, curregex.s);
+    }
+    if ((negate) && (x == 0)) {
+      if (!stralloc_copyb(&matchedregex,bmb.s + j - 1,(i - j + 1))) die_nomem();
+      if (!stralloc_0(&matchedregex)) die_nomem();      
+      return 1;
+    }
+    if (!(negate) && (x > 0)) {
+      if (!stralloc_copyb(&matchedregex,bmb.s + j,(i - j))) die_nomem();
+      if (!stralloc_0(&matchedregex)) die_nomem();
+      return 1;
+    }
+    j = i + 1;
+    negate = 0;
+  }
+  return 0;
+}
+
+int mfcheck()
+{
+  stralloc sa = {0};
+  ipalloc ia = {0};
+  unsigned int random;
   int j;
-  if (!bmfok) return 0;
-  if (constmap(&mapbmf,addr.s,addr.len - 1)) return 1;
-  j = byte_rchr(addr.s,addr.len,'@');
-  if (j < addr.len)
-    if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) return 1;
+
+  if (str_equal(addr.s,"#@[]")) return 0;
+  if (!mfchk) return 0;
+  random = now() + (getpid() << 16);
+  j = byte_rchr(addr.s,addr.len,'@') + 1;
+  if (addr.len == 1) return 0;
+  if (j < addr.len) {
+    stralloc_copys(&sa, addr.s + j);
+    dns_init(0);
+    j = dns_mxip(&ia,&sa,random);
+    stralloc_0(&sa);
+    if (j < 0) {
+      if(mfchk>1) strerr_warn5(title.s,"MFCHECK fail [",remoteip,"] ",sa.s,0);
+      return j;
+    }
+    if(mfchk>2) strerr_warn5(title.s,"MFCHECK pass [",remoteip,"] ",sa.s,0);
+    return 0;
+  }
+  if(mfchk>1) strerr_warn5(title.s,"MFCHECK invalid [",remoteip,"] ",addr.s,0);
+  return DNS_HARD;
+}
+
+void vrtlog(l,a,b)
+int l;
+const char *a;
+const char *b;
+{
+  if (l <= vrtlog_do)
+    strerr_warn6(title.s,"validrcptto [",remoteip,"] ",a,b,0);
+}
+
+int vrtcheck()
+{
+  static char *rcptto = "RCPT TO: ";
+  static char *trying = "trying: ";
+  static char *found  = "found: ";
+  static char *reject = "reject: ";
+  char *f = 0;
+  int j,k,r;
+  uint32 dlen;
+  stralloc laddr = {0};
+
+  stralloc user = {0};
+  stralloc adom = {0};
+  stralloc utry = {0};
+  stralloc dval = {0};
+
+  if (-1 == vrtfd) return 1;
+
+  /* lowercase whatever we were sent */
+  if (!stralloc_copy(&laddr,&addr)) die_nomem() ;
+  case_lowerb(laddr.s,laddr.len);
+
+  if ( !log_rcpt ) vrtlog ( 1 , rcptto , laddr.s );
+
+  /* exact match? */
+  vrtlog ( 2 , trying , laddr.s );
+  r = cdb_seek(vrtfd,laddr.s,laddr.len-1,&dlen) ;
+  if (r>0) f=laddr.s ;
+  else
+  {
+    j = byte_rchr(laddr.s,laddr.len,'@');
+    if (j < laddr.len)
+    {
+      /* start "-default" search loop */
+      stralloc_copyb(&user,laddr.s,j) ;
+      stralloc_copyb(&adom,laddr.s+j,laddr.len-j-1);
+  
+      while(1)
+      {
+        k = byte_rchr(user.s,user.len,*auto_break);
+        if (k >= user.len) break ;
+  
+        user.len = k+1;
+        stralloc_cats(&user,"default");
+  
+        stralloc_copy(&utry,&user);
+        stralloc_cat (&utry,&adom);
+        stralloc_0(&utry);
+  
+        vrtlog ( 2 , trying , utry.s );
+        r = cdb_seek(vrtfd,utry.s,utry.len-1,&dlen);
+        if (r>0) { f=utry.s; break; }
+  
+        user.len = k ;
+      }
+
+      /* try "@domain" */
+      if(!f) {
+        vrtlog ( 2 , trying , laddr.s+j );
+        r = cdb_seek(vrtfd,laddr.s+j,laddr.len-j-1,&dlen) ;
+        if (r>0) f=laddr.s+j ;
+      }
+    }
+  }
+
+  if(f) {
+    if(dlen) {
+      if(!stralloc_ready(&dval,dlen)) die_nomem();
+      dval.len = read(vrtfd,dval.s,dlen);
+      if(dval.len>0) if(dval.s[0] == '-') {
+        vrtlog ( 1 , reject , f ) ;
+        return 0;
+      }
+    }
+    vrtlog ( 1 , found , f ) ;
+    return 1;
+  }
+
   return 0;
 }
 
@@ -216,21 +713,143 @@
   return r;
 }
 
+int addrrelay()
+{
+  int j;
+  if(relayrej) {
+    j = addr.len;
+    while(--j >= 0)
+      if (addr.s[j] == '@') break;
+    if (j < 0) j = addr.len;
+    while(--j >= 0) {
+      if (addr.s[j] == '@') return 1;
+      if (addr.s[j] == '%') return 1;
+      if (addr.s[j] == '!') return 1;
+    }
+  }
+  return 0;
+}
 
 int seenmail = 0;
 int flagbarf; /* defined if seenmail */
+int flagbarfbmf; /* defined if seenmail */
+int flagbarfbrt;
+int flagbarfbhelo;
+int flagbarfspf;
+stralloc spfbarfmsg = {0};
 stralloc mailfrom = {0};
 stralloc rcptto = {0};
 
+int addrvalid()
+{
+  int pid;
+  int wstat;
+  int pierr[2] ;
+  substdio ss;
+  char ssbuf[sizeof(rcptcheck_err)];
+  int len = 0 ;
+  char ch;
+
+  if (!rcptcheck[0]) return 1;
+  if (pipe(pierr) == -1) die_rcpt2();
+
+  switch(pid = fork()) {
+    case -1:
+      close(pierr[0]);
+      close(pierr[1]);
+      die_fork();
+    case 0:
+      if (!env_put2("SENDER",mailfrom.s)) die_nomem();
+      if (!env_put2("RECIPIENT",addr.s)) die_nomem();
+      if (!env_put2("HELO",helohost.s)) die_nomem();
+      if (!env_put2("USE_FD4","1")) die_nomem();
+      close(1);
+      dup2(2,1);
+      close(pierr[0]);
+      if (fd_move(4,pierr[1]) == -1) die_rcpt2();
+      execv(*rcptcheck,rcptcheck);
+      _exit(120);
+  }
+
+  close(pierr[1]);
+  if (wait_pid(&wstat,pid) == -1) die_rcpt2();
+  if (wait_crashed(wstat)) die_rcpt2();
+
+  substdio_fdbuf(&ss,read,pierr[0],ssbuf,sizeof(ssbuf));
+  while ( substdio_bget(&ss,&ch,1) && len < (sizeof(ssbuf)-3) )
+    rcptcheck_err[len++] = ch;
+  close(pierr[0]);
+
+  while (len&&((rcptcheck_err[len-1]=='\n')||(rcptcheck_err[len-1]=='\r')))
+    len -- ;
+  if (len) {
+    rcptcheck_err[len] = '\0';
+    strerr_warn3(title.s,"RCPTCHECK error: ",rcptcheck_err,0);
+    rcptcheck_err[len++] = '\r';
+    rcptcheck_err[len++] = '\n';
+  }
+  rcptcheck_err[len] = '\0';
+
+  switch(wait_exitcode(wstat)) {
+    case 100:
+      if (!len) {
+        len = str_copy(rcptcheck_err,"553 sorry, no mailbox here by that name. (#5.1.1)\r\n");
+        rcptcheck_err[len] = '\0' ;
+      }
+    case 111:
+      if (!len) {
+        len = str_copy(rcptcheck_err,"421 unable to verify recipient (#4.3.0)\r\n");
+        rcptcheck_err[len] = '\0' ;
+      }
+      return 0;
+    case 120: die_rcpt2();
+  }
+  return 1;
+}
+
 void smtp_helo(arg) char *arg;
 {
   smtp_greet("250 "); out("\r\n");
   seenmail = 0; dohelo(arg);
+  if (bhelook) flagbarfbhelo = bmcheck(BMCHECK_BHELO);
+}
+char size_buf[FMT_ULONG];
+void smtp_size()
+{
+  size_buf[fmt_ulong(size_buf,(unsigned long) databytes)] = 0;
+  out("\r\n250-SIZE "); out(size_buf);
 }
 void smtp_ehlo(arg) char *arg;
 {
-  smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n");
+  smtp_greet("250-");
+  if (useauth)
+  {
+    if ( essl || allow_insecure_auth )
+    {
+      if (usecram)
+      {
+        out("\r\n250-AUTH LOGIN CRAM-MD5 PLAIN");
+        out("\r\n250-AUTH=LOGIN CRAM-MD5 PLAIN");
+      }
+      else
+      {
+        out("\r\n250-AUTH LOGIN PLAIN");
+        out("\r\n250-AUTH=LOGIN PLAIN");
+      }
+    }
+    else if (usecram)
+    {
+        out("\r\n250-AUTH CRAM-MD5");
+        out("\r\n250-AUTH=CRAM-MD5");
+    }
+  }
+#ifdef TLS
+  if (!(essl||deny_tls)) out("\r\n250-STARTTLS");
+#endif
+  smtp_size();
+  out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n");
   seenmail = 0; dohelo(arg);
+  if (bhelook) flagbarfbhelo = bmcheck(BMCHECK_BHELO);
 }
 void smtp_rset()
 {
@@ -239,25 +858,219 @@
 }
 void smtp_mail(arg) char *arg;
 {
+  int r;
+  if (seenmail) { err_seenmail(); return; }
+#ifdef TLS
+  if (force_tls) if (!ssl) { err_tlsfirst(); return; }
+#endif
+  if (require_auth) if (!authd) { err_authfirst(); return; }
   if (!addrparse(arg)) { err_syntax(); return; }
-  flagbarf = bmfcheck();
+  if (databytes && !sizelimit(arg)) { err_size(); return; }
+  flagbarfbmf = 0; /* bmcheck is skipped for empty envelope senders */
+  if((bmfok) && (addr.len != 1)) flagbarfbmf = bmcheck(BMCHECK_BMF);
+  if ((!flagbarfbmf) && (bmfnrok) && (addr.len != 1) && (!relayclient) && (!authd)) {
+    flagbarfbmf = bmcheck(BMCHECK_BMFNR);
+  }
+  switch(mfcheck()) { /* make sure MAIL FROM domain has an MX record */
+    case DNS_HARD: err_hmf(); return;
+    case DNS_SOFT: err_smf(); return;
+    case DNS_MEM: die_nomem();
+  }
+  flagbarfspf = 0;
+  if (spfbehavior && !relayclient)
+  {
+    switch (r = spfcheck())
+    {
+      case SPF_OK: env_put2("SPFRESULT","pass"); break;
+      case SPF_NONE: env_put2("SPFRESULT","none"); break;
+      case SPF_UNKNOWN: env_put2("SPFRESULT","unknown"); break;
+      case SPF_NEUTRAL: env_put2("SPFRESULT","neutral"); break;
+      case SPF_SOFTFAIL: env_put2("SPFRESULT","softfail"); break;
+      case SPF_FAIL: env_put2("SPFRESULT","fail"); break;
+      case SPF_ERROR: env_put2("SPFRESULT","error"); break;
+    }
+    if (spf_log)
+    {
+      stralloc si = {0};
+      if (!spfinfo(&si)) die_nomem();
+      if (!stralloc_0(&si)) die_nomem();
+      strerr_warn3(title.s,"Received-SPF: ",si.s,0);
+    }
+    switch (r)
+    {
+      case SPF_NOMEM:
+        die_nomem();
+      case SPF_ERROR:
+        if (spfbehavior < 2) break ;
+        out ("451 SPF lookup failure (#4.3.0)\r\n");
+        return;
+      case SPF_NONE:
+      case SPF_UNKNOWN:
+        if (spfbehavior < 6) break ;
+      case SPF_NEUTRAL:
+        if (spfbehavior < 5) break ;
+      case SPF_SOFTFAIL:
+        if (spfbehavior < 4) break ;
+      case SPF_FAIL:
+        if (spfbehavior < 3) break ;
+        if (!spfexplanation(&spfbarfmsg)) die_nomem();
+        if (!stralloc_0(&spfbarfmsg)) die_nomem();
+        flagbarfspf = 1;
+    }
+  }
+  else
+    env_unset("SPFRESULT");
   seenmail = 1;
   if (!stralloc_copys(&rcptto,"")) die_nomem();
   if (!stralloc_copys(&mailfrom,addr.s)) die_nomem();
   if (!stralloc_0(&mailfrom)) die_nomem();
+  if (log_mail) { strerr_warn4(title.s,"MAIL FROM:<",mailfrom.s,">",0); }
+  rcptcounter = 0 ;
   out("250 ok\r\n");
 }
+
+void err_spf()
+{
+  int i,j;
+
+  for( i=0 ; i < spfbarfmsg.len ; i=j+1 ) {
+    j = byte_chr(spfbarfmsg.s + i,spfbarfmsg.len - i, '\n') + i;
+    if (j < spfbarfmsg.len){
+      out("550-");
+      spfbarfmsg.s[j] = 0;
+      out(spfbarfmsg.s);
+      spfbarfmsg.s[j] = '\n';
+    } else {
+      out("550 ");
+      out(spfbarfmsg.s);
+      out(" (#5.7.1)\r\n");
+    }
+  }
+}
+
+#ifdef TLS
+static int verify_cb(int ok, X509_STORE_CTX * ctx)
+{
+  return(1);
+}
+#endif
+
 void smtp_rcpt(arg) char *arg; {
+  rcptcounter++;
   if (!seenmail) { err_wantmail(); return; }
+  if (checkrcptcount() == 1) { err_syntax(); return; }
   if (!addrparse(arg)) { err_syntax(); return; }
-  if (flagbarf) { err_bmf(); return; }
+  if (addrrelay()) { err_relay(); return; }
+  if (log_rcpt) { strerr_warn4(title.s,"RCPT TO:<",addr.s,">",0); }
+  if (flagbarfbhelo) {
+    if (logregex) {
+      strerr_warn7(title.s,"badhelo: <",helohost.s,"> at ",remoteip," matches pattern: ",matchedregex.s,0);
+    } else {
+      strerr_warn5(title.s,"badhelo: <",helohost.s,"> at ",remoteip,0);
+    }
+    err_bhelo();
+    return;
+  }
+  if (flagbarfbmf) {
+    if (logregex) {
+      strerr_warn7(title.s,"badmailfrom: <",mailfrom.s,"> at ",remoteip," matches pattern: ",matchedregex.s,0);
+    } else {
+      strerr_warn5(title.s,"badmailfrom: <",mailfrom.s,"> at ",remoteip,0);
+    }
+    err_bmf();
+    return;
+  }
+  if (brtok) flagbarfbrt = bmcheck(BMCHECK_BRT);
+  if ((!flagbarfbrt) && (brtnrok) && (!relayclient) && (!authd)) {
+    flagbarfbrt = bmcheck(BMCHECK_BRTNR);
+  }
+  if (flagbarfbrt) {
+    if (logregex) {
+      strerr_warn7(title.s,"badrcptto: <",addr.s,"> at ",remoteip," matches pattern: ",matchedregex.s,0);
+    } else {
+      strerr_warn5(title.s,"badrcptto: <",addr.s,"> at ",remoteip,0);
+    }
+    err_brt();
+    return;
+  }
+
+  if (flagbarfspf) { err_spf(); return; }
   if (relayclient) {
     --addr.len;
     if (!stralloc_cats(&addr,relayclient)) die_nomem();
     if (!stralloc_0(&addr)) die_nomem();
   }
   else
+#ifndef TLS
     if (!addrallowed()) { err_nogateway(); return; }
+#else
+    if (!addrallowed())
+     {
+      if (ssl)
+      { STACK_OF(X509_NAME) *sk;
+        X509 *peercert;
+        stralloc tlsclients = {0};
+        struct constmap maptlsclients;
+        int r;
+
+        SSL_set_verify(ssl,
+                       SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE,
+                       verify_cb);
+        if ((sk = SSL_load_client_CA_file("control/clientca.pem")) == NULL)
+         { err_nogateway(); return; }
+        SSL_set_client_CA_list(ssl, sk);
+        if((control_readfile(&tlsclients,"control/tlsclients",0) != 1) ||
+           !constmap_init(&maptlsclients,tlsclients.s,tlsclients.len,0))
+          { err_nogateway(); return; }
+
+        SSL_renegotiate(ssl);
+        SSL_do_handshake(ssl);
+        ssl->state = SSL_ST_ACCEPT;
+        SSL_do_handshake(ssl);
+        if ((r = SSL_get_verify_result(ssl)) != X509_V_OK)
+         {out("553 no valid cert for gatewaying: ");
+          out(X509_verify_cert_error_string(r));
+          out(" (#5.7.1)\r\n");
+          return;
+         }
+
+        if (peercert = SSL_get_peer_certificate(ssl))
+         {char emailAddress[256];
+
+          X509_NAME_get_text_by_NID(X509_get_subject_name(
+                                     SSL_get_peer_certificate(ssl)),
+                                     NID_pkcs9_emailAddress, emailAddress, 256);
+          if (!stralloc_copys(&clientcert, emailAddress)) die_nomem();
+          if (!constmap(&maptlsclients,clientcert.s,clientcert.len))
+            { err_nogwcert(); return; }
+          relayclient = "";
+         }
+          else { err_nogwcert(); return; }
+       }
+      else { err_nogateway(); return; }
+     }
+#endif
+
+  if (!relayclient && !vrtcheck()) {
+    strerr_warn5(title.s,"validrcptto [",remoteip,"] not found: ",
+      addr.s,0);
+    if(vrtlimit && (++vrtcount >= vrtlimit)) {
+      strerr_warn4(title.s,"validrcptto [",remoteip,
+        "] excessive violations, hanging up",0);
+      die_vrt();
+    }
+    err_vrt();
+    return;
+  }
+
+  if (!addrvalid()) {
+    if (rcptcheck_err[0])
+      out (rcptcheck_err);
+    else
+      err_badrcpt();
+    return;
+  }
+
   if (!stralloc_cats(&rcptto,"T")) die_nomem();
   if (!stralloc_cats(&rcptto,addr.s)) die_nomem();
   if (!stralloc_0(&rcptto)) die_nomem();
@@ -269,7 +1082,11 @@
 {
   int r;
   flush();
+#ifdef TLS
+  r = ssl_timeoutread(timeout,fd,buf,len);
+#else
   r = timeoutread(timeout,fd,buf,len);
+#endif
   if (r == -1) if (errno == error_timeout) die_alarm();
   if (r <= 0) die_read();
   return r;
@@ -316,8 +1133,8 @@
         if (flagmaybex) if (pos == 7) ++*hops;
         if (pos < 2) if (ch != "\r\n"[pos]) flagmaybey = 0;
         if (flagmaybey) if (pos == 1) flaginheader = 0;
+        ++pos;
       }
-      ++pos;
       if (ch == '\n') { pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; }
     }
     switch(state) {
@@ -351,6 +1168,25 @@
   }
 }
 
+void spfreceived()
+{
+  stralloc sa = {0};
+  stralloc rcvd_spf = {0};
+
+  if (!spfbehavior || relayclient) return;
+
+  if (!stralloc_copys(&rcvd_spf, "Received-SPF: ")) die_nomem();
+  if (!spfinfo(&sa)) die_nomem();
+  if (!stralloc_cat(&rcvd_spf, &sa)) die_nomem();
+  if (!stralloc_append(&rcvd_spf, "\n")) die_nomem();
+  if (bytestooverflow) {
+    bytestooverflow -= rcvd_spf.len;
+    if (bytestooverflow <= 0) qmail_fail(&qqt);
+  }
+  qmail_put(&qqt,rcvd_spf.s,rcvd_spf.len);
+}
+
+
 char accept_buf[FMT_ULONG];
 void acceptmessage(qp) unsigned long qp;
 {
@@ -365,10 +1201,91 @@
   out("\r\n");
 }
 
+/* addvars() - find and add environment variables after encrypted password
+   in cdb data. if doit=0, only PASSWORD_EXPIRES is searched for. this way
+   expired passwords don't get their extra vars if password is expired. */
+void addvars(s,doit)
+char *s;
+int doit;
+{
+  char *n;
+  char *v;
+  int x;
+  int y;
+
+  n = s;
+
+  while (*n)
+  {
+    if (','==*n) n++ ;
+    x = str_chr(n,'=');
+    if (!n[x]) return ;
+    if (n[x+1]!='\"') return ;
+    v = n+x+2 ;
+    y = str_chr(v,'\"');
+    if (!v[y]) return ;
+    if(!str_diff(n,"PASSWORD_EXPIRES")) scan_ulong(v,&pw_expire);
+    if(doit) {
+      n[x]=0;
+      v[y]=0;
+      env_put2(n,v);
+    }
+    n = v+y+1;
+  }
+}
+
+void auth_fixenv()
+{
+  int i,f;
+  char *envi,*eq;
+  stralloc work = {0};
+
+  do
+  {
+    f=0;
+    for (i=0;envi=environ[i];++i)
+    {
+      if (!str_diffn("AUTH_SET_",envi,9))
+      {
+        stralloc_copys(&work,envi);
+        stralloc_0(&work);
+        eq = env_findeq(work.s);
+        *eq=0;
+        env_unset(work.s);
+        if(authd)
+        {
+          env_unset(work.s+9);
+          *eq='=';
+          env_put(work.s+9);
+        }
+        f=1;
+        break;
+      }
+      if (!str_diffn("AUTH_UNSET_",envi,11))
+      {
+        stralloc_copys(&work,envi);
+        stralloc_0(&work);
+        eq = env_findeq(work.s);
+        *eq=0;
+        env_unset(work.s);
+        if(authd) env_unset(work.s+11);
+        f=1;
+        break;
+      }
+    }
+  } while (f);
+
+  if(authcdb_vars.s)
+    addvars(authcdb_vars.s,1);
+}
+
 void smtp_data() {
   int hops;
   unsigned long qp;
   char *qqx;
+#ifdef TLS
+  stralloc protocolinfo = {0};
+#endif
  
   if (!seenmail) { err_wantmail(); return; }
   if (!rcptto.len) { err_wantrcpt(); return; }
@@ -377,8 +1294,23 @@
   if (qmail_open(&qqt) == -1) { err_qqt(); return; }
   qp = qmail_qp(&qqt);
   out("354 go ahead\r\n");
- 
+
+#ifdef TLS
+  if(ssl){
+   if (!stralloc_copys(&protocolinfo, SSL_CIPHER_get_name(SSL_get_current_cipher(ssl)))) die_nomem();
+   if (!stralloc_catb(&protocolinfo, " encrypted SMTP", 15)) die_nomem();
+   if (clientcert.len){
+     if (!stralloc_catb(&protocolinfo," cert ", 6)) die_nomem();
+     if (!stralloc_catb(&protocolinfo,clientcert.s, clientcert.len)) die_nomem();
+   }
+   if (!stralloc_0(&protocolinfo)) die_nomem();
+  } else if (!stralloc_copyb(&protocolinfo,"SMTP",5)) die_nomem();
+  received(&qqt,protocolinfo.s,local,remoteip,remotehost,remoteinfo,case_diffs(remotehost,helohost.s) ? helohost.s : 0);
+#else
   received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo);
+#endif
+
+  spfreceived();
   blast(&hops);
   hops = (hops >= MAXHOPS);
   if (hops) qmail_fail(&qqt);
@@ -388,34 +1320,443 @@
   qqx = qmail_close(&qqt);
   if (!*qqx) { acceptmessage(qp); return; }
   if (hops) { out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; }
-  if (databytes) if (!bytestooverflow) { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); return; }
-  if (*qqx == 'D') out("554 "); else out("451 ");
+  if (databytes) if (!bytestooverflow) { err_size(); return; }
+  if (*qqx == 'I') out("250 ok "); else if (*qqx == 'D') out("554 "); else out("451 ");
   out(qqx + 1);
   out("\r\n");
 }
+#ifdef TLS
+static RSA *tmp_rsa_cb(ssl,export,keylength) SSL *ssl; int export; int keylength; 
+{
+  RSA* rsa;
+  BIO* in;
+
+  if (!export || keylength == 512)
+   if (in=BIO_new(BIO_s_file_internal()))
+    if (BIO_read_filename(in,"control/rsa512.pem") > 0)
+     if (rsa=PEM_read_bio_RSAPrivateKey(in,NULL,NULL,NULL))
+      return rsa;
+  return (RSA_generate_key(export?keylength:512,RSA_F4,NULL,NULL));
+}
+
+void smtp_tls(arg) char *arg;
+{
+  SSL_CTX *ctx;
+
+  if (*arg)
+   {out("501 Syntax error (no parameters allowed) (#5.5.4)\r\n");
+    return;}
+
+  if (ssl)
+   {out("454 TLS not available: TLS already active (4.3.0)\r\n");
+    return;}
+
+  if (essl)
+   {out("454 TLS not available: already SSL secured (4.3.0)\r\n");
+    return;}
+
+  if (deny_tls)
+   {out("454 TLS not available (4.3.0)\r\n");
+    return;}
+
+  SSL_library_init();
+  if(!(ctx=SSL_CTX_new(SSLv23_server_method())))
+   {out("454 TLS not available: unable to initialize ctx (#4.3.0)\r\n");
+    return;}
+  if(!SSL_CTX_use_RSAPrivateKey_file(ctx, servercert, SSL_FILETYPE_PEM))
+   {out("454 TLS not available: missing RSA private key (#4.3.0)\r\n");
+    return;}
+  if(!SSL_CTX_use_certificate_chain_file(ctx, servercert))
+   {out("454 TLS not available: missing certificate (#4.3.0)\r\n");
+    return;}
+  SSL_CTX_set_tmp_rsa_callback(ctx, tmp_rsa_cb);
+  SSL_CTX_set_cipher_list(ctx,tlsserverciphers.s);
+  SSL_CTX_load_verify_locations(ctx, "control/clientca.pem",NULL);
+  SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, verify_cb);
+
+  out("220 ready for tls\r\n"); flush();
+  essl=1;
+
+  if(!(ssl=SSL_new(ctx))) die_read();
+  SSL_set_rfd(ssl,0);
+  SSL_set_wfd(ssl,1);
+  if(SSL_accept(ssl)<=0) die_read();
+  substdio_fdbuf(&ssout,SSL_write,ssl,ssoutbuf,sizeof(ssoutbuf));
+
+  remotehost = env_get("TCPREMOTEHOST");
+  if (!remotehost) remotehost = "unknown";
+  dohelo(remotehost);
+}
+#endif
+
+
+int authgetl(void) {
+  int i;
+
+  if (!stralloc_copys(&authin, "")) die_nomem();
+
+  for (;;) {
+    if (!stralloc_readyplus(&authin,1)) die_nomem(); /* XXX */
+    i = substdio_get(&ssin,authin.s + authin.len,1);
+    if (i != 1) die_read();
+    if (authin.s[authin.len] == '\n') break;
+    ++authin.len;
+  }
+
+  if (authin.len > 0) if (authin.s[authin.len - 1] == '\r') --authin.len;
+  authin.s[authin.len] = 0;
+
+  if (*authin.s == '*' && *(authin.s + 1) == 0) { return err_authabrt(); }
+  if (authin.len == 0) { return err_input(); }
+  return authin.len;
+}
+
+int authenticate_cl(void)
+{
+  int child;
+  int wstat;
+  int pi[2];
+
+  if (!stralloc_0(&user)) die_nomem();
+  if (!stralloc_0(&pass)) die_nomem();
+  if (!stralloc_0(&chal)) die_nomem();
+
+  if (pipe(pi) == -1) return err_pipe();
+  switch(child = fork()) {
+    case -1:
+      return err_fork();
+    case 0:
+      close(pi[1]);
+      if(fd_copy(3,pi[0]) == -1) return err_pipe();
+      sig_pipedefault();
+      execvp(*childargs, childargs);
+      _exit(1);
+  }
+  close(pi[0]);
+
+  substdio_fdbuf(&ssup,write,pi[1],upbuf,sizeof upbuf);
+  if (substdio_put(&ssup,user.s,user.len) == -1) return err_write();
+  if (substdio_put(&ssup,pass.s,pass.len) == -1) return err_write();
+  if (substdio_put(&ssup,chal.s,chal.len) == -1) return err_write();
+  if (substdio_flush(&ssup) == -1) return err_write();
+
+  close(pi[1]);
+  byte_zero(pass.s,pass.len);
+  byte_zero(upbuf,sizeof upbuf);
+  if (wait_pid(&wstat,child) == -1) return err_child();
+  if (wait_crashed(wstat)) return err_child();
+  if (wait_exitcode(wstat)) {
+    strerr_warn5(title.s,"AUTH failed [",remoteip,"] ",user.s,0);
+    sleep(5);
+    return 1; /* no */
+  }
+  strerr_warn5(title.s,"AUTH successful [",remoteip,"] ",user.s,0);
+  return 0; /* yes */
+}
+
+int authenticate_cdb(void)
+{
+  int r,x;
+  uint32 dlen ;
+  stralloc epw = {0} ;
+  char *cr ;
+
+  if (!stralloc_0(&user)) die_nomem();
+  if (!stralloc_0(&pass)) die_nomem();
+
+  r = cdb_seek(auth_cdb_fd,user.s,user.len-1,&dlen);
+  if (r<=0) {
+    strerr_warn5(title.s,"AUTH failed (unknown user) [",
+      remoteip,"] ",user.s,0);
+    return 1 ;
+  }
+
+  if (!stralloc_ready(&epw,dlen+1)) die_nomem() ;
+  epw.len = dlen ;
+  if (-1 == cdb_bread(auth_cdb_fd,epw.s,epw.len)) die_auth_cdb() ;
+  if (!stralloc_0(&epw)) die_nomem();
+
+  x = str_chr(epw.s,',') ;
+  epw.s[x] = 0 ;
+
+  if ( str_diff ( crypt ( pass.s , epw.s ) , epw.s ) ) {
+    strerr_warn5(title.s,"AUTH failed (bad password) [",
+      remoteip,"] ",user.s,0);
+    return 1 ;
+  }
+
+  if ( x < dlen ) {
+    stralloc_copyb(&authcdb_vars,epw.s+x+1,dlen-x-1);
+    stralloc_0(&authcdb_vars);
+    addvars(authcdb_vars.s,0);
+    if(pw_expire && pw_expire <= time(0)) {
+      strerr_warn5(title.s,"AUTH failed (password expired) [",
+        remoteip,"] ",user.s,0);
+      return 1 ;
+    }
+  }
+
+  strerr_warn5(title.s,"AUTH successful [",remoteip,"] ",user.s,0);
+  return 0 ;
+}
+
+int authenticate(void)
+{
+  if(useauth_cdb) return authenticate_cdb();
+  if(useauth_cl)  return authenticate_cl();
+  /* future auth back-end modules here */
+  return err_authmethod();
+}
+
+int auth_login(arg) char *arg;
+{
+  int r;
+
+  if ( ! ( essl || allow_insecure_auth ) ) return err_noauthtype() ;
+
+  if (*arg) {
+    if (r = b64decode(arg,str_len(arg),&user) == 1) return err_input();
+  }
+  else {
+    out("334 VXNlcm5hbWU6\r\n"); flush(); /* Username: */
+    if (authgetl() < 0) return -1;
+    if (r = b64decode(authin.s,authin.len,&user) == 1) return err_input();
+  }
+  if (r == -1) die_nomem();
+
+  out("334 UGFzc3dvcmQ6\r\n"); flush(); /* Password: */
+
+  if (authgetl() < 0) return -1;
+  if (r = b64decode(authin.s,authin.len,&pass) == 1) return err_input();
+  if (r == -1) die_nomem();
+
+  if (!user.len || !pass.len) return err_input();
+  return authenticate();  
+}
+
+int auth_plain(arg) char *arg;
+{
+  int r, id = 0;
+
+  if ( ! ( essl || allow_insecure_auth ) ) return err_noauthtype() ;
+
+  if (*arg) {
+    if (r = b64decode(arg,str_len(arg),&slop) == 1) return err_input();
+  }
+  else {
+    out("334 \r\n"); flush();
+    if (authgetl() < 0) return -1;
+    if (r = b64decode(authin.s,authin.len,&slop) == 1) return err_input();
+  }
+  if (r == -1 || !stralloc_0(&slop)) die_nomem();
+  while (slop.s[id]) id++; /* ignore authorize-id */
+
+  if (slop.len > id + 1)
+    if (!stralloc_copys(&user,slop.s + id + 1)) die_nomem();
+  if (slop.len > id + user.len + 2)
+    if (!stralloc_copys(&pass,slop.s + id + user.len + 2)) die_nomem();
+
+  if (!user.len || !pass.len) return err_input();
+  return authenticate();
+}
+
+int auth_cram()
+{
+  int i, r;
+  char *s;
+
+  if ( ! usecram ) return err_noauthtype();
+
+  s = unique;
+  s += fmt_uint(s,getpid());
+  *s++ = '.';
+  s += fmt_ulong(s,(unsigned long) now());
+  *s++ = '@';
+  *s++ = 0;
+
+  if (!stralloc_copys(&chal,"<")) die_nomem();
+  if (!stralloc_cats(&chal,unique)) die_nomem();
+  if (!stralloc_cats(&chal,hostname)) die_nomem();
+  if (!stralloc_cats(&chal,">")) die_nomem();
+  if (b64encode(&chal,&slop) < 0) die_nomem();
+  if (!stralloc_0(&slop)) die_nomem();
+
+  out("334 ");
+  out(slop.s);
+  out("\r\n");
+  flush();
+
+  if (authgetl() < 0) return -1;
+  if (r = b64decode(authin.s,authin.len,&slop) == 1) return err_input();
+  if (r == -1 || !stralloc_0(&slop)) die_nomem();
+
+  i = str_chr(slop.s,' ');
+  s = slop.s + i;
+  while (*s == ' ') ++s;
+  slop.s[i] = 0;
+  if (!stralloc_copys(&user,slop.s)) die_nomem();
+  if (!stralloc_copys(&pass,s)) die_nomem();
+
+  if (!user.len || !pass.len) return err_input();
+  return authenticate();
+}
+
+struct authcmd {
+  char *text;
+  int (*fun)();
+} authcmds[] = {
+  { "login", auth_login }
+, { "plain", auth_plain }
+, { "cram-md5", auth_cram }
+, { 0, err_noauthtype }
+};
+
+void smtp_auth(arg)
+char *arg;
+{
+  int i;
+  char *cmd = arg;
+
+  if (!useauth) { err_noauthavail(); return; }
+  if (authd) { err_authd(); return; }
+  if (seenmail) { err_authmail(); return; }
+
+  if (!stralloc_copys(&user,"")) die_nomem();
+  if (!stralloc_copys(&pass,"")) die_nomem();
+  if (!stralloc_copys(&chal,"")) die_nomem();
+  if (!stralloc_copys(&authcdb_vars,"")) die_nomem();
+  pw_expire = 0 ;
+
+  i = str_chr(cmd,' ');   
+  arg = cmd + i;
+  while (*arg == ' ') ++arg;
+  cmd[i] = 0;
+
+  for (i = 0;authcmds[i].text;++i)
+    if (case_equals(authcmds[i].text,cmd)) break;
+
+  switch (authcmds[i].fun(arg)) {
+    case 0:
+      authd = 1;
+      relayclient = "";
+      auth_fixenv();
+      readenv();
+      remoteinfo = user.s;
+      if (!env_unset("TCPREMOTEINFO")) die_read();
+      if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem();
+      if (!env_unset("SMTP_AUTH_USER")) die_read();
+      if (!env_put2("SMTP_AUTH_USER",remoteinfo)) die_nomem();
+      out("235 ok, go ahead (#2.0.0)\r\n");
+      break;
+    case 1:
+      out("535 authorization failed (#5.7.0)\r\n");
+  }
+}
 
 struct commands smtpcommands[] = {
   { "rcpt", smtp_rcpt, 0 }
 , { "mail", smtp_mail, 0 }
 , { "data", smtp_data, flush }
+, { "auth", smtp_auth, flush }
 , { "quit", smtp_quit, flush }
 , { "helo", smtp_helo, flush }
 , { "ehlo", smtp_ehlo, flush }
 , { "rset", smtp_rset, 0 }
 , { "help", smtp_help, flush }
+#ifdef TLS
+, { "starttls", smtp_tls, flush }
+#endif
 , { "noop", err_noop, flush }
 , { "vrfy", err_vrfy, flush }
 , { 0, err_unimpl, flush }
 } ;
 
-void main()
+void main(argc,argv)
+int argc;
+char **argv;
 {
+  char *x ;
+  unsigned long u ;
+
+  if (argc>3)
+  {
+    hostname = argv[1];
+    childargs = argv + 2;
+    useauth_cl = 1;
+  }
+
+  pid_buf[fmt_ulong(pid_buf,getpid())]=0;
+  if (!stralloc_copys(&title,"qmail-smtpd[")) die_nomem();
+  if (!stralloc_cats(&title,pid_buf)) die_nomem();
+  if (!stralloc_cats(&title,"]: ")) die_nomem();
+  if (!stralloc_0(&title)) die_nomem();
+
+#ifdef TLS
+  sig_alarmcatch(sigalrm);
+#endif
   sig_pipeignore();
   if (chdir(auto_qmail) == -1) die_control();
+
   setup();
+
+#ifdef TLS
+  if(access(servercert,R_OK)) { deny_tls = 1; }
+  x = env_get("DENY_TLS");
+  if(x) { scan_ulong(x,&u); if (u>0) deny_tls = 1; }
+  x = env_get("FORCE_TLS");
+  if(x) { scan_ulong(x,&u); if (u>0) force_tls = 1; }
+#endif
+
+  x = env_get("SSL");
+  if(x) { scan_ulong(x,&u); if (u>0) essl = 1; }
+#ifdef TLS
+  if (essl) { force_tls = 0; }
+  if (force_tls && deny_tls) die_forcedenytls() ;
+#endif
+
+  x = env_get("ALLOW_INSECURE_AUTH");
+  if(x) { scan_ulong(x,&u); if (u>0) allow_insecure_auth = 1; }
+
+  x = env_get("REQUIRE_AUTH");
+  if(x) { scan_ulong(x,&u); if (u>0) require_auth = 1; }
+
+  if ( useauth_cl || useauth_cdb ) { useauth = 1 ; }
+
+  if (require_auth && (!useauth)) die_cannot_auth() ;
+
+  if (useauth)
+  {
+    x = env_get("ALLOW_CRAM");
+    if(x) { scan_ulong(x,&u); usecram = (int) u; }
+    if(useauth_cdb && usecram) die_cannot_cram() ;
+  }
+
   if (ipme_init() != 1) die_ipme();
+  if (greetdelay||drop_pre_greet) {
+    x = timeoutread(greetdelay?greetdelay:1,0,ssinbuf,sizeof ssinbuf);
+    if(-1 == x) {
+      if(errno != error_timeout) strerr_die4sys(1,
+        title.s,"before greeting: [", remoteip, "] ");
+    } else if ( 0 == x ) {
+      strerr_die4x(1,title.s,"before greeting: [", remoteip,
+        "] client disconnected");
+    } else if(drop_pre_greet) {
+      strerr_warn4(title.s,"before greeting: [", remoteip,
+        "] client sent data",0);
+      die_pre_greet();
+    }
+  }
   smtp_greet("220 ");
   out(" ESMTP\r\n");
   if (commands(&ssin,&smtpcommands) == 0) die_read();
   die_nomem();
 }
+
+int checkrcptcount() {
+  if (maxrcpt == -1) { return 0;}
+  else if (rcptcounter > maxrcpt ) {
+    strerr_warn4(title.s,"MAXRCPT fail [",remoteip,"]",0);
+    return 1;
+  }
+  return 0;
+}
diff -ruN qmail-1.03-factory/qmail-start.c qmail-1.03-7.10/qmail-start.c
--- qmail-1.03-factory/qmail-start.c	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/qmail-start.c	2010-02-14 22:53:32.000000000 -0500
@@ -8,6 +8,9 @@
 char *(qcargs[]) = { "qmail-clean", 0 };
 char *(qlargs[]) = { "qmail-lspawn", "./Mailbox", 0 };
 char *(qrargs[]) = { "qmail-rspawn", 0 };
+#ifdef EXTERNAL_TODO
+char *(qtargs[]) = { "qmail-todo", 0};
+#endif
 
 void die() { _exit(111); }
 
@@ -18,13 +21,28 @@
 int pi4[2];
 int pi5[2];
 int pi6[2];
-
-void close23456() { close(2); close(3); close(4); close(5); close(6); }
+#ifdef EXTERNAL_TODO
+int pi7[2];
+int pi8[2];
+int pi9[2];
+int pi10[2];
+#endif
+
+void close23456() { 
+  close(2); close(3); close(4); close(5); close(6); 
+#ifdef EXTERNAL_TODO
+  close(7); close(8);
+#endif
+}
 
 void closepipes() {
   close(pi1[0]); close(pi1[1]); close(pi2[0]); close(pi2[1]);
   close(pi3[0]); close(pi3[1]); close(pi4[0]); close(pi4[1]);
   close(pi5[0]); close(pi5[1]); close(pi6[0]); close(pi6[1]);
+#ifdef EXTERNAL_TODO
+  close(pi7[0]); close(pi7[1]); close(pi8[0]); close(pi8[1]);
+	close(pi9[0]); close(pi9[1]); close(pi10[0]); close(pi10[1]);
+#endif
 }
 
 void main(argc,argv)
@@ -40,6 +58,10 @@
   if (fd_copy(4,0) == -1) die();
   if (fd_copy(5,0) == -1) die();
   if (fd_copy(6,0) == -1) die();
+#ifdef EXTERNAL_TODO
+  if (fd_copy(7,0) == -1) die();
+  if (fd_copy(8,0) == -1) die();
+#endif
 
   if (argv[1]) {
     qlargs[1] = argv[1];
@@ -70,6 +92,12 @@
   if (pipe(pi4) == -1) die();
   if (pipe(pi5) == -1) die();
   if (pipe(pi6) == -1) die();
+#ifdef EXTERNAL_TODO
+  if (pipe(pi7) == -1) die();
+  if (pipe(pi8) == -1) die();
+  if (pipe(pi9) == -1) die();
+  if (pipe(pi10) == -1) die();
+#endif
  
   switch(fork()) {
     case -1: die();
@@ -105,6 +133,34 @@
       execvp(*qcargs,qcargs);
       die();
   }
+
+#ifdef EXTERNAL_TODO
+  switch(fork()) {
+    case -1: die();
+    case 0:
+      if (prot_uid(auto_uids) == -1) die();
+      if (fd_copy(0,pi7[0]) == -1) die();
+      if (fd_copy(1,pi8[1]) == -1) die();
+      close23456();
+      if (fd_copy(2,pi9[1]) == -1) die();
+      if (fd_copy(3,pi10[0]) == -1) die();
+      closepipes();
+      execvp(*qtargs,qtargs);
+      die();
+  }
+
+  switch(fork()) {
+    case -1: die();
+    case 0:
+      if (prot_uid(auto_uidq) == -1) die();
+      if (fd_copy(0,pi9[0]) == -1) die();
+      if (fd_copy(1,pi10[1]) == -1) die();
+      close23456();
+      closepipes();
+      execvp(*qcargs,qcargs);
+      die();
+  }
+#endif
  
   if (prot_uid(auto_uids) == -1) die();
   if (fd_copy(0,1) == -1) die();
@@ -114,6 +170,10 @@
   if (fd_copy(4,pi4[0]) == -1) die();
   if (fd_copy(5,pi5[1]) == -1) die();
   if (fd_copy(6,pi6[0]) == -1) die();
+#ifdef EXTERNAL_TODO
+  if (fd_copy(7,pi7[1]) == -1) die();
+  if (fd_copy(8,pi8[0]) == -1) die();
+#endif
   closepipes();
   execvp(*qsargs,qsargs);
   die();
diff -ruN qmail-1.03-factory/qmail-todo.c qmail-1.03-7.10/qmail-todo.c
--- qmail-1.03-factory/qmail-todo.c	1969-12-31 19:00:00.000000000 -0500
+++ qmail-1.03-7.10/qmail-todo.c	2010-02-14 22:53:32.000000000 -0500
@@ -0,0 +1,688 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "alloc.h"
+#include "auto_qmail.h"
+#include "byte.h"
+#include "constmap.h"
+#include "control.h"
+#include "direntry.h"
+#include "error.h"
+#include "exit.h"
+#include "fmt.h"
+#include "fmtqfn.h"
+#include "getln.h"
+#include "open.h"
+#include "ndelay.h"
+#include "now.h"
+#include "readsubdir.h"
+#include "readwrite.h"
+#include "scan.h"
+#include "select.h"
+#include "str.h"
+#include "stralloc.h"
+#include "substdio.h"
+#include "trigger.h"
+
+/* critical timing feature #1: if not triggered, do not busy-loop */
+/* critical timing feature #2: if triggered, respond within fixed time */
+/* important timing feature: when triggered, respond instantly */
+#define SLEEP_TODO 1500 /* check todo/ every 25 minutes in any case */
+#define SLEEP_FUZZ 1 /* slop a bit on sleeps to avoid zeno effect */
+#define SLEEP_FOREVER 86400 /* absolute maximum time spent in select() */
+#define SLEEP_SYSFAIL 123
+
+stralloc percenthack = {0};
+struct constmap mappercenthack;
+stralloc locals = {0};
+struct constmap maplocals;
+stralloc vdoms = {0};
+struct constmap mapvdoms;
+stralloc envnoathost = {0};
+
+char strnum[FMT_ULONG];
+
+/* XXX not good, if qmail-send.c changes this has to be updated */
+#define CHANNELS 2
+char *chanaddr[CHANNELS] = { "local/", "remote/" };
+
+datetime_sec recent;
+
+void log1(char *x);
+void log3(char* x, char* y, char* z);
+
+int flagstopasap = 0;
+void sigterm(void)
+{
+  if (flagstopasap == 0)
+    log1("status: qmail-todo stop processing asap\n");
+  flagstopasap = 1;
+}
+
+int flagreadasap = 0; void sighup(void) { flagreadasap = 1; }
+int flagsendalive = 1; void senddied(void) { flagsendalive = 0; }
+
+void nomem() { log1("alert: out of memory, sleeping...\n"); sleep(10); }
+void pausedir(dir) char *dir;
+{ log3("alert: unable to opendir ",dir,", sleeping...\n"); sleep(10); }
+
+void cleandied()
+{ 
+  log1("alert: qmail-todo: oh no! lost qmail-clean connection! dying...\n");
+  flagstopasap = 1;
+}
+
+
+/* this file is not so long ------------------------------------- FILENAMES */
+
+stralloc fn = {0};
+
+void fnmake_init(void)
+{
+ while (!stralloc_ready(&fn,FMTQFN)) nomem();
+}
+
+void fnmake_info(unsigned long id) { fn.len = fmtqfn(fn.s,"info/",id,1); }
+void fnmake_todo(unsigned long id) { fn.len = fmtqfn(fn.s,"todo/",id,0); }
+void fnmake_mess(unsigned long id) { fn.len = fmtqfn(fn.s,"mess/",id,1); }
+void fnmake_chanaddr(unsigned long id, int c)
+{ fn.len = fmtqfn(fn.s,chanaddr[c],id,1); }
+
+
+/* this file is not so long ------------------------------------- REWRITING */
+
+stralloc rwline = {0};
+
+/* 1 if by land, 2 if by sea, 0 if out of memory. not allowed to barf. */
+/* may trash recip. must set up rwline, between a T and a \0. */
+int rewrite(char *recip)
+{
+  int i;
+  int j;
+  char *x;
+  static stralloc addr = {0};
+  int at;
+
+  if (!stralloc_copys(&rwline,"T")) return 0;
+  if (!stralloc_copys(&addr,recip)) return 0;
+
+  i = byte_rchr(addr.s,addr.len,'@');
+  if (i == addr.len) {
+    if (!stralloc_cats(&addr,"@")) return 0;
+    if (!stralloc_cat(&addr,&envnoathost)) return 0;
+  }
+
+  while (constmap(&mappercenthack,addr.s + i + 1,addr.len - i - 1)) {
+    j = byte_rchr(addr.s,i,'%');
+    if (j == i) break;
+    addr.len = i;
+    i = j;
+    addr.s[i] = '@';
+  }
+
+  at = byte_rchr(addr.s,addr.len,'@');
+
+  if (constmap(&maplocals,addr.s + at + 1,addr.len - at - 1)) {
+    if (!stralloc_cat(&rwline,&addr)) return 0;
+    if (!stralloc_0(&rwline)) return 0;
+    return 1;
+  }
+
+  for (i = 0;i <= addr.len;++i)
+    if (!i || (i == at + 1) || (i == addr.len) || ((i > at) && (addr.s[i] == '.')))
+      if (x = constmap(&mapvdoms,addr.s + i,addr.len - i)) {
+        if (!*x) break;
+        if (!stralloc_cats(&rwline,x)) return 0;
+        if (!stralloc_cats(&rwline,"-")) return 0;
+        if (!stralloc_cat(&rwline,&addr)) return 0;
+        if (!stralloc_0(&rwline)) return 0;
+        return 1;
+      }
+ 
+  if (!stralloc_cat(&rwline,&addr)) return 0;
+  if (!stralloc_0(&rwline)) return 0;
+  return 2;
+}
+
+/* this file is not so long --------------------------------- COMMUNICATION */
+
+substdio sstoqc; char sstoqcbuf[1024];
+substdio ssfromqc; char ssfromqcbuf[1024];
+stralloc comm_buf = {0};
+int comm_pos;
+int fdout = -1;
+int fdin = -1;
+
+void comm_init(void)
+{
+ substdio_fdbuf(&sstoqc,write,2,sstoqcbuf,sizeof(sstoqcbuf));
+ substdio_fdbuf(&ssfromqc,read,3,ssfromqcbuf,sizeof(ssfromqcbuf));
+
+ fdout = 1; /* stdout */
+ fdin = 0;  /* stdin */
+ if (ndelay_on(fdout) == -1)
+ /* this is so stupid: NDELAY semantics should be default on write */
+   senddied(); /* drastic, but better than risking deadlock */
+
+ while (!stralloc_ready(&comm_buf,1024)) nomem();
+}
+
+int comm_canwrite(void)
+{
+ /* XXX: could allow a bigger buffer; say 10 recipients */
+ /* XXX: returns true if there is something in the buffer */
+ if (!flagsendalive) return 0;
+ if (comm_buf.s && comm_buf.len) return 1;
+ return 0;
+}
+
+void log1(char* x)
+{
+  int pos;
+  
+  pos = comm_buf.len;
+  if (!stralloc_cats(&comm_buf,"L")) goto fail;
+  if (!stralloc_cats(&comm_buf,x)) goto fail;
+  if (!stralloc_0(&comm_buf)) goto fail;
+  return;
+  
+fail:
+  /* either all or nothing */
+  comm_buf.len = pos;
+}
+
+void log3(char* x, char *y, char *z)
+{
+  int pos;
+  
+  pos = comm_buf.len;
+  if (!stralloc_cats(&comm_buf,"L")) goto fail;
+  if (!stralloc_cats(&comm_buf,x)) goto fail;
+  if (!stralloc_cats(&comm_buf,y)) goto fail;
+  if (!stralloc_cats(&comm_buf,z)) goto fail;
+  if (!stralloc_0(&comm_buf)) goto fail;
+  return;
+  
+fail:
+  /* either all or nothing */
+  comm_buf.len = pos;
+}
+
+void comm_write(unsigned long id, int local, int remote)
+{
+  int pos;
+  char *s;
+  
+  if(local && remote) s="B";
+  else if(local) s="L";
+  else if(remote) s="R";
+  else s="X";
+  
+  pos = comm_buf.len;
+  strnum[fmt_ulong(strnum,id)] = 0;
+  if (!stralloc_cats(&comm_buf,"D")) goto fail;
+  if (!stralloc_cats(&comm_buf,s)) goto fail;
+  if (!stralloc_cats(&comm_buf,strnum)) goto fail;
+  if (!stralloc_0(&comm_buf)) goto fail;
+  return;
+  
+fail:
+  /* either all or nothing */
+  comm_buf.len = pos;
+}
+
+static int issafe(char ch)
+{
+ if (ch == '%') return 0; /* general principle: allman's code is crap */
+ if (ch < 33) return 0;
+ if (ch > 126) return 0;
+ return 1;
+}
+
+void comm_info(unsigned long id, unsigned long size, char* from, unsigned long pid, unsigned long uid)
+{
+  int pos;
+  int i;
+  
+  pos = comm_buf.len;
+  if (!stralloc_cats(&comm_buf,"Linfo msg ")) goto fail;
+  strnum[fmt_ulong(strnum,id)] = 0;
+  if (!stralloc_cats(&comm_buf,strnum)) goto fail;
+  if (!stralloc_cats(&comm_buf,": bytes ")) goto fail;
+  strnum[fmt_ulong(strnum,size)] = 0;
+  if (!stralloc_cats(&comm_buf,strnum)) goto fail;
+  if (!stralloc_cats(&comm_buf," from <")) goto fail;
+  i = comm_buf.len;
+  if (!stralloc_cats(&comm_buf,from)) goto fail;
+  for (;i < comm_buf.len;++i)
+    if (comm_buf.s[i] == '\n')
+      comm_buf.s[i] = '/';
+    else
+      if (!issafe(comm_buf.s[i]))
+	comm_buf.s[i] = '_';
+  if (!stralloc_cats(&comm_buf,"> qp ")) goto fail;
+  strnum[fmt_ulong(strnum,pid)] = 0;
+  if (!stralloc_cats(&comm_buf,strnum)) goto fail;
+  if (!stralloc_cats(&comm_buf," uid ")) goto fail;
+  strnum[fmt_ulong(strnum,uid)] = 0;
+  if (!stralloc_cats(&comm_buf,strnum)) goto fail;
+  if (!stralloc_cats(&comm_buf,"\n")) goto fail;
+  if (!stralloc_0(&comm_buf)) goto fail;
+  return;
+  
+fail:
+  /* either all or nothing */
+  comm_buf.len = pos;
+}
+
+void comm_exit(void)
+{
+  int w;
+  
+  /* if it fails exit, we have already stoped */
+  if (!stralloc_cats(&comm_buf,"X")) _exit(1);
+  if (!stralloc_0(&comm_buf)) _exit(1);
+}
+
+void comm_selprep(int *nfds, fd_set *wfds, fd_set *rfds)
+{
+  if (flagsendalive) {
+    if (flagstopasap && comm_canwrite() == 0)
+      comm_exit();
+    if (comm_canwrite()) {
+      FD_SET(fdout,wfds);
+      if (*nfds <= fdout)
+	*nfds = fdout + 1;
+    }
+    FD_SET(fdin,rfds);
+    if (*nfds <= fdin)
+      *nfds = fdin + 1;
+  }
+}
+
+void comm_do(fd_set *wfds, fd_set *rfds)
+{
+  /* first write then read */
+  if (flagsendalive)
+    if (comm_canwrite())
+      if (FD_ISSET(fdout,wfds)) {
+	int w;
+	int len;
+	len = comm_buf.len;
+	w = write(fdout,comm_buf.s + comm_pos,len - comm_pos);
+	if (w <= 0) {
+	  if ((w == -1) && (errno == error_pipe))
+	    senddied();
+	} else {
+	  comm_pos += w;
+	  if (comm_pos == len) {
+	    comm_buf.len = 0;
+	    comm_pos = 0;
+	  }
+	}
+      }
+  if (flagsendalive)
+    if (FD_ISSET(fdin,rfds)) {
+      /* there are only two messages 'H' and 'X' */
+      char c;
+      int r;
+      r = read(fdin, &c, 1);
+      if (r <= 0) {
+	if ((r == -1) && (errno != error_intr))
+	  senddied();
+      } else {
+	switch(c) {
+	  case 'H':
+	    sighup();
+	    break;
+	  case 'X':
+	    sigterm();
+	    break;
+	  default:
+	    log1("warning: qmail-todo: qmail-send speaks an obscure dialect\n");
+	    break;
+	}
+      }
+    }
+}
+
+/* this file is not so long ------------------------------------------ TODO */
+
+datetime_sec nexttodorun;
+DIR *tododir; /* if 0, have to opendir again */
+stralloc todoline = {0};
+char todobuf[SUBSTDIO_INSIZE];
+char todobufinfo[512];
+char todobufchan[CHANNELS][1024];
+
+void todo_init(void)
+{
+ tododir = 0;
+ nexttodorun = now();
+ trigger_set();
+}
+
+void todo_selprep(int *nfds, fd_set *rfds, datetime_sec *wakeup)
+{
+ if (flagstopasap) return;
+ trigger_selprep(nfds,rfds);
+ if (tododir) *wakeup = 0;
+ if (*wakeup > nexttodorun) *wakeup = nexttodorun;
+}
+
+void todo_do(fd_set *rfds)
+{
+ struct stat st;
+ substdio ss; int fd;
+ substdio ssinfo; int fdinfo;
+ substdio sschan[CHANNELS];
+ int fdchan[CHANNELS];
+ int flagchan[CHANNELS];
+ char ch;
+ int match;
+ unsigned long id;
+ unsigned int len;
+ direntry *d;
+ int c;
+ unsigned long uid;
+ unsigned long pid;
+
+ fd = -1;
+ fdinfo = -1;
+ for (c = 0;c < CHANNELS;++c) fdchan[c] = -1;
+
+ if (flagstopasap) return;
+
+ if (!tododir)
+  {
+   if (!trigger_pulled(rfds))
+     if (recent < nexttodorun)
+       return;
+   trigger_set();
+   tododir = opendir("todo");
+   if (!tododir)
+    {
+     pausedir("todo");
+     return;
+    }
+   nexttodorun = recent + SLEEP_TODO;
+  }
+
+ d = readdir(tododir);
+ if (!d)
+  {
+   closedir(tododir);
+   tododir = 0;
+   return;
+  }
+ if (str_equal(d->d_name,".")) return;
+ if (str_equal(d->d_name,"..")) return;
+ len = scan_ulong(d->d_name,&id);
+ if (!len || d->d_name[len]) return;
+
+ fnmake_todo(id);
+
+ fd = open_read(fn.s);
+ if (fd == -1) { log3("warning: qmail-todo: unable to open ",fn.s,"\n"); return; }
+
+ fnmake_mess(id);
+ /* just for the statistics */
+ if (stat(fn.s,&st) == -1)
+  { log3("warning: qmail-todo: unable to stat ",fn.s,"\n"); goto fail; }
+
+ for (c = 0;c < CHANNELS;++c)
+  {
+   fnmake_chanaddr(id,c);
+   if (unlink(fn.s) == -1) if (errno != error_noent)
+    { log3("warning: qmail-todo: unable to unlink ",fn.s,"\n"); goto fail; }
+  }
+
+ fnmake_info(id);
+ if (unlink(fn.s) == -1) if (errno != error_noent)
+  { log3("warning: qmail-todo: unable to unlink ",fn.s,"\n"); goto fail; }
+
+ fdinfo = open_excl(fn.s);
+ if (fdinfo == -1)
+  { log3("warning: qmail-todo: unable to create ",fn.s,"\n"); goto fail; }
+
+ strnum[fmt_ulong(strnum,id)] = 0;
+ log3("new msg ",strnum,"\n");
+
+ for (c = 0;c < CHANNELS;++c) flagchan[c] = 0;
+
+ substdio_fdbuf(&ss,read,fd,todobuf,sizeof(todobuf));
+ substdio_fdbuf(&ssinfo,write,fdinfo,todobufinfo,sizeof(todobufinfo));
+
+ uid = 0;
+ pid = 0;
+
+ for (;;)
+  {
+   if (getln(&ss,&todoline,&match,'\0') == -1)
+    {
+     /* perhaps we're out of memory, perhaps an I/O error */
+     fnmake_todo(id);
+     log3("warning: qmail-todo: trouble reading ",fn.s,"\n"); goto fail;
+    }
+   if (!match) break;
+
+   switch(todoline.s[0])
+    {
+     case 'u':
+       scan_ulong(todoline.s + 1,&uid);
+       break;
+     case 'p':
+       scan_ulong(todoline.s + 1,&pid);
+       break;
+     case 'F':
+       if (substdio_putflush(&ssinfo,todoline.s,todoline.len) == -1)
+	{
+	 fnmake_info(id);
+         log3("warning: qmail-todo: trouble writing to ",fn.s,"\n"); goto fail;
+	}
+	comm_info(id, (unsigned long) st.st_size, todoline.s + 1, pid, uid);
+       break;
+     case 'T':
+       switch(rewrite(todoline.s + 1))
+	{
+	 case 0: nomem(); goto fail;
+	 case 2: c = 1; break;
+	 default: c = 0; break;
+        }
+       if (fdchan[c] == -1)
+	{
+	 fnmake_chanaddr(id,c);
+	 fdchan[c] = open_excl(fn.s);
+	 if (fdchan[c] == -1)
+          { log3("warning: qmail-todo: unable to create ",fn.s,"\n"); goto fail; }
+	 substdio_fdbuf(&sschan[c]
+	   ,write,fdchan[c],todobufchan[c],sizeof(todobufchan[c]));
+	 flagchan[c] = 1;
+	}
+       if (substdio_bput(&sschan[c],rwline.s,rwline.len) == -1)
+        {
+	 fnmake_chanaddr(id,c);
+         log3("warning: qmail-todo: trouble writing to ",fn.s,"\n"); goto fail;
+        }
+       break;
+     default:
+       fnmake_todo(id);
+       log3("warning: qmail-todo: unknown record type in ",fn.s,"\n"); goto fail;
+    }
+  }
+
+ close(fd); fd = -1;
+
+ fnmake_info(id);
+ if (substdio_flush(&ssinfo) == -1)
+  { log3("warning: qmail-todo: trouble writing to ",fn.s,"\n"); goto fail; }
+ if (fsync(fdinfo) == -1)
+  { log3("warning: qmail-todo: trouble fsyncing ",fn.s,"\n"); goto fail; }
+ close(fdinfo); fdinfo = -1;
+
+ for (c = 0;c < CHANNELS;++c)
+   if (fdchan[c] != -1)
+    {
+     fnmake_chanaddr(id,c);
+     if (substdio_flush(&sschan[c]) == -1)
+      { log3("warning: qmail-todo: trouble writing to ",fn.s,"\n"); goto fail; }
+     if (fsync(fdchan[c]) == -1)
+      { log3("warning: qmail-todo: trouble fsyncing ",fn.s,"\n"); goto fail; }
+     close(fdchan[c]); fdchan[c] = -1;
+    }
+
+ fnmake_todo(id);
+ if (substdio_putflush(&sstoqc,fn.s,fn.len) == -1) { cleandied(); return; }
+ if (substdio_get(&ssfromqc,&ch,1) != 1) { cleandied(); return; }
+ if (ch != '+')
+  {
+   log3("warning: qmail-clean unable to clean up ",fn.s,"\n");
+   return;
+  }
+
+ comm_write(id, flagchan[0], flagchan[1]);
+ 
+ return;
+ 
+ fail:
+ if (fd != -1) close(fd);
+ if (fdinfo != -1) close(fdinfo);
+ for (c = 0;c < CHANNELS;++c)
+   if (fdchan[c] != -1) close(fdchan[c]);
+}
+
+/* this file is too long ---------------------------------------------- MAIN */
+
+int getcontrols(void)
+{
+ if (control_init() == -1) return 0;
+ if (control_rldef(&envnoathost,"control/envnoathost",1,"envnoathost") != 1) return 0;
+ if (control_readfile(&locals,"control/locals",1) != 1) return 0;
+ if (!constmap_init(&maplocals,locals.s,locals.len,0)) return 0;
+ switch(control_readfile(&percenthack,"control/percenthack",0))
+  {
+   case -1: return 0;
+   case 0: if (!constmap_init(&mappercenthack,"",0,0)) return 0; break;
+   case 1: if (!constmap_init(&mappercenthack,percenthack.s,percenthack.len,0)) return 0; break;
+  }
+ switch(control_readfile(&vdoms,"control/virtualdomains",0))
+  {
+   case -1: return 0;
+   case 0: if (!constmap_init(&mapvdoms,"",0,1)) return 0; break;
+   case 1: if (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) return 0; break;
+  }
+ return 1;
+}
+
+stralloc newlocals = {0};
+stralloc newvdoms = {0};
+
+void regetcontrols(void)
+{
+ int r;
+
+ if (control_readfile(&newlocals,"control/locals",1) != 1)
+  { log1("alert: qmail-todo: unable to reread control/locals\n"); return; }
+ r = control_readfile(&newvdoms,"control/virtualdomains",0);
+ if (r == -1)
+  { log1("alert: qmail-todo: unable to reread control/virtualdomains\n"); return; }
+
+ constmap_free(&maplocals);
+ constmap_free(&mapvdoms);
+
+ while (!stralloc_copy(&locals,&newlocals)) nomem();
+ while (!constmap_init(&maplocals,locals.s,locals.len,0)) nomem();
+
+ if (r)
+  {
+   while (!stralloc_copy(&vdoms,&newvdoms)) nomem();
+   while (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) nomem();
+  }
+ else
+   while (!constmap_init(&mapvdoms,"",0,1)) nomem();
+}
+
+void reread(void)
+{
+ if (chdir(auto_qmail) == -1)
+  {
+   log1("alert: qmail-todo: unable to reread controls: unable to switch to home directory\n");
+   return;
+  }
+ regetcontrols();
+ while (chdir("queue") == -1)
+  {
+   log1("alert: qmail-todo: unable to switch back to queue directory; HELP! sleeping...\n");
+   sleep(10);
+  }
+}
+
+void main()
+{
+ datetime_sec wakeup;
+ fd_set rfds;
+ fd_set wfds;
+ int nfds;
+ struct timeval tv;
+ int r;
+ char c;
+
+ if (chdir(auto_qmail) == -1)
+  { log1("alert: qmail-todo: cannot start: unable to switch to home directory\n"); _exit(111); }
+ if (!getcontrols())
+  { log1("alert: qmail-todo: cannot start: unable to read controls\n"); _exit(111); }
+ if (chdir("queue") == -1)
+  { log1("alert: qmail-todo: cannot start: unable to switch to queue directory\n"); _exit(111); }
+ sig_pipeignore();
+ umask(077);
+
+ fnmake_init();
+
+ todo_init();
+ comm_init();
+ 
+ do {
+   r = read(fdin, &c, 1);
+   if ((r == -1) && (errno != error_intr))
+     _exit(100); /* read failed probably qmail-send died */
+ } while (r =! 1); /* we assume it is a 'S' */
+ 
+ for (;;)
+  {
+   recent = now();
+
+   if (flagreadasap) { flagreadasap = 0; reread(); }
+   if (!flagsendalive) {
+     /* qmail-send finaly exited, so do the same. */
+     if (flagstopasap) _exit(0);
+     /* qmail-send died. We can not log and we can not work therefor _exit(1). */
+     _exit(1);
+   }
+
+   wakeup = recent + SLEEP_FOREVER;
+   FD_ZERO(&rfds);
+   FD_ZERO(&wfds);
+   nfds = 1;
+
+   todo_selprep(&nfds,&rfds,&wakeup);
+   comm_selprep(&nfds,&wfds,&rfds);
+
+   if (wakeup <= recent) tv.tv_sec = 0;
+   else tv.tv_sec = wakeup - recent + SLEEP_FUZZ;
+   tv.tv_usec = 0;
+
+   if (select(nfds,&rfds,&wfds,(fd_set *) 0,&tv) == -1)
+     if (errno == error_intr)
+       ;
+     else
+       log1("warning: qmail-todo: trouble in select\n");
+   else
+    {
+     recent = now();
+
+     todo_do(&rfds);
+     comm_do(&wfds, &rfds);
+    }
+  }
+  /* NOTREACHED */
+}
+
diff -ruN qmail-1.03-factory/qmail.c qmail-1.03-7.10/qmail.c
--- qmail-1.03-factory/qmail.c	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/qmail.c	2010-02-14 22:53:32.000000000 -0500
@@ -6,28 +6,49 @@
 #include "fd.h"
 #include "qmail.h"
 #include "auto_qmail.h"
+#include "env.h"
 
-static char *binqqargs[2] = { "bin/qmail-queue", 0 } ;
+static char *binqqargs[2] = { 0, 0 } ;
+
+static void setup_qqargs()
+{
+  if(!binqqargs[0])
+    binqqargs[0] = env_get("QMAILQUEUE");
+  if(!binqqargs[0])
+    binqqargs[0] = "bin/qmail-queue";
+}
 
 int qmail_open(qq)
 struct qmail *qq;
 {
   int pim[2];
   int pie[2];
+  int pierr[2];
+
+  setup_qqargs();
 
   if (pipe(pim) == -1) return -1;
   if (pipe(pie) == -1) { close(pim[0]); close(pim[1]); return -1; }
- 
+  if (pipe(pierr) == -1) { 
+    close(pim[0]); close(pim[1]); 
+    close(pie[0]); close(pie[1]); 
+    close(pierr[0]); close(pierr[1]); 
+    return -1; 
+  }
+
   switch(qq->pid = vfork()) {
     case -1:
+      close(pierr[0]); close(pierr[1]); 
       close(pim[0]); close(pim[1]);
       close(pie[0]); close(pie[1]);
       return -1;
     case 0:
       close(pim[1]);
       close(pie[1]);
+      close(pierr[0]); /* we want to receive data */
       if (fd_move(0,pim[0]) == -1) _exit(120);
       if (fd_move(1,pie[0]) == -1) _exit(120);
+      if (fd_move(4,pierr[1]) == -1) _exit(120);
       if (chdir(auto_qmail) == -1) _exit(61);
       execv(*binqqargs,binqqargs);
       _exit(120);
@@ -35,6 +56,7 @@
 
   qq->fdm = pim[1]; close(pim[0]);
   qq->fde = pie[1]; close(pie[0]);
+  qq->fderr = pierr[0]; close(pierr[1]);
   substdio_fdbuf(&qq->ss,write,qq->fdm,qq->buf,sizeof(qq->buf));
   qq->flagerr = 0;
   return 0;
@@ -82,10 +104,22 @@
 {
   int wstat;
   int exitcode;
+  int match;
+  char ch;
+  static char errstr[256];
+  int len = 0;
 
   qmail_put(qq,"",1);
   if (!qq->flagerr) if (substdio_flush(&qq->ss) == -1) qq->flagerr = 1;
   close(qq->fde);
+  substdio_fdbuf(&qq->ss,read,qq->fderr,qq->buf,sizeof(qq->buf));
+  while( substdio_bget(&qq->ss,&ch,1) && len < 255){
+    errstr[len]=ch;
+    len++;
+  }
+  if (len > 0) errstr[len]='\0'; /* add str-term */
+
+  close(qq->fderr);
 
   if (wait_pid(&wstat,qq->pid) != qq->pid)
     return "Zqq waitpid surprise (#4.3.0)";
@@ -94,9 +128,11 @@
   exitcode = wait_exitcode(wstat);
 
   switch(exitcode) {
+    case  1: return "IYour SPAM has been ignored.";
     case 115: /* compatibility */
     case 11: return "Denvelope address too long for qq (#5.1.3)";
     case 31: return "Dmail server permanently rejected message (#5.3.0)";
+    case 32: return "Dwe do not accept SPAM (#5.3.0)";
     case 51: return "Zqq out of memory (#4.3.0)";
     case 52: return "Zqq timeout (#4.3.0)";
     case 53: return "Zqq write error or disk full (#4.3.0)";
@@ -118,8 +154,11 @@
     case 81: return "Zqq internal bug (#4.3.0)";
     case 120: return "Zunable to exec qq (#4.3.0)";
     default:
+      if (exitcode == 82 && len > 2){
+        return errstr;
+      }
       if ((exitcode >= 11) && (exitcode <= 40))
-	return "Dqq permanent problem (#5.3.0)";
+        return "Dqq permanent problem (#5.3.0)";
       return "Zqq temporary problem (#4.3.0)";
   }
 }
diff -ruN qmail-1.03-factory/qmail.h qmail-1.03-7.10/qmail.h
--- qmail-1.03-factory/qmail.h	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/qmail.h	2010-02-14 22:53:32.000000000 -0500
@@ -8,6 +8,7 @@
   unsigned long pid;
   int fdm;
   int fde;
+  int fderr;
   substdio ss;
   char buf[1024];
 } ;
diff -ruN qmail-1.03-factory/qregex.c qmail-1.03-7.10/qregex.c
--- qmail-1.03-factory/qregex.c	1969-12-31 19:00:00.000000000 -0500
+++ qmail-1.03-7.10/qregex.c	2010-02-14 22:53:32.000000000 -0500
@@ -0,0 +1,57 @@
+/*
+ * qregex (v2)
+ * $Id: qregex.c,v 2.1 2001/12/28 07:05:21 evan Exp $
+ *
+ * Author  : Evan Borgstrom (evan at unixpimps dot org)
+ * Created : 2001/12/14 23:08:16
+ * Modified: $Date: 2001/12/28 07:05:21 $
+ * Revision: $Revision: 2.1 $
+ *
+ * Do POSIX regex matching on addresses for anti-relay / spam control.
+ * It logs to the maillog
+ * See the qregex-readme file included with this tarball.
+ * If you didn't get this file in a tarball please see the following URL:
+ *  http://www.unixpimps.org/software/qregex
+ *
+ * qregex.c is released under a BSD style copyright.
+ * See http://www.unixpimps.org/software/qregex/copyright.html
+ *
+ * Note: this revision follows the coding guidelines set forth by the rest of
+ *       the qmail code and that described at the following URL.
+ *       http://cr.yp.to/qmail/guarantee.html
+ * 
+ */
+
+#include <sys/types.h>
+#include <regex.h>
+#include "qregex.h"
+
+#define REGCOMP(X,Y)    regcomp(&X, Y, REG_EXTENDED|REG_ICASE)
+#define REGEXEC(X,Y)    regexec(&X, Y, (size_t)0, (regmatch_t *)0, (int)0)
+
+int matchregex(char *text, char *regex) {
+  regex_t qreg;
+  int retval = 0;
+
+
+  /* build the regex */
+  if ((retval = REGCOMP(qreg, regex)) != 0) {
+    regfree(&qreg);
+    return(-retval);
+  }
+
+  /* execute the regex */
+  if ((retval = REGEXEC(qreg, text)) != 0) {
+    /* did we just not match anything? */
+    if (retval == REG_NOMATCH) {
+      regfree(&qreg);
+      return(0);
+    }
+    regfree(&qreg);
+    return(-retval);
+  }
+
+  /* signal the match */
+  regfree(&qreg);
+  return(1);
+}
diff -ruN qmail-1.03-factory/qregex.h qmail-1.03-7.10/qregex.h
--- qmail-1.03-factory/qregex.h	1969-12-31 19:00:00.000000000 -0500
+++ qmail-1.03-7.10/qregex.h	2010-02-14 22:53:32.000000000 -0500
@@ -0,0 +1,5 @@
+/* simple header file for the matchregex prototype */
+#ifndef _QREGEX_H_
+#define _QREGEX_H_
+int matchregex(char *text, char *regex);
+#endif
diff -ruN qmail-1.03-factory/sendmail.c qmail-1.03-7.10/sendmail.c
--- qmail-1.03-factory/sendmail.c	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/sendmail.c	2010-02-14 22:53:32.000000000 -0500
@@ -45,6 +45,38 @@
   _exit(111);
 }
 
+void do_sender(s)
+const char *s;
+{
+  char *x;
+  int n;
+  int a;
+  int i;
+  
+  env_unset("QMAILNAME");
+  env_unset("MAILNAME");
+  env_unset("NAME");
+  env_unset("QMAILHOST");
+  env_unset("MAILHOST");
+
+  n = str_len(s);
+  a = str_rchr(s, '@');
+  if (a == n)
+  {
+    env_put2("QMAILUSER", s);
+    return;
+  }
+  env_put2("QMAILHOST", s + a + 1);
+
+  x = (char *) alloc((a + 1) * sizeof(char));
+  if (!x) nomem();
+  for (i = 0; i < a; i++)
+    x[i] = s[i];
+  x[i] = 0;
+  env_put2("QMAILUSER", x);
+  alloc_free(x);
+}
+
 int flagh;
 char *sender;
 
@@ -118,6 +150,7 @@
   if (sender) {
     *arg++ = "-f";
     *arg++ = sender;
+    do_sender(sender);
   }
   *arg++ = "--";
   for (i = 0;i < argc;++i) *arg++ = argv[i];
diff -ruN qmail-1.03-factory/spawn.c qmail-1.03-7.10/spawn.c
--- qmail-1.03-factory/spawn.c	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/spawn.c	2010-02-14 22:53:32.000000000 -0500
@@ -63,7 +63,7 @@
 int flagreading = 1;
 char outbuf[1024]; substdio ssout;
 
-int stage = 0; /* reading 0:delnum 1:messid 2:sender 3:recip */
+int stage = 0; /* reading 0:delnum 1:delnum2 2:messid 3:sender 4:recip */
 int flagabort = 0; /* if 1, everything except delnum is garbage */
 int delnum;
 stralloc messid = {0};
@@ -73,6 +73,7 @@
 void err(s) char *s;
 {
  char ch; ch = delnum; substdio_put(&ssout,&ch,1);
+ ch = delnum >> 8; substdio_put(&ssout,&ch,1);
  substdio_puts(&ssout,s); substdio_putflush(&ssout,"",1);
 }
 
@@ -155,16 +156,19 @@
     {
      case 0:
        delnum = (unsigned int) (unsigned char) ch;
-       messid.len = 0; stage = 1; break;
+       stage = 1; break;
      case 1:
+       delnum += (unsigned int) ((unsigned int) ch) << 8;
+       messid.len = 0; stage = 2; break;
+     case 2:
        if (!stralloc_append(&messid,&ch)) flagabort = 1;
        if (ch) break;
-       sender.len = 0; stage = 2; break;
-     case 2:
+       sender.len = 0; stage = 3; break;
+     case 3:
        if (!stralloc_append(&sender,&ch)) flagabort = 1;
        if (ch) break;
-       recip.len = 0; stage = 3; break;
-     case 3:
+       recip.len = 0; stage = 4; break;
+     case 4:
        if (!stralloc_append(&recip,&ch)) flagabort = 1;
        if (ch) break;
        docmd();
@@ -201,7 +205,8 @@
 
  initialize(argc,argv);
 
- ch = auto_spawn; substdio_putflush(&ssout,&ch,1);
+ ch = auto_spawn; substdio_put(&ssout,&ch,1);
+ ch = auto_spawn >> 8; substdio_putflush(&ssout,&ch,1);
 
  for (i = 0;i < auto_spawn;++i) { d[i].used = 0; d[i].output.s = 0; }
 
@@ -236,7 +241,8 @@
 	   continue; /* read error on a readable pipe? be serious */
 	 if (r == 0)
 	  {
-           ch = i; substdio_put(&ssout,&ch,1);
+           char ch; ch = i; substdio_put(&ssout,&ch,1);
+           ch = i >> 8; substdio_put(&ssout,&ch,1);
 	   report(&ssout,d[i].wstat,d[i].output.s,d[i].output.len);
 	   substdio_put(&ssout,"",1);
 	   substdio_flush(&ssout);
diff -ruN qmail-1.03-factory/spf.c qmail-1.03-7.10/spf.c
--- qmail-1.03-factory/spf.c	1969-12-31 19:00:00.000000000 -0500
+++ qmail-1.03-7.10/spf.c	2010-02-14 22:53:32.000000000 -0500
@@ -0,0 +1,893 @@
+#include "stralloc.h"
+#include "strsalloc.h"
+#include "alloc.h"
+#include "ip.h"
+#include "ipalloc.h"
+#include "ipme.h"
+#include "str.h"
+#include "fmt.h"
+#include "scan.h"
+#include "byte.h"
+#include "now.h"
+#include "dns.h"
+#include "case.h"
+#include "spf.h"
+#include "env.h"
+#include "qregex.h"
+
+#define SPF_EXT    -1
+#define SPF_SYNTAX -2
+
+#define WSPACE(x) ((x) == ' ' || (x) == '\t' || (x) == '\r' || (x) == '\n')
+#define NXTOK(b, p, a) do { (b) = (p); \
+          while((p) < (a)->len && !WSPACE((a)->s[(p)])) ++(p); \
+          while((p) < (a)->len && WSPACE((a)->s[(p)])) (a)->s[(p)++] = 0; \
+        } while(0)
+
+/* this table and macro came from wget more or less */
+/* and was in turn stolen by me from libspf as is :) */
+const static unsigned char urlchr_table[256] =
+{
+  1,  1,  1,  1,   1,  1,  1,  1,   /* NUL SOH STX ETX  EOT ENQ ACK BEL */
+  1,  1,  1,  1,   1,  1,  1,  1,   /* BS  HT  LF  VT   FF  CR  SO  SI  */
+  1,  1,  1,  1,   1,  1,  1,  1,   /* DLE DC1 DC2 DC3  DC4 NAK SYN ETB */
+  1,  1,  1,  1,   1,  1,  1,  1,   /* CAN EM  SUB ESC  FS  GS  RS  US  */
+  1,  0,  1,  1,   0,  1,  1,  0,   /* SP  !   "   #    $   %   &   '   */
+  0,  0,  0,  1,   0,  0,  0,  1,   /* (   )   *   +    ,   -   .   /   */
+  0,  0,  0,  0,   0,  0,  0,  0,   /* 0   1   2   3    4   5   6   7   */
+  0,  0,  1,  1,   1,  1,  1,  1,   /* 8   9   :   ;    <   =   >   ?   */
+  1,  0,  0,  0,   0,  0,  0,  0,   /* @   A   B   C    D   E   F   G   */
+  0,  0,  0,  0,   0,  0,  0,  0,   /* H   I   J   K    L   M   N   O   */
+  0,  0,  0,  0,   0,  0,  0,  0,   /* P   Q   R   S    T   U   V   W   */
+  0,  0,  0,  1,   1,  1,  1,  0,   /* X   Y   Z   [    \   ]   ^   _   */
+  1,  0,  0,  0,   0,  0,  0,  0,   /* `   a   b   c    d   e   f   g   */
+  0,  0,  0,  0,   0,  0,  0,  0,   /* h   i   j   k    l   m   n   o   */
+  0,  0,  0,  0,   0,  0,  0,  0,   /* p   q   r   s    t   u   v   w   */
+  0,  0,  0,  1,   1,  1,  1,  1,   /* x   y   z   {    |   }   ~   DEL */
+
+  1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,
+  1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,
+  1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,
+  1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,
+
+  1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,
+  1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,
+  1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,
+  1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,
+};
+
+
+extern stralloc addr;
+extern stralloc helohost;
+extern char *remoteip;
+extern char *local;
+
+extern stralloc spflocal;
+extern stralloc spfguess;
+extern stralloc spfexp;
+
+static stralloc sender_fqdn = {0};
+static stralloc explanation = {0};
+static stralloc expdomain = {0};
+static stralloc errormsg = {0};
+static char *received;
+
+static int recursion;
+static struct ip_address ip;
+
+static void hdr_pass() { received = "pass (%{xr}: %{xs} designates %{i} as permitted sender)"; };
+static void hdr_softfail() { received = "softfail (%{xr}: transitioning %{xs} does not designate %{i} as permitted sender)"; };
+static void hdr_fail() { received = "fail (%{xr}: %{xs} does not designate %{i} as permitted sender)"; };
+static void hdr_plusall() { received = "fail (%{xr}: %{xs} has an improper SPF record)"; };
+static void hdr_unknown() { received = "unknown (%{xr}: domain at %{d} does not designate permitted sender hosts)"; };
+static void hdr_neutral() { received = "neutral (%{xr}: %{i} is neither permitted nor denied by %{xs})"; };
+static void hdr_none() { received = "none (%{xr}: domain at %{d} does not designate permitted sender hosts)"; };
+static void hdr_unknown_msg(e) char *e; { stralloc_copys(&errormsg, e); received = "unknown (%{xr}: %{xe})"; };
+static void hdr_ext(e) char *e; { stralloc_copys(&errormsg, e); received = "unknown %{xe} (%{xr}: %{xs} uses mechanism not recognized by this client)"; };
+static void hdr_syntax() { received = "unknown (%{xr}: parse error in %{xs})"; };
+static void hdr_error(e) char *e; { stralloc_copys(&errormsg, e); received = "error (%{xr}: error in processing during lookup of %{d}: %{xe})"; };
+static void hdr_dns() { hdr_error("DNS problem"); }
+
+
+static int matchip(struct ip_address *net, int mask, struct ip_address *ip)
+{
+	int j;
+	int bytemask;
+
+	for (j = 0; j < 4 && mask > 0; ++j) {
+		if (mask > 8) bytemask = 8; else bytemask = mask;
+		mask -= bytemask;
+
+		if ((net->d[j] ^ ip->d[j]) & (0x100 - (1 << (8 - bytemask))))
+			return 0;
+	}
+	return 1;
+}
+
+static int getipmask(char *mask, int ipv6) {
+	unsigned long r;
+	int pos;
+
+	if (!mask) return 32;
+
+	pos = scan_ulong(mask, &r);
+	if (!pos || (mask[pos] && !(mask[pos] == '/' && ipv6))) return -1;
+	if (r > 32) return -1;
+
+	return r;
+}
+
+int spfget(stralloc *spf, stralloc *domain)
+{
+	strsalloc ssa = {0};
+	int j;
+	int begin, pos, i;
+	int r = SPF_NONE;
+
+	spf->len = 0;
+
+	switch(dns_txt(&ssa, domain)) {
+		case DNS_MEM: return SPF_NOMEM;
+		case DNS_SOFT: hdr_dns(); return SPF_ERROR;
+		case DNS_HARD: return SPF_NONE;
+	}
+
+	for (j = 0;j < ssa.len;++j) {
+		pos = 0;
+
+		NXTOK(begin, pos, &ssa.sa[j]);
+		if (str_len(ssa.sa[j].s + begin) < 6) continue;
+		if (!byte_equal(ssa.sa[j].s + begin,6,"v=spf1")) continue;
+		if (ssa.sa[j].s[begin + 6]) {
+			/* check for subversion */
+			if (ssa.sa[j].s[begin + 6] != '.') continue;
+			for(i = begin + 7;;++i)
+				if (!(ssa.sa[j].s[i] >= '0' && ssa.sa[j].s[i] <= '9')) break;
+			if (i == (begin + 7)) continue;
+			if (ssa.sa[j].s[i]) continue;
+		}
+
+		if (spf->len > 0) {
+			spf->len = 0;
+			hdr_unknown_msg("Multiple SPF records returned");
+			r = SPF_UNKNOWN;
+			break;
+		}
+		if (!stralloc_0(&ssa.sa[j])) return SPF_NOMEM;
+		if (!stralloc_copys(spf,ssa.sa[j].s + pos)) return SPF_NOMEM;
+		r = SPF_OK;
+	}
+
+	for (j = 0;j < ssa.len;++j)
+		alloc_free(ssa.sa[j].s);
+	alloc_free(ssa.sa);
+	return r;
+}
+
+static int spf_ptr(char *spec, char *mask);
+
+int spfsubst(stralloc *expand, char *spec, char *domain)
+{
+	static char hexdigits[] = "0123456789abcdef";
+	stralloc sa = {0};
+	char ch;
+	int digits = -1;
+	int urlencode = 0;
+	int reverse = 0;
+	int start = expand->len;
+	int i, pos;
+	char *split = ".";
+
+	if (!stralloc_readyplus(&sa,0)) return 0;
+
+	if (*spec == 'x') { i = 1; ++spec; } else i = 0;
+	ch = *spec++;
+	if (!ch) { alloc_free(sa.s); return 1; }
+	if (ch >= 'A' && ch <= 'Z') { ch += 32; urlencode = 1; }
+	if (i) ch -= 32;
+	while(*spec >= '0' && *spec <= '9') {
+		if (digits < 0) digits = 0;
+		if (digits >= 1000000) { digits = 10000000; continue; }
+		digits = (digits * 10) + (*spec - '0');
+		spec++;
+	}
+
+	while((*spec >= 'a' && *spec <= 'z') || (*spec >= 'A' && *spec <= 'Z')) {
+		if (*spec == 'r') reverse = 1;
+		spec++;
+	}
+
+	if (*spec) split = spec;
+
+	switch(ch) {
+		case 'l':
+			pos = byte_rchr(addr.s, addr.len, '@');
+			if (pos < addr.len) {
+				if (!stralloc_copyb(&sa, addr.s, pos)) return 0;
+			} else
+				if (!stralloc_copys(&sa, "postmaster")) return 0;
+			break;
+		case 's':
+			if (!stralloc_copys(&sa, addr.s)) return 0;
+			break;
+		case 'o':
+			pos = byte_rchr(addr.s, addr.len, '@') + 1;
+			if (pos > addr.len) break;
+			if (!stralloc_copys(&sa, addr.s + pos)) return 0;
+			break;
+		case 'd':
+			if (!stralloc_copys(&sa, domain)) return 0;
+			break;
+		case 'i':
+			if (!stralloc_ready(&sa, IPFMT)) return 0;
+			sa.len = ip_fmt(sa.s, &ip);
+			break;
+		case 't':
+			if (!stralloc_ready(&sa, FMT_ULONG)) return 0;
+			sa.len = fmt_ulong(sa.s, (unsigned long)now());
+			break;
+		case 'p':
+			if (!sender_fqdn.len)
+				spf_ptr(domain, 0);
+			if (sender_fqdn.len) {
+				if (!stralloc_copy(&sa, &sender_fqdn)) return 0;
+			} else
+				if (!stralloc_copys(&sa, "unknown")) return 0;
+			break;
+		case 'v': 
+			if (!stralloc_copys(&sa, "in-addr")) return 0;
+			break;
+		case 'h':
+			if (!stralloc_copys(&sa, helohost.s)) return 0; /* FIXME: FQDN? */
+			break;
+		case 'E':
+			if (errormsg.len && !stralloc_copy(&sa, &errormsg)) return 0;
+			break;
+		case 'R':
+			if (!stralloc_copys(&sa, local)) return 0;
+			break;
+		case 'S':
+			if (expdomain.len > 0) {
+				if (!stralloc_copys(&sa, "SPF record at ")) return 0;
+				if (!stralloc_cats(&sa, expdomain.s)) return 0;
+			} else {
+				if (!stralloc_copys(&sa, "local policy")) return 0;
+			}
+			break;
+	}
+
+	if (reverse) {
+		for(pos = 0; digits; ++pos) {
+			pos += byte_cspn(sa.s + pos, sa.len - pos, split);
+			if (pos >= sa.len) break;
+			if (!--digits) break;
+		}
+
+		for(; pos > 0; pos = i - 1) {
+			i = byte_rcspn(sa.s, pos, split) + 1;
+			if (i > pos) i = 0;
+			if (!stralloc_catb(expand, sa.s + i, pos - i)) return 0;
+			if (i > 0 && !stralloc_append(expand, ".")) return 0;
+		}
+	} else {
+		for(pos = sa.len; digits; --pos) {
+			i = byte_rcspn(sa.s, pos, split) + 1;
+			if (i > pos) { pos = 0; break; }
+			pos = i;
+			if (!--digits) break;
+		}
+
+		if (!stralloc_catb(expand, sa.s + pos, sa.len - pos)) return 0;
+		if (split[0] != '.' || split[1])
+			for(pos = 0; pos < expand->len; pos++) {
+				pos += byte_cspn(expand->s + pos, expand->len - pos, split);
+				if (pos < expand->len)
+					expand->s[pos] = '.';
+			}
+	}
+
+	if (urlencode) {
+		stralloc_copyb(&sa, expand->s + start, expand->len - start);
+		expand->len = start;
+
+		for(pos = 0; pos < sa.len; ++pos) {
+			ch = sa.s[pos];
+			if (urlchr_table[(unsigned char)ch]) {
+				if (!stralloc_readyplus(expand, 3)) return 0;
+				expand->s[expand->len++] = '%';
+				expand->s[expand->len++] = hexdigits[(unsigned char)ch >> 4];
+				expand->s[expand->len++] = hexdigits[(unsigned char)ch & 15];
+			} else
+				if (!stralloc_append(expand, &ch)) return 0;
+		}
+	}
+
+	alloc_free(sa.s);
+	return 1;
+}
+
+int spfexpand(stralloc *sa, char *spec, char *domain)
+{
+	char *p;
+	char append;
+	int pos;
+
+	if (!stralloc_readyplus(sa, 0)) return 0;
+	sa->len = 0;
+
+	for(p = spec; *p; p++) {
+		append = *p;
+		if (*p == '%') {
+			p++;
+			switch(*p) {
+				case '%': break;
+				case '_': append = ' '; break;
+				case '-': if (!stralloc_cats(sa, "%20")) return 0; continue;
+				case '{':
+					pos = str_chr(p, '}');
+					if (p[pos] != '}') { p--; break; }
+					p[pos] = 0;
+					if (!spfsubst(sa, p + 1, domain)) return 0;
+					p += pos;
+					continue;
+				default: p--;
+			}
+		}
+		if (!stralloc_append(sa, &append)) return 0;
+	}
+
+	return 1;
+}
+
+static int spflookup(stralloc *domain);
+
+static int spf_include(char *spec, char *mask)
+{
+	stralloc sa = {0};
+	int r;
+
+	if (!stralloc_copys(&sa, spec)) return SPF_NOMEM;
+	r = spflookup(&sa);
+	alloc_free(sa.s);
+
+	switch(r) {
+		case SPF_NONE:
+			hdr_unknown();
+			r = SPF_UNKNOWN;
+			break;
+		case SPF_SYNTAX:
+			r = SPF_UNKNOWN;
+			break;
+		case SPF_NEUTRAL:
+		case SPF_SOFTFAIL:
+		case SPF_FAIL:
+			r = SPF_NONE;
+			break;
+	}
+
+	return r;
+}
+
+static int spf_a(char *spec, char *mask)
+{
+	stralloc sa = {0};
+	ipalloc ia = {0};
+	int ipmask = getipmask(mask, 1);
+	int r;
+	int j;
+
+	if (ipmask < 0) return SPF_SYNTAX;
+
+	if (!stralloc_copys(&sa, spec)) return SPF_NOMEM;
+	if (!stralloc_readyplus(&ia, 0)) return SPF_NOMEM;
+
+	switch(dns_ip(&ia, &sa)) {
+		case DNS_MEM: return SPF_NOMEM;
+		case DNS_SOFT: hdr_dns(); r = SPF_ERROR; break;
+		case DNS_HARD: r = SPF_NONE; break;
+		default:
+			r = SPF_NONE;
+			for(j = 0; j < ia.len; ++j)
+				if (matchip(&ia.ix[j].ip, ipmask, &ip)) {
+					r = SPF_OK;
+					break;
+				}
+	}
+
+	alloc_free(sa.s);
+	alloc_free(ia.ix);
+	return r;
+}
+
+static int spf_mx(char *spec, char *mask)
+{
+	stralloc sa = {0};
+	ipalloc ia = {0};
+	int ipmask = getipmask(mask, 1);
+	int random = now() + (getpid() << 16);
+	int r;
+	int j;
+
+	if (ipmask < 0) return SPF_SYNTAX;
+
+	if (!stralloc_copys(&sa, spec)) return SPF_NOMEM;
+	if (!stralloc_readyplus(&ia, 0)) return SPF_NOMEM;
+
+	switch(dns_mxip(&ia, &sa, random)) {
+		case DNS_MEM: return SPF_NOMEM;
+		case DNS_SOFT: hdr_dns(); r = SPF_ERROR; break;
+		case DNS_HARD: r = SPF_NONE; break;
+		default:
+			r = SPF_NONE;
+			for(j = 0; j < ia.len; ++j)
+				if (matchip(&ia.ix[j].ip, ipmask, &ip)) {
+					r = SPF_OK;
+					break;
+				}
+	}
+
+	alloc_free(sa.s);
+	alloc_free(ia.ix);
+	return r;
+}
+
+static int spf_ptr(char *spec, char *mask)
+{
+	strsalloc ssa = {0};
+	ipalloc ia = {0};
+	int len = str_len(spec);
+	int r;
+	int j, k;
+	int pos;
+
+	/* we didn't find host with the matching ip before */
+	if (sender_fqdn.len == 7 && str_equal(sender_fqdn.s, "unknown"))
+		return SPF_NONE;
+
+	/* the hostname found will probably be the same as before */
+	while (sender_fqdn.len) {
+		pos = sender_fqdn.len - len;
+		if (pos < 0) break;
+		if (pos > 0 && sender_fqdn.s[pos - 1] != '.') break;
+		if (case_diffb(sender_fqdn.s + pos, len, spec)) break;
+
+		return SPF_OK;
+	}
+
+	/* ok, either it's the first test or it's a very weird setup */
+
+	if (!stralloc_readyplus(&ssa, 0)) return SPF_NOMEM;
+	if (!stralloc_readyplus(&ia, 0)) return SPF_NOMEM;
+
+	switch(dns_ptr(&ssa, &ip)) {
+		case DNS_MEM: return SPF_NOMEM;
+		case DNS_SOFT: hdr_dns(); r = SPF_ERROR; break;
+		case DNS_HARD: r = SPF_NONE; break;
+		default:
+			r = SPF_NONE;
+			for(j = 0; j < ssa.len; ++j) {
+				switch(dns_ip(&ia, &ssa.sa[j])) {
+					case DNS_MEM: return SPF_NOMEM;
+					case DNS_SOFT: hdr_dns(); r = SPF_ERROR; break;
+					case DNS_HARD: break;
+					default:
+						for(k = 0; k < ia.len; ++k)
+							if (matchip(&ia.ix[k].ip, 32, &ip)) {
+								if (!sender_fqdn.len)
+									if (!stralloc_copy(&sender_fqdn, &ssa.sa[j])) return SPF_NOMEM;
+
+								pos = ssa.sa[j].len - len;
+								if (pos < 0) continue;
+								if (pos > 0 && ssa.sa[j].s[pos - 1] != '.') continue;
+								if (case_diffb(ssa.sa[j].s + pos, len, spec)) continue;
+
+								stralloc_copy(&sender_fqdn, &ssa.sa[j]);
+								r = SPF_OK;
+								break;
+							}
+				}
+
+				if (r == SPF_ERROR) break;
+			}
+	}
+
+	for(j = 0;j < ssa.len;++j)
+		alloc_free(ssa.sa[j].s);
+
+	alloc_free(ssa.sa);
+	alloc_free(ia.ix);
+
+	if (!sender_fqdn.len)
+		if (!stralloc_copys(&sender_fqdn, "unknown")) return SPF_NOMEM;
+
+	return r;
+}
+
+static int spf_ip(char *spec, char *mask)
+{
+	struct ip_address net;
+	int ipmask = getipmask(mask, 0);
+
+	if (ipmask < 0) return SPF_SYNTAX;
+	if (!ip_scan(spec, &net)) return SPF_SYNTAX;
+
+	if (matchip(&net, ipmask, &ip)) return SPF_OK;
+
+	return SPF_NONE;
+}
+
+static int spf_exists(char *spec, char *mask)
+{
+	stralloc sa = {0};
+	ipalloc ia = {0};
+	int r;
+
+	if (!stralloc_copys(&sa, spec)) return SPF_NOMEM;
+	if (!stralloc_readyplus(&ia, 0)) return SPF_NOMEM;
+
+	switch(dns_ip(&ia, &sa)) {
+		case DNS_MEM: return SPF_NOMEM;
+		case DNS_SOFT: hdr_dns(); r = SPF_ERROR; break;
+		case DNS_HARD: r = SPF_NONE; break;
+		default: r = SPF_OK;
+	}
+
+	alloc_free(sa.s);
+	alloc_free(ia.ix);
+	return r;
+}
+
+static struct mechanisms {
+  char *mechanism;
+  int (*func)(char *spec, char *mask);
+  unsigned int takes_spec  : 1;
+  unsigned int takes_mask  : 1;
+  unsigned int expands     : 1;
+  unsigned int filldomain  : 1;
+  int defresult            : 4;
+} mechanisms[] = {
+  { "all",      0,          0,0,0,0,SPF_OK   }
+, { "include",  spf_include,1,0,1,0,0        }
+, { "a",        spf_a,      1,1,1,1,0        }
+, { "mx",       spf_mx,     1,1,1,1,0        }
+, { "ptr",      spf_ptr,    1,0,1,1,0        }
+, { "ip4",      spf_ip,     1,1,0,0,0        }
+, { "ip6",      0,          1,1,0,0,SPF_NONE }
+, { "exists",   spf_exists, 1,0,1,0,0        }
+, { "extension",0,          1,1,0,0,SPF_EXT  }
+, { 0,          0,          1,1,0,0,SPF_EXT  }
+};
+
+static int spfmech(char *mechanism, char *spec, char *mask, char *domain)
+{
+	struct mechanisms *mech;
+	stralloc sa = {0};
+	int r;
+	int pos;
+
+	for(mech = mechanisms; mech->mechanism; mech++)
+		if (str_equal(mech->mechanism, mechanism)) break;
+
+	if (mech->takes_spec && !spec && mech->filldomain) spec = domain;
+	if (!mech->takes_spec != !spec) return SPF_SYNTAX;
+	if (!mech->takes_mask && mask) return SPF_SYNTAX;
+	if (!mech->func) return mech->defresult;
+
+	if (!stralloc_readyplus(&sa, 0)) return SPF_NOMEM;
+	if (mech->expands && spec != domain) {
+		if (!spfexpand(&sa, spec, domain)) return SPF_NOMEM;
+		for (pos = 0; (sa.len - pos) > 255;) {
+			pos += byte_chr(sa.s + pos, sa.len - pos, '.');
+			if (pos < sa.len) pos++;
+		}
+		sa.len -= pos;
+		if (pos > 0) byte_copy(sa.s, sa.len, sa.s + pos);
+		stralloc_0(&sa);
+		spec = sa.s;
+	}
+
+	r = mech->func(spec, mask);
+
+	alloc_free(sa.s);
+	return r;
+}
+
+static struct default_aliases {
+  char *alias;
+  int defret;
+} default_aliases[] = {
+  { "allow",   SPF_OK }
+, { "pass",    SPF_OK }
+, { "deny",    SPF_FAIL }
+, { "softdeny",SPF_SOFTFAIL }
+, { "fail",    SPF_FAIL }
+, { "softfail",SPF_SOFTFAIL }
+, { "unknown", SPF_NEUTRAL }
+, { 0,         SPF_UNKNOWN }
+};
+
+static int spflookup(stralloc *domain)
+{
+	stralloc spf = {0};
+	stralloc sa = {0};
+	struct default_aliases *da;
+	int main = !recursion;
+	int local_pos = -1;
+	int r, q;
+	int begin, pos;
+	int i;
+	int prefix;
+	int done;
+	int guessing = 0;
+	char *p;
+	char *x;
+
+	if (!stralloc_readyplus(&spf, 0)) return SPF_NOMEM;
+	if (!stralloc_readyplus(&sa, 0)) return SPF_NOMEM;
+
+	/* fallthrough result */
+	if (main) hdr_none();
+
+redirect:
+	if (++recursion > 20) {
+		alloc_free(spf.s);
+		alloc_free(sa.s);
+		hdr_unknown_msg("Maximum nesting level exceeded, possible loop");
+		return SPF_SYNTAX;
+	}
+
+	if (!stralloc_0(domain)) return SPF_NOMEM;
+	if (!stralloc_copy(&expdomain, domain)) return SPF_NOMEM;
+
+	r = spfget(&spf, domain);
+	if (r == SPF_NONE) {
+		if (!main) { alloc_free(spf.s); return r; }
+
+		if (spfguess.len) {
+			/* try to guess */
+			guessing = 1;
+			if (!stralloc_copys(&spf, spfguess.s)) return SPF_NOMEM;
+			if (!stralloc_append(&spf, " ")) return SPF_NOMEM;
+		} else
+			spf.len = 0;
+
+		/* append local rulest */
+		if (spflocal.len) {
+			local_pos = spf.len;
+			if (!stralloc_cats(&spf, spflocal.s)) return SPF_NOMEM;
+		}
+		if (!stralloc_0(&spf)) return SPF_NOMEM;
+
+		expdomain.len = 0;
+	} else if (r == SPF_OK) {
+		if (!stralloc_0(&spf)) return SPF_NOMEM;
+		if (main) hdr_neutral();
+		r = SPF_NEUTRAL;
+
+		/* try to add local rules before fail all mechs */
+		if (main && spflocal.len) {
+			pos = 0;
+			p = (char *) 0;
+			while(pos < spf.len) {
+				NXTOK(begin, pos, &spf);
+				if (!spf.s[begin]) continue;
+
+				if (p && spf.s[begin] != *p) p = (char *) 0;
+				if (!p && (spf.s[begin] == '-' || spf.s[begin] == '~' ||
+				           spf.s[begin] == '?')) p = &spf.s[begin];
+
+				if (p && p > spf.s && str_equal(spf.s + begin + 1, "all")) {
+					/* ok, we can insert the local rules at p */
+					local_pos = p - spf.s;
+
+					stralloc_readyplus(&spf, spflocal.len);
+					p = spf.s + local_pos;
+					byte_copyr(p + spflocal.len, spf.len - local_pos, p);
+					byte_copy(p, spflocal.len, spflocal.s);
+					spf.len += spflocal.len;
+
+					pos += spflocal.len;
+					break;
+				}
+			}
+
+			if (pos >= spf.len) pos = spf.len - 1;
+			for(i = 0; i < pos; i++)
+				if (!spf.s[i]) spf.s[i] = ' ';
+		}
+	} else {
+		alloc_free(spf.s);
+		return r;
+	}
+
+	/* if "+all" is seen, reject the message */
+	x = env_get("SPF_BLOCK_PLUS_ALL");
+	if (x)
+		if (!str_equal(x,"0"))
+			if (matchregex(spf.s,"\\+all")) {
+				alloc_free(spf.s);
+				alloc_free(sa.s);
+				hdr_plusall();
+				return SPF_FAIL;
+			}
+
+	pos = 0;
+	done = 0;
+	while(pos < spf.len) {
+		NXTOK(begin, pos, &spf);
+		if (!spf.s[begin]) continue;
+
+		/* in local ruleset? */
+		if (!done && local_pos >= 0 && begin >= local_pos) {
+			if (begin < (local_pos + spflocal.len))
+				expdomain.len = 0;
+			else
+				if (!stralloc_copy(&expdomain, domain))
+					return SPF_NOMEM;
+		}
+
+		for (p = spf.s + begin;*p;++p)
+			if (*p == ':' || *p == '/' || *p == '=') break;
+
+		if (*p == '=') {
+			*p++ = 0;
+
+			/* modifiers are simply handled here */
+			if (str_equal(spf.s + begin, "redirect")) {
+				if (done) continue;
+
+				if (!spfexpand(&sa, p, domain->s)) return SPF_NOMEM;
+				stralloc_copy(domain, &sa);
+
+				hdr_unknown();
+				r = SPF_UNKNOWN;
+
+				goto redirect;
+			} else if (str_equal(spf.s + begin, "default")) {
+				if (done) continue;
+
+				for(da = default_aliases; da->alias; ++da)
+					if (str_equal(da->alias, p)) break;
+
+				r = da->defret;
+			} else if (str_equal(spf.s + begin, "exp")) {
+				strsalloc ssa = {0};
+
+				if (!main) continue;
+
+				if (!stralloc_copys(&sa, p)) return SPF_NOMEM;
+				switch(dns_txt(&ssa, &sa)) {
+					case DNS_MEM: return SPF_NOMEM;
+					case DNS_SOFT: continue; /* FIXME... */
+					case DNS_HARD: continue;
+				}
+
+				explanation.len = 0;
+				for(i = 0; i < ssa.len; i++) {
+					if (!stralloc_cat(&explanation, &ssa.sa[i])) return SPF_NOMEM;
+					if (i < (ssa.len - 1))
+						if (!stralloc_append(&explanation, "\n")) return SPF_NOMEM;
+
+					alloc_free(ssa.sa[i].s);
+				}
+				if (!stralloc_0(&explanation)) return SPF_NOMEM;
+			} /* and unknown modifiers are ignored */
+		} else if (!done) {
+			if (!stralloc_copys(&sa, spf.s + begin)) return SPF_NOMEM;
+			if (!stralloc_0(&sa)) return SPF_NOMEM;
+
+			switch(spf.s[begin]) {
+				case '-': begin++; prefix = SPF_FAIL; break;
+				case '~': begin++; prefix = SPF_SOFTFAIL; break;
+				case '+': begin++; prefix = SPF_OK; break;
+				case '?': begin++; prefix = SPF_NEUTRAL; break;
+				default: prefix = SPF_OK;
+			}
+
+			if (*p == '/') {
+				*p++ = 0;
+				q = spfmech(spf.s + begin, 0, p, domain->s);
+			} else {
+				if (*p) *p++ = 0;
+				i = str_chr(p, '/');
+				if (p[i] == '/') {
+					p[i++] = 0;
+					q = spfmech(spf.s + begin, p, p + i, domain->s);
+				} else if (i > 0)
+					q = spfmech(spf.s + begin, p, 0, domain->s);
+				else
+					q = spfmech(spf.s + begin, 0, 0, domain->s);
+			}
+
+			if (q == SPF_OK) q = prefix;
+
+			switch(q) {
+				case SPF_OK: hdr_pass(); break;
+				case SPF_NEUTRAL: hdr_neutral(); break;
+				case SPF_SYNTAX: hdr_syntax(); break;
+				case SPF_SOFTFAIL: hdr_softfail(); break;
+				case SPF_FAIL: hdr_fail(); break;
+				case SPF_EXT: hdr_ext(sa.s); break;
+				case SPF_ERROR:
+					if (!guessing)
+						break;
+					if (local_pos >= 0 && begin >= local_pos)
+						break;
+					hdr_none();
+					q = SPF_NONE;
+					break;
+				case SPF_NONE: continue;
+			}
+
+			r = q;
+			done = 1; /* we're done, no more mechanisms */
+		}
+	}
+
+	/* we fell through, no local rule applied */
+	if (!done && !stralloc_copy(&expdomain, domain)) return SPF_NOMEM;
+
+	alloc_free(spf.s);
+	alloc_free(sa.s);
+	return r;
+}
+
+int spfcheck()
+{
+	stralloc domain = {0};
+	int pos;
+	int r;
+
+	pos = byte_rchr(addr.s, addr.len, '@') + 1;
+	if (pos < addr.len) {
+		if (!stralloc_copys(&domain, addr.s + pos)) return SPF_NOMEM;
+	} else {
+		pos = str_rchr(helohost.s, '@');
+		if (helohost.s[pos]) {
+			if (!stralloc_copys(&domain, helohost.s + pos + 1)) return SPF_NOMEM;
+		} else
+			if (!stralloc_copys(&domain, helohost.s)) return SPF_NOMEM;
+	}
+	if (!stralloc_copys(&explanation, spfexp.s)) return SPF_NOMEM;
+	if (!stralloc_0(&explanation)) return SPF_NOMEM;
+	recursion = 0;
+
+	if (!remoteip || !ip_scan(remoteip, &ip)) {
+		hdr_unknown_msg("No IP address in conversation");
+		return SPF_UNKNOWN;
+	}
+
+	if (!stralloc_readyplus(&expdomain, 0)) return SPF_NOMEM;
+	if (!stralloc_readyplus(&errormsg, 0)) return SPF_NOMEM;
+	expdomain.len = 0;
+	errormsg.len = 0;
+	sender_fqdn.len = 0;
+	received = (char *) 0;
+
+	if ((ip.d[0] == 127 && ip.d[1] == 0 && ip.d[2] == 0 && ip.d[3] == 1) || ipme_is(&ip))
+		{ hdr_pass(); r = SPF_OK; }
+	else
+		r = spflookup(&domain);
+
+	if (r < 0) r = SPF_UNKNOWN;
+
+	alloc_free(domain.s);
+	return r;
+}
+
+int spfexplanation(sa)
+stralloc *sa;
+{
+	return spfexpand(sa, explanation.s, expdomain.s);
+}
+
+int spfinfo(sa)
+stralloc *sa;
+{
+	stralloc tmp = {0};
+	if (!stralloc_copys(&tmp, received)) return 0;
+	if (!stralloc_0(&tmp)) return 0;
+	if (!spfexpand(sa, tmp.s, expdomain.s)) return 0;
+	alloc_free(tmp.s);
+	return 1;
+}
diff -ruN qmail-1.03-factory/spf.h qmail-1.03-7.10/spf.h
--- qmail-1.03-factory/spf.h	1969-12-31 19:00:00.000000000 -0500
+++ qmail-1.03-7.10/spf.h	2010-02-14 22:53:32.000000000 -0500
@@ -0,0 +1,20 @@
+#ifndef SPF_H
+#define SPF_H
+
+#define SPF_OK       0
+#define SPF_NONE     1
+#define SPF_UNKNOWN  2
+#define SPF_NEUTRAL  3
+#define SPF_SOFTFAIL 4
+#define SPF_FAIL     5
+#define SPF_ERROR    6
+#define SPF_NOMEM    7
+
+#define SPF_DEFEXP   "See http://spf.pobox.com/" \
+                     "why.html?sender=%{S}&ip=%{I}&receiver=%{xR}"
+
+extern int spfcheck();
+extern int spfexplanation();
+extern int spfinfo();
+
+#endif
diff -ruN qmail-1.03-factory/spfquery.c qmail-1.03-7.10/spfquery.c
--- qmail-1.03-factory/spfquery.c	1969-12-31 19:00:00.000000000 -0500
+++ qmail-1.03-7.10/spfquery.c	2010-02-14 22:53:32.000000000 -0500
@@ -0,0 +1,84 @@
+#include "substdio.h"
+#include "subfd.h"
+#include "stralloc.h"
+#include "alloc.h"
+#include "spf.h"
+#include "exit.h"
+
+void die(e,s) int e; char *s; { substdio_putsflush(subfderr,s); _exit(e); }
+void die_usage() { die(100,"fatal: invalid usage\nusage: spfquery <sender-ip> <sender-helo/ehlo> <envelope-from> [<local rules>] [<best guess rules>]\n"); }
+void die_nomem() { die(111,"fatal: out of memory\n"); }
+
+stralloc addr = {0};
+stralloc helohost = {0};
+char *remoteip;
+char *local;
+
+stralloc spflocal = {0};
+stralloc spfguess = {0};
+stralloc spfexp = {0};
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+	stralloc sa = {0};
+	int r;
+
+	if (argc < 4) die_usage();
+
+	remoteip = (char *)strdup(argv[1]);
+	local = "localhost";
+
+	if (!stralloc_copys(&helohost, argv[2])) die_nomem();
+	if (!stralloc_0(&helohost)) die_nomem();
+
+	if (!stralloc_copys(&addr, argv[3])) die_nomem();
+	if (!stralloc_0(&addr)) die_nomem();
+
+	if (argc > 4) {
+		if (!stralloc_copys(&spflocal, argv[4])) die_nomem();
+		if (spflocal.len && !stralloc_0(&spflocal)) die_nomem();
+	}
+
+	if (argc > 5) {
+		if (!stralloc_copys(&spfguess, argv[5])) die_nomem();
+		if (spfguess.len && !stralloc_0(&spfguess)) die_nomem();
+	}
+
+	if (argc > 6) {
+		if (!stralloc_copys(&spfexp, argv[6])) die_nomem();
+	} else
+		if (!stralloc_copys(&spfexp, SPF_DEFEXP)) die_nomem();
+	if (spfexp.len && !stralloc_0(&spfexp)) die_nomem();
+
+	dns_init(0);
+	r = spfcheck();
+	if (r == SPF_NOMEM) die_nomem();
+
+	substdio_puts(subfdout,"result=");
+	switch(r) {
+		case SPF_OK: substdio_puts(subfdout,"pass"); break;
+		case SPF_NONE: substdio_puts(subfdout,"none"); break;
+		case SPF_UNKNOWN: substdio_puts(subfdout,"unknown"); break;
+		case SPF_NEUTRAL: substdio_puts(subfdout,"neutral"); break;
+		case SPF_SOFTFAIL: substdio_puts(subfdout,"softfail"); break;
+		case SPF_FAIL: substdio_puts(subfdout,"fail"); break;
+		case SPF_ERROR: substdio_puts(subfdout,"error"); break;
+	}
+
+	if (r == SPF_FAIL) {
+		substdio_puts(subfdout,": ");
+		if (!spfexplanation(&sa)) die_nomem();
+		substdio_put(subfdout,sa.s,sa.len);
+	}
+
+	substdio_putsflush(subfdout,"\n");
+
+	substdio_puts(subfdout,"Received-SPF: ");
+	if (!spfinfo(&sa)) die_nomem();
+	substdio_put(subfdout,sa.s,sa.len);
+	substdio_putsflush(subfdout,"\n");
+
+	_exit(0);
+}
diff -ruN qmail-1.03-factory/str.h qmail-1.03-7.10/str.h
--- qmail-1.03-factory/str.h	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/str.h	2010-02-14 22:53:32.000000000 -0500
@@ -2,6 +2,7 @@
 #define STR_H
 
 extern unsigned int str_copy();
+extern unsigned int str_copyb();
 extern int str_diff();
 extern int str_diffn();
 extern unsigned int str_len();
diff -ruN qmail-1.03-factory/str_cpyb.c qmail-1.03-7.10/str_cpyb.c
--- qmail-1.03-factory/str_cpyb.c	1969-12-31 19:00:00.000000000 -0500
+++ qmail-1.03-7.10/str_cpyb.c	2010-02-14 22:53:32.000000000 -0500
@@ -0,0 +1,18 @@
+#include "str.h"
+
+unsigned int str_copyb(s,t,max)
+register char *s;
+register char *t;
+unsigned int max;
+{
+  register int len;
+
+  len = 0;
+  while (max-- > 0) {
+    if (!(*s = *t)) return len; ++s; ++t; ++len;
+    if (!(*s = *t)) return len; ++s; ++t; ++len;
+    if (!(*s = *t)) return len; ++s; ++t; ++len;
+    if (!(*s = *t)) return len; ++s; ++t; ++len;
+  }
+  return len;
+}
diff -ruN qmail-1.03-factory/strerr.h qmail-1.03-7.10/strerr.h
--- qmail-1.03-factory/strerr.h	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/strerr.h	2010-02-14 22:53:32.000000000 -0500
@@ -25,56 +25,80 @@
 #define STRERR_SYS3(r,se,a,b,c) \
 { se.who = &strerr_sys; se.x = a; se.y = b; se.z = c; return r; }
 
+#define strerr_warn9(x1,x2,x3,x4,x5,x6,x7,x8,x9,se) \
+strerr_warn((x1),(x2),(x3),(x4),(x5),(x6),(x7),(x8),(x9),(struct strerr *) (se))
+#define strerr_warn8(x1,x2,x3,x4,x5,x6,x7,x8,se) \
+strerr_warn((x1),(x2),(x3),(x4),(x5),(x6),(x7),(x8),(char *) 0,(struct strerr *) (se))
+#define strerr_warn7(x1,x2,x3,x4,x5,x6,x7,se) \
+strerr_warn((x1),(x2),(x3),(x4),(x5),(x6),(x7),(char *) 0,(char *) 0,(struct strerr *) (se))
 #define strerr_warn6(x1,x2,x3,x4,x5,x6,se) \
-strerr_warn((x1),(x2),(x3),(x4),(x5),(x6),(struct strerr *) (se))
+strerr_warn((x1),(x2),(x3),(x4),(x5),(x6),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
 #define strerr_warn5(x1,x2,x3,x4,x5,se) \
-strerr_warn((x1),(x2),(x3),(x4),(x5),(char *) 0,(struct strerr *) (se))
+strerr_warn((x1),(x2),(x3),(x4),(x5),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
 #define strerr_warn4(x1,x2,x3,x4,se) \
-strerr_warn((x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(struct strerr *) (se))
+strerr_warn((x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
 #define strerr_warn3(x1,x2,x3,se) \
-strerr_warn((x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+strerr_warn((x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
 #define strerr_warn2(x1,x2,se) \
-strerr_warn((x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+strerr_warn((x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
 #define strerr_warn1(x1,se) \
-strerr_warn((x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+strerr_warn((x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
 
+#define strerr_die9(e,x1,x2,x3,x4,x5,x6,x7,x8,x9,se) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(x7),(x8),(x9),(struct strerr *) (se))
+#define strerr_die8(e,x1,x2,x3,x4,x5,x6,x7,x8,se) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(x7),(x8),(char *) 0,(struct strerr *) (se))
+#define strerr_die7(e,x1,x2,x3,x4,x5,x6,x7,se) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(x7),(char *) 0,(char *) 0,(struct strerr *) (se))
 #define strerr_die6(e,x1,x2,x3,x4,x5,x6,se) \
-strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(struct strerr *) (se))
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
 #define strerr_die5(e,x1,x2,x3,x4,x5,se) \
-strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,(struct strerr *) (se))
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
 #define strerr_die4(e,x1,x2,x3,x4,se) \
-strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(struct strerr *) (se))
+strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
 #define strerr_die3(e,x1,x2,x3,se) \
-strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
 #define strerr_die2(e,x1,x2,se) \
-strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
 #define strerr_die1(e,x1,se) \
-strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
 
+#define strerr_die9sys(e,x1,x2,x3,x4,x5,x6,x7,x8,x9,se) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(x7),(x8),(x9),&strerr_sys)
+#define strerr_die8sys(e,x1,x2,x3,x4,x5,x6,x7,x8,se) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(x7),(x8),(char *) 0,&strerr_sys)
+#define strerr_die7sys(e,x1,x2,x3,x4,x5,x6,x7,se) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(x7),(char *) 0,(char *) 0,&strerr_sys)
 #define strerr_die6sys(e,x1,x2,x3,x4,x5,x6) \
-strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),&strerr_sys)
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(char *) 0,(char *) 0,(char *) 0,&strerr_sys)
 #define strerr_die5sys(e,x1,x2,x3,x4,x5) \
-strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,&strerr_sys)
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,(char *) 0,(char *) 0,(char *) 0,&strerr_sys)
 #define strerr_die4sys(e,x1,x2,x3,x4) \
-strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,&strerr_sys)
+strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,&strerr_sys)
 #define strerr_die3sys(e,x1,x2,x3) \
-strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,&strerr_sys)
+strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,&strerr_sys)
 #define strerr_die2sys(e,x1,x2) \
-strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,&strerr_sys)
+strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,&strerr_sys)
 #define strerr_die1sys(e,x1) \
-strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,&strerr_sys)
+strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,&strerr_sys)
 
+#define strerr_die9x(e,x1,x2,x3,x4,x5,x6,x7,x8,x9,se) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(x7),(x8),(x9),(struct strerr *) 0)
+#define strerr_die8x(e,x1,x2,x3,x4,x5,x6,x7,x8,se) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(x7),(x8),(char *) 0,(struct strerr *) 0)
+#define strerr_die7x(e,x1,x2,x3,x4,x5,x6,x7,se) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(x7),(char *) 0,(char *) 0,(struct strerr *) 0)
 #define strerr_die6x(e,x1,x2,x3,x4,x5,x6) \
-strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(struct strerr *) 0)
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0)
 #define strerr_die5x(e,x1,x2,x3,x4,x5) \
-strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,(struct strerr *) 0)
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0)
 #define strerr_die4x(e,x1,x2,x3,x4) \
-strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(struct strerr *) 0)
+strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0)
 #define strerr_die3x(e,x1,x2,x3) \
-strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0)
+strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0)
 #define strerr_die2x(e,x1,x2) \
-strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0)
+strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0)
 #define strerr_die1x(e,x1) \
-strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0)
+strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0)
 
 #endif
diff -ruN qmail-1.03-factory/strerr_die.c qmail-1.03-7.10/strerr_die.c
--- qmail-1.03-factory/strerr_die.c	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/strerr_die.c	2010-02-14 22:53:32.000000000 -0500
@@ -3,8 +3,8 @@
 #include "exit.h"
 #include "strerr.h"
 
-void strerr_warn(x1,x2,x3,x4,x5,x6,se)
-char *x1; char *x2; char *x3; char *x4; char *x5; char *x6;
+void strerr_warn(x1,x2,x3,x4,x5,x6,x7,x8,x9,se)
+char *x1; char *x2; char *x3; char *x4; char *x5; char *x6; char *x7; char *x8; char *x9;
 struct strerr *se;
 {
   strerr_sysinit();
@@ -15,6 +15,9 @@
   if (x4) substdio_puts(subfderr,x4);
   if (x5) substdio_puts(subfderr,x5);
   if (x6) substdio_puts(subfderr,x6);
+  if (x7) substdio_puts(subfderr,x7);
+  if (x8) substdio_puts(subfderr,x8);
+  if (x9) substdio_puts(subfderr,x9);
  
   while(se) {
     if (se->x) substdio_puts(subfderr,se->x);
@@ -27,11 +30,11 @@
   substdio_flush(subfderr);
 }
 
-void strerr_die(e,x1,x2,x3,x4,x5,x6,se)
+void strerr_die(e,x1,x2,x3,x4,x5,x6,x7,x8,x9,se)
 int e;
-char *x1; char *x2; char *x3; char *x4; char *x5; char *x6;
+char *x1; char *x2; char *x3; char *x4; char *x5; char *x6; char *x7; char *x8; char *x9;
 struct strerr *se;
 {
-  strerr_warn(x1,x2,x3,x4,x5,x6,se);
+  strerr_warn(x1,x2,x3,x4,x5,x6,x7,x8,x9,se);
   _exit(e);
 }
diff -ruN qmail-1.03-factory/strsalloc.c qmail-1.03-7.10/strsalloc.c
--- qmail-1.03-factory/strsalloc.c	1969-12-31 19:00:00.000000000 -0500
+++ qmail-1.03-7.10/strsalloc.c	2010-02-14 22:53:32.000000000 -0500
@@ -0,0 +1,7 @@
+#include "alloc.h"
+#include "gen_allocdefs.h"
+#include "stralloc.h"
+#include "strsalloc.h"
+
+GEN_ALLOC_readyplus(strsalloc,stralloc,sa,len,a,i,n,x,10,strsalloc_readyplus)
+GEN_ALLOC_append(strsalloc,stralloc,sa,len,a,i,n,x,10,strsalloc_readyplus,strsalloc_append)
diff -ruN qmail-1.03-factory/strsalloc.h qmail-1.03-7.10/strsalloc.h
--- qmail-1.03-factory/strsalloc.h	1969-12-31 19:00:00.000000000 -0500
+++ qmail-1.03-7.10/strsalloc.h	2010-02-14 22:53:32.000000000 -0500
@@ -0,0 +1,12 @@
+#ifndef STRSALLOC_H
+#define STRSALLOC_H
+
+#include "stralloc.h"
+
+#include "gen_alloc.h"
+
+GEN_ALLOC_typedef(strsalloc,stralloc,sa,len,a)
+extern int strsalloc_readyplus();
+extern int strsalloc_append();
+
+#endif
diff -ruN qmail-1.03-factory/tcp-env.c qmail-1.03-7.10/tcp-env.c
--- qmail-1.03-factory/tcp-env.c	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/tcp-env.c	2010-02-14 22:53:32.000000000 -0500
@@ -10,6 +10,7 @@
 #include "scan.h"
 #include "subgetopt.h"
 #include "ip.h"
+#include "strsalloc.h"
 #include "dns.h"
 #include "byte.h"
 #include "remoteinfo.h"
@@ -34,6 +35,7 @@
 int argc;
 char *argv[];
 {
+ strsalloc ssa = {0};
  int dummy;
  char *proto;
  int opt;
@@ -74,12 +76,13 @@
    temp[ip_fmt(temp,&iplocal)] = 0;
    if (!env_put2("TCPLOCALIP",temp)) die();
 
-   switch(dns_ptr(&localname,&iplocal))
+   switch(dns_ptr(&ssa,&iplocal))
     {
      case DNS_MEM: die();
      case DNS_SOFT:
        if (!stralloc_copys(&localname,"softdnserror")) die();
      case 0:
+       if (!stralloc_copy(&localname,&ssa.sa[0])) die();
        if (!stralloc_0(&localname)) die();
        case_lowers(localname.s);
        if (!env_put2("TCPLOCALHOST",localname.s)) die();
@@ -99,12 +102,13 @@
    temp[ip_fmt(temp,&ipremote)] = 0;
    if (!env_put2("TCPREMOTEIP",temp)) die();
 
-   switch(dns_ptr(&remotename,&ipremote))
+   switch(dns_ptr(&ssa,&ipremote))
     {
      case DNS_MEM: die();
      case DNS_SOFT:
        if (!stralloc_copys(&remotename,"softdnserror")) die();
      case 0:
+       if (!stralloc_copy(&remotename,&ssa.sa[0])) die();
        if (!stralloc_0(&remotename)) die();
        case_lowers(remotename.s);
        if (!env_put2("TCPREMOTEHOST",remotename.s)) die();
diff -ruN qmail-1.03-factory/timeoutconn.c qmail-1.03-7.10/timeoutconn.c
--- qmail-1.03-factory/timeoutconn.c	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/timeoutconn.c	2010-02-14 22:53:32.000000000 -0500
@@ -9,6 +9,87 @@
 #include "ip.h"
 #include "byte.h"
 #include "timeoutconn.h"
+#include "control.h"
+#include "constmap.h"
+#include "stralloc.h"
+
+/* if 1, bind() failing will be ignored */
+#define IGNORE_BIND_ERROR 0
+
+struct ip_address iplocal;
+int bindlocal = 0 ;
+
+int bind_by_sender(s,addr,force)
+int s;
+char *addr;
+int force;
+{
+  int j;
+  stralloc stext = {0} ;
+  struct constmap senderip ;
+  stralloc domain = {0} ;
+  char *chosenip = (char *) 0 ;
+
+  if (!force) if(bindlocal) return 0; /* already bound, no bind */
+
+  switch ( control_readfile ( &stext , "control/senderip" , 0 ) )
+  {
+    case  0: return  0 ; /* no file, no bind */
+    case -1: return -2 ; /* error */
+    case  1:
+      if ( ! constmap_init ( &senderip , stext.s , stext.len , 1 ) )
+        return -3 ;
+  }
+
+  j = str_chr(addr,'@') ;
+  stralloc_copys ( &domain , addr[j] ? &(addr[j+1]) : addr ) ;
+  stralloc_0 ( &domain ) ;
+  domain.len -- ;
+
+  chosenip = constmap ( &senderip , domain.s , domain.len ) ;
+  if ( !chosenip || !*chosenip ) return 0 ; /* no match, no bind */
+  if ( ! ip_scan ( chosenip , &iplocal ) ) return -4 ; /* invalid IP */
+  bindlocal = 1 ;
+  return 0 ;
+}
+
+int bind_by_remoteip(s,ip,force)
+int s;
+struct ip_address *ip;
+int force;
+{
+  struct sockaddr_in salocal;
+  char *ipstr, ipstring[IPFMT+1];
+  int iplen;
+  stralloc routes = {0};
+  struct constmap bindroutes;
+  char *bindroute = (char *)0;
+
+  if (!force) if(bindlocal) return 0; /* already bound, no bind */
+
+  /* make sure we have a control/bindroutes file */
+  switch(control_readfile(&routes,"control/bindroutes",0))
+  {
+    case  0: return  0; /* no file, no bind to worry about */
+    case -1: return -2; /* buggered up somewhere, urgh! */
+    case  1: if (!constmap_init(&bindroutes,routes.s,routes.len,1)) return -3;
+  }
+
+  /* search for d.d.d.d, d.d.d., d.d., d., none */
+  ipstring[0] = '.'; /* "cheating", but makes the loop check easier below! */
+  ipstr = ipstring+1;
+  iplen = ip_fmt(ipstr,ip); /* Well, Dan seems to trust its output! */
+
+  bindroute = constmap(&bindroutes,ipstr,iplen);
+  if (!bindroute) while (iplen--)  /* no worries - the lost char must be 0-9 */
+    if (ipstring[iplen] == '.') 
+      if (bindroute = constmap(&bindroutes,ipstr,iplen)) break;
+  if (!bindroute || !*bindroute) return 0; /* no bind required */
+  if (!ip_scan(bindroute,&iplocal)) return -4; /* wasn't an ip returned */
+  bindlocal = 1 ;
+  return 0;
+}
+
 
 int timeoutconn(s,ip,port,timeout)
 int s;
@@ -18,6 +99,7 @@
 {
   char ch;
   struct sockaddr_in sin;
+  struct sockaddr_in salocal;
   char *x;
   fd_set wfds;
   struct timeval tv;
@@ -30,8 +112,15 @@
  
   if (ndelay_on(s) == -1) return -1;
  
-  /* XXX: could bind s */
- 
+  /* bind s, if we've been given a local IP */
+  if ( bindlocal ) {
+    byte_zero ( &salocal , sizeof(salocal) ) ;
+    salocal.sin_family = AF_INET ;
+    byte_copy ( &salocal.sin_addr , 4 , &iplocal ) ;
+    if ( bind ( s , (struct sockaddr *) &salocal , sizeof(salocal) ) )
+      if ( ! IGNORE_BIND_ERROR ) return errno ;
+  }
+
   if (connect(s,(struct sockaddr *) &sin,sizeof(sin)) == 0) {
     ndelay_off(s);
     return 0;
diff -ruN qmail-1.03-factory/timeoutconn.h qmail-1.03-7.10/timeoutconn.h
--- qmail-1.03-factory/timeoutconn.h	1998-06-15 06:53:16.000000000 -0400
+++ qmail-1.03-7.10/timeoutconn.h	2010-02-14 22:53:32.000000000 -0500
@@ -2,5 +2,7 @@
 #define TIMEOUTCONN_H
 
 extern int timeoutconn();
+extern int bind_by_sender();
+extern int bind_by_remoteip();
 
 #endif
