2 * Copyright notice from original mutt:
3 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
5 * This file is part of mutt-ng, see http://www.muttng.org/.
6 * It's licensed under the GNU General Public License,
7 * please see the file GPL in the top level source directory.
21 #include <lib-lib/mem.h>
22 #include <lib-lib/ascii.h>
23 #include <lib-lib/str.h>
24 #include <lib-lib/macros.h>
27 #include "recvattach.h"
29 #include "mutt_curses.h"
36 #include "mutt_crypt.h"
41 #include "lib/debug.h"
43 typedef int handler_f (BODY *, STATE *);
44 typedef handler_f *handler_t;
46 int Index_hex[128] = {
47 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
48 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
49 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
50 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
51 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
52 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
53 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
54 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
58 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
59 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
60 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
61 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
62 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
63 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
64 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
65 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1
68 void mutt_decode_xbit (STATE * s, long len, int istext, iconv_t cd)
77 while ((c = fgetc (s->fpin)) != EOF && len--) {
78 if (c == '\r' && len) {
79 if ((ch = fgetc (s->fpin)) == '\n') {
88 if (l == sizeof (bufi))
89 mutt_convert_to_state (cd, bufi, &l, s);
92 mutt_convert_to_state (cd, bufi, &l, s);
93 mutt_convert_to_state (cd, 0, 0, s);
95 state_reset_prefix (s);
98 mutt_copy_bytes (s->fpin, s->fpout, len);
101 static int qp_decode_triple (char *s, char *d)
103 /* soft line break */
104 if (*s == '=' && !(*(s + 1)))
107 /* quoted-printable triple */
109 isxdigit ((unsigned char) *(s + 1)) &&
110 isxdigit ((unsigned char) *(s + 2))) {
111 *d = (hexval (*(s + 1)) << 4) | hexval (*(s + 2));
119 static void qp_decode_line (char *dest, char *src, size_t * l, int last)
127 /* decode the line */
129 for (d = dest, s = src; *s;) {
130 switch ((kind = qp_decode_triple (s, &c))) {
134 break; /* qp triple */
137 break; /* single character */
141 break; /* soft line break */
145 if (!soft && last == '\n')
153 * Decode an attachment encoded with quoted-printable.
155 * Why doesn't this overflow any buffers? First, it's guaranteed
156 * that the length of a line grows when you _en_-code it to
157 * quoted-printable. That means that we always can store the
158 * result in a buffer of at most the _same_ size.
160 * Now, we don't special-case if the line we read with fgets()
161 * isn't terminated. We don't care about this, since STRING > 78,
162 * so corrupted input will just be corrupted a bit more. That
163 * implies that STRING+1 bytes are always sufficient to store the
164 * result of qp_decode_line.
166 * Finally, at soft line breaks, some part of a multibyte character
167 * may have been left over by mutt_convert_to_state(). This shouldn't
168 * be more than 6 characters, so STRING + 7 should be sufficient
169 * memory to store the decoded data.
171 * Just to make sure that I didn't make some off-by-one error
172 * above, we just use STRING*2 for the target buffer's size.
176 void mutt_decode_quoted (STATE * s, long len, int istext, iconv_t cd)
179 char decline[2 * STRING];
181 size_t linelen; /* number of input bytes in `line' */
184 int last; /* store the last character in the input line */
187 state_set_prefix (s);
193 * It's ok to use a fixed size buffer for input, even if the line turns
194 * out to be longer than this. Just process the line in chunks. This
195 * really shouldn't happen according the MIME spec, since Q-P encoded
196 * lines are at most 76 characters, but we should be liberal about what
199 if (fgets (line, MIN ((ssize_t) sizeof (line), len + 1), s->fpin) == NULL)
202 linelen = m_strlen(line);
206 * inspect the last character we read so we can tell if we got the
209 last = linelen ? line[linelen - 1] : 0;
211 /* chop trailing whitespace if we got the full line */
213 while (linelen > 0 && ISSPACE (line[linelen - 1]))
218 /* decode and do character set conversion */
219 qp_decode_line (decline + l, line, &l3, last);
221 mutt_convert_to_state (cd, decline, &l, s);
224 mutt_convert_to_state (cd, 0, 0, s);
225 state_reset_prefix (s);
228 void mutt_decode_base64 (STATE * s, long len, int istext, iconv_t cd)
231 int c1, c2, c3, c4, ch, cr = 0, i;
232 char bufi[BUFI_SIZE];
238 state_set_prefix (s);
241 for (i = 0; i < 4 && len > 0; len--) {
242 if ((ch = fgetc (s->fpin)) == EOF)
244 if (ch >= 0 && ch < 128 && (base64val (ch) != -1 || ch == '='))
248 debug_print (2, ("didn't get a multiple of 4 chars.\n"));
252 c1 = base64val (buf[0]);
253 c2 = base64val (buf[1]);
254 ch = (c1 << 2) | (c2 >> 4);
256 if (cr && ch != '\n')
261 if (istext && ch == '\r')
268 c3 = base64val (buf[2]);
269 ch = ((c2 & 0xf) << 4) | (c3 >> 2);
271 if (cr && ch != '\n')
276 if (istext && ch == '\r')
283 c4 = base64val (buf[3]);
284 ch = ((c3 & 0x3) << 6) | c4;
286 if (cr && ch != '\n')
290 if (istext && ch == '\r')
295 if (l + 8 >= sizeof (bufi))
296 mutt_convert_to_state (cd, bufi, &l, s);
302 mutt_convert_to_state (cd, bufi, &l, s);
303 mutt_convert_to_state (cd, 0, 0, s);
305 state_reset_prefix (s);
308 unsigned char decode_byte (char ch)
315 void mutt_decode_uuencoded (STATE * s, long len, int istext, iconv_t cd)
317 char tmps[SHORT_STRING];
318 char linelen, c, l, out;
320 char bufi[BUFI_SIZE];
324 state_set_prefix (s);
327 if ((fgets (tmps, sizeof (tmps), s->fpin)) == NULL)
329 len -= m_strlen(tmps);
330 if ((!str_ncmp (tmps, "begin", 5)) && ISSPACE (tmps[5]))
334 if ((fgets (tmps, sizeof (tmps), s->fpin)) == NULL)
336 len -= m_strlen(tmps);
337 if (!str_ncmp (tmps, "end", 3))
340 linelen = decode_byte (*pt);
342 for (c = 0; c < linelen;) {
343 for (l = 2; l <= 6; l += 2) {
344 out = decode_byte (*pt) << l;
346 out |= (decode_byte (*pt) >> (6 - l));
352 mutt_convert_to_state (cd, bufi, &k, s);
357 mutt_convert_to_state (cd, bufi, &k, s);
358 mutt_convert_to_state (cd, 0, 0, s);
360 state_reset_prefix (s);
363 /* ----------------------------------------------------------------------------
364 * A (not so) minimal implementation of RFC1563.
367 #define IndentSize (4)
369 enum { RICH_PARAM = 0, RICH_BOLD, RICH_UNDERLINE, RICH_ITALIC, RICH_NOFILL,
370 RICH_INDENT, RICH_INDENT_RIGHT, RICH_EXCERPT, RICH_CENTER, RICH_FLUSHLEFT,
371 RICH_FLUSHRIGHT, RICH_COLOR, RICH_LAST_TAG
375 const char *tag_name;
379 "param", RICH_PARAM}, {
380 "bold", RICH_BOLD}, {
381 "italic", RICH_ITALIC}, {
382 "underline", RICH_UNDERLINE}, {
383 "nofill", RICH_NOFILL}, {
384 "excerpt", RICH_EXCERPT}, {
385 "indent", RICH_INDENT}, {
386 "indentright", RICH_INDENT_RIGHT}, {
387 "center", RICH_CENTER}, {
388 "flushleft", RICH_FLUSHLEFT}, {
389 "flushright", RICH_FLUSHRIGHT}, {
390 "flushboth", RICH_FLUSHLEFT}, {
391 "color", RICH_COLOR}, {
392 "x-color", RICH_COLOR}, {
396 struct enriched_state {
409 int tag_level[RICH_LAST_TAG];
414 static void enriched_wrap (struct enriched_state *stte)
419 if (stte->line_len) {
420 if (stte->tag_level[RICH_CENTER] || stte->tag_level[RICH_FLUSHRIGHT]) {
421 /* Strip trailing white space */
422 size_t y = stte->line_used - 1;
424 while (y && ISSPACE (stte->line[y])) {
425 stte->line[y] = '\0';
430 if (stte->tag_level[RICH_CENTER]) {
431 /* Strip leading whitespace */
434 while (stte->line[y] && ISSPACE (stte->line[y]))
439 for (z = y; z <= stte->line_used; z++) {
440 stte->line[z - y] = stte->line[z];
444 stte->line_used -= y;
449 extra = stte->WrapMargin - stte->line_len - stte->indent_len -
450 (stte->tag_level[RICH_INDENT_RIGHT] * IndentSize);
452 if (stte->tag_level[RICH_CENTER]) {
455 state_putc (' ', stte->s);
459 else if (stte->tag_level[RICH_FLUSHRIGHT]) {
462 state_putc (' ', stte->s);
467 state_puts (stte->line, stte->s);
470 state_putc ('\n', stte->s);
471 stte->line[0] = '\0';
474 stte->indent_len = 0;
475 if (stte->s->prefix) {
476 state_puts (stte->s->prefix, stte->s);
477 stte->indent_len += m_strlen(stte->s->prefix);
480 if (stte->tag_level[RICH_EXCERPT]) {
481 x = stte->tag_level[RICH_EXCERPT];
483 if (stte->s->prefix) {
484 state_puts (stte->s->prefix, stte->s);
485 stte->indent_len += m_strlen(stte->s->prefix);
488 state_puts ("> ", stte->s);
489 stte->indent_len += m_strlen("> ");
495 stte->indent_len = 0;
496 if (stte->tag_level[RICH_INDENT]) {
497 x = stte->tag_level[RICH_INDENT] * IndentSize;
498 stte->indent_len += x;
500 state_putc (' ', stte->s);
506 static void enriched_flush (struct enriched_state *stte, int wrap)
508 if (!stte->tag_level[RICH_NOFILL] && (stte->line_len + stte->word_len >
510 (stte->tag_level[RICH_INDENT_RIGHT] *
511 IndentSize) - stte->indent_len)))
512 enriched_wrap (stte);
514 if (stte->buff_used) {
515 stte->buffer[stte->buff_used] = '\0';
516 stte->line_used += stte->buff_used;
517 if (stte->line_used > stte->line_max) {
518 stte->line_max = stte->line_used;
519 p_realloc(&stte->line, stte->line_max + 1);
521 strcat (stte->line, stte->buffer); /* __STRCAT_CHECKED__ */
522 stte->line_len += stte->word_len;
527 enriched_wrap (stte);
531 static void enriched_putc (int c, struct enriched_state *stte)
533 if (stte->tag_level[RICH_PARAM]) {
534 if (stte->tag_level[RICH_COLOR]) {
535 if (stte->param_used + 1 >= stte->param_len)
536 p_realloc(&stte->param, (stte->param_len += STRING));
538 stte->param[stte->param_used++] = c;
540 return; /* nothing to do */
543 /* see if more space is needed (plus extra for possible rich characters) */
544 if (stte->buff_len < stte->buff_used + 3) {
545 stte->buff_len += LONG_STRING;
546 p_realloc(&stte->buffer, stte->buff_len + 1);
549 if ((!stte->tag_level[RICH_NOFILL] && ISSPACE (c)) || c == '\0') {
551 stte->word_len += 8 - (stte->line_len + stte->word_len) % 8;
555 stte->buffer[stte->buff_used++] = c;
556 enriched_flush (stte, 0);
559 if (stte->s->flags & M_DISPLAY) {
560 if (stte->tag_level[RICH_BOLD]) {
561 stte->buffer[stte->buff_used++] = c;
562 stte->buffer[stte->buff_used++] = '\010';
563 stte->buffer[stte->buff_used++] = c;
565 else if (stte->tag_level[RICH_UNDERLINE]) {
567 stte->buffer[stte->buff_used++] = '_';
568 stte->buffer[stte->buff_used++] = '\010';
569 stte->buffer[stte->buff_used++] = c;
571 else if (stte->tag_level[RICH_ITALIC]) {
572 stte->buffer[stte->buff_used++] = c;
573 stte->buffer[stte->buff_used++] = '\010';
574 stte->buffer[stte->buff_used++] = '_';
577 stte->buffer[stte->buff_used++] = c;
581 stte->buffer[stte->buff_used++] = c;
587 static void enriched_puts (const char *s, struct enriched_state *stte)
591 if (stte->buff_len < stte->buff_used + m_strlen(s)) {
592 stte->buff_len += LONG_STRING;
593 p_realloc(&stte->buffer, stte->buff_len + 1);
598 stte->buffer[stte->buff_used++] = *p++;
602 static void enriched_set_flags (const char *tag, struct enriched_state *stte)
604 const char *tagptr = tag;
610 for (i = 0, j = -1; EnrichedTags[i].tag_name; i++)
611 if (ascii_strcasecmp (EnrichedTags[i].tag_name, tagptr) == 0) {
612 j = EnrichedTags[i].index;
617 if (j == RICH_CENTER || j == RICH_FLUSHLEFT || j == RICH_FLUSHRIGHT)
618 enriched_flush (stte, 1);
621 if (stte->tag_level[j]) /* make sure not to go negative */
622 stte->tag_level[j]--;
623 if ((stte->s->flags & M_DISPLAY) && j == RICH_PARAM
624 && stte->tag_level[RICH_COLOR]) {
625 stte->param[stte->param_used] = '\0';
626 if (!ascii_strcasecmp (stte->param, "black")) {
627 enriched_puts ("\033[30m", stte);
629 else if (!ascii_strcasecmp (stte->param, "red")) {
630 enriched_puts ("\033[31m", stte);
632 else if (!ascii_strcasecmp (stte->param, "green")) {
633 enriched_puts ("\033[32m", stte);
635 else if (!ascii_strcasecmp (stte->param, "yellow")) {
636 enriched_puts ("\033[33m", stte);
638 else if (!ascii_strcasecmp (stte->param, "blue")) {
639 enriched_puts ("\033[34m", stte);
641 else if (!ascii_strcasecmp (stte->param, "magenta")) {
642 enriched_puts ("\033[35m", stte);
644 else if (!ascii_strcasecmp (stte->param, "cyan")) {
645 enriched_puts ("\033[36m", stte);
647 else if (!ascii_strcasecmp (stte->param, "white")) {
648 enriched_puts ("\033[37m", stte);
651 if ((stte->s->flags & M_DISPLAY) && j == RICH_COLOR) {
652 enriched_puts ("\033[0m", stte);
655 /* flush parameter buffer when closing the tag */
656 if (j == RICH_PARAM) {
657 stte->param_used = 0;
658 stte->param[0] = '\0';
662 stte->tag_level[j]++;
664 if (j == RICH_EXCERPT)
665 enriched_flush (stte, 1);
669 int text_enriched_handler (BODY * a, STATE * s)
672 TEXT, LANGLE, TAG, BOGUS_TAG, NEWLINE, ST_EOF, DONE
675 long bytes = a->length;
676 struct enriched_state stte;
679 char tag[LONG_STRING + 1];
684 ((s->flags & M_DISPLAY) ? (COLS - 4) : ((COLS - 4) <
685 72) ? (COLS - 4) : 72);
686 stte.line_max = stte.WrapMargin * 4;
687 stte.line = p_new(char, stte.line_max + 1);
688 stte.param = p_new(char, STRING);
690 stte.param_len = STRING;
694 state_puts (s->prefix, s);
695 stte.indent_len += m_strlen(s->prefix);
698 while (state != DONE) {
699 if (state != ST_EOF) {
700 if (!bytes || (c = fgetc (s->fpin)) == EOF)
714 if (stte.tag_level[RICH_NOFILL]) {
715 enriched_flush (&stte, 1);
718 enriched_putc (' ', &stte);
724 enriched_putc (c, &stte);
730 enriched_putc (c, &stte);
738 /* Yes, fall through (it wasn't a <<, so this char is first in TAG) */
742 enriched_set_flags (tag, &stte);
745 else if (tag_len < LONG_STRING) /* ignore overly long tags */
758 enriched_flush (&stte, 1);
767 enriched_putc ('\0', &stte);
768 enriched_flush (&stte, 1);
772 case DONE: /* not reached, but gcc complains if this is absent */
777 state_putc ('\n', s); /* add a final newline */
779 p_delete(&(stte.buffer));
780 p_delete(&(stte.line));
781 p_delete(&(stte.param));
788 #define TXTENRICHED 3
790 static int alternative_handler (BODY * a, STATE * s)
800 if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE ||
801 a->encoding == ENCUUENCODED) {
805 fstat (fileno (s->fpin), &st);
806 b = mutt_new_body ();
807 b->length = (long) st.st_size;
808 b->parts = mutt_parse_multipart (s->fpin,
809 mutt_get_parameter ("boundary",
812 ascii_strcasecmp ("digest",
820 /* First, search list of prefered types */
821 t = AlternativeOrderList;
822 while (t && !choice) {
824 int btlen; /* length of basetype */
825 int wild; /* do we have a wildcard to match all subtypes? */
827 c = strchr (t->data, '/');
829 wild = (c[1] == '*' && c[2] == 0);
834 btlen = m_strlen(t->data);
842 const char *bt = TYPE (b);
844 if (!ascii_strncasecmp (bt, t->data, btlen) && bt[btlen] == 0) {
845 /* the basetype matches */
846 if (wild || !ascii_strcasecmp (t->data + btlen + 1, b->subtype)) {
855 /* Next, look for an autoviewable type */
862 snprintf (buf, sizeof (buf), "%s/%s", TYPE (b), b->subtype);
863 if (mutt_is_autoview (b, buf)) {
864 rfc1524_entry *entry = rfc1524_new_entry ();
866 if (rfc1524_mailcap_lookup (b, buf, entry, M_AUTOVIEW)) {
869 rfc1524_free_entry (&entry);
875 /* Then, look for a text entry */
882 if (b->type == TYPETEXT) {
883 if (!ascii_strcasecmp ("plain", b->subtype) && type <= TXTPLAIN) {
887 else if (!ascii_strcasecmp ("enriched", b->subtype)
888 && type <= TXTENRICHED) {
892 else if (!ascii_strcasecmp ("html", b->subtype) && type <= TXTHTML) {
901 /* Finally, look for other possibilities */
908 if (mutt_can_decode (b))
915 if (s->flags & M_DISPLAY && !option (OPTWEED)) {
916 fseeko (s->fpin, choice->hdr_offset, 0);
917 mutt_copy_bytes (s->fpin, s->fpout,
918 choice->offset - choice->hdr_offset);
920 mutt_body_handler (choice, s);
922 else if (s->flags & M_DISPLAY) {
923 /* didn't find anything that we could display! */
924 state_mark_attach (s);
925 state_puts (_("[-- Error: Could not display any parts of Multipart/Alternative! --]\n"), s);
935 /* handles message/rfc822 body parts */
936 static int message_handler (BODY * a, STATE * s)
943 off_start = ftello (s->fpin);
944 if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE ||
945 a->encoding == ENCUUENCODED) {
946 fstat (fileno (s->fpin), &st);
947 b = mutt_new_body ();
948 b->length = (long) st.st_size;
949 b->parts = mutt_parse_messageRFC822 (s->fpin, b);
955 mutt_copy_hdr (s->fpin, s->fpout, off_start, b->parts->offset,
956 (((s->flags & M_WEED)
957 || ((s->flags & (M_DISPLAY | M_PRINTING))
958 && option (OPTWEED))) ? (CH_WEED | CH_REORDER) : 0) |
959 (s->prefix ? CH_PREFIX : 0) | CH_DECODE | CH_FROM,
963 state_puts (s->prefix, s);
964 state_putc ('\n', s);
966 rc = mutt_body_handler (b->parts, s);
969 if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE ||
970 a->encoding == ENCUUENCODED)
976 /* returns 1 if decoding the attachment will produce output */
977 int mutt_can_decode (BODY * a)
981 snprintf (type, sizeof (type), "%s/%s", TYPE (a), a->subtype);
982 if (mutt_is_autoview (a, type))
983 return (rfc1524_mailcap_lookup (a, type, NULL, M_AUTOVIEW));
984 else if (a->type == TYPETEXT)
986 else if (a->type == TYPEMESSAGE)
988 else if (a->type == TYPEMULTIPART) {
992 if (ascii_strcasecmp (a->subtype, "signed") == 0 ||
993 ascii_strcasecmp (a->subtype, "encrypted") == 0)
997 for (p = a->parts; p; p = p->next) {
998 if (mutt_can_decode (p))
1003 else if (WithCrypto && a->type == TYPEAPPLICATION) {
1004 if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (a))
1006 if ((WithCrypto & APPLICATION_SMIME) && mutt_is_application_smime (a))
1013 static int multipart_handler (BODY * a, STATE * s)
1021 if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE ||
1022 a->encoding == ENCUUENCODED) {
1023 fstat (fileno (s->fpin), &st);
1024 b = mutt_new_body ();
1025 b->length = (long) st.st_size;
1026 b->parts = mutt_parse_multipart (s->fpin,
1027 mutt_get_parameter ("boundary",
1030 ascii_strcasecmp ("digest",
1036 for (p = b->parts, count = 1; p; p = p->next, count++) {
1037 if (s->flags & M_DISPLAY) {
1038 state_mark_attach (s);
1039 state_printf (s, _("[-- Attachment #%d"), count);
1040 if (p->description || p->filename || p->form_name) {
1041 state_puts (": ", s);
1042 state_puts (p->description ? p->description :
1043 p->filename ? p->filename : p->form_name, s);
1045 state_puts (" --]\n", s);
1047 mutt_pretty_size (length, sizeof (length), p->length);
1049 state_mark_attach (s);
1050 state_printf (s, _("[-- Type: %s/%s, Encoding: %s, Size: %s --]\n"),
1051 TYPE (p), p->subtype, ENCODING (p->encoding), length);
1052 if (!option (OPTWEED)) {
1053 fseeko (s->fpin, p->hdr_offset, 0);
1054 mutt_copy_bytes (s->fpin, s->fpout, p->offset - p->hdr_offset);
1057 state_putc ('\n', s);
1060 if (p->description && mutt_can_decode (p))
1061 state_printf (s, "Content-Description: %s\n", p->description);
1064 state_printf (s, "%s: \n", p->form_name);
1067 rc = mutt_body_handler (p, s);
1068 state_putc ('\n', s);
1069 if (rc || ((s->flags & M_REPLYING)
1070 && (option (OPTINCLUDEONLYFIRST)) && (s->flags & M_FIRSTDONE)))
1074 if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE ||
1075 a->encoding == ENCUUENCODED)
1076 mutt_free_body (&b);
1081 static int autoview_handler (BODY * a, STATE * s)
1083 rfc1524_entry *entry = rfc1524_new_entry ();
1084 char buffer[LONG_STRING];
1086 char command[LONG_STRING];
1087 char tempfile[_POSIX_PATH_MAX] = "";
1096 snprintf (type, sizeof (type), "%s/%s", TYPE (a), a->subtype);
1097 rfc1524_mailcap_lookup (a, type, entry, M_AUTOVIEW);
1099 fname = m_strdup(a->filename);
1100 mutt_sanitize_filename (fname, 1);
1101 rfc1524_expand_filename (entry->nametemplate, fname, tempfile,
1105 if (entry->command) {
1106 strfcpy (command, entry->command, sizeof (command));
1108 /* rfc1524_expand_command returns 0 if the file is required */
1110 rfc1524_expand_command (a, tempfile, type, command, sizeof (command));
1112 if (s->flags & M_DISPLAY) {
1113 state_mark_attach (s);
1114 state_printf (s, _("[-- Autoview using %s --]\n"), command);
1115 mutt_message (_("Invoking autoview command: %s"), command);
1118 if ((fpin = safe_fopen (tempfile, "w+")) == NULL) {
1119 mutt_perror ("fopen");
1120 rfc1524_free_entry (&entry);
1124 mutt_copy_bytes (s->fpin, fpin, a->length);
1127 safe_fclose (&fpin);
1128 thepid = mutt_create_filter (command, NULL, &fpout, &fperr);
1134 thepid = mutt_create_filter_fd (command, NULL, &fpout, &fperr,
1135 fileno (fpin), -1, -1);
1139 mutt_perror (_("Can't create filter"));
1141 if (s->flags & M_DISPLAY) {
1142 state_mark_attach (s);
1143 state_printf (s, _("[-- Can't run %s. --]\n"), command);
1150 while (fgets (buffer, sizeof (buffer), fpout) != NULL) {
1151 state_puts (s->prefix, s);
1152 state_puts (buffer, s);
1154 /* check for data on stderr */
1155 if (fgets (buffer, sizeof (buffer), fperr)) {
1156 if (s->flags & M_DISPLAY) {
1157 state_mark_attach (s);
1158 state_printf (s, _("[-- Autoview stderr of %s --]\n"), command);
1161 state_puts (s->prefix, s);
1162 state_puts (buffer, s);
1163 while (fgets (buffer, sizeof (buffer), fperr) != NULL) {
1164 state_puts (s->prefix, s);
1165 state_puts (buffer, s);
1170 mutt_copy_stream (fpout, s->fpout);
1171 /* Check for stderr messages */
1172 if (fgets (buffer, sizeof (buffer), fperr)) {
1173 if (s->flags & M_DISPLAY) {
1174 state_mark_attach (s);
1175 state_printf (s, _("[-- Autoview stderr of %s --]\n"), command);
1178 state_puts (buffer, s);
1179 mutt_copy_stream (fperr, s->fpout);
1184 safe_fclose (&fpout);
1185 safe_fclose (&fperr);
1187 mutt_wait_filter (thepid);
1189 safe_fclose (&fpin);
1191 mutt_unlink (tempfile);
1193 if (s->flags & M_DISPLAY)
1194 mutt_clear_error ();
1196 rfc1524_free_entry (&entry);
1200 static int external_body_handler (BODY * b, STATE * s)
1202 const char *access_type;
1203 const char *expiration;
1206 access_type = mutt_get_parameter ("access-type", b->parameter);
1208 if (s->flags & M_DISPLAY) {
1209 state_mark_attach (s);
1210 state_puts (_("[-- Error: message/external-body has no access-type parameter --]\n"), s);
1215 expiration = mutt_get_parameter ("expiration", b->parameter);
1217 expire = mutt_parse_date (expiration, NULL);
1221 if (!ascii_strcasecmp (access_type, "x-mutt-deleted")) {
1222 if (s->flags & (M_DISPLAY | M_PRINTING)) {
1224 char pretty_size[10];
1226 state_mark_attach (s);
1227 state_printf (s, _("[-- This %s/%s attachment "),
1228 TYPE (b->parts), b->parts->subtype);
1229 length = mutt_get_parameter ("length", b->parameter);
1231 mutt_pretty_size (pretty_size, sizeof (pretty_size),
1232 strtol (length, NULL, 10));
1233 state_printf (s, _("(size %s bytes) "), pretty_size);
1235 state_puts (_("has been deleted --]\n"), s);
1238 state_mark_attach (s);
1239 state_printf (s, _("[-- on %s --]\n"), expiration);
1241 if (b->parts->filename) {
1242 state_mark_attach (s);
1243 state_printf (s, _("[-- name: %s --]\n"), b->parts->filename);
1246 mutt_copy_hdr (s->fpin, s->fpout, ftello (s->fpin), b->parts->offset,
1247 (option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) |
1251 else if (expiration && expire < time (NULL)) {
1252 if (s->flags & M_DISPLAY) {
1253 state_mark_attach (s);
1254 state_printf (s, _("[-- This %s/%s attachment is not included, --]\n"),
1255 TYPE (b->parts), b->parts->subtype);
1256 state_attach_puts (_("[-- and the indicated external source has --]\n"
1257 "[-- expired. --]\n"), s);
1259 mutt_copy_hdr (s->fpin, s->fpout, ftello (s->fpin), b->parts->offset,
1260 (option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) |
1265 if (s->flags & M_DISPLAY) {
1266 state_mark_attach (s);
1268 _("[-- This %s/%s attachment is not included, --]\n"),
1269 TYPE (b->parts), b->parts->subtype);
1270 state_mark_attach (s);
1273 ("[-- and the indicated access-type %s is unsupported --]\n"),
1275 mutt_copy_hdr (s->fpin, s->fpout, ftello (s->fpin), b->parts->offset,
1276 (option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) |
1283 void mutt_decode_attachment (BODY * b, STATE * s)
1285 int istext = mutt_is_text_part (b);
1286 iconv_t cd = (iconv_t) (-1);
1291 if (s->flags & M_CHARCONV) {
1292 const char *charset = mutt_get_parameter ("charset", b->parameter);
1294 if (!option (OPTSTRICTMIME) && !charset)
1295 charset = mutt_get_first_charset (AssumedCharset);
1296 if (charset && Charset)
1297 cd = mutt_iconv_open (Charset, charset, M_ICONV_HOOK_FROM);
1300 if (b->file_charset)
1301 cd = mutt_iconv_open (Charset, b->file_charset, M_ICONV_HOOK_FROM);
1305 fseeko (s->fpin, b->offset, 0);
1306 switch (b->encoding) {
1307 case ENCQUOTEDPRINTABLE:
1308 mutt_decode_quoted (s, b->length, istext ||
1309 ((WithCrypto & APPLICATION_PGP) &&
1310 mutt_is_application_pgp (b)), cd);
1313 mutt_decode_base64 (s, b->length, istext ||
1314 ((WithCrypto & APPLICATION_PGP) &&
1315 mutt_is_application_pgp (b)), cd);
1318 mutt_decode_uuencoded (s, b->length, istext
1319 || ((WithCrypto & APPLICATION_PGP) &&
1320 mutt_is_application_pgp (b)), cd);
1323 mutt_decode_xbit (s, b->length, istext
1324 || ((WithCrypto & APPLICATION_PGP) &&
1325 mutt_is_application_pgp (b)), cd);
1329 if (cd != (iconv_t) (-1))
1333 int mutt_body_handler (BODY * b, STATE * s)
1338 char tempfile[_POSIX_PATH_MAX];
1339 handler_t handler = NULL;
1341 size_t tmplength = 0;
1345 int oflags = s->flags;
1347 /* first determine which handler to use to process this part */
1349 snprintf (type, sizeof (type), "%s/%s", TYPE (b), b->subtype);
1350 if (mutt_is_autoview (b, type)) {
1351 rfc1524_entry *entry = rfc1524_new_entry ();
1353 if (rfc1524_mailcap_lookup (b, type, entry, M_AUTOVIEW)) {
1354 handler = autoview_handler;
1355 s->flags &= ~M_CHARCONV;
1357 rfc1524_free_entry (&entry);
1359 else if (b->type == TYPETEXT) {
1360 if (ascii_strcasecmp ("plain", b->subtype) == 0) {
1361 /* avoid copying this part twice since removing the transfer-encoding is
1362 * the only operation needed.
1364 if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b))
1365 handler = crypt_pgp_application_pgp_handler;
1367 if (ascii_strcasecmp
1368 ("flowed", mutt_get_parameter ("format", b->parameter)) == 0)
1369 handler = rfc3676_handler;
1373 else if (ascii_strcasecmp ("enriched", b->subtype) == 0)
1374 handler = text_enriched_handler;
1375 else /* text body type without a handler */
1378 else if (b->type == TYPEMESSAGE) {
1379 if (mutt_is_message_type (b->type, b->subtype))
1380 handler = message_handler;
1381 else if (!ascii_strcasecmp ("delivery-status", b->subtype))
1383 else if (!ascii_strcasecmp ("external-body", b->subtype))
1384 handler = external_body_handler;
1386 else if (b->type == TYPEMULTIPART) {
1389 if (ascii_strcasecmp ("alternative", b->subtype) == 0)
1390 handler = alternative_handler;
1391 else if (WithCrypto && ascii_strcasecmp ("signed", b->subtype) == 0) {
1392 p = mutt_get_parameter ("protocol", b->parameter);
1395 mutt_error (_("Error: multipart/signed has no protocol."));
1397 else if (s->flags & M_VERIFY)
1398 handler = mutt_signed_handler;
1400 else if ((WithCrypto & APPLICATION_PGP)
1401 && str_casecmp ("encrypted", b->subtype) == 0) {
1402 p = mutt_get_parameter ("protocol", b->parameter);
1406 ("Error: multipart/encrypted has no protocol parameter!"));
1408 else if (ascii_strcasecmp ("application/pgp-encrypted", p) == 0)
1409 handler = crypt_pgp_encrypted_handler;
1413 handler = multipart_handler;
1415 else if (WithCrypto && b->type == TYPEAPPLICATION) {
1416 if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b))
1417 handler = crypt_pgp_application_pgp_handler;
1418 if ((WithCrypto & APPLICATION_SMIME) && mutt_is_application_smime (b))
1419 handler = crypt_smime_application_smime_handler;
1423 if (plaintext || handler) {
1424 fseeko (s->fpin, b->offset, 0);
1426 /* see if we need to decode this part before processing it */
1427 if (b->encoding == ENCBASE64 || b->encoding == ENCQUOTEDPRINTABLE || b->encoding == ENCUUENCODED || plaintext || mutt_is_text_part (b)) { /* text subtypes may
1429 * set conversion even
1430 * with 8bit encoding.
1432 int origType = b->type;
1433 char *savePrefix = NULL;
1436 /* decode to a tempfile, saving the original destination */
1438 mutt_mktemp (tempfile);
1439 if ((s->fpout = safe_fopen (tempfile, "w")) == NULL) {
1440 mutt_error _("Unable to open temporary file!");
1444 /* decoding the attachment changes the size and offset, so save a copy
1445 * of the "real" values now, and restore them after processing
1447 tmplength = b->length;
1448 tmpoffset = b->offset;
1450 /* if we are decoding binary bodies, we don't want to prefix each
1451 * line with the prefix or else the data will get corrupted.
1453 savePrefix = s->prefix;
1461 mutt_decode_attachment (b, s);
1464 b->length = ftello (s->fpout);
1468 /* restore final destination and substitute the tempfile for input */
1471 s->fpin = safe_fopen (tempfile, "r");
1474 /* restore the prefix */
1475 s->prefix = savePrefix;
1481 /* process the (decoded) body part */
1483 rc = handler (b, s);
1486 b->length = tmplength;
1487 b->offset = tmpoffset;
1489 /* restore the original source stream */
1494 s->flags |= M_FIRSTDONE;
1496 else if (s->flags & M_DISPLAY) {
1497 state_mark_attach (s);
1498 state_printf (s, _("[-- %s/%s is unsupported "), TYPE (b), b->subtype);
1499 if (!option (OPTVIEWATTACH)) {
1501 (type, sizeof (type),
1502 km_find_func (MENU_PAGER, OP_VIEW_ATTACHMENTS)))
1503 fprintf (s->fpout, _("(use '%s' to view this part)"), type);
1505 fputs (_("(need 'view-attachments' bound to key!)"), s->fpout);
1507 fputs (" --]\n", s->fpout);
1511 s->flags = oflags | (s->flags & M_FIRSTDONE);