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