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