From: Richard Russon Date: Sat, 2 Jun 2018 21:33:17 +0000 (+0100) Subject: tidy handler.c X-Git-Tag: neomutt-20180622~26^2~8 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=7275a6101cec27c936ae24ad5833d7883d5463e5;p=neomutt tidy handler.c - Move the function prototypes to a new file `handler.h` - Rearrange the functions There are no changes to the code. --- diff --git a/attach.c b/attach.c index c9f8c9994..6892e9ec4 100644 --- a/attach.c +++ b/attach.c @@ -36,6 +36,7 @@ #include "copy.h" #include "filter.h" #include "globals.h" +#include "handler.h" #include "header.h" #include "mailbox.h" #include "mutt_curses.h" diff --git a/copy.c b/copy.c index f1d0ece28..f6d9ef6b2 100644 --- a/copy.c +++ b/copy.c @@ -38,6 +38,7 @@ #include "context.h" #include "envelope.h" #include "globals.h" +#include "handler.h" #include "header.h" #include "mailbox.h" #include "mutt_curses.h" diff --git a/handler.c b/handler.c index 9a8e6250c..9490b53e8 100644 --- a/handler.c +++ b/handler.c @@ -34,6 +34,7 @@ #include #include "mutt/mutt.h" #include "mutt.h" +#include "handler.h" #include "body.h" #include "copy.h" #include "enriched.h" @@ -47,10 +48,17 @@ #include "rfc1524.h" #include "rfc3676.h" #include "state.h" +#ifdef ENABLE_NLS +#include +#endif #define BUFI_SIZE 1000 #define BUFO_SIZE 2000 +#define TXTHTML 1 +#define TXTPLAIN 2 +#define TXTENRICHED 3 + typedef int (*handler_t)(struct Body *b, struct State *s); /** @@ -318,70 +326,6 @@ static void decode_quoted(struct State *s, long len, bool istext, iconv_t cd) state_reset_prefix(s); } -/** - * mutt_decode_base64 - Decode base64-encoded text - * @param s State to work with - * @param len Length of text to decode - * @param istext Mime part is plain text - * @param cd Iconv conversion descriptor - */ -void mutt_decode_base64(struct State *s, size_t len, bool istext, iconv_t cd) -{ - char buf[5]; - int ch, i; - char bufi[BUFI_SIZE]; - size_t l = 0; - - buf[4] = '\0'; - - if (istext) - state_set_prefix(s); - - while (len > 0) - { - for (i = 0; (i < 4) && (len > 0); len--) - { - ch = fgetc(s->fpin); - if (ch == EOF) - break; - if ((ch >= 0) && (ch < 128) && (base64val(ch) != -1 || ch == '=')) - buf[i++] = ch; - } - if (i != 4) - { - /* "i" may be zero if there is trailing whitespace, which is not an error */ - if (i != 0) - mutt_debug(2, "didn't get a multiple of 4 chars.\n"); - break; - } - - const int c1 = base64val(buf[0]); - const int c2 = base64val(buf[1]); - ch = (c1 << 2) | (c2 >> 4); - bufi[l++] = ch; - - if (buf[2] == '=') - break; - const int c3 = base64val(buf[2]); - ch = ((c2 & 0xf) << 4) | (c3 >> 2); - bufi[l++] = ch; - - if (buf[3] == '=') - break; - const int c4 = base64val(buf[3]); - ch = ((c3 & 0x3) << 6) | c4; - bufi[l++] = ch; - - if ((l + 8) >= sizeof(bufi)) - convert_to_state(cd, bufi, &l, s); - } - - convert_to_state(cd, bufi, &l, s); - convert_to_state(cd, 0, 0, s); - - state_reset_prefix(s); -} - /** * decode_byte - Decode a uuencoded byte * @param ch Character to decode @@ -554,294 +498,176 @@ static bool is_autoview(struct Body *b) return false; } -#define TXTHTML 1 -#define TXTPLAIN 2 -#define TXTENRICHED 3 - /** - * alternative_handler - Handler for multipart alternative emails + * autoview_handler - Handler for autoviewable attachments * @param a Body of the email * @param s State of text being processed * @retval 0 Success * @retval -1 Error */ -static int alternative_handler(struct Body *a, struct State *s) +static int autoview_handler(struct Body *a, struct State *s) { - struct Body *choice = NULL; - struct Body *b = NULL; - bool mustfree = false; + struct Rfc1524MailcapEntry *entry = rfc1524_new_entry(); + char buffer[LONG_STRING]; + char type[STRING]; + char command[LONG_STRING]; + char tempfile[_POSIX_PATH_MAX] = ""; + char *fname = NULL; + FILE *fpin = NULL; + FILE *fpout = NULL; + FILE *fperr = NULL; + int piped = false; + pid_t thepid; int rc = 0; - if ((a->encoding == ENCBASE64) || (a->encoding == ENCQUOTEDPRINTABLE) || - (a->encoding == ENCUUENCODED)) - { - struct stat st; - mustfree = true; - fstat(fileno(s->fpin), &st); - b = mutt_body_new(); - b->length = (long) st.st_size; - b->parts = mutt_parse_multipart( - s->fpin, mutt_param_get(&a->parameter, "boundary"), (long) st.st_size, - (mutt_str_strcasecmp("digest", a->subtype) == 0)); - } - else - b = a; + snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype); + rfc1524_mailcap_lookup(a, type, entry, MUTT_AUTOVIEW); - a = b; + fname = mutt_str_strdup(a->filename); + mutt_file_sanitize_filename(fname, true); + rfc1524_expand_filename(entry->nametemplate, fname, tempfile, sizeof(tempfile)); + FREE(&fname); - /* First, search list of preferred types */ - struct ListNode *np; - STAILQ_FOREACH(np, &AlternativeOrderList, entries) + if (entry->command) { - int btlen; /* length of basetype */ - bool wild; /* do we have a wildcard to match all subtypes? */ + mutt_str_strfcpy(command, entry->command, sizeof(command)); - char *c = strchr(np->data, '/'); - if (c) + /* rfc1524_expand_command returns 0 if the file is required */ + piped = rfc1524_expand_command(a, tempfile, type, command, sizeof(command)); + + if (s->flags & MUTT_DISPLAY) { - wild = ((c[1] == '*') && (c[2] == 0)); - btlen = c - np->data; + state_mark_attach(s); + state_printf(s, _("[-- Autoview using %s --]\n"), command); + mutt_message(_("Invoking autoview command: %s"), command); } - else + + fpin = mutt_file_fopen(tempfile, "w+"); + if (!fpin) { - wild = true; - btlen = mutt_str_strlen(np->data); + mutt_perror("fopen"); + rfc1524_free_entry(&entry); + return -1; } - if (a->parts) - b = a->parts; + mutt_file_copy_bytes(s->fpin, fpin, a->length); + + if (!piped) + { + mutt_file_fclose(&fpin); + thepid = mutt_create_filter(command, NULL, &fpout, &fperr); + } else - b = a; - while (b) { - const char *bt = TYPE(b); - if ((mutt_str_strncasecmp(bt, np->data, btlen) == 0) && (bt[btlen] == 0)) - { - /* the basetype matches */ - if (wild || (mutt_str_strcasecmp(np->data + btlen + 1, b->subtype) == 0)) - { - choice = b; - } - } - b = b->next; + unlink(tempfile); + fflush(fpin); + rewind(fpin); + thepid = mutt_create_filter_fd(command, NULL, &fpout, &fperr, fileno(fpin), -1, -1); } - if (choice) - break; - } - - /* Next, look for an autoviewable type */ - if (!choice) - { - if (a->parts) - b = a->parts; - else - b = a; - while (b) + if (thepid < 0) { - if (is_autoview(b)) - choice = b; - b = b->next; + mutt_perror(_("Can't create filter")); + if (s->flags & MUTT_DISPLAY) + { + state_mark_attach(s); + state_printf(s, _("[-- Can't run %s. --]\n"), command); + } + rc = -1; + goto bail; } - } - /* Then, look for a text entry */ - if (!choice) - { - if (a->parts) - b = a->parts; - else - b = a; - int type = 0; - while (b) + if (s->prefix) { - if (b->type == TYPETEXT) + while (fgets(buffer, sizeof(buffer), fpout) != NULL) { - if ((mutt_str_strcasecmp("plain", b->subtype) == 0) && (type <= TXTPLAIN)) - { - choice = b; - type = TXTPLAIN; - } - else if ((mutt_str_strcasecmp("enriched", b->subtype) == 0) && (type <= TXTENRICHED)) + state_puts(s->prefix, s); + state_puts(buffer, s); + } + /* check for data on stderr */ + if (fgets(buffer, sizeof(buffer), fperr)) + { + if (s->flags & MUTT_DISPLAY) { - choice = b; - type = TXTENRICHED; + state_mark_attach(s); + state_printf(s, _("[-- Autoview stderr of %s --]\n"), command); } - else if ((mutt_str_strcasecmp("html", b->subtype) == 0) && (type <= TXTHTML)) + + state_puts(s->prefix, s); + state_puts(buffer, s); + while (fgets(buffer, sizeof(buffer), fperr) != NULL) { - choice = b; - type = TXTHTML; + state_puts(s->prefix, s); + state_puts(buffer, s); } } - b = b->next; } - } - - /* Finally, look for other possibilities */ - if (!choice) - { - if (a->parts) - b = a->parts; else - b = a; - while (b) { - if (mutt_can_decode(b)) - choice = b; - b = b->next; + mutt_file_copy_stream(fpout, s->fpout); + /* Check for stderr messages */ + if (fgets(buffer, sizeof(buffer), fperr)) + { + if (s->flags & MUTT_DISPLAY) + { + state_mark_attach(s); + state_printf(s, _("[-- Autoview stderr of %s --]\n"), command); + } + + state_puts(buffer, s); + mutt_file_copy_stream(fperr, s->fpout); + } } - } - if (choice) - { - if (s->flags & MUTT_DISPLAY && !Weed) - { - fseeko(s->fpin, choice->hdr_offset, SEEK_SET); - mutt_file_copy_bytes(s->fpin, s->fpout, choice->offset - choice->hdr_offset); - } - - if (mutt_str_strcmp("info", ShowMultipartAlternative) == 0) - { - print_part_line(s, choice, 0); - } - mutt_body_handler(choice, s); + bail: + mutt_file_fclose(&fpout); + mutt_file_fclose(&fperr); - if (mutt_str_strcmp("info", ShowMultipartAlternative) == 0) - { - if (a->parts) - b = a->parts; - else - b = a; - int count = 0; - while (b) - { - if (choice != b) - { - count += 1; - if (count == 1) - state_putc('\n', s); + mutt_wait_filter(thepid); + if (piped) + mutt_file_fclose(&fpin); + else + mutt_file_unlink(tempfile); - print_part_line(s, b, count); - } - b = b->next; - } - } - } - else if (s->flags & MUTT_DISPLAY) - { - /* didn't find anything that we could display! */ - state_mark_attach(s); - state_puts(_("[-- Error: Could not display any parts of " - "Multipart/Alternative! --]\n"), - s); - rc = -1; + if (s->flags & MUTT_DISPLAY) + mutt_clear_error(); } - - if (mustfree) - mutt_body_free(&a); + rfc1524_free_entry(&entry); return rc; } /** - * multilingual_handler - Handler for multi-lingual emails - * @param a Body of the email - * @param s State of text being processed + * text_plain_handler - Handler for plain text + * @param b Body of email (UNUSED) + * @param s State to work with * @retval 0 Always + * + * When generating format=flowed ($text_flowed is set) from format=fixed, strip + * all trailing spaces to improve interoperability; if $text_flowed is unset, + * simply verbatim copy input. */ -static int multilingual_handler(struct Body *a, struct State *s) +static int text_plain_handler(struct Body *b, struct State *s) { - struct Body *choice = NULL; - struct Body *b = NULL; - bool mustfree = false; - int rc = 0; - struct Body *first_part = NULL; - struct Body *zxx_part = NULL; - char *lang = NULL; - - mutt_debug(2, "RFC8255 >> entering in handler multilingual handler\n"); - if ((a->encoding == ENCBASE64) || (a->encoding == ENCQUOTEDPRINTABLE) || - (a->encoding == ENCUUENCODED)) - { - struct stat st; - mustfree = true; - fstat(fileno(s->fpin), &st); - b = mutt_body_new(); - b->length = (long) st.st_size; - b->parts = mutt_parse_multipart( - s->fpin, mutt_param_get(&a->parameter, "boundary"), (long) st.st_size, - (mutt_str_strcasecmp("digest", a->subtype) == 0)); - } - else - b = a; - - a = b; - - if (a->parts) - b = a->parts; - else - b = a; - - char *preferred_languages = NULL; - if (PreferredLanguages) - { - mutt_debug(2, "RFC8255 >> preferred_languages set in config to '%s'\n", PreferredLanguages); - preferred_languages = mutt_str_strdup(PreferredLanguages); - lang = strtok(preferred_languages, ","); - } + char *buf = NULL; + size_t l = 0, sz = 0; - while (lang) + while ((buf = mutt_file_read_line(buf, &sz, s->fpin, NULL, 0))) { - while (b) + if ((mutt_str_strcmp(buf, "-- ") != 0) && TextFlowed) { - if (mutt_can_decode(b)) - { - if (!first_part) - first_part = b; - - if (b->language && (mutt_str_strcmp("zxx", b->language) == 0)) - zxx_part = b; - - mutt_debug(2, "RFC8255 >> comparing configuration preferred_language='%s' to mail part content-language='%s'\n", - lang, b->language); - if (lang && b->language && (mutt_str_strcmp(lang, b->language) == 0)) - { - mutt_debug(2, "RFC8255 >> preferred_language='%s' matches content-language='%s' >> part selected to be displayed\n", - lang, b->language); - choice = b; - break; - } - } - - b = b->next; + l = mutt_str_strlen(buf); + while (l > 0 && buf[l - 1] == ' ') + buf[--l] = '\0'; } - - if (choice) - break; - - lang = strtok(NULL, ","); - - if (a->parts) - b = a->parts; - else - b = a; - } - - if (choice) - mutt_body_handler(choice, s); - else - { - if (zxx_part) - mutt_body_handler(zxx_part, s); - else - mutt_body_handler(first_part, s); + if (s->prefix) + state_puts(s->prefix, s); + state_puts(buf, s); + state_putc('\n', s); } - if (mustfree) - mutt_body_free(&a); - - FREE(&preferred_languages); - return rc; + FREE(&buf); + return 0; } /** @@ -899,305 +725,48 @@ static int message_handler(struct Body *a, struct State *s) } /** - * mutt_can_decode - Will decoding the attachment produce any output - * @param a Body of email to test - * @retval true Decoding the attachment will produce output - */ -bool mutt_can_decode(struct Body *a) -{ - if (is_autoview(a)) - return true; - else if (a->type == TYPETEXT) - return true; - else if (a->type == TYPEMESSAGE) - return true; - else if (a->type == TYPEMULTIPART) - { - if (WithCrypto) - { - if ((mutt_str_strcasecmp(a->subtype, "signed") == 0) || - (mutt_str_strcasecmp(a->subtype, "encrypted") == 0)) - { - return true; - } - } - - for (struct Body *b = a->parts; b; b = b->next) - { - if (mutt_can_decode(b)) - return true; - } - } - else if ((WithCrypto != 0) && a->type == TYPEAPPLICATION) - { - if (((WithCrypto & APPLICATION_PGP) != 0) && mutt_is_application_pgp(a)) - return true; - if (((WithCrypto & APPLICATION_SMIME) != 0) && mutt_is_application_smime(a)) - return true; - } - - return false; -} - -/** - * multipart_handler - Handler for multipart emails - * @param a Body of the email + * external_body_handler - Handler for external-body emails + * @param b Body of the email * @param s State of text being processed * @retval 0 Success * @retval -1 Error */ -static int multipart_handler(struct Body *a, struct State *s) +static int external_body_handler(struct Body *b, struct State *s) { - struct Body *b = NULL, *p = NULL; - struct stat st; - int count; - int rc = 0; + const char *str = NULL; + char strbuf[LONG_STRING]; // STRING might be too short but LONG_STRING should be large enough - if ((a->encoding == ENCBASE64) || (a->encoding == ENCQUOTEDPRINTABLE) || - (a->encoding == ENCUUENCODED)) + const char *access_type = mutt_param_get(&b->parameter, "access-type"); + if (!access_type) { - fstat(fileno(s->fpin), &st); - b = mutt_body_new(); - b->length = (long) st.st_size; - b->parts = mutt_parse_multipart( - s->fpin, mutt_param_get(&a->parameter, "boundary"), (long) st.st_size, - (mutt_str_strcasecmp("digest", a->subtype) == 0)); + if (s->flags & MUTT_DISPLAY) + { + state_mark_attach(s); + state_puts(_("[-- Error: message/external-body has no access-type " + "parameter --]\n"), + s); + return 0; + } + else + return -1; } + + const char *expiration = mutt_param_get(&b->parameter, "expiration"); + time_t expire; + if (expiration) + expire = mutt_date_parse_date(expiration, NULL); else - b = a; + expire = -1; - for (p = b->parts, count = 1; p; p = p->next, count++) + if (mutt_str_strcasecmp(access_type, "x-mutt-deleted") == 0) { - if (s->flags & MUTT_DISPLAY) + if (s->flags & (MUTT_DISPLAY | MUTT_PRINTING)) { - state_mark_attach(s); - if (p->description || p->filename || p->form_name) - { - /* L10N: %s is the attachment description, filename or form_name. */ - state_printf(s, _("[-- Attachment #%d: %s --]\n"), count, - p->description ? p->description : - p->filename ? p->filename : p->form_name); - } - else - state_printf(s, _("[-- Attachment #%d --]\n"), count); - print_part_line(s, p, 0); - if (!Weed) - { - fseeko(s->fpin, p->hdr_offset, SEEK_SET); - mutt_file_copy_bytes(s->fpin, s->fpout, p->offset - p->hdr_offset); - } - else - state_putc('\n', s); - } - - rc = mutt_body_handler(p, s); - state_putc('\n', s); - - if (rc != 0) - { - mutt_error(_("One or more parts of this message could not be displayed")); - mutt_debug(1, "Failed on attachment #%d, type %s/%s.\n", count, TYPE(p), - NONULL(p->subtype)); - } - - if ((s->flags & MUTT_REPLYING) && IncludeOnlyfirst && (s->flags & MUTT_FIRSTDONE)) - { - break; - } - } - - if ((a->encoding == ENCBASE64) || (a->encoding == ENCQUOTEDPRINTABLE) || - (a->encoding == ENCUUENCODED)) - mutt_body_free(&b); - - /* make failure of a single part non-fatal */ - if (rc < 0) - rc = 1; - return rc; -} - -/** - * autoview_handler - Handler for autoviewable attachments - * @param a Body of the email - * @param s State of text being processed - * @retval 0 Success - * @retval -1 Error - */ -static int autoview_handler(struct Body *a, struct State *s) -{ - struct Rfc1524MailcapEntry *entry = rfc1524_new_entry(); - char buffer[LONG_STRING]; - char type[STRING]; - char command[LONG_STRING]; - char tempfile[_POSIX_PATH_MAX] = ""; - char *fname = NULL; - FILE *fpin = NULL; - FILE *fpout = NULL; - FILE *fperr = NULL; - int piped = false; - pid_t thepid; - int rc = 0; - - snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype); - rfc1524_mailcap_lookup(a, type, entry, MUTT_AUTOVIEW); - - fname = mutt_str_strdup(a->filename); - mutt_file_sanitize_filename(fname, true); - rfc1524_expand_filename(entry->nametemplate, fname, tempfile, sizeof(tempfile)); - FREE(&fname); - - if (entry->command) - { - mutt_str_strfcpy(command, entry->command, sizeof(command)); - - /* rfc1524_expand_command returns 0 if the file is required */ - piped = rfc1524_expand_command(a, tempfile, type, command, sizeof(command)); - - if (s->flags & MUTT_DISPLAY) - { - state_mark_attach(s); - state_printf(s, _("[-- Autoview using %s --]\n"), command); - mutt_message(_("Invoking autoview command: %s"), command); - } - - fpin = mutt_file_fopen(tempfile, "w+"); - if (!fpin) - { - mutt_perror("fopen"); - rfc1524_free_entry(&entry); - return -1; - } - - mutt_file_copy_bytes(s->fpin, fpin, a->length); - - if (!piped) - { - mutt_file_fclose(&fpin); - thepid = mutt_create_filter(command, NULL, &fpout, &fperr); - } - else - { - unlink(tempfile); - fflush(fpin); - rewind(fpin); - thepid = mutt_create_filter_fd(command, NULL, &fpout, &fperr, fileno(fpin), -1, -1); - } - - if (thepid < 0) - { - mutt_perror(_("Can't create filter")); - if (s->flags & MUTT_DISPLAY) - { - state_mark_attach(s); - state_printf(s, _("[-- Can't run %s. --]\n"), command); - } - rc = -1; - goto bail; - } - - if (s->prefix) - { - while (fgets(buffer, sizeof(buffer), fpout) != NULL) - { - state_puts(s->prefix, s); - state_puts(buffer, s); - } - /* check for data on stderr */ - if (fgets(buffer, sizeof(buffer), fperr)) - { - if (s->flags & MUTT_DISPLAY) - { - state_mark_attach(s); - state_printf(s, _("[-- Autoview stderr of %s --]\n"), command); - } - - state_puts(s->prefix, s); - state_puts(buffer, s); - while (fgets(buffer, sizeof(buffer), fperr) != NULL) - { - state_puts(s->prefix, s); - state_puts(buffer, s); - } - } - } - else - { - mutt_file_copy_stream(fpout, s->fpout); - /* Check for stderr messages */ - if (fgets(buffer, sizeof(buffer), fperr)) - { - if (s->flags & MUTT_DISPLAY) - { - state_mark_attach(s); - state_printf(s, _("[-- Autoview stderr of %s --]\n"), command); - } - - state_puts(buffer, s); - mutt_file_copy_stream(fperr, s->fpout); - } - } - - bail: - mutt_file_fclose(&fpout); - mutt_file_fclose(&fperr); - - mutt_wait_filter(thepid); - if (piped) - mutt_file_fclose(&fpin); - else - mutt_file_unlink(tempfile); - - if (s->flags & MUTT_DISPLAY) - mutt_clear_error(); - } - rfc1524_free_entry(&entry); - - return rc; -} - -/** - * external_body_handler - Handler for external-body emails - * @param b Body of the email - * @param s State of text being processed - * @retval 0 Success - * @retval -1 Error - */ -static int external_body_handler(struct Body *b, struct State *s) -{ - const char *str = NULL; - char strbuf[LONG_STRING]; // STRING might be too short but LONG_STRING should be large enough - - const char *access_type = mutt_param_get(&b->parameter, "access-type"); - if (!access_type) - { - if (s->flags & MUTT_DISPLAY) - { - state_mark_attach(s); - state_puts(_("[-- Error: message/external-body has no access-type " - "parameter --]\n"), - s); - return 0; - } - else - return -1; - } - - const char *expiration = mutt_param_get(&b->parameter, "expiration"); - time_t expire; - if (expiration) - expire = mutt_date_parse_date(expiration, NULL); - else - expire = -1; - - if (mutt_str_strcasecmp(access_type, "x-mutt-deleted") == 0) - { - if (s->flags & (MUTT_DISPLAY | MUTT_PRINTING)) - { - char pretty_size[10]; - long size = 0; - - char *length = mutt_param_get(&b->parameter, "length"); - if (length) + char pretty_size[10]; + long size = 0; + + char *length = mutt_param_get(&b->parameter, "length"); + if (length) { size = strtol(length, NULL, 10); mutt_str_pretty_size(pretty_size, sizeof(pretty_size), size); @@ -1328,90 +897,366 @@ static int external_body_handler(struct Body *b, struct State *s) } /** - * mutt_decode_attachment - Decode an email's attachment - * @param b Body of the email + * alternative_handler - Handler for multipart alternative emails + * @param a Body of the email * @param s State of text being processed + * @retval 0 Success + * @retval -1 Error */ -void mutt_decode_attachment(struct Body *b, struct State *s) +static int alternative_handler(struct Body *a, struct State *s) { - int istext = mutt_is_text_part(b); - iconv_t cd = (iconv_t)(-1); + struct Body *choice = NULL; + struct Body *b = NULL; + bool mustfree = false; + int rc = 0; - if (istext && s->flags & MUTT_CHARCONV) + if ((a->encoding == ENCBASE64) || (a->encoding == ENCQUOTEDPRINTABLE) || + (a->encoding == ENCUUENCODED)) { - char *charset = mutt_param_get(&b->parameter, "charset"); - if (!charset && AssumedCharset && *AssumedCharset) - charset = mutt_ch_get_default_charset(); - if (charset && Charset) - cd = mutt_ch_iconv_open(Charset, charset, MUTT_ICONV_HOOK_FROM); + struct stat st; + mustfree = true; + fstat(fileno(s->fpin), &st); + b = mutt_body_new(); + b->length = (long) st.st_size; + b->parts = mutt_parse_multipart( + s->fpin, mutt_param_get(&a->parameter, "boundary"), (long) st.st_size, + (mutt_str_strcasecmp("digest", a->subtype) == 0)); } - else if (istext && b->charset) - cd = mutt_ch_iconv_open(Charset, b->charset, MUTT_ICONV_HOOK_FROM); + else + b = a; - fseeko(s->fpin, b->offset, SEEK_SET); - switch (b->encoding) + a = b; + + /* First, search list of preferred types */ + struct ListNode *np; + STAILQ_FOREACH(np, &AlternativeOrderList, entries) { - case ENCQUOTEDPRINTABLE: - decode_quoted(s, b->length, - istext || (((WithCrypto & APPLICATION_PGP) != 0) && - mutt_is_application_pgp(b)), - cd); - break; - case ENCBASE64: - mutt_decode_base64(s, b->length, - istext || (((WithCrypto & APPLICATION_PGP) != 0) && - mutt_is_application_pgp(b)), - cd); - break; - case ENCUUENCODED: - decode_uuencoded(s, b->length, - istext || (((WithCrypto & APPLICATION_PGP) != 0) && - mutt_is_application_pgp(b)), - cd); + int btlen; /* length of basetype */ + bool wild; /* do we have a wildcard to match all subtypes? */ + + char *c = strchr(np->data, '/'); + if (c) + { + wild = ((c[1] == '*') && (c[2] == 0)); + btlen = c - np->data; + } + else + { + wild = true; + btlen = mutt_str_strlen(np->data); + } + + if (a->parts) + b = a->parts; + else + b = a; + while (b) + { + const char *bt = TYPE(b); + if ((mutt_str_strncasecmp(bt, np->data, btlen) == 0) && (bt[btlen] == 0)) + { + /* the basetype matches */ + if (wild || (mutt_str_strcasecmp(np->data + btlen + 1, b->subtype) == 0)) + { + choice = b; + } + } + b = b->next; + } + + if (choice) break; - default: - decode_xbit(s, b->length, - istext || (((WithCrypto & APPLICATION_PGP) != 0) && - mutt_is_application_pgp(b)), - cd); + } + + /* Next, look for an autoviewable type */ + if (!choice) + { + if (a->parts) + b = a->parts; + else + b = a; + while (b) + { + if (is_autoview(b)) + choice = b; + b = b->next; + } + } + + /* Then, look for a text entry */ + if (!choice) + { + if (a->parts) + b = a->parts; + else + b = a; + int type = 0; + while (b) + { + if (b->type == TYPETEXT) + { + if ((mutt_str_strcasecmp("plain", b->subtype) == 0) && (type <= TXTPLAIN)) + { + choice = b; + type = TXTPLAIN; + } + else if ((mutt_str_strcasecmp("enriched", b->subtype) == 0) && (type <= TXTENRICHED)) + { + choice = b; + type = TXTENRICHED; + } + else if ((mutt_str_strcasecmp("html", b->subtype) == 0) && (type <= TXTHTML)) + { + choice = b; + type = TXTHTML; + } + } + b = b->next; + } + } + + /* Finally, look for other possibilities */ + if (!choice) + { + if (a->parts) + b = a->parts; + else + b = a; + while (b) + { + if (mutt_can_decode(b)) + choice = b; + b = b->next; + } + } + + if (choice) + { + if (s->flags & MUTT_DISPLAY && !Weed) + { + fseeko(s->fpin, choice->hdr_offset, SEEK_SET); + mutt_file_copy_bytes(s->fpin, s->fpout, choice->offset - choice->hdr_offset); + } + + if (mutt_str_strcmp("info", ShowMultipartAlternative) == 0) + { + print_part_line(s, choice, 0); + } + mutt_body_handler(choice, s); + + if (mutt_str_strcmp("info", ShowMultipartAlternative) == 0) + { + if (a->parts) + b = a->parts; + else + b = a; + int count = 0; + while (b) + { + if (choice != b) + { + count += 1; + if (count == 1) + state_putc('\n', s); + + print_part_line(s, b, count); + } + b = b->next; + } + } + } + else if (s->flags & MUTT_DISPLAY) + { + /* didn't find anything that we could display! */ + state_mark_attach(s); + state_puts(_("[-- Error: Could not display any parts of " + "Multipart/Alternative! --]\n"), + s); + rc = -1; + } + + if (mustfree) + mutt_body_free(&a); + + return rc; +} + +/** + * multilingual_handler - Handler for multi-lingual emails + * @param a Body of the email + * @param s State of text being processed + * @retval 0 Always + */ +static int multilingual_handler(struct Body *a, struct State *s) +{ + struct Body *choice = NULL; + struct Body *b = NULL; + bool mustfree = false; + int rc = 0; + struct Body *first_part = NULL; + struct Body *zxx_part = NULL; + char *lang = NULL; + + mutt_debug(2, "RFC8255 >> entering in handler multilingual handler\n"); + if ((a->encoding == ENCBASE64) || (a->encoding == ENCQUOTEDPRINTABLE) || + (a->encoding == ENCUUENCODED)) + { + struct stat st; + mustfree = true; + fstat(fileno(s->fpin), &st); + b = mutt_body_new(); + b->length = (long) st.st_size; + b->parts = mutt_parse_multipart( + s->fpin, mutt_param_get(&a->parameter, "boundary"), (long) st.st_size, + (mutt_str_strcasecmp("digest", a->subtype) == 0)); + } + else + b = a; + + a = b; + + if (a->parts) + b = a->parts; + else + b = a; + + char *preferred_languages = NULL; + if (PreferredLanguages) + { + mutt_debug(2, "RFC8255 >> preferred_languages set in config to '%s'\n", PreferredLanguages); + preferred_languages = mutt_str_strdup(PreferredLanguages); + lang = strtok(preferred_languages, ","); + } + + while (lang) + { + while (b) + { + if (mutt_can_decode(b)) + { + if (!first_part) + first_part = b; + + if (b->language && (mutt_str_strcmp("zxx", b->language) == 0)) + zxx_part = b; + + mutt_debug(2, "RFC8255 >> comparing configuration preferred_language='%s' to mail part content-language='%s'\n", + lang, b->language); + if (lang && b->language && (mutt_str_strcmp(lang, b->language) == 0)) + { + mutt_debug(2, "RFC8255 >> preferred_language='%s' matches content-language='%s' >> part selected to be displayed\n", + lang, b->language); + choice = b; + break; + } + } + + b = b->next; + } + + if (choice) break; + + lang = strtok(NULL, ","); + + if (a->parts) + b = a->parts; + else + b = a; + } + + if (choice) + mutt_body_handler(choice, s); + else + { + if (zxx_part) + mutt_body_handler(zxx_part, s); + else + mutt_body_handler(first_part, s); } - if (cd != (iconv_t)(-1)) - iconv_close(cd); + if (mustfree) + mutt_body_free(&a); + + FREE(&preferred_languages); + return rc; } /** - * text_plain_handler - Handler for plain text - * @param b Body of email (UNUSED) - * @param s State to work with - * @retval 0 Always - * - * When generating format=flowed ($text_flowed is set) from format=fixed, strip - * all trailing spaces to improve interoperability; if $text_flowed is unset, - * simply verbatim copy input. + * multipart_handler - Handler for multipart emails + * @param a Body of the email + * @param s State of text being processed + * @retval 0 Success + * @retval -1 Error */ -static int text_plain_handler(struct Body *b, struct State *s) +static int multipart_handler(struct Body *a, struct State *s) { - char *buf = NULL; - size_t l = 0, sz = 0; + struct Body *b = NULL, *p = NULL; + struct stat st; + int count; + int rc = 0; - while ((buf = mutt_file_read_line(buf, &sz, s->fpin, NULL, 0))) + if ((a->encoding == ENCBASE64) || (a->encoding == ENCQUOTEDPRINTABLE) || + (a->encoding == ENCUUENCODED)) { - if ((mutt_str_strcmp(buf, "-- ") != 0) && TextFlowed) + fstat(fileno(s->fpin), &st); + b = mutt_body_new(); + b->length = (long) st.st_size; + b->parts = mutt_parse_multipart( + s->fpin, mutt_param_get(&a->parameter, "boundary"), (long) st.st_size, + (mutt_str_strcasecmp("digest", a->subtype) == 0)); + } + else + b = a; + + for (p = b->parts, count = 1; p; p = p->next, count++) + { + if (s->flags & MUTT_DISPLAY) { - l = mutt_str_strlen(buf); - while (l > 0 && buf[l - 1] == ' ') - buf[--l] = '\0'; + state_mark_attach(s); + if (p->description || p->filename || p->form_name) + { + /* L10N: %s is the attachment description, filename or form_name. */ + state_printf(s, _("[-- Attachment #%d: %s --]\n"), count, + p->description ? p->description : + p->filename ? p->filename : p->form_name); + } + else + state_printf(s, _("[-- Attachment #%d --]\n"), count); + print_part_line(s, p, 0); + if (!Weed) + { + fseeko(s->fpin, p->hdr_offset, SEEK_SET); + mutt_file_copy_bytes(s->fpin, s->fpout, p->offset - p->hdr_offset); + } + else + state_putc('\n', s); } - if (s->prefix) - state_puts(s->prefix, s); - state_puts(buf, s); + + rc = mutt_body_handler(p, s); state_putc('\n', s); + + if (rc != 0) + { + mutt_error(_("One or more parts of this message could not be displayed")); + mutt_debug(1, "Failed on attachment #%d, type %s/%s.\n", count, TYPE(p), + NONULL(p->subtype)); + } + + if ((s->flags & MUTT_REPLYING) && IncludeOnlyfirst && (s->flags & MUTT_FIRSTDONE)) + { + break; + } } - FREE(&buf); - return 0; + if ((a->encoding == ENCBASE64) || (a->encoding == ENCQUOTEDPRINTABLE) || + (a->encoding == ENCUUENCODED)) + mutt_body_free(&b); + + /* make failure of a single part non-fatal */ + if (rc < 0) + rc = 1; + return rc; } /** @@ -1590,6 +1435,70 @@ static int malformed_pgp_encrypted_handler(struct Body *b, struct State *s) return rc; } +/** + * mutt_decode_base64 - Decode base64-encoded text + * @param s State to work with + * @param len Length of text to decode + * @param istext Mime part is plain text + * @param cd Iconv conversion descriptor + */ +void mutt_decode_base64(struct State *s, size_t len, bool istext, iconv_t cd) +{ + char buf[5]; + int ch, i; + char bufi[BUFI_SIZE]; + size_t l = 0; + + buf[4] = '\0'; + + if (istext) + state_set_prefix(s); + + while (len > 0) + { + for (i = 0; (i < 4) && (len > 0); len--) + { + ch = fgetc(s->fpin); + if (ch == EOF) + break; + if ((ch >= 0) && (ch < 128) && (base64val(ch) != -1 || ch == '=')) + buf[i++] = ch; + } + if (i != 4) + { + /* "i" may be zero if there is trailing whitespace, which is not an error */ + if (i != 0) + mutt_debug(2, "didn't get a multiple of 4 chars.\n"); + break; + } + + const int c1 = base64val(buf[0]); + const int c2 = base64val(buf[1]); + ch = (c1 << 2) | (c2 >> 4); + bufi[l++] = ch; + + if (buf[2] == '=') + break; + const int c3 = base64val(buf[2]); + ch = ((c2 & 0xf) << 4) | (c3 >> 2); + bufi[l++] = ch; + + if (buf[3] == '=') + break; + const int c4 = base64val(buf[3]); + ch = ((c3 & 0x3) << 6) | c4; + bufi[l++] = ch; + + if ((l + 8) >= sizeof(bufi)) + convert_to_state(cd, bufi, &l, s); + } + + convert_to_state(cd, bufi, &l, s); + convert_to_state(cd, 0, 0, s); + + state_reset_prefix(s); +} + /** * mutt_body_handler - Handler for the Body of an email * @param b Body of the email @@ -1774,3 +1683,99 @@ int mutt_body_handler(struct Body *b, struct State *s) return rc; } + +/** + * mutt_can_decode - Will decoding the attachment produce any output + * @param a Body of email to test + * @retval true Decoding the attachment will produce output + */ +bool mutt_can_decode(struct Body *a) +{ + if (is_autoview(a)) + return true; + else if (a->type == TYPETEXT) + return true; + else if (a->type == TYPEMESSAGE) + return true; + else if (a->type == TYPEMULTIPART) + { + if (WithCrypto) + { + if ((mutt_str_strcasecmp(a->subtype, "signed") == 0) || + (mutt_str_strcasecmp(a->subtype, "encrypted") == 0)) + { + return true; + } + } + + for (struct Body *b = a->parts; b; b = b->next) + { + if (mutt_can_decode(b)) + return true; + } + } + else if ((WithCrypto != 0) && a->type == TYPEAPPLICATION) + { + if (((WithCrypto & APPLICATION_PGP) != 0) && mutt_is_application_pgp(a)) + return true; + if (((WithCrypto & APPLICATION_SMIME) != 0) && mutt_is_application_smime(a)) + return true; + } + + return false; +} + +/** + * mutt_decode_attachment - Decode an email's attachment + * @param b Body of the email + * @param s State of text being processed + */ +void mutt_decode_attachment(struct Body *b, struct State *s) +{ + int istext = mutt_is_text_part(b); + iconv_t cd = (iconv_t)(-1); + + if (istext && s->flags & MUTT_CHARCONV) + { + char *charset = mutt_param_get(&b->parameter, "charset"); + if (!charset && AssumedCharset && *AssumedCharset) + charset = mutt_ch_get_default_charset(); + if (charset && Charset) + cd = mutt_ch_iconv_open(Charset, charset, MUTT_ICONV_HOOK_FROM); + } + else if (istext && b->charset) + cd = mutt_ch_iconv_open(Charset, b->charset, MUTT_ICONV_HOOK_FROM); + + fseeko(s->fpin, b->offset, SEEK_SET); + switch (b->encoding) + { + case ENCQUOTEDPRINTABLE: + decode_quoted(s, b->length, + istext || (((WithCrypto & APPLICATION_PGP) != 0) && + mutt_is_application_pgp(b)), + cd); + break; + case ENCBASE64: + mutt_decode_base64(s, b->length, + istext || (((WithCrypto & APPLICATION_PGP) != 0) && + mutt_is_application_pgp(b)), + cd); + break; + case ENCUUENCODED: + decode_uuencoded(s, b->length, + istext || (((WithCrypto & APPLICATION_PGP) != 0) && + mutt_is_application_pgp(b)), + cd); + break; + default: + decode_xbit(s, b->length, + istext || (((WithCrypto & APPLICATION_PGP) != 0) && + mutt_is_application_pgp(b)), + cd); + break; + } + + if (cd != (iconv_t)(-1)) + iconv_close(cd); +} + diff --git a/handler.h b/handler.h new file mode 100644 index 000000000..e452c1521 --- /dev/null +++ b/handler.h @@ -0,0 +1,38 @@ +/** + * @file + * Decide how to display email content + * + * @authors + * Copyright (C) 1996-2000,2002,2010,2013 Michael R. Elkins + * + * @copyright + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef _MUTT_HANDLER_H +#define _MUTT_HANDLER_H + +#include +#include +#include + +struct Body; +struct State; + +int mutt_body_handler(struct Body *b, struct State *s); +bool mutt_can_decode(struct Body *a); +void mutt_decode_attachment(struct Body *b, struct State *s); +void mutt_decode_base64(struct State *s, size_t len, bool istext, iconv_t cd); + +#endif /* _MUTT_HANDLER_H */ diff --git a/ncrypt/crypt.c b/ncrypt/crypt.c index afdb41bf2..57bae2c97 100644 --- a/ncrypt/crypt.c +++ b/ncrypt/crypt.c @@ -48,6 +48,7 @@ #include "cryptglue.h" #include "envelope.h" #include "globals.h" +#include "handler.h" #include "header.h" #include "mutt_curses.h" #include "ncrypt.h" diff --git a/ncrypt/crypt_gpgme.c b/ncrypt/crypt_gpgme.c index 7dedb1620..b86530835 100644 --- a/ncrypt/crypt_gpgme.c +++ b/ncrypt/crypt_gpgme.c @@ -56,6 +56,7 @@ #include "envelope.h" #include "format_flags.h" #include "globals.h" +#include "handler.h" #include "header.h" #include "keymap.h" #include "mutt_curses.h" diff --git a/ncrypt/pgp.c b/ncrypt/pgp.c index 55c4b862e..0d051e13e 100644 --- a/ncrypt/pgp.c +++ b/ncrypt/pgp.c @@ -49,6 +49,7 @@ #include "cryptglue.h" #include "filter.h" #include "globals.h" +#include "handler.h" #include "header.h" #include "mutt_curses.h" #include "ncrypt.h" diff --git a/ncrypt/pgpmicalg.c b/ncrypt/pgpmicalg.c index 2a8e91af7..875c9f9e1 100644 --- a/ncrypt/pgpmicalg.c +++ b/ncrypt/pgpmicalg.c @@ -35,6 +35,7 @@ #include #include "mutt/mutt.h" #include "mutt.h" +#include "handler.h" #include "pgppacket.h" #include "protos.h" #include "state.h" diff --git a/ncrypt/smime.c b/ncrypt/smime.c index 3f1e77aae..f056d5f32 100644 --- a/ncrypt/smime.c +++ b/ncrypt/smime.c @@ -47,6 +47,7 @@ #include "filter.h" #include "format_flags.h" #include "globals.h" +#include "handler.h" #include "header.h" #include "keymap.h" #include "mutt_curses.h" diff --git a/pattern.c b/pattern.c index e330600e4..86f3f5f4d 100644 --- a/pattern.c +++ b/pattern.c @@ -43,6 +43,7 @@ #include "envelope.h" #include "globals.h" #include "group.h" +#include "handler.h" #include "header.h" #include "mailbox.h" #include "mutt_curses.h" diff --git a/postpone.c b/postpone.c index 8b0cd255f..5d1f2f8c3 100644 --- a/postpone.c +++ b/postpone.c @@ -43,6 +43,7 @@ #include "envelope.h" #include "format_flags.h" #include "globals.h" +#include "handler.h" #include "header.h" #include "keymap.h" #include "mailbox.h" diff --git a/protos.h b/protos.h index 7ed029a60..8c0c5dc70 100644 --- a/protos.h +++ b/protos.h @@ -144,7 +144,6 @@ void mutt_add_to_reference_headers(struct Envelope *env, struct Envelope *curenv void mutt_adv_mktemp(char *s, size_t l); void mutt_alias_menu(char *buf, size_t buflen, struct AliasList *aliases); void mutt_sig_allow_interrupt(int disposition); -int mutt_body_handler(struct Body *b, struct State *s); int mutt_bounce_message(FILE *fp, struct Header *h, struct Address *to); void mutt_buffy(char *s, size_t slen); int mutt_buffy_list(void); @@ -152,8 +151,6 @@ int mutt_count_body_parts(struct Context *ctx, struct Header *hdr); void mutt_check_rescore(struct Context *ctx); void mutt_clear_error(void); void mutt_clear_pager_position(void); -void mutt_decode_attachment(struct Body *b, struct State *s); -void mutt_decode_base64(struct State *s, size_t len, bool istext, iconv_t cd); void mutt_default_save(char *path, size_t pathlen, struct Header *hdr); void mutt_display_address(struct Envelope *env); void mutt_draw_statusline(int cols, const char *buf, int buflen); @@ -232,7 +229,6 @@ int mutt_any_key_to_continue(const char *s); int mutt_buffy_check(bool force); int mutt_buffy_notify(void); int mutt_builtin_editor(const char *path, struct Header *msg, struct Header *cur); -bool mutt_can_decode(struct Body *a); int mutt_change_flag(struct Header *h, int bf); int mutt_check_encoding(const char *c); diff --git a/recvattach.c b/recvattach.c index ed775c093..b2f284c74 100644 --- a/recvattach.c +++ b/recvattach.c @@ -37,6 +37,7 @@ #include "filter.h" #include "format_flags.h" #include "globals.h" +#include "handler.h" #include "header.h" #include "keymap.h" #include "mailbox.h" diff --git a/recvcmd.c b/recvcmd.c index 52102aaee..45cfc6d7a 100644 --- a/recvcmd.c +++ b/recvcmd.c @@ -33,6 +33,7 @@ #include "copy.h" #include "envelope.h" #include "globals.h" +#include "handler.h" #include "header.h" #include "mutt_curses.h" #include "mutt_window.h" diff --git a/remailer.c b/remailer.c index 3795f25fd..9e722abfc 100644 --- a/remailer.c +++ b/remailer.c @@ -34,6 +34,7 @@ #include "filter.h" #include "format_flags.h" #include "globals.h" +#include "handler.h" #include "header.h" #include "keymap.h" #include "mutt_curses.h" diff --git a/sendlib.c b/sendlib.c index b6ccdbddd..6f3ebdd7f 100644 --- a/sendlib.c +++ b/sendlib.c @@ -46,6 +46,7 @@ #include "filter.h" #include "format_flags.h" #include "globals.h" +#include "handler.h" #include "header.h" #include "mailbox.h" #include "mutt_curses.h"