From b36d52a0dd7315a969f2a9a7455717466e45be23 Mon Sep 17 00:00:00 2001
From: Pawel Foremski <pjf@asn.pl>
Date: Sat, 1 Apr 2006 22:48:04 +0200
Subject: [PATCH] qmail SPP patch

https://sourceforge.net/projects/qmail-spp/files/qmail-spp/0.42/qmail-spp-0.42.tar.gz
---
 Makefile      |  11 ++-
 qmail-smtpd.c |  28 +++++-
 qmail-spp.c   | 251 ++++++++++++++++++++++++++++++++++++++++++++++++++
 qmail-spp.h   |  14 +++
 4 files changed, 297 insertions(+), 7 deletions(-)
 create mode 100644 qmail-spp.c
 create mode 100644 qmail-spp.h

diff --git a/Makefile b/Makefile
index 08810df..fc0bd02 100644
--- a/Makefile
+++ b/Makefile
@@ -1541,16 +1541,21 @@ auto_uids.h auto_users.h auto_qmail.h auto_break.h auto_patrn.h \
 auto_spawn.h auto_split.h
 	./compile qmail-showctl.c
 
+qmail-spp.o: \
+compile qmail-spp.c readwrite.h stralloc.h substdio.h control.h str.h \
+byte.h env.h exit.h wait.h fork.h fd.h fmt.h getln.h
+	./compile qmail-spp.c
+
 qmail-smtpd: \
 load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \
 timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \
 date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \
 open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \
-fs.a auto_qmail.o base64.o socket.lib
+fs.a auto_qmail.o base64.o qmail-spp.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 \
-	datetime.a getln.a open.a sig.a case.a env.a stralloc.a \
+	datetime.a getln.a open.a sig.a case.a qmail-spp.o env.a stralloc.a \
 	alloc.a substdio.a error.a str.a fs.a auto_qmail.o base64.o  `cat \
 	socket.lib`
 
@@ -1562,7 +1567,7 @@ compile qmail-smtpd.c sig.h readwrite.h stralloc.h gen_alloc.h \
 substdio.h alloc.h auto_qmail.h control.h received.h constmap.h \
 error.h ipme.h ip.h ipalloc.h ip.h gen_alloc.h ip.h qmail.h \
 substdio.h str.h fmt.h scan.h byte.h case.h env.h now.h datetime.h \
-exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h base64.h
+exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h base64.h qmail-spp.h
 	./compile qmail-smtpd.c
 
 qmail-start: \
diff --git a/qmail-smtpd.c b/qmail-smtpd.c
index a87a1d8..2c2bfb5 100644
--- a/qmail-smtpd.c
+++ b/qmail-smtpd.c
@@ -24,9 +24,12 @@
 #include "timeoutwrite.h"
 #include "commands.h"
 #include "wait.h"
+#include "qmail-spp.h"
 
 #define AUTHSLEEP 5
 
+int spp_val;
+
 #define MAXHOPS 100
 unsigned int databytes = 0;
 int timeout = 1200;
@@ -129,6 +132,7 @@ void setup()
   if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control();
   if (timeout <= 0) timeout = 1;
   if (rcpthosts_init() == -1) die_control();
+  if (spp_init() == -1) die_control();
 
   bmfok = control_readfile(&bmf,"control/badmailfrom",0);
   if (bmfok == -1) die_control();
@@ -252,6 +256,7 @@ int seenauth = 0;
 int seenmail = 0;
 int flagbarf; /* defined if seenmail */
 int flagsize;
+int allowed;
 stralloc mailfrom = {0};
 stralloc rcptto = {0};
 stralloc fuser = {0};
@@ -316,14 +321,16 @@ void mailfrom_parms(arg) char *arg;
 
 void smtp_helo(arg) char *arg;
 {
+  if(!spp_helo(arg)) return;
   smtp_greet("250 "); out("\r\n");
   seenmail = 0; dohelo(arg);
 }
 void smtp_ehlo(arg) char *arg;
 {
   char size[FMT_ULONG];
+  if(!spp_helo(arg)) return;
   size[fmt_ulong(size,(unsigned int) databytes)] = 0;
-  smtp_greet("250-"); 
+  smtp_greet("250-");
   out("\r\n250-PIPELINING\r\n250-8BITMIME\r\n");
   if (smtpauth == 1 || smtpauth == 11) out("250-AUTH LOGIN PLAIN\r\n");
   if (smtpauth == 2 || smtpauth == 12) out("250-AUTH CRAM-MD5\r\n");
@@ -333,7 +340,8 @@ void smtp_ehlo(arg) char *arg;
 }
 void smtp_rset(arg) char *arg;
 {
-  seenmail = 0; seenauth = 0; 
+  spp_rset();
+  seenmail = 0; seenauth = 0;
   mailfrom.len = 0; rcptto.len = 0;
   out("250 flushed\r\n");
 }
@@ -342,9 +350,11 @@ void smtp_mail(arg) char *arg;
   if (smtpauth)
     if (smtpauth > 10 && !seenauth) { err_submission(); return; }
   if (!addrparse(arg)) { err_syntax(); return; }
+  if (!(spp_val = spp_mail())) return;
   flagsize = 0;
   mailfrom_parms(arg);
   if (flagsize) { err_size(); return; }
+  if (spp_val == 1)
   flagbarf = bmfcheck();
   seenmail = 1;
   if (!stralloc_copys(&rcptto,"")) die_nomem();
@@ -356,13 +366,18 @@ void smtp_rcpt(arg) char *arg; {
   if (!seenmail) { err_wantmail(); return; }
   if (!addrparse(arg)) { err_syntax(); return; }
   if (flagbarf) { err_bmf(); return; }
+  if (!relayclient) allowed = addrallowed();
+  else allowed = 1;
+  if (!(spp_val = spp_rcpt(allowed))) return;
   if (relayclient) {
     --addr.len;
     if (!stralloc_cats(&addr,relayclient)) die_nomem();
     if (!stralloc_0(&addr)) die_nomem();
   }
-  else
-    if (!addrallowed()) { err_nogateway(); return; }
+  else if (spp_val == 1) {
+    if (!allowed) { err_nogateway(); return; }
+  }
+  spp_rcpt_accepted();
   if (!stralloc_cats(&rcptto,"T")) die_nomem();
   if (!stralloc_cats(&rcptto,addr.s)) die_nomem();
   if (!stralloc_0(&rcptto)) die_nomem();
@@ -477,6 +492,7 @@ void smtp_data(arg) char *arg; {
  
   if (!seenmail) { err_wantmail(); return; }
   if (!rcptto.len) { err_wantrcpt(); return; }
+  if (!spp_data()) return;
   seenmail = 0;
   if (databytes) bytestooverflow = databytes + 1;
   if (qmail_open(&qqt) == -1) { err_qqt(); return; }
@@ -484,6 +500,8 @@ void smtp_data(arg) char *arg; {
   out("354 go ahead\r\n");
  
   received(&qqt,protocol,local,remoteip,remotehost,remoteinfo,fakehelo);
+  qmail_put(&qqt,sppheaders.s,sppheaders.len); /* set in qmail-spp.c */
+  spp_rset();
   blast(&hops);
   hops = (hops >= MAXHOPS);
   if (hops) qmail_fail(&qqt);
@@ -734,8 +752,10 @@ char **argv;
   if (chdir(auto_qmail) == -1) die_control();
   setup();
   if (ipme_init() != 1) die_ipme();
+  if (spp_connect()) {
   smtp_greet("220 ");
   out(" ESMTP\r\n");
+  }
   if (commands(&ssin,&smtpcommands) == 0) die_read();
   die_nomem();
 }
diff --git a/qmail-spp.c b/qmail-spp.c
new file mode 100644
index 0000000..d9f4340
--- /dev/null
+++ b/qmail-spp.c
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2004-2005 Pawel Foremski <pjf@asn.pl>
+ *
+ * This program is free software; you can redistribute it and/or 
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation; either 
+ * version 2 of the License, or (at your option) any later 
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *** Note
+ *
+ * This is the core of qmail-spp patch for qmail
+ *
+ * Why I made it a separate file? Because I wanted qmail-spp to apply more
+ * cleanly on heavily patched qmail sources and to make it bit simpler to
+ * maintain, so don't treat it as a library.
+ *
+ * "..." comments marks places where code for other SMTP commands should be
+ * added, if needed.
+ *
+ */
+
+#include "readwrite.h"
+#include "stralloc.h"
+#include "substdio.h"
+#include "control.h"
+#include "str.h"
+#include "byte.h"
+#include "env.h"
+#include "exit.h"
+#include "wait.h"
+#include "fork.h"
+#include "fd.h"
+#include "fmt.h"
+#include "getln.h"
+
+/* stuff needed from qmail-smtpd */
+extern void flush();
+extern void out();
+extern void die_nomem();
+extern stralloc addr;
+/* *** */
+
+stralloc sppheaders = {0};
+static int spprun = 0;
+static int sppfok = 0;
+static int sppret;
+static stralloc sppf = {0};
+static stralloc plugins_dummy = {0}, plugins_connect = {0}, plugins_helo = {0}, plugins_mail = {0},
+                plugins_rcpt = {0}, plugins_data = {0}; /* ... */
+static stralloc error_mail = {0}, error_rcpt = {0}, error_data = {0}; /* ... */
+static stralloc sppmsg = {0};
+static char rcptcountstr[FMT_ULONG];
+static unsigned long rcptcount;
+static unsigned long rcptcountall;
+static substdio ssdown;
+static char downbuf[128];
+
+static void err_spp(s1, s2) char *s1, *s2; { out("451 qmail-spp failure: "); out(s1); out(": "); out(s2); out(" (#4.3.0)\r\n"); }
+
+int spp_init()
+{
+  int i, len = 0;
+  stralloc *plugins_to;
+  char *x, *conffile = "control/smtpplugins";
+
+  if (!env_get("NOSPP")) {
+    spprun = 1;
+    plugins_to = &plugins_dummy;
+    x = env_get("SPPCONFFILE");
+    if (x && *x) conffile = x;
+    sppfok = control_readfile(&sppf, conffile, 0);
+    if (sppfok != 1) return -1;
+    for (i = 0; i < sppf.len; i += len) {
+      len = str_len(sppf.s + i) + 1;
+      if (sppf.s[i] == '[')
+        switch (sppf.s[i + 1]) {
+          case 'c': plugins_to = &plugins_connect; break;
+          case 'h': plugins_to = &plugins_helo; break;
+          case 'm': plugins_to = &plugins_mail; break;
+          case 'r': plugins_to = &plugins_rcpt; break;
+          case 'd': plugins_to = &plugins_data; break;
+          /* ... */
+          default: plugins_to = &plugins_dummy;
+        }
+      else
+        if (!stralloc_catb(plugins_to, sppf.s + i, len)) die_nomem();
+    }
+  }
+
+  return 0;
+}
+
+void sppout() { if (sppmsg.len) out(sppmsg.s); out("\r\n"); }
+
+int spp(plugins, addrenv) stralloc *plugins; char *addrenv;
+{
+  static int pipes[2];
+  static int i, pid, wstat, match, last;
+  static stralloc data = {0};
+  static char *(args[4]);
+  static stralloc *errors_to;
+
+  if (!spprun) return 1;
+  if (addrenv) if (!env_put2(addrenv, addr.s)) die_nomem();
+  last = 0;
+
+  for (i = 0; i < plugins->len; i += str_len(plugins->s + i) + 1) {
+    if (plugins->s[i] == ':')
+      { args[0] = "/bin/sh"; args[1] = "-c"; args[2] = plugins->s + i + 1; args[3] = 0; }
+    else
+      { args[0] = plugins->s + i; args[1] = 0; }
+
+    if (pipe(pipes) == -1)
+      { err_spp(plugins->s + i, "can't pipe()"); return 0; }
+
+    switch (pid = vfork()) {
+      case -1:
+        err_spp(plugins->s + i, "vfork() failed");
+        return 0;
+      case 0:
+        close(0); close(pipes[0]); fd_move(1, pipes[1]);
+        execv(*args, args);
+        _exit(120);
+    }
+
+    close(pipes[1]);
+    substdio_fdbuf(&ssdown, read, pipes[0], downbuf, sizeof(downbuf));
+    do {
+      if (getln(&ssdown, &data, &match, '\n') == -1) die_nomem();
+      if (data.len > 1) {
+        data.s[data.len - 1] = 0;
+        switch (data.s[0]) {
+          case 'H':
+            if (!stralloc_catb(&sppheaders, data.s + 1, data.len - 2)) die_nomem();
+            if (!stralloc_append(&sppheaders, "\n")) die_nomem();
+            break;
+          case 'C':
+            if (addrenv) {
+              if (!stralloc_copyb(&addr, data.s + 1, data.len - 1)) die_nomem();
+              if (!env_put2(addrenv, addr.s)) die_nomem();
+            }
+            break;
+          case 'S': if (!env_put(data.s + 1)) die_nomem(); break;
+          case 'U': if (!env_unset(data.s + 1)) die_nomem(); break;
+          case 'A': spprun = 0;
+          case 'O':
+          case 'N':
+          case 'D': last = 1; match = 0; break;
+          case 'E':
+          case 'R': last = 1; match = 0;
+          case 'P': out(data.s + 1); out("\r\n"); break;
+          case 'L':
+            switch (data.s[1]) {
+              case 'M': errors_to = &error_mail; break;
+              case 'R': errors_to = &error_rcpt; break;
+              case 'D': errors_to = &error_data; break;
+              /* ... */
+              default: errors_to = 0;
+            }
+            if (errors_to) {
+              if (!stralloc_catb(errors_to, data.s + 2, data.len - 3)) die_nomem();
+              if (!stralloc_catb(errors_to, "\r\n", 2)) die_nomem();
+            }
+            break;
+        }
+      }
+    } while (match);
+
+    close(pipes[0]);
+    if (wait_pid(&wstat,pid) == -1) { err_spp(plugins->s + i, "wait_pid() failed"); return 0; }
+    if (wait_crashed(wstat)) { err_spp(plugins->s + i, "child crashed"); return 0; }
+    if (wait_exitcode(wstat) == 120) { err_spp(plugins->s + i, "can't execute"); return 0; }
+
+    if (last)
+      switch (*data.s) {
+        case 'E': return 0;
+        case 'A':
+        case 'N': return 1;
+        case 'O': return 2;
+        case 'R':
+        case 'D': flush(); _exit(0);
+      }
+  }
+
+  return 1;
+}
+
+int spp_errors(errors) stralloc *errors;
+{
+  if (!errors->len) return 1;
+  if (!stralloc_0(errors)) die_nomem();
+  out(errors->s);
+  return 0;
+}
+
+int spp_connect() { return spp(&plugins_connect, 0); }
+
+int spp_helo(arg) char *arg;
+{
+  if (!env_put2("SMTPHELOHOST", arg)) die_nomem();
+  return spp(&plugins_helo, 0);
+}
+
+void spp_rset()
+{ 
+  if (!stralloc_copys(&sppheaders, "")) die_nomem();
+  if (!stralloc_copys(&error_mail, "")) die_nomem();
+  if (!stralloc_copys(&error_rcpt, "")) die_nomem();
+  if (!stralloc_copys(&error_data, "")) die_nomem();
+  /* ... */
+  rcptcount = rcptcountall = 0;
+}
+
+int spp_mail()
+{
+  if (!spp_errors(&error_mail)) return 0;
+  rcptcount = rcptcountall = 0;
+  return spp(&plugins_mail, "SMTPMAILFROM");
+}
+
+int spp_rcpt(allowed) int allowed;
+{
+  if (!spp_errors(&error_rcpt)) return 0;
+  rcptcountstr[fmt_ulong(rcptcountstr, rcptcount)] = 0;
+  if (!env_put2("SMTPRCPTCOUNT", rcptcountstr)) die_nomem();
+  rcptcountstr[fmt_ulong(rcptcountstr, ++rcptcountall)] = 0;
+  if (!env_put2("SMTPRCPTCOUNTALL", rcptcountstr)) die_nomem();
+  if (!env_put2("SMTPRCPTHOSTSOK", allowed ? "1" : "0")) die_nomem();
+  sppret = spp(&plugins_rcpt, "SMTPRCPTTO");
+  return sppret;
+}
+
+void spp_rcpt_accepted() { rcptcount++; }
+
+int spp_data()
+{
+  if (!spp_errors(&error_data)) return 0;
+  return spp(&plugins_data, 0);
+}
+
+/* ... */
diff --git a/qmail-spp.h b/qmail-spp.h
new file mode 100644
index 0000000..1ecc274
--- /dev/null
+++ b/qmail-spp.h
@@ -0,0 +1,14 @@
+#ifndef QMAIL_SPP_H
+#define QMAIL_SPP_H
+
+extern stralloc sppheaders;
+extern int spp_init();
+extern int spp_connect();
+extern int spp_helo();
+extern void spp_rset();
+extern int spp_mail();
+extern int spp_rcpt();
+extern int spp_rcpt_accepted();
+extern int spp_data();
+
+#endif
