]> granicus.if.org Git - neomutt/commitdiff
Introduce $include_onlyfirst.
authorTAKAHASHI Tamotsu <ttakah@lapis.plala.or.jp>
Wed, 14 Jul 2004 04:16:58 +0000 (04:16 +0000)
committerTAKAHASHI Tamotsu <ttakah@lapis.plala.or.jp>
Wed, 14 Jul 2004 04:16:58 +0000 (04:16 +0000)
18 files changed:
commands.c
copy.c
copy.h
doc/manual.sgml.head
doc/muttrc.man.head
globals.h
handler.c
hdrline.c
init.c
init.h
mutt.h
muttlib.c
parse.c
pattern.c
protos.h
send.c
sort.c
sort.h

index 38abc5ea7013acfca33a634ef3c591b4960f6c91..95a5334f043eedcc9413ef66de1ade83861203b1 100644 (file)
@@ -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;
diff --git a/copy.c b/copy.c
index ff889cde333ad8ee2c398c8846c2e11ef2792149..9e77ba2f7269e661a15500d90fa440691ba37165 100644 (file)
--- a/copy.c
+++ b/copy.c
@@ -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;
diff --git a/copy.h b/copy.h
index c03d5f85c6fa5f8f4bf8658a8fc66084f14a86bc..67568380f04a767e8d39b1a1888e3791971a6f5d 100644 (file)
--- a/copy.h
+++ b/copy.h
@@ -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 */
index 0ced0e5088c79e2d61e2a5904f64c1c25064f4e1..3dfb89a31b6be51225c0f7d18bd4f3d9f3916a82 100644 (file)
@@ -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; 
index b52b81b824e7cfc2ad3a7eb312761e4cfca109a4..03a9d880c999fec0622004404be3bce59615447c 100644 (file)
@@ -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
index 8841c854f74c60622d062fed1f0d4839404bcacb..4f3adbb517d53a7aba3fcc3191b42e3149f5e2a9 100644 (file)
--- a/globals.h
+++ b/globals.h
@@ -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
index 850405951b6c5f5b2c69eb11b0be67d7b8260da4..d425c222b7733cb729ec594ecdd09b77736e16a7 100644 (file)
--- a/handler.c
+++ b/handler.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);
 }
index 931bd42848515d41ec7c848b627e6c5665c39796..e2d9f98381d30724de8ab4037f918387121df7f0 100644 (file)
--- a/hdrline.c
+++ b/hdrline.c
@@ -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;
diff --git a/init.c b/init.c
index 764c23edd36e75d3343c79c3e59147b18f24eea3..4196aa0bf55fff84ce9756dc65051d12bb4a4381 100644 (file)
--- a/init.c
+++ b/init.c
@@ -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
diff --git a/init.h b/init.h
index 8a67a1532355fb0cabece471ef2bdc7aea099c49..301e7c50263dfb14f42483f7df9c2121c9967e0f 100644 (file)
--- a/init.h
+++ b/init.h
@@ -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 },
diff --git a/mutt.h b/mutt.h
index 45597ece2b28b4385a9c8b8f19ca000ce45cf73c..85afda8fd10cb63208cfcc504f2d9c643822a087 100644 (file)
--- a/mutt.h
+++ b/mutt.h
@@ -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)
index e02b44dc0760a821f8a776f90b8dd5b0048db21b..896e9982c86561db2960f5bd95aa79a5918318ef 100644 (file)
--- a/muttlib.c
+++ b/muttlib.c
@@ -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;
+}
diff --git a/parse.c b/parse.c
index 471c55e560932be153bc053f48e03c0bbe8d05a4..53ecbfda1ad7bb9fa362b6cc348232b9e013758c 100644 (file)
--- a/parse.c
+++ b/parse.c
@@ -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);
index 70130af05f29317687f4754ebea328b7f7e64cc6..6ee3e67d5edb37a53f2b55179c1311398effaf39 100644 (file)
--- a/pattern.c
+++ b/pattern.c
@@ -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:
index 5aab3d53c8953d9193002692ac8058a04e4ccb5a..a481e4f4b38f085695523034f8754c53ad2f79c7 100644 (file)
--- a/protos.h
+++ b/protos.h
@@ -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 *);
diff --git a/send.c b/send.c
index cc171e76fae33d65f654828d96ebd55e5404f183..1d791934e84ba077b290d6648c6f320663f9bb97 100644 (file)
--- a/send.c
+++ b/send.c
@@ -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))
diff --git a/sort.c b/sort.c
index cec5b3c7cafb6edda525e7df249dea71fa87852d..17e49f9947e1a427b27e7e9d8afdfb84645b5c03 100644 (file)
--- a/sort.c
+++ b/sort.c
@@ -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;
+
+  /* Firstly, require spam attributes for both msgs */
+  /* to compare. Determine which msgs have one.     */
+  ahas = (*ppa)->env && (*ppa)->env->spam;
+  bhas = (*ppb)->env && (*ppb)->env->spam;
+
+  /* If one msg has spam attr but other does not, sort the one with first. */
+  if (ahas && !bhas)
+    return (SORTCODE(1));
+  if (!ahas && bhas)
+    return (SORTCODE(-1));
+
+  /* Else, if neither has a spam attr, presume equality. Fall back on aux. */
+  if (!ahas && !bhas)
+  {
+    AUXSORT(result, a, b);
+    return (SORTCODE(result));
+  }
+
+
+  /* Both have spam attrs. */
+
+  /* preliminary numeric examination */
+  result = (strtoul((*ppa)->env->spam->data, &aptr, 10) -
+            strtoul((*ppb)->env->spam->data, &bptr, 10));
+
+  /* If either aptr or bptr is equal to data, there is no numeric    */
+  /* value for that spam attribute. In this case, compare lexically. */
+  if ((aptr == (*ppa)->env->spam->data) || (bptr == (*ppb)->env->spam->data))
+    return (SORTCODE(strcmp(aptr, bptr)));
+
+  /* Otherwise, we have numeric value for both attrs. If these values */
+  /* are equal, then we first fall back upon string comparison, then  */
+  /* upon auxiliary sort.                                             */
+  if (result == 0)
+  {
+    result = strcmp(aptr, bptr);
+    if (result == 0)
+      AUXSORT(result, a, b);
+  }
+
+  return (SORTCODE(result));
+}
+
 sort_t *mutt_get_sort_func (int method)
 {
   switch (method & SORT_MASK)
@@ -169,6 +220,8 @@ sort_t *mutt_get_sort_func (int method)
       return (compare_to);
     case SORT_SCORE:
       return (compare_score);
+    case SORT_SPAM:
+      return (compare_spam);
     default:
       return (NULL);
   }
diff --git a/sort.h b/sort.h
index 87af0592865f12e3cf13025ecfafa453a069bc57..3986c33e06f3767f06813c47c897248502494612 100644 (file)
--- a/sort.h
+++ b/sort.h
 #define SORT_ADDRESS   11
 #define SORT_KEYID     12
 #define SORT_TRUST     13
-#define SORT_MASK      0xf
-#define SORT_REVERSE   (1<<4)
-#define SORT_LAST      (1<<5)
+#define SORT_SPAM      14
+/* dgc: Sort & SortAux are shorts, so I'm bumping these bitflags up from
+ * bits 4 & 5 to bits 8 & 9 to make room for more sort keys in the future. */
+#define SORT_MASK      0xff
+#define SORT_REVERSE   (1<<8)
+#define SORT_LAST      (1<<9)
 
 typedef int sort_t (const void *, const void *);
 sort_t *mutt_get_sort_func (int);