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