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