diff options
-rw-r--r-- | Makefile | 25 | ||||
-rw-r--r-- | antispam-plugin.c | 124 | ||||
-rw-r--r-- | antispam-plugin.h | 42 | ||||
-rw-r--r-- | antispam-storage-1.0.c | 216 | ||||
-rw-r--r-- | api-compat.h | 15 | ||||
-rw-r--r-- | debug.c | 2 | ||||
-rw-r--r-- | defconfig | 6 | ||||
-rw-r--r-- | dspam-exec.c | 49 | ||||
-rw-r--r-- | plugin.c | 448 | ||||
-rw-r--r-- | plugin.h | 40 | ||||
-rw-r--r-- | signature.c | 53 | ||||
-rw-r--r-- | signature.h | 17 |
12 files changed, 517 insertions, 520 deletions
@@ -11,14 +11,6 @@ CFLAGS += -I$(DOVECOT)/src/lib-mail/ CFLAGS += -I$(DOVECOT)/src/lib-imap/ CFLAGS += -I$(DOVECOT)/src/imap/ -# per-backend configuration -ifeq ("$(BACKEND)", "dspam-exec") -CFLAGS += -DBACKEND_WANTS_SIGNATURE=1 -# can take a while, check more often -CFLAGS += -DCOPY_CHECK_INTERVAL=10 -endif - - # debug rules ifeq ("$(DEBUG)", "stderr") CFLAGS += -DCONFIG_DEBUG -DDEBUG_STDERR @@ -29,18 +21,29 @@ objs += debug.o endif # dovecot version rules -CFLAGS += -DDOVECOT_VER=$(DOVECOT_VERSION) +objs += antispam-storage-$(DOVECOT_VERSION).o +ifeq ("$(DOVECOT_VERSION)", "1.0") +CFLAGS += -Dstr_array_length=strarray_length +CFLAGS += "-Dmempool_unref(x)=pool_unref(*(x))" +else +CFLAGS += "-Dmempool_unref(x)=pool_unref(x)" +endif + +# per-backend rules +ifeq ("$(BACKEND)", "dspam-exec") +objs += signature.o +endif # main make rules CFLAGS += -fPIC -shared -Wall CC ?= "gcc" -objs += plugin.o $(BACKEND).o +objs += antispam-plugin.o $(BACKEND).o ALL = antispam all: verify_config $(ALL) -%.o: %.c .config +%.o: %.c .config antispam-plugin.h $(CC) -c $(CFLAGS) -o $@ $< antispam: $(objs) diff --git a/antispam-plugin.c b/antispam-plugin.c new file mode 100644 index 0000000..556d631 --- /dev/null +++ b/antispam-plugin.c @@ -0,0 +1,124 @@ +/* + * antispam plugin for dovecot + * + * Copyright (C) 2004-2007 Johannes Berg <johannes@sipsolutions.net> + * 2006 Frank Cusack + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * + * based on the original framework http://www.dovecot.org/patches/1.0/copy_plugin.c + * + * Please see http://johannes.sipsolutions.net/wiki/Projects/dovecot-dspam-integration + * for more information on this code. + * + * Install the plugin in the usual dovecot module location. + */ + +#include <stdlib.h> + +/* dovecot headers we need */ +#include "lib.h" +#include "str.h" +#include "client.h" +#include "ostream.h" +#include "imap-search.h" + +/* defined by imap, pop3, lda */ +extern void (*hook_mail_storage_created)(struct mail_storage *storage); + +/* internal stuff we need */ +#include "antispam-plugin.h" + +static pool_t global_pool; +static char **trash_folders = NULL; +static char *default_spam_folders[] = { + "SPAM", + NULL +}; +static char **spam_folders = default_spam_folders; + +static bool mailbox_in_list(struct mail_storage *storage, struct mailbox *box, + char **list) +{ + if (!list) + return FALSE; + + while (*list) { + if (mailbox_equals(box, storage, *list)) + return TRUE; + list++; + } + + return FALSE; +} + +bool mailbox_is_spam(struct mail_storage *storage, struct mailbox *box) +{ + return mailbox_in_list(storage, box, spam_folders); +} + +bool mailbox_is_trash(struct mail_storage *storage, struct mailbox *box) +{ + return mailbox_in_list(storage, box, trash_folders); +} + +void antispam_init(void) +{ + char *tmp, **iter; + + debug("antispam plugin intialising\n"); + + global_pool = pool_alloconly_create("antispam-pool", 1024); + + tmp = getenv("ANTISPAM_TRASH"); + if (tmp) + trash_folders = p_strsplit(global_pool, tmp, ";"); + + if (trash_folders) { + iter = trash_folders; + while (*iter) { + debug("antispam: \"%s\" is trash folder\n", *iter); + iter++; + } + } else + debug("antispam: no trash folders\n"); + + tmp = getenv("ANTISPAM_SPAM"); + if (tmp) + spam_folders = p_strsplit(global_pool, tmp, ";"); + + if (spam_folders) { + iter = spam_folders; + while (*iter) { + debug("antispam: \"%s\" is spam folder\n", *iter); + iter++; + } + } else + debug("antispam: no spam folders\n"); + + backend_init(global_pool); + + antispam_next_hook_mail_storage_created = hook_mail_storage_created; + hook_mail_storage_created = antispam_mail_storage_created; +} + +void antispam_deinit(void) +{ + hook_mail_storage_created = antispam_next_hook_mail_storage_created; + backend_exit(); + mempool_unref(&global_pool); +} + +/* put dovecot version we built against into plugin for checking */ +const char *antispam_version = PACKAGE_VERSION; diff --git a/antispam-plugin.h b/antispam-plugin.h new file mode 100644 index 0000000..7d9003b --- /dev/null +++ b/antispam-plugin.h @@ -0,0 +1,42 @@ +#ifndef _ANTISPAM_PLUGIN_H +#define _ANTISPAM_PLUGIN_H + +#include "lib.h" +#include "str.h" +#include "client.h" +#include "ostream.h" +#include "imap-search.h" + +struct antispam_transaction_context; + +/* + * Call backend giving + * - pool: dovecot memory pool, will be freed afterwards + * - spam: whether mail comes from spam folder or not + * - sigs: signatures, next == NULL terminates list + * - + */ +void backend_init(pool_t pool); +void backend_exit(void); +int backend_handle_mail(struct mailbox_transaction_context *t, + struct antispam_transaction_context *ast, + struct mail *mail); +struct antispam_transaction_context *backend_start(struct mailbox *box); +void backend_rollback(struct antispam_transaction_context *ast); +int backend_commit(struct antispam_transaction_context *ast); + +#ifdef CONFIG_DEBUG +void debug(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); +#else +static void debug(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); +static inline void debug(const char *fmt, ...) +{ +} +#endif + +void antispam_mail_storage_created(struct mail_storage *storage); +void (*antispam_next_hook_mail_storage_created)(struct mail_storage *storage); +bool mailbox_is_spam(struct mail_storage *storage, struct mailbox *box); +bool mailbox_is_trash(struct mail_storage *storage, struct mailbox *box); + +#endif /* _ANTISPAM_PLUGIN_H */ diff --git a/antispam-storage-1.0.c b/antispam-storage-1.0.c new file mode 100644 index 0000000..73a7993 --- /dev/null +++ b/antispam-storage-1.0.c @@ -0,0 +1,216 @@ +/* + * Storage implementation for antispam plugin + * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> + * + * Derived from Quota plugin: + * Copyright (C) 2005 Timo Sirainen + */ + +#include <sys/stat.h> + +#include "lib.h" +#include "array.h" +#include "istream.h" +#include "mail-search.h" +#include "mail-storage-private.h" + +#include "antispam-plugin.h" + +#define ANTISPAM_CONTEXT(obj) \ + *((void **)array_idx_modifyable(&(obj)->module_contexts, \ + antispam_storage_module_id)) + +struct antispam_mail_storage { + struct mail_storage_vfuncs super; + struct antispam *antispam; +}; + +struct antispam_mailbox { + struct mailbox_vfuncs super; + + unsigned int save_hack:1; +}; + +static unsigned int antispam_storage_module_id = 0; +static bool antispam_storage_module_id_set = FALSE; + +static int +antispam_copy(struct mailbox_transaction_context *t, struct mail *mail, + enum mail_flags flags, struct mail_keywords *keywords, + struct mail *dest_mail) +{ + struct antispam_mailbox *asbox = ANTISPAM_CONTEXT(t->box); + struct antispam_transaction_context *ast = + ANTISPAM_CONTEXT(t); + struct mail *copy_dest_mail; + int ret; + + if (dest_mail != NULL) + copy_dest_mail = dest_mail; + else + copy_dest_mail = mail_alloc(t, MAIL_FETCH_PHYSICAL_SIZE, NULL); + + asbox->save_hack = FALSE; + if (asbox->super.copy(t, mail, flags, keywords, copy_dest_mail) < 0) + return -1; + + /* + * If copying used saving internally, we already have treated the mail + */ + if (asbox->save_hack) + ret = 0; + else + ret = backend_handle_mail(t, ast, copy_dest_mail); + + if (copy_dest_mail != dest_mail) + mail_free(©_dest_mail); + return ret; +} + +static int antispam_save_finish(struct mail_save_context *ctx, + struct mail *dest_mail) +{ + struct antispam_mailbox *asbox = + ANTISPAM_CONTEXT(ctx->transaction->box); + struct antispam_transaction_context *ast = + ANTISPAM_CONTEXT(ctx->transaction); + struct mail *save_dest_mail; + int ret; + + if (dest_mail != NULL) + save_dest_mail = dest_mail; + else + save_dest_mail = mail_alloc(ctx->transaction, + MAIL_FETCH_PHYSICAL_SIZE, NULL); + + if (asbox->super.save_finish(ctx, save_dest_mail) < 0) + return -1; + + asbox->save_hack = TRUE; + ret = backend_handle_mail(ctx->transaction, ast, save_dest_mail); + + if (save_dest_mail != dest_mail) + mail_free(&save_dest_mail); + return ret; +} + +static struct antispam_transaction_context * +antispam_transaction_begin(struct mailbox *box) +{ + struct antispam_transaction_context *ast; + + ast = backend_start(box); + i_assert(ast != NULL); + + debug("antispam: transaction %p begins on %s\n", ast, box->name); + + return ast; +} + +static void +antispam_transaction_rollback(struct antispam_transaction_context **_ast) +{ + struct antispam_transaction_context *ast = *_ast; + + debug("antispam: transaction %p rolled back\n", ast); + + backend_rollback(ast); + *_ast = NULL; +} + +static int +antispam_transaction_commit(struct antispam_transaction_context **_ast) +{ + struct antispam_transaction_context *ast = *_ast; + int ret; + + ret = backend_commit(ast); + *_ast = NULL; + debug("antispam: transaction %p commit: %d\n", ast, ret); + return ret; +} + +static struct mailbox_transaction_context * +antispam_mailbox_transaction_begin(struct mailbox *box, + enum mailbox_transaction_flags flags) +{ + struct antispam_mailbox *asbox = ANTISPAM_CONTEXT(box); + struct mailbox_transaction_context *t; + struct antispam_transaction_context *ast; + + t = asbox->super.transaction_begin(box, flags); + ast = antispam_transaction_begin(box); + + array_idx_set(&t->module_contexts, antispam_storage_module_id, &ast); + return t; +} + +static int +antispam_mailbox_transaction_commit(struct mailbox_transaction_context *ctx, + enum mailbox_sync_flags flags) +{ + struct antispam_mailbox *asbox = ANTISPAM_CONTEXT(ctx->box); + struct antispam_transaction_context *ast = ANTISPAM_CONTEXT(ctx); + + if (antispam_transaction_commit(&ast) < 0) { + asbox->super.transaction_rollback(ctx); + return -1; + } else + return asbox->super.transaction_commit(ctx, flags); +} + +static void +antispam_mailbox_transaction_rollback(struct mailbox_transaction_context *ctx) +{ + struct antispam_mailbox *asbox = ANTISPAM_CONTEXT(ctx->box); + struct antispam_transaction_context *ast = ANTISPAM_CONTEXT(ctx); + + antispam_transaction_rollback(&ast); + asbox->super.transaction_rollback(ctx); +} + +static struct mailbox *antispam_mailbox_open(struct mail_storage *storage, + const char *name, + struct istream *input, + enum mailbox_open_flags flags) +{ + struct antispam_mail_storage *as_storage = ANTISPAM_CONTEXT(storage); + struct mailbox *box; + struct antispam_mailbox *asbox; + + box = as_storage->super.mailbox_open(storage, name, input, flags); + if (box == NULL) + return NULL; + + asbox = p_new(box->pool, struct antispam_mailbox, 1); + asbox->super = box->v; + + box->v.save_finish = antispam_save_finish; + box->v.transaction_begin = antispam_mailbox_transaction_begin; + box->v.transaction_commit = antispam_mailbox_transaction_commit; + box->v.transaction_rollback = antispam_mailbox_transaction_rollback; + box->v.copy = antispam_copy; + array_idx_set(&box->module_contexts, antispam_storage_module_id, + &asbox); + return box; +} + +void antispam_mail_storage_created(struct mail_storage *storage) +{ + struct antispam_mail_storage *as_storage; + + if (antispam_next_hook_mail_storage_created != NULL) + antispam_next_hook_mail_storage_created(storage); + + as_storage = p_new(storage->pool, struct antispam_mail_storage, 1); + as_storage->super = storage->v; + storage->v.mailbox_open = antispam_mailbox_open; + + if (!antispam_storage_module_id_set) { + antispam_storage_module_id = mail_storage_module_id++; + antispam_storage_module_id_set = TRUE; + } + + array_idx_set(&storage->module_contexts, + antispam_storage_module_id, &as_storage); +} diff --git a/api-compat.h b/api-compat.h deleted file mode 100644 index f0a0476..0000000 --- a/api-compat.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef _ANTISPAM_API_COMPAT_H -#define _ANTISPAM_API_COMPAT_H - -#if DOVECOT_VER==10000 -#define MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS 0 -#define str_array_length strarray_length -#define IMAP_SYNC_FLAG_SAFE 0 -#define mailbox_transaction_commit(arg) mailbox_transaction_commit(arg, 0) -#define mempool_unref(poolptr) pool_unref((*poolptr)) -#define command_register(str, func, flags) command_register(str, func) -#else -#define mempool_unref(poolptr) pool_unref(poolptr) -#endif - -#endif /* _ANTISPAM_API_COMPAT_H */ @@ -2,7 +2,7 @@ #include <syslog.h> #include <stdarg.h> #include <stdio.h> -#include "plugin.h" +#include "antispam-plugin.h" static void _debug(const char *format, va_list ap) { @@ -20,8 +20,8 @@ DOVECOT=../dovecot-1.0.5 #DOVECOT=/usr/include/dovecot # Dovecot version to build against -DOVECOT_VERSION=10000 # version 1.0 -#DOVECOT_VERSION=10100 # version 1.1 +DOVECOT_VERSION=1.0 +#DOVECOT_VERSION=1.1 # CURRENTLY BROKEN # backend # - dspam-exec: direct dspam training by calling dspam executable @@ -30,4 +30,4 @@ BACKEND=dspam-exec # enable debugging to syslog or stderr #DEBUG=stderr -#DEBUG=syslog +DEBUG=syslog diff --git a/dspam-exec.c b/dspam-exec.c index f6c0707..92f016c 100644 --- a/dspam-exec.c +++ b/dspam-exec.c @@ -18,14 +18,15 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ +#include <unistd.h> #include <stdlib.h> #include <sys/wait.h> #include <fcntl.h> #include "lib.h" -#include "plugin.h" -#include "api-compat.h" +#include "antispam-plugin.h" +#include "signature.h" static const char *dspam_binary = "/usr/bin/dspam"; static char **extra_args = NULL; @@ -139,6 +140,47 @@ static int call_dspam(pool_t pool, const char *signature, bool is_spam) } } +struct antispam_transaction_context { + struct siglist *siglist; +}; + +struct antispam_transaction_context *backend_start(struct mailbox *box) +{ + struct antispam_transaction_context *ast; + + ast = i_new(struct antispam_transaction_context, 1); + ast->siglist = NULL; + return ast; +} + +void backend_rollback(struct antispam_transaction_context *ast) +{ + signature_list_free(&ast->siglist); + i_free(ast); +} + +int backend_commit(struct antispam_transaction_context *ast) +{ + struct siglist *item = ast->siglist; + + while (item) { + debug("antispam: got signature %s\n", item->sig); + item = item->next; + } + + signature_list_free(&ast->siglist); + i_free(ast); + return 0; +} + +int backend_handle_mail(struct mailbox_transaction_context *t, + struct antispam_transaction_context *ast, + struct mail *mail) +{ + return signature_extract(t, mail, &ast->siglist); +} + +#if 0 bool backend(pool_t pool, bool spam, struct strlist *sigs) { int ret; @@ -153,6 +195,7 @@ bool backend(pool_t pool, bool spam, struct strlist *sigs) return TRUE; } +#endif void backend_init(pool_t pool) { @@ -174,6 +217,8 @@ void backend_init(pool_t pool) debug("antispam: dspam extra arg %s\n", extra_args[i]); } + + signature_init(); } void backend_exit(void) diff --git a/plugin.c b/plugin.c deleted file mode 100644 index 63c853f..0000000 --- a/plugin.c +++ /dev/null @@ -1,448 +0,0 @@ -/* - * antispam plugin for dovecot - * - * Copyright (C) 2004-2007 Johannes Berg <johannes@sipsolutions.net> - * 2006 Frank Cusack - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License Version 2 as - * published by the Free Software Foundation. - * - * 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - * - * based on the original framework http://www.dovecot.org/patches/1.0/copy_plugin.c - * - * Please see http://johannes.sipsolutions.net/wiki/Projects/dovecot-dspam-integration - * for more information on this code. - * - * Install the plugin in the usual dovecot module location. - */ - -#include <stdlib.h> - -/* dovecot headers we need */ -#include "lib.h" -#include "str.h" -#include "client.h" -#include "ostream.h" -#include "imap-search.h" - -/* internal stuff we need */ -#include "plugin.h" -#include "api-compat.h" - -static pool_t global_pool; -static char **trash_folders = NULL; -static char *default_spam_folders[] = { - "SPAM", - NULL -}; -static char **spam_folders = default_spam_folders; -#ifdef BACKEND_WANTS_SIGNATURE -static char *signature_hdr = "X-DSPAM-Signature"; -#endif - -static struct strlist *list_add(pool_t pool, struct strlist *list) -{ - struct strlist *n; - - n = p_malloc(pool, sizeof(struct strlist)); - n->next = list; - n->str = NULL; - - return n; -} - -static bool mailbox_in_list(struct mail_storage *storage, struct mailbox *box, - char **list) -{ - if (!list) - return FALSE; - - while (*list) { - if (mailbox_equals(box, storage, *list)) - return TRUE; - list++; - } - - return FALSE; -} - -static void client_send_sendalive_if_needed(struct client *client) -{ - time_t now, last_io; - - if (o_stream_get_buffer_used_size(client->output) != 0) - return; - - now = time(NULL); - last_io = I_MAX(client->last_input, client->last_output); - if (now - last_io > MAIL_STORAGE_STAYALIVE_SECS) { - o_stream_send_str(client->output, "* OK Hang in there..\r\n"); - o_stream_flush(client->output); - client->last_output = now; - } -} - -#define GENERIC_ERROR -1 -#define SIGNATURE_MISSING -2 -#define BACKEND_FAILURE -3 - -/* mostly copied from cmd-copy.c (notes added where changed) */ -/* MODIFIED: prototype to include "src_spam" */ -static int fetch_and_copy(struct client *client, struct mailbox *destbox, - struct mailbox_transaction_context *t, - struct mail_search_arg *search_args, - const char **src_uidset_r, - unsigned int *copy_count_r, - bool src_spam) -{ - struct mail_search_context *search_ctx; - struct mailbox_transaction_context *src_trans; - struct mail_keywords *keywords; - const char *const *keywords_list; - struct mail *mail; - unsigned int copy_count = 0; -#if DOVECOT_VER==10100 - struct msgset_generator_context srcset_ctx; - string_t *src_uidset; -#endif - int ret; - /* MODIFIED: new variables */ - pool_t pool = pool_alloconly_create("antispam-copy-pool", 1024); -#ifdef BACKEND_WANTS_SIGNATURE - const char *signature; - struct strlist *siglist = NULL; -#else -#error Not implemented -#endif - -#if DOVECOT_VER==10100 - src_uidset = t_str_new(256); - msgset_generator_init(&srcset_ctx, src_uidset); -#endif - - src_trans = mailbox_transaction_begin(client->mailbox, 0); - search_ctx = mailbox_search_init(src_trans, NULL, search_args, NULL); - - mail = mail_alloc(src_trans, MAIL_FETCH_STREAM_HEADER | - MAIL_FETCH_STREAM_BODY, NULL); - ret = 1; - while (mailbox_search_next(search_ctx, mail) > 0 && ret > 0) { - if (mail->expunged) { - ret = 0; - break; - } - - if ((++copy_count % COPY_CHECK_INTERVAL) == 0) - client_send_sendalive_if_needed(client); - - /* MODIFIED: keep track of mail as we copy */ -#ifdef BACKEND_WANTS_SIGNATURE -#if DOVECOT_VER==10000 - signature = mail_get_first_header(mail, signature_hdr); -#elif DOVECOT_VER==10100 - signature = NULL; - ret = mail_get_first_header(mail, signature_hdr, &signature); -#endif - /* ret can only be != 1 with new dovecot 1.1 API */ - if (ret != 1 || !signature || !signature[0]) { - ret = SIGNATURE_MISSING; - break; - } - siglist = list_add(pool, siglist); - siglist->str = p_strdup(pool, signature); -#else -#error Not implemented -#endif - -#if DOVECOT_VER==10000 - keywords_list = mail_get_keywords(mail); - keywords = str_array_length(keywords_list) == 0 ? NULL : - mailbox_keywords_create(t, keywords_list); - if (mailbox_copy(t, mail, mail_get_flags(mail), - keywords, NULL) < 0) - ret = mail->expunged ? 0 : -1; - mailbox_keywords_free(t, &keywords); -#elif DOVECOT_VER==10100 - keywords_list = mail_get_keywords(mail); - keywords = str_array_length(keywords_list) == 0 ? NULL : - mailbox_keywords_create_valid(destbox, keywords_list); - if (mailbox_copy(t, mail, mail_get_flags(mail), - keywords, NULL) < 0) - ret = mail->expunged ? 0 : -1; - mailbox_keywords_free(destbox, &keywords); - - msgset_generator_next(&srcset_ctx, mail->uid); -#endif - } - mail_free(&mail); -#if DOVECOT_VER==10100 - msgset_generator_finish(&srcset_ctx); -#endif - - if (mailbox_search_deinit(&search_ctx) < 0) - ret = -1; - - if (mailbox_transaction_commit(&src_trans) < 0) - ret = -1; - - /* MODIFIED: pass to backend */ -#ifdef BACKEND_WANTS_SIGNATURE - /* got all signatures now, pass them to backend if no errors */ - if (ret > 0 && !backend(pool, src_spam, siglist)) - ret = BACKEND_FAILURE; -#else -#error Not implemented -#endif - /* MODIFIED: kill pool */ - mempool_unref(&pool); - -#if DOVECOT_VER==10100 - *src_uidset_r = str_c(src_uidset); - *copy_count_r = copy_count; -#endif - return ret; -} - - -/* mostly copied from cmd-copy.c (notes added where changed) */ -static bool cmd_copy_antispam(struct client_command_context *cmd) -{ - struct client *client = cmd->client; - struct mail_storage *storage; - struct mailbox *destbox; - struct mailbox_transaction_context *t; - struct mail_search_arg *search_arg; - const char *messageset, *mailbox, *src_uidset; - enum mailbox_sync_flags sync_flags = 0; - unsigned int copy_count; - enum imap_sync_flags imap_flags = 0; -#if DOVECOT_VER==10100 - uint32_t uid_validity, uid1, uid2; - const char *msg = NULL; -#endif - int ret; - /* MODIFIED: added variables */ - bool dst_spam, src_spam; - - /* <message set> <mailbox> */ - if (!client_read_string_args(cmd, 2, &messageset, &mailbox)) - return FALSE; - - if (!client_verify_open_mailbox(cmd)) - return TRUE; - - /* open the destination mailbox */ - if (!client_verify_mailbox_name(cmd, mailbox, TRUE, FALSE)) - return TRUE; - - search_arg = imap_search_get_arg(cmd, messageset, cmd->uid); - if (search_arg == NULL) - return TRUE; - - storage = client_find_storage(cmd, &mailbox); - if (storage == NULL) - return TRUE; - - if (mailbox_equals(client->mailbox, storage, mailbox)) { - destbox = client->mailbox; - /* MODIFIED: don't try to reclassify on copy within folder */ - return cmd_copy(cmd); - } else { - destbox = mailbox_open(storage, mailbox, NULL, - MAILBOX_OPEN_SAVEONLY | - MAILBOX_OPEN_FAST | - MAILBOX_OPEN_KEEP_RECENT); - if (destbox == NULL) { - client_send_storage_error(cmd, storage); - return TRUE; - } - } - - /* MODIFIED: Trash detection */ - if (mailbox_in_list(storage, client->mailbox, trash_folders) || - mailbox_in_list(storage, destbox, trash_folders)) { - mailbox_close(&destbox); - return cmd_copy(cmd); - } - - /* MODIFIED: from/to-SPAM detection */ - src_spam = mailbox_in_list(storage, client->mailbox, spam_folders); - dst_spam = mailbox_in_list(storage, destbox, spam_folders); - /* - * "both spam" can happen with multiple spam folders, - * "none spam" is the common case where spam folders are not involved - */ - if ((src_spam && dst_spam) || - (!src_spam && !dst_spam)) { - mailbox_close(&destbox); - return cmd_copy(cmd); - } - - t = mailbox_transaction_begin(destbox, - MAILBOX_TRANSACTION_FLAG_EXTERNAL | - MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS); - ret = fetch_and_copy(client, destbox, t, search_arg, - &src_uidset, ©_count, src_spam); - - if (ret <= 0) - mailbox_transaction_rollback(&t); -#if DOVECOT_VER==10000 - else { - if (mailbox_transaction_commit(&t) < 0) - ret = -1; - } -#elif DOVECOT_VER==10100 - else if (mailbox_transaction_commit_get_uids(&t, &uid_validity, - &uid1, &uid2) < 0) - ret = -1; - else if (copy_count == 0) - msg = "OK No messages copied."; - else { - i_assert(copy_count == uid2 - uid1 + 1); - - if (uid1 == uid2) { - msg = t_strdup_printf("OK [COPYUID %u %s %u] " - "Copy completed.", - uid_validity, src_uidset, uid1); - } else { - msg = t_strdup_printf("OK [COPYUID %u %s %u:%u] " - "Copy completed.", - uid_validity, src_uidset, - uid1, uid2); - } - } -#endif - - if (destbox != client->mailbox) { - sync_flags |= MAILBOX_SYNC_FLAG_FAST; - imap_flags |= IMAP_SYNC_FLAG_SAFE; - mailbox_close(&destbox); - } - - /* MODIFIED: extended error codes */ - if (ret > 0) - return cmd_sync(cmd, sync_flags, imap_flags, "OK Copy completed."); - else if (ret == 0) { - /* some messages were expunged, sync them */ - return cmd_sync(cmd, 0, 0, - "NO Some of the requested messages no longer exist."); - } else if (ret == SIGNATURE_MISSING) { - return cmd_sync(cmd, 0, 0, - "NO Some of the requested messages have no" - " antispam signature."); - } else if (ret == BACKEND_FAILURE) { - return cmd_sync(cmd, 0, 0, - "NO Antispam backend failed to retrain."); - } else { - client_send_storage_error(cmd, storage); - return TRUE; - } -} - - -static bool cmd_append_antispam(struct client_command_context *cmd) -{ - const char *mailbox; - struct mail_storage *storage; - struct mailbox *box; - - /* <mailbox> */ - if (!client_read_string_args(cmd, 1, &mailbox)) - return FALSE; - - storage = client_find_storage(cmd, &mailbox); - if (!storage) - return FALSE; - - box = mailbox_open(storage, mailbox, NULL, - MAILBOX_OPEN_FAST | MAILBOX_OPEN_KEEP_RECENT); - if (box) { - if (mailbox_in_list(storage, box, spam_folders)) { - mailbox_close(&box); - return cmd_sync(cmd, 0, 0, - "NO Cannot APPEND to spam folder."); - } - - mailbox_close(&box); - } - - return cmd_append(cmd); -} - - -void antispam_init(void) -{ - char *tmp, **iter; - - debug("antispam plugin intialising\n"); - - global_pool = pool_alloconly_create("antispam-pool", 1024); - - tmp = getenv("ANTISPAM_TRASH"); - if (tmp) - trash_folders = p_strsplit(global_pool, tmp, ";"); - - if (trash_folders) { - iter = trash_folders; - while (*iter) { - debug("antispam: \"%s\" is trash folder\n", *iter); - iter++; - } - } else - debug("antispam: no trash folders\n"); - - tmp = getenv("ANTISPAM_SPAM"); - if (tmp) - spam_folders = p_strsplit(global_pool, tmp, ";"); - - if (spam_folders) { - iter = spam_folders; - while (*iter) { - debug("antispam: \"%s\" is spam folder\n", *iter); - iter++; - } - } else - debug("antispam: no spam folders\n"); - -#ifdef BACKEND_WANTS_SIGNATURE - tmp = getenv("ANTISPAM_SIGNATURE"); - if (tmp) - signature_hdr = tmp; - debug("antispam: signature header line is \"%s\"\n", signature_hdr); -#endif - - backend_init(global_pool); - - command_unregister("COPY"); - command_unregister("APPEND"); - command_unregister("UID COPY"); - /* - * i_strdup() here is a kludge to avoid crashing in commands_deinit() - * since modules are unloaded before it's called, this "COPY" string - * would otherwise point to nonexisting memory. - */ - command_register(i_strdup("COPY"), cmd_copy_antispam, - COMMAND_FLAG_USES_SEQS | COMMAND_FLAG_BREAKS_SEQS); - command_register(i_strdup("UID COPY"), cmd_copy_antispam, - COMMAND_FLAG_BREAKS_SEQS); - command_register(i_strdup("APPEND"), cmd_append_antispam, - COMMAND_FLAG_BREAKS_SEQS); -} - -void antispam_deinit(void) -{ - backend_exit(); - mempool_unref(&global_pool); -} - -/* put dovecot version we built against into plugin for checking */ -const char *antispam_version = PACKAGE_VERSION; diff --git a/plugin.h b/plugin.h deleted file mode 100644 index c2281ad..0000000 --- a/plugin.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef _ANTISPAM_PLUGIN_H -#define _ANTISPAM_PLUGIN_H - -#include <unistd.h> -#include "lib.h" -#include "mempool.h" - -struct strlist { - struct strlist *next; - const char *str; -}; - -#ifdef BACKEND_WANTS_SIGNATURE -/* - * Call backend giving - * - pool: dovecot memory pool, will be freed afterwards - * - spam: whether mail comes from spam folder or not - * - sigs: signatures, next == NULL terminates list - * - - */ -bool backend(pool_t pool, bool spam, struct strlist *sigs); -#elif CONFIG_PLUGIN_WANT_MAIL -#error TODO: no support for pristine training yet -#else -#error BUILD SYSTEM ERROR -#endif - -void backend_init(pool_t pool); -void backend_exit(void); - -#ifdef CONFIG_DEBUG -void debug(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); -#else -static void debug(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); -static inline void debug(const char *fmt, ...) -{ -} -#endif - -#endif /* _ANTISPAM_PLUGIN_H */ diff --git a/signature.c b/signature.c new file mode 100644 index 0000000..df04623 --- /dev/null +++ b/signature.c @@ -0,0 +1,53 @@ + +#include <stdlib.h> +#include "antispam-plugin.h" +#include "signature.h" +#include "mail-storage-private.h" + +static char *signature_hdr = "X-DSPAM-Signature"; + +void signature_init(void) +{ + char *tmp = getenv("ANTISPAM_SIGNATURE"); + if (tmp) + signature_hdr = tmp; + debug("antispam: signature header line is \"%s\"\n", signature_hdr); +} + +int signature_extract(struct mailbox_transaction_context *t, + struct mail *mail, struct siglist **list) +{ + const char *signature; + struct siglist *item; + + signature = mail_get_first_header(mail, signature_hdr); + if (!signature || !signature[0]) { + mail_storage_set_error(t->box->storage, + "antispam signature not found"); + return -1; + } + + item = i_new(struct siglist, 1); + item->next = *list; + *list = item; + item->sig = i_strdup(signature); + return 0; +} + +void signature_list_free(struct siglist **list) +{ + struct siglist *item, *next; + + i_assert(list); + + item = *list; + + while (item) { + next = item->next; + i_free(item->sig); + i_free(item); + item = next; + if (item) + next = item->next; + } +} diff --git a/signature.h b/signature.h new file mode 100644 index 0000000..ad3841a --- /dev/null +++ b/signature.h @@ -0,0 +1,17 @@ +#ifndef _ANTISPAM_SIGNATURE_H +#define _ANTISPAM_SIGNATURE_H + +#include "lib.h" +#include "client.h" + +struct siglist { + struct siglist *next; + char *sig; +}; + +void signature_init(void); +int signature_extract(struct mailbox_transaction_context *t, + struct mail *mail, struct siglist **list); +void signature_list_free(struct siglist **list); + +#endif /* _ANTISPAM_SIGNATURE_H */ |