06db45625f55f9756dc7e88c59eb6ad41772ae8f
[apps/madmutt.git] / lib-mime / rfc2047.c
1 /*
2  *  This program is free software; you can redistribute it and/or modify
3  *  it under the terms of the GNU General Public License as published by
4  *  the Free Software Foundation; either version 2 of the License, or (at
5  *  your option) any later version.
6  *
7  *  This program is distributed in the hope that it will be useful, but
8  *  WITHOUT ANY WARRANTY; without even the implied warranty of
9  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
10  *  General Public License for more details.
11  *
12  *  You should have received a copy of the GNU General Public License
13  *  along with this program; if not, write to the Free Software
14  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
15  *  MA 02110-1301, USA.
16  *
17  *  Copyright © 2006 Pierre Habouzit
18  */
19
20 /*
21  * Copyright notice from original mutt:
22  * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
23  * Copyright (C) 2000-2001 Edmund Grimley Evans <edmundo@rano.org>
24  *
25  * This file is part of mutt-ng, see http://www.muttng.org/.
26  * It's licensed under the GNU General Public License,
27  * please see the file GPL in the top level source directory.
28  */
29
30 #include <lib-lib/mem.h>
31 #include <lib-lib/str.h>
32 #include <lib-lib/ascii.h>
33
34 #include <lib-mime/mime.h>
35
36 #include "mutt.h"
37 #include "charset.h"
38 #include "thread.h"
39
40 #include <assert.h>
41 #include <ctype.h>
42 #include <errno.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46
47 /* If you are debugging this file, comment out the following line. */
48 /*#define NDEBUG*/
49
50 #ifdef NDEBUG
51 #define assert(x)
52 #else
53 #endif
54
55 #define ENCWORD_LEN_MAX 75
56 #define ENCWORD_LEN_MIN 9       /* m_strlen("=?.?.?.?=") */
57
58 #define HSPACE(x) ((x) == '\0' || (x) == ' ' || (x) == '\t')
59
60 #define CONTINUATION_BYTE(c) (((c) & 0xc0) == 0x80)
61
62 typedef size_t (*encoder_t) (char *, const char *, size_t,
63                              const char *);
64
65 /* converts f of len flen and charset from
66        into *t of len *tlen and charset to 
67
68    returns -1 on error
69    returns number of converted chars from f, see iconv(3)
70  */
71 static ssize_t
72 convert_string(const char *from, const char *f, ssize_t flen,
73                const char *to,   char **t, ssize_t *tlen)
74 {
75     iconv_t cd;
76     char *buf, *ob;
77     ssize_t obl, n;
78
79     cd = mutt_iconv_open(to, from, 0);
80
81     if (cd == (iconv_t)(-1))
82         return -1;
83
84     obl = 4 * flen + 1;
85     ob = buf = p_new(char, obl);
86     n = my_iconv(cd, &f, &flen, &ob, &obl);
87
88     if (n < 0 || my_iconv(cd, 0, 0, &ob, &obl) < 0) {
89         int e = errno;
90         iconv_close(cd);
91         errno = e;
92         p_delete(&buf);
93         return -1;
94     }
95     iconv_close(cd);
96
97     *ob = '\0';
98     *tlen = ob - buf;
99     *t  = buf;
100
101     return n;
102 }
103
104 /* choose the shortest encoding for u */
105 char *mutt_choose_charset(const char *fromcode, const char *charsets,
106                           char *u, ssize_t ulen, char **dst, ssize_t *dlen)
107 {
108     char *res = NULL;
109     ssize_t reslen = 0;
110
111     char *tocode = NULL;
112     ssize_t bestn = 0;
113
114     const char *p = charsets;
115
116     while (*p) {
117         char cset[SHORT_STRING];
118         ssize_t slen, n;
119         const char *q;
120         char *s;
121
122         q = strchr(p, ':');
123         if (q) {
124             n = m_strncpy(cset, sizeof(cset), p, q - p);
125             p = ++q;
126         } else {
127             n = m_strcpy(cset, sizeof(cset), p);
128             p += n;
129         }
130
131         if (!n ||
132             /* Assume that we never need more than 12 characters of
133                encoded-text to encode a single character. */
134             n > (ENCWORD_LEN_MAX - ENCWORD_LEN_MIN + 2 - 12))
135             continue;
136
137         n = convert_string(fromcode, u, ulen, cset, &s, &slen);
138         if (n < 0)
139             continue;
140
141         if (!tocode || n < bestn) {
142             m_strreplace(&tocode, cset);
143             bestn = n;
144
145             p_delete(&res);
146             res = s;
147             reslen = slen;
148             if (!bestn)
149                 break;
150         } else {
151             p_delete(&s);
152         }
153     }
154
155     if (tocode) {
156         char buf[LONG_STRING];
157
158         if (dst && dlen) {
159             *dst  = res;
160             *dlen = reslen;
161         } else {
162             p_delete(&res);
163         }
164
165         mutt_canonical_charset(buf, sizeof(buf), tocode);
166         m_strreplace(&tocode, buf);
167     }
168
169     return tocode;
170 }
171
172 static size_t b_encoder (char *s, const char *d, size_t dlen,
173                          const char *tocode)
174 {
175     char *s0 = s;
176
177     memcpy (s, "=?", 2), s += 2;
178     memcpy (s, tocode, m_strlen(tocode)), s += m_strlen(tocode);
179     memcpy (s, "?B?", 3), s += 3;
180     for (;;) {
181         if (!dlen)
182             break;
183         else if (dlen == 1) {
184             *s++ = __m_b64chars[(*d >> 2) & 0x3f];
185             *s++ = __m_b64chars[(*d & 0x03) << 4];
186             *s++ = '=';
187             *s++ = '=';
188             break;
189         }
190         else if (dlen == 2) {
191             *s++ = __m_b64chars[(*d >> 2) & 0x3f];
192             *s++ = __m_b64chars[((*d & 0x03) << 4) | ((d[1] >> 4) & 0x0f)];
193             *s++ = __m_b64chars[(d[1] & 0x0f) << 2];
194             *s++ = '=';
195             break;
196         }
197         else {
198             *s++ = __m_b64chars[(*d >> 2) & 0x3f];
199             *s++ = __m_b64chars[((*d & 0x03) << 4) | ((d[1] >> 4) & 0x0f)];
200             *s++ = __m_b64chars[((d[1] & 0x0f) << 2) | ((d[2] >> 6) & 0x03)];
201             *s++ = __m_b64chars[d[2] & 0x3f];
202             d += 3, dlen -= 3;
203         }
204     }
205     memcpy (s, "?=", 2), s += 2;
206     return s - s0;
207 }
208
209 static size_t q_encoder (char *s, const char *d, size_t dlen,
210                          const char *tocode)
211 {
212     char *s0 = s;
213
214     memcpy (s, "=?", 2), s += 2;
215     memcpy (s, tocode, m_strlen(tocode)), s += m_strlen(tocode);
216     memcpy (s, "?Q?", 3), s += 3;
217     while (dlen--) {
218         unsigned char c = *d++;
219
220         if (c == ' ')
221             *s++ = '_';
222         else if (c >= 0x7f || c < 0x20 || c == '_' || strchr (MimeSpecials, c)) {
223             *s++ = '=';
224             *s++ = __m_b36chars_upper[c >> 4];
225             *s++ = __m_b36chars_upper[c & 0xf];
226         }
227         else
228             *s++ = c;
229     }
230     memcpy (s, "?=", 2), s += 2;
231     return s - s0;
232 }
233
234 /*
235  * Return 0 if and set *encoder and *wlen if the data (d, dlen) could
236  * be converted to an encoded word of length *wlen using *encoder.
237  * Otherwise return an upper bound on the maximum length of the data
238  * which could be converted.
239  * The data is converted from fromcode (which must be stateless) to
240  * tocode, unless fromcode is 0, in which case the data is assumed to
241  * be already in tocode, which should be 8-bit and stateless.
242  */
243 static size_t try_block (const char *d, ssize_t dlen,
244                          const char *fromcode, const char *tocode,
245                          encoder_t * encoder, ssize_t *wlen)
246 {
247     char buf1[ENCWORD_LEN_MAX - ENCWORD_LEN_MIN + 1];
248     iconv_t cd;
249     const char *ib;
250     char *ob, *p;
251     ssize_t ibl, obl;
252     int count, len, len_b, len_q;
253
254     if (fromcode) {
255         cd = mutt_iconv_open (tocode, fromcode, 0);
256         assert (cd != (iconv_t) (-1));
257         ib = d, ibl = dlen, ob = buf1, obl = sizeof (buf1) - m_strlen(tocode);
258         if (my_iconv(cd, &ib, &ibl, &ob, &obl) < 0
259         ||  my_iconv(cd, 0, 0, &ob, &obl) < 0)
260         {
261             assert (errno == E2BIG);
262             iconv_close (cd);
263             assert (ib > d);
264             return (ib - d == dlen) ? dlen : ib - d + 1;
265         }
266         iconv_close (cd);
267     }
268     else {
269         if (dlen > ssizeof(buf1) - m_strlen(tocode))
270             return ssizeof(buf1) - m_strlen(tocode) + 1;
271         memcpy (buf1, d, dlen);
272         ob = buf1 + dlen;
273     }
274
275     count = 0;
276     for (p = buf1; p < ob; p++) {
277         unsigned char c = *p;
278
279         assert (strchr (MimeSpecials, '?'));
280         if (c >= 0x7f || c < 0x20 || *p == '_' ||
281             (c != ' ' && strchr (MimeSpecials, *p)))
282             ++count;
283     }
284
285     len = ENCWORD_LEN_MIN - 2 + m_strlen(tocode);
286     len_b = len + (((ob - buf1) + 2) / 3) * 4;
287     len_q = len + (ob - buf1) + 2 * count;
288
289     /* Apparently RFC 1468 says to use B encoding for iso-2022-jp. */
290     if (!ascii_strcasecmp (tocode, "ISO-2022-JP"))
291         len_q = ENCWORD_LEN_MAX + 1;
292
293     if (len_b < len_q && len_b <= ENCWORD_LEN_MAX) {
294         *encoder = b_encoder;
295         *wlen = len_b;
296         return 0;
297     }
298     else if (len_q <= ENCWORD_LEN_MAX) {
299         *encoder = q_encoder;
300         *wlen = len_q;
301         return 0;
302     }
303     else
304         return dlen;
305 }
306
307 /*
308  * Encode the data (d, dlen) into s using the encoder.
309  * Return the length of the encoded word.
310  */
311 static size_t encode_block (char *s, char *d, ssize_t dlen,
312                             const char *fromcode, const char *tocode,
313                             encoder_t encoder)
314 {
315     char buf1[ENCWORD_LEN_MAX - ENCWORD_LEN_MIN + 1];
316     ssize_t ibl, obl, n1, n2;
317     iconv_t cd;
318     const char *ib;
319     char *ob;
320
321     if (fromcode) {
322         cd = mutt_iconv_open (tocode, fromcode, 0);
323         assert (cd != (iconv_t) (-1));
324         ib = d, ibl = dlen, ob = buf1, obl = sizeof (buf1) - m_strlen(tocode);
325         n1 = my_iconv(cd, &ib, &ibl, &ob, &obl);
326         n2 = my_iconv(cd, 0, 0, &ob, &obl);
327         assert (n1 >= 0 && n2 >= 0);
328         iconv_close (cd);
329         return (*encoder) (s, buf1, ob - buf1, tocode);
330     }
331     else
332         return (*encoder) (s, d, dlen, tocode);
333 }
334
335 /*
336  * Discover how much of the data (d, dlen) can be converted into
337  * a single encoded word. Return how much data can be converted,
338  * and set the length *wlen of the encoded word and *encoder.
339  * We start in column col, which limits the length of the word.
340  */
341 static size_t choose_block(char *d, size_t dlen, int col,
342                            const char *fromcode, const char *tocode,
343                            encoder_t *encoder, ssize_t *wlen)
344 {
345     size_t n, nn;
346     int utf8 = fromcode && !ascii_strcasecmp (fromcode, "UTF-8");
347
348     n = dlen;
349     for (;;) {
350         assert (d + n > d);
351         nn = try_block (d, n, fromcode, tocode, encoder, wlen);
352         if (!nn && (col + *wlen <= ENCWORD_LEN_MAX + 1 || n <= 1))
353             break;
354         n = (nn ? nn : n) - 1;
355         assert (n > 0);
356         if (utf8)
357             while (n > 1 && CONTINUATION_BYTE (d[n]))
358                 --n;
359     }
360     return n;
361 }
362
363 /*
364  * Place the result of RFC-2047-encoding (d, dlen) into the dynamically
365  * allocated buffer (e, elen). The input data is in charset fromcode
366  * and is converted into a charset chosen from charsets.
367  * Return 1 if the conversion to UTF-8 failed, 2 if conversion from UTF-8
368  * failed, otherwise 0. If conversion failed, fromcode is assumed to be
369  * compatible with us-ascii and the original data is used.
370  * The input data is assumed to be a single line starting at column col;
371  * if col is non-zero, the preceding character was a space.
372  */
373 static int rfc2047_encode (const char *d, ssize_t dlen, int col,
374                            const char *fromcode, const char *charsets,
375                            char **e, ssize_t *elen, const char *specials)
376 {
377     int ret = 0;
378     char *buf;
379     ssize_t bufpos, buflen;
380     char *u, *t0, *t1, *t;
381     char *s0, *s1;
382     ssize_t ulen, r, n, wlen;
383     encoder_t encoder;
384     char *tocode1 = 0;
385     const char *tocode;
386     const char *icode = "UTF-8";
387
388     /* Try to convert to UTF-8. */
389     if (convert_string(fromcode, d, dlen, icode, &u, &ulen)) {
390         ret = 1;
391         icode = 0;
392         u = p_dupstr(d, ulen = dlen);
393     }
394
395     /* Find earliest and latest things we must encode. */
396     s0 = s1 = t0 = t1 = 0;
397     for (t = u; t < u + ulen; t++) {
398         if ((*t & 0x80) ||
399             (*t == '=' && t[1] == '?' && (t == u || HSPACE (*(t - 1))))) {
400             if (!t0)
401                 t0 = t;
402             t1 = t;
403         }
404         else if (specials && strchr (specials, *t)) {
405             if (!s0)
406                 s0 = t;
407             s1 = t;
408         }
409     }
410
411     /* If we have something to encode, include RFC822 specials */
412     if (t0 && s0 && s0 < t0)
413         t0 = s0;
414     if (t1 && s1 && s1 > t1)
415         t1 = s1;
416
417     if (!t0) {
418         /* No encoding is required. */
419         *e = u;
420         *elen = ulen;
421         return ret;
422     }
423
424     /* Choose target charset. */
425     tocode = fromcode;
426     if (icode) {
427         if ((tocode1 = mutt_choose_charset(icode, charsets, u, ulen,
428                                            NULL, NULL)))
429             tocode = tocode1;
430         else
431             ret = 2, icode = 0;
432     }
433
434     /* Hack to avoid labelling 8-bit data as us-ascii. */
435     if (!icode && mutt_is_us_ascii (tocode))
436         tocode = "unknown-8bit";
437
438     /* Adjust t0 for maximum length of line. */
439     t = u + (ENCWORD_LEN_MAX + 1) - col - ENCWORD_LEN_MIN;
440     if (t < u)
441         t = u;
442     if (t < t0)
443         t0 = t;
444
445
446     /* Adjust t0 until we can encode a character after a space. */
447     for (; t0 > u; t0--) {
448         if (!HSPACE (*(t0 - 1)))
449             continue;
450         t = t0 + 1;
451         if (icode)
452             while (t < u + ulen && CONTINUATION_BYTE (*t))
453                 ++t;
454         if (!try_block (t0, t - t0, icode, tocode, &encoder, &wlen) &&
455             col + (t0 - u) + wlen <= ENCWORD_LEN_MAX + 1)
456             break;
457     }
458
459     /* Adjust t1 until we can encode a character before a space. */
460     for (; t1 < u + ulen; t1++) {
461         if (!HSPACE (*t1))
462             continue;
463         t = t1 - 1;
464         if (icode)
465             while (CONTINUATION_BYTE (*t))
466                 --t;
467         if (!try_block (t, t1 - t, icode, tocode, &encoder, &wlen) &&
468             1 + wlen + (u + ulen - t1) <= ENCWORD_LEN_MAX + 1)
469             break;
470     }
471
472     /* We shall encode the region [t0,t1). */
473
474     /* Initialise the output buffer with the us-ascii prefix. */
475     buflen = 2 * ulen;
476     buf = p_new(char, buflen);
477     bufpos = t0 - u;
478     memcpy (buf, u, t0 - u);
479
480     col += t0 - u;
481
482     t = t0;
483     for (;;) {
484         /* Find how much we can encode. */
485         n = choose_block (t, t1 - t, col, icode, tocode, &encoder, &wlen);
486         if (n == t1 - t) {
487             /* See if we can fit the us-ascii suffix, too. */
488             if (col + wlen + (u + ulen - t1) <= ENCWORD_LEN_MAX + 1)
489                 break;
490             n = t1 - t - 1;
491             if (icode)
492                 while (CONTINUATION_BYTE (t[n]))
493                     --n;
494             assert (t + n >= t);
495             if (!n) {
496                 /* This should only happen in the really stupid case where the
497                    only word that needs encoding is one character long, but
498                    there is too much us-ascii stuff after it to use a single
499                    encoded word. We add the next word to the encoded region
500                    and try again. */
501                 assert (t1 < u + ulen);
502                 for (t1++; t1 < u + ulen && !HSPACE (*t1); t1++);
503                 continue;
504             }
505             n = choose_block (t, n, col, icode, tocode, &encoder, &wlen);
506         }
507
508         /* Add to output buffer. */
509 #define LINEBREAK "\n\t"
510         if (bufpos + wlen + m_strlen(LINEBREAK) > buflen) {
511             buflen = bufpos + wlen + m_strlen(LINEBREAK);
512             p_realloc(&buf, buflen);
513         }
514         r = encode_block (buf + bufpos, t, n, icode, tocode, encoder);
515         assert (r == wlen);
516         bufpos += wlen;
517         memcpy (buf + bufpos, LINEBREAK, m_strlen(LINEBREAK));
518         bufpos += m_strlen(LINEBREAK);
519 #undef LINEBREAK
520
521         col = 1;
522
523         t += n;
524     }
525
526     /* Add last encoded word and us-ascii suffix to buffer. */
527     buflen = bufpos + wlen + (u + ulen - t1);
528     p_realloc(&buf, buflen + 1);
529     r = encode_block (buf + bufpos, t, t1 - t, icode, tocode, encoder);
530     assert (r == wlen);
531     bufpos += wlen;
532     memcpy (buf + bufpos, t1, u + ulen - t1);
533
534     p_delete(&tocode1);
535     p_delete(&u);
536
537     buf[buflen] = '\0';
538
539     *e = buf;
540     *elen = buflen + 1;
541     return ret;
542 }
543
544 void _rfc2047_encode_string (char **pd, int encode_specials, int col)
545 {
546     char *e;
547     ssize_t elen;
548     const char *charsets;
549
550     if (!Charset || !*pd)
551         return;
552
553     charsets = SendCharset;
554     if (!charsets || !*charsets)
555         charsets = "UTF-8";
556
557     rfc2047_encode (*pd, m_strlen(*pd), col,
558                     Charset, charsets, &e, &elen,
559                     encode_specials ? RFC822Specials : NULL);
560
561     p_delete(pd);
562     *pd = e;
563 }
564
565 void rfc2047_encode_string(char **pd) {
566     _rfc2047_encode_string(pd, 0, 32);
567 }
568
569 void rfc2047_encode_adrlist (address_t * addr, const char *tag)
570 {
571     address_t *ptr = addr;
572     int col = tag ? m_strlen(tag) + 2 : 32;
573
574     while (ptr) {
575         if (ptr->personal)
576             _rfc2047_encode_string (&ptr->personal, 1, col);
577         ptr = ptr->next;
578     }
579 }
580
581 static int rfc2047_decode_word (char *d, const char *s, size_t len)
582 {
583     const char *pp, *pp1;
584     char *pd, *d0;
585     const char *t, *t1;
586     int enc = 0, count = 0;
587     char *charset = NULL;
588
589     pd = d0 = p_new(char, m_strlen(s));
590
591     for (pp = s; (pp1 = strchr (pp, '?')); pp = pp1 + 1) {
592         count++;
593         switch (count) {
594           case 2:
595             /* ignore language specification a la RFC 2231 */
596             t = pp1;
597             if ((t1 = memchr (pp, '*', t - pp)))
598                 t = t1;
599             charset = p_dupstr(pp, t - pp);
600             break;
601           case 3:
602             if (toupper ((unsigned char) *pp) == 'Q')
603                 enc = ENCQUOTEDPRINTABLE;
604             else if (toupper ((unsigned char) *pp) == 'B')
605                 enc = ENCBASE64;
606             else {
607                 p_delete(&charset);
608                 p_delete(&d0);
609                 return (-1);
610             }
611             break;
612           case 4:
613             if (enc == ENCQUOTEDPRINTABLE) {
614                 for (; pp < pp1; pp++) {
615                     if (*pp == '_')
616                         *pd++ = ' ';
617                     else if (*pp == '=' && hexval(pp[1]) >= 0 && hexval(pp[2]) >= 0) {
618                         *pd++ = (hexval (pp[1]) << 4) | hexval (pp[2]);
619                         pp += 2;
620                     }
621                     else
622                         *pd++ = *pp;
623                 }
624                 *pd = 0;
625             }
626             else if (enc == ENCBASE64) {
627                 int c, b = 0, k = 0;
628
629                 for (; pp < pp1; pp++) {
630                     if (*pp == '=')
631                         break;
632                     if ((c = base64val(*pp)) < 0)
633                         continue;
634                     if (k + 6 >= 8) {
635                         k -= 2;
636                         *pd++ = b | (c >> k);
637                         b = c << (8 - k);
638                     }
639                     else {
640                         b |= c << (k + 2);
641                         k += 6;
642                     }
643                 }
644                 *pd = 0;
645             }
646             break;
647         }
648     }
649
650     if (charset)
651         mutt_convert_string (&d0, charset, Charset, M_ICONV_HOOK_FROM);
652     m_strcpy(d, len, d0);
653     p_delete(&charset);
654     p_delete(&d0);
655     return (0);
656 }
657
658 /*
659  * Find the start and end of the first encoded word in the string.
660  * We use the grammar in section 2 of RFC 2047, but the "encoding"
661  * must be B or Q. Also, we don't require the encoded word to be
662  * separated by linear-white-space (section 5(1)).
663  */
664 static const char *find_encoded_word (const char *s, const char **x)
665 {
666     const char *p, *q;
667
668     q = s;
669     while ((p = strstr (q, "=?"))) {
670         for (q = p + 2;
671              0x20 < *q && *q < 0x7f && !strchr ("()<>@,;:\"/[]?.=", *q); q++);
672         if (q[0] != '?' || !strchr ("BbQq", q[1]) || q[2] != '?')
673             continue;
674         for (q = q + 3; 0x20 <= *q && *q < 0x7f && *q != '?'; q++);
675         if (q[0] != '?' || q[1] != '=') {
676             --q;
677             continue;
678         }
679
680         *x = q + 2;
681         return p;
682     }
683
684     return 0;
685 }
686
687 /* return length of linear white space */
688 static size_t lwslen (const char *s, size_t n)
689 {
690     const char *p = s;
691     size_t len = n;
692
693     if (n <= 0)
694         return 0;
695
696     for (; p < s + n; p++)
697         if (!strchr (" \t\r\n", *p)) {
698             len = (size_t) (p - s);
699             break;
700         }
701     if (strchr ("\r\n", *(p - 1)))        /* LWS doesn't end with CRLF */
702         len = (size_t) 0;
703     return len;
704 }
705
706 /* return length of linear white space : reverse */
707 static size_t lwsrlen (const char *s, size_t n)
708 {
709     const char *p = s + n - 1;
710     size_t len = n;
711
712     if (n <= 0)
713         return 0;
714
715     if (strchr ("\r\n", *p))      /* LWS doesn't end with CRLF */
716         return (size_t) 0;
717
718     for (; p >= s; p--)
719         if (!strchr (" \t\r\n", *p)) {
720             len = (size_t) (s + n - 1 - p);
721             break;
722         }
723     return len;
724 }
725
726 /* try to decode anything that looks like a valid RFC2047 encoded
727  * header field, ignoring RFC822 parsing rules
728  */
729 void rfc2047_decode (char **pd)
730 {
731     const char *p, *q;
732     size_t m, n;
733     int found_encoded = 0;
734     char *d0, *d;
735     const char *s = *pd;
736     size_t dlen;
737
738     if (!s || !*s)
739         return;
740
741     dlen = 4 * m_strlen(s);        /* should be enough */
742     d = d0 = p_new(char, dlen + 1);
743
744     while (*s && dlen > 0) {
745         if (!(p = find_encoded_word (s, &q))) {
746             /* no encoded words */
747             if (!option (OPTSTRICTMIME)) {
748                 n = m_strlen(s);
749                 if (found_encoded && (m = lwslen (s, n)) != 0) {
750                     if (m != n)
751                         *d = ' ', d++, dlen--;
752                     n -= m, s += m;
753                 }
754                 if (ascii_strcasecmp (AssumedCharset, "us-ascii")) {
755                     char *t;
756                     ssize_t tlen;
757
758                     t = p_dupstr(s, n);
759                     if (mutt_convert_nonmime_string (&t) == 0) {
760                         tlen = m_strlen(t);
761                         strncpy (d, t, tlen);
762                         d += tlen;
763                     }
764                     else {
765                         strncpy (d, s, n);
766                         d += n;
767                     }
768                     p_delete(&t);
769                     break;
770                 }
771             }
772             strncpy (d, s, dlen);
773             d += dlen;
774             break;
775         }
776
777         if (p != s) {
778             n = (p - s);
779             /* ignore spaces between encoded words
780              * and linear white spaces between encoded word and *text */
781             if (!option (OPTSTRICTMIME)) {
782                 if (found_encoded && (m = lwslen (s, n)) != 0) {
783                     if (m != n)
784                         *d = ' ', d++, dlen--;
785                     n -= m, s += m;
786                 }
787
788                 if ((m = n - lwsrlen (s, n)) != 0) {
789                     if (m > dlen)
790                         m = dlen;
791                     memcpy (d, s, m);
792                     d += m;
793                     dlen -= m;
794                     if (m != n)
795                         *d = ' ', d++, dlen--;
796                 }
797             }
798             else if (!found_encoded || strspn (s, " \t\r\n") != n) {
799                 if (n > dlen)
800                     n = dlen;
801                 memcpy (d, s, n);
802                 d += n;
803                 dlen -= n;
804             }
805         }
806
807         rfc2047_decode_word (d, p, dlen);
808         found_encoded = 1;
809         s = q;
810         n = m_strlen(d);
811         dlen -= n;
812         d += n;
813     }
814     *d = 0;
815
816     p_delete(pd);
817     *pd = d0;
818     str_adjust (pd);
819 }
820
821 void rfc2047_decode_adrlist(address_t *a)
822 {
823     while (a) {
824         if (a->personal)
825             rfc2047_decode(&a->personal);
826         a = a->next;
827     }
828 }
829
830 void rfc2047_decode_envelope(ENVELOPE* e)
831 {
832     assert (e);
833
834     /* do RFC2047 decoding */
835     rfc2047_decode_adrlist(e->from);
836     rfc2047_decode_adrlist(e->to);
837     rfc2047_decode_adrlist(e->cc);
838     rfc2047_decode_adrlist(e->bcc);
839     rfc2047_decode_adrlist(e->reply_to);
840     rfc2047_decode_adrlist(e->mail_followup_to);
841     rfc2047_decode_adrlist(e->return_path);
842     rfc2047_decode_adrlist(e->sender);
843
844     if (e->subject) {
845         rfc2047_decode(&e->subject);
846         mutt_adjust_subject(e);
847     }
848 }