summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Champion <dgc@bikeshed.us>2005-10-04 06:05:39 +0000
committerDavid Champion <dgc@bikeshed.us>2005-10-04 06:05:39 +0000
commitf5e7b39153b414d2f9c2f9e369f41f895d253f40 (patch)
tree3a5758e9515a3868385eeb593f89adbcd739186c
parent324752c8dbb1e28777518f1a01e3d4b9baf46aad (diff)
Attachment counting for index display (patch-1.5.11.dgc.attach.6).
Modifications: attach_recurse and attach_ignore_fundamental stripped, some debugging code removed, some bones thrown to check_sec.sh.
-rw-r--r--Muttrc.head.in54
-rw-r--r--doc/manual.xml.head109
-rw-r--r--doc/muttrc.man.head3
-rw-r--r--globals.h4
-rw-r--r--hdrline.c23
-rw-r--r--init.c231
-rw-r--r--init.h8
-rw-r--r--mime.h3
-rw-r--r--mutt.h25
-rw-r--r--parse.c177
-rw-r--r--pattern.c17
-rw-r--r--protos.h1
-rw-r--r--recvattach.c17
13 files changed, 658 insertions, 14 deletions
diff --git a/Muttrc.head.in b/Muttrc.head.in
index 44ec4d2c..93730726 100644
--- a/Muttrc.head.in
+++ b/Muttrc.head.in
@@ -30,6 +30,60 @@ bind browser y exit
# set use_8bitmime
##
+## *** DEFAULT SETTINGS FOR THE ATTACHMENTS PATCH ***
+##
+
+##
+## Please see the manual (section "attachments") for detailed
+## documentation of the "attachments" command.
+##
+## Removing a pattern from a list removes that pattern literally. It
+## does not remove any type matching the pattern.
+##
+## attachments +A */.*
+## attachments +A image/jpeg
+## unattachments +A */.*
+##
+## This leaves "attached" image/jpeg files on the allowed attachments
+## list. It does not remove all items, as you might expect, because the
+## second */.* is not a matching expression at this time.
+##
+## Remember: "unattachments" only undoes what "attachments" has done!
+## It does not trigger any matching on actual messages.
+
+## Qualify any MIME part with an "attachment" disposition, EXCEPT for
+## text/x-vcard and application/pgp parts. (PGP parts are already known
+## to mutt, and can be searched for with ~g, ~G, and ~k.)
+##
+## I've added x-pkcs7 to this, since it functions (for S/MIME)
+## analogously to PGP signature attachments. S/MIME isn't supported
+## in a stock mutt build, but we can still treat it specially here.
+##
+attachments +A */.*
+attachments -A text/x-vcard application/pgp.*
+attachments -A application/x-pkcs7-.*
+
+## Discount all MIME parts with an "inline" disposition, unless they're
+## text/plain. (Why inline a text/plain part unless it's external to the
+## message flow?)
+##
+attachments +I text/plain
+
+## These two lines make Mutt qualify MIME containers. (So, for example,
+## a message/rfc822 forward will count as an attachment.) The first
+## line is unnecessary if you already have "attach-allow */.*", of
+## course. These are off by default! The MIME elements contained
+## within a message/* or multipart/* are still examined, even if the
+## containers themseves don't qualify.
+##
+#attachments +A message/.* multipart/.*
+#attachments +I message/.* multipart/.*
+
+## You probably don't really care to know about deleted attachments.
+attachments -A message/external-body
+attachments -I message/external-body
+
+##
## More settings
##
diff --git a/doc/manual.xml.head b/doc/manual.xml.head
index 10155002..8bf0a101 100644
--- a/doc/manual.xml.head
+++ b/doc/manual.xml.head
@@ -3307,6 +3307,7 @@ messages:
~v message is part of a collapsed thread.
~V cryptographically verified messages
~x EXPR messages which contain EXPR in the `References' field
+~X [MIN]-[MAX] messages with MIN to MAX attachments *)
~y EXPR messages which contain EXPR in the `X-Label' field
~z [MIN]-[MAX] messages with a size in the range MIN to MAX *)
~= duplicated messages (see $duplicate_threads)
@@ -5146,6 +5147,114 @@ To remove a MIME type from the <literal>alternative&lowbar;order</literal> list,
</sect2>
+<sect2 id="attachments">
+<title>Attachment Searching and Counting</title>
+
+<para>
+If you ever lose track of attachments in your mailboxes, Mutt's
+attachment-counting and -searching support might be for you. You can
+make your message index display the number of qualifying attachments in
+each message, or search for messages by attachment count. You also can
+configure what kinds of attachments qualify for this feature with the
+attachments and unattachments commands.
+</para>
+
+<para>
+The syntax is:
+</para>
+<screen>
+attachments {+|-}disposition mime-type
+unattachments {+|-}disposition mime-type
+</screen>
+
+<para>
+Disposition is the attachment's Content-disposition type -- either
+"inline" or "attachment". You can abbreviate this to I or A.
+</para>
+
+<para>
+Disposition is prefixed by either a + symbolor a - symbol. If it's
+a +, you're saying that you want to allow this disposition and MIME
+type to qualify. If it's a -, you're saying that this disposition
+and MIME type is an exception to previous + rules. There are examples
+below of how this is useful.
+</para>
+
+<para>
+Mime-type is, unsurprisingly, the MIME type of the attachment you want
+to affect. A MIME type is always of the format "major/minor", where
+"major" describes the broad category of document you're looking at, and
+"minor" describes the specific type within that category. The major
+part of mim-type must be literal text (or the special token "*"), but
+the minor part may be a regular expression. (Therefore, "*/.*" matches
+any MIME type.)
+</para>
+
+<para>
+The MIME types you give to the attachments directive are a kind of
+pattern. When you use the attachments directive, the patterns you
+specify are added to a list. When you use unattachments, the pattern
+is removed from the list. The patterns are not expanded and matched
+to specific MIME types at this time -- they're just text in a list.
+They're only matched when actually evaluating a message.
+</para>
+
+<para>
+Some examples might help to illustrate. The examples that are not
+commented out define the default configuration of the lists.
+</para>
+
+<screen>
+## Removing a pattern from a list removes that pattern literally. It
+## does not remove any type matching the pattern.
+##
+## attachments +A */.*
+## attachments +A image/jpeg
+## unattachments +A */.*
+##
+## This leaves "attached" image/jpeg files on the allowed attachments
+## list. It does not remove all items, as you might expect, because the
+## second */.* is not a matching expression at this time.
+##
+## Remember: "unattachments" only undoes what "attachments" has done!
+## It does not trigger any matching on actual messages.
+
+
+## Qualify any MIME part with an "attachment" disposition, EXCEPT for
+## text/x-vcard and application/pgp parts. (PGP parts are already known
+## to mutt, and can be searched for with ~g, ~G, and ~k.)
+##
+## I've added x-pkcs7 to this, since it functions (for S/MIME)
+## analogously to PGP signature attachments. S/MIME isn't supported
+## in a stock mutt build, but we can still treat it specially here.
+##
+attachments +A */.*
+attachments -A text/x-vcard application/pgp.*
+attachments -A application/x-pkcs7-.*
+
+## Discount all MIME parts with an "inline" disposition, unless they're
+## text/plain. (Why inline a text/plain part unless it's external to the
+## message flow?)
+##
+attachments +I text/plain
+
+## These two lines make Mutt qualify MIME containers. (So, for example,
+## a message/rfc822 forward will count as an attachment.) The first
+## line is unnecessary if you already have "attach-allow */.*", of
+## course. These are off by default! The MIME elements contained
+## within a message/* or multipart/* are still examined, even if the
+## containers themseves don't qualify.
+##
+#attachments +A message/.* multipart/.*
+#attachments +I message/.* multipart/.*
+
+## You probably don't really care to know about deleted attachments.
+attachments -A message/external-body
+attachments -I message/external-body
+</screen>
+
+</sect2>
+
<sect2 id="mime-lookup">
<title>MIME Lookup</title>
diff --git a/doc/muttrc.man.head b/doc/muttrc.man.head
index dd7ad05e..5a1a5149 100644
--- a/doc/muttrc.man.head
+++ b/doc/muttrc.man.head
@@ -437,6 +437,7 @@ l l.
~v message is part of a collapsed thread.
~V cryptographically verified messages
~x \fIEXPR\fP messages which contain \fIEXPR\fP in the \(lqReferences\(rq field
+~X \fIMIN\fP-\fIMAX\fP messages with MIN - MAX attachments
~y \fIEXPR\fP messages which contain \fIEXPR\fP in the \(lqX-Label\(rq field
~z \fIMIN\fP-\fIMAX\fP messages with a size in the range \fIMIN\fP to \fIMAX\fP
~= duplicated messages (see $duplicate_threads)
@@ -445,7 +446,7 @@ l l.
.PP
In the above, \fIEXPR\fP is a regular expression.
.PP
-With the \fB~m\fP, \fB~n\fP, and \fB~z\fP operators, you can also
+With the \fB~m\fP, \fB~n\fP, \fB~X\fP, and \fB~z\fP operators, you can also
specify ranges in the forms \fB<\fP\fIMAX\fP, \fB>\fP\fIMIN\fP,
\fIMIN\fP\fB-\fP, and \fB-\fP\fIMAX\fP.
.SS Matching dates
diff --git a/globals.h b/globals.h
index 196594d3..ae3f8227 100644
--- a/globals.h
+++ b/globals.h
@@ -140,6 +140,10 @@ WHERE char *LastFolder;
WHERE LIST *AutoViewList INITVAL(0);
WHERE LIST *AlternativeOrderList INITVAL(0);
+WHERE LIST *AttachAllow INITVAL(0);
+WHERE LIST *AttachExclude INITVAL(0);
+WHERE LIST *InlineAllow INITVAL(0);
+WHERE LIST *InlineExclude INITVAL(0);
WHERE LIST *HeaderOrderList INITVAL(0);
WHERE LIST *Ignore INITVAL(0);
WHERE LIST *MimeLookupList INITVAL(0);
diff --git a/hdrline.c b/hdrline.c
index a938c81d..3a40747a 100644
--- a/hdrline.c
+++ b/hdrline.c
@@ -218,6 +218,7 @@ int mutt_user_is_recipient (HEADER *h)
* %T = $to_chars
* %u = user (login) name of author
* %v = first name of author, unless from self
+ * %X = number of MIME attachments
* %y = `x-label:' field (if present)
* %Y = `x-label:' field (if present, tree unfolded, and != parent's x-label)
* %Z = status flags */
@@ -654,6 +655,28 @@ hdr_format_str (char *dest,
mutt_format_s (dest, destlen, prefix, buf2);
break;
+ case 'X':
+ {
+ int count, flags;
+
+ if (hdr->content->parts)
+ count = mutt_count_body_parts(hdr, flags);
+ else
+ {
+ mutt_parse_mime_message(ctx, hdr);
+ count = mutt_count_body_parts(hdr, flags);
+ mutt_free_body(&hdr->content->parts);
+ }
+
+ /* The recursion allows messages without depth to return 0. */
+ if (optional)
+ optional = count != 0;
+
+ snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
+ snprintf (dest, destlen, fmt, count);
+ }
+ break;
+
case 'y':
if (optional)
optional = hdr->env->x_label ? 1 : 0;
diff --git a/init.c b/init.c
index b064ec25..858a6afd 100644
--- a/init.c
+++ b/init.c
@@ -793,6 +793,237 @@ static int parse_lists (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
return 0;
}
+/* always wise to do what someone else did before */
+static void _attachments_clean (void)
+{
+ int i;
+ if (Context && Context->msgcount)
+ {
+ for (i = 0; i < Context->msgcount; i++)
+ Context->hdrs[i]->attach_valid = 0;
+ }
+}
+
+static int parse_attach_list (BUFFER *buf, BUFFER *s, LIST **ldata, BUFFER *err)
+{
+ ATTACH_MATCH *a;
+ LIST *listp, *lastp;
+ char *p;
+ char *tmpminor;
+ int len;
+
+ /* Find the last item in the list that data points to. */
+ lastp = NULL;
+ dprint(5, (debugfile, "parse_attach_list: ldata = %08x, *ldata = %08x\n",
+ (unsigned int)ldata, (unsigned int)*ldata));
+ for (listp = *ldata; listp; listp = listp->next)
+ {
+ a = (ATTACH_MATCH *)listp->data;
+ dprint(5, (debugfile, "parse_attach_list: skipping %s/%s\n",
+ a->major, a->minor));
+ lastp = listp;
+ }
+
+ do
+ {
+ mutt_extract_token (buf, s, 0);
+
+ if (!buf->data || *buf->data == '\0')
+ continue;
+
+ a = safe_malloc(sizeof(ATTACH_MATCH));
+
+ /* some cheap hacks that I expect to remove */
+ if (!mutt_strcasecmp(buf->data, "any"))
+ a->major = safe_strdup("*/.*");
+ else if (!mutt_strcasecmp(buf->data, "none"))
+ a->major = safe_strdup("cheap_hack/this_should_never_match");
+ else
+ a->major = safe_strdup(buf->data);
+
+ if ((p = strchr(a->major, '/')))
+ {
+ *p = '\0';
+ ++p;
+ a->minor = p;
+ }
+ else
+ {
+ a->minor = "unknown";
+ }
+
+ len = strlen(a->minor);
+ tmpminor = safe_malloc(len+3);
+ strcpy(&tmpminor[1], a->minor); /* __STRCPY_CHECKED__ */
+ tmpminor[0] = '^';
+ tmpminor[len+1] = '$';
+ tmpminor[len+2] = '\0';
+
+ a->major_int = mutt_check_mime_type(a->major);
+ regcomp(&a->minor_rx, tmpminor, REG_ICASE|REG_EXTENDED);
+
+ safe_free(&tmpminor);
+
+ dprint(5, (debugfile, "parse_attach_list: added %s/%s [%d]\n",
+ a->major, a->minor, a->major_int));
+
+ listp = safe_malloc(sizeof(LIST));
+ listp->data = (char *)a;
+ listp->next = NULL;
+ if (lastp)
+ {
+ lastp->next = listp;
+ }
+ else
+ {
+ *ldata = listp;
+ }
+ lastp = listp;
+ }
+ while (MoreArgs (s));
+
+ _attachments_clean();
+ return 0;
+}
+
+static int parse_unattach_list (BUFFER *buf, BUFFER *s, LIST **ldata, BUFFER *err)
+{
+ ATTACH_MATCH *a;
+ LIST *lp, *lastp;
+ char *tmp;
+ int major;
+ char *minor;
+
+ do
+ {
+ mutt_extract_token (buf, s, 0);
+
+ if (!mutt_strcasecmp(buf->data, "any"))
+ tmp = safe_strdup("*/.*");
+ else if (!mutt_strcasecmp(buf->data, "none"))
+ tmp = safe_strdup("cheap_hack/this_should_never_match");
+ else
+ tmp = safe_strdup(buf->data);
+
+ if ((minor = strchr(tmp, '/')))
+ {
+ *minor = '\0';
+ ++minor;
+ }
+ else
+ {
+ minor = "unknown";
+ }
+ major = mutt_check_mime_type(tmp);
+
+ lastp = NULL;
+ for(lp = *ldata; lp; lp = lastp->next)
+ {
+ a = (ATTACH_MATCH *)lp->data;
+ dprint(5, (debugfile, "parse_unattach_list: check %s/%s [%d] : %s/%s [%d]\n",
+ a->major, a->minor, a->major_int, tmp, minor, major));
+ if (a->major_int == major && !mutt_strcasecmp(minor, a->minor))
+ {
+ dprint(5, (debugfile, "parse_unattach_list: removed %s/%s [%d]\n",
+ a->major, a->minor, a->major_int));
+ regfree(&a->minor_rx);
+ FREE(&a->major);
+ if (lastp)
+ {
+ lastp->next = lp->next;
+ }
+ lastp = lp;
+ FREE (&lp->data); /* same as a */
+ FREE (&lp);
+ }
+
+ lastp = lp;
+ lp = lp->next;
+ }
+
+ remove_from_list (ldata, buf->data);
+ }
+ while (MoreArgs (s));
+
+ _attachments_clean();
+ return 0;
+}
+
+
+static int parse_attachments (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ char op, *p;
+ LIST **listp;
+
+ mutt_extract_token(buf, s, 0);
+ if (!buf->data || *buf->data == '\0') {
+ strfcpy(err->data, _("attachments: no disposition"), err->dsize);
+ return -1;
+ }
+
+ p = buf->data;
+ op = *p++;
+ if (op != '+' && op != '-') {
+ op = '+';
+ p--;
+ }
+ if (!mutt_strncasecmp(p, "attachment", strlen(p))) {
+ if (op == '+')
+ listp = &AttachAllow;
+ else
+ listp = &AttachExclude;
+ }
+ else if (!mutt_strncasecmp(p, "inline", strlen(p))) {
+ if (op == '+')
+ listp = &InlineAllow;
+ else
+ listp = &InlineExclude;
+ }
+ else {
+ strfcpy(err->data, _("attachments: invalid disposition"), err->dsize);
+ return -1;
+ }
+
+ return parse_attach_list(buf, s, listp, err);
+}
+
+static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
+{
+ char op, *p;
+ LIST **listp;
+
+ mutt_extract_token(buf, s, 0);
+ if (!buf->data || *buf->data == '\0') {
+ strfcpy(err->data, _("unattachments: no disposition"), err->dsize);
+ return -1;
+ }
+
+ p = buf->data;
+ op = *p++;
+ if (op != '+' && op != '-') {
+ op = '+';
+ p--;
+ }
+ if (mutt_strncasecmp(p, "attachment", strlen(p))) {
+ if (op == '+')
+ listp = &AttachAllow;
+ else
+ listp = &AttachExclude;
+ }
+ else if (mutt_strncasecmp(p, "inline", strlen(p))) {
+ if (op == '+')
+ listp = &InlineAllow;
+ else
+ listp = &InlineExclude;
+ }
+ else {
+ strfcpy(err->data, _("unattachments: invalid disposition"), err->dsize);
+ return -1;
+ }
+
+ return parse_unattach_list(buf, s, listp, err);
+}
+
static int parse_unlists (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
{
do
diff --git a/init.h b/init.h
index 6b05b7ce..af82e8e7 100644
--- a/init.h
+++ b/init.h
@@ -220,10 +220,12 @@ struct option_t MuttVars[] = {
** .dt %m .dd major MIME type
** .dt %M .dd MIME subtype
** .dt %n .dd attachment number
+ ** .dt %Q .dd "Q", if MIME part qualifies for attachment counting
** .dt %s .dd size
** .dt %t .dd tagged flag
** .dt %T .dd graphic tree characters
** .dt %u .dd unlink (=to delete) flag
+ ** .dt %X .dd number of qualifying MIME parts in this part and its children
** .dt %>X .dd right justify the rest of the string and pad with character "X"
** .dt %|X .dd pad to the end of the line with character "X"
** .de
@@ -990,6 +992,7 @@ struct option_t MuttVars[] = {
** .dt %T .dd the appropriate character from the $$to_chars string
** .dt %u .dd user (login) name of the author
** .dt %v .dd first name of the author, or the recipient if the message is from you
+ ** .dt %X .dd number of attachments
** .dt %y .dd `x-label:' field, if present
** .dt %Y .dd `x-label' field, if present, and (1) not at part of a thread tree,
** (2) at the top of a thread, or (3) `x-label' is different from
@@ -2982,6 +2985,9 @@ static int parse_my_hdr (BUFFER *, BUFFER *, unsigned long, BUFFER *);
static int parse_unmy_hdr (BUFFER *, BUFFER *, unsigned long, BUFFER *);
static int parse_subscribe (BUFFER *, BUFFER *, unsigned long, BUFFER *);
static int parse_unsubscribe (BUFFER *, BUFFER *, unsigned long, BUFFER *);
+static int parse_attachments (BUFFER *, BUFFER *, unsigned long, BUFFER *);
+static int parse_unattachments (BUFFER *, BUFFER *, unsigned long, BUFFER *);
+
static int parse_alternates (BUFFER *, BUFFER *, unsigned long, BUFFER *);
static int parse_unalternates (BUFFER *, BUFFER *, unsigned long, BUFFER *);
@@ -3001,6 +3007,8 @@ struct command_t Commands[] = {
{ "account-hook", mutt_parse_hook, M_ACCOUNTHOOK },
#endif
{ "alias", parse_alias, 0 },
+ { "attachments", parse_attachments, 0 },
+ { "unattachments",parse_unattachments,0 },
{ "auto_view", parse_list, UL &AutoViewList },
{ "alternative_order", parse_list, UL &AlternativeOrderList},
{ "bind", mutt_parse_bind, 0 },
diff --git a/mime.h b/mime.h
index 4c72c2d0..a374f25a 100644
--- a/mime.h
+++ b/mime.h
@@ -27,7 +27,8 @@ enum
TYPEMODEL,
TYPEMULTIPART,
TYPETEXT,
- TYPEVIDEO
+ TYPEVIDEO,
+ TYPEANY
};
/* Content-Transfer-Encoding */
diff --git a/mutt.h b/mutt.h
index 164e5edf..7b2d3e3e 100644
--- a/mutt.h
+++ b/mutt.h
@@ -238,6 +238,7 @@ enum
M_CRYPT_ENCRYPT,
M_PGP_KEY,
M_XLABEL,
+ M_MIMEATTACH,
/* Options for Mailcap lookup */
M_EDIT,
@@ -646,7 +647,9 @@ typedef struct body
struct header *hdr; /* header information for message/rfc822 */
struct attachptr *aptr; /* Menu information, used in recvattach.c */
-
+
+ signed short attach_count;
+
time_t stamp; /* time stamp of last
* encoding update.
*/
@@ -684,6 +687,7 @@ typedef struct body
unsigned int badsig : 1; /* bad cryptographic signature (needed to check encrypted s/mime-signatures) */
unsigned int collapsed : 1; /* used by recvattach */
+ unsigned int attach_qualifies : 1;
} BODY;
@@ -722,6 +726,9 @@ typedef struct header
unsigned int searched : 1;
unsigned int matched : 1;
+ /* tells whether the attachment count is valid */
+ unsigned int attach_valid : 1;
+
/* the following are used to support collapsing threads */
unsigned int collapsed : 1; /* is this message part of a collapsed thread? */
unsigned int limited : 1; /* is this message in a limited view? */
@@ -746,6 +753,9 @@ typedef struct header
char *tree; /* character string to print thread tree */
struct thread *thread;
+ /* Number of qualifying attachments in message, if attach_valid */
+ short attach_total;
+
#ifdef MIXMASTER
LIST *chain;
#endif
@@ -883,6 +893,19 @@ void state_attach_puts (const char *, STATE *);
void state_prefix_putc (char, STATE *);
int state_printf(STATE *, const char *, ...);
+/* for attachment counter */
+typedef struct
+{
+ char *major;
+ int major_int;
+ char *minor;
+ regex_t minor_rx;
+} ATTACH_MATCH;
+
+/* Flags for mutt_count_body_parts() */
+#define M_PARTS_TOPLEVEL (1<<0) /* is the top-level part */
+#define M_PARTS_RECOUNT (1<<1) /* force recount */
+
#include "ascii.h"
#include "protos.h"
#include "lib.h"
diff --git a/parse.c b/parse.c
index 3d15af3e..697662a1 100644
--- a/parse.c
+++ b/parse.c
@@ -300,6 +300,10 @@ int mutt_check_mime_type (const char *s)
return TYPEVIDEO;
else if (ascii_strcasecmp ("model", s) == 0)
return TYPEMODEL;
+ else if (ascii_strcasecmp ("*", s) == 0)
+ return TYPEANY;
+ else if (ascii_strcasecmp (".*", s) == 0)
+ return TYPEANY;
else
return TYPEOTHER;
}
@@ -924,22 +928,28 @@ static char *extract_message_id (const char *s)
void mutt_parse_mime_message (CONTEXT *ctx, HEADER *cur)
{
MESSAGE *msg;
+ int flags = 0;
- if (cur->content->type != TYPEMESSAGE && cur->content->type != TYPEMULTIPART)
- return; /* nothing to do */
+ do {
+ if (cur->content->type != TYPEMESSAGE &&
+ cur->content->type != TYPEMULTIPART)
+ break; /* nothing to do */
- if (cur->content->parts)
- return; /* The message was parsed earlier. */
+ if (cur->content->parts)
+ break; /* The message was parsed earlier. */
- if ((msg = mx_open_message (ctx, cur->msgno)))
- {
- mutt_parse_part (msg->fp, cur->content);
+ if ((msg = mx_open_message (ctx, cur->msgno)))
+ {
+ mutt_parse_part (msg->fp, cur->content);
- if (WithCrypto)
- cur->security = crypt_query (cur->content);
+ if (WithCrypto)
+ cur->security = crypt_query (cur->content);
- mx_close_message (&msg);
- }
+ mx_close_message (&msg);
+ }
+ } while (0);
+
+ mutt_count_body_parts(cur, flags|M_PARTS_RECOUNT);
}
int mutt_parse_rfc822_line (ENVELOPE *e, HEADER *hdr, char *line, char *p, short user_hdrs, short weed,
@@ -1457,3 +1467,148 @@ ADDRESS *mutt_parse_adrlist (ADDRESS *p, const char *s)
return p;
}
+
+/* Compares mime types to the ok and except lists */
+int count_body_parts_check(LIST **checklist, BODY *b, int dflt)
+{
+ LIST *type;
+ ATTACH_MATCH *a;
+
+ /* If list is null, use default behavior. */
+ if (! *checklist)
+ {
+ /*return dflt;*/
+ return 0;
+ }
+
+ for (type = *checklist; type; type = type->next)
+ {
+ a = (ATTACH_MATCH *)type->data;
+ dprint(5, (debugfile, "cbpc: %s %d/%s ?? %s/%s [%d]... ",
+ dflt ? "[OK] " : "[EXCL] ",
+ b->type, b->subtype, a->major, a->minor, a->major_int));
+ if ((a->major_int == TYPEANY || a->major_int == b->type) &&
+ !regexec(&a->minor_rx, b->subtype, 0, NULL, 0))
+ {
+ dprint(5, (debugfile, "yes\n"));
+ return 1;
+ }
+ else
+ {
+ dprint(5, (debugfile, "no\n"));
+ }
+ }
+
+ return 0;
+}
+
+#define AT_COUNT(why) { shallcount = 1; }
+#define AT_NOCOUNT(why) { shallcount = 0; }
+
+int count_body_parts (BODY *body, int flags)
+{
+ int count = 0;
+ int shallcount, shallrecurse;
+ BODY *bp;
+
+ if (body == NULL)
+ return 0;
+
+ for (bp = body; bp != NULL; bp = bp->next)
+ {
+ /* Initial disposition is to count and not to recurse this part. */
+ AT_COUNT("default");
+ shallrecurse = 0;
+
+ dprint(5, (debugfile, "bp: desc=\"%s\"; fn=\"%s\", type=\"%d/%s\"\n",
+ bp->description ? bp->description : ("none"),
+ bp->filename ? bp->filename :
+ bp->d_filename ? bp->d_filename : "(none)",
+ bp->type, bp->subtype ? bp->subtype : "*"));
+
+ if (bp->type == TYPEMESSAGE)
+ {
+ shallrecurse = 1;
+
+ /* If it's an external body pointer, don't recurse it. */
+ if (!ascii_strcasecmp (bp->subtype, "external-body"))
+ shallrecurse = 0;
+
+ /* Don't count containers if they're top-level. */
+ if (flags & M_PARTS_TOPLEVEL)
+ AT_NOCOUNT("top-level message/*");
+ }
+ else if (bp->type == TYPEMULTIPART)
+ {
+ /* Always recurse multiparts, except multipart/alternative. */
+ shallrecurse = 1;
+ if (!mutt_strcasecmp(bp->subtype, "alternative"))
+ shallrecurse = 0;
+
+ /* Don't count containers if they're top-level. */
+ if (flags & M_PARTS_TOPLEVEL)
+ AT_NOCOUNT("top-level multipart");
+ }
+
+ if (bp->disposition == DISPINLINE &&
+ bp->type != TYPEMULTIPART && bp->type != TYPEMESSAGE && bp == body)
+ AT_NOCOUNT("ignore fundamental inlines");
+
+ /* If this body isn't scheduled for enumeration already, don't bother
+ * profiling it further.
+ */
+ if (shallcount)
+ {
+ /* Turn off shallcount if message type is not in ok list,
+ * or if it is in except list. Check is done separately for
+ * inlines vs. attachments.
+ */
+
+ if (bp->disposition == DISPATTACH)
+ {
+ if (!count_body_parts_check(&AttachAllow, bp, 1))
+ AT_NOCOUNT("attach not allowed");
+ if (count_body_parts_check(&AttachExclude, bp, 0))
+ AT_NOCOUNT("attach excluded");
+ }
+ else
+ {
+ if (!count_body_parts_check(&InlineAllow, bp, 1))
+ AT_NOCOUNT("inline not allowed");
+ if (count_body_parts_check(&InlineExclude, bp, 0))
+ AT_NOCOUNT("excluded");
+ }
+ }
+
+ if (shallcount)
+ count++;
+ bp->attach_qualifies = shallcount ? 1 : 0;
+
+ dprint(5, (debugfile, "cbp: %08x shallcount = %d\n", (unsigned int)bp, shallcount));
+
+ if (shallrecurse)
+ {
+ dprint(5, (debugfile, "cbp: %08x pre count = %d\n", (unsigned int)bp, count));
+ bp->attach_count = count_body_parts(bp->parts, flags & ~M_PARTS_TOPLEVEL);
+ count += bp->attach_count;
+ dprint(5, (debugfile, "cbp: %08x post count = %d\n", (unsigned int)bp, count));
+ }
+ }
+
+ dprint(5, (debugfile, "bp: return %d\n", count < 0 ? 0 : count));
+ return count < 0 ? 0 : count;
+}
+
+int mutt_count_body_parts (HEADER *hdr, int flags)
+{
+ if (hdr->attach_valid && !(flags & M_PARTS_RECOUNT))
+ return hdr->attach_total;
+
+ if (AttachAllow || AttachExclude || InlineAllow || InlineExclude)
+ hdr->attach_total = count_body_parts(hdr->content, flags | M_PARTS_TOPLEVEL);
+ else
+ hdr->attach_total = 0;
+
+ hdr->attach_valid = 1;
+ return hdr->attach_total;
+}
diff --git a/pattern.c b/pattern.c
index 09cde447..12d413f0 100644
--- a/pattern.c
+++ b/pattern.c
@@ -91,6 +91,7 @@ Flags[] =
{ 'v', M_COLLAPSED, 0, NULL },
{ 'V', M_CRYPT_VERIFIED, 0, NULL },
{ 'x', M_REFERENCE, 0, eat_regexp },
+ { 'X', M_MIMEATTACH, 0, eat_range },
{ 'y', M_XLABEL, 0, eat_regexp },
{ 'z', M_SIZE, 0, eat_range },
{ '=', M_DUPLICATED, 0, NULL },
@@ -1116,6 +1117,22 @@ mutt_pattern_exec (struct pattern_t *pat, pattern_exec_flag flags, CONTEXT *ctx,
return (pat->not ^ (h->env->spam && h->env->spam->data && patmatch (pat, h->env->spam->data) == 0));
case M_DUPLICATED:
return (pat->not ^ (h->thread && h->thread->duplicate_thread));
+ case M_MIMEATTACH:
+ {
+ int count;
+
+ if (h->content->parts)
+ count = mutt_count_body_parts(h, 0);
+ else
+ {
+ mutt_parse_mime_message(ctx, h);
+ count = mutt_count_body_parts(h, 0);
+ mutt_free_body(&h->content->parts);
+ }
+
+ return (pat->not ^ (count >= pat->min && (pat->max == M_MAXRANGE ||
+ count <= pat->max)));
+ }
case M_UNREFERENCED:
return (pat->not ^ (h->thread && !h->thread->child));
}
diff --git a/protos.h b/protos.h
index 327b7e14..f1fda6c2 100644
--- a/protos.h
+++ b/protos.h
@@ -169,6 +169,7 @@ void mutt_break_thread (HEADER *);
void mutt_buffy (char *, size_t);
int mutt_buffy_list (void);
void mutt_canonical_charset (char *, size_t, const char *);
+int mutt_count_body_parts (HEADER *hdr, int flags);
void mutt_check_rescore (CONTEXT *);
void mutt_clear_error (void);
void mutt_create_alias (ENVELOPE *, ADDRESS *);
diff --git a/recvattach.c b/recvattach.c
index 7442b182..03fc71a0 100644
--- a/recvattach.c
+++ b/recvattach.c
@@ -297,6 +297,14 @@ const char *mutt_attach_fmt (char *dest,
snprintf (dest, destlen, fmt, aptr->num + 1);
}
break;
+ case 'Q':
+ if (optional)
+ optional = aptr->content->attach_qualifies;
+ else {
+ snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
+ mutt_format_s (dest, destlen, fmt, "Q");
+ }
+ break;
case 's':
if (flags & M_FORMAT_STAT_FILE)
{
@@ -334,6 +342,15 @@ const char *mutt_attach_fmt (char *dest,
else if (!aptr->content->unlink)
optional = 0;
break;
+ case 'X':
+ if (optional)
+ optional = (aptr->content->attach_count + aptr->content->attach_qualifies) != 0;
+ else
+ {
+ snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
+ snprintf (dest, destlen, fmt, aptr->content->attach_count + aptr->content->attach_qualifies);
+ }
+ break;
default:
*dest = 0;
}