- Remove deprecated RCORDER, required by FreeBSD-5.x - Remove deprecated WITH_PWCHECK - Add option WITH_KAS, which installs mail/libspamtest to use Kaspersky Antispam library Submitted by: Alexey V. Degtyarev <alexey@renatasystems.org>
886 lines
26 KiB
Text
886 lines
26 KiB
Text
--- src/EDITME 2007-01-22 16:29:54.000000000 +0000
|
|
+++ src/EDITME 2009-07-24 13:46:01.000000000 +0000
|
|
@@ -1133,4 +1133,9 @@
|
|
|
|
# ENABLE_DISABLE_FSYNC=yes
|
|
|
|
+CFLAGS= -I/usr/local/ap-mailfilter3/include
|
|
+EXTRALIBS_EXIM=-L/usr/local/ap-mailfilter3/lib -lspamtest
|
|
+LOCAL_SCAN_SOURCE=Local/kas_exim.c
|
|
+LOCAL_SCAN_HAS_OPTIONS=yes
|
|
+
|
|
# End of EDITME for Exim 4.
|
|
--- /dev/null 2009-07-24 13:44:23.000000000 +0000
|
|
+++ Local/kas_exim.c 2009-07-24 13:42:28.000000000 +0000
|
|
@@ -0,0 +1,871 @@
|
|
+
|
|
+#include "local_scan.h"
|
|
+#include <stdio.h>
|
|
+#include <stdlib.h>
|
|
+#include <stdarg.h>
|
|
+#include <fcntl.h>
|
|
+#include <sys/types.h>
|
|
+#include <syslog.h>
|
|
+#include <sys/stat.h>
|
|
+#include <errno.h>
|
|
+#include "spamtest.h"
|
|
+#include "msgstore.h"
|
|
+
|
|
+//#define SPAMTEST_USE_SYSLOG
|
|
+
|
|
+/* Default values for configuration options */
|
|
+#define KAS_CONNECT_TIMEOUT 60000
|
|
+#define KAS_DEFAULT_DOMAIN ""
|
|
+#define KAS_LOG_LEVEL 0
|
|
+#define KAS_ON_ERROR "tempfail"
|
|
+#define KAS_DATA_TIMEOUT 60000
|
|
+#define KAS_CONNECT_TO "tcp:127.0.0.1:2277"
|
|
+#define KAS_FILTERING_SIZE_LIMIT 512
|
|
+
|
|
+/* Result variables for configuration options */
|
|
+static uschar *result_default_domain;
|
|
+static uschar *result_connect_to;
|
|
+static uschar *result_on_error_str;
|
|
+static int result_on_error;
|
|
+
|
|
+/* Configuration options */
|
|
+//old
|
|
+static int st_connect_timeout = 0;
|
|
+static int debug_level = 0;
|
|
+static int st_rw_timeout = 0;
|
|
+static int filtering_size_limit=0;
|
|
+static uschar *st_mail_domain;
|
|
+static uschar *st_on_error;
|
|
+static uschar *st_address;
|
|
+//new
|
|
+static int kas_connect_timeout = 0;
|
|
+static int kas_log_level = 0;
|
|
+static int kas_data_timeout = 0;
|
|
+static int kas_filtering_size_limit = 0;
|
|
+static uschar *kas_default_domain;
|
|
+static uschar *kas_on_error;
|
|
+static uschar *kas_connect_to;
|
|
+
|
|
+optionlist local_scan_options[] =
|
|
+{/* The entries must appear in alphabetical order */
|
|
+ { "connect_timeout", opt_int, &st_connect_timeout },
|
|
+ { "kas_connect_timeout", opt_int, &kas_connect_timeout },
|
|
+ { "kas_connect_to", opt_stringptr, &kas_connect_to },
|
|
+ { "kas_data_timeout", opt_int, &kas_data_timeout },
|
|
+ { "kas_default_domain", opt_stringptr, &kas_default_domain },
|
|
+ { "kas_filtering_size_limit", opt_int, &kas_filtering_size_limit },
|
|
+ { "kas_log_level",opt_int,&kas_log_level},
|
|
+ { "kas_on_error",opt_stringptr,&kas_on_error},
|
|
+ { "local_mail_domain", opt_stringptr, &st_mail_domain },
|
|
+ { "log_level",opt_int,&debug_level},
|
|
+ { "on_spamtest_error",opt_stringptr,&st_on_error},
|
|
+ { "rw_timeout", opt_int, &st_rw_timeout },
|
|
+ { "spamtest_address", opt_stringptr, &st_address },
|
|
+ { "spamtest_filtering_size_limit", opt_int, &filtering_size_limit },
|
|
+};
|
|
+
|
|
+int local_scan_options_count = sizeof(local_scan_options)/sizeof(optionlist);
|
|
+
|
|
+
|
|
+int check_results(spamtest_session_t *s,spamtest_status_t *status,int *res,int fd,uschar **return_text);
|
|
+int proceed_accept(spamtest_status_t *status,int fd);
|
|
+void logger(const int log_level,const char *fmt,...);
|
|
+void prepare_config_parameters();
|
|
+void free_conf();
|
|
+
|
|
+
|
|
+int local_scan(int fd, uschar **return_text)
|
|
+{
|
|
+ int myfd;
|
|
+ char buf[4096];
|
|
+ int cnt = 0;
|
|
+ int totalcnt = 0;
|
|
+ int i;
|
|
+ header_line * tmp;
|
|
+ spamtest_session_t s;
|
|
+ int errorcode = STS_ERR_NO_ERR;
|
|
+ spamtest_status_t *status = NULL;
|
|
+ int rcpt_sent = 0;
|
|
+ int res = 0;
|
|
+ int rest = 0;
|
|
+ char *ctmp;
|
|
+
|
|
+ /* Initialize spamtest session */
|
|
+
|
|
+ prepare_config_parameters();
|
|
+
|
|
+ if(filtering_size_limit > 0)
|
|
+ {
|
|
+ struct stat sb;
|
|
+ fstat(fd,&sb);
|
|
+ if(sb.st_size > filtering_size_limit * 1024)
|
|
+ {
|
|
+ logger(LOG_DEBUG," Message larger than filtering_size_limit, accepting");
|
|
+ free_conf();
|
|
+ return LOCAL_SCAN_ACCEPT;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ bzero(&s,sizeof(s));
|
|
+ s.access_address = result_connect_to;
|
|
+ s.rw_timeout = st_rw_timeout;
|
|
+ s.connect_timeout = st_connect_timeout;
|
|
+ s.mail_domain = result_default_domain;
|
|
+
|
|
+ if ( s.access_address == NULL || s.access_address[0] == '\0' )
|
|
+ {
|
|
+ logger(LOG_ERR," Spamtest access address not defined, cannot process mail by spam filtering");
|
|
+ free_conf();
|
|
+ return LOCAL_SCAN_ACCEPT;
|
|
+ }
|
|
+
|
|
+ logger(LOG_DEBUG," Spam test session: %s %s %d %d",s.access_address,s.mail_domain,s.connect_timeout,s.rw_timeout);
|
|
+
|
|
+ if ( (errorcode = sts_init(&s)) < 0 )
|
|
+ {
|
|
+ logger(LOG_ERR," Unable to connect to %s (%s)",s.access_address,sts_strerror(errorcode));
|
|
+ free_conf();
|
|
+ return result_on_error;
|
|
+ };
|
|
+
|
|
+ if ( ! s.should_store_message )
|
|
+ {
|
|
+ /* SMTP mode, blackholing message because filter will send message */
|
|
+ logger(LOG_ERR," Filter in SMTP mode, Incorrect situation.");
|
|
+ //recipients_count = 0;
|
|
+ sts_close(&s);
|
|
+ free_conf();
|
|
+ return result_on_error;
|
|
+ }
|
|
+
|
|
+ if ( sender_address != NULL && sender_address[0] != '\0' )
|
|
+ {
|
|
+ if ( (errorcode = sts_set_sender(&s,sender_address)) != STS_ERR_NO_ERR )
|
|
+ {
|
|
+ logger(LOG_ERR," Unable to pass sender address %s(%d)",sender_address,errorcode);
|
|
+ free_conf();
|
|
+ return result_on_error;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if ( sender_host_address != NULL && sender_host_address[0] != '\0' )
|
|
+ {
|
|
+ if ( ( errorcode = sts_set_relay(&s,sender_host_address) ) != STS_ERR_NO_ERR )
|
|
+ {
|
|
+ logger(LOG_ERR," Unable to pass relay IP");
|
|
+ if(errorcode == STS_ERR_OUT_OF_MEMORY){
|
|
+ free_conf();
|
|
+ return result_on_error;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ rcpt_sent = 0;
|
|
+ for ( i = 0;i<recipients_count;i++)
|
|
+ {
|
|
+ if ( recipients_list[i].address != NULL && recipients_list[i].address[0] != '\0' )
|
|
+ {
|
|
+ if ( ( errorcode = sts_add_recipient(&s,recipients_list[i].address) ) != STS_ERR_NO_ERR )
|
|
+ {
|
|
+ logger(LOG_ERR," Unable to pass recipient '%s' to filter",recipients_list[i].address);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ rcpt_sent++;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ if ( rcpt_sent == 0 )
|
|
+ {
|
|
+ logger(LOG_DEBUG," Recipients not sent to filter. Continue.");
|
|
+ }
|
|
+
|
|
+ tmp = header_list;
|
|
+ while ( tmp != NULL )
|
|
+ {
|
|
+ if(tmp->type != '*')
|
|
+ {
|
|
+ logger(LOG_DEBUG,"Iterating a , %c %d %s|%d",tmp->type,tmp->slen,tmp->text,tmp->text[tmp->slen-1]);
|
|
+ if ( ( status = sts_send_body(&s,tmp->text,tmp->slen,STS_REINIT_TIMEOUT) ) == NULL )
|
|
+ {
|
|
+ /* failure. finish proceed */
|
|
+ logger(LOG_ERR," NULL status after sending header %s",tmp->text);
|
|
+ sts_close(&s);
|
|
+ }
|
|
+ else
|
|
+ { /* check status and finish if need */
|
|
+ logger(LOG_DEBUG," header %s sent to filter",tmp->text);
|
|
+ if ( check_results(&s,status,&res,fd,return_text) > 0 ){
|
|
+ free_conf();
|
|
+ return res;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ if(tmp==header_last)
|
|
+ break;
|
|
+ tmp = tmp->next;
|
|
+ }
|
|
+
|
|
+ logger(LOG_DEBUG," Headers sent");
|
|
+
|
|
+ if ( ( status = sts_send_body(&s,"\n",1,STS_REINIT_TIMEOUT) ) == NULL )
|
|
+ {
|
|
+ /* failure. finish proceed */
|
|
+ logger(LOG_ERR," NULL status after sending empty string(end of headers)");
|
|
+ sts_close(&s);
|
|
+ }
|
|
+ else
|
|
+ { /* check status and finish if need */
|
|
+ logger(LOG_DEBUG," empty string (end of headers) sent to filter");
|
|
+ if ( check_results(&s,status,&res,fd,return_text) > 0 ){
|
|
+ free_conf();
|
|
+ return res;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ logger(LOG_DEBUG," Enter sent");
|
|
+
|
|
+ i = 0;
|
|
+ ctmp = buf;
|
|
+ while ( (cnt = read(fd,ctmp,buf + sizeof(buf) - ctmp)) > 0 )
|
|
+ {
|
|
+ logger(LOG_DEBUG,"sending %d bytes to filter",cnt);
|
|
+ status = sts_send_body(&s,buf,cnt,STS_REINIT_TIMEOUT);
|
|
+ logger(LOG_DEBUG,"send");
|
|
+ if ( status == NULL )
|
|
+ {
|
|
+ // failure. finish proceed
|
|
+ logger(LOG_ERR," NULL status after sending part of body");
|
|
+ sts_close(&s);
|
|
+ }
|
|
+ else
|
|
+ { // check status and finish if need
|
|
+ logger(LOG_DEBUG," part of body sent to filter");
|
|
+ if ( check_results(&s,status,&res,fd,return_text) > 0 ){
|
|
+ free_conf();
|
|
+ return res;
|
|
+ }
|
|
+ }
|
|
+ };
|
|
+
|
|
+ logger(LOG_DEBUG," Total sent %d bytes of message",totalcnt);
|
|
+
|
|
+ status = sts_body_end(&s,STS_FLUSH_DATA);
|
|
+ logger(LOG_DEBUG," Body sent");
|
|
+
|
|
+
|
|
+ if ( check_results(&s,status,&res,fd,return_text) > 0 ){
|
|
+ free_conf();
|
|
+ return res;
|
|
+ }
|
|
+ else
|
|
+ sts_close(&s);
|
|
+
|
|
+ logger(LOG_DEBUG," EOF");
|
|
+
|
|
+ free_conf();
|
|
+ return LOCAL_SCAN_ACCEPT;
|
|
+
|
|
+}
|
|
+
|
|
+
|
|
+int check_results(spamtest_session_t *s,spamtest_status_t *status,int *res,int fd,uschar **return_text)
|
|
+{
|
|
+ logger(LOG_DEBUG," Check results started.");
|
|
+ switch ( status->status )
|
|
+ {
|
|
+ case STS_SMTP_ACCEPT:
|
|
+ case STS_BLACKHOLE:
|
|
+ logger(LOG_DEBUG," SMTP_ACCEPT or BLACK_HOLE.");
|
|
+ recipients_count = 0;
|
|
+ sts_close(s);
|
|
+ *res = LOCAL_SCAN_ACCEPT;
|
|
+ return 1;
|
|
+ case STS_REJECT:
|
|
+ logger(LOG_DEBUG," REJECT");
|
|
+ *return_text=(uschar *)status->reason;
|
|
+ sts_close(s);
|
|
+ *res = LOCAL_SCAN_REJECT;
|
|
+ return 1;
|
|
+ case STS_FILTER_ERROR:
|
|
+ logger(LOG_DEBUG," FILTER_ERROR");
|
|
+ sts_close(s);
|
|
+ *res = result_on_error;
|
|
+ return 1;
|
|
+ case STS_ACCEPT:
|
|
+ logger(LOG_DEBUG," ACCEPT");
|
|
+ if ( proceed_accept(status,fd) != 0 )
|
|
+ *res = result_on_error;
|
|
+ else
|
|
+ *res = LOCAL_SCAN_ACCEPT;
|
|
+ sts_close(s);
|
|
+ return 1;
|
|
+ case STS_CONTINUE:
|
|
+ logger(LOG_DEBUG," CONTINUE");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+}
|
|
+
|
|
+extern int recipients_list_max;
|
|
+int proceed_accept(spamtest_status_t *status,int fd)
|
|
+{
|
|
+ spamtest_status_t *st;
|
|
+ message_status_t *mst;
|
|
+ int i;
|
|
+ int rcptn;
|
|
+ int good_rcpt_cnt = 0,k;
|
|
+ int found = 0;
|
|
+
|
|
+ logger(LOG_DEBUG," accept_message started.");
|
|
+
|
|
+ st = glue_actions(status);
|
|
+
|
|
+ if (st==NULL) {
|
|
+ logger(LOG_ERR," glue_actions return NULL" );
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ mst = &st->ms_array[0] ;
|
|
+
|
|
+ rcptn = rcptlist_count(mst->rcpts);
|
|
+ for( k=0; k<rcptn; k++)
|
|
+ if(strlen(rcptlist_getn(mst->rcpts, k))>0)
|
|
+ good_rcpt_cnt++;
|
|
+ if(good_rcpt_cnt <1)
|
|
+ {
|
|
+ logger(LOG_INFO," Empty recipient list");
|
|
+ recipients_count = 0;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if ( (mst != NULL) && (mst->action == STS_ACTION_CHANGE) )
|
|
+ {
|
|
+ logger(LOG_DEBUG," spamtest status returned STS_ACTION_CHANGE for message.");
|
|
+
|
|
+ // cleanup old rcpts list
|
|
+ recipients_count = 0;
|
|
+ recipients_list = NULL;
|
|
+ recipients_list_max = 0;
|
|
+ for (i = 0; i<rcptlist_count(mst->rcpts);i++)
|
|
+ {
|
|
+ char *rcpt = rcptlist_getn(mst->rcpts,i);
|
|
+ if(rcpt)
|
|
+ {
|
|
+ logger(LOG_DEBUG,"Adding recipient: %s",rcpt);
|
|
+ receive_add_recipient(string_copy(US rcpt),-1);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ logger(LOG_DEBUG," new recipients_count=%d",recipients_count);
|
|
+
|
|
+ if ( sender_address[0] != '\0' )
|
|
+ {
|
|
+ if (strcasecmp(mst->mailfrom,sender_address) != 0 )
|
|
+ { /* changing sender address */
|
|
+ logger(LOG_DEBUG," senders different");
|
|
+ sender_address = string_copy( US mst->mailfrom );
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ logger(LOG_DEBUG," senders equivalented");
|
|
+ }
|
|
+ }
|
|
+ logger(LOG_DEBUG," Actions count = %d",mst->action_count);
|
|
+ for (i=0;i<mst->action_count;i++)
|
|
+ {
|
|
+ header_line * tmp;
|
|
+ header_line * matched;
|
|
+ header_line * last;
|
|
+ int deleted_count = 0;
|
|
+ int matched_count = 0;
|
|
+ int hdrlen = strlen(mst->act_array[i].header);
|
|
+
|
|
+ switch ( mst->act_array[i].type )
|
|
+ {
|
|
+ case STS_HEADER_DEL:
|
|
+ logger(LOG_DEBUG," Delete header %s",mst->act_array[i].header);
|
|
+ tmp = header_list;
|
|
+ while ( tmp != NULL )
|
|
+ {
|
|
+ if ( (tmp->type != '*') &&
|
|
+ (tmp->slen > 0) &&
|
|
+ (strncasecmp(tmp->text,
|
|
+ mst->act_array[i].header,
|
|
+ hdrlen < tmp->slen ? hdrlen:tmp->slen) == 0 )
|
|
+ )
|
|
+ {
|
|
+ deleted_count++;
|
|
+ tmp->type = '*';
|
|
+ }
|
|
+ tmp = tmp->next;
|
|
+ }
|
|
+ logger(LOG_DEBUG," deleted %d headers",deleted_count);
|
|
+ break;
|
|
+ case STS_HEADER_ADD:
|
|
+ logger(LOG_DEBUG," Add value to header %s %s",mst->act_array[i].header,mst->act_array[i].value);
|
|
+ tmp = header_list;
|
|
+ matched = NULL;
|
|
+ while ( tmp != NULL )
|
|
+ {
|
|
+ logger(LOG_DEBUG,"Iterating, %c %d %s",tmp->type,tmp->slen,tmp->text);
|
|
+ if ( (tmp->slen > 0) &&
|
|
+ (strncasecmp(tmp->text,
|
|
+ mst->act_array[i].header,
|
|
+ hdrlen < tmp->slen ? hdrlen:tmp->slen) == 0 )
|
|
+ )
|
|
+ {
|
|
+ if ( matched != NULL )
|
|
+ {
|
|
+ if ( (tmp->type != '*') || ( matched->type == '*' ) )
|
|
+ matched = tmp;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ matched = tmp;
|
|
+ }
|
|
+ }
|
|
+ tmp = tmp->next;
|
|
+ if(matched)
|
|
+ break;
|
|
+ }
|
|
+ if ( matched != NULL )
|
|
+ { /* adding value to exits header */
|
|
+ logger(LOG_DEBUG,"Found matched header, %c %d %s",matched->type,matched->slen,matched->text);
|
|
+ if ( matched->type == '*' )
|
|
+ {
|
|
+ matched->text = string_sprintf("%s: %s\n",mst->act_array[i].header,mst->act_array[i].value);
|
|
+ matched->slen = strlen(matched->text);
|
|
+ matched->type = ' ';
|
|
+ matched->type = header_checkname(matched,0);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ logger(LOG_DEBUG,"Appending value to existing header value");
|
|
+ matched->text = string_sprintf("%s %s\n",matched->text,mst->act_array[i].value);
|
|
+ matched->slen += (strlen(mst->act_array[i].value) + 2);
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ { /* add new header in case when this header not found */
|
|
+ logger(LOG_DEBUG," We must add value to header %s but this header not found.",mst->act_array[i].header);
|
|
+ header_add(' ',"%s: %s\n",mst->act_array[i].header,mst->act_array[i].value);
|
|
+ }
|
|
+ logger(LOG_DEBUG," Add header %s %s",mst->act_array[i].header,mst->act_array[i].value);
|
|
+ break;
|
|
+
|
|
+
|
|
+ case STS_HEADER_PREPEND:
|
|
+ logger(LOG_DEBUG," Prepend value to header %s %s",mst->act_array[i].header,mst->act_array[i].value);
|
|
+ tmp = header_list;
|
|
+ matched = NULL;
|
|
+ while ( tmp != NULL )
|
|
+ {
|
|
+ logger(LOG_DEBUG,"Iterating, %c %d %s",tmp->type,tmp->slen,tmp->text);
|
|
+ if ( (tmp->slen > 0) &&
|
|
+ (strncasecmp(tmp->text,
|
|
+ mst->act_array[i].header,
|
|
+ hdrlen < tmp->slen ? hdrlen:tmp->slen) == 0 )
|
|
+ )
|
|
+ {
|
|
+ if ( matched != NULL )
|
|
+ {
|
|
+ if ( (tmp->type != '*') || ( matched->type == '*' ) )
|
|
+ matched = tmp;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ matched = tmp;
|
|
+ }
|
|
+ }
|
|
+ tmp = tmp->next;
|
|
+ if(matched)
|
|
+ break;
|
|
+ }
|
|
+ if ( matched != NULL )
|
|
+ { /* adding value to exits header */
|
|
+ logger(LOG_DEBUG,"Found matched header, %c %d %s",matched->type,matched->slen,matched->text);
|
|
+ if ( matched->type == '*' )
|
|
+ {
|
|
+ matched->text = string_sprintf("%s: %s\n",mst->act_array[i].header,mst->act_array[i].value);
|
|
+ matched->slen = strlen(matched->text);
|
|
+ matched->type = ' ';
|
|
+ matched->type = header_checkname(matched,0);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ int l = strlen(mst->act_array[i].header);
|
|
+ char *p = matched->text+l+2;
|
|
+ matched->text[l]=0;
|
|
+ matched->text = string_sprintf("%s: %s %s",matched->text,
|
|
+ mst->act_array[i].value,p);
|
|
+ matched->slen += (strlen(mst->act_array[i].value) + 1);
|
|
+ logger(LOG_DEBUG,"Prepending value %s to existing header value %s|",
|
|
+ mst->act_array[i].value, matched->text);
|
|
+
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ { /* add new header in case when this header not found */
|
|
+ logger(LOG_DEBUG," We must add value to header %s but this header not found.",mst->act_array[i].header);
|
|
+ header_add(' ',"%s: %s\n",mst->act_array[i].header,mst->act_array[i].value);
|
|
+ }
|
|
+ logger(LOG_DEBUG," Prepend header %s %s",mst->act_array[i].header,mst->act_array[i].value);
|
|
+ break;
|
|
+
|
|
+ case STS_HEADER_CHG:
|
|
+ logger(LOG_DEBUG," Change header %s %s",mst->act_array[i].header,mst->act_array[i].value);
|
|
+ tmp = header_list;
|
|
+ matched = NULL;
|
|
+ matched_count = 0;
|
|
+ while ( tmp != NULL )
|
|
+ {
|
|
+ int hdrlen = strlen(mst->act_array[i].header);
|
|
+ if ( (tmp->type != '*') &&
|
|
+ (tmp->slen > 0) &&
|
|
+ (strncasecmp(tmp->text,
|
|
+ mst->act_array[i].header,
|
|
+ hdrlen < tmp->slen ? hdrlen:tmp->slen) == 0 )
|
|
+ )
|
|
+ {
|
|
+ matched = tmp;
|
|
+ matched_count++;
|
|
+ }
|
|
+ tmp = tmp->next;
|
|
+ }
|
|
+ if ( matched != NULL )
|
|
+ {
|
|
+ matched->text = string_sprintf("%s: %s\n",mst->act_array[i].header,mst->act_array[i].value);
|
|
+ matched->slen += (strlen(mst->act_array[i].value) + strlen(mst->act_array[i].header) + 3);
|
|
+ if ( matched_count > 1 )
|
|
+ {
|
|
+ tmp = header_list;
|
|
+ while ( (tmp != NULL) && matched_count > 1 )
|
|
+ {
|
|
+ if ( (tmp->type != '*') &&
|
|
+ (tmp->slen > 0) &&
|
|
+ (strncasecmp(tmp->text,
|
|
+ mst->act_array[i].header,
|
|
+ hdrlen < tmp->slen ? hdrlen:tmp->slen) == 0 ) &&
|
|
+ (tmp != matched)
|
|
+ )
|
|
+ {
|
|
+ tmp->type = '*';
|
|
+ matched_count--;
|
|
+ }
|
|
+ tmp = tmp->next;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ logger(LOG_DEBUG," We must change value for header %s but this header not found.Adding new",mst->act_array[i].header);
|
|
+ header_add(' ',"%s: %s\n",mst->act_array[i].header,mst->act_array[i].value);
|
|
+ }
|
|
+ break;
|
|
+ case STS_HEADER_NEW:
|
|
+ logger(LOG_DEBUG," New header %s: %s",mst->act_array[i].header,mst->act_array[i].value);
|
|
+
|
|
+ tmp = header_list;
|
|
+ matched = NULL;
|
|
+ while ( tmp != NULL )
|
|
+ {
|
|
+ if ( (tmp->type != '*') &&
|
|
+ (tmp->slen > 0) &&
|
|
+ (strncasecmp(tmp->text,
|
|
+ mst->act_array[i].header,
|
|
+ hdrlen < tmp->slen ? hdrlen:tmp->slen) == 0 )
|
|
+ )
|
|
+ {
|
|
+ matched = tmp;
|
|
+ }
|
|
+ tmp = tmp->next;
|
|
+ }
|
|
+ if ( matched != NULL && matched != header_last )
|
|
+ {
|
|
+ if(matched == header_last)
|
|
+ {
|
|
+ logger(LOG_DEBUG,"Header %s is last in message, adding below",
|
|
+ mst->act_array[i].header);
|
|
+ header_add(' ',"%s: %s\n",mst->act_array[i].header,mst->act_array[i].value);
|
|
+ }
|
|
+ else
|
|
+ { /* adding value after last header with this name */
|
|
+ logger(LOG_DEBUG,"Some headers %s found",mst->act_array[i].header);
|
|
+ tmp = header_last;
|
|
+ header_add(' ',"%s: %s\n",mst->act_array[i].header,mst->act_array[i].value);
|
|
+ tmp->next->next = matched->next;
|
|
+ matched->next = tmp->next;
|
|
+ tmp->next = NULL;
|
|
+ header_last = tmp;
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ { /* add new header at the end of headers list */
|
|
+ logger(LOG_DEBUG,"Headers %s not found",mst->act_array[i].header);
|
|
+ header_add(' ',"%s: %s\n",mst->act_array[i].header,mst->act_array[i].value);
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ default:
|
|
+ logger(LOG_INFO," Invalid action type %d",mst->act_array[i].type);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ else if ( (mst != NULL) && (mst->action == STS_ACTION_BOUNCE) )
|
|
+ { /* bounce message */
|
|
+ header_line *tmp;
|
|
+ message_t *bounced_mess;
|
|
+
|
|
+ logger(LOG_DEBUG," bouncing message.");
|
|
+
|
|
+ /* preparing bounced message */
|
|
+ bounced_mess = message_init();
|
|
+ if ( bounced_mess == NULL )
|
|
+ {
|
|
+ logger(LOG_ERR," Unable to allocate memory for bounced message");
|
|
+ return -1;
|
|
+ }
|
|
+ logger(LOG_DEBUG," new message allocated.");
|
|
+
|
|
+ /* copying data from filter to prepared message (for automatically creating headers list ) */
|
|
+ for (i=0;i<msgtext_count_chunks(mst->bouncemsg);i++ )
|
|
+ {
|
|
+ if ( message_put_body(bounced_mess,msgtext_get_chunk(mst->bouncemsg,i),msgtext_chunk_len(mst->bouncemsg,i)) == -1 )
|
|
+ {
|
|
+ logger(LOG_ERR," unable to copy data from filter to bounced message");
|
|
+ message_free(bounced_mess);
|
|
+ return -1;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ logger(LOG_DEBUG," message_put_body - ok.");
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* deleting exim old headers */
|
|
+ tmp = header_list;
|
|
+ while ( tmp != NULL )
|
|
+ {
|
|
+ tmp->type = '*';
|
|
+ tmp = tmp->next;
|
|
+ };
|
|
+ logger(LOG_DEBUG," headers deleted.");
|
|
+
|
|
+ /* adding new headers */
|
|
+ for ( i = 0;i < bounced_mess->headers->used; i++ )
|
|
+ {
|
|
+ header_add(' ',"%s: %s",bounced_mess->headers->array[i].header.hptr,bounced_mess->headers->array[i].value.hptr);
|
|
+ logger(LOG_DEBUG," added header: %s: %s",bounced_mess->headers->array[i].header.hptr,bounced_mess->headers->array[i].value.hptr);
|
|
+ }
|
|
+
|
|
+ /* replacing rcpts */
|
|
+ recipients_count = 0;
|
|
+ recipients_list = NULL;
|
|
+ recipients_list_max = 0;
|
|
+
|
|
+ if ( rcptlist_count(bounced_mess->recipients) > 0 )
|
|
+ {
|
|
+ for (i = 0; i<rcptlist_count(bounced_mess->recipients);i++)
|
|
+ {
|
|
+ char *rcpt = rcptlist_getn(bounced_mess->recipients,i);
|
|
+ if ( rcpt == NULL )
|
|
+ {
|
|
+ logger(LOG_ERR," unable to get recipient %d",i);
|
|
+ message_free(bounced_mess);
|
|
+ return -1;
|
|
+ }
|
|
+ logger(LOG_DEBUG," rcpt = %s",rcpt);
|
|
+ receive_add_recipient(string_copy(US rcpt),-1);
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ if ( sender_address[0] != '\0' )
|
|
+ receive_add_recipient(string_copy(US sender_address),-1);
|
|
+ else
|
|
+ {
|
|
+ logger(LOG_ERR," Unable to get sender address to bounce, failed");
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+ /* cleaning sender ( must be empty string ) */
|
|
+ logger(LOG_DEBUG," sender address emptied");
|
|
+ sender_address = "";
|
|
+
|
|
+ /* writing new message body */
|
|
+ lseek(fd,SPOOL_DATA_START_OFFSET,SEEK_SET);
|
|
+
|
|
+ if ( get_message_bodychunks_count(bounced_mess) > 0 )
|
|
+ {
|
|
+ for (i=0;i<get_message_bodychunks_count(bounced_mess);i++ )
|
|
+ {
|
|
+ if ( write(fd,get_message_bodychunk_ptr(bounced_mess,i),get_message_bodychunk_len(bounced_mess,i)) == -1 )
|
|
+ {
|
|
+ logger(LOG_ERR," unable to write bounced message body.");
|
|
+ message_free(bounced_mess);
|
|
+ return -1;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ logger(LOG_DEBUG," written part of body to file");
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ char *autogenmsg = "Autogenerated message for bouncing\n\n\n";
|
|
+ if ( write(fd,autogenmsg,strlen(autogenmsg)) == -1 )
|
|
+ {
|
|
+ logger(LOG_ERR," unable to write bounced message body.");
|
|
+ message_free(bounced_mess);
|
|
+ return -1;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ logger(LOG_DEBUG," written autogenerated message to file");
|
|
+ }
|
|
+ }
|
|
+
|
|
+ message_free(bounced_mess);
|
|
+ logger(LOG_DEBUG," bouncing finished");
|
|
+
|
|
+
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ if ( mst != NULL )
|
|
+ logger(LOG_ERR," Invalid action %d",mst->action);
|
|
+ else
|
|
+ logger(LOG_ERR," mst is NULL");
|
|
+ }
|
|
+
|
|
+ logger(LOG_DEBUG," Message accepted.");
|
|
+ return 0;
|
|
+
|
|
+}
|
|
+
|
|
+
|
|
+void logger(const int log_level,const char *fmt,...)
|
|
+{
|
|
+ va_list params;
|
|
+ char buf[512];
|
|
+ /* LOG_ERR == 3 is minimum level for us */
|
|
+ if (log_level <= (debug_level+3))
|
|
+ {
|
|
+ va_start(params,fmt);
|
|
+ vsnprintf(buf,(size_t)512,fmt,params);
|
|
+ debug_printf("-ls- %s\n",buf);
|
|
+#ifdef SPAMTEST_USE_SYSLOG
|
|
+ va_start(params,fmt);
|
|
+ vsyslog(log_level>LOG_DEBUG?LOG_DEBUG:log_level,fmt,params);
|
|
+#else
|
|
+ log_write(0,LOG_MAIN,"spamtest: %s",buf);
|
|
+#endif
|
|
+ va_end(params);
|
|
+ }
|
|
+}
|
|
+
|
|
+void prepare_config_parameters()
|
|
+{
|
|
+ int len;
|
|
+ /*
|
|
+ * Firstly we are looking for new parameter,
|
|
+ * then - for old parameter, else - we use
|
|
+ * default value.
|
|
+ */
|
|
+ if (kas_log_level != 0)
|
|
+ debug_level = kas_log_level;
|
|
+ if (debug_level == 0)
|
|
+ debug_level = KAS_LOG_LEVEL;
|
|
+
|
|
+ if (kas_connect_timeout != 0)
|
|
+ st_connect_timeout = kas_connect_timeout*1000;
|
|
+ if (st_connect_timeout == 0)
|
|
+ st_connect_timeout = KAS_CONNECT_TIMEOUT;
|
|
+
|
|
+ if (kas_data_timeout != 0)
|
|
+ st_rw_timeout = kas_data_timeout*1000;
|
|
+ if (st_rw_timeout == 0)
|
|
+ st_rw_timeout = KAS_DATA_TIMEOUT;
|
|
+
|
|
+ if (kas_filtering_size_limit != 0)
|
|
+ filtering_size_limit = kas_filtering_size_limit;
|
|
+ if (filtering_size_limit == 0)
|
|
+ filtering_size_limit = KAS_FILTERING_SIZE_LIMIT;
|
|
+
|
|
+
|
|
+ if (kas_default_domain != NULL){
|
|
+ len = strlen(kas_default_domain);
|
|
+ result_default_domain = malloc(len+1);
|
|
+ strncpy(result_default_domain,kas_default_domain,len);
|
|
+ result_default_domain[len]='\0';
|
|
+ }
|
|
+ else if (st_mail_domain != NULL){
|
|
+ len = strlen(st_mail_domain);
|
|
+ result_default_domain = malloc(len+1);
|
|
+ strncpy(result_default_domain,st_mail_domain,len);
|
|
+ result_default_domain[len]='\0';
|
|
+ }
|
|
+ else{
|
|
+ len = strlen(KAS_DEFAULT_DOMAIN);
|
|
+ result_default_domain = malloc(len+1);
|
|
+ strncpy(result_default_domain,KAS_DEFAULT_DOMAIN,len);
|
|
+ result_default_domain[len]='\0';
|
|
+ }
|
|
+
|
|
+ if (kas_connect_to != NULL){
|
|
+ len = strlen(kas_connect_to);
|
|
+ result_connect_to = malloc(len+1);
|
|
+ strncpy(result_connect_to,kas_connect_to,len);
|
|
+ result_connect_to[len]='\0';
|
|
+ }
|
|
+ else if (st_address != NULL){
|
|
+ len = strlen(st_address);
|
|
+ result_connect_to = malloc(len+1);
|
|
+ strncpy(result_connect_to,st_address,len);
|
|
+ result_connect_to[len]='\0';
|
|
+ }/* one parameter have no default,
|
|
+ * because else we can't disable kas-exim in config file*/
|
|
+
|
|
+ if (kas_on_error != NULL){
|
|
+ len = strlen(kas_on_error);
|
|
+ result_on_error_str = malloc(len+1);
|
|
+ strncpy(result_on_error_str,kas_on_error,len);
|
|
+ result_on_error_str[len]='\0';
|
|
+ }
|
|
+ else if (st_on_error != NULL){
|
|
+ len = strlen(st_on_error);
|
|
+ result_on_error_str = malloc(len+1);
|
|
+ strncpy(result_on_error_str,st_on_error,len);
|
|
+ result_on_error_str[len]='\0';
|
|
+ }
|
|
+ else{
|
|
+ len = strlen(KAS_ON_ERROR);
|
|
+ result_on_error_str = malloc(len+1);
|
|
+ strncpy(result_on_error_str,KAS_ON_ERROR,len);
|
|
+ result_on_error_str[len]='\0';
|
|
+ }
|
|
+
|
|
+ if ( strcasecmp(result_on_error_str,"accept") == 0 ){
|
|
+ result_on_error = LOCAL_SCAN_ACCEPT;
|
|
+ }
|
|
+ else if ( strcasecmp(result_on_error_str,"reject") == 0 ){
|
|
+ result_on_error = LOCAL_SCAN_REJECT;
|
|
+ }
|
|
+ else if ( strcasecmp(result_on_error_str,"tempfail") == 0 ){
|
|
+ result_on_error = LOCAL_SCAN_TEMPREJECT;
|
|
+ }
|
|
+ else{
|
|
+ logger(LOG_ERR,"Invalid value for parameter on_spamtest_error. Will use 'accept'");
|
|
+ result_on_error = LOCAL_SCAN_ACCEPT;
|
|
+ }
|
|
+}
|
|
+
|
|
+void free_conf(){
|
|
+ free(result_default_domain);
|
|
+ free(result_connect_to);
|
|
+ free(result_on_error_str);
|
|
+}
|
|
+
|