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