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