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