From d6988dab6bd378ccdf0f17aaa16de8aee1ceaf43 Mon Sep 17 00:00:00 2001 From: pdmef Date: Sun, 9 Oct 2005 10:54:48 +0000 Subject: [PATCH] Rocco Rutte: - merge in more latest mutt changes (as part of it, kill 'A' message flag again) git-svn-id: svn://svn.berlios.de/mutt-ng/trunk@541 e385b8ad-14ed-0310-8656-cc95a2468c6d --- ChangeLog.mutt | 37 +++++++ Muttngrc.head.in | 54 ++++++++++ UPGRADING | 15 +++ VERSION.svn | 2 +- browser.c | 7 ++ doc/manual.xml.head | 137 +++++++++++++++++++++++-- doc/muttrc.man.head | 3 +- globals.h | 4 + hcache.c | 2 +- hdrline.c | 28 ++++- init.c | 241 ++++++++++++++++++++++++++++++++++++++++++++ init.h | 16 ++- mime.h | 3 +- mutt.h | 21 ++++ parse.c | 158 +++++++++++++++++++++++++++-- pattern.c | 18 ++++ protos.h | 1 + recvattach.c | 16 +++ smime.c | 17 +++- 19 files changed, 742 insertions(+), 38 deletions(-) diff --git a/ChangeLog.mutt b/ChangeLog.mutt index e89614c..bc5c973 100644 --- a/ChangeLog.mutt +++ b/ChangeLog.mutt @@ -1,3 +1,40 @@ +2005-10-06 06:15:00 Brendan Cully (brendan) + + * browser.c: Sort browser entries after every IMAP browsing + operation instead of just when explicitly requested. Closes: + #2089. + +2005-10-06 05:13:55 Jeff Ito (brendan) + + * smime.c: Add AES ciphers to S/MIME encryption options. Closes: + #2103. + +2005-10-05 19:24:40 David Champion (brendan) + + * doc/manual.xml.head, init.c: Fix 'unattachments'. Closes: #2102. + +2005-10-05 19:20:22 Jeff Ito (brendan) + + * smime.c: S/MIME key selection truncates the last character of + the selected key for no apparent reason. Removed until someone + can justify it. Closes: #2081. + +2005-10-04 19:00:05 Brendan Cully (brendan) + + * init.h: Tweak description of pop_checkinterval slightly. Closes: + #2074. + + * hcache.c: Bump hcache Id for attachment counting patch. + +2005-10-04 06:05:39 David Champion (brendan) + + * Muttrc.head.in, doc/manual.xml.head, doc/muttrc.man.head, + globals.h, hdrline.c, init.c, init.h, mime.h, mutt.h, parse.c, + pattern.c, protos.h, recvattach.c: 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. + 2005-10-04 05:24:00 Sebastien Hinderer (brendan) * pager.c: The following patch has an effect only when the diff --git a/Muttngrc.head.in b/Muttngrc.head.in index 081daf5..ddaae1a 100644 --- a/Muttngrc.head.in +++ b/Muttngrc.head.in @@ -28,6 +28,60 @@ macro pager "!less @docdir@/manual.txt\n" "Show Mutt documentation" # # 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/UPGRADING b/UPGRADING index 81ebdde..9cd2ce9 100644 --- a/UPGRADING +++ b/UPGRADING @@ -10,6 +10,21 @@ This document is not the place for verbose documentation; it only offers the necessary keywords to look them up in the manual, ChangeLog or other sources of information. +2005-10-09: + + The "attachments" and "unattachments" commands were added. The %X + expando has been added to $index_format. The ~X pattern has been + added. The %Q and %X expandos have been added to $attach_format. + + As part of the above addition(s), the 'A' message status flag has been + removed again. + +2005-10-07: + + When upgrading to after a revision after 528, header caches must be + completely removed and rebuild again. From r540 on, this problem is + solved so manual interaction will not be necessary any longer. + 2005-09-14: The $smtp_envelope_from variable has been removed in favor of the diff --git a/VERSION.svn b/VERSION.svn index b9e5dd7..9b5ddda 100644 --- a/VERSION.svn +++ b/VERSION.svn @@ -1 +1 @@ -540 +541 diff --git a/browser.c b/browser.c index 75bb211..1a1f71e 100644 --- a/browser.c +++ b/browser.c @@ -821,6 +821,7 @@ void _mutt_select_file (char *f, size_t flen, int flags, char ***files, init_state (&state, NULL); state.imap_browse = 1; imap_browse (LastDir, &state); + browser_sort (&state); } #endif } @@ -956,6 +957,7 @@ void _mutt_select_file (char *f, size_t flen, int flags, char ***files, init_state (&state, NULL); state.imap_browse = 1; imap_browse (LastDir, &state); + browser_sort (&state); menu->data = state.entry; } else @@ -1054,6 +1056,7 @@ void _mutt_select_file (char *f, size_t flen, int flags, char ***files, init_state (&state, NULL); state.imap_browse = 1; imap_browse (LastDir, &state); + browser_sort (&state); menu->data = state.entry; menu->current = 0; menu->top = 0; @@ -1073,6 +1076,7 @@ void _mutt_select_file (char *f, size_t flen, int flags, char ***files, init_state (&state, NULL); state.imap_browse = 1; imap_browse (LastDir, &state); + browser_sort (&state); menu->data = state.entry; menu->current = 0; menu->top = 0; @@ -1147,6 +1151,7 @@ void _mutt_select_file (char *f, size_t flen, int flags, char ***files, init_state (&state, NULL); state.imap_browse = 1; imap_browse (LastDir, &state); + browser_sort (&state); menu->data = state.entry; menu->current = 0; menu->top = 0; @@ -1218,6 +1223,7 @@ void _mutt_select_file (char *f, size_t flen, int flags, char ***files, init_state (&state, NULL); state.imap_browse = 1; imap_browse (LastDir, &state); + browser_sort (&state); menu->data = state.entry; init_menu (&state, menu, title, sizeof (title), buffy); } @@ -1302,6 +1308,7 @@ void _mutt_select_file (char *f, size_t flen, int flags, char ***files, init_state (&state, NULL); state.imap_browse = 1; imap_browse (LastDir, &state); + browser_sort (&state); menu->data = state.entry; } #endif diff --git a/doc/manual.xml.head b/doc/manual.xml.head index 408710a..671402a 100644 --- a/doc/manual.xml.head +++ b/doc/manual.xml.head @@ -1175,15 +1175,6 @@ - - A - - - message has one or more attachments. - - - - S @@ -9252,7 +9243,128 @@ application/postscript image/* - + + + Attachment Searching and Counting + + + 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. + + + +The syntax is: + + + + + ( {+|-}disposition mime-type | ? ) + + + + + {+|-}disposition mime-type + + + + +Disposition is the attachment's Content-disposition type -- either +"inline" or "attachment". You can abbreviate this to I or A. + + + +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. + + + +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.) + + + +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. + + + +Some examples might help to illustrate. The examples that are not +commented out define the default configuration of the lists. + + + +## 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 + + + +"attachments ?" will list your current settings in Muttrc format, so +that it can be pasted elsewhere. + + + + MIME Lookup @@ -9942,6 +10054,11 @@ mailto:joe@host?Attach=~/.gnupg/secring.gpg EXPR messages which contain EXPR in the `References' field + + + [MIN]-[MAX] + messages with MIN to MAX attachments *) + EXPR diff --git a/doc/muttrc.man.head b/doc/muttrc.man.head index 7bdcd77..faf4403 100644 --- a/doc/muttrc.man.head +++ b/doc/muttrc.man.head @@ -454,6 +454,7 @@ l l. ~u message is addressed to a subscribed mailing list ~v message is part of a collapsed thread. ~x \fIEXPR\fP messages which contain \fIEXPR\fP in the \(lqReferences\(rq field +~X \fIMIN\fP-\fiMAX\fP messages with MIN to MAX attachments ~z \fIMIN\fP-\fIMAX\fP messages with a size in the range \fIMIN\fP to \fIMAX\fP ~= duplicated messages (see $duplicate_threads) ~$ unreferenced message (requries threaded view) @@ -461,7 +462,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 d918b03..3583416 100644 --- a/globals.h +++ b/globals.h @@ -185,6 +185,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/hcache.c b/hcache.c index 0be3292..237e6f0 100644 --- a/hcache.c +++ b/hcache.c @@ -14,7 +14,7 @@ #ifdef USE_HCACHE -#define MUTTNG_HCACHE_ID "0x001" +#define MUTTNG_HCACHE_ID "0x002" # if HAVE_INTTYPES_H # include diff --git a/hdrline.c b/hdrline.c index 39ab459..d120175 100644 --- a/hdrline.c +++ b/hdrline.c @@ -207,6 +207,7 @@ int mutt_user_is_recipient (HEADER * h) * %u = user (login) name of author * %v = first name of author, unless from self * %W = where user is (organization) + * %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 */ @@ -630,6 +631,27 @@ static const char *hdr_format_str (char *dest, optional = 0; break; + case 'X': + { + int count, flags = 0; + + 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 'Z': ch = ' '; @@ -644,7 +666,7 @@ static const char *hdr_format_str (char *dest, ch = 'K'; snprintf (buf2, sizeof (buf2), - "%c%c%c%c", (THREAD_NEW ? 'n' : (THREAD_OLD ? 'o' : + "%c%c%c", (THREAD_NEW ? 'n' : (THREAD_OLD ? 'o' : ((hdr->read && (ctx && ctx->msgnotreadyet != @@ -662,9 +684,7 @@ static const char *hdr_format_str (char *dest, mutt_user_is_recipient (hdr)) < str_len (Tochars)) ? - Tochars[i] : ' ')), - (hdr->content && hdr->content->type == TYPEMULTIPART) ? - 'A' : ' '); + Tochars[i] : ' '))); mutt_format_s (dest, destlen, prefix, buf2); break; diff --git a/init.c b/init.c index 003aeea..32f913b 100644 --- a/init.c +++ b/init.c @@ -1064,6 +1064,247 @@ static int parse_lists (BUFFER * buf, BUFFER * s, unsigned long data, 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; + debug_print (5, ("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; + debug_print (5, ("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 = mem_malloc(sizeof(ATTACH_MATCH)); + + /* some cheap hacks that I expect to remove */ + if (!str_casecmp(buf->data, "any")) + a->major = str_dup("*/.*"); + else if (!str_casecmp(buf->data, "none")) + a->major = str_dup("cheap_hack/this_should_never_match"); + else + a->major = str_dup(buf->data); + + if ((p = strchr(a->major, '/'))) { + *p = '\0'; + ++p; + a->minor = p; + } else { + a->minor = "unknown"; + } + + len = str_len (a->minor); + tmpminor = mem_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); + + mem_free (&tmpminor); + + debug_print (5, ("parse_attach_list: added %s/%s [%d]\n", + a->major, a->minor, a->major_int)); + + listp = mem_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, *newlp; + char *tmp; + int major; + char *minor; + + do { + mutt_extract_token (buf, s, 0); + + if (!str_casecmp(buf->data, "any")) + tmp = str_dup("*/.*"); + else if (!str_casecmp(buf->data, "none")) + tmp = str_dup("cheap_hack/this_should_never_match"); + else + tmp = str_dup(buf->data); + + if ((minor = strchr(tmp, '/'))) { + *minor = '\0'; + ++minor; + } else { + minor = "unknown"; + } + major = mutt_check_mime_type(tmp); + + /* We must do our own walk here because remove_from_list() will only + * remove the LIST->data, not anything pointed to by the LIST->data. */ + lastp = NULL; + for(lp = *ldata; lp; ) { + a = (ATTACH_MATCH *)lp->data; + debug_print(5, ("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 && !str_casecmp(minor, a->minor)) { + debug_print(5, ("parse_unattach_list: removed %s/%s [%d]\n", + a->major, a->minor, a->major_int)); + regfree(&a->minor_rx); + mem_free(&a->major); + + /* Relink backward */ + if (lastp) + lastp->next = lp->next; + else + *ldata = lp->next; + + newlp = lp->next; + mem_free(&lp->data); /* same as a */ + mem_free(&lp); + lp = newlp; + continue; + } + + lastp = lp; + lp = lp->next; + } + } + while (MoreArgs (s)); + + mem_free (&tmp); + _attachments_clean(); + return 0; +} + +static int print_attach_list (LIST *lp, char op, char *name) { + while (lp) { + printf("attachments %c%s %s/%s\n", op, name, + ((ATTACH_MATCH *)lp->data)->major, + ((ATTACH_MATCH *)lp->data)->minor); + lp = lp->next; + } + + return 0; +} + +static int parse_attachments (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) { + char op, *category; + LIST **listp; + + mutt_extract_token(buf, s, 0); + if (!buf->data || *buf->data == '\0') { + strfcpy(err->data, _("attachments: no disposition"), err->dsize); + return -1; + } + + category = buf->data; + op = *category++; + + if (op == '?') { + mutt_endwin (NULL); + fflush (stdout); + printf("\nCurrent attachments settings:\n\n"); + print_attach_list(AttachAllow, '+', "A"); + print_attach_list(AttachExclude, '-', "A"); + print_attach_list(InlineAllow, '+', "I"); + print_attach_list(InlineExclude, '-', "I"); + set_option (OPTFORCEREDRAWINDEX); + set_option (OPTFORCEREDRAWPAGER); + mutt_any_key_to_continue (NULL); + return 0; + } + + if (op != '+' && op != '-') { + op = '+'; + category--; + } + if (!str_ncasecmp(category, "attachment", strlen(category))) { + if (op == '+') + listp = &AttachAllow; + else + listp = &AttachExclude; + } + else if (!str_ncasecmp(category, "inline", strlen(category))) { + 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 (!str_ncasecmp(p, "attachment", strlen(p))) { + if (op == '+') + listp = &AttachAllow; + else + listp = &AttachExclude; + } + else if (!str_ncasecmp(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) { diff --git a/init.h b/init.h index 255941c..3b37369 100644 --- a/init.h +++ b/init.h @@ -279,10 +279,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 @@ -1260,6 +1262,7 @@ struct option_t MuttVars[] = { ** .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 %W .dd name of organization of author (`organization:' field) + ** .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 @@ -1638,9 +1641,9 @@ struct option_t MuttVars[] = { ** If \fIset\fP, forces Mutt-ng to interpret keystrokes with the high bit (bit 8) ** set as if the user had pressed the \fTESC\fP key and whatever key remains ** after having the high bit removed. For example, if the key pressed - ** has an ASCII value of \fT0xf4\fP, then this is treated as if the user had + ** has an ASCII value of \fT0xf8\fP, then this is treated as if the user had ** pressed \fTESC\fP then ``\fTx\fP''. This is because the result of removing the - ** high bit from ``\fT0xf4\fP'' is ``\fT0x74\fP'', which is the ASCII character + ** high bit from ``\fT0xf8\fP'' is ``\fT0x78\fP'', which is the ASCII character ** ``\fTx\fP''. */ {"mh_purge", DT_BOOL, R_NONE, OPTMHPURGE, "no" }, @@ -2759,7 +2762,7 @@ struct option_t MuttVars[] = { ** Availability: POP ** ** .pp - ** This variable configures how often (in seconds) POP should look for + ** This variable configures how often (in seconds) Mutt-ng should look for ** new mail. */ {"pop_delete", DT_QUAD, R_NONE, OPT_POPDELETE, "ask-no" }, @@ -2803,7 +2806,7 @@ struct option_t MuttVars[] = { ** Availability: POP ** ** .pp - ** Controls whether or not Mutt-ng will try to reconnect to a POP server when the + ** Controls whether or not Mutt-ng will try to reconnect to a POP server if the ** connection is lost. */ {"pop_user", DT_STR, R_NONE, UL &PopUser, "" }, @@ -3965,7 +3968,8 @@ const struct mapping_t SortKeyMethods[] = { static int parse_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_attachments (BUFFER *, BUFFER *, unsigned long, BUFFER *); +static int parse_unattachments (BUFFER *, BUFFER *, unsigned long, BUFFER *); static int parse_lists (BUFFER *, BUFFER *, unsigned long, BUFFER *); static int parse_unlists (BUFFER *, BUFFER *, unsigned long, BUFFER *); static int parse_alias (BUFFER *, BUFFER *, unsigned long, BUFFER *); @@ -3992,6 +3996,8 @@ struct command_t { struct command_t Commands[] = { {"alternates", parse_alternates, 0}, + {"attachments", parse_attachments, 0 }, + {"unattachments",parse_unattachments,0 }, {"unalternates", parse_unalternates, 0}, #ifdef USE_SOCKET {"account-hook", mutt_parse_hook, M_ACCOUNTHOOK}, diff --git a/mime.h b/mime.h index c30feed..6125e7a 100644 --- a/mime.h +++ b/mime.h @@ -17,7 +17,8 @@ enum { TYPEMODEL, TYPEMULTIPART, TYPETEXT, - TYPEVIDEO + TYPEVIDEO, + TYPEANY }; /* Content-Transfer-Encoding */ diff --git a/mutt.h b/mutt.h index 5452472..13c916b 100644 --- a/mutt.h +++ b/mutt.h @@ -201,6 +201,7 @@ enum { M_CRYPT_ENCRYPT, M_PGP_KEY, M_XLABEL, + M_MIMEATTACH, #ifdef USE_NNTP M_NEWSGROUPS, #endif @@ -630,6 +631,8 @@ typedef struct body { struct attachptr *aptr; /* Menu information, used in recvattach.c */ + signed short attach_count; + time_t stamp; /* time stamp of last * encoding update. */ @@ -667,6 +670,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; @@ -706,6 +710,9 @@ typedef struct header { unsigned int searched:1; unsigned int matched:1; + /* tells whether the attach 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? */ @@ -733,6 +740,8 @@ typedef struct header { char *tree; /* character string to print thread tree */ struct thread *thread; + short attach_total; + #ifdef MIXMASTER LIST *chain; #endif @@ -834,6 +843,18 @@ typedef struct { unsigned int counting:1; /* do we just want to cound? */ } CONTEXT; +/* 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 "protos.h" #include "lib.h" #include "globals.h" diff --git a/parse.c b/parse.c index 1562faf..bf7d76d 100644 --- a/parse.c +++ b/parse.c @@ -294,6 +294,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; } @@ -925,22 +929,26 @@ 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, @@ -1426,3 +1434,131 @@ 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; + debug_print(5, ("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)) { + debug_print(5, ("yes\n")); + return 1; + } else { + debug_print(5, ("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; + + debug_print(5, ("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 (!str_casecmp(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; + + debug_print(5, ("cbp: %08x shallcount = %d\n", (unsigned int)bp, shallcount)); + + if (shallrecurse) { + debug_print(5, ("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; + debug_print(5, ("cbp: %08x post count = %d\n", (unsigned int)bp, count)); + } + } + + debug_print(5, ("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 63abd4e..7f5fd51 100644 --- a/pattern.c +++ b/pattern.c @@ -95,6 +95,7 @@ struct pattern_flags { #endif { '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}, { @@ -1094,6 +1095,23 @@ mutt_pattern_exec (struct pattern_t *pat, pattern_exec_flag flags, && 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)); case M_MULTIPART: diff --git a/protos.h b/protos.h index 1c93f53..8c3df7e 100644 --- a/protos.h +++ b/protos.h @@ -114,6 +114,7 @@ void mutt_block_signals (void); void mutt_block_signals_system (void); int mutt_bounce_message (FILE * fp, HEADER *, ADDRESS *); 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_default_save (char *, size_t, HEADER *); diff --git a/recvattach.c b/recvattach.c index 50bc81d..f770915 100644 --- a/recvattach.c +++ b/recvattach.c @@ -302,6 +302,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) { struct stat st; @@ -338,6 +346,14 @@ 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; } diff --git a/smime.c b/smime.c index 4dc14f6..ae7a995 100644 --- a/smime.c +++ b/smime.c @@ -1885,8 +1885,9 @@ int smime_send_menu (HEADER * msg, int *redraw) case 3: /* encrypt (w)ith */ msg->security |= ENCRYPT; switch (mutt_multi_choice (_("1: DES, 2: Triple-DES, 3: RC2-40," - " 4: RC2-64, 5: RC2-128, or (f)orget it? "), - _("12345f"))) { + " 4: RC2-64, 5: RC2-128, 6: AES128," + " 7: AES192, 8: AES256, or (f)orget it? "), + _("12345678f"))) { case 1: str_replace (&SmimeCryptAlg, "des"); break; @@ -1902,7 +1903,16 @@ int smime_send_menu (HEADER * msg, int *redraw) case 5: str_replace (&SmimeCryptAlg, "rc2-128"); break; - case 6: /* forget it */ + case 6: + str_replace (&SmimeCryptAlg, "aes128"); + break; + case 7: + str_replace (&SmimeCryptAlg, "aes192"); + break; + case 8: + str_replace (&SmimeCryptAlg, "aes256"); + break; + case 9: /* forget it */ break; } break; @@ -1921,7 +1931,6 @@ int smime_send_menu (HEADER * msg, int *redraw) case 4: /* sign (a)s */ if ((p = smime_ask_for_key (_("Sign as: "), NULL, 0))) { - p[str_len (p) - 1] = '\0'; str_replace (&SmimeDefaultKey, p); msg->security |= SIGN; -- 2.20.1