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