1 diff -ru a/dovecot-antispam-2.0+20120225/antispam-plugin.c b/dovecot-antispam-2.0+20120225/antispam-plugin.c
2 --- a/dovecot-antispam-2.0+20120225/antispam-plugin.c 2012-02-24 20:22:48.000000000 +0100
3 +++ b/dovecot-antispam-2.0+20120225/antispam-plugin.c 2013-07-29 07:14:51.962245023 +0200
8 +bool keyword_is_ham(const struct antispam_config *cfg, const char *keyword)
10 + char **k = cfg->ham_keywords;
12 + if (!cfg->ham_keywords)
16 + if (strcmp(*k, keyword) == 0)
24 static int parse_folder_setting(const struct antispam_config *cfg,
25 const char *setting, char ***strings,
30 + tmp = getenv("HAM_KEYWORDS", getenv_data);
32 + cfg->ham_keywords = p_strsplit(cfg->mem_pool, tmp, ";");
34 + if (cfg->ham_keywords) {
35 + iter = cfg->ham_keywords;
37 + debug(&cfg->dbgcfg, "\"%s\" is ham keyword\n", *iter);
42 tmp = getenv("BACKEND", getenv_data);
44 diff -ru a/dovecot-antispam-2.0+20120225/antispam-plugin.h b/dovecot-antispam-2.0+20120225/antispam-plugin.h
45 --- a/dovecot-antispam-2.0+20120225/antispam-plugin.h 2013-07-31 12:45:33.000000000 +0200
46 +++ b/dovecot-antispam-2.0+20120225/antispam-plugin.h 2013-07-30 05:22:21.248730740 +0200
48 struct antispam_transaction_context;
56 char **unsure_folders[3]; // = { NULL, NULL, NULL };
59 + char **ham_keywords;
61 const char *signature_hdr;
64 bool mailbox_is_trash(const struct antispam_config *cfg, struct mailbox *box);
65 bool mailbox_is_unsure(const struct antispam_config *cfg, struct mailbox *box);
66 bool keyword_is_spam(const struct antispam_config *cfg, const char *keyword);
67 +bool keyword_is_ham(const struct antispam_config *cfg, const char *keyword);
69 struct antispam_config *
70 antispam_setup_config(const char *(getenv)(const char *env, void *data),
71 diff -ru a/dovecot-antispam-2.0+20120225/antispam-storage-2.0.c b/dovecot-antispam-2.0+20120225/antispam-storage-2.0.c
72 --- a/dovecot-antispam-2.0+20120225/antispam-storage-2.0.c 2012-02-24 20:22:48.000000000 +0100
73 +++ b/dovecot-antispam-2.0+20120225/antispam-storage-2.0.c 2013-07-31 14:52:43.277922438 +0200
75 const ARRAY_TYPE(keywords) *idxkwd = mail_index_get_keywords(keywords->index);
76 const char *const *keyword_names = array_get(idxkwd, &numkwds);
77 const char *const *orig_keywords;
78 - bool previous_spam_keyword, now_spam_keyword;
80 - switch (modify_type) {
82 - debug(&amail->cfg->dbgcfg, "adding keyword(s)\n");
85 - debug(&amail->cfg->dbgcfg, "removing keyword(s)\n");
87 - case MODIFY_REPLACE:
88 - debug(&amail->cfg->dbgcfg, "replacing keyword(s)\n");
93 + bool previous_spam_keyword = FALSE, previous_ham_keyword = FALSE;
94 + enum classification spam_class = CLASS_UNSURE;
95 + struct antispam_transaction_context *ast;
97 orig_keywords = pmail->v.get_keywords(mail);
100 debug(&amail->cfg->dbgcfg, " * %s\n", *orig_keywords);
101 if (keyword_is_spam(amail->cfg, *orig_keywords))
102 previous_spam_keyword = TRUE;
103 + if (keyword_is_ham(amail->cfg, *orig_keywords))
104 + previous_ham_keyword = TRUE;
108 @@ -350,29 +340,47 @@
111 if (keyword_is_spam(amail->cfg, keyword_names[idx]))
112 - now_spam_keyword = TRUE;
113 + spam_class = CLASS_SPAM;
114 + if (keyword_is_ham(amail->cfg, keyword_names[idx]))
115 + spam_class = CLASS_NOTSPAM;
118 if (keyword_is_spam(amail->cfg, keyword_names[idx]))
119 - now_spam_keyword = FALSE;
120 + spam_class = CLASS_NOTSPAM;
121 + if (keyword_is_ham(amail->cfg, keyword_names[idx]))
122 + spam_class = CLASS_SPAM;
130 + debug(&amail->cfg->dbgcfg, "spam_class=%d previous_spam_keyword=%d previous_ham_keyword=%d\n",
131 + spam_class, previous_spam_keyword, previous_ham_keyword);
133 amail->module_ctx.super.update_keywords(mail, modify_type, keywords);
135 - debug(&amail->cfg->dbgcfg, "previous-spam, now-spam: %d, %d\n",
136 - previous_spam_keyword, now_spam_keyword);
138 - if (previous_spam_keyword != now_spam_keyword) {
140 - * Call backend here.
142 - * TODO: It is not clear how to roll back the
143 - * keyword change if the backend fails.
145 + if (previous_spam_keyword && previous_ham_keyword) {
146 + /* NOTE: avoid to call the backend two times for the same change
147 + * once when adding one spam or ham keyword
148 + * and once when removing the other.
150 + debug(&amail->cfg->dbgcfg, "does not run backend a second time\n");
154 + if (spam_class != CLASS_UNSURE) {
155 + /* NOTE: on error, backend uses mail_storage_set_error()
156 + * which is checked in commit() where a rollback() can be done.
158 + debug(&amail->cfg->dbgcfg, "call backend->start()\n");
159 + ast = amail->cfg->backend->start(amail->cfg, mail->transaction->box);
160 + i_assert(ast != NULL);
161 + debug(&amail->cfg->dbgcfg, "call backend->handle_mail()\n");
162 + if (!amail->cfg->backend->handle_mail(amail->cfg, mail->transaction, ast, mail, spam_class)) {
163 + debug(&amail->cfg->dbgcfg, "call backend->commit()\n");
164 + amail->cfg->backend->commit(amail->cfg, mail->transaction, ast);
170 struct antispam_mailbox *asbox = ANTISPAM_CONTEXT(ctx->box);
171 struct antispam_internal_context *ast = ANTISPAM_CONTEXT(ctx);
173 - if (antispam_transaction_commit(asbox->cfg, ctx, &ast->backendctx) < 0) {
174 + if ((ctx->box->storage->error != MAIL_ERROR_NONE)
175 + || antispam_transaction_commit(asbox->cfg, ctx, &ast->backendctx) < 0) {
176 + debug(&asbox->cfg->dbgcfg, "transaction rollback\n");
178 mail_free(&ast->mail);
180 diff -ru a/dovecot-antispam-2.0+20120225/crm114-exec.c b/dovecot-antispam-2.0+20120225/crm114-exec.c
181 --- a/dovecot-antispam-2.0+20120225/crm114-exec.c 2012-02-24 20:22:48.000000000 +0100
182 +++ b/dovecot-antispam-2.0+20120225/crm114-exec.c 2013-07-30 06:00:16.266596502 +0200
185 class_arg = "--spam";
192 diff -ru a/dovecot-antispam-2.0+20120225/dspam-exec.c b/dovecot-antispam-2.0+20120225/dspam-exec.c
193 --- a/dovecot-antispam-2.0+20120225/dspam-exec.c 2012-02-24 20:22:48.000000000 +0100
194 +++ b/dovecot-antispam-2.0+20120225/dspam-exec.c 2013-07-31 13:28:39.217464532 +0200
197 class_arg = t_strconcat("--class=", "spam", NULL);
204 diff -ru a/dovecot-antispam-2.0+20120225/pipe.c b/dovecot-antispam-2.0+20120225/pipe.c
205 --- a/dovecot-antispam-2.0+20120225/pipe.c 2012-02-24 20:22:48.000000000 +0100
206 +++ b/dovecot-antispam-2.0+20120225/pipe.c 2013-07-31 14:16:12.164255967 +0200
208 dest = cfg->pipe.ham_args;
209 dest_num = cfg->pipe.spam_args_num;