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