Download | Plain Text | Line Numbers


http://code.dogmap.org./qmail/#realrcptto
 
Changes in version 2009.01.31 (manuel mausz):
- userlookup + set DTUSER environment variable for quotacheck plugin
 
Changes in version 2006.12.10:
- For QMAILRRTENYALL, use error code 554 after DATA, not 550.  Thanks to
  ... sorry, I lost track of who found this.
- Log stat() errors for .qmail files.  Thanks to Chris Bensend for suggesting
  this.
 
Changes in version 2006.10.26:
- Logging uses substdio_puts() and substdio_flush() instead of
  substdio_putsflush().  This makes log entries less likely to be
  interleaved.  Thanks to Matthew Dempsky for finding this.
 
Changes in version 2004.09.14:
- Strict syntax fix: a variable declaration was accidentally put among
  statements instead of at the beginning of the block.
 
Changes in version 2004.08.30:
- Added QMAILRRTDENYALL.
- Cleanup: missing "break" for some switch statements (no visible behavior
  changes AFAICT).  Thanks to Markus Stumpf for finding these.
 
Changes in version 2004.02.05:
- Short-circuit speedup when "dash" is empty.
- Move duplicated code into realrcptto.c.
- Add logging of rejected addresses.
- Verified inter-patchability with netqmail-1.05 and Russell Nelson's
  qmail-smtpd-viruscan patch.
 
diff -Naur Makefile.orig Makefile
--- Makefile.orig	1998-06-15 06:52:55.000000000 -0400
+++ Makefile	2006-12-08 03:21:25.000000000 -0500
@@ -1447,15 +1447,15 @@
 	./compile qmail-qmqpd.c
 
 qmail-qmtpd: \
-load qmail-qmtpd.o rcpthosts.o control.o constmap.o received.o \
+load qmail-qmtpd.o realrcptto.o slurpclose.o rcpthosts.o control.o constmap.o received.o \
 date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a open.a \
 getln.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a \
-str.a fs.a auto_qmail.o dns.o ip.o ipalloc.o ipme.o byte_diff.o
-	./load qmail-qmtpd rcpthosts.o control.o constmap.o \
+str.a fs.a auto_qmail.o dns.o ip.o ipalloc.o ipme.o byte_diff.o auto_break.o auto_usera.o
+	./load qmail-qmtpd realrcptto.o slurpclose.o rcpthosts.o control.o constmap.o \
 	received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \
 	datetime.a open.a getln.a sig.a case.a env.a stralloc.a \
-	alloc.a substdio.a error.a fs.a auto_qmail.o dns.o \
+	alloc.a substdio.a error.a fs.a auto_qmail.o dns.o auto_break.o auto_usera.o \
	`cat dns.lib` ip.o ipalloc.o ipme.o byte_diff.o str.a
 
 qmail-qmtpd.0: \
 qmail-qmtpd.8
@@ -1614,18 +1614,18 @@
 	./compile qmail-showctl.c
 
 qmail-smtpd: \
-load qmail-smtpd.o rcpthosts.o qregex.o commands.o timeoutread.o \
+load qmail-smtpd.o realrcptto.o slurpclose.o rcpthosts.o qregex.o commands.o timeoutread.o \
 timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \
 date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \
 open.a sig.a case.a env.a stralloc.a alloc.a strerr.a substdio.a error.a str.a \
-fs.a auto_qmail.o base64.o qmail-spp.o socket.lib dns.o ip.o ipalloc.o
-	./load qmail-smtpd qregex.o rcpthosts.o commands.o timeoutread.o \
+fs.a auto_qmail.o base64.o qmail-spp.o auto_break.o auto_usera.o socket.lib dns.o ip.o ipalloc.o
+	./load qmail-smtpd realrcptto.o slurpclose.o qregex.o rcpthosts.o commands.o timeoutread.o \
 	timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \
 	tls.o ssl_timeoutio.o ndelay.a -L/usr/local/ssl/lib -lssl -lcrypto \
 	received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \
 	datetime.a getln.a open.a sig.a case.a qmail-spp.o env.a stralloc.a \
-	alloc.a strerr.a substdio.a error.a fs.a auto_qmail.o base64.o  `cat \
-	socket.lib` dns.o str.a `cat dns.lib`
+	alloc.a strerr.a substdio.a error.a fs.a auto_qmail.o base64.o auto_break.o \
+	auto_usera.o `cat socket.lib` dns.o str.a `cat dns.lib`
 
 qmail-smtpd.0: \
 qmail-smtpd.8
@@ -1774,6 +1774,11 @@
 auto_split.h
 	./compile readsubdir.c
 
+realrcptto.o: \
+compile realrcptto.c auto_break.h auto_usera.h byte.h case.h cdb.h \
+constmap.h error.h fmt.h open.h str.h stralloc.h uint32.h
+	./compile realrcptto.c
+
 received.o: \
 compile received.c fmt.h qmail.h substdio.h now.h datetime.h \
 datetime.h date822fmt.h received.h
diff -Naur qmail-qmtpd.c.orig qmail-qmtpd.c
--- qmail-qmtpd.c.orig	1998-06-15 06:52:55.000000000 -0400
+++ qmail-qmtpd.c	2006-12-08 03:21:25.000000000 -0500
@@ -14,6 +14,15 @@
 
 void badproto() { _exit(100); }
 void resources() { _exit(111); }
+void die_nomem() { resources(); }
+void die_control() { resources(); }
+void die_cdb() { resources(); }
+void die_sys() { resources(); }
+
+extern void realrcptto_init();
+extern void realrcptto_start();
+extern int realrcptto();
+extern int realrcptto_deny();
 
 int safewrite(fd,buf,len) int fd; char *buf; int len;
 {
@@ -98,6 +107,8 @@
   if (rcpthosts_init() == -1) resources();
   relayclient = env_get("RELAYCLIENT");
   relayclientlen = relayclient ? str_len(relayclient) : 0;
+
+  realrcptto_init();
 
   if (control_readint(&databytes,"control/databytes") == -1) resources();
   x = env_get("DATABYTES");
@@ -114,6 +125,7 @@
   if (!local) local = "unknown";
 
   for (;;) {
+    realrcptto_start();
     if (!stralloc_copys(&failure,"")) resources();
     flagsenderok = 1;
 
@@ -216,6 +228,10 @@
             case -1: resources();
             case 0: failure.s[failure.len - 1] = 'D';
           }
+
+        if (!failure.s[failure.len - 1])
+          if (!realrcptto(buf,1))
+            failure.s[failure.len - 1] = 'D';
 
         if (!failure.s[failure.len - 1]) {
           qmail_to(&qq,buf);
@@ -231,6 +247,7 @@
     result = qmail_close(&qq);
     if (!flagsenderok) result = "Dunacceptable sender (#5.1.7)";
     if (databytes) if (!bytestooverflow) result = "Dsorry, that message size exceeds my databytes limit (#5.3.4)";
+    if (!relayclient && realrcptto_deny()) result = "Dsorry, no mailbox here by that name. (#5.1.1)\r\n";
 
     if (*result)
       len = str_len(result);
diff -Naur qmail-smtpd.c.orig qmail-smtpd.c
--- qmail-smtpd.c.orig	1998-06-15 06:52:55.000000000 -0400
+++ qmail-smtpd.c	2006-12-09 04:02:00.000000000 -0500
@@ -111,12 +111,24 @@
 void die_ipme()
 {
   enew(); eout("Unable to figure out my IP addresses!\n");
   out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush();
   eflush(); _exit(1);
 }
+void die_cdb()
+{
+  enew(); eout("Unable to read cdb user database!\n");
+  out("421 unable to read cdb user database (#4.3.0)\r\n"); flush();
+  eflush(); _exit(1);
+}
+void die_sys()
+{
+  enew(); eout("Unable to read system user database!\n");
+  out("421 unable to read system user database (#4.3.0)\r\n"); flush();
+  eflush(); _exit(1);
+}
 void straynewline()
 {
   enew(); eout("Stray newline from "); eout(remoteip); eout(".\n");
   out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush();
   eflush(); _exit(1);
 }
@@ -161,6 +173,13 @@
 int err_wantstarttls() { out("530 Must issue a STARTTLS command first (#5.7.0)\r\n"); return -1; };
 void err_authfail() { out("535 authentication failed (#5.7.1)\r\n"); }
 
+extern void realrcptto_init();
+extern void realrcptto_start();
+extern int realrcptto();
+extern int realrcptto_deny();
+
+int flagauth = 0;
+
 stralloc greeting = {0};
 
 void smtp_greet(code) char *code;
@@ -259,7 +259,8 @@
  fdmbrt = open_read("control/morebadrcptto.cdb");
  if (fdmbrt == -1) if (errno != error_noent) die_control();
 
 
+  realrcptto_init();
 
   if (control_readint(&databytes,"control/databytes") == -1) die_control();
   x = env_get("DATABYTES");
@@ -635,7 +635,8 @@
   if (!stralloc_copys(&rcptto,"")) die_nomem();
   if (!stralloc_copys(&mailfrom,addr.s)) die_nomem();
   if (!stralloc_0(&mailfrom)) die_nomem();
+  realrcptto_start();
   recipcount = 0;
   out("250 ok\r\n");
 }
 void smtp_rcpt(arg) char *arg; {
@@ -680,5 +701,9 @@
     flagbrt = 1;
     log_deny("BAD RCPT TO", mailfrom.s,addr.s);
   }
+  if (!flagauth && !relayclient && !realrcptto(addr.s,1)) {
+    out("554 sorry, no mailbox here by that name. (#5.1.1)\r\n");
+    return;
+  }
   if (!(spp_val = spp_rcpt(allowed))) return;
   if (!relayclient && spp_val == 1) {
@@ -899,6 +899,7 @@
   if (mailfrom.len == 1 && recipcount > 1) { err_badbounce(); return; }
   if (flagbrt) { err_brt(); return; }
   if (!spp_data()) return;
+  if (!relayclient && realrcptto_deny()) { out("550 sorry, no mailbox here by that name. (#5.1.1)\r\n"); return; }
   seenmail = 0;
   if (databytes) bytestooverflow = databytes + 1;
   if (qmail_open(&qqt) == -1) { err_qqt(); return; }
@@ -961,6 +961,5 @@
 static stralloc slop = {0};     /* b64 challenge */
 #endif
 
-int flagauth = 0;
 char **childargs;
 char ssauthbuf[512];
diff -Naur /dev/null realrcptto.c
--- /dev/null	2006-09-14 17:39:14.000000000 +0200
+++ realrcptto.c	2009-01-31 01:01:21.000000000 +0100
@@ -0,0 +1,421 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <pwd.h>
+#include "auto_break.h"
+#include "auto_usera.h"
+#include "byte.h"
+#include "case.h"
+#include "cdb.h"
+#include "constmap.h"
+#include "error.h"
+#include "fmt.h"
+#include "open.h"
+#include "str.h"
+#include "stralloc.h"
+#include "uint32.h"
+#include "substdio.h"
+#include "env.h"
+#include "slurpclose.h"
+
+extern void die_nomem();
+extern void die_control();
+extern void die_cdb();
+extern void die_sys();
+
+static stralloc envnoathost = {0};
+static stralloc percenthack = {0};
+static stralloc locals = {0};
+static stralloc vdoms = {0};
+static struct constmap mappercenthack;
+static struct constmap maplocals;
+static struct constmap mapvdoms;
+
+static char *dash;
+static char *extension;
+static char *local;
+static struct passwd *pw;
+
+static char errbuf[128];
+static struct substdio sserr = SUBSTDIO_FDBUF(write,2,errbuf,sizeof errbuf);
+
+static char pidbuf[64];
+static char remoteipbuf[64]=" ";
+
+static int flagdenyall;
+static int flagdenyany;
+
+void realrcptto_init()
+{
+  char *x;
+
+  if (control_rldef(&envnoathost,"control/envnoathost",1,"envnoathost") != 1)
+    die_control();
+
+  if (control_readfile(&locals,"control/locals",1) != 1) die_control();
+  if (!constmap_init(&maplocals,locals.s,locals.len,0)) die_nomem();
+  switch(control_readfile(&percenthack,"control/percenthack",0)) {
+    case -1: die_control();
+    case 0: if (!constmap_init(&mappercenthack,"",0,0)) die_nomem();
+    case 1:
+      if (!constmap_init(&mappercenthack,percenthack.s,percenthack.len,0))
+        die_nomem();
+  }
+  switch(control_readfile(&vdoms,"control/virtualdomains",0)) {
+    case -1: die_control();
+    case 0: if (!constmap_init(&mapvdoms,"",0,1)) die_nomem();
+    case 1: if (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) die_nomem();
+  }
+
+  str_copy(pidbuf + fmt_ulong(pidbuf,getpid())," ");
+  x=env_get("PROTO");
+  if (x) {
+    static char const remoteip[]="REMOTEIP";
+    unsigned int len = str_len(x);
+    if (len <= sizeof remoteipbuf - sizeof remoteip) {
+      byte_copy(remoteipbuf,len,x);
+      byte_copy(remoteipbuf + len,sizeof remoteip,remoteip);
+      x = env_get(remoteipbuf);
+      len = str_len(x);
+      if (len + 1 < sizeof remoteipbuf) {
+        byte_copy(remoteipbuf,len,x);
+        remoteipbuf[len]=' ';
+        remoteipbuf[len + 1]='\0';
+      }
+    }
+  }
+
+  x = env_get("QMAILRRTDENYALL");
+  flagdenyall = (x && x[0]=='1' && x[1]=='\0');
+}
+
+void realrcptto_start()
+{
+  flagdenyany = 0;
+}
+
+static int denyaddr(addr, depth)
+char *addr;
+int depth;
+{
+  if (depth == 1)
+  {
+    substdio_puts(&sserr,"realrcptto ");
+    substdio_puts(&sserr,pidbuf);
+    substdio_puts(&sserr,remoteipbuf);
+    substdio_puts(&sserr,addr);
+    substdio_puts(&sserr,"\n");
+    substdio_flush(&sserr);
+    flagdenyany = 1;
+  }
+  return flagdenyall;
+}
+
+static void stat_error(path,error)
+char* path;
+int error;
+{
+  substdio_puts(&sserr,"unable to stat ");
+  substdio_puts(&sserr,path);
+  substdio_puts(&sserr,": ");
+  substdio_puts(&sserr,error_str(error));
+  substdio_puts(&sserr,"\n");
+  substdio_flush(&sserr);
+}
+
+#define GETPW_USERLEN 32
+
+static int userext()
+{
+  char username[GETPW_USERLEN];
+  struct stat st;
+
+  extension = local + str_len(local);
+  for (;;) {
+    if (extension - local < sizeof(username))
+      if (!*extension || (*extension == *auto_break)) {
+	byte_copy(username,extension - local,local);
+	username[extension - local] = 0;
+	case_lowers(username);
+	errno = 0;
+	pw = getpwnam(username);
+	if (errno == error_txtbsy) die_sys();
+	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 = "-"; }
+		return 1;
+	      }
+	    }
+	    else
+	      if (error_temp(errno)) die_sys();
+      }
+    if (extension == local) return 0;
+    --extension;
+  }
+}
+
+// max lookups
+#define MAXRECURSION 5
+
+static int handleqme(qme, depth)
+stralloc *qme;
+int depth;
+{
+  stralloc cmds = {0};
+  char *username = NULL;
+  int fd, i, j, k, count;
+
+  if (depth >= MAXRECURSION) return 0;
+  if (!stralloc_ready(&cmds,0)) die_nomem();
+  cmds.len = 0;
+
+  fd = open_read(qme->s);
+  if (fd == -1) return 0;
+  if (slurpclose(fd,&cmds,256) == -1) die_nomem();
+  if (!cmds.len || (cmds.s[cmds.len - 1] != '\n'))
+    if (!stralloc_cats(&cmds,"\n")) die_nomem();
+
+  i = count = 0;
+  for (j = 0;j < cmds.len;++j)
+  {
+    if (cmds.s[j] == '\n')
+    {
+      cmds.s[j] = 0;
+      k = j;
+      while ((k > i) && ((cmds.s[k - 1] == ' ') || (cmds.s[k - 1] == '\t')))
+        cmds.s[--k] = 0;
+      switch(cmds.s[i])
+      {
+        case 0:
+        case '#':
+        case '.':
+        case '/':
+        case '|':
+        case '+':
+          break;
+        case '&':
+          ++i;
+        default:
+          count++;
+          username = cmds.s + i;
+          break;
+      }
+      i = j + 1;
+    }
+  }
+
+  if (count == 1 && username)
+    realrcptto(username, ++depth);
+
+  return 1;
+}
+
+int realrcptto(addr, depth)
+char *addr;
+int depth;
+{
+  env_unset("DTUSER");
+  return realrcptto_ex(addr, depth);
+}
+
+int realrcptto_ex(addr, depth)
+char *addr;
+int depth;
+{
+  char *homedir, *username;
+  static stralloc localpart = {0};
+  static stralloc lower = {0};
+  static stralloc nughde = {0};
+  static stralloc wildchars = {0};
+  static stralloc safeext = {0};
+  static stralloc qme = {0};
+  unsigned int i,at;
+
+  /* Short circuit, or full logging?  Short circuit. */
+  if (flagdenyall && flagdenyany) return 1;
+
+  /* qmail-send:rewrite */
+  if (!stralloc_copys(&localpart,addr)) die_nomem();
+  i = byte_rchr(localpart.s,localpart.len,'@');
+  if (i == localpart.len) {
+    if (!stralloc_cats(&localpart,"@")) die_nomem();
+    if (!stralloc_cat(&localpart,&envnoathost)) die_nomem();
+  }
+  while (constmap(&mappercenthack,localpart.s + i + 1,localpart.len - i - 1)) {
+    unsigned int j = byte_rchr(localpart.s,i,'%');
+    if (j == i) break;
+    localpart.len = i;
+    i = j;
+    localpart.s[i] = '@';
+  }
+  at = byte_rchr(localpart.s,localpart.len,'@');
+  if (constmap(&maplocals,localpart.s + at + 1,localpart.len - at - 1)) {
+    localpart.len = at;
+    localpart.s[at] = '\0';
+  } else {
+    unsigned int xlen,newlen;
+    char *x;
+    for (i = 0;;++i) {
+      if (i > localpart.len) return denyaddr(addr, depth);
+      if (!i || (i == at + 1) || (i == localpart.len) ||
+          ((i > at) && (localpart.s[i] == '.'))) {
+        x = constmap(&mapvdoms,localpart.s + i,localpart.len - i);
+        if (x && i == at + 1) {
+          // set QMAILQUEUE if catch-all
+          char *qmailqueue;
+          qmailqueue = env_get("QMAILQUEUE");
+          if (qmailqueue) {
+            if (!env_put("QMAILQUEUE=bin/qmail-queue")) die_nomem();
+          }
+        }
+        if (x) break;
+      }
+    }
+    if (!*x) return 1;
+    xlen = str_len(x) + 1;  /* +1 for '-' */
+    newlen = xlen + at + 1; /* +1 for \0 */
+    if (xlen < 1 || newlen - 1 < xlen || newlen < 1 ||
+        !stralloc_ready(&localpart,newlen))
+      die_nomem();
+    localpart.s[newlen - 1] = '\0';
+    byte_copyr(localpart.s + xlen,at,localpart.s);
+    localpart.s[xlen - 1] = '-';
+    byte_copy(localpart.s,xlen - 1,x);
+    localpart.len = newlen;
+  }
+
+  /* qmail-lspawn:nughde_get */
+  {
+    int r,fd,flagwild;
+    if (!stralloc_copys(&lower,"!")) die_nomem();
+    if (!stralloc_cats(&lower,localpart.s)) die_nomem();
+    if (!stralloc_0(&lower)) die_nomem();
+    case_lowerb(lower.s,lower.len);
+    if (!stralloc_copys(&nughde,"")) die_nomem();
+    fd = open_read("users/cdb");
+    if (fd == -1) {
+      if (errno != error_noent) die_cdb();
+    } else {
+      uint32 dlen;
+      r = cdb_seek(fd,"",0,&dlen);
+      if (r != 1) die_cdb();
+      if (!stralloc_ready(&wildchars,(unsigned int) dlen)) die_nomem();
+      wildchars.len = dlen;
+      if (cdb_bread(fd,wildchars.s,wildchars.len) == -1) die_cdb();
+      i = lower.len;
+      flagwild = 0;
+      do { /* i > 0 */
+        if (!flagwild || (i == 1) ||
+            (byte_chr(wildchars.s,wildchars.len,lower.s[i - 1])
+             < wildchars.len)) {
+          r = cdb_seek(fd,lower.s,i,&dlen);
+          if (r == -1) die_cdb();
+          if (r == 1) {
+            char *x;
+            if (!stralloc_ready(&nughde,(unsigned int) dlen)) die_nomem();
+            nughde.len = dlen;
+            if (cdb_bread(fd,nughde.s,nughde.len) == -1) die_cdb();
+            if (flagwild)
+              if (!stralloc_cats(&nughde,localpart.s + i - 1)) die_nomem();
+            if (!stralloc_0(&nughde)) die_nomem();
+            close(fd);
+            x=nughde.s;
+            /* skip username */
+            username=x;
+            x += byte_chr(x,nughde.s + nughde.len - x,'\0');
+            if (x == nughde.s + nughde.len) return 1;
+            ++x;
+            /* skip uid */
+            x += byte_chr(x,nughde.s + nughde.len - x,'\0');
+            if (x == nughde.s + nughde.len) return 1;
+            ++x;
+            /* skip gid */
+            x += byte_chr(x,nughde.s + nughde.len - x,'\0');
+            if (x == nughde.s + nughde.len) return 1;
+            ++x;
+            /* skip homedir */
+            homedir=x;
+            x += byte_chr(x,nughde.s + nughde.len - x,'\0');
+            if (x == nughde.s + nughde.len) return 1;
+            ++x;
+            /* skip dash */
+            dash=x;
+            x += byte_chr(x,nughde.s + nughde.len - x,'\0');
+            if (x == nughde.s + nughde.len) return 1;
+            ++x;
+            extension=x;
+            goto got_nughde;
+          }
+        }
+        --i;
+        flagwild = 1;
+      } while (i);
+      close(fd);
+    }
+  }
+
+  /* qmail-getpw */
+  local = localpart.s;
+  if (!userext()) {
+    extension = local;
+    dash = "-";
+    pw = getpwnam(auto_usera);
+  }
+  if (!pw) return denyaddr(addr, depth);
+  if (!stralloc_copys(&nughde,pw->pw_dir)) die_nomem();
+  if (!stralloc_0(&nughde)) die_nomem();
+  homedir=nughde.s;
+  username=pw->pw_name;
+
+  got_nughde:
+
+  /* qmail-local:qmesearch */
+  //if (!*dash) return 1;
+  if (!*dash) { env_put2("DTUSER", username); return 1; }
+  if (!stralloc_copys(&safeext,extension)) die_nomem();
+  case_lowerb(safeext.s,safeext.len);
+  for (i = 0;i < safeext.len;++i)
+  {
+    if (safeext.s[i] == '.')
+      safeext.s[i] = ':';
+  }
+  {
+    struct stat st;
+    int i;
+    if (!stralloc_copys(&qme,homedir)) die_nomem();
+    if (!stralloc_cats(&qme,"/.qmail")) die_nomem();
+    if (!stralloc_cats(&qme,dash)) die_nomem();
+    if (!stralloc_cat(&qme,&safeext)) die_nomem();
+    if (!stralloc_0(&qme)) die_nomem();
+    //if (stat(qme.s,&st) == 0) return 1;
+    if (stat(qme.s,&st) == 0) { handleqme(&qme, depth); return 1; }
+    if (errno != error_noent) {
+      stat_error(qme.s,errno);
+      return 1;
+    }
+    for (i = safeext.len;i >= 0;--i)
+      if (!i || (safeext.s[i - 1] == '-')) {
+        if (!stralloc_copys(&qme,homedir)) die_nomem();
+        if (!stralloc_cats(&qme,"/.qmail")) die_nomem();
+        if (!stralloc_cats(&qme,dash)) die_nomem();
+        if (!stralloc_catb(&qme,safeext.s,i)) die_nomem();
+        if (!stralloc_cats(&qme,"default")) die_nomem();
+        if (!stralloc_0(&qme)) die_nomem();
+        //if (stat(qme.s,&st) == 0) return 1;
+        if (stat(qme.s,&st) == 0) { handleqme(&qme, depth); return 1; }
+        if (errno != error_noent) {
+          stat_error(qme.s,errno);
+          return 1;
+        }
+      }
+    return denyaddr(addr, depth);
+  }
+}
+
+int realrcptto_deny()
+{
+  return flagdenyall && flagdenyany;
+}