mutt stable branch with some hacks
1/*
2 * Copyright (C) 1996-2000,2002,2010,2013 Michael R. Elkins <me@mutt.org>
3 *
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.
8 *
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.
13 *
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.
17 */
18
19#if HAVE_CONFIG_H
20# include "config.h"
21#endif
22
23#include <stdlib.h>
24#include <string.h>
25#include <unistd.h>
26#include <ctype.h>
27#include <sys/wait.h>
28#include <sys/stat.h>
29
30#include "mutt.h"
31#include "mutt_curses.h"
32#include "rfc1524.h"
33#include "keymap.h"
34#include "mime.h"
35#include "copy.h"
36#include "charset.h"
37#include "mutt_crypt.h"
38#include "rfc3676.h"
39
40#define BUFI_SIZE 1000
41#define BUFO_SIZE 2000
42
43
44typedef int (*handler_t) (BODY *, STATE *);
45
46const 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
55};
56
57const 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
66};
67
68static void state_prefix_put (const char *d, size_t dlen, STATE *s)
69{
70 if (s->prefix)
71 while (dlen--)
72 state_prefix_putc (*d++, s);
73 else
74 fwrite (d, dlen, 1, s->fpout);
75}
76
77static void mutt_convert_to_state(iconv_t cd, char *bufi, size_t *l, STATE *s)
78{
79 char bufo[BUFO_SIZE];
80 ICONV_CONST char *ib;
81 char *ob;
82 size_t ibl, obl;
83
84 if (!bufi)
85 {
86 if (cd != (iconv_t)(-1))
87 {
88 ob = bufo, obl = sizeof (bufo);
89 iconv (cd, 0, 0, &ob, &obl);
90 if (ob != bufo)
91 state_prefix_put (bufo, ob - bufo, s);
92 }
93 return;
94 }
95
96 if (cd == (iconv_t)(-1))
97 {
98 state_prefix_put (bufi, *l, s);
99 *l = 0;
100 return;
101 }
102
103 ib = bufi, ibl = *l;
104 for (;;)
105 {
106 ob = bufo, obl = sizeof (bufo);
107 mutt_iconv (cd, &ib, &ibl, &ob, &obl, 0, "?");
108 if (ob == bufo)
109 break;
110 state_prefix_put (bufo, ob - bufo, s);
111 }
112 memmove (bufi, ib, ibl);
113 *l = ibl;
114}
115
116static void mutt_decode_xbit (STATE *s, long len, int istext, iconv_t cd)
117{
118 int c, ch;
119 char bufi[BUFI_SIZE];
120 size_t l = 0;
121
122 if (istext)
123 {
124 state_set_prefix(s);
125
126 while ((c = fgetc(s->fpin)) != EOF && len--)
127 {
128 if (c == '\r' && len)
129 {
130 if ((ch = fgetc(s->fpin)) == '\n')
131 {
132 c = ch;
133 len--;
134 }
135 else
136 ungetc(ch, s->fpin);
137 }
138
139 bufi[l++] = c;
140 if (l == sizeof (bufi))
141 mutt_convert_to_state (cd, bufi, &l, s);
142 }
143
144 mutt_convert_to_state (cd, bufi, &l, s);
145 mutt_convert_to_state (cd, 0, 0, s);
146
147 state_reset_prefix (s);
148 }
149 else
150 mutt_copy_bytes (s->fpin, s->fpout, len);
151}
152
153static int qp_decode_triple (char *s, char *d)
154{
155 /* soft line break */
156 if (*s == '=' && !(*(s+1)))
157 return 1;
158
159 /* quoted-printable triple */
160 if (*s == '=' &&
161 isxdigit ((unsigned char) *(s+1)) &&
162 isxdigit ((unsigned char) *(s+2)))
163 {
164 *d = (hexval (*(s+1)) << 4) | hexval (*(s+2));
165 return 0;
166 }
167
168 /* something else */
169 return -1;
170}
171
172static void qp_decode_line (char *dest, char *src, size_t *l,
173 int last)
174{
175 char *d, *s;
176 char c = 0;
177
178 int kind = -1;
179 int soft = 0;
180
181 /* decode the line */
182
183 for (d = dest, s = src; *s;)
184 {
185 switch ((kind = qp_decode_triple (s, &c)))
186 {
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 */
190 }
191 }
192
193 if (!soft && last == '\n')
194 {
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')
199 *(d-1) = '\n';
200 else
201 *d++ = '\n';
202 }
203
204 *d = '\0';
205 *l = d - dest;
206}
207
208/*
209 * Decode an attachment encoded with quoted-printable.
210 *
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.
215 *
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.
221 *
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.
226 *
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.
229 *
230 */
231
232static void mutt_decode_quoted (STATE *s, long len, int istext, iconv_t cd)
233{
234 char line[STRING];
235 char decline[2*STRING];
236 size_t l = 0;
237 size_t linelen; /* number of input bytes in `line' */
238 size_t l3;
239
240 int last; /* store the last character in the input line */
241
242 if (istext)
243 state_set_prefix(s);
244
245 while (len > 0)
246 {
247 last = 0;
248
249 /*
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
254 * we accept.
255 */
256 if (fgets (line, MIN ((ssize_t)sizeof (line), len + 1), s->fpin) == NULL)
257 break;
258
259 linelen = strlen(line);
260 len -= linelen;
261
262 /*
263 * inspect the last character we read so we can tell if we got the
264 * entire line.
265 */
266 last = linelen ? line[linelen - 1] : 0;
267
268 /* chop trailing whitespace if we got the full line */
269 if (last == '\n')
270 {
271 while (linelen > 0 && ISSPACE (line[linelen-1]))
272 linelen--;
273 line[linelen]=0;
274 }
275
276 /* decode and do character set conversion */
277 qp_decode_line (decline + l, line, &l3, last);
278 l += l3;
279 mutt_convert_to_state (cd, decline, &l, s);
280 }
281
282 mutt_convert_to_state (cd, 0, 0, s);
283 state_reset_prefix(s);
284}
285
286void mutt_decode_base64 (STATE *s, long len, int istext, iconv_t cd)
287{
288 char buf[5];
289 int c1, c2, c3, c4, ch, cr = 0, i;
290 char bufi[BUFI_SIZE];
291 size_t l = 0;
292
293 buf[4] = 0;
294
295 if (istext)
296 state_set_prefix(s);
297
298 while (len > 0)
299 {
300 for (i = 0 ; i < 4 && len > 0 ; len--)
301 {
302 if ((ch = fgetc (s->fpin)) == EOF)
303 break;
304 if (ch >= 0 && ch < 128 && (base64val(ch) != -1 || ch == '='))
305 buf[i++] = ch;
306 }
307 if (i != 4)
308 {
309 /* "i" may be zero if there is trailing whitespace, which is not an error */
310 if (i != 0)
311 dprint (2, (debugfile, "%s:%d [mutt_decode_base64()]: "
312 "didn't get a multiple of 4 chars.\n", __FILE__, __LINE__));
313 break;
314 }
315
316 c1 = base64val (buf[0]);
317 c2 = base64val (buf[1]);
318 ch = (c1 << 2) | (c2 >> 4);
319
320 if (cr && ch != '\n')
321 bufi[l++] = '\r';
322
323 cr = 0;
324
325 if (istext && ch == '\r')
326 cr = 1;
327 else
328 bufi[l++] = ch;
329
330 if (buf[2] == '=')
331 break;
332 c3 = base64val (buf[2]);
333 ch = ((c2 & 0xf) << 4) | (c3 >> 2);
334
335 if (cr && ch != '\n')
336 bufi[l++] = '\r';
337
338 cr = 0;
339
340 if (istext && ch == '\r')
341 cr = 1;
342 else
343 bufi[l++] = ch;
344
345 if (buf[3] == '=') break;
346 c4 = base64val (buf[3]);
347 ch = ((c3 & 0x3) << 6) | c4;
348
349 if (cr && ch != '\n')
350 bufi[l++] = '\r';
351 cr = 0;
352
353 if (istext && ch == '\r')
354 cr = 1;
355 else
356 bufi[l++] = ch;
357
358 if (l + 8 >= sizeof (bufi))
359 mutt_convert_to_state (cd, bufi, &l, s);
360 }
361
362 if (cr) bufi[l++] = '\r';
363
364 mutt_convert_to_state (cd, bufi, &l, s);
365 mutt_convert_to_state (cd, 0, 0, s);
366
367 state_reset_prefix(s);
368}
369
370static unsigned char decode_byte (char ch)
371{
372 if (ch == 96)
373 return 0;
374 return ch - 32;
375}
376
377static void mutt_decode_uuencoded (STATE *s, long len, int istext, iconv_t cd)
378{
379 char tmps[SHORT_STRING];
380 char linelen, c, l, out;
381 char *pt;
382 char bufi[BUFI_SIZE];
383 size_t k = 0;
384
385 if (istext)
386 state_set_prefix(s);
387
388 while (len > 0)
389 {
390 if ((fgets(tmps, sizeof(tmps), s->fpin)) == NULL)
391 return;
392 len -= mutt_strlen(tmps);
393 if ((!mutt_strncmp (tmps, "begin", 5)) && ISSPACE (tmps[5]))
394 break;
395 }
396 while (len > 0)
397 {
398 if ((fgets(tmps, sizeof(tmps), s->fpin)) == NULL)
399 return;
400 len -= mutt_strlen(tmps);
401 if (!mutt_strncmp (tmps, "end", 3))
402 break;
403 pt = tmps;
404 linelen = decode_byte (*pt);
405 pt++;
406 for (c = 0; c < linelen;)
407 {
408 for (l = 2; l <= 6; l += 2)
409 {
410 out = decode_byte (*pt) << l;
411 pt++;
412 out |= (decode_byte (*pt) >> (6 - l));
413 bufi[k++] = out;
414 c++;
415 if (c == linelen)
416 break;
417 }
418 mutt_convert_to_state (cd, bufi, &k, s);
419 pt++;
420 }
421 }
422
423 mutt_convert_to_state (cd, bufi, &k, s);
424 mutt_convert_to_state (cd, 0, 0, s);
425
426 state_reset_prefix(s);
427}
428
429/* ----------------------------------------------------------------------------
430 * A (not so) minimal implementation of RFC1563.
431 */
432
433#define IndentSize (4)
434
435enum { 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 };
438
439static const struct {
440 const wchar_t *tag_name;
441 int index;
442} EnrichedTags[] = {
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 },
457 { NULL, -1 }
458};
459
460struct enriched_state
461{
462 wchar_t *buffer;
463 wchar_t *line;
464 wchar_t *param;
465 size_t buff_len;
466 size_t line_len;
467 size_t line_used;
468 size_t line_max;
469 size_t indent_len;
470 size_t word_len;
471 size_t buff_used;
472 size_t param_used;
473 size_t param_len;
474 int tag_level[RICH_LAST_TAG];
475 int WrapMargin;
476 STATE *s;
477};
478
479static void enriched_wrap (struct enriched_state *stte)
480{
481 int x;
482 int extra;
483
484 if (stte->line_len)
485 {
486 if (stte->tag_level[RICH_CENTER] || stte->tag_level[RICH_FLUSHRIGHT])
487 {
488 /* Strip trailing white space */
489 size_t y = stte->line_used - 1;
490
491 while (y && iswspace (stte->line[y]))
492 {
493 stte->line[y] = (wchar_t) '\0';
494 y--;
495 stte->line_used--;
496 stte->line_len--;
497 }
498 if (stte->tag_level[RICH_CENTER])
499 {
500 /* Strip leading whitespace */
501 y = 0;
502
503 while (stte->line[y] && iswspace (stte->line[y]))
504 y++;
505 if (y)
506 {
507 size_t z;
508
509 for (z = y ; z <= stte->line_used; z++)
510 {
511 stte->line[z - y] = stte->line[z];
512 }
513
514 stte->line_len -= y;
515 stte->line_used -= y;
516 }
517 }
518 }
519
520 extra = stte->WrapMargin - stte->line_len - stte->indent_len -
521 (stte->tag_level[RICH_INDENT_RIGHT] * IndentSize);
522 if (extra > 0)
523 {
524 if (stte->tag_level[RICH_CENTER])
525 {
526 x = extra / 2;
527 while (x)
528 {
529 state_putc (' ', stte->s);
530 x--;
531 }
532 }
533 else if (stte->tag_level[RICH_FLUSHRIGHT])
534 {
535 x = extra-1;
536 while (x)
537 {
538 state_putc (' ', stte->s);
539 x--;
540 }
541 }
542 }
543 state_putws ((const wchar_t*) stte->line, stte->s);
544 }
545
546 state_putc ('\n', stte->s);
547 stte->line[0] = (wchar_t) '\0';
548 stte->line_len = 0;
549 stte->line_used = 0;
550 stte->indent_len = 0;
551 if (stte->s->prefix)
552 {
553 state_puts (stte->s->prefix, stte->s);
554 stte->indent_len += mutt_strlen (stte->s->prefix);
555 }
556
557 if (stte->tag_level[RICH_EXCERPT])
558 {
559 x = stte->tag_level[RICH_EXCERPT];
560 while (x)
561 {
562 if (stte->s->prefix)
563 {
564 state_puts (stte->s->prefix, stte->s);
565 stte->indent_len += mutt_strlen (stte->s->prefix);
566 }
567 else
568 {
569 state_puts ("> ", stte->s);
570 stte->indent_len += mutt_strlen ("> ");
571 }
572 x--;
573 }
574 }
575 else
576 stte->indent_len = 0;
577 if (stte->tag_level[RICH_INDENT])
578 {
579 x = stte->tag_level[RICH_INDENT] * IndentSize;
580 stte->indent_len += x;
581 while (x)
582 {
583 state_putc (' ', stte->s);
584 x--;
585 }
586 }
587}
588
589static void enriched_flush (struct enriched_state *stte, int wrap)
590{
591 if (!stte->tag_level[RICH_NOFILL] && (stte->line_len + stte->word_len >
592 (stte->WrapMargin - (stte->tag_level[RICH_INDENT_RIGHT] * IndentSize) -
593 stte->indent_len)))
594 enriched_wrap (stte);
595
596 if (stte->buff_used)
597 {
598 stte->buffer[stte->buff_used] = (wchar_t) '\0';
599 stte->line_used += stte->buff_used;
600 if (stte->line_used > stte->line_max)
601 {
602 stte->line_max = stte->line_used;
603 safe_realloc (&stte->line, (stte->line_max + 1) * sizeof (wchar_t));
604 }
605 wcscat (stte->line, stte->buffer);
606 stte->line_len += stte->word_len;
607 stte->word_len = 0;
608 stte->buff_used = 0;
609 }
610 if (wrap)
611 enriched_wrap(stte);
612 fflush (stte->s->fpout);
613}
614
615
616static void enriched_putwc (wchar_t c, struct enriched_state *stte)
617{
618 if (stte->tag_level[RICH_PARAM])
619 {
620 if (stte->tag_level[RICH_COLOR])
621 {
622 if (stte->param_used + 1 >= stte->param_len)
623 safe_realloc (&stte->param, (stte->param_len += STRING) * sizeof (wchar_t));
624
625 stte->param[stte->param_used++] = c;
626 }
627 return; /* nothing to do */
628 }
629
630 /* see if more space is needed (plus extra for possible rich characters) */
631 if (stte->buff_len < stte->buff_used + 3)
632 {
633 stte->buff_len += LONG_STRING;
634 safe_realloc (&stte->buffer, (stte->buff_len + 1) * sizeof (wchar_t));
635 }
636
637 if ((!stte->tag_level[RICH_NOFILL] && iswspace (c)) || c == (wchar_t) '\0')
638 {
639 if (c == (wchar_t) '\t')
640 stte->word_len += 8 - (stte->line_len + stte->word_len) % 8;
641 else
642 stte->word_len++;
643
644 stte->buffer[stte->buff_used++] = c;
645 enriched_flush (stte, 0);
646 }
647 else
648 {
649 if (stte->s->flags & MUTT_DISPLAY)
650 {
651 if (stte->tag_level[RICH_BOLD])
652 {
653 stte->buffer[stte->buff_used++] = c;
654 stte->buffer[stte->buff_used++] = (wchar_t) '\010';
655 stte->buffer[stte->buff_used++] = c;
656 }
657 else if (stte->tag_level[RICH_UNDERLINE])
658 {
659
660 stte->buffer[stte->buff_used++] = '_';
661 stte->buffer[stte->buff_used++] = (wchar_t) '\010';
662 stte->buffer[stte->buff_used++] = c;
663 }
664 else if (stte->tag_level[RICH_ITALIC])
665 {
666 stte->buffer[stte->buff_used++] = c;
667 stte->buffer[stte->buff_used++] = (wchar_t) '\010';
668 stte->buffer[stte->buff_used++] = '_';
669 }
670 else
671 {
672 stte->buffer[stte->buff_used++] = c;
673 }
674 }
675 else
676 {
677 stte->buffer[stte->buff_used++] = c;
678 }
679 stte->word_len++;
680 }
681}
682
683static void enriched_puts (const char *s, struct enriched_state *stte)
684{
685 const char *c;
686
687 if (stte->buff_len < stte->buff_used + mutt_strlen (s))
688 {
689 stte->buff_len += LONG_STRING;
690 safe_realloc (&stte->buffer, (stte->buff_len + 1) * sizeof (wchar_t));
691 }
692 c = s;
693 while (*c)
694 {
695 stte->buffer[stte->buff_used++] = (wchar_t) *c;
696 c++;
697 }
698}
699
700static void enriched_set_flags (const wchar_t *tag, struct enriched_state *stte)
701{
702 const wchar_t *tagptr = tag;
703 int i, j;
704
705 if (*tagptr == (wchar_t) '/')
706 tagptr++;
707
708 for (i = 0, j = -1; EnrichedTags[i].tag_name; i++)
709 if (wcscasecmp (EnrichedTags[i].tag_name, tagptr) == 0)
710 {
711 j = EnrichedTags[i].index;
712 break;
713 }
714
715 if (j != -1)
716 {
717 if (j == RICH_CENTER || j == RICH_FLUSHLEFT || j == RICH_FLUSHRIGHT)
718 enriched_flush (stte, 1);
719
720 if (*tag == (wchar_t) '/')
721 {
722 if (stte->tag_level[j]) /* make sure not to go negative */
723 stte->tag_level[j]--;
724 if ((stte->s->flags & MUTT_DISPLAY) && j == RICH_PARAM && stte->tag_level[RICH_COLOR])
725 {
726 stte->param[stte->param_used] = (wchar_t) '\0';
727 if (!wcscasecmp(L"black", stte->param))
728 {
729 enriched_puts("\033[30m", stte);
730 }
731 else if (!wcscasecmp(L"red", stte->param))
732 {
733 enriched_puts("\033[31m", stte);
734 }
735 else if (!wcscasecmp(L"green", stte->param))
736 {
737 enriched_puts("\033[32m", stte);
738 }
739 else if (!wcscasecmp(L"yellow", stte->param))
740 {
741 enriched_puts("\033[33m", stte);
742 }
743 else if (!wcscasecmp(L"blue", stte->param))
744 {
745 enriched_puts("\033[34m", stte);
746 }
747 else if (!wcscasecmp(L"magenta", stte->param))
748 {
749 enriched_puts("\033[35m", stte);
750 }
751 else if (!wcscasecmp(L"cyan", stte->param))
752 {
753 enriched_puts("\033[36m", stte);
754 }
755 else if (!wcscasecmp(L"white", stte->param))
756 {
757 enriched_puts("\033[37m", stte);
758 }
759 }
760 if ((stte->s->flags & MUTT_DISPLAY) && j == RICH_COLOR)
761 {
762 enriched_puts("\033[0m", stte);
763 }
764
765 /* flush parameter buffer when closing the tag */
766 if (j == RICH_PARAM)
767 {
768 stte->param_used = 0;
769 stte->param[0] = (wchar_t) '\0';
770 }
771 }
772 else
773 stte->tag_level[j]++;
774
775 if (j == RICH_EXCERPT)
776 enriched_flush(stte, 1);
777 }
778}
779
780static int text_enriched_handler (BODY *a, STATE *s)
781{
782 enum {
783 TEXT, LANGLE, TAG, BOGUS_TAG, NEWLINE, ST_EOF, DONE
784 } state = TEXT;
785
786 long bytes = a->length;
787 struct enriched_state stte;
788 wchar_t wc = 0;
789 int tag_len = 0;
790 wchar_t tag[LONG_STRING + 1];
791
792 memset (&stte, 0, sizeof (stte));
793 stte.s = s;
794 stte.WrapMargin = ((s->flags & MUTT_DISPLAY) ? (MuttIndexWindow->cols-4) :
795 ((MuttIndexWindow->cols-4)<72)?(MuttIndexWindow->cols-4):72);
796 stte.line_max = stte.WrapMargin * 4;
797 stte.line = (wchar_t *) safe_calloc (1, (stte.line_max + 1) * sizeof (wchar_t));
798 stte.param = (wchar_t *) safe_calloc (1, (STRING) * sizeof (wchar_t));
799
800 stte.param_len = STRING;
801 stte.param_used = 0;
802
803 if (s->prefix)
804 {
805 state_puts (s->prefix, s);
806 stte.indent_len += mutt_strlen (s->prefix);
807 }
808
809 while (state != DONE)
810 {
811 if (state != ST_EOF)
812 {
813 if (!bytes || (wc = fgetwc (s->fpin)) == WEOF)
814 state = ST_EOF;
815 else
816 bytes--;
817 }
818
819 switch (state)
820 {
821 case TEXT :
822 switch (wc)
823 {
824 case '<' :
825 state = LANGLE;
826 break;
827
828 case '\n' :
829 if (stte.tag_level[RICH_NOFILL])
830 {
831 enriched_flush (&stte, 1);
832 }
833 else
834 {
835 enriched_putwc ((wchar_t) ' ', &stte);
836 state = NEWLINE;
837 }
838 break;
839
840 default:
841 enriched_putwc (wc, &stte);
842 }
843 break;
844
845 case LANGLE :
846 if (wc == (wchar_t) '<')
847 {
848 enriched_putwc (wc, &stte);
849 state = TEXT;
850 break;
851 }
852 else
853 {
854 tag_len = 0;
855 state = TAG;
856 }
857 /* fall through */
858 /* it wasn't a <<, so this char is first in TAG */
859 case TAG :
860 if (wc == (wchar_t) '>')
861 {
862 tag[tag_len] = (wchar_t) '\0';
863 enriched_set_flags (tag, &stte);
864 state = TEXT;
865 }
866 else if (tag_len < LONG_STRING) /* ignore overly long tags */
867 tag[tag_len++] = wc;
868 else
869 state = BOGUS_TAG;
870 break;
871
872 case BOGUS_TAG :
873 if (wc == (wchar_t) '>')
874 state = TEXT;
875 break;
876
877 case NEWLINE :
878 if (wc == (wchar_t) '\n')
879 enriched_flush (&stte, 1);
880 else
881 {
882 ungetwc (wc, s->fpin);
883 bytes++;
884 state = TEXT;
885 }
886 break;
887
888 case ST_EOF :
889 enriched_putwc ((wchar_t) '\0', &stte);
890 enriched_flush (&stte, 1);
891 state = DONE;
892 break;
893
894 case DONE: /* not reached, but gcc complains if this is absent */
895 break;
896 }
897 }
898
899 state_putc ('\n', s); /* add a final newline */
900
901 FREE (&(stte.buffer));
902 FREE (&(stte.line));
903 FREE (&(stte.param));
904
905 return 0;
906}
907
908/* for compatibility with metamail */
909static int is_mmnoask (const char *buf)
910{
911 char tmp[LONG_STRING], *p, *q;
912 int lng;
913
914 if ((p = getenv ("MM_NOASK")) != NULL && *p)
915 {
916 if (mutt_strcmp (p, "1") == 0)
917 return (1);
918
919 strfcpy (tmp, p, sizeof (tmp));
920 p = tmp;
921
922 while ((p = strtok (p, ",")) != NULL)
923 {
924 if ((q = strrchr (p, '/')) != NULL)
925 {
926 if (*(q+1) == '*')
927 {
928 if (ascii_strncasecmp (buf, p, q-p) == 0)
929 return (1);
930 }
931 else
932 {
933 if (ascii_strcasecmp (buf, p) == 0)
934 return (1);
935 }
936 }
937 else
938 {
939 lng = mutt_strlen (p);
940 if (buf[lng] == '/' && mutt_strncasecmp (buf, p, lng) == 0)
941 return (1);
942 }
943
944 p = NULL;
945 }
946 }
947
948 return (0);
949}
950
951/*
952 * Returns:
953 * 1 if the body part should be filtered by a mailcap entry prior to viewing inline.
954 *
955 * 0 otherwise
956 */
957static int mutt_is_autoview (BODY *b)
958{
959 char type[STRING];
960 int is_autoview = 0;
961
962 snprintf (type, sizeof (type), "%s/%s", TYPE (b), b->subtype);
963
964 if (option(OPTIMPLICITAUTOVIEW))
965 {
966 /* $implicit_autoview is essentially the same as "auto_view *" */
967 is_autoview = 1;
968 }
969 else
970 {
971 /* determine if this type is on the user's auto_view list */
972 LIST *t = AutoViewList;
973
974 mutt_check_lookup_list (b, type, sizeof (type));
975 for (; t; t = t->next)
976 {
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)
981 is_autoview = 1;
982 }
983
984 if (is_mmnoask (type))
985 is_autoview = 1;
986 }
987
988 /* determine if there is a mailcap entry suitable for auto_view
989 *
990 * WARNING: type is altered by this call as a result of `mime_lookup' support */
991 if (is_autoview)
992 return rfc1524_mailcap_lookup(b, type, sizeof(type), NULL, MUTT_AUTOVIEW);
993
994 return 0;
995}
996
997#define TXTHTML 1
998#define TXTPLAIN 2
999#define TXTENRICHED 3
1000
1001static int alternative_handler (BODY *a, STATE *s)
1002{
1003 BODY *choice = NULL;
1004 BODY *b;
1005 LIST *t;
1006 int type = 0;
1007 int mustfree = 0;
1008 int rc = 0;
1009
1010 if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE ||
1011 a->encoding == ENCUUENCODED)
1012 {
1013 struct stat st;
1014 mustfree = 1;
1015 fstat (fileno (s->fpin), &st);
1016 b = mutt_new_body ();
1017 b->length = (long) st.st_size;
1018 b->parts = mutt_parse_multipart (s->fpin,
1019 mutt_get_parameter ("boundary", a->parameter),
1020 (long) st.st_size,
1021 ascii_strcasecmp ("digest", a->subtype) == 0);
1022 }
1023 else
1024 b = a;
1025
1026 a = b;
1027
1028 /* First, search list of preferred types */
1029 t = AlternativeOrderList;
1030 while (t && !choice)
1031 {
1032 char *c;
1033 int btlen; /* length of basetype */
1034 int wild; /* do we have a wildcard to match all subtypes? */
1035
1036 c = strchr (t->data, '/');
1037 if (c)
1038 {
1039 wild = (c[1] == '*' && c[2] == 0);
1040 btlen = c - t->data;
1041 }
1042 else
1043 {
1044 wild = 1;
1045 btlen = mutt_strlen (t->data);
1046 }
1047
1048 if (a && a->parts)
1049 b = a->parts;
1050 else
1051 b = a;
1052 while (b)
1053 {
1054 const char *bt = TYPE(b);
1055 if (!ascii_strncasecmp (bt, t->data, btlen) && bt[btlen] == 0)
1056 {
1057 /* the basetype matches */
1058 if (wild || !ascii_strcasecmp (t->data + btlen + 1, b->subtype))
1059 {
1060 choice = b;
1061 }
1062 }
1063 b = b->next;
1064 }
1065 t = t->next;
1066 }
1067
1068 /* Next, look for an autoviewable type */
1069 if (!choice)
1070 {
1071 if (a && a->parts)
1072 b = a->parts;
1073 else
1074 b = a;
1075 while (b)
1076 {
1077 if (mutt_is_autoview (b))
1078 choice = b;
1079 b = b->next;
1080 }
1081 }
1082
1083 /* Then, look for a text entry */
1084 if (!choice)
1085 {
1086 if (a && a->parts)
1087 b = a->parts;
1088 else
1089 b = a;
1090 while (b)
1091 {
1092 if (b->type == TYPETEXT)
1093 {
1094 if (! ascii_strcasecmp ("plain", b->subtype) && type <= TXTPLAIN)
1095 {
1096 choice = b;
1097 type = TXTPLAIN;
1098 }
1099 else if (! ascii_strcasecmp ("enriched", b->subtype) && type <= TXTENRICHED)
1100 {
1101 choice = b;
1102 type = TXTENRICHED;
1103 }
1104 else if (! ascii_strcasecmp ("html", b->subtype) && type <= TXTHTML)
1105 {
1106 choice = b;
1107 type = TXTHTML;
1108 }
1109 }
1110 b = b->next;
1111 }
1112 }
1113
1114 /* Finally, look for other possibilities */
1115 if (!choice)
1116 {
1117 if (a && a->parts)
1118 b = a->parts;
1119 else
1120 b = a;
1121 while (b)
1122 {
1123 if (mutt_can_decode (b))
1124 choice = b;
1125 b = b->next;
1126 }
1127 }
1128
1129 if (choice)
1130 {
1131 if (s->flags & MUTT_DISPLAY && !option (OPTWEED))
1132 {
1133 fseeko (s->fpin, choice->hdr_offset, 0);
1134 mutt_copy_bytes(s->fpin, s->fpout, choice->offset-choice->hdr_offset);
1135 }
1136 mutt_body_handler (choice, s);
1137 }
1138 else if (s->flags & MUTT_DISPLAY)
1139 {
1140 /* didn't find anything that we could display! */
1141 state_mark_attach (s);
1142 state_puts(_("[-- Error: Could not display any parts of Multipart/Alternative! --]\n"), s);
1143 rc = -1;
1144 }
1145
1146 if (mustfree)
1147 mutt_free_body(&a);
1148
1149 return rc;
1150}
1151
1152/* handles message/rfc822 body parts */
1153static int message_handler (BODY *a, STATE *s)
1154{
1155 struct stat st;
1156 BODY *b;
1157 LOFF_T off_start;
1158 int rc = 0;
1159
1160 off_start = ftello (s->fpin);
1161 if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE ||
1162 a->encoding == ENCUUENCODED)
1163 {
1164 fstat (fileno (s->fpin), &st);
1165 b = mutt_new_body ();
1166 b->length = (LOFF_T) st.st_size;
1167 b->parts = mutt_parse_messageRFC822 (s->fpin, b);
1168 }
1169 else
1170 b = a;
1171
1172 if (b->parts)
1173 {
1174 mutt_copy_hdr (s->fpin, s->fpout, off_start, b->parts->offset,
1175 (((s->flags & MUTT_WEED) || ((s->flags & (MUTT_DISPLAY|MUTT_PRINTING)) && option (OPTWEED))) ? (CH_WEED | CH_REORDER) : 0) |
1176 (s->prefix ? CH_PREFIX : 0) | CH_DECODE | CH_FROM |
1177 ((s->flags & MUTT_DISPLAY) ? CH_DISPLAY : 0), s->prefix);
1178
1179 if (s->prefix)
1180 state_puts (s->prefix, s);
1181 state_putc ('\n', s);
1182
1183 rc = mutt_body_handler (b->parts, s);
1184 }
1185
1186 if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE ||
1187 a->encoding == ENCUUENCODED)
1188 mutt_free_body (&b);
1189
1190 return rc;
1191}
1192
1193/* returns 1 if decoding the attachment will produce output */
1194int mutt_can_decode (BODY *a)
1195{
1196 if (mutt_is_autoview (a))
1197 return 1;
1198 else if (a->type == TYPETEXT)
1199 return (1);
1200 else if (a->type == TYPEMESSAGE)
1201 return (1);
1202 else if (a->type == TYPEMULTIPART)
1203 {
1204 BODY *p;
1205
1206 if (WithCrypto)
1207 {
1208 if (ascii_strcasecmp (a->subtype, "signed") == 0 ||
1209 ascii_strcasecmp (a->subtype, "encrypted") == 0)
1210 return (1);
1211 }
1212
1213 for (p = a->parts; p; p = p->next)
1214 {
1215 if (mutt_can_decode (p))
1216 return (1);
1217 }
1218
1219 }
1220 else if (WithCrypto && a->type == TYPEAPPLICATION)
1221 {
1222 if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp(a))
1223 return (1);
1224 if ((WithCrypto & APPLICATION_SMIME) && mutt_is_application_smime(a))
1225 return (1);
1226 }
1227
1228 return (0);
1229}
1230
1231static int multipart_handler (BODY *a, STATE *s)
1232{
1233 BODY *b, *p;
1234 char length[5];
1235 struct stat st;
1236 int count;
1237 int rc = 0;
1238
1239 if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE ||
1240 a->encoding == ENCUUENCODED)
1241 {
1242 fstat (fileno (s->fpin), &st);
1243 b = mutt_new_body ();
1244 b->length = (long) st.st_size;
1245 b->parts = mutt_parse_multipart (s->fpin,
1246 mutt_get_parameter ("boundary", a->parameter),
1247 (long) st.st_size,
1248 ascii_strcasecmp ("digest", a->subtype) == 0);
1249 }
1250 else
1251 b = a;
1252
1253 for (p = b->parts, count = 1; p; p = p->next, count++)
1254 {
1255 if (s->flags & MUTT_DISPLAY)
1256 {
1257 state_mark_attach (s);
1258 state_printf (s, _("[-- Attachment #%d"), count);
1259 if (p->description || p->filename || p->form_name)
1260 {
1261 state_puts (": ", s);
1262 state_puts (p->description ? p->description :
1263 p->filename ? p->filename : p->form_name, s);
1264 }
1265 state_puts (" --]\n", s);
1266
1267 mutt_pretty_size (length, sizeof (length), p->length);
1268
1269 state_mark_attach (s);
1270 state_printf (s, _("[-- Type: %s/%s, Encoding: %s, Size: %s --]\n"),
1271 TYPE (p), p->subtype, ENCODING (p->encoding), length);
1272 if (!option (OPTWEED))
1273 {
1274 fseeko (s->fpin, p->hdr_offset, 0);
1275 mutt_copy_bytes(s->fpin, s->fpout, p->offset-p->hdr_offset);
1276 }
1277 else
1278 state_putc ('\n', s);
1279 }
1280
1281 rc = mutt_body_handler (p, s);
1282 state_putc ('\n', s);
1283
1284 if (rc)
1285 {
1286 mutt_error (_("One or more parts of this message could not be displayed"));
1287 dprint (1, (debugfile, "Failed on attachment #%d, type %s/%s.\n", count, TYPE(p), NONULL (p->subtype)));
1288 }
1289
1290 if ((s->flags & MUTT_REPLYING)
1291 && (option (OPTINCLUDEONLYFIRST)) && (s->flags & MUTT_FIRSTDONE))
1292 break;
1293 }
1294
1295 if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE ||
1296 a->encoding == ENCUUENCODED)
1297 mutt_free_body (&b);
1298
1299 /* make failure of a single part non-fatal */
1300 if (rc < 0)
1301 rc = 1;
1302 return rc;
1303}
1304
1305static int autoview_handler (BODY *a, STATE *s)
1306{
1307 rfc1524_entry *entry = rfc1524_new_entry ();
1308 char buffer[LONG_STRING];
1309 char type[STRING];
1310 BUFFER *command = NULL;
1311 BUFFER *tempfile = NULL;
1312 char *fname;
1313 FILE *fpin = NULL;
1314 FILE *fpout = NULL;
1315 FILE *fperr = NULL;
1316 int piped = FALSE;
1317 pid_t thepid;
1318 int rc = 0;
1319
1320 command = mutt_buffer_pool_get ();
1321 tempfile = mutt_buffer_pool_get ();
1322
1323 snprintf (type, sizeof (type), "%s/%s", TYPE (a), a->subtype);
1324 rfc1524_mailcap_lookup (a, type, sizeof(type), entry, MUTT_AUTOVIEW);
1325
1326 fname = safe_strdup (a->filename);
1327 mutt_sanitize_filename (fname, 1);
1328 mutt_rfc1524_expand_filename (entry->nametemplate, fname, tempfile);
1329 FREE (&fname);
1330
1331 if (entry->command)
1332 {
1333 mutt_buffer_strcpy (command, entry->command);
1334
1335 /* rfc1524_expand_command returns 0 if the file is required */
1336 piped = mutt_rfc1524_expand_command (a, mutt_b2s (tempfile), type, command);
1337
1338 if (s->flags & MUTT_DISPLAY)
1339 {
1340 state_mark_attach (s);
1341 state_printf (s, _("[-- Autoview using %s --]\n"), mutt_b2s (command));
1342 mutt_message(_("Invoking autoview command: %s"), mutt_b2s (command));
1343 }
1344
1345 if ((fpin = safe_fopen (mutt_b2s (tempfile), "w+")) == NULL)
1346 {
1347 mutt_perror ("fopen");
1348 rc = -1;
1349 goto cleanup;
1350 }
1351
1352 mutt_copy_bytes (s->fpin, fpin, a->length);
1353
1354 if (!piped)
1355 {
1356 safe_fclose (&fpin);
1357 thepid = mutt_create_filter (mutt_b2s (command), NULL, &fpout, &fperr);
1358 }
1359 else
1360 {
1361 unlink (mutt_b2s (tempfile));
1362 fflush (fpin);
1363 rewind (fpin);
1364 thepid = mutt_create_filter_fd (mutt_b2s (command), NULL, &fpout, &fperr,
1365 fileno(fpin), -1, -1);
1366 }
1367
1368 if (thepid < 0)
1369 {
1370 mutt_perror _("Can't create filter");
1371 if (s->flags & MUTT_DISPLAY)
1372 {
1373 state_mark_attach (s);
1374 state_printf (s, _("[-- Can't run %s. --]\n"), mutt_b2s (command));
1375 }
1376 rc = -1;
1377 goto bail;
1378 }
1379
1380 if (s->prefix)
1381 {
1382 while (fgets (buffer, sizeof(buffer), fpout) != NULL)
1383 {
1384 state_puts (s->prefix, s);
1385 state_puts (buffer, s);
1386 }
1387 /* check for data on stderr */
1388 if (fgets (buffer, sizeof(buffer), fperr))
1389 {
1390 if (s->flags & MUTT_DISPLAY)
1391 {
1392 state_mark_attach (s);
1393 state_printf (s, _("[-- Autoview stderr of %s --]\n"), mutt_b2s (command));
1394 }
1395
1396 state_puts (s->prefix, s);
1397 state_puts (buffer, s);
1398 while (fgets (buffer, sizeof(buffer), fperr) != NULL)
1399 {
1400 state_puts (s->prefix, s);
1401 state_puts (buffer, s);
1402 }
1403 }
1404 }
1405 else
1406 {
1407 mutt_copy_stream (fpout, s->fpout);
1408 /* Check for stderr messages */
1409 if (fgets (buffer, sizeof(buffer), fperr))
1410 {
1411 if (s->flags & MUTT_DISPLAY)
1412 {
1413 state_mark_attach (s);
1414 state_printf (s, _("[-- Autoview stderr of %s --]\n"),
1415 mutt_b2s (command));
1416 }
1417
1418 state_puts (buffer, s);
1419 mutt_copy_stream (fperr, s->fpout);
1420 }
1421 }
1422
1423 bail:
1424 safe_fclose (&fpout);
1425 safe_fclose (&fperr);
1426
1427 mutt_wait_filter (thepid);
1428 if (piped)
1429 safe_fclose (&fpin);
1430 else
1431 mutt_unlink (mutt_b2s (tempfile));
1432
1433 if (s->flags & MUTT_DISPLAY)
1434 mutt_clear_error ();
1435 }
1436
1437cleanup:
1438 rfc1524_free_entry (&entry);
1439
1440 mutt_buffer_pool_release (&command);
1441 mutt_buffer_pool_release (&tempfile);
1442
1443 return rc;
1444}
1445
1446static int external_body_handler (BODY *b, STATE *s)
1447{
1448 const char *access_type;
1449 const char *expiration;
1450 time_t expire;
1451
1452 access_type = mutt_get_parameter ("access-type", b->parameter);
1453 if (!access_type)
1454 {
1455 if (s->flags & MUTT_DISPLAY)
1456 {
1457 state_mark_attach (s);
1458 state_puts (_("[-- Error: message/external-body has no access-type parameter --]\n"), s);
1459 return 0;
1460 }
1461 else
1462 return -1;
1463 }
1464
1465 expiration = mutt_get_parameter ("expiration", b->parameter);
1466 if (expiration)
1467 expire = mutt_parse_date (expiration, NULL);
1468 else
1469 expire = -1;
1470
1471 if (!ascii_strcasecmp (access_type, "x-mutt-deleted"))
1472 {
1473 if (s->flags & (MUTT_DISPLAY|MUTT_PRINTING))
1474 {
1475 char *length;
1476 char pretty_size[10];
1477
1478 state_mark_attach (s);
1479 state_printf (s, _("[-- This %s/%s attachment "),
1480 TYPE(b->parts), b->parts->subtype);
1481 length = mutt_get_parameter ("length", b->parameter);
1482 if (length)
1483 {
1484 mutt_pretty_size (pretty_size, sizeof (pretty_size),
1485 strtol (length, NULL, 10));
1486 state_printf (s, _("(size %s bytes) "), pretty_size);
1487 }
1488 state_puts (_("has been deleted --]\n"), s);
1489
1490 if (expire != -1)
1491 {
1492 state_mark_attach (s);
1493 state_printf (s, _("[-- on %s --]\n"), expiration);
1494 }
1495 if (b->parts->filename)
1496 {
1497 state_mark_attach (s);
1498 state_printf (s, _("[-- name: %s --]\n"), b->parts->filename);
1499 }
1500
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 , NULL);
1504 }
1505 }
1506 else if (expiration && expire < time(NULL))
1507 {
1508 if (s->flags & MUTT_DISPLAY)
1509 {
1510 state_mark_attach (s);
1511 state_printf (s, _("[-- This %s/%s attachment is not included, --]\n"),
1512 TYPE(b->parts), b->parts->subtype);
1513 state_attach_puts (_("[-- and the indicated external source has --]\n"
1514 "[-- expired. --]\n"), s);
1515
1516 mutt_copy_hdr(s->fpin, s->fpout, ftello (s->fpin), b->parts->offset,
1517 (option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) |
1518 CH_DECODE | CH_DISPLAY, NULL);
1519 }
1520 }
1521 else
1522 {
1523 if (s->flags & MUTT_DISPLAY)
1524 {
1525 state_mark_attach (s);
1526 state_printf (s,
1527 _("[-- This %s/%s attachment is not included, --]\n"),
1528 TYPE (b->parts), b->parts->subtype);
1529 state_mark_attach (s);
1530 state_printf (s,
1531 _("[-- and the indicated access-type %s is unsupported --]\n"),
1532 access_type);
1533 mutt_copy_hdr (s->fpin, s->fpout, ftello (s->fpin), b->parts->offset,
1534 (option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) |
1535 CH_DECODE | CH_DISPLAY, NULL);
1536 }
1537 }
1538
1539 return 0;
1540}
1541
1542void mutt_decode_attachment (BODY *b, STATE *s)
1543{
1544 int istext = mutt_is_text_part (b);
1545 iconv_t cd = (iconv_t)(-1);
1546
1547 if (istext && s->flags & MUTT_CHARCONV)
1548 {
1549 char *charset = mutt_get_parameter ("charset", b->parameter);
1550 if (!charset && AssumedCharset)
1551 charset = mutt_get_default_charset ();
1552 if (charset && Charset)
1553 cd = mutt_iconv_open (Charset, charset, MUTT_ICONV_HOOK_FROM);
1554 }
1555 else if (istext && b->charset)
1556 cd = mutt_iconv_open (Charset, b->charset, MUTT_ICONV_HOOK_FROM);
1557
1558 fseeko (s->fpin, b->offset, 0);
1559 switch (b->encoding)
1560 {
1561 case ENCQUOTEDPRINTABLE:
1562 mutt_decode_quoted (s, b->length, istext || ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b)), cd);
1563 break;
1564 case ENCBASE64:
1565 mutt_decode_base64 (s, b->length, istext || ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b)), cd);
1566 break;
1567 case ENCUUENCODED:
1568 mutt_decode_uuencoded (s, b->length, istext || ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b)), cd);
1569 break;
1570 default:
1571 mutt_decode_xbit (s, b->length, istext || ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b)), cd);
1572 break;
1573 }
1574
1575 if (cd != (iconv_t)(-1))
1576 iconv_close (cd);
1577}
1578
1579/* when generating format=flowed ($text_flowed is set) from format=fixed,
1580 * strip all trailing spaces to improve interoperability;
1581 * if $text_flowed is unset, simply verbatim copy input
1582 */
1583static int text_plain_handler (BODY *b, STATE *s)
1584{
1585 char *buf = NULL;
1586 size_t l = 0, sz = 0;
1587
1588 while ((buf = mutt_read_line (buf, &sz, s->fpin, NULL, 0)))
1589 {
1590 if (mutt_strcmp (buf, "-- ") != 0 && option (OPTTEXTFLOWED))
1591 {
1592 l = mutt_strlen (buf);
1593 while (l > 0 && buf[l-1] == ' ')
1594 buf[--l] = 0;
1595 }
1596 if (s->prefix)
1597 state_puts (s->prefix, s);
1598 state_puts (buf, s);
1599 state_putc ('\n', s);
1600 }
1601
1602 FREE (&buf);
1603 return 0;
1604}
1605
1606static int run_decode_and_handler (BODY *b, STATE *s, handler_t handler, int plaintext)
1607{
1608 int origType;
1609 char *savePrefix = NULL;
1610 FILE *fp = NULL;
1611 BUFFER *tempfile = NULL;
1612 size_t tmplength = 0;
1613 LOFF_T tmpoffset = 0;
1614 int decode = 0;
1615 int rc = 0;
1616
1617 fseeko (s->fpin, b->offset, 0);
1618
1619 /* see if we need to decode this part before processing it */
1620 if (b->encoding == ENCBASE64 || b->encoding == ENCQUOTEDPRINTABLE ||
1621 b->encoding == ENCUUENCODED || plaintext ||
1622 mutt_is_text_part (b)) /* text subtypes may
1623 * require character
1624 * set conversion even
1625 * with 8bit encoding.
1626 */
1627 {
1628 origType = b->type;
1629
1630 if (!plaintext)
1631 {
1632 /* decode to a tempfile, saving the original destination */
1633 fp = s->fpout;
1634 tempfile = mutt_buffer_pool_get ();
1635 mutt_buffer_mktemp (tempfile);
1636 if ((s->fpout = safe_fopen (mutt_b2s (tempfile), "w")) == NULL)
1637 {
1638 mutt_error _("Unable to open temporary file!");
1639 dprint (1, (debugfile, "Can't open %s.\n", mutt_b2s (tempfile)));
1640 mutt_buffer_pool_release (&tempfile);
1641 return -1;
1642 }
1643 /* decoding the attachment changes the size and offset, so save a copy
1644 * of the "real" values now, and restore them after processing
1645 */
1646 tmplength = b->length;
1647 tmpoffset = b->offset;
1648
1649 /* if we are decoding binary bodies, we don't want to prefix each
1650 * line with the prefix or else the data will get corrupted.
1651 */
1652 savePrefix = s->prefix;
1653 s->prefix = NULL;
1654
1655 decode = 1;
1656 }
1657 else
1658 b->type = TYPETEXT;
1659
1660 mutt_decode_attachment (b, s);
1661
1662 if (decode)
1663 {
1664 b->length = ftello (s->fpout);
1665 b->offset = 0;
1666 safe_fclose (&s->fpout);
1667
1668 /* restore final destination and substitute the tempfile for input */
1669 s->fpout = fp;
1670 fp = s->fpin;
1671 s->fpin = fopen (mutt_b2s (tempfile), "r");
1672 unlink (mutt_b2s (tempfile));
1673 mutt_buffer_pool_release (&tempfile);
1674
1675 /* restore the prefix */
1676 s->prefix = savePrefix;
1677 }
1678
1679 b->type = origType;
1680 }
1681
1682 /* process the (decoded) body part */
1683 if (handler)
1684 {
1685 rc = handler (b, s);
1686
1687 if (rc)
1688 {
1689 dprint (1, (debugfile, "Failed on attachment of type %s/%s.\n", TYPE(b), NONULL (b->subtype)));
1690 }
1691
1692 if (decode)
1693 {
1694 b->length = tmplength;
1695 b->offset = tmpoffset;
1696
1697 /* restore the original source stream */
1698 safe_fclose (&s->fpin);
1699 s->fpin = fp;
1700 }
1701 }
1702 s->flags |= MUTT_FIRSTDONE;
1703
1704 return rc;
1705}
1706
1707static int valid_pgp_encrypted_handler (BODY *b, STATE *s)
1708{
1709 int rc;
1710 BODY *octetstream;
1711
1712 octetstream = b->parts->next;
1713
1714 /* clear out any mime headers before the handler, so they can't be
1715 * spoofed. */
1716 mutt_free_envelope (&b->mime_headers);
1717 mutt_free_envelope (&octetstream->mime_headers);
1718
1719 /* Some clients improperly encode the octetstream part. */
1720 if (octetstream->encoding != ENC7BIT)
1721 rc = run_decode_and_handler (octetstream, s, crypt_pgp_encrypted_handler, 0);
1722 else
1723 rc = crypt_pgp_encrypted_handler (octetstream, s);
1724 b->goodsig |= octetstream->goodsig;
1725#ifdef USE_AUTOCRYPT
1726 b->is_autocrypt |= octetstream->is_autocrypt;
1727#endif
1728
1729 /* Relocate protected headers onto the multipart/encrypted part */
1730 if (!rc && octetstream->mime_headers)
1731 {
1732 b->mime_headers = octetstream->mime_headers;
1733 octetstream->mime_headers = NULL;
1734 }
1735
1736 return rc;
1737}
1738
1739static int malformed_pgp_encrypted_handler (BODY *b, STATE *s)
1740{
1741 int rc;
1742 BODY *octetstream;
1743
1744 octetstream = b->parts->next->next;
1745
1746 /* clear out any mime headers before the handler, so they can't be
1747 * spoofed. */
1748 mutt_free_envelope (&b->mime_headers);
1749 mutt_free_envelope (&octetstream->mime_headers);
1750
1751 /* exchange encodes the octet-stream, so re-run it through the decoder */
1752 rc = run_decode_and_handler (octetstream, s, crypt_pgp_encrypted_handler, 0);
1753 b->goodsig |= octetstream->goodsig;
1754#ifdef USE_AUTOCRYPT
1755 b->is_autocrypt |= octetstream->is_autocrypt;
1756#endif
1757
1758 /* Relocate protected headers onto the multipart/encrypted part */
1759 if (!rc && octetstream->mime_headers)
1760 {
1761 b->mime_headers = octetstream->mime_headers;
1762 octetstream->mime_headers = NULL;
1763 }
1764
1765 return rc;
1766}
1767
1768int mutt_body_handler (BODY *b, STATE *s)
1769{
1770 int plaintext = 0;
1771 handler_t handler = NULL, encrypted_handler = NULL;
1772 int rc = 0;
1773
1774 int oflags = s->flags;
1775
1776 /* first determine which handler to use to process this part */
1777
1778 if (mutt_is_autoview (b))
1779 {
1780 handler = autoview_handler;
1781 s->flags &= ~MUTT_CHARCONV;
1782 }
1783 else if (b->type == TYPETEXT)
1784 {
1785 if (ascii_strcasecmp ("plain", b->subtype) == 0)
1786 {
1787 /* avoid copying this part twice since removing the transfer-encoding is
1788 * the only operation needed.
1789 */
1790 if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b))
1791 encrypted_handler = handler = crypt_pgp_application_pgp_handler;
1792 else if (option(OPTREFLOWTEXT) && ascii_strcasecmp ("flowed", mutt_get_parameter ("format", b->parameter)) == 0)
1793 handler = rfc3676_handler;
1794 else
1795 handler = text_plain_handler;
1796 }
1797 else if (ascii_strcasecmp ("enriched", b->subtype) == 0)
1798 handler = text_enriched_handler;
1799 else /* text body type without a handler */
1800 plaintext = 1;
1801 }
1802 else if (b->type == TYPEMESSAGE)
1803 {
1804 if (mutt_is_message_type(b->type, b->subtype))
1805 handler = message_handler;
1806 else if (!ascii_strcasecmp ("delivery-status", b->subtype))
1807 plaintext = 1;
1808 else if (!ascii_strcasecmp ("external-body", b->subtype))
1809 handler = external_body_handler;
1810 }
1811 else if (b->type == TYPEMULTIPART)
1812 {
1813 char *p;
1814
1815 if (ascii_strcasecmp ("alternative", b->subtype) == 0)
1816 handler = alternative_handler;
1817 else if (WithCrypto && ascii_strcasecmp ("signed", b->subtype) == 0)
1818 {
1819 p = mutt_get_parameter ("protocol", b->parameter);
1820
1821 if (!p)
1822 mutt_error _("Error: multipart/signed has no protocol.");
1823 else if (s->flags & MUTT_VERIFY)
1824 handler = mutt_signed_handler;
1825 }
1826 else if (mutt_is_valid_multipart_pgp_encrypted (b))
1827 encrypted_handler = handler = valid_pgp_encrypted_handler;
1828 else if (mutt_is_malformed_multipart_pgp_encrypted (b))
1829 encrypted_handler = handler = malformed_pgp_encrypted_handler;
1830
1831 if (!handler)
1832 handler = multipart_handler;
1833
1834 if (b->encoding != ENC7BIT && b->encoding != ENC8BIT
1835 && b->encoding != ENCBINARY)
1836 {
1837 dprint (1, (debugfile, "Bad encoding type %d for multipart entity, "
1838 "assuming 7 bit\n", b->encoding));
1839 b->encoding = ENC7BIT;
1840 }
1841 }
1842 else if (WithCrypto && b->type == TYPEAPPLICATION)
1843 {
1844 if (option (OPTDONTHANDLEPGPKEYS)
1845 && !ascii_strcasecmp("pgp-keys", b->subtype))
1846 {
1847 /* pass raw part through for key extraction */
1848 plaintext = 1;
1849 }
1850 else if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b))
1851 encrypted_handler = handler = crypt_pgp_application_pgp_handler;
1852 else if ((WithCrypto & APPLICATION_SMIME) && mutt_is_application_smime(b))
1853 encrypted_handler = handler = crypt_smime_application_smime_handler;
1854 }
1855
1856 /* only respect disposition == attachment if we're not
1857 displaying from the attachment menu (i.e. pager) */
1858 if ((!option (OPTHONORDISP) || (b->disposition != DISPATTACH ||
1859 option(OPTVIEWATTACH))) &&
1860 (plaintext || handler))
1861 {
1862 /* Prevent encrypted attachments from being included in replies
1863 * unless $include_encrypted is set. */
1864 if ((s->flags & MUTT_REPLYING) &&
1865 (s->flags & MUTT_FIRSTDONE) &&
1866 encrypted_handler &&
1867 !option (OPTINCLUDEENCRYPTED))
1868 goto cleanup;
1869
1870 rc = run_decode_and_handler (b, s, handler, plaintext);
1871 }
1872 /* print hint to use attachment menu for disposition == attachment
1873 if we're not already being called from there */
1874 else if (s->flags & MUTT_DISPLAY)
1875 {
1876 state_mark_attach (s);
1877 if (option (OPTHONORDISP) && b->disposition == DISPATTACH)
1878 fputs (_("[-- This is an attachment "), s->fpout);
1879 else
1880 state_printf (s, _("[-- %s/%s is unsupported "), TYPE (b), b->subtype);
1881 if (!option (OPTVIEWATTACH))
1882 {
1883 char keystroke[SHORT_STRING];
1884
1885 if (km_expand_key (keystroke, sizeof(keystroke),
1886 km_find_func (MENU_PAGER, OP_VIEW_ATTACHMENTS)))
1887 fprintf (s->fpout, _("(use '%s' to view this part)"), keystroke);
1888 else
1889 fputs (_("(need 'view-attachments' bound to key!)"), s->fpout);
1890 }
1891 fputs (" --]\n", s->fpout);
1892 }
1893
1894cleanup:
1895 s->flags = oflags | (s->flags & MUTT_FIRSTDONE);
1896 if (rc)
1897 {
1898 dprint (1, (debugfile, "Bailing on attachment of type %s/%s.\n", TYPE(b), NONULL (b->subtype)));
1899 }
1900
1901 return rc;
1902}