many simplifications, copyright statements.
[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 = mutt_new_body ();
785     b->length = (long) st.st_size;
786     b->parts = mutt_parse_multipart (s->fpin,
787                                      mutt_get_parameter ("boundary",
788                                                          a->parameter),
789                                      (long) st.st_size,
790                                      ascii_strcasecmp ("digest",
791                                                        a->subtype) == 0);
792   }
793   else
794     b = a;
795
796   a = b;
797
798   /* First, search list of prefered types */
799   t = AlternativeOrderList;
800   while (t && !choice) {
801     char *c;
802     int btlen;                  /* length of basetype */
803     int wild;                   /* do we have a wildcard to match all subtypes? */
804
805     c = strchr (t->data, '/');
806     if (c) {
807       wild = (c[1] == '*' && c[2] == 0);
808       btlen = c - t->data;
809     }
810     else {
811       wild = 1;
812       btlen = m_strlen(t->data);
813     }
814
815     if (a && a->parts)
816       b = a->parts;
817     else
818       b = a;
819     while (b) {
820       const char *bt = TYPE (b);
821
822       if (!ascii_strncasecmp (bt, t->data, btlen) && bt[btlen] == 0) {
823         /* the basetype matches */
824         if (wild || !ascii_strcasecmp (t->data + btlen + 1, b->subtype)) {
825           choice = b;
826         }
827       }
828       b = b->next;
829     }
830     t = t->next;
831   }
832
833   /* Next, look for an autoviewable type */
834   if (!choice) {
835     if (a && a->parts)
836       b = a->parts;
837     else
838       b = a;
839     while (b) {
840       snprintf (buf, sizeof (buf), "%s/%s", TYPE (b), b->subtype);
841       if (mutt_is_autoview (b, buf)) {
842         rfc1524_entry *entry = rfc1524_entry_new();
843
844         if (rfc1524_mailcap_lookup (b, buf, entry, M_AUTOVIEW)) {
845           choice = b;
846         }
847         rfc1524_entry_delete(&entry);
848       }
849       b = b->next;
850     }
851   }
852
853   /* Then, look for a text entry */
854   if (!choice) {
855     if (a && a->parts)
856       b = a->parts;
857     else
858       b = a;
859     while (b) {
860       if (b->type == TYPETEXT) {
861         if (!ascii_strcasecmp ("plain", b->subtype) && type <= TXTPLAIN) {
862           choice = b;
863           type = TXTPLAIN;
864         }
865         else if (!ascii_strcasecmp ("enriched", b->subtype)
866                  && type <= TXTENRICHED) {
867           choice = b;
868           type = TXTENRICHED;
869         }
870         else if (!ascii_strcasecmp ("html", b->subtype) && type <= TXTHTML) {
871           choice = b;
872           type = TXTHTML;
873         }
874       }
875       b = b->next;
876     }
877   }
878
879   /* Finally, look for other possibilities */
880   if (!choice) {
881     if (a && a->parts)
882       b = a->parts;
883     else
884       b = a;
885     while (b) {
886       if (mutt_can_decode (b))
887         choice = b;
888       b = b->next;
889     }
890   }
891
892   if (choice) {
893     if (s->flags & M_DISPLAY && !option (OPTWEED)) {
894       fseeko (s->fpin, choice->hdr_offset, 0);
895       mutt_copy_bytes (s->fpin, s->fpout,
896                        choice->offset - choice->hdr_offset);
897     }
898     mutt_body_handler (choice, s);
899   }
900   else if (s->flags & M_DISPLAY) {
901     /* didn't find anything that we could display! */
902     state_mark_attach (s);
903     state_puts (_("[-- Error:  Could not display any parts of Multipart/Alternative! --]\n"), s);
904     rc = -1;
905   }
906
907   if (mustfree)
908     mutt_free_body (&a);
909
910   return (rc);
911 }
912
913 /* handles message/rfc822 body parts */
914 static int message_handler (BODY * a, STATE * s)
915 {
916   struct stat st;
917   BODY *b;
918   off_t off_start;
919   int rc = 0;
920
921   off_start = ftello (s->fpin);
922   if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE ||
923       a->encoding == ENCUUENCODED) {
924     fstat (fileno (s->fpin), &st);
925     b = mutt_new_body ();
926     b->length = (long) st.st_size;
927     b->parts = mutt_parse_messageRFC822 (s->fpin, b);
928   }
929   else
930     b = a;
931
932   if (b->parts) {
933     mutt_copy_hdr (s->fpin, s->fpout, off_start, b->parts->offset,
934                    (((s->flags & M_WEED)
935                      || ((s->flags & (M_DISPLAY | M_PRINTING))
936                          && option (OPTWEED))) ? (CH_WEED | CH_REORDER) : 0) |
937                    (s->prefix ? CH_PREFIX : 0) | CH_DECODE | CH_FROM,
938                    s->prefix);
939
940     if (s->prefix)
941       state_puts (s->prefix, s);
942     state_putc ('\n', s);
943
944     rc = mutt_body_handler (b->parts, s);
945   }
946
947   if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE ||
948       a->encoding == ENCUUENCODED)
949     mutt_free_body (&b);
950
951   return (rc);
952 }
953
954 /* returns 1 if decoding the attachment will produce output */
955 int mutt_can_decode (BODY * a)
956 {
957   char type[STRING];
958
959   snprintf (type, sizeof (type), "%s/%s", TYPE (a), a->subtype);
960   if (mutt_is_autoview (a, type))
961     return (rfc1524_mailcap_lookup (a, type, NULL, M_AUTOVIEW));
962   else if (a->type == TYPETEXT)
963     return (1);
964   else if (a->type == TYPEMESSAGE)
965     return (1);
966   else if (a->type == TYPEMULTIPART) {
967     BODY *p;
968
969     if (ascii_strcasecmp (a->subtype, "signed") == 0 ||
970         ascii_strcasecmp (a->subtype, "encrypted") == 0)
971       return (1);
972
973     for (p = a->parts; p; p = p->next) {
974       if (mutt_can_decode (p))
975         return (1);
976     }
977
978   }
979   else if (a->type == TYPEAPPLICATION) {
980     if (mutt_is_application_pgp(a))
981       return (1);
982     if (mutt_is_application_smime (a))
983       return (1);
984   }
985
986   return (0);
987 }
988
989 static int multipart_handler (BODY * a, STATE * s)
990 {
991   BODY *b, *p;
992   char length[5];
993   struct stat st;
994   int count;
995   int rc = 0;
996
997   if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE ||
998       a->encoding == ENCUUENCODED) {
999     fstat (fileno (s->fpin), &st);
1000     b = mutt_new_body ();
1001     b->length = (long) st.st_size;
1002     b->parts = mutt_parse_multipart (s->fpin,
1003                                      mutt_get_parameter ("boundary",
1004                                                          a->parameter),
1005                                      (long) st.st_size,
1006                                      ascii_strcasecmp ("digest",
1007                                                        a->subtype) == 0);
1008   }
1009   else
1010     b = a;
1011
1012   for (p = b->parts, count = 1; p; p = p->next, count++) {
1013     if (s->flags & M_DISPLAY) {
1014       state_mark_attach (s);
1015       state_printf (s, _("[-- Attachment #%d"), count);
1016       if (p->description || p->filename || p->form_name) {
1017         state_puts (": ", s);
1018         state_puts (p->description ? p->description :
1019                     p->filename ? p->filename : p->form_name, s);
1020       }
1021       state_puts (" --]\n", s);
1022
1023       mutt_pretty_size (length, sizeof (length), p->length);
1024
1025       state_mark_attach (s);
1026       state_printf (s, _("[-- Type: %s/%s, Encoding: %s, Size: %s --]\n"),
1027                     TYPE (p), p->subtype, ENCODING (p->encoding), length);
1028       if (!option (OPTWEED)) {
1029         fseeko (s->fpin, p->hdr_offset, 0);
1030         mutt_copy_bytes (s->fpin, s->fpout, p->offset - p->hdr_offset);
1031       }
1032       else
1033         state_putc ('\n', s);
1034     }
1035     else {
1036       if (p->description && mutt_can_decode (p))
1037         state_printf (s, "Content-Description: %s\n", p->description);
1038
1039       if (p->form_name)
1040         state_printf (s, "%s: \n", p->form_name);
1041
1042     }
1043     rc = mutt_body_handler (p, s);
1044     state_putc ('\n', s);
1045     if (rc || ((s->flags & M_REPLYING)
1046         && (option (OPTINCLUDEONLYFIRST)) && (s->flags & M_FIRSTDONE)))
1047       break;
1048   }
1049
1050   if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE ||
1051       a->encoding == ENCUUENCODED)
1052     mutt_free_body (&b);
1053
1054   return (rc);
1055 }
1056
1057 static int autoview_handler (BODY * a, STATE * s)
1058 {
1059   rfc1524_entry *entry = rfc1524_entry_new();
1060   char buffer[LONG_STRING];
1061   char type[STRING];
1062   char command[LONG_STRING];
1063   char tempfile[_POSIX_PATH_MAX] = "";
1064   char *fname;
1065   FILE *fpin = NULL;
1066   FILE *fpout = NULL;
1067   FILE *fperr = NULL;
1068   int piped = FALSE;
1069   pid_t thepid;
1070   int rc = 0;
1071
1072   snprintf (type, sizeof (type), "%s/%s", TYPE (a), a->subtype);
1073   rfc1524_mailcap_lookup (a, type, entry, M_AUTOVIEW);
1074
1075   fname = m_strdup(a->filename);
1076   mutt_sanitize_filename (fname, 1);
1077   rfc1524_expand_filename (entry->nametemplate, fname, tempfile,
1078                            sizeof (tempfile));
1079   p_delete(&fname);
1080
1081   if (entry->command) {
1082     m_strcpy(command, sizeof(command), entry->command);
1083
1084     /* rfc1524_expand_command returns 0 if the file is required */
1085     piped =
1086       rfc1524_expand_command (a, tempfile, type, command, sizeof (command));
1087
1088     if (s->flags & M_DISPLAY) {
1089       state_mark_attach (s);
1090       state_printf (s, _("[-- Autoview using %s --]\n"), command);
1091       mutt_message (_("Invoking autoview command: %s"), command);
1092     }
1093
1094     if ((fpin = safe_fopen (tempfile, "w+")) == NULL) {
1095       mutt_perror ("fopen");
1096       rfc1524_entry_delete(&entry);
1097       return (-1);
1098     }
1099
1100     mutt_copy_bytes (s->fpin, fpin, a->length);
1101
1102     if (!piped) {
1103       safe_fclose (&fpin);
1104       thepid = mutt_create_filter (command, NULL, &fpout, &fperr);
1105     }
1106     else {
1107       unlink (tempfile);
1108       fflush (fpin);
1109       rewind (fpin);
1110       thepid = mutt_create_filter_fd (command, NULL, &fpout, &fperr,
1111                                       fileno (fpin), -1, -1);
1112     }
1113
1114     if (thepid < 0) {
1115       mutt_perror (_("Can't create filter"));
1116
1117       if (s->flags & M_DISPLAY) {
1118         state_mark_attach (s);
1119         state_printf (s, _("[-- Can't run %s. --]\n"), command);
1120       }
1121       rc = -1;
1122       goto bail;
1123     }
1124
1125     if (s->prefix) {
1126       while (fgets (buffer, sizeof (buffer), fpout) != NULL) {
1127         state_puts (s->prefix, s);
1128         state_puts (buffer, s);
1129       }
1130       /* check for data on stderr */
1131       if (fgets (buffer, sizeof (buffer), fperr)) {
1132         if (s->flags & M_DISPLAY) {
1133           state_mark_attach (s);
1134           state_printf (s, _("[-- Autoview stderr of %s --]\n"), command);
1135         }
1136
1137         state_puts (s->prefix, s);
1138         state_puts (buffer, s);
1139         while (fgets (buffer, sizeof (buffer), fperr) != NULL) {
1140           state_puts (s->prefix, s);
1141           state_puts (buffer, s);
1142         }
1143       }
1144     }
1145     else {
1146       mutt_copy_stream (fpout, s->fpout);
1147       /* Check for stderr messages */
1148       if (fgets (buffer, sizeof (buffer), fperr)) {
1149         if (s->flags & M_DISPLAY) {
1150           state_mark_attach (s);
1151           state_printf (s, _("[-- Autoview stderr of %s --]\n"), command);
1152         }
1153
1154         state_puts (buffer, s);
1155         mutt_copy_stream (fperr, s->fpout);
1156       }
1157     }
1158
1159   bail:
1160     safe_fclose (&fpout);
1161     safe_fclose (&fperr);
1162
1163     mutt_wait_filter (thepid);
1164     if (piped)
1165       safe_fclose (&fpin);
1166     else
1167       mutt_unlink (tempfile);
1168
1169     if (s->flags & M_DISPLAY)
1170       mutt_clear_error ();
1171   }
1172   rfc1524_entry_delete(&entry);
1173   return (rc);
1174 }
1175
1176 static int external_body_handler (BODY * b, STATE * s)
1177 {
1178   const char *access_type;
1179   const char *expiration;
1180   time_t expire;
1181
1182   access_type = mutt_get_parameter ("access-type", b->parameter);
1183   if (!access_type) {
1184     if (s->flags & M_DISPLAY) {
1185       state_mark_attach (s);
1186       state_puts (_("[-- Error: message/external-body has no access-type parameter --]\n"), s);
1187     }
1188     return (-1);
1189   }
1190
1191   expiration = mutt_get_parameter ("expiration", b->parameter);
1192   if (expiration)
1193     expire = mutt_parse_date (expiration, NULL);
1194   else
1195     expire = -1;
1196
1197   if (!ascii_strcasecmp (access_type, "x-mutt-deleted")) {
1198     if (s->flags & (M_DISPLAY | M_PRINTING)) {
1199       char *length;
1200       char pretty_size[10];
1201
1202       state_mark_attach (s);
1203       state_printf (s, _("[-- This %s/%s attachment "),
1204                     TYPE (b->parts), b->parts->subtype);
1205       length = mutt_get_parameter ("length", b->parameter);
1206       if (length) {
1207         mutt_pretty_size (pretty_size, sizeof (pretty_size),
1208                           strtol (length, NULL, 10));
1209         state_printf (s, _("(size %s bytes) "), pretty_size);
1210       }
1211       state_puts (_("has been deleted --]\n"), s);
1212
1213       if (expire != -1) {
1214         state_mark_attach (s);
1215         state_printf (s, _("[-- on %s --]\n"), expiration);
1216       }
1217       if (b->parts->filename) {
1218         state_mark_attach (s);
1219         state_printf (s, _("[-- name: %s --]\n"), b->parts->filename);
1220       }
1221
1222       mutt_copy_hdr (s->fpin, s->fpout, ftello (s->fpin), b->parts->offset,
1223                      (option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) |
1224                      CH_DECODE, NULL);
1225     }
1226   }
1227   else if (expiration && expire < time (NULL)) {
1228     if (s->flags & M_DISPLAY) {
1229       state_mark_attach (s);
1230       state_printf (s, _("[-- This %s/%s attachment is not included, --]\n"),
1231                     TYPE (b->parts), b->parts->subtype);
1232       state_attach_puts (_("[-- and the indicated external source has --]\n"
1233                            "[-- expired. --]\n"), s);
1234
1235       mutt_copy_hdr (s->fpin, s->fpout, ftello (s->fpin), b->parts->offset,
1236                      (option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) |
1237                      CH_DECODE, NULL);
1238     }
1239   }
1240   else {
1241     if (s->flags & M_DISPLAY) {
1242       state_mark_attach (s);
1243       state_printf (s,
1244                     _("[-- This %s/%s attachment is not included, --]\n"),
1245                     TYPE (b->parts), b->parts->subtype);
1246       state_mark_attach (s);
1247       state_printf (s,
1248                     _
1249                     ("[-- and the indicated access-type %s is unsupported --]\n"),
1250                     access_type);
1251       mutt_copy_hdr (s->fpin, s->fpout, ftello (s->fpin), b->parts->offset,
1252                      (option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) |
1253                      CH_DECODE, NULL);
1254     }
1255   }
1256   return (0);
1257 }
1258
1259 void mutt_decode_attachment (BODY * b, STATE * s)
1260 {
1261   int istext = mutt_is_text_part (b);
1262   iconv_t cd = (iconv_t) (-1);
1263
1264   Quotebuf[0] = '\0';
1265
1266   if (istext) {
1267     if (s->flags & M_CHARCONV) {
1268       const char *charset = mutt_get_parameter ("charset", b->parameter);
1269
1270       if (!option (OPTSTRICTMIME) && !charset)
1271         charset = mutt_get_first_charset (AssumedCharset);
1272       if (charset && Charset)
1273         cd = mutt_iconv_open (Charset, charset, M_ICONV_HOOK_FROM);
1274     }
1275     else {
1276       if (b->file_charset)
1277         cd = mutt_iconv_open (Charset, b->file_charset, M_ICONV_HOOK_FROM);
1278     }
1279   }
1280
1281   fseeko (s->fpin, b->offset, 0);
1282   switch (b->encoding) {
1283   case ENCQUOTEDPRINTABLE:
1284     mutt_decode_quoted(s, b->length,
1285                        istext || mutt_is_application_pgp(b), cd);
1286     break;
1287   case ENCBASE64:
1288     mutt_decode_base64(s, b->length,
1289                        istext || mutt_is_application_pgp(b), cd);
1290     break;
1291   case ENCUUENCODED:
1292     mutt_decode_uuencoded(s, b->length,
1293                           istext || mutt_is_application_pgp(b), cd);
1294     break;
1295   default:
1296     mutt_decode_xbit(s, b->length,
1297                      istext || mutt_is_application_pgp(b), cd);
1298     break;
1299   }
1300
1301   if (cd != (iconv_t) (-1))
1302     iconv_close (cd);
1303 }
1304
1305 int mutt_body_handler (BODY * b, STATE * s)
1306 {
1307   int decode = 0;
1308   int plaintext = 0;
1309   FILE *fp = NULL;
1310   char tempfile[_POSIX_PATH_MAX];
1311   handler_t handler = NULL;
1312   long tmpoffset = 0;
1313   ssize_t tmplength = 0;
1314   char type[STRING];
1315   int rc = 0;
1316
1317   int oflags = s->flags;
1318
1319   /* first determine which handler to use to process this part */
1320
1321   snprintf (type, sizeof (type), "%s/%s", TYPE (b), b->subtype);
1322   if (mutt_is_autoview (b, type)) {
1323     rfc1524_entry *entry = rfc1524_entry_new();
1324
1325     if (rfc1524_mailcap_lookup (b, type, entry, M_AUTOVIEW)) {
1326       handler = autoview_handler;
1327       s->flags &= ~M_CHARCONV;
1328     }
1329     rfc1524_entry_delete(&entry);
1330   }
1331   else if (b->type == TYPETEXT) {
1332     if (ascii_strcasecmp ("plain", b->subtype) == 0) {
1333       /* avoid copying this part twice since removing the transfer-encoding is
1334        * the only operation needed.
1335        */
1336       if (mutt_is_application_pgp (b))
1337         handler = crypt_pgp_application_pgp_handler;
1338       else
1339         if (ascii_strcasecmp
1340             ("flowed", mutt_get_parameter ("format", b->parameter)) == 0)
1341         handler = rfc3676_handler;
1342       else
1343         plaintext = 1;
1344     }
1345     else if (ascii_strcasecmp ("enriched", b->subtype) == 0)
1346       handler = text_enriched_handler;
1347     else                        /* text body type without a handler */
1348       plaintext = 1;
1349   }
1350   else if (b->type == TYPEMESSAGE) {
1351     if (mutt_is_message_type (b->type, b->subtype))
1352       handler = message_handler;
1353     else if (!ascii_strcasecmp ("delivery-status", b->subtype))
1354       plaintext = 1;
1355     else if (!ascii_strcasecmp ("external-body", b->subtype))
1356       handler = external_body_handler;
1357   }
1358   else if (b->type == TYPEMULTIPART) {
1359     char *p;
1360
1361     if (ascii_strcasecmp ("alternative", b->subtype) == 0)
1362       handler = alternative_handler;
1363     else if (ascii_strcasecmp ("signed", b->subtype) == 0) {
1364       p = mutt_get_parameter ("protocol", b->parameter);
1365
1366       if (!p)
1367         mutt_error (_("Error: multipart/signed has no protocol."));
1368
1369       else if (s->flags & M_VERIFY)
1370         handler = mutt_signed_handler;
1371     }
1372     else if (m_strcasecmp("encrypted", b->subtype) == 0) {
1373       p = mutt_get_parameter ("protocol", b->parameter);
1374
1375       if (!p)
1376         mutt_error (_
1377                     ("Error: multipart/encrypted has no protocol parameter!"));
1378
1379       else if (ascii_strcasecmp ("application/pgp-encrypted", p) == 0)
1380         handler = crypt_pgp_encrypted_handler;
1381     }
1382
1383     if (!handler)
1384       handler = multipart_handler;
1385   }
1386   else if (b->type == TYPEAPPLICATION) {
1387     if (mutt_is_application_pgp (b))
1388       handler = crypt_pgp_application_pgp_handler;
1389     if (mutt_is_application_smime (b))
1390       handler = crypt_smime_application_smime_handler;
1391   }
1392
1393
1394   if (plaintext || handler) {
1395     fseeko (s->fpin, b->offset, 0);
1396
1397     /* see if we need to decode this part before processing it */
1398     if (b->encoding == ENCBASE64 || b->encoding == ENCQUOTEDPRINTABLE || b->encoding == ENCUUENCODED || plaintext || mutt_is_text_part (b)) {   /* text subtypes may
1399                                                                                                                                                  * require character
1400                                                                                                                                                  * set conversion even
1401                                                                                                                                                  * with 8bit encoding.
1402                                                                                                                                                  */
1403       int origType = b->type;
1404       char *savePrefix = NULL;
1405
1406       if (!plaintext) {
1407         /* decode to a tempfile, saving the original destination */
1408         fp = s->fpout;
1409         mutt_mktemp (tempfile);
1410         if ((s->fpout = safe_fopen (tempfile, "w")) == NULL) {
1411           mutt_error _("Unable to open temporary file!");
1412
1413           goto bail;
1414         }
1415         /* decoding the attachment changes the size and offset, so save a copy
1416          * of the "real" values now, and restore them after processing
1417          */
1418         tmplength = b->length;
1419         tmpoffset = b->offset;
1420
1421         /* if we are decoding binary bodies, we don't want to prefix each
1422          * line with the prefix or else the data will get corrupted.
1423          */
1424         savePrefix = s->prefix;
1425         s->prefix = NULL;
1426
1427         decode = 1;
1428       }
1429       else
1430         b->type = TYPETEXT;
1431
1432       mutt_decode_attachment (b, s);
1433
1434       if (decode) {
1435         b->length = ftello (s->fpout);
1436         b->offset = 0;
1437         fclose (s->fpout);
1438
1439         /* restore final destination and substitute the tempfile for input */
1440         s->fpout = fp;
1441         fp = s->fpin;
1442         s->fpin = safe_fopen (tempfile, "r");
1443         unlink (tempfile);
1444
1445         /* restore the prefix */
1446         s->prefix = savePrefix;
1447       }
1448
1449       b->type = origType;
1450     }
1451
1452     /* process the (decoded) body part */
1453     if (handler) {
1454       rc = handler (b, s);
1455
1456       if (decode) {
1457         b->length = tmplength;
1458         b->offset = tmpoffset;
1459
1460         /* restore the original source stream */
1461         fclose (s->fpin);
1462         s->fpin = fp;
1463       }
1464     }
1465     s->flags |= M_FIRSTDONE;
1466   }
1467   else if (s->flags & M_DISPLAY) {
1468     state_mark_attach (s);
1469     state_printf (s, _("[-- %s/%s is unsupported "), TYPE (b), b->subtype);
1470     if (!option (OPTVIEWATTACH)) {
1471       if (km_expand_key
1472           (type, sizeof (type),
1473            km_find_func (MENU_PAGER, OP_VIEW_ATTACHMENTS)))
1474         fprintf (s->fpout, _("(use '%s' to view this part)"), type);
1475       else
1476         fputs (_("(need 'view-attachments' bound to key!)"), s->fpout);
1477     }
1478     fputs (" --]\n", s->fpout);
1479   }
1480
1481 bail:
1482   s->flags = oflags | (s->flags & M_FIRSTDONE);
1483
1484   return (rc);
1485 }