===================================================================
RCS file: .cvsignore
diff -N .cvsignore
--- .cvsignore.orig	1 Jan 1970 00:00:00 -0000
+++ .cvsignore	27 Dec 2001 04:36:30 -0000	1.1
@@ -0,0 +1,107 @@
+*.[0-9]
+auto-ccld.sh
+make-load
+find-systype
+systype
+load
+make-compile
+compile
+fork.h
+make-makelib
+makelib
+hassgprm.h
+hassgact.h
+hasflock.h
+haswaitp.h
+auto-str
+auto_qmail.c
+auto-int8
+auto_patrn.c
+socket.lib
+qmail-local
+uint32.h
+select.h
+auto-int
+auto_spawn.c
+chkspawn
+chkshsgr
+hasshsgr.h
+auto-uid
+auto-gid
+auto_uids.c
+qmail-lspawn
+auto_break.c
+auto_usera.c
+qmail-getpw
+hassalen.h
+dns.lib
+qmail-remote
+qmail-rspawn
+direntry.h
+auto_split.c
+qmail-clean
+hasmkffo.h
+hasnpbg1.h
+qmail-send
+qmail-start
+syslog.lib
+splogger
+qmail-queue
+qmail-inject
+predate
+datemail
+mailsubj
+qmail-upq
+qmail-showctl
+qmail-newu
+qmail-pw2u
+qmail-qread
+qmail-qstat
+qmail-tcpto
+qmail-tcpok
+qmail-pop3d
+qmail-popup
+qmail-qmqpc
+qmail-qmqpd
+qmail-qmtpd
+qmail-smtpd
+sendmail
+tcp-env
+qmail-newmrh
+config
+config-fast
+dnscname
+dnsptr
+dnsip
+dnsmxip
+dnsfq
+hostname
+ipmeprint
+qreceipt
+qsmhook
+qbiff
+forward
+preline
+condredirect
+bouncesaying
+except
+maildirmake
+maildir2mbox
+maildirwatch
+qail
+elq
+pinq
+idedit
+install-big
+install
+instcheck
+home
+home+df
+proc
+proc+df
+binm1
+binm1+df
+binm2
+binm2+df
+binm3
+binm3+df
===================================================================
RCS file: ChangeLog.arctic.org
diff -N ChangeLog.arctic.org
--- ChangeLog.arctic.org.orig	1 Jan 1970 00:00:00 -0000
+++ ChangeLog.arctic.org	9 Jun 2002 01:15:47 -0000	1.7
@@ -0,0 +1,43 @@
+Sat Jun  8 18:14:17 2002  dean gaudet <dean@arctic.org>
+
+	* qmail-smtpd now logs when it rejects mail due to badmailfrom,
+	  badrcptto, or mfcheck.
+
+Fri May 31 10:06:20 2002  dean gaudet <dean@arctic.org>
+	
+	* applied http://www.qmail.org/www.jedi.claranet.fr/qmail-bounce.patch
+	  which limits the size of bounced messages
+	* applied http://www.qmail.org/qmail-1.03-mfcheck.3.patch which
+	  requires a valid domain in envelope from.
+
+Mon Jan 28 13:01:25 2002  dean gaudet <dean@arctic.org>
+	
+	* update / apply my bare linefeed patch
+	  http://arctic.org/~dean/patches/qmail-0.95-liberal-lf.patch
+	  
+Fri Dec 28 11:03:31 2001  dean gaudet <dean@arctic.org>
+
+	* tagged arctic-1 to distribute patch
+
+Thu Dec 27 10:06:44 2001  dean gaudet <dean@arctic.org>
+
+	* picked up syncdir library from <http://untroubled.org/syncdir/>,
+	  this is necessary on linux because linux doesn't sync directory
+	  metadata.
+	* tweaked make-load.sh and Makefile to use syncdir for all
+	  executables.
+
+Thu Dec 27 09:35:45 2001  dean gaudet <dean@arctic.org>
+
+	* qmail-1.03 was imported, source <ftp://cr.yp.to/software/>
+	* badrcptto patch was added, source
+	  <http://patch.be/qmail/badrcptto.html>
+	* STARTTLS, SMTP AUTH, requireauth all added from patch at
+	  <http://drpepper.org/~zwhite/qmail-1.03-starttls-requireauth.patch>
+	  there was a small conflict with badrcptto which was easy to fix
+	* added (AUTH username) to Received: headers when SMTP AUTH is used
+	* note that there's a small difficulty with the checkpassword package
+	  when used with qmail-smtpd (for SMTP AUTH) -- since qmail-smtpd
+	  does NOT run as root, checkpassword won't be able to change
+	  uid/gid ... i just commented out the uid/gid changes and recompiled,
+	  and then named it /bin/checkpassword-smtp.
===================================================================
RCS file: /home/src/cvsroot/qmail/Makefile,v
retrieving revision 1.1.1.1
retrieving revision 1.5
diff -u -r1.1.1.1 -r1.5
--- Makefile.orig	27 Nov 2001 02:20:07 -0000	1.1.1.1
+++ Makefile	31 May 2002 17:07:35 -0000	1.5
@@ -810,7 +810,11 @@
 instcheck home home+df proc proc+df binm1 binm1+df binm2 binm2+df \
 binm3 binm3+df
 
-load: \
+libsyncdir.a: \
+makelib syncdir.o
+	./makelib libsyncdir.a syncdir.o
+
+load: libsyncdir.a \
 make-load warn-auto.sh systype
 	( cat warn-auto.sh; ./make-load "`cat systype`" ) > load
 	chmod 755 load
@@ -1446,7 +1450,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` \
+	-L/usr/local/ssl/lib -lssl -lcrypto
 
 qmail-remote.0: \
 qmail-remote.8
@@ -1536,13 +1541,14 @@
 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
+fs.a auto_qmail.o socket.lib dns.o dns.lib
 	./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \
 	timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \
 	received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \
 	datetime.a getln.a open.a sig.a case.a env.a stralloc.a \
 	alloc.a substdio.a error.a str.a fs.a auto_qmail.o  `cat \
-	socket.lib`
+	socket.lib` dns.o `cat dns.lib` \
+	-L/usr/local/ssl/lib -lssl -lcrypto
 
 qmail-smtpd.0: \
 qmail-smtpd.8
@@ -2053,6 +2059,10 @@
 compile substdo.c substdio.h str.h byte.h error.h
 	./compile substdo.c
 
+syncdir.o: \
+compile syncdir.c
+	./compile syncdir.c
+
 syslog.lib: \
 trysyslog.c compile load
 	( ( ./compile trysyslog.c && \
@@ -2139,3 +2149,22 @@
 wait_pid.o: \
 compile wait_pid.c error.h haswaitp.h
 	./compile wait_pid.c
+
+cert:
+	/usr/bin/openssl req -new -x509 -nodes \
+	-out /var/qmail/control/servercert.pem -days 366 \
+	-keyout /var/qmail/control/servercert.pem
+	chmod 640 /var/qmail/control/servercert.pem
+	chown qmaild.qmail /var/qmail/control/servercert.pem
+	ln -s /var/qmail/control/servercert.pem /var/qmail/control/clientcert.pem
+
+cert-req:
+	/usr/bin/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"
===================================================================
RCS file: README-starttls-requireauth
diff -N README-starttls-requireauth
--- README-starttls-requireauth.orig	1 Jan 1970 00:00:00 -0000
+++ README-starttls-requireauth	27 Dec 2001 04:41:14 -0000	1.2
@@ -0,0 +1,1450 @@
+Wed Dec 26 20:40:00 2001  dean gaudet <dean@arctic.org>
+- note that there's some stupid prot_gid()/prot_uid() crap
+  in checkpassword.c which isn't necessary for qmail-smtpd to
+  work with SMTP AUTH ... and since it won't be running as root
+  you'll need to hack checkpassword.c to disable that crap.
+
+
+---
+
+
+Zach White <zwhite@netlsd.org> 20010812
+http://drpepper.org/~zwhite/qmail-1.03-starttls-requireauth
+
+This patch combines 3 patches that seem to conflict with one another.
+The first is Frederick Wermeulen's tls.patch to implement STARTTLS in
+qmail-smtpd and qmail-remote. The other two are for SMTP AUTH, so you can
+authenticate a user before allowing them to relay. The first is Krzysztof 
+Dabrowski's patch to implement SMTP AUTH, the second is dburkes@netable.com's
+patch to require AUTH.
+
+Frederick Wermeulen released his patch under the same license as qmail, 
+however after consulting him the license was more restrictive than he
+intended, and he has granted me permission to incorporate his patch into
+this patch.
+
+Sources:
+http://www.esat.kuleuven.ac.be/~vermeule/qmail/tls.patch  STARTTLS
+http://members.elysium.pl/brush/qmail-smtpd-auth/         SMTP AUTH
+http://www.netable.com/~dburkes/qmail-smtpd-requireauth/  Require AUTH
+http://www.elysium.pl/members/brush/cmd5checkpw/         CRAM-MD5 Checkpassword
+
+Usage:
+
+Apply this patch. Compile, install, blah, blah. Put your pem enceded server
+key in /var/qmail/control/servercert.pem. Chown it to qmails, chmod it 600.
+Change your qmail-smtpd invoction to call "qmail-smtpd <checkpassword> <path 
+to true> <cram-md5 checkpassword> <pah to true>" instead of just qmail-send. 
+Setup either tcp wrappers or your tcprules file to add the environment 
+variable REQUIREAUTH="" for IP's you wish to require authentication from.
+
+If you run into problems with this patch, tcpdump and strace are your friend.
+
+This was tested using Netscape 4.75's mail client, with the require TLS option
+checked. It asked me for a password and then sent the mail. You should see
+headers similer to the following if everything worked correctly:
+
+Received: from unknown (HELO netlsd.org) (192.168.0.13) by 192.168.0.1 with RC4-MD5 encrypted SMTP; 12 Aug 2001 23:26:59 -0000
+
+The Recieved header indicates that tls was used and what cipher type.
+
+Questions, comments, and deathtreats can be sent to zwhite@netlsd.org. If you
+are having problems getting it to work, I probably won't help you, I do 
+enough of that with my real job. If you've found a bug and have a patch, 
+I'd love to see it. If you've found a bug and don't know how to fix it,
+I'll see if I can (But I'm not a programmer, and I'm not really sure this
+is the best way to have combined the patches. I'm just a sysadmin who needed
+this to work.)
+
+I've attached all the original documentation. You may find it useful.
+
+---- Included at the top of the starttls patchfile ----
+Frederik Vermeulen <jos-tls@kotnet.org> 20010627
+http://www.esat.kuleuven.ac.be/~vermeule/qmail/tls.patch
+
+This patch implements RFC2487 in qmail. This means you can 
+get SSL or TLS encrypted and authenticated SMTP between 
+the MTAs and between MTA and an MUA like Netscape. 
+The code is considered experimental (but has worked for
+many since its first release on 1999-03-21).
+
+Usage: - install OpenSSL-0.9.6a http://www.openssl.org/
+       - apply patch to qmail-1.03 http://www.qmail.org/ 
+         Makefile and conf-cc were patched for appropriate
+         linking. Apart from that, 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
+         somothing 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 ping@mail.linux.student.kuleuven.ac.be,
+         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;\
+             chmod 600 /var/qmail/control/rsa512.new; 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 with openssl-0.9.5).
+            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 read from 
+              /var/qmail/control/tlsclientciphers
+            qmail-smtpd: 
+              openssl cipher string read from TLSCIPHERS environment variable
+              (can be different based on client IP address e.g.)
+              or if that is not available /var/qmail/control/tlsserverciphers
+
+Caveats: - from this version on the server cert is in servercert.pem.
+         - 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.
+
+Copyright: Same terms as qmail
+           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>),
+           and modssl (R. Engelschall <rse@engelschall.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 Bodo M"oller <bmoeller@acm.org>
+           Bug report from Andy Dustman <adustman@comstar.net>
+
+Bug reports: mailto:<jos-tls@kotnet.org>
+
+----- README file from qmail-smtpd-auth-0.26 -----
+
+This patch adds support for smtp auth authetication protocol to qmail.
+It's based on Mrs.Brisby's smtp-auth patch with large enhancemets from me.
+
+You can always get the newest version from:
+http://members.elysium.pl/brush/qmail-smtpd-auth/
+
+To use all of it's fuctionality you will also have to obtain and install
+my 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/
+
+How to install it:
+
+Simply patch your qmail-1.03 distribution with the included patch file and
+recompile & install like usual.
+Also obtain, unpack , compile and isntall cmd5checkpw utility and add sample
+account to /etc/poppasswd file.
+
+How to use it:
+
+Patched qmail-smtpd should be invoked from inetd or tcpserver like this:
+
+smtp stream tcp nowait qmaild /var/qmail/bin/tcp-env tcp-env /var/qmail/bin/qmail-smtpd /bin/checkpassword /bin/true /bin/cmd5checkpw /bin/true
+
+the first parameter for qmail-smtpd is a checkpassword utility for clear
+text auth types, the second is arg for checkpassword, the third is a name of
+cram-md5 checkpassword utlity (my cmd5checkpw) and fourth is arg for it.
+
+give yout inetd a kill -HUP or restart tcpserver and here you go.
+
+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
+The Bat 1.39		-	LOGIN & CRAM-MD5
+Outlook Express 4	- 	LOGIN
+Outlook Express 5	-	LOGIN
+Netscape 4.x		-	LOGIN
+Netscape 4.0x		-	LOGIN
+Pegassus Mail 3.1x	-	CRAM-MD5
+
+Various compatibility issues:
+
+There used to be a problem with Netscape's Messenger but it's over.
+
+After test with Pegasus Mail 3.1 i've added the new style (rfc
+recommended) greeting message. Yoo can select beetwen them by #defining or
+ew#undefining USE_NEW_GREETING and USE_OLD_GREETING on the begining of 
+qmail-smtpd.c (around line 30). You can even enable both to maintain highest degree of 
+compatibility with various clients. This fix was suggested by 
+David Harris <David.Harris@pmail.gen.nz>, the developer of Pegasus Mail.
+
+----- README file from qmail-smtpd-requireauth-0.30 -----
+
+This patch enables you to optionally require authentication for qmail-smtpd 
+clients.
+
+How to install it:
+
+First, apply Krzysztof Dabrowski's qmail-smtpd-auth patch version 0.30 (see 
+http://members.elysium.pl/brush/qmail-smtpd-auth/).  Then, simply patch the 
+resulting qmail-smtpd.c with the included patch file.  Recompile and install 
+as usual.
+
+How to use it:
+
+If the REQUIREAUTH environment variable is set to anything (empty or 
+non-empty), the patched qmail-smtpd will require the client to authenticate 
+before accepting any MAIL FROM commands.
+
+See http://www.netable.com/~dburkes/qmail-smtpd-requireauth/index.html for a 
+practical usage scenario.
+
+----- End of Included documentation, patch below -----
+
+diff -ur qmail-1.03.orig/Makefile qmail-1.03/Makefile
+--- qmail-1.03.orig/Makefile	Mon Jun 15 10:53:16 1998
++++ qmail-1.03/Makefile	Sun Aug 12 23:35:33 2001
+@@ -1446,7 +1446,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` \
++	-L/usr/local/ssl/lib -lssl -lcrypto
+ 
+ qmail-remote.0: \
+ qmail-remote.8
+@@ -1542,7 +1543,7 @@
+ 	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`
++	socket.lib` -L/usr/local/ssl/lib -lssl -lcrypto
+ 
+ qmail-smtpd.0: \
+ qmail-smtpd.8
+@@ -2139,3 +2140,22 @@
+ wait_pid.o: \
+ compile wait_pid.c error.h haswaitp.h
+ 	./compile wait_pid.c
++
++cert:
++	/usr/local/ssl/bin/openssl req -new -x509 -nodes \
++	-out /var/qmail/control/servercert.pem -days 366 \
++	-keyout /var/qmail/control/servercert.pem
++	chmod 640 /var/qmail/control/servercert.pem
++	chown qmaild.qmail /var/qmail/control/servercert.pem
++	ln -s /var/qmail/control/servercert.pem /var/qmail/control/clientcert.pem
++
++cert-req:
++	/usr/local/ssl/bin/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"
+Only in qmail-1.03: Makefile.orig
+diff -ur qmail-1.03.orig/conf-cc qmail-1.03/conf-cc
+--- qmail-1.03.orig/conf-cc	Mon Jun 15 10:53:16 1998
++++ qmail-1.03/conf-cc	Sun Aug 12 23:35:35 2001
+@@ -1,3 +1,3 @@
+-cc -O2
++cc -O2 -DTLS -I/usr/local/ssl/include
+ 
+ This will be used to compile .c files.
+Only in qmail-1.03: conf-cc.orig
+diff -ur qmail-1.03.orig/dns.c qmail-1.03/dns.c
+--- qmail-1.03.orig/dns.c	Mon Jun 15 10:53:16 1998
++++ qmail-1.03/dns.c	Sun Aug 12 23:35:38 2001
+@@ -270,6 +270,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 +338,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;
+     }
+Only in qmail-1.03: dns.c.orig
+diff -ur qmail-1.03.orig/ipalloc.h qmail-1.03/ipalloc.h
+--- qmail-1.03.orig/ipalloc.h	Mon Jun 15 10:53:16 1998
++++ qmail-1.03/ipalloc.h	Sun Aug 12 23:35:38 2001
+@@ -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"
+ 
+Only in qmail-1.03: ipalloc.h.orig
+diff -ur qmail-1.03.orig/qmail-remote.c qmail-1.03/qmail-remote.c
+--- qmail-1.03.orig/qmail-remote.c	Mon Jun 15 10:53:16 1998
++++ qmail-1.03/qmail-remote.c	Sun Aug 12 23:35:34 2001
+@@ -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);
++   Free(str);
++   str=ONELINE_NAME(X509_get_issuer_name(peer));
++   out("; issuer="); out(str);
++   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);
+Only in qmail-1.03: qmail-remote.c.orig
+diff -ur qmail-1.03.orig/qmail-smtpd.c qmail-1.03/qmail-smtpd.c
+--- qmail-1.03.orig/qmail-smtpd.c	Mon Jun 15 10:53:16 1998
++++ qmail-1.03/qmail-smtpd.c	Sun Aug 12 23:35:34 2001
+@@ -1,4 +1,7 @@
+ #include "sig.h"
++#include <stdio.h>
++#include <unistd.h>
++#include <string.h>
+ #include "readwrite.h"
+ #include "stralloc.h"
+ #include "substdio.h"
+@@ -16,22 +19,80 @@
+ #include "scan.h"
+ #include "byte.h"
+ #include "case.h"
++#include "wait.h"
+ #include "env.h"
+ #include "now.h"
+ #include "exit.h"
+ #include "rcpthosts.h"
++#ifndef TLS
+ #include "timeoutread.h"
+ #include "timeoutwrite.h"
++#endif
+ #include "commands.h"
++#ifdef TLS
++#include <openssl/ssl.h>
++SSL *ssl = NULL;
++
++stralloc clientcert = {0};
++stralloc tlsserverciphers = {0};
++#endif
+ 
+ #define MAXHOPS 100
++#define USE_SMTPAUTH
++/*
++#define USE_OLD_GREETING
++#define USE_NEW_GREETING
++*/
++
+ unsigned int databytes = 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;
+ }
+@@ -45,12 +106,17 @@
+ void die_read() { _exit(1); }
+ void die_alarm() { out("451 timeout (#4.4.2)\r\n"); flush(); _exit(1); }
+ void die_nomem() { out("421 out of memory (#4.3.0)\r\n"); flush(); _exit(1); }
++void die_crash() { out("421 child crashed (#4.3.0)\r\n"); flush(); _exit(1); }
++void die_fork() { out("421 unable to start checkpassword.\r\n"); flush(); _exit(1); }
+ void die_control() { out("421 unable to read controls (#4.3.0)\r\n"); flush(); _exit(1); }
+ void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); }
+ void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); }
+ 
+ void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); }
+ void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); }
++#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"); }
+@@ -81,13 +147,16 @@
+ char *remoteinfo;
+ char *local;
+ char *relayclient;
++#ifdef TLS
++char *tlsciphers;
++#endif
+ 
+ stralloc helohost = {0};
+ char *fakehelo; /* pointer into helohost, or 0 */
+ 
+ void dohelo(arg) char *arg; {
+-  if (!stralloc_copys(&helohost,arg)) die_nomem(); 
+-  if (!stralloc_0(&helohost)) die_nomem(); 
++  if (!stralloc_copys(&helohost,arg)) die_nomem();
++  if (!stralloc_0(&helohost)) die_nomem();
+   fakehelo = case_diffs(remotehost,helohost.s) ? helohost.s : 0;
+ }
+ 
+@@ -101,7 +170,10 @@
+ {
+   char *x;
+   unsigned long u;
+- 
++#ifdef TLS
++  char *tlsciphers;
++#endif
++
+   if (control_init() == -1) die_control();
+   if (control_rldef(&greeting,"control/smtpgreeting",1,(char *) 0) != 1)
+     die_control();
+@@ -116,12 +188,12 @@
+   if (bmfok == -1) die_control();
+   if (bmfok)
+     if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem();
+- 
++
+   if (control_readint(&databytes,"control/databytes") == -1) die_control();
+   x = env_get("DATABYTES");
+   if (x) { scan_ulong(x,&u); databytes = u; }
+   if (!(databytes + 1)) --databytes;
+- 
++
+   remoteip = env_get("TCPREMOTEIP");
+   if (!remoteip) remoteip = "unknown";
+   local = env_get("TCPLOCALHOST");
+@@ -131,6 +203,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);
+ }
+ 
+@@ -146,7 +229,7 @@
+   struct ip_address ip;
+   int flagesc;
+   int flagquoted;
+- 
++
+   terminator = '>';
+   i = str_chr(arg,'<');
+   if (arg[i])
+@@ -229,7 +312,18 @@
+ }
+ void smtp_ehlo(arg) char *arg;
+ {
+-  smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n");
++  smtp_greet("250-"); 
++#ifdef TLS
++# ifdef USE_SMTPAUTH
++   if (ssl) out("\r\n250-PIPELINING\r\n250-AUTH LOGIN CRAM-MD5 PLAIN\r\n250 8BITMIME\r\n");
++   else out("\r\n250-PIPELINING\r\n250-STARTTLS\r\n250-AUTH LOGIN CRAM-MD5 PLAIN\r\n250 8BITMIME\r\n");
++# else
++   if (ssl) out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n");
++   else out("\r\n250-PIPELINING\r\n250-STARTTLS\r\n250 8BITMIME\r\n");
++# endif /* USE_SMTP_AUTH */
++#else
++  out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n");
++#endif /* TLS */
+   seenmail = 0; dohelo(arg);
+ }
+ void smtp_rset()
+@@ -247,6 +341,12 @@
+   if (!stralloc_0(&mailfrom)) die_nomem();
+   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; }
+@@ -257,7 +357,55 @@
+     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();
+@@ -269,7 +417,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;
+@@ -300,7 +452,7 @@
+   int flagmaybex; /* 1 if this line might match RECEIVED, if fih */
+   int flagmaybey; /* 1 if this line might match \r\n, if fih */
+   int flagmaybez; /* 1 if this line might match DELIVERED, if fih */
+- 
++
+   state = 1;
+   *hops = 0;
+   flaginheader = 1;
+@@ -369,6 +521,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 +532,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);
+@@ -394,22 +561,437 @@
+   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
++
++#ifdef USE_SMTPAUTH
++static unsigned char authenticated=0;
++static unsigned char *base64_alphabet =
++"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
++static int unbase64(ch) int ch; {
++  int i;
++  if (ch == '=') return 0;
++  for (i = 0; i < 64; i++)
++    if (ch == base64_alphabet[i])
++      return i;
++  return 0;
++}
++static int base64_dec_buffer(str,dst,len) const char *str;void *dst;int len;
++{
++  int i, j, l;
++  unsigned char input[4], output[3], *result = (char *)dst;
++  if (str == 0)
++    return 0;
++  l = str_len(str);
++  if (dst == 0 || l > len)
++    return (l / 4) * 3;
++  memset(dst,0,len);
++  for (i=j=0; i<l; i +=4) {
++    input[0] = unbase64(str[i]);
++    input[1] = unbase64(str[i+1]);
++    input[2] = unbase64(str[i+2]);
++    input[3] = unbase64(str[i+3]);
++    output[0] = (input[0] << 2) | (input[1] >> 4);
++    output[1] = (input[1] << 4) | (input[2] >> 2);
++    output[2] = (input[2] << 6) | (input[3]);
++    result[j] = output[0];
++    if (str[i+1] == '=') return j+1;
++    result[j+1]=output[1];
++    if (str[i+2] == '=') return j+2;
++    result[j+2]=output[2];
++    j += 3;
++  }
++  return j;
++}
++static char **smtpauth_argv;
++static char *auth_argv[4];
++static stralloc smtpauth = {0};
++static char smtpauthlogin[65];
++static char smtpauthpass[65];
++static char smtpauthtimestamp[65];
++
++char unique[FMT_ULONG + FMT_ULONG + 3];
++
++void base64encode(stralloc *input, stralloc *output)
++{
++int	a=0,b=0,c=0;
++int	i, j;
++int	d, e, f, g;
++
++	if (input->len == 0)	return;
++
++	for (j=i=0; i< input->len ; i += 3)
++	{
++		a=input->s[i];
++		b= i+1 < input->len ? input->s[i+1]:0;
++		c= i+2 < input->len ? input->s[i+2]:0;
++
++		d=base64_alphabet[ a >> 2 ];
++		e=base64_alphabet[ ((a & 3 ) << 4) | (b >> 4)];
++		f=base64_alphabet[ ((b & 15) << 2) | (c >> 6)];
++		g=base64_alphabet[ c & 63 ];
++		if (i + 1 >= input->len) f='=';
++		if (i + 2 >= input->len) g='=';
++		stralloc_append(output,&d);
++		stralloc_append(output,&e);
++		stralloc_append(output,&f);
++		stralloc_append(output,&g);
++	}
++}
++
++static int smtpauth_getl(void) {
++  int i;
++  if (!stralloc_copys(&smtpauth, "")) return -1;
++  for (;;) {
++    if (!stralloc_readyplus(&smtpauth,1)) return -1;
++    i = substdio_get(&ssin, smtpauth.s + smtpauth.len, 1);
++    if (i != 1) return i;
++    if (smtpauth.s[smtpauth.len] == '\n') break;
++    ++smtpauth.len;
++  }
++  if (smtpauth.len > 0) if (smtpauth.s[smtpauth.len-1] == '\r') --smtpauth.len;
++  smtpauth.s[smtpauth.len] = 0;
++  return smtpauth.len;
++}
++
++static void smtpauth_authenticate(void)
++{
++  int st, pid, fds[2];
++  
++    if (pipe(fds)) {
++      out("535 pipe failure\r\n");
++      flush();
++      _exit(0);
++    }
++    /* spawn external program
++
++    external program should return '0' if it was successful,
++
++    submit: /bin/checkpassword /bin/true
++  
++    */
++    switch ((pid=fork())) {
++      case -1: die_fork();
++      case 0: close(fds[1]);
++        fd_copy(3,fds[0]);
++        flush();
++        execvp(auth_argv[1], auth_argv+1);
++        die_nomem();
++    };
++    close(fds[0]);
++    write(fds[1], smtpauthlogin, str_len(smtpauthlogin)+1);
++    write(fds[1], smtpauthpass, str_len(smtpauthpass)+1);
++    if (str_len(smtpauthtimestamp))
++    {
++          write(fds[1], smtpauthtimestamp, str_len(smtpauthtimestamp)+1);
++    }
++    close(fds[1]);
++    wait_pid(&st, pid);
++
++    if (wait_crashed(st))
++      die_crash();
++    if (wait_exitcode(st) == 0) {
++      out("235 go ahead\r\n");
++      flush();
++      relayclient="";
++      authenticated=1;
++      return;
++    }
++    sleep(2);
++    out("535 auth failure\r\n"); 
++    flush(); 
++    return;
++}
++
++void smtp_auth(arg) char *arg; {
++  int ret,i,toolong,start;
++  char *helper;
++  /* netscape 4.5 sends AUTH LOGIN <base64encodedusername>
++     microsoft outlook express sends AUTH LOGIN
++
++     idea is simple
++
++     use an external program to test authority
++     if success, set 'RELAYCLIENT'
++     otherwise, let them know nicely (hangup)
++
++     note, i really don't like djb's coding style even though i'm using it here.
++     i think using spaces for tabs is bad.
++                                                -mrs.brisby@nimh.org
++  */
++  /* Here i've added support for other auth types.
++  
++  						-brush@elysium.pl	*/
++  if (!authenticated)
++  {
++    if ((ret=strncasecmp(arg,"login",5))==0)
++    {
++      while (arg && *arg && *arg != ' ') arg++;
++
++      /* pass over the space */
++      while (arg && *arg && *arg == ' ') arg++;
++
++      if (arg && *arg) {
++        /* here's the base64 encoded login */
++        base64_dec_buffer(arg, smtpauthlogin, sizeof(smtpauthlogin));
++      } else {
++        out("334 VXNlcm5hbWU6\r\n"); /* b64 <- 'Username:' */
++        flush();
++        if (smtpauth_getl() > 0)
++          base64_dec_buffer(smtpauth.s, smtpauthlogin, sizeof(smtpauthlogin));
++        else
++          die_read();
++      }
++      out("334 UGFzc3dvcmQ6\r\n"); /* b64 <- 'Password:' */
++      flush();
++      if (smtpauth_getl() > 0)
++        base64_dec_buffer(smtpauth.s, smtpauthpass, sizeof(smtpauthpass));
++      else
++        die_read();
++      smtpauthtimestamp[0]=0;
++      auth_argv[1]=smtpauth_argv[1];	/* change checkpass prg */
++      auth_argv[2]=smtpauth_argv[2];	/* change checkpass prg */
++      auth_argv[3]=NULL;			/* change checkpass prg */
++      smtpauth_authenticate();  
++      return;
++    }
++    else if ((ret=strncasecmp(arg,"plain",5))==0)
++    {
++      static char smtpauthloginpass[200];
++
++      while (arg && *arg && *arg != ' ') arg++;
++
++      /* pass over the space */
++      while (arg && *arg && *arg == ' ') arg++;
++ 
++      if (arg && *arg)
++      {
++        if(strlen(arg)*3/4 >= sizeof(smtpauthloginpass))
++        {
++          out("535 input too long\r\n");
++          flush();
++          return;
++        }
++        /* here's the base64 encoded login/password */
++        base64_dec_buffer(arg, smtpauthloginpass, sizeof(smtpauthloginpass)-1);
++      } else {
++        out("334 ok. go on.\r\n");
++        flush();
++        i=smtpauth_getl();
++        if(i <= 0)
++          die_read();
++        else if(i*3/4 >= sizeof(smtpauthloginpass))
++        {
++          out("535 input too long\r\n");
++          flush();
++          return;
++        } else base64_dec_buffer(smtpauth.s, smtpauthloginpass, sizeof(smtpauthloginpass)-1);
++      }
++      smtpauthloginpass[sizeof(smtpauthloginpass)-1]=0;
++      start=strlen(smtpauthloginpass)+1;
++      if((start >= sizeof(smtpauthloginpass)) || (strlen(smtpauthloginpass+start) >= 65))
++      {
++          out("535 malformed input\r\n");
++          flush();
++          return;
++      }
++      strcpy(smtpauthlogin,smtpauthloginpass+start);
++
++      start+=strlen(smtpauthlogin)+1;
++      if((start >= sizeof(smtpauthloginpass)) || (strlen(smtpauthloginpass+start) >= 65))
++      {
++          out("535 malformed input\r\n");
++          flush();
++          return;
++      }
++      strcpy(smtpauthpass,smtpauthloginpass+start);
++
++      smtpauthtimestamp[0]=0;
++      auth_argv[1]=smtpauth_argv[1];	/* change checkpass prg */
++      auth_argv[2]=smtpauth_argv[2];	/* change checkpass prg */
++      auth_argv[3]=NULL;		/* change checkpass prg */
++      smtpauth_authenticate();
++      return;
++    }
++    else if ((ret=strncasecmp(arg,"cram-md5",8))==0)  
++    {
++      int r;
++      static stralloc me = {0};
++      static stralloc greet = {0};
++      static stralloc greetenc = {0};
++      char *s;
++      r = control_readline(&me,"control/me");
++      if (r != 1) 
++      {
++        out("535 internal server error\r\n"); flush(); _exit(0);
++      }
++      for (r=0;r <= me.len;r++)
++      {
++        if (me.s[r]=='\n')
++        {
++          me.s[r]=0;
++          break;
++        }
++      }
++
++      s = unique;
++      s += fmt_uint(s,getpid());
++      *s++ = '.';
++      s += fmt_ulong(s,(unsigned long) now());
++      *s++ = '@';
++      *s++ = 0;
++      if (greet.len)
++      {
++	for (i=0;i<greet.len;i++)
++	{
++	  greet.s[i]=0;
++	}
++        greet.len=0;
++      }
++      stralloc_append(&greet,"<");
++      stralloc_cats(&greet,unique);
++      stralloc_cats(&greet,me.s);
++      stralloc_cats(&greet,">");
++      greet.s[greet.len]=0;	// obscure fix but it works
++      stralloc_readyplus(&greet,3);
++      if (greetenc.len)
++      {
++	for (i=0;i<greetenc.len;i++)
++	{
++	  greetenc.s[i]=0;
++	}
++        greetenc.len=0;
++      }
++      flush();
++      base64encode(&greet,&greetenc);
++      greetenc.s[greetenc.len]=0;	// obscure fix but it works
++      out("334 ");
++      out(greetenc.s);
++      out("\r\n");
++      flush();
++      if (smtpauth_getl() > 0)
++      {
++        s=calloc((size_t) strlen(smtpauth.s),(size_t)1);
++        base64_dec_buffer(smtpauth.s, s, strlen(smtpauth.s));
++      }
++      helper=strtok(s," ");
++      if(helper!=NULL)
++      {
++        strncpy (smtpauthlogin,helper,64);
++      }
++      else
++      {
++        out("535 malformed input\r\n"); 
++        return;
++      }
++      helper=strtok(NULL," ");
++      if(helper!=NULL)
++      {
++        strncpy (smtpauthtimestamp,helper,64);
++      }
++      else
++      {
++        out("535 malformed input\r\n"); 
++        return;
++      }
++      strncpy (smtpauthpass,greet.s,64);
++      auth_argv[1]=smtpauth_argv[3];	/* change checkpass prg */
++      auth_argv[2]=smtpauth_argv[4];	/* change checkpass prg */
++      auth_argv[3]=NULL;		/* change checkpass prg */
++      smtpauth_authenticate();
++      return;
++    }
++    else
++    {
++      out("504 auth type not supported\r\n"); 
++      flush();
++      return;
++    }
++  }
++  else
++  {
++      out("503 you are already authenticated\r\n"); 
++      flush(); 
++      return;
++  }
++}
++#endif
++
+ struct commands smtpcommands[] = {
+   { "rcpt", smtp_rcpt, 0 }
+ , { "mail", smtp_mail, 0 }
+ , { "data", smtp_data, flush }
++#ifdef USE_SMTPAUTH
++, { "auth", smtp_auth, flush }
++#endif
+ , { "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;
+ {
++#ifdef TLS
++  sig_alarmcatch(sigalrm);
++#endif
++#ifdef USE_SMTPAUTH
++  smtpauth_argv = argv;
++#endif
+   sig_pipeignore();
+   if (chdir(auto_qmail) == -1) die_control();
+   setup();
===================================================================
RCS file: /home/src/cvsroot/qmail/conf-cc,v
retrieving revision 1.1.1.1
retrieving revision 1.3
diff -u -r1.1.1.1 -r1.3
--- conf-cc.orig	27 Nov 2001 02:20:07 -0000	1.1.1.1
+++ conf-cc	22 May 2002 02:37:53 -0000	1.3
@@ -1,3 +1,3 @@
-cc -O2
+cc -O2 -DTLS -I/usr/include
 
 This will be used to compile .c files.
===================================================================
RCS file: /home/src/cvsroot/qmail/dns.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- dns.c.orig	27 Nov 2001 02:20:07 -0000	1.1.1.1
+++ dns.c	21 Dec 2001 03:29:00 -0000	1.2
@@ -270,6 +270,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 +338,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;
     }
===================================================================
RCS file: /home/src/cvsroot/qmail/ipalloc.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- ipalloc.h.orig	27 Nov 2001 02:20:07 -0000	1.1.1.1
+++ ipalloc.h	21 Dec 2001 03:29:00 -0000	1.2
@@ -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"
 
===================================================================
RCS file: /home/src/cvsroot/qmail/make-load.sh,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- make-load.sh.orig	27 Nov 2001 02:20:07 -0000	1.1.1.1
+++ make-load.sh	27 Dec 2001 18:08:09 -0000	1.2
@@ -1,2 +1,2 @@
 echo 'main="$1"; shift'
-echo exec "$LD" '-o "$main" "$main".o ${1+"$@"}'
+echo exec "$LD" '-o "$main" "$main".o ${1+"$@"} -L. -lsyncdir'
===================================================================
RCS file: /home/src/cvsroot/qmail/qmail-control.9,v
retrieving revision 1.1.1.1
retrieving revision 1.3
diff -u -r1.1.1.1 -r1.3
--- qmail-control.9.orig	27 Nov 2001 02:20:07 -0000	1.1.1.1
+++ qmail-control.9	31 May 2002 17:40:23 -0000	1.3
@@ -43,6 +43,7 @@
 .I badmailfrom	\fR(none)	\fRqmail-smtpd
 .I bouncefrom	\fRMAILER-DAEMON	\fRqmail-send
 .I bouncehost	\fIme	\fRqmail-send
+.I bouncemaxbytes	\fR50000	\fRqmail-send
 .I concurrencylocal	\fR10	\fRqmail-send
 .I concurrencyremote	\fR20	\fRqmail-send
 .I defaultdomain	\fIme	\fRqmail-inject
@@ -55,6 +56,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
===================================================================
RCS file: /home/src/cvsroot/qmail/qmail-qmqpd.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- qmail-qmqpd.c.orig	27 Nov 2001 02:20:07 -0000	1.1.1.1
+++ qmail-qmqpd.c	27 Dec 2001 04:55:04 -0000	1.2
@@ -78,7 +78,7 @@
   if (!local) local = env_get("TCPLOCALIP");
   if (!local) local = "unknown";
  
-  received(&qq,"QMQP",local,remoteip,remotehost,remoteinfo,(char *) 0);
+  received(&qq,"QMQP",local,remoteip,remotehost,remoteinfo,(char *) 0, (char *) 0);
 }
 
 char buf[1000];
===================================================================
RCS file: /home/src/cvsroot/qmail/qmail-remote.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- qmail-remote.c.orig	27 Nov 2001 02:20:07 -0000	1.1.1.1
+++ qmail-remote.c	21 Dec 2001 03:29:00 -0000	1.2
@@ -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);
+   Free(str);
+   str=ONELINE_NAME(X509_get_issuer_name(peer));
+   out("; issuer="); out(str);
+   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);
===================================================================
RCS file: /home/src/cvsroot/qmail/qmail-send.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- qmail-send.c.orig	27 Nov 2001 02:20:07 -0000	1.1.1.1
+++ qmail-send.c	31 May 2002 17:40:23 -0000	1.2
@@ -44,6 +44,8 @@
 
 int lifetime = 604800;
 
+int bouncemaxbytes = 50000;
+
 stralloc percenthack = {0};
 struct constmap mappercenthack;
 stralloc locals = {0};
@@ -740,9 +742,17 @@
      qmail_fail(&qqt);
    else
     {
+     int bytestogo = bouncemaxbytes;
+     int bytestoget = (bytestogo < sizeof buf) ? bytestogo : sizeof buf;
      substdio_fdbuf(&ssread,read,fd,inbuf,sizeof(inbuf));
-     while ((r = substdio_get(&ssread,buf,sizeof(buf))) > 0)
+     while (bytestoget > 0 && (r = substdio_get(&ssread,buf,bytestoget)) > 0) {
        qmail_put(&qqt,buf,r);
+       bytestogo -= bytestoget;
+       bytestoget = (bytestogo < sizeof buf) ? bytestogo : sizeof buf;
+     }
+     if (r > 0) {
+       qmail_puts(&qqt,"\n\n--- End of message stripped.\n");
+     }
      close(fd);
      if (r == -1)
        qmail_fail(&qqt);
@@ -1442,6 +1452,7 @@
 /* this file is too long ---------------------------------------------- MAIN */
 
 int getcontrols() { if (control_init() == -1) return 0;
+ if (control_readint(&bouncemaxbytes,"control/bouncemaxbytes") == -1) return 0;   
  if (control_readint(&lifetime,"control/queuelifetime") == -1) return 0;
  if (control_readint(&concurrency[0],"control/concurrencylocal") == -1) return 0;
  if (control_readint(&concurrency[1],"control/concurrencyremote") == -1) return 0;
===================================================================
RCS file: /home/src/cvsroot/qmail/qmail-smtpd.8,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- qmail-smtpd.8.orig	27 Nov 2001 02:20:07 -0000	1.1.1.1
+++ qmail-smtpd.8	31 May 2002 17:07:36 -0000	1.2
@@ -97,6 +97,13 @@
 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, and
+refuses mail if the domain does not resolve.  It can be
+handy when you want to filter out spamhosts.
+.TP 5
 .I morercpthosts
 Extra allowed RCPT domains.
 If
===================================================================
RCS file: /home/src/cvsroot/qmail/qmail-smtpd.c,v
retrieving revision 1.1.1.1
retrieving revision 1.7
diff -u -r1.1.1.1 -r1.7
--- qmail-smtpd.c.orig	27 Nov 2001 02:20:07 -0000	1.1.1.1
+++ qmail-smtpd.c	9 Jun 2002 01:15:47 -0000	1.7
@@ -1,4 +1,7 @@
 #include "sig.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
 #include "readwrite.h"
 #include "stralloc.h"
 #include "substdio.h"
@@ -16,22 +19,87 @@
 #include "scan.h"
 #include "byte.h"
 #include "case.h"
+#include "wait.h"
 #include "env.h"
 #include "now.h"
 #include "exit.h"
 #include "rcpthosts.h"
+#ifndef TLS
 #include "timeoutread.h"
 #include "timeoutwrite.h"
+#endif
 #include "commands.h"
+#include "dns.h"
+#ifdef TLS
+#include <openssl/ssl.h>
+SSL *ssl = NULL;
+
+stralloc clientcert = {0};
+stralloc tlsserverciphers = {0};
+#endif
 
 #define MAXHOPS 100
+#define USE_SMTPAUTH
+/*
+#define USE_OLD_GREETING
+#define USE_NEW_GREETING
+*/
+
 unsigned int databytes = 0;
+unsigned int mfchk = 0;
 int timeout = 1200;
 
+#ifdef USE_SMTPAUTH
+static unsigned char authenticated=0;
+static char smtpauthlogin[65];
+#endif
+
+#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;
 }
@@ -39,18 +107,40 @@
 char ssoutbuf[512];
 substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf);
 
+int errwrite(fd,buf,len) int fd; char *buf; int len;
+{
+  int r;
+  r = timeoutwrite(timeout,fd,buf,len);
+  if (r <= 0) _exit(1);
+  return r;
+}
+
+char sserrbuf[512];
+substdio sserr = SUBSTDIO_FDBUF(errwrite,2,sserrbuf,sizeof sserrbuf);
+
+static stralloc err;
+char strnum[FMT_ULONG];
+
 void flush() { substdio_flush(&ssout); }
 void out(s) char *s; { substdio_puts(&ssout,s); }
 
 void die_read() { _exit(1); }
 void die_alarm() { out("451 timeout (#4.4.2)\r\n"); flush(); _exit(1); }
 void die_nomem() { out("421 out of memory (#4.3.0)\r\n"); flush(); _exit(1); }
+void die_crash() { out("421 child crashed (#4.3.0)\r\n"); flush(); _exit(1); }
+void die_fork() { out("421 unable to start checkpassword.\r\n"); flush(); _exit(1); }
 void die_control() { out("421 unable to read controls (#4.3.0)\r\n"); flush(); _exit(1); }
 void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); }
 void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); }
 
 void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); }
+void err_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_brt() { out("553 sorry, this recipient is in my badrcptto list (#5.7.1)\r\n"); }
 void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); }
+#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"); }
@@ -81,13 +171,16 @@
 char *remoteinfo;
 char *local;
 char *relayclient;
+#ifdef TLS
+char *tlsciphers;
+#endif
 
 stralloc helohost = {0};
 char *fakehelo; /* pointer into helohost, or 0 */
 
 void dohelo(arg) char *arg; {
-  if (!stralloc_copys(&helohost,arg)) die_nomem(); 
-  if (!stralloc_0(&helohost)) die_nomem(); 
+  if (!stralloc_copys(&helohost,arg)) die_nomem();
+  if (!stralloc_0(&helohost)) die_nomem();
   fakehelo = case_diffs(remotehost,helohost.s) ? helohost.s : 0;
 }
 
@@ -96,12 +189,18 @@
 int bmfok = 0;
 stralloc bmf = {0};
 struct constmap mapbmf;
+int brtok = 0;
+stralloc brt = {0};
+struct constmap mapbrt;
 
 void setup()
 {
   char *x;
   unsigned long u;
- 
+#ifdef TLS
+  char *tlsciphers;
+#endif
+
   if (control_init() == -1) die_control();
   if (control_rldef(&greeting,"control/smtpgreeting",1,(char *) 0) != 1)
     die_control();
@@ -112,10 +211,19 @@
 
   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();
+
+  brtok = control_readfile(&brt,"control/badrcptto",0);
+  if (brtok == -1) die_control();
+  if (brtok)
+    if (!constmap_init(&mapbrt,brt.s,brt.len,0)) die_nomem();
  
   if (control_readint(&databytes,"control/databytes") == -1) die_control();
   x = env_get("DATABYTES");
@@ -131,6 +239,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);
 }
 
@@ -146,7 +265,7 @@
   struct ip_address ip;
   int flagesc;
   int flagquoted;
- 
+
   terminator = '>';
   i = str_chr(arg,'<');
   if (arg[i])
@@ -201,10 +320,71 @@
 {
   int j;
   if (!bmfok) return 0;
-  if (constmap(&mapbmf,addr.s,addr.len - 1)) return 1;
+  if (constmap(&mapbmf,addr.s,addr.len - 1)) goto match;
   j = byte_rchr(addr.s,addr.len,'@');
   if (j < addr.len)
-    if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) return 1;
+    if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) goto match;
+  return 0;
+
+match:
+  if (!stralloc_copys(&err,"qmail-smtpd: pid ")) die_nomem();
+  if (!stralloc_catb(&err,strnum,fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem();
+  if (!stralloc_cats(&err," badmailfrom ")) die_nomem();
+  if (!stralloc_catb(&err,addr.s,addr.len - 1)) die_nomem();
+  if (!stralloc_cats(&err,"\n")) die_nomem();
+  substdio_put(&sserr,err.s,err.len);
+  substdio_flush(&sserr);
+  return 1;
+}
+
+int brtcheck()
+{
+  int j;
+  if (!brtok) return 0;
+  if (constmap(&mapbrt,addr.s,addr.len - 1)) goto match;
+  j = byte_rchr(addr.s,addr.len,'@');
+  if (j < addr.len)
+    if (constmap(&mapbrt,addr.s + j,addr.len - j - 1)) goto match;
+  return 0;
+
+match:
+  if (!stralloc_copys(&err,"qmail-smtpd: pid ")) die_nomem();
+  if (!stralloc_catb(&err,strnum,fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem();
+  if (!stralloc_cats(&err," badrcptto ")) die_nomem();
+  if (!stralloc_catb(&err,addr.s,addr.len - 1)) die_nomem();
+  if (!stralloc_cats(&err,"\n")) die_nomem();
+  substdio_put(&sserr,err.s,err.len);
+  substdio_flush(&sserr);
+  return 1;
+}
+
+int mfcheck()
+{
+  stralloc sa = {0};
+  ipalloc ia = {0};
+  unsigned int random;
+  int j;
+
+  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) {
+      if (j != DNS_MEM) {
+	if (!stralloc_copys(&err,"qmail-smtpd: pid ")) die_nomem();
+	if (!stralloc_catb(&err,strnum,fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem();
+	if (!stralloc_cats(&err," mfcheck ")) die_nomem();
+	if (!stralloc_catb(&err,addr.s,addr.len - 1)) die_nomem();
+	if (!stralloc_cats(&err,(j==DNS_HARD)?" hard\n":" soft\n")) die_nomem();
+	substdio_put(&sserr,err.s,err.len);
+	substdio_flush(&sserr);
+      }
+      return j;
+    }
+  }
   return 0;
 }
 
@@ -229,7 +409,18 @@
 }
 void smtp_ehlo(arg) char *arg;
 {
-  smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n");
+  smtp_greet("250-"); 
+#ifdef TLS
+# ifdef USE_SMTPAUTH
+   if (ssl) out("\r\n250-PIPELINING\r\n250-AUTH LOGIN CRAM-MD5 PLAIN\r\n250 8BITMIME\r\n");
+   else out("\r\n250-PIPELINING\r\n250-STARTTLS\r\n250-AUTH LOGIN CRAM-MD5 PLAIN\r\n250 8BITMIME\r\n");
+# else
+   if (ssl) out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n");
+   else out("\r\n250-PIPELINING\r\n250-STARTTLS\r\n250 8BITMIME\r\n");
+# endif /* USE_SMTP_AUTH */
+#else
+  out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n");
+#endif /* TLS */
   seenmail = 0; dohelo(arg);
 }
 void smtp_rset()
@@ -241,12 +432,23 @@
 {
   if (!addrparse(arg)) { err_syntax(); return; }
   flagbarf = bmfcheck();
+  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();
   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; }
@@ -257,7 +459,56 @@
     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 (!env_get("RELAYCLIENT") && brtcheck()) { err_brt(); return; }
   if (!stralloc_cats(&rcptto,"T")) die_nomem();
   if (!stralloc_cats(&rcptto,addr.s)) die_nomem();
   if (!stralloc_0(&rcptto)) die_nomem();
@@ -269,7 +520,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;
@@ -300,7 +555,7 @@
   int flagmaybex; /* 1 if this line might match RECEIVED, if fih */
   int flagmaybey; /* 1 if this line might match \r\n, if fih */
   int flagmaybez; /* 1 if this line might match DELIVERED, if fih */
- 
+
   state = 1;
   *hops = 0;
   flaginheader = 1;
@@ -322,17 +577,16 @@
     }
     switch(state) {
       case 0:
-        if (ch == '\n') straynewline();
+        if (ch == '\n') { state = 1; break; }
         if (ch == '\r') { state = 4; continue; }
         break;
       case 1: /* \r\n */
-        if (ch == '\n') straynewline();
         if (ch == '.') { state = 2; continue; }
         if (ch == '\r') { state = 4; continue; }
-        state = 0;
+        if (ch != '\n') state = 0;
         break;
       case 2: /* \r\n + . */
-        if (ch == '\n') straynewline();
+        if (ch == '\n') return;	/* this is what sendmail-8.8.4 does -djg */
         if (ch == '\r') { state = 3; continue; }
         state = 0;
         break;
@@ -369,6 +623,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 +634,21 @@
   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,
+  	authenticated ? smtpauthlogin : 0);
+#else
   received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo);
+#endif
   blast(&hops);
   hops = (hops >= MAXHOPS);
   if (hops) qmail_fail(&qqt);
@@ -394,22 +664,435 @@
   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
+
+#ifdef USE_SMTPAUTH
+static unsigned char *base64_alphabet =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+static int unbase64(ch) int ch; {
+  int i;
+  if (ch == '=') return 0;
+  for (i = 0; i < 64; i++)
+    if (ch == base64_alphabet[i])
+      return i;
+  return 0;
+}
+static int base64_dec_buffer(str,dst,len) const char *str;void *dst;int len;
+{
+  int i, j, l;
+  unsigned char input[4], output[3], *result = (char *)dst;
+  if (str == 0)
+    return 0;
+  l = str_len(str);
+  if (dst == 0 || l > len)
+    return (l / 4) * 3;
+  memset(dst,0,len);
+  for (i=j=0; i<l; i +=4) {
+    input[0] = unbase64(str[i]);
+    input[1] = unbase64(str[i+1]);
+    input[2] = unbase64(str[i+2]);
+    input[3] = unbase64(str[i+3]);
+    output[0] = (input[0] << 2) | (input[1] >> 4);
+    output[1] = (input[1] << 4) | (input[2] >> 2);
+    output[2] = (input[2] << 6) | (input[3]);
+    result[j] = output[0];
+    if (str[i+1] == '=') return j+1;
+    result[j+1]=output[1];
+    if (str[i+2] == '=') return j+2;
+    result[j+2]=output[2];
+    j += 3;
+  }
+  return j;
+}
+static char **smtpauth_argv;
+static char *auth_argv[4];
+static stralloc smtpauth = {0};
+static char smtpauthpass[65];
+static char smtpauthtimestamp[65];
+
+char unique[FMT_ULONG + FMT_ULONG + 3];
+
+void base64encode(stralloc *input, stralloc *output)
+{
+int	a=0,b=0,c=0;
+int	i, j;
+int	d, e, f, g;
+
+	if (input->len == 0)	return;
+
+	for (j=i=0; i< input->len ; i += 3)
+	{
+		a=input->s[i];
+		b= i+1 < input->len ? input->s[i+1]:0;
+		c= i+2 < input->len ? input->s[i+2]:0;
+
+		d=base64_alphabet[ a >> 2 ];
+		e=base64_alphabet[ ((a & 3 ) << 4) | (b >> 4)];
+		f=base64_alphabet[ ((b & 15) << 2) | (c >> 6)];
+		g=base64_alphabet[ c & 63 ];
+		if (i + 1 >= input->len) f='=';
+		if (i + 2 >= input->len) g='=';
+		stralloc_append(output,&d);
+		stralloc_append(output,&e);
+		stralloc_append(output,&f);
+		stralloc_append(output,&g);
+	}
+}
+
+static int smtpauth_getl(void) {
+  int i;
+  if (!stralloc_copys(&smtpauth, "")) return -1;
+  for (;;) {
+    if (!stralloc_readyplus(&smtpauth,1)) return -1;
+    i = substdio_get(&ssin, smtpauth.s + smtpauth.len, 1);
+    if (i != 1) return i;
+    if (smtpauth.s[smtpauth.len] == '\n') break;
+    ++smtpauth.len;
+  }
+  if (smtpauth.len > 0) if (smtpauth.s[smtpauth.len-1] == '\r') --smtpauth.len;
+  smtpauth.s[smtpauth.len] = 0;
+  return smtpauth.len;
+}
+
+static void smtpauth_authenticate(void)
+{
+  int st, pid, fds[2];
+  
+    if (pipe(fds)) {
+      out("535 pipe failure\r\n");
+      flush();
+      _exit(0);
+    }
+    /* spawn external program
+
+    external program should return '0' if it was successful,
+
+    submit: /bin/checkpassword /bin/true
+  
+    */
+    switch ((pid=fork())) {
+      case -1: die_fork();
+      case 0: close(fds[1]);
+        fd_copy(3,fds[0]);
+        flush();
+        execvp(auth_argv[1], auth_argv+1);
+        die_nomem();
+    };
+    close(fds[0]);
+    write(fds[1], smtpauthlogin, str_len(smtpauthlogin)+1);
+    write(fds[1], smtpauthpass, str_len(smtpauthpass)+1);
+    if (str_len(smtpauthtimestamp))
+    {
+          write(fds[1], smtpauthtimestamp, str_len(smtpauthtimestamp)+1);
+    }
+    close(fds[1]);
+    wait_pid(&st, pid);
+
+    if (wait_crashed(st))
+      die_crash();
+    if (wait_exitcode(st) == 0) {
+      out("235 go ahead\r\n");
+      flush();
+      relayclient="";
+      authenticated=1;
+      return;
+    }
+    sleep(2);
+    out("535 auth failure\r\n"); 
+    flush(); 
+    return;
+}
+
+void smtp_auth(arg) char *arg; {
+  int ret,i,toolong,start;
+  char *helper;
+  /* netscape 4.5 sends AUTH LOGIN <base64encodedusername>
+     microsoft outlook express sends AUTH LOGIN
+
+     idea is simple
+
+     use an external program to test authority
+     if success, set 'RELAYCLIENT'
+     otherwise, let them know nicely (hangup)
+
+     note, i really don't like djb's coding style even though i'm using it here.
+     i think using spaces for tabs is bad.
+                                                -mrs.brisby@nimh.org
+  */
+  /* Here i've added support for other auth types.
+  
+  						-brush@elysium.pl	*/
+  if (!authenticated)
+  {
+    if ((ret=strncasecmp(arg,"login",5))==0)
+    {
+      while (arg && *arg && *arg != ' ') arg++;
+
+      /* pass over the space */
+      while (arg && *arg && *arg == ' ') arg++;
+
+      if (arg && *arg) {
+        /* here's the base64 encoded login */
+        base64_dec_buffer(arg, smtpauthlogin, sizeof(smtpauthlogin));
+      } else {
+        out("334 VXNlcm5hbWU6\r\n"); /* b64 <- 'Username:' */
+        flush();
+        if (smtpauth_getl() > 0)
+          base64_dec_buffer(smtpauth.s, smtpauthlogin, sizeof(smtpauthlogin));
+        else
+          die_read();
+      }
+      out("334 UGFzc3dvcmQ6\r\n"); /* b64 <- 'Password:' */
+      flush();
+      if (smtpauth_getl() > 0)
+        base64_dec_buffer(smtpauth.s, smtpauthpass, sizeof(smtpauthpass));
+      else
+        die_read();
+      smtpauthtimestamp[0]=0;
+      auth_argv[1]=smtpauth_argv[1];	/* change checkpass prg */
+      auth_argv[2]=smtpauth_argv[2];	/* change checkpass prg */
+      auth_argv[3]=NULL;			/* change checkpass prg */
+      smtpauth_authenticate();  
+      return;
+    }
+    else if ((ret=strncasecmp(arg,"plain",5))==0)
+    {
+      static char smtpauthloginpass[200];
+
+      while (arg && *arg && *arg != ' ') arg++;
+
+      /* pass over the space */
+      while (arg && *arg && *arg == ' ') arg++;
+ 
+      if (arg && *arg)
+      {
+        if(strlen(arg)*3/4 >= sizeof(smtpauthloginpass))
+        {
+          out("535 input too long\r\n");
+          flush();
+          return;
+        }
+        /* here's the base64 encoded login/password */
+        base64_dec_buffer(arg, smtpauthloginpass, sizeof(smtpauthloginpass)-1);
+      } else {
+        out("334 ok. go on.\r\n");
+        flush();
+        i=smtpauth_getl();
+        if(i <= 0)
+          die_read();
+        else if(i*3/4 >= sizeof(smtpauthloginpass))
+        {
+          out("535 input too long\r\n");
+          flush();
+          return;
+        } else base64_dec_buffer(smtpauth.s, smtpauthloginpass, sizeof(smtpauthloginpass)-1);
+      }
+      smtpauthloginpass[sizeof(smtpauthloginpass)-1]=0;
+      start=strlen(smtpauthloginpass)+1;
+      if((start >= sizeof(smtpauthloginpass)) || (strlen(smtpauthloginpass+start) >= 65))
+      {
+          out("535 malformed input\r\n");
+          flush();
+          return;
+      }
+      strcpy(smtpauthlogin,smtpauthloginpass+start);
+
+      start+=strlen(smtpauthlogin)+1;
+      if((start >= sizeof(smtpauthloginpass)) || (strlen(smtpauthloginpass+start) >= 65))
+      {
+          out("535 malformed input\r\n");
+          flush();
+          return;
+      }
+      strcpy(smtpauthpass,smtpauthloginpass+start);
+
+      smtpauthtimestamp[0]=0;
+      auth_argv[1]=smtpauth_argv[1];	/* change checkpass prg */
+      auth_argv[2]=smtpauth_argv[2];	/* change checkpass prg */
+      auth_argv[3]=NULL;		/* change checkpass prg */
+      smtpauth_authenticate();
+      return;
+    }
+    else if ((ret=strncasecmp(arg,"cram-md5",8))==0)  
+    {
+      int r;
+      static stralloc me = {0};
+      static stralloc greet = {0};
+      static stralloc greetenc = {0};
+      char *s;
+      r = control_readline(&me,"control/me");
+      if (r != 1) 
+      {
+        out("535 internal server error\r\n"); flush(); _exit(0);
+      }
+      for (r=0;r <= me.len;r++)
+      {
+        if (me.s[r]=='\n')
+        {
+          me.s[r]=0;
+          break;
+        }
+      }
+
+      s = unique;
+      s += fmt_uint(s,getpid());
+      *s++ = '.';
+      s += fmt_ulong(s,(unsigned long) now());
+      *s++ = '@';
+      *s++ = 0;
+      if (greet.len)
+      {
+	for (i=0;i<greet.len;i++)
+	{
+	  greet.s[i]=0;
+	}
+        greet.len=0;
+      }
+      stralloc_append(&greet,"<");
+      stralloc_cats(&greet,unique);
+      stralloc_cats(&greet,me.s);
+      stralloc_cats(&greet,">");
+      greet.s[greet.len]=0;	// obscure fix but it works
+      stralloc_readyplus(&greet,3);
+      if (greetenc.len)
+      {
+	for (i=0;i<greetenc.len;i++)
+	{
+	  greetenc.s[i]=0;
+	}
+        greetenc.len=0;
+      }
+      flush();
+      base64encode(&greet,&greetenc);
+      greetenc.s[greetenc.len]=0;	// obscure fix but it works
+      out("334 ");
+      out(greetenc.s);
+      out("\r\n");
+      flush();
+      if (smtpauth_getl() > 0)
+      {
+        s=calloc((size_t) strlen(smtpauth.s),(size_t)1);
+        base64_dec_buffer(smtpauth.s, s, strlen(smtpauth.s));
+      }
+      helper=strtok(s," ");
+      if(helper!=NULL)
+      {
+        strncpy (smtpauthlogin,helper,64);
+      }
+      else
+      {
+        out("535 malformed input\r\n"); 
+        return;
+      }
+      helper=strtok(NULL," ");
+      if(helper!=NULL)
+      {
+        strncpy (smtpauthtimestamp,helper,64);
+      }
+      else
+      {
+        out("535 malformed input\r\n"); 
+        return;
+      }
+      strncpy (smtpauthpass,greet.s,64);
+      auth_argv[1]=smtpauth_argv[3];	/* change checkpass prg */
+      auth_argv[2]=smtpauth_argv[4];	/* change checkpass prg */
+      auth_argv[3]=NULL;		/* change checkpass prg */
+      smtpauth_authenticate();
+      return;
+    }
+    else
+    {
+      out("504 auth type not supported\r\n"); 
+      flush();
+      return;
+    }
+  }
+  else
+  {
+      out("503 you are already authenticated\r\n"); 
+      flush(); 
+      return;
+  }
+}
+#endif
+
 struct commands smtpcommands[] = {
   { "rcpt", smtp_rcpt, 0 }
 , { "mail", smtp_mail, 0 }
 , { "data", smtp_data, flush }
+#ifdef USE_SMTPAUTH
+, { "auth", smtp_auth, flush }
+#endif
 , { "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;
 {
+#ifdef TLS
+  sig_alarmcatch(sigalrm);
+#endif
+#ifdef USE_SMTPAUTH
+  smtpauth_argv = argv;
+#endif
   sig_pipeignore();
   if (chdir(auto_qmail) == -1) die_control();
   setup();
===================================================================
RCS file: /home/src/cvsroot/qmail/received.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- received.c.orig	27 Nov 2001 02:20:07 -0000	1.1.1.1
+++ received.c	27 Dec 2001 04:55:04 -0000	1.2
@@ -37,7 +37,7 @@
 /* "Received: from relay1.uu.net (HELO uunet.uu.net) (7@192.48.96.5)\n" */
 /* "  by silverton.berkeley.edu with SMTP; 26 Sep 1995 04:46:54 -0000\n" */
 
-void received(qqt,protocol,local,remoteip,remotehost,remoteinfo,helo)
+void received(qqt,protocol,local,remoteip,remotehost,remoteinfo,helo,authuser)
 struct qmail *qqt;
 char *protocol;
 char *local;
@@ -45,6 +45,7 @@
 char *remotehost;
 char *remoteinfo;
 char *helo;
+char *authuser;
 {
   struct datetime dt;
 
@@ -53,6 +54,11 @@
   if (helo) {
     qmail_puts(qqt," (HELO ");
     safeput(qqt,helo);
+    qmail_puts(qqt,")");
+  }
+  if (authuser) {
+    qmail_puts(qqt," (AUTH ");
+    safeput(qqt,authuser);
     qmail_puts(qqt,")");
   }
   qmail_puts(qqt," (");
===================================================================
RCS file: syncdir.c
diff -N syncdir.c
--- syncdir.c.orig	1 Jan 1970 00:00:00 -0000
+++ syncdir.c	27 Dec 2001 18:08:09 -0000	1.1
@@ -0,0 +1,110 @@
+/* syncdir -- emulate synchronous directories
+    Copyright (C) 1998 Bruce Guenter
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+    You can reach me at bruce.guenter@qcc.sk.ca
+*/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#define open XXX_open
+#include <fcntl.h>
+#undef open
+#include <unistd.h>
+#include <string.h>
+#include <syscall.h>
+#include <stdio.h>
+#include <errno.h>
+
+#define SYS_OPEN(FILE,FLAG,MODE) syscall(SYS_open, FILE, FLAG, MODE)
+#define SYS_CLOSE(FD) syscall(SYS_close, FD)
+#define SYS_LINK(OLD,NEW) syscall(SYS_link, OLD, NEW)
+#define SYS_UNLINK(PATH) syscall(SYS_unlink, PATH)
+#define SYS_RENAME(OLD,NEW) syscall(SYS_rename, OLD, NEW)
+#define SYS_FSYNC(FD) syscall(SYS_fsync, FD)
+
+static int fdirsync(const char* filename, unsigned length)
+{
+  char dirname[length+1];
+  /* This could also be:
+   * char* dirname = alloca(length+1); */
+  int dirfd;
+  int retval;
+  memcpy(dirname, filename, length);
+  dirname[length] = 0;
+  if((dirfd = SYS_OPEN(dirname,O_RDONLY,0)) == -1)
+    return -1;
+  retval = (SYS_FSYNC(dirfd) == -1 && errno == EIO) ? -1 : 0;
+  SYS_CLOSE(dirfd);
+  return retval;
+}
+
+static int fdirsyncfn(const char *filename)
+{
+   const char *slash = filename+strlen(filename)-1;
+
+   /* Skip over trailing slashes, which would be ignored by some
+    * operations */
+   while(slash > filename && *slash == '/')
+     --slash;
+
+   /* Skip back to the next slash */
+   while(slash > filename && *slash != '/')
+     --slash;
+
+   /* slash now either points to a '/' character, or no slash was found */
+   if(*slash == '/')
+      return fdirsync(filename,
+                      (slash == filename) ? 1 : slash-filename);
+   else
+     return fdirsync(".", 1);
+}
+
+int open(const char *file,int oflag,mode_t mode)
+{
+  int fd = SYS_OPEN(file, oflag, mode);
+  if(fd == -1)
+    return fd;
+  if (oflag & (O_WRONLY | O_RDWR))
+    if (fdirsyncfn(file) == -1) {
+      SYS_CLOSE(fd);
+      return -1;
+    }
+  return fd;
+}
+
+int link(const char *oldpath,const char *newpath)
+{
+   if(SYS_LINK(oldpath,newpath) == -1)
+     return -1;
+   return fdirsyncfn(newpath);
+}
+
+int unlink(const char *path)
+{
+   if(SYS_UNLINK(path) == -1)
+     return -1;
+   return fdirsyncfn(path);
+}
+
+int rename(const char *oldpath,const char *newpath)
+{
+   if (SYS_RENAME(oldpath,newpath) == -1)
+     return -1;
+   if (fdirsyncfn(newpath) == -1)
+     return -1;
+   return fdirsyncfn(oldpath);
+}
