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