--- ../qmail-1.03-orig/Makefile	Fri Feb 11 15:51:20 2000
+++ Makefile	Fri Feb 11 15:47:13 2000
@@ -1,9 +1,51 @@
 # Don't edit Makefile! Use conf-* for configuration.
 
+MYSQL_LIBS=/usr/local/lib/mysql/libmysqlclient.a -lm
+MYSQL_INCLUDE=-I/usr/local/include
+LOGGING=#-DO_NOT_LOG
 SHELL=/bin/sh
 
 default: it
 
+init_mysql.o: compile init_mysql.c auto_qmail.h
+	./compile init_mysql.c $(MYSQL_INCLUDE)
+
+connect_mysql.o: compile connect_mysql.c
+	./compile connect_mysql.c $(MYSQL_INCLUDE)
+
+vdoms_mysql.o: compile vdoms_mysql.c mysql_queries.h
+	./compile vdoms_mysql.c $(MYSQL_INCLUDE) $(LOGGING)
+
+rcpthosts_mysql.o: compile rcpthosts_mysql.c mysql_queries.h
+	./compile rcpthosts_mysql.c $(MYSQL_INCLUDE) $(LOGGING)
+
+getpw_mysql.o: compile getpw_mysql.c mysql_queries.h
+	./compile getpw_mysql.c $(MYSQL_INCLUDE) $(LOGGING)
+
+dotqmail_mysql.o: compile dotqmail_mysql.c mysql_queries.h
+	./compile dotqmail_mysql.c $(MYSQL_INCLUDE) $(LOGGING)
+
+do_query.o: compile do_query.c mysql_queries.h
+	./compile do_query.c $(MYSQL_INCLUDE) $(LOGGING)
+
+splog.o: compile splog.c
+	./compile splog.c $(LOGGING)
+
+addrexp.o: \
+compile addrexp.c
+	./compile addrexp.c
+
+addrexp: \
+load addrexp.o init_mysql.o connect_mysql.o do_query.o getpw_mysql.o vdoms_mysql.o \
+splog.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 init_mysql.o connect_mysql.o \
+	do_query.o getpw_mysql.o vdoms_mysql.o \
+	splog.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 +847,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 +1151,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 
+auto_usera.o auto_qmail.o init_mysql.o connect_mysql.o getpw_mysql.o \
+do_query.o splog.o scan_ulong.o open_read.o \
+getln.a stralloc.a alloc.a substdio.a
+	./load qmail-getpw \
+	auto_break.o auto_usera.o auto_qmail.o init_mysql.o connect_mysql.o \
+	getpw_mysql.o do_query.o splog.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
@@ -1172,14 +1219,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 \
+splog.o slurpclose.o init_mysql.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 \
+	slurpclose.o init_mysql.o connect_mysql.o do_query.o dotqmail_mysql.o \
+	splog.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
@@ -1481,14 +1530,16 @@
 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 \
+init_mysql.o connect_mysql.o getpw_mysql.o vdoms_mysql.o do_query.o splog.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 \
+	init_mysql.o connect_mysql.o getpw_mysql.o vdoms_mysql.o do_query.o splog.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 $(MYSQL_LIBS)
 
 qmail-send.0: \
 qmail-send.8
@@ -1509,7 +1560,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 +1585,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 init_mysql.o connect_mysql.o rcpthosts_mysql.o \
+do_query.o splog.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 \
+	received.o date822fmt.o now.o qmail.o init_mysql.o connect_mysql.o \
+	rcpthosts_mysql.o do_query.o splog.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
--- ../qmail-1.03-orig/addrexp.c	Fri Feb 11 15:51:20 2000
+++ addrexp.c	Thu Feb 10 15:23:17 2000
@@ -0,0 +1,108 @@
+#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 char *vdoms_mysql();
+extern int checkuser_mysql();
+
+extern char auto_qmail[];
+
+stralloc envnoathost = { 0 };
+stralloc locals = { 0 };
+stralloc percenthack = { 0 };
+struct constmap maplocals;
+struct constmap mappercenthack;
+
+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) printf("vdoms_mysql(%s, %i, &ret) = ", addr.s, addr.len);
+  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-orig/connect_mysql.c	Fri Feb 11 15:51:20 2000
+++ connect_mysql.c	Thu Feb 10 13:23:54 2000
@@ -0,0 +1,41 @@
+#include <mysql/mysql.h>
+#include  "stralloc.h"
+
+#define TRIES 3
+
+/* let's alloc our strallocs */
+extern stralloc DB_HOST;
+extern stralloc DB_USER;
+extern stralloc DB_PASS;
+extern stralloc DB_NAME;
+extern stralloc DB_SOCK;
+extern stralloc DB_PORT;
+
+extern MYSQL dbh, *mysql;
+
+/* connect to the database                                                    */
+int connect_mysql() {
+  char *sock;
+  int i, port;
+
+  if (mysql) return 1;
+  if (init_mysql() == -1) return -1;
+  if (DB_PORT.a > 0) port = atoi(DB_PORT.s);
+  else port = 0;
+  if (DB_SOCK.a > 0) sock = DB_SOCK.s;
+  else sock = (char) 0;
+  for (i = 0; i < TRIES; i++) {
+    mysql = mysql_real_connect(&dbh, DB_HOST.s, DB_USER.s, DB_PASS.s, DB_NAME.s,
+                               port, sock, 0);
+    if (mysql) break;
+    splog("mysql-error", mysql_error(&dbh));
+    sleep(3);
+  }
+
+  if (i < TRIES) return 1;
+  else return 0;
+}
+
+void disconnect_mysql() {
+  mysql_close(&dbh);
+}
--- ../qmail-1.03-orig/do_query.c	Fri Feb 11 15:51:20 2000
+++ do_query.c	Thu Feb 10 14:29:40 2000
@@ -0,0 +1,30 @@
+#include <mysql/mysql.h>
+#include "stralloc.h"
+
+extern MYSQL dbh, *mysql;
+extern MYSQL_RES *result;
+
+extern splog();
+
+/* run a given query and return 1 row only */
+MYSQL_ROW do_query(stralloc *query) {
+  int num;
+
+  if (! connect_mysql()) return -1;
+  splog("qmail-mysql", query->s);
+  if (mysql_real_query(mysql, query->s, query->len) < 0) {
+    splog("mysql-error", mysql_error(&dbh));
+    return -1;
+  }
+  if (! (result = mysql_store_result(mysql))) return -1;
+
+  num = mysql_num_rows(result);
+
+  if (num != 1) {
+    mysql_free_result(result);
+    return 0;
+  }
+
+  return mysql_fetch_row(result);
+}
+
--- ../qmail-1.03-orig/dotqmail_mysql.c	Fri Feb 11 15:51:20 2000
+++ dotqmail_mysql.c	Thu Feb 10 14:37:50 2000
@@ -0,0 +1,113 @@
+#include <mysql/mysql.h>
+#include "stralloc.h"
+#include "mysql_queries.h"
+
+extern int init_mysql();
+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 };
+
+  /* initialise database handle or signal trouble */
+  if (! connect_mysql()) return -1;
+
+  num = 0;
+  if (*ext) {
+    /* make the query or signal an out of memory exception */
+    len = str_len(ALIAS) + str_len(ext) + str_len(username) - 3;
+    if (! stralloc_ready(&query, len)) return zoiks(-1);
+    if (! stralloc_cats(&query, ALIAS1)) return zoiks(-1);
+    if (! stralloc_cats(&query, username)) return zoiks(-1);
+    if (! stralloc_cats(&query, ALIAS2)) return zoiks(-1);
+    if (! stralloc_cats(&query, ext)) return zoiks(-1);
+    if (! stralloc_cats(&query, "'")) return zoiks(-1);
+    if (! stralloc_0(&query)) return zoiks(-1);
+
+    splog("qmail-mysql", query.s);
+
+    /* do the query or signal to use .qmail */
+    if (mysql_real_query(mysql, query.s, query.len) < 0) {
+      splog("mysql-error", mysql_error(&dbh));
+      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 */
+  if (num == 0) {
+    mysql_free_result(result);
+    query.len = 0;
+    len = str_len(ALIAS) + str_len(username) - 3;
+    if (! stralloc_ready(&query, len)) return zoiks(-1);
+    if (! stralloc_cats(&query, ALIAS1)) return zoiks(-1);
+    if (! stralloc_cats(&query, username)) return zoiks(-1);
+    if (! stralloc_cats(&query, ALIAS2)) return zoiks(-1);
+    if (! stralloc_append(&query, "'")) return zoiks(-1);
+    if (! stralloc_0(&query)) return zoiks(-1);
+
+    splog("qmail-mysql", query.s);
+
+    /* hmm, a function would have been nice here */
+    if (mysql_real_query(mysql, query.s, query.len) < 0) {
+      splog("mysql-error", mysql_error(&dbh));
+      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);
+
+  for (i = 0; i < num; i++) {
+    row = mysql_fetch_row(result);
+    /* 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-orig/dotqmail_mysql.h	Fri Feb 11 15:51:20 2000
+++ dotqmail_mysql.h	Wed Feb  9 17:16:32 2000
@@ -0,0 +1 @@
+int dotqmail_mysql(char *username, char *ext, stralloc *sa, int bufsize);
--- ../qmail-1.03-orig/getpw_mysql.c	Fri Feb 11 15:51:20 2000
+++ getpw_mysql.c	Fri Feb 11 14:35:39 2000
@@ -0,0 +1,73 @@
+#include <mysql/mysql.h>
+#include "mysql_queries.h"
+#include "stralloc.h"
+
+stralloc tmpname = { 0 };
+stralloc tmphome = { 0 };
+stralloc tmpshell = { 0 };
+
+extern MYSQL dbh, *mysql;
+extern MYSQL_RES *result;
+
+extern MYSQL_ROW do_query(stralloc *query);
+
+stralloc make_query(char *username);
+
+/* get a pw entry from the database */
+int getpw_mysql(char *username, int *uid, int *gid) {
+  int num, len;
+  MYSQL_ROW row;
+  stralloc query = { 0 };
+
+  query = make_query(username);
+  if (query.len == 0) return -1;
+  row = do_query(&query);
+  /* row might be -1 (error) or 0 (no data returned) - these codes are fine */
+  if ((int) row < 1) return 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 */
+  *uid = atoi(row[0]);
+  *gid = atoi(row[1]);
+
+  return 1;
+}
+
+int checkuser_mysql(char *username, int i) {
+  MYSQL_ROW row;
+  stralloc user = { 0 };
+  stralloc query = { 0 };
+
+  if (! stralloc_ready(&user, i)) return -1;
+  if (! stralloc_cats(&user, username)) return -1;
+  user.s[i] = '\0';
+  query = make_query(user.s);
+  if (query.len == 0) return -1;
+
+  row = do_query(&query);
+  if ((int) row < 1) return row;
+  return 1;
+}
+
+stralloc make_query(char *username) {
+  int len;
+  stralloc query = { 0 };
+  stralloc nullstralloc = { 0 };
+
+  len = str_len(QUERY) + str_len(username) + 2;
+  if (! stralloc_ready(&query, len)) return nullstralloc;
+  if (! stralloc_cats(&query, QUERY)) return nullstralloc;
+  if (! stralloc_cats(&query, username)) return nullstralloc;
+  if (! stralloc_cats(&query, "'")) return nullstralloc;
+  if (! stralloc_0(&query)) return nullstralloc;
+  return query;
+}
--- ../qmail-1.03-orig/init_mysql.c	Fri Feb 11 15:51:20 2000
+++ init_mysql.c	Fri Feb 11 15:42:48 2000
@@ -0,0 +1,112 @@
+#include <mysql/mysql.h>
+#include "auto_qmail.h"
+#include "readwrite.h"
+#include "str.h"
+#include "stralloc.h"
+#include "substdio.h"
+#include "subfd.h"
+
+#define SQLSERVER "/control/sqlserver"
+
+/* let's alloc our strallocs */
+stralloc DB_HOST = { 0 };
+stralloc DB_USER = { 0 };
+stralloc DB_PASS = { 0 };
+stralloc DB_NAME = { 0 };
+stralloc DB_SOCK = { 0 };
+stralloc DB_PORT = { 0 };
+
+static char inbuf[64];
+MYSQL dbh, *mysql;
+MYSQL_RES *result;
+
+/* read the config file and decide which server the database is on, what      */
+/* username and password we need to connect to it and which database to use   */
+/* return -1 if a bad error occurred, 0 if everything was OK or 1 if not      */
+int init_mysql() {
+  int i, error, file, match;
+  substdio ss;
+  stralloc filename = { 0 };
+  stralloc buf = { 0 };
+  
+  mysql = mysql_init(&dbh);
+  if (! mysql) return -1;
+
+  /* get the filename of the sqlserver control file */
+  i = str_len(SQLSERVER) + str_len(auto_qmail) + 1;
+  if (! stralloc_ready(&filename, i)) return -1;
+  if (! stralloc_cats(&filename, auto_qmail)) return -1;
+  if (! stralloc_cats(&filename, SQLSERVER)) return -1;
+  if (! stralloc_0(&filename)) return -1;
+  
+  /* try to open the file - much code robbed from control.c*/
+  file = open_read(filename.s);
+  if (file == -1) {
+    substdio_puts(subfderr, "warning: could not open sqlserver file ");
+    substdio_puts(subfderr, filename.s);
+    substdio_putsflush(subfderr, "\n");
+    return 0;
+  }
+  substdio_fdbuf(&ss, read, file, inbuf, sizeof(inbuf));
+  
+  i = 0;
+  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 s,l,p or d */
+    switch (buf.s[0]) {
+      case 's':
+        if (buf.s[1] == 'e') error = getconfig(&buf, "server", &DB_HOST);
+        else error = getconfig(&buf, "socket", &DB_SOCK);
+        break;
+      case 'l': error = getconfig(&buf, "login", &DB_USER); break;
+      case 'p':
+        if (buf.s[1] == 'a') error = getconfig(&buf, "password", &DB_PASS);
+        else error = getconfig(&buf, "port", &DB_PORT);
+        break;
+      case 'd': error = getconfig(&buf, "db", &DB_NAME); break;
+      default: continue;
+    }
+    if (error == -1) {
+      i = -1;
+      continue;
+    }
+    else i += error;
+    if (! match) break;
+  }
+  close(file);
+
+  if (i == -1)
+    substdio_putsflush(subfderr, "warning: there were errors reading sqlserver file\n");
+  else if (i > 0) {
+    substdio_putsflush(subfderr, "warning: bogus lines in sqlserver file\n");
+    i = 0;
+  }
+  return i;
+}
+
+/* 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 == '=') && str_len(value) > 0) {
+    if (! stralloc_ready(store, str_len(value))) 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 */
+  substdio_puts(subfderr, "error reading sqlserver file: expected '");
+  substdio_puts(subfderr, expected);
+  substdio_puts(subfderr, "' but got '");
+  substdio_puts(subfderr, got);
+  substdio_putsflush(subfderr, "'\n");
+  /* of course, we might have been expecting the wrong thing... */
+  return 1;
+}
--- ../qmail-1.03-orig/mysql_queries.h	Fri Feb 11 15:51:20 2000
+++ mysql_queries.h	Fri Feb 11 14:34:52 2000
@@ -0,0 +1,34 @@
+/******************************************************************************/
+/* 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 */
+#ifndef VIRTUAL
+#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"
+#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
+#define ALIAS1 "select alias_username, alias_host from alias where username='"
+#define ALIAS2 "' and alias='"
+#define ALIAS ALIAS1 ALIAS2
+#endif
+
+/* is this domain in rcpthosts */
+#ifndef RCPTHOSTS
+#define RCPTHOSTS "select count(*) from rcpthosts where host='"
+#endif
+
+/* and can we deliver to it */
+#ifndef LOCALS
+#define LOCALS "select count(*) from virtual where virtual_host='"
+#endif
--- ../qmail-1.03-orig/qmail-getpw.c	Fri Feb 11 15:51:20 2000
+++ qmail-getpw.c	Thu Feb 10 15:09:41 2000
@@ -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,8 +45,19 @@
 	username[extension - local] = 0;
 	case_lowers(username);
 	errno = 0;
-	pw = getpwnam(username);
 	if (errno == error_txtbsy) _exit(QLX_SYS);
+	num = 0;
+	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) {
@@ -48,7 +70,13 @@
 	    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,11 +90,15 @@
   local = argv[1];
   if (!local) _exit(100);
 
+  connection = connect_mysql();
+
   if (!userext()) {
     extension = local;
     dash = "-";
     pw = getpwnam(auto_usera);
   }
+
+  if (connection) disconnect_mysql();
 
   if (!pw) _exit(QLX_NOALIAS);
 
--- ../qmail-1.03-orig/qmail-local.c	Fri Feb 11 15:51:20 2000
+++ qmail-local.c	Thu Feb 10 14:46:42 2000
@@ -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-orig/qmail-send.c	Fri Feb 11 15:51:20 2000
+++ qmail-send.c	Thu Feb 10 15:46:17 2000
@@ -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,29 @@
   }
 
   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) 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 == 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 +182,7 @@
  
   if (!stralloc_cat(&rwline,&addr)) return 0;
   if (!stralloc_0(&rwline)) return 0;
-  return 2;
+  return ret;
 }
 
 void senderadd(sa,sender,recip)
--- ../qmail-1.03-orig/qmail-smtpd.c	Fri Feb 11 15:51:20 2000
+++ qmail-smtpd.c	Wed Feb  9 16:02:34 2000
@@ -24,6 +24,10 @@
 #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;
@@ -45,6 +49,7 @@
 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_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(); _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); }
@@ -73,6 +78,7 @@
 }
 void smtp_quit()
 {
+  disconnect_mysql();
   smtp_greet("221 "); out("\r\n"); flush(); _exit(0);
 }
 
@@ -213,6 +219,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 +419,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-orig/rcpthosts_mysql.c	Fri Feb 11 15:51:20 2000
+++ rcpthosts_mysql.c	Wed Feb  9 16:14:19 2000
@@ -0,0 +1,52 @@
+#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 num;
+  MYSQL_ROW row;
+  stralloc query = { 0 };
+
+  /* maybe the client's so slow we lost the MySQL connection... */
+  if (! mysql && ! 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++;
+
+  len = str_len(host) + str_len(RCPTHOSTS) + 2;
+  if (! stralloc_ready(&query, len)) return -1;
+  if (! stralloc_cats(&query, RCPTHOSTS)) return -1;
+  if (! stralloc_cats(&query, host)) return -1;
+  if (! stralloc_cats(&query, "'")) return -1;
+  if (! stralloc_0(&query)) return -1;
+
+  row = do_query(&query);
+
+  if ((int) row == 0) return -1;
+
+  num = atoi(row[0]);
+  mysql_free_result(result);
+
+  return num;
+}
--- ../qmail-1.03-orig/splog.c	Fri Feb 11 15:51:20 2000
+++ splog.c	Wed Feb  9 13:28:41 2000
@@ -0,0 +1,75 @@
+#ifndef O_NOT_LOG
+#include <sys/types.h>
+#include <sys/time.h>
+#include <syslog.h>
+#include "error.h"
+#include "substdio.h"
+#include "subfd.h"
+#include "exit.h"
+#include "str.h"
+#include "scan.h"
+#include "fmt.h"
+
+char buf[800]; /* syslog truncates long lines (or crashes); GPACIC */
+int bufpos = 0; /* 0 <= bufpos < sizeof(buf) */
+int flagcont = 0;
+int priority; /* defined if flagcont */
+char stamp[FMT_ULONG + FMT_ULONG + 3]; /* defined if flagcont */
+
+void stamp_make()
+{
+  struct timeval tv;
+  char *s;
+  gettimeofday(&tv,(struct timezone *) 0);
+  s = stamp;
+  s += fmt_ulong(s,(unsigned long) tv.tv_sec);
+  *s++ = '.';
+  s += fmt_uint0(s,(unsigned int) tv.tv_usec,6);
+  *s = 0;
+}
+
+void splogflush()
+{
+  if (bufpos) {
+    buf[bufpos] = 0;
+    if (flagcont)
+      syslog(priority,"%s+%s",stamp,buf); /* logger folds invisibly; GPACIC */
+    else {
+      stamp_make();
+      priority = LOG_INFO;
+      if (str_start(buf,"warning:")) priority = LOG_WARNING;
+      if (str_start(buf,"alert:")) priority = LOG_ALERT;
+      syslog(priority,"%s %s",stamp,buf);
+      flagcont = 1;
+    }
+  }
+  bufpos = 0;
+}
+
+void splog(char *splogger, char *message)
+{
+  int i;
+  int max = sizeof(buf) - 1;
+
+  openlog(splogger, 0, LOG_MAIL);
+  for (i = 0; *message != '\0' && i < max - 1; i++) {
+    if (*message == '\n') { splogflush(); flagcont = 0; continue; }
+    if (bufpos == max) splogflush();
+    if ((*message < 32) || (*message > 126))
+      *message = '?'; /* logger truncates at 0; GPACIC */
+    buf[bufpos++] = *message++;
+  }
+  if (bufpos < max) {
+    i++;
+    buf[bufpos++] = '\n';
+    splogflush();
+  }
+  closelog();
+  return;
+//if (i == max && *message != '\0') return 1;
+//return 0;
+}
+#else
+void splog(char *splogger, char *message) {
+}
+#endif
--- ../qmail-1.03-orig/vdoms_mysql.c	Fri Feb 11 15:51:20 2000
+++ vdoms_mysql.c	Wed Feb  9 18:28:24 2000
@@ -0,0 +1,66 @@
+#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 stralloc envnoathost;
+
+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 query = { 0 };
+
+  /* stralloc we're passed is unterminated */
+  addr[len] = '\0';
+  splog("vdoms_mysql", addr);
+  num = len;
+  host = addr + len;
+  while (*host != '@') {
+    host--;
+    num--;
+    if (host == addr) break;
+  }
+  at = num;
+
+//if (num) addr[num] = '\0';
+
+  /* let's lose the @ we found */
+  host++;
+
+  if (connect_mysql() < 1) return -1;
+
+  len += num + str_len(VIRTUAL) + 2;
+  if (! stralloc_ready(&query, len)) return -1;
+  if (! stralloc_cats(&query, VIRTUAL1)) return -1;
+  if (! stralloc_catb(&query, addr, num)) return -1;
+  if (! stralloc_cats(&query, VIRTUAL2)) return -1;
+  if (! stralloc_cats(&query, host)) return -1;
+  if (! stralloc_cats(&query, VIRTUAL3)) return -1;
+  if (! stralloc_0(&query)) return -1;
+
+  row = do_query(&query);
+
+  /* no row doesn't mean an error */
+  if ((int) row < 1) return (int) row;
+
+  num = str_len(row[0]) + str_len(row[1]) + 2;
+  if (! stralloc_cats(ret, row[0])) return -1;  /* username */
+  if (str_len(row[1])) {
+    if (! stralloc_append(ret, "-")) return -1;
+    if (*row[1] != '@') { if (! stralloc_cats(ret, row[1])) return -1; }
+    else if (! stralloc_catb(ret, addr, at)) return -1;  /* ext */
+  }
+  if (! stralloc_append(ret, "@")) return -1;
+  if (! stralloc_cat(ret, &envnoathost)) return -1;
+  if (! stralloc_0(ret)) return -1;
+
+  return 1;
+}
