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