]> granicus.if.org Git - mutt/commitdiff
v3 of the generic spam detection patch.
authorDavid Champion <dgc@bikeshed.us>
Thu, 15 Jul 2004 08:08:32 +0000 (08:08 +0000)
committerDavid Champion <dgc@bikeshed.us>
Thu, 15 Jul 2004 08:08:32 +0000 (08:08 +0000)
doc/manual.sgml.head
init.c
init.h
mutt.h

index 3dfb89a31b6be51225c0f7d18bd4f3d9f3916a82..6698dcb983927b91bbe4e12418534ec29a06e3df 100644 (file)
@@ -1575,15 +1575,24 @@ priority than ``z''. Clearly, in general, sorting by spam tags is most
 effective when you can coerce your filter to give you a raw number. But
 in case you can't, mutt can still do something useful.
 
-Finally, the <tt/nospam/ command can be used to write exceptions to
-<tt/spam/ patterns. If a header pattern matches something in a <tt/spam/
-command, but you nonetheless do not want it to receive a spam tag,
-you can list a more precise pattern under a <tt/nospam/ command.
+The <tt/nospam/ command can be used to write exceptions to <tt/spam/
+patterns. If a header pattern matches something in a <tt/spam/ command,
+but you nonetheless do not want it to receive a spam tag, you can list a
+more precise pattern under a <tt/nospam/ command.
+
+If the <em/pattern/ given to <tt/nospam/ is exactly the same as the
+<em/pattern/ on an existing <tt/spam/ list entry, the effect will be to
+remove the entry from the spam list, instead of adding an exception.
+Likewise, if the <em/pattern/ for a <tt/spam/ command matches an entry
+on the <tt/nospam/ list, that <tt/nospam/ entry will be removed. If the
+<em/pattern/ for <tt/nospam/ is ``*'', <em/all entries on both lists/
+will be removed. This might be the default action if you use <tt/spam/
+and <tt/nospam/ in conjunction with a <tt/folder-hook/.
 
 You can have as many <tt/spam/ or <tt/nospam/ commands as you like.
 You can even do your own primitive spam detection within mutt -- for
-example, if you consider all mail from MAILER-DAEMON to be spam, you can
-use a <tt/spam/ command like this:
+example, if you consider all mail from <tt/MAILER-DAEMON/ to be spam,
+you can use a <tt/spam/ command like this:
 
 <tscreen><verb>
 spam "^From: .*MAILER-DAEMON"       "999"
diff --git a/init.c b/init.c
index 4196aa0bf55fff84ce9756dc65051d12bb4a4381..c07df0e1a48599bd0a09cbc2b8694714b1fc20d7 100644 (file)
--- a/init.c
+++ b/init.c
@@ -368,7 +368,7 @@ static int add_to_rx_list (RX_LIST **list, const char *s, int flags, BUFFER *err
 
 static int add_to_spam_list (SPAM_LIST **list, const char *pat, const char *templ, BUFFER *err)
 {
-  SPAM_LIST *t, *last = NULL;
+  SPAM_LIST *t = NULL, *last = NULL;
   REGEXP *rx;
   int n;
   const char *p;
@@ -387,49 +387,89 @@ static int add_to_spam_list (SPAM_LIST **list, const char *pat, const char *temp
   {
     if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0)
     {
-      /* already on the list, so just ignore it */
-      last = NULL;
+      /* Already on the list. Formerly we just skipped this case, but
+       * now we're supporting removals, which means we're supporting
+       * re-adds conceptually. So we probably want this to imply a
+       * removal, then do an add. We can achieve the removal by freeing
+       * the template, and leaving t pointed at the current item.
+       */
+      t = last;
+      safe_free(&t->template);
       break;
     }
     if (!last->next)
       break;
   }
 
-  if (!*list || last)
+  /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
+   * update. Otherwise we want to make a new one to link at the list's end.
+   */
+  if (!t)
   {
     t = mutt_new_spam_list();
     t->rx = rx;
-    t->template = safe_strdup(templ);
+    if (last)
+      last->next = t;
+    else
+      *list = t;
+  }
 
-    /* find highest match number in template string */
-    t->nmatch = 0;
-    for (p = templ; *p;)
+  /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
+  t->template = safe_strdup(templ);
+
+  /* Find highest match number in template string */
+  t->nmatch = 0;
+  for (p = templ; *p;)
+  {
+    if (*p == '%')
     {
-      if (*p == '%')
-      {
-       n = atoi(++p);
-       if (n > t->nmatch)
-         t->nmatch = n;
-       while (*p && isdigit(*p))
-         ++p;
-      }
-      else
-       ++p;
+        n = atoi(++p);
+        if (n > t->nmatch)
+          t->nmatch = n;
+        while (*p && isdigit((int)*p))
+          ++p;
     }
-    t->nmatch++;               /* match 0 is always the whole expr */
+    else
+        ++p;
+  }
+  t->nmatch++;         /* match 0 is always the whole expr */
 
-    if (last)
+  return 0;
+}
+
+static int remove_from_spam_list (SPAM_LIST **list, const char *pat)
+{
+  SPAM_LIST *spam, *prev;
+  int nremoved = 0;
+
+  /* Being first is a special case. */
+  spam = *list;
+  if (spam->rx && !mutt_strcmp(spam->rx->pattern, pat))
+  {
+    *list = spam->next;
+    mutt_free_regexp(&spam->rx);
+    safe_free(&spam->template);
+    safe_free(&spam);
+    return 1;
+  }
+
+  prev = spam;
+  for (spam = prev->next; spam;)
+  {
+    if (!mutt_strcmp(spam->rx->pattern, pat))
     {
-      last->next = t;
-      last = last->next;
+      prev->next = spam->next;
+      mutt_free_regexp(&spam->rx);
+      safe_free(&spam->template);
+      safe_free(&spam);
+      spam = prev->next;
+      ++nremoved;
     }
     else
-      *list = last = t;
+      spam = spam->next;
   }
-  else /* duplicate */
-    mutt_free_regexp (&rx);
 
-  return 0;
+  return nremoved;
 }
 
 
@@ -577,29 +617,71 @@ static int parse_spam_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *
 
   memset(&templ, 0, sizeof(templ));
 
+  /* Insist on at least one parameter */
   if (!MoreArgs(s))
   {
-    strfcpy(err->data, _("spam: no matching pattern"), err->dsize);
+    if (data == M_SPAM)
+      strfcpy(err->data, _("spam: no matching pattern"), err->dsize);
+    else
+      strfcpy(err->data, _("nospam: no matching pattern"), err->dsize);
     return -1;
   }
+
+  /* Extract the first token, a regexp */
   mutt_extract_token (buf, s, 0);
 
-  if (MoreArgs(s))
+  /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
+  if (data == M_SPAM)
   {
-    mutt_extract_token (&templ, s, 0);
+    /* If there's a second parameter, it's a template for the spam tag. */
+    if (MoreArgs(s))
+    {
+      mutt_extract_token (&templ, s, 0);
+
+      /* Add to the spam list. */
+      if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0)
+          return -1;
+    }
+
+    /* If not, try to remove from the nospam list. */
+    else
+    {
+      remove_from_rx_list(&NoSpamList, buf->data);
+    }
+
+    return 0;
   }
-  else
+
+  /* M_NOSPAM is for nospam commands. */
+  else if (data == M_NOSPAM)
   {
-    templ.data = NULL;
-    templ.dsize = 0;
-  }
+    /* nospam only ever has one parameter. */
+
+    /* "*" is a special case. */
+    if (!mutt_strcmp(buf->data, "*"))
+    {
+      mutt_free_spam_list (&SpamList);
+      mutt_free_rx_list (&NoSpamList);
+      return 0;
+    }
+
+    /* If it's on the spam list, just remove it. */
+    if (remove_from_spam_list(&SpamList, buf->data) != 0)
+      return 0;
 
-  if (add_to_spam_list ((SPAM_LIST **) data, buf->data, templ.data, err) != 0)
+    /* Otherwise, add it to the nospam list. */
+    if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
       return -1;
-  
-  return 0;
+
+    return 0;
+  }
+
+  /* This should not happen. */
+  strfcpy(err->data, "This is no good at all.", err->dsize);
+  return -1;
 }
 
+
 static int parse_unlist (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
 {
   do
diff --git a/init.h b/init.h
index 1a1633f9218625cf844498340d93a4990e55b479..59e1bf91465495c165948de4b596f546f26adfa1 100644 (file)
--- a/init.h
+++ b/init.h
@@ -2434,7 +2434,7 @@ struct option_t MuttVars[] = {
   ** the message whether or not this is the case, as long as the
   ** non-``$$reply_regexp'' parts of both messages are identical.
   */
-  { "spam_separator",   DT_STR, R_NONE, UL &SpamSep, UL 0 },
+  { "spam_separator",   DT_STR, R_NONE, UL &SpamSep, UL "," },
   /*
   ** .pp
   ** ``$spam_separator'' controls what happens when multiple spam headers
@@ -2860,8 +2860,8 @@ struct command_t Commands[] = {
   { "send-hook",       mutt_parse_hook,        M_SENDHOOK },
   { "set",             parse_set,              0 },
   { "source",          parse_source,           0 },
-  { "spam",            parse_spam_list,        UL &SpamList },
-  { "nospam",          parse_rx_list,          UL &NoSpamList },
+  { "spam",            parse_spam_list,        M_SPAM },
+  { "nospam",          parse_spam_list,        M_NOSPAM },
   { "subscribe",       parse_subscribe,        0 },
   { "toggle",          parse_set,              M_SET_INV },
   { "unalias",         parse_unalias,          0 },
diff --git a/mutt.h b/mutt.h
index cfd60755ff8ee8872f7ebbf232382edf5f5bcf5e..e26865edb9a09b48f585a69a4107fe4df0db34a5 100644 (file)
--- a/mutt.h
+++ b/mutt.h
@@ -316,6 +316,9 @@ enum
 #define M_SEL_MULTI    (1<<1)
 #define M_SEL_FOLDER   (1<<2)
 
+/* flags for parse_spam_list */
+#define M_SPAM          1
+#define M_NOSPAM        2
 
 /* boolean vars */
 enum