#include <unistd.h>
#include "mutt/mutt.h"
#include "mutt.h"
+#include "handler.h"
#include "body.h"
#include "copy.h"
#include "enriched.h"
#include "rfc1524.h"
#include "rfc3676.h"
#include "state.h"
+#ifdef ENABLE_NLS
+#include <libintl.h>
+#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);
/**
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
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;
}
/**
}
/**
- * 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);
}
/**
- * 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;
}
/**
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
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);
+}
+