Commit 551a3491 authored by David Champion's avatar David Champion

v3 of the generic spam detection patch.

parent 7d707c6f
......@@ -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"
......
......@@ -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
......
......@@ -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 },
......
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment