diff options
-rw-r--r-- | antispam-plugin.c | 221 | ||||
-rw-r--r-- | antispam.7 | 31 | ||||
-rw-r--r-- | mailtrain.c | 11 | ||||
-rw-r--r-- | spool2dir.c | 11 | ||||
-rwxr-xr-x | version.sh | 2 |
5 files changed, 206 insertions, 70 deletions
diff --git a/antispam-plugin.c b/antispam-plugin.c index cebbd97..cf14d19 100644 --- a/antispam-plugin.c +++ b/antispam-plugin.c @@ -26,6 +26,7 @@ */ #include <stdlib.h> +#include <ctype.h> /* dovecot headers we need */ #include "lib.h" @@ -44,29 +45,146 @@ extern void (*hook_mail_storage_created)(struct mail_storage *storage); PLUGIN_ID; 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 char **unsure_folders = NULL; + +enum match_type { + MT_REG, + MT_PATTERN, + MT_PATTERN_IGNCASE, + + /* keep last */ + NUM_MT +}; + +/* + * There are three different matches for each folder type + * + * type usage + * ---------------------------- + * MT_REG plain strcmp() + * MT_PATTERN case sensitive match with possible wildcards + * MT_PATTERN_IGNCASE case insensitive match with possible wildcards + */ +static char **trash_folders[] = { NULL, NULL, NULL }; +static char **spam_folders[] = { default_spam_folders,NULL, NULL }; +static char **unsure_folders[] = { NULL, NULL, NULL }; + bool antispam_can_append_to_spam = FALSE; static char **spam_keywords = NULL; bool need_keyword_hook; bool need_folder_hook; +/* lower-case string, but keep modified UTF7 unchanged */ +void lowercase_string(const char *in, char *out) +{ + char ch; + + while ((ch = *out++ = i_tolower(*in++))) { + /* lower case */ + if (ch == '&') { + /* modified UTF7 -- find end of sequence ('-') */ + while ((ch = *out++ = *in++)) { + if (ch == '-') + break; + } + } + } +} -static bool mailbox_in_list(struct mailbox *box, char **list) +static bool mailbox_patternmatch(struct mailbox *box, + struct mail_storage *storage, + const char *name, + bool lowercase) { - if (!list) + const char *boxname; + char *lowerboxname; + int len; + int rc; + + if (storage && mailbox_get_storage(box) != storage) return FALSE; - while (*list) { - if (mailbox_equals(box, box->storage, *list)) - return TRUE; - list++; + t_push(); + + boxname = mailbox_get_name(box); + if (lowercase) { + lowerboxname = t_buffer_get(strlen(boxname) + 1); + lowercase_string(boxname, lowerboxname); + boxname = lowerboxname; + } + + len = strlen(name); + if (len && name[len - 1] == '*') { + /* any wildcard */ + --len; + } else { + /* compare EOS too */ + ++len; + } + + rc = memcmp(name, boxname, len) == 0; + + t_pop(); + + return rc; +} + +static bool mailbox_patternmatch_case(struct mailbox *box, + struct mail_storage *storage, + const char *name) +{ + return mailbox_patternmatch(box, storage, name, FALSE); +} + +static bool mailbox_patternmatch_icase(struct mailbox *box, + struct mail_storage *storage, + const char *name) +{ + return mailbox_patternmatch(box, storage, name, TRUE); +} + +typedef bool (*match_fn_t)(struct mailbox *, struct mail_storage *, + const char *); + +/* match information */ +static const struct { + const char *human, *suffix; + match_fn_t fn; +} match_info[NUM_MT] = { + [MT_REG] = { .human = "exact match", + .suffix = "", + .fn = mailbox_equals, }, + [MT_PATTERN] = { .human = "wildcard match", + .suffix = "_PATTERN", + .fn = mailbox_patternmatch_case, }, + [MT_PATTERN_IGNCASE] = { .human = "case-insensitive wildcard match", + .suffix = "_PATTERN_IGNORECASE", + .fn = mailbox_patternmatch_icase, }, +}; + +static bool mailbox_in_list(struct mailbox *box, char ***patterns) +{ + enum match_type i; + char **list; + + if (!patterns) + return FALSE; + + for (i = 0; i < NUM_MT; i++) { + list = patterns[i]; + if (!list) + continue; + + while (*list) { + if (match_info[i].fn(box, box->storage, *list)) + return TRUE; + list++; + } } return FALSE; @@ -130,55 +248,62 @@ const char *get_setting(const char *name) return env; } -void PLUGIN_FUNCTION(init)(void) +int parse_folder_setting(const char *setting, char ***strings, + const char *display_name) { const char *tmp; - char * const *iter; - int spam_folder_count = 0; - - debug("plugin initialising (%s)\n", ANTISPAM_VERSION); + int cnt = 0; + enum match_type i; - global_pool = pool_alloconly_create("antispam-pool", 1024); + t_push(); - tmp = get_setting("TRASH"); - if (tmp) - trash_folders = p_strsplit(global_pool, tmp, ";"); + for (i = 0; i < NUM_MT; ++i) { + tmp = get_setting(t_strconcat(setting, match_info[i].suffix, + NULL)); + if (tmp) { + strings[i] = p_strsplit(global_pool, tmp, ";"); + if (i == MT_PATTERN_IGNCASE) { + /* lower case the string */ + char **list = strings[i]; + while (*list) { + lowercase_string(*list, *list); + ++list; + } + } + } - if (trash_folders) { - iter = trash_folders; - while (*iter) { - debug("\"%s\" is trash folder\n", *iter); - iter++; + if (strings[i]) { + char **iter = strings[i]; + while (*iter) { + ++cnt; + debug("\"%s\" is %s %s folder\n", *iter, + match_info[i].human, display_name); + iter++; + } } - } else - debug("no trash folders\n"); + } - tmp = get_setting("SPAM"); - if (tmp) - spam_folders = p_strsplit(global_pool, tmp, ";"); + t_pop(); - 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"); + if (!cnt) + debug("no %s folders\n", display_name); - tmp = get_setting("UNSURE"); - if (tmp) - unsure_folders = p_strsplit(global_pool, tmp, ";"); + return cnt; +} - if (unsure_folders) { - iter = unsure_folders; - while (*iter) { - debug("\"%s\" is unsure folder\n", *iter); - iter++; - } - } else - debug("no unsure folders\n"); +void PLUGIN_FUNCTION(init)(void) +{ + const char *tmp; + char * const *iter; + int spam_folder_count; + + debug("plugin initialising (%s)\n", ANTISPAM_VERSION); + + global_pool = pool_alloconly_create("antispam-pool", 1024); + + parse_folder_setting("TRASH", trash_folders, "trash"); + spam_folder_count = parse_folder_setting("SPAM", spam_folders, "spam"); + parse_folder_setting("UNSURE", unsure_folders, "unsure"); tmp = get_setting("ALLOW_APPEND_TO_SPAM"); if (tmp && strcasecmp(tmp, "yes") == 0) { @@ -121,15 +121,44 @@ plugin { # antispam_signature_missing = move # move silently without training antispam_signature_missing = error + # The list of folders for trash, spam and unsure can be given + # with three options, e.g. "trash" matches the given folders + # exactly as written, "trash_pattern" accept the * wildcard at + # the end of the foldername, "trash_pattern_ignorecase" + # accepts the * wildcard at the end of the foldername _and_ + # matches the name case insensitivly. + + # the *-wildcard with the following meaning: + # * at the end: any folder that _start_ with the string + # e.g.: + # antispam_trash_pattern = deleted *;Gel&APY-schte * + # match any folders that start with "deleted " or "Gelöschte " + # match is _case_senstive_! + # + # antispam_trash_pattern_ignorecase = deleted *;Gel&APY-schte * + # match any folders that start with "deleted " or "gelöschte " + # match is _case_insenstive_, except the non-USASCII letters, + # "ö" in this example. + # To match the upper-case Ö, too, you need to add yet another + # pattern "gel&ANY-schte *", note the different UTF7 encoding: + # &ANY- instead of &APY-. + + # semicolon-separated list of Trash folders (default unset i.e. none) # antispam_trash = - # antispam_trash = trash;Trash;Deleted Items + # antispam_trash = trash;Trash;Deleted Items; Deleted Messages + # antispam_trash_pattern = trash;Trash;Deleted * + # antispam_trash_pattern_ignorecase = trash;Deleted * # semicolon-separated list of spam folders antispam_spam = SPAM + # antispam_spam_pattern = SPAM + # antispam_spam_pattern_ignorecase = SPAM # semicolon-separated list of unsure folders (default unset i.e. none) # antispam_unsure = + # antispam_unsure_pattern = + # antispam_unsure_pattern_ignorecase = # Whether to allow APPENDing to SPAM folders or not. Must be set to # "yes" (case insensitive) to be activated. Before activating, please diff --git a/mailtrain.c b/mailtrain.c index fe2868d..41071bc 100644 --- a/mailtrain.c +++ b/mailtrain.c @@ -285,17 +285,8 @@ int backend_handle_mail(struct mailbox_transaction_context *t, } /* "From "? skip line */ - if (memcmp("From ", beginning, 5) == 0) { + if (memcmp("From ", beginning, 5) == 0) i_stream_read_next_line(mailstream); - } else { - if (o_stream_send(outstream, beginning, 5) != 5) { - ret = -1; - mail_storage_set_error(t->box->storage, - ME(NOTPOSSIBLE) - "Failed to write line to temp"); - goto failed_to_copy; - } - } if (o_stream_send_istream(outstream, mailstream) < 0) { ret = -1; diff --git a/spool2dir.c b/spool2dir.c index 06bd307..aa53e71 100644 --- a/spool2dir.c +++ b/spool2dir.c @@ -206,17 +206,8 @@ int backend_handle_mail(struct mailbox_transaction_context *t, } /* "From "? skip line */ - if (memcmp("From ", beginning, 5) == 0) { + if (memcmp("From ", beginning, 5) == 0) i_stream_read_next_line(mailstream); - } else { - if (o_stream_send(outstream, beginning, 5) != 5) { - ret = -1; - mail_storage_set_error(t->box->storage, - ME(NOTPOSSIBLE) - "Failed to write line to temp"); - goto failed_to_copy; - } - } if (o_stream_send_istream(outstream, mailstream) < 0) { ret = -1; @@ -1,6 +1,6 @@ #!/bin/sh -VERSION=1.1 +VERSION=1.2 if head=$(git rev-parse --verify HEAD 2>/dev/null); then git update-index --refresh --unmerged > /dev/null |