diff -Nru netqmail-1.06_errmsg/Makefile netqmail-1.06_errmsg_spamrefuse/Makefile
--- netqmail-1.06_errmsg/Makefile	2009-04-28 16:26:19.000000000 +0100
+++ netqmail-1.06_errmsg_spamrefuse/Makefile	2009-08-23 22:01:17.000000000 +0100
@@ -1425,9 +1425,11 @@
 qmail-queue: \
 load qmail-queue.o triggerpull.o fmtqfn.o now.o date822fmt.o \
 datetime.a seek.a ndelay.a open.a sig.a alloc.a substdio.a error.a \
+getln.a stralloc.a env.a \
 str.a fs.a auto_qmail.o auto_split.o auto_uids.o
 	./load qmail-queue triggerpull.o fmtqfn.o now.o \
 	date822fmt.o datetime.a seek.a ndelay.a open.a sig.a \
+	getln.a stralloc.a env.a \
 	alloc.a substdio.a error.a str.a fs.a auto_qmail.o \
 	auto_split.o auto_uids.o 
 
@@ -1437,6 +1439,7 @@
 
 qmail-queue.o: \
 compile qmail-queue.c readwrite.h sig.h exit.h open.h seek.h fmt.h \
+getln.h stralloc.h env.h \
 alloc.h substdio.h datetime.h now.h datetime.h triggerpull.h extra.h \
 auto_qmail.h auto_uids.h date822fmt.h fmtqfn.h
 	./compile qmail-queue.c
diff -Nru netqmail-1.06_errmsg/qmail.c netqmail-1.06_errmsg_spamrefuse/qmail.c
--- netqmail-1.06_errmsg/qmail.c	2007-11-30 20:22:54.000000000 +0000
+++ netqmail-1.06_errmsg_spamrefuse/qmail.c	2009-08-25 12:16:15.000000000 +0100
@@ -108,6 +108,7 @@
     case 115: /* compatibility */
     case 11: return "Denvelope address too long for qq (#5.1.3)";
     case 31: return "Dmail server permanently rejected message (#5.3.0)";
+    case 32: return "DMessage rejected, appears to be spam (#5.3.0)"; /* 5.7.1 maybe better but qmail-smtpd starts error message with 554 which isn't specified for 5.7.1 */
     case 51: return "Zqq out of memory (#4.3.0)";
     case 52: return "Zqq timeout (#4.3.0)";
     case 53: return "Zqq write error or disk full (#4.3.0)";
diff -Nru netqmail-1.06_errmsg/qmail-control.9 netqmail-1.06_errmsg_spamrefuse/qmail-control.9
--- netqmail-1.06_errmsg/qmail-control.9	1998-06-15 11:53:16.000000000 +0100
+++ netqmail-1.06_errmsg_spamrefuse/qmail-control.9	2009-08-28 19:42:19.000000000 +0100
@@ -22,6 +22,7 @@
 in
 .IR badmailfrom ,
 .IR locals ,
+.IR nospamrefuse ,
 .IR percenthack ,
 .IR qmqpservers ,
 .IR rcpthosts ,
@@ -56,6 +57,7 @@
 .I localiphost	\fIme	\fRqmail-smtpd
 .I locals	\fIme	\fRqmail-send
 .I morercpthosts	\fR(none)	\fRqmail-smtpd
+.I nospamrefuse	\fRpostmaster	\fRqmail-queue
 .I percenthack	\fR(none)	\fRqmail-send
 .I plusdomain	\fIme	\fRqmail-inject
 .I qmqpservers	\fR(none)	\fRqmail-qmqpc
diff -Nru netqmail-1.06_errmsg/qmail-queue.8 netqmail-1.06_errmsg_spamrefuse/qmail-queue.8
--- netqmail-1.06_errmsg/qmail-queue.8	2007-11-30 20:22:54.000000000 +0000
+++ netqmail-1.06_errmsg_spamrefuse/qmail-queue.8	2009-08-28 19:32:48.000000000 +0100
@@ -58,6 +58,31 @@
 subdirectory must be in the same filesystem as the
 .B intd
 directory.
+
+.SH "ENVIRONMENT VARIABLES"
+.TP 5
+.I SPAMREFUSE
+Maximum acceptable score from Spam Assassin (assumes Spam Assassin
+has processed the message prior to reception by
+.BR qmail-queue ,
+typically via Spam Assassin's
+.BR qmail-spamc ).
+Messages are rejected if the (first, if more than one)
+.I X-Spam-Status:
+header shows a score higher than
+.IR SPAMREFUSE .
+If no
+.I X-Spam-Status:
+header is found the message is accepted. Up to 2 decimal places are
+significant in the
+.I SPAMREFUSE
+value. The
+.B nospamrefuse
+control file disables
+.I SPAMREFUSE
+for particular addresses, see the
+.B qmail-smtpd
+man page.
 .SH "EXIT CODES"
 .B qmail-queue
 does not print diagnostics.
@@ -80,6 +105,12 @@
 (Not used by
 .BR qmail-queue ,
 but can be used by programs offering the same interface.)
+.TP 
+.B 32
+Message rejected since it appears to be spam (the
+.I X-Spam-Status:
+score exceeded
+.IR SPAMREFUSE ).
 .PP
 All other
 .B qmail-queue
diff -Nru netqmail-1.06_errmsg/qmail-queue.c netqmail-1.06_errmsg_spamrefuse/qmail-queue.c
--- netqmail-1.06_errmsg/qmail-queue.c	1998-06-15 11:53:16.000000000 +0100
+++ netqmail-1.06_errmsg_spamrefuse/qmail-queue.c	2009-08-27 21:52:19.000000000 +0100
@@ -7,6 +7,8 @@
 #include "seek.h"
 #include "fmt.h"
 #include "alloc.h"
+#include "str.h"
+#include "stralloc.h"
 #include "substdio.h"
 #include "datetime.h"
 #include "now.h"
@@ -55,6 +57,7 @@
 }
 
 void die(e) int e; { _exit(e); }
+void die_spam()  { cleanup(); die(32); }
 void die_write() { cleanup(); die(53); }
 void die_read() { cleanup(); die(54); }
 void sigalrm() { /* thou shalt not clean up here */ die(52); }
@@ -149,12 +152,131 @@
  die(63);
 }
 
+/* Convert the number with up to 2 decimal places in
+   string s to that number multiplied by 100 as an integer.
+   Any additional decimals are discarded. The number may be
+   negative. If no number is found, 0 is returned.
+
+   Enhancement: It will be better to return the number*100
+   separately to the result (success or fail) of this
+   subroutine so the caller can use a sensible fallback
+   value for number*100 instead of 0.
+ */
+int getnum100(s)
+char *s;
+{
+  int ret = 0;
+  int neg = 0;
+
+  /* Find the number or EOS */
+  while ((*s != '\0') && (*s != '-') && (*s != '.') && ((*s < '0') || (*s > '9')))
+    s++;
+  if (*s == '-') { neg=1; s++; }
+  while ((*s >= '0') && (*s <= '9'))
+  {
+    ret = ret*10 + (int)(*s)-(int)'0';
+    s++;
+  }
+  ret *= 100;
+  if (*s == '.')
+  { /* Get up to 2 digits after decimal point */
+    s++;
+    if ((*s >= '0') && (*s <= '9'))
+    {
+      ret += ((int)(*s) - (int)'0') * 10;
+      s++;
+      if ((*s >= '0') && (*s <= '9'))
+        ret += (int)(*s) - (int)'0';
+    }
+  }
+  if (neg) return (-ret);
+  else     return ( ret);
+}
+
+/* This function examines the X-Spam-Status header in the
+   message with a view to causing qmail-queue to reject the
+   message if the spam score is over a certain limit.
+   It is assumed that the message has already been processed
+   by SpamAssassin and its X-Spam-Status header has been
+   added (such as by setting QMAILQUEUE to qmail-spamc).
+   Additionally it is assumed that the first number on the
+   X-Spam-Status: header line is the score - and that this
+   occurs on the first line of this header.
+
+   If a limit has been set to check against the score
+   value in the X-Spam-Status line, extract this score and
+   compare it against the supplied limit. Since the
+   score may have a fractional (decimal) component but I
+   don't wish to introduce floating point operations, both
+   the score and limit are multiplied by 100 before
+   comparison to enable comparison to 2 decimal places. Also
+   note that both the score and limit may be negative.
+
+   Inspiration for this patch comes from Erwin Hoffmann's
+   QHPSI patch; I've chosen to implement this functionality
+   internally to qmail-queue since relatively little code
+   is required to examine the already-generated SpamAssassin
+   headers, I think having an external program to do this
+   would be overkill.
+
+   Some email addresses may be excluded from spamrefusecheck()
+   (like postmaster); these exceptions are dealt with in
+   qmail-smtpd.c rather than here since constmap is used to
+   manage the list of exceptions, and this is already used
+   elsewhere in qmail-smtpd.c - exceptions are dealt with by
+   unsetting the environment variable that would otherwise
+   cause spamrefusecheck() to be invoked.
+			Andrew Richards, 23rd August 2009.
+ */
+void spamrefusecheck(limit_s)
+char *limit_s;
+{
+ int mfd;
+ struct stat st;
+ static stralloc mline={0};
+ int match;
+ substdio mss;
+ char buf[128];
+ int score100, limit100;
+ int n;
+
+ if ((!limit_s) || (*limit_s == '\0')) return; /* Check disabled */
+ limit100 = getnum100(limit_s);
+
+ if (stat(messfn,&st) == -1) die_read();
+ mfd = open_read(messfn);
+ substdio_fdbuf(&mss,read,mfd,buf,sizeof(buf));
+ do
+ {
+   if (getln(&mss,&mline,&match,'\n') != 0) die_read();
+   if (mline.len <= 1) return;	/* End of headers, give up */
+   if (str_start(mline.s,"X-Spam-Status: "))
+   {
+    stralloc_0(&mline);
+    n = 15;			/* 15 = str_len("X-Spam-Status: ") */
+    while (n+6 < mline.len)	/*  6 = str_len("score=") */
+    {
+      if (str_start(mline.s+n,"score="))
+      {
+        score100 = getnum100(mline.s+n+6);
+        break;
+      }
+      n++;
+    }
+    if (score100 && (score100 >= limit100)) die_spam();
+    else return; /* message can be accepted */
+   }
+ }
+ while (match);
+}
+
 char tmp[FMT_ULONG];
 
 void main()
 {
  unsigned int len;
  char ch;
+ char *spamrefuse;
 
  sig_blocknone();
  umask(033);
@@ -165,6 +287,8 @@
  uid = getuid();
  starttime = now();
  datetime_tai(&dt,starttime);
+ spamrefuse = env_get("SPAMREFUSE");
+ if ((spamrefuse) && (*spamrefuse == '\0')) spamrefuse = (char *)0; /* Disable spamrefuse check if SPAMREFUSE="" */
 
  received_setup();
 
@@ -244,6 +368,9 @@
    if (len >= ADDR) die(11);
   }
 
+ /* Reject if X-Spam-Level header shows a score > SPAMREFUSE */
+ if (spamrefuse) spamrefusecheck(spamrefuse);
+
  if (substdio_flush(&ssout) == -1) die_write();
  if (fsync(intdfd) == -1) die_write();
 
diff -Nru netqmail-1.06_errmsg/qmail-smtpd.8 netqmail-1.06_errmsg_spamrefuse/qmail-smtpd.8
--- netqmail-1.06_errmsg/qmail-smtpd.8	1998-06-15 11:53:16.000000000 +0100
+++ netqmail-1.06_errmsg_spamrefuse/qmail-smtpd.8	2009-08-28 18:02:56.000000000 +0100
@@ -120,6 +120,31 @@
 and the rest into
 .IR morercpthosts .
 .TP 5
+.I nospamrefuse
+List of destinations that get spam regardless of any
+.B SPAMREFUSE
+setting (see
+.B qmail-queue
+man page).
+Destinations can be specified as full addresses, every address in a given
+domain by using the form
+.BR @\fIhost ,
+or an address at all domains (the full part before the
+.B @
+- fractional addresses are not supported).
+Examples of each:
+
+.EX
+   someone@example.com
+   @example.com
+   someone
+.EE
+
+Default:
+.I postmaster
+(at all domains).
+
+.TP 5
 .I rcpthosts
 Allowed RCPT domains.
 If
diff -Nru netqmail-1.06_errmsg/qmail-smtpd.c netqmail-1.06_errmsg_spamrefuse/qmail-smtpd.c
--- netqmail-1.06_errmsg/qmail-smtpd.c	2009-04-28 16:26:19.000000000 +0100
+++ netqmail-1.06_errmsg_spamrefuse/qmail-smtpd.c	2009-08-27 22:05:51.000000000 +0100
@@ -181,6 +181,10 @@
 int bmfok = 0;
 stralloc bmf = {0};
 struct constmap mapbmf;
+char *spamrefuse;
+int nsrok = 0;
+stralloc nsr = {0};
+struct constmap mapnsr;
 
 void setup()
 {
@@ -202,6 +206,27 @@
   if (bmfok)
     if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem();
  
+  /* Is SMTP-time spam blocking on > SPAMREFUSE desired: */
+  spamrefuse = env_get("SPAMREFUSE");
+  if ((spamrefuse) && (*spamrefuse == '\0')) spamrefuse = (char *)0;
+  if (spamrefuse)
+  { /* See which recipients omit spamrefuse check */
+    nsrok = control_readfile(&nsr,"control/nospamrefuse",0);
+    if (nsrok == -1) die_control();
+    if (nsrok)
+    {
+      if (!constmap_init(&mapnsr,nsr.s,nsr.len,0)) die_nomem();
+    }
+    else /* Default to letting spam through for postmaster mailboxes only */
+    { /* Note that must use str_len(...)+1 because str_len will treat trailing \0 as EOS */
+      /* \0 in string (in addition to \0 for EOS) is to play nicely with constmap_init: */
+      if (!constmap_init(&mapnsr,"postmaster\0",str_len("postmaster")+1,0)) die_nomem();
+      nsrok = 1;
+    }
+  }
+  else
+    nsrok = 0;
+ 
   if (control_readint(&databytes,"control/databytes") == -1) die_control();
   x = env_get("DATABYTES");
   if (x) { scan_ulong(x,&u); databytes = u; }
@@ -292,6 +317,22 @@
   return 0;
 }
 
+int nsrcheck() /* See if recipient omits spamrefuse check */
+{
+  int j;
+  if (!nsrok) return 0;
+  /* username@domain: */
+  if (constmap(&mapnsr,addr.s,addr.len - 1)) return 1;
+  j = byte_rchr(addr.s,addr.len,'@');
+  if ((j < addr.len) && (j > 0 ))
+  { /* @domain:  */
+    if (constmap(&mapnsr,addr.s + j,addr.len - j - 1)) return 1;
+    /* username: */
+    if (constmap(&mapnsr,addr.s, j)) return 1;
+  }
+  return 0;
+}
+
 int addrallowed()
 {
   int r;
@@ -303,6 +344,7 @@
 
 int seenmail = 0;
 int flagbarf; /* defined if seenmail */
+int blockspam = 0;
 
 void smtp_helo(arg) char *arg;
 {
@@ -319,6 +361,7 @@
 void smtp_rset(arg) char *arg;
 {
   seenmail = 0;
+  blockspam = 0;
   enew(); eout("Session RSET\n"); eflush();
   out("250 flushed\r\n");
 }
@@ -345,6 +388,7 @@
   }
   else
     if (!addrallowed()) { err_nogateway(); return; }
+  if ((!blockspam) && (!nsrcheck())) { blockspam = 1; }
   if (!stralloc_cats(&rcptto,"T")) die_nomem();
   if (!stralloc_cats(&rcptto,addr.s)) die_nomem();
   if (!stralloc_0(&rcptto)) die_nomem();
@@ -471,6 +515,16 @@
   seenmail = 0;
   if (databytes) bytestooverflow = databytes + 1;
   messagebytes = 0;
+  if (spamrefuse)
+  {
+    if (!blockspam) { env_unset("SPAMREFUSE"); }
+    else /* Re-enable SPAMREFUSE if we removed it for a previous message (persistent SMTP session) */
+    {
+      if (!env_get("SPAMREFUSE"))
+        if (!env_put2("SPAMREFUSE",spamrefuse)) die_nomem();
+    }
+  }
+  blockspam = 0; /* Reset for any subsequent message in a persistent SMTP session */
   if (qmail_open(&qqt) == -1) { err_qqt(); return; }
   qp = qmail_qp(&qqt);
   out("354 go ahead\r\n");
