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