--- qmail-smtpd.c.orig  1998-06-15
+++ qmail-smtpd.c       2007-03-21
--- .././qmail-1.03/qmail-smtpd.c	Mon Jun 15 12:53:16 1998
+++ ../qmail-1.03.2418/qmail-smtpd.c	Sat Jan  6 21:33:00 2007
@@ -1,4 +1,3 @@
-#include "sig.h"
 #include "readwrite.h"
 #include "stralloc.h"
 #include "substdio.h"
@@ -20,14 +19,52 @@
 #include "now.h"
 #include "exit.h"
 #include "rcpthosts.h"
+#include "recipients.h"
+#include "mfrules.h"
+#include "ucspitls.h"
 #include "timeoutread.h"
 #include "timeoutwrite.h"
 #include "commands.h"
+#include "cdb.h"
+#include "dns.h"
+#include "wait.h"
+
+#define RELAYMAILFROM
+#define REQBRACKETS
+#define AUTHSLEEP 5
+
+#define MIMETYPE_LEN 9
+#define LOADER_LEN 5
+#define FDLOG 2
 
 #define MAXHOPS 100
-unsigned int databytes = 0;
+unsigned long databytes = 0;
 int timeout = 1200;
 
+char *reply554;
+char *replymav;
+
+/* this file is too long -------------------------------------- logging */
+
+char sslogbuf[512];
+substdio sslog = SUBSTDIO_FDBUF(write,FDLOG,sslogbuf,sizeof(sslogbuf));
+void logc(s) char *s; { if(substdio_puts(&sslog,s) == -1) _exit(1); }  	/* single string */
+void logp(s1) char *s1; { logc(" P:"); logc(s1); }			/* protocol */
+void logh(s1,s2,s3) char *s1, *s2, *s3; { logc(" S:"); logc(s1); logc(":"); logc(s2); logc(" H:"); logc(s3); }	/* host */
+void logf(s) char *s; { logc(" F:"); logc(s); }				/* mailfrom */
+void logt(s) char *s; { logc(" T:"); logc(s); }				/* rcptto */
+void logi(s) char *s; { logc(" '"); logc(s); logc("'"); }		/* information */
+void logn(s) char *s; { if(substdio_puts(&sslog,s) == -1) _exit(1); if(substdio_flush(&sslog) == -1) _exit(1); } /* end */
+
+void loga(s1,s2,s3,s4,s5,s6,s7) char *s1, *s2, *s3, *s4, *s5, *s6, *s7;
+  { logc(s1); logc(s2), logh(s3,s4,s5); logi(s7); logc(" ?="); logi(s6); logn("\n"); }
+void logb(s1,s2,s3,s4,s5,s6,s7) char *s1, *s2, *s3, *s4, *s5, *s6, *s7;
+  { logc(s1); logc(s2), logh(s3,s4,s5); logi(s7); logc(" !="); logi(s6); logn("\n"); }
+void logs(s1,s2,s3,s4,s5,s6,s7) char *s1, *s2, *s3, *s4, *s5, *s6, *s7;
+  { logc(s1); logp(s2); logh(s3,s4,s5); logf(s6); logt(s7); logn("\n"); }
+void logd(s1,s2,s3,s4,s5,s6,s7,s8) char *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8;
+  { logc(s1); logp(s2); logh(s3,s4,s5); logf(s6); logt(s7); logi(s8); logn("\n"); }
+
 int safewrite(fd,buf,len) int fd; char *buf; int len;
 {
   int r;
@@ -42,15 +79,15 @@
 void flush() { substdio_flush(&ssout); }
 void out(s) char *s; { substdio_puts(&ssout,s); }
 
+/* this file is too long -------------------------------------- Exit codes     */
+
 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 die_starttls() { out("454 TLS not available due to temporary reason (#5.7.3)\r\n"); flush(); _exit(1); }
 void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); }
-
-void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); }
-void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); }
 void err_unimpl() { out("502 unimplemented (#5.5.1)\r\n"); }
 void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); }
 void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); }
@@ -58,7 +95,79 @@
 void err_noop() { out("250 ok\r\n"); }
 void err_vrfy() { out("252 send some mail, i'll try my best\r\n"); }
 void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); }
+void err_size() { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); }
 
+void err_helo(s1,s2,s3,s4,s5,s6,s7,s8) char *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8; { 
+  out("550 sorry, invalid HELO/EHLO greeting (#5.7.1)\r\n");
+  logd(s1,s2,s3,s4,s5,s6,s7,s8);
+  return;
+  }
+void err_bmf(s1,s2,s3,s4,s5,s6,s7,s8) char *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8; {
+  out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n");
+  logd(s1,s2,s3,s4,s5,s6,s7,s8);
+  return;
+  }
+void err_mav(s1,s2,s3,s4,s5,s6,s7) char *s1, *s2, *s3, *s4, *s5, *s6, *s7; {
+  out("553 sorry, invalid sender address specified "); 
+  if (replymav) out(replymav);
+  out(" (#5.7.1)\r\n");
+  logs(s1,s2,s3,s4,s5,s6,s7);
+  return;
+  }
+void err_nogateway(s1,s2,s3,s4,s5,s6,s7) char *s1, *s2, *s3, *s4, *s5, *s6, *s7; { 
+  out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); 
+  logs(s1,s2,s3,s4,s5,s6,s7);
+  return;
+  }
+void err_mfdns(s1,s2,s3,s4,s5,s6,s7) char *s1, *s2, *s3, *s4, *s5, *s6, *s7; {
+  out("553 sorry, your envelope sender must exist (#5.7.1)\r\n");
+  logs(s1,s2,s3,s4,s5,s6,s7);
+  return;
+  }
+void err_rcpts(s1,s2,s3,s4,s5,s6,s7) char *s1, *s2, *s3, *s4, *s5, *s6, *s7; {
+  out("550 sorry, too many recipients (#5.7.1)\r\n"); 
+  logs(s1,s2,s3,s4,s5,s6,s7);
+  return;
+  }
+void err_brcptto(s1,s2,s3,s4,s5,s6,s7) char *s1, *s2, *s3, *s4, *s5, *s6, *s7; {
+  out("550 sorry, your envelope recipient is in my badrcptto list (#5.7.1)\r\n");
+  logs(s1,s2,s3,s4,s5,s6,s7);
+  return;
+  }
+void err_recipient(s1,s2,s3,s4,s5,s6,s7) char *s1, *s2, *s3, *s4, *s5, *s6, *s7; {
+  if (env_get("RECIPIENTS450"))
+    out("450 sorry, mailbox currently unavailable (#4.2.1)\r\n"); 
+  else
+    out("550 sorry, no mailbox by that name (#5.7.1)\r\n");
+  logs(s1,s2,s3,s4,s5,s6,s7);
+  return;
+  }
+void err_data(s1,s2,s3,s4,s5,s6,s7,s8) char *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8; {
+  out("554 sorry, invalid message content "); 
+  if (reply554) out(reply554); 
+  out(" (#5.3.2)\r\n"); 
+  logd(s1,s2,s3,s4,s5,s6,s7,s8);
+  return;
+  }
+
+int err_child() { out("454 oops, problem with child and I can't auth (#4.3.0)\r\n"); return -1; }
+int err_fork() { out("454 oops, child won't start and I can't auth (#4.3.0)\r\n"); return -1; }
+int err_pipe() { out("454 oops, unable to open pipe and I can't auth (#4.3.0)\r\n"); return -1; }
+int err_write() { out("454 oops, unable to write pipe and I can't auth (#4.3.0)\r\n"); return -1; }
+void err_authd() { out("503 you're already authenticated (#5.5.0)\r\n"); }
+void err_authmail() { out("503 no auth during mail transaction (#5.5.0)\r\n"); }
+int err_noauth() { out("504 auth type unimplemented (#5.5.1)\r\n"); return -1; }
+int err_authabrt() { out("501 auth exchange canceled (#5.0.0)\r\n"); return -1; }
+int err_input() { out("501 malformed auth input (#5.5.4)\r\n"); return -1; }
+void err_authfail(s1,s2,s3,s4,s5,s6,s7) char *s1, *s2, *s3, *s4, *s5, *s6, *s7; { 
+  out("535 authentication failed (#5.7.1)\r\n"); loga(s1,s2,s3,s4,s5,s6,s7); }
+void err_authreq(s1,s2,s3,s4,s5,s6,s7) char *s1, *s2, *s3, *s4, *s5, *s6, *s7; { 
+  out("535 authentication required (#5.7.1)\r\n"); logs(s1,s2,s3,s4,s5,s6,s7); }
+int err_starttls() { out("454 TLS not available due to temporary reason (#5.7.3)\r\n"); return -1; }
+void err_tlsreq(s1,s2,s3,s4,s5,s6,s7) char *s1, *s2, *s3, *s4, *s5, *s6, *s7; { 
+  out("535 STARTTLS required (#5.7.1)\r\n"); logs(s1,s2,s3,s4,s5,s6,s7); }
+
+/* this file is too long -------------------------------------- Greeting      */
 
 stralloc greeting = {0};
 
@@ -82,21 +191,99 @@
 char *local;
 char *relayclient;
 
+stralloc protocol = {0};
 stralloc helohost = {0};
 char *fakehelo; /* pointer into helohost, or 0 */
+stralloc tlsinfo = {0};
+
+char *helocheck;
+int flagbadhelo;
+int flagdnshelo;
+int seenhelo = 0;
+
+char *badmailcond;
+char *badhelocond;
 
-void dohelo(arg) char *arg; {
+void dohelo(arg) char *arg; 
+{
   if (!stralloc_copys(&helohost,arg)) die_nomem(); 
   if (!stralloc_0(&helohost)) die_nomem(); 
   fakehelo = case_diffs(remotehost,helohost.s) ? helohost.s : 0;
+  if (helocheck) {
+      if (str_len(helocheck) == 1) {
+        switch (*helocheck) {
+          case '=': flagbadhelo = bhelocheck(); if (fakehelo) { flagdnshelo = 1; badhelocond = "="; } break;
+          case 'A': flagdnshelo = dnsq(helohost.s,"A"); badhelocond = "A"; flagbadhelo = bhelocheck(); break;
+          case 'M': flagdnshelo = dnsq(helohost.s,"M"); badhelocond = "M"; flagbadhelo = bhelocheck(); break;
+          case '.': flagbadhelo = bhelocheck(); if (!str_len(arg)) flagbadhelo = -2; break;
+          case '!': if (!str_len(arg)) flagbadhelo = -2; break;
+        }
+      } 
+      else 
+        flagbadhelo = bhelocheck(); 
+  }
 }
 
 int liphostok = 0;
 stralloc liphost = {0};
+
 int bmfok = 0;
 stralloc bmf = {0};
 struct constmap mapbmf;
 
+int brtok= 0;
+stralloc brt = {0};
+struct constmap mapbrt;
+
+int badhelook = 0;
+stralloc badhelo = {0};
+struct constmap mapbadhelo;
+
+#ifdef RELAYMAILFROM
+int relaymailfromok = 0;
+stralloc relaymailfrom = {0};
+struct constmap maprmf;
+#endif
+
+static int fdbmt;
+int flagmimetype = 0;
+
+static int fdblt;
+int flagloadertype = 0;
+char *badloaderinit;
+
+static int fdmav;
+int flagmav = 0;
+int localmf = 0;
+int flaglocal = -1;
+char *localmfcheck;
+
+char *mfdnscheck;
+char *qhpsi;
+char *base64;
+
+int maxrcptcount = 0;
+int flagerrcpts = 0;
+
+int tarpitcount = 0;
+int tarpitdelay = 5;
+
+unsigned int greetdelay = 0;
+
+char *auth;
+char *reqauth;
+int smtpauth = 0;
+int seenauth = 0;
+
+int starttls = 0;
+int seenttls = 0;
+char *reqttls;
+char *tlsversion;
+char *cipher;
+char *cipherperm;
+char *cipherused;
+char *clientdn;
+
 void setup()
 {
   char *x;
@@ -111,17 +298,24 @@
   if (timeout <= 0) timeout = 1;
 
   if (rcpthosts_init() == -1) die_control();
+  if (recipients_init() == -1) die_control();
 
   bmfok = control_readfile(&bmf,"control/badmailfrom",0);
   if (bmfok == -1) die_control();
   if (bmfok)
     if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem();
  
+  brtok = control_readfile(&brt,"control/badrcptto",0);
+  if (brtok == -1) die_control();
+  if (brtok)
+    if (!constmap_init(&mapbrt,brt.s,brt.len,0)) die_nomem();
+ 
   if (control_readint(&databytes,"control/databytes") == -1) die_control();
   x = env_get("DATABYTES");
   if (x) { scan_ulong(x,&u); databytes = u; }
   if (!(databytes + 1)) --databytes;
- 
+
+  if (!stralloc_copys(&protocol,"ESMTP")) die_nomem();   /* RFC 3848 */ 
   remoteip = env_get("TCPREMOTEIP");
   if (!remoteip) remoteip = "unknown";
   local = env_get("TCPLOCALHOST");
@@ -131,11 +325,93 @@
   if (!remotehost) remotehost = "unknown";
   remoteinfo = env_get("TCPREMOTEINFO");
   relayclient = env_get("RELAYCLIENT");
-  dohelo(remotehost);
+
+  mfdnscheck = env_get("MFDNSCHECK");
+
+  x = env_get("MAXRECIPIENTS");
+  if (x) { scan_ulong(x,&u); maxrcptcount = u; };
+  if (!(maxrcptcount + 1)) --maxrcptcount;
+
+  helocheck = env_get("HELOCHECK");
+  if (helocheck) {
+    badhelook = control_readfile(&badhelo,"control/badhelo",0);
+    if (badhelook == -1) die_control();
+    if (badhelook)
+      if (!constmap_init(&mapbadhelo,badhelo.s,badhelo.len,0)) die_nomem();
+  }
+
+  if (control_readint(&tarpitcount,"control/tarpitcount") == -1) die_control();
+  if (tarpitcount < 0) tarpitcount = 0;
+  x = env_get("TARPITCOUNT");
+  if (x) { scan_ulong(x,&u); tarpitcount = u; };
+  if (control_readint(&tarpitdelay,"control/tarpitdelay") == -1) die_control();
+  if (tarpitdelay < 0) tarpitdelay = 0;
+  x = env_get("TARPITDELAY");
+  if (x) { scan_ulong(x,&u); tarpitdelay = u; };
+  x = env_get("GREETDELAY");
+  if (x) { scan_ulong(x,&u); greetdelay = u; };
+
+  localmfcheck = env_get("LOCALMFCHECK");
+  if (localmfcheck) {
+    localmf = 1;
+    replymav = env_get("REPLYMAV");
+    if (str_len(localmfcheck) == 1 && *localmfcheck == '!') {
+      localmf = 2;
+      fdmav = open_read("control/mailfromrules.cdb");
+      if (fdmav == -1 ) localmf = 1;
+    }
+  }
+
+  if (env_get("BADMIMETYPE")) {
+    flagmimetype = 1;
+    fdbmt = open_read("control/badmimetypes.cdb");
+    if (fdbmt == -1 ) flagmimetype = 0;
+  }
+
+  badloaderinit = env_get("BADLOADERTYPE");
+  if (badloaderinit) {
+    if (str_len(badloaderinit) == 1) {
+      flagloadertype = 1;
+      fdblt = open_read("control/badloadertypes.cdb");
+      if (fdblt == -1 ) flagloadertype = 0;
+    }
+  }
+
+  base64 = env_get("BASE64"); 
+  reply554 = env_get("REPLY554"); 
+  qhpsi = env_get("QHPSI");
+  if (!qhpsi) qhpsi = "unknown";
+
+  auth = env_get("SMTPAUTH");
+  if (auth) {
+    smtpauth = 2;
+    if (case_starts(auth,"cram") || case_starts(auth,"CRAM")) smtpauth = 3;
+    reqauth = env_get("REQUIREAUTH");
+  }
+
+  if(env_get("UCSPITLS")) starttls = 1;
+  if(starttls) reqttls = env_get("REQUIRETLS");
+
+#ifdef RELAYMAILFROM
+  if (!relayclient) {
+    relaymailfromok = control_readfile(&relaymailfrom,"control/relaymailfrom",0);
+    if (relaymailfromok == -1) die_control();
+    if (relaymailfromok)
+      if (!constmap_init(&maprmf,relaymailfrom.s,relaymailfrom.len,0)) die_nomem();
+  }
+#endif
+
+  if (!stralloc_copys(&helohost,"")) die_nomem();    /* helohost is empty */ 
+  if (!stralloc_0(&helohost)) die_nomem();
+  fakehelo = 0;
 }
 
+/* this file is too long -------------------------------------- SMTP ADDRESSES */
 
 stralloc addr = {0}; /* will be 0-terminated, if addrparse returns 1 */
+stralloc eddr = {0}; /* extended address; used for smart address recognition */
+stralloc sa = {0};
+ipalloc ia = {0};
 
 int addrparse(arg)
 char *arg;
@@ -151,12 +427,17 @@
   i = str_chr(arg,'<');
   if (arg[i])
     arg += i + 1;
+#ifdef REQBRACKETS
+  else
+    return 0;
+#else
   else { /* partner should go read rfc 821 */
     terminator = ' ';
     arg += str_chr(arg,':');
     if (*arg == ':') ++arg;
     while (*arg == ' ') ++arg;
   }
+#endif
 
   /* strip source route */
   if (*arg == '@') while (*arg) if (*arg++ == ':') break;
@@ -199,71 +480,489 @@
 
 int bmfcheck()
 {
+  int i;
   int j;
-  if (!bmfok) return 0;
-  if (constmap(&mapbmf,addr.s,addr.len - 1)) return 1;
-  j = byte_rchr(addr.s,addr.len,'@');
-  if (j < addr.len)
-    if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) return 1;
+  int k = 0;
+  char subvalue;
+
+  if (bmfok) {
+
+    if (!relayclient) {                          /* '+' address for none-RELAYCLIENTS */
+      eddr.len = 0;
+      if (!stralloc_copyb(&eddr,addr.s,addr.len - 1)) die_nomem();
+      if (!stralloc_append(&eddr,"+")) die_nomem();
+      if (!stralloc_0(&eddr)) die_nomem();
+
+      if (constmap(&mapbmf,eddr.s,eddr.len - 1)) return -6;
+      j = byte_rchr(eddr.s,eddr.len,'@');
+      if (j < eddr.len)
+        if (constmap(&mapbmf,eddr.s + j,eddr.len - j - 1)) return -5;
+    }
+
+    if (!case_diffs(remotehost,"unknown")) {     /* '-' address from UNKNOWN */
+      eddr.len = 0;
+      if (!stralloc_copyb(&eddr,addr.s,addr.len - 1)) die_nomem();
+      if (!stralloc_append(&eddr,"-")) die_nomem();
+      if (!stralloc_0(&eddr)) die_nomem();
+
+      if (constmap(&mapbmf,eddr.s,eddr.len - 1)) return -4;
+      j = byte_rchr(eddr.s,eddr.len,'@');
+      if (j < eddr.len)
+        if (constmap(&mapbmf,eddr.s + j,eddr.len - j - 1)) return -3;
+    }
+
+    if (constmap(&mapbmf,addr.s,addr.len - 1)) return -2;
+    j = byte_rchr(addr.s,addr.len,'@');
+    if (j < addr.len)
+      if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) return -1;
+
+    i = 0;
+    for (j = 0;j < bmf.len;++j)
+      if (!bmf.s[j]) {
+        subvalue = bmf.s[i] != '!';
+        if (!subvalue) i++;
+        if ((k != subvalue) && wildmat(addr.s,bmf.s + i)) k = subvalue;
+        i = j + 1;
+      }
+    return k;
+  }
   return 0;
 }
+int brtcheck()
+{
+  int i;
+  int j;
+  int k = 0;
+  char subvalue;
 
-int addrallowed()
+  if (brtok) {
+    if (constmap(&mapbrt,addr.s,addr.len - 1)) return -2;
+    j = byte_rchr(addr.s,addr.len,'@');
+    if (j < addr.len)
+      if (constmap(&mapbrt,addr.s + j,addr.len - j - 1)) return -1;
+
+    i = 0;
+    for (j = 0;j < brt.len;++j)
+      if (!brt.s[j]) {
+        subvalue = brt.s[i] != '!';
+        if (!subvalue) i++;
+        if ((k != subvalue) && wildmat(addr.s,brt.s + i)) k = subvalue;
+        i = j + 1;
+      }
+    return k;
+  }
+  return 0;
+}
+
+int bhelocheck()
+{
+  int i;
+  int j;
+  int k = 0;
+  char subvalue;
+
+  if (badhelook) {
+
+    eddr.len = 0;                                                       /* helohost! */
+    if (!stralloc_copyb(&eddr,helohost.s,helohost.len - 1)) die_nomem();
+    if (!stralloc_append(&eddr,"!")) die_nomem();
+    if (!stralloc_0(&eddr)) die_nomem();
+    if (constmap(&mapbadhelo,eddr.s,eddr.len - 1)) return -3;
+
+    if (constmap(&mapbadhelo,helohost.s,helohost.len - 1)) return -1;
+
+    i = 0;
+    for (j = 0;j < badhelo.len;++j)
+      if (!badhelo.s[j]) {
+        subvalue = badhelo.s[i] != '!';
+        if (!subvalue) i++;
+        if ((k != subvalue) && wildmat(helohost.s,badhelo.s + i)) k = subvalue;
+        i = j + 1;
+      }
+    return k;
+  }
+  return 0;
+}
+int dnsq(arg,type) char *arg, *type;
+{
+  unsigned int random;
+  int i;
+  int len;
+
+  len = str_len(arg);
+  if (len < 1) return -2;
+
+  sa.len = 0;
+  if (arg[len-1] == ' ') len--;	/* trailing blank */
+  i = byte_rchr(arg,len,'@');
+  if (i < len) {
+    if (!stralloc_copyb(&sa,arg+i+1,len-i-1)) die_nomem();
+  } else
+    if (!stralloc_copyb(&sa,arg,len)) die_nomem();
+
+  dns_init(0);
+  random = now() + (getpid() << 16);
+  switch(*type) {
+    case 'A':  i = dns_ip(&ia,&sa); break;
+    case 'M':  i = dns_mxip(&ia,&sa,random); break;
+  }
+  switch(i) {
+    case DNS_HARD: return 1;
+    case DNS_SOFT: out("451 DNS temporary failure (#4.3.0)\r\n"); return -1;
+    case DNS_MEM:  die_nomem();
+  }
+
+  return 0;
+}
+
+int addrallowed(char *add)
 {
   int r;
-  r = rcpthosts(addr.s,str_len(addr.s));
+  r = rcpthosts(add,str_len(add));
   if (r == -1) die_control();
   return r;
 }
 
+int rcptallowed()
+{
+  int r;
+  r = recipients(addr.s,str_len(addr.s));
+  if (r == -2) die_nomem();
+  if (r == -1) die_control();
+  return r;
+}
+
+int localaddr(char *mf)
+{
+  int j;
+  int mflen;
+
+  mflen = str_len(mf);
+  if (localmf == 2) return mfrules(fdmav,remoteip,remotehost,remoteinfo,mf);
+  else {
+    if (str_len(localmfcheck) > 1) {
+      case_lowerb(localmfcheck,str_len(localmfcheck));
+      j = byte_rchr(mf,mflen,'@');
+      if (j < mflen)
+        if (!str_diffn(localmfcheck,mf+j+1,mflen-j-1)) return 2;
+    }
+    if(addrallowed(mf)) return 3;
+    return -2;
+  }
+}
 
 int seenmail = 0;
 int flagbarf; /* defined if seenmail */
+int flagrcpt;
+int flagdnsmf;
+int flagsize;
+int rcptcount = 0;
+
 stralloc mailfrom = {0};
 stralloc rcptto = {0};
+stralloc user = {0};
+stralloc fuser = {0};
+stralloc mfparms = {0};
+
+int mailfrom_size(arg) char *arg;
+{
+  long r;
+  unsigned long sizebytes = 0;
+
+  scan_ulong(arg,&r);
+  sizebytes = r;
+  if (databytes) if (sizebytes > databytes) return 1;
+  return 0;
+}
+
+void mailfrom_auth(arg,len)
+char *arg;
+int len;
+{
+  if (!stralloc_copys(&fuser,"")) die_nomem();
+  if (case_starts(arg,"<>")) { if (!stralloc_cats(&fuser,"unknown")) die_nomem(); }
+  else
+    while (len) {
+      if (*arg == '+') {
+        if (case_starts(arg,"+3D")) { arg=arg+2; len=len-2; if (!stralloc_cats(&fuser,"=")) die_nomem(); }
+        if (case_starts(arg,"+2B")) { arg=arg+2; len=len-2; if (!stralloc_cats(&fuser,"+")) die_nomem(); }
+      }
+      else
+        if (!stralloc_catb(&fuser,arg,1)) die_nomem();
+      arg++; len--;
+    }
+  if (!stralloc_0(&fuser)) die_nomem();
+  if (!remoteinfo) {
+    remoteinfo = fuser.s;
+    if (!env_unset("TCPREMOTEINFO")) die_read();
+    if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem();
+  }
+}
+
+void mailfrom_parms(arg) char *arg;
+{
+  int i;
+  int len;
+
+    len = str_len(arg);
+    if (!stralloc_copys(&mfparms,"")) die_nomem;
+    i = byte_chr(arg,len,'>');
+    if (i > 4 && i < len) {
+      while (len) {
+        arg++; len--;
+        if (*arg == ' ' || *arg == '\0' ) {
+           if (case_starts(mfparms.s,"SIZE=")) if (mailfrom_size(mfparms.s+5)) { flagsize = 1; return; }
+           if (case_starts(mfparms.s,"AUTH=")) mailfrom_auth(mfparms.s+5,mfparms.len-5);
+           if (!stralloc_copys(&mfparms,"")) die_nomem;
+        }
+        else
+          if (!stralloc_catb(&mfparms,arg,1)) die_nomem;
+      }
+    }
+}
+
+#ifdef RELAYMAILFROM
+int rmfcheck()
+{
+  if (!relaymailfromok) return 0;
+  if (constmap(&maprmf,addr.s,addr.len - 1)) return 1;
+  return 0;
+}
+#endif
+
+/* this file is too long ----------------------------------------- SMTP DIALOG */
 
 void smtp_helo(arg) char *arg;
 {
   smtp_greet("250 "); out("\r\n");
-  seenmail = 0; dohelo(arg);
+  seenmail = 0; rcptcount = 0; seenhelo++; dohelo(arg); 
 }
 void smtp_ehlo(arg) char *arg;
 {
-  smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n");
-  seenmail = 0; dohelo(arg);
+  char size[FMT_ULONG];
+  size[fmt_ulong(size,(unsigned long) databytes)] = 0;  
+  smtp_greet("250-"); out("\r\n");
+  out("250-PIPELINING\r\n250-8BITMIME\r\n");
+  out("250");
+  if (smtpauth || (starttls && !seenttls)) out("-"); 
+  else out(" ");
+  out("SIZE "); out(size); out("\r\n");
+  if (smtpauth) {
+    if(starttls && !seenttls) out("250-");
+    else out("250 ");
+    if (smtpauth <= 2) out("AUTH LOGIN PLAIN\r\n");
+    if (smtpauth == 3) out("AUTH LOGIN PLAIN CRAM-MD5\r\n");
+  }
+  if (starttls && !seenttls) out("250 STARTTLS\r\n");
+  seenhelo++; seenmail = 0; rcptcount = 0; dohelo(arg); 
 }
 void smtp_rset()
 {
-  seenmail = 0;
+  seenmail = 0; rcptcount = 0; seenauth = 0; seenttls = 0;
+  mailfrom.len = 0; rcptto.len = 0; tlsinfo.len = 0;
   out("250 flushed\r\n");
 }
+void smtp_starttls()
+{
+  if (starttls != 1) err_unimpl;
+  out("220 Ready to start TLS (#5.7.0)\r\n");
+  flush();
+
+  if(!ucspitls()) die_starttls();
+  seenttls = 1;
+
+  if(!ucspitlsinfo()) die_starttls();
+
+  tlsversion = env_get("SSL_PROTOCOL");
+  if (!tlsversion) tlsversion = "unknown";
+  cipher = env_get("SSL_CIPHER");
+  if (!cipher) cipher = "unknown";
+  cipherperm = env_get("SSL_CIPHER_ALGKEYSIZE");
+  if (!cipherperm) cipherperm = "unknown";
+  cipherused = env_get("SSL_CIPHER_USEKEYSIZE");
+  if (!cipherused) cipherused = "unknown";
+  clientdn = env_get("SSL_CLIENT_S_DN");
+  if (!clientdn) clientdn = "unknown";
+
+  if (!stralloc_copys(&tlsinfo,tlsversion)) die_nomem();
+  if (!stralloc_cats(&tlsinfo,": ")) die_nomem(); 
+  if (!stralloc_cats(&tlsinfo,cipher)) die_nomem();
+  if (!stralloc_cats(&tlsinfo," [")) die_nomem(); 
+  if (!stralloc_cats(&tlsinfo,cipherused)) die_nomem();
+  if (!stralloc_cats(&tlsinfo,"/")) die_nomem(); 
+  if (!stralloc_cats(&tlsinfo,cipherperm)) die_nomem();
+  if (!stralloc_cats(&tlsinfo,"] ")) die_nomem();
+  if (!stralloc_cats(&tlsinfo,"DN=")) die_nomem();
+  if (!stralloc_cats(&tlsinfo,clientdn)) die_nomem();
+  if (!stralloc_0(&tlsinfo)) die_nomem(); 
+
+  if (!stralloc_cats(&protocol,"S")) die_nomem();
+  logb("Accept::SNDR::Start_TLS:"," P:ESMTPS",remoteip,remotehost,helohost.s,clientdn,cipher);
+
+  /* reset SMTP state */
+  seenhelo = 0; seenmail = 0; rcptcount = 0; seenauth = 0;
+  helohost.len = 0; mailfrom.len = 0; rcptto.len = 0;
+}
 void smtp_mail(arg) char *arg;
 {
   if (!addrparse(arg)) { err_syntax(); return; }
-  flagbarf = bmfcheck();
-  seenmail = 1;
+  flagsize = 0;
+  rcptcount = 0;
+  mailfrom_parms(arg);
+  seenmail++;
+  if (relayclient) { 
+    if(localmf) flagmav = localaddr(addr.s);
+    switch(flagmav) {
+      case -9: die_nomem(); break;
+      case  2: if (!stralloc_cats(&protocol,"M")) die_nomem(); break;
+      default: break;
+    }
+  }
+  else {
+#ifdef RELAYMAILFROM
+    if (rmfcheck()) { relayclient = ""; flaglocal = -2; } else relayclient = 0; 
+#endif
+  }
   if (!stralloc_copys(&rcptto,"")) die_nomem();
   if (!stralloc_copys(&mailfrom,addr.s)) die_nomem();
   if (!stralloc_0(&mailfrom)) die_nomem();
+  flagbarf = bmfcheck(); 
+  if (mfdnscheck) flagdnsmf = dnsq(mailfrom.s,"M");
+  if (!stralloc_0(&protocol)) die_nomem(); 
   out("250 ok\r\n");
 }
 void smtp_rcpt(arg) char *arg; {
   if (!seenmail) { err_wantmail(); return; }
   if (!addrparse(arg)) { err_syntax(); return; }
-  if (flagbarf) { err_bmf(); return; }
-  if (relayclient) {
+  rcptcount++;
+
+/* this file is too long --------------------------------- Sesssion checks */
+
+  if (reqttls) 					/* STTARTTLS rejects */
+    if (!seenttls) { 
+      err_tlsreq("Reject::SNDR::Missing_TLS:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s);
+      return;
+    }
+
+  if (reqauth) 					/* Auth rejects */
+    if (!seenauth) { 
+      err_authreq("Reject::ORIG::Missing_Auth:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s);
+      return; 
+    }
+
+/* this file is too long --------------------------------- Split Horizon envelope checks */
+
+  if (!relayclient) {
+    if (!seenhelo && helocheck) 			/* Helo rejects */
+      if (str_len(helocheck) == 1) { 
+        err_helo("Reject::SNDR::Bad_Helo:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); 
+        return;
+      }
+    if (flagbadhelo) { 
+      switch(flagbadhelo) {
+        case -2: badhelocond = "!"; break;
+        case -1: badhelocond = "."; break;
+        default: badhelocond = "*"; break;
+      }
+      err_helo("Reject::SNDR::Bad_Helo:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,badhelocond);
+      return;
+    }
+    if (flagdnshelo > 0) { 
+      err_helo("Reject::SNDR::DNS_Helo:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,badhelocond); 
+      return; 
+    }
+    if (flagdnsmf > 0) { 				/* Mail from rejects */
+      err_mfdns("Reject::ORIG::DNS_MF:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s);
+      return; 
+    }
+    if (!addrallowed(addr.s)) { 			/* Relaying rejects */
+      err_nogateway("Reject::SNDR::Invalid_Relay:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); 
+      return; 
+    }
+    flagrcpt = rcptallowed();				/* Rcpt to rejects */
+    if (!flagrcpt) {
+      err_recipient("Reject::RCPT::Failed_Rcptto:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); 
+      flagerrcpts++;
+      return; 
+    } 
+    if (tarpitcount && flagerrcpts >= tarpitcount) { 	/* Tarpitting et al. */
+      err_rcpts("Reject::RCPT::Toomany_Rcptto:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); 
+      return; 
+    }
+    if (maxrcptcount && rcptcount > maxrcptcount) {
+      err_rcpts("Reject::RCPT::Toomany_Rcptto:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); 
+      return; 
+    }
+    if (tarpitcount && rcptcount >= tarpitcount) { 
+      unsigned int rest = tarpitdelay; 
+      while (rest = sleep(rest)); 
+    }
+  }					 		
+
+/* this file is too long --------------------------------- Local checks */
+
+  else {
+    if (flagmav < 0) { 
+      err_mav("Reject::ORIG::Invalid_Mailfrom:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s);
+      return;
+    }
     --addr.len;
     if (!stralloc_cats(&addr,relayclient)) die_nomem();
     if (!stralloc_0(&addr)) die_nomem();
+  }						
+
+/* this file is too long --------------------------------- Common checks */
+
+  if (flagbarf) { 
+    switch(flagbarf) {
+      case -1: badmailcond = "@"; break;
+      case -2: badmailcond = "@"; break;
+      case -3: badmailcond = "-"; break;
+      case -4: badmailcond = "-"; break;
+      case -5: badmailcond = "+"; break;
+      case -6: badmailcond = "+"; break;
+      default: badmailcond = "*"; break;
+    }
+    err_bmf("Reject::ORIG::Bad_Mailfrom:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,badmailcond); 
+    return; 
   }
-  else
-    if (!addrallowed()) { err_nogateway(); return; }
+
+  flagrcpt = brtcheck();
+  if (flagrcpt) { 
+    err_brcptto("Reject::RCPT::Bad_Rcptto:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); 
+    return; 
+  }
+
+  if (flagsize) { 
+    err_size(); 
+    logs("Reject::DATA::Invalid_Size:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s);
+    return;
+  }
+
+/* this file is too long --------------------------------- Checks done; mailfrom/recipient accepted */
+
   if (!stralloc_cats(&rcptto,"T")) die_nomem();
   if (!stralloc_cats(&rcptto,addr.s)) die_nomem();
   if (!stralloc_0(&rcptto)) die_nomem();
   out("250 ok\r\n");
-}
 
+/* this file is too long --------------------------------- Additional logging */
+
+  if (flaglocal > 0) 
+    logs("Accept::ORIG::Local_Sender:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s);
+  else if (relayclient) 
+    if (flaglocal == -2) 
+      logs("Accept::ORIG::Relay_Mailfrom:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s);
+    else
+      logs("Accept::SNDR::Relay_Client:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s);
+  else
+    switch(flagrcpt) {
+      case 1:  logs("Accept::RCPT::Recipients_Rcptto:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); break;
+      case 2:  logs("Accept::RCPT::Recipients_Verp:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); break; 
+      case 3:  logs("Accept::RCPT::Recipients_Domain:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); break;
+      default: logs("Accept::RCPT::Rcpthosts_Rcptto:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); break;
+    }
+}
 
 int saferead(fd,buf,len) int fd; char *buf; int len;
 {
@@ -279,11 +978,69 @@
 substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf);
 
 struct qmail qqt;
-unsigned int bytestooverflow = 0;
+unsigned long bytestooverflow = 0;
+
+stralloc line = {0};
+stralloc base64types = {0};
+stralloc badmimetype = {0};
+stralloc badloadertype = {0};
+
+unsigned int nolines = 0;
+unsigned int flag64 = 0;
+unsigned int flagbase = 0;
+unsigned int flagblank = 0;
 
 void put(ch)
 char *ch;
 {
+  uint32 dlen;
+  int i;
+
+  if (line.len < 1025) 
+    if (!stralloc_catb(&line,ch,1)) die_nomem(); 	/* Reassamble chars to line; prepend with 'L' */
+
+  if (*ch == '\n') {
+    nolines++;
+
+    if (line.len == 2) { flagblank = nolines; flagbase = 0; }   /* trigger */
+    if (*(line.s+1) == 'C' || *(line.s+1) == 'c')
+      if (case_startb(line.s+1,line.len-2,"content-transfer-encoding: base64")) flag64 = nolines;       /* base64 attachments */
+    if (flag64 && nolines == flagblank+1 && line.len > MIMETYPE_LEN+2) {
+      flagbase = nolines;
+      if (!stralloc_catb(&base64types,line.s+1,MIMETYPE_LEN)) die_nomem();
+      if (!stralloc_0(&base64types)) die_nomem();
+      if (flagmimetype == 1) {
+        if (cdb_seek(fdbmt,line.s+1,MIMETYPE_LEN,&dlen)) {
+          if (!stralloc_copyb(&badmimetype,line.s+1,MIMETYPE_LEN)) die_nomem();
+          if (!stralloc_0(&badmimetype)) die_nomem();
+          if (!stralloc_cats(&rcptto,"M")) die_nomem();
+          if (!stralloc_0(&rcptto)) die_nomem(); 
+          qmail_fail(&qqt);
+          flagmimetype = -1;
+        }
+      }
+    }
+
+    if (flagloadertype == 1 && flagmimetype != -1) {
+      if (line.len > MIMETYPE_LEN + 2 && flagbase > flagblank) {
+        for ( i = 0; i < line.len - LOADER_LEN; ++i ) {
+          if (*(line.s+i) == *badloaderinit) {
+            if (cdb_seek(fdblt,line.s+i,LOADER_LEN,&dlen)) {
+              if (!stralloc_copyb(&badloadertype,line.s+i,LOADER_LEN)) die_nomem();
+              if (!stralloc_0(&badloadertype)) die_nomem();
+              if (!stralloc_cats(&rcptto,"L")) die_nomem();
+              if (!stralloc_0(&rcptto)) die_nomem(); 
+              qmail_fail(&qqt);
+              flagloadertype = -1;
+            }
+          }
+        }
+      }
+    }
+    line.len = 0;
+    if(!stralloc_copys(&line,"L")) die_nomem();
+  }
+
   if (bytestooverflow)
     if (!--bytestooverflow)
       qmail_fail(&qqt);
@@ -316,8 +1073,8 @@
         if (flagmaybex) if (pos == 7) ++*hops;
         if (pos < 2) if (ch != "\r\n"[pos]) flagmaybey = 0;
         if (flagmaybey) if (pos == 1) flaginheader = 0;
+        ++pos;
       }
-      ++pos;
       if (ch == '\n') { pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; }
     }
     switch(state) {
@@ -378,26 +1135,251 @@
   qp = qmail_qp(&qqt);
   out("354 go ahead\r\n");
  
-  received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo);
+  received(&qqt,protocol.s,local,remoteip,remotehost,remoteinfo,fakehelo,tlsinfo.s);
   blast(&hops);
   hops = (hops >= MAXHOPS);
   if (hops) qmail_fail(&qqt);
+  if (base64 && base64types.len == 0) {
+    if (!stralloc_cats(&rcptto,"Q")) die_nomem();
+    if (!stralloc_0(&rcptto)) die_nomem(); 
+  } 
   qmail_from(&qqt,mailfrom.s);
   qmail_put(&qqt,rcptto.s,rcptto.len);
  
   qqx = qmail_close(&qqt);
   if (!*qqx) { acceptmessage(qp); return; }
   if (hops) { out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; }
-  if (databytes) if (!bytestooverflow) { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); return; }
-  if (*qqx == 'D') out("554 "); else out("451 ");
-  out(qqx + 1);
+  if (databytes) 
+    if (!bytestooverflow) {
+       err_size();
+       logs("Reject::DATA::Invalid_Size:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s);
+       return;
+    }
+  if (flagmimetype == -1) { 
+    err_data("Reject::DATA::Bad_MIME:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,badmimetype.s);
+    return; 
+  }
+  if (flagloadertype == -1) { 
+    err_data("Reject::DATA::Bad_Loader:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,badloadertype.s);
+    return;
+  }
+  if (*qqx == 'D') { 
+    err_data("Reject::DATA::Virus_Infected:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,qhpsi);
+    return;
+  }
+  else { out("451 "); out(qqx + 1); out("\r\n"); }
+}
+
+
+/* this file is too long ----------------------------------------- SMTP AUTH */
+
+char unique[FMT_ULONG + FMT_ULONG + 3];
+static stralloc authin = {0};   /* input from SMTP client */
+static stralloc pass = {0};     /* plain passwd or digest */
+static stralloc resp = {0};     /* b64 response */
+static stralloc chal = {0};     /* CRAM-MD5 plain challenge */
+static stralloc slop = {0};     /* CRAM-MD5 b64 challenge */
+
+char **childargs;
+char ssauthbuf[512];
+substdio ssauth = SUBSTDIO_FDBUF(safewrite,3,ssauthbuf,sizeof(ssauthbuf));
+
+int authgetl(void) {
+  int i;
+
+  if (!stralloc_copys(&authin,"")) die_nomem();
+  for (;;) {
+    if (!stralloc_readyplus(&authin,1)) die_nomem(); /* XXX */
+    i = substdio_get(&ssin,authin.s + authin.len,1);
+    if (i != 1) die_read();
+    if (authin.s[authin.len] == '\n') break;
+    ++authin.len;
+  }
+
+  if (authin.len > 0) if (authin.s[authin.len - 1] == '\r') --authin.len;
+  authin.s[authin.len] = 0;
+  if (*authin.s == '*' && *(authin.s + 1) == 0) { return err_authabrt(); }
+  if (authin.len == 0) { return err_input(); }
+  return authin.len;
+}
+
+int authenticate(void)
+{
+  int child;
+  int wstat;
+  int pi[2];
+
+  if (!stralloc_0(&user)) die_nomem();
+  if (!stralloc_0(&pass)) die_nomem();
+  if (!stralloc_0(&chal)) die_nomem();
+
+  if (pipe(pi) == -1) return err_pipe();
+  switch(child = fork()) {
+    case -1:
+      return err_fork();
+    case 0:
+      close(pi[1]);
+      if (fd_copy(3,pi[0]) == -1) return err_pipe();
+      sig_pipedefault();
+        execvp(*childargs, childargs);
+      _exit(1);
+  }
+  close(pi[0]);
+
+  substdio_fdbuf(&ssauth,write,pi[1],ssauthbuf,sizeof ssauthbuf);
+  if (substdio_put(&ssauth,user.s,user.len) == -1) return err_write();
+  if (substdio_put(&ssauth,pass.s,pass.len) == -1) return err_write();
+  if (smtpauth == 3) if (substdio_put(&ssauth,chal.s,chal.len) == -1) return err_write();
+  if (substdio_flush(&ssauth) == -1) return err_write();
+
+  close(pi[1]);
+  if (!stralloc_copys(&chal,"")) die_nomem(); 
+  if (!stralloc_copys(&slop,"")) die_nomem(); 
+  byte_zero(ssauthbuf,sizeof ssauthbuf);
+  if (wait_pid(&wstat,child) == -1) return err_child();
+  if (wait_crashed(wstat)) return err_child();
+  if (wait_exitcode(wstat)) { sleep(AUTHSLEEP); return 1; } /* no */
+  return 0; /* yes */
+}
+
+int auth_login(arg) char *arg;
+{
+  int r;
+
+  if (*arg) {
+    if (r = b64decode(arg,str_len(arg),&user) == 1) return err_input();
+  }
+  else {
+    out("334 VXNlcm5hbWU6\r\n"); flush();       /* Username: */
+    if (authgetl() < 0) return -1;
+    if (r = b64decode(authin.s,authin.len,&user) == 1) return err_input();
+  }
+  if (r == -1) die_nomem();
+
+  out("334 UGFzc3dvcmQ6\r\n"); flush();         /* Password: */
+
+  if (authgetl() < 0) return -1;
+  if (r = b64decode(authin.s,authin.len,&pass) == 1) return err_input();
+  if (r == -1) die_nomem();
+
+  if (!user.len || !pass.len) return err_input();
+  return authenticate();
+}
+
+int auth_plain(arg) char *arg;
+{
+  int r, id = 0;
+
+  if (*arg) {
+    if (r = b64decode(arg,str_len(arg),&resp) == 1) return err_input();
+  }
+  else {
+    out("334 \r\n"); flush();
+    if (authgetl() < 0) return -1;
+    if (r = b64decode(authin.s,authin.len,&resp) == 1) return err_input();
+  }
+  if (r == -1 || !stralloc_0(&resp)) die_nomem();
+  while (resp.s[id]) id++;                       /* "authorize-id\0userid\0passwd\0" */
+
+  if (resp.len > id + 1)
+    if (!stralloc_copys(&user,resp.s + id + 1)) die_nomem();
+  if (resp.len > id + user.len + 2)
+    if (!stralloc_copys(&pass,resp.s + id + user.len + 2)) die_nomem();
+
+  if (!user.len || !pass.len) return err_input();
+  return authenticate();
+}
+
+int auth_cram()
+{
+  int i, r;
+  char *s;
+
+  s = unique;						/* generate challenge */
+  s += fmt_uint(s,getpid());
+  *s++ = '.';
+  s += fmt_ulong(s,(unsigned long) now());
+  *s++ = '@';
+  *s++ = 0;
+  if (!stralloc_copys(&chal,"<")) die_nomem(); 
+  if (!stralloc_cats(&chal,unique)) die_nomem();
+  if (!stralloc_cats(&chal,local)) die_nomem();
+  if (!stralloc_cats(&chal,">")) die_nomem();
+  if (b64encode(&chal,&slop) < 0) die_nomem();
+  if (!stralloc_0(&slop)) die_nomem();
+
+  out("334 ");                                          /* "334 base64_challenge \r\n" */
+  out(slop.s);
   out("\r\n");
+  flush();
+
+  if (authgetl() < 0) return -1;                        /* got response */
+  if (r = b64decode(authin.s,authin.len,&resp) == 1) return err_input();
+  if (r == -1 || !stralloc_0(&resp)) die_nomem();
+
+  i = str_chr(resp.s,' ');
+  s = resp.s + i;
+  while (*s == ' ') ++s;
+  resp.s[i] = 0;
+  if (!stralloc_copys(&user,resp.s)) die_nomem();       /* userid */
+  if (!stralloc_copys(&pass,s)) die_nomem();    	/* digest */
+
+  if (!user.len || !pass.len) return err_input();
+  return authenticate();
+}
+
+struct authcmd {
+  char *text;
+  int (*fun)();
+} authcmds[] = { { "login",auth_login }, { "plain",auth_plain }, { "cram-md5",auth_cram }, { 0,err_noauth } };
+
+void smtp_auth(arg)
+char *arg;
+{
+  int i;
+  char *cmd = arg;
+
+  if (!*childargs) { out("503 auth not available (#5.3.3)\r\n"); return; }
+  if (seenauth) { err_authd(); return; }
+  if (seenmail) { err_authmail(); return; }
+
+  if (!stralloc_copys(&user,"")) die_nomem();
+  if (!stralloc_copys(&pass,"")) die_nomem();
+  if (!stralloc_copys(&resp,"")) die_nomem();
+  if (!stralloc_copys(&chal,"")) die_nomem();		/* only needed for CRAM-MD5 */
+
+  i = str_chr(cmd,' ');					/* get AUTH type */
+  arg = cmd + i;
+  while (*arg == ' ') ++arg;
+  cmd[i] = 0;
+
+  for (i = 0;authcmds[i].text;++i)
+    if (case_equals(authcmds[i].text,cmd)) break;
+
+  switch (authcmds[i].fun(arg)) {
+    case 0:
+      seenauth = 1;
+      if (!stralloc_cats(&protocol,"A")) die_nomem();
+      relayclient = "";
+      remoteinfo = user.s;
+      if (!env_unset("TCPREMOTEINFO")) die_read();
+      if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem();
+      if (!env_put2("RELAYCLIENT",relayclient)) die_nomem();
+      out("235 ok, go ahead (#2.0.0)\r\n");
+      loga("Accept::ORIG::Valid_Auth:"," P:EMSTPA",remoteip,remotehost,helohost.s,user.s,authcmds[i].text);
+      break;
+    case 1:
+      err_authfail("Reject::ORIG::Failed_Auth:"," P:ESMTPA",remoteip,remotehost,helohost.s,user.s,authcmds[i].text);
+  }
 }
 
+/* this file is too long --------------------------------------------- GO ON */
+
 struct commands smtpcommands[] = {
   { "rcpt", smtp_rcpt, 0 }
 , { "mail", smtp_mail, 0 }
 , { "data", smtp_data, flush }
+, { "auth", smtp_auth, flush }
 , { "quit", smtp_quit, flush }
 , { "helo", smtp_helo, flush }
 , { "ehlo", smtp_ehlo, flush }
@@ -405,15 +1387,20 @@
 , { "help", smtp_help, flush }
 , { "noop", err_noop, flush }
 , { "vrfy", err_vrfy, flush }
+, { "starttls", smtp_starttls, flush }
 , { 0, err_unimpl, flush }
 } ;
 
-void main()
+void main(argc,argv)
+int argc;
+char **argv;
 {
+  childargs = argv + 1;
   sig_pipeignore();
   if (chdir(auto_qmail) == -1) die_control();
   setup();
   if (ipme_init() != 1) die_ipme();
+  if (greetdelay) sleep(greetdelay);
   smtp_greet("220 ");
   out(" ESMTP\r\n");
   if (commands(&ssin,&smtpcommands) == 0) die_read();


syntax highlighted by Code2HTML, v. 0.9.1