--- /dev/null
+diff -u b/dovecot-antispam-2.0+20120225/antispam-plugin.h c/dovecot-antispam-2.0+20120225/antispam-plugin.h
+--- b/dovecot-antispam-2.0+20120225/antispam-plugin.h 2013-07-30 05:22:21.248730740 +0200
++++ c/dovecot-antispam-2.0+20120225/antispam-plugin.h 2013-08-05 05:50:13.430597556 +0200
+@@ -30,6 +30,8 @@
+ CLASS_UNSURE,
+ CLASS_NOTSPAM,
+ CLASS_SPAM,
++ CLASS_UNSPAM,
++ CLASS_UNNOTSPAM,
+ };
+
+ enum antispam_debug_target {
+@@ -104,6 +106,10 @@
+ int spam_args_num;
+ char **ham_args;
+ int ham_args_num;
++ char **unlearn_spam_args;
++ int unlearn_spam_args_num;
++ char **unlearn_ham_args;
++ int unlearn_ham_args_num;
+ const char *pipe_binary;// = "/usr/sbin/sendmail";
+ const char *tmpdir;// = "/tmp";
+ char **extra_args;
+diff -u b/dovecot-antispam-2.0+20120225/antispam-storage-2.0.c c/dovecot-antispam-2.0+20120225/antispam-storage-2.0.c
+--- b/dovecot-antispam-2.0+20120225/antispam-storage-2.0.c 2013-07-31 14:52:43.277922438 +0200
++++ c/dovecot-antispam-2.0+20120225/antispam-storage-2.0.c 2013-08-05 07:51:45.235555750 +0200
+@@ -327,7 +327,11 @@
+ }
+ }
+
+- debug(&amail->cfg->dbgcfg, "keyword list:\n");
++ debug(&amail->cfg->dbgcfg, "keyword list: modify_type=%s\n"
++ , modify_type == MODIFY_ADD ? "MODIFY_ADD"
++ : modify_type == MODIFY_REPLACE ? "MODIFY_REPLACE"
++ : modify_type == MODIFY_REMOVE ? "MODIFY_REMOVE"
++ : "ERROR");
+
+ for (i = 0; i < keywords->count; i++) {
+ unsigned int idx = keywords->idx[i];
+@@ -346,28 +350,34 @@
+ break;
+ case MODIFY_REMOVE:
+ if (keyword_is_spam(amail->cfg, keyword_names[idx]))
+- spam_class = CLASS_NOTSPAM;
++ spam_class = previous_spam_keyword ? CLASS_UNSPAM : CLASS_UNSURE;
+ if (keyword_is_ham(amail->cfg, keyword_names[idx]))
+- spam_class = CLASS_SPAM;
++ spam_class = previous_ham_keyword ? CLASS_UNNOTSPAM : CLASS_UNSURE;
+ break;
+ default:
+ i_assert(0);
+ }
+ }
+
+- debug(&amail->cfg->dbgcfg, "spam_class=%d previous_spam_keyword=%d previous_ham_keyword=%d\n",
+- spam_class, previous_spam_keyword, previous_ham_keyword);
++ debug(&amail->cfg->dbgcfg, "spam_class=%s previous_spam_keyword=%d previous_ham_keyword=%d\n"
++ , spam_class == CLASS_UNSURE ? "CLASS_UNSURE"
++ : spam_class == CLASS_SPAM ? "CLASS_SPAM"
++ : spam_class == CLASS_NOTSPAM ? "CLASS_NOTSPAM"
++ : spam_class == CLASS_UNSPAM ? "CLASS_UNSPAM"
++ : spam_class == CLASS_UNNOTSPAM ? "CLASS_UNNOTSPAM"
++ : "ERROR"
++ , previous_spam_keyword, previous_ham_keyword);
+
+ amail->module_ctx.super.update_keywords(mail, modify_type, keywords);
+
+- if (previous_spam_keyword && previous_ham_keyword) {
+- /* NOTE: avoid to call the backend two times for the same change
+- * once when adding one spam or ham keyword
+- * and once when removing the other.
+- */
+- debug(&amail->cfg->dbgcfg, "does not run backend a second time\n");
+- return;
+- }
++ //if (previous_spam_keyword && previous_ham_keyword) {
++ // /* NOTE: avoid to call the backend two times for the same change
++ // * once when adding one spam or ham keyword
++ // * and once when removing the other.
++ // */
++ // debug(&amail->cfg->dbgcfg, "does not run backend a second time\n");
++ // return;
++ // }
+
+ if (spam_class != CLASS_UNSURE) {
+ /* NOTE: on error, backend uses mail_storage_set_error()
+Les sous-répertoires b/dovecot-antispam-2.0+20120225/debian et c/dovecot-antispam-2.0+20120225/debian sont identiques
+Les sous-répertoires b/dovecot-antispam-2.0+20120225/.pc et c/dovecot-antispam-2.0+20120225/.pc sont identiques
+diff -u b/dovecot-antispam-2.0+20120225/pipe.c c/dovecot-antispam-2.0+20120225/pipe.c
+--- b/dovecot-antispam-2.0+20120225/pipe.c 2013-07-31 14:16:12.164255967 +0200
++++ c/dovecot-antispam-2.0+20120225/pipe.c 2013-08-05 06:12:36.137800536 +0200
+@@ -46,7 +46,15 @@
+ break;
+ case CLASS_NOTSPAM:
+ dest = cfg->pipe.ham_args;
+- dest_num = cfg->pipe.spam_args_num;
++ dest_num = cfg->pipe.ham_args_num;
++ break;
++ case CLASS_UNSPAM:
++ dest = cfg->pipe.unlearn_spam_args;
++ dest_num = cfg->pipe.unlearn_spam_args_num;
++ break;
++ case CLASS_UNNOTSPAM:
++ dest = cfg->pipe.unlearn_ham_args;
++ dest_num = cfg->pipe.unlearn_ham_args_num;
+ break;
+ default:
+ i_assert(0);
+@@ -55,13 +63,13 @@
+ if (!dest)
+ return -1;
+
++ debug(&cfg->dbgcfg, "running mailtrain backend program %s", cfg->pipe.pipe_binary);
++
+ pid = fork();
+
+ if (pid == -1)
+ return -1;
+
+- debug(&cfg->dbgcfg, "running mailtrain backend program %s", cfg->pipe.pipe_binary);
+-
+ if (pid) {
+ if (waitpid(pid, &status, 0) == -1)
+ return -1;
+@@ -238,7 +246,7 @@
+ return -1;
+ }
+
+- if (!cfg->pipe.ham_args || !cfg->pipe.spam_args) {
++ if (!cfg->pipe.ham_args || !cfg->pipe.spam_args || !cfg->pipe.unlearn_ham_args || !cfg->pipe.unlearn_spam_args) {
+ mail_storage_set_error(t->box->storage,
+ ME(NOTPOSSIBLE)
+ "antispam plugin not configured");
+@@ -371,6 +379,52 @@
+ tmp = NULL;
+ }
+ }
++
++ tmp = getenv("PIPE_PROGRAM_UNLEARN_SPAM_ARGS", getenv_data);
++ if (tmp) {
++ cfg->pipe.unlearn_spam_args = p_strsplit(cfg->mem_pool, tmp, ";");
++ cfg->pipe.unlearn_spam_args_num = str_array_length(
++ (const char *const *)cfg->pipe.unlearn_spam_args);
++ for (i = 0; i < cfg->pipe.unlearn_spam_args_num; i++)
++ debug(&cfg->dbgcfg, "pipe backend unlearn spam arg[%d] = %s\n",
++ i, cfg->pipe.unlearn_spam_args[i]);
++ } else {
++ tmp = getenv("PIPE_PROGRAM_UNLEARN_SPAM_ARG", getenv_data);
++ if (!tmp)
++ tmp = getenv("MAIL_UNLEARN_SPAM", getenv_data);
++ if (tmp) {
++ /* bit of a hack */
++ cfg->pipe.unlearn_spam_args =
++ p_strsplit(cfg->mem_pool, tmp, "\x01");
++ cfg->pipe.unlearn_spam_args_num = 1;
++ debug(&cfg->dbgcfg,
++ "pipe backend unlearn spam argument = %s\n", tmp);
++ tmp = NULL;
++ }
++ }
++
++ tmp = getenv("PIPE_PROGRAM_UNLEARN_NOTSPAM_ARGS", getenv_data);
++ if (tmp) {
++ cfg->pipe.unlearn_ham_args = p_strsplit(cfg->mem_pool, tmp, ";");
++ cfg->pipe.unlearn_ham_args_num = str_array_length(
++ (const char *const *)cfg->pipe.unlearn_ham_args);
++ for (i = 0; i < cfg->pipe.unlearn_ham_args_num; i++)
++ debug(&cfg->dbgcfg, "pipe backend unlearn not-spam arg[%d] = %s\n",
++ i, cfg->pipe.unlearn_ham_args[i]);
++ } else {
++ tmp = getenv("PIPE_PROGRAM_UNLEARN_NOTSPAM_ARG", getenv_data);
++ if (!tmp)
++ tmp = getenv("MAIL_UNLEARN_NOTSPAM", getenv_data);
++ if (tmp) {
++ /* bit of a hack */
++ cfg->pipe.unlearn_ham_args =
++ p_strsplit(cfg->mem_pool, tmp, "\x01");
++ cfg->pipe.unlearn_ham_args_num = 1;
++ debug(&cfg->dbgcfg,
++ "pipe backend unlearn not-spam argument = %s\n", tmp);
++ tmp = NULL;
++ }
++ }
+
+ tmp = getenv("PIPE_PROGRAM", getenv_data);
+ if (!tmp)