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