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