diff options
Diffstat (limited to '')
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | antispam-plugin.c | 51 | ||||
-rw-r--r-- | antispam-plugin.h | 4 | ||||
-rw-r--r-- | antispam-storage-1.0.c | 144 |
4 files changed, 187 insertions, 13 deletions
@@ -16,6 +16,7 @@ CFLAGS += -I$(DOVECOT)/src/lib-storage/ CFLAGS += -I$(DOVECOT)/src/lib-mail/ CFLAGS += -I$(DOVECOT)/src/lib-imap/ CFLAGS += -I$(DOVECOT)/src/lib-dict/ +CFLAGS += -I$(DOVECOT)/src/lib-index/ CFLAGS += -I$(DOVECOT)/src/imap/ # output name diff --git a/antispam-plugin.c b/antispam-plugin.c index 407b909..5dd9a02 100644 --- a/antispam-plugin.c +++ b/antispam-plugin.c @@ -49,6 +49,11 @@ static char *default_spam_folders[] = { static char **spam_folders = default_spam_folders; static char **unsure_folders = NULL; bool antispam_can_append_to_spam = FALSE; +static char **spam_keywords = NULL; + +bool need_keyword_hook; +bool need_folder_hook; + static bool mailbox_in_list(struct mailbox *box, char **list) { @@ -79,6 +84,22 @@ bool mailbox_is_unsure(struct mailbox *box) return mailbox_in_list(box, unsure_folders); } +bool keyword_is_spam(const char *keyword) +{ + char **k = spam_keywords; + + if (!spam_keywords) + return FALSE; + + while (*k) { + if (strcmp(*k, keyword) == 0) + return TRUE; + k++; + } + + return FALSE; +} + const char *get_setting(const char *name) { const char *env; @@ -105,6 +126,7 @@ void PLUGIN_FUNCTION(init)(void) { const char *tmp; char * const *iter; + int spam_folder_count = 0; debug("plugin initialising\n"); @@ -127,17 +149,12 @@ void PLUGIN_FUNCTION(init)(void) if (tmp) spam_folders = p_strsplit(global_pool, tmp, ";"); - tmp = get_setting("ALLOW_APPEND_TO_SPAM"); - if (tmp && strcasecmp(tmp, "yes") == 0) { - antispam_can_append_to_spam = TRUE; - debug("allowing APPEND to spam folders"); - } - if (spam_folders) { iter = spam_folders; while (*iter) { debug("\"%s\" is spam folder\n", *iter); iter++; + spam_folder_count++; } } else debug("no spam folders\n"); @@ -155,6 +172,28 @@ void PLUGIN_FUNCTION(init)(void) } else debug("no unsure folders\n"); + tmp = get_setting("ALLOW_APPEND_TO_SPAM"); + if (tmp && strcasecmp(tmp, "yes") == 0) { + antispam_can_append_to_spam = TRUE; + debug("allowing APPEND to spam folders"); + } + + tmp = get_setting("SPAM_KEYWORDS"); + if (tmp) + spam_keywords = p_strsplit(global_pool, tmp, ";"); + + if (spam_keywords) { + iter = spam_keywords; + while (*iter) { + debug("\"%s\" is spam keyword\n", *iter); + iter++; + } + } + + /* set spam_folders to empty to only allow keywords */ + need_folder_hook = !!spam_folder_count; + need_keyword_hook = !!spam_keywords; + backend_init(global_pool); antispam_next_hook_mail_storage_created = hook_mail_storage_created; diff --git a/antispam-plugin.h b/antispam-plugin.h index 98fed8f..9869a14 100644 --- a/antispam-plugin.h +++ b/antispam-plugin.h @@ -51,5 +51,9 @@ 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; +bool keyword_is_spam(const char *keyword); + +extern bool need_keyword_hook; +extern bool need_folder_hook; #endif /* _ANTISPAM_PLUGIN_H */ diff --git a/antispam-storage-1.0.c b/antispam-storage-1.0.c index e1b00fa..1949454 100644 --- a/antispam-storage-1.0.c +++ b/antispam-storage-1.0.c @@ -12,6 +12,7 @@ #include "array.h" #include "istream.h" #include "mail-search.h" +#include "mail-index.h" #include "mail-storage-private.h" #include "antispam-plugin.h" @@ -53,6 +54,10 @@ struct antispam_mailbox { unsigned int save_hack:1; }; +struct antispam_mail { + struct mail_vfuncs super; +}; + static unsigned int antispam_storage_module_id = 0; static bool antispam_storage_module_id_set = FALSE; @@ -257,6 +262,98 @@ antispam_transaction_commit(struct mailbox_transaction_context *ctx, return ret; } +static int +antispam_mail_update_keywords(struct mail *mail, + enum modify_type modify_type, + struct mail_keywords *keywords) +{ + struct mail_private *pmail = (struct mail_private *)mail; + struct antispam_mail *amail = ANTISPAM_CONTEXT(pmail); + int ret; + unsigned int i, numkwds; + const array_t *idxkwd = mail_index_get_keywords(keywords->index); + const char *const *keyword_names = array_get(idxkwd, &numkwds); + const char *const *orig_keywords; + bool previous_spam_keyword, now_spam_keyword; + + switch (modify_type) { + case MODIFY_ADD: + debug("adding keyword(s)\n"); + break; + case MODIFY_REMOVE: + debug("removing keyword(s)\n"); + break; + case MODIFY_REPLACE: + debug("replacing keyword(s)\n"); + break; + default: + i_assert(0); + } + + orig_keywords = pmail->v.get_keywords(mail); + if (orig_keywords) { + debug("original keyword list:\n"); + while (*orig_keywords) { + debug(" * %s\n", *orig_keywords); + if (keyword_is_spam(*orig_keywords)) + previous_spam_keyword = TRUE; + orig_keywords++; + } + } + + debug("keyword list:\n"); + + for (i = 0; i < keywords->count; i++) { + unsigned int idx = keywords->idx[i]; + + i_assert(idx < numkwds); + + debug(" * %s\n", keyword_names[idx]); + + switch (modify_type) { + case MODIFY_ADD: + case MODIFY_REPLACE: + if (keyword_is_spam(keyword_names[idx])) + now_spam_keyword = TRUE; + break; + case MODIFY_REMOVE: + if (keyword_is_spam(keyword_names[idx])) + now_spam_keyword = FALSE; + break; + default: + i_assert(0); + } + } + + ret = amail->super.update_keywords(mail, modify_type, keywords); + + debug("ret = %d\n", ret); + + debug("previous-spam, now-spam: %d, %d\n", + previous_spam_keyword, now_spam_keyword); + + if (previous_spam_keyword != now_spam_keyword) { + /* + * Call backend here. + * + * TODO: It is not clear how to roll back the + * keyword change if the backend fails. + */ + } + + return ret; +} + +static void +antispam_mail_free(struct mail *mail) +{ + struct mail_private *pmail = (struct mail_private *)mail; + struct antispam_mail *amail = ANTISPAM_CONTEXT(pmail); + + amail->super.free(mail); + i_free(amail); +} + static struct mailbox_transaction_context * antispam_mailbox_transaction_begin(struct mailbox *box, enum mailbox_transaction_flags flags) @@ -296,6 +393,33 @@ antispam_mailbox_transaction_rollback(struct mailbox_transaction_context *ctx) asbox->super.transaction_rollback(ctx); } +static struct mail * +antispam_mailbox_mail_alloc(struct mailbox_transaction_context *ctx, + enum mail_fetch_field wanted_fields, + struct mailbox_header_lookup_ctx *wanted_headers) +{ + struct antispam_mailbox *asbox = ANTISPAM_CONTEXT(ctx->box); + struct antispam_mail *amail = i_new(struct antispam_mail, 1); + struct mail_private *pmail; + + /* XXX: is this cast the right thing to do? */ + pmail = (struct mail_private *) asbox->super.mail_alloc( + ctx, + wanted_fields, + wanted_headers); + + amail->super = pmail->v; + + array_idx_set(&pmail->module_contexts, + antispam_storage_module_id, + &amail); + + pmail->v.update_keywords = antispam_mail_update_keywords; + pmail->v.free = antispam_mail_free; + + return (struct mail *)pmail; +} + static struct mailbox *antispam_mailbox_open(struct mail_storage *storage, const char *name, struct istream *input, @@ -314,13 +438,19 @@ static struct mailbox *antispam_mailbox_open(struct mail_storage *storage, asbox->save_hack = FALSE; asbox->movetype = MMT_APPEND; - /* override save_init to override want_mail, we need that */ - box->v.save_init = antispam_save_init; - 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; + if (need_folder_hook) { + /* override save_init to override want_mail, we need that */ + box->v.save_init = antispam_save_init; + 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; + } + + if (need_keyword_hook) + box->v.mail_alloc = antispam_mailbox_mail_alloc; + array_idx_set(&box->module_contexts, antispam_storage_module_id, &asbox); return box; |