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