diff options
author | Alexander Sulfrian <alexander@sulfrian.net> | 2011-05-03 19:26:38 +0200 |
---|---|---|
committer | Alexander Sulfrian <alexander@sulfrian.net> | 2011-05-03 19:26:38 +0200 |
commit | a1527ce7177df11a8ee1b46a0e7b64f0f0df234b (patch) | |
tree | 41f724c2a04b3582878d40dc451930e71a7b86aa | |
parent | c8a9a6550b84412f919b772a9cb8db1e3ef79d19 (diff) | |
parent | 808eaa49efe2dc2438ef98ddf4bac4cfd2898a3d (diff) | |
download | dovecot-antispam-master.tar.gz dovecot-antispam-master.tar.xz dovecot-antispam-master.zip |
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile | 73 | ||||
-rw-r--r-- | NOTES | 2 | ||||
-rw-r--r-- | antispam-plugin.c | 59 | ||||
-rw-r--r-- | antispam-plugin.h | 85 | ||||
-rw-r--r-- | antispam-storage-1.0.c | 14 | ||||
-rw-r--r-- | antispam-storage-1.1.c | 16 | ||||
-rw-r--r-- | antispam-storage-1.2.c | 16 | ||||
-rw-r--r-- | antispam.7 | 77 | ||||
-rw-r--r-- | crm114-exec.c | 25 | ||||
-rw-r--r-- | debug.c | 46 | ||||
-rw-r--r-- | defconfig | 33 | ||||
-rw-r--r-- | dspam-exec.c | 31 | ||||
-rw-r--r-- | pipe.c (renamed from mailtrain.c) | 95 | ||||
-rw-r--r-- | signature-log.c | 27 | ||||
-rw-r--r-- | spool2dir.c | 50 | ||||
-rwxr-xr-x | version.sh | 2 |
17 files changed, 385 insertions, 267 deletions
@@ -6,4 +6,5 @@ dovecot-version.h antispam-version.h dovecot-version *-stamp +*.sw[lmnop] dovecot-1.1.8-src/ @@ -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 @@ -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; } @@ -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, +}; @@ -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); +} @@ -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, +}; @@ -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, +}; @@ -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 |