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