diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2011-08-20 17:43:21 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2011-08-20 17:46:45 +0300 |
commit | dbb042cb96ec855120b11b27c4ea4a72a6920ed8 (patch) | |
tree | 8115aca3a885ecfa872cfa8d64b7087760c96762 | |
parent | 65d790e09a96dd8319a9a3399ce85b40c7b57e77 (diff) | |
download | mailfromd-dbb042cb96ec855120b11b27c4ea4a72a6920ed8.tar.gz mailfromd-dbb042cb96ec855120b11b27c4ea4a72a6920ed8.tar.bz2 |
Implement dspam support.
* configure.ac: Check for dspam
* mflib/.gitignore: Add dspam.h
* mflib/Makefile.am: Build dspam.h
Install dspam.mf
* mflib/dspam.mf: New file.
* src/Makefile.am (mailfromd_LDADD): Add DSPAM_LIBS
(INCLUDES): Add DSPAM_CFLAGS
* src/builtin/Makefile.am (BI_FILES): Add dspam.bi.
(INCLUDES): Add DSPAM_CFLAGS
* src/builtin/dspam.bi: New file.
* src/main.c (mailfromd_show_defaults): Reflect dspam support.
-rw-r--r-- | configure.ac | 21 | ||||
-rw-r--r-- | mflib/.gitignore | 1 | ||||
-rw-r--r-- | mflib/Makefile.am | 4 | ||||
-rw-r--r-- | mflib/dspam.mf | 54 | ||||
-rw-r--r-- | src/Makefile.am | 6 | ||||
-rw-r--r-- | src/builtin/.gitignore | 1 | ||||
-rw-r--r-- | src/builtin/Makefile.am | 4 | ||||
-rw-r--r-- | src/builtin/dspam.bi | 261 | ||||
-rw-r--r-- | src/main.c | 7 |
9 files changed, 352 insertions, 7 deletions
diff --git a/configure.ac b/configure.ac index 926d8f23..4a52ef05 100644 --- a/configure.ac +++ b/configure.ac @@ -46,6 +46,7 @@ AC_PROG_RANLIB AC_PROG_YACC AC_PROG_LEX AC_PROG_LN_S +PKG_PROG_PKG_CONFIG # Debugging mode MU_DEBUG_MODE @@ -334,6 +335,7 @@ if test "$status_dbm" = "no"; then AC_MSG_ERROR([Cannot find DBM library to link with.]) fi +## Preprocessor AC_ARG_WITH([preprocessor], AC_HELP_STRING([--without-preprocessor], [do not use external preprocessor]), @@ -650,6 +652,23 @@ if test "$status_geoip" != "no"; then fi fi +## Dspam +status_dspam=maybe +AC_SUBST(DSPAM_CFLAGS) +AC_SUBST(DSPAM_LIBS) + +AC_ARG_WITH([dspam], + [status_dspam=$withval], + [status_dspam=no], + [status_dspam=maybe]) + +if test $status_dspam != no; then + PKG_CHECK_MODULES([DSPAM], [dspam], [status_dspam=yes], [status_dspam=no]) +fi +if test "$status_dspam" = "yes"; then + AC_DEFINE([WITH_DSPAM], [1], [Enable use of DSPAM library]) +fi + # Doc hints. # Select a rendition level: # DISTRIB for stable releases (at most one dot in the version number) @@ -700,6 +719,7 @@ Readline (for mtasim)..................... $usereadline Documentation rendition type.............. $rendition Enable pmilter support.................... $enable_pmilter Enable GeoIP support...................... $status_geoip +Enable DSPAM support...................... $status_dspam IPv6 support.............................. $status_ipv6 ******************************************************************* @@ -723,6 +743,7 @@ rendition=$RENDITION syslog_async=$syslog_async enable_pmilter=$enable_pmilter status_geoip=$status_geoip +status_dspam=$status_dspam status_ipv6=$status_ipv6 ]) diff --git a/mflib/.gitignore b/mflib/.gitignore index 3e507dc9..9088ef1e 100644 --- a/mflib/.gitignore +++ b/mflib/.gitignore @@ -1,6 +1,7 @@ _register.h callout.mf dns.mf +dspam.h header_rename.mf email.h portprobe.mf diff --git a/mflib/Makefile.am b/mflib/Makefile.am index 457b03b3..c1d1c648 100644 --- a/mflib/Makefile.am +++ b/mflib/Makefile.am @@ -50,8 +50,8 @@ MF_FILES =\ verp.mf\ $(MF4_FILES:.mf4=.mf) -noinst_HEADERS = email.h sieve.h syslog.h _register.h status.ex -BUILT_SOURCES = email.h sieve.h syslog.h _register.h status.ex +noinst_HEADERS = dspam.h email.h sieve.h syslog.h _register.h status.ex +BUILT_SOURCES = dspam.h email.h sieve.h syslog.h _register.h status.ex EXTRA_DIST=$(inc_DATA) pp-setup $(MF4_FILES) mfex.awk mfh.awk diff --git a/mflib/dspam.mf b/mflib/dspam.mf new file mode 100644 index 00000000..15a376d3 --- /dev/null +++ b/mflib/dspam.mf @@ -0,0 +1,54 @@ +/* Constants for dspam interface. + Copyright (C) 2011 Sergey Poznyakoff + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +module 'dspam'. + +#prefix _MF_ + +# Operating Modes: +const DSM_PROCESS 0 /* Process message */ +const DSM_CLASSIFY 1 /* Classify message only (do not write changes) */ +const _DSM_MASK 0x000f +# Flags: +const DSF_SIGNATURE 0x0010 /* Signature Mode (Use a signature) */ +const DSF_NOISE 0x0020 /* Use Bayesian Noise Reduction */ +const DSF_WHITELIST 0x0040 /* Use Automatic Whitelisting */ + +# Tokenizers: +const DSZ_WORD (0<<8) /* Use WORD tokenizer */ +const DSZ_CHAIN (1<<8) /* Use CHAIN tokenizer */ +const DSZ_SBPH (2<<8) /* Use SBPH tokenizer */ +const DSZ_OSB (3<<8) /* Use OSB tokenizer */ +const _DSZ_MASK 0x0f00 + +# Training Modes: +const DST_TEFT (0<<12) /* Train Everything */ +const DST_TOE (1<<12) /* Train-on-Error */ +const DST_TUM (2<<12) /* Train-until-Mature */ +const _DST_MASK 0xf000 + +# Classifications: +const DSR_ISSPAM 0 /* Message is spam (learn as spam) */ +const DSR_ISINNOCENT 1 /* Message is innocent (learn as innocent) */ +const DSR_NONE 2 /* No predetermined classification (classify message) */ +const _DSR_MASK 0x000f + +# Sources: +const DSS_ERROR (0<<4) /* Misclassification by libdspam */ +const DSS_CORPUS (1<<4) /* Corpused message */ +const DSS_INOCULATION (2<<4) /* Message inoculation */ +const DSS_NONE (3<<4) /* No classification source (use only with DSR_NONE) */ +const _DSS_MASK 0x00f0
\ No newline at end of file diff --git a/src/Makefile.am b/src/Makefile.am index e301eb10..4400f8d4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -52,7 +52,8 @@ mailfromd_LDADD = \ ../gnu/libgnu.a\ $(MAILUTILS_LIBS)\ $(MILTER)\ - $(GEOIP_LIBS) + $(GEOIP_LIBS)\ + $(DSPAM_LIBS) noinst_HEADERS = \ bitmask.h\ @@ -122,7 +123,8 @@ INCLUDES = \ -I$(top_srcdir)/gnu\ -I../gnu\ -I$(top_srcdir)/src/builtin\ - $(MILTER_INCLUDES) + $(MILTER_INCLUDES)\ + $(DSPAM_CFLAGS) node-type.h: drivers.c $(AM_V_GEN) $(AWK) -v MODE=types -f $(top_srcdir)/src/drv.awk drivers.c > node-type.h diff --git a/src/builtin/.gitignore b/src/builtin/.gitignore index 15202284..e72cf081 100644 --- a/src/builtin/.gitignore +++ b/src/builtin/.gitignore @@ -6,6 +6,7 @@ curhdr.c db.c debug.c dns.c +dspam.c email.c geoip.c gethostname.c diff --git a/src/builtin/Makefile.am b/src/builtin/Makefile.am index fc025469..904cd02b 100644 --- a/src/builtin/Makefile.am +++ b/src/builtin/Makefile.am @@ -26,6 +26,7 @@ BI_FILES=\ db.bi\ debug.bi\ dns.bi\ + dspam.bi\ email.bi\ geoip.bi\ gethostname.bi\ @@ -76,7 +77,8 @@ INCLUDES = \ -I../gnu\ $(MILTER_INCLUDES)\ -I$(top_srcdir)/src\ - -I$(top_srcdir) + -I$(top_srcdir)\ + $(DSPAM_CFLAGS) builtin.h: Makefile.am diff --git a/src/builtin/dspam.bi b/src/builtin/dspam.bi new file mode 100644 index 00000000..a07451c3 --- /dev/null +++ b/src/builtin/dspam.bi @@ -0,0 +1,261 @@ +/* This file is part of Mailfromd. -*- c -*- + Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Sergey Poznyakoff + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +MF_COND(WITH_DSPAM) + +#include "srvcfg.h" +#undef HAVE_CONFIG_H +#include <libdspam.h> +#include "mflib/dspam.h" +#include "msg.h" + +MF_VAR(dspam_home, STRING, SYM_PRECIOUS); +MF_VAR(dspam_user, STRING, SYM_PRECIOUS); +MF_VAR(dspam_group, STRING, SYM_PRECIOUS); +#define DEFAULT_DSPAM_HOME_STR "dspam" +#define DEFAULT_DSPAM_HOME_LEN (sizeof(DEFAULT_DSPAM_HOME_STR)-1) +MF_VAR(dspam_probability, NUMBER); +MF_VAR(dspam_confidence, NUMBER); +MF_VAR(dspam_prec, NUMBER); +#define DEFAULT_DSPAM_PREC 3 + +static int _dspam_initialized; + +static void +_dspam_shutdown() +{ + dspam_shutdown_driver(NULL); +} + +struct transtab +{ + int trans_from; + int trans_to; +}; + +static struct transtab mode_trans[] = { + { _MF_DSM_PROCESS, DSM_PROCESS }, + { _MF_DSM_CLASSIFY, DSM_CLASSIFY } +}; + +static struct transtab flag_trans[] = { + { _MF_DSF_SIGNATURE, DSF_SIGNATURE }, + { _MF_DSF_NOISE, DSF_NOISE }, + { _MF_DSF_WHITELIST, DSF_WHITELIST } +}; + +static struct transtab tokenizer_trans[] = { + { _MF_DSZ_WORD, DSZ_WORD }, + { _MF_DSZ_CHAIN, DSZ_CHAIN }, + { _MF_DSZ_SBPH, DSZ_SBPH }, + { _MF_DSZ_OSB, DSZ_OSB } +}; + +static struct transtab tmod_trans[] = { + { _MF_DST_TEFT, DST_TEFT }, + { _MF_DST_TOE, DST_TOE }, + { _MF_DST_TUM, DST_TUM } +}; + +static struct transtab class_trans[] = { + { _MF_DSR_ISSPAM, DSR_ISSPAM }, + { _MF_DSR_ISINNOCENT, DSR_ISINNOCENT }, + { _MF_DSR_NONE, DSR_NONE } +}; + +static struct transtab source_trans[] = { + { _MF_DSS_ERROR, DSS_ERROR }, + { _MF_DSS_CORPUS, DSS_CORPUS }, + { _MF_DSS_INOCULATION, DSS_INOCULATION }, + { _MF_DSS_NONE, DSS_NONE } +}; + +static int +translate(struct transtab *tab, size_t count, int num, int *ret) +{ + for (; count; tab++, count--) + if (tab->trans_from == num) { + *ret = tab->trans_to; + return 0; + } + return -1; +} + +static int +translate_flags(struct transtab *tab, size_t count, int num) +{ + int f = 0; + for (; count; tab++, count--) + if (tab->trans_from & num) + f |= tab->trans_to; + return f; +} + +static void +stream_cleanup(void *ptr) +{ + mu_stream_t str = ptr; + mu_stream_unref(str); +} + +static void +ctx_cleanup(void *ptr) +{ + DSPAM_CTX *ctx = ptr; + dspam_destroy(ctx); +} + +/* number dspam(number msg, number flags; number class_source) */ +MF_DSEXP +MF_DEFUN(dspam, NUMBER, NUMBER nmsg, NUMBER mode_flags, OPTIONAL, NUMBER class_src) +{ + int rc, n; + DSPAM_CTX *ctx; /* DSPAM Context */ + int mode; + int flags; + mu_message_t msg; + mu_stream_t msgstr, instr; + const char *msgbuf; + size_t msgsize; + unsigned prec; + mu_transport_t trans[2]; + + /* Prepare message buffer */ + msg = bi_message_from_descr(env, nmsg); + rc = mu_message_size(msg, &msgsize); + MF_ASSERT(rc == 0, + mfe_failure, + "mu_message_size: %s", mu_strerror(rc)); + + rc = mu_memory_stream_create(&msgstr, MU_STREAM_RDWR); + MF_ASSERT(rc == 0, + mfe_failure, + "mu_static_memory_stream_create: %s", + mu_strerror(rc)); + MF_DCL_CLEANUP(msgstr, stream_cleanup); + + rc = mu_message_get_streamref(msg, &instr); + MF_ASSERT(rc == 0, + mfe_failure, + "mu_message_get_streamref: %s", + mu_strerror(rc)); + MF_DCL_CLEANUP(instr, stream_cleanup); + + rc = mu_stream_copy(msgstr, instr, msgsize, NULL); + MF_ASSERT(rc == 0, + mfe_failure, + "mu_stream_copy: %s", + mu_strerror(rc)); + + MF_CLEANUP(instr); + + mu_stream_ioctl(msgstr, MU_IOCTL_TRANSPORT, MU_IOCTL_OP_GET, trans); + msgbuf = (const char*)trans[0]; + + /* Initialize dspam library and set up global variables, if + needed */ + if (!_dspam_initialized) { + size_t off, len; + char *s; + + dspam_init_driver(NULL); + atexit(_dspam_shutdown); + _dspam_initialized = 1; + + if (MF_VAR_REF(dspam_home) == NULL) { + len = strlen(mailfromd_state_dir); + while (len > 0 && mailfromd_state_dir[len-1] == '/') + len--; + s = MF_ALLOC_HEAP(off, + len + DEFAULT_DSPAM_HOME_LEN + 2); + memcpy(s, mailfromd_state_dir, len); + s[len++] = '/'; + strcpy(s + len, DEFAULT_DSPAM_HOME_STR); + MF_VAR_REF(dspam_home, off); + } + + if (MF_VAR_REF(dspam_user) == NULL) + MF_VAR_SET_STRING(dspam_user, mf_server_user); + + if (MF_VAR_REF(dspam_prec) == 0) + MF_VAR_REF(dspam_prec, DEFAULT_DSPAM_PREC); + } + + /* Prepare DSPAM context */ + MF_ASSERT(translate(mode_trans, MU_ARRAY_SIZE(mode_trans), + mode_flags & _MF__DSM_MASK, &mode) == 0, + mfe_failure, + "bad dspam mode"); + flags = translate_flags(flag_trans, MU_ARRAY_SIZE(flag_trans), + mode_flags); + + /* Initialize the DSPAM context */ + ctx = dspam_init(MF_VAR_STRING(dspam_user), + MF_VAR_STRING(dspam_group), + MF_VAR_STRING(dspam_home), mode, + flags); + MF_ASSERT(ctx != NULL, + mfe_failure, + "dspam_init failed"); + MF_DCL_CLEANUP(ctx, ctx_cleanup); + + /* Use graham and robinson algorithms, graham's p-values */ + /* FIXME: Get from args? */ + ctx->algorithms = DSA_GRAHAM | DSA_BURTON | DSP_GRAHAM; + + /* Use CHAIN tokenizer */ + MF_ASSERT(translate(tokenizer_trans, MU_ARRAY_SIZE(tokenizer_trans), + mode_flags & _MF__DSZ_MASK, &ctx->tokenizer) == 0, + mfe_failure, + "bad dspam tokenizer"); + + /* Set up classification and source */ + if (n = MF_OPTVAL(class_src)) { + MF_ASSERT(translate(class_trans, MU_ARRAY_SIZE(class_trans), + n & _MF__DSR_MASK, + &ctx->classification) == 0, + mfe_failure, + "bad dspam classification flag"); + MF_ASSERT(translate(source_trans, MU_ARRAY_SIZE(source_trans), + n & _MF__DSS_MASK, + &ctx->source) == 0, + mfe_failure, + "bad dspam source flag"); + } + + /* Process the message */ + MF_ASSERT(dspam_process(ctx, msgbuf) == 0, + mfe_failure, + "dspam_process failed"); + + rc = (unsigned) MF_VAR_REF(dspam_prec); + prec = 1; + while (rc--) + prec *= 10; + MF_VAR_REF(dspam_probability, + (unsigned long) (ctx->probability * prec)); + MF_VAR_REF(dspam_confidence, + (unsigned long) (ctx->confidence * prec)); + rc = ctx->result; + MF_CLEANUP(ctx); + + /* FIXME: Any additional processing? */ + + MF_RETURN(rc); +} +END + +MF_INIT @@ -1030,10 +1030,13 @@ mailfromd_show_defaults() #endif printf("\n"); - printf("Optional features: "); + printf("Optional features: "); #if defined WITH_GEOIP - printf("GeoIP"); + printf(" GeoIP"); #endif +#if defined WITH_DSPAM + printf(" DSPAM"); +#endif printf("\n"); db_format_enumerate(db_format_enumerator, NULL); |