Re: [Exim] ANNOUNCE: exiscan-acl-4.22-11

Top Page
Delete this message
Reply to this message
Author: Michael Haardt
Date:  
To: exim-users
Subject: Re: [Exim] ANNOUNCE: exiscan-acl-4.22-11
--
On Tue, Sep 02, 2003 at 10:32:56AM +0200, Tom Kistner wrote:
> OK, here is the latest instance of the exiscan patch. It mainly contains
> cleanups, and fixes one bug that may affect extension blocking with
> rfc822 attached messages in some cases. So there is nothing really
> critical, no need to hurry with updating.


And as usual, my patch to allow using multiple SA servers, which makes
global option obsolete and modifies the ACL condition to include server
and port:

spam = 127.0.0.1:783:user:true

Michael
--
--- src/spam.c.orig    2003-09-02 11:28:08.000000000 +0200
+++ src/spam.c    2003-09-02 11:34:02.000000000 +0200
@@ -13,39 +13,66 @@
 #include "exim.h"
 #include "spam.h"


-uschar spam_score_buffer[16];
-uschar spam_score_int_buffer[16];
-uschar spam_bar_buffer[128];
-uschar spam_report_buffer[32600];
-uschar prev_user_name[128];
-int spam_ok = 0;
-int spam_rc = 0;
+static struct SpamCache {
+ /* key */
+ uschar *tcp_addr;
+ unsigned short tcp_port;
+ uschar *user_name;
+ /* result */
+ uschar *spam_score,*spam_score_int,*spam_bar,*spam_report;
+ int spam_rc;
+ /* next entry */
+ struct SpamCache *next;
+} *spamCache;

int spam(uschar **listptr) {
int sep = 0;
uschar *list = *listptr;
uschar *user_name;
- uschar user_name_buffer[128];
+ struct SpamCache **entry;
unsigned long long mbox_size;
FILE *mbox_file;
int spamd_sock;
- uschar tcp_addr[24];
+ uschar *tcp_addr,*tcp_port_str;
unsigned int tcp_port;
uschar spamd_buffer[32600];
- int i, j, offset;
- uschar spamd_version[8];
+ int i, j, offset, spam_rc;
+ unsigned int spamd_major, spamd_minor, spamd_status;
uschar spamd_score_char;
double spamd_threshold, spamd_score;
- int spamd_report_offset;
- uschar *p,*q;
+ int advance, capacity, length;
+ uschar *p;
int override = 0;

+  /* find the spamd server address from the option list */
+  if ((tcp_addr = string_nextinlist(&list, &sep,
+                                     NULL,
+                                     0)) == NULL) {
+    /* no address given, this means no scanning should be done */
+    return FAIL;
+  }
+
+  /* find the spamd port from the option list */
+  if ((tcp_port_str = string_nextinlist(&list, &sep,
+                                     NULL,
+                                     0)) == NULL) {
+    log_write(0, LOG_MAIN|LOG_PANIC,
+         "spam acl condition: no spamd port specified");
+    return DEFER;
+  }
+  if( sscanf(CS tcp_port_str, "%u", &tcp_port) != 1 ) {
+    log_write(0, LOG_MAIN|LOG_PANIC,
+         "spam acl condition: invalid spamd port: '%s'", tcp_port_str);
+    return DEFER;
+  }
+
   /* find the username from the option list */
   if ((user_name = string_nextinlist(&list, &sep,
-                                     user_name_buffer,
-                                     sizeof(user_name_buffer))) == NULL) {
-    /* no username given, this means no scanning should be done */
-    return FAIL;
+                                     NULL,
+                                     0)) == NULL) {
+    log_write(0, LOG_MAIN|LOG_PANIC,
+         "spam acl condition: no user specified");
+    return DEFER;
   };


   /* if username is "0" or "false", do not scan */
@@ -60,14 +87,19 @@
     override = 1;
   };


-  /* if we scanned for this username last time, just return */
-  if ( spam_ok && ( Ustrcmp(prev_user_name, user_name) == 0 ) ) {
-    if (override)
-      return OK;
-    else
-      return spam_rc;
-  };
-
+  /* if we scanned with these paramters before, just return */
+  for (entry=&spamCache; *entry; entry=&(*entry)->next) {
+    if (strcmp((*entry)->tcp_addr,tcp_addr)==0
+        && (*entry)->tcp_port==tcp_port
+        && strcmp((*entry)->user_name,user_name)==0) {
+      spam_score = (*entry)->spam_score;
+      spam_score_int = (*entry)->spam_score_int;
+      spam_bar = (*entry)->spam_bar;
+      spam_report = (*entry)->spam_report;
+      return (override ? OK : (*entry)->spam_rc);
+    }
+  }
+
   /* make sure the eml mbox file is spooled up */
   mbox_file = spool_mbox(&mbox_size);


@@ -95,15 +127,6 @@
     return DEFER;
   };


-  /* grok spamd address and port */
-  if( sscanf(CS spamd_address, "%s %u", tcp_addr, &tcp_port) != 2 ) {
-    log_write(0, LOG_MAIN|LOG_PANIC,
-         "spam acl condition: invalid spamd address: '%s'", spamd_address);
-    fclose(mbox_file);
-    close(spamd_sock);
-    return DEFER;
-  };
-
   if (ip_connect(spamd_sock, AF_INET, tcp_addr, tcp_port, 5) < 0) {
     log_write(0, LOG_MAIN|LOG_PANIC,
          "spam acl condition: spamd connection to %s, port %u failed: %s", tcp_addr, tcp_port, strerror(errno));
@@ -115,9 +138,10 @@
   /* now we are connected to spamd on spamd_sock */
   snprintf(CS spamd_buffer,
            sizeof(spamd_buffer),
-           "REPORT SPAMC/1.2\r\nUser: %s\r\nContent-length: %lld\r\n\r\n",
+           "REPORT SPAMC/1.3\r\nUser: %s\r\nContent-length: %lld\r\nSender: %s\r\n\r\n",
            user_name,
-           mbox_size);
+           mbox_size,
+           sender_address);


/* send our request */
if (send(spamd_sock, spamd_buffer, Ustrlen(spamd_buffer), 0) < 0) {
@@ -171,34 +195,53 @@
/* reading done */
close(spamd_sock);

+  p = spamd_buffer;
+
   /* dig in the spamd output and put the report in a multiline header, if requested */
-  if( sscanf(CS spamd_buffer,"SPAMD/%s 0 EX_OK\r\nContent-length: %*u\r\n\r\n%lf/%lf\r\n%n",
-             spamd_version,&spamd_score,&spamd_threshold,&spamd_report_offset) != 3 ) {
-
-    /* try to fall back to pre-2.50 spamd output */
-    if( sscanf(CS spamd_buffer,"SPAMD/%s 0 EX_OK\r\nSpam: %*s ; %lf / %lf\r\n\r\n%n",
-               spamd_version,&spamd_score,&spamd_threshold,&spamd_report_offset) != 3 ) {
+  if( sscanf(CS p,"SPAMD/%d.%d %d %*s\r\n%n",&spamd_major,&spamd_minor,&spamd_status,&advance) != 3) {
       log_write(0, LOG_MAIN|LOG_PANIC,
-         "spam acl condition: cannot parse spamd output");
+         "spam acl condition: cannot parse spamd response");
       return DEFER;
-    };
-  };
+  }
+  p += advance;
+
+  if( spamd_status != 0 ) {
+      log_write(0, LOG_MAIN,
+         "spam acl condition: spamd returns status %d");
+      return DEFER;
+  }
+
+  /* process header */
+  while (*p && (*p!='\r' || *(p+1)!='\n')) {
+      /* process header field here */
+      /* skip header field */
+      while (*p && (*p!='\r' || *(p+1)!='\n')) ++p;
+      if (*p) p+=2;
+  }
+  /* skip empty line after header */
+  if (*p) p+=2;
+
+  /* process body */
+  if( sscanf(CS p,"%lf/%lf\r\n%n",&spamd_score,&spamd_threshold,&advance) != 2 ) {
+      log_write(0, LOG_MAIN|LOG_PANIC,
+             "spam acl condition: cannot parse spamd response body");
+      return DEFER;
+  }
+  p+=advance;


   /* Create report. Since this is a multiline string,
   we must hack it into shape first */
-  p = &spamd_buffer[spamd_report_offset];
-  q = spam_report_buffer;
+  spam_report = NULL;
+  capacity = length = 0;
   while (*p != '\0') {
     /* skip \r */
     if (*p == '\r') {
       p++;
       continue;
     };
-    *q = *p;
-    q++;
+    spam_report = string_cat(spam_report,&capacity,&length,p,1);
     if (*p == '\n') {
-      *q = '\t';
-      q++;
+      spam_report = string_cat(spam_report,&capacity,&length,CUS "\t",1);
       /* eat whitespace */
       while( (*p <= ' ') && (*p != '\0') ) {
         p++;
@@ -207,39 +250,28 @@
     };
     p++;
   };
-  /* NULL-terminate */
-  *q = '\0';
-  q--;
   /* cut off trailing leftovers */
-  while (*q <= ' ') {
-    *q = '\0';
-    q--;
-  };
-  spam_report = spam_report_buffer;
+  while (length>0 && spam_report[length-1]<=' ') --length;
+  /* NUL-terminate */
+  spam_report = string_cat(spam_report,&capacity,&length,CUS "",1);


   /* create spam bar */
   spamd_score_char = spamd_score > 0 ? '+' : '-';
-  j = abs((int)(spamd_score));
-  i = 0;
-  if( j != 0 ) {
-    while((i < j) && (i <= MAX_SPAM_BAR_CHARS))
-       spam_bar_buffer[i++] = spamd_score_char;
-  }
-  else{
-    spam_bar_buffer[0] = '/';
-    i = 1;
+  spam_bar = NULL;
+  length = capacity = 0;
+  if ((j = abs((int)(spamd_score)))==0) {
+    spam_bar = string_cat(spam_bar,&capacity,&length,CUS "/",1);
   }
-  spam_bar_buffer[i] = '\0';
-  spam_bar = spam_bar_buffer;
+  else {
+    for (i=0; i<j && i<=MAX_SPAM_BAR_CHARS; ++i) spam_bar = string_cat(spam_bar,&capacity,&length,&spamd_score_char,1);
+  }
+  spam_bar[length]='\0';


/* create "float" spam score */
- snprintf(CS spam_score_buffer, sizeof(spam_score_buffer),"%.1f", spamd_score);
- spam_score = spam_score_buffer;
+ spam_score = string_sprintf("%.1f", spamd_score);

/* create "int" spam score */
- j = (int)(spamd_score*10);
- snprintf(CS spam_score_int_buffer, sizeof(spam_score_int_buffer), "%d", j);
- spam_score_int = spam_score_int_buffer;
+ spam_score_int = string_sprintf("%d",(int)(spamd_score*10));

   /* compare threshold against score */
   if (spamd_score >= spamd_threshold) {
@@ -251,10 +283,18 @@
     spam_rc = FAIL;
   };


-  /* remember user name and "been here" for it */
-  Ustrcpy(prev_user_name, user_name);
-  spam_ok = 1;
-
+  /* cache result */
+  *entry = store_get(sizeof(struct SpamCache));
+  (*entry)->tcp_addr = tcp_addr;
+  (*entry)->tcp_port = tcp_port;
+  (*entry)->user_name = user_name;
+  (*entry)->spam_score = spam_score;
+  (*entry)->spam_score_int = spam_score_int;
+  (*entry)->spam_bar = spam_bar;
+  (*entry)->spam_report = spam_report;
+  (*entry)->spam_rc = spam_rc;
+  (*entry)->next = (struct SpamCache*)0;
+
   if (override) {
     /* always return OK, no matter what the score */
     return OK;
@@ -263,3 +303,8 @@
     return spam_rc;
   };
 }
+
+void spam_reset(void)
+{
+  spamCache = (struct SpamCache*)0;
+}
--- src/spool_mbox.c.orig    2003-09-02 11:28:08.000000000 +0200
+++ src/spool_mbox.c    2003-09-02 11:29:13.000000000 +0200
@@ -16,7 +16,7 @@
 /* externals, we must reset them on unspooling */
 extern int demime_ok;
 extern int malware_ok;
-extern int spam_ok;
+extern void spam_reset(void);
 extern struct file_extension *file_extensions;


int spool_mbox_ok = 0;
@@ -124,7 +124,7 @@
/* reset all exiscan state variables */
demime_ok = 0;
file_extensions = NULL;
- spam_ok = 0;
+ spam_reset();
malware_ok = 0;

if (spool_mbox_ok) {
--