This patch changes qmail-smtpd so that it parses incoming emails.  It
looks at the first line of MIME attachments to see if they're Windows
executables which are base64-encoded.  This catches nearly all current
Microsoft viruses.

Apply this patch like so:
    cd /usr/local/src/qmail-1.03
    wget http://qmail.org/qmail-smtpd-viruscan-1.1.patch
    patch <qmail-smtpd-viruscan-1.1.patch

Charles Cazabon wanted to be able to be able to enable/disable
it based on IP, etc, so he made version 1.11.  It disables the check
if the environment variable EXECUTABLEOK is set.  Also, he added
nine additional signatures, as he found them all in his DOS/Windows
executables.


diff -urN qmail-1.03.orig/Makefile qmail-1.03-condrejectexecutable/Makefile
--- qmail-1.03.orig/Makefile	Mon Jun 15 04:53:16 1998
+++ qmail-1.03-condrejectexecutable/Makefile	Tue Jul  8 09:46:47 2003
@@ -217,9 +217,9 @@
 
 case.a: \
 makelib case_diffb.o case_diffs.o case_lowerb.o case_lowers.o \
-case_starts.o
+case_starts.o case_startb.o
 	./makelib case.a case_diffb.o case_diffs.o case_lowerb.o \
-	case_lowers.o case_starts.o
+	case_lowers.o case_starts.o case_startb.o
 
 case_diffb.o: \
 compile case_diffb.c case.h
@@ -237,6 +237,10 @@
 compile case_lowers.c case.h
 	./compile case_lowers.c
 
+case_startb.o: \
+compile case_startb.c case.h
+	./compile case_startb.c
+
 case_starts.o: \
 compile case_starts.c case.h
 	./compile case_starts.c
diff -urN qmail-1.03.orig/case_startb.c qmail-1.03-condrejectexecutable/case_startb.c
--- qmail-1.03.orig/case_startb.c	Wed Dec 31 18:00:00 1969
+++ qmail-1.03-condrejectexecutable/case_startb.c	Tue Jul  8 09:46:47 2003
@@ -0,0 +1,21 @@
+#include "case.h"
+
+int case_startb(s,len,t)
+register char *s;
+unsigned int len;
+register char *t;
+{
+  register unsigned char x;
+  register unsigned char y;
+
+  for (;;) {
+    y = *t++ - 'A';
+    if (y <= 'Z' - 'A') y += 'a'; else y += 'A';
+    if (!y) return 1;
+    if (!len) return 0;
+    --len;
+    x = *s++ - 'A';
+    if (x <= 'Z' - 'A') x += 'a'; else x += 'A';
+    if (x != y) return 0;
+  }
+}
diff -urN qmail-1.03.orig/qmail-smtpd.c qmail-1.03-condrejectexecutable/qmail-smtpd.c
--- qmail-1.03.orig/qmail-smtpd.c	Mon Jun 15 04:53:16 1998
+++ qmail-1.03-condrejectexecutable/qmail-smtpd.c	Tue Jul  8 11:21:06 2003
@@ -91,6 +91,7 @@
   fakehelo = case_diffs(remotehost,helohost.s) ? helohost.s : 0;
 }
 
+int checkexecutable = 1;
 int liphostok = 0;
 stralloc liphost = {0};
 int bmfok = 0;
@@ -131,6 +132,7 @@
   if (!remotehost) remotehost = "unknown";
   remoteinfo = env_get("TCPREMOTEINFO");
   relayclient = env_get("RELAYCLIENT");
+  if (env_get("EXECUTABLEOK")) checkexecutable = 0;
   dohelo(remotehost);
 }
 
@@ -281,9 +283,111 @@
 struct qmail qqt;
 unsigned int bytestooverflow = 0;
 
+int putinheader;
+int linespastheader;       /* =0 after boundary is found in body, */
+                                /* until blank line */
+char linetype;
+int flagexecutable;
+
+stralloc line = {0};
+stralloc content = {0};
+stralloc boundary = {0};
+
 void put(ch)
 char *ch;
 {
+  char *cp, *cpstart, *cpafter;
+  unsigned int len;
+
+  if (checkexecutable) {
+    if (line.len < 1024)
+      if (!stralloc_catb(&line,ch,1)) die_nomem();
+
+    if (*ch == '\n') {
+      if (putinheader) {
+        /*substdio_put(&ssout,line.s,line.len);*/
+        if (line.len == 1) {
+          putinheader = 0;
+          if (content.len) {                      /* MIME header */
+            cp = content.s;
+            len = content.len;
+            while (len && (*cp == ' ' || *cp == '\t')) { ++cp; --len; }
+            cpstart = cp;
+            if (len && *cp == '"') {                      /* might be commented */
+              ++cp; --len; cpstart = cp;
+              while (len && *cp != '"') { ++cp; --len; }
+            } else {
+              while (len && *cp != ' ' && *cp != '\t' && *cp != ';') {
+                ++cp; --len;
+              }
+            }
+
+            cpafter = content.s+content.len;
+            while((cp += byte_chr(cp,cpafter-cp,';')) != cpafter) {
+              ++cp;
+              while (cp < cpafter && (*cp == ' ' || *cp == '\t')) ++cp;
+              if (case_startb(cp,cpafter - cp,"boundary=")) {
+                cp += 9;                  /* after boundary= */
+                if (cp < cpafter && *cp == '"') {
+                  ++cp;
+                  cpstart = cp;
+                  while (cp < cpafter && *cp != '"') ++cp;
+                } else {
+                  cpstart = cp;
+                  while (cp < cpafter &&
+                     *cp != ';' && *cp != ' ' && *cp != '\t') ++cp;
+                }
+                if (!stralloc_copys(&boundary,"--")) die_nomem();
+                if (!stralloc_catb(&boundary,cpstart,cp-cpstart))
+                        die_nomem();
+                break;
+              }
+            }
+          }
+        } else {
+          if ((*line.s == ' ' || *line.s == '\t')) {
+            switch(linetype) {
+              case 'C': if (!stralloc_catb(&content,line.s,line.len-1)) die_nomem(); break;
+              default: break;
+            }
+          } else {
+            if (case_startb(line.s,line.len,"content-type:")) {
+              if (!stralloc_copyb(&content,line.s+13,line.len-14)) die_nomem();
+              linetype = 'C';
+            } else {
+              linetype = ' ';
+            }
+          }
+        }
+      } else {
+        if (boundary.len && *line.s == '-' && line.len > boundary.len &&
+            !str_diffn(line.s,boundary.s,boundary.len)) {
+            linespastheader = 0;
+        } else if (linespastheader == 0 && line.len == 1) {
+          linespastheader = 1;
+        } else if (linespastheader == 1) {
+          if (line.len >= 9)
+            if (!str_diffn(line.s,"TVqQAAMAA",9) ||
+                !str_diffn(line.s,"TVpQAAIAA",9) ||
+                !str_diffn(line.s,"TVpAALQAc",9) ||
+                !str_diffn(line.s,"TVpyAXkAX",9) ||
+                !str_diffn(line.s,"TVrmAU4AA",9) ||
+                !str_diffn(line.s,"TVrhARwAk",9) ||
+                !str_diffn(line.s,"TVoFAQUAA",9) ||
+                !str_diffn(line.s,"TVoAAAQAA",9) ||
+                !str_diffn(line.s,"TVoIARMAA",9) ||
+                !str_diffn(line.s,"TVouARsAA",9) ||
+                !str_diffn(line.s,"TVrQAT8AA",9)) {
+              flagexecutable = 1;
+              qmail_fail(&qqt);
+          }
+          linespastheader = 2;
+        }
+      }
+      line.len = 0;
+    }
+  }
+
   if (bytestooverflow)
     if (!--bytestooverflow)
       qmail_fail(&qqt);
@@ -374,6 +478,12 @@
   if (!rcptto.len) { err_wantrcpt(); return; }
   seenmail = 0;
   if (databytes) bytestooverflow = databytes + 1;
+  boundary.len = 0;
+  content.len = 0;
+  putinheader = 1;
+  linespastheader = -1;
+  flagexecutable = 0;
+  linetype = ' ';
   if (qmail_open(&qqt) == -1) { err_qqt(); return; }
   qp = qmail_qp(&qqt);
   out("354 go ahead\r\n");
@@ -389,6 +499,7 @@
   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 (flagexecutable) { out("552 we don't accept email with executable content (#5.3.4)\r\n"); return; }
   if (*qqx == 'D') out("554 "); else out("451 ");
   out(qqx + 1);
   out("\r\n");
