This is a collection of several popular qmail patches rolled into one.  The 
patches included are:

smtp-auth patch v. 0.31 by Krzysztof Dabrowski <brush@elysium.pl> and
Eric M. Johnston <emj@postal.net>.  For more information please see:
http://members.elysium.pl/brush/qmail-smtpd-auth/

TLS patch by Frederik Vermeulen <jos-tls@Kotnet.org>.  Here's the original
patch with documentation at the top:
http://www.esat.kuleuven.ac.be/~vermeule/qmail/tls.patch

** The two patches above were merged together by Neal Groothuis.

qmail-queue patch by Bruce Guenter <bguenter-djb-qmail@qcc.sk.ca>.  Here's
the link to the original patch, and there is documentaion at the top as well:
http://www.qmail.org/qmailqueue-patch

mfcheck patch (check for valid dns on envelope sender) is Nagy Balazs.
http://lsc.kva.hu/dl/qmail-1.03-mfcheck.3.patch

oversize dns patch by Christopher K. Davis.
http://www.ckdhr.com/ckd/qmail-103.patch

Tarpit patch by Chris Johnson.  http://www.palomine.net/qmail/tarpit.html

qregex.patch-20020129 by Unix Pimps.  http://www.unixpimps.org/software/qregex/
This adds regular expression matchin go badmailfrom and badmailto.

Big Concurrency patch by Johannes Erdfelt
http://qmail.org/big-concurrency.patch

qmail-maildir++.patch by me.  Here's the link to the original patch:
http://shupp.org/patches/qmail-maildir++.patch
This adds maildirquota support to qmail-pop3d only and qmail-local.

Cheers,

Bill Shupp
www.shupp.org


###################### NOTES FROM ORIGINAL PATCHES ####################

TLS PATCH:

Frederik Vermeulen <qmail-tls akrul inoa.net> 20021228
http://inoa.net/qmail/qmail-1.03-tls.patch

This patch implements RFC2487 in qmail. This means you can 
get SSL or TLS encrypted and authenticated SMTP between 
the MTAs and from MUA to MTA. 
The code is considered experimental (but has worked for
many since its first release on 1999-03-21).

Usage: - install OpenSSL-0.9.6g http://www.openssl.org/
         (any 0.9.6 version is presumed to work)
       - apply patch to qmail-1.03 http://www.qmail.org/ 
         The patches to qmail-remote.c
         and qmail-smtpd.c can be applied separately.
       - provide a server certificate in /var/qmail/control/servercert.pem.
         "make cert" makes a self-signed certificate.
         "make cert-req" makes a certificate request.
         Note: you can add the CA certificate and intermediate
         certs to the end of servercert.pem.
       - replace qmail-smtpd and/or qmail-remote binary
       - verify operation (header information should show
         something like
         "Received [..] with DES-CBC3-SHA encrypted SMTP;")
         If you don't have a server to test with, you can test
         by sending mail to tag-ping@tbs-internet.com,
         which will bounce your mail.

Optional: - when DEBUG is defined, some extra TLS info will be logged
          - qmail-remote will authenticate with the certificate in
            /var/qmail/control/clientcert.pem. By preference this is
            the same as servercert.pem, where nsCertType should be 
            == server,client or be a generic certificate (no usage specified). 
          - when a 512 RSA key is provided in /var/qmail/control/rsa512.pem,
            this key will be used instead of on-the-fly generation by
       	    qmail-smtpd. Periodical replacement can be done by crontab:
       	    01 01 * * *  umask 0077; /usr/local/ssl/bin/openssl genrsa \
       	     -out /var/qmail/control/rsa512.new 512 > /dev/null 2>&1 &&\
       	     chown qmaild.qmail /var/qmail/control/rsa512.new && /bin/mv -f \
       	     /var/qmail/control/rsa512.new /var/qmail/control/rsa512.pem
          - server authentication:
            qmail-remote requires authentication from servers for which
            /var/qmail/control/tlshosts/host.dom.ain.pem exists.
            The .pem file contains the validating CA certificates
            (or self-signed server certificate).
            CommonName has to match.
            WARNING: this option may cause mail to be delayed, bounced,
            doublebounced, and lost.
          - client authentication:
            when relay rules would reject an incoming mail, 
            qmail-smtpd can allow the mail based on a presented cert.
            Certs are verified against a CA list in 
            /var/qmail/control/clientca.pem (eg. http://www.modssl.org/
            source/cvs/exp/mod_ssl/pkg.mod_ssl/pkg.sslcfg/ca-bundle.crt)
            and the cert email-address has to match a line in
            /var/qmail/control/tlsclients. This email-address is logged
            in the headers.
          - cipher selection:
            qmail-remote: 
              openssl cipher string (`man ciphers`) read from 
              /var/qmail/control/tlsclientciphers
            qmail-smtpd: 
              openssl cipher string read from TLSCIPHERS environment variable
              (can vary based on client IP address e.g.)
              or if that is not available /var/qmail/control/tlsserverciphers
          - smtps (deprecated SMTP over TLS via port 465):
            qmail-remote: when connecting to port 465 
            qmail-smtpd: when SMTPS environment variable is not empty

Caveats: - do a `make clean` after patching
         - binaries dynamically linked with current openssl versions need
           recompilation when the shared openssl libs are upgraded.
         - this patch could conflict with other patches (notably those
           replacing \n with \r\n, which is a bad idea on encrypted links).
         - some broken servers have a problem with TLSv1 compatibility.
           Uncomment the line where we set the SSL_OP_NO_TLSv1 option.
         - needs working /dev/urandom (or EGD for openssl versions >0.9.7)
           for seeding random number generator.
         - packagers should make sure that installing without a valid 
           servercert is impossible
         - when applied in combination with AUTH patch, AUTH patch
           should be applied first and first part of this patch
           will fail. This error can be ignored. Packagers should
           cut the first 12 lines of this patch to make a happy
           patch

Copyright: GPL
           Links with OpenSSL
           Inspiration and code from examples in SSLeay (E. Young
           <eay@cryptsoft.com> and T. Hudson <tjh@cryptsoft.com>),
           stunnel (M. Trojnara <mtrojnar@ddc.daewoo.com.pl>),
           Postfix/TLS (L. Jaenicke <Lutz.Jaenicke@aet.tu-cottbus.de>),
           modssl (R. Engelschall <rse@engelschall.com>),
           openssl examples of E. Rescorla <ekr@rtfm.com>.
           Debug code, tlscipher selection, many feature suggestions,
           French docs https://www.TBS-internet.com/ssl/qmail-tls.html 
           from Jean-Philippe Donnio <tag-ssl@tbs-internet.com>.
           Openssl usage consulting from B. M"oller <bmoeller@acm.org>.
           Bug report from A. Dustman <adustman@comstar.net>.
           Ssl_timeoutio functions (non-blocking io, timeouts), smtps, 
           auth, qmtp, mxps patch compatibility, man pages, code cleanup,
           improved error reporting, RFC2595 server identity check
           from A. Meltzer <albertikm (a) hotmail.com>.
           Bug report from Niall Richard Murphy, Tim Helton.

Bug reports: mailto:<jos-tls@kotnet.org>


TLS/SMTP-AUTH Composite:
A word of warning: the TLS part of this patch is not type-safe at
at least one point (hey, I didn't write it.)  I don't think this
causes problems on i386 architectures, but it made qmail-smtpd
crash frequently on an Alpha.  Commenting out the substdio_fdbuf(...);
call in qmail-smtpd appears to fix the issue.


QMAILQUEUE PATCH:

From qmail-return-26459-nelson-l-6ca615d95fcb76f5bb3afdc1a6ef9814=crynwr.com@list.cr.yp.to Mon Jan 25 21:40:06 1999
X-VM-v5-Data: ([nil nil nil nil nil nil nil nil nil]
	[nil nil nil nil nil nil nil nil nil nil nil nil "^From:" nil nil nil nil nil nil nil nil]
	nil)
Return-Path: <qmail-return-26459-nelson-l-6ca615d95fcb76f5bb3afdc1a6ef9814=crynwr.com@list.cr.yp.to>
Delivered-To: nelson-l-6ca615d95fcb76f5bb3afdc1a6ef9814@desk.crynwr.com
Received: (qmail 17256 invoked from network); 25 Jan 1999 21:40:03 -0000
Received: from ns.crynwr.com (192.203.178.14)
  by desk.crynwr.com with SMTP; 25 Jan 1999 21:40:03 -0000
Received: (qmail 246 invoked by uid 500); 25 Jan 1999 21:38:36 -0000
Delivered-To: nelson-l-6ca615d95fcb76f5bb3afdc1a6ef9814@crynwr.com
Received: (qmail 241 invoked by uid 0); 25 Jan 1999 21:38:34 -0000
Received: from muncher.math.uic.edu (131.193.178.181)
  by pdam.crynwr.com with SMTP; 25 Jan 1999 21:38:34 -0000
Received: (qmail 27129 invoked by uid 1002); 25 Jan 1999 21:37:13 -0000
Mailing-List: contact qmail-help@list.cr.yp.to; run by ezmlm
Precedence: bulk
Delivered-To: mailing list qmail@list.cr.yp.to
Received: (qmail 7464 invoked from network); 25 Jan 1999 21:37:13 -0000
Received: from hal.qcc.sk.ca (198.169.27.1)
  by muncher.math.uic.edu with SMTP; 25 Jan 1999 21:37:13 -0000
Received: (qmail 9995 invoked from network); 25 Jan 1999 21:37:22 -0000
Received: from mikhail.qcc.sk.ca (198.169.27.34)
  by hal.qcc.sk.ca with QMQP; 25 Jan 1999 21:37:22 -0000
Message-ID: <19990125153721.A20307@qcc.sk.ca>
Mime-Version: 1.0
Content-Type: text/plain; charset=us-ascii
X-Mailer: Mutt 0.91.1
From: Bruce Guenter <bguenter-djb-qmail@qcc.sk.ca>
To: qmail@list.cr.yp.to
Subject: QMAILQUEUE patch for qmail-1.03
Date: Mon, 25 Jan 1999 15:37:21 -0600

Greetings.

Appended is a patch to qmail-1.03 that causes any program that would run
qmail-queue to look for an environment variable QMAILQUEUE.  If it is
present, it is used in place of the string "bin/qmail-queue" when
running qmail-queue.  This could be used, for example, to add a program
into the qmail-smtpd->qmail-queue pipeline that could do filtering,
rewrite broken headers, etc. (this is my planned usage for it).

This has undergone virtually no testing, but it looks so simple that it
almost has to be correct.  No warranties, etc.  Note that the chdir to
/var/qmail is always done before exec'ing the program.

Does this look like a reasonable thing to do?
-- 
Bruce Guenter, QCC Communications Corp.  EMail: bruce.guenter@qcc.sk.ca
Phone: (306)249-0220               WWW: http://www.qcc.sk.ca/~bguenter/


MAILDIR++ PATCH:

                                                                       6/9/2002

This patch adds maildirquota (Maildir++) support to qmail-pop3d and
qmail-local.  It was created because when vpopmail switched to maildirquotas,
a user's quota usage was not decreased after deleting mail via qmail-pop3d.
Also, because .qmail files would allow qmail-local to write directly to a
Maildir whithout piping through vdelivermail first, quotas were not effective
for aliases.  Actually, this was the case with vpopmail's old quota system as
well.

This patch is not specific to vpopmail.  If you use qmail with other agents that
support Maildir++, this should work for you.

The functions used in this patch are taken from maildrop 1.3.9 and courier's
pop daemon, by Sam Varshavchik (www.courier-mta.org).  The Maildir++
specification, also by Sam, can be viewed here:

http://inter7.com/courierimap/README.maildirquota.html

However, Sam had NOTHING to do with this patch, so please don't bug him about
it.  Either bug me directly, or the vpopmail list (vchkpw@inter7.com), who
actually requested it.

Cheers,

Bill Shupp
hostmaster@shupp.org
www.shupp.org


BIG CONCURRENCY PATCH:

From: "Johannes Erdfelt" <jerdfelt@suse.com>
To: qmail@list.cr.yp.to
Subject: Re: mail volume
From: "Johannes Erdfelt" <jerdfelt@suse.com>
To: qmail@list.cr.yp.to
Subject: Re: mail volume
Date: 4 Aug 1999 20:41:00 -0700
Mime-Version: 1.0
Content-Type: multipart/mixed; boundary=7AUc2qLy4jB3hD7Z

--7AUc2qLy4jB3hD7Z
Content-Type: text/plain; charset=us-ascii

On Thu, Aug 05, 1999, richard@illuin.demon.co.uk <richard@illuin.demon.co.uk> wrote:
> On Wed, 4 Aug 1999, Daemeon Reiydelle wrote:
> 
> > (2.6 or later). There may be limitations within e.g. qmail-[lr]spawn
> > about how many children it can manage. I am not working with that code
> > right know so I don't know. Anyone?
> 
> This is what people have been trying to say -- the protocol between
> qmail-Xspawn and qmail-send only passes a single byte for the delivery
> attempt back in the status messages. if you want to increase the maximum
> number above 256 one has to modify qmail-send and the common code in
> qmail-Xspawn. making it a short should allow up to 2**16 concurrency
> remotes.
> 
> **CAUTION** if you do this one should realise that qmail-send might try to
> open 64K connections to the /same/ host because it doesn't maintain a
> per-domain concurrency. this is distinctly Unfriendly. I produced some
> code for qmail to do this, but when I asked my ISP if i could open >>1024
> connections to one of their mail relays for testing they were less than
> enthusiastic... (the code is on my desktop system somewhere between here
> and Austin where I'm moving to next week, so I can't email it, and without
> testing it I won't email it. the changes to up the concurrency are fairly 
> straightforward, the once for a per-domain concurrency are non-trivial)

This is the patch that I use at suse.com. We do almost 1 million
messages a day with this patch and concurrencyremote set to 400.

This patch comes with the standard disclaimer. No warranty, it may not
work, etc. But it works for me :)

It's also not pretty. It's against qmail-1.03+verh-0.02 (the ezmlm patch
l and h patch). So the offsets may be off a little bit.

JE


TARPIT PATCH:

Chris Johnson
dcj-qmaildoc@palomine.net

What's tarpitting? It's the practice of inserting a small sleep in an SMTP
session for each RCPT TO after some set number of RCPT TOs. The idea is to
thwart spammers who would hand your SMTP server a single message with a long
list of RCPT TOs. If a spammer were to attempt to use your server to relay a
message with, say, 10,000 recipients, and you inserted a five-second delay for
each recipient after the fiftieth, the spammer would be "tarpitted" and would
likely assume that his connection had stalled and give up.

The subject originally came up in a discussion on the qmail mailing list of
ways to run an open relay safely (I didn't suggest it, and I don't do that kind
of thing), but it could also be useful in keeping your own dial-up customers
from using you as a spam relay.

This patch will allow qmail-smtpd to do tarpitting. There are two control files
involved: control/tarpitcount and control/tarpitdelay. tarpitcount is the
number of RCPT TOs you accept before you start tarpitting, and tarpitdelay is
the number of seconds of delay to introduce after each subsequent RCPT TO.
tarpitcount defaults to 0 (which means no tarpitting), and tarpitdelay defaults
to 5. You can override both tarpitcount and tarpitdelay by setting TARPITCOUNT
and TARPITDELAY in qmail-smtpd's environment (with tcpserver). If you used the
earlier version of this patch, note that this version no longer uses the
NOTARPIT environment variable; set TARPITCOUNT to 0 to achieve the same effect.

############################### END NOTES ###############################


diff -urN ../qmail-1.03/Makefile ./Makefile
--- ../qmail-1.03/Makefile	Mon Jun 15 03:53:16 1998
+++ ./Makefile	Thu Jun  5 09:33:20 2003
@@ -136,6 +136,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 \
@@ -890,6 +894,38 @@
 readwrite.h open.h headerbody.h maildir.h strerr.h
 	./compile maildirwatch.c
 
+maildirgetquota.o: \
+compile maildirgetquota.c maildirgetquota.h maildirmisc.h
+	./compile maildirgetquota.c
+
+maildirflags.o: \
+compile maildirflags.c
+	./compile maildirflags.c
+
+maildiropen.o: \
+compile maildiropen.c maildirmisc.h
+	./compile maildiropen.c
+
+maildirparsequota.o: \
+compile maildirparsequota.c
+	./compile maildirparsequota.c
+
+maildirquota.o: \
+compile maildirquota.c maildirquota.h maildirmisc.h numlib.h
+	./compile maildirquota.c
+
+overmaildirquota.o: \
+compile overmaildirquota.c 
+	./compile overmaildirquota.c
+
+strtimet.o: \
+compile strtimet.c 
+	./compile strtimet.c
+
+strpidt.o: \
+compile strpidt.c 
+	./compile strpidt.c
+
 mailsubj: \
 warn-auto.sh mailsubj.sh conf-qmail conf-break conf-split
 	cat warn-auto.sh mailsubj.sh \
@@ -1174,12 +1210,15 @@
 load qmail-local.o qmail.o quote.o now.o gfrom.o myctime.o \
 slurpclose.o case.a getln.a getopt.a sig.a open.a seek.a lock.a fd.a \
 wait.a env.a stralloc.a alloc.a strerr.a substdio.a error.a str.a \
-fs.a datetime.a auto_qmail.o auto_patrn.o socket.lib
+fs.a datetime.a auto_qmail.o auto_patrn.o socket.lib maildirquota.o \
+maildirgetquota.o maildiropen.o maildirparsequota.o overmaildirquota.o \
+strtimet.o strpidt.o
 	./load qmail-local qmail.o quote.o now.o gfrom.o myctime.o \
 	slurpclose.o case.a getln.a getopt.a sig.a open.a seek.a \
 	lock.a fd.a wait.a env.a stralloc.a alloc.a strerr.a \
 	substdio.a error.a str.a fs.a datetime.a auto_qmail.o \
-	auto_patrn.o  `cat socket.lib`
+	auto_patrn.o  `cat socket.lib` maildirquota.o maildirgetquota.o \
+    maildiropen.o maildirparsequota.o overmaildirquota.o strtimet.o strpidt.o
 
 qmail-local.0: \
 qmail-local.8
@@ -1269,11 +1308,13 @@
 qmail-pop3d: \
 load qmail-pop3d.o commands.o case.a timeoutread.o timeoutwrite.o \
 maildir.o prioq.o now.o env.a strerr.a sig.a open.a getln.a \
-stralloc.a alloc.a substdio.a error.a str.a fs.a socket.lib
+stralloc.a alloc.a substdio.a error.a str.a fs.a socket.lib maildirquota.o \
+maildirparsequota.o maildirflags.o maildiropen.o strtimet.o strpidt.o
 	./load qmail-pop3d commands.o case.a timeoutread.o \
 	timeoutwrite.o maildir.o prioq.o now.o env.a strerr.a sig.a \
 	open.a getln.a stralloc.a alloc.a substdio.a error.a str.a \
-	fs.a  `cat socket.lib`
+	fs.a  `cat socket.lib` maildirquota.o maildirgetquota.o \
+    maildirparsequota.o maildirflags.o maildiropen.o strtimet.o strpidt.o
 
 qmail-pop3d.0: \
 qmail-pop3d.8
@@ -1446,7 +1487,8 @@
 	timeoutwrite.o timeoutconn.o tcpto.o now.o dns.o ip.o \
 	ipalloc.o ipme.o quote.o ndelay.a case.a sig.a open.a \
 	lock.a seek.a getln.a stralloc.a alloc.a substdio.a error.a \
-	str.a fs.a auto_qmail.o  `cat dns.lib` `cat socket.lib`
+	str.a fs.a auto_qmail.o  `cat dns.lib` `cat socket.lib` \
+	-lssl -lcrypto
 
 qmail-remote.0: \
 qmail-remote.8
@@ -1483,12 +1525,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
@@ -1532,17 +1574,17 @@
 	./compile qmail-showctl.c
 
 qmail-smtpd: \
-load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \
+load qmail-smtpd.o rcpthosts.o qregex.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 \
+fs.a auto_qmail.o base64.o socket.lib dns.o dns.lib
+	./load qmail-smtpd qregex.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  `cat \
-	socket.lib`
+	alloc.a substdio.a error.a str.a fs.a auto_qmail.o base64.o  `cat \
+	socket.lib` -lssl -lcrypto dns.o `cat dns.lib`
 
 qmail-smtpd.0: \
 qmail-smtpd.8
@@ -1553,7 +1595,8 @@
 substdio.h alloc.h auto_qmail.h control.h received.h constmap.h \
 error.h ipme.h ip.h ipalloc.h ip.h gen_alloc.h ip.h qmail.h \
 substdio.h str.h fmt.h scan.h byte.h case.h env.h now.h datetime.h \
-exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h
+exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h wait.h \
+fd.h base64.h
 	./compile qmail-smtpd.c
 
 qmail-start: \
@@ -1681,6 +1724,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
@@ -2139,3 +2186,24 @@
 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
+	cp /var/qmail/control/servercert.pem /var/qmail/control/clientcert.pem
+	chown vpopmail:vchkpw /var/qmail/control/servercert.pem
+	chown qmaild:qmail /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 -urN ../qmail-1.03/README.auth ./README.auth
--- ../qmail-1.03/README.auth	Wed Dec 31 16:00:00 1969
+++ ./README.auth	Thu Jun  5 09:33:20 2003
@@ -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 -urN ../qmail-1.03/README.qregex ./README.qregex
--- ../qmail-1.03/README.qregex	Wed Dec 31 16:00:00 1969
+++ ./README.qregex	Thu Jun  5 09:33:20 2003
@@ -0,0 +1,110 @@
+QREGEX (v2) - README [12/28/01]
+A Regular Expression matching patch for qmail 1.03
+
+
+OVERVIEW:
+
+qregex adds the ability to match address evelopes via Regular Expressions (REs)
+in the qmail-smtpd process. It has the abiltiy to match both `mail from` and
+`rcpt to` commands with no load at all the parent process. 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 noting is because qregex was re-written to better conform to the security
+gaurantee set forth by the author. The original version used stdio.h and stdlib.h
+for reading the control files where as v2 now uses all stralloc functions which
+are much more regulated against buffer overruns and the likes.
+See: http://cr.yp.to/qmail/guarantee.html
+
+
+
+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 provide a regex
+interface) but if you run into problems let me know.
+
+        - Solaris 2.7 (7, SunOS 5.7)
+	- Solaris 2.8 (8, SunOS 5.8)
+	- OpenBSD 2.8
+	- OpenBSD 2.9
+	- FreeBSD 4.3-RELEASE
+	- FreeBSD 5.0-CURRENT
+	- Linux
+
+
+
+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 like me it is installed as 'gpatch')
+
+- If this is a new setup.
+Uncompress and untar the qmail archive, copy the 'qregex.patch' file into the new
+qmail-1.03 directory and run "patch < qregex.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 this is an existing setup.
+FIRST: create your control files (see below).
+Copy the 'qregex.patch' file into your existing qmail source directory.
+Run "patch < qregex.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)
+
+
+
+CONTROL FILES:
+
+qregex provides you with two new control files.
+The first (which really isn't new) is "control/badmailfrom". This file used to be
+used to statically match addresses and now will contain your REs for matching from 
+the 'mail from' command.
+The second is "control/badmailto", it is the exact same as the first except it matches
+against the 'rcpt to' command.
+
+If you prefer you can symlink the two files (ln -s badmailfrom badmailto) and only
+need to maintain one set of rules. Beware this might cause problems in certian
+setups.
+        
+	Here's an example "badmailfrom" file.
+	-----------------------------------
+	# drop everything containing the word spam
+	.*spam.*
+	# force users to fully qualify themselves (ie deny "user", accept "user@domain")
+	!@
+	-----------------------------------
+
+	And "badmailto" (a litte more interesting)
+	-----------------------------------
+	# must not contain invalid characters, brakets or multiple @'s
+	[\W\D!%#:\*\^]
+	[\(\)]
+	[\{\}]
+	@.*@
+	-----------------------------------
+
+Also you can use the non-RE character '!' to start a RE to 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.
+
+
+INTERNALS:
+
+qregex (or regexmatch as the function is called) will be called during both the
+`rcpt to` and `mail from` handling routenes in "qmail-smtpd.c". When called it will
+read the proper control file then one by one compile and execute the regex on the
+envelope 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:
+All comments/questions/critisim welcomed...
+        www  : http://www.unixpimps.org/software/qregex
+	email: evan at unixpimps dot org
diff -urN ../qmail-1.03/TARGETS ./TARGETS
--- ../qmail-1.03/TARGETS	Mon Jun 15 03:53:16 1998
+++ ./TARGETS	Thu Jun  5 09:33:20 2003
@@ -15,6 +15,14 @@
 slurpclose.o
 make-makelib
 makelib
+maildirflags.o
+maildirparsequota.o
+maildiropen.o
+maildirgetquota.o
+maildirquota.o
+overmaildirquota.o
+strtimet.o
+strpidt.o
 case_diffb.o
 case_diffs.o
 case_lowerb.o
@@ -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
diff -urN ../qmail-1.03/base64.c ./base64.c
--- ../qmail-1.03/base64.c	Wed Dec 31 16:00:00 1969
+++ ./base64.c	Thu Jun  5 09:33:20 2003
@@ -0,0 +1,90 @@
+#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 i, j;
+  unsigned char a[4];
+  unsigned char b[3];
+  char *s;
+
+  if (l == 0)
+  {
+    if (!stralloc_copys(out,"")) return -1;
+    return 0;
+  }
+
+  if (!stralloc_ready(out,l + 2)) return -1; /* XXX generous */
+  s = out->s;
+
+  for (i = 0;i < l;i += 4) {
+    for (j = 0;j < 4;j++)
+      if ((i + j) < l && in[i + j] != B64PAD)
+      {
+        a[j] = str_chr(b64alpha,in[i + j]);
+        if (a[j] > 63) return 1;
+      }
+      else a[j] = 0;
+
+    b[0] = (a[0] << 2) | (a[1] >> 4);
+    b[1] = (a[1] << 4) | (a[2] >> 2);
+    b[2] = (a[2] << 6) | (a[3]);
+
+    *s++ = b[0];
+
+    if (in[i + 1] == B64PAD) break;
+    *s++ = b[1];
+
+    if (in[i + 2] == B64PAD) break;
+    *s++ = b[2];
+  }
+  out->len = s - out->s;
+  while (out->len && !out->s[out->len - 1]) --out->len; /* XXX avoid? */
+  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 -urN ../qmail-1.03/base64.h ./base64.h
--- ../qmail-1.03/base64.h	Wed Dec 31 16:00:00 1969
+++ ./base64.h	Thu Jun  5 09:33:20 2003
@@ -0,0 +1,7 @@
+#ifndef BASE64_H
+#define BASE64_H
+
+extern int b64decode();
+extern int b64encode();
+
+#endif
diff -urN ../qmail-1.03/chkspawn.c ./chkspawn.c
--- ../qmail-1.03/chkspawn.c	Mon Jun 15 03:53:16 1998
+++ ./chkspawn.c	Thu Jun  5 09:33:20 2003
@@ -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 -urN ../qmail-1.03/conf-cc ./conf-cc
--- ../qmail-1.03/conf-cc	Mon Jun 15 03:53:16 1998
+++ ./conf-cc	Thu Jun  5 09:33:20 2003
@@ -1,3 +1,3 @@
-cc -O2
+cc -O2 -DTLS -I/usr/local/ssl/include
 
 This will be used to compile .c files.
diff -urN ../qmail-1.03/conf-spawn ./conf-spawn
--- ../qmail-1.03/conf-spawn	Mon Jun 15 03:53:16 1998
+++ ./conf-spawn	Thu Jun  5 09:33:20 2003
@@ -1,4 +1,4 @@
-120
+509
 
 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
diff -urN ../qmail-1.03/dns.c ./dns.c
--- ../qmail-1.03/dns.c	Mon Jun 15 03:53:16 1998
+++ ./dns.c	Thu Jun  5 09:34:58 2003
@@ -21,10 +21,12 @@
 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];
@@ -45,18 +47,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 +83,7 @@
    if (i < QFIXEDSZ) return DNS_SOFT;
    responsepos += QFIXEDSZ;
   }
- numanswers = ntohs(response.hdr.ancount);
+ numanswers = ntohs(((HEADER *)response.buf)->ancount);
  return 0;
 }
 
@@ -270,6 +287,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 +355,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;
     }
diff -urN ../qmail-1.03/ipalloc.h ./ipalloc.h
--- ../qmail-1.03/ipalloc.h	Mon Jun 15 03:53:16 1998
+++ ./ipalloc.h	Thu Jun  5 09:33:20 2003
@@ -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 -urN ../qmail-1.03/maildirflags.c ./maildirflags.c
--- ../qmail-1.03/maildirflags.c	Wed Dec 31 16:00:00 1969
+++ ./maildirflags.c	Thu Jun  5 09:33:20 2003
@@ -0,0 +1,23 @@
+/*
+** Copyright 2000 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#include	<sys/types.h>
+#include	<string.h>
+
+static const char rcsid[]="$Id: maildirflags.c,v 1.1 2000/10/07 01:10:19 mrsam Exp $";
+
+int maildir_hasflag(const char *filename, char flag)
+{
+	const char *p=strrchr(filename, '/');
+
+	if (p)
+		filename=p+1;
+
+	p=strrchr(p, ':');
+	if (p && strncmp(p, ":2,", 3) == 0 &&
+	    strchr(p+3, flag))
+		return (1);
+	return (0);
+}
diff -urN ../qmail-1.03/maildirgetquota.c ./maildirgetquota.c
--- ../qmail-1.03/maildirgetquota.c	Wed Dec 31 16:00:00 1969
+++ ./maildirgetquota.c	Thu Jun  5 09:33:20 2003
@@ -0,0 +1,50 @@
+/*
+** Copyright 1998 - 2000 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#include	"maildirgetquota.h"
+#include	"maildirmisc.h"
+#if	HAVE_UNISTD_H
+#include	<unistd.h>
+#endif
+#include	<stdlib.h>
+#include	<string.h>
+#include	<fcntl.h>
+#include	<sys/types.h>
+#include	<sys/stat.h>
+
+int	maildir_getquota(const char *dir, char buf[QUOTABUFSIZE])
+{
+char	*p;
+struct	stat	stat_buf;
+int	n;
+int	l;
+
+	p=(char *)malloc(strlen(dir)+sizeof("/maildirfolder"));
+	if (!p)	return (-1);
+
+	strcat(strcpy(p, dir), "/maildirfolder");
+	if (stat(p, &stat_buf) == 0)
+	{
+		strcat(strcpy(p, dir), "/..");
+		n=maildir_getquota(p, buf);
+		free(p);
+		return (n);
+	}
+
+	strcat(strcpy(p, dir), "/maildirsize");
+	n=maildir_safeopen(p, O_RDONLY, 0);
+	free(p);
+	if (n < 0)	return (n);
+	if ((l=read(n, buf, QUOTABUFSIZE-1)) < 0)
+	{
+		close(n);
+		return (-1);
+	}
+	close(n);
+	for (n=0; n<l; n++)
+		if (buf[n] == '\n')	break;
+	buf[n]=0;
+	return (0);
+}
diff -urN ../qmail-1.03/maildirgetquota.h ./maildirgetquota.h
--- ../qmail-1.03/maildirgetquota.h	Wed Dec 31 16:00:00 1969
+++ ./maildirgetquota.h	Thu Jun  5 09:33:20 2003
@@ -0,0 +1,30 @@
+#ifndef	maildirgetquota_h
+#define	maildirgetquota_h
+
+/*
+** Copyright 1998 - 1999 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#if	HAVE_CONFIG_H
+#include	"config.h"
+#endif
+
+#include	<sys/types.h>
+#include	<stdio.h>
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+static const char maildirgetquota_h_rcsid[]="$Id: maildirgetquota.h,v 1.5 1999/12/06 13:21:05 mrsam Exp $";
+
+#define	QUOTABUFSIZE	256
+
+int maildir_getquota(const char *, char [QUOTABUFSIZE]);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif
diff -urN ../qmail-1.03/maildirmisc.h ./maildirmisc.h
--- ../qmail-1.03/maildirmisc.h	Wed Dec 31 16:00:00 1969
+++ ./maildirmisc.h	Thu Jun  5 09:33:20 2003
@@ -0,0 +1,145 @@
+#ifndef	maildirmisc_h
+#define	maildirmisc_h
+
+/*
+** Copyright 2000 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#if	HAVE_CONFIG_H
+#include	"config.h"
+#endif
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+static const char maildirmisc_h_rcsid[]="$Id: maildirmisc.h,v 1.8 2000/12/25 17:33:06 mrsam Exp $";
+
+/*
+**
+** Miscellaneous maildir-related code
+**
+*/
+
+/* Some special folders */
+
+#define	INBOX	"INBOX"
+#define	DRAFTS	"Drafts"
+#define	SENT	"Sent"
+#define	TRASH	"Trash"
+
+#define	SHAREDSUBDIR	"shared-folders"
+
+char *maildir_folderdir(const char *,		/* maildir */
+	const char *);				/* folder name */
+	/* Returns the directory corresponding to foldername (foldername is
+	** checked to make sure that it's a valid name, else we set errno
+	** to EINVAL, and return (0).
+	*/
+
+char *maildir_filename(const char *,		/* maildir */
+	const char *,				/* folder */
+	const char *);				/* filename */
+	/*
+	** Builds the filename to this message, suitable for opening.
+	** If the file doesn't appear to be there, search the maildir to
+	** see if someone changed the flags, and return the current filename.
+	*/
+
+int maildir_safeopen(const char *,		/* filename */
+	int,				/* mode */
+	int);				/* perm */
+
+/*
+**	Same arguments as open().  When we're accessing a shared maildir,
+**	prevent someone from playing cute and dumping a bunch of symlinks
+**	in there.  This function will open the indicate file only if the
+**	last component is not a symlink.
+**	This is implemented by opening the file with O_NONBLOCK (to prevent
+**	a DOS attack of someone pointing the symlink to a pipe, causing
+**	the open to hang), clearing O_NONBLOCK, then stat-int the file
+**	descriptor, lstating the filename, and making sure that dev/ino
+**	match.
+*/
+
+int maildir_semisafeopen(const char *,	/* filename */
+	int,				/* mode */
+	int);				/* perm */
+
+/*
+** Same thing, except that we allow ONE level of soft link indirection,
+** because we're reading from our own maildir, which points to the
+** message in the sharable maildir.
+*/
+
+int maildir_mkdir(const char *);	/* directory */
+/*
+** Create maildir including all subdirectories in the path (like mkdir -p)
+*/
+
+void maildir_purgetmp(const char *);		/* maildir */
+	/* purges old stuff out of tmp */
+
+void maildir_purge(const char *,		/* directory */
+	unsigned);				/* time_t to purge */
+
+void maildir_getnew(const char *,		/* maildir */
+	const char *);				/* folder */
+	/* move messages from new to cur */
+
+int maildir_deletefolder(const char *,		/* maildir */
+	const char *);				/* folder */
+	/* deletes a folder */
+
+int maildir_mddelete(const char *);	/* delete a maildir folder by path */
+
+void maildir_list_sharable(const char *,	/* maildir */
+	void (*)(const char *, void *),		/* callback function */
+	void *);				/* 2nd arg to callback func */
+	/* list sharable folders */
+
+int maildir_shared_subscribe(const char *,	/* maildir */
+		const char *);			/* folder */
+	/* subscribe to a shared folder */
+
+void maildir_list_shared(const char *,		/* maildir */
+	void (*)(const char *, void *),		/* callback function */
+	void *);			/* 2nd arg to the callback func */
+	/* list subscribed folders */
+
+int maildir_shared_unsubscribe(const char *,	/* maildir */
+		const char *);			/* folder */
+	/* unsubscribe from a shared folder */
+
+char *maildir_shareddir(const char *,		/* maildir */
+	const char *);				/* folder */
+	/*
+	** Validate and return a path to a shared folder.  folderdir must be
+	** a name of a valid shared folder.
+	*/
+
+void maildir_shared_sync(const char *);		/* maildir */
+	/* "sync" the shared folder */
+
+int maildir_sharedisro(const char *);		/* maildir */
+	/* maildir is a shared read-only folder */
+
+int maildir_unlinksharedmsg(const char *);	/* filename */
+	/* Remove a message from a shared folder */
+
+/* Internal function that reads a symlink */
+
+char *maildir_getlink(const char *);
+
+	/* Determine whether the maildir filename has a certain flag */
+
+int maildir_hasflag(const char *filename, char);
+
+#define	MAILDIR_DELETED(f)	maildir_hasflag((f), 'T')
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif
diff -urN ../qmail-1.03/maildiropen.c ./maildiropen.c
--- ../qmail-1.03/maildiropen.c	Wed Dec 31 16:00:00 1969
+++ ./maildiropen.c	Thu Jun  5 09:33:20 2003
@@ -0,0 +1,133 @@
+/*
+** Copyright 2000 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include	<sys/types.h>
+#include	<sys/stat.h>
+#include	<string.h>
+#include	<stdlib.h>
+#include	<time.h>
+#if	HAVE_UNISTD_H
+#include	<unistd.h>
+#endif
+#include	<stdio.h>
+#include	<ctype.h>
+#include	<errno.h>
+#include	<fcntl.h>
+
+#include	"maildirmisc.h"
+
+static const char rcsid[]="$Id: maildiropen.c,v 1.7 2000/12/10 04:43:44 mrsam Exp $";
+
+char *maildir_getlink(const char *filename)
+{
+#if     HAVE_READLINK
+size_t	bufsiz;
+char	*buf;
+
+	bufsiz=0;
+	buf=0;
+
+	for (;;)
+	{
+	int	n;
+
+		if (buf)	free(buf);
+		bufsiz += 256;
+		if ((buf=malloc(bufsiz)) == 0)
+		{
+			perror("malloc");
+			return (0);
+		}
+		if ((n=readlink(filename, buf, bufsiz)) < 0)
+		{
+			free(buf);
+			return (0);
+		}
+		if (n < bufsiz)
+		{
+			buf[n]=0;
+			break;
+		}
+	}
+	return (buf);
+#else
+	return (0);
+#endif
+}
+
+int maildir_semisafeopen(const char *path, int mode, int perm)
+{
+
+#if	HAVE_READLINK
+
+char	*l=maildir_getlink(path);
+
+	if (l)
+	{
+	int	f;
+
+		if (*l != '/')
+		{
+		char	*q=malloc(strlen(path)+strlen(l)+2);
+		char	*s;
+
+			if (!q)
+			{
+				free(l);
+				return (-1);
+			}
+
+			strcpy(q, path);
+			if ((s=strchr(q, '/')) != 0)
+				s[1]=0;
+			else	*q=0;
+			strcat(q, l);
+			free(l);
+			l=q;
+		}
+
+		f=maildir_safeopen(l, mode, perm);
+
+		free(l);
+		return (f);
+	}
+#endif
+
+	return (maildir_safeopen(path, mode, perm));
+}
+		
+int maildir_safeopen(const char *path, int mode, int perm)
+{
+struct	stat	stat1, stat2;
+
+int	fd=open(path, mode
+#ifdef	O_NONBLOCK
+			| O_NONBLOCK
+#else
+			| O_NDELAY
+#endif
+				, perm);
+
+	if (fd < 0)	return (fd);
+	if (fcntl(fd, F_SETFL, (mode & O_APPEND)) || fstat(fd, &stat1)
+	    || lstat(path, &stat2))
+	{
+		close(fd);
+		return (-1);
+	}
+
+	if (stat1.st_dev != stat2.st_dev || stat1.st_ino != stat2.st_ino)
+	{
+		close(fd);
+		errno=ENOENT;
+		return (-1);
+	}
+
+	return (fd);
+}
diff -urN ../qmail-1.03/maildirparsequota.c ./maildirparsequota.c
--- ../qmail-1.03/maildirparsequota.c	Wed Dec 31 16:00:00 1969
+++ ./maildirparsequota.c	Thu Jun  5 09:33:20 2003
@@ -0,0 +1,44 @@
+/*
+** Copyright 1998 - 1999 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include	"maildirquota.h"
+#include	<stdlib.h>
+#include	<string.h>
+
+static const char rcsid[]="$Id: maildirparsequota.c,v 1.2 1999/12/06 13:21:05 mrsam Exp $";
+
+int maildir_parsequota(const char *n, unsigned long *s)
+{
+const char *o;
+int	yes;
+
+	if ((o=strrchr(n, '/')) == 0)	o=n;
+
+	for (; *o; o++)
+		if (*o == ':')	break;
+	yes=0;
+	for ( ; o >= n; --o)
+	{
+		if (*o == '/')	break;
+
+		if (*o == ',' && o[1] == 'S' && o[2] == '=')
+		{
+			yes=1;
+			o += 3;
+			break;
+		}
+	}
+	if (yes)
+	{
+		*s=0;
+		while (*o >= '0' && *o <= '9')
+			*s= *s*10 + (*o++ - '0');
+		return (0);
+	}
+	return (-1);
+}
diff -urN ../qmail-1.03/maildirquota.c ./maildirquota.c
--- ../qmail-1.03/maildirquota.c	Wed Dec 31 16:00:00 1969
+++ ./maildirquota.c	Thu Jun  5 09:33:20 2003
@@ -0,0 +1,685 @@
+/*
+** Copyright 1998 - 2002 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+/* #if HAVE_DIRENT_H */
+#include <dirent.h>
+#define NAMLEN(dirent) strlen((dirent)->d_name)
+/* #else
+#define dirent direct
+#define NAMLEN(dirent) (dirent)->d_namlen
+#if HAVE_SYS_NDIR_H
+#include <sys/ndir.h>
+#endif
+#if HAVE_SYS_DIR_H
+#include <sys/dir.h>
+#endif
+#if HAVE_NDIR_H
+#include <ndir.h>
+#endif
+#endif */
+#include	<sys/types.h>
+/* #if	HAVE_SYS_STAT_H */
+#include	<sys/stat.h>
+/* #endif */
+#include	<sys/uio.h>
+
+#include	"maildirquota.h"
+#include	"maildirmisc.h"
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<string.h>
+#include	<errno.h>
+/* #if	HAVE_FCNTL_H */
+#include	<fcntl.h>
+/* #endif */
+#if	HAVE_UNISTD_H
+#include	<unistd.h>
+#endif
+#include	<time.h>
+#include	"numlib.h"
+
+static const char rcsid[]="$Id: maildirquota.c,v 1.9 2002/05/01 04:05:33 mrsam Exp $";
+
+/* Read the maildirsize file */
+
+int maildirsize_read(const char *filename,	/* The filename */
+	int *fdptr,	/* Keep the file descriptor open */
+	off_t *sizeptr,	/* Grand total of maildir size */
+	unsigned *cntptr, /* Grand total of message count */
+	unsigned *nlines, /* # of lines in maildirsize */
+	struct stat *statptr)	/* The stats on maildirsize */
+{
+char buf[5120];
+int f;
+char *p;
+unsigned l;
+int n;
+int first;
+
+	if ((f=maildir_safeopen(filename, O_RDWR|O_APPEND, 0)) < 0)
+		return (-1);
+	p=buf;
+	l=sizeof(buf);
+
+	while (l)
+	{
+		n=read(f, p, l);
+		if (n < 0)
+		{
+			close(f);
+			return (-1);
+		}
+		if (n == 0)	break;
+		p += n;
+		l -= n;
+	}
+	if (l == 0 || fstat(f, statptr))	/* maildir too big */
+	{
+		close(f);
+		return (-1);
+	}
+
+	*sizeptr=0;
+	*cntptr=0;
+	*nlines=0;
+	*p=0;
+	p=buf;
+	first=1;
+	while (*p)
+	{
+	long n=0;
+	int c=0;
+	char	*q=p;
+
+		while (*p)
+			if (*p++ == '\n')
+			{
+				p[-1]=0;
+				break;
+			}
+
+		if (first)
+		{
+			first=0;
+			continue;
+		}
+		sscanf(q, "%ld %d", &n, &c);
+		*sizeptr += n;
+		*cntptr += c;
+		++ *nlines;
+	}
+	*fdptr=f;
+	return (0);
+}
+
+static char *makenewmaildirsizename(const char *, int *);
+static int countcurnew(const char *, time_t *, off_t *, unsigned *);
+static int countsubdir(const char *, const char *,
+		time_t *, off_t *, unsigned *);
+static int statcurnew(const char *, time_t *);
+static int statsubdir(const char *, const char *, time_t *);
+
+#define	MDQUOTA_SIZE	'S'	/* Total size of all messages in maildir */
+#define	MDQUOTA_BLOCKS	'B'	/* Total # of blocks for all messages in
+				maildir -- NOT IMPLEMENTED */
+#define	MDQUOTA_COUNT	'C'	/* Total number of messages in maildir */
+
+static int qcalc(off_t s, unsigned n, const char *quota, int *percentage)
+{
+off_t i;
+int	spercentage=0;
+int	npercentage=0;
+
+	errno=ENOSPC;
+	while (quota && *quota)
+	{
+		int x=1;
+
+		if (*quota < '0' || *quota > '9')
+		{
+			++quota;
+			continue;
+		}
+		i=0;
+		while (*quota >= '0' && *quota <= '9')
+			i=i*10 + (*quota++ - '0');
+		switch (*quota)	{
+		default:
+			if (i < s)
+			{
+				*percentage=100;
+				return (-1);
+			}
+
+			/*
+			** For huge quotas, over 20mb,
+			** divide numerator & denominator by 1024 to prevent
+			** an overflow when multiplying by 100
+			*/
+
+			x=1;
+			if (i > 20000000) x=1024;
+
+			spercentage = i ? (s/x) * 100 / (i/x):100;
+			break;
+		case 'C':
+
+			if (i < n)
+			{
+				*percentage=100;
+				return (-1);
+			}
+
+			/* Ditto */
+
+			x=1;
+			if (i > 20000000) x=1024;
+
+			npercentage = i ? ((off_t)n/x) * 100 / (i/x):100;
+			break;
+		}
+	}
+	*percentage = spercentage > npercentage ? spercentage:npercentage;
+	return (0);
+}
+
+static int	doaddquota(const char *, int, const char *, long, int, int);
+
+static int docheckquota(const char *dir,
+	int *maildirsize_fdptr,
+	const char *quota_type,
+	long xtra_size,
+	int xtra_cnt, int *percentage);
+
+
+int maildir_checkquota(const char *dir,
+	int *maildirsize_fdptr,
+	const char *quota_type,
+	long xtra_size,
+	int xtra_cnt)
+{
+int	dummy;
+
+	return (docheckquota(dir, maildirsize_fdptr, quota_type,
+		xtra_size, xtra_cnt, &dummy));
+}
+
+int maildir_readquota(const char *dir, const char *quota_type)
+{
+int	percentage=0;
+int	fd=-1;
+
+	(void)docheckquota(dir, &fd, quota_type, 0, 0, &percentage);
+	if (fd >= 0)
+		close(fd);
+	return (percentage);
+}
+
+static int docheckquota(const char *dir,
+	int *maildirsize_fdptr,
+	const char *quota_type,
+	long xtra_size,
+	int xtra_cnt,
+	int *percentage)
+{
+char	*checkfolder=(char *)malloc(strlen(dir)+sizeof("/maildirfolder"));
+char	*newmaildirsizename;
+struct stat stat_buf;
+int	maildirsize_fd;
+off_t	maildirsize_size;
+unsigned maildirsize_cnt;
+unsigned maildirsize_nlines;
+int	n;
+time_t	tm;
+time_t	maxtime;
+DIR	*dirp;
+struct dirent *de;
+
+	if (checkfolder == 0)	return (-1);
+	*maildirsize_fdptr= -1;
+	strcat(strcpy(checkfolder, dir), "/maildirfolder");
+	if (stat(checkfolder, &stat_buf) == 0)	/* Go to parent */
+	{
+		strcat(strcpy(checkfolder, dir), "/..");
+		n=docheckquota(checkfolder, maildirsize_fdptr,
+			quota_type, xtra_size, xtra_cnt, percentage);
+		free(checkfolder);
+		return (n);
+	}
+	if (!quota_type || !*quota_type)	return (0);
+
+	strcat(strcpy(checkfolder, dir), "/maildirsize");
+	time(&tm);
+	if (maildirsize_read(checkfolder, &maildirsize_fd,
+		&maildirsize_size, &maildirsize_cnt,
+		&maildirsize_nlines, &stat_buf) == 0)
+	{
+		n=qcalc(maildirsize_size+xtra_size, maildirsize_cnt+xtra_cnt,
+			quota_type, percentage);
+
+		if (n == 0)
+		{
+			free(checkfolder);
+			*maildirsize_fdptr=maildirsize_fd;
+			return (0);
+		}
+		close(maildirsize_fd);
+
+		if (maildirsize_nlines == 1 && tm < stat_buf.st_mtime + 15*60)
+			return (n);
+	}
+
+	maxtime=0;
+	maildirsize_size=0;
+	maildirsize_cnt=0;
+
+	if (countcurnew(dir, &maxtime, &maildirsize_size, &maildirsize_cnt))
+	{
+		free(checkfolder);
+		return (-1);
+	}
+
+	dirp=opendir(dir);
+	while (dirp && (de=readdir(dirp)) != 0)
+	{
+		if (countsubdir(dir, de->d_name, &maxtime, &maildirsize_size,
+			&maildirsize_cnt))
+		{
+			free(checkfolder);
+			closedir(dirp);
+			return (-1);
+		}
+	}
+	if (dirp)
+	{
+#if	CLOSEDIR_VOID
+		closedir(dirp);
+#else
+		if (closedir(dirp))
+		{
+			free(checkfolder);
+			return (-1);
+		}
+#endif
+	}
+
+	newmaildirsizename=makenewmaildirsizename(dir, &maildirsize_fd);
+	if (!newmaildirsizename)
+	{
+		free(checkfolder);
+		return (-1);
+	}
+
+	*maildirsize_fdptr=maildirsize_fd;
+
+	if (doaddquota(dir, maildirsize_fd, quota_type, maildirsize_size,
+		maildirsize_cnt, 1))
+	{
+		free(newmaildirsizename);
+		unlink(newmaildirsizename);
+		close(maildirsize_fd);
+		*maildirsize_fdptr= -1;
+		free(checkfolder);
+		return (-1);
+	}
+
+	strcat(strcpy(checkfolder, dir), "/maildirsize");
+
+	if (rename(newmaildirsizename, checkfolder))
+	{
+		free(checkfolder);
+		unlink(newmaildirsizename);
+		close(maildirsize_fd);
+		*maildirsize_fdptr= -1;
+	}
+	free(checkfolder);
+	free(newmaildirsizename);
+
+	tm=0;
+
+	if (statcurnew(dir, &tm))
+	{
+		close(maildirsize_fd);
+		*maildirsize_fdptr= -1;
+		return (-1);
+	}
+
+	dirp=opendir(dir);
+	while (dirp && (de=readdir(dirp)) != 0)
+	{
+		if (statsubdir(dir, de->d_name, &tm))
+		{
+			close(maildirsize_fd);
+			*maildirsize_fdptr= -1;
+			closedir(dirp);
+			return (-1);
+		}
+	}
+	if (dirp)
+	{
+#if	CLOSEDIR_VOID
+		closedir(dirp);
+#else
+		if (closedir(dirp))
+		{
+			close(maildirsize_fd);
+			*maildirsize_fdptr= -1;
+			return (-1);
+		}
+#endif
+	}
+
+	if (tm != maxtime)	/* Race condition, someone changed something */
+	{
+		errno=EAGAIN;
+		return (-1);
+	}
+
+	return (qcalc(maildirsize_size+xtra_size, maildirsize_cnt+xtra_cnt,
+		quota_type, percentage));
+}
+
+int	maildir_addquota(const char *dir, int maildirsize_fd,
+	const char *quota_type, long maildirsize_size, int maildirsize_cnt)
+{
+	if (!quota_type || !*quota_type)	return (0);
+	return (doaddquota(dir, maildirsize_fd, quota_type, maildirsize_size,
+			maildirsize_cnt, 0));
+}
+
+static int doaddquota(const char *dir, int maildirsize_fd,
+	const char *quota_type, long maildirsize_size, int maildirsize_cnt,
+	int isnew)
+{
+union	{
+	char	buf[100];
+	struct stat stat_buf;
+	} u;				/* Scrooge */
+char	*newname2=0;
+char	*newmaildirsizename=0;
+struct	iovec	iov[3];
+int	niov;
+struct	iovec	*p;
+int	n;
+
+	niov=0;
+	if ( maildirsize_fd < 0)
+	{
+		newname2=(char *)malloc(strlen(dir)+sizeof("/maildirfolder"));
+		if (!newname2)	return (-1);
+		strcat(strcpy(newname2, dir), "/maildirfolder");
+		if (stat(newname2, &u.stat_buf) == 0)
+		{
+			strcat(strcpy(newname2, dir), "/..");
+			n=doaddquota(newname2, maildirsize_fd, quota_type,
+					maildirsize_size, maildirsize_cnt,
+					isnew);
+			free(newname2);
+			return (n);
+		}
+
+		strcat(strcpy(newname2, dir), "/maildirsize");
+
+		if ((maildirsize_fd=maildir_safeopen(newname2,
+			O_RDWR|O_APPEND, 0644)) < 0)
+		{
+			newmaildirsizename=makenewmaildirsizename(dir, &maildirsize_fd);
+			if (!newmaildirsizename)
+			{
+				free(newname2);
+				return (-1);
+			}
+
+			maildirsize_fd=maildir_safeopen(newmaildirsizename,
+				O_CREAT|O_RDWR|O_APPEND, 0644);
+
+			if (maildirsize_fd < 0)
+			{
+				free(newname2);
+				return (-1);
+			}
+			isnew=1;
+		}
+	}
+
+	if (isnew)
+	{
+		iov[0].iov_base=(caddr_t)quota_type;
+		iov[0].iov_len=strlen(quota_type);
+		iov[1].iov_base=(caddr_t)"\n";
+		iov[1].iov_len=1;
+		niov=2;
+	}
+
+
+	sprintf(u.buf, "%ld %d\n", maildirsize_size, maildirsize_cnt);
+	iov[niov].iov_base=(caddr_t)u.buf;
+	iov[niov].iov_len=strlen(u.buf);
+
+	p=iov;
+	++niov;
+	n=0;
+	while (niov)
+	{
+		if (n)
+		{
+			if (n < p->iov_len)
+			{
+				p->iov_base=
+					(caddr_t)((char *)p->iov_base + n);
+				p->iov_len -= n;
+			}
+			else
+			{
+				n -= p->iov_len;
+				++p;
+				--niov;
+				continue;
+			}
+		}
+
+		n=writev( maildirsize_fd, p, niov);
+
+		if (n <= 0)
+		{
+			if (newname2)
+			{
+				close(maildirsize_fd);
+				free(newname2);
+			}
+			return (-1);
+		}
+	}
+	if (newname2)
+	{
+		close(maildirsize_fd);
+
+		if (newmaildirsizename)
+		{
+			rename(newmaildirsizename, newname2);
+			free(newmaildirsizename);
+		}
+		free(newname2);
+	}
+	return (0);
+}
+
+/* New maildirsize is built in the tmp subdirectory */
+
+static char *makenewmaildirsizename(const char *dir, int *fd)
+{
+char	hostname[256];
+struct	stat stat_buf;
+time_t	t;
+char	*p;
+
+	hostname[0]=0;
+	hostname[sizeof(hostname)-1]=0;
+	gethostname(hostname, sizeof(hostname)-1);
+	p=(char *)malloc(strlen(dir)+strlen(hostname)+130);
+	if (!p)	return (0);
+
+	for (;;)
+	{
+	char	tbuf[NUMBUFSIZE];
+	char	pbuf[NUMBUFSIZE];
+
+		time(&t);
+		strcat(strcpy(p, dir), "/tmp/");
+		sprintf(p+strlen(p), "%s.%s_NeWmAiLdIrSiZe.%s",
+			str_time_t(t, tbuf),
+			str_pid_t(getpid(), pbuf), hostname);
+
+		if (stat( (const char *)p, &stat_buf) < 0 &&
+			(*fd=maildir_safeopen(p,
+				O_CREAT|O_RDWR|O_APPEND, 0644)) >= 0)
+			break;
+		sleep(3);
+	}
+	return (p);
+}
+
+static int statcurnew(const char *dir, time_t *maxtimestamp)
+{
+char	*p=(char *)malloc(strlen(dir)+5);
+struct	stat	stat_buf;
+
+	if (!p)	return (-1);
+	strcat(strcpy(p, dir), "/cur");
+	if ( stat(p, &stat_buf) == 0 && stat_buf.st_mtime > *maxtimestamp)
+		*maxtimestamp=stat_buf.st_mtime;
+	strcat(strcpy(p, dir), "/new");
+	if ( stat(p, &stat_buf) == 0 && stat_buf.st_mtime > *maxtimestamp)
+		*maxtimestamp=stat_buf.st_mtime;
+	free(p);
+	return (0);
+}
+
+static int statsubdir(const char *dir, const char *subdir, time_t *maxtime)
+{
+char	*p;
+int	n;
+
+	if ( *subdir != '.' || strcmp(subdir, ".") == 0 ||
+		strcmp(subdir, "..") == 0 || strcmp(subdir, "." TRASH) == 0)
+		return (0);
+
+	p=(char *)malloc(strlen(dir)+strlen(subdir)+2);
+	if (!p)	return (-1);
+	strcat(strcat(strcpy(p, dir), "/"), subdir);
+	n=statcurnew(p, maxtime);
+	free(p);
+	return (n);
+}
+
+static int docount(const char *, time_t *, off_t *, unsigned *);
+
+static int countcurnew(const char *dir, time_t *maxtime,
+	off_t *sizep, unsigned *cntp)
+{
+char	*p=(char *)malloc(strlen(dir)+5);
+int	n;
+
+	if (!p)	return (-1);
+	strcat(strcpy(p, dir), "/new");
+	n=docount(p, maxtime, sizep, cntp);
+	if (n == 0)
+	{
+		strcat(strcpy(p, dir), "/cur");
+		n=docount(p, maxtime, sizep, cntp);
+	}
+	free(p);
+	return (n);
+}
+
+static int countsubdir(const char *dir, const char *subdir, time_t *maxtime,
+	off_t *sizep, unsigned *cntp)
+{
+char	*p;
+int	n;
+
+	if ( *subdir != '.' || strcmp(subdir, ".") == 0 ||
+		strcmp(subdir, "..") == 0 || strcmp(subdir, "." TRASH) == 0)
+		return (0);
+
+	p=(char *)malloc(strlen(dir)+strlen(subdir)+2);
+	if (!p)	return (2);
+	strcat(strcat(strcpy(p, dir), "/"), subdir);
+	n=countcurnew(p, maxtime, sizep, cntp);
+	free(p);
+	return (n);
+}
+
+static int docount(const char *dir, time_t *dirstamp,
+	off_t *sizep, unsigned *cntp)
+{
+struct	stat	stat_buf;
+char	*p;
+DIR	*dirp;
+struct dirent *de;
+unsigned long	s;
+
+	if (stat(dir, &stat_buf))	return (0);	/* Ignore */
+	if (stat_buf.st_mtime > *dirstamp)	*dirstamp=stat_buf.st_mtime;
+	if ((dirp=opendir(dir)) == 0)	return (0);
+	while ((de=readdir(dirp)) != 0)
+	{
+	const char *n=de->d_name;
+
+		if (*n == '.')	continue;
+
+		/* PATCH - do not count msgs marked as deleted */
+
+		for ( ; *n; n++)
+		{
+			if (n[0] != ':' || n[1] != '2' ||
+				n[2] != ',')	continue;
+			n += 3;
+			while (*n >= 'A' && *n <= 'Z')
+			{
+				if (*n == 'T')	break;
+				++n;
+			}
+			break;
+		}
+		if (*n == 'T')	continue;
+		n=de->d_name;
+
+
+		if (maildir_parsequota(n, &s) == 0)
+			stat_buf.st_size=s;
+		else
+		{
+			p=(char *)malloc(strlen(dir)+strlen(n)+2);
+			if (!p)
+			{
+				closedir(dirp);
+				return (-1);
+			}
+			strcat(strcat(strcpy(p, dir), "/"), n);
+			if (stat(p, &stat_buf))
+			{
+				free(p);
+				continue;
+			}
+			free(p);
+		}
+		*sizep += stat_buf.st_size;
+		++*cntp;
+	}
+
+#if	CLOSEDIR_VOID
+	closedir(dirp);
+#else
+	if (closedir(dirp))
+		return (-1);
+#endif
+	return (0);
+}
diff -urN ../qmail-1.03/maildirquota.h ./maildirquota.h
--- ../qmail-1.03/maildirquota.h	Wed Dec 31 16:00:00 1969
+++ ./maildirquota.h	Thu Jun  5 09:33:21 2003
@@ -0,0 +1,45 @@
+#ifndef	maildirquota_h
+#define	maildirquota_h
+
+/*
+** Copyright 1998 - 1999 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#if	HAVE_CONFIG_H
+#include	"config.h"
+#endif
+
+#include	<sys/types.h>
+#include	<stdio.h>
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+static const char maildirquota_h_rcsid[]="$Id: maildirquota.h,v 1.2 2000/09/04 17:10:28 mrsam Exp $";
+
+int maildir_checkquota(const char *,	/* Pointer to directory */
+	int *,	/* Initialized to -1, or opened descriptor for maildirsize */
+	const char *,	/* The quota */
+	long,		/* Extra bytes planning to add/remove from maildir */
+	int);		/* Extra messages planning to add/remove from maildir */
+
+int maildir_addquota(const char *,	/* Pointer to the maildir */
+	int,	/* Must be the int pointed to by 2nd arg to checkquota */
+	const char *,	/* The quota */
+	long,	/* +/- bytes */
+	int);	/* +/- files */
+
+int maildir_readquota(const char *,	/* Directory */
+	const char *);			/* Quota, from getquota */
+
+int maildir_parsequota(const char *, unsigned long *);
+	/* Attempt to parse file size encoded in filename.  Returns 0 if
+	** parsed, non-zero if we didn't parse. */
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif
diff -urN ../qmail-1.03/numlib.h ./numlib.h
--- ../qmail-1.03/numlib.h	Wed Dec 31 16:00:00 1969
+++ ./numlib.h	Thu Jun  5 09:33:21 2003
@@ -0,0 +1,45 @@
+#ifndef	numlib_h
+#define	numlib_h
+
+/*
+** Copyright 1998 - 1999 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+static const char numlib_h_rcsid[]="$Id: numlib.h,v 1.3 2001/08/12 15:46:40 mrsam Exp $";
+
+#if	HAVE_CONFIG_H
+#include	"config.h"
+#endif
+
+#include	<sys/types.h>
+#include	<time.h>
+
+#define	NUMBUFSIZE	60
+
+/* Convert various system types to decimal */
+
+char	*str_time_t(time_t, char *);
+char	*str_off_t(off_t, char *);
+char	*str_pid_t(pid_t, char *);
+char	*str_ino_t(ino_t, char *);
+char	*str_uid_t(uid_t, char *);
+char	*str_gid_t(gid_t, char *);
+char	*str_size_t(size_t, char *);
+
+char	*str_sizekb(unsigned long, char *);	/* X Kb or X Mb */
+
+/* Convert selected system types to hex */
+
+char	*strh_time_t(time_t, char *);
+char	*strh_pid_t(pid_t, char *);
+char	*strh_ino_t(ino_t, char *);
+
+#ifdef	__cplusplus
+}
+#endif
+#endif
diff -urN ../qmail-1.03/overmaildirquota.c ./overmaildirquota.c
--- ../qmail-1.03/overmaildirquota.c	Wed Dec 31 16:00:00 1969
+++ ./overmaildirquota.c	Thu Jun  5 09:33:21 2003
@@ -0,0 +1,43 @@
+/*
+** Copyright 1998 - 1999 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include        "maildirquota.h"
+#include        <stdlib.h>
+#include        <string.h>
+#include        <errno.h>
+#include        <sys/stat.h>
+
+static const char rcsid[]="$Id: overquota.c,v 1.0 2002/06/09 16:21:05 mr
+sam Exp $";
+
+
+int user_over_maildirquota( const char *dir, const char *q)
+{
+struct  stat    stat_buf;
+int     quotafd;
+int     ret_value;
+
+        if (fstat(0, &stat_buf) == 0 && S_ISREG(stat_buf.st_mode) &&
+                stat_buf.st_size > 0 && *q)
+        {
+                if (maildir_checkquota(dir, &quotafd, q, stat_buf.st_size, 1)
+                        && errno != EAGAIN)
+                {
+                        if (quotafd >= 0)       close(quotafd);
+                        ret_value = 1;
+                } else {
+                        maildir_addquota(dir, quotafd, q, stat_buf.st_size, 1);
+                        if (quotafd >= 0)       close(quotafd);
+                        ret_value = 0;
+                }
+        } else {
+                ret_value = 0;
+        }
+
+        return(ret_value);
+}
diff -urN ../qmail-1.03/qmail-control.9 ./qmail-control.9
--- ../qmail-1.03/qmail-control.9	Mon Jun 15 03:53:16 1998
+++ ./qmail-control.9	Thu Jun  5 09:33:21 2003
@@ -55,6 +55,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
diff -urN ../qmail-1.03/qmail-local.c ./qmail-local.c
--- ../qmail-1.03/qmail-local.c	Mon Jun 15 03:53:16 1998
+++ ./qmail-local.c	Thu Jun  5 09:33:21 2003
@@ -66,6 +66,7 @@
 
 char buf[1024];
 char outbuf[1024];
+#define QUOTABUFSIZE    256
 
 /* child process */
 
@@ -86,9 +87,15 @@
  int fd;
  substdio ss;
  substdio ssout;
+ char quotabuf[QUOTABUFSIZE];
 
  sig_alarmcatch(sigalrm);
  if (chdir(dir) == -1) { if (error_temp(errno)) _exit(1); _exit(2); }
+ if (maildir_getquota(dir, quotabuf) == 0) {
+  if (user_over_maildirquota(dir,quotabuf)==1) {
+   _exit(1);
+  }
+ }
  pid = getpid();
  host[0] = 0;
  gethostname(host,sizeof(host));
@@ -99,7 +106,10 @@
    s += fmt_str(s,"tmp/");
    s += fmt_ulong(s,time); *s++ = '.';
    s += fmt_ulong(s,pid); *s++ = '.';
-   s += fmt_strn(s,host,sizeof(host)); *s++ = 0;
+   s += fmt_strn(s,host,sizeof(host));
+   s += fmt_strn(s,",S=",sizeof(",S="));
+   if (fstat(0,&st) == -1) if (errno == error_noent) break;
+   s += fmt_ulong(s,st.st_size); *s++ = 0;
    if (stat(fntmptph,&st) == -1) if (errno == error_noent) break;
    /* really should never get to this point */
    if (loop == 2) _exit(1);
@@ -159,6 +169,7 @@
  switch(wait_exitcode(wstat))
   {
    case 0: break;
+   case 1: strerr_die1x(1, "User over quota. (#5.1.1)");
    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)");
diff -urN ../qmail-1.03/qmail-pop3d.c ./qmail-pop3d.c
--- ../qmail-1.03/qmail-pop3d.c	Mon Jun 15 03:53:16 1998
+++ ./qmail-pop3d.c	Thu Jun  5 09:33:21 2003
@@ -16,6 +16,11 @@
 #include "readwrite.h"
 #include "timeoutread.h"
 #include "timeoutwrite.h"
+#include <errno.h>
+#include "maildirquota.h"
+#include "maildirmisc.h"
+
+#define QUOTABUFSIZE 256
 
 void die() { _exit(0); }
 
@@ -45,19 +50,15 @@
 {
   substdio_put(&ssout,buf,len);
 }
-void puts(s) char *s;
-{
-  substdio_puts(&ssout,s);
-}
 void flush()
 {
   substdio_flush(&ssout);
 }
 void err(s) char *s;
 {
-  puts("-ERR ");
-  puts(s);
-  puts("\r\n");
+  substdio_puts(&ssout,"-ERR ");
+  substdio_puts(&ssout,s);
+  substdio_puts(&ssout,"\r\n");
   flush();
 }
 
@@ -73,7 +74,7 @@
 void err_nosuch() { err("unable to open that message"); }
 void err_nounlink() { err("unable to unlink all deleted messages"); }
 
-void okay() { puts("+OK \r\n"); flush(); }
+void okay() { substdio_puts(&ssout,"+OK \r\n"); flush(); }
 
 void printfn(fn) char *fn;
 {
@@ -153,11 +154,11 @@
  
   total = 0;
   for (i = 0;i < numm;++i) if (!m[i].flagdeleted) total += m[i].size;
-  puts("+OK ");
+  substdio_puts(&ssout,"+OK ");
   put(strnum,fmt_uint(strnum,numm));
-  puts(" ");
+  substdio_puts(&ssout," ");
   put(strnum,fmt_ulong(strnum,total));
-  puts("\r\n");
+  substdio_puts(&ssout,"\r\n");
   flush();
 }
 
@@ -171,18 +172,41 @@
 
 void pop3_last()
 {
-  puts("+OK ");
+  substdio_puts(&ssout,"+OK ");
   put(strnum,fmt_uint(strnum,last));
-  puts("\r\n");
+  substdio_puts(&ssout,"\r\n");
   flush();
 }
 
 void pop3_quit()
 {
   int i;
+  char quotabuf[QUOTABUFSIZE];
+  int has_quota=maildir_getquota(".", quotabuf);
+
+  long deleted_bytes=0;
+  long deleted_messages=0;
+
   for (i = 0;i < numm;++i)
     if (m[i].flagdeleted) {
-      if (unlink(m[i].fn) == -1) err_nounlink();
+      unsigned long un=0;
+      const char *filename=m[i].fn;
+      if (has_quota == 0 && !MAILDIR_DELETED(filename)) {
+          if (maildir_parsequota(filename, &un)) {
+              struct stat stat_buf;
+
+              if (stat(filename, &stat_buf) == 0)
+                  un=stat_buf.st_size;
+          }
+      }
+      if (unlink(m[i].fn) == -1) {
+          err_nounlink();
+          un=0;
+      }
+      if (un) {
+          deleted_bytes -= un;
+          deleted_messages -= 1;
+      }
     }
     else
       if (str_start(m[i].fn,"new/")) {
@@ -192,6 +216,21 @@
 	if (!stralloc_0(&line)) die_nomem();
 	rename(m[i].fn,line.s); /* if it fails, bummer */
       }
+
+    if (deleted_messages < 0) {
+        int quotafd;
+
+        if (maildir_checkquota(".", &quotafd, quotabuf, deleted_bytes,
+                               deleted_messages) && errno != EAGAIN &&
+                               deleted_bytes >= 0)
+            {
+                if (quotafd >= 0) close (quotafd);
+            } else {
+                 maildir_addquota(".", quotafd, quotabuf,
+                                 deleted_bytes, deleted_messages);
+                 if (quotafd >= 0) close(quotafd);
+            }
+        }
   okay();
   die();
 }
@@ -222,10 +261,10 @@
 int flaguidl;
 {
   put(strnum,fmt_uint(strnum,i + 1));
-  puts(" ");
+  substdio_puts(&ssout," ");
   if (flaguidl) printfn(m[i].fn);
   else put(strnum,fmt_ulong(strnum,m[i].size));
-  puts("\r\n");
+  substdio_puts(&ssout,"\r\n");
 }
 
 void dolisting(arg,flaguidl) char *arg; int flaguidl;
@@ -234,7 +273,7 @@
   if (*arg) {
     i = msgno(arg);
     if (i == -1) return;
-    puts("+OK ");
+    substdio_puts(&ssout,"+OK ");
     list(i,flaguidl);
   }
   else {
@@ -242,7 +281,7 @@
     for (i = 0;i < numm;++i)
       if (!m[i].flagdeleted)
 	list(i,flaguidl);
-    puts(".\r\n");
+    substdio_puts(&ssout,".\r\n");
   }
   flush();
 }
diff -urN ../qmail-1.03/qmail-remote.c ./qmail-remote.c
--- ../qmail-1.03/qmail-remote.c	Mon Jun 15 03:53:16 1998
+++ ./qmail-remote.c	Thu Jun  5 09:33:21 2003
@@ -26,8 +26,18 @@
 #include "tcpto.h"
 #include "readwrite.h"
 #include "timeoutconn.h"
+#ifndef TLS
 #include "timeoutread.h"
 #include "timeoutwrite.h"
+#endif
+
+#ifdef TLS
+#include <sys/stat.h>
+#include <openssl/ssl.h>
+SSL *ssl = NULL;
+
+stralloc tlsclientciphers = {0};
+#endif
 
 #define HUGESMTPTEXT 5000
 
@@ -107,17 +117,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 +273,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();
 }
 
@@ -216,20 +331,158 @@
 
 stralloc recip = {0};
 
+#ifdef TLS
+void smtp(fqdn)
+char *fqdn;
+#else
 void smtp()
+#endif
 {
   unsigned long code;
   int flagbother;
   int i;
- 
+#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
+
   if (smtpcode() != 220) quit("ZConnected to "," but greeting failed");
  
+#ifdef TLS
+  substdio_puts(&smtpto,"EHLO ");
+#else
   substdio_puts(&smtpto,"HELO ");
+#endif
   substdio_put(&smtpto,helohost.s,helohost.len);
   substdio_puts(&smtpto,"\r\n");
   substdio_flush(&smtpto);
+#ifdef TLS
+  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");
+  }
+#else
   if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected");
- 
+#endif
+
+#ifdef 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();}
+#endif
+
   substdio_puts(&smtpto,"MAIL FROM:<");
   substdio_put(&smtpto,sender.s,sender.len);
   substdio_puts(&smtpto,">\r\n");
@@ -324,6 +577,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)
@@ -338,7 +596,10 @@
   int flagallaliases;
   int flagalias;
   char *relayhost;
- 
+
+#ifdef TLS
+  sig_alarmcatch(sigalrm);
+#endif
   sig_pipeignore();
   if (argc < 4) perm_usage();
   if (chdir(auto_qmail) == -1) temp_chdir();
@@ -417,7 +678,11 @@
     if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0) {
       tcpto_err(&ip.ix[i].ip,0);
       partner = ip.ix[i].ip;
+#ifdef TLS
+      smtp(ip.ix[i].fqdn); /* does not return */
+#else
       smtp(); /* does not return */
+#endif
     }
     tcpto_err(&ip.ix[i].ip,errno == error_timeout);
     close(smtpfd);
diff -urN ../qmail-1.03/qmail-send.c ./qmail-send.c
--- ../qmail-1.03/qmail-send.c	Mon Jun 15 03:53:16 1998
+++ ./qmail-send.c	Thu Jun  5 09:33:21 2003
@@ -262,6 +262,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();
@@ -906,41 +908,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;
@@ -1544,7 +1547,7 @@
  numjobs = 0;
  for (c = 0;c < CHANNELS;++c)
   {
-   char ch;
+   char ch, ch1;
    int u;
    int r;
    do
@@ -1552,7 +1555,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];
   }
diff -urN ../qmail-1.03/qmail-smtpd.8 ./qmail-smtpd.8
--- ../qmail-1.03/qmail-smtpd.8	Mon Jun 15 03:53:16 1998
+++ ./qmail-smtpd.8	Thu Jun  5 09:33:21 2003
@@ -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,29 @@
 header fields.
 
 .B qmail-smtpd
-supports ESMTP, including the 8BITMIME and PIPELINING options.
+supports ESMTP, including the 8BITMIME, PIPELINING, and AUTH options.
+
+.B qmail-smtpd
+can accept LOGIN, PLAIN, and CRAM-MD5 AUTH types.  It invokes
+.IR checkprogram ,
+which reads on file descriptor 3 the username, a 0 byte, the password
+or challenge derived from
+.IR hostname ,
+another 0 byte, a CRAM-MD5 response (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
@@ -97,6 +124,12 @@
 This is done before
 .IR rcpthosts .
 .TP 5
+.I mfcheck
+If set,
+.B qmail-smtpd
+tries to resolve the domain of the envelope from address.  It can be
+handy when you want to filter out spamhosts.
+.TP 5
 .I morercpthosts
 Extra allowed RCPT domains.
 If
@@ -177,3 +210,6 @@
 qmail-newmrh(8),
 qmail-queue(8),
 qmail-remote(8)
+.SH "HISTORY"
+The patch enabling the ESMTP AUTH option is not part of the standard
+qmail-1.03 distribution.
diff -urN ../qmail-1.03/qmail-smtpd.c ./qmail-smtpd.c
--- ../qmail-1.03/qmail-smtpd.c	Mon Jun 15 03:53:16 1998
+++ ./qmail-smtpd.c	Thu Jun  5 09:33:21 2003
@@ -20,18 +20,80 @@
 #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 "wait.h"
+#include "fd.h"
+#include "dns.h"
+
+#ifdef TLS
+#include <openssl/ssl.h>
+SSL *ssl = NULL;
+
+stralloc clientcert = {0};
+stralloc tlsserverciphers = {0};
+#endif
 
+#define AUTHCRAM
 #define MAXHOPS 100
+#define BMCHECK_BMF 0
+#define BMCHECK_BMT 1
 unsigned int databytes = 0;
+unsigned int mfchk = 0;
 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));
+  }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;
 }
@@ -49,8 +111,14 @@
 void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); }
 void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); }
 
-void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); }
+void err_bmf() { out("553 sorry, your envelope sender 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_bmt() { out("533 sorry, your envelope recipient has been denied (#5.7.1)\r\n"); }
 void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); }
+#ifdef TLS
+void err_nogwcert() { out("553 no valid cert for gatewaying (#5.7.1)\r\n"); }
+#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_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); }
@@ -59,6 +127,15 @@
 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"); }
 
+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"); }
+int err_noauth() { out("504 auth type unimplemented (#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; }
 
 stralloc greeting = {0};
 
@@ -81,6 +158,9 @@
 char *remoteinfo;
 char *local;
 char *relayclient;
+#ifdef TLS
+char *tlsciphers;
+#endif
 
 stralloc helohost = {0};
 char *fakehelo; /* pointer into helohost, or 0 */
@@ -96,11 +176,20 @@
 int bmfok = 0;
 stralloc bmf = {0};
 struct constmap mapbmf;
+int tarpitcount = 0;
+int tarpitdelay = 5;
+
+int bmtok = 0;
+stralloc bmt = {0};
+struct constmap mapbmt;
 
 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)
@@ -110,12 +199,28 @@
   if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control();
   if (timeout <= 0) timeout = 1;
 
+  if (control_readint(&tarpitcount,"control/tarpitcount") == -1) die_control();
+  if (tarpitcount < 0) tarpitcount = 0;
+  x = env_get("TARPITCOUNT");
+  if (x) { scan_ulong(x,&u); tarpitcount = u; };
+  if (control_readint(&tarpitdelay,"control/tarpitdelay") == -1) die_control();
+  if (tarpitdelay < 0) tarpitdelay = 0;
+  x = env_get("TARPITDELAY");
+  if (x) { scan_ulong(x,&u); tarpitdelay = u; };
+
   if (rcpthosts_init() == -1) die_control();
 
+  if (control_readint(&mfchk,"control/mfcheck") == -1) die_control();
+  x = env_get("MFCHECK");
+  if (x) { scan_ulong(x,&u); mfchk = u; }
+
   bmfok = control_readfile(&bmf,"control/badmailfrom",0);
   if (bmfok == -1) die_control();
-  if (bmfok)
-    if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem();
+  if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem();
+
+  bmtok = control_readfile(&bmt,"control/badmailto",0);
+  if (bmtok == -1) die_control();
+  if (!constmap_init(&mapbmt,bmt.s,bmt.len,0)) die_nomem();
  
   if (control_readint(&databytes,"control/databytes") == -1) die_control();
   x = env_get("DATABYTES");
@@ -131,6 +236,17 @@
   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();
+#endif
+
   dohelo(remotehost);
 }
 
@@ -197,14 +313,57 @@
   return 1;
 }
 
-int bmfcheck()
+int bmcheck(which) int which;
 {
+  int i = 0;
+  int j = 0;
+  int x = 0;
+  int negate = 0;
+  stralloc bmb = {0};
+  stralloc curregex = {0};
+
+  if (which == BMCHECK_BMF) {
+    if (!stralloc_copy(&bmb,&bmf)) die_nomem();
+  } else if (which == BMCHECK_BMT) {
+    if (!stralloc_copy(&bmb,&bmt)) 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++;
+    }
+    stralloc_copyb(&curregex,bmb.s + j,(i - j));
+    stralloc_0(&curregex);
+    x = matchregex(addr.s, curregex.s);
+    if ((negate) && (x == 0)) return 1;
+    if (!(negate) && (x > 0)) 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 (!mfchk) return 0;
+  random = now() + (getpid() << 16);
+  j = byte_rchr(addr.s,addr.len,'@') + 1;
+  if (j < addr.len) {
+    stralloc_copys(&sa, addr.s + j);
+    dns_init(0);
+    j = dns_mxip(&ia,&sa,random);
+    if (j < 0) return j;
+  }
   return 0;
 }
 
@@ -218,9 +377,11 @@
 
 
 int seenmail = 0;
-int flagbarf; /* defined if seenmail */
+int flagbarfbmf; /* defined if seenmail */
+int flagbarfbmt;
 stralloc mailfrom = {0};
 stralloc rcptto = {0};
+int rcptcount;
 
 void smtp_helo(arg) char *arg;
 {
@@ -229,7 +390,18 @@
 }
 void smtp_ehlo(arg) char *arg;
 {
-  smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n");
+  smtp_greet("250-");
+#ifdef AUTHCRAM
+  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");
+#endif
+#ifdef TLS
+  if (!ssl) out("\r\n250-STARTTLS");
+#endif
+  out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n");
   seenmail = 0; dohelo(arg);
 }
 void smtp_rset()
@@ -240,27 +412,89 @@
 void smtp_mail(arg) char *arg;
 {
   if (!addrparse(arg)) { err_syntax(); return; }
-  flagbarf = bmfcheck();
+  if (bmfok) flagbarfbmf = bmcheck(BMCHECK_BMF);
+  switch(mfcheck()) {
+    case DNS_HARD: err_hmf(); return;
+    case DNS_SOFT: err_smf(); return;
+    case DNS_MEM: die_nomem();
+  }
   seenmail = 1;
   if (!stralloc_copys(&rcptto,"")) die_nomem();
   if (!stralloc_copys(&mailfrom,addr.s)) die_nomem();
   if (!stralloc_0(&mailfrom)) die_nomem();
+  rcptcount = 0;
   out("250 ok\r\n");
 }
+#ifdef TLS
+static int verify_cb(int ok, X509_STORE_CTX * ctx)
+{
+  return (1);
+}
+#endif
 void smtp_rcpt(arg) char *arg; {
   if (!seenmail) { err_wantmail(); return; }
   if (!addrparse(arg)) { err_syntax(); return; }
-  if (flagbarf) { err_bmf(); return; }
+  if ((!flagbarfbmf) && (bmtok)) { flagbarfbmt = bmcheck(BMCHECK_BMT); }
+  if (flagbarfbmf) { err_bmf(); return; }
+  if (flagbarfbmt) { err_bmt(); 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 (!stralloc_cats(&rcptto,"T")) die_nomem();
   if (!stralloc_cats(&rcptto,addr.s)) die_nomem();
   if (!stralloc_0(&rcptto)) die_nomem();
+  if (tarpitcount && ++rcptcount >= tarpitcount) while (sleep(tarpitdelay)); 
   out("250 ok\r\n");
 }
 
@@ -269,7 +503,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;
@@ -369,6 +607,9 @@
   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 +618,20 @@
   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
   blast(&hops);
   hops = (hops >= MAXHOPS);
   if (hops) qmail_fail(&qqt);
@@ -393,23 +646,299 @@
   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;}
+
+  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, "control/servercert.pem", 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, "control/servercert.pem"))
+   {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();
+
+  if(!(ssl=SSL_new(ctx))) die_read();
+  SSL_set_fd(ssl,0);
+  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
+
+
+char unique[FMT_ULONG + FMT_ULONG + 3];
+static stralloc authin = {0};
+static stralloc user = {0};
+static stralloc pass = {0};
+static stralloc resp = {0};
+static stralloc slop = {0};
+char *hostname;
+char **childargs;
+substdio ssup;
+char upbuf[128];
+int authd = 0;
+
+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(void)
+{
+  int child;
+  int wstat;
+  int pi[2];
+
+  if (!stralloc_0(&user)) die_nomem();
+  if (!stralloc_0(&pass)) die_nomem();
+  if (!stralloc_0(&resp)) die_nomem();
+
+  if (fd_copy(2,1) == -1) return err_pipe();
+  close(3);
+  if (pipe(pi) == -1) return err_pipe();
+  if (pi[0] != 3) return err_pipe();
+  switch(child = fork()) {
+    case -1:
+      return err_fork();
+    case 0:
+      close(pi[1]);
+      sig_pipedefault();
+      execvp(*childargs, childargs);
+      _exit(1);
+  }
+  close(pi[0]);
+
+  substdio_fdbuf(&ssup,write,pi[1],upbuf,sizeof upbuf);
+  if (substdio_put(&ssup,user.s,user.len) == -1) return err_write();
+  if (substdio_put(&ssup,pass.s,pass.len) == -1) return err_write();
+  if (substdio_put(&ssup,resp.s,resp.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)) { sleep(5); return 1; } /* no */
+  return 0; /* yes */
+}
+
+int auth_login(arg) char *arg;
+{
+  int r;
+
+  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 (*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();
+}
+
+#ifdef AUTHCRAM
+int auth_cram()
+{
+  int i, r;
+  char *s;
+
+  s = unique;
+  s += fmt_uint(s,getpid());
+  *s++ = '.';
+  s += fmt_ulong(s,(unsigned long) now());
+  *s++ = '@';
+  *s++ = 0;
+
+  if (!stralloc_copys(&pass,"<")) die_nomem();
+  if (!stralloc_cats(&pass,unique)) die_nomem();
+  if (!stralloc_cats(&pass,hostname)) die_nomem();
+  if (!stralloc_cats(&pass,">")) die_nomem();
+  if (b64encode(&pass,&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(&resp,s)) die_nomem();
+
+  if (!user.len || !resp.len) return err_input();
+  return authenticate();
+}
+#endif
+
+struct authcmd {
+  char *text;
+  int (*fun)();
+} authcmds[] = {
+  { "login", auth_login }
+, { "plain", auth_plain }
+#ifdef AUTHCRAM
+, { "cram-md5", auth_cram }
+#endif
+, { 0, err_noauth }
+};
+
+void smtp_auth(arg)
+char *arg;
+{
+  int i;
+  char *cmd = arg;
+
+  if (!hostname || !*childargs)
+  {
+    out("503 auth not available (#5.3.3)\r\n");
+    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(&resp,"")) die_nomem();
+
+  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 = "";
+      remoteinfo = user.s;
+      if (!env_unset("TCPREMOTEINFO")) die_read();
+      if (!env_put2("TCPREMOTEINFO",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;
+{
+  hostname = argv[1];
+  childargs = argv + 2;
+
+#ifdef TLS
+  sig_alarmcatch(sigalrm);
+#endif
   sig_pipeignore();
   if (chdir(auto_qmail) == -1) die_control();
   setup();
diff -urN ../qmail-1.03/qmail.c ./qmail.c
--- ../qmail-1.03/qmail.c	Mon Jun 15 03:53:16 1998
+++ ./qmail.c	Thu Jun  5 09:33:21 2003
@@ -6,14 +6,25 @@
 #include "fd.h"
 #include "qmail.h"
 #include "auto_qmail.h"
+#include "env.h"
 
-static char *binqqargs[2] = { "bin/qmail-queue", 0 } ;
+static char *binqqargs[2] = { 0, 0 } ;
+
+static void setup_qqargs()
+{
+  if(!binqqargs[0])
+    binqqargs[0] = env_get("QMAILQUEUE");
+  if(!binqqargs[0])
+    binqqargs[0] = "bin/qmail-queue";
+}
 
 int qmail_open(qq)
 struct qmail *qq;
 {
   int pim[2];
   int pie[2];
+
+  setup_qqargs();
 
   if (pipe(pim) == -1) return -1;
   if (pipe(pie) == -1) { close(pim[0]); close(pim[1]); return -1; }
diff -urN ../qmail-1.03/qregex.c ./qregex.c
--- ../qmail-1.03/qregex.c	Wed Dec 31 16:00:00 1969
+++ ./qregex.c	Thu Jun  5 09:33:21 2003
@@ -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)
+#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 -urN ../qmail-1.03/qregex.h ./qregex.h
--- ../qmail-1.03/qregex.h	Wed Dec 31 16:00:00 1969
+++ ./qregex.h	Thu Jun  5 09:33:21 2003
@@ -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 -urN ../qmail-1.03/spawn.c ./spawn.c
--- ../qmail-1.03/spawn.c	Mon Jun 15 03:53:16 1998
+++ ./spawn.c	Thu Jun  5 09:33:21 2003
@@ -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 -urN ../qmail-1.03/strpidt.c ./strpidt.c
--- ../qmail-1.03/strpidt.c	Wed Dec 31 16:00:00 1969
+++ ./strpidt.c	Thu Jun  5 09:33:21 2003
@@ -0,0 +1,26 @@
+/*
+** Copyright 1998 - 2000 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#if	HAVE_CONFIG_H
+#include	"config.h"
+#endif
+#include	"numlib.h"
+#include	<string.h>
+
+static const char rcsid[]="$Id: strpidt.c,v 1.3 2000/05/27 04:59:26 mrsam Exp $";
+
+char *str_pid_t(pid_t t, char *arg)
+{
+char	buf[NUMBUFSIZE];
+char	*p=buf+sizeof(buf)-1;
+
+	*p=0;
+	do
+	{
+		*--p= '0' + (t % 10);
+		t=t / 10;
+	} while(t);
+	return (strcpy(arg, p));
+}
diff -urN ../qmail-1.03/strtimet.c ./strtimet.c
--- ../qmail-1.03/strtimet.c	Wed Dec 31 16:00:00 1969
+++ ./strtimet.c	Thu Jun  5 09:33:21 2003
@@ -0,0 +1,26 @@
+/*
+** Copyright 1998 - 2000 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#if	HAVE_CONFIG_H
+#include	"config.h"
+#endif
+#include	"numlib.h"
+#include	<string.h>
+
+static const char rcsid[]="$Id: strtimet.c,v 1.3 2000/05/27 04:59:26 mrsam Exp $";
+
+char *str_time_t(time_t t, char *arg)
+{
+char	buf[NUMBUFSIZE];
+char	*p=buf+sizeof(buf)-1;
+
+	*p=0;
+	do
+	{
+		*--p= '0' + (t % 10);
+		t=t / 10;
+	} while(t);
+	return (strcpy(arg, p));
+}
