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