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