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