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"
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;
{
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;
}
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
** 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
{ "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 },