aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Sulfrian <alexander@sulfrian.net>2011-05-03 19:26:38 +0200
committerAlexander Sulfrian <alexander@sulfrian.net>2011-05-03 19:26:38 +0200
commita1527ce7177df11a8ee1b46a0e7b64f0f0df234b (patch)
tree41f724c2a04b3582878d40dc451930e71a7b86aa
parentc8a9a6550b84412f919b772a9cb8db1e3ef79d19 (diff)
parent808eaa49efe2dc2438ef98ddf4bac4cfd2898a3d (diff)
downloaddovecot-antispam-master.tar.gz
dovecot-antispam-master.tar.xz
dovecot-antispam-master.zip
merge with base/masterHEADmaster
-rw-r--r--.gitignore1
-rw-r--r--Makefile73
-rw-r--r--NOTES2
-rw-r--r--antispam-plugin.c59
-rw-r--r--antispam-plugin.h85
-rw-r--r--antispam-storage-1.0.c14
-rw-r--r--antispam-storage-1.1.c16
-rw-r--r--antispam-storage-1.2.c16
-rw-r--r--antispam.777
-rw-r--r--crm114-exec.c25
-rw-r--r--debug.c46
-rw-r--r--defconfig33
-rw-r--r--dspam-exec.c31
-rw-r--r--pipe.c (renamed from mailtrain.c)95
-rw-r--r--signature-log.c27
-rw-r--r--spool2dir.c50
-rwxr-xr-xversion.sh2
17 files changed, 385 insertions, 267 deletions
diff --git a/.gitignore b/.gitignore
index 9b5a19c..09a8578 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,4 +6,5 @@ dovecot-version.h
antispam-version.h
dovecot-version
*-stamp
+*.sw[lmnop]
dovecot-1.1.8-src/
diff --git a/Makefile b/Makefile
index 6308038..33869c8 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,9 @@
-# include config file
+# include config file if present
CONFIG ?= .config
-include $(CONFIG)
CFLAGSORIG := $(CFLAGS)
+DOVECOT ?= /usr/include/dovecot
+PLUGINNAME ?= antispam
-include $(DOVECOT)/dovecot-config
INSTALLDIR ?= $(moduledir)/imap
# Kill CFLAGS from dovecot-config
@@ -21,62 +23,29 @@ INCS += -I$(DOVECOT)/src/imap/
# output name
LIBRARY_NAME ?= lib90_$(PLUGINNAME)_plugin.so
-
-# debug rules
-ifeq ("$(DEBUG)", "stderr")
-CFLAGS += -DCONFIG_DEBUG -DDEBUG_STDERR
-objs += debug.o
-else
-ifeq ("$(DEBUG)", "syslog")
-CFLAGS += -DCONFIG_DEBUG -DDEBUG_SYSLOG
-objs += debug.o
-endif
-endif
-
-ifeq ("$(DEBUG_VERBOSE)", "1")
-CFLAGS += -DCONFIG_DEBUG_VERBOSE
-endif
-
-# backend error check
-ifeq ("$(BACKEND)", "")
-error: verify_config
- @echo "Error! no backend configured"
- @false
-endif
-
-# per-backend rules
-ifeq ("$(BACKEND)", "dspam-exec")
-objs += signature.o
-endif
-ifeq ("$(BACKEND)", "signature-log")
-objs += signature.o
-endif
-ifeq ("$(BACKEND)", "crm114-exec")
-objs += signature.o
-endif
+objs = antispam-storage.o antispam-plugin.o debug.o
+objs += dspam-exec.o signature-log.o crm114-exec.o pipe.o spool2dir.o signature.o
ifeq ("$(BACKEND)", "trainstore")
LDFLAGS += -lssl
endif
# main make rules
-CFLAGS += -fPIC -shared -Wall -Wextra -DPLUGINNAME=$(PLUGINNAME)
+LOCALCFLAGS += -fPIC -shared -Wall -Wextra -DPLUGINNAME=$(PLUGINNAME)
CC ?= cc
HOSTCC ?= cc
-objs += antispam-plugin.o antispam-storage.o $(BACKEND).o
-
-all: verify_config $(LIBRARY_NAME)
+all: $(LIBRARY_NAME)
-antispam-storage.o: antispam-storage.c antispam-storage-*.c $(CONFIG) antispam-plugin.h dovecot-version.h
- $(CC) -c $(CFLAGS) $(INCS) -o $@ $<
+antispam-storage.o: antispam-storage.c antispam-storage-*.c antispam-plugin.h dovecot-version.h
+ $(CC) -c $(CFLAGS) $(LOCALCFLAGS) $(INCS) -o $@ $<
-%.o: %.c $(CONFIG) antispam-plugin.h dovecot-version.h antispam-version.h
- $(CC) -c $(CFLAGS) $(INCS) -o $@ $<
+%.o: %.c antispam-plugin.h dovecot-version.h antispam-version.h
+ $(CC) -c $(CFLAGS) $(LOCALCFLAGS) $(INCS) -o $@ $<
$(LIBRARY_NAME): $(objs)
- $(CC) $(CFLAGS) $(INCS) $(objs) -o $(LIBRARY_NAME) $(LDFLAGS)
+ $(CC) $(CFLAGS) $(LOCALCFLAGS) $(INCS) $(objs) -o $(LIBRARY_NAME) $(LDFLAGS)
-dovecot-version: dovecot-version.c $(CONFIG)
+dovecot-version: dovecot-version.c
$(HOSTCC) $(INCS) -o dovecot-version dovecot-version.c
dovecot-version.h: dovecot-version
@@ -85,16 +54,16 @@ dovecot-version.h: dovecot-version
antispam-version.h: version.sh
./version.sh > antispam-version.h
+
clean:
rm -f *.so *.o *~ dovecot-version dovecot-version.h antispam-version.h
-install: all
- install -o $(USER) -g $(GROUP) -m 0755 $(LIBRARY_NAME) $(DESTDIR)$(INSTALLDIR)/
+install: all checkinstalldir
+ install -p -m 0755 $(LIBRARY_NAME) $(DESTDIR)$(INSTALLDIR)/
-verify_config:
- @if [ ! -r $(CONFIG) ]; then \
- echo -e "\nBuilding the plugin requires a configuration file"; \
- echo -e $(CONFIG)'. Copy defconfig ("cp defconfig' $(CONFIG)'")' ; \
- echo -e "to create an example configuration.\n"; \
- exit 1; \
+checkinstalldir:
+ @if [ ! -d "$(DESTDIR)$(INSTALLDIR)/" ] ; then \
+ echo "Installation directory $(DESTDIR)$(INSTALLDIR)/ doesn't exist," ; \
+ echo "run make install INSTALLDIR=..." ; \
+ exit 2 ; \
fi
diff --git a/NOTES b/NOTES
index 0eaf63c..70c4fb5 100644
--- a/NOTES
+++ b/NOTES
@@ -7,4 +7,4 @@ Full-message availability
Pristine retraining or SpamAssassin retraining might need the full message
available. This can easily be implemented since the backend is passed each
-struct mail *mail that is moved. For an example see the mailtrain backend.
+struct mail *mail that is moved. For an example see the pipe backend.
diff --git a/antispam-plugin.c b/antispam-plugin.c
index 455668d..bbcc0e6 100644
--- a/antispam-plugin.c
+++ b/antispam-plugin.c
@@ -73,14 +73,17 @@ static char **trash_folders[] = { NULL, NULL, NULL };
static char **spam_folders[] = { default_spam_folders,NULL, NULL };
static char **unsure_folders[] = { NULL, NULL, NULL };
+void (*antispam_next_hook_mail_storage_created)(struct mail_storage *storage);
bool antispam_can_append_to_spam = FALSE;
static char **spam_keywords = NULL;
bool need_keyword_hook;
bool need_folder_hook;
+struct backend *backend = NULL;
+
/* lower-case string, but keep modified UTF7 unchanged */
-void lowercase_string(const char *in, char *out)
+static void lowercase_string(const char *in, char *out)
{
char ch;
@@ -248,8 +251,8 @@ const char *get_setting(const char *name)
return env;
}
-int parse_folder_setting(const char *setting, char ***strings,
- const char *display_name)
+static int parse_folder_setting(const char *setting, char ***strings,
+ const char *display_name)
{
const char *tmp;
int cnt = 0;
@@ -297,8 +300,33 @@ void PLUGIN_FUNCTION(init)(void)
char * const *iter;
int spam_folder_count;
+ debug_target = ADT_NONE;
+ verbose_debug = 0;
+
+ tmp = get_setting("DEBUG_TARGET");
+ if (tmp) {
+ if (strcmp(tmp, "syslog") == 0)
+ debug_target = ADT_SYSLOG;
+ else if (strcmp(tmp, "stderr") == 0)
+ debug_target = ADT_STDERR;
+ else
+ exit(4);
+ }
+
debug("plugin initialising (%s)\n", ANTISPAM_VERSION);
+ tmp = get_setting("VERBOSE_DEBUG");
+ if (tmp) {
+ char *endp;
+ unsigned long val = strtoul(tmp, &endp, 10);
+ if (*endp || val >= 2) {
+ debug("Invalid verbose_debug setting");
+ exit(5);
+ }
+ verbose_debug = val;
+ debug_verbose("verbose debug enabled");
+ }
+
global_pool = pool_alloconly_create("antispam-pool", 1024);
parse_folder_setting("TRASH", trash_folders, "trash");
@@ -323,11 +351,32 @@ void PLUGIN_FUNCTION(init)(void)
}
}
+ tmp = get_setting("BACKEND");
+ if (tmp) {
+ if (strcmp(tmp, "crm114") == 0)
+ backend = &crm114_backend;
+ else if (strcmp(tmp, "dspam") == 0)
+ backend = &dspam_backend;
+ else if (strcmp(tmp, "pipe") == 0)
+ backend = &pipe_backend;
+ else if (strcmp(tmp, "signature") == 0)
+ backend = &signature_backend;
+ else if (strcmp(tmp, "spool2dir") == 0)
+ backend = &spool2dir_backend;
+ else {
+ debug("selected invalid backend!\n");
+ exit(3);
+ }
+ } else {
+ debug("no backend selected!\n");
+ exit(2);
+ }
+
/* set spam_folders to empty to only allow keywords */
need_folder_hook = spam_folder_count > 0;
need_keyword_hook = !!spam_keywords;
- backend_init(global_pool);
+ backend->init(global_pool);
antispam_next_hook_mail_storage_created = hook_mail_storage_created;
hook_mail_storage_created = antispam_mail_storage_created;
@@ -336,7 +385,7 @@ void PLUGIN_FUNCTION(init)(void)
void PLUGIN_FUNCTION(deinit)(void)
{
hook_mail_storage_created = antispam_next_hook_mail_storage_created;
- backend_exit();
+ backend->exit();
mempool_unref(&global_pool);
}
diff --git a/antispam-plugin.h b/antispam-plugin.h
index 38a3be6..a06a1e9 100644
--- a/antispam-plugin.h
+++ b/antispam-plugin.h
@@ -29,53 +29,56 @@ enum classification {
CLASS_SPAM,
};
-void backend_init(pool_t pool);
-void backend_exit(void);
-/*
- * Handle mail; parameters are
- * - t: transaction context
- * - ast: transaction context from backend_start()
- * - mail: the message
- * - wanted: the wanted classification determined by the user
- */
-int backend_handle_mail(struct mailbox_transaction_context *t,
- struct antispam_transaction_context *ast,
- struct mail *mail, enum classification wanted);
-struct antispam_transaction_context *backend_start(struct mailbox *box);
-void backend_rollback(struct antispam_transaction_context *ast);
-int backend_commit(struct mailbox_transaction_context *ctx,
- struct antispam_transaction_context *ast);
-
-#ifdef CONFIG_DEBUG
+struct backend {
+ void (*init)(pool_t pool);
+ void (*exit)(void);
+ /*
+ * Handle mail; parameters are
+ * - t: transaction context
+ * - ast: transaction context from backend_start()
+ * - mail: the message
+ * - wanted: the wanted classification determined by the user
+ */
+ int (*handle_mail)(struct mailbox_transaction_context *t,
+ struct antispam_transaction_context *ast,
+ struct mail *mail, enum classification wanted);
+ struct antispam_transaction_context *(*start)(struct mailbox *box);
+ void (*rollback)(struct antispam_transaction_context *ast);
+ int (*commit)(struct mailbox_transaction_context *ctx,
+ struct antispam_transaction_context *ast);
+};
+
+/* the selected backend */
+extern struct backend *backend;
+
+/* possible backends */
+extern struct backend crm114_backend;
+extern struct backend dspam_backend;
+extern struct backend pipe_backend;
+extern struct backend signature_backend;
+extern struct backend spool2dir_backend;
+
+enum antispam_debug_target {
+ ADT_NONE,
+ ADT_STDERR,
+ ADT_SYSLOG,
+};
+
+extern enum antispam_debug_target debug_target;
+extern int verbose_debug;
+
void debug(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
void debugv(char **args);
-#else
-static void debug(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
-static inline void debug(const char *fmt __attribute__((unused)), ...)
-{
-}
-static inline void debugv(char **args __attribute__((unused)))
-{
-}
-#endif
-
-#if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_VERBOSE)
-/* bit of an ugly short-cut */
-#define debug_verbose debug
-#else
-static void debug_verbose(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
-static inline void debug_verbose(const char *fmt __attribute__((unused)), ...)
-{
-}
-#endif
+void debugv_not_stderr(char **args);
+void debug_verbose(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
void antispam_mail_storage_created(struct mail_storage *storage);
-void (*antispam_next_hook_mail_storage_created)(struct mail_storage *storage);
+extern void (*antispam_next_hook_mail_storage_created)(struct mail_storage *storage);
bool mailbox_is_spam(struct mailbox *box);
bool mailbox_is_trash(struct mailbox *box);
bool mailbox_is_unsure(struct mailbox *box);
const char *get_setting(const char *name);
-bool antispam_can_append_to_spam;
+extern bool antispam_can_append_to_spam;
bool keyword_is_spam(const char *keyword);
extern bool need_keyword_hook;
@@ -84,6 +87,10 @@ extern bool need_folder_hook;
/*
* Dovecot version compat code
*/
+#if DOVECOT_VERSION <= DOVECOT_VERSION_CODE(1, 1, 0)
+#define mailbox_get_storage(s) mailbox_get_storage((struct mailbox *)s)
+#define mailbox_get_name(s) mailbox_get_name((struct mailbox *)s)
+#endif
#if DOVECOT_VERSION_CODE(1, 1, 0) == DOVECOT_VERSION || DOVECOT_VERSION_CODE(1, 2, 0) == DOVECOT_VERSION
#define __attr_unused__ ATTR_UNUSED
diff --git a/antispam-storage-1.0.c b/antispam-storage-1.0.c
index 1651281..3112a22 100644
--- a/antispam-storage-1.0.c
+++ b/antispam-storage-1.0.c
@@ -119,8 +119,8 @@ antispam_copy(struct mailbox_transaction_context *t, struct mail *mail,
if (asbox->save_hack || asbox->movetype == MMT_UNINTERESTING)
ret = 0;
else
- ret = backend_handle_mail(t, ast, copy_dest_mail,
- move_to_class(asbox->movetype));
+ ret = backend->handle_mail(t, ast, copy_dest_mail,
+ move_to_class(asbox->movetype));
/*
* Both save_hack and movetype are only valid within a copy operation,
@@ -231,8 +231,8 @@ static int antispam_save_finish(struct mail_save_context *ctx,
}
/* fall through */
default:
- ret = backend_handle_mail(ctx->transaction, ast, save_dest_mail,
- move_to_class(asbox->movetype));
+ ret = backend->handle_mail(ctx->transaction, ast, save_dest_mail,
+ move_to_class(asbox->movetype));
}
if (save_dest_mail != dest_mail)
@@ -245,7 +245,7 @@ antispam_transaction_begin(struct mailbox *box)
{
struct antispam_transaction_context *ast;
- ast = backend_start(box);
+ ast = backend->start(box);
i_assert(ast != NULL);
return ast;
@@ -256,7 +256,7 @@ antispam_transaction_rollback(struct antispam_transaction_context **_ast)
{
struct antispam_transaction_context *ast = *_ast;
- backend_rollback(ast);
+ backend->rollback(ast);
*_ast = NULL;
}
@@ -267,7 +267,7 @@ antispam_transaction_commit(struct mailbox_transaction_context *ctx,
struct antispam_transaction_context *ast = *_ast;
int ret;
- ret = backend_commit(ctx, ast);
+ ret = backend->commit(ctx, ast);
*_ast = NULL;
return ret;
}
diff --git a/antispam-storage-1.1.c b/antispam-storage-1.1.c
index 80c80e3..e21ac82 100644
--- a/antispam-storage-1.1.c
+++ b/antispam-storage-1.1.c
@@ -127,8 +127,8 @@ antispam_copy(struct mailbox_transaction_context *t, struct mail *mail,
if (asbox->save_hack || asbox->movetype == MMT_UNINTERESTING)
ret = 0;
else
- ret = backend_handle_mail(t, ast->backendctx, dest_mail,
- move_to_class(asbox->movetype));
+ ret = backend->handle_mail(t, ast->backendctx, dest_mail,
+ move_to_class(asbox->movetype));
/*
* Both save_hack and movetype are only valid within a copy operation,
@@ -247,9 +247,9 @@ static int antispam_save_finish(struct mail_save_context *ctx)
}
/* fall through */
default:
- ret = backend_handle_mail(ctx->transaction, ast->backendctx,
- dest_mail,
- move_to_class(asbox->movetype));
+ ret = backend->handle_mail(ctx->transaction, ast->backendctx,
+ dest_mail,
+ move_to_class(asbox->movetype));
}
return ret;
@@ -260,7 +260,7 @@ antispam_transaction_begin(struct mailbox *box)
{
struct antispam_transaction_context *ast;
- ast = backend_start(box);
+ ast = backend->start(box);
i_assert(ast != NULL);
return ast;
@@ -271,7 +271,7 @@ antispam_transaction_rollback(struct antispam_transaction_context **_ast)
{
struct antispam_transaction_context *ast = *_ast;
- backend_rollback(ast);
+ backend->rollback(ast);
*_ast = NULL;
}
@@ -282,7 +282,7 @@ antispam_transaction_commit(struct mailbox_transaction_context *ctx,
struct antispam_transaction_context *ast = *_ast;
int ret;
- ret = backend_commit(ctx, ast);
+ ret = backend->commit(ctx, ast);
*_ast = NULL;
return ret;
}
diff --git a/antispam-storage-1.2.c b/antispam-storage-1.2.c
index 54b8f7b..2c11ce7 100644
--- a/antispam-storage-1.2.c
+++ b/antispam-storage-1.2.c
@@ -126,8 +126,8 @@ antispam_copy(struct mail_save_context *ctx, struct mail *mail)
if (asbox->save_hack || asbox->movetype == MMT_UNINTERESTING)
ret = 0;
else
- ret = backend_handle_mail(t, ast->backendctx, ctx->dest_mail,
- move_to_class(asbox->movetype));
+ ret = backend->handle_mail(t, ast->backendctx, ctx->dest_mail,
+ move_to_class(asbox->movetype));
/*
* Both save_hack and movetype are only valid within a copy operation,
@@ -237,9 +237,9 @@ static int antispam_save_finish(struct mail_save_context *ctx)
}
/* fall through */
default:
- ret = backend_handle_mail(ctx->transaction, ast->backendctx,
- dest_mail,
- move_to_class(asbox->movetype));
+ ret = backend->handle_mail(ctx->transaction, ast->backendctx,
+ dest_mail,
+ move_to_class(asbox->movetype));
}
return ret;
@@ -250,7 +250,7 @@ antispam_transaction_begin(struct mailbox *box)
{
struct antispam_transaction_context *ast;
- ast = backend_start(box);
+ ast = backend->start(box);
i_assert(ast != NULL);
return ast;
@@ -261,7 +261,7 @@ antispam_transaction_rollback(struct antispam_transaction_context **_ast)
{
struct antispam_transaction_context *ast = *_ast;
- backend_rollback(ast);
+ backend->rollback(ast);
*_ast = NULL;
}
@@ -272,7 +272,7 @@ antispam_transaction_commit(struct mailbox_transaction_context *ctx,
struct antispam_transaction_context *ast = *_ast;
int ret;
- ret = backend_commit(ctx, ast);
+ ret = backend->commit(ctx, ast);
*_ast = NULL;
return ret;
}
diff --git a/antispam.7 b/antispam.7
index 141ef5c..b506ce9 100644
--- a/antispam.7
+++ b/antispam.7
@@ -61,7 +61,7 @@ protocol imap {
.SH BACKENDS
-The plugin supports multiple backends, there are currently two working
+The plugin supports multiple backends, there are currently a few working
backends included in the distribution:
.SS dspam executable backend (dspam specific)
@@ -71,12 +71,13 @@ problems with this approach including
(1) it can take a long time during which the IMAP session is blocked
(2) when many users retrain many messages at once server load may spike
-.SS email sender backend (spam filter agnostic)
+.SS pipe backend (spam filter agnostic)
-This backend sends mail to ham@example.com or spam@example.com
-(actual addresses are configurable) for retraining. This backend can
-be very fast to set up if you already have a working setup that uses
-training addresses as recommended by many spam filter setups.
+This backend simply pipes the mail to train to a process it executes.
+This can for example be used to send it as email to mail aliases for
+retraining. This backend can be very easy to set up if you already
+have a working setup that uses training addresses as recommended by
+many spam filter setups.
Since this backend simply pipes the message to a program (by default
sendmail) it can also be used for all kinds of other spam filters,
@@ -92,7 +93,7 @@ You need to use the unsure folder option (see below) together with
this plugin and deliver unsure mail into an unsure folder, spam mail
into a spam folder and other mail regularly.
-Has the same drawbacks as the dspam-exec approach.
+Has the same drawbacks as the dspam approach.
.SS spool2dir backend (general)
@@ -111,6 +112,23 @@ plugin {
##################
# GENERIC OPTIONS
+ # Debugging options
+ # Uncomment to get the desired debugging behaviour.
+ # Note that in some cases stderr debugging will not be as
+ # verbose as syslog debugging due to internal limitations.
+ #
+ # antispam_debug_target = syslog
+ # antispam_debug_target = stderr
+ # antispam_verbose_debug = 1
+
+ # backend selection, MUST be configured first,
+ # there's no default so you need to set one of
+ # these options:
+ # antispam_backend = crm114
+ # antispam_backend = dspam
+ # antispam_backend = pipe
+ # antispam_backend = spool2dir
+
# mail signature (used with any backend requiring a signature)
antispam_signature = X-DSPAM-Signature
@@ -170,7 +188,7 @@ plugin {
#
#===================
- # dspam-exec plugin
+ # dspam plugin
# dspam binary
antispam_dspam_binary = /usr/bin/dspam
@@ -189,34 +207,41 @@ plugin {
# antispam_dspam_result_blacklist = Virus
#=====================
- # mail sending plugin
+ # pipe plugin
+ #
+ # This plug can be used to train via an arbitrary program that
+ # receives the message on standard input. Since sendmail can be
+ # such a program, it can be used to send the message to another
+ # email address for training there.
#
- # Because of the way this plugin works, you can also use it
- # to train via an arbitrary program that receives the message
- # on standard input, in that case you can use the config
- # options antispam_mail_spam and antispam_mail_notspam for
- # the argument that distinguishes between ham and spam.
# For example:
- # antispam_mail_sendmail = /path/to/mailtrain
- # antispam_mail_sendmail_args = --for;%u
- # antispam_mail_spam = --spam
- # antispam_mail_notspam = --ham
+ # antispam_pipe_program = /path/to/mailtrain
+ # (defaults to /usr/sbin/sendmail)
+ # antispam_pipe_program_args = --for;%u
+ # antispam_pipe_program_spam_arg = --spam
+ # antispam_pipe_program_notspam_arg = --ham
+ # antispam_pipe_tmpdir = /tmp
# will call it, for example, like this:
# /path/to/mailtrain --for jberg --spam
+ #
+ # The old configuration options from when this plugin was called
+ # "mailtrain" are still valid, these are, in the same order as
+ # above: antispam_mail_sendmail, antispam_mail_sendmail_args,
+ # antispam_mail_spam, antispam_mail_notspam and antispam_mail_tmpdir.
# temporary directory
- antispam_mail_tmpdir = /tmp
+ antispam_pipe_tmpdir = /tmp
- # spam/not-spam addresses (default unset which will give errors)
- # antispam_mail_spam =
- # antispam_mail_notspam =
+ # spam/not-spam argument (default unset which will is not what you want)
+ # antispam_pipe_program_spam_arg =
+ # antispam_pipe_program_notspam_arg =
- # sendmail binary
- antispam_mail_sendmail = /usr/sbin/sendmail
- #antispam_mail_sendmail_args = -f;%u@example.com # % expansion done by dovecot
+ # binary to pipe mail to
+ antispam_pipe_program = /usr/sbin/sendmail
+ #antispam_pipe_program_args = -f;%u@example.com # % expansion done by dovecot
#===================
- # crm114-exec plugin
+ # crm114 plugin
# mailreaver binary
antispam_crm_binary = /bin/false
diff --git a/crm114-exec.c b/crm114-exec.c
index 7727409..ea0babc 100644
--- a/crm114-exec.c
+++ b/crm114-exec.c
@@ -129,7 +129,7 @@ struct antispam_transaction_context {
struct siglist *siglist;
};
-struct antispam_transaction_context *
+static struct antispam_transaction_context *
backend_start(struct mailbox *box __attr_unused__)
{
struct antispam_transaction_context *ast;
@@ -139,13 +139,13 @@ backend_start(struct mailbox *box __attr_unused__)
return ast;
}
-void backend_rollback(struct antispam_transaction_context *ast)
+static void backend_rollback(struct antispam_transaction_context *ast)
{
signature_list_free(&ast->siglist);
i_free(ast);
}
-int backend_commit(struct mailbox_transaction_context *ctx,
+static int backend_commit(struct mailbox_transaction_context *ctx,
struct antispam_transaction_context *ast)
{
struct siglist *item = ast->siglist;
@@ -167,14 +167,14 @@ int backend_commit(struct mailbox_transaction_context *ctx,
return ret;
}
-int backend_handle_mail(struct mailbox_transaction_context *t,
- struct antispam_transaction_context *ast,
- struct mail *mail, enum classification want)
+static int backend_handle_mail(struct mailbox_transaction_context *t,
+ struct antispam_transaction_context *ast,
+ struct mail *mail, enum classification want)
{
return signature_extract_to_list(t, mail, &ast->siglist, want);
}
-void backend_init(pool_t pool)
+static void backend_init(pool_t pool)
{
const char *tmp;
int i;
@@ -198,6 +198,15 @@ void backend_init(pool_t pool)
signature_init();
}
-void backend_exit(void)
+static void backend_exit(void)
{
}
+
+struct backend crm114_backend = {
+ .init = backend_init,
+ .exit = backend_exit,
+ .handle_mail = backend_handle_mail,
+ .start = backend_start,
+ .rollback = backend_rollback,
+ .commit = backend_commit,
+};
diff --git a/debug.c b/debug.c
index 7f3e407..a3e0fc4 100644
--- a/debug.c
+++ b/debug.c
@@ -4,22 +4,32 @@
#include <stdio.h>
#include "antispam-plugin.h"
+enum antispam_debug_target debug_target;
+int verbose_debug;
+
static void _debug(const char *format, va_list ap)
{
const char *fmt;
+ if (debug_target == ADT_NONE)
+ return;
+
t_push();
fmt = t_strconcat(stringify(PLUGINNAME), ": ", format, NULL);
-#if defined(DEBUG_SYSLOG)
- vsyslog(LOG_DEBUG, fmt, ap);
-#elif defined(DEBUG_STDERR)
- vfprintf(stderr, fmt, ap);
- fflush(stderr);
-#else
-#error no logging method
-#endif
+ switch (debug_target) {
+ case ADT_NONE:
+ break;
+ case ADT_SYSLOG:
+ vsyslog(LOG_DEBUG, fmt, ap);
+ break;
+ case ADT_STDERR:
+ vfprintf(stderr, fmt, ap);
+ fflush(stderr);
+ break;
+ }
+
t_pop();
}
@@ -64,3 +74,23 @@ void debugv(char **args)
debug("%s", buf);
t_pop();
}
+
+void debugv_not_stderr(char **args)
+{
+ if (debug_target == ADT_STDERR)
+ return;
+
+ debugv(args);
+}
+
+void debug_verbose(const char *fmt, ...)
+{
+ va_list args;
+
+ if (!verbose_debug)
+ return;
+
+ va_start(args, fmt);
+ _debug(fmt, args);
+ va_end(args);
+}
diff --git a/defconfig b/defconfig
index 3bbda18..34a1633 100644
--- a/defconfig
+++ b/defconfig
@@ -9,44 +9,25 @@
# be modified from here. In most cases, these lines should use += in order not
# to override previous values of the variables.
-# backend (select exactly one)
-# dspam-exec - direct dspam training by calling dspam executable
-# signature-log - signature logging using dovecot's dict API
-# mailtrain - send mail to special addresses for training
-# crm114-exec - direct crm114 training by calling mailreaver.crm
-# spool2dir - spool trained emails to a directory
-#BACKEND=dspam-exec
-#BACKEND=signature-log
-#BACKEND=mailtrain
-#BACKEND=crm114-exec
-#BACKEND=spool2dir
-
# Dovecot build/header directory
# Building the plugin requires configured dovecot sources or having
# configured it with --enable-header-install in which case you can
# point DOVECOT= to the installed headers too.
+# If unset, it defaults to /usr/include/dovecot which typically is the
+# right place so you don't have to worry about it.
#DOVECOT=../dovecot-1.0.5
#DOVECOT=../dovecot-1.1
-DOVECOT=/usr/include/dovecot
+#DOVECOT=/usr/include/dovecot
# install directory for 'make install'
# NB no need for a final '/'
INSTALLDIR=/usr/lib/dovecot/modules/imap
-# The user and group for the installed plugin
-USER=root
-GROUP=root
-
-# enable debugging to syslog or stderr
-#DEBUG=stderr
-#DEBUG=syslog
-
-# verbose debugging (lots of output!)
-#DEBUG_VERBOSE=1
# plugin name, change only if you need to rename the plugin
-# (because, for example, you need two instances for different
-# spam filters installed)
-PLUGINNAME=antispam
+# (for backward compatibility reasons, there no longer is a
+# need for this since backends can be configured dynamically).
+# If unset, defaults to "antispam" which is what you want.
+# PLUGINNAME=antispam
# extra CFLAGS
# CFLAGS += -g3
diff --git a/dspam-exec.c b/dspam-exec.c
index ecfb9f1..c4a49b4 100644
--- a/dspam-exec.c
+++ b/dspam-exec.c
@@ -141,13 +141,11 @@ static int call_dspam(const char *signature, enum classification wanted)
for (i = 0; i < extra_args_num; i++)
argv[i + 4] = (char *)extra_args[i];
-#ifdef DEBUG_SYSLOG
/*
* not good with stderr debuggin since we then write to
* stderr which our parent takes as a bug
*/
- debugv(argv);
-#endif
+ debugv_not_stderr(argv);
execv(dspam_binary, argv);
debug("executing %s failed: %d (uid=%d, gid=%d)",
@@ -163,7 +161,7 @@ struct antispam_transaction_context {
struct siglist *siglist;
};
-struct antispam_transaction_context *
+static struct antispam_transaction_context *
backend_start(struct mailbox *box __attr_unused__)
{
struct antispam_transaction_context *ast;
@@ -173,14 +171,14 @@ backend_start(struct mailbox *box __attr_unused__)
return ast;
}
-void backend_rollback(struct antispam_transaction_context *ast)
+static void backend_rollback(struct antispam_transaction_context *ast)
{
signature_list_free(&ast->siglist);
i_free(ast);
}
-int backend_commit(struct mailbox_transaction_context *ctx,
- struct antispam_transaction_context *ast)
+static int backend_commit(struct mailbox_transaction_context *ctx,
+ struct antispam_transaction_context *ast)
{
struct siglist *item = ast->siglist;
int ret = 0;
@@ -201,9 +199,9 @@ int backend_commit(struct mailbox_transaction_context *ctx,
return ret;
}
-int backend_handle_mail(struct mailbox_transaction_context *t,
- struct antispam_transaction_context *ast,
- struct mail *mail, enum classification want)
+static int backend_handle_mail(struct mailbox_transaction_context *t,
+ struct antispam_transaction_context *ast,
+ struct mail *mail, enum classification want)
{
const char *const *result = NULL;
int i;
@@ -224,7 +222,7 @@ int backend_handle_mail(struct mailbox_transaction_context *t,
return signature_extract_to_list(t, mail, &ast->siglist, want);
}
-void backend_init(pool_t pool)
+static void backend_init(pool_t pool)
{
const char *tmp;
int i;
@@ -263,6 +261,15 @@ void backend_init(pool_t pool)
signature_init();
}
-void backend_exit(void)
+static void backend_exit(void)
{
}
+
+struct backend dspam_backend = {
+ .init = backend_init,
+ .exit = backend_exit,
+ .handle_mail = backend_handle_mail,
+ .start = backend_start,
+ .rollback = backend_rollback,
+ .commit = backend_commit,
+};
diff --git a/mailtrain.c b/pipe.c
index 41071bc..c0853d6 100644
--- a/mailtrain.c
+++ b/pipe.c
@@ -30,14 +30,14 @@
#include "antispam-plugin.h"
-static const char *spamaddr = NULL;
-static const char *hamaddr = NULL;
-static const char *sendmail_binary = "/usr/sbin/sendmail";
+static const char *spam_arg = NULL;
+static const char *ham_arg = NULL;
+static const char *pipe_binary = "/usr/sbin/sendmail";
static const char *tmpdir = "/tmp";
static char **extra_args = NULL;
static int extra_args_num = 0;
-static int run_sendmail(int mailfd, enum classification wanted)
+static int run_pipe(int mailfd, enum classification wanted)
{
const char *dest;
pid_t pid;
@@ -45,10 +45,10 @@ static int run_sendmail(int mailfd, enum classification wanted)
switch (wanted) {
case CLASS_SPAM:
- dest = spamaddr;
+ dest = spam_arg;
break;
case CLASS_NOTSPAM:
- dest = hamaddr;
+ dest = ham_arg;
break;
}
@@ -60,7 +60,7 @@ static int run_sendmail(int mailfd, enum classification wanted)
if (pid == -1)
return -1;
- debug("running mailtrain backend program %s", sendmail_binary);
+ debug("running mailtrain backend program %s", pipe_binary);
if (pid) {
if (waitpid(pid, &status, 0) == -1)
@@ -71,12 +71,12 @@ static int run_sendmail(int mailfd, enum classification wanted)
} else {
char **argv;
int sz = sizeof(char *) * (2 + extra_args_num + 1);
- int i;
+ int i, fd;
argv = i_malloc(sz);
memset(argv, 0, sz);
- argv[0] = (char *) sendmail_binary;
+ argv[0] = (char *) pipe_binary;
for (i = 0; i < extra_args_num; i++)
argv[i + 1] = (char *) extra_args[i];
@@ -84,9 +84,11 @@ static int run_sendmail(int mailfd, enum classification wanted)
argv[i + 1] = (char *) dest;
dup2(mailfd, 0);
- close(1);
- close(2);
- execv(sendmail_binary, argv);
+ fd = open("/dev/null", O_WRONLY);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ close(fd);
+ execv(pipe_binary, argv);
_exit(1);
/* not reached */
return -1;
@@ -99,7 +101,7 @@ struct antispam_transaction_context {
int tmplen;
};
-struct antispam_transaction_context *
+static struct antispam_transaction_context *
backend_start(struct mailbox *box __attr_unused__)
{
struct antispam_transaction_context *ast;
@@ -141,7 +143,7 @@ static int process_tmpdir(struct mailbox_transaction_context *ctx,
fd = open(buf, O_RDONLY);
read(fd, &wanted, sizeof(wanted));
- if ((rc = run_sendmail(fd, wanted))) {
+ if ((rc = run_pipe(fd, wanted))) {
mail_storage_set_error(ctx->box->storage,
ME(TEMP)
"failed to send mail");
@@ -176,7 +178,7 @@ static void clear_tmpdir(struct antispam_transaction_context *ast)
t_pop();
}
-void backend_rollback(struct antispam_transaction_context *ast)
+static void backend_rollback(struct antispam_transaction_context *ast)
{
if (ast->tmpdir) {
/* clear it! */
@@ -187,8 +189,8 @@ void backend_rollback(struct antispam_transaction_context *ast)
i_free(ast);
}
-int backend_commit(struct mailbox_transaction_context *ctx,
- struct antispam_transaction_context *ast)
+static int backend_commit(struct mailbox_transaction_context *ctx,
+ struct antispam_transaction_context *ast)
{
int ret;
@@ -207,9 +209,9 @@ int backend_commit(struct mailbox_transaction_context *ctx,
return ret;
}
-int backend_handle_mail(struct mailbox_transaction_context *t,
- struct antispam_transaction_context *ast,
- struct mail *mail, enum classification wanted)
+static int backend_handle_mail(struct mailbox_transaction_context *t,
+ struct antispam_transaction_context *ast,
+ struct mail *mail, enum classification wanted)
{
struct istream *mailstream;
struct ostream *outstream;
@@ -226,7 +228,7 @@ int backend_handle_mail(struct mailbox_transaction_context *t,
return -1;
}
- if (!hamaddr || !spamaddr) {
+ if (!ham_arg || !spam_arg) {
mail_storage_set_error(t->box->storage,
ME(NOTPOSSIBLE)
"antispam plugin not configured");
@@ -308,45 +310,64 @@ int backend_handle_mail(struct mailbox_transaction_context *t,
return ret;
}
-void backend_init(pool_t pool __attr_unused__)
+static void backend_init(pool_t pool __attr_unused__)
{
const char *tmp;
int i;
- tmp = get_setting("MAIL_SPAM");
+ tmp = get_setting("PIPE_PROGRAM_SPAM_ARG");
+ if (!tmp)
+ tmp = get_setting("MAIL_SPAM");
if (tmp) {
- spamaddr = tmp;
- debug("mail backend spam address %s\n", tmp);
+ spam_arg = tmp;
+ debug("pipe backend spam argument = %s\n", tmp);
}
- tmp = get_setting("MAIL_NOTSPAM");
+ tmp = get_setting("PIPE_PROGRAM_NOTSPAM_ARG");
+ if (!tmp)
+ tmp = get_setting("MAIL_NOTSPAM");
if (tmp) {
- hamaddr = tmp;
- debug("mail backend not-spam address %s\n", tmp);
+ ham_arg = tmp;
+ debug("pipe backend not-spam argument = %s\n", tmp);
}
- tmp = get_setting("MAIL_SENDMAIL");
+ tmp = get_setting("PIPE_PROGRAM");
+ if (!tmp)
+ tmp = get_setting("MAIL_SENDMAIL");
if (tmp) {
- sendmail_binary = tmp;
- debug("mail backend sendmail %s\n", tmp);
+ pipe_binary = tmp;
+ debug("pipe backend program = %s\n", tmp);
}
- tmp = get_setting("MAIL_SENDMAIL_ARGS");
+ tmp = get_setting("PIPE_PROGRAM_ARGS");
+ if (!tmp)
+ tmp = get_setting("MAIL_SENDMAIL_ARGS");
if (tmp) {
extra_args = p_strsplit(pool, tmp, ";");
extra_args_num = str_array_length(
(const char *const *)extra_args);
for (i = 0; i < extra_args_num; i++)
- debug("mail backend sendmail arg %s\n",
- extra_args[i]);
+ debug("pipe backend program arg[%d] = %s\n",
+ i, extra_args[i]);
}
- tmp = get_setting("MAIL_TMPDIR");
+ tmp = get_setting("PIPE_TMPDIR");
+ if (!tmp)
+ tmp = get_setting("MAIL_TMPDIR");
if (tmp)
tmpdir = tmp;
- debug("mail backend tmpdir %s\n", tmpdir);
+ debug("pipe backend tmpdir %s\n", tmpdir);
}
-void backend_exit(void)
+static void backend_exit(void)
{
}
+
+struct backend pipe_backend = {
+ .init = backend_init,
+ .exit = backend_exit,
+ .handle_mail = backend_handle_mail,
+ .start = backend_start,
+ .rollback = backend_rollback,
+ .commit = backend_commit,
+};
diff --git a/signature-log.c b/signature-log.c
index 5b7b306..7a9b422 100644
--- a/signature-log.c
+++ b/signature-log.c
@@ -44,7 +44,7 @@ struct antispam_transaction_context {
struct dict_transaction_context *dict_ctx;
};
-struct antispam_transaction_context *
+static struct antispam_transaction_context *
backend_start(struct mailbox *box __attr_unused__)
{
struct antispam_transaction_context *ast;
@@ -59,7 +59,7 @@ backend_start(struct mailbox *box __attr_unused__)
return ast;
}
-void backend_rollback(struct antispam_transaction_context *ast)
+static void backend_rollback(struct antispam_transaction_context *ast)
{
if (ast->dict) {
// dict_transaction_rollback(ast->dict_ctx);
@@ -69,7 +69,7 @@ void backend_rollback(struct antispam_transaction_context *ast)
i_free(ast);
}
-int backend_commit(struct mailbox_transaction_context *ctx __attr_unused__,
+static int backend_commit(struct mailbox_transaction_context *ctx __attr_unused__,
struct antispam_transaction_context *ast)
{
int ret = 0;
@@ -84,9 +84,9 @@ int backend_commit(struct mailbox_transaction_context *ctx __attr_unused__,
return ret;
}
-int backend_handle_mail(struct mailbox_transaction_context *t,
- struct antispam_transaction_context *ast,
- struct mail *mail, enum classification wanted)
+static int backend_handle_mail(struct mailbox_transaction_context *t,
+ struct antispam_transaction_context *ast,
+ struct mail *mail, enum classification wanted)
{
const char *signature;
int ret;
@@ -128,7 +128,7 @@ int backend_handle_mail(struct mailbox_transaction_context *t,
ast->dict_ctx = dict_transaction_begin(ast->dict);
signature = t_strconcat("priv/", signature, NULL);
dict_atomic_inc(ast->dict_ctx, signature, inc);
- ret = dict_transaction_commit(ast->dict_ctx);
+ ret = dict_transaction_commit(&ast->dict_ctx);
if (ret)
mail_storage_set_error(t->box->storage,
ME(NOTPOSSIBLE)
@@ -137,7 +137,7 @@ int backend_handle_mail(struct mailbox_transaction_context *t,
return ret;
}
-void backend_init(pool_t pool __attr_unused__)
+static void backend_init(pool_t pool __attr_unused__)
{
const char *tmp;
@@ -156,6 +156,15 @@ void backend_init(pool_t pool __attr_unused__)
signature_init();
}
-void backend_exit(void)
+static void backend_exit(void)
{
}
+
+struct backend signature_backend = {
+ .init = backend_init,
+ .exit = backend_exit,
+ .handle_mail = backend_handle_mail,
+ .start = backend_start,
+ .rollback = backend_rollback,
+ .commit = backend_commit,
+};
diff --git a/spool2dir.c b/spool2dir.c
index aa53e71..edd5bd3 100644
--- a/spool2dir.c
+++ b/spool2dir.c
@@ -112,30 +112,30 @@ struct antispam_transaction_context {
};
-void backend_rollback(struct antispam_transaction_context *ast)
+static void backend_rollback(struct antispam_transaction_context *ast)
{
i_free(ast);
}
-int backend_commit(struct mailbox_transaction_context *ctx __attr_unused__,
- struct antispam_transaction_context *ast)
+static int backend_commit(struct mailbox_transaction_context *ctx __attr_unused__,
+ struct antispam_transaction_context *ast)
{
i_free(ast);
return 0;
}
-int backend_handle_mail(struct mailbox_transaction_context *t,
- struct antispam_transaction_context *ast,
- struct mail *mail, enum classification wanted)
+static int backend_handle_mail(struct mailbox_transaction_context *t,
+ struct antispam_transaction_context *ast,
+ struct mail *mail, enum classification wanted)
{
struct istream *mailstream;
struct ostream *outstream;
- int ret;
+ int ret = -1;
const char *dest, *buf;
const unsigned char *beginning;
size_t size;
- int fd;
+ int fd = -1;
i_assert(ast);
@@ -171,13 +171,14 @@ int backend_handle_mail(struct mailbox_transaction_context *t,
while (ast->count <= 9999) {
buf = t_strdup_printf(dest, (long)time(0), (long)++ast->count);
fd = open(buf, O_CREAT | O_EXCL | O_WRONLY, 0600);
- if(fd >= 0 || errno != EEXIST)
+ if (fd >= 0 || errno != EEXIST)
break;
/* current filename in buf already exists, zap it */
t_pop();
t_push();
- /* buf is invalid now ! */
+ /* buf is invalid now! */
}
+
if (fd < 0) {
debug("spool2dir backend: Failed to create spool file %s: %s\n",
dest, strerror(errno));
@@ -188,6 +189,7 @@ int backend_handle_mail(struct mailbox_transaction_context *t,
}
/* buf still points to allocated memory, because fd >= 0 */
+
outstream = o_stream_create_from_fd(fd, t->box->pool);
if (!outstream) {
mail_storage_set_error(t->box->storage,
@@ -198,7 +200,6 @@ int backend_handle_mail(struct mailbox_transaction_context *t,
if (i_stream_read_data(mailstream, &beginning, &size, 5) < 0 ||
size < 5) {
- ret = -1;
mail_storage_set_error(t->box->storage,
ME(NOTPOSSIBLE)
"Failed to read mail beginning");
@@ -210,7 +211,6 @@ int backend_handle_mail(struct mailbox_transaction_context *t,
i_stream_read_next_line(mailstream);
if (o_stream_send_istream(outstream, mailstream) < 0) {
- ret = -1;
mail_storage_set_error(t->box->storage,
ME(NOTPOSSIBLE)
"Failed to copy to spool file");
@@ -223,26 +223,27 @@ int backend_handle_mail(struct mailbox_transaction_context *t,
o_stream_destroy(&outstream);
out_close:
close(fd);
- out:
if (ret)
unlink(buf);
-
+ out:
t_pop();
return ret;
}
-void backend_init(pool_t pool __attr_unused__)
+static void backend_init(pool_t pool __attr_unused__)
{
- if((spamspool = get_setting("SPOOL2DIR_SPAM")))
+ spamspool = get_setting("SPOOL2DIR_SPAM");
+ if (spamspool)
debug("spool2dir spamspool %s\n", spamspool);
- if((hamspool = get_setting("SPOOL2DIR_NOTSPAM")))
+ hamspool = get_setting("SPOOL2DIR_NOTSPAM");
+ if (hamspool)
debug("spool2dir hamspool %s\n", hamspool);
}
-struct antispam_transaction_context *
- backend_start(struct mailbox *box __attr_unused__)
+static struct antispam_transaction_context *
+backend_start(struct mailbox *box __attr_unused__)
{
struct antispam_transaction_context *ast;
@@ -254,6 +255,15 @@ struct antispam_transaction_context *
}
-void backend_exit(void)
+static void backend_exit(void)
{
}
+
+struct backend spool2dir_backend = {
+ .init = backend_init,
+ .exit = backend_exit,
+ .handle_mail = backend_handle_mail,
+ .start = backend_start,
+ .rollback = backend_rollback,
+ .commit = backend_commit,
+};
diff --git a/version.sh b/version.sh
index 2cbdde6..7849cd8 100755
--- a/version.sh
+++ b/version.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-VERSION=1.2
+VERSION=1.4
if head=$(git rev-parse --verify HEAD 2>/dev/null); then
git update-index --refresh --unmerged > /dev/null