a1a135394bf298a9336a9cb1e31d1524616639d2
[apps/madmutt.git] / handler.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
4  *
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.
8  */
9
10 #if HAVE_CONFIG_H
11 # include "config.h"
12 #endif
13
14 #include <stdlib.h>
15 #include <string.h>
16 #include <unistd.h>
17 #include <ctype.h>
18 #include <sys/wait.h>
19 #include <sys/stat.h>
20
21 #include <lib-lib/mem.h>
22
23 #include "mutt.h"
24 #include "ascii.h"
25 #include "recvattach.h"
26 #include "handler.h"
27 #include "mutt_curses.h"
28 #include "rfc1524.h"
29 #include "rfc3676.h"
30 #include "keymap.h"
31 #include "mime.h"
32 #include "copy.h"
33 #include "charset.h"
34 #include "mutt_crypt.h"
35 #include "state.h"
36 #include "attach.h"
37 #include "lib.h"
38
39 #include "lib/mem.h"
40 #include "lib/intl.h"
41 #include "lib/str.h"
42 #include "lib/debug.h"
43
44 typedef int handler_f (BODY *, STATE *);
45 typedef handler_f *handler_t;
46
47 int Index_hex[128] = {
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   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
51   0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
52   -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
53   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
54   -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
55   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
56 };
57
58 int Index_64[128] = {
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, -1, -1, -1, -1, -1,
61   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
62   52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
63   -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
64   15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
65   -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
66   41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1
67 };
68
69 void mutt_decode_xbit (STATE * s, long len, int istext, iconv_t cd)
70 {
71   int c, ch;
72   char bufi[BUFI_SIZE];
73   size_t l = 0;
74
75   if (istext) {
76     state_set_prefix (s);
77
78     while ((c = fgetc (s->fpin)) != EOF && len--) {
79       if (c == '\r' && len) {
80         if ((ch = fgetc (s->fpin)) == '\n') {
81           c = ch;
82           len--;
83         }
84         else
85           ungetc (ch, s->fpin);
86       }
87
88       bufi[l++] = c;
89       if (l == sizeof (bufi))
90         mutt_convert_to_state (cd, bufi, &l, s);
91     }
92
93     mutt_convert_to_state (cd, bufi, &l, s);
94     mutt_convert_to_state (cd, 0, 0, s);
95
96     state_reset_prefix (s);
97   }
98   else
99     mutt_copy_bytes (s->fpin, s->fpout, len);
100 }
101
102 static int qp_decode_triple (char *s, char *d)
103 {
104   /* soft line break */
105   if (*s == '=' && !(*(s + 1)))
106     return 1;
107
108   /* quoted-printable triple */
109   if (*s == '=' &&
110       isxdigit ((unsigned char) *(s + 1)) &&
111       isxdigit ((unsigned char) *(s + 2))) {
112     *d = (hexval (*(s + 1)) << 4) | hexval (*(s + 2));
113     return 0;
114   }
115
116   /* something else */
117   return -1;
118 }
119
120 static void qp_decode_line (char *dest, char *src, size_t * l, int last)
121 {
122   char *d, *s;
123   char c;
124
125   int kind;
126   int soft = 0;
127
128   /* decode the line */
129
130   for (d = dest, s = src; *s;) {
131     switch ((kind = qp_decode_triple (s, &c))) {
132     case 0:
133       *d++ = c;
134       s += 3;
135       break;                    /* qp triple */
136     case -1:
137       *d++ = *s++;
138       break;                    /* single character */
139     case 1:
140       soft = 1;
141       s++;
142       break;                    /* soft line break */
143     }
144   }
145
146   if (!soft && last == '\n')
147     *d++ = '\n';
148
149   *d = '\0';
150   *l = d - dest;
151 }
152
153 /* 
154  * Decode an attachment encoded with quoted-printable.
155  * 
156  * Why doesn't this overflow any buffers?  First, it's guaranteed
157  * that the length of a line grows when you _en_-code it to
158  * quoted-printable.  That means that we always can store the
159  * result in a buffer of at most the _same_ size.
160  * 
161  * Now, we don't special-case if the line we read with fgets()
162  * isn't terminated.  We don't care about this, since STRING > 78,
163  * so corrupted input will just be corrupted a bit more.  That
164  * implies that STRING+1 bytes are always sufficient to store the
165  * result of qp_decode_line.
166  * 
167  * Finally, at soft line breaks, some part of a multibyte character
168  * may have been left over by mutt_convert_to_state().  This shouldn't
169  * be more than 6 characters, so STRING + 7 should be sufficient
170  * memory to store the decoded data.
171  * 
172  * Just to make sure that I didn't make some off-by-one error
173  * above, we just use STRING*2 for the target buffer's size.
174  * 
175  */
176
177 void mutt_decode_quoted (STATE * s, long len, int istext, iconv_t cd)
178 {
179   char line[STRING];
180   char decline[2 * STRING];
181   size_t l = 0;
182   size_t linelen;               /* number of input bytes in `line' */
183   size_t l3;
184
185   int last;                     /* store the last character in the input line */
186
187   if (istext)
188     state_set_prefix (s);
189
190   while (len > 0) {
191     last = 0;
192
193     /*
194      * It's ok to use a fixed size buffer for input, even if the line turns
195      * out to be longer than this.  Just process the line in chunks.  This
196      * really shouldn't happen according the MIME spec, since Q-P encoded
197      * lines are at most 76 characters, but we should be liberal about what
198      * we accept.
199      */
200     if (fgets (line, MIN ((ssize_t) sizeof (line), len + 1), s->fpin) == NULL)
201       break;
202
203     linelen = str_len (line);
204     len -= linelen;
205
206     /*
207      * inspect the last character we read so we can tell if we got the
208      * entire line.
209      */
210     last = linelen ? line[linelen - 1] : 0;
211
212     /* chop trailing whitespace if we got the full line */
213     if (last == '\n') {
214       while (linelen > 0 && ISSPACE (line[linelen - 1]))
215         linelen--;
216       line[linelen] = 0;
217     }
218
219     /* decode and do character set conversion */
220     qp_decode_line (decline + l, line, &l3, last);
221     l += l3;
222     mutt_convert_to_state (cd, decline, &l, s);
223   }
224
225   mutt_convert_to_state (cd, 0, 0, s);
226   state_reset_prefix (s);
227 }
228
229 void mutt_decode_base64 (STATE * s, long len, int istext, iconv_t cd)
230 {
231   char buf[5];
232   int c1, c2, c3, c4, ch, cr = 0, i;
233   char bufi[BUFI_SIZE];
234   size_t l = 0;
235
236   buf[4] = 0;
237
238   if (istext)
239     state_set_prefix (s);
240
241   while (len > 0) {
242     for (i = 0; i < 4 && len > 0; len--) {
243       if ((ch = fgetc (s->fpin)) == EOF)
244         break;
245       if (ch >= 0 && ch < 128 && (base64val (ch) != -1 || ch == '='))
246         buf[i++] = ch;
247     }
248     if (i != 4) {
249       debug_print (2, ("didn't get a multiple of 4 chars.\n"));
250       break;
251     }
252
253     c1 = base64val (buf[0]);
254     c2 = base64val (buf[1]);
255     ch = (c1 << 2) | (c2 >> 4);
256
257     if (cr && ch != '\n')
258       bufi[l++] = '\r';
259
260     cr = 0;
261
262     if (istext && ch == '\r')
263       cr = 1;
264     else
265       bufi[l++] = ch;
266
267     if (buf[2] == '=')
268       break;
269     c3 = base64val (buf[2]);
270     ch = ((c2 & 0xf) << 4) | (c3 >> 2);
271
272     if (cr && ch != '\n')
273       bufi[l++] = '\r';
274
275     cr = 0;
276
277     if (istext && ch == '\r')
278       cr = 1;
279     else
280       bufi[l++] = ch;
281
282     if (buf[3] == '=')
283       break;
284     c4 = base64val (buf[3]);
285     ch = ((c3 & 0x3) << 6) | c4;
286
287     if (cr && ch != '\n')
288       bufi[l++] = '\r';
289     cr = 0;
290
291     if (istext && ch == '\r')
292       cr = 1;
293     else
294       bufi[l++] = ch;
295
296     if (l + 8 >= sizeof (bufi))
297       mutt_convert_to_state (cd, bufi, &l, s);
298   }
299
300   if (cr)
301     bufi[l++] = '\r';
302
303   mutt_convert_to_state (cd, bufi, &l, s);
304   mutt_convert_to_state (cd, 0, 0, s);
305
306   state_reset_prefix (s);
307 }
308
309 unsigned char decode_byte (char ch)
310 {
311   if (ch == 96)
312     return 0;
313   return ch - 32;
314 }
315
316 void mutt_decode_uuencoded (STATE * s, long len, int istext, iconv_t cd)
317 {
318   char tmps[SHORT_STRING];
319   char linelen, c, l, out;
320   char *pt;
321   char bufi[BUFI_SIZE];
322   size_t k = 0;
323
324   if (istext)
325     state_set_prefix (s);
326
327   while (len > 0) {
328     if ((fgets (tmps, sizeof (tmps), s->fpin)) == NULL)
329       return;
330     len -= str_len (tmps);
331     if ((!str_ncmp (tmps, "begin", 5)) && ISSPACE (tmps[5]))
332       break;
333   }
334   while (len > 0) {
335     if ((fgets (tmps, sizeof (tmps), s->fpin)) == NULL)
336       return;
337     len -= str_len (tmps);
338     if (!str_ncmp (tmps, "end", 3))
339       break;
340     pt = tmps;
341     linelen = decode_byte (*pt);
342     pt++;
343     for (c = 0; c < linelen;) {
344       for (l = 2; l <= 6; l += 2) {
345         out = decode_byte (*pt) << l;
346         pt++;
347         out |= (decode_byte (*pt) >> (6 - l));
348         bufi[k++] = out;
349         c++;
350         if (c == linelen)
351           break;
352       }
353       mutt_convert_to_state (cd, bufi, &k, s);
354       pt++;
355     }
356   }
357
358   mutt_convert_to_state (cd, bufi, &k, s);
359   mutt_convert_to_state (cd, 0, 0, s);
360
361   state_reset_prefix (s);
362 }
363
364 /* ----------------------------------------------------------------------------
365  * A (not so) minimal implementation of RFC1563.
366  */
367
368 #define IndentSize (4)
369
370 enum { RICH_PARAM = 0, RICH_BOLD, RICH_UNDERLINE, RICH_ITALIC, RICH_NOFILL,
371   RICH_INDENT, RICH_INDENT_RIGHT, RICH_EXCERPT, RICH_CENTER, RICH_FLUSHLEFT,
372   RICH_FLUSHRIGHT, RICH_COLOR, RICH_LAST_TAG
373 };
374
375 static struct {
376   const char *tag_name;
377   int index;
378 } EnrichedTags[] = {
379   {
380   "param", RICH_PARAM}, {
381   "bold", RICH_BOLD}, {
382   "italic", RICH_ITALIC}, {
383   "underline", RICH_UNDERLINE}, {
384   "nofill", RICH_NOFILL}, {
385   "excerpt", RICH_EXCERPT}, {
386   "indent", RICH_INDENT}, {
387   "indentright", RICH_INDENT_RIGHT}, {
388   "center", RICH_CENTER}, {
389   "flushleft", RICH_FLUSHLEFT}, {
390   "flushright", RICH_FLUSHRIGHT}, {
391   "flushboth", RICH_FLUSHLEFT}, {
392   "color", RICH_COLOR}, {
393   "x-color", RICH_COLOR}, {
394   NULL, -1}
395 };
396
397 struct enriched_state {
398   char *buffer;
399   char *line;
400   char *param;
401   size_t buff_len;
402   size_t line_len;
403   size_t line_used;
404   size_t line_max;
405   size_t indent_len;
406   size_t word_len;
407   size_t buff_used;
408   size_t param_used;
409   size_t param_len;
410   int tag_level[RICH_LAST_TAG];
411   int WrapMargin;
412   STATE *s;
413 };
414
415 static void enriched_wrap (struct enriched_state *stte)
416 {
417   int x;
418   int extra;
419
420   if (stte->line_len) {
421     if (stte->tag_level[RICH_CENTER] || stte->tag_level[RICH_FLUSHRIGHT]) {
422       /* Strip trailing white space */
423       size_t y = stte->line_used - 1;
424
425       while (y && ISSPACE (stte->line[y])) {
426         stte->line[y] = '\0';
427         y--;
428         stte->line_used--;
429         stte->line_len--;
430       }
431       if (stte->tag_level[RICH_CENTER]) {
432         /* Strip leading whitespace */
433         y = 0;
434
435         while (stte->line[y] && ISSPACE (stte->line[y]))
436           y++;
437         if (y) {
438           size_t z;
439
440           for (z = y; z <= stte->line_used; z++) {
441             stte->line[z - y] = stte->line[z];
442           }
443
444           stte->line_len -= y;
445           stte->line_used -= y;
446         }
447       }
448     }
449
450     extra = stte->WrapMargin - stte->line_len - stte->indent_len -
451       (stte->tag_level[RICH_INDENT_RIGHT] * IndentSize);
452     if (extra > 0) {
453       if (stte->tag_level[RICH_CENTER]) {
454         x = extra / 2;
455         while (x) {
456           state_putc (' ', stte->s);
457           x--;
458         }
459       }
460       else if (stte->tag_level[RICH_FLUSHRIGHT]) {
461         x = extra - 1;
462         while (x) {
463           state_putc (' ', stte->s);
464           x--;
465         }
466       }
467     }
468     state_puts (stte->line, stte->s);
469   }
470
471   state_putc ('\n', stte->s);
472   stte->line[0] = '\0';
473   stte->line_len = 0;
474   stte->line_used = 0;
475   stte->indent_len = 0;
476   if (stte->s->prefix) {
477     state_puts (stte->s->prefix, stte->s);
478     stte->indent_len += str_len (stte->s->prefix);
479   }
480
481   if (stte->tag_level[RICH_EXCERPT]) {
482     x = stte->tag_level[RICH_EXCERPT];
483     while (x) {
484       if (stte->s->prefix) {
485         state_puts (stte->s->prefix, stte->s);
486         stte->indent_len += str_len (stte->s->prefix);
487       }
488       else {
489         state_puts ("> ", stte->s);
490         stte->indent_len += str_len ("> ");
491       }
492       x--;
493     }
494   }
495   else
496     stte->indent_len = 0;
497   if (stte->tag_level[RICH_INDENT]) {
498     x = stte->tag_level[RICH_INDENT] * IndentSize;
499     stte->indent_len += x;
500     while (x) {
501       state_putc (' ', stte->s);
502       x--;
503     }
504   }
505 }
506
507 static void enriched_flush (struct enriched_state *stte, int wrap)
508 {
509   if (!stte->tag_level[RICH_NOFILL] && (stte->line_len + stte->word_len >
510                                         (stte->WrapMargin -
511                                          (stte->tag_level[RICH_INDENT_RIGHT] *
512                                           IndentSize) - stte->indent_len)))
513     enriched_wrap (stte);
514
515   if (stte->buff_used) {
516     stte->buffer[stte->buff_used] = '\0';
517     stte->line_used += stte->buff_used;
518     if (stte->line_used > stte->line_max) {
519       stte->line_max = stte->line_used;
520       mem_realloc (&stte->line, stte->line_max + 1);
521     }
522     strcat (stte->line, stte->buffer);  /* __STRCAT_CHECKED__ */
523     stte->line_len += stte->word_len;
524     stte->word_len = 0;
525     stte->buff_used = 0;
526   }
527   if (wrap)
528     enriched_wrap (stte);
529 }
530
531
532 static void enriched_putc (int c, struct enriched_state *stte)
533 {
534   if (stte->tag_level[RICH_PARAM]) {
535     if (stte->tag_level[RICH_COLOR]) {
536       if (stte->param_used + 1 >= stte->param_len)
537         mem_realloc (&stte->param, (stte->param_len += STRING));
538
539       stte->param[stte->param_used++] = c;
540     }
541     return;                     /* nothing to do */
542   }
543
544   /* see if more space is needed (plus extra for possible rich characters) */
545   if (stte->buff_len < stte->buff_used + 3) {
546     stte->buff_len += LONG_STRING;
547     mem_realloc (&stte->buffer, stte->buff_len + 1);
548   }
549
550   if ((!stte->tag_level[RICH_NOFILL] && ISSPACE (c)) || c == '\0') {
551     if (c == '\t')
552       stte->word_len += 8 - (stte->line_len + stte->word_len) % 8;
553     else
554       stte->word_len++;
555
556     stte->buffer[stte->buff_used++] = c;
557     enriched_flush (stte, 0);
558   }
559   else {
560     if (stte->s->flags & M_DISPLAY) {
561       if (stte->tag_level[RICH_BOLD]) {
562         stte->buffer[stte->buff_used++] = c;
563         stte->buffer[stte->buff_used++] = '\010';
564         stte->buffer[stte->buff_used++] = c;
565       }
566       else if (stte->tag_level[RICH_UNDERLINE]) {
567
568         stte->buffer[stte->buff_used++] = '_';
569         stte->buffer[stte->buff_used++] = '\010';
570         stte->buffer[stte->buff_used++] = c;
571       }
572       else if (stte->tag_level[RICH_ITALIC]) {
573         stte->buffer[stte->buff_used++] = c;
574         stte->buffer[stte->buff_used++] = '\010';
575         stte->buffer[stte->buff_used++] = '_';
576       }
577       else {
578         stte->buffer[stte->buff_used++] = c;
579       }
580     }
581     else {
582       stte->buffer[stte->buff_used++] = c;
583     }
584     stte->word_len++;
585   }
586 }
587
588 static void enriched_puts (const char *s, struct enriched_state *stte)
589 {
590   const char *p;
591
592   if (stte->buff_len < stte->buff_used + str_len (s)) {
593     stte->buff_len += LONG_STRING;
594     mem_realloc (&stte->buffer, stte->buff_len + 1);
595   }
596
597   p = s;
598   while (*p) {
599     stte->buffer[stte->buff_used++] = *p++;
600   }
601 }
602
603 static void enriched_set_flags (const char *tag, struct enriched_state *stte)
604 {
605   const char *tagptr = tag;
606   int i, j;
607
608   if (*tagptr == '/')
609     tagptr++;
610
611   for (i = 0, j = -1; EnrichedTags[i].tag_name; i++)
612     if (ascii_strcasecmp (EnrichedTags[i].tag_name, tagptr) == 0) {
613       j = EnrichedTags[i].index;
614       break;
615     }
616
617   if (j != -1) {
618     if (j == RICH_CENTER || j == RICH_FLUSHLEFT || j == RICH_FLUSHRIGHT)
619       enriched_flush (stte, 1);
620
621     if (*tag == '/') {
622       if (stte->tag_level[j])   /* make sure not to go negative */
623         stte->tag_level[j]--;
624       if ((stte->s->flags & M_DISPLAY) && j == RICH_PARAM
625           && stte->tag_level[RICH_COLOR]) {
626         stte->param[stte->param_used] = '\0';
627         if (!ascii_strcasecmp (stte->param, "black")) {
628           enriched_puts ("\033[30m", stte);
629         }
630         else if (!ascii_strcasecmp (stte->param, "red")) {
631           enriched_puts ("\033[31m", stte);
632         }
633         else if (!ascii_strcasecmp (stte->param, "green")) {
634           enriched_puts ("\033[32m", stte);
635         }
636         else if (!ascii_strcasecmp (stte->param, "yellow")) {
637           enriched_puts ("\033[33m", stte);
638         }
639         else if (!ascii_strcasecmp (stte->param, "blue")) {
640           enriched_puts ("\033[34m", stte);
641         }
642         else if (!ascii_strcasecmp (stte->param, "magenta")) {
643           enriched_puts ("\033[35m", stte);
644         }
645         else if (!ascii_strcasecmp (stte->param, "cyan")) {
646           enriched_puts ("\033[36m", stte);
647         }
648         else if (!ascii_strcasecmp (stte->param, "white")) {
649           enriched_puts ("\033[37m", stte);
650         }
651       }
652       if ((stte->s->flags & M_DISPLAY) && j == RICH_COLOR) {
653         enriched_puts ("\033[0m", stte);
654       }
655
656       /* flush parameter buffer when closing the tag */
657       if (j == RICH_PARAM) {
658         stte->param_used = 0;
659         stte->param[0] = '\0';
660       }
661     }
662     else
663       stte->tag_level[j]++;
664
665     if (j == RICH_EXCERPT)
666       enriched_flush (stte, 1);
667   }
668 }
669
670 int text_enriched_handler (BODY * a, STATE * s)
671 {
672   enum {
673     TEXT, LANGLE, TAG, BOGUS_TAG, NEWLINE, ST_EOF, DONE
674   } state = TEXT;
675
676   long bytes = a->length;
677   struct enriched_state stte;
678   int c = 0;
679   int tag_len = 0;
680   char tag[LONG_STRING + 1];
681
682   memset (&stte, 0, sizeof (stte));
683   stte.s = s;
684   stte.WrapMargin =
685     ((s->flags & M_DISPLAY) ? (COLS - 4) : ((COLS - 4) <
686                                             72) ? (COLS - 4) : 72);
687   stte.line_max = stte.WrapMargin * 4;
688   stte.line = (char *) mem_calloc (1, stte.line_max + 1);
689   stte.param = (char *) mem_calloc (1, STRING);
690
691   stte.param_len = STRING;
692   stte.param_used = 0;
693
694   if (s->prefix) {
695     state_puts (s->prefix, s);
696     stte.indent_len += str_len (s->prefix);
697   }
698
699   while (state != DONE) {
700     if (state != ST_EOF) {
701       if (!bytes || (c = fgetc (s->fpin)) == EOF)
702         state = ST_EOF;
703       else
704         bytes--;
705     }
706
707     switch (state) {
708     case TEXT:
709       switch (c) {
710       case '<':
711         state = LANGLE;
712         break;
713
714       case '\n':
715         if (stte.tag_level[RICH_NOFILL]) {
716           enriched_flush (&stte, 1);
717         }
718         else {
719           enriched_putc (' ', &stte);
720           state = NEWLINE;
721         }
722         break;
723
724       default:
725         enriched_putc (c, &stte);
726       }
727       break;
728
729     case LANGLE:
730       if (c == '<') {
731         enriched_putc (c, &stte);
732         state = TEXT;
733         break;
734       }
735       else {
736         tag_len = 0;
737         state = TAG;
738       }
739       /* Yes, fall through (it wasn't a <<, so this char is first in TAG) */
740     case TAG:
741       if (c == '>') {
742         tag[tag_len] = '\0';
743         enriched_set_flags (tag, &stte);
744         state = TEXT;
745       }
746       else if (tag_len < LONG_STRING)   /* ignore overly long tags */
747         tag[tag_len++] = c;
748       else
749         state = BOGUS_TAG;
750       break;
751
752     case BOGUS_TAG:
753       if (c == '>')
754         state = TEXT;
755       break;
756
757     case NEWLINE:
758       if (c == '\n')
759         enriched_flush (&stte, 1);
760       else {
761         ungetc (c, s->fpin);
762         bytes++;
763         state = TEXT;
764       }
765       break;
766
767     case ST_EOF:
768       enriched_putc ('\0', &stte);
769       enriched_flush (&stte, 1);
770       state = DONE;
771       break;
772
773     case DONE:                 /* not reached, but gcc complains if this is absent */
774       break;
775     }
776   }
777
778   state_putc ('\n', s);         /* add a final newline */
779
780   p_delete(&(stte.buffer));
781   p_delete(&(stte.line));
782   p_delete(&(stte.param));
783
784   return (0);
785 }
786
787 #define TXTHTML     1
788 #define TXTPLAIN    2
789 #define TXTENRICHED 3
790
791 static int alternative_handler (BODY * a, STATE * s)
792 {
793   BODY *choice = NULL;
794   BODY *b;
795   LIST *t;
796   char buf[STRING];
797   int type = 0;
798   int mustfree = 0;
799   int rc = 0;
800
801   if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE ||
802       a->encoding == ENCUUENCODED) {
803     struct stat st;
804
805     mustfree = 1;
806     fstat (fileno (s->fpin), &st);
807     b = mutt_new_body ();
808     b->length = (long) st.st_size;
809     b->parts = mutt_parse_multipart (s->fpin,
810                                      mutt_get_parameter ("boundary",
811                                                          a->parameter),
812                                      (long) st.st_size,
813                                      ascii_strcasecmp ("digest",
814                                                        a->subtype) == 0);
815   }
816   else
817     b = a;
818
819   a = b;
820
821   /* First, search list of prefered types */
822   t = AlternativeOrderList;
823   while (t && !choice) {
824     char *c;
825     int btlen;                  /* length of basetype */
826     int wild;                   /* do we have a wildcard to match all subtypes? */
827
828     c = strchr (t->data, '/');
829     if (c) {
830       wild = (c[1] == '*' && c[2] == 0);
831       btlen = c - t->data;
832     }
833     else {
834       wild = 1;
835       btlen = str_len (t->data);
836     }
837
838     if (a && a->parts)
839       b = a->parts;
840     else
841       b = a;
842     while (b) {
843       const char *bt = TYPE (b);
844
845       if (!ascii_strncasecmp (bt, t->data, btlen) && bt[btlen] == 0) {
846         /* the basetype matches */
847         if (wild || !ascii_strcasecmp (t->data + btlen + 1, b->subtype)) {
848           choice = b;
849         }
850       }
851       b = b->next;
852     }
853     t = t->next;
854   }
855
856   /* Next, look for an autoviewable type */
857   if (!choice) {
858     if (a && a->parts)
859       b = a->parts;
860     else
861       b = a;
862     while (b) {
863       snprintf (buf, sizeof (buf), "%s/%s", TYPE (b), b->subtype);
864       if (mutt_is_autoview (b, buf)) {
865         rfc1524_entry *entry = rfc1524_new_entry ();
866
867         if (rfc1524_mailcap_lookup (b, buf, entry, M_AUTOVIEW)) {
868           choice = b;
869         }
870         rfc1524_free_entry (&entry);
871       }
872       b = b->next;
873     }
874   }
875
876   /* Then, look for a text entry */
877   if (!choice) {
878     if (a && a->parts)
879       b = a->parts;
880     else
881       b = a;
882     while (b) {
883       if (b->type == TYPETEXT) {
884         if (!ascii_strcasecmp ("plain", b->subtype) && type <= TXTPLAIN) {
885           choice = b;
886           type = TXTPLAIN;
887         }
888         else if (!ascii_strcasecmp ("enriched", b->subtype)
889                  && type <= TXTENRICHED) {
890           choice = b;
891           type = TXTENRICHED;
892         }
893         else if (!ascii_strcasecmp ("html", b->subtype) && type <= TXTHTML) {
894           choice = b;
895           type = TXTHTML;
896         }
897       }
898       b = b->next;
899     }
900   }
901
902   /* Finally, look for other possibilities */
903   if (!choice) {
904     if (a && a->parts)
905       b = a->parts;
906     else
907       b = a;
908     while (b) {
909       if (mutt_can_decode (b))
910         choice = b;
911       b = b->next;
912     }
913   }
914
915   if (choice) {
916     if (s->flags & M_DISPLAY && !option (OPTWEED)) {
917       fseeko (s->fpin, choice->hdr_offset, 0);
918       mutt_copy_bytes (s->fpin, s->fpout,
919                        choice->offset - choice->hdr_offset);
920     }
921     mutt_body_handler (choice, s);
922   }
923   else if (s->flags & M_DISPLAY) {
924     /* didn't find anything that we could display! */
925     state_mark_attach (s);
926     state_puts (_("[-- Error:  Could not display any parts of Multipart/Alternative! --]\n"), s);
927     rc = -1;
928   }
929
930   if (mustfree)
931     mutt_free_body (&a);
932
933   return (rc);
934 }
935
936 /* handles message/rfc822 body parts */
937 static int message_handler (BODY * a, STATE * s)
938 {
939   struct stat st;
940   BODY *b;
941   off_t off_start;
942   int rc = 0;
943
944   off_start = ftello (s->fpin);
945   if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE ||
946       a->encoding == ENCUUENCODED) {
947     fstat (fileno (s->fpin), &st);
948     b = mutt_new_body ();
949     b->length = (long) st.st_size;
950     b->parts = mutt_parse_messageRFC822 (s->fpin, b);
951   }
952   else
953     b = a;
954
955   if (b->parts) {
956     mutt_copy_hdr (s->fpin, s->fpout, off_start, b->parts->offset,
957                    (((s->flags & M_WEED)
958                      || ((s->flags & (M_DISPLAY | M_PRINTING))
959                          && option (OPTWEED))) ? (CH_WEED | CH_REORDER) : 0) |
960                    (s->prefix ? CH_PREFIX : 0) | CH_DECODE | CH_FROM,
961                    s->prefix);
962
963     if (s->prefix)
964       state_puts (s->prefix, s);
965     state_putc ('\n', s);
966
967     rc = mutt_body_handler (b->parts, s);
968   }
969
970   if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE ||
971       a->encoding == ENCUUENCODED)
972     mutt_free_body (&b);
973
974   return (rc);
975 }
976
977 /* returns 1 if decoding the attachment will produce output */
978 int mutt_can_decode (BODY * a)
979 {
980   char type[STRING];
981
982   snprintf (type, sizeof (type), "%s/%s", TYPE (a), a->subtype);
983   if (mutt_is_autoview (a, type))
984     return (rfc1524_mailcap_lookup (a, type, NULL, M_AUTOVIEW));
985   else if (a->type == TYPETEXT)
986     return (1);
987   else if (a->type == TYPEMESSAGE)
988     return (1);
989   else if (a->type == TYPEMULTIPART) {
990     BODY *p;
991
992     if (WithCrypto) {
993       if (ascii_strcasecmp (a->subtype, "signed") == 0 ||
994           ascii_strcasecmp (a->subtype, "encrypted") == 0)
995         return (1);
996     }
997
998     for (p = a->parts; p; p = p->next) {
999       if (mutt_can_decode (p))
1000         return (1);
1001     }
1002
1003   }
1004   else if (WithCrypto && a->type == TYPEAPPLICATION) {
1005     if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (a))
1006       return (1);
1007     if ((WithCrypto & APPLICATION_SMIME) && mutt_is_application_smime (a))
1008       return (1);
1009   }
1010
1011   return (0);
1012 }
1013
1014 static int multipart_handler (BODY * a, STATE * s)
1015 {
1016   BODY *b, *p;
1017   char length[5];
1018   struct stat st;
1019   int count;
1020   int rc = 0;
1021
1022   if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE ||
1023       a->encoding == ENCUUENCODED) {
1024     fstat (fileno (s->fpin), &st);
1025     b = mutt_new_body ();
1026     b->length = (long) st.st_size;
1027     b->parts = mutt_parse_multipart (s->fpin,
1028                                      mutt_get_parameter ("boundary",
1029                                                          a->parameter),
1030                                      (long) st.st_size,
1031                                      ascii_strcasecmp ("digest",
1032                                                        a->subtype) == 0);
1033   }
1034   else
1035     b = a;
1036
1037   for (p = b->parts, count = 1; p; p = p->next, count++) {
1038     if (s->flags & M_DISPLAY) {
1039       state_mark_attach (s);
1040       state_printf (s, _("[-- Attachment #%d"), count);
1041       if (p->description || p->filename || p->form_name) {
1042         state_puts (": ", s);
1043         state_puts (p->description ? p->description :
1044                     p->filename ? p->filename : p->form_name, s);
1045       }
1046       state_puts (" --]\n", s);
1047
1048       mutt_pretty_size (length, sizeof (length), p->length);
1049
1050       state_mark_attach (s);
1051       state_printf (s, _("[-- Type: %s/%s, Encoding: %s, Size: %s --]\n"),
1052                     TYPE (p), p->subtype, ENCODING (p->encoding), length);
1053       if (!option (OPTWEED)) {
1054         fseeko (s->fpin, p->hdr_offset, 0);
1055         mutt_copy_bytes (s->fpin, s->fpout, p->offset - p->hdr_offset);
1056       }
1057       else
1058         state_putc ('\n', s);
1059     }
1060     else {
1061       if (p->description && mutt_can_decode (p))
1062         state_printf (s, "Content-Description: %s\n", p->description);
1063
1064       if (p->form_name)
1065         state_printf (s, "%s: \n", p->form_name);
1066
1067     }
1068     rc = mutt_body_handler (p, s);
1069     state_putc ('\n', s);
1070     if (rc || ((s->flags & M_REPLYING)
1071         && (option (OPTINCLUDEONLYFIRST)) && (s->flags & M_FIRSTDONE)))
1072       break;
1073   }
1074
1075   if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE ||
1076       a->encoding == ENCUUENCODED)
1077     mutt_free_body (&b);
1078
1079   return (rc);
1080 }
1081
1082 static int autoview_handler (BODY * a, STATE * s)
1083 {
1084   rfc1524_entry *entry = rfc1524_new_entry ();
1085   char buffer[LONG_STRING];
1086   char type[STRING];
1087   char command[LONG_STRING];
1088   char tempfile[_POSIX_PATH_MAX] = "";
1089   char *fname;
1090   FILE *fpin = NULL;
1091   FILE *fpout = NULL;
1092   FILE *fperr = NULL;
1093   int piped = FALSE;
1094   pid_t thepid;
1095   int rc = 0;
1096
1097   snprintf (type, sizeof (type), "%s/%s", TYPE (a), a->subtype);
1098   rfc1524_mailcap_lookup (a, type, entry, M_AUTOVIEW);
1099
1100   fname = str_dup (a->filename);
1101   mutt_sanitize_filename (fname, 1);
1102   rfc1524_expand_filename (entry->nametemplate, fname, tempfile,
1103                            sizeof (tempfile));
1104   p_delete(&fname);
1105
1106   if (entry->command) {
1107     strfcpy (command, entry->command, sizeof (command));
1108
1109     /* rfc1524_expand_command returns 0 if the file is required */
1110     piped =
1111       rfc1524_expand_command (a, tempfile, type, command, sizeof (command));
1112
1113     if (s->flags & M_DISPLAY) {
1114       state_mark_attach (s);
1115       state_printf (s, _("[-- Autoview using %s --]\n"), command);
1116       mutt_message (_("Invoking autoview command: %s"), command);
1117     }
1118
1119     if ((fpin = safe_fopen (tempfile, "w+")) == NULL) {
1120       mutt_perror ("fopen");
1121       rfc1524_free_entry (&entry);
1122       return (-1);
1123     }
1124
1125     mutt_copy_bytes (s->fpin, fpin, a->length);
1126
1127     if (!piped) {
1128       safe_fclose (&fpin);
1129       thepid = mutt_create_filter (command, NULL, &fpout, &fperr);
1130     }
1131     else {
1132       unlink (tempfile);
1133       fflush (fpin);
1134       rewind (fpin);
1135       thepid = mutt_create_filter_fd (command, NULL, &fpout, &fperr,
1136                                       fileno (fpin), -1, -1);
1137     }
1138
1139     if (thepid < 0) {
1140       mutt_perror (_("Can't create filter"));
1141
1142       if (s->flags & M_DISPLAY) {
1143         state_mark_attach (s);
1144         state_printf (s, _("[-- Can't run %s. --]\n"), command);
1145       }
1146       rc = -1;
1147       goto bail;
1148     }
1149
1150     if (s->prefix) {
1151       while (fgets (buffer, sizeof (buffer), fpout) != NULL) {
1152         state_puts (s->prefix, s);
1153         state_puts (buffer, s);
1154       }
1155       /* check for data on stderr */
1156       if (fgets (buffer, sizeof (buffer), fperr)) {
1157         if (s->flags & M_DISPLAY) {
1158           state_mark_attach (s);
1159           state_printf (s, _("[-- Autoview stderr of %s --]\n"), command);
1160         }
1161
1162         state_puts (s->prefix, s);
1163         state_puts (buffer, s);
1164         while (fgets (buffer, sizeof (buffer), fperr) != NULL) {
1165           state_puts (s->prefix, s);
1166           state_puts (buffer, s);
1167         }
1168       }
1169     }
1170     else {
1171       mutt_copy_stream (fpout, s->fpout);
1172       /* Check for stderr messages */
1173       if (fgets (buffer, sizeof (buffer), fperr)) {
1174         if (s->flags & M_DISPLAY) {
1175           state_mark_attach (s);
1176           state_printf (s, _("[-- Autoview stderr of %s --]\n"), command);
1177         }
1178
1179         state_puts (buffer, s);
1180         mutt_copy_stream (fperr, s->fpout);
1181       }
1182     }
1183
1184   bail:
1185     safe_fclose (&fpout);
1186     safe_fclose (&fperr);
1187
1188     mutt_wait_filter (thepid);
1189     if (piped)
1190       safe_fclose (&fpin);
1191     else
1192       mutt_unlink (tempfile);
1193
1194     if (s->flags & M_DISPLAY)
1195       mutt_clear_error ();
1196   }
1197   rfc1524_free_entry (&entry);
1198   return (rc);
1199 }
1200
1201 static int external_body_handler (BODY * b, STATE * s)
1202 {
1203   const char *access_type;
1204   const char *expiration;
1205   time_t expire;
1206
1207   access_type = mutt_get_parameter ("access-type", b->parameter);
1208   if (!access_type) {
1209     if (s->flags & M_DISPLAY) {
1210       state_mark_attach (s);
1211       state_puts (_("[-- Error: message/external-body has no access-type parameter --]\n"), s);
1212     }
1213     return (-1);
1214   }
1215
1216   expiration = mutt_get_parameter ("expiration", b->parameter);
1217   if (expiration)
1218     expire = mutt_parse_date (expiration, NULL);
1219   else
1220     expire = -1;
1221
1222   if (!ascii_strcasecmp (access_type, "x-mutt-deleted")) {
1223     if (s->flags & (M_DISPLAY | M_PRINTING)) {
1224       char *length;
1225       char pretty_size[10];
1226
1227       state_mark_attach (s);
1228       state_printf (s, _("[-- This %s/%s attachment "),
1229                     TYPE (b->parts), b->parts->subtype);
1230       length = mutt_get_parameter ("length", b->parameter);
1231       if (length) {
1232         mutt_pretty_size (pretty_size, sizeof (pretty_size),
1233                           strtol (length, NULL, 10));
1234         state_printf (s, _("(size %s bytes) "), pretty_size);
1235       }
1236       state_puts (_("has been deleted --]\n"), s);
1237
1238       if (expire != -1) {
1239         state_mark_attach (s);
1240         state_printf (s, _("[-- on %s --]\n"), expiration);
1241       }
1242       if (b->parts->filename) {
1243         state_mark_attach (s);
1244         state_printf (s, _("[-- name: %s --]\n"), b->parts->filename);
1245       }
1246
1247       mutt_copy_hdr (s->fpin, s->fpout, ftello (s->fpin), b->parts->offset,
1248                      (option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) |
1249                      CH_DECODE, NULL);
1250     }
1251   }
1252   else if (expiration && expire < time (NULL)) {
1253     if (s->flags & M_DISPLAY) {
1254       state_mark_attach (s);
1255       state_printf (s, _("[-- This %s/%s attachment is not included, --]\n"),
1256                     TYPE (b->parts), b->parts->subtype);
1257       state_attach_puts (_("[-- and the indicated external source has --]\n"
1258                            "[-- expired. --]\n"), s);
1259
1260       mutt_copy_hdr (s->fpin, s->fpout, ftello (s->fpin), b->parts->offset,
1261                      (option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) |
1262                      CH_DECODE, NULL);
1263     }
1264   }
1265   else {
1266     if (s->flags & M_DISPLAY) {
1267       state_mark_attach (s);
1268       state_printf (s,
1269                     _("[-- This %s/%s attachment is not included, --]\n"),
1270                     TYPE (b->parts), b->parts->subtype);
1271       state_mark_attach (s);
1272       state_printf (s,
1273                     _
1274                     ("[-- and the indicated access-type %s is unsupported --]\n"),
1275                     access_type);
1276       mutt_copy_hdr (s->fpin, s->fpout, ftello (s->fpin), b->parts->offset,
1277                      (option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) |
1278                      CH_DECODE, NULL);
1279     }
1280   }
1281   return (0);
1282 }
1283
1284 void mutt_decode_attachment (BODY * b, STATE * s)
1285 {
1286   int istext = mutt_is_text_part (b);
1287   iconv_t cd = (iconv_t) (-1);
1288
1289   Quotebuf[0] = '\0';
1290
1291   if (istext) {
1292     if (s->flags & M_CHARCONV) {
1293       const char *charset = mutt_get_parameter ("charset", b->parameter);
1294
1295       if (!option (OPTSTRICTMIME) && !charset)
1296         charset = mutt_get_first_charset (AssumedCharset);
1297       if (charset && Charset)
1298         cd = mutt_iconv_open (Charset, charset, M_ICONV_HOOK_FROM);
1299     }
1300     else {
1301       if (b->file_charset)
1302         cd = mutt_iconv_open (Charset, b->file_charset, M_ICONV_HOOK_FROM);
1303     }
1304   }
1305
1306   fseeko (s->fpin, b->offset, 0);
1307   switch (b->encoding) {
1308   case ENCQUOTEDPRINTABLE:
1309     mutt_decode_quoted (s, b->length, istext ||
1310                         ((WithCrypto & APPLICATION_PGP) &&
1311                          mutt_is_application_pgp (b)), cd);
1312     break;
1313   case ENCBASE64:
1314     mutt_decode_base64 (s, b->length, istext ||
1315                         ((WithCrypto & APPLICATION_PGP) &&
1316                          mutt_is_application_pgp (b)), cd);
1317     break;
1318   case ENCUUENCODED:
1319     mutt_decode_uuencoded (s, b->length, istext
1320                            || ((WithCrypto & APPLICATION_PGP) &&
1321                                mutt_is_application_pgp (b)), cd);
1322     break;
1323   default:
1324     mutt_decode_xbit (s, b->length, istext
1325                       || ((WithCrypto & APPLICATION_PGP) &&
1326                           mutt_is_application_pgp (b)), cd);
1327     break;
1328   }
1329
1330   if (cd != (iconv_t) (-1))
1331     iconv_close (cd);
1332 }
1333
1334 int mutt_body_handler (BODY * b, STATE * s)
1335 {
1336   int decode = 0;
1337   int plaintext = 0;
1338   FILE *fp = NULL;
1339   char tempfile[_POSIX_PATH_MAX];
1340   handler_t handler = NULL;
1341   long tmpoffset = 0;
1342   size_t tmplength = 0;
1343   char type[STRING];
1344   int rc = 0;
1345
1346   int oflags = s->flags;
1347
1348   /* first determine which handler to use to process this part */
1349
1350   snprintf (type, sizeof (type), "%s/%s", TYPE (b), b->subtype);
1351   if (mutt_is_autoview (b, type)) {
1352     rfc1524_entry *entry = rfc1524_new_entry ();
1353
1354     if (rfc1524_mailcap_lookup (b, type, entry, M_AUTOVIEW)) {
1355       handler = autoview_handler;
1356       s->flags &= ~M_CHARCONV;
1357     }
1358     rfc1524_free_entry (&entry);
1359   }
1360   else if (b->type == TYPETEXT) {
1361     if (ascii_strcasecmp ("plain", b->subtype) == 0) {
1362       /* avoid copying this part twice since removing the transfer-encoding is
1363        * the only operation needed.
1364        */
1365       if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b))
1366         handler = crypt_pgp_application_pgp_handler;
1367       else
1368         if (ascii_strcasecmp
1369             ("flowed", mutt_get_parameter ("format", b->parameter)) == 0)
1370         handler = rfc3676_handler;
1371       else
1372         plaintext = 1;
1373     }
1374     else if (ascii_strcasecmp ("enriched", b->subtype) == 0)
1375       handler = text_enriched_handler;
1376     else                        /* text body type without a handler */
1377       plaintext = 1;
1378   }
1379   else if (b->type == TYPEMESSAGE) {
1380     if (mutt_is_message_type (b->type, b->subtype))
1381       handler = message_handler;
1382     else if (!ascii_strcasecmp ("delivery-status", b->subtype))
1383       plaintext = 1;
1384     else if (!ascii_strcasecmp ("external-body", b->subtype))
1385       handler = external_body_handler;
1386   }
1387   else if (b->type == TYPEMULTIPART) {
1388     char *p;
1389
1390     if (ascii_strcasecmp ("alternative", b->subtype) == 0)
1391       handler = alternative_handler;
1392     else if (WithCrypto && ascii_strcasecmp ("signed", b->subtype) == 0) {
1393       p = mutt_get_parameter ("protocol", b->parameter);
1394
1395       if (!p)
1396         mutt_error (_("Error: multipart/signed has no protocol."));
1397
1398       else if (s->flags & M_VERIFY)
1399         handler = mutt_signed_handler;
1400     }
1401     else if ((WithCrypto & APPLICATION_PGP)
1402              && str_casecmp ("encrypted", b->subtype) == 0) {
1403       p = mutt_get_parameter ("protocol", b->parameter);
1404
1405       if (!p)
1406         mutt_error (_
1407                     ("Error: multipart/encrypted has no protocol parameter!"));
1408
1409       else if (ascii_strcasecmp ("application/pgp-encrypted", p) == 0)
1410         handler = crypt_pgp_encrypted_handler;
1411     }
1412
1413     if (!handler)
1414       handler = multipart_handler;
1415   }
1416   else if (WithCrypto && b->type == TYPEAPPLICATION) {
1417     if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b))
1418       handler = crypt_pgp_application_pgp_handler;
1419     if ((WithCrypto & APPLICATION_SMIME) && mutt_is_application_smime (b))
1420       handler = crypt_smime_application_smime_handler;
1421   }
1422
1423
1424   if (plaintext || handler) {
1425     fseeko (s->fpin, b->offset, 0);
1426
1427     /* see if we need to decode this part before processing it */
1428     if (b->encoding == ENCBASE64 || b->encoding == ENCQUOTEDPRINTABLE || b->encoding == ENCUUENCODED || plaintext || mutt_is_text_part (b)) {   /* text subtypes may
1429                                                                                                                                                  * require character
1430                                                                                                                                                  * set conversion even
1431                                                                                                                                                  * with 8bit encoding.
1432                                                                                                                                                  */
1433       int origType = b->type;
1434       char *savePrefix = NULL;
1435
1436       if (!plaintext) {
1437         /* decode to a tempfile, saving the original destination */
1438         fp = s->fpout;
1439         mutt_mktemp (tempfile);
1440         if ((s->fpout = safe_fopen (tempfile, "w")) == NULL) {
1441           mutt_error _("Unable to open temporary file!");
1442
1443           goto bail;
1444         }
1445         /* decoding the attachment changes the size and offset, so save a copy
1446          * of the "real" values now, and restore them after processing
1447          */
1448         tmplength = b->length;
1449         tmpoffset = b->offset;
1450
1451         /* if we are decoding binary bodies, we don't want to prefix each
1452          * line with the prefix or else the data will get corrupted.
1453          */
1454         savePrefix = s->prefix;
1455         s->prefix = NULL;
1456
1457         decode = 1;
1458       }
1459       else
1460         b->type = TYPETEXT;
1461
1462       mutt_decode_attachment (b, s);
1463
1464       if (decode) {
1465         b->length = ftello (s->fpout);
1466         b->offset = 0;
1467         fclose (s->fpout);
1468
1469         /* restore final destination and substitute the tempfile for input */
1470         s->fpout = fp;
1471         fp = s->fpin;
1472         s->fpin = safe_fopen (tempfile, "r");
1473         unlink (tempfile);
1474
1475         /* restore the prefix */
1476         s->prefix = savePrefix;
1477       }
1478
1479       b->type = origType;
1480     }
1481
1482     /* process the (decoded) body part */
1483     if (handler) {
1484       rc = handler (b, s);
1485
1486       if (decode) {
1487         b->length = tmplength;
1488         b->offset = tmpoffset;
1489
1490         /* restore the original source stream */
1491         fclose (s->fpin);
1492         s->fpin = fp;
1493       }
1494     }
1495     s->flags |= M_FIRSTDONE;
1496   }
1497   else if (s->flags & M_DISPLAY) {
1498     state_mark_attach (s);
1499     state_printf (s, _("[-- %s/%s is unsupported "), TYPE (b), b->subtype);
1500     if (!option (OPTVIEWATTACH)) {
1501       if (km_expand_key
1502           (type, sizeof (type),
1503            km_find_func (MENU_PAGER, OP_VIEW_ATTACHMENTS)))
1504         fprintf (s->fpout, _("(use '%s' to view this part)"), type);
1505       else
1506         fputs (_("(need 'view-attachments' bound to key!)"), s->fpout);
1507     }
1508     fputs (" --]\n", s->fpout);
1509   }
1510
1511 bail:
1512   s->flags = oflags | (s->flags & M_FIRSTDONE);
1513
1514   return (rc);
1515 }