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