move some decoding functions right into the str module, with a bit more
[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 "mutt.h"
28 #include "recvattach.h"
29 #include "handler.h"
30 #include "mutt_curses.h"
31 #include "rfc1524.h"
32 #include "rfc3676.h"
33 #include "keymap.h"
34 #include "mime.h"
35 #include "copy.h"
36 #include "charset.h"
37 #include "mutt_crypt.h"
38 #include "state.h"
39 #include "attach.h"
40 #include "lib.h"
41
42 #include "lib/debug.h"
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   size_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, size_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   size_t l = 0;
160   size_t linelen;               /* number of input bytes in `line' */
161   size_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   size_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       debug_print (2, ("didn't get a multiple of 4 chars.\n"));
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   size_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   size_t buff_len;
380   size_t line_len;
381   size_t line_used;
382   size_t line_max;
383   size_t indent_len;
384   size_t word_len;
385   size_t buff_used;
386   size_t param_used;
387   size_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       size_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           size_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   LIST *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 (WithCrypto) {
971       if (ascii_strcasecmp (a->subtype, "signed") == 0 ||
972           ascii_strcasecmp (a->subtype, "encrypted") == 0)
973         return (1);
974     }
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 (WithCrypto && a->type == TYPEAPPLICATION) {
983     if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (a))
984       return (1);
985     if ((WithCrypto & APPLICATION_SMIME) && 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, istext ||
1288                         ((WithCrypto & APPLICATION_PGP) &&
1289                          mutt_is_application_pgp (b)), cd);
1290     break;
1291   case ENCBASE64:
1292     mutt_decode_base64 (s, b->length, istext ||
1293                         ((WithCrypto & APPLICATION_PGP) &&
1294                          mutt_is_application_pgp (b)), cd);
1295     break;
1296   case ENCUUENCODED:
1297     mutt_decode_uuencoded (s, b->length, istext
1298                            || ((WithCrypto & APPLICATION_PGP) &&
1299                                mutt_is_application_pgp (b)), cd);
1300     break;
1301   default:
1302     mutt_decode_xbit (s, b->length, istext
1303                       || ((WithCrypto & APPLICATION_PGP) &&
1304                           mutt_is_application_pgp (b)), cd);
1305     break;
1306   }
1307
1308   if (cd != (iconv_t) (-1))
1309     iconv_close (cd);
1310 }
1311
1312 int mutt_body_handler (BODY * b, STATE * s)
1313 {
1314   int decode = 0;
1315   int plaintext = 0;
1316   FILE *fp = NULL;
1317   char tempfile[_POSIX_PATH_MAX];
1318   handler_t handler = NULL;
1319   long tmpoffset = 0;
1320   size_t tmplength = 0;
1321   char type[STRING];
1322   int rc = 0;
1323
1324   int oflags = s->flags;
1325
1326   /* first determine which handler to use to process this part */
1327
1328   snprintf (type, sizeof (type), "%s/%s", TYPE (b), b->subtype);
1329   if (mutt_is_autoview (b, type)) {
1330     rfc1524_entry *entry = rfc1524_new_entry ();
1331
1332     if (rfc1524_mailcap_lookup (b, type, entry, M_AUTOVIEW)) {
1333       handler = autoview_handler;
1334       s->flags &= ~M_CHARCONV;
1335     }
1336     rfc1524_free_entry (&entry);
1337   }
1338   else if (b->type == TYPETEXT) {
1339     if (ascii_strcasecmp ("plain", b->subtype) == 0) {
1340       /* avoid copying this part twice since removing the transfer-encoding is
1341        * the only operation needed.
1342        */
1343       if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b))
1344         handler = crypt_pgp_application_pgp_handler;
1345       else
1346         if (ascii_strcasecmp
1347             ("flowed", mutt_get_parameter ("format", b->parameter)) == 0)
1348         handler = rfc3676_handler;
1349       else
1350         plaintext = 1;
1351     }
1352     else if (ascii_strcasecmp ("enriched", b->subtype) == 0)
1353       handler = text_enriched_handler;
1354     else                        /* text body type without a handler */
1355       plaintext = 1;
1356   }
1357   else if (b->type == TYPEMESSAGE) {
1358     if (mutt_is_message_type (b->type, b->subtype))
1359       handler = message_handler;
1360     else if (!ascii_strcasecmp ("delivery-status", b->subtype))
1361       plaintext = 1;
1362     else if (!ascii_strcasecmp ("external-body", b->subtype))
1363       handler = external_body_handler;
1364   }
1365   else if (b->type == TYPEMULTIPART) {
1366     char *p;
1367
1368     if (ascii_strcasecmp ("alternative", b->subtype) == 0)
1369       handler = alternative_handler;
1370     else if (WithCrypto && ascii_strcasecmp ("signed", b->subtype) == 0) {
1371       p = mutt_get_parameter ("protocol", b->parameter);
1372
1373       if (!p)
1374         mutt_error (_("Error: multipart/signed has no protocol."));
1375
1376       else if (s->flags & M_VERIFY)
1377         handler = mutt_signed_handler;
1378     }
1379     else if ((WithCrypto & APPLICATION_PGP)
1380              && m_strcasecmp("encrypted", b->subtype) == 0) {
1381       p = mutt_get_parameter ("protocol", b->parameter);
1382
1383       if (!p)
1384         mutt_error (_
1385                     ("Error: multipart/encrypted has no protocol parameter!"));
1386
1387       else if (ascii_strcasecmp ("application/pgp-encrypted", p) == 0)
1388         handler = crypt_pgp_encrypted_handler;
1389     }
1390
1391     if (!handler)
1392       handler = multipart_handler;
1393   }
1394   else if (WithCrypto && b->type == TYPEAPPLICATION) {
1395     if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b))
1396       handler = crypt_pgp_application_pgp_handler;
1397     if ((WithCrypto & APPLICATION_SMIME) && mutt_is_application_smime (b))
1398       handler = crypt_smime_application_smime_handler;
1399   }
1400
1401
1402   if (plaintext || handler) {
1403     fseeko (s->fpin, b->offset, 0);
1404
1405     /* see if we need to decode this part before processing it */
1406     if (b->encoding == ENCBASE64 || b->encoding == ENCQUOTEDPRINTABLE || b->encoding == ENCUUENCODED || plaintext || mutt_is_text_part (b)) {   /* text subtypes may
1407                                                                                                                                                  * require character
1408                                                                                                                                                  * set conversion even
1409                                                                                                                                                  * with 8bit encoding.
1410                                                                                                                                                  */
1411       int origType = b->type;
1412       char *savePrefix = NULL;
1413
1414       if (!plaintext) {
1415         /* decode to a tempfile, saving the original destination */
1416         fp = s->fpout;
1417         mutt_mktemp (tempfile);
1418         if ((s->fpout = safe_fopen (tempfile, "w")) == NULL) {
1419           mutt_error _("Unable to open temporary file!");
1420
1421           goto bail;
1422         }
1423         /* decoding the attachment changes the size and offset, so save a copy
1424          * of the "real" values now, and restore them after processing
1425          */
1426         tmplength = b->length;
1427         tmpoffset = b->offset;
1428
1429         /* if we are decoding binary bodies, we don't want to prefix each
1430          * line with the prefix or else the data will get corrupted.
1431          */
1432         savePrefix = s->prefix;
1433         s->prefix = NULL;
1434
1435         decode = 1;
1436       }
1437       else
1438         b->type = TYPETEXT;
1439
1440       mutt_decode_attachment (b, s);
1441
1442       if (decode) {
1443         b->length = ftello (s->fpout);
1444         b->offset = 0;
1445         fclose (s->fpout);
1446
1447         /* restore final destination and substitute the tempfile for input */
1448         s->fpout = fp;
1449         fp = s->fpin;
1450         s->fpin = safe_fopen (tempfile, "r");
1451         unlink (tempfile);
1452
1453         /* restore the prefix */
1454         s->prefix = savePrefix;
1455       }
1456
1457       b->type = origType;
1458     }
1459
1460     /* process the (decoded) body part */
1461     if (handler) {
1462       rc = handler (b, s);
1463
1464       if (decode) {
1465         b->length = tmplength;
1466         b->offset = tmpoffset;
1467
1468         /* restore the original source stream */
1469         fclose (s->fpin);
1470         s->fpin = fp;
1471       }
1472     }
1473     s->flags |= M_FIRSTDONE;
1474   }
1475   else if (s->flags & M_DISPLAY) {
1476     state_mark_attach (s);
1477     state_printf (s, _("[-- %s/%s is unsupported "), TYPE (b), b->subtype);
1478     if (!option (OPTVIEWATTACH)) {
1479       if (km_expand_key
1480           (type, sizeof (type),
1481            km_find_func (MENU_PAGER, OP_VIEW_ATTACHMENTS)))
1482         fprintf (s->fpout, _("(use '%s' to view this part)"), type);
1483       else
1484         fputs (_("(need 'view-attachments' bound to key!)"), s->fpout);
1485     }
1486     fputs (" --]\n", s->fpout);
1487   }
1488
1489 bail:
1490   s->flags = oflags | (s->flags & M_FIRSTDONE);
1491
1492   return (rc);
1493 }