diff -ruN qmail-1.03-spamcontrol-2.6.20/control.c qmail-1.03-spamcontrol-2.6.20-taps-greylisting/control.c
--- qmail-1.03-spamcontrol-2.6.20/control.c	2010-05-03 18:26:45.000000000 +0200
+++ qmail-1.03-spamcontrol-2.6.20-taps-greylisting/control.c	2010-05-03 18:27:54.000000000 +0200
@@ -128,3 +128,10 @@
  close(fd);
  return -1;
 }
+
+stralloc *get_me()
+{
+  if (meok)
+    return &me;
+  return 0;
+}
diff -ruN qmail-1.03-spamcontrol-2.6.20/control.h qmail-1.03-spamcontrol-2.6.20-taps-greylisting/control.h
--- qmail-1.03-spamcontrol-2.6.20/control.h	1998-06-15 12:53:16.000000000 +0200
+++ qmail-1.03-spamcontrol-2.6.20-taps-greylisting/control.h	2010-05-03 18:27:54.000000000 +0200
@@ -1,10 +1,14 @@
 #ifndef CONTROL_H
 #define CONTROL_H
 
+#include "stralloc.h"
+
 extern int control_init();
 extern int control_readline();
 extern int control_rldef();
 extern int control_readint();
 extern int control_readfile();
 
+extern stralloc *get_me();
+
 #endif
diff -ruN qmail-1.03-spamcontrol-2.6.20/customerror.h qmail-1.03-spamcontrol-2.6.20-taps-greylisting/customerror.h
--- qmail-1.03-spamcontrol-2.6.20/customerror.h	1970-01-01 01:00:00.000000000 +0100
+++ qmail-1.03-spamcontrol-2.6.20-taps-greylisting/customerror.h	2010-05-03 18:27:54.000000000 +0200
@@ -0,0 +1 @@
+#define FDCUSTOMERR 4
diff -ruN qmail-1.03-spamcontrol-2.6.20/dbdef.sql qmail-1.03-spamcontrol-2.6.20-taps-greylisting/dbdef.sql
--- qmail-1.03-spamcontrol-2.6.20/dbdef.sql	1970-01-01 01:00:00.000000000 +0100
+++ qmail-1.03-spamcontrol-2.6.20-taps-greylisting/dbdef.sql	2010-05-03 18:27:54.000000000 +0200
@@ -0,0 +1,73 @@
+
+#######################################################################
+# Database definitions
+#######################################################################
+
+# Using Mysql 3.23.2 or later (required for NULLs allowed in indexed fields
+
+#CREATE DATABASE relaydelay;
+#grant select,insert,update,delete tables on relaydelay.* to milter@"localhost" identified by 'milter';
+
+USE relaydelay;
+
+# NOTE: We allow nulls in the 3 "triplet" fields for manual white/black listing.  A null indicates that that field 
+#   should not be considered when looking for a match.  Automatic entries will always have all three populated.
+# Note: Since we index the triplet fields, and allow them to be null, this requires at least Mysql 3.23.2 and MYISAM
+#   tables.
+create table relaytofrom     # Stores settings for each [relay, to, from] triplet
+(
+        id              bigint          NOT NULL        auto_increment, # unique triplet id
+        relay_ip        char(16),                                       # sending relay in IPV4 ascii dotted quad notation
+        mail_from       varchar(255),                                   # ascii address of sender
+        rcpt_to         varchar(255),                                   # the recipient address.
+        block_expires   datetime        NOT NULL,                       # the time that an initial block will/did expire
+        record_expires  datetime        NOT NULL,                       # the date after which this record is ignored
+        
+        blocked_count   bigint          default 0 NOT NULL,             # num of blocked attempts to deliver
+        passed_count    bigint          default 0 NOT NULL,             # num of passed attempts we have allowed
+        aborted_count   bigint          default 0 NOT NULL,             # num of attempts we have passed, but were later aborted
+        origin_type     enum("MANUAL","AUTO") NOT NULL,                 # indicates the origin of this record (auto, or manual)
+        create_time     datetime        NOT NULL,                       # timestamp of creation time of this record
+        last_update     timestamp       NOT NULL,                       # timestamp of last change to this record (automatic)
+
+        primary key(id),
+        key(relay_ip),
+        key(mail_from(20)),                                             # To keep the index size down, only index first 20 chars
+        key(rcpt_to(20))
+);
+
+create table dns_name        # Stores the reverse dns name lookup for records
+(
+       relay_ip      varchar(18)       NOT NULL,
+       relay_name    varchar(255)      NOT NULL,                       # dns name, stored in reversed character order (for index)
+       last_update   timestamp         NOT NULL,                       # timestamp of last change to this record (automatic)
+       primary key(relay_ip),
+       key(relay_name(20))
+);
+
+# This table is not used yet, possibly never will be
+create table mail_log        # Stores a record for every mail delivery attempt
+(
+        id              bigint          NOT NULL        auto_increment, # unique log entry id
+        relay_ip        varchar(16)     NOT NULL,                       # sending relay in IPV4 ascii dotted quad notation
+        relay_name      varchar(255),                                   # sending relay dns name
+        dns_mismatch    bool            NOT NULL,                       # true if does not match, false if matches or no dns
+        mail_from       varchar(255)    NOT NULL,                       # the mail from: address
+        rcpt_to         varchar(255)    NOT NULL,                       # the rcpt to: address
+        rcpt_host       varchar(80)     NOT NULL,                       # the id (hostname) of the host that generated this row
+        create_time     datetime        NOT NULL,                       # timestamp of inserted time, since no updates
+
+        primary key(id),
+        key(relay_ip),
+        key(mail_from(20)),
+        key(rcpt_to(20))
+);
+
+# Example wildcard whitelists for subnets
+insert into relaytofrom values (0,"127.0.0.1"   ,NULL,NULL,"0000-00-00 00:00:00","9999-12-31 23:59:59",0,0,0,"MANUAL",NOW(),NOW());
+insert into relaytofrom values (0,"192.168"     ,NULL,NULL,"0000-00-00 00:00:00","9999-12-31 23:59:59",0,0,0,"MANUAL",NOW(),NOW());
+
+# Example wildcard whitelist entry for a recieved domain or subdomain
+insert into relaytofrom values (0,NULL,NULL,"sub.domain.com","0000-00-00 00:00:00","9999-12-31 23:59:59",0,0,0,"MANUAL",NOW(),NOW());
+
+
diff -ruN qmail-1.03-spamcontrol-2.6.20/error.h qmail-1.03-spamcontrol-2.6.20-taps-greylisting/error.h
--- qmail-1.03-spamcontrol-2.6.20/error.h	2010-05-03 18:26:47.000000000 +0200
+++ qmail-1.03-spamcontrol-2.6.20-taps-greylisting/error.h	2010-05-03 18:27:54.000000000 +0200
@@ -1,5 +1,6 @@
 #ifndef ERROR_H
 #define ERROR_H
+#include <errno.h>
 
 extern int errno;
 
diff -ruN qmail-1.03-spamcontrol-2.6.20/FILES qmail-1.03-spamcontrol-2.6.20-taps-greylisting/FILES
--- qmail-1.03-spamcontrol-2.6.20/FILES	1998-06-15 12:53:16.000000000 +0200
+++ qmail-1.03-spamcontrol-2.6.20-taps-greylisting/FILES	2010-05-03 18:27:54.000000000 +0200
@@ -431,3 +431,7 @@
 tcp-environ.5
 constmap.h
 constmap.c
+local_scan.c
+qmail-envelope-scanner.c
+dbdef.sql
+local_scan.h 
diff -ruN qmail-1.03-spamcontrol-2.6.20/hier.c qmail-1.03-spamcontrol-2.6.20-taps-greylisting/hier.c
--- qmail-1.03-spamcontrol-2.6.20/hier.c	2010-05-03 18:26:49.000000000 +0200
+++ qmail-1.03-spamcontrol-2.6.20-taps-greylisting/hier.c	2010-05-03 18:27:54.000000000 +0200
@@ -137,6 +137,7 @@
   c(auto_qmail,"bin","qmail-qmqpd",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","qmail-qmtpd",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","qmail-smtpd",auto_uido,auto_gidq,0755);
+  c(auto_qmail,"bin","qmail-envelope-scanner",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","sendmail",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","tcp-env",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","qreceipt",auto_uido,auto_gidq,0755);
diff -ruN qmail-1.03-spamcontrol-2.6.20/install-big.c qmail-1.03-spamcontrol-2.6.20-taps-greylisting/install-big.c
--- qmail-1.03-spamcontrol-2.6.20/install-big.c	2010-05-03 18:26:49.000000000 +0200
+++ qmail-1.03-spamcontrol-2.6.20-taps-greylisting/install-big.c	2010-05-03 18:27:54.000000000 +0200
@@ -131,6 +131,7 @@
   c(auto_qmail,"bin","qmail-qmqpd",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","qmail-qmtpd",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","qmail-smtpd",auto_uido,auto_gidq,0755);
+  c(auto_qmail,"bin","qmail-envelope-scanner",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","sendmail",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","tcp-env",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","qreceipt",auto_uido,auto_gidq,0755);
diff -ruN qmail-1.03-spamcontrol-2.6.20/ldap_pam.pl qmail-1.03-spamcontrol-2.6.20-taps-greylisting/ldap_pam.pl
--- qmail-1.03-spamcontrol-2.6.20/ldap_pam.pl	2009-02-11 10:47:58.000000000 +0100
+++ qmail-1.03-spamcontrol-2.6.20-taps-greylisting/ldap_pam.pl	2010-05-03 18:27:54.000000000 +0200
@@ -1,4 +1,4 @@
-#!/usr/local/bin/perl 
+#!/usr/bin/perl 
 #
 # checkpassword compatible LDAP pam for ADDRESSS (version 0.9.2)
 #
diff -ruN qmail-1.03-spamcontrol-2.6.20/local_scan.c qmail-1.03-spamcontrol-2.6.20-taps-greylisting/local_scan.c
--- qmail-1.03-spamcontrol-2.6.20/local_scan.c	1970-01-01 01:00:00.000000000 +0100
+++ qmail-1.03-spamcontrol-2.6.20-taps-greylisting/local_scan.c	2010-05-03 18:27:54.000000000 +0200
@@ -0,0 +1,300 @@
+/**************
+ * Copyright (c) mjd@digitaleveryware.com 2003 
+ * GPL Licensed see http://www.gnu.org/licenses/gpl.html
+ * Version 0.04: I don't even have it in cvs yet 
+ * ChangeHistory:
+ * Version 0.04: support for qmail
+ * Version 0.03: added whitelisting by incoming domain address
+ * Version 0.02: added relay_ip matching by /24 subnet
+ *************/
+
+#include "local_scan.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mysql/mysql.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define SQLCMDSIZE 1024
+
+char *mysqlhost, *mysqluser, *mysqlpass, *mysqldb;
+int block_expire, record_expire, record_expire_good, local_scan_debug;
+
+int mysql_query_wrapper(MYSQL *mysql, char *sqltext) 
+{
+  int result;
+
+  result = mysql_query(mysql,sqltext);
+  if(local_scan_debug) fprintf(stderr,"local_scan[%d]: SQL: ret=%d  |%s|\n",getppid(),result,sqltext);
+  return result;
+}
+
+void AddOrClause(char *subquery,char *in_ipaddr)
+{
+ int loop;
+ char *ptr;
+ char ipaddr[256];
+ 
+
+ strncpy(ipaddr, in_ipaddr, sizeof(ipaddr));
+ for (loop = 0; loop < 4; loop++) {
+   if (loop)
+     strcat(subquery, " OR ");
+   strcat(subquery , "relay_ip = '" );
+   strcat(subquery ,ipaddr);
+   strcat(subquery , "'");
+   ptr = strrchr(ipaddr,'.'); // strip off the last octet
+   if(ptr)
+     *ptr = '\0';
+ }
+}
+
+/* returns TRUE FALSE if record exists in database */
+int checkWhiteListIP( MYSQL *mysql,  int *action) 
+{
+  MYSQL_RES *myres; 
+  MYSQL_ROW myrow;
+
+  char sql[SQLCMDSIZE], sid[32];
+  int  white, black,exists = FALSE;
+
+  /* check for whitelisted sender ip address */
+  sprintf(sql, "SELECT id, block_expires > NOW(), block_expires < NOW() FROM relaytofrom WHERE record_expires > NOW()  AND mail_from IS NULL AND rcpt_to IS NULL AND (");
+  AddOrClause(sql, sender_host_address);
+  strcat(sql, ") ORDER BY length(relay_ip)");
+  if(local_scan_debug) 
+    fprintf(stderr,"local_scan[%d]: check whitelist: %s\n",getppid(),sql);
+  if(!mysql_query_wrapper(mysql,sql)) {
+    myres = mysql_store_result(mysql);
+    while ((myrow = mysql_fetch_row(myres)) != NULL) {
+      exists = TRUE;
+      strncpy(sid,myrow[0],sizeof(sid));
+      black = atoi(myrow[1]);
+      white = atoi(myrow[2]);
+    }
+    mysql_free_result(myres); // free memory used by result
+  }
+  if(exists && white) {
+    fprintf(stderr,"local_scan[%d]: %s -> xxx (%s) Whitelist IP Accept id = %s  \n",getppid(),sender_address, sender_host_address,sid);
+    *action = LOCAL_SCAN_ACCEPT;
+  }
+  else
+    if (exists && black) {
+      fprintf(stderr,"local_scan[%d]: %s -> xxx (%s) Blacklist IP Permanent Reject id = %s  \n",getppid(),sender_address, sender_host_address,sid);
+      *action = LOCAL_SCAN_REJECT;
+    }
+  return exists;
+}
+
+
+/* returns TRUE FALSE if record exists in database */
+int checkWhiteListDomain( MYSQL *mysql, int *action, int i) 
+{
+  MYSQL_RES *myres; 
+  MYSQL_ROW myrow;
+
+  char sql[SQLCMDSIZE], sid[32], *indomain;
+  int  white, black,exists = FALSE;
+
+  indomain = strrchr(recipients_list[i].address,'@');
+  if(indomain != NULL) {
+    indomain++; /* skip the '@' char */
+
+    /* check for whitelisted receiver domain address */
+    sprintf(sql, "SELECT id, block_expires > NOW(), block_expires < NOW() FROM relaytofrom WHERE record_expires > NOW()  AND mail_from IS NULL AND relay_ip IS NULL AND rcpt_to = '%s'",indomain);
+    if(!mysql_query_wrapper(mysql,sql)) {
+      myres = mysql_store_result(mysql);
+      while ((myrow = mysql_fetch_row(myres)) != NULL) {
+	exists = TRUE;
+	strncpy(sid,myrow[0],sizeof(sid));
+	black = atoi(myrow[1]);
+	white = atoi(myrow[2]);
+      }
+      mysql_free_result(myres); // free memory used by result
+    }
+    if (exists && white) {
+      fprintf(stderr,"local_scan[%d]: %s -> %s (%s) Whitelist Domain Accept id = %s  \n",getppid(),sender_address, recipients_list[i].address, sender_host_address,sid);
+      *action = LOCAL_SCAN_ACCEPT;
+    }
+    else if (exists && black) {
+      fprintf(stderr,"local_scan[%d]: %s -> xxx (%s) Blacklist Domain Permanent Reject id = %s  \n",getppid(),sender_address, sender_host_address,sid);
+      *action = LOCAL_SCAN_REJECT;
+    }
+  }
+  if (!exists) {
+    /* check for whitelisted receiver address */
+    sprintf(sql, "SELECT id, block_expires > NOW(), block_expires < NOW() FROM relaytofrom WHERE record_expires > NOW() AND mail_from IS NULL AND relay_ip IS NULL AND rcpt_to = '%s'", recipients_list[i].address);
+    if(!mysql_query_wrapper(mysql,sql)) {
+      myres = mysql_store_result(mysql);
+      while ((myrow = mysql_fetch_row(myres)) != NULL) {
+	exists = TRUE;
+	strncpy(sid,myrow[0],sizeof(sid));
+	black = atoi(myrow[1]);
+	white = atoi(myrow[2]);
+      }
+      mysql_free_result(myres); // free memory used by result
+    }
+    if (exists && white) {
+      fprintf(stderr,"local_scan[%d]: %s -> %s (%s) Whitelist Address Accept id = %s  \n",getppid(),sender_address, recipients_list[i].address, sender_host_address, sid);
+      *action = LOCAL_SCAN_ACCEPT;
+    }
+    else if (exists && black) {
+      fprintf(stderr,"local_scan[%d]: %s -> xxx (%s) Blacklist Address Permanent Reject id = %s  \n",getppid(),sender_address, sender_host_address,sid);
+      *action = LOCAL_SCAN_REJECT;
+    }
+  }
+  return exists;
+}
+
+void  buildLookupSql(char *sql, int recipIndex) 
+{
+
+  if (TRUE) {
+    /* this first sprintf does an exact match on ipaddr */
+    sprintf(sql,"SELECT id, NOW() > block_expires FROM relaytofrom WHERE record_expires > NOW() AND mail_from = '%s'  AND rcpt_to   = '%s' AND relay_ip  = '%s' order by block_expires desc",sender_address,  recipients_list[recipIndex].address,sender_host_address);
+  } else {
+    char hostip[32];
+    char *ptr;
+
+    /* this second version matches anything in the same /24 subnet */
+    sprintf(sql,"SELECT id, NOW() > block_expires FROM relaytofrom WHERE record_expires > NOW() AND mail_from = '%s'  AND rcpt_to   = '%s' AND relay_ip  like '",sender_address,  recipients_list[recipIndex].address);
+    strcpy(hostip,sender_host_address);
+    /* now remove the last octet and add a '%' */
+    ptr = strrchr(hostip,'.'); // strip off the last octet    
+    if(ptr)
+      *ptr = '\0';
+    strcat(sql,hostip);
+    strcat(sql, "%' order by block_expires desc");
+  }
+}
+
+
+int checkGreylist( MYSQL *mysql,  int *action, int i) 
+{
+ MYSQL_RES *myres; 
+ MYSQL_ROW myrow;
+
+ char sql[SQLCMDSIZE], sid[32];
+ int notexpired, exists = FALSE;
+
+ // lookup to see if record exists
+ buildLookupSql(sql,i);
+ if(!mysql_query_wrapper(mysql,sql)) {
+   myres = mysql_store_result(mysql);
+   while ((myrow = mysql_fetch_row(myres)) != NULL) {
+     exists = TRUE;
+     strncpy(sid,myrow[0],sizeof(sid));
+     notexpired = atoi(myrow[1]);
+     //fprintf(stderr,"local_scan[%d]: id = %s  expire = %d\n",getppid(),sid,notexpired);
+   }
+   mysql_free_result(myres); // free memory used by result
+   // if it exists and is not record expired and the block_expired passes
+   if (exists && notexpired) {
+     // update expire time to 36 days, and pass count
+     sprintf(sql,"update relaytofrom set record_expires = NOW() + INTERVAL %d DAY, passed_count = passed_count + 1 where id ='%s'",record_expire_good,sid);
+     mysql_query_wrapper(mysql, sql);
+     fprintf(stderr,"local_scan[%d]: %s -> %s (%s) Exists Accept id = %s  expire = %d\n",getppid(),sender_address,recipients_list[i].address, sender_host_address,sid,notexpired);
+     *action = LOCAL_SCAN_ACCEPT;
+   }
+   else if ( exists && ! notexpired) {
+     // update fail count
+     sprintf(sql,"update relaytofrom set blocked_count = blocked_count + 1 where id = %s",sid);
+     mysql_query_wrapper(mysql, sql);
+     fprintf(stderr,"local_scan[%d]: %s -> %s (%s) Exists Block id = %s  expire = %d\n",getppid(),sender_address,recipients_list[i].address, sender_host_address,sid,notexpired);
+     *action = LOCAL_SCAN_TEMPREJECT;
+   }
+   else /* doesn't exist */ {
+     // it doesn't exist make and entry
+     sprintf(sql,"insert into relaytofrom values (0,'%s','%s','%s',NOW() + INTERVAL %d MINUTE,NOW() + INTERVAL %d MINUTE,1,0,0,'AUTO',NOW(),NOW())",sender_host_address,sender_address,  recipients_list[i].address,block_expire,record_expire);
+     mysql_query_wrapper(mysql,sql);
+     fprintf(stderr,"local_scan[%d]: %s -> %s (%s) Doesn't Exist Block id = %d\n",getppid(),sender_address,recipients_list[i].address, sender_host_address, (int)mysql_insert_id(mysql));
+     *action = LOCAL_SCAN_TEMPREJECT;
+   }
+ }
+}
+
+void get_config(void) {
+
+	char *t;
+
+	mysqlhost = getenv("MYSQLHOST");
+	if (!mysqlhost) {
+		mysqlhost = malloc(strlen(DEFAULT_MYSQLHOST)+1);
+		strcpy(mysqlhost, DEFAULT_MYSQLHOST);
+	}
+	mysqluser = getenv("MYSQLUSER");
+	if (!mysqluser) {
+		mysqluser = malloc(strlen(DEFAULT_MYSQLUSER)+1);
+		strcpy(mysqluser, DEFAULT_MYSQLUSER);
+	}
+	mysqlpass = getenv("MYSQLPASS");
+	if (!mysqlpass) {
+		mysqlpass = malloc(strlen(DEFAULT_MYSQLPASS)+1);
+		strcpy(mysqlpass, DEFAULT_MYSQLPASS);
+	}
+	mysqldb = getenv("MYSQLDB");
+	if (!mysqldb) {
+		mysqldb = malloc(strlen(DEFAULT_MYSQLDB)+1);
+		strcpy(mysqldb, DEFAULT_MYSQLDB);
+	}
+	t = getenv("BLOCK_EXPIRE");
+	if (t) {
+		block_expire = atoi(t);
+	} else {
+		block_expire = DEFAULT_BLOCK_EXPIRE;
+	}
+	t = getenv("RECORD_EXPIRE");
+	if (t) {
+		record_expire = atoi(t);
+	} else {
+		record_expire = DEFAULT_RECORD_EXPIRE;
+	}
+	t = getenv("RECORD_EXPIRE_GOOD");
+	if (t) {
+		record_expire_good = atoi(t);
+	} else {
+		record_expire_good = DEFAULT_RECORD_EXPIRE_GOOD;
+	}
+	t = getenv("LOCAL_SCAN_DEBUG");
+	if (t) {
+		local_scan_debug = atoi(t);
+	} else {
+		local_scan_debug = DEFAULT_LOCAL_SCAN_DEBUG;
+	}
+}
+
+int local_scan(int fd, uschar **return_text)
+{
+ MYSQL *mysql = NULL;
+ int i,  ret = LOCAL_SCAN_ACCEPT;
+
+ get_config();
+
+ if(local_scan_debug)  fprintf(stderr,"local_scan[%d]: protocol = %s %s  \n",getppid(),received_protocol,sender_address);
+
+
+ fd = fd;                      /* Keep picky compilers happy */
+ return_text = return_text;                     /* Keep picky compilers happy */
+ mysql = mysql_init(NULL);
+ if (mysql && strcmp(received_protocol,"local") ) {
+   if (mysql_real_connect(mysql,mysqlhost,mysqluser,mysqlpass,mysqldb,0,NULL,0)) {
+     if ( !checkWhiteListIP( mysql, &ret )) {      /* check for whitelisted sender ip address */
+       for(i= 0 ; i <  recipients_count; i++ ) {
+	 if(strlen(sender_host_address) + strlen(sender_address) +
+	    strlen(recipients_list[i].address) < SQLCMDSIZE - 200) { /* check to avoid buffer overflows */
+	   if(!checkWhiteListDomain( mysql, &ret, i))
+	     checkGreylist(mysql, &ret,i);
+	 }
+       }
+     }
+   } else {
+     fprintf(stderr, "local_scan: unable to connect to mysql database %s@%s\n", mysqldb, mysqlhost);
+   }
+ }
+ if(mysql) mysql_close(mysql);
+ if(local_scan_debug) fprintf(stderr, "local_scan: finished with %d\n", ret);
+ return ret;
+}
+
+/* End of local_scan.c */
diff -ruN qmail-1.03-spamcontrol-2.6.20/local_scan.h qmail-1.03-spamcontrol-2.6.20-taps-greylisting/local_scan.h
--- qmail-1.03-spamcontrol-2.6.20/local_scan.h	1970-01-01 01:00:00.000000000 +0100
+++ qmail-1.03-spamcontrol-2.6.20-taps-greylisting/local_scan.h	2010-05-03 18:27:55.000000000 +0200
@@ -0,0 +1,46 @@
+/**************
+ * Copyright (c) mjd@digitaleveryware.com 2003 
+ * GPL Licensed see http://www.gnu.org/licenses/gpl.html
+ * Version 0.04: I don't even have it in cvs yet 
+ * ChangeHistory:
+ * Version 0.04: support for qmail
+ * Version 0.03: added whitelisting by incoming domain address
+ * Version 0.02: added relay_ip matching by /24 subnet
+ *************/
+
+/* Configuration **********************/
+
+#define DEFAULT_MYSQLHOST "localhost"
+#define DEFAULT_MYSQLUSER "milter"
+#define DEFAULT_MYSQLPASS "retlim"
+#define DEFAULT_MYSQLDB   "relaydelay"
+#define DEFAULT_BLOCK_EXPIRE  45   /* minutes until email is accepted */
+#define DEFAULT_RECORD_EXPIRE 500  /* minutes until record expires */
+#define DEFAULT_RECORD_EXPIRE_GOOD  72 /* days until record expires after accepting email */
+#define DEFAULT_LOCAL_SCAN_DEBUG   0 /* set to 1 to enable debugging */
+
+/* End of Configuration ***************/
+
+#define FALSE 0
+#define TRUE 1
+#define uschar char
+
+#define LOCAL_SCAN_ACCEPT  0
+#define LOCAL_SCAN_REJECT  100
+#define LOCAL_SCAN_TEMPREJECT  101
+#define SOMETHING_BAD      102
+
+struct recipients_list {
+  char *address;
+};
+
+
+int local_scan(int fd, uschar **return_text);
+
+/* globals used by exim */
+
+extern int recipients_count;
+extern char *received_protocol;
+extern char *sender_host_address;
+extern char *sender_address;
+extern struct recipients_list recipients_list[];
diff -ruN qmail-1.03-spamcontrol-2.6.20/Makefile qmail-1.03-spamcontrol-2.6.20-taps-greylisting/Makefile
--- qmail-1.03-spamcontrol-2.6.20/Makefile	2010-05-03 18:26:45.000000000 +0200
+++ qmail-1.03-spamcontrol-2.6.20-taps-greylisting/Makefile	2010-05-03 18:27:55.000000000 +0200
@@ -826,7 +826,7 @@
 forward preline condredirect bouncesaying except maildirmake \
 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
+binm3 binm3+df qmail-envelope-scanner
 
 load: \
 make-load warn-auto.sh systype
@@ -1540,13 +1540,13 @@
 	nroff -man qmail-qstat.8 > qmail-qstat.0
 
 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 \
-env.a wait.a \
+load qmail-queue.o triggerpull.o fmtqfn.o now.o date822fmt.o qregex.o \
+datetime.a seek.a case.a ndelay.a open.a sig.a getln.a stralloc.a alloc.a substdio.a error.a \
+env.a wait.a control.o constmap.o \
 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 \
-	env.a wait.a \
+	date822fmt.o qregex.o control.o datetime.a seek.a ndelay.a open.a sig.a \
+	env.a wait.a constmap.o case.a getln.a stralloc.a \
 	alloc.a substdio.a error.a str.a fs.a auto_qmail.o \
 	auto_split.o auto_uids.o 
 
@@ -1558,7 +1558,7 @@
 compile qmail-queue.c readwrite.h sig.h exit.h open.h seek.h fmt.h \
 env.h fork.h wait.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
+auto_qmail.h auto_uids.h date822fmt.h fmtqfn.h customerror.h
 	./compile qmail-queue.c
 
 qmail-remote: \
@@ -1646,7 +1646,7 @@
 	./load qmail-showctl auto_uids.o control.o open.a getln.a \
 	stralloc.a alloc.a substdio.a error.a str.a fs.a \
 	auto_qmail.o auto_break.o auto_patrn.o auto_spawn.o \
-	auto_split.o 
+	auto_split.o
 
 qmail-showctl.0: \
 qmail-showctl.8
@@ -1674,6 +1674,20 @@
 	alloc.a substdio.a error.a str.a fs.a auto_qmail.o base64.o  \
  	dns.o `cat socket.lib`  `cat dns.lib`
 
+qmail-envelope-scanner: \
+load qmail-envelope-scanner.o local_scan.o /usr/lib/libmysqlclient.a
+	./load qmail-envelope-scanner -lz -lm local_scan.o -lmysqlclient
+
+qmail-envelope-scanner.o: \
+compile qmail-envelope-scanner.c local_scan.h
+	./compile qmail-envelope-scanner.c
+
+local_scan.o: \
+compile local_scan.c local_scan.h
+	./compile local_scan.c
+
+
+
 qmail-smtpd.0: \
 qmail-smtpd.8
 	nroff -man qmail-smtpd.8 > qmail-smtpd.0
@@ -1764,7 +1778,7 @@
 
 qmail.o: \
 compile qmail.c substdio.h readwrite.h wait.h exit.h fork.h fd.h \
-qmail.h substdio.h auto_qmail.h
+qmail.h substdio.h auto_qmail.h customerror.h
 	./compile qmail.c
 
 qreceipt: \
@@ -1822,6 +1836,10 @@
 case.h mfrules.h stralloc.h
 	./compile mfrules.c
 
+qregex.o: \
+compile qregex.c qregex.h
+	./compile qregex.c
+
 readsubdir.o: \
 compile readsubdir.c readsubdir.h direntry.h fmt.h scan.h str.h \
 auto_split.h
@@ -1920,7 +1938,7 @@
 qmail-popup.c qmail-pw2u.c qmail-qmqpc.c qmail-qmqpd.c qmail-qmtpd.c \
 qmail-qread.c qmail-qstat.sh qmail-queue.c qmail-remote.c \
 qmail-rspawn.c qmail-send.c qmail-showctl.c qmail-smtpd.c \
-qmail-mfrules.c mfrules.c mfrules.h \
+qmail-mfrules.c mfrules.c mfrules.h qmail-envelope-scanner.c \
 qmail-start.c qmail-tcpok.c qmail-tcpto.c spawn.c dnscname.c dnsfq.c \
 dnsip.c dnsmxip.c dnsptr.c hostname.c ipmeprint.c tcp-env.c \
 sendmail.c qreceipt.c qsmhook.c qbiff.c forward.c preline.c predate.c \
diff -ruN qmail-1.03-spamcontrol-2.6.20/multiple-queues.ksh qmail-1.03-spamcontrol-2.6.20-taps-greylisting/multiple-queues.ksh
--- qmail-1.03-spamcontrol-2.6.20/multiple-queues.ksh	2007-08-17 23:04:29.000000000 +0200
+++ qmail-1.03-spamcontrol-2.6.20-taps-greylisting/multiple-queues.ksh	2010-05-03 18:27:55.000000000 +0200
@@ -1,4 +1,4 @@
-#!/usr/local/bin/ksh
+#!/usr/bin/ksh
 #set -o xtrace
 
 QMAIL_HOME=$(head -1 conf-qmail)
diff -ruN qmail-1.03-spamcontrol-2.6.20/qmail.7 qmail-1.03-spamcontrol-2.6.20-taps-greylisting/qmail.7
--- qmail-1.03-spamcontrol-2.6.20/qmail.7	2010-05-03 18:26:46.000000000 +0200
+++ qmail-1.03-spamcontrol-2.6.20-taps-greylisting/qmail.7	2010-05-03 18:27:55.000000000 +0200
@@ -60,7 +60,7 @@
 of
 .BR qmail .
 See
-.B http://pobox.com/~djb/qmail.html
+.B http://cr.yp.to/qmail.html
 for other
 .BR qmail -related
 software.
diff -ruN qmail-1.03-spamcontrol-2.6.20/qmail.c qmail-1.03-spamcontrol-2.6.20-taps-greylisting/qmail.c
--- qmail-1.03-spamcontrol-2.6.20/qmail.c	2010-05-03 18:26:49.000000000 +0200
+++ qmail-1.03-spamcontrol-2.6.20-taps-greylisting/qmail.c	2010-05-03 18:27:55.000000000 +0200
@@ -7,6 +7,7 @@
 #include "qmail.h"
 #include "auto_qmail.h"
 #include "env.h"
+#include "customerror.h"
 
 static char *binqqargs[2] = { 0, 0 } ;
 
@@ -23,22 +24,31 @@
 {
   int pim[2];
   int pie[2];
+  int pierr[2];
 
   setup_qqargs();
 
   if (pipe(pim) == -1) return -1;
   if (pipe(pie) == -1) { close(pim[0]); close(pim[1]); return -1; }
- 
+  if (pipe(pierr) == -1) {
+    close(pim[0]); close(pim[1]);
+    close(pierr[0]); close(pierr[1]);
+    return -1;
+  }
+
   switch(qq->pid = vfork()) {
     case -1:
+      close(pierr[0]); close(pierr[1]);
       close(pim[0]); close(pim[1]);
       close(pie[0]); close(pie[1]);
       return -1;
     case 0:
       close(pim[1]);
       close(pie[1]);
+      close(pierr[0]); /* we want to receive data */
       if (fd_move(0,pim[0]) == -1) _exit(120);
       if (fd_move(1,pie[0]) == -1) _exit(120);
+      if (fd_move(FDCUSTOMERR,pierr[1]) == -1) _exit(120);
       if (chdir(auto_qmail) == -1) _exit(61);
       execv(*binqqargs,binqqargs);
       _exit(120);
@@ -46,6 +56,7 @@
 
   qq->fdm = pim[1]; close(pim[0]);
   qq->fde = pie[1]; close(pie[0]);
+  qq->fderr = pierr[0]; close(pierr[1]);
   substdio_fdbuf(&qq->ss,write,qq->fdm,qq->buf,sizeof(qq->buf));
   qq->flagerr = 0;
   return 0;
@@ -93,10 +104,22 @@
 {
   int wstat;
   int exitcode;
+  int match;
+  char ch;
+  static char errstr[256];
+  int len = 0;
 
   qmail_put(qq,"",1);
   if (!qq->flagerr) if (substdio_flush(&qq->ss) == -1) qq->flagerr = 1;
   close(qq->fde);
+  substdio_fdbuf(&qq->ss,read,qq->fderr,qq->buf,sizeof(qq->buf));
+  while( substdio_bget(&qq->ss,&ch,1) && len < 255){
+    errstr[len]=ch;
+    len++;
+  }
+  if (len > 0) errstr[len]='\0'; /* add str-term */
+
+  close(qq->fderr);
 
   if (wait_pid(&wstat,qq->pid) != qq->pid)
     return "Zqq waitpid surprise (#4.3.0)";
@@ -131,8 +154,13 @@
     case 81: return "Zqq internal bug (#4.3.0)";
     case 120: return "Zunable to exec qq (#4.3.0)";
     default:
+      if (exitcode == 82)
+	if (len > 2)
+	  return errstr;
+	else
+	  return "Zerrstr too short";
       if ((exitcode >= 11) && (exitcode <= 40))
-	return "Dqq permanent problem (#5.3.0)";
+        return "Dqq permanent problem (#5.3.0)";
       return "Zqq temporary problem (#4.3.0)";
   }
 }
diff -ruN qmail-1.03-spamcontrol-2.6.20/qmail-control.9 qmail-1.03-spamcontrol-2.6.20-taps-greylisting/qmail-control.9
--- qmail-1.03-spamcontrol-2.6.20/qmail-control.9	2010-05-03 18:26:45.000000000 +0200
+++ qmail-1.03-spamcontrol-2.6.20-taps-greylisting/qmail-control.9	2010-05-03 18:27:55.000000000 +0200
@@ -70,6 +70,7 @@
 .I notipme	\fR(none)	\fRqmail-smtpd (c)
 .I percenthack	\fR(none)	\fRqmail-send
 .I plusdomain	\fIme	\fRqmail-inject
+.I postmaster	\fIpostmaster@me	\fRqmail-smtpd
 .I qmqpservers	\fR(none)	\fRqmail-qmqpc
 .I qmtproutes	\fR(none)	\fRqmail-remote
 .I queuelifetime	\fR604800	\fRqmail-send
@@ -77,6 +78,7 @@
 .I recipients	\fR(none)	\fRqmail-smtpd 
 .I smtpgreeting	\fIme	\fRqmail-smtpd
 .I smtproutes	\fR(none)	\fRqmail-remote
+.I taps	\fR(none)	\fRqmail-queue
 .I timeoutconnect	\fR60	\fRqmail-remote
 .I timeoutremote	\fR1200	\fRqmail-remote
 .I timeoutsmtpd	\fR1200	\fRqmail-smtpd
@@ -98,6 +100,7 @@
 .SH "SEE ALSO"
 qmail-inject(8),
 qmail-qmqpc(8),
+qmail-queue(8),
 qmail-remote(8),
 qmail-send(8),
 qmail-showctl(8),
diff -ruN qmail-1.03-spamcontrol-2.6.20/qmail-envelope-scanner.8 qmail-1.03-spamcontrol-2.6.20-taps-greylisting/qmail-envelope-scanner.8
--- qmail-1.03-spamcontrol-2.6.20/qmail-envelope-scanner.8	1970-01-01 01:00:00.000000000 +0100
+++ qmail-1.03-spamcontrol-2.6.20-taps-greylisting/qmail-envelope-scanner.8	2010-05-03 18:27:55.000000000 +0200
@@ -0,0 +1,70 @@
+.TH qmail-envelope-scanner 8
+.SH NAME
+qmail-envelope-scanner \- greylisting for sender IP, mail from
+and rcpt to.
+.SH SYNOPSIS
+.B qmail-envelope-scanner
+.I sender
+.I recipient
+.SH DESCRIPTION
+.B qmail-envelope-scanner
+gets MAIL FROM (sender) and RCPT TO (recipient) as arguments and
+reads
+.IR TCPREMOTEIP
+for the sender's IP address. It then connects to a MySQL database
+and does greylisting according to 
+.BR http://www.greylisting.org/ .
+
+.B qmail-envelope-scanner
+is invoked by
+.B qmail-smtpd(8)
+when
+.IR GREYLIST
+is set and the client is not a
+.IR RELAYCLIENT .
+
+.SH CONFIGURATION
+
+All settings can be configured at compile time in
+.BR local_scan.h .
+
+.SH "ENVIRONMENT VARIABLES READ"
+Environment variables override the compile time settings.
+
+Environment variables may be defined globally in the
+.B qmail-smtpd 
+startup script and/or individually as part of the
+.B tcpserver's
+cdb database.
+The environment variables may be quoted ("variable", or 'variable') and
+in case of global use, have to be exported.
+.TP 5
+.I MYSQLHOST
+Hostname of MySQL server.
+.TP 5
+.I MYSQLDB
+Database name on the MySQL server.
+.TP 5
+.I MYSQLUSER
+Username for connecting to the Database.
+.TP 5
+.I MYSQLPASS
+Password for the MySQL user.
+.TP 5
+.I BLOCK_EXPIRE
+minutes until email is accepted
+.TP 5
+.I RECORD_EXPIRE
+minutes until record expires
+.TP 5
+.I RECORD_EXPIRE_GOOD
+days until record expires after accepting email
+.TP 5
+.I LOCAL_SCAN_DEBUG
+if set to 1 enables debug output via stderr
+.SH "SEE ALSO"
+qmail-smtpd(8)
+.SH HISTORY
+Written by Robert Sander <gurubert@gurubert.de>
+The greylisting code is based on 
+.BR http://www.shupp.org/patches/greylisting-20060105.patch .
\ Kein Zeilenumbruch am Dateiende.
diff -ruN qmail-1.03-spamcontrol-2.6.20/qmail-envelope-scanner.c qmail-1.03-spamcontrol-2.6.20-taps-greylisting/qmail-envelope-scanner.c
--- qmail-1.03-spamcontrol-2.6.20/qmail-envelope-scanner.c	1970-01-01 01:00:00.000000000 +0100
+++ qmail-1.03-spamcontrol-2.6.20-taps-greylisting/qmail-envelope-scanner.c	2010-05-03 18:27:55.000000000 +0200
@@ -0,0 +1,33 @@
+/**************
+ * Copyright (c) mjd@digitaleveryware.com 2003 
+ * GPL Licensed see http://www.gnu.org/licenses/gpl.html
+ * Version 0.04: I don't even have it in cvs yet 
+ * ChangeHistory:
+ * Version 0.04: support for qmail
+ * Version 0.03: added whitelisting by incoming domain address
+ * Version 0.02: added relay_ip matching by /24 subnet
+ *************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "local_scan.h"
+
+
+char *received_protocol;
+char *sender_host_address;
+char *sender_address;
+struct recipients_list recipients_list[1];
+int recipients_count = 1;
+
+int main(int argv, char *argc[])
+{
+  received_protocol = "notneeded4qmail";
+  sender_host_address = getenv("TCPREMOTEIP");
+  recipients_list[0].address = argc[2];
+  sender_address = argc[1];
+
+  if (argv == 3 && sender_host_address != NULL)
+    return local_scan(1,NULL);
+  else
+    return SOMETHING_BAD;
+}
diff -ruN qmail-1.03-spamcontrol-2.6.20/qmail.h qmail-1.03-spamcontrol-2.6.20-taps-greylisting/qmail.h
--- qmail-1.03-spamcontrol-2.6.20/qmail.h	1998-06-15 12:53:16.000000000 +0200
+++ qmail-1.03-spamcontrol-2.6.20-taps-greylisting/qmail.h	2010-05-03 18:27:55.000000000 +0200
@@ -8,6 +8,7 @@
   unsigned long pid;
   int fdm;
   int fde;
+  int fderr;
   substdio ss;
   char buf[1024];
 } ;
diff -ruN qmail-1.03-spamcontrol-2.6.20/qmail-queue.8 qmail-1.03-spamcontrol-2.6.20-taps-greylisting/qmail-queue.8
--- qmail-1.03-spamcontrol-2.6.20/qmail-queue.8	2010-05-03 18:26:46.000000000 +0200
+++ qmail-1.03-spamcontrol-2.6.20-taps-greylisting/qmail-queue.8	2010-05-03 18:27:55.000000000 +0200
@@ -46,6 +46,12 @@
 will invoke the contents of
 .B QMAILQUEUE
 instead, if that environment variable is set.
+.SH "CONTROL FILES"
+.TP 5
+.I taps
+Should contain regex syntax of email addresses to tap and
+the associated email address to send the copy to. The two
+fields should be separated by a colon.
 .SH "FILESYSTEM RESTRICTIONS"
 .B qmail-queue
 imposes two constraints on the queue structure:
@@ -158,6 +164,15 @@
 .B 81
 Internal bug; e.g., segmentation fault.
 .TP
+.B 82
+Custom error (=bounce) messages. You have to write the error message to FDCUSTOMERR 
+from customerror.h (usually 4) and exit 82, in order to use the custom message.
+Format of the message: 
+
+Dthis is a custom fatal error message
+
+Zthis is a custom temporary failure message
+.TP
 .B 91
 Envelope format error.
 .SH "QHPSI ARGUMENTS"
diff -ruN qmail-1.03-spamcontrol-2.6.20/qmail-queue.c qmail-1.03-spamcontrol-2.6.20-taps-greylisting/qmail-queue.c
--- qmail-1.03-spamcontrol-2.6.20/qmail-queue.c	2010-05-03 18:26:50.000000000 +0200
+++ qmail-1.03-spamcontrol-2.6.20-taps-greylisting/qmail-queue.c	2010-05-03 18:27:55.000000000 +0200
@@ -19,6 +19,9 @@
 #include "env.h"
 #include "fork.h"
 #include "wait.h"
+#include "stralloc.h"
+#include "constmap.h"
+#include "customerror.h"
 
 /* #define BIGTODO */
 
@@ -30,6 +33,17 @@
 char outbuf[256];
 struct substdio ssout;
 
+char errbuf[256];
+substdio sserr = SUBSTDIO_FDBUF(write,FDCUSTOMERR,errbuf,sizeof(errbuf));
+
+int tapok = 0;
+stralloc tap = {0};
+struct constmap maptap;
+stralloc chkaddr = {0};
+int tapped;
+stralloc tapaddr = {0};
+stralloc controlfile = {0};
+
 datetime_sec starttime;
 struct datetime dt;
 unsigned long mypid;
@@ -60,7 +74,14 @@
 }
 
 void die(e) int e; { _exit(e); }
-void die_qhpsi() { cleanup(); die(71); }
+void die_qhpsi(s)
+char *s;
+{
+  cleanup();
+  if (substdio_putsflush(&sserr, s) == -1)
+    die(81);
+  die(82);
+}
 void die_write() { cleanup(); die(53); }
 void die_read() { cleanup(); die(54); }
 void sigalrm() { /* thou shalt not clean up here */ die(52); }
@@ -185,7 +206,7 @@
  if (*arg) {
    switch(child = fork()) {
      case -1:
-       die_qhpsi();
+       die_qhpsi("Zunable to fork qhpsi");
      case 0:
        qhpsiargs[0] = arg; 
        qhpsiargs[1] = messfn;
@@ -198,13 +219,13 @@
        x = env_get("QHPSIRC");
        if (x) { scan_ulong(x,&u); qhpsirc = (int) u; }
        execvp(*qhpsiargs,qhpsiargs);
-       die_qhpsi();
+       die_qhpsi("Zunable to exec qhpsi");
    }
-   if (wait_pid(&wstat,child) == -1) die_qhpsi();
-   if (wait_crashed(wstat)) die_qhpsi();
+   if (wait_pid(&wstat,child) == -1) die_qhpsi("Zqhpsi waitpid returned error");
+   if (wait_crashed(wstat)) die_qhpsi("Zqhspi child crashed");
    childrc = wait_exitcode(wstat); 
    if (childrc == qhpsirc) { cleanup(); die(32); }
-   else if (childrc != 0) die_qhpsi(); 
+   else if (childrc != 0) die_qhpsi("Zunknown exit code from qhpsi"); 
   }
 }
 
@@ -235,6 +256,13 @@
 
  alarm(DEATH);
 
+ stralloc_copys( &controlfile, auto_qmail);
+ stralloc_cats( &controlfile, "/control/taps");
+ stralloc_0( &controlfile);
+ tapok = control_readfile(&tap,controlfile.s,0);
+ if (tapok == -1) die(65);
+ if (!constmap_init(&maptap,tap.s,tap.len,0)) die(65);
+
  pidopen();
  if (fstat(messfd,&pidst) == -1) die(63);
 
@@ -284,14 +312,28 @@
  if (substdio_get(&ssin,&ch,1) < 1) die_read();
  if (ch != 'F') die(91);
  if (substdio_bput(&ssout,&ch,1) == -1) die_write();
+ stralloc_0(&chkaddr);
  for (len = 0;len < ADDR;++len)
   {
+   if ( len == 1 ) stralloc_copyb(&chkaddr, &ch,1);
+   else if ( len > 1 ) stralloc_catb(&chkaddr, &ch,1);
    if (substdio_get(&ssin,&ch,1) < 1) die_read();
    if (substdio_put(&ssout,&ch,1) == -1) die_write();
    if (!ch) break;
   }
  if (len >= ADDR) die(11);
 
+ /* check the from address */
+ stralloc_0(&chkaddr);
+ if (tapped == 0 && tapcheck()==1 ) {
+   tapped = 1;
+   if ( tapaddr.len > 0 ) {
+     if (substdio_bput(&ssout,"T",1) == -1) die_write();
+     if (substdio_bput(&ssout,tapaddr.s,tapaddr.len) == -1) die_write();
+     if (substdio_bput(&ssout,"",1) == -1) die_write();
+   }
+ }
+
  if (substdio_bput(&ssout,QUEUE_EXTRA,QUEUE_EXTRALEN) == -1) die_write();
 
  for (;;)
@@ -303,10 +345,24 @@
    if (substdio_bput(&ssout,&ch,1) == -1) die_write();
    for (len = 0;len < ADDR;++len)
     {
+     if ( len == 1 ) stralloc_copyb(&chkaddr, &ch,1);
+     else if ( len > 1 ) stralloc_catb(&chkaddr, &ch,1);
      if (substdio_get(&ssin,&ch,1) < 1) die_read();
      if (substdio_bput(&ssout,&ch,1) == -1) die_write();
      if (!ch) break;
     }
+
+    /* check the to address */
+    stralloc_0(&chkaddr);
+    if (tapped == 0 && tapcheck()==1 ) {
+      tapped = 1;
+      if ( tapaddr.len > 0 ) {
+        if (substdio_bput(&ssout,"T",1) == -1) die_write();
+        if (substdio_bput(&ssout,tapaddr.s,tapaddr.len) == -1) die_write();
+        if (substdio_bput(&ssout,"",1) == -1) die_write();
+       }
+     }
+
    if (len >= ADDR) die(11);
   }
 
@@ -320,3 +376,42 @@
  triggerpull();
  die(0);
 }
+
+int tapcheck()
+{
+  int i = 0;
+  int j = 0;
+  int x = 0;
+  int negate = 0;
+  stralloc curregex = {0};
+  char tmpbuf[200];
+
+  while (j < tap.len) {
+    i = j;
+    while ((tap.s[i] != ':') && (i < tap.len)) i++;
+    if (tap.s[j] == '!') {
+      negate = 1;
+      j++;
+    }
+    stralloc_copys(&tapaddr, &tap.s[i+1]);
+
+    stralloc_copyb(&curregex,tap.s + j,(i - j));
+    stralloc_0(&curregex);
+    x = matchregex(chkaddr.s, curregex.s, tmpbuf);
+
+    while ((tap.s[i] != '\0') && (i < tap.len)) i++;
+  
+    if ((negate) && (x == 0)) {
+      return 1;
+    }
+    if (!(negate) && (x > 0)) {
+      return 1;
+    }
+    j = i + 1;
+    negate = 0;
+
+
+  }
+  return 0;
+}
+
diff -ruN qmail-1.03-spamcontrol-2.6.20/qmail-recipients.c qmail-1.03-spamcontrol-2.6.20-taps-greylisting/qmail-recipients.c
--- qmail-1.03-spamcontrol-2.6.20/qmail-recipients.c	2010-05-03 18:26:56.000000000 +0200
+++ qmail-1.03-spamcontrol-2.6.20-taps-greylisting/qmail-recipients.c	2010-05-03 18:27:55.000000000 +0200
@@ -30,13 +30,18 @@
 stralloc key  = {0};
 int match;
 
-void main()
+void main(argc, argv)
+int argc;
+char** argv;
 {
   umask(033);
   if (chdir(auto_qmail) == -1)
     strerr_die4sys(111,FATAL,"unable to chdir to ",auto_qmail,": ");
 
-  fd = open_read("users/recipients");
+  if (argc == 2)
+    fd = open_read(argv[1]);
+  else
+    fd = open_read("users/recipients");
   if (fd == -1) die_read();
 
   substdio_fdbuf(&ssin,read,fd,inbuf,sizeof inbuf);
diff -ruN qmail-1.03-spamcontrol-2.6.20/qmail-showctl.c qmail-1.03-spamcontrol-2.6.20-taps-greylisting/qmail-showctl.c
--- qmail-1.03-spamcontrol-2.6.20/qmail-showctl.c	2010-05-03 18:26:48.000000000 +0200
+++ qmail-1.03-spamcontrol-2.6.20-taps-greylisting/qmail-showctl.c	2010-05-03 18:27:55.000000000 +0200
@@ -22,6 +22,9 @@
 stralloc me = {0};
 int meok;
 
+stralloc postmaster = {0};
+int postmasterok;
+
 stralloc line = {0};
 char num[FMT_ULONG];
 
@@ -295,6 +298,24 @@
 #endif
   do_lst("percenthack","The percent hack is not allowed.","The percent hack is allowed for user%host@",".");
   do_str("plusdomain",1,"plusdomain","Plus domain name is ");
+
+  postmasterok = control_readline(&postmaster,"postmaster");
+  if (!postmasterok)
+    if (meok) {
+      if (!stralloc_copys(&postmaster,"postmaster@")) _exit(111);
+      if (!stralloc_cat(&postmaster,&me)) _exit(111);
+      postmasterok = -2;
+    } else
+      postmasterok = -1;
+  if (postmasterok != -1) {
+    substdio_puts(subfdout,"\npostmaster: ");
+    if (postmasterok == -2)
+      substdio_puts(subfdout,"(Default.) ");
+    safeput(postmaster.s,postmaster.len);
+    substdio_puts(subfdout,".\n");
+  } else
+    do_str("postmaster",0,"Unable to determine postmaster address","");
+
   do_lst("qmqpservers","No QMQP servers.","QMQP server: ",".");
   do_int("queuelifetime","604800","Message lifetime in the queue is "," seconds");
 
@@ -366,6 +387,7 @@
     if (str_equal(d->d_name,"morercpthosts.cdb")) continue;
     if (str_equal(d->d_name,"percenthack")) continue;
     if (str_equal(d->d_name,"plusdomain")) continue;
+    if (str_equal(d->d_name,"postmaster")) continue;
     if (str_equal(d->d_name,"qmqpservers")) continue;
     if (str_equal(d->d_name,"queuelifetime")) continue;
     if (str_equal(d->d_name,"rcpthosts")) continue;
diff -ruN qmail-1.03-spamcontrol-2.6.20/qmail-smtpd.8 qmail-1.03-spamcontrol-2.6.20-taps-greylisting/qmail-smtpd.8
--- qmail-1.03-spamcontrol-2.6.20/qmail-smtpd.8	2010-05-03 18:26:45.000000000 +0200
+++ qmail-1.03-spamcontrol-2.6.20-taps-greylisting/qmail-smtpd.8	2010-05-03 18:27:55.000000000 +0200
@@ -85,7 +85,7 @@
 by converting CR LF into LF.
 Usually, it returns a temporary error and drops the connection on bare LFs;
 see
-.BR http://pobox.com/~djb/docs/smtplf.html .
+.BR http://cr.yp.to/docs/smtplf.html .
 
 .B qmail-smtpd
 accepts messages that contain long lines or non-ASCII characters,
@@ -359,6 +359,15 @@
 This has precedence over
 .IR moreipme .
 .TP 5
+.IR postmaster
+Used in SMTP replies when sender is grey- or blacklisted. Default:
+.IR postmaster@me ,
+if that is supplied;
+otherwise
+.B qmail-smtpd
+will refuse to run. Can be overridden by setting
+.IR POSTMASTER .
+.TP 5
 .I rcpthosts
 Allowed RCPT domains.
 If
@@ -441,7 +450,7 @@
    !*
 .EE
 
-Lagacy format:
+Legacy format:
 
 .EX
    users/recipients.cdb
@@ -547,6 +556,15 @@
 see control file
 .IR databytes .
 .TP 5
+.I GREYLIST
+enables the greylisting procedure if
+.IR RELAYCLIENT
+is not set, i.e. for unknown senders.
+.IR RELAYCLIENT
+will also be set if SMTP AUTH was successful. Other
+environment variables may be set to configure greylisting.
+See qmail-envelope-scanner(8) for more details.
+.TP 5
 .I HELOCHECK=''
 enables a check of the provided HELO/EHLO greeting against
 the content of the control file
@@ -599,6 +617,10 @@
 .IR MFDNSCHECK
 enable DNS MX lookup for the domain part of the "MAIL FROM:" envelope sender address.
 .TP 5
+.I POSTMASTER
+see control file
+.IR postmaster .
+.TP 5
 .I QHPSI
 is used by 
 .B qmail-smtpd
@@ -690,7 +712,7 @@
 
 .SH "ENVIRONMENT VARIABLES SET"
 By means of the following environment variables, 
-the SMTP session an be interrogated:
+the SMTP session can be interrogated:
 .TP 5
 .IR HELOHOST
 the HELO/EHLO greeting of the SMTP client.
@@ -726,7 +748,8 @@
 qmail-mfrules(8),
 qmail-queue(8),
 qmail-remote(8),
-qmail-send(8)
+qmail-send(8),
+qmail-envelope-scanner(8)
 
 .SH "HISTORY"
 The patch enabling the ESMTP AUTH and STARTTLS option is not part of the standard
diff -ruN qmail-1.03-spamcontrol-2.6.20/qmail-smtpd.c qmail-1.03-spamcontrol-2.6.20-taps-greylisting/qmail-smtpd.c
--- qmail-1.03-spamcontrol-2.6.20/qmail-smtpd.c	2010-05-03 18:26:48.000000000 +0200
+++ qmail-1.03-spamcontrol-2.6.20-taps-greylisting/qmail-smtpd.c	2010-05-03 19:12:19.000000000 +0200
@@ -18,6 +18,8 @@
 #include "env.h"
 #include "now.h"
 #include "exit.h"
+#include "fork.h"
+#include "wait.h"
 #include "rcpthosts.h"
 #include "recipients.h"
 #include "mfrules.h"
@@ -90,24 +92,28 @@
 void flush() { substdio_flush(&ssout); }
 void out(s) char *s; { substdio_puts(&ssout,s); }
 
+stralloc postmaster = {0};
+int postmasterok = 0;
+
 /* 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 die_recipients() { out("421 unable to check recipients (#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); }
+void logsg();
+void die_read() { logsg("error reading data from network"); _exit(1); }
+void die_alarm() { logsg("SMTP timeout"); out("451 timeout (#4.4.2)\r\n"); flush(); _exit(1); }
+void die_nomem() { logsg("out of memory"); out("421 out of memory (#4.3.0)\r\n"); flush(); _exit(1); }
+void die_control() { logsg("unable to read controls"); out("421 unable to read controls (#4.3.0)\r\n"); flush(); _exit(1); }
+void die_ipme() { logsg("unable to figure out my IP address"); out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); }
+void die_starttls() { logsg("TLS not available due to temporary reason"); out("454 TLS not available due to temporary reason (#5.7.3)\r\n"); flush(); _exit(1); }
+void die_recipients() { logsg("unable to check recipients"); out("421 unable to check recipients (#4.3.0)\r\n"); flush(); _exit(1); }
+void straynewline() { logsg("SMTP linefeed problem encountered"); out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); }
 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"); }
 void err_wantrcpt() { out("503 RCPT first (#5.5.1)\r\n"); }
-void err_notorious() { out("503 DATA command not accepted at this time (#5.5.1)\r\n"); }
+void err_notorious() { logsg("notorious"); out("503 DATA command not accepted at this time (#5.5.1)\r\n"); }
 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_qqt() { logsg("qqt failure"); out("451 qqt failure (#4.3.0)\r\n"); }
 void err_size() { out("552 sorry, that message size exceeds my databytes limit ");
   if (reply552siz) out (reply552siz); 
   out (" (#5.3.4)\r\n");
@@ -120,6 +126,9 @@
   return;
   }
 void err_bmf(s1,s2,s3,s4,s5,s6,s7,s8) char *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8; {
+  out("553-Your mail has been permanently blocked.\r\n553-If you feel this is an error,\r\n553-send an email to ");
+  substdio_put(&ssout,postmaster.s,postmaster.len);
+  out(".\r\n");
   out("553 sorry, your envelope sender is in my badmailfrom list ");
   if (reply553bmf) out(reply553bmf); 
   out(" (#5.7.1)\r\n");
@@ -127,6 +136,9 @@
   return;
   }
 void err_mav(s1,s2,s3,s4,s5,s6,s7) char *s1, *s2, *s3, *s4, *s5, *s6, *s7; {
+  out("553-Your mail has been permanently blocked.\r\n553-If you feel this is an error,\r\n553-send an email to ");
+  substdio_put(&ssout,postmaster.s,postmaster.len);
+  out(".\r\n");
   out("553 sorry, invalid sender address specified "); 
   if (reply553inv) out(reply553inv); 
   out(" (#5.7.1)\r\n");
@@ -141,6 +153,9 @@
   return;
   }
 void err_mfdns(s1,s2,s3,s4,s5,s6,s7) char *s1, *s2, *s3, *s4, *s5, *s6, *s7; {
+  out("553-Your mail has been permanently blocked.\r\n553-If you feel this is an error,\r\n553-send an email to ");
+  substdio_put(&ssout,postmaster.s,postmaster.len);
+  out(".\r\n");
   out("553 sorry, your envelope sender must exist ");
   if (reply553env) out(reply553env); 
   out(" (#5.7.1)\r\n");
@@ -148,11 +163,17 @@
   return;
   }
 void err_rcpts(s1,s2,s3,s4,s5,s6,s7) char *s1, *s2, *s3, *s4, *s5, *s6, *s7; {
+  out("452-Your mail has been temporarily blocked.\r\n452-If you feel this is an error,\r\n452-send an email to ");
+  substdio_put(&ssout,postmaster.s,postmaster.len);
+  out(".\r\n");
   out("452 sorry, too many recipients (#4.5.3)\r\n"); 	/* RFC 5321 */
   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-Your mail has been permanently blocked.\r\n550-If you feel this is an error,\r\n550-send an email to ");
+  substdio_put(&ssout,postmaster.s,postmaster.len);
+  out(".\r\n");
   out("550 sorry, your envelope recipient is in my badrcptto list ");
   if (reply550brt) out(reply550brt); 
   out(" (#5.7.1)\r\n");
@@ -160,6 +181,19 @@
   return;
   }
 void err_recipient(s1,s2,s3,s4,s5,s6,s7) char *s1, *s2, *s3, *s4, *s5, *s6, *s7; {
+  char *code;
+  if (env_get("RECIPIENTS450"))
+    code = "450";
+  else
+    code = "553";
+  out(code);
+  out("-Your mail has been permanently blocked.\r\n");
+  out(code);
+  out("-If you feel this is an error,\r\n");
+  out(code);
+  out("-send an email to ");
+  substdio_put(&ssout,postmaster.s,postmaster.len);
+  out(".\r\n");
   if (env_get("RECIPIENTS450"))
     out("450 sorry, mailbox currently unavailable (#4.2.1)\r\n"); 
   else {
@@ -170,6 +204,9 @@
   return;
   }
 void err_data(s1,s2,s3,s4,s5,s6,s7,s8) char *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8; {
+  out("554-Your mail has been permanently blocked.\r\n554-If you feel this is an error,\r\n554-send an email to ");
+  substdio_put(&ssout,postmaster.s,postmaster.len);
+  out(".\r\n");
   out("554 sorry, invalid message content "); 
   if (reply554cnt) out(reply554cnt); 
   out(" (#5.3.2)\r\n"); 
@@ -177,12 +214,12 @@
   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_child() { logsg("child error"); out("454 oops, problem with child and I can't auth (#4.3.0)\r\n"); return -1; }
+int err_fork() { logsg("fork error"); out("454 oops, child won't start and I can't auth (#4.3.0)\r\n"); return -1; }
+int err_pipe() { logsg("pipe error"); out("454 oops, unable to open pipe and I can't auth (#4.3.0)\r\n"); return -1; }
+int err_write() { logsg("write error"); out("454 oops, unable to write pipe and I can't auth (#4.3.0)\r\n"); return -1; }
+void err_authd() { logsg("authd error"); out("503 you're already authenticated (#5.5.0)\r\n"); }
+void err_authmail() { logsg("authmail error"); 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; }
@@ -190,10 +227,25 @@
   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); }
-void err_submission() { out("530 Authorization required (#5.7.1) \r\n"); }
+void err_submission() { logsg("Authorization required on submission port"); out("530 Authorization required (#5.7.1) \r\n"); }
 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); }
+void err_tempfail()
+{
+  logsg("greylisted");
+  out("421-Your mailserver has not yet been identified as legitimate sender.\r\n421-It has been temporarily blocked.\r\n421-If you feel this is an error,\r\n421-send an email to ");
+  substdio_put(&ssout,postmaster.s,postmaster.len);
+  out(".\r\n421 temporary envelope failure (#4.3.0)\r\n"); 
+}
+void err_permfail()
+{
+  logsg("blacklisted");
+  out("553-Your mailserver has been identified as sending SPAM.\r\n553-It has been permanently blocked.\r\n553-If you feel this is an error,\r\n553-send an email to ");
+  substdio_put(&ssout,postmaster.s,postmaster.len);
+  out(".\r\n553 sorry, permanent envelope failure (#5.7.1)\r\n");
+}
+void err_nullrcpt() { logsg("empty recipient"); out("553 unable to accept empty recipient address\r\n"); };
 
 /* this file is too long -------------------------------------- Greeting      */
 
@@ -206,7 +258,7 @@
 }
 void smtp_help()
 {
-  out("214 qmail home page: http://pobox.com/~djb/qmail.html\r\n");
+  out("214 qmail home page: http://cr.yp.to/qmail.html\r\n");
 }
 void smtp_quit()
 {
@@ -220,6 +272,7 @@
 char *localport;
 char *submission;
 char *relayclient;
+char *greylist;
 
 stralloc protocol = {0};
 stralloc helohost = {0};
@@ -361,6 +414,7 @@
   if (!remotehost) remotehost = "unknown";
   remoteinfo = env_get("TCPREMOTEINFO");
   relayclient = env_get("RELAYCLIENT");
+  greylist = env_get("GREYLIST");
 
   mfdnscheck = env_get("MFDNSCHECK");
   x = env_get("MAXRECIPIENTS");
@@ -438,6 +492,18 @@
     if (!stralloc_cats(&mailto," ")) die_nomem();
   }
 
+  postmasterok = control_rldef(&postmaster,"control/postmaster",0,env_get("POSTMASTER"));
+  if (postmasterok == 0) {
+    stralloc* me;
+    if (me = get_me()) {
+      if (!stralloc_copys(&postmaster,"postmaster@")) die_control();
+      if (!stralloc_cat(&postmaster,me)) die_control();
+      postmasterok = 1;
+    } else
+      die_control();
+  }
+  if (!postmasterok) die_control();
+
   if (!stralloc_copys(&helohost,"")) die_nomem();    /* helohost is empty */ 
   if (!stralloc_0(&helohost)) die_nomem();
   fakehelo = 0;
@@ -720,6 +786,11 @@
 stralloc fuser = {0};
 stralloc mfparms = {0};
 
+void logdg(s1,s8) char *s1, *s8;
+  { logd(s1,protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,s8); }
+void logsg(s1) char *s1;
+  { logdg(s1, ""); }
+
 int mailfrom_size(arg) char *arg;
 {
   long r;
@@ -779,6 +850,37 @@
 
 /* this file is too long ----------------------------------------- SMTP DIALOG */
 
+int envelope_scanner()
+{
+  int child;
+  int wstat;
+  char *envelope_scannerarg[] = { "bin/qmail-envelope-scanner", mailfrom.s, addr.s, 0 };
+
+  switch(child = vfork()) {
+    case -1:
+      return 1;
+    case 0:
+      execv(*envelope_scannerarg,envelope_scannerarg);
+      _exit(111);
+  }
+
+  wait_pid(&wstat,child);
+  if (wait_crashed(wstat)) {
+    return 1;
+  }
+
+  switch(wait_exitcode(wstat)) {
+    case 101:
+      err_tempfail();
+      return 0;
+    case 100:
+      err_permfail();
+      return 0;
+    default:
+      return 1;
+  }
+}
+
 void smtp_helo(arg) char *arg;
 {
 smtp_greet("250 "); out("\r\n");
@@ -879,9 +981,11 @@
   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 (!str_len(addr.s)) { err_nullrcpt(); return; }
   rcptcount++;
 
 /* this file is too long --------------------------------- Sesssion checks */
@@ -948,7 +1052,7 @@
     } 
   }					 		
 
-/* this file is too long --------------------------------- Local checks */
+/* this file is too long --------------------------------- Local checks, relayclient = true */
 
   else {
     if (flagmav < 0) { 
@@ -986,6 +1090,9 @@
     return;
   }
 
+/* this file is too long --------------------------------- Greylisting if relayclient false */
+  if (!relayclient && greylist && !envelope_scanner()) return;
+
 /* this file is too long --------------------------------- Checks done; mailfrom/recipient accepted */
 
   if (!stralloc_cats(&rcptto,"T")) die_nomem();
@@ -1231,7 +1338,7 @@
 
   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 (hops) { logsg("too many hops, this message is looping"); out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; }
   if (databytes) 
     if (!bytestooverflow) {
        err_size();
diff -ruN qmail-1.03-spamcontrol-2.6.20/qregex.c qmail-1.03-spamcontrol-2.6.20-taps-greylisting/qregex.c
--- qmail-1.03-spamcontrol-2.6.20/qregex.c	1970-01-01 01:00:00.000000000 +0100
+++ qmail-1.03-spamcontrol-2.6.20-taps-greylisting/qregex.c	2010-05-03 18:27:55.000000000 +0200
@@ -0,0 +1,57 @@
+/*
+ * qregex (v2)
+ * $Id: qregex.c,v 2.1 2001/12/28 07:05:21 evan Exp $
+ *
+ * Author  : Evan Borgstrom (evan at unixpimps dot org)
+ * Created : 2001/12/14 23:08:16
+ * Modified: $Date: 2001/12/28 07:05:21 $
+ * Revision: $Revision: 2.1 $
+ *
+ * Do POSIX regex matching on addresses for anti-relay / spam control.
+ * It logs to the maillog
+ * See the qregex-readme file included with this tarball.
+ * If you didn't get this file in a tarball please see the following URL:
+ *  http://www.unixpimps.org/software/qregex
+ *
+ * qregex.c is released under a BSD style copyright.
+ * See http://www.unixpimps.org/software/qregex/copyright.html
+ *
+ * Note: this revision follows the coding guidelines set forth by the rest of
+ *       the qmail code and that described at the following URL.
+ *       http://cr.yp.to/qmail/guarantee.html
+ * 
+ */
+
+#include <sys/types.h>
+#include <regex.h>
+#include "qregex.h"
+
+#define REGCOMP(X,Y)    regcomp(&X, Y, REG_EXTENDED|REG_ICASE)
+#define REGEXEC(X,Y)    regexec(&X, Y, (size_t)0, (regmatch_t *)0, (int)0)
+
+int matchregex(char *text, char *regex) {
+  regex_t qreg;
+  int retval = 0;
+
+
+  /* build the regex */
+  if ((retval = REGCOMP(qreg, regex)) != 0) {
+    regfree(&qreg);
+    return(-retval);
+  }
+
+  /* execute the regex */
+  if ((retval = REGEXEC(qreg, text)) != 0) {
+    /* did we just not match anything? */
+    if (retval == REG_NOMATCH) {
+      regfree(&qreg);
+      return(0);
+    }
+    regfree(&qreg);
+    return(-retval);
+  }
+
+  /* signal the match */
+  regfree(&qreg);
+  return(1);
+}
diff -ruN qmail-1.03-spamcontrol-2.6.20/qregex.h qmail-1.03-spamcontrol-2.6.20-taps-greylisting/qregex.h
--- qmail-1.03-spamcontrol-2.6.20/qregex.h	1970-01-01 01:00:00.000000000 +0100
+++ qmail-1.03-spamcontrol-2.6.20-taps-greylisting/qregex.h	2010-05-03 18:27:55.000000000 +0200
@@ -0,0 +1,5 @@
+/* simple header file for the matchregex prototype */
+#ifndef _QREGEX_H_
+#define _QREGEX_H_
+int matchregex(char *text, char *regex);
+#endif
diff -ruN qmail-1.03-spamcontrol-2.6.20/rcpthosts.c qmail-1.03-spamcontrol-2.6.20-taps-greylisting/rcpthosts.c
--- qmail-1.03-spamcontrol-2.6.20/rcpthosts.c	2010-05-03 18:26:51.000000000 +0200
+++ qmail-1.03-spamcontrol-2.6.20-taps-greylisting/rcpthosts.c	2010-05-03 18:27:55.000000000 +0200
@@ -15,10 +15,19 @@
 
 int rcpthosts_init()
 {
-  flagrh = control_readfile(&rh,"control/rcpthosts",0);
+  char *x;
+  x = env_get("RCPTHOSTS");
+  if (x)
+    flagrh = control_readfile(&rh,x,0);
+  else
+    flagrh = control_readfile(&rh,"control/rcpthosts",0);
   if (flagrh != 1) return flagrh;
   if (!constmap_init(&maprh,rh.s,rh.len,0)) return flagrh = -1;
-  fdmrh = open_read("control/morercpthosts.cdb");
+  x = env_get("MORERCPTHOSTS");
+  if (x)
+    fdmrh = open_read(x);
+  else
+    fdmrh = open_read("control/morercpthosts.cdb");
   if (fdmrh == -1) if (errno != error_noent) return flagmrh = -1;
   if (fdmrh > 0) flagmrh = 1;
   return 0;
@@ -48,7 +57,12 @@
       if (constmap(&maprh,buf + j,len - j)) return 1;
 
   if (flagmrh == 1) {
-    fdmrh = open_read("control/morercpthosts.cdb");
+    char *x;
+    x = env_get("MORERCPTHOSTS");
+    if (x)
+      fdmrh = open_read(x);
+    else
+      fdmrh = open_read("control/morercpthosts.cdb");
     if (fdmrh == -1) if (errno == error_noent) return 0;
     uint32 dlen;
     int r;
diff -ruN qmail-1.03-spamcontrol-2.6.20/README.tap qmail-1.03-spamcontrol-2.6.20-taps-greylisting/README.tap
--- qmail-1.03-spamcontrol-2.6.20/README.tap	1970-01-01 01:00:00.000000000 +0100
+++ qmail-1.03-spamcontrol-2.6.20-taps-greylisting/README.tap	2010-05-03 18:27:55.000000000 +0200
@@ -0,0 +1,28 @@
+qmail provides the ability to make a copy of each email that flows through the system.
+This is done using the QUEUE_EXTRA code. See qmail FAQ #8.2
+
+The qmail tap patch adds additional functionality:
+1) Specify which email addresses to tap using a regex style control file. With the
+   regex function, you can specify full domains or individual email addresses.
+
+2) Specify which email address to send the emails to.
+
+3) Qmail does not need to be restated to when the taps control file is changed.
+
+The regex match is applied to both the to and from email addresses. So email
+sent to or from the addresses will be copied. Matching is case insensitive.
+If there are multiple matches, the first match is used.
+
+The queue tap patch adds a new control file:
+
+/var/qmail/control/taps
+Contains a regex style list of addresses to tap and the email
+address of where you want the copy sent to.
+
+Examples:
+a) To tap a whole domain add a line like:
+.*@domain.com:joe@example.com
+
+
+b) To tap an individual email address add a line like:
+user@domain.com:other@example.com
diff -ruN qmail-1.03-spamcontrol-2.6.20/TARGETS qmail-1.03-spamcontrol-2.6.20-taps-greylisting/TARGETS
--- qmail-1.03-spamcontrol-2.6.20/TARGETS	2010-05-03 18:26:45.000000000 +0200
+++ qmail-1.03-spamcontrol-2.6.20-taps-greylisting/TARGETS	2010-05-03 18:30:00.000000000 +0200
@@ -152,6 +152,7 @@
 coe.o
 cdb.o
 cdb_hash.o
+cdb_make.o
 cdb_unpack.o
 cdb_seek.o
 cdb.a
@@ -267,14 +268,19 @@
 tcp-env
 qmail-badloadertypes.o
 qmail-badloadertypes
+qmail-badloadertypes.0
+qmail-badloadertypes.8
 qmail-badmimetypes.o
 qmail-badmimetypes
+qmail-badmimetypes.0
+qmail-badmimetypes.8
 qmail-newmrh.o
 qmail-newmrh
 qmail-recipients.o
 qmail-recipients
 qmail-mfrules.o
-qmail-mfules
+qmail-mfrules
+qmail-mfrules.0
 config
 config-fast
 dnscname.o
@@ -356,7 +362,6 @@
 qmail-recipients.8
 qmail-recipients.0
 qmail-mfrules.8
-qmail-mrfules.0
 qmail-newu.8
 qmail-newu.0
 qmail-pw2u.8
@@ -405,6 +410,10 @@
 man
 setup
 check
+qregex.o
+local_scan.o
+qmail-envelope-scanner.o
+qmail-envelope-scanner
 alloc.o
 alloc_re.o
 auto-gid.o

