2
* Copyright (C) 1996-2000,2002,2010 Michael R. Elkins <me@mutt.org>
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation; either version 2 of the License, or
7
* (at your option) any later version.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program; if not, write to the Free Software
16
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
31
#include "mutt_curses.h"
37
#include "mutt_crypt.h"
40
#define BUFI_SIZE 1000
41
#define BUFO_SIZE 2000
44
typedef int (*handler_t) (BODY *, STATE *);
46
const 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
57
const int Index_64[128] = {
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
static void state_prefix_put (const char *d, size_t dlen, STATE *s)
72
state_prefix_putc (*d++, s);
74
fwrite (d, dlen, 1, s->fpout);
77
static void mutt_convert_to_state(iconv_t cd, char *bufi, size_t *l, STATE *s)
86
if (cd != (iconv_t)(-1))
88
ob = bufo, obl = sizeof (bufo);
89
iconv (cd, 0, 0, &ob, &obl);
91
state_prefix_put (bufo, ob - bufo, s);
96
if (cd == (iconv_t)(-1))
98
state_prefix_put (bufi, *l, s);
106
ob = bufo, obl = sizeof (bufo);
107
mutt_iconv (cd, &ib, &ibl, &ob, &obl, 0, "?");
110
state_prefix_put (bufo, ob - bufo, s);
112
memmove (bufi, ib, ibl);
116
static void mutt_decode_xbit (STATE *s, long len, int istext, iconv_t cd)
119
char bufi[BUFI_SIZE];
126
while ((c = fgetc(s->fpin)) != EOF && len--)
130
if((ch = fgetc(s->fpin)) == '\n')
140
if (l == sizeof (bufi))
141
mutt_convert_to_state (cd, bufi, &l, s);
144
mutt_convert_to_state (cd, bufi, &l, s);
145
mutt_convert_to_state (cd, 0, 0, s);
147
state_reset_prefix (s);
150
mutt_copy_bytes (s->fpin, s->fpout, len);
153
static int qp_decode_triple (char *s, char *d)
155
/* soft line break */
156
if (*s == '=' && !(*(s+1)))
159
/* quoted-printable triple */
161
isxdigit ((unsigned char) *(s+1)) &&
162
isxdigit ((unsigned char) *(s+2)))
164
*d = (hexval (*(s+1)) << 4) | hexval (*(s+2));
172
static void qp_decode_line (char *dest, char *src, size_t *l,
181
/* decode the line */
183
for (d = dest, s = src; *s;)
185
switch ((kind = qp_decode_triple (s, &c)))
187
case 0: *d++ = c; s += 3; break; /* qp triple */
188
case -1: *d++ = *s++; break; /* single character */
189
case 1: soft = 1; s++; break; /* soft line break */
193
if (!soft && last == '\n')
195
/* neither \r nor \n as part of line-terminating CRLF
196
* may be qp-encoded, so remove \r and \n-terminate;
197
* see RfC2045, sect. 6.7, (1): General 8bit representation */
198
if (kind == 0 && c == '\r')
209
* Decode an attachment encoded with quoted-printable.
211
* Why doesn't this overflow any buffers? First, it's guaranteed
212
* that the length of a line grows when you _en_-code it to
213
* quoted-printable. That means that we always can store the
214
* result in a buffer of at most the _same_ size.
216
* Now, we don't special-case if the line we read with fgets()
217
* isn't terminated. We don't care about this, since STRING > 78,
218
* so corrupted input will just be corrupted a bit more. That
219
* implies that STRING+1 bytes are always sufficient to store the
220
* result of qp_decode_line.
222
* Finally, at soft line breaks, some part of a multibyte character
223
* may have been left over by mutt_convert_to_state(). This shouldn't
224
* be more than 6 characters, so STRING + 7 should be sufficient
225
* memory to store the decoded data.
227
* Just to make sure that I didn't make some off-by-one error
228
* above, we just use STRING*2 for the target buffer's size.
232
static void mutt_decode_quoted (STATE *s, long len, int istext, iconv_t cd)
235
char decline[2*STRING];
237
size_t linelen; /* number of input bytes in `line' */
240
int last; /* store the last character in the input line */
250
* It's ok to use a fixed size buffer for input, even if the line turns
251
* out to be longer than this. Just process the line in chunks. This
252
* really shouldn't happen according the MIME spec, since Q-P encoded
253
* lines are at most 76 characters, but we should be liberal about what
256
if (fgets (line, MIN ((ssize_t)sizeof (line), len + 1), s->fpin) == NULL)
259
linelen = strlen(line);
263
* inspect the last character we read so we can tell if we got the
266
last = linelen ? line[linelen - 1] : 0;
268
/* chop trailing whitespace if we got the full line */
271
while (linelen > 0 && ISSPACE (line[linelen-1]))
276
/* decode and do character set conversion */
277
qp_decode_line (decline + l, line, &l3, last);
279
mutt_convert_to_state (cd, decline, &l, s);
282
mutt_convert_to_state (cd, 0, 0, s);
283
state_reset_prefix(s);
286
void mutt_decode_base64 (STATE *s, long len, int istext, iconv_t cd)
289
int c1, c2, c3, c4, ch, cr = 0, i;
290
char bufi[BUFI_SIZE];
300
for (i = 0 ; i < 4 && len > 0 ; len--)
302
if ((ch = fgetc (s->fpin)) == EOF)
304
if (ch >= 0 && ch < 128 && (base64val(ch) != -1 || ch == '='))
309
/* "i" may be zero if there is trailing whitespace, which is not an error */
311
dprint (2, (debugfile, "%s:%d [mutt_decode_base64()]: "
312
"didn't get a multiple of 4 chars.\n", __FILE__, __LINE__));
316
c1 = base64val (buf[0]);
317
c2 = base64val (buf[1]);
318
ch = (c1 << 2) | (c2 >> 4);
320
if (cr && ch != '\n')
325
if (istext && ch == '\r')
332
c3 = base64val (buf[2]);
333
ch = ((c2 & 0xf) << 4) | (c3 >> 2);
335
if (cr && ch != '\n')
340
if (istext && ch == '\r')
345
if (buf[3] == '=') break;
346
c4 = base64val (buf[3]);
347
ch = ((c3 & 0x3) << 6) | c4;
349
if (cr && ch != '\n')
353
if (istext && ch == '\r')
358
if (l + 8 >= sizeof (bufi))
359
mutt_convert_to_state (cd, bufi, &l, s);
362
if (cr) bufi[l++] = '\r';
364
mutt_convert_to_state (cd, bufi, &l, s);
365
mutt_convert_to_state (cd, 0, 0, s);
367
state_reset_prefix(s);
370
static unsigned char decode_byte (char ch)
377
static void mutt_decode_uuencoded (STATE *s, long len, int istext, iconv_t cd)
379
char tmps[SHORT_STRING];
380
char linelen, c, l, out;
382
char bufi[BUFI_SIZE];
390
if ((fgets(tmps, sizeof(tmps), s->fpin)) == NULL)
392
len -= mutt_strlen(tmps);
393
if ((!mutt_strncmp (tmps, "begin", 5)) && ISSPACE (tmps[5]))
398
if ((fgets(tmps, sizeof(tmps), s->fpin)) == NULL)
400
len -= mutt_strlen(tmps);
401
if (!mutt_strncmp (tmps, "end", 3))
404
linelen = decode_byte (*pt);
406
for (c = 0; c < linelen;)
408
for (l = 2; l <= 6; l += 2)
410
out = decode_byte (*pt) << l;
412
out |= (decode_byte (*pt) >> (6 - l));
418
mutt_convert_to_state (cd, bufi, &k, s);
423
mutt_convert_to_state (cd, bufi, &k, s);
424
mutt_convert_to_state (cd, 0, 0, s);
426
state_reset_prefix(s);
429
/* ----------------------------------------------------------------------------
430
* A (not so) minimal implementation of RFC1563.
433
#define IndentSize (4)
435
enum { RICH_PARAM=0, RICH_BOLD, RICH_UNDERLINE, RICH_ITALIC, RICH_NOFILL,
436
RICH_INDENT, RICH_INDENT_RIGHT, RICH_EXCERPT, RICH_CENTER, RICH_FLUSHLEFT,
437
RICH_FLUSHRIGHT, RICH_COLOR, RICH_LAST_TAG };
439
static const struct {
440
const wchar_t *tag_name;
443
{ L"param", RICH_PARAM },
444
{ L"bold", RICH_BOLD },
445
{ L"italic", RICH_ITALIC },
446
{ L"underline", RICH_UNDERLINE },
447
{ L"nofill", RICH_NOFILL },
448
{ L"excerpt", RICH_EXCERPT },
449
{ L"indent", RICH_INDENT },
450
{ L"indentright", RICH_INDENT_RIGHT },
451
{ L"center", RICH_CENTER },
452
{ L"flushleft", RICH_FLUSHLEFT },
453
{ L"flushright", RICH_FLUSHRIGHT },
454
{ L"flushboth", RICH_FLUSHLEFT },
455
{ L"color", RICH_COLOR },
456
{ L"x-color", RICH_COLOR },
460
struct enriched_state
474
int tag_level[RICH_LAST_TAG];
479
static void enriched_wrap (struct enriched_state *stte)
486
if (stte->tag_level[RICH_CENTER] || stte->tag_level[RICH_FLUSHRIGHT])
488
/* Strip trailing white space */
489
size_t y = stte->line_used - 1;
491
while (y && iswspace (stte->line[y]))
493
stte->line[y] = (wchar_t) '\0';
498
if (stte->tag_level[RICH_CENTER])
500
/* Strip leading whitespace */
503
while (stte->line[y] && iswspace (stte->line[y]))
509
for (z = y ; z <= stte->line_used; z++)
511
stte->line[z - y] = stte->line[z];
515
stte->line_used -= y;
520
extra = stte->WrapMargin - stte->line_len - stte->indent_len -
521
(stte->tag_level[RICH_INDENT_RIGHT] * IndentSize);
524
if (stte->tag_level[RICH_CENTER])
529
state_putc (' ', stte->s);
533
else if (stte->tag_level[RICH_FLUSHRIGHT])
538
state_putc (' ', stte->s);
543
state_putws ((const wchar_t*) stte->line, stte->s);
546
state_putc ('\n', stte->s);
547
stte->line[0] = (wchar_t) '\0';
550
stte->indent_len = 0;
553
state_puts (stte->s->prefix, stte->s);
554
stte->indent_len += mutt_strlen (stte->s->prefix);
557
if (stte->tag_level[RICH_EXCERPT])
559
x = stte->tag_level[RICH_EXCERPT];
564
state_puts (stte->s->prefix, stte->s);
565
stte->indent_len += mutt_strlen (stte->s->prefix);
569
state_puts ("> ", stte->s);
570
stte->indent_len += mutt_strlen ("> ");
576
stte->indent_len = 0;
577
if (stte->tag_level[RICH_INDENT])
579
x = stte->tag_level[RICH_INDENT] * IndentSize;
580
stte->indent_len += x;
583
state_putc (' ', stte->s);
589
static void enriched_flush (struct enriched_state *stte, int wrap)
591
if (!stte->tag_level[RICH_NOFILL] && (stte->line_len + stte->word_len >
592
(stte->WrapMargin - (stte->tag_level[RICH_INDENT_RIGHT] * IndentSize) -
594
enriched_wrap (stte);
598
stte->buffer[stte->buff_used] = (wchar_t) '\0';
599
stte->line_used += stte->buff_used;
600
if (stte->line_used > stte->line_max)
602
stte->line_max = stte->line_used;
603
safe_realloc (&stte->line, (stte->line_max + 1) * sizeof (wchar_t));
605
wcscat (stte->line, stte->buffer);
606
stte->line_len += stte->word_len;
612
fflush (stte->s->fpout);
616
static void enriched_putwc (wchar_t c, struct enriched_state *stte)
618
if (stte->tag_level[RICH_PARAM])
620
if (stte->tag_level[RICH_COLOR])
622
if (stte->param_used + 1 >= stte->param_len)
623
safe_realloc (&stte->param, (stte->param_len += STRING) * sizeof (wchar_t));
625
stte->param[stte->param_used++] = c;
627
return; /* nothing to do */
630
/* see if more space is needed (plus extra for possible rich characters) */
631
if (stte->buff_len < stte->buff_used + 3)
633
stte->buff_len += LONG_STRING;
634
safe_realloc (&stte->buffer, (stte->buff_len + 1) * sizeof (wchar_t));
637
if ((!stte->tag_level[RICH_NOFILL] && iswspace (c)) || c == (wchar_t) '\0')
639
if (c == (wchar_t) '\t')
640
stte->word_len += 8 - (stte->line_len + stte->word_len) % 8;
644
stte->buffer[stte->buff_used++] = c;
645
enriched_flush (stte, 0);
649
if (stte->s->flags & M_DISPLAY)
651
if (stte->tag_level[RICH_BOLD])
653
stte->buffer[stte->buff_used++] = c;
654
stte->buffer[stte->buff_used++] = (wchar_t) '\010';
655
stte->buffer[stte->buff_used++] = c;
657
else if (stte->tag_level[RICH_UNDERLINE])
660
stte->buffer[stte->buff_used++] = '_';
661
stte->buffer[stte->buff_used++] = (wchar_t) '\010';
662
stte->buffer[stte->buff_used++] = c;
664
else if (stte->tag_level[RICH_ITALIC])
666
stte->buffer[stte->buff_used++] = c;
667
stte->buffer[stte->buff_used++] = (wchar_t) '\010';
668
stte->buffer[stte->buff_used++] = '_';
672
stte->buffer[stte->buff_used++] = c;
677
stte->buffer[stte->buff_used++] = c;
683
static void enriched_puts (const char *s, struct enriched_state *stte)
687
if (stte->buff_len < stte->buff_used + mutt_strlen (s))
689
stte->buff_len += LONG_STRING;
690
safe_realloc (&stte->buffer, (stte->buff_len + 1) * sizeof (wchar_t));
695
stte->buffer[stte->buff_used++] = (wchar_t) *c;
700
static void enriched_set_flags (const wchar_t *tag, struct enriched_state *stte)
702
const wchar_t *tagptr = tag;
705
if (*tagptr == (wchar_t) '/')
708
for (i = 0, j = -1; EnrichedTags[i].tag_name; i++)
709
if (wcscasecmp (EnrichedTags[i].tag_name, tagptr) == 0)
711
j = EnrichedTags[i].index;
717
if (j == RICH_CENTER || j == RICH_FLUSHLEFT || j == RICH_FLUSHRIGHT)
718
enriched_flush (stte, 1);
720
if (*tag == (wchar_t) '/')
722
if (stte->tag_level[j]) /* make sure not to go negative */
723
stte->tag_level[j]--;
724
if ((stte->s->flags & M_DISPLAY) && j == RICH_PARAM && stte->tag_level[RICH_COLOR])
726
stte->param[stte->param_used] = (wchar_t) '\0';
727
if (!wcscasecmp(L"black", stte->param))
729
enriched_puts("\033[30m", stte);
731
else if (!wcscasecmp(L"red", stte->param))
733
enriched_puts("\033[31m", stte);
735
else if (!wcscasecmp(L"green", stte->param))
737
enriched_puts("\033[32m", stte);
739
else if (!wcscasecmp(L"yellow", stte->param))
741
enriched_puts("\033[33m", stte);
743
else if (!wcscasecmp(L"blue", stte->param))
745
enriched_puts("\033[34m", stte);
747
else if (!wcscasecmp(L"magenta", stte->param))
749
enriched_puts("\033[35m", stte);
751
else if (!wcscasecmp(L"cyan", stte->param))
753
enriched_puts("\033[36m", stte);
755
else if (!wcscasecmp(L"white", stte->param))
757
enriched_puts("\033[37m", stte);
760
if ((stte->s->flags & M_DISPLAY) && j == RICH_COLOR)
762
enriched_puts("\033[0m", stte);
765
/* flush parameter buffer when closing the tag */
768
stte->param_used = 0;
769
stte->param[0] = (wchar_t) '\0';
773
stte->tag_level[j]++;
775
if (j == RICH_EXCERPT)
776
enriched_flush(stte, 1);
780
static int text_enriched_handler (BODY *a, STATE *s)
783
TEXT, LANGLE, TAG, BOGUS_TAG, NEWLINE, ST_EOF, DONE
786
long bytes = a->length;
787
struct enriched_state stte;
790
wchar_t tag[LONG_STRING + 1];
792
memset (&stte, 0, sizeof (stte));
794
stte.WrapMargin = ((s->flags & M_DISPLAY) ? (COLS-4) : ((COLS-4)<72)?(COLS-4):72);
795
stte.line_max = stte.WrapMargin * 4;
796
stte.line = (wchar_t *) safe_calloc (1, (stte.line_max + 1) * sizeof (wchar_t));
797
stte.param = (wchar_t *) safe_calloc (1, (STRING) * sizeof (wchar_t));
799
stte.param_len = STRING;
804
state_puts (s->prefix, s);
805
stte.indent_len += mutt_strlen (s->prefix);
808
while (state != DONE)
812
if (!bytes || (wc = fgetwc (s->fpin)) == WEOF)
828
if (stte.tag_level[RICH_NOFILL])
830
enriched_flush (&stte, 1);
834
enriched_putwc ((wchar_t) ' ', &stte);
840
enriched_putwc (wc, &stte);
845
if (wc == (wchar_t) '<')
847
enriched_putwc (wc, &stte);
856
/* Yes, fall through (it wasn't a <<, so this char is first in TAG) */
858
if (wc == (wchar_t) '>')
860
tag[tag_len] = (wchar_t) '\0';
861
enriched_set_flags (tag, &stte);
864
else if (tag_len < LONG_STRING) /* ignore overly long tags */
871
if (wc == (wchar_t) '>')
876
if (wc == (wchar_t) '\n')
877
enriched_flush (&stte, 1);
880
ungetwc (wc, s->fpin);
887
enriched_putwc ((wchar_t) '\0', &stte);
888
enriched_flush (&stte, 1);
892
case DONE: /* not reached, but gcc complains if this is absent */
897
state_putc ('\n', s); /* add a final newline */
899
FREE (&(stte.buffer));
901
FREE (&(stte.param));
906
/* for compatibility with metamail */
907
static int is_mmnoask (const char *buf)
909
char tmp[LONG_STRING], *p, *q;
912
if ((p = getenv ("MM_NOASK")) != NULL && *p)
914
if (mutt_strcmp (p, "1") == 0)
917
strfcpy (tmp, p, sizeof (tmp));
920
while ((p = strtok (p, ",")) != NULL)
922
if ((q = strrchr (p, '/')) != NULL)
926
if (ascii_strncasecmp (buf, p, q-p) == 0)
931
if (ascii_strcasecmp (buf, p) == 0)
937
lng = mutt_strlen (p);
938
if (buf[lng] == '/' && mutt_strncasecmp (buf, p, lng) == 0)
951
* 1 if the body part should be filtered by a mailcap entry prior to viewing inline.
955
static int mutt_is_autoview (BODY *b)
957
char type[SHORT_STRING];
959
snprintf (type, sizeof (type), "%s/%s", TYPE (b), b->subtype);
961
/* determine if there is a mailcap entry suitable for auto_view
963
* WARNING: _type is altered by this call as a result of `mime_lookup' support */
964
if (rfc1524_mailcap_lookup(b, type, NULL, M_AUTOVIEW))
966
if (option(OPTIMPLICITAUTOVIEW))
968
/* $implicit_autoview is essentially the same as "auto_view *" */
973
/* determine if this type is on the user's auto_view list */
974
LIST *t = AutoViewList;
976
for (; t; t = t->next) {
977
int i = mutt_strlen (t->data) - 1;
978
if ((i > 0 && t->data[i-1] == '/' && t->data[i] == '*' &&
979
ascii_strncasecmp (type, t->data, i) == 0) ||
980
ascii_strcasecmp (type, t->data) == 0)
984
if (is_mmnoask (type))
994
#define TXTENRICHED 3
996
static int alternative_handler (BODY *a, STATE *s)
1005
if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE ||
1006
a->encoding == ENCUUENCODED)
1010
fstat (fileno (s->fpin), &st);
1011
b = mutt_new_body ();
1012
b->length = (long) st.st_size;
1013
b->parts = mutt_parse_multipart (s->fpin,
1014
mutt_get_parameter ("boundary", a->parameter),
1015
(long) st.st_size, ascii_strcasecmp ("digest", a->subtype) == 0);
1022
/* First, search list of preferred types */
1023
t = AlternativeOrderList;
1024
while (t && !choice)
1027
int btlen; /* length of basetype */
1028
int wild; /* do we have a wildcard to match all subtypes? */
1030
c = strchr (t->data, '/');
1033
wild = (c[1] == '*' && c[2] == 0);
1034
btlen = c - t->data;
1039
btlen = mutt_strlen (t->data);
1048
const char *bt = TYPE(b);
1049
if (!ascii_strncasecmp (bt, t->data, btlen) && bt[btlen] == 0)
1051
/* the basetype matches */
1052
if (wild || !ascii_strcasecmp (t->data + btlen + 1, b->subtype))
1062
/* Next, look for an autoviewable type */
1071
if (mutt_is_autoview (b))
1077
/* Then, look for a text entry */
1086
if (b->type == TYPETEXT)
1088
if (! ascii_strcasecmp ("plain", b->subtype) && type <= TXTPLAIN)
1093
else if (! ascii_strcasecmp ("enriched", b->subtype) && type <= TXTENRICHED)
1098
else if (! ascii_strcasecmp ("html", b->subtype) && type <= TXTHTML)
1108
/* Finally, look for other possibilities */
1117
if (mutt_can_decode (b))
1125
if (s->flags & M_DISPLAY && !option (OPTWEED))
1127
fseeko (s->fpin, choice->hdr_offset, 0);
1128
mutt_copy_bytes(s->fpin, s->fpout, choice->offset-choice->hdr_offset);
1130
mutt_body_handler (choice, s);
1132
else if (s->flags & M_DISPLAY)
1134
/* didn't find anything that we could display! */
1135
state_mark_attach (s);
1136
state_puts(_("[-- Error: Could not display any parts of Multipart/Alternative! --]\n"), s);
1146
/* handles message/rfc822 body parts */
1147
static int message_handler (BODY *a, STATE *s)
1154
off_start = ftello (s->fpin);
1155
if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE ||
1156
a->encoding == ENCUUENCODED)
1158
fstat (fileno (s->fpin), &st);
1159
b = mutt_new_body ();
1160
b->length = (LOFF_T) st.st_size;
1161
b->parts = mutt_parse_messageRFC822 (s->fpin, b);
1168
mutt_copy_hdr (s->fpin, s->fpout, off_start, b->parts->offset,
1169
(((s->flags & M_WEED) || ((s->flags & (M_DISPLAY|M_PRINTING)) && option (OPTWEED))) ? (CH_WEED | CH_REORDER) : 0) |
1170
(s->prefix ? CH_PREFIX : 0) | CH_DECODE | CH_FROM |
1171
((s->flags & M_DISPLAY) ? CH_DISPLAY : 0), s->prefix);
1174
state_puts (s->prefix, s);
1175
state_putc ('\n', s);
1177
rc = mutt_body_handler (b->parts, s);
1180
if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE ||
1181
a->encoding == ENCUUENCODED)
1182
mutt_free_body (&b);
1187
/* returns 1 if decoding the attachment will produce output */
1188
int mutt_can_decode (BODY *a)
1190
if (mutt_is_autoview (a))
1192
else if (a->type == TYPETEXT)
1194
else if (a->type == TYPEMESSAGE)
1196
else if (a->type == TYPEMULTIPART)
1202
if (ascii_strcasecmp (a->subtype, "signed") == 0 ||
1203
ascii_strcasecmp (a->subtype, "encrypted") == 0)
1207
for (p = a->parts; p; p = p->next)
1209
if (mutt_can_decode (p))
1214
else if (WithCrypto && a->type == TYPEAPPLICATION)
1216
if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp(a))
1218
if ((WithCrypto & APPLICATION_SMIME) && mutt_is_application_smime(a))
1225
static int multipart_handler (BODY *a, STATE *s)
1233
if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE ||
1234
a->encoding == ENCUUENCODED)
1236
fstat (fileno (s->fpin), &st);
1237
b = mutt_new_body ();
1238
b->length = (long) st.st_size;
1239
b->parts = mutt_parse_multipart (s->fpin,
1240
mutt_get_parameter ("boundary", a->parameter),
1241
(long) st.st_size, ascii_strcasecmp ("digest", a->subtype) == 0);
1246
for (p = b->parts, count = 1; p; p = p->next, count++)
1248
if (s->flags & M_DISPLAY)
1250
state_mark_attach (s);
1251
state_printf (s, _("[-- Attachment #%d"), count);
1252
if (p->description || p->filename || p->form_name)
1254
state_puts (": ", s);
1255
state_puts (p->description ? p->description :
1256
p->filename ? p->filename : p->form_name, s);
1258
state_puts (" --]\n", s);
1260
mutt_pretty_size (length, sizeof (length), p->length);
1262
state_mark_attach (s);
1263
state_printf (s, _("[-- Type: %s/%s, Encoding: %s, Size: %s --]\n"),
1264
TYPE (p), p->subtype, ENCODING (p->encoding), length);
1265
if (!option (OPTWEED))
1267
fseeko (s->fpin, p->hdr_offset, 0);
1268
mutt_copy_bytes(s->fpin, s->fpout, p->offset-p->hdr_offset);
1271
state_putc ('\n', s);
1274
rc = mutt_body_handler (p, s);
1275
state_putc ('\n', s);
1279
mutt_error (_("One or more parts of this message could not be displayed"));
1280
dprint (1, (debugfile, "Failed on attachment #%d, type %s/%s.\n", count, TYPE(p), NONULL (p->subtype)));
1283
if ((s->flags & M_REPLYING)
1284
&& (option (OPTINCLUDEONLYFIRST)) && (s->flags & M_FIRSTDONE))
1288
if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE ||
1289
a->encoding == ENCUUENCODED)
1290
mutt_free_body (&b);
1292
/* make failure of a single part non-fatal */
1298
static int autoview_handler (BODY *a, STATE *s)
1300
rfc1524_entry *entry = rfc1524_new_entry ();
1301
char buffer[LONG_STRING];
1303
char command[LONG_STRING];
1304
char tempfile[_POSIX_PATH_MAX] = "";
1313
snprintf (type, sizeof (type), "%s/%s", TYPE (a), a->subtype);
1314
rfc1524_mailcap_lookup (a, type, entry, M_AUTOVIEW);
1316
fname = safe_strdup (a->filename);
1317
mutt_sanitize_filename (fname, 1);
1318
rfc1524_expand_filename (entry->nametemplate, fname, tempfile, sizeof (tempfile));
1323
strfcpy (command, entry->command, sizeof (command));
1325
/* rfc1524_expand_command returns 0 if the file is required */
1326
piped = rfc1524_expand_command (a, tempfile, type, command, sizeof (command));
1328
if (s->flags & M_DISPLAY)
1330
state_mark_attach (s);
1331
state_printf (s, _("[-- Autoview using %s --]\n"), command);
1332
mutt_message(_("Invoking autoview command: %s"),command);
1335
if ((fpin = safe_fopen (tempfile, "w+")) == NULL)
1337
mutt_perror ("fopen");
1338
rfc1524_free_entry (&entry);
1342
mutt_copy_bytes (s->fpin, fpin, a->length);
1346
safe_fclose (&fpin);
1347
thepid = mutt_create_filter (command, NULL, &fpout, &fperr);
1354
thepid = mutt_create_filter_fd (command, NULL, &fpout, &fperr,
1355
fileno(fpin), -1, -1);
1360
mutt_perror _("Can't create filter");
1361
if (s->flags & M_DISPLAY)
1363
state_mark_attach (s);
1364
state_printf (s, _("[-- Can't run %s. --]\n"), command);
1372
while (fgets (buffer, sizeof(buffer), fpout) != NULL)
1374
state_puts (s->prefix, s);
1375
state_puts (buffer, s);
1377
/* check for data on stderr */
1378
if (fgets (buffer, sizeof(buffer), fperr))
1380
if (s->flags & M_DISPLAY)
1382
state_mark_attach (s);
1383
state_printf (s, _("[-- Autoview stderr of %s --]\n"), command);
1386
state_puts (s->prefix, s);
1387
state_puts (buffer, s);
1388
while (fgets (buffer, sizeof(buffer), fperr) != NULL)
1390
state_puts (s->prefix, s);
1391
state_puts (buffer, s);
1397
mutt_copy_stream (fpout, s->fpout);
1398
/* Check for stderr messages */
1399
if (fgets (buffer, sizeof(buffer), fperr))
1401
if (s->flags & M_DISPLAY)
1403
state_mark_attach (s);
1404
state_printf (s, _("[-- Autoview stderr of %s --]\n"),
1408
state_puts (buffer, s);
1409
mutt_copy_stream (fperr, s->fpout);
1414
safe_fclose (&fpout);
1415
safe_fclose (&fperr);
1417
mutt_wait_filter (thepid);
1419
safe_fclose (&fpin);
1421
mutt_unlink (tempfile);
1423
if (s->flags & M_DISPLAY)
1424
mutt_clear_error ();
1426
rfc1524_free_entry (&entry);
1431
static int external_body_handler (BODY *b, STATE *s)
1433
const char *access_type;
1434
const char *expiration;
1437
access_type = mutt_get_parameter ("access-type", b->parameter);
1440
if (s->flags & M_DISPLAY)
1442
state_mark_attach (s);
1443
state_puts (_("[-- Error: message/external-body has no access-type parameter --]\n"), s);
1450
expiration = mutt_get_parameter ("expiration", b->parameter);
1452
expire = mutt_parse_date (expiration, NULL);
1456
if (!ascii_strcasecmp (access_type, "x-mutt-deleted"))
1458
if (s->flags & (M_DISPLAY|M_PRINTING))
1461
char pretty_size[10];
1463
state_mark_attach (s);
1464
state_printf (s, _("[-- This %s/%s attachment "),
1465
TYPE(b->parts), b->parts->subtype);
1466
length = mutt_get_parameter ("length", b->parameter);
1469
mutt_pretty_size (pretty_size, sizeof (pretty_size),
1470
strtol (length, NULL, 10));
1471
state_printf (s, _("(size %s bytes) "), pretty_size);
1473
state_puts (_("has been deleted --]\n"), s);
1477
state_mark_attach (s);
1478
state_printf (s, _("[-- on %s --]\n"), expiration);
1480
if (b->parts->filename)
1482
state_mark_attach (s);
1483
state_printf (s, _("[-- name: %s --]\n"), b->parts->filename);
1486
mutt_copy_hdr (s->fpin, s->fpout, ftello (s->fpin), b->parts->offset,
1487
(option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) |
1491
else if(expiration && expire < time(NULL))
1493
if (s->flags & M_DISPLAY)
1495
state_mark_attach (s);
1496
state_printf (s, _("[-- This %s/%s attachment is not included, --]\n"),
1497
TYPE(b->parts), b->parts->subtype);
1498
state_attach_puts (_("[-- and the indicated external source has --]\n"
1499
"[-- expired. --]\n"), s);
1501
mutt_copy_hdr(s->fpin, s->fpout, ftello (s->fpin), b->parts->offset,
1502
(option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) |
1503
CH_DECODE | CH_DISPLAY, NULL);
1508
if (s->flags & M_DISPLAY)
1510
state_mark_attach (s);
1512
_("[-- This %s/%s attachment is not included, --]\n"),
1513
TYPE (b->parts), b->parts->subtype);
1514
state_mark_attach (s);
1516
_("[-- and the indicated access-type %s is unsupported --]\n"),
1518
mutt_copy_hdr (s->fpin, s->fpout, ftello (s->fpin), b->parts->offset,
1519
(option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) |
1520
CH_DECODE | CH_DISPLAY, NULL);
1527
void mutt_decode_attachment (BODY *b, STATE *s)
1529
int istext = mutt_is_text_part (b);
1530
iconv_t cd = (iconv_t)(-1);
1532
if (istext && s->flags & M_CHARCONV)
1534
char *charset = mutt_get_parameter ("charset", b->parameter);
1535
if (!charset && AssumedCharset && *AssumedCharset)
1536
charset = mutt_get_default_charset ();
1537
if (charset && Charset)
1538
cd = mutt_iconv_open (Charset, charset, M_ICONV_HOOK_FROM);
1540
else if (istext && b->charset)
1541
cd = mutt_iconv_open (Charset, b->charset, M_ICONV_HOOK_FROM);
1543
fseeko (s->fpin, b->offset, 0);
1544
switch (b->encoding)
1546
case ENCQUOTEDPRINTABLE:
1547
mutt_decode_quoted (s, b->length, istext || ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b)), cd);
1550
mutt_decode_base64 (s, b->length, istext || ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b)), cd);
1553
mutt_decode_uuencoded (s, b->length, istext || ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b)), cd);
1556
mutt_decode_xbit (s, b->length, istext || ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b)), cd);
1560
if (cd != (iconv_t)(-1))
1564
/* when generating format=flowed ($text_flowed is set) from format=fixed,
1565
* strip all trailing spaces to improve interoperability;
1566
* if $text_flowed is unset, simply verbatim copy input
1568
static int text_plain_handler (BODY *b, STATE *s)
1571
size_t l = 0, sz = 0;
1573
while ((buf = mutt_read_line (buf, &sz, s->fpin, NULL, 0)))
1575
if (mutt_strcmp (buf, "-- ") != 0 && option (OPTTEXTFLOWED))
1577
l = mutt_strlen (buf);
1578
while (l > 0 && buf[l-1] == ' ')
1582
state_puts (s->prefix, s);
1583
state_puts (buf, s);
1584
state_putc ('\n', s);
1591
int mutt_body_handler (BODY *b, STATE *s)
1596
char tempfile[_POSIX_PATH_MAX];
1597
handler_t handler = NULL;
1598
LOFF_T tmpoffset = 0;
1599
size_t tmplength = 0;
1602
int oflags = s->flags;
1604
/* first determine which handler to use to process this part */
1606
if (mutt_is_autoview (b))
1608
handler = autoview_handler;
1609
s->flags &= ~M_CHARCONV;
1611
else if (b->type == TYPETEXT)
1613
if (ascii_strcasecmp ("plain", b->subtype) == 0)
1615
/* avoid copying this part twice since removing the transfer-encoding is
1616
* the only operation needed.
1618
if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b))
1619
handler = crypt_pgp_application_pgp_handler;
1620
else if (option(OPTREFLOWTEXT) && ascii_strcasecmp ("flowed", mutt_get_parameter ("format", b->parameter)) == 0)
1621
handler = rfc3676_handler;
1623
handler = text_plain_handler;
1625
else if (ascii_strcasecmp ("enriched", b->subtype) == 0)
1626
handler = text_enriched_handler;
1627
else /* text body type without a handler */
1630
else if (b->type == TYPEMESSAGE)
1632
if(mutt_is_message_type(b->type, b->subtype))
1633
handler = message_handler;
1634
else if (!ascii_strcasecmp ("delivery-status", b->subtype))
1636
else if (!ascii_strcasecmp ("external-body", b->subtype))
1637
handler = external_body_handler;
1639
else if (b->type == TYPEMULTIPART)
1643
if (ascii_strcasecmp ("alternative", b->subtype) == 0)
1644
handler = alternative_handler;
1645
else if (WithCrypto && ascii_strcasecmp ("signed", b->subtype) == 0)
1647
p = mutt_get_parameter ("protocol", b->parameter);
1650
mutt_error _("Error: multipart/signed has no protocol.");
1651
else if (s->flags & M_VERIFY)
1652
handler = mutt_signed_handler;
1654
else if ((WithCrypto & APPLICATION_PGP)
1655
&& ascii_strcasecmp ("encrypted", b->subtype) == 0)
1657
p = mutt_get_parameter ("protocol", b->parameter);
1660
mutt_error _("Error: multipart/encrypted has no protocol parameter!");
1661
else if (ascii_strcasecmp ("application/pgp-encrypted", p) == 0)
1662
handler = crypt_pgp_encrypted_handler;
1666
handler = multipart_handler;
1668
if (b->encoding != ENC7BIT && b->encoding != ENC8BIT
1669
&& b->encoding != ENCBINARY)
1671
dprint (1, (debugfile, "Bad encoding type %d for multipart entity, "
1672
"assuming 7 bit\n", b->encoding));
1673
b->encoding = ENC7BIT;
1676
else if (WithCrypto && b->type == TYPEAPPLICATION)
1678
if (option (OPTDONTHANDLEPGPKEYS)
1679
&& !ascii_strcasecmp("pgp-keys", b->subtype))
1681
/* pass raw part through for key extraction */
1684
else if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b))
1685
handler = crypt_pgp_application_pgp_handler;
1686
else if ((WithCrypto & APPLICATION_SMIME) && mutt_is_application_smime(b))
1687
handler = crypt_smime_application_smime_handler;
1690
/* only respect disposition == attachment if we're not
1691
displaying from the attachment menu (i.e. pager) */
1692
if ((!option (OPTHONORDISP) || (b->disposition != DISPATTACH ||
1693
option(OPTVIEWATTACH))) &&
1694
(plaintext || handler))
1696
fseeko (s->fpin, b->offset, 0);
1698
/* see if we need to decode this part before processing it */
1699
if (b->encoding == ENCBASE64 || b->encoding == ENCQUOTEDPRINTABLE ||
1700
b->encoding == ENCUUENCODED || plaintext ||
1701
mutt_is_text_part (b)) /* text subtypes may
1703
* set conversion even
1704
* with 8bit encoding.
1707
int origType = b->type;
1708
char *savePrefix = NULL;
1712
/* decode to a tempfile, saving the original destination */
1714
mutt_mktemp (tempfile, sizeof (tempfile));
1715
if ((s->fpout = safe_fopen (tempfile, "w")) == NULL)
1717
mutt_error _("Unable to open temporary file!");
1718
dprint (1, (debugfile, "Can't open %s.\n", tempfile));
1721
/* decoding the attachment changes the size and offset, so save a copy
1722
* of the "real" values now, and restore them after processing
1724
tmplength = b->length;
1725
tmpoffset = b->offset;
1727
/* if we are decoding binary bodies, we don't want to prefix each
1728
* line with the prefix or else the data will get corrupted.
1730
savePrefix = s->prefix;
1738
mutt_decode_attachment (b, s);
1742
b->length = ftello (s->fpout);
1744
safe_fclose (&s->fpout);
1746
/* restore final destination and substitute the tempfile for input */
1749
s->fpin = fopen (tempfile, "r");
1752
/* restore the prefix */
1753
s->prefix = savePrefix;
1759
/* process the (decoded) body part */
1762
rc = handler (b, s);
1766
dprint (1, (debugfile, "Failed on attachment of type %s/%s.\n", TYPE(b), NONULL (b->subtype)));
1771
b->length = tmplength;
1772
b->offset = tmpoffset;
1774
/* restore the original source stream */
1775
safe_fclose (&s->fpin);
1779
s->flags |= M_FIRSTDONE;
1781
/* print hint to use attachment menu for disposition == attachment
1782
if we're not already being called from there */
1783
else if ((s->flags & M_DISPLAY) || (b->disposition == DISPATTACH &&
1784
!option (OPTVIEWATTACH) &&
1785
option (OPTHONORDISP) &&
1786
(plaintext || handler)))
1788
state_mark_attach (s);
1789
if (option (OPTHONORDISP) && b->disposition == DISPATTACH)
1790
fputs (_("[-- This is an attachment "), s->fpout);
1792
state_printf (s, _("[-- %s/%s is unsupported "), TYPE (b), b->subtype);
1793
if (!option (OPTVIEWATTACH))
1795
char keystroke[SHORT_STRING];
1797
if (km_expand_key (keystroke, sizeof(keystroke),
1798
km_find_func (MENU_PAGER, OP_VIEW_ATTACHMENTS)))
1799
fprintf (s->fpout, _("(use '%s' to view this part)"), keystroke);
1801
fputs (_("(need 'view-attachments' bound to key!)"), s->fpout);
1803
fputs (" --]\n", s->fpout);
1807
s->flags = oflags | (s->flags & M_FIRSTDONE);
1810
dprint (1, (debugfile, "Bailing on attachment of type %s/%s.\n", TYPE(b), NONULL (b->subtype)));