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