diff -urN ../qmail-1.03_unpatched/CHKUSER.automatic_patching ./CHKUSER.automatic_patching
--- ../qmail-1.03_unpatched/CHKUSER.automatic_patching	1970-01-01 01:00:00.000000000 +0100
+++ ./CHKUSER.automatic_patching	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,98 @@
+Chkuser 2.0 automatic patching
+
+When to use automatic patching
+==============================
+
+The release.tar package contains some .patch files, ready for installation,
+trying to semplify the most frequent situations.
+
+You may use one of these patches if you have these sources:
+
+	- a clean qmail 1.03 or netqmail 1.05 
+	- qmail 1.03 or netqmail 1.05 patched with auth-0.4.2 (distribuited with
+		vpopmail within the contrib dir, author Erwin Hoffmann - www.fehcom.de) 
+	- qmail 1.03 or netqmail 1.05 patched with toaster-0.6-1 (author Bill
+		Shupp - see www.shupp.org/toaster for more info or newer releases) 
+
+You may also consider using one of these patches if you have additional compatible
+patches installed. This means that these additional patches should not have changed
+the same sources and lines which are going to be used by chkuser.
+
+If you have any doubt, backup your sources and try the automatic installation,
+otherwise execute the manual installation (that's very easy).
+
+Backup
+======
+
+Save you qmail working sources before making any change.
+
+Basic installation
+==================
+
+Download the newest release.tar package and untar it. It will create a directory
+containing all release chkuser files and patches.
+
+Chose the most appropriate .patch file to be applied, according to your qmail
+installation: .patch files names are self-describing.
+
+Position in the qmail/netqmail source directory:
+
+	$ cd /usr/.../netqmail-1.05
+
+Apply selected patch:
+
+	$ patch < /path_to_chkuser_release_dir/netqmail-1.05_chkuser-2.x.x.patch
+
+No errors should be displayed. If you see any error, better you restore your
+sources and go to manual editing.
+
+editing vpopmail home path
+
+	If your production home path for vpopmail (or whatever you call him) user
+	is NOT /home/vpopmail, you must perform the following additional actions.
+
+	Edit Makefile, changing the line referring to vpopmail's home path and
+	putting the right home path:
+
+		VPOPMAIL_HOME=/home/vpopmail 
+
+	Edit conf-cc, changing the string referring to vpopmail's home path and
+	putting the right home path:
+
+		cc -O2 -I/home/vpopmail/include 
+
+chkuser settings
+================
+
+Edit chkuser_settings.h, uncommenting the options you prefer, and commenting the
+ones you don't want. Default settings should cover the most of situations.
+
+See the related settings pages for more informations.
+
+Make
+====
+Now, make (or gmake on *BSD) as your usual. No errors (just warnings) should
+come out. If you see any error, better you restore your sources
+and go to manual editing.
+
+Checking
+========
+Select a domain, contained in your rcpthosts, for which bouncing is enabled, and run:
+
+	$ ./qmail-smtpd
+	mail from <wrong_sender>
+	mail from <right_sender>
+	rcpt to: <fake_user@your_domain
+	rcpt to: <real_user@your_domain
+
+You should see error and ok messages, depending on the addresses you typed.
+
+Install
+=======
+Copy the new executable in the /var/qmail/bin directory.
+
+Running
+=======
+This patched qmail-smtpd must be executed in a different way than the normal one.
+See the running pages for detailed instructions.
+
diff -urN ../qmail-1.03_unpatched/CHKUSER.changelog ./CHKUSER.changelog
--- ../qmail-1.03_unpatched/CHKUSER.changelog	1970-01-01 01:00:00.000000000 +0100
+++ ./CHKUSER.changelog	2005-01-21 20:23:49.000000000 +0100
@@ -0,0 +1,141 @@
+
+CHKUSER 2.0 change log
+
+V 2.0.8 - 7 december 2004
+	Features
+	Freeze of new features of 2.0.7, except null senders behaviour.
+	CHKUSER_ENABLE_NULL_SENDER_WITH_TCPREMOTEHOST is no more available
+	CHKUSER_ENABLE_NULL_SENDER is no more available
+	NULL SENDERS are now always accepted. No option is available to disable
+		this behaviour. Previous chkuser versions broke RFC compatibility on
+		null senders, and complicated real life e-mailing.
+	Logging of null senders <> is now available.
+	
+	Bugs corrected
+	Sender controls were not executed if CHKUSER_STARTING_VARIABLE was defined
+		(thanks to Charles Sprickman)
+	Domains not in control/virtualdomains are now explicitely excluded from
+		following cascade checks; in previous versions following cascade
+		checks were done using fake domains paths.
+	vget_assign is now handled correctly (a domain in rcpthosts but not
+		in virtualdomains could have an incorrect path in previous versions
+		(this bug is also in all chkusr versions)
+
+	Defaults changed
+	CHKUSER_RCPT_FORMAT is now undefined as default
+	CHKUSER_RCPT_MX is now undefined as default.
+	CHKUSER_SENDER_FORMAT is now undefined as default
+	CHKUSER_SENDER_MX is now undefined as default.
+	CHKUSER_ERROR_DELAY_INCREASE new default is 300 milliseconds
+	
+V 2.0.7 - 25 october 2004
+	Features
+	added vclose() of DB auth connection, overriding
+		qmail-smtpd _exit call
+	improved MX checking; now SOFT failure is handled as
+		temporary error.
+	added #define CHKUSER_RCPTMX_TMP_STRING
+	added #define CHKUSER_SENDERMX_TMP_STRING 
+	added handling of mailman mailing lists
+		(and related #define CHKUSER_ENABLE_MAILMAN_LISTS)
+	changed order of checking for recipients:
+		1 - valias
+		2 - alias
+		3 - alias extensions
+		4 - users
+		5 - users extensions
+		6 - lists
+	added #define CHKUSER_ACCEPT_NULL_SENDER (default defined)
+	added #define CHKUSER_ENABLE_ALIAS_DEFAULT (default not defined)
+		enables checking of .qmail-alias-default
+	added #define CHKUSER_IDENTIFY_REMOTE_VARIABLE "CHKUSER_IDENTIFY"
+		in order to allow a easy identification of remote IP
+		(substitutes RELAYCLIENT in chkuser logging)
+	added #define CHKUSER_ALLOW_RCPT_SRS
+		enable usage of "#" and "+" characters within rcpt address
+	added #define CHKUSER_ALLOW_RCPT_CHAR_1 '$'
+	added #define CHKUSER_ALLOW_RCPT_CHAR_2 '%'
+	added #define CHKUSER_ALLOW_RCPT_CHAR_3 '£'
+	added #define CHKUSER_ALLOW_RCPT_CHAR_4 '?'
+	added #define CHKUSER_ALLOW_RCPT_CHAR_5 '*'
+	#define CHKUSER_ENABLE_USERS_EXTENSIONS
+		substitutes #define CHKUSER_ENABLE_EXTENSIONS
+	#define CHKUSER_ENABLE_EZMLM_LISTS
+		substitutes #define CHKUSER_ENABLE_LISTS
+	#define CHKUSER_USERS_DASH
+		substitutes #define CHKUSER_EXTENSION_DASH
+
+	Bugs
+	sender address "name@" could cause a crash. Corrected
+		(Thanks to Dmitry Petukhov)
+	Corrected Makefile: now qmail-smtpd.c recompiles if chkuser.h
+		changes
+	Corrected a bug in #endif sequence related to
+		#define CHKUSER_RCPT_FORMAT (thanks to Alex Plainer)
+	Corrected a bug in chkuser_sender; now is not executed when
+		chkuser is disabled
+	Corrected check of format for domains:
+		"xn--" admitted as leading string
+	Deleted correction over usage of RELAYCLIENT variable
+		Previous correction could affect a special
+		feature of RELAYCLIENT (thanks to Alex Pleiner)
+
+	Defaults changed
+	#define CHKUSER_ENABLE_NULL_SENDER_WITH_TCPREMOTEHOST (default undefined)
+
+
+V 2.0.6 - 25 september 2004
+	No bugs, just doc updates and an empty patch file corrected
+
+	#define CHKUSER_ENABLE_VGET_REAL_DOMAIN was existing and working in code,
+		but not reported both in docs and inside chkuser_settings.h
+			(default is commented, but this #define is important)	
+	patch for toaster-0.6-1 was empty. Now the correct one is provided
+
+V 2.0.5 - 23 september 2004
+	This is the first public release.
+
+	added #define CHKUSER_ALLOW_SENDER_CHAR_1 (default not defined) 
+	added #define CHKUSER_ALLOW_SENDER_CHAR_2 (default not defined) 
+	added #define CHKUSER_ALLOW_SENDER_CHAR_3 (default not defined) 
+	added #define CHKUSER_ALLOW_SENDER_CHAR_4 (default not defined) 
+	added #define CHKUSER_ALLOW_SENDER_CHAR_5 (default not defined) 
+	added #define CHKUSER_MIN_DOMAIN_LEN (default defined 4) -
+		Previously it was hard coded as 5. Juergen Kendzorra
+		showed me some existing names long only 4 chars. 
+	added #define CHKUSER_LOG_VALID_SENDER (default defined)
+
+V 2.0.4 - 15 september 2004
+
+	added #define CHKUSER_SENDER_NOCHECK_VARIABLE (default not defined) 
+	added #define CHKUSER_DEBUG_STDERR (default not defined) 
+	added #define CHKUSER_ALLOW_SENDER_SRS (default not defined) 
+	cleaned some typos in code and documentation (thanks to Juergen
+		Kendzorra - http://www.kendzorra.de) 
+
+
+V 2.0.3 - 8 september 2004
+	This is the first version released outside, for wider testing.
+
+	Tested Makefile for netqmail 1.05 
+	Added Makefiles for applying over other patches 
+
+V 2.0.0 - july 2004
+	chkuser 2.0.0 starts here, and is a private internal release.
+	Version 2.0 is much more modular than previous one (named chkusr),
+	and has been designed with the goal of enabling more features and
+	semplifying installations and upgrades of the patch himself. 
+
+	chkusr changes his name, to reflect a deep change of the patch. 
+
+	Chkusr 1.0 received a lot of feedbacks and suggestions.
+	The most of these suggestions are now inside version 2.0.
+
+		- Marcelo Coelho (marcelo at tpn.com.br), segnaled me some
+		unseen minor bugs of chkusr 1.0 (minor but very annoying to
+		my pride) and suggested some very interesting features
+		(some of them are now in chkuser 2.0). 
+		- Iulian Margarintescu (http:://www.erata.net) suggested a
+		workable way of introducing quota check on recipients
+		(now in chkuser 2.0). 
+
diff -urN ../qmail-1.03_unpatched/CHKUSER.copyright ./CHKUSER.copyright
--- ../qmail-1.03_unpatched/CHKUSER.copyright	1970-01-01 01:00:00.000000000 +0100
+++ ./CHKUSER.copyright	2005-01-21 20:23:49.000000000 +0100
@@ -0,0 +1,15 @@
+
+chkuser for qmail/netqmail > 1.0.3 and vpopmail > 5.3.x
+
+Author: Antonio Nati tonix@interazioni.it
+
+All rights on this software and
+the identifying words chkusr and chkuser kept by the author
+
+This software may be freely used, modified and distributed,
+but this lines must be kept in every original or derived version.
+
+Original author "Antonio Nati" and the web URL
+"http://www.interazioni.it/opensource"
+must be indicated in every related work or web page
+
diff -urN ../qmail-1.03_unpatched/CHKUSER.install ./CHKUSER.install
--- ../qmail-1.03_unpatched/CHKUSER.install	1970-01-01 01:00:00.000000000 +0100
+++ ./CHKUSER.install	2005-01-21 20:23:49.000000000 +0100
@@ -0,0 +1,76 @@
+
+
+This 2.0.8b version has been released as update release.
+
+
+New installation
+================
+This "update" version must be used to update older chkuser 2.0 installations.
+
+If you did not install chkuser 2.0 yet, install the complete chkuser package
+"chkuser-2.0.8-release".
+
+
+Updating installation from 2.0 up to 2.0.6
+==========================================
+
+Untar this package, and:
+
+save your existing chkuser_settings.h
+
+copy newer chkuser.h, chkuser.c and chkuser_settings.h in your qmail source
+	directory. 
+
+1) patch Makefile using Makefile.patch
+	patch < Makefile.patch
+
+2) patch qmail-smtpd.c
+	If you are using Bill Shupp's toaster use this command
+		patch < qmail-smtpd_toaster.patch
+	otherwise use
+		patch < qmail-smtpd.patch
+
+Now, check your old chkuser_settings.h and restore the options you'ld like to enable/disable.
+
+make
+
+install & test
+
+
+Updating installation from 2.0.7, 2.0.8, 2.0.8a
+===============================================
+
+Untar this package, and:
+
+if you're using Shupp's Toaster, patch qmail-smtpd.c using
+	qmail-smtpd-toaster-2.0.7.patch
+
+	patch < qmail-smtpd-toaster-2.0.7.patch
+
+copy newer chkuser.h, chkuser.c, chkuser_settings.h in your qmail source
+        directory.
+
+
+check defines behaviours in chkuser_settings.h.
+
+make
+
+install & test
+
+
+DEFAULT DEFINES
+===============
+
+Some defines are changed in this version.
+Please check carefully what's changed.
+
+
+BUGS AND FEEDBACKS.
+===================
+
+For bugs and feeedbacks, please use (only) one of these lists:
+
+	vchkpw@inter7.com
+	mail-toaster@simerson.net
+	chkuser@interazioni.it
+
diff -urN ../qmail-1.03_unpatched/CHKUSER.log_format ./CHKUSER.log_format
--- ../qmail-1.03_unpatched/CHKUSER.log_format	1970-01-01 01:00:00.000000000 +0100
+++ ./CHKUSER.log_format	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,59 @@
+
+chkuser 2.0.5 logging format
+
+When #defines for logging are enabled, chkuser patch emits log informations
+on the same qmail-smtpd log destination
+
+This is the log format:
+
+    CHKUSER "brief message": \
+	    from <sender:remoteinfo:identifyremote> \
+	    remote <helo:remotehostname:remotehostip> \
+	    rcpt <recipient> : "extended message"
+
+where
+	brief message 	
+		    * accepted rcpt
+		    * relaying rcpt
+		    * rejected relaying
+		    * rejected rcpt
+		    * no auth resource
+		    * mbx overquota
+		    * rejected intrusion
+		    * intrusion threshold
+		    * rejected sender
+
+	sender 	sender declared within "mail from"
+
+	remoteinfo	the value of "TCPREMOTEINFO" or the autenticated user
+
+	identifyremote	the value of "CHKUSER_IDENTIFY" env variable - or the name
+			you've chosen in chkuser_settings.h - (be careful to set it,
+			inside your tcp.cdb with a value that let you understand who is sending)
+
+	helo 		helo declared from remote system
+
+	hostname 	the value of "TCPREMOTEHOST"
+
+	remotehostip 	the value of "TCPREMOTEIP"
+
+	recipient 	recipient address
+
+	extended message 	this field has more wide description for
+				some generic "brief message":
+			accepted rcpt 	found existing recipient
+			relaying rcpt 	client allowed to relay
+			rejected relaying 	client not allowed to relay
+			rejected rcpt 	not existing recipient
+			rejected rcpt 	max number of recipients
+			rejected rcpt 	max number of invalid recipients
+			rejected rcpt 	invalid rcpt address format
+			rejected rcpt 	invalid rcpt MX domain
+			intrusion threshold 	max number of allowed rcpt
+			intrusion threshold 	max number of allowed invalid rcpt
+			rejected intrusion 	rcpt ignored, session over intrusion threshold
+			no auth resource 	no auth resource available
+			mbx overquota 	rcpt mailbox is overquota
+			rejected sender 	invalid sender address format
+			rejected sender 	invalid sender MX domain
+	
diff -urN ../qmail-1.03_unpatched/CHKUSER.manual_patching ./CHKUSER.manual_patching
--- ../qmail-1.03_unpatched/CHKUSER.manual_patching	1970-01-01 01:00:00.000000000 +0100
+++ ./CHKUSER.manual_patching	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,183 @@
+Chkuser 2.0 manual editing
+
+Manual editing is a very simple operation.
+
+Watching the patch design, shown in the patch design page, you may see that
+only some simple changes must be done to qmail-smtpd.c and Makefile.
+
+Backup
+======
+
+Save you qmail working sources before making any change.
+
+Basic installation
+==================
+
+Download the newest release.tar package and untar it. It will create a directory
+containing all chkuser files and patches.
+
+Position in the qmail/netqmail source directory:
+
+	$ cd /usr/.../netqmail-1.05
+
+Copy all the chkuser sources:
+
+	$ cp /path_to_release_tar/chkuser* .
+
+edit qmail-smtpd.c
+	within qmail-smtpd.c, change the following lines:
+
+	At the end of initial #include declarations, add the following (+) lines:
+
+	#include "timeoutwrite.h"
+	#include "commands.h"
+	
++	/* start chkuser code */ 
++	#include "chkuser.h"
++	/* end chkuser code */
+
+	#define MAXHOPS 100 
+
+Within smtp_mail routine, add the following (+) lines
+
+	void smtp_mail(arg) char *arg;
+	{
+	if (!addrparse(arg)) { err_syntax(); return; }
++	/* start chkuser code */
++	if (chkuser_sender (&addr) != CHKUSER_OK) { return; }
++	/* end chkuser code */
+	flagbarf = bmfcheck();
+
+	Within smtp_rcpt routine, delete the following (-) lines and substitute
+	them with the (+) ones:
+
+-	  if (relayclient) {
+-	    --addr.len;
+-	    if (!stralloc_cats(&addr,relayclient)) die_nomem();
+-	  }
+-	  else
+-	    if (!addrallowed()) { err_nogateway(); return; }
+
++	/* start chkuser code */
++	  switch (chkuser_realrcpt (&mailfrom, &addr)) {
++	    case CHKUSER_KO:
++	      return;
++	      break;
++	    case CHKUSER_RELAYING:
++	/*
+	--addr.len;
+	if (!stralloc_cats(&addr,relayclient)) die_nomem();
+	if (!stralloc_0(&addr)) die_nomem();
+	*/
+	      break;
+	}
+	/* end chkuser code */
+	if (!stralloc_cats(&rcptto,"T")) die_nomem();
+	if (!stralloc_cats(&rcptto,addr.s)) die_nomem();
+	if (!stralloc_0(&rcptto)) die_nomem();
+
+edit Makefile
+	Within Makefile, change or add the following lines.
+
+	At the begininng of the file:
+
+	# Don't edit Makefile! Use conf-* for configuration.
+
++	VPOPMAIL_HOME=/home/vpopmail
++	SMTPD_CHKUSER_OBJ=chkuser.o dns.o
++	VPOPMAIL_LIBS=`head -1 $(VPOPMAIL_HOME)/etc/lib_deps` `cat dns.lib`
+
+	SHELL=/bin/sh
+
+	Be carefule to use the right path, if your vpopmail production home
+	path is NOT "/home/vpopmail".
+
+	dns.lib is added to qmail-smtpd building instructions, so, if you
+	have previously patched qmail-smtpd in order to include dns.lib, take
+	care to delete the duplication from the previous lines.
+
+	Before "clean:" insert the chkuser.o definition:
+
+	exit.h auto_spawn.h
+      		./compile chkspawn.c
++       chkuser.o: \
++       compile chkuser.c chkuser.h chkuser_settings.h
++       	./compile chkuser.c
+
+	clean: \
+
+	Beware: the "./compile chkuser.c" line has an heading TAB.
+
+	Change the qmail-smtpd compiling and linking instructions,
+	deleting the (-) lines and adding the (+) ones.
+
+
+	qmail-smtpd: \
+	load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \
+	timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \
+	date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \
+	open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \
+-	fs.a auto_qmail.o socket.lib
++	fs.a auto_qmail.o socket.lib $(SMTPD_CHKUSER_OBJ)
+-	      ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \
++	      ./load qmail-smtpd $(SMTPD_CHKUSER_OBJ) rcpthosts.o commands.o timeoutread.o \
+	      timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \
+	      received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \
+	      datetime.a getln.a open.a sig.a case.a env.a stralloc.a \
+-	      alloc.a substdio.a error.a str.a fs.a auto_qmail.o `cat \
+-	      socket.lib`
++	      alloc.a substdio.a error.a str.a fs.a auto_qmail.o \
++	      $(VPOPMAIL_LIBS) \
++	      `cat socket.lib`
+
+	Beware: all the lines starting from and following "./load" have an heading TAB.
+
+edit TARGETS
+	Append the following blue line at the end of TARGETS file:
+
+	man
+	setup
+	check
++	chkuser.o
+
+edit conf-cc
+	Edit conf-cc, adding the include path of production vpopmail:
+
+	cc -O2 -I/home/vpopmail/include 
+
+	Be carefule to use the right path, if your vpopmail production home path
+	is NOT "/home/vpopmail".
+
+chkuser settings
+================
+Edit chkuser_settings.h, uncommenting the options you prefer, and commenting the
+ones you don't want. Default settings should cover the most of situations.
+
+See the related settings pages for more informations.
+
+Make
+====
+Now, make (or gmake on *BSD) as your usual. No errors (just warnings)
+should come out. If you see any error, check carefully edited lines.
+
+Checking
+========
+Select a domain, contained in your rcpthosts, for which bouncing is enabled, and run:
+
+	$ ./qmail-smtpd
+	mail from <wrong_sender>
+	mail from <right_sender>
+	rcpt to: <fake_user@your_domain
+	rcpt to: <real_user@your_domain
+
+You should see error and ok messages, depending on the addresses you typed.
+
+Install
+=======
+Copy the new executable in the /var/qmail/bin directory.
+
+Running
+=======
+This patched qmail-smtpd must be executed in a different way than the normal one.
+See the running pages for detailed instructions.
+
diff -urN ../qmail-1.03_unpatched/CHKUSER.notes ./CHKUSER.notes
--- ../qmail-1.03_unpatched/CHKUSER.notes	1970-01-01 01:00:00.000000000 +0100
+++ ./CHKUSER.notes	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,56 @@
+
+CHKUSER 2.0.5 - Notes on surrounding software
+
+RELAYCLIENT variable
+====================
+Inside qmail-smtpd.c, in the routine smtp_rcpt, there are these lines:
+
+  if (relayclient) {
+    --addr.len;
+    if (!stralloc_cats(&addr,relayclient)) die_nomem();
+    if (!stralloc_0(&addr)) die_nomem();
+  }
+
+For what I may see now, these lines look to be a mistake.
+
+Actually, the value of this field is appended to the rcpt address, making it (the address)
+not valid if RELAYCLIENT has any value different from "".
+
+It looks like there is no reason why this value should be appended to the rcpt address.
+
+Commenting these lines, would permit giving a value to this field (setting
+the environment variable RELAYCLIENT).
+Routines authorizing SMTP after POP should set this variable, so this could be an
+important information about who's relaying.
+
+QUOTA CHECKING
+==============
+Chkuser may reject messages when users have more than X% of quota occupied.
+
+This is achieved by defining a variable for such usage, and setting the environment
+variable to the wanted limit.
+
+For example:
+
+	in chkuser_settings.h:
+		#define CHKUSER_MBXQUOTA_VARIABLE "CHKUSER_MBXQUOTA"
+
+	in tcp.smtp:
+		:allow,CHKUSER_MBXQUOTA="95"
+
+	
+It could be interesting to "compensate" this value within qmailadmin
+management of accounts.
+
+Qmailadmin could have an optional parameter, like a "coefficient display", that could act this way.
+
+Quota rejecting percentage = 95%
+Qmailadmin coefficient = 100/95 = 1.0526
+
+If you assign a quota to an user, qmailadmin should multiply this value by the coefficient.
+When displaying quota assignments values (only assignments, not real area occupied), qmailadmin should
+divide these values by the coefficient.
+
+In such a way, when a quota occupation is over 95%, user is using 100% of real area and bouncing
+of the incoming message is correct both formally and in merit.
+
diff -urN ../qmail-1.03_unpatched/CHKUSER.readme ./CHKUSER.readme
--- ../qmail-1.03_unpatched/CHKUSER.readme	1970-01-01 01:00:00.000000000 +0100
+++ ./CHKUSER.readme	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,49 @@
+chkuser 2.0 -README
+
+Description
+===========
+The original qmail-smtpd accepts by default all messages, checking later for
+the existence of the recipient. So, if the message is delivered to not existing
+recipients a lot of additional system work and network traffic are generated,
+with multiple expensive bouncing if the sender is a fake one.
+
+chkuser has been developed with the goal to improve the acceptance SMTP phase
+of qmail-smtpd. qmail-smtpd patched with chkuser may check the existance of
+e-mail recipients immediately in the SMTP acceptance phase of a message and
+rejects istantly all messages not directed to existing users, avoiding
+additional traffic, work and messages bounced more times.
+
+These goals are achieved enquirying the existing vpopmail archives (each
+format is supported: cdb, MySQL, LDAP, etc.) by using standard vpopmail calls,
+or using customized chkuser routines.
+
+Version 2.0 - From chkusr to chkuser
+====================================
+Version 2.0 is a lot different from previous versions, so it deserves a more
+evident change in the name.
+
+Version 2.0 has been designed with the goal to be modular, and to make more easy
+both adding new features to checkuser code and semplifing code update.
+
+Patching over original qmail files is done over a few points, while the most of
+chkuser code remains ouside, in dedicated chkuser's files.
+
+Same for settings, that are inside a dedicated chkuser_settings.h file.
+
+The intention is to semplify upgrading: for future chkuser releases, upgrading
+will require only to update chkuser specific files, leaving all the rest
+untouched, and changing chkuser_settings.h only if new features must be enabled.
+
+Logging and SPAM
+================
+chkuser 2.0 has detailed logging of accepted and refused recipients and senders,
+allowing a deep analysis of "who's sending to who". This can lead to more
+sophisticated future enhancements of anti-SPAM features.
+
+URL Location
+============
+For any new release, support, FAQ, mailing lists, or other information, see:
+
+	http://www.interazioni.it/opensource
+
+
diff -urN ../qmail-1.03_unpatched/CHKUSER.running ./CHKUSER.running
--- ../qmail-1.03_unpatched/CHKUSER.running	1970-01-01 01:00:00.000000000 +0100
+++ ./CHKUSER.running	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,103 @@
+
+CHKUSER 2.0.5 - Running instructions
+
+Chkuser may run using the most of security, following very strictly the sacurity
+model used By Dan Berstein. To achieve this goal, chkuser may switch between
+differents UID/GID, for differente purposes.
+
+However this is incompatible with TLS patches (like toaster-0.6-1), as these patches
+want to run under a unique UID/GID. Luckily, qmail is enought robust to let us
+run this way.
+
+To achieve both these goals, chkuser uses a #define (CHKUSER_ENABLE_UIDGID)
+that indicates if UID/GID switching is wanted, and running instructions must
+adapt to this way.
+
+Instead, when this define is not used, another way of running must be used.
+(Just for precision, even if the CHKUSER_ENABLE_UIDGID define is used, chkuser
+may be run without switching UID/GID).
+
+Running with UID/GID switch
+===========================
+
+If you want the most security when using chkuser, and you have enabled 
+CHKUSER_ENABLE_UIDGID within chkuser_settings.h (it's enabled by default), use
+these instructions.
+
+Description.
+	qmail-smtpd-chkusr must be installed (by default in /var/qmail/bin) with
+	setuid (user qmaild) and setgid (group qnofiles), and executed by tcpserver 
+	with -u vpopmail-user and -g vchkpw-group  parameters.
+
+	qmail-smtpd-chkusr starts running with the original qmail-smtpd uid and gid,
+	switching to needed uid and gid only for vpopmail checks on user existance,
+	turning back to the starting uid and gid.
+
+Instructions.
+	You have to set SUID (set-user-ID-on-execution) and SGID
+	(set-group-ID-on-execution) bits on qmail-smtpd-chkusr:
+		chown qmaild qmail-smtpd
+		chgrp nofiles qmail-smtpd
+		chmod 6555 qmail-smtpd
+
+	and the result you see should be like (different size and date, of course):
+		-r-sr-sr-x 1 qmaild nofiles 57056 Feb 14 18:18 qmail-smtpd-chkusr
+
+	Integrate qmail-smtpd in your start files:
+
+	As example, a real start command for qmail-smtpd-chkusr may be
+
+	#!/bin/sh -e
+	#
+	# Using splogger to send the log through syslog.
+
+	exec env - PATH="/var/qmail/bin:/usr/local/bin" \
+	tcpserver -t 5 -v -p -x <your.tcp.smtp.cdb> \
+	-u <vpopmail-user> -g <vchkpw-group> -l <your-host.domain> 0 smtp \
+	qmail-smtpd-chkusr splogger smtpd &
+
+	where
+		<vpopmail-user> = vpopmail uid
+		<vchkpw-group> = vchkpw gid
+		<your-host.domain> = your host.domain (!)
+		<your.tcp.smtp.cdb> = your tcp.permission.to.relay cdb
+
+	NOTE: if you are using more system users for your domains, the execution
+	uid (which I indicated as vpopmail) should be set to root.
+
+
+Running with fixed UID/GID
+==========================
+You may use these instructions if you've not defined CHKUSER_ENABLE_UIDGID, or if
+you want to run qmail-smtpd as unique user, despite of CHKUSER_ENABLE_UIDGID define.
+qmail-smtpd is well safe and robust, and there is no risk running it directly as
+vpopmail user, unless you use untrusted software layered down.
+
+Description.
+	qmail-smtpd must be installed normally (-r-xr-xr-x) and executed by tcpserver
+	with -u vpopmail-user and -g vchkpw-group parameters.
+
+Instructions.
+	Integrate qmail-smtpd-chkusr in your start files:
+
+	As example, a real start command for qmail-smtpd-chkusr may be
+
+	#!/bin/sh -e
+	#
+	# Using splogger to send the log through syslog.
+
+	exec env - PATH="/var/qmail/bin:/usr/local/bin" \
+	tcpserver -t 5 -v -p -x <your.tcp.smtp.cdb> \
+	-u <vpopmail-user> -g <vchkpw-group> -l <your-host.domain> 0 smtp \
+	qmail-smtpd-chkusr splogger smtpd &
+
+	where
+		<vpopmail-user> = vpopmail uid
+		<vchkpw-group> = vchkpw gid
+		<your-host.domain> = your host.domain (!)
+		<your.tcp.smtp.cdb> = your tcp.permission.to.relay cdb
+
+	NOTE: if you are using more system users for your domains, the execution user
+	(which I indicated as vpopmail) should be set to root.
+
+
diff -urN ../qmail-1.03_unpatched/CHKUSER.settings ./CHKUSER.settings
--- ../qmail-1.03_unpatched/CHKUSER.settings	1970-01-01 01:00:00.000000000 +0100
+++ ./CHKUSER.settings	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,222 @@
+
+CHKUSER 2.0.5 - SETTINGS
+
+#define CHKUSER_DEBUG
+	Only enables eventual debugging instructions put inside the code.
+	When enabled, 
+		CHKUSER_DBG ("string")
+		CHKUSER_DBG_INT (integer)
+	display informations to terminal.
+	[default NOT defined]
+
+#define CHKUSER_DEBUG_STDERR
+	Enables displaying of debugging informations to STDERR, instead of STDOUT.
+	[default NOT defined]
+
+#define CHKUSER_ALWAYS_ON
+	This enables chkuser ALWAYS, despite of any other global or domain setting.
+	Be careful, when this define is activated, your .qmail-default is no more
+	important; if the domain is within rcpthosts it will be checked for rcpts.
+	[default NOT defined]
+
+#define CHKUSER_VPOPMAIL
+	This set the virtual mail manager used.
+	Up to now only vpopmail is supported, so this define is just here for future use
+	[default defined - must be defined]
+
+#define CHKUSER_STARTING_VARIABLE "CHKUSER_START"
+	If defined, tells which variable must be read, at qmail-smtpd start, in order
+	to understand how to use chkuser for any domain
+	The variable must be filled with the following values
+		NONE    = chkuser will not work
+		ALWAYS  = chkuser will work always
+		DOMAIN  = chkuser will work depending on single domain settings
+	Ony other value, or a missing value, will disable chkuser.
+	[default NOT defined]
+
+#define CHKUSER_ENABLE_UIDGID
+	Used to switch between UIDS/GIDS, used if you want to apply a more safe mechanism, and
+	if you're NOT using TLS (as TLS seems not to like UID/GID changing)
+	When not defined, qmail-smtpd must be executed as vpopmail user.
+	When defined, qmail-smtpd runs as inoffensive qmail user, playing as vpopmail user
+	only for the needed operations.
+	[default defined, except for toaster-0.6-1, where default is not defined]
+
+#define CHKUSER_ENABLE_EXTENSIONS
+	Enables checking of user's extensions. Used by some customized installations, or some
+	products (like TMDA)
+	[default NOT defined]
+
+
+#define CHKUSER_EXTENSION_DASH '-'
+	Defines the character used to start user's extensions.
+	Has no effect on mailing lists extensions.
+	[must always be defined if CHKUSER_ENABLE_EXTENSIONS is defined]
+
+#define CHKUSER_DOMAIN_WANTED
+	Display error if a recipient address does not contain a domain.
+	[default defined]
+
+#define CHKUSER_ENABLE_USERS
+	Enables checking on users
+	[default defined]
+
+#define CHKUSER_ENABLE_ALIAS
+	Enables checking on aliases
+	[default defined]
+
+#define CHKUSER_ENABLE_VALIAS
+	Enables checking on valiases
+	[default defined]
+
+#define CHKUSER_ENABLE_LISTS
+	Enables checking on mailing lists
+	[default defined]
+
+#define CHKUSER_EZMLM_DASH '-'
+	Defines the character used to start the extensions of mailing lists.
+	[must always be defined if CHKUSER_ENABLE_LISTS is defined]
+
+#define CHKUSER_SPECIFIC_BOUNCING ".qmailchkuser-bouncing"
+	Defines an alternative file to use while checking if bouncing must be
+	done for a domain.
+	If defined, this file must exist in the domain dir in order to enable
+	chkuser checking for that domain.
+	[default NOT defined]
+
+#define CHKUSER_BOUNCE_STRING "bounce-no-mailbox"
+	String to look for inside .qmail-default in order to check for bouncing.
+	[must always be defined if domain checking must be done
+	and CHKUSER_SPECIFIC_BOUNCING is not defined]
+
+#define CHKUSER_ENABLE_VAUTH_OPEN
+	Defines if a call is available for checking MySQL/Postgres/LDAP connect.
+	Check your vpopmail release, it should be released with 5.5.0.
+	[default NOT defined]
+
+
+#define CHKUSER_ENABLE_LOGGING
+	Enables logging
+	[default defined]
+
+#define CHKUSER_LOG_VALID_RCPT
+	Enables logging of valid recipients
+	[default defined]
+
+#define CHKUSER_RCPT_FORMAT
+	Enables checking of valid format for recipient addresses
+		user    =       [a-z0-9_-]
+		domain  =       [a-z0-9-.] without consecutive "-" or ".",
+		                           without leading or ending "-" or "."
+	[default defined]
+
+#define CHKUSER_RCPT_MX
+	Enables checking of valid MX for recipient addresses
+	[default defined]
+
+#define CHKUSER_SENDER_NOCHECK_VARIABLE "SENDER_NOCHECK"
+	This define enable usage of a variable escluding any check on the sender.
+	The variable should be set in tcp.smtp for clients, with static IP, whose mailer
+	is composing bad sender addresses
+	[default NOT defined]
+
+#define CHKUSER_SENDER_FORMAT
+	Enables checking of valid format for sender addresses
+		user    =       [a-z0-9_-]
+		domain  =       [a-z0-9-.] without consecutive "-" or ".",
+	        without leading or ending "-" or "."
+	[default defined]
+
+#define CHKUSER_ALLOW_SENDER_SRS
+	When CHKUSER_SENDER_FORMAT is enabled, this define add "#" and "+" as permitted
+	characters within sender address
+	It is used by SRS (Sender Rewriting Scheme)
+	[default NOT defined]
+
+/* #define CHKUSER_ALLOW_SENDER_CHAR_1 '$' */
+chkuser_settings.h:/* #define CHKUSER_ALLOW_SENDER_CHAR_2 '%' */
+chkuser_settings.h:/* #define CHKUSER_ALLOW_SENDER_CHAR_3 '£' */
+chkuser_settings.h:/* #define CHKUSER_ALLOW_SENDER_CHAR_4 '?' */
+chkuser_settings.h:/* #define CHKUSER_ALLOW_SENDER_CHAR_5 '*' */
+
+#define CHKUSER_ALLOW_SENDER_CHAR_1 '$'
+#define CHKUSER_ALLOW_SENDER_CHAR_2 '%'
+#define CHKUSER_ALLOW_SENDER_CHAR_3 '£'
+#define CHKUSER_ALLOW_SENDER_CHAR_4 '?'
+#define CHKUSER_ALLOW_SENDER_CHAR_5 '*'
+	This defines allow to accept further characters within the sender address.
+	[default NOT defined]
+
+#define CHKUSER_SENDER_MX
+	Enables checking of valid MX for sender address
+	[default defined]
+
+#define CHKUSER_MIN_DOMAIN_LEN 4
+	This is a sub checking enabled by CHKUSER_SENDER_FORMAT define.
+	Sets the minimum length of a domain, as formal control of the address.
+	As far as I know, k.st is the shortest domain, so 4 characters is the
+	minimum length.
+	If CHKUSER_SENDER_FORMAT is undefined, no check is done.
+	If CHKUSER_MIN_DOMAIN_LEN is undefined, no check is done.
+	[default defined]
+
+#define CHKUSER_LOG_VALID_SENDER
+        Enables logging of valid senders
+        [default defined]
+
+#define CHKUSER_ENABLE_NULL_SENDER_WITH_TCPREMOTEHOST
+	Enables accepting null sender "<>" from hosts which have a name
+	associated to their IP
+	[default defined]
+
+#define CHKUSER_RCPT_LIMIT_VARIABLE "CHKUSER_RCPTLIMIT"
+	defines the variable which contains the maximimum number of allowed recipients.
+	When the first value, between CHKUSER_RCPT_LIMIT_VARIABLE's variable and
+	CHKUSER_WRONGRCPT_LIMIT_VARIABLE's variable is reached, chkuser rejects everything
+	[default defined]
+
+#define CHKUSER_WRONGRCPT_LIMIT_VARIABLE "CHKUSER_WRONGRCPTLIMIT"
+        defines the variable which contains the maximimum number of wrong recipients.
+        When the first value, between CHKUSER_RCPT_LIMIT_VARIABLE's variable and
+        CHKUSER_WRONGRCPT_LIMIT_VARIABLE's variable is reached, chkuser rejects everything
+        [default defined]
+
+#define CHKUSER_MBXQUOTA_VARIABLE "CHKUSER_MBXQUOTA"
+	Enables the usage of the environment variable containing the limit percent of quota.
+	Here you only define which variable will be used to set the percent limit, then
+	in tcp.smpt or running tcpserver you must set the environment variable.
+	If the variable is not found, or variable is <= 0, the quota checking is not performed.
+        [default defined]
+
+#define CHKUSER_ERROR_DELAY 1000
+	Delay to wait for each not existing recipient
+        [default defined]
+
+#define CHKUSER_ERROR_DELAY_INCREASE 100
+	Delay to add to the default, for each additional wrong recipient found
+        [default defined]
+
+#define CHKUSER_RCPT_DELAY_ANYERROR
+	Use delay for each error on recipients
+        [default defined]
+
+#define CHKUSER_SENDER_DELAY_ANYERROR
+	Use delay for each error on senders
+        [default defined]
+
+
+#define CHKUSER_NORCPT_STRING "511 sorry, no mailbox here by that name (#5.1.1 - chkuser)\r\n"
+#define CHKUSER_RESOURCE_STRING "430 system temporary unavailable, try again later (#4.3.0 - chkuser)\r\n"
+#define CHKUSER_MBXFULL_STRING "522 sorry, recipient mailbox is full (#5.2.2 - chkuser)\r\n"
+#define CHKUSER_MAXRCPT_STRING "571 sorry, reached maximum number of recipients for one session (#5.7.1 - chkuser)\r\n"
+#define CHKUSER_MAXWRONGRCPT_STRING "571 sorry, you are violating our security policies (#5.1.1 - chkuser)\r\n"
+#define CHKUSER_DOMAINMISSING_STRING "511 sorry, you must specify a domain (#5.1.1 - chkuser)\r\n"
+#define CHKUSER_RCPTFORMAT_STRING "511 sorry, recipient address has invalid format (#5.1.1 - chkuser)\r\n"
+#define CHKUSER_RCPTMX_STRING "511 sorry, can't find a valid MX for rcpt domain (#5.1.1 - chkuser)\r\n"
+#define CHKUSER_SENDERFORMAT_STRING "571 sorry, sender address has invalid format (#5.7.1 - chkuser)\r\n"
+#define CHKUSER_SENDERMX_STRING "511 sorry, can't find a valid MX for sender domain (#5.1.1 - chkuser)\r\n"
+#define CHKUSER_INTRUSIONTHRESHOLD_STRING "571 sorry, you are violating our security policies (#5.7.1 - chkuser)\r\n"
+#define CHKUSER_NORELAY_STRING "553 sorry, that domain isn't in my list of allowed rcpthosts (#5.5.3 - chkuser)\r\n"
+	These are the errors handled by chkuser and emitted by qmail-smtpd as error responses.
+	You may change them here, if you don't like them.
+
diff -urN ../qmail-1.03_unpatched/Makefile ./Makefile
--- ../qmail-1.03_unpatched/Makefile	1998-06-15 12:53:16.000000000 +0200
+++ ./Makefile	2005-01-21 19:25:03.000000000 +0100
@@ -1,5 +1,9 @@
 # Don't edit Makefile! Use conf-* for configuration.
 
+VPOPMAIL_HOME=/home/vpopmail
+SMTPD_CHKUSER_OBJ=chkuser.o dns.o
+VPOPMAIL_LIBS=`head -1 $(VPOPMAIL_HOME)/etc/lib_deps` `cat dns.lib`
+
 SHELL=/bin/sh
 
 default: it
@@ -23,7 +27,7 @@
 auto-ccld.sh: \
 conf-cc conf-ld warn-auto.sh
 	( cat warn-auto.sh; \
-	echo CC=\'`head -1 conf-cc`\'; \
+        echo CC=\'`head -1 conf-cc` -I`head -1 conf-vpopmail`/include\'; \
 	echo LD=\'`head -1 conf-ld`\' \
 	) > auto-ccld.sh
 
@@ -136,6 +140,10 @@
 compile auto_usera.c
 	./compile auto_usera.c
 
+base64.o: \
+compile base64.c base64.h stralloc.h substdio.h str.h
+	./compile base64.c
+
 binm1: \
 binm1.sh conf-qmail
 	cat binm1.sh \
@@ -217,9 +225,9 @@
 
 case.a: \
 makelib case_diffb.o case_diffs.o case_lowerb.o case_lowers.o \
-case_starts.o
+case_starts.o case_startb.o
 	./makelib case.a case_diffb.o case_diffs.o case_lowerb.o \
-	case_lowers.o case_starts.o
+	case_lowers.o case_starts.o case_startb.o
 
 case_diffb.o: \
 compile case_diffb.c case.h
@@ -237,6 +245,10 @@
 compile case_lowers.c case.h
 	./compile case_lowers.c
 
+case_startb.o: \
+compile case_startb.c case.h
+	./compile case_startb.c
+
 case_starts.o: \
 compile case_starts.c case.h
 	./compile case_starts.c
@@ -263,7 +275,7 @@
 	cdbmake_add.o
 
 cdbmake_add.o: \
-compile cdbmake_add.c cdbmake.h uint32.h
+compile cdbmake_add.c cdbmake.h alloc.h uint32.h
 	./compile cdbmake_add.c
 
 cdbmake_hash.o: \
@@ -300,6 +312,10 @@
 exit.h auto_spawn.h
 	./compile chkspawn.c
 
+chkuser.o: \
+compile chkuser.c chkuser.h chkuser_settings.h
+	./compile chkuser.c
+
 clean: \
 TARGETS
 	rm -f `cat TARGETS`
@@ -808,7 +824,7 @@
 forward preline condredirect bouncesaying except maildirmake \
 maildir2mbox maildirwatch qail elq pinq idedit install-big install \
 instcheck home home+df proc proc+df binm1 binm1+df binm2 binm2+df \
-binm3 binm3+df
+binm3 binm3+df update_tmprsadh
 
 load: \
 make-load warn-auto.sh systype
@@ -890,6 +906,38 @@
 readwrite.h open.h headerbody.h maildir.h strerr.h
 	./compile maildirwatch.c
 
+maildirgetquota.o: \
+compile maildirgetquota.c maildirgetquota.h maildirmisc.h
+	./compile maildirgetquota.c
+
+maildirflags.o: \
+compile maildirflags.c
+	./compile maildirflags.c
+
+maildiropen.o: \
+compile maildiropen.c maildirmisc.h
+	./compile maildiropen.c
+
+maildirparsequota.o: \
+compile maildirparsequota.c
+	./compile maildirparsequota.c
+
+maildirquota.o: \
+compile maildirquota.c maildirquota.h maildirmisc.h numlib.h
+	./compile maildirquota.c
+
+overmaildirquota.o: \
+compile overmaildirquota.c 
+	./compile overmaildirquota.c
+
+strtimet.o: \
+compile strtimet.c 
+	./compile strtimet.c
+
+strpidt.o: \
+compile strpidt.c 
+	./compile strpidt.c
+
 mailsubj: \
 warn-auto.sh mailsubj.sh conf-qmail conf-break conf-split
 	cat warn-auto.sh mailsubj.sh \
@@ -1174,12 +1222,15 @@
 load qmail-local.o qmail.o quote.o now.o gfrom.o myctime.o \
 slurpclose.o case.a getln.a getopt.a sig.a open.a seek.a lock.a fd.a \
 wait.a env.a stralloc.a alloc.a strerr.a substdio.a error.a str.a \
-fs.a datetime.a auto_qmail.o auto_patrn.o socket.lib
+fs.a datetime.a auto_qmail.o auto_patrn.o socket.lib maildirquota.o \
+maildirgetquota.o maildiropen.o maildirparsequota.o overmaildirquota.o \
+strtimet.o strpidt.o
 	./load qmail-local qmail.o quote.o now.o gfrom.o myctime.o \
 	slurpclose.o case.a getln.a getopt.a sig.a open.a seek.a \
 	lock.a fd.a wait.a env.a stralloc.a alloc.a strerr.a \
 	substdio.a error.a str.a fs.a datetime.a auto_qmail.o \
-	auto_patrn.o  `cat socket.lib`
+	auto_patrn.o  `cat socket.lib` maildirquota.o maildirgetquota.o \
+    maildiropen.o maildirparsequota.o overmaildirquota.o strtimet.o strpidt.o
 
 qmail-local.0: \
 qmail-local.8
@@ -1269,11 +1320,13 @@
 qmail-pop3d: \
 load qmail-pop3d.o commands.o case.a timeoutread.o timeoutwrite.o \
 maildir.o prioq.o now.o env.a strerr.a sig.a open.a getln.a \
-stralloc.a alloc.a substdio.a error.a str.a fs.a socket.lib
+stralloc.a alloc.a substdio.a error.a str.a fs.a socket.lib maildirquota.o \
+maildirparsequota.o maildirflags.o maildiropen.o strtimet.o strpidt.o
 	./load qmail-pop3d commands.o case.a timeoutread.o \
 	timeoutwrite.o maildir.o prioq.o now.o env.a strerr.a sig.a \
 	open.a getln.a stralloc.a alloc.a substdio.a error.a str.a \
-	fs.a  `cat socket.lib`
+	fs.a  `cat socket.lib` maildirquota.o maildirgetquota.o \
+    maildirparsequota.o maildirflags.o maildiropen.o strtimet.o strpidt.o
 
 qmail-pop3d.0: \
 qmail-pop3d.8
@@ -1289,10 +1342,10 @@
 qmail-popup: \
 load qmail-popup.o commands.o timeoutread.o timeoutwrite.o now.o \
 case.a fd.a sig.a wait.a stralloc.a alloc.a substdio.a error.a str.a \
-fs.a socket.lib
+fs.a base64.o socket.lib
 	./load qmail-popup commands.o timeoutread.o timeoutwrite.o \
 	now.o case.a fd.a sig.a wait.a stralloc.a alloc.a \
-	substdio.a error.a str.a fs.a  `cat socket.lib`
+	substdio.a error.a str.a fs.a base64.o `cat socket.lib`
 
 qmail-popup.0: \
 qmail-popup.8
@@ -1301,7 +1354,7 @@
 qmail-popup.o: \
 compile qmail-popup.c commands.h fd.h sig.h stralloc.h gen_alloc.h \
 substdio.h alloc.h wait.h str.h byte.h now.h datetime.h fmt.h exit.h \
-readwrite.h timeoutread.h timeoutwrite.h
+readwrite.h timeoutread.h timeoutwrite.h base64.h
 	./compile qmail-popup.c
 
 qmail-pw2u: \
@@ -1444,6 +1497,7 @@
 substdio.a error.a str.a fs.a auto_qmail.o dns.lib socket.lib
 	./load qmail-remote control.o constmap.o timeoutread.o \
 	timeoutwrite.o timeoutconn.o tcpto.o now.o dns.o ip.o \
+	tls.o ssl_timeoutio.o -L/usr/local/ssl/lib -lssl -lcrypto \
 	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`
@@ -1483,12 +1537,12 @@
 trigger.o fmtqfn.o quote.o now.o readsubdir.o qmail.o date822fmt.o \
 datetime.a case.a ndelay.a getln.a wait.a seek.a fd.a sig.a open.a \
 lock.a stralloc.a alloc.a substdio.a error.a str.a fs.a auto_qmail.o \
-auto_split.o
+auto_split.o env.a
 	./load qmail-send qsutil.o control.o constmap.o newfield.o \
 	prioq.o trigger.o fmtqfn.o quote.o now.o readsubdir.o \
 	qmail.o date822fmt.o datetime.a case.a ndelay.a getln.a \
 	wait.a seek.a fd.a sig.a open.a lock.a stralloc.a alloc.a \
-	substdio.a error.a str.a fs.a auto_qmail.o auto_split.o 
+	substdio.a error.a str.a fs.a auto_qmail.o auto_split.o env.a
 
 qmail-send.0: \
 qmail-send.8
@@ -1532,28 +1586,31 @@
 	./compile qmail-showctl.c
 
 qmail-smtpd: \
-load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \
+load qmail-smtpd.o rcpthosts.o qregex.o commands.o timeoutread.o \
 timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \
 date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \
 open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \
-fs.a auto_qmail.o socket.lib
-	./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \
+fs.a auto_qmail.o base64.o socket.lib $(SMTPD_CHKUSER_OBJ)
+	./load qmail-smtpd $(SMTPD_CHKUSER_OBJ) qregex.o rcpthosts.o commands.o timeoutread.o \
 	timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \
+	tls.o ssl_timeoutio.o ndelay.a -L/usr/local/ssl/lib -lssl -lcrypto \
 	received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \
 	datetime.a getln.a open.a sig.a case.a env.a stralloc.a \
-	alloc.a substdio.a error.a str.a fs.a auto_qmail.o  `cat \
-	socket.lib`
+	alloc.a substdio.a error.a str.a fs.a auto_qmail.o base64.o \
+	$(VPOPMAIL_LIBS) \
+	`cat socket.lib` \
+        -lcrypt 
 
 qmail-smtpd.0: \
 qmail-smtpd.8
 	nroff -man qmail-smtpd.8 > qmail-smtpd.0
 
 qmail-smtpd.o: \
-compile qmail-smtpd.c sig.h readwrite.h stralloc.h gen_alloc.h \
+compile qmail-smtpd.c chkuser.h sig.h readwrite.h stralloc.h gen_alloc.h \
 substdio.h alloc.h auto_qmail.h control.h received.h constmap.h \
 error.h ipme.h ip.h ipalloc.h ip.h gen_alloc.h ip.h qmail.h \
 substdio.h str.h fmt.h scan.h byte.h case.h env.h now.h datetime.h \
-exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h
+exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h base64.h
 	./compile qmail-smtpd.c
 
 qmail-start: \
@@ -1681,6 +1738,10 @@
 constmap.h stralloc.h gen_alloc.h rcpthosts.h
 	./compile rcpthosts.c
 
+qregex.o: \
+compile qregex.c qregex.h
+	./compile qregex.c
+
 readsubdir.o: \
 compile readsubdir.c readsubdir.h direntry.h fmt.h scan.h str.h \
 auto_split.h
@@ -1827,7 +1888,8 @@
 ipalloc.h ipalloc.c select.h1 select.h2 trysysel.c ndelay.h ndelay.c \
 ndelay_off.c direntry.3 direntry.h1 direntry.h2 trydrent.c prot.h \
 prot.c chkshsgr.c warn-shsgr tryshsgr.c ipme.h ipme.c trysalen.c \
-maildir.5 maildir.h maildir.c tcp-environ.5 constmap.h constmap.c
+maildir.5 maildir.h maildir.c tcp-environ.5 constmap.h constmap.c \
+update_tmprsadh
 	shar -m `cat FILES` > shar
 	chmod 400 shar
 
@@ -1892,7 +1954,7 @@
 
 spawn.o: \
 compile chkspawn spawn.c sig.h wait.h substdio.h byte.h str.h \
-stralloc.h gen_alloc.h select.h exit.h coe.h open.h error.h \
+stralloc.h gen_alloc.h select.h exit.h alloc.h coe.h open.h error.h \
 auto_qmail.h auto_uids.h auto_spawn.h
 	./chkspawn
 	./compile spawn.c
@@ -2108,6 +2170,19 @@
 compile timeoutwrite.c timeoutwrite.h select.h error.h readwrite.h
 	./compile timeoutwrite.c
 
+qmail-smtpd: tls.o ssl_timeoutio.o ndelay.a
+qmail-remote: tls.o ssl_timeoutio.o
+qmail-smtpd.o: tls.h ssl_timeoutio.h
+qmail-remote.o: tls.h ssl_timeoutio.h
+
+tls.o: \
+compile tls.c exit.h error.h
+	./compile tls.c
+
+ssl_timeoutio.o: \
+compile ssl_timeoutio.c ssl_timeoutio.h select.h error.h ndelay.h
+	./compile ssl_timeoutio.c
+
 token822.o: \
 compile token822.c stralloc.h gen_alloc.h alloc.h str.h token822.h \
 gen_alloc.h gen_allocdefs.h
@@ -2139,3 +2214,25 @@
 wait_pid.o: \
 compile wait_pid.c error.h haswaitp.h
 	./compile wait_pid.c
+
+cert cert-req: \
+Makefile-cert
+	@$(MAKE) -sf $< $@
+
+Makefile-cert: \
+conf-qmail Makefile-cert.mk
+	@cat Makefile-cert.mk \
+	| sed s}QMAIL}"`head -1 conf-qmail`"}g \
+	> $@
+
+update_tmprsadh: \
+conf-qmail update_tmprsadh.sh
+	@cat update_tmprsadh.sh\
+	| sed s}QMAIL}"`head -1 conf-qmail`"}g \
+	> $@
+	chmod 755 update_tmprsadh 
+
+tmprsadh: \
+update_tmprsadh
+	echo "Creating new temporary RSA and DH parameters"
+	./update_tmprsadh
diff -urN ../qmail-1.03_unpatched/Makefile-cert.mk ./Makefile-cert.mk
--- ../qmail-1.03_unpatched/Makefile-cert.mk	1970-01-01 01:00:00.000000000 +0100
+++ ./Makefile-cert.mk	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,21 @@
+cert-req: req.pem
+cert cert-req: QMAIL/control/clientcert.pem
+	@:
+
+QMAIL/control/clientcert.pem: QMAIL/control/servercert.pem
+	ln -s $< $@
+
+QMAIL/control/servercert.pem:
+	PATH=$$PATH:/usr/local/ssl/bin \
+		openssl req -new -x509 -nodes -days 366 -out $@ -keyout $@
+	chmod 640 $@
+	chown qmaild.qmail $@
+
+req.pem:
+	PATH=$$PATH:/usr/local/ssl/bin openssl req \
+		-new -nodes -out $@ -keyout QMAIL/control/servercert.pem
+	chmod 640 QMAIL/control/servercert.pem
+	chown qmaild.qmail QMAIL/control/servercert.pem
+	@echo
+	@echo "Send req.pem to your CA to obtain signed_req.pem, and do:"
+	@echo "cat signed_req.pem >> QMAIL/control/servercert.pem"
diff -urN ../qmail-1.03_unpatched/README.auth ./README.auth
--- ../qmail-1.03_unpatched/README.auth	1970-01-01 01:00:00.000000000 +0100
+++ ./README.auth	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,64 @@
+README qmail-smtpd SMTP Authentication
+======================================
+
+
+History:
+--------
+
+This patch is based on Krzysztof Dabrowski's qmail-smtpd-auth-0.31 patch 
+which itself uses "Mrs. Brisby's" initial code. 
+Version 0.41 of this patch fixes the "CAPS-LOCK" typo announcing
+'CRAM_MD5' instead of 'CRAM-MD5' (german keyboard) - tx to Mike Garrison.
+Version 0.42 fixes the '421 unable to read controls (#4.3.0)' problem
+(can't read control/morercpthosts.cdb) because FD 3 was already closed - tx Richard Lyons.
+Version 0.43 fixes the ba64decode() failure in case CRAM_MD5 is not enabled - tx Vladimir Zidar.
+Version 0.51 includes the evaluation of the 'Auth' and the 'Size' parameter in the 'Mail From:' command.
+Version 0.52 uses DJB functions to copy FDs.
+Version 0.56 corrects some minor mistakes displaying the 'Auth' userid.
+
+
+Scope:
+------
+
+This patch supports RFC 2554 "SMTP Service Extension for Authentication" for qmail-smtpd.
+Additionally, RFC 1870 is honoured ("SMTP Service Extension for Message Size Declaration").
+For more technical details see: http://www.fehcom.de/qmail/docu/smtpauth.html.
+
+
+Installation:
+-------------
+
+* Untar the source in the qmail-1.03 home direcotry.
+* Run ./install_auth.
+* Modify the compile time option "#define CRAM_MD5" to your needs.
+* Re-make qmail.
+
+
+Setup:
+------
+
+In order to use SMTP Authentication you have to use a 'Pluggable Authentication Module'
+PAM to be called by qmail-smtpd; typically
+
+	/var/qmail/bin/qmail-smtpd /bin/checkpassword true 2>&1
+
+Since qmail-smtpd does not run as root, checkpassword has to be made sticky.
+There is no need to include additionally the hostname in the call.
+In order to compute the CRAM-MD5 challenge, qmail-smtpd uses the 'tcplocalhost' information.
+
+
+Changes wrt. Krysztof Dabrowski's patch:
+----------------------------------------
+
+* Avoid the 'hostname' in the call of the PAM.
+* Confirm to Dan Bernstein's checkpassword interface even for CRAM-MD5.
+* Doesn't close FD 2; thus not inhibiting logging to STDERR.
+* Fixed bugs in base64.c.
+* Modified unconditional close of FD 3 in order to sustain reading of 'control/morecpthosts.cdb'.
+* Evaluation of the (informational) Mail From: < > Auth=username.
+* Additional support for the advertised "Size" via 'Mail From: <return-path> SIZE=123456780' (RFC 1870).
+
+
+Erwin Hoffmann - Cologne 2004-09-17 (www.fehcom.de)
+
+
diff -urN ../qmail-1.03_unpatched/README.qregex ./README.qregex
--- ../qmail-1.03_unpatched/README.qregex	1970-01-01 01:00:00.000000000 +0100
+++ ./README.qregex	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,155 @@
+QREGEX (v2) 20040315 - README March 15, 2004
+A Regular Expression matching patch for qmail 1.03
+
+
+OVERVIEW:
+
+qregex adds the ability to match address evelopes via Regular Expressions (REs)
+in the qmail-smtpd process. It has the abiltiy to match `helo/ehlo` (host name),
+`mail from` (envelope sender), and `rcpt to` (envelope recipient) commands with 
+no load at all to the parent process. It follows all the base rules that are
+set out with qmail (ie using control files) so it makes for easy integretion
+into an existing setup (see the install instructions for more info). The v2 is
+specified because qregex was re-written to better conform to the security
+guarantee set forth by the author of qmail. The original version used stdio.h
+and stdlib.h for reading the control files whereas v2 now uses all stralloc
+functions which are much more regulated against buffer overruns and the like.
+See: http://cr.yp.to/qmail/guarantee.html
+
+NEW FEATURES:
+
+Features added to qregex since its creation are:
+
+1. Performs pattern matching on the helo/ehlo host name. Setting the
+   NOBADHELO environment variable prevents the host name from being
+   compared to the patterns in the badhelo control file.
+
+2. Matches to patterns in the badhelo, badmailfrom, and badmailto control
+   files are logged.
+
+3. Matching is case insensitive.
+
+4. Qregex ignores empty envelope senders. An empty envelope sender is not
+   compared to the patterns in the badmailfrom control file and is always
+   accepted.
+
+
+
+PLATFORMS:
+
+Qregex has been built and tested on the following platforms. I'm sure it won't
+have any problems on any platform that qmail will run on (providing they have
+a regex interface) but if you run into problems let me know.
+
+       - OpenBSD 3.x
+       - FreeBSD 4.x
+       - Mandrake Linux 9.x
+
+
+
+
+INSTALLATION INSTRUCTIONS:
+
+Installation is very simple, there is only one requirement. You need to use the
+GNU version of the patch utility (http://www.gnu.org/software/patch/patch.html).
+(For Solaris 8 users like me it is installed as 'gpatch')
+
+- If this is a new setup.
+Uncompress and untar the qmail archive, copy the 'qregex.patch' file into the
+new qmail-1.03 directory and run "patch < qregex.patch"
+Follow the instructions as per the included qmail INSTALL file. Once you are
+done come back to this file and read the section on the control files.
+
+- If this is an existing setup.
+FIRST: create your control files (see below).
+Copy the 'qregex.patch' file into your existing qmail source directory.
+Run "patch < qregex.patch" then "make qmail-smtpd". Now run ./qmail-smtpd and
+test your new rules to make sure they work as expected.
+
+Install the new binary by cd'ing to /var/qmail/bin and as root (in one command)
+copy the existing binary to 'qmail-smtpd.old' and copy the new binary from the
+source directory to 'qmail-smtpd'.
+(ex. cp qmail-smtpd qmail-smtpd.old && cp ~/qmail-1.03/qmail-smtpd qmail-smtpd)
+
+You can also optionally just run "make setup check" as it will install the
+updated documentation and man pages provided with this patch. Stopping qmail
+before doing the "make setup check" is always a good idea.
+
+
+CONTROL FILES:
+
+qregex provides you with three new control files.
+The first (which really isn't new) is "control/badmailfrom". This file
+originally matched addresses statically and now contains your REs for matching
+from the 'mail from' command.
+The other two are "control/badmailto" and "control/badhelo". They are exactly
+the same as the first except that they match against the 'rcpt to' and
+'helo/ehlo' commands respectively.
+
+If you prefer you can symlink the badmailfrom and badmailto control files
+(ln -s badmailfrom badmailto) and only maintain two sets of rules. Beware
+this might cause problems in certain setups.
+
+       Here's an example "badhelo" file.
+       -----------------------------------
+       # block host strings with no dot (not a FQDN)
+       !\.
+       -----------------------------------
+
+       An example "badmailfrom" file.
+       -----------------------------------
+       # this will drop everything containing the string
+       # bad.domain.com or Bad.Domain.Com or BAD.domain.COM
+       bad\.domain\.com
+       # force users to fully qualify themselves
+       # (i.e. deny "user", accept "user@domain")
+       !@
+       -----------------------------------
+
+       And "badmailto" (a litte more interesting)
+       -----------------------------------
+       # must not contain invalid characters, brakets or multiple @'s
+       [!%#:*^(){}]
+       @.*@
+       -----------------------------------
+
+Also you can use the non-RE character '!' to start an RE as a signal to qregex
+to negate the action. As used above in the badmailfrom file, by negating the '@'
+symbol qregex will signal qmail-smtpd to deny the 'mail from' command whenever
+the address doesn't contain an @ symbol. When used inside a bracket expression,
+the '!' character looses this special meaning as shown by the badmailto example.
+
+
+INTERNALS:
+
+Qregex (or regexmatch as the function is called) will be called during the
+`helo/ehlo`, `rcpt to` and `mail from` handling routines in "qmail-smtpd.c".
+When called it will read the proper control file then one by one compile and
+execute the regex on the string passed into qmail-smtpd. If the regex matches
+it returns TRUE (1) and the qmail-smtpd process will deny the user the ability
+to continue. If you change anything and think it betters this patch please
+send me a new diff file so I can take a peek.
+
+
+CONTACT:
+This version of qregex was prepared by:
+       Andrew St. Jean
+       andrew@arda.homeunix.net
+       www.arda.homeunix.net/store/qmail/
+
+Documentation (manpage updates) and qmail-showctl patch added by:
+       Jeremy Kitchen
+       kitchen at scriptkitchen dot com
+       http://www.scriptkitchen.com/qmail
+
+Badhelo additions supplied by:
+       Alex Pleiner
+       alex@zeitform.de
+       zeitform Internet Dienste
+       http://www.zeitform.de/
+
+Original qregex patch can be found at:
+        www  : http://www.unixpimps.org/software/qregex
+       email: evan at unixpimps dot org
+
+
diff -urN ../qmail-1.03_unpatched/README.tls ./README.tls
--- ../qmail-1.03_unpatched/README.tls	1970-01-01 01:00:00.000000000 +0100
+++ ./README.tls	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,99 @@
+Frederik Vermeulen <qmail-tls akrul inoa.net> 20040419
+http://inoa.net/qmail-tls/
+
+This patch implements RFC 3207 (was RFC 2487) in qmail. 
+This means you can get SSL or TLS encrypted and 
+authenticated SMTP between the MTAs and from MUA to MTA. 
+The code is considered experimental (but has worked for
+many since its first release on 1999-03-21).
+
+Usage: - install OpenSSL-0.9.7d http://www.openssl.org/
+         (any 0.9.6 and 0.9.7 version is presumed to work)
+       - apply patch to netqmail-1.05 http://qmail.org/netqmail
+         (should work on qmail-1.03 too).  The patches to 
+         qmail-remote.c and qmail-smtpd.c can be applied separately.
+       - provide a server certificate in /var/qmail/control/servercert.pem.
+         "make cert" makes a self-signed certificate.
+         "make cert-req" makes a certificate request.
+         Note: you can add the CA certificate and intermediate
+         certs to the end of servercert.pem.
+       - replace qmail-smtpd and/or qmail-remote binary
+       - verify operation (header information should show
+         something like
+         "Received [..] with (DHE-RSA-AES256-SHA encrypted) SMTP;")
+         If you don't have a server to test with, you can test
+         by sending mail to tag-ping@tbs-internet.com,
+         which will bounce your mail.
+
+Optional: - when DEBUG is defined, some extra TLS info will be logged
+          - qmail-remote will authenticate with the certificate in
+            /var/qmail/control/clientcert.pem. By preference this is
+            the same as servercert.pem, where nsCertType should be 
+            == server,client or be a generic certificate (no usage specified). 
+          - when a 512 bit RSA key is provided in /var/qmail/control/rsa512.pem,
+            this key will be used instead of (slow) on-the-fly generation by
+            qmail-smtpd. Idem for 512 and 1024 DH params in control/dh512.pem
+            and control/dh1024.pem. `make tmprsadh` does this.
+            Periodical replacement can be done by crontab:
+            01 01 * * * /var/qmail/bin/update_tmprsadh > /dev/null 2>&1
+          - server authentication:
+            qmail-remote requires authentication from servers for which
+            /var/qmail/control/tlshosts/host.dom.ain.pem exists.
+            The .pem file contains the validating CA certificates
+            (or self-signed server certificate).
+            CommonName has to match.
+            WARNING: this option may cause mail to be delayed, bounced,
+            doublebounced, and lost.
+          - client authentication:
+            when relay rules would reject an incoming mail, 
+            qmail-smtpd can allow the mail based on a presented cert.
+            Certs are verified against a CA list in 
+            /var/qmail/control/clientca.pem (eg. http://www.modssl.org/
+            source/cvs/exp/mod_ssl/pkg.mod_ssl/pkg.sslcfg/ca-bundle.crt)
+            and the cert email-address has to match a line in
+            /var/qmail/control/tlsclients. This email-address is logged
+            in the headers. CRLs can be provided through 
+            /var/qmail/control/clientcrl.pem.
+          - cipher selection:
+            qmail-remote: 
+              openssl cipher string (`man ciphers`) read from 
+              /var/qmail/control/tlsclientciphers
+            qmail-smtpd: 
+              openssl cipher string read from TLSCIPHERS environment variable
+              (can vary based on client IP address e.g.)
+              or if that is not available /var/qmail/control/tlsserverciphers
+          - smtps (deprecated SMTP over TLS via port 465):
+            qmail-remote: when connecting to port 465 
+            qmail-smtpd: when SMTPS environment variable is not empty
+
+Caveats: - do a `make clean` after patching
+         - binaries dynamically linked with current openssl versions need
+           recompilation when the shared openssl libs are upgraded.
+         - this patch could conflict with other patches (notably those
+           replacing \n with \r\n, which is a bad idea on encrypted links).
+         - some broken servers have a problem with TLSv1 compatibility.
+           Uncomment the line where we set the SSL_OP_NO_TLSv1 option.
+         - needs working /dev/urandom (or EGD for openssl versions >0.9.7)
+           for seeding random number generator.
+         - packagers should make sure that installing without a valid 
+           servercert is impossible
+         - when applied in combination with AUTH patch, AUTH patch
+           should be applied first and first part of this patch
+           will fail. This error can be ignored. Packagers should
+           cut the first 12 lines of this patch to make a happy
+           patch
+         - `make tmprsadh` is recommended (or should I say required), 
+           otherwise DH generation can be unpredictably slow
+         - some need "-I/usr/kerberos/include" to be added in conf-cc
+
+Copyright: GPL
+           Links with OpenSSL
+           Inspiration and code from examples in SSLeay (E. Young
+           <eay@cryptsoft.com> and T. Hudson <tjh@cryptsoft.com>),
+           stunnel (M. Trojnara <mtrojnar@ddc.daewoo.com.pl>),
+           Postfix/TLS (L. Jaenicke <Lutz.Jaenicke@aet.tu-cottbus.de>),
+           modssl (R. Engelschall <rse@engelschall.com>),
+           openssl examples of E. Rescorla <ekr@rtfm.com>.
+
+Bug reports: mailto:<qmail-tls akrul inoa.net>
+
diff -urN ../qmail-1.03_unpatched/README.zeitform ./README.zeitform
--- ../qmail-1.03_unpatched/README.zeitform	1970-01-01 01:00:00.000000000 +0100
+++ ./README.zeitform	2005-01-21 20:14:54.000000000 +0100
@@ -0,0 +1,262 @@
+README.zeitform
+zeitform-qmail-toaster, version 0.21
+2003-2004 alex pleiner, alex@zeitform.de
+(c) zeitform Internet Dienste
+
+http://alex.zeitform.de/qmail-new/qmail.html
+
+
+Patches for a zeitform qmail toaster based on qmail 1.03
+---------------------------------------------------------
+
+USAGE:
+
+patch -p0 < zeitform-qmail-toaster-x.x.patch
+
+
+OVERVIEW:
+
+This patch is just a collection of other patches found on the web or
+provided by some developers around. This patch-collection
+(toaster-patch) has been tested under Linux only. Use with care.
+
+The following patches are included:
+
+  BUGFIXES
+
+  * errno (Phil Edwards):
+    fix for the changes in the new glibc (2.3.1 or newer). This patch
+    is not needed on systems with an older glibc but won't hurt.
+    http://memoryhole.net/qmail/glibc-2.3.x.patch
+    http://news.gmane.org/article.php?id=13960&group=gmane.mail.qmail.general
+    http://www.qmail.org/moni.csi.hu/pub/glibc-2.3.1/
+
+  * qmail-local-fix (Erik Sjoelund):
+    fixes a bug (a logic error) in qmail-local.
+    http://memoryhole.net/qmail/bugfix.qmail-local.patch
+    http://www.ornl.gov/its/archives/mailing-lists/qmail/2000/10/msg00696.html
+
+  * 0.0.0.0 (Scott Gifford):
+    Qmail ought to recognize 0.0.0.0 as a local IP address, but
+    doesn't by default. That may allow spammers to spoof you, and
+    certainly means that Qmail is incorrect. 
+    http://memoryhole.net/qmail/qmail-0.0.0.0.patch
+    http://www.suspectclass.com/~sgifford/qmail/qmail-0.0.0.0.patch
+
+  * sendmail-flagf (David Phillips): 
+    sendmail emulation doesn't correctly support the -f flag as it should
+    http://memoryhole.net/qmail/sendmail-flagf.patch
+    http://david.acz.org/software/sendmail-flagf.patch
+
+  * link-sync (Frank Denis):
+    Qmail was designed for BSD-like filesystems. And it is unreliable
+    under Linux because it assumes that link() is a synchronous
+    operation. This is not the case with EXT2 and ReiserFS. 
+    http://www.jedi.claranet.fr/qmail-tuning.html
+    http://www.jedi.claranet.fr/qmail-link-sync.patch
+
+  * isoc (James Craig Burley)
+    This patch improves ISO C conformance of qmail code -- specifically,
+    of qmail-lspawn, qmail-newmrh, qmail-newu, qmail-pop3d, qmail-popup,
+    qmail-rspawn, and qmail-smtpd.
+    http://www.jcb-sc.com/qmail/patches/qmail-isoc.patch
+
+  EXTENSIONS
+
+  * qmailqueue (Bruce Guenter):
+    The QMAILQUEUE patch is used as a generic way to insert another
+    program into the Qmail structure. It is merely a support patch
+    (i.e. supports other uses), and has no real additional
+    functionality in and of itself. QMAILQUEUE is needed to run
+    qmail-scanner (antivirus, anti-spam).
+    http://memoryhole.net/qmail/qmailqueue-patch
+    http://www.qmail.org/qmailqueue-patch
+
+    Usage: QMAILQUEUE="/path/to/queue-replacement"
+  
+  * oversizedns (Christopher K. Davis):
+    get qmail to handle oversize DNS packets. (Other DNS servers
+    sometimes send DNS packets that are bigger than they ought to be.)
+    http://www.ckdhr.com/ckd/qmail-103.patch
+
+  * qregex (Andrew St. Jean):
+    With REs (Regular Expresions) it becomes quite easy to filter out
+    email addresses that contain invalid characters or simply aren't a
+    real address.
+    http://www.arda.homeunix.net/store/qmail/
+
+    Usage: see URL above or README.qregex
+
+  * starttls (Frederik Vereulen):
+    support for STARTTLS ESMTP extension
+    old: http://memoryhole.net/qmail/qmail-1.03-tls-20021228.patch
+    old: http://inoa.net/qmail/qmail-1.03-tls.patch
+    http://inoa.net/qmail-tls/netqmail-1.04-tls-20040120.patch
+
+    Usage: see documentation in patch file or README.tls
+
+  * maildir++ (Bill Shupp):
+    This patch adds maildirquota (Maildir++) support to qmail-pop3d
+    and qmail-local.  It was created because when vpopmail switched to
+    maildirquotas, a user's quota usage was not decreased after
+    deleting mail via qmail-pop3d.
+    http://shupp.org/patches/qmail-maildir++.patch
+
+  * rfc2821 (Matthias Andree):
+    changes qmail-remote to skip over MX servers that greet with
+    codes 4xx or 5xx and try the next MX for real RFC-2821 compliance
+    (Sendmail and Postfix do that).
+    http://www-dt.e-technik.uni-dortmund.de/~ma/qmail/patch-qmail-1.03-rfc2821.diff
+
+  * smtp_auth and size (Erwin Hoffmann):
+    add SMTP_AUTH to qmail-smtpd (CRAM-MD5, PLAIN and LOGIN). This works 
+    with vpopmail versions above 5.4.0 without modification. Please note that
+    you must omit the hostname argument in the qmail-smtpd startup file as
+    documented in README.auth.
+    ESMTP has a way of explicitly defining this size limit up front,
+    which allows valid clients and standards-compliant email servers
+    to avoid spending the time to send the large email before
+    discovering that it's too big.
+    http://www.fehcom.de/qmail/smtpauth.html
+
+    Usage: echo 50000000 > /var/qmail/control/databytes
+
+  * pop3_auth_and_capa (Alex Pleiner)
+    adds AUTH to qmail-popup (CRAM-MD5 only). This is based on Erwin Hoffmann's
+    code and slightly modified. This patch also adds the CAPA command to
+    qmail-popup.
+    http://alex.zeitform.de/qmail/qmail-popup-auth_cram_md5/
+
+  * capa_pop3d (Tom Clegg): 
+    adds CAPA to qmail-pop3d. Based on qmail-capa-pop3d.patch and the
+    capa features of pop3_auth_and_capa for qmail-popup.
+    http://tomclegg.net/qmail/qmail-capa-pop3d.patch
+
+  * pop3d-stat (Dwayne Koonce)
+    This patch changes number of messages returned in qmail-pop3d's reponse to STAT. 
+    It now returns number of not deleted messages. Returned number of all messages.
+    http://alex.zeitform.de/qmail/qmail_single_patches/qmail-pop3d-stat.tls.patch
+
+  * viruscan (Russel Nelson, mods by zeitform):
+    this patch blocks executable attachments at smtp level. The modifications bring 
+    back Charles Cazabon's EXECUTABLEOK from version 1.1
+    http://qmail.mirrors.space.net/qmail-smtpd-viruscan-1.3.patch
+
+    Usage: Set EXECUTABLEOK to disable this
+           Edit /var/qmail/control/signatures (see below)
+
+  * chkuser2 (Antonio Nati): 
+    this patch checks for valid users based on vpopmail before accepting
+    mails for local domains
+    http://www.interazioni.it/opensource/chkuser/
+
+    Usage: Edit chkuser_settings.h and recompile
+
+  * qmail-queue-custom-error (Flavio Curti)
+    Adds the possibility for a qmail-queue-replacement to offer custom
+    error (=bounce) messages (used by simscan when configured with 
+    --enable-custom-smtp-reject=y)
+    Part of simscan (http://www.inter7.com)
+
+
+FURTHER ACTION:
+
+  * modify the file /var/qmail/bin/update_tmprsadh (read README.tls
+    for further instructions). create crontab entry:
+
+    01 01 * * * /var/qmail/bin/update_tmprsadh > /dev/null 2>&1
+
+  * create the signature file like this:
+
+cat <<EOF >/var/qmail/control/signatures
+# Windows executables seen in active virii
+TVqQAAMAA
+TVpQAAIAA
+# Additional windows executable signatures not yet seen in virii
+TVpAALQAc
+TVpyAXkAX
+TVrmAU4AA
+TVrhARwAk
+TVoFAQUAA
+TVoAAAQAA
+TVoIARMAA
+TVouARsAA
+TVrQAT8AA
+# .ZIPfile signature seen in SoBig.E and mydoom:
+# selective zip blocking is not really working - block all or none
+#UEsDBBQAA
+#UEsDBAoAAA
+# .GIF file found in a previous Microsoft virus making the rounds.
+#R0lGODlhaAA7APcAAP///+rp6puSp6GZrDUjUUc6Zn53mFJMdbGvvVtXh2xre8bF1x8cU4yLprOy
+EOF
+
+
+HISTORY:
+
+v0.21: updated maildir++ (duplicate free bug)
+       updated chkuser 2.0.8b
+v0.20: updated chkuser 2.0.7 (removed mfcheck), added qmail-queue-custom-error
+v0.19: updated chkuser 2.0.5 (edit chkuser_settings.h)
+v0.18: dropped size patch, upgraded smtp_auth by Erwin Hoffmann (0.56,
+       includes size extension). Take care: smtp_auth versions before 0.43 
+       are buggy if you disable CRAM-MD5.
+v0.17: upgraded TLS patch (20040419)
+v0.16: added logging for mfcheck, viruscan, badrcptto
+       updated mfcheck.
+v0.15: change: update of qregex to include bad HELO checks
+       announce: website for zeitform qmail toaster 
+       http://alex.zeitform.de/qmail-new/qmail.html
+v0.14: changes: removed all code from Vladimir Kabanov and reimplemented
+       AUTH CRAM-MD5 for qmail-popup. Sorry Vlad, your code was very 
+       useful, but we need to upgrade vpopmail. This toaster patch now
+       supports the current version of vpopmail. SMTP AUTH is now done
+       by the patch provided by Erwin Hoffmann.
+       change: chkusr for vpopmail > 5.2.25
+       change: modification to chkusr for qmail-ext
+v0.13: fix: fixed the CAPA in qmail-pop3d.c that still used puts()
+       fix: fixed qregex memleak in qmail-smtpd.c (according to
+       qmail-1.03-qregex-memleak-fix.patch)
+       added qmail-pop3d-stat patch       
+       updated tls patch and added README.tls
+v0.12: updated Russel's viruscan patch (1.3)
+v0.11: added isoc patch 
+v0.10: added chkusr patch (needs vpopmail < 5.3.25)
+       added documentation for various patches to qmail-showctl and
+       qmail-control.9
+v0.9 : testing stuff (not published)
+v0.8 : added Russel's viruscan patch.
+v0.7 : added bugfix for size patch, removed *.orig and *.rej files
+       created by this patch
+v0.6 : added smtpd-auth-close3 (for morercpthosts)
+v0.5 : fixed a bug causing smtp to fail. After applying the size
+       patch qmail-smtp sended "250 8BITMIME" followed by "250 SIZE"
+       This is wrong.
+v0.4 : added rfc2821 patch  *** DO NOT USE ***
+v0.3 : added some bugfixes and extensions from *** DO NOT USE ***
+       http://memoryhole.net/qmail/. qmail-local-fix, 0.0.0.0,
+       sendmail-flagf, size
+v0.2 : added link-sync
+v0.1 : Initial collection
+
+
+LICENSE:
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
diff -urN ../qmail-1.03_unpatched/TARGETS ./TARGETS
--- ../qmail-1.03_unpatched/TARGETS	1998-06-15 12:53:16.000000000 +0200
+++ ./TARGETS	2005-01-21 19:25:03.000000000 +0100
@@ -15,11 +15,20 @@
 slurpclose.o
 make-makelib
 makelib
+maildirflags.o
+maildirparsequota.o
+maildiropen.o
+maildirgetquota.o
+maildirquota.o
+overmaildirquota.o
+strtimet.o
+strpidt.o
 case_diffb.o
 case_diffs.o
 case_lowerb.o
 case_lowers.o
 case_starts.o
+case_startb.o
 case.a
 getln.o
 getln2.o
@@ -168,6 +177,8 @@
 constmap.o
 timeoutread.o
 timeoutwrite.o
+tls.o
+ssl_timeoutio.o
 timeoutconn.o
 tcpto.o
 dns.o
@@ -250,8 +261,10 @@
 qmail-qmtpd.o
 rcpthosts.o
 qmail-qmtpd
+base64.o
 qmail-smtpd.o
 qmail-smtpd
+qregex.o
 sendmail.o
 sendmail
 tcp-env.o
@@ -320,6 +333,7 @@
 binm2+df
 binm3
 binm3+df
+Makefile-cert
 it
 qmail-local.0
 qmail-lspawn.0
@@ -385,3 +399,5 @@
 man
 setup
 check
+chkuser.o
+update_tmprsadh
diff -urN ../qmail-1.03_unpatched/base64.c ./base64.c
--- ../qmail-1.03_unpatched/base64.c	1970-01-01 01:00:00.000000000 +0100
+++ ./base64.c	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,123 @@
+#include "base64.h"
+#include "stralloc.h"
+#include "substdio.h"
+#include "str.h"
+
+static char *b64alpha =
+  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+#define B64PAD '='
+
+/* returns 0 ok, 1 illegal, -1 problem */
+
+int b64decode(in,l,out)
+const unsigned char *in;
+int l;
+stralloc *out; /* not null terminated */
+{
+  int p = 0;
+  int n;
+  unsigned int x;
+  int i, j;
+  char *s;
+  unsigned char b[3];
+
+  if (l == 0)
+  {
+    if (!stralloc_copys(out,"")) return -1;
+    return 0;
+  }
+
+  while(in[l-1] == B64PAD) {
+    p ++;
+    l--;
+  }
+
+  n = (l + p) / 4;
+  out->len = (n * 3) - p;
+  if (!stralloc_ready(out,out->len)) return -1;
+  s = out->s;
+
+  for(i = 0; i < n - 1 ; i++) {
+    x = 0;
+    for(j = 0; j < 4; j++) {
+      if(in[j] >= 'A' && in[j] <= 'Z')
+        x = (x << 6) + (unsigned int)(in[j] - 'A' + 0);
+      else if(in[j] >= 'a' && in[j] <= 'z')
+        x = (x << 6) + (unsigned int)(in[j] - 'a' + 26);
+      else if(in[j] >= '0' && in[j] <= '9')
+        x = (x << 6) + (unsigned int)(in[j] - '0' + 52);
+      else if(in[j] == '+')
+        x = (x << 6) + 62;
+      else if(in[j] == '/')
+        x = (x << 6) + 63;
+      else if(in[j] == '=')
+        x = (x << 6);
+    }
+
+    s[2] = (unsigned char)(x & 255); x >>= 8;
+    s[1] = (unsigned char)(x & 255); x >>= 8;
+    s[0] = (unsigned char)(x & 255); x >>= 8;
+    s += 3; in += 4;
+  }
+
+  x = 0;
+  for(j = 0; j < 4; j++) {
+    if(in[j] >= 'A' && in[j] <= 'Z')
+      x = (x << 6) + (unsigned int)(in[j] - 'A' + 0);
+    else if(in[j] >= 'a' && in[j] <= 'z')
+      x = (x << 6) + (unsigned int)(in[j] - 'a' + 26);
+    else if(in[j] >= '0' && in[j] <= '9')
+      x = (x << 6) + (unsigned int)(in[j] - '0' + 52);
+    else if(in[j] == '+')
+      x = (x << 6) + 62;
+    else if(in[j] == '/')
+      x = (x << 6) + 63;
+    else if(in[j] == '=')
+      x = (x << 6);
+  }
+
+  b[2] = (unsigned char)(x & 255); x >>= 8;
+  b[1] = (unsigned char)(x & 255); x >>= 8;
+  b[0] = (unsigned char)(x & 255); x >>= 8;
+
+  for(i = 0; i < 3 - p; i++)
+    s[i] = b[i];
+
+  return 0;
+}
+
+int b64encode(in,out)
+stralloc *in;
+stralloc *out; /* not null terminated */
+{
+  unsigned char a, b, c;
+  int i;
+  char *s;
+
+  if (in->len == 0)
+  {
+    if (!stralloc_copys(out,"")) return -1;
+    return 0;
+  }
+
+  if (!stralloc_ready(out,in->len / 3 * 4 + 4)) return -1;
+  s = out->s;
+
+  for (i = 0;i < in->len;i += 3) {
+    a = in->s[i];
+    b = i + 1 < in->len ? in->s[i + 1] : 0;
+    c = i + 2 < in->len ? in->s[i + 2] : 0;
+
+    *s++ = b64alpha[a >> 2];
+    *s++ = b64alpha[((a & 3 ) << 4) | (b >> 4)];
+
+    if (i + 1 >= in->len) *s++ = B64PAD;
+    else *s++ = b64alpha[((b & 15) << 2) | (c >> 6)];
+
+    if (i + 2 >= in->len) *s++ = B64PAD;
+    else *s++ = b64alpha[c & 63];
+  }
+  out->len = s - out->s;
+  return 0;
+}
+
diff -urN ../qmail-1.03_unpatched/base64.h ./base64.h
--- ../qmail-1.03_unpatched/base64.h	1970-01-01 01:00:00.000000000 +0100
+++ ./base64.h	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,7 @@
+#ifndef BASE64_H
+#define BASE64_H
+
+extern int b64decode();
+extern int b64encode();
+
+#endif
diff -urN ../qmail-1.03_unpatched/case_startb.c ./case_startb.c
--- ../qmail-1.03_unpatched/case_startb.c	1970-01-01 01:00:00.000000000 +0100
+++ ./case_startb.c	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,21 @@
+#include "case.h"
+
+int case_startb(s,len,t)
+register char *s;
+unsigned int len;
+register char *t;
+{
+  register unsigned char x;
+  register unsigned char y;
+
+  for (;;) {
+    y = *t++ - 'A';
+    if (y <= 'Z' - 'A') y += 'a'; else y += 'A';
+    if (!y) return 1;
+    if (!len) return 0;
+    --len;
+    x = *s++ - 'A';
+    if (x <= 'Z' - 'A') x += 'a'; else x += 'A';
+    if (x != y) return 0;
+  }
+}
diff -urN ../qmail-1.03_unpatched/cdb_seek.c ./cdb_seek.c
--- ../qmail-1.03_unpatched/cdb_seek.c	1998-06-15 12:53:16.000000000 +0200
+++ ./cdb_seek.c	2005-01-21 19:25:03.000000000 +0100
@@ -1,6 +1,5 @@
 #include <sys/types.h>
 #include <errno.h>
-extern int errno;
 #include "cdb.h"
 
 #ifndef SEEK_SET
diff -urN ../qmail-1.03_unpatched/cdbmake_add.c ./cdbmake_add.c
--- ../qmail-1.03_unpatched/cdbmake_add.c	1998-06-15 12:53:16.000000000 +0200
+++ ./cdbmake_add.c	2005-01-21 19:25:03.000000000 +0100
@@ -1,3 +1,4 @@
+#include "alloc.h"
 #include "cdbmake.h"
 
 void cdbmake_init(cdbm)
diff -urN ../qmail-1.03_unpatched/chkuser.c ./chkuser.c
--- ../qmail-1.03_unpatched/chkuser.c	1970-01-01 01:00:00.000000000 +0100
+++ ./chkuser.c	2005-01-21 20:15:34.000000000 +0100
@@ -0,0 +1,1156 @@
+
+/*
+ *
+ * 'chkuser.c' v.2.0.8
+ * for qmail/netqmail > 1.0.3 and vpopmail > 5.3.x
+ *
+ * Author: Antonio Nati tonix@interazioni.it
+ * All rights on this software and
+ * the identifying words chkusr and chkuser kept by the author
+ *
+ * This software may be freely used, modified and distributed,
+ * but this lines must be kept in every original or derived version.
+ * Original author "Antonio Nati" and the web URL
+ * "http://www.interazioni.it/opensource"
+ * must be indicated in every related work or web page
+ *
+ */
+
+#include <pwd.h>
+
+/* required by vpopmail */
+#include <stdio.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "dns.h"
+#include "env.h"
+#include "ipme.h"
+#include "now.h"
+#include "open.h"
+#include "subfd.h"
+#include "substdio.h"
+#include "stralloc.h"
+
+#include "vpopmail.h"
+#include "vauth.h"
+#include "vpopmail_config.h"
+
+#include "chkuser.h"
+#include "chkuser_settings.h"
+
+#if defined _exit
+#undef _exit
+#endif
+
+extern void flush();
+extern void out (char *s);
+
+extern char *remotehost;
+extern char *remoteip;
+extern char *remoteinfo;
+extern char *relayclient;
+extern char *fakehelo;
+
+extern void die_nomem();
+
+#define DIE_NOMEM() die_nomem()
+
+#if defined CHKUSER_DEBUG
+
+#if defined CHKUSER_DEBUG_STDERR
+
+#define CHKUSER_DBG(a) write (STDERR_FILENO, a, strlen (a))
+#define CHKUSER_DBG_INT(a) { int x; char str[30]; sprintf (str, "%d", a); write (STDERR_FILENO, str, strlen (str));}
+
+#else
+
+#define CHKUSER_DBG(a) write (STDOUT_FILENO, a, strlen (a))
+#define CHKUSER_DBG_INT(a) { int x; char str[30]; sprintf (str, "%d", a); write (STDOUT_FILENO, str, strlen (str));}
+
+#endif
+#else
+
+#define CHKUSER_DBG(a) /* DBG dummy */
+#define CHKUSER_DBG_INT(a) /* DBG dummy */
+
+#endif
+
+static int INTRUSION_threshold_reached = 0;
+static int first_time_init_flag = 1;
+
+static int recipients = 0;
+static int wrong_recipients = 0;
+
+static stralloc user = {0};
+static stralloc domain = {0};
+static stralloc domain_path = {0};
+static stralloc tmp_path = {0};
+static stralloc alias_path = {0};
+
+#if defined CHKUSER_IDENTIFY_REMOTE_VARIABLE
+ static char *identify_remote;
+#endif
+
+#if defined CHKUSER_ENABLE_EXTENSIONS
+#define CHKUSER_ENABLE_USERS_EXTENSIONS
+#endif
+
+#if defined CHKUSER_ENABLE_LISTS
+#define CHKUSER_ENABLE_EZMLM_LISTS
+#endif
+
+#if defined CHKUSER_EXTENSION_DASH
+#define CHKUSER_USERS_DASH CHKUSER_EXTENSION_DASH
+#endif
+
+
+#if defined CHKUSER_ENABLE_VAUTH_OPEN
+ static int db_already_open = 0;
+#endif
+
+#if !defined CHKUSER_ALWAYS_ON && defined CHKUSER_STARTING_VARIABLE
+  static char *starting_string = 0;
+  static int starting_value = -1;
+#endif
+
+#if defined CHKUSER_RCPT_LIMIT_VARIABLE
+  static char *maxrcpt_string = 0;
+  static int maxrcpt_limit = 0;
+  static int maxrcpt_limit_reached = 0;
+#endif
+
+#if defined CHKUSER_WRONGRCPT_LIMIT_VARIABLE
+  static char *maxwrongrcpt_string = 0;
+  static int maxwrongrcpt_limit = 0;
+  static int maxwrongrcpt_limit_reached = 0;
+#endif
+
+#if defined CHKUSER_MBXQUOTA_VARIABLE
+  static char *maxmbxquota_string = 0;
+  static int maxmbxquota_limit = 0;
+#endif
+
+#if defined CHKUSER_SENDER_NOCHECK_VARIABLE
+
+  static unsigned int sender_nocheck = 0;
+
+#endif
+
+#if defined CHKUSER_SENDER_FORMAT || defined CHKUSER_SENDER_MX
+static stralloc sender_user = {0};
+static stralloc sender_domain = {0};
+#endif
+
+
+#if defined CHKUSER_ERROR_DELAY
+
+  static int chkuser_delay_interval = CHKUSER_ERROR_DELAY * 1000;
+
+#define CHKUSER_DELAY()	chkuser_delay()
+
+void chkuser_delay (void) {
+
+        usleep (chkuser_delay_interval);
+
+#if defined CHKUSER_ERROR_DELAY_INCREASE
+        chkuser_delay_interval += CHKUSER_ERROR_DELAY_INCREASE * 1000;
+#endif
+}
+
+#if defined CHKUSER_RCPT_DELAY_ANYERROR
+#define CHKUSER_RCPT_DELAY_ANY() chkuser_delay()
+#else
+#define CHKUSER_RCPT_DELAY_ANY() /* no delay for any error */
+#endif
+
+#if defined CHKUSER_SENDER_DELAY_ANYERROR
+#define CHKUSER_SENDER_DELAY_ANY() chkuser_delay()
+#else
+#define CHKUSER_SENDER_DELAY_ANY() /* no delay for any error */
+#endif
+
+
+#else
+#define CHKUSER_DELAY() /* no delay */
+#define CHKUSER_RCPT_DELAY_ANY() /* no delay */
+#define CHKUSER_SENDER_DELAY_ANY() /* no delay */
+#endif
+
+#if defined CHKUSER_ENABLE_LOGGING
+
+static stralloc logstr = { 0 };
+
+static void chkuser_commonlog (char *sender, char *rcpt, char *title, char *description) {
+
+  substdio_puts (subfderr, "CHKUSER ");
+  substdio_puts (subfderr, title);
+  substdio_puts (subfderr, ": from <");
+  substdio_puts (subfderr, sender);
+  substdio_puts (subfderr, ":" );
+  if (remoteinfo) {
+	substdio_puts (subfderr, remoteinfo);
+  }
+  substdio_puts (subfderr, ":" );
+#if defined CHKUSER_IDENTIFY_REMOTE_VARIABLE
+  if (identify_remote) substdio_puts (subfderr, identify_remote);
+#endif
+  substdio_puts (subfderr, "> remote <");
+  if (fakehelo) substdio_puts (subfderr, fakehelo);
+  substdio_puts (subfderr, ":" );
+  if (remotehost) substdio_puts (subfderr, remotehost);
+  substdio_puts (subfderr, ":" );
+  if (remoteip) substdio_puts (subfderr, remoteip);
+  substdio_puts (subfderr, "> rcpt <");
+  substdio_puts (subfderr, rcpt);
+  substdio_puts (subfderr, "> : ");
+  substdio_puts (subfderr, description);
+  substdio_puts (subfderr, "\n");
+  substdio_flush (subfderr);
+}
+
+#else
+#define chkuser_commonlog(a,b,c,d) /* no log */
+#endif
+
+#if defined CHKUSER_SENDER_FORMAT
+
+static int check_sender_address_format (stralloc *user, stralloc *domain) {
+
+        int x;
+
+        for (x = 0; x < (user->len -1); ++x) {
+                if ((!isalnum (user->s[x])) 
+
+#if defined CHKUSER_ALLOW_SENDER_SRS
+		&& (user->s[x] != '#')
+		&& (user->s[x] != '+')
+#endif
+#if defined CHKUSER_ALLOW_SENDER_CHAR_1
+		&& (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_1)
+#endif
+#if defined CHKUSER_ALLOW_SENDER_CHAR_2
+		&& (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_2)
+#endif
+#if defined CHKUSER_ALLOW_SENDER_CHAR_3
+		&& (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_3)
+#endif
+#if defined CHKUSER_ALLOW_SENDER_CHAR_4
+		&& (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_4)
+#endif
+#if defined CHKUSER_ALLOW_SENDER_CHAR_5
+		&& (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_5)
+#endif
+		&& (user->s[x] != '_') && (user->s[x] != '-') && (user->s[x] != '.') && (user->s[x] != '=')) {
+                        return 0;
+                }
+        }
+
+/*
+ * Be careful, this is a base check
+ *      Minimum is x.xx + ending \0
+ *      Minimum characters needed are 5
+ */
+#if defined CHKUSER_MIN_DOMAIN_LEN
+        if (domain->len < (CHKUSER_MIN_DOMAIN_LEN +1)) {
+                return 0;
+        }
+#endif
+
+/*
+ *      This is a safety check
+ */
+#if defined CHKUSER_MIN_DOMAIN_LEN
+        if (domain->len < 2) {
+                return 0;
+        }
+#endif
+
+        for (x = 0; x < (domain->len -1); ++x) {
+                if ((!isalnum (domain->s[x])) && (domain->s[x] != '-') && (domain->s[x] != '.')) {
+                        return 0;
+                }
+        }
+
+        if ((domain->s[0] == '-') || (domain->s[domain->len -2] == '-') || (domain->s[0] == '.') || (domain->s[domain->len -2] == '.')) {
+                return 0;
+        }
+        if (strstr (domain->s, "..") != NULL) {
+                return 0;
+        }
+	if (strncmp (domain->s, "xn--", 4) == 0) {
+		if (strstr (&domain->s[4], "--") != NULL)
+			return 0;
+	} else {
+		if (strstr (domain->s, "--") != NULL)
+			return 0;
+	}
+        if (strstr (domain->s, ".-") != NULL) {
+                return 0;
+        }
+        if (strstr (domain->s, "-.") != NULL) {
+                return 0;
+        }
+        if (strchr (domain->s, '.') == NULL) {
+                return 0;
+        }
+
+        return 1;
+}
+
+#endif
+
+#if defined CHKUSER_RCPT_FORMAT
+
+static int check_rcpt_address_format (stralloc *user, stralloc *domain) {
+
+        int x;
+
+        for (x = 0; x < (user->len -1); ++x) {
+                if ((!isalnum (user->s[x])) 
+#if defined CHKUSER_ALLOW_RCPT_SRS
+                && (user->s[x] != '#')
+                && (user->s[x] != '+')
+#endif
+#if defined CHKUSER_ALLOW_RCPT_CHAR_1
+                && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_1)
+#endif
+#if defined CHKUSER_ALLOW_RCPT_CHAR_2
+                && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_2)
+#endif
+#if defined CHKUSER_ALLOW_RCPT_CHAR_3
+                && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_3)
+#endif
+#if defined CHKUSER_ALLOW_RCPT_CHAR_4
+                && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_4)
+#endif
+#if defined CHKUSER_ALLOW_RCPT_CHAR_5
+                && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_5)
+#endif
+
+		&& (user->s[x] != '_') && (user->s[x] != '-') && (user->s[x] != '.') && (user->s[x] != '=')) {
+                        return 0;
+                }
+        }
+
+/*
+ * Be careful, this is a base check
+ *      Minimum is x.xx + ending \0
+ *      Minimum characters needed are 5
+ */
+#if defined CHKUSER_MIN_DOMAIN_LEN
+        if (domain->len < (CHKUSER_MIN_DOMAIN_LEN +1)) {
+                return 0;
+        }
+#endif
+
+/*
+ *      This is a safety check
+ */
+#if defined CHKUSER_MIN_DOMAIN_LEN
+        if (domain->len < 2) {
+                return 0;
+        }
+#endif
+        for (x = 0; x < (domain->len -1); ++x) {
+                if ((!isalnum (domain->s[x])) && (domain->s[x] != '-') && (domain->s[x] != '.')) {
+                        return 0;
+                }
+        }
+
+        if ((domain->s[0] == '-') || (domain->s[domain->len -2] == '-') || (domain->s[0] == '.') || (domain->s[domain->len -2] == '.')) {
+                return 0;
+        }
+        if (strstr (domain->s, "..") != NULL) {
+                return 0;
+        }
+	if (strncmp (domain->s, "xn--", 4) == 0) {
+		if (strstr (&domain->s[4], "--") != NULL)
+			return 0;
+	} else {
+		if (strstr (domain->s, "--") != NULL)
+			return 0;
+	}
+        if (strstr (domain->s, ".-") != NULL) {
+                return 0;
+        }
+        if (strstr (domain->s, "-.") != NULL) {
+                return 0;
+        }
+        if (strchr (domain->s, '.') == NULL) {
+                return 0;
+        }
+
+        return 1;
+}
+
+#endif
+
+#if defined CHKUSER_SENDER_MX || defined CHKUSER_RCPT_MX
+
+static   unsigned long mx_random;
+static  ipalloc mx_ip = {0};
+
+static int chkuser_mx_lookup (stralloc *domain) {
+
+  int status;
+
+	mx_random = now() + getpid();
+	dns_init(0);
+	status = dns_mxip (&mx_ip, domain, mx_random);
+
+	if (status == DNS_MEM) DIE_NOMEM();
+
+	return status;
+}
+
+#endif
+
+
+void chkuser_cleanup (int exit_value) {
+
+#if defined CHKUSER_DB_CLEANUP
+	vclose ();
+#endif
+	_exit (exit_value);
+}
+
+static void first_time_init (void) {
+
+  char * temp_string;
+
+#if !defined CHKUSER_ALWAYS_ON && defined CHKUSER_STARTING_VARIABLE
+        starting_string = env_get (CHKUSER_STARTING_VARIABLE);
+        if (starting_string) {
+                if (strcasecmp(starting_string, "ALWAYS") == 0) {
+                        starting_value = 1;
+                } else if (strcasecmp(starting_string, "DOMAIN") == 0) {
+                        starting_value = 0;
+                }
+        } else {
+                starting_string = "";
+        }
+#endif
+
+#if defined CHKUSER_RCPT_LIMIT_VARIABLE
+        maxrcpt_string = env_get (CHKUSER_RCPT_LIMIT_VARIABLE);
+        if (maxrcpt_string) {
+                maxrcpt_limit = atoi (maxrcpt_string);
+                if (maxrcpt_limit < 1) {
+                        maxrcpt_limit = 0;
+                }
+        } else {
+                maxrcpt_string = "";;
+        }
+#endif
+
+#if defined CHKUSER_WRONGRCPT_LIMIT_VARIABLE
+        maxwrongrcpt_string = env_get (CHKUSER_WRONGRCPT_LIMIT_VARIABLE);
+        if (maxwrongrcpt_string) {
+                maxwrongrcpt_limit = atoi (maxwrongrcpt_string);
+                if (maxwrongrcpt_limit < 1) {
+                        maxwrongrcpt_limit = 0;
+                }
+        } else {
+                maxwrongrcpt_string = "";
+        }
+#endif
+
+#if defined CHKUSER_MBXQUOTA_VARIABLE
+        maxmbxquota_string = env_get (CHKUSER_MBXQUOTA_VARIABLE);
+        if (maxmbxquota_string) {
+                maxmbxquota_limit = atoi (maxmbxquota_string);
+                if (maxmbxquota_limit < 1) {
+                	maxmbxquota_limit = 0;
+                }
+	} else {
+               	maxmbxquota_string = "";
+	}
+#endif
+
+#if defined CHKUSER_SENDER_NOCHECK_VARIABLE
+
+        temp_string = env_get (CHKUSER_SENDER_NOCHECK_VARIABLE);
+        if (temp_string) {
+		sender_nocheck = 1;
+        } else {
+		sender_nocheck = 0;
+        }
+
+#endif
+
+#if defined CHKUSER_IDENTIFY_REMOTE_VARIABLE
+
+        identify_remote = env_get (CHKUSER_IDENTIFY_REMOTE_VARIABLE);
+        if (identify_remote) {
+        }
+
+#endif
+
+        if (!stralloc_ready (&user, 300)) DIE_NOMEM();
+        if (!stralloc_ready (&domain, 500)) DIE_NOMEM();
+        if (!stralloc_ready (&domain_path, 1000)) DIE_NOMEM();
+        if (!stralloc_ready (&tmp_path, 1000)) DIE_NOMEM();
+        if (!stralloc_ready (&alias_path, 1000)) DIE_NOMEM();
+
+	first_time_init_flag = 0;
+
+}
+
+/*
+ * realrcpt ()
+ *
+ * Returns:
+ *
+ *	CHKUSER_OK = 1 = Ok, recipients does exists
+ *
+ *	0 = Not in rcpthosts
+ *
+ *	< 0 various errors
+ *
+ *
+ * Parameters:
+ *	stralloc *sender = sender address
+ *	stralloc *rcpt = rcpt address to check
+ *
+ *
+*/
+
+static int realrcpt (stralloc *sender, stralloc *rcpt)
+{
+  int count;
+  int retstat = CHKUSER_KO;
+  struct vqpasswd *user_passwd = NULL;
+  int fd_file = -1;
+  int read_char;
+  int offset;
+  char read_buf[1024];
+
+#if defined CHKUSER_ENABLE_UIDGID
+  uid_t eff_uid;
+  gid_t eff_gid;
+#endif
+
+#if !defined CHKUSER_ALWAYS_ON && defined CHKUSER_STARTING_VARIABLE
+  if (starting_value == -1) {
+                if (addrallowed()) {
+                        return CHKUSER_OK;
+                } else {
+                        if (relayclient) {
+                                return CHKUSER_RELAYING;
+                        }
+
+			return CHKUSER_NORCPTHOSTS;
+		}
+  }
+#endif
+
+  if (INTRUSION_threshold_reached == 1) {
+	return CHKUSER_ERR_INTRUSION_THRESHOLD;
+  }
+
+#if defined CHKUSER_RCPT_LIMIT_VARIABLE
+
+  ++recipients;
+  if ((maxrcpt_limit > 0) && (recipients >= maxrcpt_limit)) {
+	chkuser_commonlog (sender->s, rcpt->s, "intrusion threshold", "max number of allowed rcpt");
+	INTRUSION_threshold_reached = 1;
+        return CHKUSER_ERR_MAXRCPT;
+  }
+#endif
+
+/* Search the '@' character */
+  count = byte_rchr(rcpt->s,rcpt->len,'@');
+
+  if (count < rcpt->len) {
+    if (!stralloc_copyb (&user, rcpt->s, count)) DIE_NOMEM();
+    if (!stralloc_copys (&domain, rcpt->s + count + 1)) DIE_NOMEM();
+  }
+  else {
+    if (!stralloc_copys (&user, rcpt->s)) DIE_NOMEM();
+    domain.len = 0;
+  }
+  if (!stralloc_0 (&user)) DIE_NOMEM();
+  if (!stralloc_0 (&domain)) DIE_NOMEM();
+
+#if defined CHKUSER_ENABLE_UIDGID
+
+/* qmail-smtpd is running now as (effective) qmaild:nofiles */
+/* Save the effective UID & GID (qmaild:nofiles) */
+  eff_uid = geteuid ();
+  eff_gid = getegid ();
+
+/* Now set new effective UID & GID, getting it from real UID & GID (vpopmail:vchkpw) */
+  setegid (getgid());
+  seteuid (getuid());
+
+/* qmail-smtpd is running now as effective vpopmail:vchkpw */
+#endif
+
+
+/*
+ * 
+ * Now let's start the test/setting suite
+ *
+ **/
+
+	switch (0) {
+
+	case 0:
+/* These are some preliminary settings */
+  		case_lowers (user.s);
+  		case_lowers (domain.s);
+
+	case 1:
+
+                if (domain.len == 1) {
+#if defined CHKUSER_DOMAIN_WANTED
+                        retstat = CHKUSER_ERR_DOMAIN_MISSING;
+			break;
+#else
+                        if (!stralloc_copys (&domain, DEFAULT_DOMAIN)) DIE_NOMEM();
+  			if (!stralloc_0 (&domain)) DIE_NOMEM();
+#endif
+                }
+
+	case 2:
+
+#if defined CHKUSER_RCPT_FORMAT
+
+                if (check_rcpt_address_format (&user, &domain) == 0) {
+                        retstat = CHKUSER_ERR_RCPT_FORMAT;
+                        break;
+                }
+#endif
+
+	case 3:
+
+                if (!addrallowed()) {
+
+#if defined CHKUSER_RCPT_MX
+			switch (chkuser_mx_lookup(&domain)) {
+
+				case DNS_HARD:
+					retstat = CHKUSER_ERR_RCPT_MX;
+					break;
+
+				case DNS_SOFT:
+					retstat = CHKUSER_ERR_RCPT_MX_TMP;
+					break;
+			}
+
+			if (retstat != CHKUSER_KO) {
+				break;
+			}
+#endif
+
+  			if (relayclient) {
+				retstat = CHKUSER_RELAYING;
+				break;
+  			}
+
+                        retstat = CHKUSER_NORCPTHOSTS;
+                        break;
+                }
+
+	case 4:
+
+#if defined CHKUSER_ENABLE_VAUTH_OPEN
+                if (db_already_open != 1) {
+                        if (vauth_open () == 0) {
+                                db_already_open == 1;
+                        } else {
+                                retstat = CHKUSER_ERR_AUTH_RESOURCE;
+                        }
+                };
+#endif
+
+	case 5:
+
+#if defined CHKUSER_ENABLE_VGET_REAL_DOMAIN
+/* Check if domain is a real domain */
+
+                vget_real_domain(domain.s, domain.a);
+
+                domain.len = strlen (domain.s) +1;
+                if (domain.len > (domain.a - 1)) DIE_NOMEM();
+#endif
+
+/* Let's get domain's real path */
+                if (vget_assign(domain.s, domain_path.s, domain_path.a -1, NULL, NULL) == NULL) {
+			retstat = CHKUSER_OK;
+			break;
+		}
+	
+		domain_path.len = strlen (domain_path.s);
+
+	case 6:
+
+/* Check if domain has bouncing enabled */
+
+#if !defined CHKUSER_ALWAYS_ON
+
+#if defined CHKUSER_STARTING_VARIABLE
+		if (starting_value == 0) {
+#endif
+
+	                if (!stralloc_copy (&tmp_path, &domain_path)) DIE_NOMEM();
+
+#if defined CHKUSER_SPECIFIC_BOUNCING
+	  		if (!stralloc_cats (&tmp_path, "/")) DIE_NOMEM();
+	  		if (!stralloc_cats (&tmp_path, CHKUSER_SPECIFIC_BOUNCING)) DIE_NOMEM();
+			if (!stralloc_0 (&tmp_path)) DIE_NOMEM();
+	  		fd_file = open_read (tmp_path.s);	
+	  		if (fd_file != -1) {
+	      			close (fd_file);
+			} else {
+				retstat = CHKUSER_OK;
+				break;
+			}
+#else
+	  		if (!stralloc_cats (&tmp_path, "/.qmail-default")) DIE_NOMEM();
+			if (!stralloc_0 (&tmp_path)) DIE_NOMEM();
+
+	  		read_char = 0;
+	  		fd_file = open_read (tmp_path.s);	
+	  		if (fd_file != -1) {
+	      			read_char = read (fd_file, read_buf, sizeof(read_buf) - 1);
+	      			close (fd_file);
+	      			if (read_char < 0) read_char = 0;
+	  			}
+	  		read_buf[read_char] = 0;
+
+	  		if ( strstr(read_buf, CHKUSER_BOUNCE_STRING) == NULL ) {
+				retstat = CHKUSER_OK;
+				break;
+	  		}
+#endif
+#if defined CHKUSER_STARTING_VARIABLE
+		}
+#endif
+#endif
+
+	case 7:
+#if defined VALIAS
+/* Check for aliases/forwards - valias*/
+
+		if (valias_select (user.s, domain.s) != NULL) {
+			retstat = CHKUSER_OK;
+			break;
+		}
+#endif
+
+	case 8:
+#if defined CHKUSER_ENABLE_ALIAS
+/* Check for aliases/forwards - .qmail.x files */
+
+		if (!stralloc_copy (&tmp_path, &user)) DIE_NOMEM();
+                /* Change all '.' in ':' before continuing on aliases */
+                for (count = 0; count < tmp_path.len; ++count)
+        	        if (*(tmp_path.s + count) == '.') *(tmp_path.s + count) = ':';
+
+                if (!stralloc_copy (&alias_path, &domain_path)) DIE_NOMEM();
+                if (!stralloc_cats (&alias_path, "/.qmail-")) DIE_NOMEM();
+                if (!stralloc_cats (&alias_path, tmp_path.s)) DIE_NOMEM();
+                if (!stralloc_0 (&alias_path)) DIE_NOMEM();
+
+		fd_file = open_read (alias_path.s);
+		if (fd_file != -1) {
+			close (fd_file);
+			retstat = CHKUSER_OK;
+			break;
+		}
+#endif
+
+	case 9:
+
+#if defined CHKUSER_ENABLE_ALIAS_DEFAULT
+
+		if (!stralloc_copy (&tmp_path, &user)) DIE_NOMEM();
+                /* Change all '.' in ':' before continuing on aliases */
+                for (count = 0; count < tmp_path.len; ++count)
+        	        if (*(tmp_path.s + count) == '.') *(tmp_path.s + count) = ':';
+
+                /* Search for the outer '-' character */
+                for (offset = user.len - 1; offset > 0; --offset)
+                        if (*(user.s + offset) == CHKUSER_USERS_DASH)  {
+                                if (!stralloc_copy (&alias_path, &domain_path)) die_nomem();
+                                if (!stralloc_cats (&alias_path, "/.qmail-")) die_nomem();
+                                if (!stralloc_catb (&alias_path, user.s, offset)) die_nomem();
+                                if (!stralloc_cats (&alias_path, "-default")) die_nomem();
+                                if (!stralloc_0 (&alias_path)) die_nomem();
+
+                                fd_file = open_read (alias_path.s);
+                                if (fd_file != -1) {
+                                        close (fd_file);
+                                        retstat = CHKUSER_OK;
+                                        break;
+                                }
+                        }
+
+                if (retstat != CHKUSER_KO) {
+                        break;
+                }
+
+#endif
+
+        case 10:
+#if defined CHKUSER_ENABLE_USERS
+/* User control: check the existance of a real user */
+
+                user_passwd = vauth_getpw (user.s, domain.s);
+
+#if defined CHKUSER_ENABLE_USERS_EXTENSIONS
+                if (user_passwd == NULL) {
+                       count = 0;
+                       while ((count < (user.len -1)) && (user_passwd == NULL)) {
+                               count += byte_chr(&user.s[count], user.len - count, CHKUSER_USERS_DASH);
+                               if (count < user.len) {
+                                       if (!stralloc_copyb (&tmp_path, user.s, count)) DIE_NOMEM();
+                                       if (!stralloc_0 (&tmp_path)) DIE_NOMEM();
+                                       user_passwd = vauth_getpw (tmp_path.s, domain.s);
+                                         ++count;
+                               }
+                        }
+                }
+
+#endif
+                if (user_passwd != NULL) {
+
+                /* If user exists check if he has BOUNCE_MAIL flag set */
+
+                        if (user_passwd->pw_gid & BOUNCE_MAIL)
+                                retstat = CHKUSER_KO;
+                        else {
+                                retstat = CHKUSER_OK;
+#if defined CHKUSER_MBXQUOTA_VARIABLE
+                                if ((maxmbxquota_limit > 0) && (strcasecmp(user_passwd->pw_shell, "NOQUOTA") != 0)) {
+                                        if (!stralloc_copys (&tmp_path, user_passwd->pw_dir)) DIE_NOMEM();
+                                        if (!stralloc_cats (&tmp_path, "/Maildir")) DIE_NOMEM();
+                                        if (!stralloc_0 (&tmp_path)) DIE_NOMEM();
+
+                                        if (vmaildir_readquota(tmp_path.s,format_maildirquota(user_passwd->pw_shell))
+                                                >= maxmbxquota_limit) {
+                                                retstat = CHKUSER_ERR_MBXFULL;
+                                        }
+                                }
+#endif
+                        }
+                        break;
+                }
+#endif
+
+	case 11:
+#if defined CHKUSER_ENABLE_EZMLM_LISTS
+/* Let's check for mailing lists */
+
+		/* Search for the outer CHKUSER_EZMLM_DASH character */
+	      	for (offset = user.len - 2; offset > 0; --offset) {
+			if (*(user.s + offset) == CHKUSER_EZMLM_DASH)  {
+				if (!stralloc_copy (&tmp_path, &domain_path)) DIE_NOMEM();
+	      			if (!stralloc_cats (&tmp_path, "/")) DIE_NOMEM();
+	      			if (!stralloc_catb (&tmp_path, user.s, offset)) DIE_NOMEM();
+	      			if (!stralloc_cats (&tmp_path, "/mailinglist")) DIE_NOMEM();
+	      			if (!stralloc_0 (&tmp_path)) DIE_NOMEM();
+				fd_file = open_read (tmp_path.s);
+				if (fd_file != -1) {
+					close (fd_file);
+					retstat = CHKUSER_OK;
+					break;
+				}
+	        	}
+		}
+		if (retstat != CHKUSER_KO) {
+			break;
+		}
+#endif
+
+        case 12:
+#if defined CHKUSER_ENABLE_MAILMAN_LISTS
+/* Let's check for mailing lists */
+
+                /* Search for the outer CHKUSER_MAILMAN_DASH character */
+                for (offset = user.len - 2; offset > 0; --offset) {
+                        if (*(user.s + offset) == CHKUSER_MAILMAN_DASH)  {
+                                if (!stralloc_copy (&tmp_path, &domain_path)) DIE_NOMEM();
+                                if (!stralloc_cats (&tmp_path, "/")) DIE_NOMEM();
+				if (!stralloc_cats (&alias_path, "/.qmail-")) DIE_NOMEM();
+                                if (!stralloc_catb (&tmp_path, user.s, offset)) DIE_NOMEM();
+                                if (!stralloc_0 (&tmp_path)) DIE_NOMEM();
+                                fd_file = open_read (tmp_path.s);
+	                        read_char = 0;
+        	                if (fd_file != -1) {
+                	                read_char = read (fd_file, read_buf, sizeof(read_buf) - 1);
+                        	        close (fd_file);
+                                	if (read_char < 0) read_char = 0;
+                                }
+	                        read_buf[read_char] = 0;
+
+        	                if ( strstr(read_buf, CHKUSER_MAILMAN_STRING) == NULL ) {
+	                                retstat = CHKUSER_OK;
+	                                break;
+	                        }
+
+                        }
+                }
+                if (retstat != CHKUSER_KO) {
+                        break;
+                }
+#endif
+
+/*
+ * Add this code if another case is following
+	case xx:
+		code ....
+		code ....
+		code ....
+		code ....
+
+		if (xxxxxxxx) {
+			retstat != CHKUSER_KO)
+			break;
+		}
+*/
+	    
+        default:
+                retstat = CHKUSER_KO;
+
+	} /* end switch */
+
+#if defined CHKUSER_ENABLE_UIDGID
+/* Now switch back effective to saved UID & GID (qmaild:nofiles) */
+
+  setegid (eff_gid);
+  seteuid (eff_uid);
+
+/* qmail-smtpd is running again as (effective) qmaild:nofiles */
+#endif
+
+  return retstat;
+
+}
+
+
+
+/*
+ * chkuser_realrcpt ()
+ *
+ * Returns a simple status:
+ *
+ *      CHKUSER_OK = 1 = Ok, recipients does exists
+ *
+ *      CHKUSER_NORCPTHOSTS = Not in rcpthosts
+ *
+ *      CHKUSER_KO = ERROR
+ *
+ *
+ * Parameters:
+ *      stralloc *sender = sender address
+ *      stralloc *rcpt = rcpt address to check
+ *
+ *
+*/
+
+int chkuser_realrcpt (stralloc *sender, stralloc *rcpt) {
+
+int retstat;
+
+  if (first_time_init_flag) {
+        first_time_init ();
+  }
+
+  retstat = realrcpt (sender, rcpt);
+
+	switch (retstat) {
+
+		case CHKUSER_OK:
+#if defined CHKUSER_LOG_VALID_RCPT
+			chkuser_commonlog (sender->s, rcpt->s, "accepted rcpt", "found existing recipient");
+#endif
+			return CHKUSER_OK;
+			break;
+
+                case CHKUSER_RELAYING:
+#if defined CHKUSER_LOG_VALID_RCPT
+                        chkuser_commonlog (sender->s, rcpt->s, "relaying rcpt", "client allowed to relay");
+#endif
+                        return CHKUSER_RELAYING;
+                        break;
+
+		case CHKUSER_NORCPTHOSTS:
+                        chkuser_commonlog (sender->s, rcpt->s, "rejected relaying", "client not allowed to relay");
+		        CHKUSER_RCPT_DELAY_ANY();
+			out(CHKUSER_NORELAY_STRING);
+			break;
+
+		case CHKUSER_KO:
+			chkuser_commonlog (sender->s, rcpt->s, "rejected rcpt", "not existing recipient");
+		        CHKUSER_DELAY();
+ 			out(CHKUSER_NORCPT_STRING);
+			break;
+
+		case CHKUSER_ERR_AUTH_RESOURCE:
+			chkuser_commonlog (sender->s, rcpt->s, "no auth resource", "no auth resource available");
+		        CHKUSER_RCPT_DELAY_ANY();
+			out(CHKUSER_RESOURCE_STRING);
+			break;
+
+		case CHKUSER_ERR_MBXFULL:
+			chkuser_commonlog (sender->s, rcpt->s, "mbx overquota", "rcpt mailbox is overquota");
+		        CHKUSER_RCPT_DELAY_ANY();
+			out(CHKUSER_MBXFULL_STRING);
+			break;
+
+		case CHKUSER_ERR_MAXRCPT:
+			chkuser_commonlog (sender->s, rcpt->s, "rejected rcpt", "max number of recipients");
+		        CHKUSER_DELAY ();
+			out(CHKUSER_MAXRCPT_STRING);
+			break;
+
+		case CHKUSER_ERR_MAXWRONGRCPT:
+			chkuser_commonlog (sender->s, rcpt->s, "rejected rcpt", "max number of invalid recipients");
+		        CHKUSER_DELAY ();
+			out(CHKUSER_MAXWRONGRCPT_STRING);
+			break;
+
+		case CHKUSER_ERR_INTRUSION_THRESHOLD:
+			chkuser_commonlog (sender->s, rcpt->s, "rejected intrusion", "rcpt ignored, session over INTRUSION threshold");
+			CHKUSER_DELAY ();
+			out(CHKUSER_INTRUSIONTHRESHOLD_STRING);
+			break;
+
+		case CHKUSER_ERR_DOMAIN_MISSING:
+		        CHKUSER_DELAY ();
+			out(CHKUSER_DOMAINMISSING_STRING);
+			break;
+
+                case CHKUSER_ERR_RCPT_FORMAT:
+                        chkuser_commonlog (sender->s, rcpt->s, "rejected rcpt", "invalid rcpt address format");
+		        CHKUSER_RCPT_DELAY_ANY();
+			out(CHKUSER_RCPTFORMAT_STRING);
+                        break;
+
+                case CHKUSER_ERR_RCPT_MX:
+			chkuser_commonlog (sender->s, rcpt->s, "rejected rcpt", "invalid rcpt MX domain");
+		        CHKUSER_RCPT_DELAY_ANY();
+			out(CHKUSER_RCPTMX_STRING);
+                        break;
+
+                case CHKUSER_ERR_RCPT_MX_TMP:
+                        chkuser_commonlog (sender->s, rcpt->s, "rejected rcpt", "temporary DNS problem");
+                        CHKUSER_RCPT_DELAY_ANY();
+                        out(CHKUSER_RCPTMX_TMP_STRING);
+                        break;
+	}
+
+
+
+#if defined CHKUSER_WRONGRCPT_LIMIT_VARIABLE
+	if ((retstat == CHKUSER_KO) || (retstat == CHKUSER_ERR_DOMAIN_MISSING)) {
+        	++wrong_recipients;
+        	if ((INTRUSION_threshold_reached == 0) && (maxwrongrcpt_limit > 0) && (wrong_recipients >= maxwrongrcpt_limit)) {
+        	        chkuser_commonlog (sender->s, rcpt->s, "intrusion threshold", "max number of allowed invalid rcpt");
+        	        INTRUSION_threshold_reached = 1;
+        	}
+	}
+#endif
+
+	return CHKUSER_KO;
+}
+
+
+/*
+ *
+ * This routine checks for sender format and MX
+ *
+ */
+
+
+int chkuser_sender (stralloc *sender) {
+
+int count;
+
+	if (first_time_init_flag) {
+		first_time_init ();
+	}
+
+#if !defined CHKUSER_ALWAYS_ON && defined CHKUSER_STARTING_VARIABLE
+	if (starting_value == -1) {
+		return CHKUSER_OK;
+	}
+#endif
+
+#if defined CHKUSER_SENDER_FORMAT || defined CHKUSER_SENDER_MX
+
+#if defined CHKUSER_SENDER_NOCHECK_VARIABLE
+
+	if (sender_nocheck == 1) {
+		return CHKUSER_OK;
+	}
+#endif
+
+        if (sender->len <= 1) {
+#if defined CHKUSER_LOG_VALID_SENDER
+		chkuser_commonlog (sender->s, "", "accepted null sender", "accepted null sender always");
+#endif
+                return CHKUSER_OK;
+	}
+
+        count = byte_rchr(sender->s,sender->len,'@');
+        if (count < sender->len) {
+                if (!stralloc_copyb (&sender_user, sender->s, count)) DIE_NOMEM();
+                if (!stralloc_copys (&sender_domain, sender->s + count + 1)) DIE_NOMEM();
+        } else {
+                if (!stralloc_copys (&sender_user, sender->s)) DIE_NOMEM();
+                sender_domain.len = 0;
+        }
+        if (!stralloc_0 (&sender_user)) DIE_NOMEM();
+        if (!stralloc_0 (&sender_domain)) DIE_NOMEM();
+
+#if defined CHKUSER_SENDER_FORMAT
+        if (check_sender_address_format (&sender_user, &sender_domain) == 0) {
+                chkuser_commonlog (sender->s, "", "rejected sender", "invalid sender address format");
+		CHKUSER_SENDER_DELAY_ANY();
+		out(CHKUSER_SENDERFORMAT_STRING);
+	        return CHKUSER_ERR_SENDER_FORMAT;
+        }
+
+#endif
+
+#if defined CHKUSER_SENDER_MX
+
+	switch (chkuser_mx_lookup(&sender_domain)) {
+
+		case DNS_HARD:
+			CHKUSER_SENDER_DELAY_ANY();
+			out(CHKUSER_SENDERMX_STRING);
+			chkuser_commonlog (sender->s, "", "rejected sender", "invalid sender MX domain");
+			return CHKUSER_ERR_SENDER_MX;
+			break;
+
+		case DNS_SOFT:
+			CHKUSER_SENDER_DELAY_ANY();
+			out(CHKUSER_SENDERMX_TMP_STRING);
+			chkuser_commonlog (sender->s, "", "rejected sender", "temporary DNS problem");
+			return CHKUSER_ERR_SENDER_MX_TMP;
+			break;
+	}
+
+#if defined CHKUSER_LOG_VALID_SENDER
+                        chkuser_commonlog (sender->s, "", "accepted sender", "sender accepted");
+#endif
+
+	return CHKUSER_OK;
+#endif
+
+#else
+
+	return CHKUSER_OK;
+
+#endif
+
+}
+
+
diff -urN ../qmail-1.03_unpatched/chkuser.h ./chkuser.h
--- ../qmail-1.03_unpatched/chkuser.h	1970-01-01 01:00:00.000000000 +0100
+++ ./chkuser.h	2005-01-21 20:15:34.000000000 +0100
@@ -0,0 +1,51 @@
+
+/*
+ *
+ * 'chkuser.h' v.2.0.8
+ * for qmail/netqmail > 1.0.3 and vpopmail > 5.3.x
+ *
+ * Author: Antonio Nati tonix@interazioni.it
+ * All rights on this software and
+ * the identifying words chkusr and chkuser kept by the author
+ *
+ * This software may be freely used, modified and distributed,
+ * but this lines must be kept in every original or derived version.
+ * Original author "Antonio Nati" and the web URL
+ * "http://www.interazioni.it/opensource"
+ * must be indicated in every related work or web page
+ *
+ */
+
+#define CHKUSER
+#define CHKUSER_VERSION		"2.0.8"
+#define CHKUSER_VERSION_RL	2
+#define CHKUSER_VERSION_MJ	0
+#define CHKUSER_VERSION_MN	8
+
+#define CHKUSER_OK			1
+#define CHKUSER_RELAYING		0
+#define CHKUSER_KO			-1
+#define CHKUSER_NORCPTHOSTS		-10
+#define CHKUSER_ERR_AUTH_RESOURCE	-20
+#define CHKUSER_ERR_MBXFULL		-30
+#define CHKUSER_ERR_MAXRCPT		-40
+#define CHKUSER_ERR_MAXWRONGRCPT	-50
+#define CHKUSER_ERR_DOMAIN_MISSING	-60
+#define CHKUSER_ERR_RCPT_FORMAT		-70
+#define CHKUSER_ERR_RCPT_MX		-75
+#define CHKUSER_ERR_RCPT_MX_TMP		-76
+#define CHKUSER_ERR_SENDER_FORMAT	-80
+#define CHKUSER_ERR_SENDER_MX		-85
+#define CHKUSER_ERR_SENDER_MX_TMP	-86
+#define CHKUSER_ERR_INTRUSION_THRESHOLD	-90
+
+void chkuser_cleanup (int exit_value);
+int chkuser_realrcpt (stralloc *sender, stralloc *rcpt);
+int chkuser_sender (stralloc *sender);
+
+#ifdef TLS_H
+#undef _exit
+#define _exit(value) { if (ssl) ssl_free(ssl); chkuser_cleanup(value); }
+#else
+#define _exit(value) chkuser_cleanup(value);
+#endif
diff -urN ../qmail-1.03_unpatched/chkuser_settings.h ./chkuser_settings.h
--- ../qmail-1.03_unpatched/chkuser_settings.h	1970-01-01 01:00:00.000000000 +0100
+++ ./chkuser_settings.h	2005-01-21 20:13:03.000000000 +0100
@@ -0,0 +1,406 @@
+
+/*
+ *
+ * 'chkuser_settings.h' v.2.0.8b
+ * for qmail/netqmail > 1.0.3 and vpopmail > 5.3.x
+ *
+ * Author: Antonio Nati tonix@interazioni.it
+ * All rights on this software and
+ * the identifying words chkusr and chkuser kept by the author
+ *
+ * This software may be freely used, modified and distributed,
+ * but this lines must be kept in every original or derived version.
+ * Original author "Antonio Nati" and the web URL
+ * "http://www.interazioni.it/opensource"
+ * must be indicated in every related work or web page
+ *
+ */
+
+/********************************************************
+ * Debugging
+ ********************************************************/
+
+/*
+ * the following line enables debugging of chkuser
+ */
+/* #define CHKUSER_DEBUG */
+
+/*
+ * The following line moves DEBUG output from STDOUT (default) to STDERR
+ * Example of usage within sh: ./qmail-smtpd 2> /var/log/smtpd-debug.log
+ */
+/* #define CHKUSER_DEBUG_STDERR */
+
+
+/********************************************************
+ * Enabling chkuser
+ ********************************************************/
+
+/*
+ * Uncomment the following define if you want chkuser ALWAYS enabled.
+ * If uncommented, it will check for rcpt existance despite any .qmail-default
+ * setting.
+ * So, unsomments this if you are aware that ALL rcpt in all domains will be
+ * ALWAYS checked.
+ */
+/* #define CHKUSER_ALWAYS_ON */
+
+/*
+ * Uncomment the following line if you want chkuser to work depending on a VARIABLE setting
+ * VALUE HERE DEFINED is the name of the variable
+ * Values admitted inside the variable: NONE | ALWAYS | DOMAIN
+ *              NONE    = chkuser will not work
+ *              ALWAYS  = chkuser will work always
+ *              DOMAIN  = chkuser will work depending by single domain settings
+ * if CHKUSER_ALWAYS_ON is defined, this define is useless
+ * if CHKUSER_STARTING_VARIABLE is defined, and no variable or no value is set, then chkuser is disabled
+ */
+/* #define CHKUSER_STARTING_VARIABLE "CHKUSER_START" */
+
+/*
+ * Uncomment this to check for vpopmail users
+ */
+#define CHKUSER_ENABLE_USERS
+
+/*
+ * Uncomment this to enable user extension on names (i.e. TMDA)
+ * (for mailing lists this is done without checking this define)
+ * This define substitutes #define CHKUSER_ENABLE_EXTENSIONS
+ */
+#define CHKUSER_ENABLE_USERS_EXTENSIONS
+
+/*
+ * The following #define set the character used for users extensions
+ * be careful: this is a  single char '-' definition, not a "string"
+ * this define substitutes #define CHKUSER_EXTENSION_DASH
+ * MUST be defined if CHKUSER_ENABLE_USERS_EXTENSIONS is defined
+ */
+#define CHKUSER_USERS_DASH '-'
+
+/*
+ * Uncomment this to check for alias
+ */
+#define CHKUSER_ENABLE_ALIAS
+
+/*
+ * This define has been eliminated.
+ * Turning it ON or OFF has no effect, as we consider the existence
+ * of #define VALIAS inside ~vpopmail/include/vpopmail_config.h
+ */
+/* #define CHKUSER_ENABLE_VALIAS */
+
+/*
+ * Enables checking for EZMLM lists
+ * this define substitutes #define CHKUSER_ENABLE_LISTS
+ *
+ */
+#define CHKUSER_ENABLE_EZMLM_LISTS
+
+/*
+ * The following #define set the character used for lists extensions
+ * be careful: this is a  single char '-' definition, not a "string"
+ */
+#define CHKUSER_EZMLM_DASH '-'
+
+/* 
+ * Enables checking for mailman lists
+ *
+ */
+/* #define CHKUSER_ENABLE_MAILMAN_LISTS */
+
+/* 
+ * Identifies the pattern string to be searched within mailman aliases
+ *
+ */
+#define CHKUSER_MAILMAN_STRING "mailman"
+
+/* 
+ * The following #define set the character used for mailman lists extensions
+ * be careful: this is a  single char '-' definition, not a "string"
+ */
+#define CHKUSER_MAILMAN_DASH '-'
+
+/*
+ * Uncomment this to set an alternative way to check for bouncing enabling;
+ * with this option enabled, the file here defined
+ * will be searched, inside the domain dir, in order to check if bouncing is enabled
+ * The content of this file is not important, just it's existence is enough
+ */
+/* #define CHKUSER_SPECIFIC_BOUNCING ".qmailchkuser-bouncing" */
+
+/*
+ * This is the string to look for inside .qmail-default
+ * Be careful, chkuser looks within the first 1023 characters of .qmail-default for
+ * this string (despite the line containing the string is working or commented).
+ */
+#define CHKUSER_BOUNCE_STRING "bounce-no-mailbox"
+
+/*
+ * Uncomment next define to accept recipients for
+ * aliases that have a -default extension
+ */
+#define CHKUSER_ENABLE_ALIAS_DEFAULT
+
+
+/********************************************************
+ * Vpopmail depepnding features
+ ********************************************************/
+
+/*
+ * Before version 5.3.25, vpopmail used the function vget_real_domain()
+ * to get the real name of a domain (useful if rcpt domain is aliasing
+ * another domain).
+ * From version 5.3.25, this call is not available and has been
+ * substituted by other calls.
+ *
+ *        must be enabled if vpopmail version< 5.3.5
+ *        must be disabled  if vpopmail version => 5.3.5 *
+ */
+/* #define CHKUSER_ENABLE_VGET_REAL_DOMAIN */
+
+
+/*
+ * This is to enable auth open checking 
+ * it is useful to avoid bouncing if MySQL/LDAP/PostGRES/etc are down or not reachable
+ */
+/* #define CHKUSER_ENABLE_VAUTH_OPEN */
+
+/*
+ * The following defines which virtual manager is used.
+ * Up to know, only vpopmail, but versions with pure qmail are in the mind.
+ */
+#define CHKUSER_VPOPMAIL
+
+/*
+ * Enables final clean-up routine of chkuser
+ * This routine cleans open DB connections used for checking users and valiases
+ */
+/* #define CHKUSER_DB_CLEANUP */
+
+
+/********************************************************
+ * UID/GID switching
+ ********************************************************/
+
+/*
+ * Uncomment this to enable uid/gid changing
+ * (switching UID/GID is NOT compatible with TLS; you may keep this commented if you have TLS)
+ */
+/* #define CHKUSER_ENABLE_UIDGID */
+
+
+/********************************************************
+ * Checking addresses format & MX
+ ********************************************************/
+
+/*
+ * Uncomment this to check if a domain is ALWAYS specified in rcpt addresses
+ */
+/* #define CHKUSER_DOMAIN_WANTED */
+
+/* 
+ * Uncomment to enable checking of user and domain format for rcpt addresses
+ *      user    =       [a-z0-9_-]
+ *      domain  =       [a-z0-9-.] with not consecutive "-.", not leading or end
+ing "-."
+ */
+/* #define CHKUSER_RCPT_FORMAT */
+
+/*
+ * Uncomment to enable checking of domain MX for rcpt addresses
+ * It works on any rcpt address domain that is not inside rcpthosts
+ */
+/* #define CHKUSER_RCPT_MX */
+
+/*
+ * Uncomment to enable checking of domain MX for sender address
+ * it works on the first rcpt address, despite of any domain setting on chkuser
+ */
+#define CHKUSER_SENDER_MX
+
+
+/*
+ * Uncomment to enable usage of a variable escluding any check on the sender.
+ * The variable should be set in tcp.smtp for clients, with static IP, whose mailer
+ * is composing bad sender addresses
+ */
+#define CHKUSER_SENDER_NOCHECK_VARIABLE "SENDER_NOCHECK"
+
+/*
+ * Uncomment to enable checking of user and domain format for sender address
+ *      user    =       [a-z0-9_-]
+ *      domain  =       [a-z0-9-.] with not consecutive "-.", not leading or ending "-."
+ */
+/* #define CHKUSER_SENDER_FORMAT */
+
+
+/*
+ * Uncomment to enable usage of "#" and "+" characters within sender address
+ * This is used by SRS (Sender Rewriting Scheme) products
+ */
+/* #define CHKUSER_ALLOW_SENDER_SRS */
+ 
+/*
+ * If you need more additional characters to be accepted within sender address
+ * uncomment one of the following #define and edit the character value.
+ * Be careful to use '*' (single hiphen) and NOT "*" (double hiphen) around the
+ * wanted char.
+ */
+/* #define CHKUSER_ALLOW_SENDER_CHAR_1 '$' */
+/* #define CHKUSER_ALLOW_SENDER_CHAR_2 '%' */
+/* #define CHKUSER_ALLOW_SENDER_CHAR_3 '£' */
+/* #define CHKUSER_ALLOW_SENDER_CHAR_4 '?' */
+/* #define CHKUSER_ALLOW_SENDER_CHAR_5 '*' */
+
+/*
+ * Uncomment to enable usage of "#" and "+" characters within rcpt address
+ * This is used by SRS (Sender Rewriting Scheme) products
+ */
+/* #define CHKUSER_ALLOW_RCPT_SRS */
+
+/*
+ * If you need more additional characters to be accepted within rcpt address
+ * uncomment one of the following #define and edit the character value.
+ * Be careful to use '*' (single hiphen) and NOT "*" (double hiphen) around the
+ * wanted char.
+ */
+/* #define CHKUSER_ALLOW_RCPT_CHAR_1 '$' */
+/* #define CHKUSER_ALLOW_RCPT_CHAR_2 '%' */
+/* #define CHKUSER_ALLOW_RCPT_CHAR_3 '£' */
+/* #define CHKUSER_ALLOW_RCPT_CHAR_4 '?' */
+/* #define CHKUSER_ALLOW_RCPT_CHAR_5 '*' */
+
+/*
+ * The following #define sets the minimum length of a domain:
+ * as far as I know, "k.st" is the shortest domain, so 4 characters is the
+ * minimum length.
+ * This value is used to check formally a domain name validity.
+ * if CHKUSER_SENDER_FORMAT is undefined, no check on length is done.
+ * If you comment this define, no check on length is done.
+ */
+/* #define CHKUSER_MIN_DOMAIN_LEN 4 */
+
+/*
+ * Uncomment next define to accept always a null sender, despite of
+ * other settings
+ * NOT USED!!!
+ */
+/* #define CHKUSER_ACCEPT_NULL_SENDER */
+
+/*
+ * Default commented from 2.0.7
+ * Uncomment to accept <> senders from hosts which have name in DNS (TCPREMOTE
+ * (should be set, otherwise will not accept any bouncing message)
+ * This define will not work if CHKUSER_ACCEPT_NULL_SENDER is defined
+ * NOT USED!!!
+ */
+/* #define CHKUSER_ENABLE_NULL_SENDER_WITH_TCPREMOTEHOST */
+
+
+/********************************************************
+ * Logging
+ ********************************************************/
+
+/*
+ * Uncomment to enable logging of rejected recipients and variuos limits reached
+ */
+#define CHKUSER_ENABLE_LOGGING
+
+/*
+ * Uncomment to enable logging of "good" rcpts
+ * valid only if CHKUSER_ENABLE_LOGGING is defined
+ */
+/* #define CHKUSER_LOG_VALID_RCPT */
+
+/*
+ * Uncomment to enable logging of "good" senders
+ * valid only if CHKUSER_ENABLE_LOGGING is defined
+ */
+/* #define CHKUSER_LOG_VALID_SENDER */
+
+/*
+ * Help identifying remote authorized IPs giving them a descriptive name
+ * Can be put in tcp.smtp, and will be displayed inside chkuser log
+ * Substitutes RELAYCLIENT in chkuser logging
+ */
+#define CHKUSER_IDENTIFY_REMOTE_VARIABLE "CHKUSER_IDENTIFY"
+
+
+/********************************************************
+ * Tarpitting
+ ********************************************************/
+
+/*
+ * Uncomment to define a variable which contains the max recipients number
+ * this will return always error if total recipients exceed this limit.
+ * The first reached, between CHKUSER_RCPT_LIMIT_VARIABLE and CHKUSER_WRONGRCPT_LIMIT_VARIABLE,
+ * makes chkuser rejecting everything else
+ */
+#define CHKUSER_RCPT_LIMIT_VARIABLE "CHKUSER_RCPTLIMIT"
+
+/*
+ * Uncomment to define a variable which contains the max unknown recipients number
+ * this will return always error if not existing recipients exceed this limit.
+ * The first reached, between CHKUSER_RCPT_LIMIT_VARIABLE and CHKUSER_WRONGRCPT_LIMIT_VARIABLE,
+ * makes chkuser rejecting everything else
+ */
+#define CHKUSER_WRONGRCPT_LIMIT_VARIABLE "CHKUSER_WRONGRCPTLIMIT"
+
+/*
+ * Delay to wait for each not existing recipient
+ * value is expressed in milliseconds
+ */
+#define CHKUSER_ERROR_DELAY 1000
+
+/*
+ * Delay to add, for each not existing recipient, to the initial CHKUSER_ERROR_DELAY value
+ * value is expressed in milliseconds
+ */
+#define CHKUSER_ERROR_DELAY_INCREASE 500
+
+/*
+ * Uncomment to consider rcpt errors on address format and MX as intrusive
+ *
+ */
+#define CHKUSER_RCPT_DELAY_ANYERROR
+
+/*
+ * Uncomment to consider sender errors on address format and MX as intrusive
+ *
+ */
+#define CHKUSER_SENDER_DELAY_ANYERROR
+
+/********************************************************
+ * Quota Checking 
+ ********************************************************/
+
+/*
+ * Uncomment to define the variable containing the percent to check for.
+ * Remember to define externally (i.e. in tcp.smtp) the environment variable containing
+ * the limit percent.
+ * If the variable is not defined, or it is <= 0, quota checking is not performed.
+ */
+/* #define CHKUSER_MBXQUOTA_VARIABLE "CHKUSER_MBXQUOTA" */
+
+
+/********************************************************
+ * Error strings         
+ ********************************************************/
+
+#define CHKUSER_NORCPT_STRING "511 sorry, no mailbox here by that name (#5.1.1 - chkuser)\r\n"
+#define CHKUSER_RESOURCE_STRING "430 system temporary unavailable, try again later (#4.3.0 - chkuser)\r\n"
+#define CHKUSER_MBXFULL_STRING "522 sorry, recipient mailbox is full (#5.2.2 - chkuser)\r\n"
+#define CHKUSER_MAXRCPT_STRING "571 sorry, reached maximum number of recipients for one session (#5.7.1 - chkuser)\r\n"
+#define CHKUSER_MAXWRONGRCPT_STRING "571 sorry, you are violating our security policies (#5.1.1 - chkuser)\r\n"
+#define CHKUSER_DOMAINMISSING_STRING "511 sorry, you must specify a domain (#5.1.1 - chkuser)\r\n"
+#define CHKUSER_RCPTFORMAT_STRING "511 sorry, recipient address has invalid format (#5.1.1 - chkuser)\r\n"
+#define CHKUSER_RCPTMX_STRING "511 sorry, can't find a valid MX for rcpt domain (#5.1.1 - chkuser)\r\n"
+#define CHKUSER_SENDERFORMAT_STRING "571 sorry, sender address has invalid format (#5.7.1 - chkuser)\r\n"
+#define CHKUSER_SENDERMX_STRING "511 sorry, can't find a valid MX for sender domain (#5.1.1 - chkuser)\r\n"
+#define CHKUSER_INTRUSIONTHRESHOLD_STRING "571 sorry, you are violating our security policies (#5.7.1 - chkuser)\r\n"
+#define CHKUSER_NORELAY_STRING "553 sorry, that domain isn't in my list of allowed rcpthosts (#5.5.3 - chkuser)\r\n"
+
+#define CHKUSER_RCPTMX_TMP_STRING "451 DNS temporary failure (#4.5.1 - chkuser)\r\n"
+#define CHKUSER_SENDERMX_TMP_STRING "451 DNS temporary failure (#4.5.1 - chkuser)\r\n"
+
+
diff -urN ../qmail-1.03_unpatched/conf-cc ./conf-cc
--- ../qmail-1.03_unpatched/conf-cc	1998-06-15 12:53:16.000000000 +0200
+++ ./conf-cc	2005-01-21 19:25:03.000000000 +0100
@@ -1,3 +1,3 @@
-cc -O2
+cc -O2 -DTLS=20040120 -I/usr/local/ssl/include -I/home/vpopmail/include
 
 This will be used to compile .c files.
diff -urN ../qmail-1.03_unpatched/dns.c ./dns.c
--- ../qmail-1.03_unpatched/dns.c	1998-06-15 12:53:16.000000000 +0200
+++ ./dns.c	2005-01-21 19:25:03.000000000 +0100
@@ -7,8 +7,6 @@
 #include <errno.h>
 extern int res_query();
 extern int res_search();
-extern int errno;
-extern int h_errno;
 #include "ip.h"
 #include "ipalloc.h"
 #include "fmt.h"
@@ -21,10 +19,12 @@
 static unsigned short getshort(c) unsigned char *c;
 { unsigned short u; u = c[0]; return (u << 8) + c[1]; }
 
-static union { HEADER hdr; unsigned char buf[PACKETSZ]; } response;
+static struct { unsigned char *buf; } response;
+static int responsebuflen = 0;
 static int responselen;
 static unsigned char *responseend;
 static unsigned char *responsepos;
+static u_long saveresoptions;
 
 static int numanswers;
 static char name[MAXDNAME];
@@ -45,18 +45,33 @@
  errno = 0;
  if (!stralloc_copy(&glue,domain)) return DNS_MEM;
  if (!stralloc_0(&glue)) return DNS_MEM;
- responselen = lookup(glue.s,C_IN,type,response.buf,sizeof(response));
+ if (!responsebuflen)
+  if (response.buf = (unsigned char *)alloc(PACKETSZ+1))
+   responsebuflen = PACKETSZ+1;
+  else return DNS_MEM;
+
+ responselen = lookup(glue.s,C_IN,type,response.buf,responsebuflen);
+ if ((responselen >= responsebuflen) ||
+     (responselen > 0 && (((HEADER *)response.buf)->tc)))
+  {
+   if (responsebuflen < 65536)
+    if (alloc_re(&response.buf, responsebuflen, 65536))
+     responsebuflen = 65536;
+    else return DNS_MEM;
+    saveresoptions = _res.options;
+    _res.options |= RES_USEVC;
+    responselen = lookup(glue.s,C_IN,type,response.buf,responsebuflen);
+    _res.options = saveresoptions;
+  }
  if (responselen <= 0)
   {
    if (errno == ECONNREFUSED) return DNS_SOFT;
    if (h_errno == TRY_AGAIN) return DNS_SOFT;
    return DNS_HARD;
   }
- if (responselen >= sizeof(response))
-   responselen = sizeof(response);
  responseend = response.buf + responselen;
  responsepos = response.buf + sizeof(HEADER);
- n = ntohs(response.hdr.qdcount);
+ n = ntohs(((HEADER *)response.buf)->qdcount);
  while (n-- > 0)
   {
    i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME);
@@ -66,7 +81,7 @@
    if (i < QFIXEDSZ) return DNS_SOFT;
    responsepos += QFIXEDSZ;
   }
- numanswers = ntohs(response.hdr.ancount);
+ numanswers = ntohs(((HEADER *)response.buf)->ancount);
  return 0;
 }
 
@@ -269,12 +284,11 @@
 int pref;
 {
  int r;
- struct ip_mx ix;
+ struct ip_mx ix = {0};
 
  if (!stralloc_copy(&glue,sa)) return DNS_MEM;
  if (!stralloc_0(&glue)) return DNS_MEM;
  if (glue.s[0]) {
-   ix.pref = 0;
    if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)])
     {
      if (!ipalloc_append(ia,&ix)) return DNS_MEM;
@@ -293,9 +307,16 @@
    ix.ip = ip;
    ix.pref = pref;
    if (r == DNS_SOFT) return DNS_SOFT;
-   if (r == 1)
+   if (r == 1) {
+#ifdef IX_FQDN
+     ix.fqdn = glue.s;
+#endif
      if (!ipalloc_append(ia,&ix)) return DNS_MEM;
   }
+  }
+#ifdef IX_FQDN
+ glue.s = 0;
+#endif
  return 0;
 }
 
@@ -315,7 +336,7 @@
 {
  int r;
  struct mx { stralloc sa; unsigned short p; } *mx;
- struct ip_mx ix;
+ struct ip_mx ix = {0};
  int nummx;
  int i;
  int j;
@@ -327,7 +348,6 @@
  if (!stralloc_copy(&glue,sa)) return DNS_MEM;
  if (!stralloc_0(&glue)) return DNS_MEM;
  if (glue.s[0]) {
-   ix.pref = 0;
    if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)])
     {
      if (!ipalloc_append(ia,&ix)) return DNS_MEM;
diff -urN ../qmail-1.03_unpatched/error.3 ./error.3
--- ../qmail-1.03_unpatched/error.3	1998-06-15 12:53:16.000000000 +0200
+++ ./error.3	2005-01-21 19:25:03.000000000 +0100
@@ -3,8 +3,7 @@
 error \- syscall error codes
 .SH SYNTAX
 .B #include <error.h>
-
-extern int \fBerrno\fP;
+.B #include <errno.h>
 
 extern int \fBerror_intr\fP;
 .br
diff -urN ../qmail-1.03_unpatched/error.h ./error.h
--- ../qmail-1.03_unpatched/error.h	1998-06-15 12:53:16.000000000 +0200
+++ ./error.h	2005-01-21 19:25:03.000000000 +0100
@@ -1,7 +1,7 @@
 #ifndef ERROR_H
 #define ERROR_H
 
-extern int errno;
+#include <errno.h>
 
 extern int error_intr;
 extern int error_nomem;
diff -urN ../qmail-1.03_unpatched/hier.c ./hier.c
--- ../qmail-1.03_unpatched/hier.c	1998-06-15 12:53:16.000000000 +0200
+++ ./hier.c	2005-01-21 19:25:03.000000000 +0100
@@ -143,6 +143,9 @@
   c(auto_qmail,"bin","qail",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","elq",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","pinq",auto_uido,auto_gidq,0755);
+#ifdef TLS
+  c(auto_qmail,"bin","update_tmprsadh",auto_uido,auto_gidq,0755);
+#endif
 
   c(auto_qmail,"man/man5","addresses.5",auto_uido,auto_gidq,0644);
   c(auto_qmail,"man/cat5","addresses.0",auto_uido,auto_gidq,0644);
diff -urN ../qmail-1.03_unpatched/ipalloc.h ./ipalloc.h
--- ../qmail-1.03_unpatched/ipalloc.h	1998-06-15 12:53:16.000000000 +0200
+++ ./ipalloc.h	2005-01-21 19:25:03.000000000 +0100
@@ -3,7 +3,15 @@
 
 #include "ip.h"
 
+#ifdef TLS
+# define IX_FQDN 1
+#endif
+
+#ifdef IX_FQDN
+struct ip_mx { struct ip_address ip; int pref; char *fqdn; } ;
+#else
 struct ip_mx { struct ip_address ip; int pref; } ;
+#endif
 
 #include "gen_alloc.h"
 
diff -urN ../qmail-1.03_unpatched/ipme.c ./ipme.c
--- ../qmail-1.03_unpatched/ipme.c	1998-06-15 12:53:16.000000000 +0200
+++ ./ipme.c	2005-01-21 19:25:03.000000000 +0100
@@ -46,6 +46,11 @@
   ipme.len = 0;
   ix.pref = 0;
  
+  /* 0.0.0.0 is a special address which always refers to 
+   * "this host, this network", according to RFC 1122, Sec. 3.2.1.3a.
+  */
+  byte_copy(&ix.ip,4,"\0\0\0\0");
+  if (!ipalloc_append(&ipme,&ix)) { return 0; }
   if ((s = socket(AF_INET,SOCK_STREAM,0)) == -1) return -1;
  
   len = 256;
diff -urN ../qmail-1.03_unpatched/maildirflags.c ./maildirflags.c
--- ../qmail-1.03_unpatched/maildirflags.c	1970-01-01 01:00:00.000000000 +0100
+++ ./maildirflags.c	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,23 @@
+/*
+** Copyright 2000 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#include	<sys/types.h>
+#include	<string.h>
+
+static const char rcsid[]="$Id: maildirflags.c,v 1.1 2000/10/07 01:10:19 mrsam Exp $";
+
+int maildir_hasflag(const char *filename, char flag)
+{
+	const char *p=strrchr(filename, '/');
+
+	if (p)
+		filename=p+1;
+
+	p=strrchr(p, ':');
+	if (p && strncmp(p, ":2,", 3) == 0 &&
+	    strchr(p+3, flag))
+		return (1);
+	return (0);
+}
diff -urN ../qmail-1.03_unpatched/maildirgetquota.c ./maildirgetquota.c
--- ../qmail-1.03_unpatched/maildirgetquota.c	1970-01-01 01:00:00.000000000 +0100
+++ ./maildirgetquota.c	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,50 @@
+/*
+** Copyright 1998 - 2000 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#include	"maildirgetquota.h"
+#include	"maildirmisc.h"
+#if	HAVE_UNISTD_H
+#include	<unistd.h>
+#endif
+#include	<stdlib.h>
+#include	<string.h>
+#include	<fcntl.h>
+#include	<sys/types.h>
+#include	<sys/stat.h>
+
+int	maildir_getquota(const char *dir, char buf[QUOTABUFSIZE])
+{
+char	*p;
+struct	stat	stat_buf;
+int	n;
+int	l;
+
+	p=(char *)malloc(strlen(dir)+sizeof("/maildirfolder"));
+	if (!p)	return (-1);
+
+	strcat(strcpy(p, dir), "/maildirfolder");
+	if (stat(p, &stat_buf) == 0)
+	{
+		strcat(strcpy(p, dir), "/..");
+		n=maildir_getquota(p, buf);
+		free(p);
+		return (n);
+	}
+
+	strcat(strcpy(p, dir), "/maildirsize");
+	n=maildir_safeopen(p, O_RDONLY, 0);
+	free(p);
+	if (n < 0)	return (n);
+	if ((l=read(n, buf, QUOTABUFSIZE-1)) < 0)
+	{
+		close(n);
+		return (-1);
+	}
+	close(n);
+	for (n=0; n<l; n++)
+		if (buf[n] == '\n')	break;
+	buf[n]=0;
+	return (0);
+}
diff -urN ../qmail-1.03_unpatched/maildirgetquota.h ./maildirgetquota.h
--- ../qmail-1.03_unpatched/maildirgetquota.h	1970-01-01 01:00:00.000000000 +0100
+++ ./maildirgetquota.h	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,30 @@
+#ifndef	maildirgetquota_h
+#define	maildirgetquota_h
+
+/*
+** Copyright 1998 - 1999 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#if	HAVE_CONFIG_H
+#include	"config.h"
+#endif
+
+#include	<sys/types.h>
+#include	<stdio.h>
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+static const char maildirgetquota_h_rcsid[]="$Id: maildirgetquota.h,v 1.5 1999/12/06 13:21:05 mrsam Exp $";
+
+#define	QUOTABUFSIZE	256
+
+int maildir_getquota(const char *, char [QUOTABUFSIZE]);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif
diff -urN ../qmail-1.03_unpatched/maildirmisc.h ./maildirmisc.h
--- ../qmail-1.03_unpatched/maildirmisc.h	1970-01-01 01:00:00.000000000 +0100
+++ ./maildirmisc.h	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,145 @@
+#ifndef	maildirmisc_h
+#define	maildirmisc_h
+
+/*
+** Copyright 2000 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#if	HAVE_CONFIG_H
+#include	"config.h"
+#endif
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+static const char maildirmisc_h_rcsid[]="$Id: maildirmisc.h,v 1.8 2000/12/25 17:33:06 mrsam Exp $";
+
+/*
+**
+** Miscellaneous maildir-related code
+**
+*/
+
+/* Some special folders */
+
+#define	INBOX	"INBOX"
+#define	DRAFTS	"Drafts"
+#define	SENT	"Sent"
+#define	TRASH	"Trash"
+
+#define	SHAREDSUBDIR	"shared-folders"
+
+char *maildir_folderdir(const char *,		/* maildir */
+	const char *);				/* folder name */
+	/* Returns the directory corresponding to foldername (foldername is
+	** checked to make sure that it's a valid name, else we set errno
+	** to EINVAL, and return (0).
+	*/
+
+char *maildir_filename(const char *,		/* maildir */
+	const char *,				/* folder */
+	const char *);				/* filename */
+	/*
+	** Builds the filename to this message, suitable for opening.
+	** If the file doesn't appear to be there, search the maildir to
+	** see if someone changed the flags, and return the current filename.
+	*/
+
+int maildir_safeopen(const char *,		/* filename */
+	int,				/* mode */
+	int);				/* perm */
+
+/*
+**	Same arguments as open().  When we're accessing a shared maildir,
+**	prevent someone from playing cute and dumping a bunch of symlinks
+**	in there.  This function will open the indicate file only if the
+**	last component is not a symlink.
+**	This is implemented by opening the file with O_NONBLOCK (to prevent
+**	a DOS attack of someone pointing the symlink to a pipe, causing
+**	the open to hang), clearing O_NONBLOCK, then stat-int the file
+**	descriptor, lstating the filename, and making sure that dev/ino
+**	match.
+*/
+
+int maildir_semisafeopen(const char *,	/* filename */
+	int,				/* mode */
+	int);				/* perm */
+
+/*
+** Same thing, except that we allow ONE level of soft link indirection,
+** because we're reading from our own maildir, which points to the
+** message in the sharable maildir.
+*/
+
+int maildir_mkdir(const char *);	/* directory */
+/*
+** Create maildir including all subdirectories in the path (like mkdir -p)
+*/
+
+void maildir_purgetmp(const char *);		/* maildir */
+	/* purges old stuff out of tmp */
+
+void maildir_purge(const char *,		/* directory */
+	unsigned);				/* time_t to purge */
+
+void maildir_getnew(const char *,		/* maildir */
+	const char *);				/* folder */
+	/* move messages from new to cur */
+
+int maildir_deletefolder(const char *,		/* maildir */
+	const char *);				/* folder */
+	/* deletes a folder */
+
+int maildir_mddelete(const char *);	/* delete a maildir folder by path */
+
+void maildir_list_sharable(const char *,	/* maildir */
+	void (*)(const char *, void *),		/* callback function */
+	void *);				/* 2nd arg to callback func */
+	/* list sharable folders */
+
+int maildir_shared_subscribe(const char *,	/* maildir */
+		const char *);			/* folder */
+	/* subscribe to a shared folder */
+
+void maildir_list_shared(const char *,		/* maildir */
+	void (*)(const char *, void *),		/* callback function */
+	void *);			/* 2nd arg to the callback func */
+	/* list subscribed folders */
+
+int maildir_shared_unsubscribe(const char *,	/* maildir */
+		const char *);			/* folder */
+	/* unsubscribe from a shared folder */
+
+char *maildir_shareddir(const char *,		/* maildir */
+	const char *);				/* folder */
+	/*
+	** Validate and return a path to a shared folder.  folderdir must be
+	** a name of a valid shared folder.
+	*/
+
+void maildir_shared_sync(const char *);		/* maildir */
+	/* "sync" the shared folder */
+
+int maildir_sharedisro(const char *);		/* maildir */
+	/* maildir is a shared read-only folder */
+
+int maildir_unlinksharedmsg(const char *);	/* filename */
+	/* Remove a message from a shared folder */
+
+/* Internal function that reads a symlink */
+
+char *maildir_getlink(const char *);
+
+	/* Determine whether the maildir filename has a certain flag */
+
+int maildir_hasflag(const char *filename, char);
+
+#define	MAILDIR_DELETED(f)	maildir_hasflag((f), 'T')
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif
diff -urN ../qmail-1.03_unpatched/maildiropen.c ./maildiropen.c
--- ../qmail-1.03_unpatched/maildiropen.c	1970-01-01 01:00:00.000000000 +0100
+++ ./maildiropen.c	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,133 @@
+/*
+** Copyright 2000 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include	<sys/types.h>
+#include	<sys/stat.h>
+#include	<string.h>
+#include	<stdlib.h>
+#include	<time.h>
+#if	HAVE_UNISTD_H
+#include	<unistd.h>
+#endif
+#include	<stdio.h>
+#include	<ctype.h>
+#include	<errno.h>
+#include	<fcntl.h>
+
+#include	"maildirmisc.h"
+
+static const char rcsid[]="$Id: maildiropen.c,v 1.7 2000/12/10 04:43:44 mrsam Exp $";
+
+char *maildir_getlink(const char *filename)
+{
+#if     HAVE_READLINK
+size_t	bufsiz;
+char	*buf;
+
+	bufsiz=0;
+	buf=0;
+
+	for (;;)
+	{
+	int	n;
+
+		if (buf)	free(buf);
+		bufsiz += 256;
+		if ((buf=malloc(bufsiz)) == 0)
+		{
+			perror("malloc");
+			return (0);
+		}
+		if ((n=readlink(filename, buf, bufsiz)) < 0)
+		{
+			free(buf);
+			return (0);
+		}
+		if (n < bufsiz)
+		{
+			buf[n]=0;
+			break;
+		}
+	}
+	return (buf);
+#else
+	return (0);
+#endif
+}
+
+int maildir_semisafeopen(const char *path, int mode, int perm)
+{
+
+#if	HAVE_READLINK
+
+char	*l=maildir_getlink(path);
+
+	if (l)
+	{
+	int	f;
+
+		if (*l != '/')
+		{
+		char	*q=malloc(strlen(path)+strlen(l)+2);
+		char	*s;
+
+			if (!q)
+			{
+				free(l);
+				return (-1);
+			}
+
+			strcpy(q, path);
+			if ((s=strchr(q, '/')) != 0)
+				s[1]=0;
+			else	*q=0;
+			strcat(q, l);
+			free(l);
+			l=q;
+		}
+
+		f=maildir_safeopen(l, mode, perm);
+
+		free(l);
+		return (f);
+	}
+#endif
+
+	return (maildir_safeopen(path, mode, perm));
+}
+		
+int maildir_safeopen(const char *path, int mode, int perm)
+{
+struct	stat	stat1, stat2;
+
+int	fd=open(path, mode
+#ifdef	O_NONBLOCK
+			| O_NONBLOCK
+#else
+			| O_NDELAY
+#endif
+				, perm);
+
+	if (fd < 0)	return (fd);
+	if (fcntl(fd, F_SETFL, (mode & O_APPEND)) || fstat(fd, &stat1)
+	    || lstat(path, &stat2))
+	{
+		close(fd);
+		return (-1);
+	}
+
+	if (stat1.st_dev != stat2.st_dev || stat1.st_ino != stat2.st_ino)
+	{
+		close(fd);
+		errno=ENOENT;
+		return (-1);
+	}
+
+	return (fd);
+}
diff -urN ../qmail-1.03_unpatched/maildirparsequota.c ./maildirparsequota.c
--- ../qmail-1.03_unpatched/maildirparsequota.c	1970-01-01 01:00:00.000000000 +0100
+++ ./maildirparsequota.c	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,44 @@
+/*
+** Copyright 1998 - 1999 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include	"maildirquota.h"
+#include	<stdlib.h>
+#include	<string.h>
+
+static const char rcsid[]="$Id: maildirparsequota.c,v 1.2 1999/12/06 13:21:05 mrsam Exp $";
+
+int maildir_parsequota(const char *n, unsigned long *s)
+{
+const char *o;
+int	yes;
+
+	if ((o=strrchr(n, '/')) == 0)	o=n;
+
+	for (; *o; o++)
+		if (*o == ':')	break;
+	yes=0;
+	for ( ; o >= n; --o)
+	{
+		if (*o == '/')	break;
+
+		if (*o == ',' && o[1] == 'S' && o[2] == '=')
+		{
+			yes=1;
+			o += 3;
+			break;
+		}
+	}
+	if (yes)
+	{
+		*s=0;
+		while (*o >= '0' && *o <= '9')
+			*s= *s*10 + (*o++ - '0');
+		return (0);
+	}
+	return (-1);
+}
diff -urN ../qmail-1.03_unpatched/maildirquota.c ./maildirquota.c
--- ../qmail-1.03_unpatched/maildirquota.c	1970-01-01 01:00:00.000000000 +0100
+++ ./maildirquota.c	2005-01-21 19:30:44.000000000 +0100
@@ -0,0 +1,685 @@
+/*
+** Copyright 1998 - 2002 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+/* #if HAVE_DIRENT_H */
+#include <dirent.h>
+#define NAMLEN(dirent) strlen((dirent)->d_name)
+/* #else
+#define dirent direct
+#define NAMLEN(dirent) (dirent)->d_namlen
+#if HAVE_SYS_NDIR_H
+#include <sys/ndir.h>
+#endif
+#if HAVE_SYS_DIR_H
+#include <sys/dir.h>
+#endif
+#if HAVE_NDIR_H
+#include <ndir.h>
+#endif
+#endif */
+#include	<sys/types.h>
+/* #if	HAVE_SYS_STAT_H */
+#include	<sys/stat.h>
+/* #endif */
+#include	<sys/uio.h>
+
+#include	"maildirquota.h"
+#include	"maildirmisc.h"
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<string.h>
+#include	<errno.h>
+/* #if	HAVE_FCNTL_H */
+#include	<fcntl.h>
+/* #endif */
+#if	HAVE_UNISTD_H
+#include	<unistd.h>
+#endif
+#include	<time.h>
+#include	"numlib.h"
+
+static const char rcsid[]="$Id: maildirquota.c,v 1.9 2002/05/01 04:05:33 mrsam Exp $";
+
+/* Read the maildirsize file */
+
+int maildirsize_read(const char *filename,	/* The filename */
+	int *fdptr,	/* Keep the file descriptor open */
+	off_t *sizeptr,	/* Grand total of maildir size */
+	unsigned *cntptr, /* Grand total of message count */
+	unsigned *nlines, /* # of lines in maildirsize */
+	struct stat *statptr)	/* The stats on maildirsize */
+{
+char buf[5120];
+int f;
+char *p;
+unsigned l;
+int n;
+int first;
+
+	if ((f=maildir_safeopen(filename, O_RDWR|O_APPEND, 0)) < 0)
+		return (-1);
+	p=buf;
+	l=sizeof(buf);
+
+	while (l)
+	{
+		n=read(f, p, l);
+		if (n < 0)
+		{
+			close(f);
+			return (-1);
+		}
+		if (n == 0)	break;
+		p += n;
+		l -= n;
+	}
+	if (l == 0 || fstat(f, statptr))	/* maildir too big */
+	{
+		close(f);
+		return (-1);
+	}
+
+	*sizeptr=0;
+	*cntptr=0;
+	*nlines=0;
+	*p=0;
+	p=buf;
+	first=1;
+	while (*p)
+	{
+	long n=0;
+	int c=0;
+	char	*q=p;
+
+		while (*p)
+			if (*p++ == '\n')
+			{
+				p[-1]=0;
+				break;
+			}
+
+		if (first)
+		{
+			first=0;
+			continue;
+		}
+		sscanf(q, "%ld %d", &n, &c);
+		*sizeptr += n;
+		*cntptr += c;
+		++ *nlines;
+	}
+	*fdptr=f;
+	return (0);
+}
+
+static char *makenewmaildirsizename(const char *, int *);
+static int countcurnew(const char *, time_t *, off_t *, unsigned *);
+static int countsubdir(const char *, const char *,
+		time_t *, off_t *, unsigned *);
+static int statcurnew(const char *, time_t *);
+static int statsubdir(const char *, const char *, time_t *);
+
+#define	MDQUOTA_SIZE	'S'	/* Total size of all messages in maildir */
+#define	MDQUOTA_BLOCKS	'B'	/* Total # of blocks for all messages in
+				maildir -- NOT IMPLEMENTED */
+#define	MDQUOTA_COUNT	'C'	/* Total number of messages in maildir */
+
+static int qcalc(off_t s, unsigned n, const char *quota, int *percentage)
+{
+off_t i;
+int	spercentage=0;
+int	npercentage=0;
+
+	errno=ENOSPC;
+	while (quota && *quota)
+	{
+		int x=1;
+
+		if (*quota < '0' || *quota > '9')
+		{
+			++quota;
+			continue;
+		}
+		i=0;
+		while (*quota >= '0' && *quota <= '9')
+			i=i*10 + (*quota++ - '0');
+		switch (*quota)	{
+		default:
+			if (i < s)
+			{
+				*percentage=100;
+				return (-1);
+			}
+
+			/*
+			** For huge quotas, over 20mb,
+			** divide numerator & denominator by 1024 to prevent
+			** an overflow when multiplying by 100
+			*/
+
+			x=1;
+			if (i > 20000000) x=1024;
+
+			spercentage = i ? (s/x) * 100 / (i/x):100;
+			break;
+		case 'C':
+
+			if (i < n)
+			{
+				*percentage=100;
+				return (-1);
+			}
+
+			/* Ditto */
+
+			x=1;
+			if (i > 20000000) x=1024;
+
+			npercentage = i ? ((off_t)n/x) * 100 / (i/x):100;
+			break;
+		}
+	}
+	*percentage = spercentage > npercentage ? spercentage:npercentage;
+	return (0);
+}
+
+static int	doaddquota(const char *, int, const char *, long, int, int);
+
+static int docheckquota(const char *dir,
+	int *maildirsize_fdptr,
+	const char *quota_type,
+	long xtra_size,
+	int xtra_cnt, int *percentage);
+
+
+int maildir_checkquota(const char *dir,
+	int *maildirsize_fdptr,
+	const char *quota_type,
+	long xtra_size,
+	int xtra_cnt)
+{
+int	dummy;
+
+	return (docheckquota(dir, maildirsize_fdptr, quota_type,
+		xtra_size, xtra_cnt, &dummy));
+}
+
+int maildir_readquota(const char *dir, const char *quota_type)
+{
+int	percentage=0;
+int	fd=-1;
+
+	(void)docheckquota(dir, &fd, quota_type, 0, 0, &percentage);
+	if (fd >= 0)
+		close(fd);
+	return (percentage);
+}
+
+static int docheckquota(const char *dir,
+	int *maildirsize_fdptr,
+	const char *quota_type,
+	long xtra_size,
+	int xtra_cnt,
+	int *percentage)
+{
+char	*checkfolder=(char *)malloc(strlen(dir)+sizeof("/maildirfolder"));
+char	*newmaildirsizename;
+struct stat stat_buf;
+int	maildirsize_fd;
+off_t	maildirsize_size;
+unsigned maildirsize_cnt;
+unsigned maildirsize_nlines;
+int	n;
+time_t	tm;
+time_t	maxtime;
+DIR	*dirp;
+struct dirent *de;
+
+	if (checkfolder == 0)	return (-1);
+	*maildirsize_fdptr= -1;
+	strcat(strcpy(checkfolder, dir), "/maildirfolder");
+	if (stat(checkfolder, &stat_buf) == 0)	/* Go to parent */
+	{
+		strcat(strcpy(checkfolder, dir), "/..");
+		n=docheckquota(checkfolder, maildirsize_fdptr,
+			quota_type, xtra_size, xtra_cnt, percentage);
+		free(checkfolder);
+		return (n);
+	}
+	if (!quota_type || !*quota_type)	return (0);
+
+	strcat(strcpy(checkfolder, dir), "/maildirsize");
+	time(&tm);
+	if (maildirsize_read(checkfolder, &maildirsize_fd,
+		&maildirsize_size, &maildirsize_cnt,
+		&maildirsize_nlines, &stat_buf) == 0)
+	{
+		n=qcalc(maildirsize_size+xtra_size, maildirsize_cnt+xtra_cnt,
+			quota_type, percentage);
+
+		if (n == 0)
+		{
+			free(checkfolder);
+			*maildirsize_fdptr=maildirsize_fd;
+			return (0);
+		}
+		close(maildirsize_fd);
+
+		if (maildirsize_nlines == 1 && tm < stat_buf.st_mtime + 15*60)
+			return (n);
+	}
+
+	maxtime=0;
+	maildirsize_size=0;
+	maildirsize_cnt=0;
+
+	if (countcurnew(dir, &maxtime, &maildirsize_size, &maildirsize_cnt))
+	{
+		free(checkfolder);
+		return (-1);
+	}
+
+	dirp=opendir(dir);
+	while (dirp && (de=readdir(dirp)) != 0)
+	{
+		if (countsubdir(dir, de->d_name, &maxtime, &maildirsize_size,
+			&maildirsize_cnt))
+		{
+			free(checkfolder);
+			closedir(dirp);
+			return (-1);
+		}
+	}
+	if (dirp)
+	{
+#if	CLOSEDIR_VOID
+		closedir(dirp);
+#else
+		if (closedir(dirp))
+		{
+			free(checkfolder);
+			return (-1);
+		}
+#endif
+	}
+
+	newmaildirsizename=makenewmaildirsizename(dir, &maildirsize_fd);
+	if (!newmaildirsizename)
+	{
+		free(checkfolder);
+		return (-1);
+	}
+
+	*maildirsize_fdptr=maildirsize_fd;
+
+	if (doaddquota(dir, maildirsize_fd, quota_type, maildirsize_size,
+		maildirsize_cnt, 1))
+	{
+		unlink(newmaildirsizename);
+                free(newmaildirsizename);
+		close(maildirsize_fd);
+		*maildirsize_fdptr= -1;
+		free(checkfolder);
+		return (-1);
+	}
+
+	strcat(strcpy(checkfolder, dir), "/maildirsize");
+
+	if (rename(newmaildirsizename, checkfolder))
+	{
+		/* free(checkfolder); */
+		unlink(newmaildirsizename);
+		close(maildirsize_fd);
+		*maildirsize_fdptr= -1;
+	}
+	free(checkfolder);
+	free(newmaildirsizename);
+
+	tm=0;
+
+	if (statcurnew(dir, &tm))
+	{
+		close(maildirsize_fd);
+		*maildirsize_fdptr= -1;
+		return (-1);
+	}
+
+	dirp=opendir(dir);
+	while (dirp && (de=readdir(dirp)) != 0)
+	{
+		if (statsubdir(dir, de->d_name, &tm))
+		{
+			close(maildirsize_fd);
+			*maildirsize_fdptr= -1;
+			closedir(dirp);
+			return (-1);
+		}
+	}
+	if (dirp)
+	{
+#if	CLOSEDIR_VOID
+		closedir(dirp);
+#else
+		if (closedir(dirp))
+		{
+			close(maildirsize_fd);
+			*maildirsize_fdptr= -1;
+			return (-1);
+		}
+#endif
+	}
+
+	if (tm != maxtime)	/* Race condition, someone changed something */
+	{
+		errno=EAGAIN;
+		return (-1);
+	}
+
+	return (qcalc(maildirsize_size+xtra_size, maildirsize_cnt+xtra_cnt,
+		quota_type, percentage));
+}
+
+int	maildir_addquota(const char *dir, int maildirsize_fd,
+	const char *quota_type, long maildirsize_size, int maildirsize_cnt)
+{
+	if (!quota_type || !*quota_type)	return (0);
+	return (doaddquota(dir, maildirsize_fd, quota_type, maildirsize_size,
+			maildirsize_cnt, 0));
+}
+
+static int doaddquota(const char *dir, int maildirsize_fd,
+	const char *quota_type, long maildirsize_size, int maildirsize_cnt,
+	int isnew)
+{
+union	{
+	char	buf[100];
+	struct stat stat_buf;
+	} u;				/* Scrooge */
+char	*newname2=0;
+char	*newmaildirsizename=0;
+struct	iovec	iov[3];
+int	niov;
+struct	iovec	*p;
+int	n;
+
+	niov=0;
+	if ( maildirsize_fd < 0)
+	{
+		newname2=(char *)malloc(strlen(dir)+sizeof("/maildirfolder"));
+		if (!newname2)	return (-1);
+		strcat(strcpy(newname2, dir), "/maildirfolder");
+		if (stat(newname2, &u.stat_buf) == 0)
+		{
+			strcat(strcpy(newname2, dir), "/..");
+			n=doaddquota(newname2, maildirsize_fd, quota_type,
+					maildirsize_size, maildirsize_cnt,
+					isnew);
+			free(newname2);
+			return (n);
+		}
+
+		strcat(strcpy(newname2, dir), "/maildirsize");
+
+		if ((maildirsize_fd=maildir_safeopen(newname2,
+			O_RDWR|O_APPEND, 0644)) < 0)
+		{
+			newmaildirsizename=makenewmaildirsizename(dir, &maildirsize_fd);
+			if (!newmaildirsizename)
+			{
+				free(newname2);
+				return (-1);
+			}
+
+			maildirsize_fd=maildir_safeopen(newmaildirsizename,
+				O_CREAT|O_RDWR|O_APPEND, 0644);
+
+			if (maildirsize_fd < 0)
+			{
+				free(newname2);
+				return (-1);
+			}
+			isnew=1;
+		}
+	}
+
+	if (isnew)
+	{
+		iov[0].iov_base=(caddr_t)quota_type;
+		iov[0].iov_len=strlen(quota_type);
+		iov[1].iov_base=(caddr_t)"\n";
+		iov[1].iov_len=1;
+		niov=2;
+	}
+
+
+	sprintf(u.buf, "%ld %d\n", maildirsize_size, maildirsize_cnt);
+	iov[niov].iov_base=(caddr_t)u.buf;
+	iov[niov].iov_len=strlen(u.buf);
+
+	p=iov;
+	++niov;
+	n=0;
+	while (niov)
+	{
+		if (n)
+		{
+			if (n < p->iov_len)
+			{
+				p->iov_base=
+					(caddr_t)((char *)p->iov_base + n);
+				p->iov_len -= n;
+			}
+			else
+			{
+				n -= p->iov_len;
+				++p;
+				--niov;
+				continue;
+			}
+		}
+
+		n=writev( maildirsize_fd, p, niov);
+
+		if (n <= 0)
+		{
+			if (newname2)
+			{
+				close(maildirsize_fd);
+				free(newname2);
+			}
+			return (-1);
+		}
+	}
+	if (newname2)
+	{
+		close(maildirsize_fd);
+
+		if (newmaildirsizename)
+		{
+			rename(newmaildirsizename, newname2);
+			free(newmaildirsizename);
+		}
+		free(newname2);
+	}
+	return (0);
+}
+
+/* New maildirsize is built in the tmp subdirectory */
+
+static char *makenewmaildirsizename(const char *dir, int *fd)
+{
+char	hostname[256];
+struct	stat stat_buf;
+time_t	t;
+char	*p;
+
+	hostname[0]=0;
+	hostname[sizeof(hostname)-1]=0;
+	gethostname(hostname, sizeof(hostname)-1);
+	p=(char *)malloc(strlen(dir)+strlen(hostname)+130);
+	if (!p)	return (0);
+
+	for (;;)
+	{
+	char	tbuf[NUMBUFSIZE];
+	char	pbuf[NUMBUFSIZE];
+
+		time(&t);
+		strcat(strcpy(p, dir), "/tmp/");
+		sprintf(p+strlen(p), "%s.%s_NeWmAiLdIrSiZe.%s",
+			str_time_t(t, tbuf),
+			str_pid_t(getpid(), pbuf), hostname);
+
+		if (stat( (const char *)p, &stat_buf) < 0 &&
+			(*fd=maildir_safeopen(p,
+				O_CREAT|O_RDWR|O_APPEND, 0644)) >= 0)
+			break;
+		sleep(3);
+	}
+	return (p);
+}
+
+static int statcurnew(const char *dir, time_t *maxtimestamp)
+{
+char	*p=(char *)malloc(strlen(dir)+5);
+struct	stat	stat_buf;
+
+	if (!p)	return (-1);
+	strcat(strcpy(p, dir), "/cur");
+	if ( stat(p, &stat_buf) == 0 && stat_buf.st_mtime > *maxtimestamp)
+		*maxtimestamp=stat_buf.st_mtime;
+	strcat(strcpy(p, dir), "/new");
+	if ( stat(p, &stat_buf) == 0 && stat_buf.st_mtime > *maxtimestamp)
+		*maxtimestamp=stat_buf.st_mtime;
+	free(p);
+	return (0);
+}
+
+static int statsubdir(const char *dir, const char *subdir, time_t *maxtime)
+{
+char	*p;
+int	n;
+
+	if ( *subdir != '.' || strcmp(subdir, ".") == 0 ||
+		strcmp(subdir, "..") == 0 || strcmp(subdir, "." TRASH) == 0)
+		return (0);
+
+	p=(char *)malloc(strlen(dir)+strlen(subdir)+2);
+	if (!p)	return (-1);
+	strcat(strcat(strcpy(p, dir), "/"), subdir);
+	n=statcurnew(p, maxtime);
+	free(p);
+	return (n);
+}
+
+static int docount(const char *, time_t *, off_t *, unsigned *);
+
+static int countcurnew(const char *dir, time_t *maxtime,
+	off_t *sizep, unsigned *cntp)
+{
+char	*p=(char *)malloc(strlen(dir)+5);
+int	n;
+
+	if (!p)	return (-1);
+	strcat(strcpy(p, dir), "/new");
+	n=docount(p, maxtime, sizep, cntp);
+	if (n == 0)
+	{
+		strcat(strcpy(p, dir), "/cur");
+		n=docount(p, maxtime, sizep, cntp);
+	}
+	free(p);
+	return (n);
+}
+
+static int countsubdir(const char *dir, const char *subdir, time_t *maxtime,
+	off_t *sizep, unsigned *cntp)
+{
+char	*p;
+int	n;
+
+	if ( *subdir != '.' || strcmp(subdir, ".") == 0 ||
+		strcmp(subdir, "..") == 0 || strcmp(subdir, "." TRASH) == 0)
+		return (0);
+
+	p=(char *)malloc(strlen(dir)+strlen(subdir)+2);
+	if (!p)	return (2);
+	strcat(strcat(strcpy(p, dir), "/"), subdir);
+	n=countcurnew(p, maxtime, sizep, cntp);
+	free(p);
+	return (n);
+}
+
+static int docount(const char *dir, time_t *dirstamp,
+	off_t *sizep, unsigned *cntp)
+{
+struct	stat	stat_buf;
+char	*p;
+DIR	*dirp;
+struct dirent *de;
+unsigned long	s;
+
+	if (stat(dir, &stat_buf))	return (0);	/* Ignore */
+	if (stat_buf.st_mtime > *dirstamp)	*dirstamp=stat_buf.st_mtime;
+	if ((dirp=opendir(dir)) == 0)	return (0);
+	while ((de=readdir(dirp)) != 0)
+	{
+	const char *n=de->d_name;
+
+		if (*n == '.')	continue;
+
+		/* PATCH - do not count msgs marked as deleted */
+
+		for ( ; *n; n++)
+		{
+			if (n[0] != ':' || n[1] != '2' ||
+				n[2] != ',')	continue;
+			n += 3;
+			while (*n >= 'A' && *n <= 'Z')
+			{
+				if (*n == 'T')	break;
+				++n;
+			}
+			break;
+		}
+		if (*n == 'T')	continue;
+		n=de->d_name;
+
+
+		if (maildir_parsequota(n, &s) == 0)
+			stat_buf.st_size=s;
+		else
+		{
+			p=(char *)malloc(strlen(dir)+strlen(n)+2);
+			if (!p)
+			{
+				closedir(dirp);
+				return (-1);
+			}
+			strcat(strcat(strcpy(p, dir), "/"), n);
+			if (stat(p, &stat_buf))
+			{
+				free(p);
+				continue;
+			}
+			free(p);
+		}
+		*sizep += stat_buf.st_size;
+		++*cntp;
+	}
+
+#if	CLOSEDIR_VOID
+	closedir(dirp);
+#else
+	if (closedir(dirp))
+		return (-1);
+#endif
+	return (0);
+}
diff -urN ../qmail-1.03_unpatched/maildirquota.h ./maildirquota.h
--- ../qmail-1.03_unpatched/maildirquota.h	1970-01-01 01:00:00.000000000 +0100
+++ ./maildirquota.h	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,45 @@
+#ifndef	maildirquota_h
+#define	maildirquota_h
+
+/*
+** Copyright 1998 - 1999 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#if	HAVE_CONFIG_H
+#include	"config.h"
+#endif
+
+#include	<sys/types.h>
+#include	<stdio.h>
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+static const char maildirquota_h_rcsid[]="$Id: maildirquota.h,v 1.2 2000/09/04 17:10:28 mrsam Exp $";
+
+int maildir_checkquota(const char *,	/* Pointer to directory */
+	int *,	/* Initialized to -1, or opened descriptor for maildirsize */
+	const char *,	/* The quota */
+	long,		/* Extra bytes planning to add/remove from maildir */
+	int);		/* Extra messages planning to add/remove from maildir */
+
+int maildir_addquota(const char *,	/* Pointer to the maildir */
+	int,	/* Must be the int pointed to by 2nd arg to checkquota */
+	const char *,	/* The quota */
+	long,	/* +/- bytes */
+	int);	/* +/- files */
+
+int maildir_readquota(const char *,	/* Directory */
+	const char *);			/* Quota, from getquota */
+
+int maildir_parsequota(const char *, unsigned long *);
+	/* Attempt to parse file size encoded in filename.  Returns 0 if
+	** parsed, non-zero if we didn't parse. */
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif
diff -urN ../qmail-1.03_unpatched/numlib.h ./numlib.h
--- ../qmail-1.03_unpatched/numlib.h	1970-01-01 01:00:00.000000000 +0100
+++ ./numlib.h	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,45 @@
+#ifndef	numlib_h
+#define	numlib_h
+
+/*
+** Copyright 1998 - 1999 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+static const char numlib_h_rcsid[]="$Id: numlib.h,v 1.3 2001/08/12 15:46:40 mrsam Exp $";
+
+#if	HAVE_CONFIG_H
+#include	"config.h"
+#endif
+
+#include	<sys/types.h>
+#include	<time.h>
+
+#define	NUMBUFSIZE	60
+
+/* Convert various system types to decimal */
+
+char	*str_time_t(time_t, char *);
+char	*str_off_t(off_t, char *);
+char	*str_pid_t(pid_t, char *);
+char	*str_ino_t(ino_t, char *);
+char	*str_uid_t(uid_t, char *);
+char	*str_gid_t(gid_t, char *);
+char	*str_size_t(size_t, char *);
+
+char	*str_sizekb(unsigned long, char *);	/* X Kb or X Mb */
+
+/* Convert selected system types to hex */
+
+char	*strh_time_t(time_t, char *);
+char	*strh_pid_t(pid_t, char *);
+char	*strh_ino_t(ino_t, char *);
+
+#ifdef	__cplusplus
+}
+#endif
+#endif
diff -urN ../qmail-1.03_unpatched/overmaildirquota.c ./overmaildirquota.c
--- ../qmail-1.03_unpatched/overmaildirquota.c	1970-01-01 01:00:00.000000000 +0100
+++ ./overmaildirquota.c	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,42 @@
+/*
+** Copyright 1998 - 1999 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include        "maildirquota.h"
+#include        <stdlib.h>
+#include        <string.h>
+#include        <errno.h>
+#include        <sys/stat.h>
+
+static const char rcsid[]="$Id: overquota.c,v 1.0 2002/06/09 16:21:05 mr sam Exp $";
+
+
+int user_over_maildirquota( const char *dir, const char *q)
+{
+struct  stat    stat_buf;
+int     quotafd;
+int     ret_value;
+
+        if (fstat(0, &stat_buf) == 0 && S_ISREG(stat_buf.st_mode) &&
+                stat_buf.st_size > 0 && *q)
+        {
+                if (maildir_checkquota(dir, &quotafd, q, stat_buf.st_size, 1)
+                        && errno != EAGAIN)
+                {
+                        if (quotafd >= 0)       close(quotafd);
+                        ret_value = 1;
+                } else {
+                        maildir_addquota(dir, quotafd, q, stat_buf.st_size, 1);
+                        if (quotafd >= 0)       close(quotafd);
+                        ret_value = 0;
+                }
+        } else {
+                ret_value = 0;
+        }
+
+        return(ret_value);
+}
diff -urN ../qmail-1.03_unpatched/qmail-control.9 ./qmail-control.9
--- ../qmail-1.03_unpatched/qmail-control.9	1998-06-15 12:53:16.000000000 +0200
+++ ./qmail-control.9	2005-01-21 19:25:03.000000000 +0100
@@ -20,7 +20,9 @@
 
 Comments are allowed
 in
+.IR badhelo ,
 .IR badmailfrom ,
+.IR badmailto ,
 .IR locals ,
 .IR percenthack ,
 .IR qmqpservers ,
@@ -32,7 +34,9 @@
 
 The following table lists all control files
 other than
-.IR me .
+.IR me 
+(rows marked by [*] are provided by patches).
+
 See the corresponding man pages for further details.
 
 .RS
@@ -40,14 +44,20 @@
 .ta 5c 10c
 control	default	used by
 
+.I badhelo	\fR(none) [*]	\fRqmail-smtpd
 .I badmailfrom	\fR(none)	\fRqmail-smtpd
+.I badmailto	\fR(none) [*]	\fRqmail-smtpd
 .I bouncefrom	\fRMAILER-DAEMON	\fRqmail-send
 .I bouncehost	\fIme	\fRqmail-send
+.I clientca.pem	\fR(none)	\fRqmail-smtpd
+.I clientcert.pem	\fR(none)	\fRqmail-remote
 .I concurrencylocal	\fR10	\fRqmail-send
 .I concurrencyremote	\fR20	\fRqmail-send
 .I defaultdomain	\fIme	\fRqmail-inject
 .I defaulthost	\fIme	\fRqmail-inject
 .I databytes	\fR0	\fRqmail-smtpd
+.I dh1024.pem	\fR(none)	\fRqmail-smtpd
+.I dh512.pem	\fR(none)	\fRqmail-smtpd
 .I doublebouncehost	\fIme	\fRqmail-send
 .I doublebounceto	\fRpostmaster	\fRqmail-send
 .I envnoathost	\fIme	\fRqmail-send
@@ -61,11 +71,19 @@
 .I qmqpservers	\fR(none)	\fRqmail-qmqpc
 .I queuelifetime	\fR604800	\fRqmail-send
 .I rcpthosts	\fR(none)	\fRqmail-smtpd
+.I rsa512.pem	\fR(none)	\fRqmail-smtpd
+.I servercert.pem	\fR(none)	\fRqmail-smtpd
 .I smtpgreeting	\fIme	\fRqmail-smtpd
 .I smtproutes	\fR(none)	\fRqmail-remote
+.I tarpitcount	\fR0 [*]	\fRqmail-smtpd
+.I tarpitdelay	\fR5 [*]	\fRqmail-smtpd
 .I timeoutconnect	\fR60	\fRqmail-remote
 .I timeoutremote	\fR1200	\fRqmail-remote
 .I timeoutsmtpd	\fR1200	\fRqmail-smtpd
+.I tlsclients	\fR(none)	\fRqmail-smtpd
+.I tlsclientciphers	\fR(none)	\fRqmail-remote
+.I tlshosts/FQDN.pem	\fR(none)	\fRqmail-remote
+.I tlsserverciphers	\fR(none)	\fRqmail-smtpd
 .I virtualdomains	\fR(none)	\fRqmail-send
 .fi
 .RE
diff -urN ../qmail-1.03_unpatched/qmail-local.c ./qmail-local.c
--- ../qmail-1.03_unpatched/qmail-local.c	1998-06-15 12:53:16.000000000 +0200
+++ ./qmail-local.c	2005-01-21 19:25:03.000000000 +0100
@@ -1,5 +1,6 @@
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <fcntl.h>
 #include "readwrite.h"
 #include "sig.h"
 #include "env.h"
@@ -66,6 +67,7 @@
 
 char buf[1024];
 char outbuf[1024];
+#define QUOTABUFSIZE    256
 
 /* child process */
 
@@ -86,9 +88,15 @@
  int fd;
  substdio ss;
  substdio ssout;
+ char quotabuf[QUOTABUFSIZE];
 
  sig_alarmcatch(sigalrm);
  if (chdir(dir) == -1) { if (error_temp(errno)) _exit(1); _exit(2); }
+ if (maildir_getquota(dir, quotabuf) == 0) {
+  if (user_over_maildirquota(dir,quotabuf)==1) {
+   _exit(1);
+  }
+ }
  pid = getpid();
  host[0] = 0;
  gethostname(host,sizeof(host));
@@ -99,7 +107,10 @@
    s += fmt_str(s,"tmp/");
    s += fmt_ulong(s,time); *s++ = '.';
    s += fmt_ulong(s,pid); *s++ = '.';
-   s += fmt_strn(s,host,sizeof(host)); *s++ = 0;
+   s += fmt_strn(s,host,sizeof(host));
+   s += fmt_strn(s,",S=",sizeof(",S="));
+   if (fstat(0,&st) == -1) if (errno == error_noent) break;
+   s += fmt_ulong(s,st.st_size); *s++ = 0;
    if (stat(fntmptph,&st) == -1) if (errno == error_noent) break;
    /* really should never get to this point */
    if (loop == 2) _exit(1);
@@ -128,6 +139,9 @@
  if (close(fd) == -1) goto fail; /* NFS dorks */
 
  if (link(fntmptph,fnnewtph) == -1) goto fail;
+ if ((fd = open(fnnewtph, O_RDONLY)) < 0 ||
+     fsync(fd) < 0 || close(fd) < 0) goto fail;
+   
    /* if it was error_exist, almost certainly successful; i hate NFS */
  tryunlinktmp(); _exit(0);
 
@@ -159,6 +173,7 @@
  switch(wait_exitcode(wstat))
   {
    case 0: break;
+   case 1: strerr_die1x(1, "User over quota. (#5.1.1)");
    case 2: strerr_die1x(111,"Unable to chdir to maildir. (#4.2.1)");
    case 3: strerr_die1x(111,"Timeout on maildir delivery. (#4.3.0)");
    case 4: strerr_die1x(111,"Unable to read message. (#4.3.0)");
@@ -645,7 +660,7 @@
     {
      cmds.s[j] = 0;
      k = j;
-     while ((k > i) && (cmds.s[k - 1] == ' ') || (cmds.s[k - 1] == '\t'))
+     while ((k > i) && ((cmds.s[k - 1] == ' ') || (cmds.s[k - 1] == '\t')))
        cmds.s[--k] = 0;
      switch(cmds.s[i])
       {
diff -urN ../qmail-1.03_unpatched/qmail-pop3d.c ./qmail-pop3d.c
--- ../qmail-1.03_unpatched/qmail-pop3d.c	1998-06-15 12:53:16.000000000 +0200
+++ ./qmail-pop3d.c	2005-01-21 19:25:03.000000000 +0100
@@ -16,6 +16,11 @@
 #include "readwrite.h"
 #include "timeoutread.h"
 #include "timeoutwrite.h"
+#include <errno.h>
+#include "maildirquota.h"
+#include "maildirmisc.h"
+
+#define QUOTABUFSIZE 256
 
 void die() { _exit(0); }
 
@@ -45,19 +50,15 @@
 {
   substdio_put(&ssout,buf,len);
 }
-void puts(s) char *s;
-{
-  substdio_puts(&ssout,s);
-}
 void flush()
 {
   substdio_flush(&ssout);
 }
 void err(s) char *s;
 {
-  puts("-ERR ");
-  puts(s);
-  puts("\r\n");
+  substdio_puts(&ssout,"-ERR ");
+  substdio_puts(&ssout,s);
+  substdio_puts(&ssout,"\r\n");
   flush();
 }
 
@@ -66,14 +67,14 @@
 void die_scan() { err("unable to scan $HOME/Maildir"); die(); }
 
 void err_syntax() { err("syntax error"); }
-void err_unimpl() { err("unimplemented"); }
+void err_unimpl(arg) char *arg; { err("unimplemented"); }
 void err_deleted() { err("already deleted"); }
 void err_nozero() { err("messages are counted from 1"); }
 void err_toobig() { err("not that many messages"); }
 void err_nosuch() { err("unable to open that message"); }
 void err_nounlink() { err("unable to unlink all deleted messages"); }
 
-void okay() { puts("+OK \r\n"); flush(); }
+void okay(arg) char *arg; { substdio_puts(&ssout,"+OK \r\n"); flush(); }
 
 void printfn(fn) char *fn;
 {
@@ -146,43 +147,79 @@
   }
 }
 
-void pop3_stat()
+void pop3_stat(arg) char *arg;
 {
   int i;
+  int realnumm;
   unsigned long total;
  
-  total = 0;
-  for (i = 0;i < numm;++i) if (!m[i].flagdeleted) total += m[i].size;
-  puts("+OK ");
-  put(strnum,fmt_uint(strnum,numm));
-  puts(" ");
+  total = realnumm = 0;
+  
+  for (i = 0;i < numm;++i) 
+      if (!m[i].flagdeleted) { 
+          total += m[i].size;
+          ++realnumm;
+      }
+
+  substdio_puts(&ssout,"+OK ");
+  put(strnum,fmt_uint(strnum,realnumm));
+
+  /* for (i = 0;i < numm;++i) if (!m[i].flagdeleted) total += m[i].size;
+     substdio_puts(&ssout,"+OK ");
+     put(strnum,fmt_uint(strnum,numm));
+   */
+
+  substdio_puts(&ssout," ");
   put(strnum,fmt_ulong(strnum,total));
-  puts("\r\n");
+  substdio_puts(&ssout,"\r\n");
   flush();
 }
 
-void pop3_rset()
+void pop3_rset(arg) char *arg;
 {
   int i;
   for (i = 0;i < numm;++i) m[i].flagdeleted = 0;
   last = 0;
-  okay();
+  okay(0);
 }
 
-void pop3_last()
+void pop3_last(arg) char *arg;
 {
-  puts("+OK ");
+  substdio_puts(&ssout,"+OK ");
   put(strnum,fmt_uint(strnum,last));
-  puts("\r\n");
+  substdio_puts(&ssout,"\r\n");
   flush();
 }
 
-void pop3_quit()
+void pop3_quit(arg) char *arg;
 {
   int i;
+  char quotabuf[QUOTABUFSIZE];
+  int has_quota=maildir_getquota(".", quotabuf);
+
+  long deleted_bytes=0;
+  long deleted_messages=0;
+
   for (i = 0;i < numm;++i)
     if (m[i].flagdeleted) {
-      if (unlink(m[i].fn) == -1) err_nounlink();
+      unsigned long un=0;
+      const char *filename=m[i].fn;
+      if (has_quota == 0 && !MAILDIR_DELETED(filename)) {
+          if (maildir_parsequota(filename, &un)) {
+              struct stat stat_buf;
+
+              if (stat(filename, &stat_buf) == 0)
+                  un=stat_buf.st_size;
+          }
+      }
+      if (unlink(m[i].fn) == -1) {
+          err_nounlink();
+          un=0;
+      }
+      if (un) {
+          deleted_bytes -= un;
+          deleted_messages -= 1;
+      }
     }
     else
       if (str_start(m[i].fn,"new/")) {
@@ -192,7 +229,22 @@
 	if (!stralloc_0(&line)) die_nomem();
 	rename(m[i].fn,line.s); /* if it fails, bummer */
       }
-  okay();
+
+    if (deleted_messages < 0) {
+        int quotafd;
+
+        if (maildir_checkquota(".", &quotafd, quotabuf, deleted_bytes,
+                               deleted_messages) && errno != EAGAIN &&
+                               deleted_bytes >= 0)
+            {
+                if (quotafd >= 0) close (quotafd);
+            } else {
+                 maildir_addquota(".", quotafd, quotabuf,
+                                 deleted_bytes, deleted_messages);
+                 if (quotafd >= 0) close(quotafd);
+            }
+        }
+  okay(0);
   die();
 }
 
@@ -214,7 +266,7 @@
   if (i == -1) return;
   m[i].flagdeleted = 1;
   if (i + 1 > last) last = i + 1;
-  okay();
+  okay(0);
 }
 
 void list(i,flaguidl)
@@ -222,10 +274,10 @@
 int flaguidl;
 {
   put(strnum,fmt_uint(strnum,i + 1));
-  puts(" ");
+  substdio_puts(&ssout," ");
   if (flaguidl) printfn(m[i].fn);
   else put(strnum,fmt_ulong(strnum,m[i].size));
-  puts("\r\n");
+  substdio_puts(&ssout,"\r\n");
 }
 
 void dolisting(arg,flaguidl) char *arg; int flaguidl;
@@ -234,15 +286,15 @@
   if (*arg) {
     i = msgno(arg);
     if (i == -1) return;
-    puts("+OK ");
+    substdio_puts(&ssout,"+OK ");
     list(i,flaguidl);
   }
   else {
-    okay();
+    okay(0);
     for (i = 0;i < numm;++i)
       if (!m[i].flagdeleted)
 	list(i,flaguidl);
-    puts(".\r\n");
+    substdio_puts(&ssout,".\r\n");
   }
   flush();
 }
@@ -267,14 +319,28 @@
  
   fd = open_read(m[i].fn);
   if (fd == -1) { err_nosuch(); return; }
-  okay();
+  okay(0);
   substdio_fdbuf(&ssmsg,read,fd,ssmsgbuf,sizeof(ssmsgbuf));
   blast(&ssmsg,limit);
   close(fd);
 }
 
+void pop3_capa(arg) char *arg;
+{
+  substdio_puts(&ssout,"+OK Capability list follows\r\n"
+       "TOP\r\n"
+       "UIDL\r\n"
+       "LAST\r\n"
+       "USER\r\n"
+       "APOP\r\n"
+       "SASL CRAM-MD5\r\n"
+       ".\r\n");
+  flush();
+}
+
 struct commands pop3commands[] = {
   { "quit", pop3_quit, 0 }
+, { "capa", pop3_capa, 0 }
 , { "stat", pop3_stat, 0 }
 , { "list", pop3_list, 0 }
 , { "uidl", pop3_uidl, 0 }
@@ -299,7 +365,7 @@
  
   getlist();
 
-  okay();
+  okay(0);
   commands(&ssin,pop3commands);
   die();
 }
diff -urN ../qmail-1.03_unpatched/qmail-popup.c ./qmail-popup.c
--- ../qmail-1.03_unpatched/qmail-popup.c	1998-06-15 12:53:16.000000000 +0200
+++ ./qmail-popup.c	2005-01-21 19:25:03.000000000 +0100
@@ -14,6 +14,9 @@
 #include "timeoutread.h"
 #include "timeoutwrite.h"
 
+#include "case.h"
+#include "base64.h"
+
 void die() { _exit(1); }
 
 int saferead(fd,buf,len) int fd; char *buf; int len;
@@ -64,15 +67,20 @@
 
 void err_syntax() { err("syntax error"); }
 void err_wantuser() { err("USER first"); }
-void err_authoriz() { err("authorization first"); }
+void err_authoriz(arg) char *arg; { err("authorization first"); }
 
-void okay() { puts("+OK \r\n"); flush(); }
-void pop3_quit() { okay(); die(); }
+void okay(arg) char *arg; { puts("+OK \r\n"); flush(); }
+void pop3_quit(arg) char *arg; { okay(0); die(); }
 
 
 char unique[FMT_ULONG + FMT_ULONG + 3];
 char *hostname;
-stralloc username = {0};
+stralloc authin = {0};   /* input from POP3 client */
+stralloc username = {0}; /* username */
+stralloc chal = {0};     /* challenge */
+stralloc slop = {0};     /* b64 challenge */
+stralloc resp = {0};     /* b64 response */
+
 int seenuser = 0;
 char **childargs;
 substdio ssup;
@@ -133,10 +141,23 @@
   puts(">\r\n");
   flush();
 }
+void pop3_capa(arg) char *arg;
+{
+  puts("+OK Capability list follows\r\n");
+  puts("TOP\r\n");
+  puts("UIDL\r\n");
+  puts("LAST\r\n");
+  puts("USER\r\n");
+  puts("APOP\r\n");
+  puts("SASL CRAM-MD5\r\n");    
+  puts(".\r\n");
+  flush();
+}
+
 void pop3_user(arg) char *arg;
 {
   if (!*arg) { err_syntax(); return; }
-  okay();
+  okay(0);
   seenuser = 1;
   if (!stralloc_copys(&username,arg)) die_nomem(); 
   if (!stralloc_0(&username)) die_nomem(); 
@@ -156,11 +177,100 @@
   doanddie(arg,space - arg,space);
 }
 
+int authgetl(void) {
+  int i;
+
+  if (!stralloc_copys(&authin,"")) die_nomem();
+  for (;;) {
+    if (!stralloc_readyplus(&authin,1)) die_nomem(); /* XXX */
+    i = substdio_get(&ssin,authin.s + authin.len,1);
+    if (i != 1) die();
+    if (authin.s[authin.len] == '\n') break;
+    ++authin.len;
+  }
+
+  if (authin.len > 0) if (authin.s[authin.len - 1] == '\r') --authin.len;
+  authin.s[authin.len] = 0;
+  if (*authin.s == '*' && *(authin.s + 1) == 0) { die_badauth(); }
+  if (authin.len == 0) { die_badauth(); }
+  return authin.len;
+}
+
+
+void pop3_auth_cram(arg) char *arg;
+{
+  int i, r;
+  char *s;
+
+  
+  s = unique;
+  s += fmt_uint(s,getpid());
+  *s++ = '.';
+  s += fmt_ulong(s,(unsigned long) now());
+  *s++ = '@';
+  *s++ = 0;
+
+  if (!stralloc_copys(&chal,"<")) die_nomem();          /* generate challenge */
+  if (!stralloc_cats(&chal,unique)) die_nomem();
+  if (!stralloc_cats(&chal,hostname)) die_nomem();
+  if (!stralloc_cats(&chal,">")) die_nomem();
+  if (b64encode(&chal,&slop) < 0) die_nomem();
+  if (!stralloc_0(&slop)) die_nomem();
+
+  puts("+ ");
+  puts(slop.s);
+  puts("\r\n");
+  flush();
+
+  if (authgetl() < 0) die_badauth();                        /* got response */
+
+  if (r = b64decode(authin.s,authin.len,&resp) == 1) die_badauth();
+
+  /* if (r == -1 || !stralloc_0(&resp)) die_nomem(); */
+  if (r == -1) die_nomem();
+
+  i = str_chr(resp.s,' ');
+  s = resp.s + i;
+  while (*s == ' ') ++s;
+  resp.s[i] = 0;
+
+  if (!stralloc_copys(&username,resp.s)) die_nomem();       /* userid */
+  if (!stralloc_0(&username)) die_nomem();
+  if (!username.len || !s) die_badauth();
+
+  doanddie(username.s,username.len,s);
+}
+struct authcmd {
+  char *text;
+  void (*fun)();
+} authcmds[] = {
+  { "cram-md5",pop3_auth_cram }
+,  { 0,die_badauth }
+};
+
+void pop3_auth(arg) char *arg;
+{
+  int i;
+  char *cmd = arg;
+
+  i = str_chr(cmd,' ');
+  arg = cmd + i;
+  while (*arg == ' ') ++arg;
+  cmd[i] = 0;
+
+  for (i = 0;authcmds[i].text;++i)
+    if (case_equals(authcmds[i].text,cmd)) authcmds[i].fun(arg);
+
+  puts("-ERR unsupported method\r\n"); flush(); return;
+}
+
 struct commands pop3commands[] = {
   { "user", pop3_user, 0 }
 , { "pass", pop3_pass, 0 }
 , { "apop", pop3_apop, 0 }
+, { "auth", pop3_auth, 0 }
 , { "quit", pop3_quit, 0 }
+, { "capa", pop3_capa, 0 }
 , { "noop", okay, 0 }
 , { 0, err_authoriz, 0 }
 } ;
diff -urN ../qmail-1.03_unpatched/qmail-queue.c ./qmail-queue.c
--- ../qmail-1.03_unpatched/qmail-queue.c	1998-06-15 12:53:16.000000000 +0200
+++ ./qmail-queue.c	2005-01-21 19:25:03.000000000 +0100
@@ -1,5 +1,6 @@
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <fcntl.h>
 #include "readwrite.h"
 #include "sig.h"
 #include "exit.h"
@@ -155,6 +156,7 @@
 {
  unsigned int len;
  char ch;
+ int fd;
 
  sig_blocknone();
  umask(033);
@@ -184,6 +186,7 @@
  intdfn = fnnum("intd/",0);
 
  if (link(pidfn,messfn) == -1) die(64);
+ if ((fd = open(messfn, O_RDONLY)) < 0 || fsync(fd) < 0 || close(fd) < 0) die(64);
  if (unlink(pidfn) == -1) die(63);
  flagmademess = 1;
 
@@ -248,6 +251,8 @@
  if (fsync(intdfd) == -1) die_write();
 
  if (link(intdfn,todofn) == -1) die(66);
+ if ((fd = open(todofn, O_RDONLY)) < 0 ||
+     fsync(fd) < 0 || close(fd) < 0) die(66); 
 
  triggerpull();
  die(0);
diff -urN ../qmail-1.03_unpatched/qmail-remote.8 ./qmail-remote.8
--- ../qmail-1.03_unpatched/qmail-remote.8	1998-06-15 12:53:16.000000000 +0200
+++ ./qmail-remote.8	2005-01-21 19:25:03.000000000 +0100
@@ -114,6 +114,10 @@
 always exits zero.
 .SH "CONTROL FILES"
 .TP 5
+.I clientcert.pem
+SSL certificate that is used to authenticate with the remote server
+during a TLS session.
+.TP 5
 .I helohost
 Current host name,
 for use solely in saying hello to the remote SMTP server.
@@ -156,6 +160,8 @@
 this tells
 .B qmail-remote
 to look up MX records as usual.
+.I port 
+value of 465 (deprecated smtps port) causes TLS session to be started.
 .I smtproutes
 may include wildcards:
 
@@ -195,6 +201,26 @@
 .B qmail-remote
 will wait for each response from the remote SMTP server.
 Default: 1200.
+
+.TP 5
+.I tlsclientciphers
+A set of OpenSSL client cipher strings. Multiple ciphers
+contained in a string should be separated by a colon.
+
+.TP 5
+.I tlshosts/<FQDN>.pem
+.B qmail-remote
+requires authentication from servers for which this certificate exists
+.RB ( <FQDN>
+is the fully-qualified domain name of the server). One of the
+.I dNSName
+or the
+.I CommonName
+attributes have to match.
+
+.B WARNING:
+this option may cause mail to be delayed, bounced, doublebounced, or lost.
+
 .SH "SEE ALSO"
 addresses(5),
 envelopes(5),
diff -urN ../qmail-1.03_unpatched/qmail-remote.c ./qmail-remote.c
--- ../qmail-1.03_unpatched/qmail-remote.c	1998-06-15 12:53:16.000000000 +0200
+++ ./qmail-remote.c	2005-01-21 19:25:03.000000000 +0100
@@ -48,6 +48,17 @@
 
 struct ip_address partner;
 
+#ifdef TLS
+# include <sys/stat.h>
+# include "tls.h"
+# include "ssl_timeoutio.h"
+# include <openssl/x509v3.h>
+# define EHLO 1
+
+int tls_init();
+const char *ssl_err_str = 0;
+#endif 
+
 void out(s) char *s; { if (substdio_puts(subfdoutsmall,s) == -1) _exit(0); }
 void zero() { if (substdio_put(subfdoutsmall,"\0",1) == -1) _exit(0); }
 void zerodie() { zero(); substdio_flush(subfdoutsmall); _exit(0); }
@@ -99,6 +110,9 @@
   outhost();
   out(" but connection died. ");
   if (flagcritical) out("Possible duplicate! ");
+#ifdef TLS
+  if (ssl_err_str) { out(ssl_err_str); out(" "); }
+#endif
   out("(#4.4.2)\n");
   zerodie();
 }
@@ -110,6 +124,12 @@
 int saferead(fd,buf,len) int fd; char *buf; int len;
 {
   int r;
+#ifdef TLS
+  if (ssl) {
+    r = ssl_timeoutread(timeout, smtpfd, smtpfd, ssl, buf, len);
+    if (r < 0) ssl_err_str = ssl_strerror();
+  } else
+#endif
   r = timeoutread(timeout,smtpfd,buf,len);
   if (r <= 0) dropped();
   return r;
@@ -117,6 +137,12 @@
 int safewrite(fd,buf,len) int fd; char *buf; int len;
 {
   int r;
+#ifdef TLS
+  if (ssl) {
+    r = ssl_timeoutwrite(timeout, smtpfd, smtpfd, ssl, buf, len);
+    if (r < 0) ssl_err_str = ssl_strerror();
+  } else
+#endif 
   r = timeoutwrite(timeout,smtpfd,buf,len);
   if (r <= 0) dropped();
   return r;
@@ -163,6 +189,64 @@
   return code;
 }
 
+#ifdef EHLO
+saa ehlokw = {0}; /* list of EHLO keywords and parameters */
+int maxehlokwlen = 0;
+
+unsigned long ehlo()
+{
+  stralloc *sa;
+  char *s, *e, *p;
+  unsigned long code;
+
+  if (ehlokw.len > maxehlokwlen) maxehlokwlen = ehlokw.len;
+  ehlokw.len = 0;
+
+# ifdef MXPS
+  if (type == 's') return 0;
+# endif
+
+  substdio_puts(&smtpto, "EHLO ");
+  substdio_put(&smtpto, helohost.s, helohost.len);
+  substdio_puts(&smtpto, "\r\n");
+  substdio_flush(&smtpto);
+
+  code = smtpcode();
+  if (code != 250) return code;
+
+  s = smtptext.s;
+  while (*s++ != '\n') ; /* skip the first line: contains the domain */
+
+  e = smtptext.s + smtptext.len - 6; /* 250-?\n */
+  while (s <= e)
+  {
+    int wasspace = 0;
+
+    if (!saa_readyplus(&ehlokw, 1)) temp_nomem();
+    sa = ehlokw.sa + ehlokw.len++;
+    if (ehlokw.len > maxehlokwlen) *sa = sauninit; else sa->len = 0;
+
+    /* smtptext is known to end in a '\n' */
+    for (p = (s += 4); ; ++p)
+      if (*p == '\n' || *p == ' ' || *p == '\t') {
+        if (!wasspace)
+          if (!stralloc_catb(sa, s, p - s) || !stralloc_0(sa)) temp_nomem();
+        if (*p == '\n') break;
+        wasspace = 1;
+      } else if (wasspace == 1) {
+        wasspace = 0;
+        s = p;
+      }
+    s = p;
+    /* keyword should consist of alpha-num and '-'
+     * broken AUTH might use '=' instead of space */
+    for (p = sa->s; *p; ++p) if (*p == '=') { *p = 0; break; }
+  }
+
+  return 250;
+}
+#endif
+
 void outsmtptext()
 {
   int i; 
@@ -179,6 +263,11 @@
 char *prepend;
 char *append;
 {
+#ifdef TLS
+  /* shouldn't talk to the client unless in an appropriate state */
+  int state = ssl ? ssl->state : SSL_ST_BEFORE;
+  if (state & SSL_ST_OK || !smtps && state & SSL_ST_BEFORE)
+#endif
   substdio_putsflush(&smtpto,"QUIT\r\n");
   /* waiting for remote side is just too ridiculous */
   out(prepend);
@@ -186,6 +275,30 @@
   out(append);
   out(".\n");
   outsmtptext();
+
+#if defined(TLS) && defined(DEBUG)
+  if (ssl) {
+    X509 *peercert;
+
+    out("STARTTLS proto="); out(SSL_get_version(ssl));
+    out("; cipher="); out(SSL_get_cipher(ssl));
+
+    /* we want certificate details */
+    if (peercert = SSL_get_peer_certificate(ssl)) {
+      char *str;
+
+      str = X509_NAME_oneline(X509_get_subject_name(peercert), NULL, 0);
+      out("; subject="); out(str); OPENSSL_free(str);
+
+      str = X509_NAME_oneline(X509_get_issuer_name(peercert), NULL, 0);
+      out("; issuer="); out(str); OPENSSL_free(str);
+
+      X509_free(peercert);
+    }
+    out(";\n");
+  }
+#endif
+
   zerodie();
 }
 
@@ -214,6 +327,182 @@
   substdio_flush(&smtpto);
 }
 
+#ifdef TLS
+char *partner_fqdn = 0;
+
+# define TLS_QUIT quit(ssl ? "; connected to " : "; connecting to ", "")
+void tls_quit(const char *s1, const char *s2)
+{
+  out(s1); if (s2) { out(": "); out(s2); } TLS_QUIT;
+}
+# define tls_quit_error(s) tls_quit(s, ssl_error())
+
+int match_partner(const char *s, int len)
+{
+  if (!case_diffb(partner_fqdn, len, s) && !partner_fqdn[len]) return 1;
+  /* we also match if the name is *.domainname */
+  if (*s == '*') {
+    const char *domain = partner_fqdn + str_chr(partner_fqdn, '.');
+    if (!case_diffb(domain, --len, ++s) && !domain[len]) return 1;
+  }
+  return 0;
+}
+
+/* don't want to fail handshake if certificate can't be verified */
+int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; }
+
+int tls_init()
+{
+  int i;
+  SSL *myssl;
+  SSL_CTX *ctx;
+  stralloc saciphers = {0};
+  const char *ciphers, *servercert = 0;
+
+  if (partner_fqdn) {
+    struct stat st;
+    stralloc tmp = {0};
+    if (!stralloc_copys(&tmp, "control/tlshosts/")
+      || !stralloc_catb(&tmp, partner_fqdn, str_len(partner_fqdn))
+      || !stralloc_catb(&tmp, ".pem", 5)) temp_nomem();
+    if (stat(tmp.s, &st)) alloc_free(tmp.s); else servercert = tmp.s;
+  }
+ 
+  if (!smtps) {
+    stralloc *sa = ehlokw.sa;
+    unsigned int len = ehlokw.len;
+    /* look for STARTTLS among EHLO keywords */
+    for ( ; len && case_diffs(sa->s, "STARTTLS"); ++sa, --len) ;
+    if (!len) {
+      if (!servercert) return 0;
+      out("ZNo TLS achieved while "); out(servercert);
+      out(" exists"); smtptext.len = 0; TLS_QUIT;
+    }
+  }
+
+  SSL_library_init();
+  ctx = SSL_CTX_new(SSLv23_client_method());
+  if (!ctx) {
+    if (!smtps && !servercert) return 0;
+    smtptext.len = 0;
+    tls_quit_error("ZTLS error initializing ctx");
+  }
+
+  if (servercert) {
+    if (!SSL_CTX_load_verify_locations(ctx, servercert, NULL)) {
+      SSL_CTX_free(ctx);
+      smtptext.len = 0;
+      out("ZTLS unable to load "); tls_quit_error(servercert);
+    }
+    /* set the callback here; SSL_set_verify didn't work before 0.9.6c */
+    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_cb);
+  }
+
+  /* let the other side complain if it needs a cert and we don't have one */
+# define CLIENTCERT "control/clientcert.pem"
+  if (SSL_CTX_use_certificate_chain_file(ctx, CLIENTCERT))
+    SSL_CTX_use_RSAPrivateKey_file(ctx, CLIENTCERT, SSL_FILETYPE_PEM);
+# undef CLIENTCERT
+
+  myssl = SSL_new(ctx);
+  SSL_CTX_free(ctx);
+  if (!myssl) {
+    if (!smtps && !servercert) return 0;
+    smtptext.len = 0;
+    tls_quit_error("ZTLS error initializing ssl");
+  }
+
+  if (!smtps) substdio_putsflush(&smtpto, "STARTTLS\r\n");
+
+  /* while the server is preparing a responce, do something else */
+  if (control_readfile(&saciphers, "control/tlsclientciphers", 0) == -1)
+    { SSL_free(myssl); temp_control(); }
+  if (saciphers.len) {
+    for (i = 0; i < saciphers.len - 1; ++i)
+      if (!saciphers.s[i]) saciphers.s[i] = ':';
+    ciphers = saciphers.s;
+  }
+  else ciphers = "DEFAULT";
+  SSL_set_cipher_list(myssl, ciphers);
+  alloc_free(saciphers.s);
+
+  /* SSL_set_options(myssl, SSL_OP_NO_TLSv1); */
+  SSL_set_fd(myssl, smtpfd);
+
+  /* read the responce to STARTTLS */
+  if (!smtps) {
+    if (smtpcode() != 220) {
+      SSL_free(myssl);
+      if (!servercert) return 0;
+      out("ZSTARTTLS rejected while ");
+      out(servercert); out(" exists"); TLS_QUIT;
+    }
+    smtptext.len = 0;
+  }
+
+  ssl = myssl;
+  if (ssl_timeoutconn(timeout, smtpfd, smtpfd, ssl) <= 0)
+    tls_quit("ZTLS connect failed", ssl_strerror());
+
+  if (servercert) {
+    X509 *peercert;
+    STACK_OF(GENERAL_NAME) *gens;
+
+    int r = SSL_get_verify_result(ssl);
+    if (r != X509_V_OK) {
+      out("ZTLS unable to verify server with ");
+      tls_quit(servercert, X509_verify_cert_error_string(r));
+    }
+    alloc_free(servercert);
+
+    peercert = SSL_get_peer_certificate(ssl);
+    if (!peercert) {
+      out("ZTLS unable to verify server ");
+      tls_quit(partner_fqdn, "no certificate provided");
+    }
+
+    /* RFC 2595 section 2.4: find a matching name
+     * first find a match among alternative names */
+    gens = X509_get_ext_d2i(peercert, NID_subject_alt_name, 0, 0);
+    if (gens) {
+      for (i = 0, r = sk_GENERAL_NAME_num(gens); i < r; ++i)
+      {
+        const GENERAL_NAME *gn = sk_GENERAL_NAME_value(gens, i);
+        if (gn->type == GEN_DNS)
+          if (match_partner(gn->d.ia5->data, gn->d.ia5->length)) break;
+      }
+      sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);
+    }
+
+    /* no alternative name matched, look up commonName */
+    if (!gens || i >= r) {
+      stralloc peer = {0};
+      X509_NAME *subj = X509_get_subject_name(peercert);
+      i = X509_NAME_get_index_by_NID(subj, NID_commonName, -1);
+      if (i >= 0) {
+        const ASN1_STRING *s = X509_NAME_get_entry(subj, i)->value;
+        if (s) { peer.len = s->length; peer.s = s->data; }
+      }
+      if (peer.len <= 0) {
+        out("ZTLS unable to verify server ");
+        tls_quit(partner_fqdn, "certificate contains no valid commonName");
+      }
+      if (!match_partner(peer.s, peer.len)) {
+        out("ZTLS unable to verify server "); out(partner_fqdn);
+        out(": received certificate for "); outsafe(&peer); TLS_QUIT;
+      }
+    }
+
+    X509_free(peercert);
+  }
+
+  if (smtps) if (smtpcode() != 220)
+    quit("ZTLS Connected to "," but greeting failed");
+
+  return 1;
+}
+#endif
+
 stralloc recip = {0};
 
 void smtp()
@@ -221,8 +510,50 @@
   unsigned long code;
   int flagbother;
   int i;
- 
-  if (smtpcode() != 220) quit("ZConnected to "," but greeting failed");
+
+#ifndef PORT_SMTP
+  /* the qmtpc patch uses smtp_port and undefines PORT_SMTP */
+# define port smtp_port
+#endif
+
+#ifdef TLS
+# ifdef MXPS
+  if (type == 'S') smtps = 1;
+  else if (type != 's')
+# endif
+    if (port == 465) smtps = 1;
+  if (!smtps)
+#endif
+
+  code = smtpcode();
+  /* just bounce on a 5xx error
+   *   if (code >= 500 && code < 600) quit("DConnected to "," but greeting failed");
+   * or try next mx --- alex 
+   */
+  if (code >= 500 && code < 600) return;
+  if (code >= 400 && code < 500) return; /* try next MX, see RFC-2821 */
+  if (code != 220) quit("ZConnected to "," but greeting failed");
+ 
+#ifdef EHLO
+# ifdef TLS
+  if (!smtps)
+# endif
+  code = ehlo();
+
+# ifdef TLS
+  if (tls_init())
+    /* RFC2487 says we should issue EHLO (even if we might not need
+     * extensions); at the same time, it does not prohibit a server
+     * to reject the EHLO and make us fallback to HELO */
+    code = ehlo();
+# endif
+
+  if (code == 250) {
+    /* add EHLO response checks here */
+
+    /* and if EHLO failed, use HELO */
+  } else {
+#endif
  
   substdio_puts(&smtpto,"HELO ");
   substdio_put(&smtpto,helohost.s,helohost.len);
@@ -230,6 +561,10 @@
   substdio_flush(&smtpto);
   if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected");
  
+#ifdef EHLO
+  }
+#endif
+ 
   substdio_puts(&smtpto,"MAIL FROM:<");
   substdio_put(&smtpto,sender.s,sender.len);
   substdio_puts(&smtpto,">\r\n");
@@ -417,7 +752,10 @@
     if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0) {
       tcpto_err(&ip.ix[i].ip,0);
       partner = ip.ix[i].ip;
-      smtp(); /* does not return */
+#ifdef TLS
+      partner_fqdn = ip.ix[i].fqdn;
+#endif
+      smtp(); /* only returns when the next MX is to be tried */
     }
     tcpto_err(&ip.ix[i].ip,errno == error_timeout);
     close(smtpfd);
diff -urN ../qmail-1.03_unpatched/qmail-showctl.c ./qmail-showctl.c
--- ../qmail-1.03_unpatched/qmail-showctl.c	1998-06-15 12:53:16.000000000 +0200
+++ ./qmail-showctl.c	2005-01-21 19:25:03.000000000 +0100
@@ -214,14 +214,47 @@
     _exit(111);
   }
 
+  do_lst("badhelo","Any HELO host name is allowed.",""," not accepted in HELO.");
   do_lst("badmailfrom","Any MAIL FROM is allowed.",""," not accepted in MAIL FROM.");
+  do_lst("badmailto","Any RCPT TO is allowed.",""," not accepted in RCPT TO.");
   do_str("bouncefrom",0,"MAILER-DAEMON","Bounce user name is ");
   do_str("bouncehost",1,"bouncehost","Bounce host name is ");
+
+  struct stat stcca;
+  substdio_puts(subfdout,"\nclientca.pem: ");
+  if (stat("clientca.pem",&stcca) == -1) 
+    substdio_puts(subfdout,"(Default.) No client CA pem file.\n"); 
+  else 
+    substdio_puts(subfdout,"client CA pem file exists.\n"); 
+
+  struct stat stccert;
+  substdio_puts(subfdout,"\nclientcert.pem: ");
+  if (stat("clientcert.pem",&stccert) == -1) 
+    substdio_puts(subfdout,"(Default.) No client certificate pem file.\n"); 
+  else 
+    substdio_puts(subfdout,"client certificate pem file exists.\n"); 
+
+
   do_int("concurrencylocal","10","Local concurrency is ","");
   do_int("concurrencyremote","20","Remote concurrency is ","");
   do_int("databytes","0","SMTP DATA limit is "," bytes");
   do_str("defaultdomain",1,"defaultdomain","Default domain name is ");
   do_str("defaulthost",1,"defaulthost","Default host name is ");
+
+  struct stat stdh512;
+  substdio_puts(subfdout,"\ndh512.pem: ");
+  if (stat("dh512.pem",&stdh512) == -1)
+    substdio_puts(subfdout,"(Default.) No 512 DH key provided.\n");
+  else
+    substdio_puts(subfdout,"512 DH key exists.\n");
+
+  struct stat stdh1024;
+  substdio_puts(subfdout,"\ndh1024.pem: ");
+  if (stat("dh1024.pem",&stdh1024) == -1)
+    substdio_puts(subfdout,"(Default.) No 1024 DH key provided.\n");
+  else
+    substdio_puts(subfdout,"1024 DH key exists.\n");
+
   do_str("doublebouncehost",1,"doublebouncehost","2B recipient host: ");
   do_str("doublebounceto",0,"postmaster","2B recipient user: ");
   do_str("envnoathost",1,"envnoathost","Presumed domain name is ");
@@ -230,6 +263,7 @@
   do_str("localiphost",1,"localiphost","Local IP address becomes ");
   do_lst("locals","Messages for me are delivered locally.","Messages for "," are delivered locally.");
   do_str("me",0,"undefined! Uh-oh","My name is ");
+  do_int("mfcheck","0","MAIL FROM DNS check is set to "," (0=disable, 1=enable)");
   do_lst("percenthack","The percent hack is not allowed.","The percent hack is allowed for user%host@",".");
   do_str("plusdomain",1,"plusdomain","Plus domain name is ");
   do_lst("qmqpservers","No QMQP servers.","QMQP server: ",".");
@@ -255,11 +289,37 @@
       else
         substdio_puts(subfdout,"Modified recently enough; hopefully up to date.\n");
 
+  struct stat strsa;
+  substdio_puts(subfdout,"\nrsa512.pem: ");
+  if (stat("rsa512.pem",&strsa) == -1) 
+    substdio_puts(subfdout,"(Default.) No 512 RSA key provided.\n"); 
+  else 
+    substdio_puts(subfdout,"512 RSA key exists.\n"); 
+
+  struct stat stscert;
+  substdio_puts(subfdout,"\nservercert.pem: ");
+  if (stat("servercert.pem",&stscert) == -1) 
+    substdio_puts(subfdout,"(Default.) No server certificate pem file.\n"); 
+  else 
+    substdio_puts(subfdout,"server certificate pem file exists.\n"); 
+
+  struct stat stsig;
+  substdio_puts(subfdout,"\nsignatures: ");
+  if (stat("signatures",&stsig) == -1)
+    substdio_puts(subfdout,"(Default.) No binary signatures for blocking.\n");
+  else 
+    substdio_puts(subfdout,"binary signatures for blocking exist.\n");
+
   do_str("smtpgreeting",1,"smtpgreeting","SMTP greeting: 220 ");
   do_lst("smtproutes","No artificial SMTP routes.","SMTP route: ","");
+  do_int("tarpitcount","0",""," RCPT TOs before tarpitting (0=off)");
+  do_int("tarpitdelay","5","tarpit delay is "," seconds");
   do_int("timeoutconnect","60","SMTP client connection timeout is "," seconds");
   do_int("timeoutremote","1200","SMTP client data timeout is "," seconds");
   do_int("timeoutsmtpd","1200","SMTP server data timeout is "," seconds");
+  do_lst("tlsclients","No TLS clients","TLS client: ","");
+  do_lst("tlsclientciphers","No TLS client ciphers","TLS client cipher:","");
+  do_lst("tlsserverciphers","No TLS server ciphers","TLS server cipher:","");
   do_lst("virtualdomains","No virtual domains.","Virtual domain: ","");
 
   while (d = readdir(dir)) {
@@ -267,14 +327,18 @@
     if (str_equal(d->d_name,"..")) continue;
     if (str_equal(d->d_name,"bouncefrom")) continue;
     if (str_equal(d->d_name,"bouncehost")) continue;
+    if (str_equal(d->d_name,"badhelo")) continue;
     if (str_equal(d->d_name,"badmailfrom")) continue;
-    if (str_equal(d->d_name,"bouncefrom")) continue;
-    if (str_equal(d->d_name,"bouncehost")) continue;
+    if (str_equal(d->d_name,"badmailto")) continue;
+    if (str_equal(d->d_name,"clientca.pem")) continue;
+    if (str_equal(d->d_name,"clientcert.pem")) continue; 
     if (str_equal(d->d_name,"concurrencylocal")) continue;
     if (str_equal(d->d_name,"concurrencyremote")) continue;
     if (str_equal(d->d_name,"databytes")) continue;
     if (str_equal(d->d_name,"defaultdomain")) continue;
     if (str_equal(d->d_name,"defaulthost")) continue;
+    if (str_equal(d->d_name,"dh512.pem")) continue;
+    if (str_equal(d->d_name,"dh1024.pem")) continue;
     if (str_equal(d->d_name,"doublebouncehost")) continue;
     if (str_equal(d->d_name,"doublebounceto")) continue;
     if (str_equal(d->d_name,"envnoathost")) continue;
@@ -282,7 +346,9 @@
     if (str_equal(d->d_name,"idhost")) continue;
     if (str_equal(d->d_name,"localiphost")) continue;
     if (str_equal(d->d_name,"locals")) continue;
+    if (str_equal(d->d_name,"locals.lock")) continue;
     if (str_equal(d->d_name,"me")) continue;
+    if (str_equal(d->d_name,"mfcheck")) continue;
     if (str_equal(d->d_name,"morercpthosts")) continue;
     if (str_equal(d->d_name,"morercpthosts.cdb")) continue;
     if (str_equal(d->d_name,"percenthack")) continue;
@@ -290,12 +356,23 @@
     if (str_equal(d->d_name,"qmqpservers")) continue;
     if (str_equal(d->d_name,"queuelifetime")) continue;
     if (str_equal(d->d_name,"rcpthosts")) continue;
+    if (str_equal(d->d_name,"rcpthosts.lock")) continue;
+    if (str_equal(d->d_name,"rsa512.pem")) continue;
+    if (str_equal(d->d_name,"servercert.pem")) continue;
+    if (str_equal(d->d_name,"signatures")) continue;
     if (str_equal(d->d_name,"smtpgreeting")) continue;
     if (str_equal(d->d_name,"smtproutes")) continue;
+    if (str_equal(d->d_name,"tarpitcount")) continue; 
+    if (str_equal(d->d_name,"tarpitdelay")) continue; 
     if (str_equal(d->d_name,"timeoutconnect")) continue;
     if (str_equal(d->d_name,"timeoutremote")) continue;
     if (str_equal(d->d_name,"timeoutsmtpd")) continue;
+    if (str_equal(d->d_name,"tlsclients")) continue;
+    if (str_equal(d->d_name,"tlsclientciphers")) continue;
+    if (str_equal(d->d_name,"tlshosts")) continue;
+    if (str_equal(d->d_name,"tlsserverciphers")) continue;
     if (str_equal(d->d_name,"virtualdomains")) continue;
+    if (str_equal(d->d_name,"virtualdomains.lock")) continue; 
     substdio_puts(subfdout,"\n");
     substdio_puts(subfdout,d->d_name);
     substdio_puts(subfdout,": I have no idea what this file does.\n");
diff -urN ../qmail-1.03_unpatched/qmail-smtpd.8 ./qmail-smtpd.8
--- ../qmail-1.03_unpatched/qmail-smtpd.8	1998-06-15 12:53:16.000000000 +0200
+++ ./qmail-smtpd.8	2005-01-21 19:25:03.000000000 +0100
@@ -3,6 +3,10 @@
 qmail-smtpd \- receive mail via SMTP
 .SH SYNOPSIS
 .B qmail-smtpd
+[
+.I checkprogram
+.I subprogram
+]
 .SH DESCRIPTION
 .B qmail-smtpd
 receives mail messages via the Simple Mail Transfer Protocol (SMTP)
@@ -14,6 +18,15 @@
 see
 .BR tcp-environ(5) .
 
+If the environment variable
+.B SMTPS
+is non-empty,
+.B qmail-smtpd
+starts a TLS session (to support the deprecated SMTPS protocol,
+normally on port 465). Otherwise,
+.B qmail-smtpd
+offers the STARTTLS extension to ESMTP.
+
 .B qmail-smtpd
 is responsible for counting hops.
 It rejects any message with 100 or more 
@@ -23,7 +36,30 @@
 header fields.
 
 .B qmail-smtpd
-supports ESMTP, including the 8BITMIME and PIPELINING options.
+supports ESMTP, including the 8BITMIME, DATA, PIPELINING, SIZE and AUTH options.
+.B qmail-smtpd
+includes a \'MAIL FROM:\' parameter parser and obeys \'Auth\' and \'Size\' advertisements.
+.B qmail-smtpd
+can accept LOGIN, PLAIN, and CRAM-MD5 AUTH types.  It invokes
+.IR checkprogram ,
+which reads on file descriptor 3 the username, a 0 byte, the password
+or CRAM-MD5 digest/response derived from the SMTP client,
+another 0 byte, a CRAM-MD5 challenge (if applicable to the AUTH type),
+and a final 0 byte.
+.I checkprogram
+invokes
+.I subprogram
+upon successful authentication, which should in turn return 0 to
+.BR qmail-smtpd ,
+effectively setting the environment variables $RELAYCLIENT and $TCPREMOTEINFO
+(any supplied value replaced with the authenticated username).
+.B qmail-smtpd
+will reject the authentication attempt if it receives a nonzero return
+value from
+.I checkprogram
+or
+.IR subprogram .
+
 .SH TRANSPARENCY
 .B qmail-smtpd
 converts the SMTP newline convention into the UNIX newline convention
@@ -37,11 +73,25 @@
 even though such messages violate the SMTP protocol.
 .SH "CONTROL FILES"
 .TP 5
+.I badhelo
+Unacceptable HELO/EHLO host names.
+.B qmail-smtpd
+will reject every recipient address for a message if
+the host name is listed in,
+or matches a POSIX regular expression pattern listed in,
+.IR badhelo .
+If the NOBADHELO environment variable is set, then the
+contents of
+.IR badhelo
+will be ignored.
+For more information, please have a look at doc/README.qregex.
+.TP 5
 .I badmailfrom
 Unacceptable envelope sender addresses.
 .B qmail-smtpd
 will reject every recipient address for a message
-if the envelope sender address is listed in
+if the envelope sender address is listed in, or matches a POSIX regular expression
+pattern listed in,
 .IR badmailfrom .
 A line in
 .I badmailfrom
@@ -49,6 +99,28 @@
 .BR @\fIhost ,
 meaning every address at
 .IR host .
+For more information, please have a look at doc/README.qregex.
+.TP 5
+.I badmailto
+Unacceptable recipient addresses.
+.B qmail-smtpd
+will reject every recipient address for a message if the recipient address
+is listed in,
+or matches a POSIX regular expression pattern listed in,
+.IR badmailto .
+For more information, please have a look at doc/README.qregex.
+.TP 5
+.I clientca.pem
+A list of Certifying Authority (CA) certificates that are used to verify
+the client-presented certificates during a TLS-encrypted session.
+
+.TP 5
+.I clientcrl.pem
+A list of Certificate Revocation Lists (CRLs). If present it
+should contain the CRLs of the CAs in 
+.I clientca.pem 
+and client certs will be checked for revocation.
+
 .TP 5
 .I databytes
 Maximum number of bytes allowed in a message,
@@ -76,6 +148,18 @@
 .B DATABYTES
 is set, it overrides
 .IR databytes .
+
+.TP 5
+.I dh1024.pem
+If these 1024 bit DH parameters are provided,
+.B qmail-smtpd
+will use them for TLS sessions instead of generating one on-the-fly 
+(which is very timeconsuming).
+.TP 5
+.I dh512.pem
+512 bit counterpart for 
+.B dh1024.pem. 
+
 .TP 5
 .I localiphost
 Replacement host name for local IP addresses.
@@ -151,6 +235,19 @@
 
 Envelope recipient addresses without @ signs are
 always allowed through.
+
+.TP 5
+.I rsa512.pem
+If this 512 bit RSA key is provided,
+.B qmail-smtpd
+will use it for TLS sessions instead of generating one on-the-fly.
+
+.TP 5
+.I servercert.pem
+SSL certificate to be presented to clients in
+TLS-encrypted sessions. Certifying Authority
+(CA) and intermediate certificates can be added at the end of the file.
+
 .TP 5
 .I smtpgreeting
 SMTP greeting message.
@@ -169,6 +266,24 @@
 .B qmail-smtpd
 will wait for each new buffer of data from the remote SMTP client.
 Default: 1200.
+
+.TP 5
+.I tlsclients
+A list of email addresses. When relay rules would reject an incoming message,
+.B qmail-smtpd
+can allow it if the client presents a certificate that can be verified against
+the CA list in
+.I clientca.pem
+and the certificate email address is in
+.IR tlsclients .
+
+.TP 5
+.I tlsserverciphers
+A set of OpenSSL cipher strings. Multiple ciphers contained in a
+string should be separated by a colon. If the environment variable
+.B TLSCIPHERS
+is set to such a string, it takes precedence.
+
 .SH "SEE ALSO"
 tcp-env(1),
 tcp-environ(5),
@@ -177,3 +292,6 @@
 qmail-newmrh(8),
 qmail-queue(8),
 qmail-remote(8)
+.SH "HISTORY"
+The patch enabling the ESMTP AUTH option is not part of the standard
+qmail-1.03 distribution.
diff -urN ../qmail-1.03_unpatched/qmail-smtpd.c ./qmail-smtpd.c
--- ../qmail-1.03_unpatched/qmail-smtpd.c	1998-06-15 12:53:16.000000000 +0200
+++ ./qmail-smtpd.c	2005-01-21 19:25:03.000000000 +0100
@@ -22,15 +22,44 @@
 #include "rcpthosts.h"
 #include "timeoutread.h"
 #include "timeoutwrite.h"
+
 #include "commands.h"
+#include "qregex.h"
+#include "wait.h"
+
+#define CRAM_MD5
+#define AUTHSLEEP 5
+
+#define BMCHECK_BMF 0
+#define BMCHECK_BMT 1
+#define BMCHECK_BHELO 2
 
 #define MAXHOPS 100
 unsigned int databytes = 0;
 int timeout = 1200;
 
+const char *protocol = "SMTP";
+
+#ifdef TLS
+# include "tls.h"
+# include "ssl_timeoutio.h"
+
+void tls_init();
+int tls_verify();
+void tls_nogateway();
+int ssl_rfd = -1, ssl_wfd = -1; /* SSL_get_Xfd() are broken */
+#endif
+
+#include "chkuser.h"
+
 int safewrite(fd,buf,len) int fd; char *buf; int len;
 {
   int r;
+#ifdef TLS
+  if (ssl && fd == ssl_wfd)
+    r = ssl_timeoutwrite(timeout, ssl_rfd, ssl_wfd, ssl, buf, len);
+  else
+#endif
   r = timeoutwrite(timeout,fd,buf,len);
   if (r <= 0) _exit(1);
   return r;
@@ -39,26 +68,63 @@
 char ssoutbuf[512];
 substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf);
 
+char sserrbuf[512];
+substdio sserr = SUBSTDIO_FDBUF(safewrite,2,sserrbuf,sizeof sserrbuf);
+
 void flush() { substdio_flush(&ssout); }
 void out(s) char *s; { substdio_puts(&ssout,s); }
 
+char strnum[FMT_ULONG];
+void logerr4 (s1,s2,s3,s4) char *s1; char *s2; char *s3; char *s4;
+{
+  strnum[fmt_ulong(strnum,getpid())] = 0;
+  substdio_puts(&sserr,"qmail-smtpd: pid "); /* pid */
+  substdio_puts(&sserr,strnum);
+  substdio_puts(&sserr,": ");
+  substdio_puts(&sserr,s1); substdio_puts(&sserr,s2); substdio_puts(&sserr,s3); substdio_puts(&sserr,s4);
+  substdio_puts(&sserr,"\n");
+  substdio_flush(&sserr);
+} 
+
 void die_read() { _exit(1); }
 void die_alarm() { out("451 timeout (#4.4.2)\r\n"); flush(); _exit(1); }
 void die_nomem() { out("421 out of memory (#4.3.0)\r\n"); flush(); _exit(1); }
 void die_control() { out("421 unable to read controls (#4.3.0)\r\n"); flush(); _exit(1); }
 void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); }
 void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); }
-
-void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); }
+void err_bmf() { out("553 sorry, your envelope sender has been denied (#5.7.1)\r\n"); }
+void err_bmt() { out("533 sorry, your envelope recipient has been denied (#5.7.1)\r\n"); }
+void err_bhelo() { out("553 sorry, your HELO host name has been denied (#5.7.1)\r\n"); }
+#ifndef TLS
 void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); }
-void err_unimpl() { out("502 unimplemented (#5.5.1)\r\n"); }
+#else
+void err_nogateway()
+{
+  out("553 sorry, that domain isn't in my list of allowed rcpthosts");
+  tls_nogateway();
+  out(" (#5.7.1)\r\n");
+}
+#endif
+void err_unimpl(arg) char *arg; { out("502 unimplemented (#5.5.1)\r\n"); }
+void err_size() { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); }
 void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); }
 void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); }
 void err_wantrcpt() { out("503 RCPT first (#5.5.1)\r\n"); }
-void err_noop() { out("250 ok\r\n"); }
-void err_vrfy() { out("252 send some mail, i'll try my best\r\n"); }
+void err_noop(arg) char *arg; { out("250 ok\r\n"); }
+void err_vrfy(arg) char *arg; { out("252 send some mail, i'll try my best\r\n"); }
 void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); }
+void err_noexec() { out("552 we don't accept email with executable content (#5.3.4)\r\n"); }
 
+int err_child() { out("454 oops, problem with child and I can't auth (#4.3.0)\r\n"); return -1; }
+int err_fork() { out("454 oops, child won't start and I can't auth (#4.3.0)\r\n"); return -1; }
+int err_pipe() { out("454 oops, unable to open pipe and I can't auth (#4.3.0)\r\n"); return -1; }
+int err_write() { out("454 oops, unable to write pipe and I can't auth (#4.3.0)\r\n"); return -1; }
+void err_authd() { out("503 you're already authenticated (#5.5.0)\r\n"); }
+void err_authmail() { out("503 no auth during mail transaction (#5.5.0)\r\n"); }
+int err_noauth() { out("504 auth type unimplemented (#5.5.1)\r\n"); return -1; }
+int err_authabrt() { out("501 auth exchange canceled (#5.0.0)\r\n"); return -1; }
+int err_input() { out("501 malformed auth input (#5.5.4)\r\n"); return -1; }
+void err_authfail() { out("535 authentication failed (#5.7.1)\r\n"); }
 
 stralloc greeting = {0};
 
@@ -67,11 +133,11 @@
   substdio_puts(&ssout,code);
   substdio_put(&ssout,greeting.s,greeting.len);
 }
-void smtp_help()
+void smtp_help(arg) char *arg;
 {
   out("214 qmail home page: http://pobox.com/~djb/qmail.html\r\n");
 }
-void smtp_quit()
+void smtp_quit(arg) char *arg;
 {
   smtp_greet("221 "); out("\r\n"); flush(); _exit(0);
 }
@@ -91,11 +157,19 @@
   fakehelo = case_diffs(remotehost,helohost.s) ? helohost.s : 0;
 }
 
+int checkexecutable = 1;
 int liphostok = 0;
 stralloc liphost = {0};
 int bmfok = 0;
+int bmtok = 0;
+int bhelook = 0;
+stralloc bmt = {0};
 stralloc bmf = {0};
-struct constmap mapbmf;
+stralloc bhelo = {0};
+int sigsok = 0;
+stralloc sigs = {0};
+
+int flagauth = 0; /* moved here to use it in mfcheck and others */
 
 void setup()
 {
@@ -114,9 +188,17 @@
 
   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();
+
+  bmtok = control_readfile(&bmt,"control/badmailto",0);
+  if (bmtok == -1) die_control();
  
+  bhelook = control_readfile(&bhelo, "control/badhelo",0);
+  if (bhelook == -1) die_control();
+  if (env_get("NOBADHELO")) bhelook = 0;
+
+  sigsok = control_readfile(&sigs,"control/signatures",0);
+  if (sigsok == -1) die_control();
+
   if (control_readint(&databytes,"control/databytes") == -1) die_control();
   x = env_get("DATABYTES");
   if (x) { scan_ulong(x,&u); databytes = u; }
@@ -131,6 +213,11 @@
   if (!remotehost) remotehost = "unknown";
   remoteinfo = env_get("TCPREMOTEINFO");
   relayclient = env_get("RELAYCLIENT");
+  if (env_get("EXECUTABLEOK")) checkexecutable = 0;
+#ifdef TLS
+  if (env_get("SMTPS")) { smtps = 1; tls_init(); }
+  else
+#endif
   dohelo(remotehost);
 }
 
@@ -197,14 +284,57 @@
   return 1;
 }
 
-int bmfcheck()
+int bmcheck(which) int which;
 {
-  int j;
-  if (!bmfok) return 0;
-  if (constmap(&mapbmf,addr.s,addr.len - 1)) return 1;
-  j = byte_rchr(addr.s,addr.len,'@');
-  if (j < addr.len)
-    if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) return 1;
+  int i = 0;
+  int j = 0;
+  int x = 0;
+  int negate = 0;
+  static stralloc bmb = {0};
+  static stralloc curregex = {0};
+
+  if (which == BMCHECK_BMF) {
+    if (!stralloc_copy(&bmb,&bmf)) die_nomem();
+  } else if (which == BMCHECK_BMT) {
+    if (!stralloc_copy(&bmb,&bmt)) die_nomem();
+  } else if (which == BMCHECK_BHELO) {
+    if (!stralloc_copy(&bmb,&bhelo)) die_nomem();
+  } else {
+    die_control();
+  }
+
+  while (j < bmb.len) {
+    i = j;
+    while ((bmb.s[i] != '\0') && (i < bmb.len)) i++;
+    if (bmb.s[j] == '!') {
+      negate = 1;
+      j++;
+    }
+    if (!stralloc_copyb(&curregex,bmb.s + j,(i - j))) die_nomem();
+    if (!stralloc_0(&curregex)) die_nomem();
+    if (which == BMCHECK_BHELO) {
+      x = matchregex(helohost.s, curregex.s);
+    } else {
+      x = matchregex(addr.s, curregex.s);
+    }
+    if ((negate) && (x == 0)) return 1;
+    if (!(negate) && (x > 0)) return 1;
+    j = i + 1;
+    negate = 0;
+  }
+  return 0;
+}
+
+int sigscheck(stralloc *line) {
+  int i, j;
+
+  j = 0;
+  for (i = 0; i < sigs.len; i++) if (!sigs.s[i]) {
+    if (i-j < line->len)
+      if (!str_diffn(line->s,sigs.s+j,i-j))
+       return 1;
+    j = i+1;
+  }
   return 0;
 }
 
@@ -213,26 +343,112 @@
   int r;
   r = rcpthosts(addr.s,str_len(addr.s));
   if (r == -1) die_control();
+#ifdef TLS
+  if (r == 0) if (tls_verify()) r = -2;
+#endif
   return r;
 }
 
 
 int seenmail = 0;
-int flagbarf; /* defined if seenmail */
+int flagbarfbmf; /* defined if seenmail */
+int flagbarfbmt;
+int flagbarfbhelo;
+int flagsize;
 stralloc mailfrom = {0};
 stralloc rcptto = {0};
+stralloc fuser = {0};
+stralloc mfparms = {0};
+
+int mailfrom_size(arg) char *arg;
+{
+  long r;
+  unsigned long sizebytes = 0;
+
+  scan_ulong(arg,&r);
+  sizebytes = r;
+  if (databytes) if (sizebytes > databytes) return 1;
+  return 0;
+}
+
+void mailfrom_auth(arg,len) 
+char *arg; 
+int len;
+{
+  int j;
+
+  if (!stralloc_copys(&fuser,"")) die_nomem();
+  if (case_starts(arg,"<>")) { if (!stralloc_cats(&fuser,"unknown")) die_nomem(); }
+  else 
+    while (len) {
+      if (*arg == '+') {
+        if (case_starts(arg,"+3D")) { arg=arg+2; len=len-2; if (!stralloc_cats(&fuser,"=")) die_nomem(); }
+        if (case_starts(arg,"+2B")) { arg=arg+2; len=len-2; if (!stralloc_cats(&fuser,"+")) die_nomem(); }
+      }
+      else
+        if (!stralloc_catb(&fuser,arg,1)) die_nomem();
+      arg++; len--;
+    }
+  if(!stralloc_0(&fuser)) die_nomem();
+  if (!remoteinfo) {
+    remoteinfo = fuser.s;
+    if (!env_unset("TCPREMOTEINFO")) die_read();
+    if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem();
+  }
+}
+
+void mailfrom_parms(arg) char *arg;
+{
+  int i;
+  int len;
+
+    len = str_len(arg);
+    if (!stralloc_copys(&mfparms,"")) die_nomem;
+    i = byte_chr(arg,len,'>');
+    if (i > 4 && i < len) {
+      while (len) {
+        arg++; len--; 
+        if (*arg == ' ' || *arg == '\0' ) {
+           if (case_starts(mfparms.s,"SIZE=")) if (mailfrom_size(mfparms.s+5)) { flagsize = 1; return; }
+           if (case_starts(mfparms.s,"AUTH=")) mailfrom_auth(mfparms.s+5,mfparms.len-5);  
+           if (!stralloc_copys(&mfparms,"")) die_nomem;
+        }
+        else
+          if (!stralloc_catb(&mfparms,arg,1)) die_nomem; 
+      }
+    }
+}
+
 
 void smtp_helo(arg) char *arg;
 {
   smtp_greet("250 "); out("\r\n");
   seenmail = 0; dohelo(arg);
+  if (bhelook) flagbarfbhelo = bmcheck(BMCHECK_BHELO);
 }
+
+/* ESMTP extensions are published here */
 void smtp_ehlo(arg) char *arg;
 {
-  smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n");
+  char size[FMT_ULONG];
+  size[fmt_ulong(size,(unsigned int) databytes)] = 0;
+  smtp_greet("250-");
+#ifdef TLS
+  if (!ssl) out("\r\n250-STARTTLS");
+#endif
+#ifdef CRAM_MD5
+  out("\r\n250-AUTH LOGIN CRAM-MD5 PLAIN");
+  out("\r\n250-AUTH=LOGIN CRAM-MD5 PLAIN");
+#else
+  out("\r\n250-AUTH LOGIN PLAIN");
+  out("\r\n250-AUTH=LOGIN PLAIN");
+#endif
+  out("\r\n250-PIPELINING\r\n250-8BITMIME");
+  out("\r\n250 SIZE "); out(size); out("\r\n");
   seenmail = 0; dohelo(arg);
+  if (bhelook) flagbarfbhelo = bmcheck(BMCHECK_BHELO);
 }
-void smtp_rset()
+void smtp_rset(arg) char *arg;
 {
   seenmail = 0;
   out("250 flushed\r\n");
@@ -240,7 +456,15 @@
 void smtp_mail(arg) char *arg;
 {
   if (!addrparse(arg)) { err_syntax(); return; }
-  flagbarf = bmfcheck();
+  flagsize = 0;
+  mailfrom_parms(arg);
+  if (flagsize) { err_size(); return; }
+  /* start chkuser code */
+  if (chkuser_sender (&addr) != CHKUSER_OK) { return; }
+  /* end chkuser code */
+
+  flagbarfbmf = 0; /* bmcheck is skipped for empty envelope senders */
+  if ((addr.len != 1) && (bmfok)) flagbarfbmf = bmcheck(BMCHECK_BMF);
   seenmail = 1;
   if (!stralloc_copys(&rcptto,"")) die_nomem();
   if (!stralloc_copys(&mailfrom,addr.s)) die_nomem();
@@ -250,7 +474,14 @@
 void smtp_rcpt(arg) char *arg; {
   if (!seenmail) { err_wantmail(); return; }
   if (!addrparse(arg)) { err_syntax(); return; }
-  if (flagbarf) { err_bmf(); return; }
+  if (flagbarfbhelo) { logerr4("bad HELO <",helohost.s,"> at ",remoteip); err_bhelo(); return; }
+  if (flagbarfbmf) { logerr4("bad MAIL FROM <",mailfrom.s,"> at ",remoteip); err_bmf(); return; }
+  if (bmtok) { flagbarfbmt = bmcheck(BMCHECK_BMT); }
+  if (flagbarfbmt) { logerr4("bad RCPT TO <",addr.s,"> at ",remoteip); err_bmt(); return; }
+
+/*
+ * Original code substituted by chkuser code
+
   if (relayclient) {
     --addr.len;
     if (!stralloc_cats(&addr,relayclient)) die_nomem();
@@ -258,6 +489,26 @@
   }
   else
     if (!addrallowed()) { err_nogateway(); return; }
+
+ * end of substituted code
+ */
+
+/* start chkuser code */
+  switch (chkuser_realrcpt (&mailfrom, &addr)) {
+
+        case CHKUSER_KO:
+                return;
+                break;
+
+        case CHKUSER_RELAYING:
+                --addr.len;
+                if (!stralloc_cats(&addr,relayclient)) die_nomem();
+                if (!stralloc_0(&addr)) die_nomem();
+                break;
+
+  }
+/* end chkuser code */
+
   if (!stralloc_cats(&rcptto,"T")) die_nomem();
   if (!stralloc_cats(&rcptto,addr.s)) die_nomem();
   if (!stralloc_0(&rcptto)) die_nomem();
@@ -269,6 +520,11 @@
 {
   int r;
   flush();
+#ifdef TLS
+  if (ssl && fd == ssl_rfd)
+    r = ssl_timeoutread(timeout, ssl_rfd, ssl_wfd, ssl, buf, len);
+  else
+#endif
   r = timeoutread(timeout,fd,buf,len);
   if (r == -1) if (errno == error_timeout) die_alarm();
   if (r <= 0) die_read();
@@ -281,9 +537,149 @@
 struct qmail qqt;
 unsigned int bytestooverflow = 0;
 
+int linespastheader;       /* =0 after boundary is found in body, */
+                                /* until blank line */
+char linetype;
+int flagexecutable;
+int flagqsbmf;
+
+stralloc line = {0};
+stralloc content = {0};
+stralloc boundary = {0};
+int boundary_start;
+
+/*
+
+def put(ch):
+    line.append(ch)
+    if ch == '\n':
+        if linepastheader == 0:
+            if line.startswith('Content-Type:'):
+                content = 
+
+ put() puts characters into the queue.  We remember those characters
+   and form them into a line.  When we get a newline, we examine the
+   line.  If we're currently in a header (0 linespastheader), we look
+   for Content-Type.  If we're at the newline that ends a header, we
+   look to see if the content is multipart.  If it is, then we push
+   the current boundary, remember the boundary, otherwise we set the
+   boundary to the empty string.  Set the linespastheader to 1.  When
+   linespastheader is 1, and the boundary is empty, scan the line for
+   signatures.  If the boundary is non-empty, look for a match against
+   the boundary.  If it matches and is followed by two dashes, pop the
+   boundary, otherwise set linespastheader to 0.
+*/
+
 void put(ch)
 char *ch;
 {
+  char *cp, *cpstart, *cpafter;
+  unsigned int len;
+
+  if (checkexecutable) {
+    if (line.len < 1024)
+      if (!stralloc_catb(&line,ch,1)) die_nomem();
+  
+    if (*ch == '\n') {
+      if (linespastheader == 0) {
+        if (line.len == 1) {
+         linespastheader = 1;    
+         if (flagqsbmf) {
+            flagqsbmf = 0;
+            linespastheader = 0;
+          }
+         if (content.len) {                      /* MIME header */
+           cp = content.s;
+           len = content.len;
+           while (len && (*cp == ' ' || *cp == '\t')) { ++cp; --len; }
+           cpstart = cp;
+           if (len && *cp == '"') {                      /* might be commented */
+             ++cp; --len; cpstart = cp;
+             while (len && *cp != '"') { ++cp; --len; }
+           } else {
+             while (len && *cp != ' ' && *cp != '\t' && *cp != ';') {
+               ++cp; --len;
+             }
+           }
+           if (!case_diffb(cpstart,cp-cpstart,"message/rfc822"))
+             linespastheader = 0;
+
+           cpafter = content.s+content.len;
+           while((cp += byte_chr(cp,cpafter-cp,';')) != cpafter) {
+             ++cp;
+             while (cp < cpafter && (*cp == ' ' || *cp == '\t')) ++cp;
+             if (case_startb(cp,cpafter - cp,"boundary=")) {
+               cp += 9;                  /* after boundary= */
+               if (cp < cpafter && *cp == '"') {
+                 ++cp;
+                 cpstart = cp;
+                 while (cp < cpafter && *cp != '"') ++cp;
+               } else {
+                 cpstart = cp;
+                 while (cp < cpafter &&
+                    *cp != ';' && *cp != ' ' && *cp != '\t') ++cp;
+               }
+               /* push the current boundary.  Append a null and remember start. */
+               if (!stralloc_0(&boundary)) die_nomem();
+               boundary_start = boundary.len;
+               if (!stralloc_cats(&boundary,"--")) die_nomem();
+               if (!stralloc_catb(&boundary,cpstart,cp-cpstart))
+                       die_nomem();
+               break;
+             }
+           }
+         }
+        } else { /* non-blank header line */
+         if ((*line.s == ' ' || *line.s == '\t')) {
+           switch(linetype) {
+             case 'C': if (!stralloc_catb(&content,line.s,line.len-1)) die_nomem(); break;
+             default: break;
+           }
+         } else {
+           if (case_startb(line.s,line.len,"content-type:")) {
+             if (!stralloc_copyb(&content,line.s+13,line.len-14)) die_nomem();
+             linetype = 'C';
+           } else {
+             linetype = ' ';
+           }
+         }
+        }
+      } else {      /* non-header line */
+        if (boundary.len-boundary_start && *line.s == '-' && line.len > (boundary.len-boundary_start) &&
+           !str_diffn(line.s,boundary.s+boundary_start,boundary.len-boundary_start)) { /* matches a boundary */
+         if (line.len > boundary.len-boundary_start + 2 &&
+              line.s[boundary.len-boundary_start+0] == '-' &&
+              line.s[boundary.len-boundary_start+1] == '-') {
+           /* XXXX - pop the boundary here */
+           if (boundary_start) boundary.len = boundary_start - 1;
+           boundary_start = boundary.len;
+           while(boundary_start--) if (!boundary.s[boundary_start]) break;
+           boundary_start++;
+           linespastheader = 2;
+         } else {
+           linespastheader = 0;
+         }
+       } else if (linespastheader == 1) { /* first line -- match a signature? */
+         if (/*mailfrom.s[0] == '\0' && */
+             str_start(line.s,"Hi. This is the "))
+           flagqsbmf = 1;
+         else if (/*mailfrom.s[0] == '\0' && */
+             str_start(line.s,"This message was created automatically by mail delivery software"))
+           flagqsbmf = 1;
+         else if (sigscheck(&line)) {
+           flagexecutable = 1;
+           qmail_fail(&qqt);
+         }
+         linespastheader = 2;
+        }
+        if (flagqsbmf && str_start(line.s,"---")) {
+          linespastheader = 0;
+        }
+      }
+      line.len = 0;
+    }
+  }
+
   if (bytestooverflow)
     if (!--bytestooverflow)
       qmail_fail(&qqt);
@@ -316,8 +712,8 @@
         if (flagmaybex) if (pos == 7) ++*hops;
         if (pos < 2) if (ch != "\r\n"[pos]) flagmaybey = 0;
         if (flagmaybey) if (pos == 1) flaginheader = 0;
+	++pos;
       }
-      ++pos;
       if (ch == '\n') { pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; }
     }
     switch(state) {
@@ -365,7 +761,7 @@
   out("\r\n");
 }
 
-void smtp_data() {
+void smtp_data(arg) char *arg; {
   int hops;
   unsigned long qp;
   char *qqx;
@@ -374,11 +770,18 @@
   if (!rcptto.len) { err_wantrcpt(); return; }
   seenmail = 0;
   if (databytes) bytestooverflow = databytes + 1;
+  boundary.len = 0;
+  boundary_start = 0;
+  content.len = 0;
+  linespastheader = 0;
+  flagexecutable = 0;
+  flagqsbmf = 0;
+  linetype = ' ';
   if (qmail_open(&qqt) == -1) { err_qqt(); return; }
   qp = qmail_qp(&qqt);
   out("354 go ahead\r\n");
  
-  received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo);
+  received(&qqt,protocol,local,remoteip,remotehost,remoteinfo,fakehelo);
   blast(&hops);
   hops = (hops >= MAXHOPS);
   if (hops) qmail_fail(&qqt);
@@ -388,28 +791,489 @@
   qqx = qmail_close(&qqt);
   if (!*qqx) { acceptmessage(qp); return; }
   if (hops) { out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; }
-  if (databytes) if (!bytestooverflow) { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); return; }
+  if (databytes) if (!bytestooverflow) { err_size(); return; }
+  if (flagexecutable) { logerr4("executable content from <",mailfrom.s,"> at ",remoteip); err_noexec(); return; }
   if (*qqx == 'D') out("554 "); else out("451 ");
   out(qqx + 1);
   out("\r\n");
 }
 
+char unique[FMT_ULONG + FMT_ULONG + 3];
+static stralloc authin = {0};   /* input from SMTP client */
+static stralloc user = {0};     /* plain userid */
+static stralloc pass = {0};     /* plain passwd or digest */
+#ifdef CRAM_MD5
+static stralloc chal = {0};     /* plain challenge */
+static stralloc slop = {0};     /* b64 challenge */
+#endif
+static stralloc resp = {0};     /* b64 response */
+
+char **childargs;
+char ssauthbuf[512];
+substdio ssauth = SUBSTDIO_FDBUF(safewrite,3,ssauthbuf,sizeof(ssauthbuf));
+
+int authgetl(void) {
+  int i;
+
+  if (!stralloc_copys(&authin,"")) die_nomem();
+  for (;;) {
+    if (!stralloc_readyplus(&authin,1)) die_nomem(); /* XXX */
+    i = substdio_get(&ssin,authin.s + authin.len,1);
+    if (i != 1) die_read();
+    if (authin.s[authin.len] == '\n') break;
+    ++authin.len;
+  }
+
+  if (authin.len > 0) if (authin.s[authin.len - 1] == '\r') --authin.len;
+  authin.s[authin.len] = 0;
+  if (*authin.s == '*' && *(authin.s + 1) == 0) { return err_authabrt(); }
+  if (authin.len == 0) { return err_input(); }
+  return authin.len;
+}
+
+int authenticate(void)
+{
+  int child;
+  int wstat;
+  int pi[2];
+
+  if (!stralloc_0(&user)) die_nomem();
+  if (!stralloc_0(&pass)) die_nomem();
+#ifdef CRAM_MD5
+  if (!stralloc_0(&chal)) die_nomem();
+#endif
+
+  if (pipe(pi) == -1) return err_pipe();
+  switch(child = fork()) {
+    case -1:
+      return err_fork();
+    case 0:
+      close(pi[1]);
+      if(fd_copy(3,pi[0]) == -1) return err_pipe();
+      sig_pipedefault();
+        execvp(*childargs, childargs);
+      _exit(1);
+  }
+  close(pi[0]);
+
+  substdio_fdbuf(&ssauth,write,pi[1],ssauthbuf,sizeof ssauthbuf);
+  if (substdio_put(&ssauth,user.s,user.len) == -1) return err_write();
+  if (substdio_put(&ssauth,pass.s,pass.len) == -1) return err_write();
+#ifdef CRAM_MD5
+  if (substdio_put(&ssauth,chal.s,chal.len) == -1) return err_write();
+#endif
+  if (substdio_flush(&ssauth) == -1) return err_write();
+
+  close(pi[1]);
+#ifdef CRAM_MD5
+  if (!stralloc_copys(&chal,"")) die_nomem();
+  if (!stralloc_copys(&slop,"")) die_nomem();
+#endif
+  byte_zero(ssauthbuf,sizeof ssauthbuf);
+  if (wait_pid(&wstat,child) == -1) return err_child();
+  if (wait_crashed(wstat)) return err_child();
+  if (wait_exitcode(wstat)) { sleep(AUTHSLEEP); return 1; } /* no */
+  return 0; /* yes */
+}
+
+int auth_login(arg) char *arg;
+{
+  int r;
+
+  if (*arg) {
+    if (r = b64decode(arg,str_len(arg),&user) == 1) return err_input();
+  }
+  else {
+    out("334 VXNlcm5hbWU6\r\n"); flush();       /* Username: */
+    if (authgetl() < 0) return -1;
+    if (r = b64decode(authin.s,authin.len,&user) == 1) return err_input();
+  }
+  if (r == -1) die_nomem();
+
+  out("334 UGFzc3dvcmQ6\r\n"); flush();         /* Password: */
+
+  if (authgetl() < 0) return -1;
+  if (r = b64decode(authin.s,authin.len,&pass) == 1) return err_input();
+  if (r == -1) die_nomem();
+
+  if (!user.len || !pass.len) return err_input();
+  return authenticate();
+}
+
+int auth_plain(arg) char *arg;
+{
+  int r, id = 0;
+
+  if (*arg) {
+    if (r = b64decode(arg,str_len(arg),&resp) == 1) return err_input();
+  }
+  else {
+    out("334 \r\n"); flush();
+    if (authgetl() < 0) return -1;
+    if (r = b64decode(authin.s,authin.len,&resp) == 1) return err_input();
+  }
+  if (r == -1 || !stralloc_0(&resp)) die_nomem();
+  while (resp.s[id]) id++;                       /* "authorize-id\0userid\0passwd\0" */
+
+  if (resp.len > id + 1)
+    if (!stralloc_copys(&user,resp.s + id + 1)) die_nomem();
+  if (resp.len > id + user.len + 2)
+    if (!stralloc_copys(&pass,resp.s + id + user.len + 2)) die_nomem();
+
+  if (!user.len || !pass.len) return err_input();
+  return authenticate();
+}
+
+#ifdef CRAM_MD5
+int auth_cram()
+{
+  int i, r;
+  char *s;
+
+  s = unique;
+  s += fmt_uint(s,getpid());
+  *s++ = '.';
+  s += fmt_ulong(s,(unsigned long) now());
+  *s++ = '@';
+  *s++ = 0;
+
+  if (!stralloc_copys(&chal,"<")) die_nomem();          /* generate challenge */
+  if (!stralloc_cats(&chal,unique)) die_nomem();
+  if (!stralloc_cats(&chal,local)) die_nomem();
+  if (!stralloc_cats(&chal,">")) die_nomem();
+  if (b64encode(&chal,&slop) < 0) die_nomem();
+  if (!stralloc_0(&slop)) die_nomem();
+
+  out("334 ");                                          /* "334 mychallenge \r\n" */
+  out(slop.s);
+  out("\r\n");
+  flush();
+
+  if (authgetl() < 0) return -1;                        /* got response */
+  if (r = b64decode(authin.s,authin.len,&resp) == 1) return err_input();
+  if (r == -1 || !stralloc_0(&resp)) die_nomem();
+
+  i = str_chr(resp.s,' ');
+  s = resp.s + i;
+  while (*s == ' ') ++s;
+  resp.s[i] = 0;
+  if (!stralloc_copys(&user,resp.s)) die_nomem();       /* userid */
+  if (!stralloc_copys(&pass,s)) die_nomem();            /* digest */
+
+  if (!user.len || !pass.len) return err_input();
+  return authenticate();
+}
+#endif
+
+struct authcmd {
+  char *text;
+  int (*fun)();
+} authcmds[] = {
+  { "login",auth_login }
+,  { "plain",auth_plain }
+#ifdef CRAM_MD5
+, { "cram-md5",auth_cram }
+#endif
+, { 0,err_noauth }
+};
+
+void smtp_auth(arg)
+char *arg;
+{
+  int i;
+  char *cmd = arg;
+
+  if (!*childargs) { out("503 auth not available (#5.3.3)\r\n"); return; }
+  if (flagauth) { err_authd(); return; }
+  if (seenmail) { err_authmail(); return; }
+
+  if (!stralloc_copys(&user,"")) die_nomem();
+  if (!stralloc_copys(&pass,"")) die_nomem();
+  if (!stralloc_copys(&resp,"")) die_nomem();
+#ifdef CRAM_MD5
+  if (!stralloc_copys(&chal,"")) die_nomem();
+#endif
+
+  i = str_chr(cmd,' ');
+  arg = cmd + i;
+  while (*arg == ' ') ++arg;
+  cmd[i] = 0;
+
+  for (i = 0;authcmds[i].text;++i)
+    if (case_equals(authcmds[i].text,cmd)) break;
+
+  switch (authcmds[i].fun(arg)) {
+    case 0:
+      flagauth = 1;
+      relayclient = "";
+      remoteinfo = user.s;
+      if (!env_unset("TCPREMOTEINFO")) die_read();
+      if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem();
+      if (!env_put2("RELAYCLIENT",relayclient)) die_nomem();
+      out("235 ok, go ahead (#2.0.0)\r\n");
+      break;
+    case 1:
+      err_authfail(user.s,authcmds[i].text);
+  }
+}
+
+#ifdef TLS
+stralloc proto = {0};
+int ssl_verified = 0;
+const char *ssl_verify_err = 0;
+
+void smtp_tls(char *arg)
+{
+  if (ssl) err_unimpl();
+  else if (*arg) out("501 Syntax error (no parameters allowed) (#5.5.4)\r\n");
+  else tls_init();
+}
+
+RSA *tmp_rsa_cb(SSL *ssl, int export, int keylen)
+{
+  if (!export) keylen = 512;
+  if (keylen == 512) {
+    FILE *in = fopen("control/rsa512.pem", "r");
+    if (in) {
+      RSA *rsa = PEM_read_RSAPrivateKey(in, NULL, NULL, NULL);
+      fclose(in);
+      if (rsa) return rsa;
+    }
+  }
+  return RSA_generate_key(keylen, RSA_F4, NULL, NULL);
+}
+
+DH *tmp_dh_cb(SSL *ssl, int export, int keylen)
+{
+  if (!export) keylen = 1024;
+  if (keylen == 512) {
+    FILE *in = fopen("control/dh512.pem", "r");
+    if (in) {
+      DH *dh = PEM_read_DHparams(in, NULL, NULL, NULL);
+      fclose(in);
+      if (dh) return dh;
+    }
+  }
+  if (keylen == 1024) {
+    FILE *in = fopen("control/dh1024.pem", "r");
+    if (in) {
+      DH *dh = PEM_read_DHparams(in, NULL, NULL, NULL);
+      fclose(in);
+      if (dh) return dh;
+    }
+  }
+  return DH_generate_parameters(keylen, DH_GENERATOR_2, NULL, NULL);
+} 
+
+/* don't want to fail handshake if cert isn't verifiable */
+int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; }
+
+void tls_nogateway()
+{
+  /* there may be cases when relayclient is set */
+  if (!ssl || relayclient) return;
+  out("; no valid cert for gatewaying");
+  if (ssl_verify_err) { out(": "); out(ssl_verify_err); }
+}
+void tls_out(const char *s1, const char *s2)
+{
+  out("454 TLS "); out(s1);
+  if (s2) { out(": "); out(s2); }
+  out(" (#4.3.0)\r\n"); flush();
+}
+void tls_err(const char *s) { tls_out(s, ssl_error()); if (smtps) die_read(); }
+
+# define CLIENTCA "control/clientca.pem"
+# define CLIENTCRL "control/clientcrl.pem"
+# define SERVERCERT "control/servercert.pem"
+
+int tls_verify()
+{
+  stralloc clients = {0};
+  struct constmap mapclients;
+
+  if (!ssl || relayclient || ssl_verified) return 0;
+  ssl_verified = 1; /* don't do this twice */
+
+  /* request client cert to see if it can be verified by one of our CAs
+   * and the associated email address matches an entry in tlsclients */
+  switch (control_readfile(&clients, "control/tlsclients", 0))
+  {
+  case 1:
+    if (constmap_init(&mapclients, clients.s, clients.len, 0)) {
+      /* if CLIENTCA contains all the standard root certificates, a
+       * 0.9.6b client might fail with SSL_R_EXCESSIVE_MESSAGE_SIZE;
+       * it is probably due to 0.9.6b supporting only 8k key exchange
+       * data while the 0.9.6c release increases that limit to 100k */
+      STACK_OF(X509_NAME) *sk = SSL_load_client_CA_file(CLIENTCA);
+      if (sk) {
+        SSL_set_client_CA_list(ssl, sk);
+        SSL_set_verify(ssl, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, NULL);
+        break;
+      }
+      constmap_free(&mapclients);
+    }
+  case 0: alloc_free(clients.s); return 0;
+  case -1: die_control();
+  }
+
+  if (ssl_timeoutrehandshake(timeout, ssl_rfd, ssl_wfd, ssl) <= 0) {
+    const char *err = ssl_strerror();
+    tls_out("rehandshake failed", err); die_read();
+  }
+
+  do { /* one iteration */
+    X509 *peercert;
+    X509_NAME *subj;
+    stralloc email = {0};
+
+    int n = SSL_get_verify_result(ssl);
+    if (n != X509_V_OK)
+      { ssl_verify_err = X509_verify_cert_error_string(n); break; }
+    peercert = SSL_get_peer_certificate(ssl);
+    if (!peercert) break;
+
+    subj = X509_get_subject_name(peercert);
+    n = X509_NAME_get_index_by_NID(subj, NID_pkcs9_emailAddress, -1);
+    if (n >= 0) {
+      const ASN1_STRING *s = X509_NAME_get_entry(subj, n)->value;
+      if (s) { email.len = s->length; email.s = s->data; }
+    }
+
+    if (email.len <= 0)
+      ssl_verify_err = "contains no email address";
+    else if (!constmap(&mapclients, email.s, email.len))
+      ssl_verify_err = "email address not in my list of tlsclients";
+    else {
+      /* add the cert email to the proto if it helped allow relaying */
+      --proto.len;
+      if (!stralloc_cats(&proto, "\n  (cert ") /* continuation line */
+        || !stralloc_catb(&proto, email.s, email.len)
+        || !stralloc_cats(&proto, ")")
+        || !stralloc_0(&proto)) die_nomem();
+      relayclient = "";
+      protocol = proto.s;
+    }
+
+    X509_free(peercert);
+  } while (0);
+  constmap_free(&mapclients); alloc_free(clients.s);
+
+  /* we are not going to need this anymore: free the memory */
+  SSL_set_client_CA_list(ssl, NULL);
+  SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL);
+
+  return relayclient ? 1 : 0;
+}
+
+void tls_init()
+{
+  SSL *myssl;
+  SSL_CTX *ctx;
+  const char *ciphers;
+  stralloc saciphers = {0};
+  X509_STORE *store;
+  X509_LOOKUP *lookup;
+
+  SSL_library_init();
+
+  /* a new SSL context with the bare minimum of options */
+  ctx = SSL_CTX_new(SSLv23_server_method());
+  if (!ctx) { tls_err("unable to initialize ctx"); return; }
+
+  if (!SSL_CTX_use_certificate_chain_file(ctx, SERVERCERT))
+    { SSL_CTX_free(ctx); tls_err("missing certificate"); return; }
+  SSL_CTX_load_verify_locations(ctx, CLIENTCA, NULL);
+
+#if OPENSSL_VERSION_NUMBER >= 0x00907000L
+  /* crl checking */
+  store = SSL_CTX_get_cert_store(ctx);
+  if ((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) &&
+      (X509_load_crl_file(lookup, CLIENTCRL, X509_FILETYPE_PEM) == 1))
+    X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK |
+                                X509_V_FLAG_CRL_CHECK_ALL);
+#endif
+
+  /* set the callback here; SSL_set_verify didn't work before 0.9.6c */
+  SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, verify_cb);
+
+  /* a new SSL object, with the rest added to it directly to avoid copying */
+  myssl = SSL_new(ctx);
+  SSL_CTX_free(ctx);
+  if (!myssl) { tls_err("unable to initialize ssl"); return; }
+
+  /* this will also check whether public and private keys match */
+  if (!SSL_use_RSAPrivateKey_file(myssl, SERVERCERT, SSL_FILETYPE_PEM))
+    { SSL_free(myssl); tls_err("no valid RSA private key"); return; }
+
+  ciphers = env_get("TLSCIPHERS");
+  if (!ciphers) {
+    if (control_readfile(&saciphers, "control/tlsserverciphers") == -1)
+      { SSL_free(myssl); die_control(); }
+    if (saciphers.len) { /* convert all '\0's except the last one to ':' */
+      int i;
+      for (i = 0; i < saciphers.len - 1; ++i)
+        if (!saciphers.s[i]) saciphers.s[i] = ':';
+      ciphers = saciphers.s;
+    }
+  }
+  if (!ciphers || !*ciphers) ciphers = "DEFAULT";
+  SSL_set_cipher_list(myssl, ciphers);
+  alloc_free(saciphers.s);
+
+  SSL_set_tmp_rsa_callback(myssl, tmp_rsa_cb);
+  SSL_set_tmp_dh_callback(myssl, tmp_dh_cb);
+  SSL_set_rfd(myssl, ssl_rfd = substdio_fileno(&ssin));
+  SSL_set_wfd(myssl, ssl_wfd = substdio_fileno(&ssout));
+
+  if (!smtps) { out("220 ready for tls\r\n"); flush(); }
+
+  if (ssl_timeoutaccept(timeout, ssl_rfd, ssl_wfd, myssl) <= 0) {
+    /* neither cleartext nor any other response here is part of a standard */
+    const char *err = ssl_strerror();
+    ssl_free(myssl); tls_out("connection failed", err); die_read();
+  }
+  ssl = myssl;
+
+  /* populate the protocol string, used in Received */
+  if (!stralloc_copys(&proto, "(")
+    || !stralloc_cats(&proto, SSL_get_cipher(ssl))
+    || !stralloc_cats(&proto, " encrypted) SMTP")) die_nomem();
+  if (!stralloc_0(&proto)) die_nomem();
+  protocol = proto.s;
+
+  /* have to discard the pre-STARTTLS HELO/EHLO argument, if any */
+  dohelo(remotehost);
+}
+
+# undef SERVERCERT
+# undef CLIENTCA
+
+#endif
+
 struct commands smtpcommands[] = {
   { "rcpt", smtp_rcpt, 0 }
 , { "mail", smtp_mail, 0 }
 , { "data", smtp_data, flush }
+, { "auth", smtp_auth, flush }
 , { "quit", smtp_quit, flush }
 , { "helo", smtp_helo, flush }
 , { "ehlo", smtp_ehlo, flush }
 , { "rset", smtp_rset, 0 }
 , { "help", smtp_help, flush }
+#ifdef TLS
+, { "starttls", smtp_tls, flush }
+#endif
 , { "noop", err_noop, flush }
 , { "vrfy", err_vrfy, flush }
 , { 0, err_unimpl, flush }
 } ;
 
-void main()
+void main(argc,argv)
+int argc;
+char **argv;
 {
+  childargs = argv + 1;
   sig_pipeignore();
   if (chdir(auto_qmail) == -1) die_control();
   setup();
diff -urN ../qmail-1.03_unpatched/qmail.c ./qmail.c
--- ../qmail-1.03_unpatched/qmail.c	1998-06-15 12:53:16.000000000 +0200
+++ ./qmail.c	2005-01-21 19:25:03.000000000 +0100
@@ -6,28 +6,49 @@
 #include "fd.h"
 #include "qmail.h"
 #include "auto_qmail.h"
+#include "env.h"
 
-static char *binqqargs[2] = { "bin/qmail-queue", 0 } ;
+static char *binqqargs[2] = { 0, 0 } ;
+
+static void setup_qqargs()
+{
+  if(!binqqargs[0])
+    binqqargs[0] = env_get("QMAILQUEUE");
+  if(!binqqargs[0])
+    binqqargs[0] = "bin/qmail-queue";
+}
 
 int qmail_open(qq)
 struct qmail *qq;
 {
   int pim[2];
   int pie[2];
+  int pierr[2];
+
+  setup_qqargs();
 
   if (pipe(pim) == -1) return -1;
   if (pipe(pie) == -1) { close(pim[0]); close(pim[1]); return -1; }
+  if (pipe(pierr) == -1) { 
+    close(pim[0]); close(pim[1]); 
+    close(pie[0]); close(pie[1]); 
+    close(pierr[0]); close(pierr[1]); 
+    return -1; 
+  }
  
   switch(qq->pid = vfork()) {
     case -1:
+      close(pierr[0]); close(pierr[1]);
       close(pim[0]); close(pim[1]);
       close(pie[0]); close(pie[1]);
       return -1;
     case 0:
       close(pim[1]);
       close(pie[1]);
+      close(pierr[0]); /* we want to receive data */
       if (fd_move(0,pim[0]) == -1) _exit(120);
       if (fd_move(1,pie[0]) == -1) _exit(120);
+      if (fd_move(4,pierr[1]) == -1) _exit(120);
       if (chdir(auto_qmail) == -1) _exit(61);
       execv(*binqqargs,binqqargs);
       _exit(120);
@@ -35,6 +56,7 @@
 
   qq->fdm = pim[1]; close(pim[0]);
   qq->fde = pie[1]; close(pie[0]);
+  qq->fderr = pierr[0]; close(pierr[1]);
   substdio_fdbuf(&qq->ss,write,qq->fdm,qq->buf,sizeof(qq->buf));
   qq->flagerr = 0;
   return 0;
@@ -82,10 +104,22 @@
 {
   int wstat;
   int exitcode;
+  int match;
+  char ch;
+  static char errstr[256];
+  int len = 0;
 
   qmail_put(qq,"",1);
   if (!qq->flagerr) if (substdio_flush(&qq->ss) == -1) qq->flagerr = 1;
   close(qq->fde);
+  substdio_fdbuf(&qq->ss,read,qq->fderr,qq->buf,sizeof(qq->buf));
+  while( substdio_bget(&qq->ss,&ch,1) && len < 255){
+    errstr[len]=ch;
+    len++;
+  }
+  if (len > 0) errstr[len]='\0'; /* add str-term */
+
+  close(qq->fderr);
 
   if (wait_pid(&wstat,qq->pid) != qq->pid)
     return "Zqq waitpid surprise (#4.3.0)";
@@ -118,6 +152,9 @@
     case 81: return "Zqq internal bug (#4.3.0)";
     case 120: return "Zunable to exec qq (#4.3.0)";
     default:
+      if (exitcode == 82 && len > 2){
+        return errstr;
+      }
       if ((exitcode >= 11) && (exitcode <= 40))
 	return "Dqq permanent problem (#5.3.0)";
       return "Zqq temporary problem (#4.3.0)";
diff -urN ../qmail-1.03_unpatched/qmail.h ./qmail.h
--- ../qmail-1.03_unpatched/qmail.h	1998-06-15 12:53:16.000000000 +0200
+++ ./qmail.h	2005-01-21 19:25:03.000000000 +0100
@@ -8,6 +8,7 @@
   unsigned long pid;
   int fdm;
   int fde;
+  int fderr;
   substdio ss;
   char buf[1024];
 } ;
diff -urN ../qmail-1.03_unpatched/qregex.c ./qregex.c
--- ../qmail-1.03_unpatched/qregex.c	1970-01-01 01:00:00.000000000 +0100
+++ ./qregex.c	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,57 @@
+/*
+ * qregex (v2)
+ * $Id: qregex.c,v 2.1 2001/12/28 07:05:21 evan Exp $
+ *
+ * Author  : Evan Borgstrom (evan at unixpimps dot org)
+ * Created : 2001/12/14 23:08:16
+ * Modified: $Date: 2001/12/28 07:05:21 $
+ * Revision: $Revision: 2.1 $
+ *
+ * Do POSIX regex matching on addresses for anti-relay / spam control.
+ * It logs to the maillog
+ * See the qregex-readme file included with this tarball.
+ * If you didn't get this file in a tarball please see the following URL:
+ *  http://www.unixpimps.org/software/qregex
+ *
+ * qregex.c is released under a BSD style copyright.
+ * See http://www.unixpimps.org/software/qregex/copyright.html
+ *
+ * Note: this revision follows the coding guidelines set forth by the rest of
+ *       the qmail code and that described at the following URL.
+ *       http://cr.yp.to/qmail/guarantee.html
+ * 
+ */
+
+#include <sys/types.h>
+#include <regex.h>
+#include "qregex.h"
+
+#define REGCOMP(X,Y)    regcomp(&X, Y, REG_EXTENDED|REG_ICASE)
+#define REGEXEC(X,Y)    regexec(&X, Y, (size_t)0, (regmatch_t *)0, (int)0)
+
+int matchregex(char *text, char *regex) {
+  regex_t qreg;
+  int retval = 0;
+
+
+  /* build the regex */
+  if ((retval = REGCOMP(qreg, regex)) != 0) {
+    regfree(&qreg);
+    return(-retval);
+  }
+
+  /* execute the regex */
+  if ((retval = REGEXEC(qreg, text)) != 0) {
+    /* did we just not match anything? */
+    if (retval == REG_NOMATCH) {
+      regfree(&qreg);
+      return(0);
+    }
+    regfree(&qreg);
+    return(-retval);
+  }
+
+  /* signal the match */
+  regfree(&qreg);
+  return(1);
+}
diff -urN ../qmail-1.03_unpatched/qregex.h ./qregex.h
--- ../qmail-1.03_unpatched/qregex.h	1970-01-01 01:00:00.000000000 +0100
+++ ./qregex.h	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,5 @@
+/* simple header file for the matchregex prototype */
+#ifndef _QREGEX_H_
+#define _QREGEX_H_
+int matchregex(char *text, char *regex);
+#endif
diff -urN ../qmail-1.03_unpatched/sendmail.c ./sendmail.c
--- ../qmail-1.03_unpatched/sendmail.c	1998-06-15 12:53:16.000000000 +0200
+++ ./sendmail.c	2005-01-21 19:25:03.000000000 +0100
@@ -45,6 +45,38 @@
   _exit(111);
 }
 
+void do_sender(s)
+const char *s;
+{
+  char *x;
+  int n;
+  int a;
+  int i;
+  
+  env_unset("QMAILNAME");
+  env_unset("MAILNAME");
+  env_unset("NAME");
+  env_unset("QMAILHOST");
+  env_unset("MAILHOST");
+
+  n = str_len(s);
+  a = str_rchr(s, '@');
+  if (a == n)
+  {
+    env_put2("QMAILUSER", s);
+    return;
+  }
+  env_put2("QMAILHOST", s + a + 1);
+
+  x = (char *) alloc((a + 1) * sizeof(char));
+  if (!x) nomem();
+  for (i = 0; i < a; i++)
+    x[i] = s[i];
+  x[i] = 0;
+  env_put2("QMAILUSER", x);
+  alloc_free(x);
+}
+
 int flagh;
 char *sender;
 
@@ -118,6 +150,7 @@
   if (sender) {
     *arg++ = "-f";
     *arg++ = sender;
+    do_sender(sender);
   }
   *arg++ = "--";
   for (i = 0;i < argc;++i) *arg++ = argv[i];
diff -urN ../qmail-1.03_unpatched/spawn.c ./spawn.c
--- ../qmail-1.03_unpatched/spawn.c	1998-06-15 12:53:16.000000000 +0200
+++ ./spawn.c	2005-01-21 19:25:03.000000000 +0100
@@ -5,6 +5,7 @@
 #include "substdio.h"
 #include "byte.h"
 #include "str.h"
+#include "alloc.h"
 #include "stralloc.h"
 #include "select.h"
 #include "exit.h"
diff -urN ../qmail-1.03_unpatched/ssl_timeoutio.c ./ssl_timeoutio.c
--- ../qmail-1.03_unpatched/ssl_timeoutio.c	1970-01-01 01:00:00.000000000 +0100
+++ ./ssl_timeoutio.c	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,94 @@
+#include "select.h"
+#include "error.h"
+#include "ndelay.h"
+#include "ssl_timeoutio.h"
+
+int ssl_timeoutio(int (*fun)(),
+  long t, int rfd, int wfd, SSL *ssl, char *buf, int len)
+{
+  int n;
+  const long end = t + time(NULL);
+
+  do {
+    fd_set fds;
+    struct timeval tv;
+
+    const int r = buf ? fun(ssl, buf, len) : fun(ssl);
+    if (r > 0) return r;
+
+    t = end - time(NULL);
+    if (t < 0) break;
+    tv.tv_sec = t; tv.tv_usec = 0;
+
+    FD_ZERO(&fds);
+    switch (SSL_get_error(ssl, r))
+    {
+    default: return r; /* some other error */
+    case SSL_ERROR_WANT_READ:
+      FD_SET(rfd, &fds); n = select(rfd + 1, &fds, NULL, NULL, &tv);
+      break;
+    case SSL_ERROR_WANT_WRITE:
+      FD_SET(wfd, &fds); n = select(wfd + 1, NULL, &fds, NULL, &tv);
+      break;
+    }
+
+    /* n is the number of descriptors that changed status */
+  } while (n > 0);
+
+  if (n != -1) errno = error_timeout;
+  return -1;
+}
+
+int ssl_timeoutaccept(long t, int rfd, int wfd, SSL *ssl)
+{
+  int r;
+
+  /* if connection is established, keep NDELAY */
+  if (ndelay_on(rfd) == -1 || ndelay_on(wfd) == -1) return -1;
+  r = ssl_timeoutio(SSL_accept, t, rfd, wfd, ssl, NULL, 0);
+
+  if (r <= 0) { ndelay_off(rfd); ndelay_off(wfd); }
+  else SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);
+
+  return r;
+}
+
+int ssl_timeoutconn(long t, int rfd, int wfd, SSL *ssl)
+{
+  int r;
+
+  /* if connection is established, keep NDELAY */
+  if (ndelay_on(rfd) == -1 || ndelay_on(wfd) == -1) return -1;
+  r = ssl_timeoutio(SSL_connect, t, rfd, wfd, ssl, NULL, 0);
+
+  if (r <= 0) { ndelay_off(rfd); ndelay_off(wfd); }
+  else SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);
+
+  return r;
+}
+
+int ssl_timeoutrehandshake(long t, int rfd, int wfd, SSL *ssl)
+{
+  int r;
+
+  SSL_renegotiate(ssl);
+  r = ssl_timeoutio(SSL_do_handshake, t, rfd, wfd, ssl, NULL, 0);
+  if (r <= 0 || ssl->type == SSL_ST_CONNECT) return r;
+
+  /* this is for the server only */
+  ssl->state = SSL_ST_ACCEPT;
+  return ssl_timeoutio(SSL_do_handshake, t, rfd, wfd, ssl, NULL, 0);
+}
+
+int ssl_timeoutread(long t, int rfd, int wfd, SSL *ssl, char *buf, int len)
+{
+  if (!buf) return 0;
+  if (SSL_pending(ssl)) return SSL_read(ssl, buf, len);
+  return ssl_timeoutio(SSL_read, t, rfd, wfd, ssl, buf, len);
+}
+
+int ssl_timeoutwrite(long t, int rfd, int wfd, SSL *ssl, char *buf, int len)
+{
+  if (!buf) return 0;
+  return ssl_timeoutio(SSL_write, t, rfd, wfd, ssl, buf, len);
+}
diff -urN ../qmail-1.03_unpatched/ssl_timeoutio.h ./ssl_timeoutio.h
--- ../qmail-1.03_unpatched/ssl_timeoutio.h	1970-01-01 01:00:00.000000000 +0100
+++ ./ssl_timeoutio.h	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,21 @@
+#ifndef SSL_TIMEOUTIO_H
+#define SSL_TIMEOUTIO_H
+
+#include <openssl/ssl.h>
+
+/* the version is like this: 0xMNNFFPPS: major minor fix patch status */
+#if OPENSSL_VERSION_NUMBER < 0x00906000L
+# error "Need OpenSSL version at least 0.9.6"
+#endif
+
+int ssl_timeoutconn(long t, int rfd, int wfd, SSL *ssl);
+int ssl_timeoutaccept(long t, int rfd, int wfd, SSL *ssl);
+int ssl_timeoutrehandshake(long t, int rfd, int wfd, SSL *ssl);
+
+int ssl_timeoutread(long t, int rfd, int wfd, SSL *ssl, char *buf, int len);
+int ssl_timeoutwrite(long t, int rfd, int wfd, SSL *ssl, char *buf, int len);
+
+int ssl_timeoutio(
+  int (*fun)(), long t, int rfd, int wfd, SSL *ssl, char *buf, int len);
+
+#endif
diff -urN ../qmail-1.03_unpatched/strpidt.c ./strpidt.c
--- ../qmail-1.03_unpatched/strpidt.c	1970-01-01 01:00:00.000000000 +0100
+++ ./strpidt.c	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,26 @@
+/*
+** Copyright 1998 - 2000 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#if	HAVE_CONFIG_H
+#include	"config.h"
+#endif
+#include	"numlib.h"
+#include	<string.h>
+
+static const char rcsid[]="$Id: strpidt.c,v 1.3 2000/05/27 04:59:26 mrsam Exp $";
+
+char *str_pid_t(pid_t t, char *arg)
+{
+char	buf[NUMBUFSIZE];
+char	*p=buf+sizeof(buf)-1;
+
+	*p=0;
+	do
+	{
+		*--p= '0' + (t % 10);
+		t=t / 10;
+	} while(t);
+	return (strcpy(arg, p));
+}
diff -urN ../qmail-1.03_unpatched/strtimet.c ./strtimet.c
--- ../qmail-1.03_unpatched/strtimet.c	1970-01-01 01:00:00.000000000 +0100
+++ ./strtimet.c	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,26 @@
+/*
+** Copyright 1998 - 2000 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#if	HAVE_CONFIG_H
+#include	"config.h"
+#endif
+#include	"numlib.h"
+#include	<string.h>
+
+static const char rcsid[]="$Id: strtimet.c,v 1.3 2000/05/27 04:59:26 mrsam Exp $";
+
+char *str_time_t(time_t t, char *arg)
+{
+char	buf[NUMBUFSIZE];
+char	*p=buf+sizeof(buf)-1;
+
+	*p=0;
+	do
+	{
+		*--p= '0' + (t % 10);
+		t=t / 10;
+	} while(t);
+	return (strcpy(arg, p));
+}
diff -urN ../qmail-1.03_unpatched/tls.c ./tls.c
--- ../qmail-1.03_unpatched/tls.c	1970-01-01 01:00:00.000000000 +0100
+++ ./tls.c	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,26 @@
+#include "exit.h"
+#include "error.h"
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+int smtps = 0;
+SSL *ssl = NULL;
+
+void ssl_free(SSL *myssl) { SSL_shutdown(myssl); SSL_free(myssl); }
+void ssl_exit(int status) { if (ssl) ssl_free(ssl); _exit(status); }
+
+const char *strerror(int);
+const char *ssl_error()
+{
+  int r = ERR_get_error();
+  if (!r) return NULL;
+  SSL_load_error_strings();
+  return ERR_error_string(r, NULL);
+}
+const char *ssl_strerror()
+{
+  const char *err = ssl_error();
+  if (err) return err;
+  if (!errno) return 0;
+  return errno == error_timeout ? "timed out" : strerror(errno);
+}
diff -urN ../qmail-1.03_unpatched/tls.h ./tls.h
--- ../qmail-1.03_unpatched/tls.h	1970-01-01 01:00:00.000000000 +0100
+++ ./tls.h	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,16 @@
+#ifndef TLS_H
+#define TLS_H
+
+#include <openssl/ssl.h>
+
+extern int smtps;
+extern SSL *ssl;
+
+void ssl_free(SSL *myssl);
+void ssl_exit(int status);
+# define _exit ssl_exit
+
+const char *ssl_error();
+const char *ssl_strerror();
+
+#endif
diff -urN ../qmail-1.03_unpatched/update_tmprsadh.sh ./update_tmprsadh.sh
--- ../qmail-1.03_unpatched/update_tmprsadh.sh	1970-01-01 01:00:00.000000000 +0100
+++ ./update_tmprsadh.sh	2005-01-21 19:25:03.000000000 +0100
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+# Update temporary RSA and DH keys
+# Frederik Vermeulen 2003-12-28 GPL
+# modified zeitform Internet Dienste
+
+umask 0077 || exit 0
+
+#export PATH="$PATH:/usr/local/bin/ssl"
+
+openssl genrsa -out QMAIL/control/rsa512.new 512
+chmod 660 QMAIL/control/rsa512.new
+chown vpopmail.qmail QMAIL/control/rsa512.new 
+mv -f QMAIL/control/rsa512.new QMAIL/control/rsa512.pem
+echo
+
+openssl dhparam -2 -out QMAIL/control/dh512.new 512
+chmod 660 QMAIL/control/dh512.new
+chown vpopmail.qmail QMAIL/control/dh512.new
+mv -f QMAIL/control/dh512.new QMAIL/control/dh512.pem
+echo
+
+openssl dhparam -2 -out QMAIL/control/dh1024.new 1024
+chmod 660 QMAIL/control/dh1024.new
+chown vpopmail.qmail QMAIL/control/dh1024.new
+mv -f QMAIL/control/dh1024.new QMAIL/control/dh1024.pem
