Commit b6720bb8 authored by TAKAHASHI Tamotsu's avatar TAKAHASHI Tamotsu

Introduce $include_onlyfirst.

parent d274ec03
......@@ -501,9 +501,9 @@ int mutt_select_sort (int reverse)
int method = Sort; /* save the current method in case of abort */
switch (mutt_multi_choice (reverse ?
_("Rev-Sort (d)ate/(f)rm/(r)ecv/(s)ubj/t(o)/(t)hread/(u)nsort/si(z)e/s(c)ore?: ") :
_("Sort (d)ate/(f)rm/(r)ecv/(s)ubj/t(o)/(t)hread/(u)nsort/si(z)e/s(c)ore?: "),
_("dfrsotuzc")))
_("Rev-Sort (d)ate/(f)rm/(r)ecv/(s)ubj/t(o)/(t)hread/(u)nsort/si(z)e/s(c)ore/s(p)am?: ") :
_("Sort (d)ate/(f)rm/(r)ecv/(s)ubj/t(o)/(t)hread/(u)nsort/si(z)e/s(c)ore/s(p)am?: "),
_("dfrsotuzcp")))
{
case -1: /* abort - don't resort */
return -1;
......@@ -543,6 +543,10 @@ int mutt_select_sort (int reverse)
case 9: /* s(c)ore */
Sort = SORT_SCORE;
break;
case 10: /* s(p)am */
Sort = SORT_SPAM;
break;
}
if (reverse)
Sort |= SORT_REVERSE;
......
......@@ -586,6 +586,8 @@ _mutt_copy_message (FILE *fpout, FILE *fpin, HEADER *hdr, BODY *body,
s.flags |= M_WEED;
if (flags & M_CM_CHARCONV)
s.flags |= M_CHARCONV;
if (flags & M_CM_REPLYING)
s.flags |= M_REPLYING;
if (WithCrypto && flags & M_CM_VERIFY)
s.flags |= M_VERIFY;
......
......@@ -25,6 +25,7 @@
#define M_CM_WEED (1<<5) /* weed message/rfc822 attachment headers */
#define M_CM_CHARCONV (1<<6) /* perform character set conversions */
#define M_CM_PRINTING (1<<7) /* printing the message - display light */
#define M_CM_REPLYING (1<<8) /* replying the message */
#define M_CM_DECODE_PGP (1<<8) /* used for decoding PGP messages */
......
......@@ -1499,6 +1499,97 @@ specify the same pattern specified in the <tt/score/ command for it to be
removed. The pattern ``*'' is a special token which means to clear the list
of all score entries.
<sect1>Spam detection<label id="spam">
<p>
Usage: <tt/spam/ <em/pattern/ <em/format/
Usage: <tt/nospam/ <em/pattern/
Mutt has generalized support for external spam-scoring filters.
By defining your spam patterns with the <tt/spam/ and <tt/nospam/
commands, you can <em/limit/, <em/search/, and <em/sort/ your
mail based on its spam attributes, as determined by the external
filter. You also can display the spam attributes in your index
display using the <tt/%H/ selector in the <ref id="index_format"
name="&dollar;index&lowbar;format"> variable. (Tip: try <tt/%?H?[%H] ?/
to display spam tags only when they are defined for a given message.)
Your first step is to define your external filter's spam patterns using
the <tt/spam/ command. <em/pattern/ should be a regular expression
that matches a header in a mail message. If any message in the mailbox
matches this regular expression, it will receive a ``spam tag'' or
``spam attribute'' (unless it also matches a <tt/nospam/ pattern -- see
below.) The appearance of this attribute is entirely up to you, and is
governed by the <em/format/ parameter. <em/format/ can be any static
text, but it also can include back-references from the <em/pattern/
expression. (A regular expression ``back-reference'' refers to a
sub-expression contained within parentheses.) <tt/%1/ is replaced with
the first back-reference in the regex, <tt/%2/ with the second, etc.
If you're using multiple spam filters, a message can have more than
one spam-related header. You can define <tt/spam/ patterns for each
filter you use. If a message matches two or more of these patterns, and
the &dollar;spam&lowbar;separator variable is set to a string, then the
message's spam tag will consist of all the <em/format/ strings joined
together, with the value of &dollar;spam&lowbar;separator separating
them.
For example, suppose I use DCC, SpamAssassin, and PureMessage. I might
define these spam settings:
<tscreen><verb>
spam "X-DCC-.*-Metrics:.*(....)=many" "90+/DCC-%1"
spam "X-Spam-Status: Yes" "90+/SA"
spam "X-PerlMX-Spam: .*Probability=([0-9]+)%" "%1/PM"
set spam_separator=", "
</verb></tscreen>
If I then received a message that DCC registered with ``many'' hits
under the ``Fuz2'' checksum, and that PureMessage registered with a
97% probability of being spam, that message's spam tag would read
<tt>90+/DCC-Fuz2, 97/PM</tt>. (The four characters before ``=many'' in a
DCC report indicate the checksum used -- in this case, ``Fuz2''.)
If the &dollar;spam&lowbar;separator variable is unset, then each
spam pattern match supercedes the previous one. Instead of getting
joined <em/format/ strings, you'll get only the last one to match.
The spam tag is what will be displayed in the index when you use
<tt/%H/ in the <tt/&dollar;index&lowbar;format/ variable. It's also the
string that the <tt/~H/ pattern-matching expression matches against for
<em/search/ and <em/limit/ functions. And it's what sorting by spam
attribute will use as a sort key.
That's a pretty complicated example, and most people's actual
environments will have only one spam filter. The simpler your
configuration, the more effective mutt can be, especially when it comes
to sorting.
Generally, when you sort by spam tag, mutt will sort <em/lexically/ --
that is, by ordering strings alphnumerically. However, if a spam tag
begins with a number, mutt will sort numerically first, and lexically
only when two numbers are equal in value. (This is like UNIX's
<tt/sort -n/.) A message with no spam attributes at all -- that is, one
that didn't match <em/any/ of your <tt/spam/ patterns -- is sorted at
lowest priority. Numbers are sorted next, beginning with 0 and ranging
upward. Finally, non-numeric strings are sorted, with ``a'' taking lower
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.
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:
<tscreen><verb>
spam "^From: .*MAILER-DAEMON" "999"
</verb></tscreen>
<sect1>Setting variables<label id="set">
<p>
Usage: <tt/set/ &lsqb;no|inv&rsqb;<em/variable/&lsqb;=<em/value/&rsqb; &lsqb; <em/variable/ ... &rsqb;<newline>
......@@ -1766,6 +1857,7 @@ messages:
~f USER messages originating from USER
~g cryptographically signed messages
~G cryptographically encrypted messages
~H EXPR messages with a spam attribute matching EXPR
~h EXPR messages which contain EXPR in the message header
~k message contains PGP key material
~i ID message which match ID in the ``Message-ID'' field
......@@ -2399,7 +2491,7 @@ account-hook imap://host2/ 'set tunnel="ssh host2 /usr/libexec/imapd"'
<sect1>Start a WWW Browser on URLs (EXTERNAL)<label id="urlview">
<p>
If a message contains URLs (<em/unified ressource locator/ = address in the
If a message contains URLs (<em/unified resource locator/ = address in the
WWW space like <em>http://www.mutt.org/</em>), it is efficient to get
a menu with all the URLs and start a WWW browser on one of them. This
functionality is provided by the external urlview program which can be
......@@ -3063,6 +3155,10 @@ The following are the commands understood by mutt.
<item>
<tt><ref id="source" name="source"></tt> <em/filename/
<item>
<tt><ref id="spam" name="spam"></tt> <em/pattern/ <em/format/
<item>
<tt><ref id="spam" name="nospam"></tt> <em/pattern/
<item>
<tt><ref id="lists" name="subscribe"></tt> <em/regexp/ &lsqb; <em/regexp/ ... &rsqb;
<item>
<tt><ref id="lists" name="unsubscribe"></tt> <em/regexp/ &lsqb; <em/regexp/ ... &rsqb;
......
......@@ -338,6 +338,15 @@ variables will reset to their system defaults.
\fBsource\fP \fIfilename\fP
The given file will be evaluated as a configuration file.
.TP
.nf
\fBspam\fP \fIpattern\fP \fIformat\fP
\fBnospam\fP \fIpattern\fP
.fi
These commands define spam-detection patterns from external spam
filters, so that mutt can sort, limit, and search on
``spam tags'' or ``spam attributes'', or display them
in the index. See the Mutt manual for details.
.TP
\fBunhook\fP [\fB * \fP | \fIhook-type\fP ]
This command will remove all hooks of a given type, or all hooks
when \(lq\fB*\fP\(rq is used as an argument. \fIhook-type\fP
......@@ -386,6 +395,7 @@ l l.
~f \fIEXPR\fP messages originating from \fIEXPR\fP
~g PGP signed messages
~G PGP encrypted messages
~H \fIEXPR\fP messages with spam tags matching \fIEXPR\fP
~h \fIEXPR\fP messages which contain \fIEXPR\fP in the message header
~k message contains PGP key material
~i \fIEXPR\fP message which match \fIEXPR\fP in the \(lqMessage-ID\(rq field
......
......@@ -103,6 +103,7 @@ WHERE char *Shell;
WHERE char *Signature;
WHERE char *SimpleSearch;
WHERE char *Spoolfile;
WHERE char *SpamSep;
#if defined(USE_SSL) || defined(USE_NSS)
WHERE char *SslCertFile INITVAL (NULL);
WHERE char *SslEntropyFile INITVAL (NULL);
......@@ -126,6 +127,8 @@ WHERE LIST *UnIgnore INITVAL(0);
WHERE RX_LIST *Alternates INITVAL(0);
WHERE RX_LIST *MailLists INITVAL(0);
WHERE RX_LIST *SubscribedLists INITVAL(0);
WHERE SPAM_LIST *SpamList INITVAL(0);
WHERE RX_LIST *NoSpamList INITVAL(0);
/* bit vector for boolean variables */
#ifdef MAIN_C
......
......@@ -1485,6 +1485,9 @@ void multipart_handler (BODY *a, STATE *s)
}
mutt_body_handler (p, s);
state_putc ('\n', s);
if ((s->flags & M_REPLYING)
&& (option (OPTINCLUDEONLYFIRST)) && (s->flags & M_FIRSTDONE))
break;
}
if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE ||
......@@ -1918,6 +1921,7 @@ void mutt_body_handler (BODY *b, STATE *s)
s->fpin = fp;
}
}
s->flags |= M_FIRSTDONE;
}
else if (s->flags & M_DISPLAY)
{
......@@ -1935,5 +1939,5 @@ void mutt_body_handler (BODY *b, STATE *s)
}
bail:
s->flags = oflags;
s->flags = oflags | (s->flags & M_FIRSTDONE);
}
......@@ -433,6 +433,18 @@ hdr_format_str (char *dest,
optional = 0;
break;
case 'H':
/* (Hormel) spam score */
if (optional)
optional = hdr->env->spam ? 1 : 0;
if (hdr->env->spam)
mutt_format_s (dest, destlen, prefix, NONULL (hdr->env->spam->data));
else
mutt_format_s (dest, destlen, prefix, "");
break;
case 'i':
mutt_format_s (dest, destlen, prefix, hdr->env->message_id ? hdr->env->message_id : "<no.id>");
break;
......
......@@ -366,6 +366,73 @@ 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;
REGEXP *rx;
int n;
const char *p;
if (!pat || !*pat || !templ)
return 0;
if (!(rx = mutt_compile_regexp (pat, REG_ICASE)))
{
snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
return -1;
}
/* check to make sure the item is not already on this list */
for (last = *list; last; last = last->next)
{
if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0)
{
/* already on the list, so just ignore it */
last = NULL;
break;
}
if (!last->next)
break;
}
if (!*list || last)
{
t = mutt_new_spam_list();
t->rx = rx;
t->template = safe_strdup(templ);
/* find highest match number in template string */
t->nmatch = 0;
for (p = templ; *p;)
{
if (*p == '%')
{
n = atoi(++p);
if (n > t->nmatch)
t->nmatch = n;
while (*p && isdigit(*p))
++p;
}
else
++p;
}
t->nmatch++; /* match 0 is always the whole expr */
if (last)
{
last->next = t;
last = last->next;
}
else
*list = last = t;
}
else /* duplicate */
mutt_free_regexp (&rx);
return 0;
}
static void remove_from_list (LIST **l, const char *str)
{
LIST *p, *last = NULL;
......@@ -504,6 +571,35 @@ static int parse_rx_unlist (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *
return 0;
}
static int parse_spam_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
{
BUFFER templ;
memset(&templ, 0, sizeof(templ));
if (!MoreArgs(s))
{
strfcpy(err->data, _("spam: no matching pattern"), err->dsize);
return -1;
}
mutt_extract_token (buf, s, 0);
if (MoreArgs(s))
{
mutt_extract_token (&templ, s, 0);
}
else
{
templ.data = NULL;
templ.dsize = 0;
}
if (add_to_spam_list ((SPAM_LIST **) data, buf->data, templ.data, err) != 0)
return -1;
return 0;
}
static int parse_unlist (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
{
do
......
......@@ -884,6 +884,12 @@ struct option_t MuttVars[] = {
** Controls whether or not a copy of the message(s) you are replying to
** is included in your reply.
*/
{ "include_onlyfirst", DT_BOOL, R_NONE, OPTINCLUDEONLYFIRST, 0},
/*
** .pp
** Controls whether or not Mutt includes only the first attachment
** of the message you are replying.
*/
{ "indent_string", DT_STR, R_NONE, UL &Prefix, UL "> " },
/*
** .pp
......@@ -919,6 +925,7 @@ struct option_t MuttVars[] = {
** .dt %E .dd number of messages in current thread
** .dt %f .dd entire From: line (address + real name)
** .dt %F .dd author name, or recipient name if the message is from you
** .dt %H .dd spam attribute(s) of this message
** .dt %i .dd message-id of the current message
** .dt %l .dd number of lines in the message (does not work with maildir,
** mh, and possibly IMAP folders)
......@@ -2354,6 +2361,7 @@ struct option_t MuttVars[] = {
** . mailbox-order (unsorted)
** . score
** . size
** . spam
** . subject
** . threads
** . to
......@@ -2419,6 +2427,15 @@ 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 },
/*
** .pp
** ``$spam_separator'' controls what happens when multiple spam headers
** are matched: if unset, each successive header will overwrite any
** previous matches value for the spam label. If set, each successive
** match will append to the previous, using ``$spam_separator'' as a
** separator.
*/
{ "spoolfile", DT_PATH, R_NONE, UL &Spoolfile, 0 },
/*
** .pp
......@@ -2718,6 +2735,7 @@ const struct mapping_t SortMethods[] = {
{ "threads", SORT_THREADS },
{ "to", SORT_TO },
{ "score", SORT_SCORE },
{ "spam", SORT_SPAM },
{ NULL, 0 }
};
......@@ -2736,6 +2754,7 @@ const struct mapping_t SortAuxMethods[] = {
*/
{ "to", SORT_TO },
{ "score", SORT_SCORE },
{ "spam", SORT_SPAM },
{ NULL, 0 }
};
......@@ -2768,6 +2787,7 @@ const struct mapping_t SortKeyMethods[] = {
static int parse_list (BUFFER *, BUFFER *, unsigned long, BUFFER *);
static int parse_rx_list (BUFFER *, BUFFER *, unsigned long, BUFFER *);
static int parse_spam_list (BUFFER *, BUFFER *, unsigned long, BUFFER *);
static int parse_unlist (BUFFER *, BUFFER *, unsigned long, BUFFER *);
static int parse_rx_unlist (BUFFER *, BUFFER *, unsigned long, BUFFER *);
......@@ -2833,6 +2853,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 },
{ "subscribe", parse_subscribe, 0 },
{ "toggle", parse_set, M_SET_INV },
{ "unalias", parse_unalias, 0 },
......
......@@ -223,6 +223,7 @@ enum
M_ID,
M_BODY,
M_HEADER,
M_HORMEL,
M_WHOLE_MSG,
M_SENDER,
M_MESSAGE,
......@@ -373,6 +374,7 @@ enum
OPTSSLSYSTEMCERTS,
#endif
OPTIMPLICITAUTOVIEW,
OPTINCLUDEONLYFIRST,
OPTKEEPFLAGGED,
OPTMAILCAPSANITIZE,
OPTMAILDIRTRASH,
......@@ -408,6 +410,7 @@ enum
OPTSIGDASHES,
OPTSIGONTOP,
OPTSORTRE,
OPTSPAMSEP,
OPTSTATUSONTOP,
OPTSTRICTTHREADS,
OPTSUSPEND,
......@@ -518,10 +521,20 @@ typedef struct rx_list_t
struct rx_list_t *next;
} RX_LIST;
typedef struct spam_list_t
{
REGEXP *rx;
int nmatch;
char *template;
struct spam_list_t *next;
} SPAM_LIST;
#define mutt_new_list() safe_calloc (1, sizeof (LIST))
#define mutt_new_rx_list() safe_calloc (1, sizeof (RX_LIST))
#define mutt_new_spam_list() safe_calloc (1, sizeof (SPAM_LIST))
void mutt_free_list (LIST **);
void mutt_free_rx_list (RX_LIST **);
void mutt_free_spam_list (SPAM_LIST **);
int mutt_matches_ignore (const char *, LIST *);
/* add an element to a list */
......@@ -556,6 +569,7 @@ typedef struct envelope
char *supersedes;
char *date;
char *x_label;
BUFFER *spam;
LIST *references; /* message references (in reverse order) */
LIST *in_reply_to; /* in-reply-to header content */
LIST *userhdrs; /* user defined headers */
......@@ -845,6 +859,8 @@ typedef struct
#define M_WEED (1<<3) /* weed headers even when not in display mode */
#define M_CHARCONV (1<<4) /* Do character set conversions */
#define M_PRINTING (1<<5) /* are we printing? - M_DISPLAY "light" */
#define M_REPLYING (1<<6) /* are we replying? */
#define M_FIRSTDONE (1<<7) /* the first attachment has been done */
#define state_set_prefix(s) ((s)->flags |= M_PENDINGPREFIX)
#define state_reset_prefix(s) ((s)->flags &= ~M_PENDINGPREFIX)
......
......@@ -1292,6 +1292,60 @@ void mutt_sleep (short s)
sleep (s);
}
/*
* Creates and initializes a BUFFER*. If passed an existing BUFFER*,
* just initializes. Frees anything already in the buffer.
*
* Disregards the 'destroy' flag, which seems reserved for caller.
* This is bad, but there's no apparent protocol for it.
*/
BUFFER * mutt_buffer_init(BUFFER *b)
{
if (!b)
{
b = safe_malloc(sizeof(BUFFER));
if (!b)
return NULL;
}
else
{
safe_free(b->data);
}
memset(b, 0, sizeof(BUFFER));
return b;
}
/*
* Creates and initializes a BUFFER*. If passed an existing BUFFER*,
* just initializes. Frees anything already in the buffer. Copies in
* the seed string.
*
* Disregards the 'destroy' flag, which seems reserved for caller.
* This is bad, but there's no apparent protocol for it.
*/
BUFFER * mutt_buffer_from(BUFFER *b, char *seed)
{
int n;
if (!seed)
return NULL;
b = mutt_buffer_init(b);
b->data = safe_strdup (seed);
b->dsize = mutt_strlen (seed);
b->dptr = (char *) b->data + b->dsize;
return b;
}
void mutt_buffer_free(BUFFER **b)
{
if (!b)
return;
if ((*b)->data)
safe_free(&((*b)->data));
safe_free(b);
}
void mutt_buffer_addstr (BUFFER* buf, const char* s)
{
mutt_buffer_add (buf, s, mutt_strlen (s));
......@@ -1388,6 +1442,21 @@ void mutt_free_rx_list (RX_LIST **list)
}
}
void mutt_free_spam_list (SPAM_LIST **list)
{
SPAM_LIST *p;
if (!list) return;
while (*list)
{
p = *list;
*list = (*list)->next;
mutt_free_regexp (&p->rx);
safe_free(&p->template);
FREE (&p);
}
}
int mutt_match_rx_list (const char *s, RX_LIST *l)
{
if (!s) return 0;
......@@ -1403,3 +1472,54 @@ int mutt_match_rx_list (const char *s, RX_LIST *l)
return 0;
}
int mutt_match_spam_list (const char *s, SPAM_LIST *l, char *text, int x)
{
static regmatch_t *pmatch = NULL;
static int nmatch = 0;
int i, n, tlen;
char *p;
if (!s) return 0;
tlen = 0;
for (; l; l = l->next)
{
/* If this pattern needs more matches, expand pmatch. */
if (l->nmatch > nmatch)
{
safe_realloc ((void**) &pmatch, l->nmatch * sizeof(regmatch_t));
nmatch = l->nmatch;
}
/* Does this pattern match? */
if (regexec (l->rx->rx, s, (size_t) l->nmatch, (regmatch_t *) pmatch, (int) 0) == 0)
{
dprint (5, (debugfile, "mutt_match_spam_list: %s matches %s\n", s, l->rx->pattern));
dprint (5, (debugfile, "mutt_match_spam_list: %d subs\n", l->rx->rx->re_nsub));
/* Copy template into text, with substitutions. */
for (p = l->template; *p;)
{
if (*p == '%')
{
n = atoi(++p); /* find pmatch index */
while (isdigit(*p))
++p; /* skip subst token */
for (i = pmatch[n].rm_so; (i < pmatch[n].rm_eo) && (tlen < x); i++)
text[tlen++] = s[i];
}
else
{
text[tlen++] = *p++;
}
}
text[tlen] = '\0';
dprint (5, (debugfile, "mutt_match_spam_list: \"%s\"\n", text));
return 1;
}
}
return 0;
}
......@@ -1269,6 +1269,7 @@ ENVELOPE *mutt_read_rfc822_header (FILE *f, HEADER *hdr, short user_hdrs,
long loc;
int matched;
size_t linelen = LONG_STRING;
char buf[LONG_STRING+1];
if (hdr)
{
......@@ -1312,6 +1313,49 @@ ENVELOPE *mutt_read_rfc822_header (FILE *f, HEADER *hdr, short user_hdrs,
break; /* end of header */
}
*buf = '\0';
if (mutt_match_spam_list(line, SpamList, buf, sizeof(buf)))
{
if (!mutt_match_rx_list(line, NoSpamList))
{
/* if spam tag already exists, figure out how to amend it */
if (e->spam && *buf)
{
/* If SpamSep defined, append with separator */
if (SpamSep)
{
mutt_buffer_addstr(e->spam, SpamSep);
mutt_buffer_addstr(e->spam, buf);
}
/* else overwrite */
else
{
e->spam->dptr = e->spam->data;
*e->spam->dptr = '\0';
mutt_buffer_addstr(e->spam, buf);
}
}
/* spam tag is new, and match expr is non-empty; copy */
else if (!e->spam && *buf)
{
e->spam = mutt_buffer_from(NULL, buf);
}
/* match expr is empty; plug in null string if no existing tag */
else if (!e->spam)
{
e->spam = mutt_buffer_from(NULL, "");
}
if (e->spam && e->spam->data)
dprint(5, (debugfile, "p822: spam = %s\n", e->spam->data));
}
}
*p = 0;
p++;
SKIPWS (p);
......
......@@ -58,6 +58,7 @@ Flags[] =
{ 'g', M_CRYPT_SIGN, 0, NULL },
{ 'G', M_CRYPT_ENCRYPT, 0, NULL },
{ 'h', M_HEADER, M_FULL_MSG, eat_regexp },
{ 'H', M_HORMEL, 0, eat_regexp },
{ 'i', M_ID, 0, eat_regexp },
{ 'k', M_PGP_KEY, 0, NULL },
{ 'L', M_ADDRESS, 0, eat_regexp },
......@@ -1046,6 +1047,8 @@ mutt_pattern_exec (struct pattern_t *pat, pattern_exec_flag flags, CONTEXT *ctx,
return (pat->not ^ ((h->security & APPLICATION_PGP) && (h->security & PGPKEY)));
case M_XLABEL:
return (pat->not ^ (h->env->x_label && regexec (pat->rx, h->env->x_label, 0, NULL, 0) == 0));
case M_HORMEL:
return (pat->not ^ (h->env->spam && h->env->spam->data && regexec (pat->rx, h->env->spam->data, 0, NULL, 0) == 0));
case M_DUPLICATED:
return (pat->not ^ (h->thread && h->thread->duplicate_thread));
case M_UNREFERENCED:
......
......@@ -32,6 +32,9 @@ void _mutt_make_string (char *, size_t, const char *, CONTEXT *,
HEADER *, format_flag);
int mutt_extract_token (BUFFER *, BUFFER *, int);
BUFFER * mutt_buffer_init (BUFFER *);
BUFFER * mutt_buffer_from (BUFFER *, char *);
void mutt_buffer_free(BUFFER **);
void mutt_buffer_add (BUFFER*, const char*, size_t);
void mutt_buffer_addstr (BUFFER*, const char*);
void mutt_buffer_addch (BUFFER*, char);
......@@ -291,6 +294,7 @@ int mutt_is_text_part (BODY *);
int mutt_is_valid_mailbox (const char *);
int mutt_lookup_mime_type (BODY *, const char *);
int mutt_match_rx_list (const char *, RX_LIST *);
int mutt_match_spam_list (const char *, SPAM_LIST *, char *, int);
int mutt_messages_in_thread (CONTEXT *, HEADER *, int);
int mutt_multi_choice (char *prompt, char *letters);
int mutt_needs_mailcap (BODY *);
......
......@@ -403,7 +403,7 @@ void mutt_make_post_indent (CONTEXT *ctx, HEADER *cur, FILE *out)
static int include_reply (CONTEXT *ctx, HEADER *cur, FILE *out)
{
int cmflags = M_CM_PREFIX | M_CM_DECODE | M_CM_CHARCONV;
int cmflags = M_CM_PREFIX | M_CM_DECODE | M_CM_CHARCONV | M_CM_REPLYING;
int chflags = CH_DECODE;
if (WithCrypto && (cur->security & ENCRYPT))
......
......@@ -149,6 +149,57 @@ int compare_order (const void *a, const void *b)
return (SORTCODE ((*ha)->index - (*hb)->index));
}
int compare_spam (const void *a, const void *b)
{
HEADER **ppa = (HEADER **) a;
HEADER **ppb = (HEADER **) b;
char *aptr, *bptr;
int ahas, bhas;
int result = 0;