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