a39bc0c9e5cc9f80e1c7edcbb6fde48d34fd17e5
[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 void 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 void 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
782 /*
783  * An implementation of RFC 2646.
784  *
785  * NOTE: This still has to be made UTF-8 aware.
786  *
787  */
788
789 #define FLOWED_MAX 77
790
791 static int get_quote_level (char *line)
792 {
793   int quoted;
794
795   for (quoted = 0; line[quoted] == '>'; quoted++);
796   return quoted;
797 }
798
799 static void print_flowed_line (char *line, STATE * s, int ql)
800 {
801   int width;
802   char *pos, *oldpos;
803   int len = str_len (line);
804   int i;
805
806   if (MaxLineLength > 0) {
807     width = MaxLineLength - WrapMargin - ql - 1;
808     if (option (OPTSTUFFQUOTED))
809       --width;
810     if (width < 0)
811       width = MaxLineLength;
812   }
813   else {
814     if (option (OPTMBOXPANE))
815       width = COLS - SidebarWidth - WrapMargin - ql - 1;
816     else
817       width = COLS - WrapMargin - ql - 1;
818
819     if (option (OPTSTUFFQUOTED))
820       --width;
821     if (width < 0)
822       width = COLS;
823   }
824
825   /* fprintf(stderr,"print_flowed_line will print `%s' with ql = %d\n",line,ql); */
826
827   if (str_len (line) == 0) {
828     if (option (OPTQUOTEEMPTY)) {
829       if (s->prefix)
830         state_puts(s->prefix,s);
831       for (i=0;i<ql;++i) state_putc('>',s);
832       if (option(OPTSTUFFQUOTED))
833         state_putc(' ',s);
834     }
835     state_putc('\n',s);
836     return;
837   }
838
839   pos = line + ql + width;
840   oldpos = line + ql;
841   if (ql > 0 && ISBLANK (*oldpos))
842     ++oldpos;
843
844   /* fprintf(stderr,"oldpos = %p line+len = %p\n",oldpos,line+len); */
845
846   for (; oldpos < line + len; pos += width) {
847     /* fprintf(stderr,"outer for loop\n"); */
848     if (pos < line + len) {     /* only search a new position when we're not over the end of the string w/ pos */
849       /* fprintf(stderr,"if 1\n"); */
850       if (*pos == ' ') {
851         /* fprintf(stderr,"if 2: good luck! found a space\n"); */
852         *pos = '\0';
853         ++pos;
854       }
855       else {
856         /* fprintf(stderr,"if 2: else\n"); */
857         char *save = pos;
858
859         while (pos >= oldpos && !isspace (*pos)) {
860           /* fprintf(stderr,"pos(%p) > oldpos(%p)\n",pos,oldpos); */
861           --pos;
862         }
863         if (pos < oldpos) {
864           /* fprintf(stderr,"wow, no space found, searching the other direction\n"); */
865           pos = save;
866           while (pos < line + len && *pos && !isspace (*pos)) {
867             /* fprintf(stderr,"pos(%p) < line+len(%p)\n",pos,line+len); */
868             ++pos;
869           }
870           /* fprintf(stderr,"found a space pos = %p\n",pos); */
871         }
872         *pos = '\0';
873         ++pos;
874       }
875     }
876     else {
877       /* fprintf(stderr,"if 1 else\n"); */
878     }
879     if (s->prefix)
880       state_puts (s->prefix, s);
881     for (i = 0; i < ql; ++i)
882       state_putc ('>', s);
883     if (option (OPTSTUFFQUOTED) && (ql > 0 || s->prefix))
884       state_putc (' ', s);
885     state_puts (oldpos, s);
886     /* fprintf(stderr,"print_flowed_line: `%s'\n",oldpos); */
887     if (pos < line + len)
888       state_putc (' ', s);
889     state_putc ('\n', s);
890     oldpos = pos;
891   }
892   /*state_puts(line,s);
893      state_putc('\n',s); */
894 }
895
896 static void text_plain_flowed_handler (BODY * a, STATE * s)
897 {
898   int bytes = a->length;
899   char buf[LONG_STRING];
900   char *curline = strdup ("");
901   char *t = NULL;
902   unsigned int curline_len = 1;
903   unsigned int quotelevel = 0, newql = 0;
904   int first_line = 1;
905
906   while (bytes > 0 && fgets (buf, sizeof (buf), s->fpin)) {
907
908     bytes -= str_len (buf);
909
910     newql = get_quote_level (buf);
911
912     if (bytes == 0 || ((t = strrchr (buf, '\n')) || (t = strrchr (buf, '\r')))) {
913       if (t)
914         *t = '\0';
915       if (str_len (curline) > 0 && curline[str_len (curline) - 1] == ' '
916           && newql == quotelevel
917           && strcmp (curline + quotelevel, "-- ") != 0) {
918         if (buf[newql] == ' ')
919           curline[str_len (curline) - 1] = '\0';
920
921         curline = realloc (curline, curline_len + str_len (buf));
922         if (curline_len == 1)
923           *curline = '\0';
924         curline_len += str_len (buf);
925         str_ncat (curline, curline_len, buf + newql,
926                       str_len (buf + newql));
927       }
928       else {
929         if (first_line) {
930           first_line = 0;
931         }
932         else {
933           print_flowed_line (curline, s, quotelevel);
934         }
935         mem_free (&curline);
936         curline_len = 1;
937         curline = realloc (curline, curline_len + str_len (buf));
938         if (curline_len == 1)
939           *curline = '\0';
940         curline_len += str_len (buf);
941         str_ncat (curline, curline_len, buf, str_len (buf));
942         quotelevel = newql;
943       }
944     }
945   }
946   if (curline) {
947     print_flowed_line (curline, s, quotelevel);
948     mem_free (&curline);
949   }
950 }
951
952 #define TXTHTML     1
953 #define TXTPLAIN    2
954 #define TXTENRICHED 3
955
956 static void alternative_handler (BODY * a, STATE * s)
957 {
958   BODY *choice = NULL;
959   BODY *b;
960   LIST *t;
961   char buf[STRING];
962   int type = 0;
963   int mustfree = 0;
964
965   if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE ||
966       a->encoding == ENCUUENCODED) {
967     struct stat st;
968
969     mustfree = 1;
970     fstat (fileno (s->fpin), &st);
971     b = mutt_new_body ();
972     b->length = (long) st.st_size;
973     b->parts = mutt_parse_multipart (s->fpin,
974                                      mutt_get_parameter ("boundary",
975                                                          a->parameter),
976                                      (long) st.st_size,
977                                      ascii_strcasecmp ("digest",
978                                                        a->subtype) == 0);
979   }
980   else
981     b = a;
982
983   a = b;
984
985   /* First, search list of prefered types */
986   t = AlternativeOrderList;
987   while (t && !choice) {
988     char *c;
989     int btlen;                  /* length of basetype */
990     int wild;                   /* do we have a wildcard to match all subtypes? */
991
992     c = strchr (t->data, '/');
993     if (c) {
994       wild = (c[1] == '*' && c[2] == 0);
995       btlen = c - t->data;
996     }
997     else {
998       wild = 1;
999       btlen = str_len (t->data);
1000     }
1001
1002     if (a && a->parts)
1003       b = a->parts;
1004     else
1005       b = a;
1006     while (b) {
1007       const char *bt = TYPE (b);
1008
1009       if (!ascii_strncasecmp (bt, t->data, btlen) && bt[btlen] == 0) {
1010         /* the basetype matches */
1011         if (wild || !ascii_strcasecmp (t->data + btlen + 1, b->subtype)) {
1012           choice = b;
1013         }
1014       }
1015       b = b->next;
1016     }
1017     t = t->next;
1018   }
1019
1020   /* Next, look for an autoviewable type */
1021   if (!choice) {
1022     if (a && a->parts)
1023       b = a->parts;
1024     else
1025       b = a;
1026     while (b) {
1027       snprintf (buf, sizeof (buf), "%s/%s", TYPE (b), b->subtype);
1028       if (mutt_is_autoview (b, buf)) {
1029         rfc1524_entry *entry = rfc1524_new_entry ();
1030
1031         if (rfc1524_mailcap_lookup (b, buf, entry, M_AUTOVIEW)) {
1032           choice = b;
1033         }
1034         rfc1524_free_entry (&entry);
1035       }
1036       b = b->next;
1037     }
1038   }
1039
1040   /* Then, look for a text entry */
1041   if (!choice) {
1042     if (a && a->parts)
1043       b = a->parts;
1044     else
1045       b = a;
1046     while (b) {
1047       if (b->type == TYPETEXT) {
1048         if (!ascii_strcasecmp ("plain", b->subtype) && type <= TXTPLAIN) {
1049           choice = b;
1050           type = TXTPLAIN;
1051         }
1052         else if (!ascii_strcasecmp ("enriched", b->subtype)
1053                  && type <= TXTENRICHED) {
1054           choice = b;
1055           type = TXTENRICHED;
1056         }
1057         else if (!ascii_strcasecmp ("html", b->subtype) && type <= TXTHTML) {
1058           choice = b;
1059           type = TXTHTML;
1060         }
1061       }
1062       b = b->next;
1063     }
1064   }
1065
1066   /* Finally, look for other possibilities */
1067   if (!choice) {
1068     if (a && a->parts)
1069       b = a->parts;
1070     else
1071       b = a;
1072     while (b) {
1073       if (mutt_can_decode (b))
1074         choice = b;
1075       b = b->next;
1076     }
1077   }
1078
1079   if (choice) {
1080     if (s->flags & M_DISPLAY && !option (OPTWEED)) {
1081       fseek (s->fpin, choice->hdr_offset, 0);
1082       mutt_copy_bytes (s->fpin, s->fpout,
1083                        choice->offset - choice->hdr_offset);
1084     }
1085     mutt_body_handler (choice, s);
1086   }
1087   else if (s->flags & M_DISPLAY) {
1088     /* didn't find anything that we could display! */
1089     state_mark_attach (s);
1090     state_puts (_
1091                 ("[-- Error:  Could not display any parts of Multipart/Alternative! --]\n"),
1092                 s);
1093   }
1094
1095   if (mustfree)
1096     mutt_free_body (&a);
1097 }
1098
1099 /* handles message/rfc822 body parts */
1100 void message_handler (BODY * a, STATE * s)
1101 {
1102   struct stat st;
1103   BODY *b;
1104   long off_start;
1105
1106   off_start = ftell (s->fpin);
1107   if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE ||
1108       a->encoding == ENCUUENCODED) {
1109     fstat (fileno (s->fpin), &st);
1110     b = mutt_new_body ();
1111     b->length = (long) st.st_size;
1112     b->parts = mutt_parse_messageRFC822 (s->fpin, b);
1113   }
1114   else
1115     b = a;
1116
1117   if (b->parts) {
1118     mutt_copy_hdr (s->fpin, s->fpout, off_start, b->parts->offset,
1119                    (((s->flags & M_WEED)
1120                      || ((s->flags & (M_DISPLAY | M_PRINTING))
1121                          && option (OPTWEED))) ? (CH_WEED | CH_REORDER) : 0) |
1122                    (s->prefix ? CH_PREFIX : 0) | CH_DECODE | CH_FROM,
1123                    s->prefix);
1124
1125     if (s->prefix)
1126       state_puts (s->prefix, s);
1127     state_putc ('\n', s);
1128
1129     mutt_body_handler (b->parts, s);
1130   }
1131
1132   if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE ||
1133       a->encoding == ENCUUENCODED)
1134     mutt_free_body (&b);
1135 }
1136
1137 /* returns 1 if decoding the attachment will produce output */
1138 int mutt_can_decode (BODY * a)
1139 {
1140   char type[STRING];
1141
1142   snprintf (type, sizeof (type), "%s/%s", TYPE (a), a->subtype);
1143   if (mutt_is_autoview (a, type))
1144     return (rfc1524_mailcap_lookup (a, type, NULL, M_AUTOVIEW));
1145   else if (a->type == TYPETEXT)
1146     return (1);
1147   else if (a->type == TYPEMESSAGE)
1148     return (1);
1149   else if (a->type == TYPEMULTIPART) {
1150     BODY *p;
1151
1152     if (WithCrypto) {
1153       if (ascii_strcasecmp (a->subtype, "signed") == 0 ||
1154           ascii_strcasecmp (a->subtype, "encrypted") == 0)
1155         return (1);
1156     }
1157
1158     for (p = a->parts; p; p = p->next) {
1159       if (mutt_can_decode (p))
1160         return (1);
1161     }
1162
1163   }
1164   else if (WithCrypto && a->type == TYPEAPPLICATION) {
1165     if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (a))
1166       return (1);
1167     if ((WithCrypto & APPLICATION_SMIME) && mutt_is_application_smime (a))
1168       return (1);
1169   }
1170
1171   return (0);
1172 }
1173
1174 void multipart_handler (BODY * a, STATE * s)
1175 {
1176   BODY *b, *p;
1177   char length[5];
1178   struct stat st;
1179   int count;
1180
1181   if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE ||
1182       a->encoding == ENCUUENCODED) {
1183     fstat (fileno (s->fpin), &st);
1184     b = mutt_new_body ();
1185     b->length = (long) st.st_size;
1186     b->parts = mutt_parse_multipart (s->fpin,
1187                                      mutt_get_parameter ("boundary",
1188                                                          a->parameter),
1189                                      (long) st.st_size,
1190                                      ascii_strcasecmp ("digest",
1191                                                        a->subtype) == 0);
1192   }
1193   else
1194     b = a;
1195
1196   for (p = b->parts, count = 1; p; p = p->next, count++) {
1197     if (s->flags & M_DISPLAY) {
1198       state_mark_attach (s);
1199       state_printf (s, _("[-- Attachment #%d"), count);
1200       if (p->description || p->filename || p->form_name) {
1201         state_puts (": ", s);
1202         state_puts (p->description ? p->description :
1203                     p->filename ? p->filename : p->form_name, s);
1204       }
1205       state_puts (" --]\n", s);
1206
1207       mutt_pretty_size (length, sizeof (length), p->length);
1208
1209       state_mark_attach (s);
1210       state_printf (s, _("[-- Type: %s/%s, Encoding: %s, Size: %s --]\n"),
1211                     TYPE (p), p->subtype, ENCODING (p->encoding), length);
1212       if (!option (OPTWEED)) {
1213         fseek (s->fpin, p->hdr_offset, 0);
1214         mutt_copy_bytes (s->fpin, s->fpout, p->offset - p->hdr_offset);
1215       }
1216       else
1217         state_putc ('\n', s);
1218     }
1219     else {
1220       if (p->description && mutt_can_decode (p))
1221         state_printf (s, "Content-Description: %s\n", p->description);
1222
1223       if (p->form_name)
1224         state_printf (s, "%s: \n", p->form_name);
1225
1226     }
1227     mutt_body_handler (p, s);
1228     state_putc ('\n', s);
1229     if ((s->flags & M_REPLYING)
1230         && (option (OPTINCLUDEONLYFIRST)) && (s->flags & M_FIRSTDONE))
1231       break;
1232   }
1233
1234   if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE ||
1235       a->encoding == ENCUUENCODED)
1236     mutt_free_body (&b);
1237 }
1238
1239 void autoview_handler (BODY * a, STATE * s)
1240 {
1241   rfc1524_entry *entry = rfc1524_new_entry ();
1242   char buffer[LONG_STRING];
1243   char type[STRING];
1244   char command[LONG_STRING];
1245   char tempfile[_POSIX_PATH_MAX] = "";
1246   char *fname;
1247   FILE *fpin = NULL;
1248   FILE *fpout = NULL;
1249   FILE *fperr = NULL;
1250   int piped = FALSE;
1251   pid_t thepid;
1252
1253   snprintf (type, sizeof (type), "%s/%s", TYPE (a), a->subtype);
1254   rfc1524_mailcap_lookup (a, type, entry, M_AUTOVIEW);
1255
1256   fname = str_dup (a->filename);
1257   mutt_sanitize_filename (fname, 1);
1258   rfc1524_expand_filename (entry->nametemplate, fname, tempfile,
1259                            sizeof (tempfile));
1260   mem_free (&fname);
1261
1262   if (entry->command) {
1263     strfcpy (command, entry->command, sizeof (command));
1264
1265     /* rfc1524_expand_command returns 0 if the file is required */
1266     piped =
1267       rfc1524_expand_command (a, tempfile, type, command, sizeof (command));
1268
1269     if (s->flags & M_DISPLAY) {
1270       state_mark_attach (s);
1271       state_printf (s, _("[-- Autoview using %s --]\n"), command);
1272       mutt_message (_("Invoking autoview command: %s"), command);
1273     }
1274
1275     if ((fpin = safe_fopen (tempfile, "w+")) == NULL) {
1276       mutt_perror ("fopen");
1277       rfc1524_free_entry (&entry);
1278       return;
1279     }
1280
1281     mutt_copy_bytes (s->fpin, fpin, a->length);
1282
1283     if (!piped) {
1284       safe_fclose (&fpin);
1285       thepid = mutt_create_filter (command, NULL, &fpout, &fperr);
1286     }
1287     else {
1288       unlink (tempfile);
1289       fflush (fpin);
1290       rewind (fpin);
1291       thepid = mutt_create_filter_fd (command, NULL, &fpout, &fperr,
1292                                       fileno (fpin), -1, -1);
1293     }
1294
1295     if (thepid < 0) {
1296       mutt_perror (_("Can't create filter"));
1297
1298       if (s->flags & M_DISPLAY) {
1299         state_mark_attach (s);
1300         state_printf (s, _("[-- Can't run %s. --]\n"), command);
1301       }
1302       goto bail;
1303     }
1304
1305     if (s->prefix) {
1306       while (fgets (buffer, sizeof (buffer), fpout) != NULL) {
1307         state_puts (s->prefix, s);
1308         state_puts (buffer, s);
1309       }
1310       /* check for data on stderr */
1311       if (fgets (buffer, sizeof (buffer), fperr)) {
1312         if (s->flags & M_DISPLAY) {
1313           state_mark_attach (s);
1314           state_printf (s, _("[-- Autoview stderr of %s --]\n"), command);
1315         }
1316
1317         state_puts (s->prefix, s);
1318         state_puts (buffer, s);
1319         while (fgets (buffer, sizeof (buffer), fperr) != NULL) {
1320           state_puts (s->prefix, s);
1321           state_puts (buffer, s);
1322         }
1323       }
1324     }
1325     else {
1326       mutt_copy_stream (fpout, s->fpout);
1327       /* Check for stderr messages */
1328       if (fgets (buffer, sizeof (buffer), fperr)) {
1329         if (s->flags & M_DISPLAY) {
1330           state_mark_attach (s);
1331           state_printf (s, _("[-- Autoview stderr of %s --]\n"), command);
1332         }
1333
1334         state_puts (buffer, s);
1335         mutt_copy_stream (fperr, s->fpout);
1336       }
1337     }
1338
1339   bail:
1340     safe_fclose (&fpout);
1341     safe_fclose (&fperr);
1342
1343     mutt_wait_filter (thepid);
1344     if (piped)
1345       safe_fclose (&fpin);
1346     else
1347       mutt_unlink (tempfile);
1348
1349     if (s->flags & M_DISPLAY)
1350       mutt_clear_error ();
1351   }
1352   rfc1524_free_entry (&entry);
1353 }
1354
1355 static void external_body_handler (BODY * b, STATE * s)
1356 {
1357   const char *access_type;
1358   const char *expiration;
1359   time_t expire;
1360
1361   access_type = mutt_get_parameter ("access-type", b->parameter);
1362   if (!access_type) {
1363     if (s->flags & M_DISPLAY) {
1364       state_mark_attach (s);
1365       state_puts (_
1366                   ("[-- Error: message/external-body has no access-type parameter --]\n"),
1367                   s);
1368     }
1369     return;
1370   }
1371
1372   expiration = mutt_get_parameter ("expiration", b->parameter);
1373   if (expiration)
1374     expire = mutt_parse_date (expiration, NULL);
1375   else
1376     expire = -1;
1377
1378   if (!ascii_strcasecmp (access_type, "x-mutt-deleted")) {
1379     if (s->flags & (M_DISPLAY | M_PRINTING)) {
1380       char *length;
1381       char pretty_size[10];
1382
1383       state_mark_attach (s);
1384       state_printf (s, _("[-- This %s/%s attachment "),
1385                     TYPE (b->parts), b->parts->subtype);
1386       length = mutt_get_parameter ("length", b->parameter);
1387       if (length) {
1388         mutt_pretty_size (pretty_size, sizeof (pretty_size),
1389                           strtol (length, NULL, 10));
1390         state_printf (s, _("(size %s bytes) "), pretty_size);
1391       }
1392       state_puts (_("has been deleted --]\n"), s);
1393
1394       if (expire != -1) {
1395         state_mark_attach (s);
1396         state_printf (s, _("[-- on %s --]\n"), expiration);
1397       }
1398       if (b->parts->filename) {
1399         state_mark_attach (s);
1400         state_printf (s, _("[-- name: %s --]\n"), b->parts->filename);
1401       }
1402
1403       mutt_copy_hdr (s->fpin, s->fpout, ftell (s->fpin), b->parts->offset,
1404                      (option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) |
1405                      CH_DECODE, NULL);
1406     }
1407   }
1408   else if (expiration && expire < time (NULL)) {
1409     if (s->flags & M_DISPLAY) {
1410       state_mark_attach (s);
1411       state_printf (s, _("[-- This %s/%s attachment is not included, --]\n"),
1412                     TYPE (b->parts), b->parts->subtype);
1413       state_attach_puts (_("[-- and the indicated external source has --]\n"
1414                            "[-- expired. --]\n"), s);
1415
1416       mutt_copy_hdr (s->fpin, s->fpout, ftell (s->fpin), b->parts->offset,
1417                      (option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) |
1418                      CH_DECODE, NULL);
1419     }
1420   }
1421   else {
1422     if (s->flags & M_DISPLAY) {
1423       state_mark_attach (s);
1424       state_printf (s,
1425                     _("[-- This %s/%s attachment is not included, --]\n"),
1426                     TYPE (b->parts), b->parts->subtype);
1427       state_mark_attach (s);
1428       state_printf (s,
1429                     _
1430                     ("[-- and the indicated access-type %s is unsupported --]\n"),
1431                     access_type);
1432       mutt_copy_hdr (s->fpin, s->fpout, ftell (s->fpin), b->parts->offset,
1433                      (option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) |
1434                      CH_DECODE, NULL);
1435     }
1436   }
1437 }
1438
1439 void mutt_decode_attachment (BODY * b, STATE * s)
1440 {
1441   int istext = mutt_is_text_part (b);
1442   iconv_t cd = (iconv_t) (-1);
1443
1444   Quotebuf[0] = '\0';
1445
1446   if (istext) {
1447     if (s->flags & M_CHARCONV) {
1448       char *charset = mutt_get_parameter ("charset", b->parameter);
1449
1450       if (!option (OPTSTRICTMIME) && !charset)
1451         charset = mutt_get_first_charset (AssumedCharset);
1452       if (charset && Charset)
1453         cd = mutt_iconv_open (Charset, charset, M_ICONV_HOOK_FROM);
1454     }
1455     else {
1456       if (b->file_charset)
1457         cd = mutt_iconv_open (Charset, b->file_charset, M_ICONV_HOOK_FROM);
1458     }
1459   }
1460
1461   fseek (s->fpin, b->offset, 0);
1462   switch (b->encoding) {
1463   case ENCQUOTEDPRINTABLE:
1464     mutt_decode_quoted (s, b->length, istext, cd);
1465     break;
1466   case ENCBASE64:
1467     mutt_decode_base64 (s, b->length, istext, cd);
1468     break;
1469   case ENCUUENCODED:
1470     mutt_decode_uuencoded (s, b->length, istext, cd);
1471     break;
1472   default:
1473     mutt_decode_xbit (s, b->length, istext, cd);
1474     break;
1475   }
1476
1477   if (cd != (iconv_t) (-1))
1478     iconv_close (cd);
1479 }
1480
1481 void mutt_body_handler (BODY * b, STATE * s)
1482 {
1483   int decode = 0;
1484   int plaintext = 0;
1485   FILE *fp = NULL;
1486   char tempfile[_POSIX_PATH_MAX];
1487   handler_t handler = NULL;
1488   long tmpoffset = 0;
1489   size_t tmplength = 0;
1490   char type[STRING];
1491
1492   int oflags = s->flags;
1493
1494   /* first determine which handler to use to process this part */
1495
1496   snprintf (type, sizeof (type), "%s/%s", TYPE (b), b->subtype);
1497   if (mutt_is_autoview (b, type)) {
1498     rfc1524_entry *entry = rfc1524_new_entry ();
1499
1500     if (rfc1524_mailcap_lookup (b, type, entry, M_AUTOVIEW)) {
1501       handler = autoview_handler;
1502       s->flags &= ~M_CHARCONV;
1503     }
1504     rfc1524_free_entry (&entry);
1505   }
1506   else if (b->type == TYPETEXT) {
1507     if (ascii_strcasecmp ("plain", b->subtype) == 0) {
1508       /* avoid copying this part twice since removing the transfer-encoding is
1509        * the only operation needed.
1510        */
1511       if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b))
1512         handler = crypt_pgp_application_pgp_handler;
1513       else
1514         if (ascii_strcasecmp
1515             ("flowed", mutt_get_parameter ("format", b->parameter)) == 0)
1516         handler = text_plain_flowed_handler;
1517       else
1518         plaintext = 1;
1519     }
1520     else if (ascii_strcasecmp ("enriched", b->subtype) == 0)
1521       handler = text_enriched_handler;
1522     else                        /* text body type without a handler */
1523       plaintext = 1;
1524   }
1525   else if (b->type == TYPEMESSAGE) {
1526     if (mutt_is_message_type (b->type, b->subtype))
1527       handler = message_handler;
1528     else if (!ascii_strcasecmp ("delivery-status", b->subtype))
1529       plaintext = 1;
1530     else if (!ascii_strcasecmp ("external-body", b->subtype))
1531       handler = external_body_handler;
1532   }
1533   else if (b->type == TYPEMULTIPART) {
1534     char *p;
1535
1536     if (ascii_strcasecmp ("alternative", b->subtype) == 0)
1537       handler = alternative_handler;
1538     else if (WithCrypto && ascii_strcasecmp ("signed", b->subtype) == 0) {
1539       p = mutt_get_parameter ("protocol", b->parameter);
1540
1541       if (!p)
1542         mutt_error (_("Error: multipart/signed has no protocol."));
1543
1544       else if (s->flags & M_VERIFY)
1545         handler = mutt_signed_handler;
1546     }
1547     else if ((WithCrypto & APPLICATION_PGP)
1548              && str_casecmp ("encrypted", b->subtype) == 0) {
1549       p = mutt_get_parameter ("protocol", b->parameter);
1550
1551       if (!p)
1552         mutt_error (_
1553                     ("Error: multipart/encrypted has no protocol parameter!"));
1554
1555       else if (ascii_strcasecmp ("application/pgp-encrypted", p) == 0)
1556         handler = crypt_pgp_encrypted_handler;
1557     }
1558
1559     if (!handler)
1560       handler = multipart_handler;
1561   }
1562   else if (WithCrypto && b->type == TYPEAPPLICATION) {
1563     if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b))
1564       handler = crypt_pgp_application_pgp_handler;
1565     if ((WithCrypto & APPLICATION_SMIME) && mutt_is_application_smime (b))
1566       handler = crypt_smime_application_smime_handler;
1567   }
1568
1569
1570   if (plaintext || handler) {
1571     fseek (s->fpin, b->offset, 0);
1572
1573     /* see if we need to decode this part before processing it */
1574     if (b->encoding == ENCBASE64 || b->encoding == ENCQUOTEDPRINTABLE || b->encoding == ENCUUENCODED || plaintext || mutt_is_text_part (b)) {   /* text subtypes may
1575                                                                                                                                                  * require character
1576                                                                                                                                                  * set conversion even
1577                                                                                                                                                  * with 8bit encoding.
1578                                                                                                                                                  */
1579       int origType = b->type;
1580       char *savePrefix = NULL;
1581
1582       if (!plaintext) {
1583         /* decode to a tempfile, saving the original destination */
1584         fp = s->fpout;
1585         mutt_mktemp (tempfile);
1586         if ((s->fpout = safe_fopen (tempfile, "w")) == NULL) {
1587           mutt_error _("Unable to open temporary file!");
1588
1589           goto bail;
1590         }
1591         /* decoding the attachment changes the size and offset, so save a copy
1592          * of the "real" values now, and restore them after processing
1593          */
1594         tmplength = b->length;
1595         tmpoffset = b->offset;
1596
1597         /* if we are decoding binary bodies, we don't want to prefix each
1598          * line with the prefix or else the data will get corrupted.
1599          */
1600         savePrefix = s->prefix;
1601         s->prefix = NULL;
1602
1603         decode = 1;
1604       }
1605       else
1606         b->type = TYPETEXT;
1607
1608       mutt_decode_attachment (b, s);
1609
1610       if (decode) {
1611         b->length = ftell (s->fpout);
1612         b->offset = 0;
1613         fclose (s->fpout);
1614
1615         /* restore final destination and substitute the tempfile for input */
1616         s->fpout = fp;
1617         fp = s->fpin;
1618         s->fpin = safe_fopen (tempfile, "r");
1619         unlink (tempfile);
1620
1621         /* restore the prefix */
1622         s->prefix = savePrefix;
1623       }
1624
1625       b->type = origType;
1626     }
1627
1628     /* process the (decoded) body part */
1629     if (handler) {
1630       handler (b, s);
1631
1632       if (decode) {
1633         b->length = tmplength;
1634         b->offset = tmpoffset;
1635
1636         /* restore the original source stream */
1637         fclose (s->fpin);
1638         s->fpin = fp;
1639       }
1640     }
1641     s->flags |= M_FIRSTDONE;
1642   }
1643   else if (s->flags & M_DISPLAY) {
1644     state_mark_attach (s);
1645     state_printf (s, _("[-- %s/%s is unsupported "), TYPE (b), b->subtype);
1646     if (!option (OPTVIEWATTACH)) {
1647       if (km_expand_key
1648           (type, sizeof (type),
1649            km_find_func (MENU_PAGER, OP_VIEW_ATTACHMENTS)))
1650         fprintf (s->fpout, _("(use '%s' to view this part)"), type);
1651       else
1652         fputs (_("(need 'view-attachments' bound to key!)"), s->fpout);
1653     }
1654     fputs (" --]\n", s->fpout);
1655   }
1656
1657 bail:
1658   s->flags = oflags | (s->flags & M_FIRSTDONE);
1659 }