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