diff -Naur proxsmtp-1.6-orig/common/smtppass.c proxsmtp-1.6/common/smtppass.c
--- proxsmtp-1.6-orig/common/smtppass.c	2008-02-06 09:57:48.000000000 +0100
+++ proxsmtp-1.6/common/smtppass.c	2008-02-06 10:00:23.000000000 +0100
@@ -45,6 +45,8 @@
 #include <sys/socket.h>
 #include <sys/param.h>
 #include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
 
 #include <ctype.h>
 #include <stdlib.h>
@@ -57,6 +59,8 @@
 #include <stdarg.h>
 #include <pwd.h>
 #include <time.h>
+#include <netdb.h>
+#include <string.h>
 
 #include "usuals.h"
 
@@ -64,6 +68,8 @@
 #include <linux/netfilter_ipv4.h>
 #endif
 
+#include <pa/loglayer.h>
+#include <pa/spaced.h>
 #include "compat.h"
 #include "sock_any.h"
 #include "stringx.h"
@@ -80,6 +86,22 @@
 }
 spthread_t;
 
+struct rbl_list{
+    char* name;                   /* The name of the rbl-list */
+    struct rbl_list *next;
+};
+
+struct domain_list {
+    char* name;
+    struct domain_list *next;
+};
+
+struct ip_system {
+    struct in_addr ip;
+    struct in_addr netmask;
+    struct ip_system *next;
+};
+
 /* -----------------------------------------------------------------------
  *  DATA
  */
@@ -95,13 +117,14 @@
 #define SMTP_NOTAUTH        "554 Insufficient authorization" CRLF
 #define SMTP_OK             "250 Ok" CRLF
 #define SMTP_REJPREFIX      "550 Content Rejected; "
+#define SMTP_TOOBIG         "500 Mail is to long" CRLF
 
 #define SMTP_DATA           "DATA" CRLF
 #define SMTP_NOOP           "NOOP" CRLF
 #define SMTP_XCLIENT        "XCLIENT ADDR=%s" CRLF
-#define SMTP_BANNER         "220 smtp.passthru" CRLF
-#define SMTP_HELO_RSP       "250 smtp.passthru" CRLF
-#define SMTP_EHLO_RSP       "250-smtp.passthru" CRLF
+#define SMTP_BANNER         "220 funkwerk UTM SMTP Proxy" CRLF
+#define SMTP_HELO_RSP       "250 funkwerk UTM SMTP Proxy" CRLF
+#define SMTP_EHLO_RSP       "250-funkwerk UTM SMTP Proxy" CRLF
 #define SMTP_FEAT_RSP       "250 XFILTERED" CRLF
 #define SMTP_DELIMS         "\r\n\t :"
 #define SMTP_MULTI_DELIMS   " -"
@@ -132,6 +155,7 @@
 #define START_RSP           "220"
 
 #define RCVD_HEADER         "Received:"
+#define RCVD_SUBJECT        "Subject:"
 
 /* The set of delimiters that can be present between config and value */
 #define CFG_DELIMS      	": \t"
@@ -146,8 +170,13 @@
  */
 #define MAX_DATE_LENGTH 	64
 
+#define EMAIL_TOOBIG        -2
+#define EMAIL_UNCHECKED     -3
 #define LINE_TOO_LONG(l)    ((l) >= (SP_LINE_LENGTH - 2))
 
+#define TYPE_PASS           "pass"
+#define TYPE_BLOCK          "block"
+
 /* -----------------------------------------------------------------------
  *  CONFIGURATION OPTIONS
  * 
@@ -168,6 +197,21 @@
 #define CFG_USER            "User"
 #define CFG_PIDFILE         "PidFile"
 #define CFG_XCLIENT         "XClient"
+#define CFG_MAXMESSAGESIZE            "MaxMessageSize"
+#define CFG_MAXMESSAGESIZEACTION      "MaxMessageSizeAction"
+#define CFG_RBLSERVER                 "RBLServer"
+#define CFG_BLACKLIST                 "BlackList"
+#define CFG_WHITELIST                 "WhiteList"
+#define CFG_DOMAINS                   "MailDomains"
+#define CFG_TIMEOUT                   "TimeOut"
+#define CFG_OUTADDR                   "OutAddress"
+#define CFG_LISTENADDR                "Listen"
+#define CFG_TRANSPARENT               "TransparentProxy"
+#define CFG_DIRECTORY                 "TempDirectory"
+#define CFG_KEEPALIVES                "KeepAlives"
+#define CFG_USER                      "User"
+#define CFG_PIDFILE                   "PidFile"
+#define CFG_XCLIENT                   "XClient"
 
 /* -----------------------------------------------------------------------
  *  DEFAULT SETTINGS
@@ -178,6 +222,7 @@
 #define DEFAULT_MAXTHREADS  64
 #define DEFAULT_TIMEOUT   180
 #define DEFAULT_KEEPALIVES 0
+#define DEFAULT_MESSAGESIZE  5*1024*1024
 
 /* -----------------------------------------------------------------------
  *  GLOBALS
@@ -187,6 +232,11 @@
 unsigned int g_unique_id = 0x00100000;      /* For connection ids */
 pthread_mutex_t g_mutex;                    /* The main mutex */
 pthread_mutexattr_t g_mtxattr;
+struct ip_system *ip_blacklist = NULL;
+struct ip_system *ip_whitelist = NULL;
+struct ip_system *ip_list_next = NULL;
+struct rbl_list *rbl_liste  = NULL;
+struct domain_list *domain_liste = NULL;
  
 /* -----------------------------------------------------------------------
  *  FORWARD DECLARATIONS
@@ -208,6 +258,8 @@
 
 /* Used externally in some cases */
 int sp_parse_option(const char* name, const char* option);
+int sp_pass_data_to_server(spctx_t* ctx, const char *line, int rc);
+int sp_pass_response_to_client(spctx_t* ctx);
 
 /* ----------------------------------------------------------------------------------
  *  BASIC RUN FUNCTIONALITY 
@@ -230,6 +282,14 @@
     g_state.keepalives = DEFAULT_KEEPALIVES;
     g_state.directory = _PATH_TMP;
     g_state.name = name;
+    g_state.max_message_size = DEFAULT_MESSAGESIZE;
+    g_state.max_message_size_pass = 0;
+    g_state.rbl_server = 0;
+    g_state.check_virus = 1;
+    g_state.pass_on_virus = 0;
+    g_state.check_spam = 1;
+    g_state.pass_on_spam = 0;
+    g_state.domains = 0;
     
     /* We need the default to parse into a useable form, so we do this: */
     r = sp_parse_option(CFG_LISTENADDR, DEFAULT_SOCKET);
@@ -279,9 +339,6 @@
 
     sp_messagex(NULL, LOG_DEBUG, "starting up (%s)...", VERSION);
 
-    /* Drop privileges before daemonizing */
-    drop_privileges();
-    
     /* When set to this we daemonize */
     if(g_state.debug_level == -1)
     {
@@ -329,6 +386,8 @@
         exit(1);
     }
     
+    /* privileges may have been necessary until the bind. Drop them now */
+    drop_privileges();
     sp_messagex(NULL, LOG_DEBUG, "created socket: %s", g_state.listenname);
     
     /* Let 5 connections queue up */
@@ -528,8 +587,18 @@
             /* Start a new thread if neccessary */
             if(fd != -1 && threads[i].tid == 0)
             {
+				pthread_attr_t	stack_size_attr, *p_attr;
+
+				p_attr = NULL;
+
+				if (pthread_attr_init(&stack_size_attr) == 0) {
+					if (pthread_attr_setstacksize(&stack_size_attr, 256 * 1024) == 0) {
+						p_attr = &stack_size_attr;
+					}
+				}
+
                 threads[i].fd = fd;
-                r = pthread_create(&(threads[i].tid), NULL, thread_main, 
+                r = pthread_create(&(threads[i].tid), p_attr, thread_main, 
                                    (void*)(threads + i));
                 if(r != 0)
                 {
@@ -588,9 +657,111 @@
     free(threads);
 }
 
+static int match(struct in_addr ip_p, struct in_addr ip_z, struct in_addr netmask)
+{
+    unsigned long mask;
+    mask = netmask.s_addr;
+    if(ip_z.s_addr == (ip_p.s_addr & mask))
+    {
+        return (1);
+    } else {
+        return (0);
+    }
+}
+
+static int buildNetmaskBits(struct in_addr netmask)
+{
+    int bits = 0;
+    unsigned long mask;
+    mask = ntohl(netmask.s_addr);
+    while (mask != 0 && bits < 32)
+    {
+        mask = (mask << 1);
+        bits++;
+    };
+    return bits;
+}
+
+/**
+ * Check address against blacklists, whitelists and rbls.
+ * @param addr address of peer
+ * @return 1 if address is considered a spammer, 0 if not.
+ */
+static int is_spammer(struct in_addr addr, spctx_t *ctx)
+{
+    struct ip_system *whitelist = ip_whitelist;
+    struct ip_system *blacklist = ip_blacklist;
+    struct rbl_list *rbl_server = rbl_liste;
+    char dns_host[512];
+    struct hostent hostbuf, *hostp;
+    char hbuf[1024];
+    int herr,hres;
+
+    sp_messagex(ctx, LOG_DEBUG, "Check System %s", ctx->client.peername);
+    if(whitelist != NULL)
+    {
+        sp_messagex(ctx, LOG_DEBUG, "Check if IP %s is whitelisted",
+          ctx->client.peername);
+    }
+    while(whitelist != NULL)
+    {
+        if(match(addr, whitelist->ip, whitelist->netmask))
+        {
+            sp_messagex(ctx, LOG_INFO, "yes, %s is whitelisted by %s/%d",
+              ctx->client.peername, inet_ntoa(whitelist->ip),
+              buildNetmaskBits((whitelist->netmask)));
+            return 0;
+        }
+        whitelist=whitelist->next;
+    }
+    if(blacklist != NULL)
+    {
+        sp_messagex(ctx, LOG_DEBUG, "Check if IP %s is blacklisted",
+          ctx->client.peername);
+    }
+    while(blacklist != NULL)
+    {
+        if(match(addr, blacklist->ip, blacklist->netmask))
+        {
+            sp_messagex(ctx, LOG_INFO, "yes, %s is blacklisted by %s/%d",
+              ctx->client.peername, inet_ntoa(blacklist->ip),
+              buildNetmaskBits((blacklist->netmask)));
+            return 1;
+        }
+        blacklist=blacklist->next;
+    }
+    if(rbl_server != NULL)
+    {
+        sp_messagex(ctx, LOG_INFO, "Checking if IP %s is rbl listed",
+          ctx->client.peername);
+    }
+    while(rbl_server != NULL)
+    {
+        snprintf(dns_host, sizeof(dns_host), "%u.%u.%u.%u.%s",
+          ((unsigned char *)&addr)[3], ((unsigned char *)&addr)[2],
+          ((unsigned char *)&addr)[1], ((unsigned char *)&addr)[0],
+          rbl_server->name);
+        sp_messagex(ctx, LOG_DEBUG, "Checking %s", dns_host);
+        hres = gethostbyname_r(dns_host, &hostbuf, hbuf, sizeof(hbuf),
+          &hostp, &herr);
+        if (hres == 0 && hostp != NULL)
+        {
+            sp_messagex(ctx, LOG_INFO, "%s is in RBL of %s",
+              ctx->client.peername, rbl_server->name);
+            return 1;
+        }
+        rbl_server=rbl_server->next;
+    }
+    sp_messagex(ctx, LOG_DEBUG, "System %s appears ok", ctx->client.peername);
+    return 0;
+}
+
+
 static spctx_t* init_thread(int fd)
 {
     spctx_t* ctx;
+    struct sockaddr_any peeraddr;
+    struct in_addr addr;
     
     ctx = cb_new_context();
     if(ctx)
@@ -611,9 +782,22 @@
             
         sp_messagex(ctx, LOG_DEBUG, "processing %d on thread %x", fd, (int)pthread_self());
         
-        /* Connect to the outgoing server ... */
-        if(make_connections(ctx, fd) == -1)
+        /* before connecting to the outgoing server check our blacklists */
+        spio_attach(ctx, &(ctx->client), fd, &peeraddr);
+        inet_aton(ctx->client.peername, &addr);
+        if (is_spammer(addr, ctx)) {
+            sp_messagex(ctx, LOG_INFO, "System %s appears to be a spammer",
+              ctx->client.peername);
+            pa_sendlogf(NULL, PA_LOG_INFO, PA_SUBSYS_SMTPPROXY,
+              "%s is declared as spammer. Not accepting Mail",
+              ctx->client.peername);
+            cb_del_context(ctx);
+            ctx = NULL;
+        }
+        else if(make_connections(ctx, fd) == -1)
         {
+            if(ctx->spacedaemon != NULL)
+                pasd_release(ctx->spacedaemon);
             cb_del_context(ctx);
             ctx = NULL;
         }
@@ -668,6 +852,8 @@
     spio_disconnect(ctx, &(ctx->server));
     
     /* Clean up file stuff */
+    /* Release Space from SpaceDaemon */
+    pasd_release(ctx->spacedaemon);
     cleanup_context(ctx);            
     cb_del_context(ctx);
 }
@@ -732,6 +918,7 @@
     const char* outname;
     
     ASSERT(client != -1);
+    ctx->spacedaemon = NULL;
 
     /* Setup the incoming connection. This also fills in peeraddr for us */
     spio_attach(ctx, &(ctx->client), client, &peeraddr);
@@ -799,6 +986,14 @@
             outname = "unknown";
     }
     
+    /* Acquire space from SpaceDaemon */
+    if(! (ctx->spacedaemon = pasd_request(pasd_client_type_smtp,
+      g_state.max_message_size)))
+    {
+        sp_messagex(ctx, LOG_INFO, "PA-SpaceDaemon returned: %s", pasd_errstr);
+        return(-1);
+    }
+
     /* Connect to the server */
     if(spio_connect(ctx, &(ctx->server), outaddr, outname) == -1)
         return -1;
@@ -824,6 +1019,9 @@
     /* XCLIENT is for use in access control */
     int xclient_sup = 0;    /* Is XCLIENT supported? */
     int xclient_sent = 0;   /* Have we sent an XCLIENT command? */
+
+    char *recdom;
+    struct domain_list *domain;
         
     ASSERT(spio_valid(&(ctx->client)) &&
            spio_valid(&(ctx->server)));
@@ -900,6 +1098,7 @@
 
                 /* Print the log out for this email */
                 sp_messagex(ctx, LOG_INFO, "%s", ctx->logline);
+                pa_sendlog(NULL, PA_LOG_INFO, PA_SUBSYS_SMTPPROXY, ctx->logline);
                     
                 /* Done with that email */
                 cleanup_context(ctx);
@@ -1108,6 +1307,33 @@
                             
                         strcat(ctx->recipients, t);
                     }
+                    /* Check if recipient in in one of our configured domains */
+                    /* Omit the check if no domains are configured or if we are */
+                    /* in transparent mode (which is outgoing only) */
+                    if(domain_liste != NULL && !g_state.transparent)
+                    {
+                        sp_messagex(ctx, LOG_DEBUG, "Checking if EMail %s is in local Domains", t);
+                        recdom = strchr(t, '@');
+                        if (recdom) recdom++;
+                        domain = domain_liste;
+                        while(domain && recdom)
+                        {
+                            sp_messagex(ctx, LOG_DEBUG, "Checking Domain %s ", domain->name);
+                            if (strcasecmp(recdom, domain->name) == 0) {
+                                sp_messagex(ctx, LOG_DEBUG, "Email %s is in Domain %s",
+                                  t, domain->name);
+                                break;
+                            }
+                            domain=domain->next;
+                        }
+                        if (!recdom || !domain) {
+                            sp_messagex(ctx, LOG_INFO, "Email for %s is not for local delivery", t);
+                            pa_sendlogf(NULL, PA_LOG_INFO, PA_SUBSYS_SMTPPROXY,
+                              "%s is not for local delivery. Not accepting Mail", t);
+                            sp_fail_data(ctx, "450 Requested mail action not taken: mailbox unavailable\n");
+                            RETURN(-1);
+                        }
+                    }
                 }
                             
                 /*
@@ -1363,12 +1589,34 @@
     return len;    
 }
 
+int sp_passthru_data(spctx_t* ctx)
+{
+    int r, count = 0;
+    const char* data;
+
+    sp_messagex(ctx, LOG_DEBUG, "sending directly to server");
+    while((r = sp_read_data(ctx, &data)) != 0)
+    {
+        if(r < 0)
+            return -1;
+        count += r;
+        if(sp_pass_data_to_server(ctx, data, r) < 0)
+            return -1;
+    }
+   /* End the data */
+   if(sp_pass_data_to_server(ctx, ".\r\n", 3) < 0)
+       return -1;
+   if(sp_pass_response_to_client(ctx) == -1)
+       return -1;
+   return count;
+}
+
 int sp_cache_data(spctx_t* ctx)
 {
     int r, count = 0;
     const char* data;
     
-    while((r = sp_read_data(ctx, &data)) != 0)
+    while(count < g_state.max_message_size && ((r = sp_read_data(ctx, &data)) != 0))
     {
         if(r < 0)
             return -1;  /* Message already printed */
@@ -1379,10 +1627,27 @@
             return -1;  /* Message already printed */
     }
     
+    if(count > g_state.max_message_size && g_state.max_message_size_pass) {
+        if(sp_write_data(ctx, NULL, 0) < 0)
+            return -1;
+        sp_messagex(ctx, LOG_DEBUG,
+          "Message is bigger than allowed and will be passed without scanning");
+        return EMAIL_UNCHECKED;
+    }
+
     /* End the caching */
     if(sp_write_data(ctx, NULL, 0) < 0)
         return -1;
         
+    if( count > g_state.max_message_size ) {
+        //Dump Additonal data
+        while((r = sp_read_data(ctx, &data)) != 0)
+        { }
+        sp_messagex(ctx, LOG_DEBUG,
+          "Message is bigger than allowed and will be blocked");
+        return EMAIL_TOOBIG;
+    }
+
     sp_messagex(ctx, LOG_DEBUG, "wrote %d bytes to cache", count);
     return count;   
 }
@@ -1544,11 +1809,35 @@
     return l >= MAX_HEADER_LENGTH ? MAX_HEADER_LENGTH - 1 : l; 
 }
 
-int sp_done_data(spctx_t* ctx, const char *headertmpl)
+int sp_pass_data_to_server(spctx_t* ctx, const char *line, int rc)
+{
+    int r = 0;
+    if((r = spio_write_data_raw(ctx, &(ctx->server),
+      (unsigned char*)line, rc)) == -1)
+        return(-1);
+
+    return r;
+}
+
+int sp_pass_response_to_client(spctx_t* ctx)
+{
+    sp_messagex(ctx, LOG_DEBUG, "sent email data");
+
+    /* Okay read the response from the server and echo it to the client */
+    if(read_server_response(ctx) == -1)
+        return (-1);
+
+    if(spio_write_data(ctx, &(ctx->client), ctx->server.line) == -1)
+        return (-1);
+
+    return(0);
+}
+
+int sp_done_data(spctx_t* ctx, const char *headertmpl, int finished)
 {
     FILE* file = 0;
     int ret = 0;
-    char *line;    
+    char *line, *subjectline, *tmp;
     char header[MAX_HEADER_LENGTH] = "";
     size_t header_len, line_len;
     int header_prepend = 0;
@@ -1563,6 +1852,8 @@
     line_len = SP_LINE_LENGTH;
     if((line = (char *)malloc(line_len)) == NULL)
         RETURN(-1);
+    if((subjectline = (char *)malloc(line_len)) == NULL)
+        RETURN(-1);
 
     /* Open the file */
     file = fopen(ctx->cachename, "r");
@@ -1595,8 +1886,7 @@
     if(headertmpl)
     {
         header_len = make_header(ctx, headertmpl, header);
-        if(is_first_word(RCVD_HEADER, header, KL(RCVD_HEADER)))
-            header_prepend = 1;
+        header_prepend = 1;
     }
 
 
@@ -1606,7 +1896,6 @@
         if(spio_write_data_raw(ctx, &(ctx->server), (unsigned char*)header, header_len) == -1 ||
            spio_write_data_raw(ctx, &(ctx->server), (unsigned char*)CRLF, KL(CRLF)) == -1)
             RETURN(-1);
-        header[0] = '\0';
     }
 
     /* Transfer actual file data */    
@@ -1629,11 +1918,27 @@
              */
             if(is_blank_line(line))
             {
-                if(spio_write_data_raw(ctx, &(ctx->server), (unsigned char*)header, header_len) == -1 ||
-                   spio_write_data_raw(ctx, &(ctx->server), (unsigned char*)CRLF, KL(CRLF)) == -1)
-                    RETURN(-1);
                 header[0] = '\0';
             }
+            else if(is_first_word(line, RCVD_SUBJECT, KL(RCVD_SUBJECT)))
+            {
+                if((tmp = strcasestr(line, RCVD_SUBJECT)) != NULL)
+                    tmp = tmp + strlen(RCVD_SUBJECT);
+                if(is_last_word(ctx->logline, "status=UNFILTERED",
+                  KL("status=UNFILTERED")) && !g_state.transparent) {
+                    snprintf(subjectline, SP_LINE_LENGTH,
+                      "%s ***UNCHECKED*** %s",  RCVD_SUBJECT, tmp);
+                    strncpy(line, subjectline, SP_LINE_LENGTH);
+                    rc = rc + strlen(" ***UNCHECKED*** ");
+                } else if(is_last_word(ctx->logline, "status=VIRUS",
+                  KL("status=VIRUS"))) {
+                    snprintf(subjectline, SP_LINE_LENGTH,
+                      "%s ***VIRUS*** %s",  RCVD_SUBJECT, tmp);
+                    strncpy(line, subjectline, SP_LINE_LENGTH);
+                    rc = rc + strlen(" ***VIRUS*** ");
+                }
+                sp_message(ctx, LOG_DEBUG, "Subjectline is %s", line);
+            }
         }
         
         if(spio_write_data_raw(ctx, &(ctx->server), (unsigned char*)line, rc) == -1)
@@ -1641,28 +1946,31 @@
     }
     
     if(ferror(file))
-        sp_message(ctx, LOG_ERR, "error reading cache file: %s", ctx->cachename);
-      
-    if(ferror(file) || spio_write_data(ctx, &(ctx->server), DATA_END_SIG) == -1)
     {
+        sp_message(ctx, LOG_ERR, "error reading cache file: %s", ctx->cachename);
         /* Tell the client it went wrong */
         spio_write_data(ctx, &(ctx->client), SMTP_FAILED);
         RETURN(-1);
     }
 
-    sp_messagex(ctx, LOG_DEBUG, "sent email data");    
-    
-    /* Okay read the response from the server and echo it to the client */
-    if(read_server_response(ctx) == -1)
-        RETURN(-1);
-        
-    if(spio_write_data(ctx, &(ctx->client), ctx->server.line) == -1)
-        RETURN(-1);
+    if(finished)
+    {
+        sp_message(ctx, LOG_DEBUG, "Finished with Email");
+        if(spio_write_data(ctx, &(ctx->server), DATA_END_SIG) == -1)
+        {
+            spio_write_data(ctx, &(ctx->client), SMTP_FAILED);
+            RETURN(-1);
+        }
+        if(sp_pass_response_to_client(ctx) == -1)
+            RETURN(-1);
+    }
         
 cleanup:
     
 	if(line)
 		free(line); 
+    if(subjectline)
+        free(subjectline);
     if(file)
         fclose(file); /* read-only so no error check */
     
@@ -1900,6 +2208,8 @@
 {
     char* t;
     int ret = 0;
+    int num_toks, i, num_toks2;
+    char **tokens, **tokens2;
     
     if(strcasecmp(CFG_MAXTHREADS, name) == 0)
     {
@@ -1909,6 +2219,88 @@
         ret = 1;
     }
         
+    if(strcasecmp(CFG_MAXMESSAGESIZE, name) == 0)
+    {
+        g_state.max_message_size = strtol(value, &t, 10);
+        if(*t || g_state.max_message_size < 1 || g_state.max_message_size > 50*1024*1024)
+            errx(2, "invalid setting: " CFG_MAXMESSAGESIZE " (must be between 1 and %d)", 1);
+        ret = 1;
+    }
+
+    if(strcasecmp(CFG_MAXMESSAGESIZEACTION, name) == 0)
+    {
+        if(strcasecmp(value, TYPE_PASS) == 0)
+            g_state.max_message_size_pass = 1;
+        else if(strcasecmp(value, TYPE_BLOCK) == 0)
+            g_state.max_message_size_pass = 0;
+        else
+            errx(2, "invalid value for " CFG_MAXMESSAGESIZEACTION
+              " (must specify 'pass' or 'block')");
+        ret = 1;
+    }
+
+    if(strcasecmp(CFG_RBLSERVER, name) == 0)
+    {
+        tokens = split_line(value,",",&num_toks);
+        if(tokens != NULL )
+        {
+            for (i=0; i < num_toks; i++)
+            {
+                append_rbl(trim_space(tokens[i]));
+            }
+            g_state.rbl_server = i;
+        }
+        ret = 1;
+    }
+
+    if(strcasecmp(CFG_BLACKLIST, name) == 0)
+    {
+        tokens = split_line(value,",",&num_toks);
+        if(tokens != NULL )
+        {
+            for (i=0; i < num_toks; i++)
+            {
+                tokens2 = split_line(tokens[i],"/",&num_toks2);
+                if(tokens2 != NULL)
+                {
+                    append_blacklist(tokens2[0], atoi(tokens2[1]));
+                }
+            }
+        }
+        ret = 1;
+    }
+
+    if(strcasecmp(CFG_WHITELIST, name) == 0)
+    {
+        tokens = split_line(value,",",&num_toks);
+        if(tokens != NULL )
+        {
+            for (i=0; i < num_toks; i++)
+            {
+                tokens2 = split_line(tokens[i],"/",&num_toks2);
+                if(tokens2 != NULL)
+                {
+                    append_whitelist(tokens2[0], atoi(tokens2[1]));
+                }
+            }
+        }
+        ret = 1;
+    }
+
+    if(strcasecmp(CFG_DOMAINS, name) == 0)
+    {
+        tokens = split_line(value,",",&num_toks);
+        if(tokens != NULL )
+        {
+            for (i=0; i < num_toks; i++)
+            {
+                append_domain(trim_space(tokens[i]));
+            }
+            g_state.domains = i;
+        }
+        ret = 1;
+    }
+
     else if(strcasecmp(CFG_TIMEOUT, name) == 0)
     {
         g_state.timeout.tv_sec = strtol(value, &t, 10);
@@ -2069,3 +2461,159 @@
     return 0;
 }        
 
+static unsigned long buildNetmask(int bits)
+{
+    unsigned long netmask;
+    netmask = 0xffffffff;
+    netmask = bits == 0 ? 0 : (netmask << (32 - bits));
+    return(netmask);
+}
+
+void append_blacklist(char *ip, int netmask)
+{
+    struct ip_system *zeiger;
+    sp_messagex(NULL, LOG_DEBUG, "Adding Entry %s/%d to BlackList",
+      ip, netmask);
+    if(ip_blacklist == NULL)
+    {
+        if((ip_blacklist = (struct ip_system *)
+          malloc(sizeof(struct ip_system))) == NULL)
+        {
+            errx(1, "out of memory");
+            return;
+        }
+        ip_blacklist->next = NULL;
+        inet_aton(ip, &ip_blacklist->ip);
+        ip_blacklist->netmask.s_addr = htonl(buildNetmask(netmask));
+    } else {
+        zeiger = ip_blacklist;
+        while(zeiger->next != NULL)
+            zeiger = zeiger->next;
+        if((zeiger->next = (struct ip_system *)
+          malloc(sizeof(struct ip_system))) == NULL)
+        {
+            errx(1, "out of memory");
+            return;
+        }
+        zeiger->next = NULL;
+        inet_aton(ip, &zeiger->ip);
+        zeiger->netmask.s_addr = htonl(buildNetmask(netmask));
+    }
+}
+
+void append_whitelist(char *ip, int netmask)
+{
+    struct ip_system *zeiger;
+    sp_messagex(NULL, LOG_DEBUG, "Adding Entry %s/%d to WhiteList",
+      ip, netmask);
+    if(ip_whitelist == NULL)
+    {
+        if((ip_whitelist = (struct ip_system *)
+          malloc(sizeof(struct ip_system))) == NULL)
+        {
+            errx(1, "out of memory");
+            return;
+        }
+        ip_whitelist->next = NULL;
+        inet_aton(ip, &ip_whitelist->ip);
+        ip_whitelist->netmask.s_addr = htonl(buildNetmask(netmask));
+    } else {
+        zeiger = ip_whitelist;
+        while(zeiger->next != NULL)
+            zeiger = zeiger->next;
+        if((zeiger->next = (struct ip_system *)
+          malloc(sizeof(struct ip_system))) == NULL)
+        {
+            errx(1, "out of memory");
+            return;
+        }
+        zeiger->next = NULL;
+        inet_aton(ip, &zeiger->ip);
+        zeiger->netmask.s_addr = htonl(buildNetmask(netmask));
+    }
+}
+
+
+void append_rbl(char *server)
+{
+    struct rbl_list *zeiger;
+    sp_messagex(NULL, LOG_DEBUG, "Adding Entry %s to RBL List", server);
+    if(rbl_liste == NULL)
+    {
+        if((rbl_liste = (struct rbl_list *)
+          malloc(sizeof(struct rbl_list))) == NULL)
+        {
+            errx(1, "out of memory");
+            return;
+        }
+        rbl_liste->next=NULL;
+        rbl_liste->name = server;
+    } else {
+        zeiger=rbl_liste;
+        while(zeiger->next != NULL)
+            zeiger=zeiger->next;
+        if((zeiger->next =(struct rbl_list *)
+          malloc(sizeof(struct rbl_list))) == NULL)
+        {
+            errx(1, "out of memory");
+            return;
+        }
+        zeiger=zeiger->next;
+        zeiger->name = server;
+        zeiger->next=NULL;
+    }
+}
+
+void append_domain(char *name)
+{
+    struct domain_list *zeiger;
+    sp_messagex(NULL, LOG_DEBUG, "Adding Entry %s to Domain List", name);
+    if(domain_liste == NULL)
+    {
+        if((domain_liste = (struct domain_list *)
+          malloc(sizeof(struct domain_list))) == NULL)
+        {
+            errx(1, "out of memory");
+            return;
+        }
+        domain_liste->next=NULL;
+        domain_liste->name = name;
+    } else {
+        zeiger=domain_liste;
+        while(zeiger->next != NULL)
+            zeiger=zeiger->next;
+        if((zeiger->next =(struct domain_list *)
+          malloc(sizeof(struct domain_list))) == NULL)
+        {
+            errx(1, "out of memory");
+            return;
+        }
+        zeiger=zeiger->next;
+        zeiger->name = name;
+        zeiger->next=NULL;
+    }
+}
+
+
+char **split_line(const char *string, char *token, int *count)
+{
+    char **retval = NULL;
+    char *nxt;
+    *count = 0;
+
+    while (1) {
+        nxt = strchrnul(string, token[0]);
+        if (nxt == string) break;
+        (*count)++;
+        retval = (char **) realloc(retval,(*count * sizeof(char*)));
+        retval[*count-1] = strndup(string, nxt-string);
+        string = nxt + 1;
+    }
+    return retval;
+}
+
+/*
+ * Local Variables:
+ * indent-tabs-mode: nil
+ * End:
+ */
diff -Naur proxsmtp-1.6-orig/common/smtppass.h proxsmtp-1.6/common/smtppass.h
--- proxsmtp-1.6-orig/common/smtppass.h	2008-02-06 09:57:48.000000000 +0100
+++ proxsmtp-1.6/common/smtppass.h	2008-02-06 09:58:29.000000000 +0100
@@ -39,6 +39,8 @@
 #ifndef __SMTPPASS_H__
 #define __SMTPPASS_H__
 
+#include <pa/spaced.h>
+
 /* Forward declarations */
 struct sockaddr_any;
 struct spctx;
@@ -127,6 +129,7 @@
 
     FILE* cachefile;                /* The file handle for the cached file */
     char cachename[MAXPATHLEN];     /* The name of the file that we cache into */
+    pasd_handle_t *spacedaemon;     /* Handle for PA-Spacedaemon */
     char logline[SP_LOG_LINE_LEN];  /* Log line */  
 
     char* sender;                   /* The email of the sender */
@@ -210,11 +213,13 @@
  */
 int sp_cache_data(spctx_t* ctx);
 
+int sp_passthru_data(spctx_t* ctx);
+
 /* 
  * Sends the data in file buffer off to server. This is 
  * completes a successful mail transfer. 
  */
-int sp_done_data(spctx_t* ctx, const char *header);
+int sp_done_data(spctx_t* ctx, const char *header, int finished);
 
 /* 
  * Fails the data, deletes any temp data, and sends given 
@@ -244,6 +249,40 @@
 void sp_lock();
 void sp_unlock();
 
+/*
+ *  Appends an entry to the blacklisted
+ *  ip is a ip-Adress in dotted format eg 192.168.12.1
+ *  netmask are the netmaskbits 24 --> 255.255.255.0
+ */
+void append_blacklist(char *ip, int netmask);
+
+/*
+ *  Appends an entry to the whitelisted
+ *  ip is a ip-Adress in dotted format eg 192.168.12.1
+ *  netmask are the netmaskbits 24 --> 255.255.255.0
+ */
+void append_whitelist(char *ip, int netmask);
+
+/*
+ *  Appends an server for rblcheck
+ *  server is the fqdn for the dnsquery
+ */
+void append_rbl(char *server);
+
+/*
+ * Splits a line into severeal parts
+ * string is the line to split
+ * token is the tokenizer to split
+ * count is the value of splitted parts
+ */
+char **split_line(const char *string, char *token, int *count);
+
+
+/*
+ *  *  Appends an domain entry for Mailcheck
+ *   *  name is the domain/subdomain-name
+ *    */
+void append_domain(char *name);
 
 /* -----------------------------------------------------------------------------
  * CALLBACKS IMPLMEMENTED BY PROGRAM
@@ -280,3 +319,9 @@
 
 
 #endif /* __SMTPPASS_H__ */
+
+/*
+ * Local Variables:
+ * indent-tabs-mode: nil
+ * End:
+ */
diff -Naur proxsmtp-1.6-orig/common/sppriv.h proxsmtp-1.6/common/sppriv.h
--- proxsmtp-1.6-orig/common/sppriv.h	2008-02-06 09:57:48.000000000 +0100
+++ proxsmtp-1.6/common/sppriv.h	2008-02-06 09:58:29.000000000 +0100
@@ -59,6 +59,17 @@
     const char* outname;
     struct sockaddr_any listenaddr; /* Address to listen on */
     const char* listenname;
+    int max_queue;                  /* Maximum number of connections to queue */
+    int max_message_size;           /* Maximum size for messages */
+    int max_message_size_pass;      /* Boolean value if too big messages should be
+                                     * blocked or passed without scanning */
+    int domains;                    /* Number of configured Mail Domains */
+    int rbl_server;                 /* Number of configured RBL Server */
+    int check_virus;                /* Messages should be checked for viruses */
+    int check_spam;                 /* Messages should be checked for spam */
+    int pass_on_virus;              /* Pass Messages that contain a virus */
+    int pass_on_spam;               /* Pass Messages that are identified as Spam */
+
            
     /* State --------------------------------- */   
     const char* name;               /* The name of the program */
diff -Naur proxsmtp-1.6-orig/doc/proxsmtpd.conf proxsmtp-1.6/doc/proxsmtpd.conf
--- proxsmtp-1.6-orig/doc/proxsmtpd.conf	2008-02-06 09:57:48.000000000 +0100
+++ proxsmtp-1.6/doc/proxsmtpd.conf	2008-02-06 09:59:48.000000000 +0100
@@ -8,28 +8,29 @@
 
 # The address to send scanned mail to. 
 # This option is required unless TransparentProxy is enabled
-OutAddress: 10026
+OutAddress: 192.168.1.1:25
 
 # The Filter Command run for each email. See 'man proxsmtpd' for details
 # The following command is a simple which just creates temp files.
 #FilterCommand: tee `mktemp -t sample-filter.XXXXXX`
 
 # The amount of time to wait for data from FilterCommand
-#FilterTimeout: 10
+FilterTimeout: 120
 
 # The type of filter ('pipe' to pipe data through filter,
-# or 'file' to pass a file to the filter)
-#FilterType: pipe
+# or 'file' to pass a file to the filter or 'packetalarm'
+# to use the PacketAlarm Option)
+FilterType: packetalarm
 
 # The maximum number of connection allowed at once.
 # Be sure that clamd can also handle this many connections
-#MaxConnections: 64
+MaxConnections: 2
 
 # Amount of time (in seconds) to wait on network IO
-#TimeOut: 180
+TimeOut: 180
 
 # A header to add to all scanned email
-#Header: X-Filtered: By ProxSMTP
+Header: X-Filtered: By funkwerk UTM
 
 # Keep Alives (ie: NOOP's to server)
 #KeepAlives: 0
@@ -38,15 +39,64 @@
 #XClient: off
 
 # Address to listen on (defaults to all local addresses on port 10025)
-#Listen: 0.0.0.0:10025
+Listen: 0.0.0.0:25
 
 # Directory for temporary files
-#TempDirectory: /tmp
+TempDirectory: /var/spool/packetalarm/vscan
 
 # Enable transparent proxy support 
-#TransparentProxy: off
+TransparentProxy: off
 
 # User to switch to
 #User: nobody
 
+# Using Domains to check if the Mail will be accepted or denied.
+# Everything after the '@' in an mail must match one of the configured
+# domains.
+# Syntax MailDomains: test.de,test.mydomain.com,mydomain.com
+# MailDomains:
+
+# Using RBL Server to check in Realtime if System is defined as spammer
+# Syntax RBLServer: bl.csma.biz,bl2.csma.biz
+# RBLServer:
+
+# Using Blacklist for receiving Email
+# Syntax BlackList: 192.168.1.1/32,172.13.1.0/24
+# BlackList:
+
+# Using Whitelist for receiving Email
+# Syntax WhiteList: 192.168.1.1/32,172.13.1.0/24
+# WhiteList:
+
+# Define the maximum of Messagesize that is accepted for checking.
+# This is the maximal filesize that wil be written on the disk. If the Mail
+# is bigger than it will be passed unchecked or dropped (depends on
+# MaxMessageSizeAction)
+# Value is given in Megabytes
+MaxMessageSize: 5
+
+# Action that will be taken if the maximum Message size is reached.
+# ('pass' to pass the Mail unfiltered, or 'block' to interrupt the transfer)
+# When the Mail will be passed the subject will be added by a *** UNCHECKED ***
+# and the header contains 'X-Filtered: Unchecked Message by funkwerk UTM'
+MaxMessageSizeAction: pass
+
+# Defines if in PacketAlarm-Mode the system should check the Mails for Viruses
+# Options are 'YES' and 'NO'
+FilterCheckVirus: YES
+
+# If Virus was found in the Mail, should it be passed to the Destination or not.
+# When the Mail will be passed the subject will be added by a *** VIRUS ***
+# and the header contains 'X-Filtered: Passed Virus Message by funkwerk UTM'
+# Options are 'YES' and 'NO'
+FilterPassOnVirus: YES
+
+# Defines if in PacketAlarm-Mode the system should check the Mails if they are
+# classified as Spam or not
+# Options are 'YES' and 'NO'
+FilterCheckSpam: YES
+
+# Messages with a spam score greater or eqaul this level will be dropped.
+# The value '0' will disable this feature
+FilterSpamDropLevel: 0
 
diff -Naur proxsmtp-1.6-orig/src/Makefile.in proxsmtp-1.6/src/Makefile.in
--- proxsmtp-1.6-orig/src/Makefile.in	2008-02-06 09:57:48.000000000 +0100
+++ proxsmtp-1.6/src/Makefile.in	2008-02-06 09:58:29.000000000 +0100
@@ -54,7 +54,7 @@
 	proxsmtpd-stringx.$(OBJEXT) proxsmtpd-sock_any.$(OBJEXT) \
 	proxsmtpd-compat.$(OBJEXT)
 proxsmtpd_OBJECTS = $(am_proxsmtpd_OBJECTS)
-proxsmtpd_LDADD = $(LDADD)
+proxsmtpd_LDADD = $(LDADD) -lpaiface
 DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
 depcomp = $(SHELL) $(top_srcdir)/depcomp
 am__depfiles_maybe = depfiles
diff -Naur proxsmtp-1.6-orig/src/proxsmtpd.c proxsmtp-1.6/src/proxsmtpd.c
--- proxsmtp-1.6-orig/src/proxsmtpd.c	2008-02-06 09:57:48.000000000 +0100
+++ proxsmtp-1.6/src/proxsmtpd.c	2008-02-06 10:00:41.000000000 +0100
@@ -46,6 +46,8 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <signal.h>
+#include <dirent.h>
+#include <sys/stat.h>
 
 #include "usuals.h"
 
@@ -53,6 +55,11 @@
 #include "sock_any.h"
 #include "stringx.h"
 #include "smtppass.h"
+#include "proxsmtpd.h"
+
+#include <pa/stat.h>
+#include <pa/scanqclient.h>
+#include <pa/loglayer.h>
 
 /* -----------------------------------------------------------------------
  *  STRUCTURES
@@ -66,6 +73,11 @@
     int pipe_cmd;                   /* Whether command is a pipe or not */
     const char* directory;          /* The directory for temp files */
     const char* header;             /* Header to include in output */
+    int rbl_server;                 /* Number of configured RBL Server */
+    int check_virus;                /* Messages should be checked for viruses */
+    int check_spam;                 /* Messages should be checked for spam */
+    int pass_on_virus;              /* Pass Messages that contain a virus */
+    int spam_drop_level;            /* Drop message if spam score is >= */
 }
 pxstate_t;
 
@@ -83,11 +95,30 @@
 #define CFG_DIRECTORY       "TempDirectory"
 #define CFG_DEBUGFILES      "DebugFiles"
 #define CFG_CMDTIMEOUT      "FilterTimeout"
-#define CFG_HEADER      	"Header"
+#define CFG_HEADER          "Header"
+#define CFG_MAXMESSAGESIZE  "MaxMessageSize"
+#define CFG_CHECKVIRUS      "FilterCheckVirus"
+#define CFG_PASSVIRUS       "FilterPassOnVirus"
+#define CFG_CHECKSPAM       "FilterCheckSpam"
+#define CFG_SPAMDROPLEVEL   "FilterSpamDropLevel"
 
 #define TYPE_PIPE           "pipe"
 #define TYPE_FILE           "file"
+#define TYPE_PACKETALARM    "packetalarm"
 
+#define STATE_PIPE          1
+#define STATE_FILE          0
+#define STATE_PACKETALARM   2
+
+#define STATE_YES           "YES"
+#define STATE_NO            "NO"
+
+#define EMAIL_OK            0
+#define EMAIL_SPAM          1
+#define EMAIL_VIRUS         2
+#define EMAIL_TOOBIG       -2
+#define EMAIL_UNCHECKED     -3
+#define FILTER_TIMEOUT      -4
 /* Poll time for waiting operations in milli seconds */
 #define POLL_TIME           20
 
@@ -106,6 +137,13 @@
  
 pxstate_t g_pxstate;
 
+struct {
+    pa_stat_counter_t *total;
+    pa_stat_counter_t *spam_action;
+    pa_stat_counter_t *spam_pass;
+    pa_stat_counter_t *virus;
+} pa_stat;
+
 /* -----------------------------------------------------------------------
  *  FORWARD DECLARATIONS
  */
@@ -113,6 +151,7 @@
 static void usage();
 static int process_file_command(spctx_t* sp);
 static int process_pipe_command(spctx_t* sp);
+static int process_packetalarm_command(spctx_t* sp);
 static void final_reject_message(char* buf, int buflen);
 static void buffer_reject_message(char* data, char* buf, int buflen);
 static int kill_process(spctx_t* sp, pid_t pid);
@@ -138,12 +177,21 @@
 #ifndef HAVE___ARGV
     __argv = argv;
 #endif
+
+    pa_stat.total = pa_stat_getcnt("smtpmail");
+    pa_stat.spam_pass = pa_stat_getcnt("smtpspam_pass");
+    pa_stat.spam_action = pa_stat_getcnt("smtpspam_action");
+    pa_stat.virus = pa_stat_getcnt("smtpvirus");
 	
     /* Setup some defaults */
     memset(&g_pxstate, 0, sizeof(g_pxstate));
     g_pxstate.directory = _PATH_TMP;
     g_pxstate.pipe_cmd = 1;
     g_pxstate.timeout.tv_sec = DEFAULT_TIMEOUT;
+    g_pxstate.check_virus = 1;
+    g_pxstate.check_spam = 1;
+    g_pxstate.pass_on_virus = 0;
+    g_pxstate.spam_drop_level = 0;
    
     sp_init("proxsmtpd");
     
@@ -177,7 +225,7 @@
 
         /* Print version number */
         case 'v':
-            printf("clamsmtpd (version %s)\n", VERSION);
+            printf("proxsmtpd (version %s)\n", VERSION);
             printf("          (config: %s)\n", DEFAULT_CONFIG);
             exit(0);
             break;
@@ -218,12 +266,12 @@
 {
     int r = 0;
         
-    if(!g_pxstate.command)
+    if(!g_pxstate.command && g_pxstate.pipe_cmd != STATE_PACKETALARM)
     {
         sp_messagex(ctx, LOG_WARNING, "no filter command specified. passing message through");
         
         if(sp_cache_data(ctx) == -1 ||
-           sp_done_data(ctx, g_pxstate.header) == -1)
+           sp_done_data(ctx, g_pxstate.header, 1) == -1)
             return -1;  /* Message already printed */
             
         return 0;
@@ -233,17 +281,37 @@
     while(waitpid(-1, &r, WNOHANG) > 0)
         ;
                 
-    if(g_pxstate.pipe_cmd)
+    pa_stat_inc(pa_stat.total);
+    if(g_pxstate.pipe_cmd == 1)
         r = process_pipe_command(ctx);
-    else
+    else if (g_pxstate.pipe_cmd == 0)
         r = process_file_command(ctx);
-        
-    if(r == -1)
-    {
-        if(sp_fail_data(ctx, NULL) == -1)
-            return -1;
-    }
-    
+    else
+        r = process_packetalarm_command(ctx);
+
+	switch (r) {
+		case EMAIL_TOOBIG:
+			if (sp_fail_data(ctx, "552 Requested mail action aborted: exceeded storage allocation\n") == -1) {
+				return (-1);
+			}
+			break;
+
+		case FILTER_TIMEOUT:
+			if (sp_fail_data(ctx, "451 Requested action aborted: processing exceeded timeout\n") == -1) {
+				return (-1);
+			}
+			break;
+
+		case 0:
+			break;
+
+		default:
+			if (sp_fail_data(ctx, "451 Requested action aborted: local error in processing\n") == -1) {
+				return (-1);
+			}
+			break;
+	}
+		
     return 0;
 }
 
@@ -270,13 +338,54 @@
             errx(2, "invalid setting: " CFG_CMDTIMEOUT);
         return 1;
     }
-                
+
+    else if(strcasecmp(CFG_CHECKSPAM, name) == 0)
+    {
+        if(strcasecmp(value, STATE_YES) == 0)
+            g_pxstate.check_spam = 1;
+        else if(strcasecmp(value, STATE_NO) == 0)
+            g_pxstate.check_spam = 0;
+        else
+            errx(2, "invalid value for " CFG_CHECKSPAM " (must specify '"STATE_YES"' or '"STATE_NO"')");
+        return 1;
+    }
+
+    else if(strcasecmp(CFG_SPAMDROPLEVEL, name) == 0)
+    {
+        g_pxstate.spam_drop_level = atoi(value);
+        return 1;
+    }
+
+    else if(strcasecmp(CFG_CHECKVIRUS, name) == 0)
+    {
+        if(strcasecmp(value, STATE_YES) == 0)
+            g_pxstate.check_virus = 1;
+        else if(strcasecmp(value, STATE_NO) == 0)
+            g_pxstate.check_virus = 0;
+        else
+            errx(2, "invalid value for " CFG_CHECKVIRUS " (must specify '"STATE_YES"' or '"STATE_NO"')");
+        return 1;
+    }
+
+    else if(strcasecmp(CFG_PASSVIRUS, name) == 0)
+    {
+        if(strcasecmp(value, STATE_YES) == 0)
+            g_pxstate.pass_on_virus = 1;
+        else if(strcasecmp(value, STATE_NO) == 0)
+            g_pxstate.pass_on_virus = 0;
+        else
+            errx(2, "invalid value for " CFG_PASSVIRUS " (must specify '"STATE_YES"' or '"STATE_NO"')");
+        return 1;
+    }
+
     else if(strcasecmp(CFG_FILTERTYPE, name) == 0)
     {
         if(strcasecmp(value, TYPE_PIPE) == 0)
-            g_pxstate.pipe_cmd = 1;
+            g_pxstate.pipe_cmd = STATE_PIPE;
         else if(strcasecmp(value, TYPE_FILE) == 0)
-            g_pxstate.pipe_cmd = 0;
+            g_pxstate.pipe_cmd = STATE_FILE;
+        else if(strcasecmp(value, TYPE_PACKETALARM) == 0)
+            g_pxstate.pipe_cmd = STATE_PACKETALARM;
         else
             errx(2, "invalid value for " CFG_FILTERTYPE " (must specify 'pipe' or 'file')");
         return 1;
@@ -317,7 +426,206 @@
         sleep(1);
     }
 }
- 
+
+
+static int scan_file(char *filename, spctx_t* sp)
+{
+    int ret;
+    char info[1000];
+
+    snprintf(info, sizeof(info), "From <%s> (%s) To: <%s> (%s)",
+      sp->sender, sp->client.peername, sp->recipients, sp->server.peername);
+    ret = pa_virus_scan("SMTP", filename, info);
+    return(ret);
+}
+
+
+int run_spamc(char *fname)
+{
+	int		status;
+	pid_t	pid;
+
+	if ((pid = fork()) == -1) {
+		return (-1);
+	}
+
+	if (pid == 0) {
+		if (freopen(fname, "r", stdin) == NULL) {
+			exit(-1);
+		}
+
+		execlp("/usr/bin/spamc", "spamc", "-E", NULL);
+		exit(-1);
+	} else {
+		if (waitpid(pid, &status, 0) == -1) {
+			return (-1);
+		} else {
+			if (WIFEXITED(status)) {
+				return (WEXITSTATUS(status));
+			} else {
+				return (-1);
+			}
+		}
+	}
+
+	return (0);
+}
+
+
+static pid_t fork_packetalarm(spctx_t* sp, int* infd, int* outfd, int* errfd, int tfd)
+{
+    pid_t pid;
+    int ret = 0;
+    int r = 0;
+    int rc = 0;
+    char command[MAXPATHLEN];
+    int virus = 0;
+    int spam = 0;
+
+    /* Pipes for input, output, err */
+    int pipe_i[2];
+    int pipe_o[2];
+    int pipe_e[2];
+
+    memset(pipe_i, ~0, sizeof(pipe_i));
+    memset(pipe_o, ~0, sizeof(pipe_o));
+    memset(pipe_e, ~0, sizeof(pipe_e));
+
+    //ASSERT(g_pxstate.command);
+
+    /* Create the pipes we need */
+    if((infd && pipe(pipe_i) == -1) ||
+      (outfd && pipe(pipe_o) == -1) ||
+      (errfd && pipe(pipe_e) == -1))
+    {
+        sp_message(sp, LOG_ERR, "couldn't create pipe for filter command");
+        RETURN(-1);
+    }
+
+    /* Now fork the pipes across processes */
+    switch(pid = fork())
+    {
+        case -1:
+            sp_message(sp, LOG_ERR, "couldn't fork for filter command");
+			exit(42);
+            /* RETURN(-1); */
+
+        /* The child process */
+        case 0:
+            if(r >= 0 && infd)
+            {
+                close(pipe_i[WRITE_END]);
+                r = dup2(pipe_i[READ_END], STDIN);
+                close(pipe_i[READ_END]);
+            }
+            if(r >= 0 && outfd)
+            {
+                close(pipe_o[READ_END]);
+                r = dup2(pipe_o[WRITE_END], STDOUT);
+                close(pipe_o[WRITE_END]);
+            }
+            if(r >= 0 && errfd)
+            {
+                close(pipe_e[READ_END]);
+                r = dup2(pipe_e[WRITE_END], STDERR);
+                close(pipe_e[WRITE_END]);
+            }
+            if(r < 0)
+            {
+                sp_message(sp, LOG_ERR, "couldn't dup descriptors for filter command");
+                kill_myself();
+            }
+
+            /* All the necessary environment vars */
+            sp_setup_forked(sp, 1);
+
+            /* Nkw run the filter command */
+            if(g_pxstate.check_virus == 1) {
+                umask(000);
+                rc = scan_file(sp->cachename, sp);
+                if(rc == 0) {
+                    sp_message(sp, LOG_DEBUG, "Mail contains Virus");
+                    virus = 1;
+                } else if(virus < 0) {
+                    sp_message(sp, LOG_DEBUG, "Internal Error on Virus Scan");
+                    virus = 0;
+                } else {
+                    sp_message(sp, LOG_DEBUG, "Mail contains no Virus");
+                    virus = 0;
+                }
+            }
+            if ((virus == 0) && (g_pxstate.check_spam == 1)) {
+				r = dup2(tfd, STDOUT);
+
+				spam = run_spamc(sp->cachename);
+				switch (spam) {
+					case 0:
+						sp_message(sp, LOG_DEBUG, "Mail is ham");
+						break;
+
+					case 1:
+						sp_message(sp, LOG_DEBUG, "Mail is spam.");
+						exit(EMAIL_SPAM);
+						break;
+
+					default:
+						sp_message(sp, LOG_DEBUG, "Error executing spam filter");
+						exit(EMAIL_SPAM);
+						break;
+				}
+            } else {
+                r = dup2(tfd, STDOUT);
+                snprintf(command, MAXPATHLEN, "cat %s", sp->cachename);
+                sp_message(sp, LOG_DEBUG, "Running Command %s", command);
+                system(command);
+            }
+            if(virus == 1)
+                exit(EMAIL_VIRUS);
+            //Neither Virus nor Spam
+            exit(0);
+            break;
+    };
+
+    /* The parent process */
+    sp_messagex(sp, LOG_DEBUG, "executed filter with pid: %d", (int)pid);
+
+    /* Setup all our return values */
+    if(infd)
+    {
+        *infd = pipe_i[WRITE_END];
+        pipe_i[WRITE_END] = -1;
+        fcntl(*infd, F_SETFL, fcntl(*infd, F_GETFL, 0) | O_NONBLOCK);
+    }
+    if(outfd)
+    {
+        *outfd = pipe_o[READ_END];
+        pipe_o[READ_END] = -1;
+        fcntl(*outfd, F_SETFL, fcntl(*outfd, F_GETFL, 0) | O_NONBLOCK);
+    }
+    if(errfd)
+    {
+        *errfd = pipe_e[READ_END];
+        pipe_e[READ_END] = -1;
+        fcntl(*errfd, F_SETFL, fcntl(*errfd, F_GETFL, 0) | O_NONBLOCK);
+    }
+
+cleanup:
+    if(pipe_i[READ_END] != -1)
+        close(pipe_i[READ_END]);
+    if(pipe_i[WRITE_END] != -1)
+        close(pipe_i[WRITE_END]);
+    if(pipe_o[READ_END] != -1)
+        close(pipe_o[READ_END]);
+    if(pipe_o[WRITE_END] != -1)
+        close(pipe_o[WRITE_END]);
+    if(pipe_e[READ_END] != -1)
+        close(pipe_e[READ_END]);
+    if(pipe_e[WRITE_END] != -1)
+        close(pipe_e[WRITE_END]);
+
+    return ret >= 0 ? pid : (pid_t)-1;
+}
+
 static pid_t fork_filter(spctx_t* sp, int* infd, int* outfd, int* errfd)
 {
     pid_t pid;
@@ -435,6 +743,269 @@
     return ret >= 0 ? pid : (pid_t)-1;
 }
 
+
+static int get_spam_score(spctx_t* ctx)
+{
+    FILE *file;
+    char buf[100];
+    int score = 0;
+
+    if(! (file = fopen(ctx->cachename, "r")))
+        return 0;
+    while(fgets(buf, sizeof(buf), file)) {
+        int l;
+        char *p;
+
+        l = strlen(buf);
+        while(l > 0 && buf[l - 1] < 31) buf[--l] = 0;
+        if(buf[0] == '0')
+            break;
+        if(strncmp("X-Spam-Status: Yes, score=", buf, 26) != 0)
+            continue;
+        p = index(buf, '=');
+        p++;
+        score = atoi(p);
+        break;
+    }
+    fclose(file);
+    return score;
+}
+
+
+static int process_packetalarm_command(spctx_t* sp)
+{
+    pid_t pid;
+    int ret = 0, status, r;
+    int tfd;
+    struct timeval timeout;
+    int message_size = 0;
+
+    /* For reading data from the process */
+    int errfd, returnfd;
+    fd_set rmask;
+    char obuf[1024];
+    char ebuf[256];
+
+    /* FILE* tempfile; */
+    char template[MAXPATHLEN];
+
+    sp_messagex(sp, LOG_DEBUG, "Running process_packetalarm_command");
+    memset(ebuf, 0, sizeof(ebuf));
+
+    pid = 0;
+    ret = 0;
+    message_size = sp_cache_data(sp);
+    if(message_size == -1)
+        RETURN(-1); /* message already printed */
+    if(message_size == EMAIL_TOOBIG) {
+        sp_add_log(sp, "status=", "BLOCKED");
+        RETURN(EMAIL_TOOBIG); /* message is too big and blocked*/
+    }
+    if(message_size == EMAIL_UNCHECKED) {
+        sp_add_log(sp, "status=", "UNFILTERED");
+        if(sp_done_data(sp, "X-Filtered: Unchecked Message by funkwerk UTM", 0) == -1)
+            RETURN(-1); /* message already printed */
+        if(sp_passthru_data(sp) == -1)
+            RETURN(-1);
+        RETURN(0);
+    }
+
+    snprintf(template, MAXPATHLEN, "%s/%s.XXXXXX",
+      "/var/spool/packetalarm/vscan", "smtpprox");
+
+	if ((tfd = mkstemp(template)) == -1) {
+		sp_message(sp, LOG_ERR, "couldn't open tmpfile");
+		RETURN(-1);
+	}
+    sp_messagex(sp, LOG_DEBUG, "created tmpfile file: %s", template);
+    fcntl(tfd, F_SETFD, fcntl(tfd, F_GETFD, 0) | FD_CLOEXEC);
+
+    pid = fork_packetalarm(sp, NULL, &returnfd, &errfd, tfd);
+    if(pid == (pid_t)-1) {
+		close(tfd);
+		unlink(template);
+        RETURN(-1);
+    }
+
+    /* Main read write loop */
+    while(errfd != -1)
+    {
+        FD_ZERO(&rmask);
+        FD_SET(errfd, &rmask);
+        FD_SET(returnfd, &rmask);
+
+        /* Select can modify the timeout argument so we copy */
+        memcpy(&timeout, &(g_pxstate.timeout), sizeof(timeout));
+
+        r = select(FD_SETSIZE, &rmask, NULL, NULL, &timeout);
+        switch(r)
+        {
+            case -1:
+                sp_message(sp, LOG_ERR, "couldn't select while listening to filter command");
+				close(tfd);
+				unlink(template);
+                RETURN(-1);
+            case 0:
+                sp_messagex(sp, LOG_ERR, "timeout while listening to filter command");
+				close(tfd);
+				unlink(template);
+                RETURN(FILTER_TIMEOUT);
+        };
+
+        r = read(returnfd, obuf, sizeof(obuf));
+        if(r < 0)
+        {
+            if(errno != EINTR && errno != EAGAIN)
+            {
+                sp_message(sp, LOG_ERR, "couldn't read data from filter command");
+				close(tfd);
+				unlink(template);
+                RETURN(-1);
+            }
+            continue;
+        }
+        if(r == 0)
+        {
+            close(returnfd);
+            returnfd = -1;
+            break;
+        }
+        obuf[r] = 0;
+
+        /* Note because we handle as string we save one byte for null-termination */
+        r = read(errfd, obuf, sizeof(obuf) - 1);
+        if(r < 0)
+        {
+            if(errno != EINTR && errno != EAGAIN)
+            {
+                sp_message(sp, LOG_ERR, "couldn't read data from filter command");
+				close(tfd);
+				unlink(template);
+                RETURN(-1);
+            }
+            continue;
+        }
+        if(r == 0)
+        {
+            close(errfd);
+            errfd = -1;
+            break;
+        }
+
+        /* Null terminate */
+        obuf[r] = 0;
+
+        if(sp_is_quit()) {
+			close(tfd);
+			unlink(template);
+            RETURN(-1);
+		}
+    }
+
+    /* exit the process if not completed */
+    if(wait_process(sp, pid, &status) == -1)
+    {
+        sp_messagex(sp, LOG_ERR, "timeout waiting for filter command to exit");
+		close(tfd);
+		unlink(template);
+        RETURN(FILTER_TIMEOUT);
+    }
+
+    pid = 0;
+
+	close(tfd);
+	unlink(sp->cachename);
+    snprintf(sp->cachename, MAXPATHLEN, "%s", template);
+
+    /* We only trust well behaved programs */
+    if(!WIFEXITED(status))
+    {
+        sp_messagex(sp, LOG_ERR, "filter command terminated abnormally");
+        RETURN(-1);
+    }
+
+    switch((int)WEXITSTATUS(status))
+    {
+        case EMAIL_OK:
+            sp_messagex(sp, LOG_DEBUG, "Sending %s", sp->cachename);
+            if(sp_done_data(sp, g_pxstate.header, 1) == -1)
+                RETURN(-1); /* message already printed */
+            sp_add_log(sp, "status=", "FILTERED");
+            break;
+
+        case EMAIL_SPAM: {
+	    int spam_score;
+
+	    spam_score = get_spam_score(sp);
+            sp_messagex(sp, LOG_DEBUG, "Spam Score %d", spam_score);
+	    if(g_pxstate.spam_drop_level == 0 ||
+	      spam_score < g_pxstate.spam_drop_level) {
+                pa_stat_inc(pa_stat.spam_pass);
+                sp_messagex(sp, LOG_DEBUG, "Sending %s", sp->cachename);
+                sp_add_log(sp, "status=", "SPAM");
+                if(sp_done_data(sp,
+                  "X-Filtered: Passed Spam Message by funkwerk UTM", 1) == -1)
+                    RETURN(-1); /* message already printed */
+            } else {
+                pa_stat_inc(pa_stat.spam_action);
+                snprintf(ebuf, sizeof(ebuf),
+                  "Message anaylsed as Spam and got dropped");
+                final_reject_message(ebuf, sizeof(ebuf));
+                if(sp_fail_data(sp, ebuf) == -1)
+                    RETURN(-1); /* message already printed */
+                sp_add_log(sp, "status=", "DROPPED_SPAM");
+                pa_sendlog(NULL, 3 , 9, sp->logline);
+            }
+            break;
+	}
+
+        case EMAIL_VIRUS:
+            pa_stat_inc(pa_stat.virus);
+            if(g_pxstate.pass_on_virus == 1) {
+                sp_add_log(sp, "status=", "VIRUS");
+                sp_messagex(sp, LOG_DEBUG, "Sending %s", sp->cachename);
+                if(sp_done_data(sp,
+                  "X-Filtered: Passed Virus Message by funkwerk UTM", 1) == -1)
+                    RETURN(-1); /* message already printed */
+            } else {
+                snprintf(ebuf, sizeof(ebuf), "Message contains Virus and got dropped");
+                final_reject_message(ebuf, sizeof(ebuf));
+                if(sp_fail_data(sp, ebuf) == -1)
+                    RETURN(-1); /* message already printed */
+                sp_add_log(sp, "status=", "DROPPED_VIRUS");
+                pa_sendlog(NULL, 3 , 9, sp->logline);
+            }
+            break;
+
+        default:
+            snprintf(ebuf, sizeof(ebuf), "Internal Error");
+            final_reject_message(ebuf, sizeof(ebuf));
+            if(sp_fail_data(sp, ebuf) == -1)
+                RETURN(-1); /* message already printed */
+            sp_add_log(sp, "status=", "FILTER_ERROR");
+            pa_sendlog(NULL, 1 , 9, sp->logline);
+    }
+
+    ret = 0;
+
+cleanup:
+
+    if(pid > 0)
+    {
+        sp_messagex(sp, LOG_WARNING, "killing filter process (pid %d)", (int)pid);
+        kill_process(sp, pid);
+    }
+
+    if(errfd != -1)
+        close(errfd);
+
+    if(ret < 0 && ret != -2)
+        sp_add_log(sp, "status=", "FILTER-ERROR");
+
+    return ret;
+}
+
+
 static int process_file_command(spctx_t* sp)
 {
     pid_t pid;
@@ -447,6 +1018,7 @@
     char obuf[1024];
     char ebuf[256];
 
+    sp_messagex(sp, LOG_DEBUG, "Running process_file_command");
     memset(ebuf, 0, sizeof(ebuf));
         
     if(sp_cache_data(sp) == -1)
@@ -530,7 +1102,7 @@
     /* A successful response */    
     if(WEXITSTATUS(status) == 0)
     {
-        if(sp_done_data(sp, g_pxstate.header) == -1)
+        if(sp_done_data(sp, g_pxstate.header, 1) == -1)
             RETURN(-1); /* message already printed */
 
         sp_add_log(sp, "status=", "FILTERED");        
@@ -589,6 +1161,7 @@
     
     ASSERT(g_pxstate.command);
     
+    sp_messagex(sp, LOG_DEBUG, "Running process_pipe_command");
     memset(ebuf, 0, sizeof(ebuf));
 
     pid = fork_filter(sp, &infd, &outfd, &errfd);
@@ -773,7 +1346,7 @@
     /* A successful response */    
     if(WEXITSTATUS(status) == 0)
     {
-        if(sp_done_data(sp, g_pxstate.header) == -1)
+        if(sp_done_data(sp, g_pxstate.header, 1) == -1)
             RETURN(-1); /* message already printed */
 
         sp_add_log(sp, "status=", "FILTERED");        
@@ -929,3 +1502,9 @@
     
    return 0;
 }
+
+/*
+ * Local Variables:
+ * indent-tabs-mode: nil
+ * End:
+ */
diff -Naur proxsmtp-1.6-orig/src/proxsmtpd.h proxsmtp-1.6/src/proxsmtpd.h
--- proxsmtp-1.6-orig/src/proxsmtpd.h	2008-02-06 09:57:48.000000000 +0100
+++ proxsmtp-1.6/src/proxsmtpd.h	2008-02-06 09:58:29.000000000 +0100
@@ -39,5 +39,15 @@
 #ifndef __PROXSMTPD_H__
 #define __PROXSMTPD_H__
 
+/*
+ * Delivers a temporarely Directory, used for scanning
+ */
+char *_mkTempDir();
+
+/*
+ * Deletes the content of the directory and the directory itself
+ */
+int _cleanTempDir(char *dirname);
+
 
 #endif /* __PROXSMTPD_H__ */

