--- ../qmail-1.03/Makefile	2004-11-09 10:36:02.000000000 +0000
+++ Makefile	2004-11-09 10:36:13.000000000 +0000
@@ -1,9 +1,61 @@
 # Don't edit Makefile! Use conf-* for configuration.
 
+# uncomment -lz if you get compilation errors about compress
+# uncomment -lsocket -lnsl if you get compilation errors about sockets
+MYSQL_LIBS=/opt/mysql/lib/mysql/libmysqlclient.a -lm -lz #-lsocket -lnsl
+MYSQL_INCLUDE=-I/opt/mysql/include
+LOGGING=-DO_NOT_LOG_CONNECTS #-DEBUG_DEBUG_DEBUG #-DO_NOT_LOG
+# read README.queries before uncommenting the next two lines
+ALIAS_QUERY_STYLE=#-DFUNKY_ALIAS_QUERIES -DOIL_AND_WATER -DALIAS_HAS_PRECEDENCE
+VIRTUAL_QUERY_STYLE=#-DFUNKY_VIRTUAL_QUERIES -DHOST_HAS_PRECEDENCE
+QUERY_STYLE=$(ALIAS_QUERY_STYLE) $(VIRTUAL_QUERY_STYLE)
+OS_SPECIFIC=#-DSOLARIS_STUPIDITY
+# MAGIC_FORWARDING is not recommended: read the changelog online
+MAGIC=#-DMAGIC_FORWARDING -DMAGIC_FORWARD_USER="\"forward\"" -DMAGIC_LOGGING
 SHELL=/bin/sh
 
 default: it
 
+connect_mysql.o: compile connect_mysql.c
+	./compile connect_mysql.c $(MYSQL_INCLUDE) $(LOGGING)
+
+vdoms_mysql.o: compile vdoms_mysql.c mysql_queries.h
+	./compile vdoms_mysql.c $(MYSQL_INCLUDE) $(LOGGING) $(QUERY_STYLE) $(MAGIC)
+
+rcpthosts_mysql.o: compile rcpthosts_mysql.c mysql_queries.h
+	./compile rcpthosts_mysql.c $(MYSQL_INCLUDE) $(LOGGING)
+
+checkuser_mysql.o: compile checkuser_mysql.c
+	./compile checkuser_mysql.c $(MYSQL_INCLUDE)
+
+getpw_mysql.o: compile getpw_mysql.c mysql_queries.h
+	./compile getpw_mysql.c $(MYSQL_INCLUDE)
+
+dotqmail_mysql.o: compile dotqmail_mysql.c mysql_queries.h
+	./compile dotqmail_mysql.c $(MYSQL_INCLUDE) $(LOGGING) $(QUERY_STYLE)
+
+do_query.o: compile do_query.c
+	./compile do_query.c $(MYSQL_INCLUDE) $(LOGGING)
+
+make_query.o: compile make_query.c mysql_queries.h
+	./compile make_query.c $(MYSQL_INCLUDE)
+
+addrexp.o: \
+compile addrexp.c
+	./compile addrexp.c
+
+addrexp: \
+load addrexp.o connect_mysql.o make_query.o do_query.o \
+checkuser_mysql.o getpw_mysql.o vdoms_mysql.o \
+qsutil.o auto_break.o auto_qmail.o fmt_ulong.o fmt_uint.o fmt_uint0.o \
+open_read.o stralloc.a str.a getln.a case.a substdio.a error.a alloc.a \
+constmap.o control.o scan_ulong.o mysql_queries.h
+	./load addrexp constmap.o control.o scan_ulong.o \
+	connect_mysql.o qsutil.o make_query.o do_query.o checkuser_mysql.o \
+	getpw_mysql.o vdoms_mysql.o auto_break.o auto_qmail.o fmt_ulong.o \
+	fmt_uint.o fmt_uint0.o open_read.o getln.a substdio.a stralloc.a \
+	str.a error.a alloc.a case.a $(MYSQL_LIBS)
+
 addresses.0: \
 addresses.5
 	nroff -man addresses.5 > addresses.0
@@ -805,7 +857,7 @@
 qmail-pop3d qmail-popup qmail-qmqpc qmail-qmqpd qmail-qmtpd \
 qmail-smtpd sendmail tcp-env qmail-newmrh config config-fast dnscname \
 dnsptr dnsip dnsmxip dnsfq hostname ipmeprint qreceipt qsmhook qbiff \
-forward preline condredirect bouncesaying except maildirmake \
+forward preline condredirect bouncesaying except maildirmake addrexp \
 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
@@ -1109,9 +1161,14 @@
 
 qmail-getpw: \
 load qmail-getpw.o case.a substdio.a error.a str.a fs.a auto_break.o \
-auto_usera.o
-	./load qmail-getpw case.a substdio.a error.a str.a fs.a \
-	auto_break.o auto_usera.o 
+qsutil.o auto_usera.o auto_qmail.o connect_mysql.o getpw_mysql.o \
+make_query.o do_query.o scan_ulong.o open_read.o \
+getln.a stralloc.a alloc.a substdio.a env.a
+	./load qmail-getpw \
+	qsutil.o auto_break.o auto_usera.o auto_qmail.o connect_mysql.o \
+	getpw_mysql.o make_query.o do_query.o scan_ulong.o open_read.o \
+	case.a substdio.a error.a str.a fs.a \
+	getln.a stralloc.a alloc.a substdio.a env.a str.a $(MYSQL_LIBS)
 
 qmail-getpw.0: \
 qmail-getpw.8
@@ -1129,7 +1186,7 @@
 compile qmail-getpw.c readwrite.h substdio.h subfd.h substdio.h \
 error.h exit.h byte.h str.h case.h fmt.h auto_usera.h auto_break.h \
 qlx.h
-	./compile qmail-getpw.c
+	./compile qmail-getpw.c $(OS_SPECIFIC)
 
 qmail-header.0: \
 qmail-header.5
@@ -1172,14 +1229,16 @@
 
 qmail-local: \
 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 \
+qsutil.o slurpclose.o connect_mysql.o do_query.o \
+dotqmail_mysql.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
 	./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 \
+	qsutil.o slurpclose.o connect_mysql.o do_query.o \
+	dotqmail_mysql.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` $(MYSQL_LIBS)
 
 qmail-local.0: \
 qmail-local.8
@@ -1480,15 +1539,18 @@
 
 qmail-send: \
 load qmail-send.o qsutil.o control.o constmap.o newfield.o prioq.o \
-trigger.o fmtqfn.o quote.o now.o readsubdir.o qmail.o date822fmt.o \
+trigger.o fmtqfn.o quote.o now.o readsubdir.o connect_mysql.o \
+checkuser_mysql.o getpw_mysql.o vdoms_mysql.o make_query.o do_query.o qmail.o date822fmt.o auto_break.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
 	./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 \
+	connect_mysql.o checkuser_mysql.o getpw_mysql.o vdoms_mysql.o \
+	make_query.o do_query.o qmail.o date822fmt.o datetime.a case.a ndelay.a getln.a auto_break.o \
 	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 \
+	$(MYSQL_LIBS)
 
 qmail-send.0: \
 qmail-send.8
@@ -1509,7 +1571,7 @@
 scan.h case.h auto_qmail.h trigger.h newfield.h stralloc.h quote.h \
 qmail.h substdio.h qsutil.h prioq.h datetime.h gen_alloc.h constmap.h \
 fmtqfn.h readsubdir.h direntry.h
-	./compile qmail-send.c
+	./compile qmail-send.c $(MYSQL_INCLUDE)
 
 qmail-showctl: \
 load qmail-showctl.o auto_uids.o control.o open.a getln.a stralloc.a \
@@ -1534,15 +1596,17 @@
 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 \
+date822fmt.o now.o qmail.o connect_mysql.o rcpthosts_mysql.o \
+qsutil.o do_query.o cdb.a fd.a wait.a datetime.a getln.a \
 open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \
 fs.a auto_qmail.o socket.lib
 	./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \
 	timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \
-	received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \
+	qsutil.o received.o date822fmt.o now.o qmail.o connect_mysql.o \
+	rcpthosts_mysql.o do_query.o cdb.a fd.a wait.a \
 	datetime.a getln.a open.a sig.a case.a env.a stralloc.a \
 	alloc.a substdio.a error.a str.a fs.a auto_qmail.o  `cat \
-	socket.lib`
+	socket.lib` $(MYSQL_LIBS)
 
 qmail-smtpd.0: \
 qmail-smtpd.8
@@ -1950,11 +2014,11 @@
 stralloc.a: \
 makelib stralloc_eady.o stralloc_pend.o stralloc_copy.o \
 stralloc_opys.o stralloc_opyb.o stralloc_cat.o stralloc_cats.o \
-stralloc_catb.o stralloc_arts.o
+stralloc_catb.o stralloc_arts.o stralloc_free.o
 	./makelib stralloc.a stralloc_eady.o stralloc_pend.o \
 	stralloc_copy.o stralloc_opys.o stralloc_opyb.o \
 	stralloc_cat.o stralloc_cats.o stralloc_catb.o \
-	stralloc_arts.o
+	stralloc_arts.o stralloc_free.o
 
 stralloc_arts.o: \
 compile stralloc_arts.c byte.h str.h stralloc.h gen_alloc.h
@@ -1976,6 +2040,10 @@
 compile stralloc_copy.c byte.h stralloc.h gen_alloc.h
 	./compile stralloc_copy.c
 
+stralloc_free.o: \
+compile stralloc_free.c stralloc.h gen_alloc.h
+	./compile stralloc_free.c
+
 stralloc_eady.o: \
 compile stralloc_eady.c alloc.h stralloc.h gen_alloc.h \
 gen_allocdefs.h
--- ../qmail-1.03/README.queries	1970-01-01 01:00:00.000000000 +0100
+++ README.queries	2004-11-09 10:36:13.000000000 +0000
@@ -0,0 +1,213 @@
+All about the new "FUNKY" queries
+=================================
+  Starting with version 1.1.2 of these patches, you can now choose to 
+use "FUNKY" queries on the virtual and alias tables.  To decide to use 
+these new queries, you must uncomment -DFUNKY_ALIAS_QUERIES and/or 
+-DFUNKY_VIRTUAL_QUERIES in the Makefile.  The old-style behaviour will 
+be the default.
+  In a nutshell, FUNKY queries let you have SQL wildcards in certain 
+fields, enabling you, for example, to catch all postmaster addresses 
+at virtual domains you host by mapping postmaster@% to alias-postmaster.
+
+IMPORTANT
+=========
+  Because of the nature of these new queries, using wildcards and so 
+on, the possibility exists for you to deliver mail to THE WRONG 
+PEOPLE if you don't think carefully about the setup you're going to 
+use.  Read on to get the details and decide if you want to use this 
+new functionality.
+
+The old virtual queries
+=======================
+  Given an address of the form vu@vh, where vu is the virtual username 
+and vh is the virtual host, the following query is generated in order 
+to find the local user we should deliver to:
+
+      select username, ext from virtual where
+      (virtual_username='vu' or virtual_username='') and
+      virtual_host='vh' order by virtual_username desc limit 1
+
+  By select rows where the virtual username is blank OR where it 
+matches exactly we can have wildcard matches.  By ordering by the 
+username in descending order we ensure that the exact match comes out 
+before the wildcard, which is important because the limit 1 ensures 
+that we (correctly) only get one row back.
+
+  Hence, given a table of the form:
+
+  +---------------+-----+------------------+--------------+
+  | username      | ext | virtual_username | virtual_host |
+  +---------------+-----+------------------+--------------+
+  | user_one      |     | one              | domain.tld   |
+  | user_two      |     | two              | domain.tld   |
+  | user_three    |     |                  | domain.tld   |
+  +---------------+-----+------------------+--------------+
+
+we can see at a glance that mail for one@domain.tld goes to user_one, 
+mail for two@domain.tld goes to user_two and mail for xxx@domain.tld, 
+where xxx is anything you like (DJB would say xxx is a member of the 
+complement of the set { "one", "two" } in the set of valid "username 
+parts" of mail addresses :-), goes to user_three.
+  Easy.
+
+The new queries
+===============
+  Here's the query that gets sent to the database if you #define 
+FUNKY_VIRTUAL_QUERIES:
+
+        select username, ext, virtual_host rlike '[%_]' as wild_h,
+        virtual_username rlike '[%_]' as wild_u from virtual where
+        'vu' like virtual_username and 'vh' like virtual_host
+        order by wild_u, wild_h limit 1
+
+  See, they ain't called FUNKY for nothing!  Let's have a close look.
+
+  Selecting username and ext is simple enough but what's this 
+"virtual_host rlike '[%_]' as wild_h" stuff?  Well it simply matches 
+rows in the virtual table where the value contains a wildcard 
+character (% or _) and assigns the value 0 (false) or 1 (true) to 
+the field of wild_h.  Similarly for virtual_username.  This is 
+useful because we then order by the values of wild_u and wild_h, 
+effectively forcing rows without wildcards to the front.  In other 
+words we trust entries more the fewer wildcards they contain.
+  Here comes the example.  I've tacked on the values of wild_u and 
+wild_h as they would appear in the result set.
+
+  +---------------+-----+------------------+--------------+--------+--------+
+  | username      | ext | virtual_username | virtual_host | wild_u | wild_h |
+  +---------------+-----+------------------+--------------+--------+--------+
+  | user_one      |     | one              | domain.tld   | 0      | 0      |
+  | user_two      |     | %                | domain.tld   | 1      | 0      |
+  | user_three    |     | abuse            | %            | 0      | 1      |
+  | user_four     |     | %                | %            | 1      | 1      |
+  +---------------+-----+------------------+--------------+--------+--------+
+
+  In this example, mail for one@domain.tld gets delivered to user_one as 
+before.  As it happens, mail for two@domain.tld still gets delivered to 
+user_two but three@domain.tld now also goes to user_two because 
+virtual_username contains the SQL % wildcard.  Mail for abuse@example.org, 
+assuming you also host example.org, goes to user_three, as does mail for 
+abuse@evil.org (same disclaimer) and abuse@anything.  user_four picks up 
+anything not mentioned previously.
+
+  I know what you're thinking.  You've spotted a little omission on my 
+part, haven't you?  What happens to mail for abuse@domain.tld?  According 
+to the the table above it could potentially be delivered to user_two, 
+user_three or even user_four.
+  In fact, the query outlined a few paragraphs up specifies that the 
+mail should go to user_three because the matching rows of the table 
+appear in the following order after sorting:
+
+  +---------------+-----+------------------+--------------+--------+--------+
+  | username      | ext | virtual_username | virtual_host | wild_u | wild_h |
+  +---------------+-----+------------------+--------------+--------+--------+
+  | user_three    |     | abuse            | %            | 0      | 1      |
+  | user_two      |     | %                | domain.tld   | 1      | 0      |
+  | user_four     |     | %                | %            | 1      | 1      |
+  +---------------+-----+------------------+--------------+--------+--------+
+
+  Recall we order by wild_u, wild_h...
+
+  There is a way to override this.  If you #define HOST_HAS_PRECEDENCE, 
+the query will be rewritten to order by wild_h, wild_u, giving the table 
+the following appearance: 
+
+  +---------------+-----+------------------+--------------+--------+--------+
+  | username      | ext | virtual_username | virtual_host | wild_u | wild_h |
+  +---------------+-----+------------------+--------------+--------+--------+
+  | user_two      |     | %                | domain.tld   | 1      | 0      |
+  | user_three    |     | abuse            | %            | 0      | 1      |
+  | user_four     |     | %                | %            | 1      | 1      |
+  +---------------+-----+------------------+--------------+--------+--------+
+
+  This is why I said you could deliver mail to the wrong people.  You need 
+to think about how you want wildcarded delivery to work.  What's more 
+important for YOUR site?  That you, the administrator, catch abuse, postmaster, 
+mailer-daemon etc etc messages for ANY domain you host?  Or that your users 
+catch ANY address at their domains?
+  You can't have it both ways, unless you add explicit entries for all your 
+domains (eg abuse@domain.tld goes to user_five).
+
+New alias table queries
+=======================
+  These new queries are a bit more experimental because there's a lot 
+more potential for confusion.
+  The alias table query, given a username 'u' and alias 'a', has been 
+changed from
+
+        select alias_username, alias_host from alias where
+        username='u' and alias='a'
+to
+        select alias_username, alias_host, username rlike '[%_]' as wild_u,
+        alias rlike '[%_]' as wild_a from alias where
+        'u' like username and 'a' like alias order by wild_u, wild_a
+
+  What this means is that you can have wildcards in the username or 
+alias fields of the alias table, IF YOU WANT.  Remember that SQL "like" 
+behaves exactly like "=" if there are no wildcards floating around.
+  This lets you have entries in the alias table viz:
+
+  +----------+-----------+----------------+--------------------+
+  | username | alias     | alias_username | alias_host         |
+  +----------+-----------+----------------+--------------------+
+  | user\_%  | %         | forward_one    | somewhere.else.com |
+  | user_two | %         | forward_two    | somewhere.else.com |
+  +----------+-----------+----------------+--------------------+
+
+  Here we forward all user_xxx's mail to forward_one@somewhere.else.com 
+and user_two's mail to forward_two@somewhere.else.com AS WELL.
+  As with the virtual table, you can force non-default behaviour by 
+uncommenting -DALIAS_HAS_PRECEDENCE.  You would only need to do this 
+if you also uncommented -DOIL_AND_WATER, which won't let you mix rows 
+which have wildcarded entries and rows which do not.  In other words, 
+all entries for which a user (or alias) is explicitly matched are 
+processed and all others are discarded.  In the example above, this 
+would mean that the user_two would not have a copy of his mail 
+forwarded to forward_one@somewhere.else.com because the matching 
+stops after the forward_two@somewhere.else.com is found.
+  Confused?  You can always turn off FUNKY_ALIAS_QUERIES...
+
+  Seriously though, I would recommend setting both -DOIL_AND_WATER and 
+-DALIAS_HAS_PRECEDENCE.  In fact, they may well end up being the default 
+by the time you read this.  Default, that is, in that they will be 
+uncommented in the makefile, while still being non-default in the sense 
+that you still need to uncomment FUNKY_ALIAS_QUEREIS.  Oh, you know what 
+I mean.  Anyway, check out the next example, which should make things a 
+little clearer.
+
+  +----------+-----------+----------------+--------------------+
+  | username | alias     | alias_username | alias_host         |
+  +----------+-----------+----------------+--------------------+
+  | user_two | %         | forward_two    | somewhere.else.com |
+  | user_two | one       | user_one       |                    |
+  +----------+-----------+----------------+--------------------+
+
+  With -DALIAS_HAS_PRECEDENCE and -DOIL_AND_WATER set, mail for 
+user_two-one will go to user_one whereas mail for user_two-anything 
+will go to forward_two@somewhere.else.com.  With either 
+-DOIL_AND_WATER or -DALIAS_HAS_PRECEDENCE set, mail for user_two-one 
+will go to both user_one and to forward_two@somewhere.else.com.  Note, 
+however, that the REASON that the mail is sent to both addresses will 
+be different depending on the #defines you set.  If you set only 
+-DOIL_AND_WATER, both rules are followed because the matching is 
+done on username.  If you set only -DALIAS_HAS_PRECEDENCE, both rules 
+are followed (the second before the first, for all it matters) 
+because both alias fields match the 'one' extension.
+
+  Got all that?  Good.  Now to confuse you again.  Say my table 
+looks like this:
+
+  +----------+-----------+----------------+--------------------+
+  | username | alias     | alias_username | alias_host         |
+  +----------+-----------+----------------+--------------------+
+  | %        | one       | user_three     |                    |
+  | user_two | %         | forward_two    | somewhere.else.com |
+  | user_two | one       | user_one       |                    |
+  +----------+-----------+----------------+--------------------+
+
+  What happens for mail to user_two-one now?  What should happen?  
+The answer to these questions will help you decide whether or 
+not you want to run FUNKY alias queries.
+
+  Drop me a line with your comments on these new queries.  I'd 
+especially like some feedback on the alias stuff.
--- ../qmail-1.03/addrexp.c	1970-01-01 01:00:00.000000000 +0100
+++ addrexp.c	2004-11-09 10:36:13.000000000 +0000
@@ -0,0 +1,109 @@
+#include "constmap.h"
+#include "control.h"
+#include "str.h"
+#include "stralloc.h"
+#include "substdio.h"
+#include "subfd.h"
+#include "mysql_queries.h"
+
+extern int connect_mysql();
+extern void disconnect_mysql();
+extern int vdoms_mysql();
+extern int checkuser_mysql();
+
+extern char auto_qmail[];
+
+stralloc envnoathost = { 0 };
+stralloc locals = { 0 };
+stralloc percenthack = { 0 };
+stralloc qmail_mysql_query = { 0 };
+struct constmap maplocals;
+struct constmap mappercenthack;
+char *strnum3 = "test";
+
+int main(int argc, char **argv) {
+  int at, i, j, local;
+  stralloc addr = { 0 };
+  stralloc ret = { 0 };
+  
+  if (argc == 1) {
+    substdio_putsflush(subfdout, "addrexp: expand a virtual address\n");
+    substdio_putsflush(subfdout, "usage: addrexp virtual_username@virtual_host\n");
+    substdio_putsflush(subfdout, "returns: final delivery address for virtual_username@virtual_host\n");
+    substdio_putsflush(subfdout, "exit codes:   0 if the address is local\n");
+    substdio_putsflush(subfdout, "              1 if the address is a virtual domain\n");
+    substdio_putsflush(subfdout, "              2 if the address is remote\n");
+    substdio_putsflush(subfdout, "            100 if there was an error reading controls\n");
+    substdio_putsflush(subfdout, "            111 if there was a database error\n");
+    _exit(1);
+  }
+
+  if (chdir(auto_qmail) == -1) _exit(100);
+  if (control_init() == -1) _exit(100);
+  if (control_rldef(&envnoathost, "control/envnoathost", 1, "envnoathost") == -1) _exit(100);
+  if (control_readfile(&locals,"control/locals",1) != 1) _exit(100);
+  if (!constmap_init(&maplocals,locals.s,locals.len,0)) _exit(100);
+  switch(control_readfile(&percenthack,"control/percenthack",0)) {
+    case -1: return 0;
+    case 0: if (!constmap_init(&mappercenthack,"",0,0)) return 0; break;
+    case 1: if (!constmap_init(&mappercenthack,percenthack.s,percenthack.len,0)) return 0; break;
+  }
+  if (! stralloc_ready(&addr, str_len(argv[1]) + 1)) _exit(100);
+  if (! stralloc_cats(&addr, argv[1])) _exit(100);
+
+  /* some of this ripped from qmail-send */
+  i = byte_rchr(addr.s, addr.len, '@');
+  if (i == addr.len) {
+    if (! stralloc_readyplus(&addr, envnoathost.len + 1)) _exit(100);
+    if (! stralloc_append(&addr, "@")) _exit(100);
+    if (! stralloc_cat(&addr, &envnoathost)) _exit(100);
+  }
+
+  while (constmap(&mappercenthack,addr.s + i + 1,addr.len - i - 1)) {
+    j = byte_rchr(addr.s,i,'%');
+    if (j == i) break;
+    addr.len = i;
+    i = j;
+    addr.s[i] = '@';
+  }
+
+  at = byte_rchr(addr.s,addr.len,'@');
+
+  if (! connect_mysql()) _exit(111);
+  local = 2;
+  i = 0;
+  if (constmap(&maplocals,addr.s + at + 1,addr.len - at - 1)) {
+    local = 1; /* maybe one day expand to alias etc */
+/*printf("checkuser_mysql(%s, %d) = ", addr.s, at);*/
+    i = checkuser_mysql(addr.s, at);
+/*printf("%d\n", i);*/
+    if (i == -1) _exit(111);
+    if (i) {
+      if (!stralloc_cat(&ret,&addr)) _exit(100);
+      if (!stralloc_0(&ret)) _exit(100);
+      local = 0;
+    }
+    else {
+      /* try virtual with the given host - then fall back to envnoathost */
+/*printf("local vdoms_mysql(%s, %i, &ret) = ", addr.s, addr.len);*/
+      i = vdoms_mysql(addr.s, addr.len, &ret);
+/*printf("%d\n", i);*/
+      if (i == -1) _exit(111);
+      if (i) local = 0;
+      else {
+        addr.len = at + 1;
+        if (! stralloc_cat(&addr, &envnoathost)) _exit(100);
+      }
+    }
+  }
+
+  if (! i) if (i = vdoms_mysql(addr.s, addr.len, &ret)) local = 1;
+/*printf("%d\n", i);*/
+  if (i == -1) _exit(111);
+ 
+  if (i) substdio_puts(subfdout, ret.s);
+  else substdio_puts(subfdout, argv[1]);
+  substdio_putsflush(subfdout, "\n");
+  disconnect_mysql();
+  _exit(local);
+}
--- ../qmail-1.03/checkuser_mysql.c	1970-01-01 01:00:00.000000000 +0100
+++ checkuser_mysql.c	2004-11-09 10:36:13.000000000 +0000
@@ -0,0 +1,39 @@
+#include <mysql/mysql.h>
+#include "auto_break.h"
+#include "stralloc.h"
+
+extern MYSQL_ROW do_query(stralloc *query);
+extern MYSQL_RES *result;
+
+int checkuser_mysql(char *username, int i) {
+  MYSQL_ROW row;
+  int j;
+  stralloc qmail_mysql_query = { 0 };
+  stralloc user = { 0 };
+
+  if (! stralloc_ready(&user, i)) return -1;
+  if (! stralloc_catb(&user, username, i)) {
+    stralloc_free(&user);
+    return -1;
+  }
+  user.s[i] = '\0';
+  if (! make_query(&qmail_mysql_query, user.s)) {
+    stralloc_free(&user);
+    return -1;
+  }
+
+  stralloc_free(&user);
+  row = do_query(&qmail_mysql_query);
+
+  switch ((int) row) {
+    case -1: return -1; break;
+    case 0:
+      for (j = i - 1; j > 0; j--) {
+        if (*(username + j) == *auto_break) return checkuser_mysql(username, j);
+      }
+      return 0;
+      break;
+  }
+  mysql_free_result(result);
+  return 1;
+}
--- ../qmail-1.03/connect_mysql.c	1970-01-01 01:00:00.000000000 +0100
+++ connect_mysql.c	2004-11-09 10:36:13.000000000 +0000
@@ -0,0 +1,236 @@
+#include "mysql/mysql.h"
+#include "auto_qmail.h"
+#include "fmt.h"
+#include "qsutil.h"
+#include "readwrite.h"
+#include "scan.h"
+#include "str.h"
+#include "stralloc.h"
+#include "subfd.h"
+#include "substdio.h"
+
+#define SQLSERVER "sqlserver"
+#define TRIES 3
+
+MYSQL dbh, *mysql;
+MYSQL_RES *result;
+
+int connect_mysql() {
+  /* if database handle is available AND it pings then there's nothing to do */
+  DEBUG_SAY("connect_mysql(): checking existing connection\n");
+  if (mysql) if (! mysql_ping(&dbh)) return 1;
+  DEBUG_SAY("no connection, must reconnect\n");
+  return init_mysql();
+}
+
+/* try to connect to database with the username/password from config file     */
+/* returns 1 if we were already connected or we connected successfully        */
+/* returns 0 if we could not connect                                          */
+int init_mysql() {
+  int i, tries;
+  unsigned long portnum;
+  stralloc host = { 0 };
+  stralloc user = { 0 };
+  stralloc pass = { 0 };
+  stralloc name = { 0 };
+  stralloc port = { 0 };
+  stralloc sock = { 0 };
+  /* already used tries AND it has four letters :-) */
+  stralloc fois = { 0 };
+
+  DEBUG_SAY("calling mysql_init()\n");
+  mysql = mysql_init(&dbh);
+  if (! mysql) return 0;
+  DEBUG_SAY("mysql_init() returned successfully\n");
+
+  DEBUG_SAY("calling read_config_file()\n");
+  if (! read_config_file(&host, &user, &pass, &name, &port, &sock, &fois, &portnum, &tries)) {
+    log1("error: without a valid config file we can't access the database!\n");
+    return 0;
+  }
+  DEBUG_SAY("read_config_file() returned successfully\n");
+
+  /* have several goes attempting to the database */
+  for (i = 0; i < tries; i++) {
+    DEBUG_SAY("earth to mysqld, come in please mysqld\n");
+    mysql = mysql_real_connect(&dbh, host.s, user.s, pass.s, name.s, 
+                               portnum, sock.s, 0);
+    if (mysql) {
+#ifndef O_NOT_LOG_CONNECTS
+      log3("connected to '", name.s, "' database\n");
+#endif
+      break;
+    }
+    log3("error: ", mysql_error(&dbh), "\n");
+    sleep(3);
+  }
+
+  stralloc_free(&host);
+  stralloc_free(&user);
+  stralloc_free(&pass);
+  stralloc_free(&port);
+  stralloc_free(&sock);
+  stralloc_free(&fois);
+
+  if (! mysql) {
+    log3("giving up on connection to '", name.s, "' database\n");
+    stralloc_free(&name);
+    return 0;
+  }
+  stralloc_free(&name);
+  return 1;
+}
+
+void disconnect_mysql() {
+  mysql_close(&dbh);
+}
+
+int read_config_file(stralloc *host, stralloc *user, stralloc *pass, stralloc *name, stralloc *port, stralloc *sock, stralloc *fois, unsigned long *portnum, int *tries) {
+  int error, i, file, match;
+  substdio ss;
+  stralloc filename = { 0 };
+  stralloc buf = { 0 };
+  char inbuf[64];
+
+  /* get the filename of the sqlserver control file */
+  DEBUG_SAY("figuring out sqlserver path\n");
+  i = str_len(SQLSERVER) + str_len(auto_qmail) + 10;
+  if (! stralloc_ready(&filename, i)) return 0;
+  if (! stralloc_cats(&filename, auto_qmail)) return 0;
+  if (! stralloc_cats(&filename, "/control/")) return 0;
+  if (! stralloc_cats(&filename, SQLSERVER)) return 0;
+  if (! stralloc_0(&filename)) return 0;
+  DEBUG_PUT("sqlserver file is \"");
+  DEBUG_PUT(filename.s);
+  DEBUG_SAY("\"\n");
+ 
+  DEBUG_SAY("stand by, we're trying the file\n");
+  file = open_read(filename.s);
+  if (file == -1) {
+    log3("error: could not open sqlserver file \"", filename.s, "\"\n");
+    stralloc_free(&filename);
+    stralloc_free(&buf);
+    return 0;
+  }
+
+  substdio_fdbuf(&ss, read, file, inbuf, sizeof(inbuf));
+  while (getln(&ss, &buf, &match, '\n') != -1) {
+    if (! match && ! buf.len) break;
+    buf.len--;
+    if (! stralloc_0(&buf)) break;
+    /* we consider only lines starting with the letters h, s,l,p,d or t */
+    switch (buf.s[0]) {
+      case 'h': error = getconfig(&buf, "host", host); break;
+      case 's':
+        if (buf.s[1] == 'e') error = getconfig(&buf, "server", host);
+        else error = getconfig(&buf, "socket", sock);
+        break;
+      case 'l': error = getconfig(&buf, "login", user); break;
+      case 'p':
+        if (buf.s[1] == 'a') error = getconfig(&buf, "password", pass);
+        else error = getconfig(&buf, "port", port);
+        break;
+      case 'd': error = getconfig(&buf, "db", name); break;
+      case 't': error = getconfig(&buf, "tries", fois); break;
+      default: continue;
+    }
+    if (error == -1) continue;
+    if (! match) break;
+  }
+
+  DEBUG_SAY("closing configuration file\n");
+  close(file);
+  stralloc_free(&filename);
+  stralloc_free(&buf);
+
+  /* if a port is provided it must be a valid number */
+  if (port->a > 0) {
+    DEBUG_SAY("checking validity of provided port number\n");
+    i = scan_ulong(port->s, portnum);
+    if (! i || port->s[i]) {
+      log3("error: bogus port number: \"", port->s, "\"\n");
+      return 0;
+    }
+  }
+  else *portnum = 0;
+
+  /* specific number of connection attempts */
+  if (fois->a > 0) {
+    DEBUG_SAY("checking validity of requested connection attempts\n");
+    i = scan_ulong(fois->s, tries);
+    if (! i || fois->s[i]) {
+      log3("warning: bogus connection attempts number: \"", fois->s, "\"\n");
+      if (stralloc_ready(fois, FMT_ULONG)) {
+        fois->s[fmt_ulong(fois->s, TRIES)] = '\0';
+        log3("warning: using default ", fois->s, "\n");
+      }
+      *tries = TRIES;
+    }
+    else {
+      if (*tries == 0) {
+        log1("error: we must make at least one connection attempt\n");
+        return 0;
+      }
+      DEBUG_PUT("we will try ");
+      if (*tries == 1) DEBUG_PUT("once (only) ");
+      else {
+        DEBUG_PUT(fois->s);
+        DEBUG_PUT(" times ");
+      }
+      DEBUG_SAY("to connect to the database\n");
+    }
+  }
+  else *tries = TRIES;
+
+  /* we must have at least a username */
+  if (! user->a) {
+    log3("error: no username provided in \"", filename.s, "\"\n");
+    return 0;
+  }
+
+  if (host->a) {
+    DEBUG_PUT("host is \"");
+    DEBUG_PUT(host->s);
+    DEBUG_SAY("\"\n");
+  }
+
+  if (port->a) {
+    if (sock->a) {
+      log3("error: you can't specify a port number AND a socket\n");
+      return 0;
+    }
+    DEBUG_PUT("port is ");
+    DEBUG_PUT(port->s);
+    DEBUG_SAY("\n");
+  }
+
+  DEBUG_PUT("user is \"");
+  DEBUG_PUT(user->s);
+  DEBUG_SAY("\"\n");
+
+  if (pass->a) DEBUG_SAY("a password was provided\n");
+
+  return 1;
+}
+
+/* read lines in the config file and grok entries of the form "x=y" or "x y"  */
+/* got is the line in the file, expected is the parmeter we're checking for   */
+/* and store is a stralloc to put the result into if it was found             */
+int getconfig(stralloc *got, char *expected, stralloc *store) {
+  char *value, *x;
+
+  x = got->s + str_len(expected);
+  value = x + 1;
+  /* we got a meaningful value */
+  if (! stralloc_starts(&got, expected) && (*x == ' ' || *x == '\t' || *x == '=') && str_len(value) > 0) {
+    if (! stralloc_ready(store, str_len(value) + 1)) return -1;
+    if (! stralloc_cats(store, value)) return -1;
+    if (! stralloc_0(store)) return -1;
+    return 0;
+  }
+  /* oh dear, the line we got didn't contain what we expected it to */
+  log3("error: reading sqlserver file, expected \"", expected, "\" but got \"");
+  log2(got, "\"\n");
+  /* of course, we might have been expecting the wrong thing... */
+  return -1;
+}
--- ../qmail-1.03/do_query.c	1970-01-01 01:00:00.000000000 +0100
+++ do_query.c	2004-11-09 10:36:13.000000000 +0000
@@ -0,0 +1,33 @@
+#include <mysql/mysql.h>
+#include "stralloc.h"
+#include "qsutil.h"
+
+extern MYSQL dbh, *mysql;
+extern MYSQL_RES *result;
+
+/* run a given query and return 1 row only */
+MYSQL_ROW do_query(stralloc *query) {
+  int num;
+
+  if (! connect_mysql()) return (MYSQL_ROW) -1;
+#ifndef O_NOT_LOG
+  tcplog("query: ", query->s, ";\n");
+#endif
+  if (mysql_real_query(mysql, query->s, query->len) < 0) {
+    /* bad => log it anyway */
+    tcplog("error: ", mysql_error(&dbh), "\n");
+    return (MYSQL_ROW) -1;
+  }
+  stralloc_free(query);
+  if (! (result = mysql_store_result(mysql))) return (MYSQL_ROW) -1;
+
+  num = mysql_num_rows(result);
+
+  if (num != 1) {
+    mysql_free_result(result);
+    return 0;
+  }
+
+  return mysql_fetch_row(result);
+}
+
--- ../qmail-1.03/dotqmail_mysql.c	1970-01-01 01:00:00.000000000 +0100
+++ dotqmail_mysql.c	2004-11-09 10:36:13.000000000 +0000
@@ -0,0 +1,161 @@
+#include <mysql/mysql.h>
+#include "stralloc.h"
+#include "qsutil.h"
+#include "mysql_queries.h"
+
+extern int connect_mysql();
+extern int disconnect_mysql();
+
+extern MYSQL dbh, *mysql;
+extern MYSQL_RES *result;
+
+/* simulate reading a .qmail file from ye olde database */
+int dotqmail_mysql(char *username, char *ext, stralloc *sa, int bufsize) {
+  int i, len, num;
+  MYSQL_ROW row;
+  stralloc query = { 0 };
+  stralloc user = { 0 };
+  stralloc real_ext = { 0 };
+#ifdef FUNKY_ALIAS_QUERIES
+#ifdef OIL_AND_WATER
+  stralloc last1 = { 0 };
+  stralloc last2 = { 0 };
+#endif
+#endif
+
+  /* initialise database handle or signal trouble */
+  if (! connect_mysql()) return -1;
+
+  i = str_len(username);
+  if (! stralloc_ready(&user, 2 * i + 1)) return zoiks(-1);
+  mysql_escape_string(user.s, username, i);
+
+  i = str_len(ext);
+  if (! stralloc_ready(&real_ext, 2 * i + 1)) return zoiks(-1);
+  mysql_escape_string(real_ext.s, ext, i);
+
+  /* make the query or signal an out of memory exception */
+  len = str_len(ALIAS) + real_ext.len + user.len - 3;
+  if (! stralloc_ready(&query, len)) return zoiks(-1);
+  if (! stralloc_cats(&query, ALIAS1)) return zoiks(-1);
+  if (! stralloc_cats(&query, user.s)) return zoiks(-1);
+  if (! stralloc_cats(&query, ALIAS2)) return zoiks(-1);
+  if (! stralloc_cats(&query, real_ext.s)) return zoiks(-1);
+  if (! stralloc_cats(&query, ALIAS3)) return zoiks(-1);
+  if (! stralloc_0(&query)) return zoiks(-1);
+
+#ifndef O_NOT_LOG
+  tcplog("query: ", query.s, ";\n");
+#endif
+
+  /* do the query or signal to use .qmail */
+  if (mysql_real_query(mysql, query.s, query.len) < 0) {
+    /* bad => log it anyway */
+    tcplog("error: ", mysql_error(&dbh), "\n");
+    disconnect_mysql();
+    return 0;
+  }
+  result = mysql_store_result(mysql);
+  if (!result) return zoiks(-1);
+
+  num = mysql_num_rows(result);
+
+  /* no rows: let's try the default alias - but not if extension was blank */
+  if (num == 0 && *ext) {
+    mysql_free_result(result);
+    query.len = 0;
+    len = str_len(ALIAS) + user.len - 2;
+    if (! stralloc_ready(&query, len)) return zoiks(-1);
+    if (! stralloc_cats(&query, ALIAS1)) return zoiks(-1);
+    if (! stralloc_cats(&query, user.s)) return zoiks(-1);
+    if (! stralloc_cats(&query, ALIAS2)) return zoiks(-1);
+    if (! stralloc_append(&query, "@")) return zoiks(-1);
+    if (! stralloc_cats(&query, ALIAS3)) return zoiks(-1);
+    if (! stralloc_0(&query)) return zoiks(-1);
+
+#ifndef O_NOT_LOG
+    tcplog("query: ", query.s, ";\n");
+#endif
+
+    /* hmm, a function would have been nice here */
+    if (mysql_real_query(mysql, query.s, query.len) < 0) {
+      /* bad => log it anyway */
+      tcplog("error: ", mysql_error(&dbh), "\n");
+      disconnect_mysql();
+      return 0;
+    }
+
+    result = mysql_store_result(mysql);
+    if (!result) return zoiks(0);
+
+    num = mysql_num_rows(result);
+  }
+
+  /* well it looks like there's really no match */
+  if (num == 0) return zoiks(0);
+
+#ifdef FUNKY_ALIAS_QUERIES
+#ifdef OIL_AND_WATER
+  if (! stralloc_ready(&last1, 2)) return zoiks(-1);
+  if (! stralloc_append(&last1, "0")) return zoiks(-1);
+  if (! stralloc_0(&last1)) return zoiks(-1);
+  if (! stralloc_ready(&last2, 2)) return zoiks(-1);
+  if (! stralloc_append(&last2, "0")) return zoiks(-1);
+  if (! stralloc_0(&last2)) return zoiks(-1);
+#endif
+#endif
+
+  for (i = 0; i < num; i++) {
+    row = mysql_fetch_row(result);
+    if (! *(row[0])) {
+      tcplog("MySQL misconfiguration: alias_username is blank for username '", user.s, "' in alias table!\n");
+      continue;
+    }
+#ifdef FUNKY_ALIAS_QUERIES
+#ifdef OIL_AND_WATER
+    /* discard wildcard matches if we already had explicit matches */
+    if (i && (str_diff(row[2], last1.s) || str_diff(row[3], last2.s))) break;
+
+    if (! stralloc_ready(&last1, str_len(row[2]) + 1)) return zoiks(-1);
+    if (! stralloc_cats(&last1, row[2])) return zoiks(-1);
+    if (! stralloc_0(&last1)) return zoiks(-1);
+    if (! stralloc_ready(&last2, str_len(row[3]) + 1)) return zoiks(-1);
+    if (! stralloc_cats(&last2, row[3])) return zoiks(-1);
+    if (! stralloc_0(&last2)) return zoiks(-1);
+#endif
+#endif
+    /* we need space for &blabla@crap\n\0 */
+    len = str_len(row[0]) + str_len(row[1]) + 4;
+    if (! stralloc_readyplus(sa, len)) return zoiks(-1);
+    /* if the result is too big for the buffer we skip it */
+    if (len > bufsize) continue;
+    /* if the alias username is a | we use the alias host value as the target */
+    if (*row[0] == '|') {
+      /* skip if the pipe would break */
+      if (str_len(row[1]) == 0) continue;
+      if (! stralloc_cats(sa, "| ")) return zoiks(-1);
+    }
+    /* . or / means delivery to a mailbox or maildir */
+    else if (*row[0] == '/' || *row[0] == '.') {
+      *(row[0] + 1) = '\0';
+      if (str_len(row[1]) == 0) continue;
+      if (! stralloc_cats(sa, row[0])) return zoiks(-1);
+    }
+    else {
+      if (! stralloc_cats(sa, "&")) return zoiks(-1);
+      if (! stralloc_cats(sa, row[0])) return zoiks(-1);
+      if (strcmp(row[1], "") && ! stralloc_cats(sa, "@")) return zoiks(-1);
+    }
+    if (! stralloc_cats(sa, row[1])) return zoiks(-1);
+    if (! stralloc_cats(sa, "\n")) return zoiks(-1);
+  }
+
+  return zoiks(num);
+}
+
+/* free memory used by query, disconnect from database and return */
+int zoiks(int ret) {
+  mysql_free_result(result);
+  disconnect_mysql();
+  return ret;
+}
--- ../qmail-1.03/dotqmail_mysql.h	1970-01-01 01:00:00.000000000 +0100
+++ dotqmail_mysql.h	2004-11-09 10:36:13.000000000 +0000
@@ -0,0 +1 @@
+int dotqmail_mysql(char *username, char *ext, stralloc *sa, int bufsize);
--- ../qmail-1.03/getpw_mysql.c	1970-01-01 01:00:00.000000000 +0100
+++ getpw_mysql.c	2004-11-09 10:36:13.000000000 +0000
@@ -0,0 +1,50 @@
+#include <mysql/mysql.h>
+#include "mysql_queries.h"
+#include "scan.h"
+#include "stralloc.h"
+
+stralloc tmpname = { 0 };
+stralloc tmphome = { 0 };
+stralloc tmpshell = { 0 };
+
+extern MYSQL_ROW do_query(stralloc *query);
+extern MYSQL_RES *result;
+extern int make_query(stralloc *query, char *username);
+
+/* get a pw entry from the database */
+int getpw_mysql(char *username, int *uid, int *gid) {
+  MYSQL_ROW row;
+  int num, len, ret;
+  stralloc qmail_mysql_query = { 0 };
+
+  if (! make_query(&qmail_mysql_query, username)) return -1;
+  if (! (int) row) return -1;
+  row = do_query(&qmail_mysql_query);
+  if ((int) row < 1) return (int) row;
+
+  if (! stralloc_ready(&tmpname, str_len(username))) return -1;
+  if (! stralloc_copys(&tmpname, username)) return -1;
+  if (! stralloc_0(&tmpname)) return -1;
+  if (! stralloc_ready(&tmphome, str_len(row[2]))) return -1;
+  if (! stralloc_copys(&tmphome, row[2])) return -1;
+  if (! stralloc_0(&tmphome)) return -1;
+  if (! stralloc_ready(&tmpshell, str_len(row[3]))) return -1;
+  if (! stralloc_copys(&tmpshell, row[3])) return -1;
+  if (! stralloc_0(&tmpshell)) return -1;
+
+  /* OK so we're assuming the database really does return ints */
+  len = scan_ulong(row[0], uid);
+  if (! len || (row[0])[len]) {
+    log3("MySQL misconfiguration: uid should be a number for user ", username , "!\n");
+    mysql_free_result(result);
+    return -1;
+  }
+  len = scan_ulong(row[1], gid);
+  if (! len || (row[1])[len]) {
+    log3("MySQL misconfiguration: gid should be a number for user ", username, "!\n");
+    mysql_free_result(result);
+    return -1;
+  }
+
+  return 1;
+}
--- ../qmail-1.03/make_query.c	1970-01-01 01:00:00.000000000 +0100
+++ make_query.c	2004-11-09 10:36:13.000000000 +0000
@@ -0,0 +1,20 @@
+#include "mysql_queries.h"
+#include "stralloc.h"
+
+int make_query(stralloc *query, char *username) {
+  int i, len;
+  stralloc user = { 0 };
+
+  i = 2 * str_len(username) + 1;
+  if (! stralloc_ready(&user, i)) return 0;
+  mysql_escape_string(user.s, username, str_len(username));
+
+  len = str_len(QUERY) + user.len + 2;
+  if (! stralloc_ready(query, len)) { stralloc_free(&user); return 0; }
+  if (! stralloc_cats(query, QUERY)) { stralloc_free(&user); return 0; }
+  if (! stralloc_cats(query, user.s)) { stralloc_free(&user); return 0; }
+  stralloc_free(&user);
+  if (! stralloc_cats(query, "'")) return 0;
+  if (! stralloc_0(query)) return 0;
+  return 1;
+}
--- ../qmail-1.03/mysql_queries.h	1970-01-01 01:00:00.000000000 +0100
+++ mysql_queries.h	2004-11-09 10:36:13.000000000 +0000
@@ -0,0 +1,55 @@
+/******************************************************************************/
+/* We define two queries that will be used to authenticate users from the     */
+/* database.  QUERY will be used to check for normal mailboxes and            */
+/* VIRTUAL will query virtual domains                                         */
+/* Furthermore we define the query that will allow us to expand aliases       */
+/******************************************************************************/
+
+/* virtual host query */
+/* please read the README.queries file */
+#ifndef VIRTUAL
+#ifdef FUNKY_VIRTUAL_QUERIES
+#ifdef HOST_HAS_PRECEDENCE
+#define VIRTUAL_SORTING "wild_h, wild_u"
+#else
+#define VIRTUAL_SORTING "wild_u, wild_h"
+#endif
+#define VIRTUAL1 "select username, ext, virtual_host rlike '[%_]' as wild_h, virtual_username rlike '[%_]' as wild_u from virtual where '"
+#define VIRTUAL2 "' like virtual_username and '"
+#define VIRTUAL3 "' like virtual_host order by " VIRTUAL_SORTING " limit 1"
+#else
+#define VIRTUAL1 "select username, ext from virtual where (virtual_username='' or virtual_username='"
+#define VIRTUAL2 "') and virtual_host='"
+#define VIRTUAL3 "' order by virtual_username desc limit 1"
+#endif
+#define VIRTUAL VIRTUAL1 VIRTUAL2 VIRTUAL3
+#endif
+
+/* standard query */
+#define QUERY "select uid, gid, home, '/bin/false' from mailbox where username='"
+
+/* if we couldn't find a user we process an alias */
+#ifndef ALIAS
+#ifdef FUNKY_ALIAS_QUERIES
+#ifdef ALIAS_HAS_PRECEDENCE
+#define ALIAS_SORTING "wild_a, wild_u"
+#define WILD "alias rlike '[%_]' as wild_a, username rlike '[%_]' as wild_u from alias where '"
+#else
+#define ALIAS_SORTING "wild_u, wild_a"
+#define WILD "username rlike '[%_]' as wild_u, alias rlike '[%_]' as wild_a from alias where '"
+#endif
+#define ALIAS1 "select alias_username, alias_host, " WILD
+#define ALIAS2 "' like username and '"
+#define ALIAS3 "' like alias order by " ALIAS_SORTING
+#else
+#define ALIAS1 "select alias_username, alias_host from alias where username='"
+#define ALIAS2 "' and alias='"
+#define ALIAS3 "'"
+#endif
+#define ALIAS ALIAS1 ALIAS2 ALIAS3
+#endif
+
+/* is this domain in rcpthosts? */
+#ifndef RCPTHOSTS
+#define RCPTHOSTS "select count(*) from rcpthosts where host='"
+#endif
--- ../qmail-1.03/qmail-getpw.c	2004-11-09 10:36:02.000000000 +0000
+++ qmail-getpw.c	2004-11-09 10:36:13.000000000 +0000
@@ -13,18 +13,29 @@
 #include "auto_usera.h"
 #include "auto_break.h"
 #include "qlx.h"
+#include "stralloc.h"
 
 #define GETPW_USERLEN 32
 
+extern int getpw_mysql (char *username, int *UID, int *GID);
+extern void disconnect_mysql();
+extern int connect_mysql();
+
 char *local;
 struct passwd *pw;
 char *dash;
 char *extension;
+int connection;
+
+extern stralloc tmpname;
+extern stralloc tmphome;
+extern stralloc tmpshell;
 
 int userext()
 {
   char username[GETPW_USERLEN];
   struct stat st;
+  int num, uid, gid;
 
   extension = local + str_len(local);
   for (;;) {
@@ -34,21 +45,41 @@
 	username[extension - local] = 0;
 	case_lowers(username);
 	errno = 0;
-	pw = getpwnam(username);
 	if (errno == error_txtbsy) _exit(QLX_SYS);
+	num = 0;
+#ifdef SOLARIS_STUPIDITY
+  substdio_put(subfderr,"",1);
+#endif
+	if (connection) num = getpw_mysql(username, &uid, &gid);
+	if (num == -1) _exit(QLX_SYS);
+	else if (num) {
+	  pw = (struct passwd *) malloc(sizeof(struct passwd));
+	  pw->pw_name = tmpname.s;
+	  pw->pw_uid = uid;
+	  pw->pw_gid = gid;
+	  pw->pw_dir = tmphome.s;
+	  pw->pw_shell = tmpshell.s;
+	}
+	if (! pw) pw = getpwnam(username);
 	if (pw)
 	  if (pw->pw_uid)
 	    if (stat(pw->pw_dir,&st) == 0) {
 	      if (st.st_uid == pw->pw_uid) {
 		dash = "";
-		if (*extension) { ++extension; dash = "-"; }
+		if (*extension) { ++extension; dash = auto_break; }
 		return 1;
 	      }
 	    }
 	    else
 	      if (error_temp(errno)) _exit(QLX_NFS);
       }
-    if (extension == local) return 0;
+    if (extension == local) {
+      /* we didn't find a system account */
+      /* but the database is down */
+      if (connection) return 0;
+      disconnect_mysql();
+      _exit(QLX_SYS);
+    }
     --extension;
   }
 }
@@ -62,12 +93,16 @@
   local = argv[1];
   if (!local) _exit(100);
 
+  connection = connect_mysql();
+
   if (!userext()) {
     extension = local;
-    dash = "-";
+    dash = auto_break;
     pw = getpwnam(auto_usera);
   }
 
+  if (connection) disconnect_mysql();
+
   if (!pw) _exit(QLX_NOALIAS);
 
   substdio_puts(subfdoutsmall,pw->pw_name);
--- ../qmail-1.03/qmail-local.c	2004-11-09 10:36:02.000000000 +0000
+++ qmail-local.c	2004-11-09 10:36:13.000000000 +0000
@@ -28,6 +28,7 @@
 #include "myctime.h"
 #include "gfrom.h"
 #include "auto_patrn.h"
+#include "dotqmail_mysql.h"
 
 void usage() { strerr_die1x(100,"qmail-local: usage: qmail-local [ -nN ] user homedir local dash ext domain sender aliasempty"); }
 
@@ -458,6 +459,7 @@
  datetime_sec starttime;
  int flagforwardonly;
  char *x;
+ int mysql_alias;
 
  umask(077);
  sig_pipeignore();
@@ -584,9 +586,14 @@
 
  flagforwardonly = 0;
  qmesearch(&fd,&flagforwardonly);
- if (fd == -1)
-   if (*dash)
-     strerr_die1x(100,"Sorry, no mailbox here by that name. (#5.1.1)");
+ mysql_alias = dotqmail_mysql(user, ext, &cmds, 256);
+ if (mysql_alias == -1) temp_nomem();
+ if (mysql_alias == 0) {
+   if (fd == -1 && *dash) {
+       strerr_die1x(100,"Sorry, no mailbox here by that name. (#5.1.1)");
+   }
+ }
+ else close(fd);
 
  if (!stralloc_copys(&ueo,sender)) temp_nomem();
  if (str_diff(sender,""))
@@ -611,8 +618,7 @@
  if (!env_put2("NEWSENDER",ueo.s)) temp_nomem();
 
  if (!stralloc_ready(&cmds,0)) temp_nomem();
- cmds.len = 0;
- if (fd != -1)
+ if (mysql_alias == 0 && fd != -1)
    if (slurpclose(fd,&cmds,256) == -1) temp_nomem();
 
  if (!cmds.len)
--- ../qmail-1.03/qmail-send.c	2004-11-09 10:36:02.000000000 +0000
+++ qmail-send.c	2004-11-09 10:36:13.000000000 +0000
@@ -1,3 +1,4 @@
+#include <mysql/mysql.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include "readwrite.h"
@@ -42,6 +43,12 @@
 #define SLEEP_SYSFAIL 123
 #define OSSIFIED 129600 /* 36 hours; _must_ exceed q-q's DEATH (24 hours) */
 
+extern int checkuser_mysql(char *username, int i);
+extern int vdoms_mysql(char *host, int len, stralloc *store);
+
+extern MYSQL dbh, *mysql;
+extern MYSQL_RES *result;
+
 int lifetime = 604800;
 
 stralloc percenthack = {0};
@@ -119,7 +126,7 @@
   int j;
   char *x;
   static stralloc addr = {0};
-  int at;
+  int at, ret;
 
   if (!stralloc_copys(&rwline,"T")) return 0;
   if (!stralloc_copys(&addr,recip)) return 0;
@@ -139,13 +146,31 @@
   }
 
   at = byte_rchr(addr.s,addr.len,'@');
+  i = 0;
+  ret = 2;
 
   if (constmap(&maplocals,addr.s + at + 1,addr.len - at - 1)) {
-    if (!stralloc_cat(&rwline,&addr)) return 0;
-    if (!stralloc_0(&rwline)) return 0;
-    return 1;
+    ret = 1;
+    i = checkuser_mysql(addr.s, at);
+    if (i == -1) return 0;
+    if (i) {
+      if (!stralloc_cat(&rwline,&addr)) return 0;
+      if (!stralloc_0(&rwline)) return 0;
+      return 1;
+    }
+    i = vdoms_mysql(addr.s, addr.len, &rwline);
+    if (i == -1) return 0;
+    if (i == 2) return 2;
+    if (i) return 1;
+    addr.len = at + 1;
+    if (!stralloc_cat(&addr, &envnoathost)) return 0;
   }
 
+  i = vdoms_mysql(addr.s, addr.len, &rwline);
+  if (i == -1) return 0;
+  if (i == 2) return 2;
+  if (i == 1) return 1;
+
   for (i = 0;i <= addr.len;++i)
     if (!i || (i == at + 1) || (i == addr.len) || ((i > at) && (addr.s[i] == '.')))
       if (x = constmap(&mapvdoms,addr.s + i,addr.len - i)) {
@@ -159,7 +184,7 @@
  
   if (!stralloc_cat(&rwline,&addr)) return 0;
   if (!stralloc_0(&rwline)) return 0;
-  return 2;
+  return ret;
 }
 
 void senderadd(sa,sender,recip)
@@ -1608,5 +1633,6 @@
   }
  pqfinish();
  log1("status: exiting\n");
+ disconnect_mysql();
  _exit(0);
 }
--- ../qmail-1.03/qmail-smtpd.c	2004-11-09 10:36:02.000000000 +0000
+++ qmail-smtpd.c	2005-05-07 13:05:20.000000000 +0100
@@ -24,15 +24,25 @@
 #include "timeoutwrite.h"
 #include "commands.h"
 
+extern int connect_mysql();
+extern void disconnect_mysql();
+extern int rcpthosts_mysql(char *host, int len);
+
 #define MAXHOPS 100
 unsigned int databytes = 0;
 int timeout = 1200;
+stralloc qmail_mysql_query = { 0 };
+
+#define ACK {\
+  disconnect_mysql();\
+  _exit(1);\
+}
 
 int safewrite(fd,buf,len) int fd; char *buf; int len;
 {
   int r;
   r = timeoutwrite(timeout,fd,buf,len);
-  if (r <= 0) _exit(1);
+  if (r <= 0) ACK
   return r;
 }
 
@@ -42,12 +52,13 @@
 void flush() { substdio_flush(&ssout); }
 void out(s) char *s; { substdio_puts(&ssout,s); }
 
-void die_read() { _exit(1); }
-void die_alarm() { out("451 timeout (#4.4.2)\r\n"); flush(); _exit(1); }
-void die_nomem() { out("421 out of memory (#4.3.0)\r\n"); flush(); _exit(1); }
-void die_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 die_read() { ACK }
+void die_alarm() { out("451 timeout (#4.4.2)\r\n"); flush(); ACK }
+void die_nomem() { out("421 out of memory (#4.3.0)\r\n"); flush(); ACK }
+void die_mysql() { out("421 who killed the MySQL server? (#4.3.0)\r\n"); flush(); _exit(1); }
+void die_control() { out("421 unable to read controls (#4.3.0)\r\n"); flush(); ACK }
+void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); ACK }
+void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); ACK }
 
 void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); }
 void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); }
@@ -73,6 +84,7 @@
 }
 void smtp_quit()
 {
+  disconnect_mysql();
   smtp_greet("221 "); out("\r\n"); flush(); _exit(0);
 }
 
@@ -213,6 +225,8 @@
   int r;
   r = rcpthosts(addr.s,str_len(addr.s));
   if (r == -1) die_control();
+  if (! r) r = rcpthosts_mysql(addr.s,str_len(addr.s));
+  if (r == -1) die_control();
   return r;
 }
 
@@ -411,6 +425,7 @@
 void main()
 {
   sig_pipeignore();
+  if (! connect_mysql()) die_mysql();
   if (chdir(auto_qmail) == -1) die_control();
   setup();
   if (ipme_init() != 1) die_ipme();
--- ../qmail-1.03/qsutil.c	2004-11-09 10:36:02.000000000 +0000
+++ qsutil.c	2004-11-09 10:36:13.000000000 +0000
@@ -7,6 +7,7 @@
 
 static char errbuf[1];
 static struct substdio sserr = SUBSTDIO_FDBUF(write,0,errbuf,1);
+static struct substdio tcpsserr = SUBSTDIO_FDBUF(write,2,errbuf,1);
 
 void logsa(sa) stralloc *sa; {
  substdio_putflush(&sserr,sa->s,sa->len); }
@@ -19,6 +20,10 @@
  substdio_putsflush(&sserr,s1);
  substdio_putsflush(&sserr,s2);
  substdio_putsflush(&sserr,s3); }
+void tcplog(s1,s2,s3) char *s1; char *s2; char *s3; {
+ substdio_putsflush(&tcpsserr,s1);
+ substdio_putsflush(&tcpsserr,s2);
+ substdio_putsflush(&tcpsserr,s3); }
 void nomem() { log1("alert: out of memory, sleeping...\n"); sleep(10); }
 
 void pausedir(dir) char *dir;
--- ../qmail-1.03/qsutil.h	2004-11-09 10:36:02.000000000 +0000
+++ qsutil.h	2004-11-09 10:36:13.000000000 +0000
@@ -9,4 +9,12 @@
 extern void pausedir();
 extern void logsafe();
 
+#ifdef EBUG_DEBUG_DEBUG
+#define DEBUG_PUT(x) substdio_puts(subfderr, x)
+#define DEBUG_SAY(x) substdio_putsflush(subfderr, x)
+#else
+#define DEBUG_PUT(x) (0)
+#define DEBUG_SAY(x) (0)
+#endif
+
 #endif
--- ../qmail-1.03/rcpthosts_mysql.c	1970-01-01 01:00:00.000000000 +0100
+++ rcpthosts_mysql.c	2004-11-09 10:36:13.000000000 +0000
@@ -0,0 +1,62 @@
+#include <mysql/mysql.h>
+#include "control.h"
+#include "mysql_queries.h"
+#include "stralloc.h"
+
+extern int connect_mysql();
+
+extern MYSQL dbh, *mysql;
+extern MYSQL_RES *result;
+
+extern MYSQL_ROW do_query(stralloc *query);
+
+/* qmail-smtpd needs to know if it can accept delivery for this domain */
+int rcpthosts_mysql(char *addr, int len) {
+  char *host;
+  int i, num;
+  MYSQL_ROW row;
+  stralloc real_host = { 0 };
+  stralloc qmail_mysql_query = { 0 };
+
+  /* maybe the client's so slow we lost the MySQL connection... */
+  if (! connect_mysql()) return -1;
+
+  num = len;
+  host = addr + len;
+  while (*host != '@') {
+    host--;
+    num--;
+    if (host == addr) break;
+  }
+
+  /* no @ => envnoathost */
+  if (num == 0) return 1;
+
+  /* let's lose the @ we found and adjust len */
+  host++;
+
+  if (! stralloc_ready(&real_host, 2 * str_len(host) + 1)) return -1;
+  mysql_escape_string(real_host.s, host, str_len(host));
+
+#define OOF {\
+  stralloc_free(&real_host);\
+  stralloc_free(&qmail_mysql_query);\
+  return -1;\
+}
+  len = real_host.len + str_len(RCPTHOSTS) + 2;
+  if (! stralloc_ready(&qmail_mysql_query, len)) OOF
+  if (! stralloc_cats(&qmail_mysql_query, RCPTHOSTS)) OOF
+  if (! stralloc_cats(&qmail_mysql_query, real_host.s)) OOF
+  if (! stralloc_cats(&qmail_mysql_query, "'")) OOF
+  if (! stralloc_0(&qmail_mysql_query)) OOF
+  stralloc_free(&real_host);
+
+  row = do_query(&qmail_mysql_query);
+
+  if ((int) row == 0) return -1;
+
+  num = atoi(row[0]);
+  mysql_free_result(result);
+
+  return num;
+}
--- ../qmail-1.03/stralloc.h	2004-11-09 10:36:02.000000000 +0000
+++ stralloc.h	2004-11-09 10:36:13.000000000 +0000
@@ -13,6 +13,7 @@
 extern int stralloc_cats();
 extern int stralloc_copyb();
 extern int stralloc_catb();
+extern void stralloc_free();
 extern int stralloc_append(); /* beware: this takes a pointer to 1 char */
 extern int stralloc_starts();
 
--- ../qmail-1.03/stralloc_free.c	1970-01-01 01:00:00.000000000 +0100
+++ stralloc_free.c	2004-11-09 10:36:13.000000000 +0000
@@ -0,0 +1,6 @@
+#include "stralloc.h"
+
+void stralloc_free(stralloc *labrat) {
+  alloc_free(labrat->s);
+  labrat->len = 0;
+}
--- ../qmail-1.03/vdoms_mysql.c	1970-01-01 01:00:00.000000000 +0100
+++ vdoms_mysql.c	2004-11-09 10:36:38.000000000 +0000
@@ -0,0 +1,125 @@
+#include <mysql/mysql.h>
+#include "auto_break.h"
+#include "control.h"
+#include "fmt.h"
+#include "mysql_queries.h"
+#include "qsutil.h"
+#include "subfd.h"
+#include "substdio.h"
+#include "stralloc.h"
+
+extern int connect_mysql();
+
+extern MYSQL dbh, *mysql;
+extern MYSQL_RES *result;
+extern stralloc envnoathost;
+extern char strnum3[FMT_ULONG];
+
+extern MYSQL_ROW do_query(stralloc *query);
+
+/* map a virtual domain to a user-ext stralloc */
+int vdoms_mysql(char *addr, int len, stralloc *ret) {
+  char *host;
+  int at, num;
+  MYSQL_ROW row;
+  stralloc qmail_mysql_query = { 0 };
+  stralloc real_addr = { 0 };
+  stralloc real_host = { 0 };
+
+  /* stralloc we're passed is unterminated */
+  addr[len] = '\0';
+  DEBUG_SAY("vdoms_mysql: ");
+  DEBUG_SAY(addr);
+  DEBUG_SAY("\n");
+  num = len;
+  host = addr + len;
+  while (*host != '@') {
+    host--;
+    num--;
+    if (host == addr) break;
+  }
+  at = num;
+
+  /* let's lose the @ we found */
+  host++;
+  if (! stralloc_ready(&real_addr, 2 * str_len(addr) + 1)) return -1;
+  mysql_escape_string(real_addr.s, addr, at);
+  if (! stralloc_ready(&real_host, 2 * str_len(host) + 1)) {
+    stralloc_free(&real_addr);
+    return -1;
+  }
+  mysql_escape_string(real_host.s, host, str_len(host));
+
+#define ACK {\
+  stralloc_free(&real_addr);\
+  stralloc_free(&real_host);\
+  stralloc_free(&qmail_mysql_query);\
+  return -1;\
+}
+  if (! connect_mysql()) ACK
+
+  len += real_addr.len + real_host.len + str_len(VIRTUAL) + 2;
+  if (! stralloc_ready(&qmail_mysql_query, len)) ACK
+  if (! stralloc_cats(&qmail_mysql_query, VIRTUAL1)) ACK
+  if (! stralloc_cats(&qmail_mysql_query, real_addr.s)) ACK
+  if (! stralloc_cats(&qmail_mysql_query, VIRTUAL2)) ACK
+  if (! stralloc_cats(&qmail_mysql_query, real_host.s)) ACK
+  if (! stralloc_cats(&qmail_mysql_query, VIRTUAL3)) ACK
+  if (! stralloc_0(&qmail_mysql_query)) ACK
+
+#ifndef O_NOT_LOG
+  log3("query: ", qmail_mysql_query.s, ";\n");
+#endif
+  row = do_query(&qmail_mysql_query);
+
+  /* no row doesn't mean an error */
+  if ((int) row < 1) {
+    stralloc_free(&real_addr);
+    stralloc_free(&real_host);
+    return (int) row;
+  }
+
+  /* run database sanity check: username may not be blank */
+  if (! *(row[0])) {
+    log3("MySQL misconfiguration: username for host '", real_host.s, "' is blank in virtual table!\n");
+    stralloc_free(&real_addr);
+    stralloc_free(&real_host);
+    mysql_free_result(result);
+    return 0;
+  }
+
+#define YOW {\
+  stralloc_free(&real_addr);\
+  stralloc_free(&real_host);\
+  mysql_free_result(result);\
+  return -1;\
+}
+#ifdef MAGIC_FORWARDING
+  if (! str_diff(row[0], MAGIC_FORWARD_USER)) {
+    if (! stralloc_cats(ret, row[1])) YOW
+    if (! stralloc_0(ret)) YOW
+    mysql_free_result(result);
+    stralloc_free(&real_addr);
+    stralloc_free(&real_host);
+#ifdef MAGIC_LOGGING
+    log2("magic forward: msg ", strnum3);
+    log2(" for ", addr);
+    log3(" to ", row[1], "\n");
+#endif
+    return 2;
+  }
+#endif
+  if (! stralloc_cats(ret, row[0])) YOW  /* username */
+  if (str_len(row[1])) {
+    if (! stralloc_append(ret, auto_break)) YOW
+    if (*row[1] != '@') { if (! stralloc_cats(ret, row[1])) YOW }
+    else if (! stralloc_cats(ret, real_addr.s)) YOW  /* ext */
+  }
+  if (! stralloc_append(ret, "@")) YOW
+  if (! stralloc_cats(ret, real_host.s)) YOW
+  if (! stralloc_0(ret)) YOW
+  mysql_free_result(result);
+  stralloc_free(&real_addr);
+  stralloc_free(&real_host);
+  return 1;
+}
