NeoMutt  2025-12-11-1039-g550ac6
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
rfc2047.c File Reference

RFC2047 MIME extensions encoding / decoding routines. More...

#include "config.h"
#include <errno.h>
#include <iconv.h>
#include <stdbool.h>
#include <string.h>
#include "mutt/lib.h"
#include "address/lib.h"
#include "config/lib.h"
#include "core/lib.h"
#include "rfc2047.h"
#include "envelope.h"
#include "mime.h"
Include dependency graph for rfc2047.c:

Go to the source code of this file.

Macros

#define ENCWORD_LEN_MAX   75
 Maximum length of an RFC2047 encoded word (75 chars per RFC2047).
#define ENCWORD_LEN_MIN   9 /* strlen ("=?.?.?.?=") */
 Minimum length of an RFC2047 encoded word ("=?.?.?.?=" = 9 chars).
#define HSPACE(ch)
 Check if character is horizontal whitespace (space, tab, or null).
#define CONTINUATION_BYTE(ch)
 Check if a byte is a UTF-8 continuation byte (10xxxxxx pattern).

Typedefs

typedef size_t(* encoder_t) (char *res, const char *buf, size_t buflen, const char *tocode)

Functions

static size_t b_encoder (char *res, const char *src, size_t srclen, const char *tocode)
 Base64 Encode a string - Implements encoder_t -.
static size_t q_encoder (char *res, const char *src, size_t srclen, const char *tocode)
 Quoted-printable Encode a string - Implements encoder_t -.
static char * parse_encoded_word (char *str, enum ContentEncoding *enc, char **charset, size_t *charsetlen, char **text, size_t *textlen)
 Parse a string and report RFC2047 elements.
static size_t try_block (const char *d, size_t dlen, const char *fromcode, const char *tocode, encoder_t *encoder, size_t *wlen)
 Attempt to convert a block of text.
static size_t encode_block (char *str, char *buf, size_t buflen, const char *fromcode, const char *tocode, encoder_t encoder)
 Encode a block of text using an encoder.
static size_t choose_block (char *d, size_t dlen, int col, const char *fromcode, const char *tocode, encoder_t *encoder, size_t *wlen)
 Calculate how much data can be converted.
static void finalize_chunk (struct Buffer *res, struct Buffer *buf, char *charset, size_t charsetlen)
 Perform charset conversion and filtering.
static char * decode_word (const char *s, size_t len, enum ContentEncoding enc)
 Decode an RFC2047-encoded string.
static int encode (const char *d, size_t dlen, int col, const char *fromcode, const struct Slist *charsets, char **e, size_t *elen, const char *specials)
 RFC2047-encode a string.
void rfc2047_encode (char **pd, const char *specials, int col, const struct Slist *charsets)
 RFC-2047-encode a string.
void rfc2047_decode (char **pd)
 Decode any RFC2047-encoded header fields.
void rfc2047_encode_addrlist (struct AddressList *al, const char *tag)
 Encode any RFC2047 headers, where required, in an Address list.
void rfc2047_decode_addrlist (struct AddressList *al)
 Decode any RFC2047 headers in an Address list.
void rfc2047_decode_envelope (struct Envelope *env)
 Decode the fields of an Envelope.
void rfc2047_encode_envelope (struct Envelope *env)
 Encode the fields of an Envelope.

Variables

static const char RFC2047Specials [] = "@.,;:<>[]\\\"()?/= \t"

Detailed Description

RFC2047 MIME extensions encoding / decoding routines.

Authors
  • Federico Kircheis
  • Pietro Cerutti
  • Richard Russon
  • Anna Figueiredo Gomes
  • наб

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file rfc2047.c.

Macro Definition Documentation

◆ ENCWORD_LEN_MAX

#define ENCWORD_LEN_MAX   75

Maximum length of an RFC2047 encoded word (75 chars per RFC2047).

Definition at line 47 of file rfc2047.c.

◆ ENCWORD_LEN_MIN

#define ENCWORD_LEN_MIN   9 /* strlen ("=?.?.?.?=") */

Minimum length of an RFC2047 encoded word ("=?.?.?.?=" = 9 chars).

Definition at line 49 of file rfc2047.c.

◆ HSPACE

#define HSPACE ( ch)
Value:
(((ch) == '\0') || ((ch) == ' ') || ((ch) == '\t'))

Check if character is horizontal whitespace (space, tab, or null).

Definition at line 52 of file rfc2047.c.

◆ CONTINUATION_BYTE

#define CONTINUATION_BYTE ( ch)
Value:
(((ch) & 0xc0) == 0x80)

Check if a byte is a UTF-8 continuation byte (10xxxxxx pattern).

Definition at line 55 of file rfc2047.c.

Typedef Documentation

◆ encoder_t

typedef size_t(* encoder_t) (char *res, const char *buf, size_t buflen, const char *tocode)

Definition at line 68 of file rfc2047.c.

Function Documentation

◆ parse_encoded_word()

char * parse_encoded_word ( char * str,
enum ContentEncoding * enc,
char ** charset,
size_t * charsetlen,
char ** text,
size_t * textlen )
static

Parse a string and report RFC2047 elements.

Parameters
[in]strString to parse
[out]encContent encoding found in the first RFC2047 word
[out]charsetCharset found in the first RFC2047 word
[out]charsetlenLength of the charset string found
[out]textStart of the first RFC2047 encoded text
[out]textlenLength of the encoded text found
Return values
ptrStart of the RFC2047 encoded word
NULLNone was found

Definition at line 155 of file rfc2047.c.

157{
158 regmatch_t *match = mutt_prex_capture(PREX_RFC2047_ENCODED_WORD, str);
159 if (!match)
160 return NULL;
161
162 const regmatch_t *mfull = &match[PREX_RFC2047_ENCODED_WORD_MATCH_FULL];
163 const regmatch_t *mcharset = &match[PREX_RFC2047_ENCODED_WORD_MATCH_CHARSET];
164 const regmatch_t *mencoding = &match[PREX_RFC2047_ENCODED_WORD_MATCH_ENCODING];
165 const regmatch_t *mtext = &match[PREX_RFC2047_ENCODED_WORD_MATCH_TEXT];
166
167 /* Charset */
168 *charset = str + mutt_regmatch_start(mcharset);
169 *charsetlen = mutt_regmatch_len(mcharset);
170
171 /* Encoding: either Q or B */
172 *enc = (mutt_tolower(str[mutt_regmatch_start(mencoding)]) == 'q') ? ENC_QUOTED_PRINTABLE :
174
175 *text = str + mutt_regmatch_start(mtext);
176 *textlen = mutt_regmatch_len(mtext);
177 return str + mutt_regmatch_start(mfull);
178}
int mutt_tolower(int arg)
Wrapper for tolower(3).
Definition ctype.c:125
@ ENC_BASE64
Base-64 encoded text.
Definition mime.h:52
@ ENC_QUOTED_PRINTABLE
Quoted-printable text.
Definition mime.h:51
regmatch_t * mutt_prex_capture(enum Prex which, const char *str)
Match a precompiled regex against a string.
Definition prex.c:301
@ PREX_RFC2047_ENCODED_WORD_MATCH_ENCODING
=?utf-8?[Q]?=E8=81...?=
Definition prex.h:98
@ PREX_RFC2047_ENCODED_WORD_MATCH_TEXT
=?utf-8?Q?[=E8=81...]?=
Definition prex.h:99
@ PREX_RFC2047_ENCODED_WORD_MATCH_CHARSET
=?[utf-8]?Q?=E8=81...?=
Definition prex.h:97
@ PREX_RFC2047_ENCODED_WORD_MATCH_FULL
[=?utf-8?Q?=E8=81...?=]
Definition prex.h:96
@ PREX_RFC2047_ENCODED_WORD
[=?utf-8?Q?=E8=81=AA=E6=98=8E=E7=9A=84?=]
Definition prex.h:36
static size_t mutt_regmatch_len(const regmatch_t *match)
Return the length of a match.
Definition regex3.h:76
static regoff_t mutt_regmatch_start(const regmatch_t *match)
Return the start of a match.
Definition regex3.h:56
Here is the call graph for this function:
Here is the caller graph for this function:

◆ try_block()

size_t try_block ( const char * d,
size_t dlen,
const char * fromcode,
const char * tocode,
encoder_t * encoder,
size_t * wlen )
static

Attempt to convert a block of text.

Parameters
dString to convert
dlenLength of string
fromcodeOriginal encoding
tocodeNew encoding
encoderEncoding function
wlenNumber of characters converted
Return values
0Success, string converted
>0Error, number of bytes that could be converted

If the data could be converted using encoder, then set *encoder and *wlen. Otherwise return an upper bound on the maximum length of the data which could be converted.

The data is converted from fromcode (which must be stateless) to tocode, unless fromcode is NULL, in which case the data is assumed to be already in tocode, which should be 8-bit and stateless.

Definition at line 199 of file rfc2047.c.

201{
202 char buf[ENCWORD_LEN_MAX - ENCWORD_LEN_MIN + 1] = { 0 };
203 const char *ib = NULL;
204 char *ob = NULL;
205 size_t ibl = 0;
206 size_t obl = 0;
207 int count;
208 int len;
209 int len_b;
210 int len_q;
211
212 if (fromcode)
213 {
214 iconv_t cd = mutt_ch_iconv_open(tocode, fromcode, MUTT_ICONV_NONE);
216 ib = d;
217 ibl = dlen;
218 ob = buf;
219 obl = sizeof(buf) - strlen(tocode);
220 if ((iconv(cd, (ICONV_CONST char **) &ib, &ibl, &ob, &obl) == ICONV_ILLEGAL_SEQ) ||
221 (iconv(cd, NULL, NULL, &ob, &obl) == ICONV_ILLEGAL_SEQ))
222 {
223 ASSERT(errno == E2BIG);
224 ASSERT(ib > d);
225 return ((ib - d) == dlen) ? dlen : ib - d + 1;
226 }
227 }
228 else
229 {
230 if (dlen > (sizeof(buf) - strlen(tocode)))
231 return sizeof(buf) - strlen(tocode) + 1;
232 memcpy(buf, d, dlen);
233 ob = buf + dlen;
234 }
235
236 count = 0;
237 for (char *p = buf; p < ob; p++)
238 {
239 unsigned char c = *p;
240 ASSERT(strchr(RFC2047Specials, '?'));
241 if ((c >= 0x7f) || (c < 0x20) || (*p == '_') ||
242 ((c != ' ') && strchr(RFC2047Specials, *p)))
243 {
244 count++;
245 }
246 }
247
248 len = ENCWORD_LEN_MIN - 2 + strlen(tocode);
249 len_b = len + (((ob - buf) + 2) / 3) * 4;
250 len_q = len + (ob - buf) + 2 * count;
251
252 /* Apparently RFC1468 says to use B encoding for iso-2022-jp. */
253 if (mutt_istr_equal(tocode, "ISO-2022-JP"))
254 len_q = ENCWORD_LEN_MAX + 1;
255
256 if ((len_b < len_q) && (len_b <= ENCWORD_LEN_MAX))
257 {
258 *encoder = b_encoder;
259 *wlen = len_b;
260 return 0;
261 }
262 else if (len_q <= ENCWORD_LEN_MAX)
263 {
264 *encoder = q_encoder;
265 *wlen = len_q;
266 return 0;
267 }
268 else
269 {
270 return dlen;
271 }
272}
static size_t b_encoder(char *res, const char *src, size_t srclen, const char *tocode)
Base64 Encode a string - Implements encoder_t -.
Definition rfc2047.c:77
static size_t q_encoder(char *res, const char *src, size_t srclen, const char *tocode)
Quoted-printable Encode a string - Implements encoder_t -.
Definition rfc2047.c:110
iconv_t mutt_ch_iconv_open(const char *tocode, const char *fromcode, uint8_t flags)
Set up iconv for conversions.
Definition charset.c:581
#define MUTT_ICONV_NONE
No flags are set.
Definition charset.h:66
#define ICONV_ILLEGAL_SEQ
Error value for iconv() - Illegal sequence.
Definition charset.h:114
static bool iconv_t_valid(const iconv_t cd)
Is the conversion descriptor valid?
Definition charset.h:123
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition string.c:678
#define ENCWORD_LEN_MIN
Minimum length of an RFC2047 encoded word ("=?.?.?.?=" = 9 chars).
Definition rfc2047.c:49
static const char RFC2047Specials[]
Definition rfc2047.c:72
#define ENCWORD_LEN_MAX
Maximum length of an RFC2047 encoded word (75 chars per RFC2047).
Definition rfc2047.c:47
#define ASSERT(COND)
Definition signal2.h:59
Here is the call graph for this function:
Here is the caller graph for this function:

◆ encode_block()

size_t encode_block ( char * str,
char * buf,
size_t buflen,
const char * fromcode,
const char * tocode,
encoder_t encoder )
static

Encode a block of text using an encoder.

Parameters
strString to convert
bufBuffer for result
buflenBuffer length
fromcodeOriginal encoding
tocodeNew encoding
encoderEncoding function
Return values
numLength of the encoded word

Encode the data (buf, buflen) into str using the encoder.

Definition at line 286 of file rfc2047.c.

288{
289 if (!fromcode)
290 {
291 return (*encoder)(str, buf, buflen, tocode);
292 }
293
294 const iconv_t cd = mutt_ch_iconv_open(tocode, fromcode, MUTT_ICONV_NONE);
296 const char *ib = buf;
297 size_t ibl = buflen;
298 char tmp[ENCWORD_LEN_MAX - ENCWORD_LEN_MIN + 1] = { 0 };
299 char *ob = tmp;
300 size_t obl = sizeof(tmp) - strlen(tocode);
301 const size_t n1 = iconv(cd, (ICONV_CONST char **) &ib, &ibl, &ob, &obl);
302 const size_t n2 = iconv(cd, NULL, NULL, &ob, &obl);
303 ASSERT((n1 != ICONV_ILLEGAL_SEQ) && (n2 != ICONV_ILLEGAL_SEQ));
304 return (*encoder)(str, tmp, ob - tmp, tocode);
305}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ choose_block()

size_t choose_block ( char * d,
size_t dlen,
int col,
const char * fromcode,
const char * tocode,
encoder_t * encoder,
size_t * wlen )
static

Calculate how much data can be converted.

Parameters
dString to convert
dlenLength of string
colStarting column to convert
fromcodeOriginal encoding
tocodeNew encoding
encoderEncoding function
wlenNumber of characters converted
Return values
numBytes that can be converted

Discover how much of the data (d, dlen) can be converted into a single encoded word. Return how much data can be converted, and set the length *wlen of the encoded word and *encoder. We start in column col, which limits the length of the word.

Definition at line 323 of file rfc2047.c.

325{
326 const bool utf8 = fromcode && mutt_istr_equal(fromcode, "utf-8");
327
328 size_t n = dlen;
329 while (true)
330 {
331 ASSERT(n > 0);
332 const size_t nn = try_block(d, n, fromcode, tocode, encoder, wlen);
333 if ((nn == 0) && (((col + *wlen) <= (ENCWORD_LEN_MAX + 1)) || (n <= 1)))
334 break;
335 n = ((nn != 0) ? nn : n) - 1;
336 ASSERT(n > 0);
337 if (utf8)
338 while ((n > 1) && CONTINUATION_BYTE(d[n]))
339 n--;
340 }
341 return n;
342}
#define CONTINUATION_BYTE(ch)
Check if a byte is a UTF-8 continuation byte (10xxxxxx pattern).
Definition rfc2047.c:55
static size_t try_block(const char *d, size_t dlen, const char *fromcode, const char *tocode, encoder_t *encoder, size_t *wlen)
Attempt to convert a block of text.
Definition rfc2047.c:199
Here is the call graph for this function:
Here is the caller graph for this function:

◆ finalize_chunk()

void finalize_chunk ( struct Buffer * res,
struct Buffer * buf,
char * charset,
size_t charsetlen )
static

Perform charset conversion and filtering.

Parameters
[out]resBuffer where the resulting string is appended
[in]bufBuffer with the input string
[in]charsetCharset to use for the conversion
[in]charsetlenLength of the charset parameter

The buffer buf is reinitialized at the end of this function.

Definition at line 353 of file rfc2047.c.

354{
355 if (!charset)
356 return;
357 char end = charset[charsetlen];
358 charset[charsetlen] = '\0';
360 charset[charsetlen] = end;
362 buf_addstr(res, buf->data);
363 FREE(&buf->data);
364 buf_init(buf);
365}
struct Buffer * buf_init(struct Buffer *buf)
Initialise a new Buffer.
Definition buffer.c:74
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:233
const char * cc_charset(void)
Get the cached value of $charset.
int mutt_mb_filter_unprintable(char **s)
Replace unprintable characters.
Definition mbyte.c:424
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
int mutt_ch_convert_string(char **ps, const char *from, const char *to, uint8_t flags)
Convert a string between encodings.
Definition charset.c:819
#define MUTT_ICONV_HOOK_FROM
apply charset-hooks to fromcode
Definition charset.h:67
char * data
Pointer to data.
Definition buffer.h:37
Here is the call graph for this function:
Here is the caller graph for this function:

◆ decode_word()

char * decode_word ( const char * s,
size_t len,
enum ContentEncoding enc )
static

Decode an RFC2047-encoded string.

Parameters
sString to decode
lenLength of the string
encEncoding type
Return values
ptrDecoded string
Note
The input string must be NUL-terminated; the len parameter is an optimization. The caller must free the returned string.

Definition at line 377 of file rfc2047.c.

378{
379 const char *it = s;
380 const char *end = s + len;
381
382 ASSERT(*end == '\0');
383
384 if (enc == ENC_QUOTED_PRINTABLE)
385 {
386 struct Buffer *buf = buf_pool_get();
387 for (; it < end; it++)
388 {
389 if (*it == '_')
390 {
391 buf_addch(buf, ' ');
392 }
393 else if ((it[0] == '=') && (!(it[1] & ~127) && (hexval(it[1]) != -1)) &&
394 (!(it[2] & ~127) && (hexval(it[2]) != -1)))
395 {
396 buf_addch(buf, (hexval(it[1]) << 4) | hexval(it[2]));
397 it += 2;
398 }
399 else
400 {
401 buf_addch(buf, *it);
402 }
403 }
404 char *str = buf_strdup(buf);
405 buf_pool_release(&buf);
406 return str;
407 }
408 else if (enc == ENC_BASE64)
409 {
410 const int olen = 3 * len / 4 + 1;
411 char *out = MUTT_MEM_MALLOC(olen, char);
412 int dlen = mutt_b64_decode(it, out, olen);
413 if (dlen == -1)
414 {
415 FREE(&out);
416 return NULL;
417 }
418 out[dlen] = '\0';
419 return out;
420 }
421
422 ASSERT(0); /* The enc parameter has an invalid value */
423 return NULL;
424}
int mutt_b64_decode(const char *in, char *out, size_t olen)
Convert NUL-terminated base64 string to raw bytes.
Definition base64.c:180
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition buffer.c:248
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition buffer.c:577
#define MUTT_MEM_MALLOC(n, type)
Definition memory.h:53
#define hexval(ch)
Convert hexadecimal character to its integer value.
Definition mime.h:81
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
String manipulation buffer.
Definition buffer.h:36
Here is the call graph for this function:
Here is the caller graph for this function:

◆ encode()

int encode ( const char * d,
size_t dlen,
int col,
const char * fromcode,
const struct Slist * charsets,
char ** e,
size_t * elen,
const char * specials )
static

RFC2047-encode a string.

Parameters
[in]dString to convert
[in]dlenLength of string
[in]colStarting column to convert
[in]fromcodeOriginal encoding
[in]charsetsList of allowable encodings (colon separated)
[out]eEncoded string
[out]elenLength of encoded string
[in]specialsSpecial characters to be encoded
Return values
0Success

Definition at line 438 of file rfc2047.c.

440{
441 int rc = 0;
442 char *buf = NULL;
443 size_t bufpos;
444 size_t buflen;
445 char *t0 = NULL;
446 char *t1 = NULL;
447 char *t = NULL;
448 char *s0 = NULL;
449 char *s1 = NULL;
450 size_t ulen;
451 size_t r;
452 size_t wlen = 0;
453 encoder_t encoder = NULL;
454 char *tocode1 = NULL;
455 const char *tocode = NULL;
456 const char *icode = "utf-8";
457
458 /* Try to convert to UTF-8. */
459 char *u = mutt_strn_dup(d, dlen);
460 if (mutt_ch_convert_string(&u, fromcode, icode, MUTT_ICONV_NONE) != 0)
461 {
462 rc = 1;
463 icode = 0;
464 }
465 ulen = mutt_str_len(u);
466
467 /* Find earliest and latest things we must encode. */
468 s0 = 0;
469 s1 = 0;
470 t0 = 0;
471 t1 = 0;
472 for (t = u; t < (u + ulen); t++)
473 {
474 if ((*t & 0x80) || ((*t == '=') && (t[1] == '?') && ((t == u) || HSPACE(*(t - 1)))))
475 {
476 if (!t0)
477 t0 = t;
478 t1 = t;
479 }
480 else if (specials && *t && strchr(specials, *t))
481 {
482 if (!s0)
483 s0 = t;
484 s1 = t;
485 }
486 }
487
488 /* If we have something to encode, include RFC822 specials */
489 if (t0 && s0 && (s0 < t0))
490 t0 = s0;
491 if (t1 && s1 && (s1 > t1))
492 t1 = s1;
493
494 if (!t0)
495 {
496 /* No encoding is required. */
497 *e = u;
498 *elen = ulen;
499 return rc;
500 }
501
502 /* Choose target charset. */
503 tocode = fromcode;
504 if (icode)
505 {
506 tocode1 = mutt_ch_choose(icode, charsets, u, ulen, 0, 0);
507 if (tocode1)
508 {
509 tocode = tocode1;
510 }
511 else
512 {
513 rc = 2;
514 icode = 0;
515 }
516 }
517
518 /* Hack to avoid labelling 8-bit data as us-ascii. */
519 if (!icode && mutt_ch_is_us_ascii(tocode))
520 tocode = "unknown-8bit";
521
522 /* Adjust t0 for maximum length of line. */
523 t = u + (ENCWORD_LEN_MAX + 1) - col - ENCWORD_LEN_MIN;
524 if (t < u)
525 t = u;
526 if (t < t0)
527 t0 = t;
528
529 /* Adjust t0 until we can encode a character after a space. */
530 for (; t0 > u; t0--)
531 {
532 if (!HSPACE(*(t0 - 1)))
533 continue;
534 t = t0 + 1;
535 if (icode)
536 while ((t < (u + ulen)) && CONTINUATION_BYTE(*t))
537 t++;
538 if ((try_block(t0, t - t0, icode, tocode, &encoder, &wlen) == 0) &&
539 ((col + (t0 - u) + wlen) <= (ENCWORD_LEN_MAX + 1)))
540 {
541 break;
542 }
543 }
544
545 /* Adjust t1 until we can encode a character before a space. */
546 for (; t1 < (u + ulen); t1++)
547 {
548 if (!HSPACE(*t1))
549 continue;
550 t = t1 - 1;
551 if (icode)
552 while (CONTINUATION_BYTE(*t))
553 t--;
554 if ((try_block(t, t1 - t, icode, tocode, &encoder, &wlen) == 0) &&
555 ((1 + wlen + (u + ulen - t1)) <= (ENCWORD_LEN_MAX + 1)))
556 {
557 break;
558 }
559 }
560
561 /* We shall encode the region [t0,t1). */
562
563 /* Initialise the output buffer with the us-ascii prefix. */
564 buflen = 2 * ulen;
565 buf = MUTT_MEM_MALLOC(buflen, char);
566 bufpos = t0 - u;
567 memcpy(buf, u, t0 - u);
568
569 col += t0 - u;
570
571 t = t0;
572 while (true)
573 {
574 /* Find how much we can encode. */
575 size_t n = choose_block(t, t1 - t, col, icode, tocode, &encoder, &wlen);
576 if (n == (t1 - t))
577 {
578 /* See if we can fit the us-ascii suffix, too. */
579 if ((col + wlen + (u + ulen - t1)) <= (ENCWORD_LEN_MAX + 1))
580 break;
581 n = t1 - t - 1;
582 if (icode)
583 while (CONTINUATION_BYTE(t[n]))
584 n--;
585 if (n == 0)
586 {
587 /* This should only happen in the really stupid case where the
588 * only word that needs encoding is one character long, but
589 * there is too much us-ascii stuff after it to use a single
590 * encoded word. We add the next word to the encoded region
591 * and try again. */
592 ASSERT(t1 < (u + ulen));
593 for (t1++; (t1 < (u + ulen)) && !HSPACE(*t1); t1++)
594 ; // do nothing
595
596 continue;
597 }
598 n = choose_block(t, n, col, icode, tocode, &encoder, &wlen);
599 }
600
601 /* Add to output buffer. */
602 const char *line_break = "\n\t";
603 const int lb_len = 2; /* strlen(line_break) */
604
605 if ((bufpos + wlen + lb_len) > buflen)
606 {
607 buflen = bufpos + wlen + lb_len;
608 MUTT_MEM_REALLOC(&buf, buflen, char);
609 }
610 r = encode_block(buf + bufpos, t, n, icode, tocode, encoder);
611 ASSERT(r == wlen);
612 bufpos += wlen;
613 memcpy(buf + bufpos, line_break, lb_len);
614 bufpos += lb_len;
615
616 col = 1;
617
618 t += n;
619 }
620
621 /* Add last encoded word and us-ascii suffix to buffer. */
622 buflen = bufpos + wlen + (u + ulen - t1);
623 MUTT_MEM_REALLOC(&buf, buflen + 1, char);
624 r = encode_block(buf + bufpos, t, t1 - t, icode, tocode, encoder);
625 ASSERT(r == wlen);
626 bufpos += wlen;
627 memcpy(buf + bufpos, t1, u + ulen - t1);
628
629 FREE(&tocode1);
630 FREE(&u);
631
632 buf[buflen] = '\0';
633
634 *e = buf;
635 *elen = buflen + 1;
636 return rc;
637}
#define MUTT_MEM_REALLOC(pptr, n, type)
Definition memory.h:55
char * mutt_ch_choose(const char *fromcode, const struct Slist *charsets, const char *u, size_t ulen, char **d, size_t *dlen)
Figure the best charset to encode a string.
Definition charset.c:1096
#define mutt_ch_is_us_ascii(str)
Definition charset.h:108
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition string.c:384
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:503
static size_t choose_block(char *d, size_t dlen, int col, const char *fromcode, const char *tocode, encoder_t *encoder, size_t *wlen)
Calculate how much data can be converted.
Definition rfc2047.c:323
size_t(* encoder_t)(char *res, const char *buf, size_t buflen, const char *tocode)
Definition rfc2047.c:68
static size_t encode_block(char *str, char *buf, size_t buflen, const char *fromcode, const char *tocode, encoder_t encoder)
Encode a block of text using an encoder.
Definition rfc2047.c:286
#define HSPACE(ch)
Check if character is horizontal whitespace (space, tab, or null).
Definition rfc2047.c:52
Here is the call graph for this function:
Here is the caller graph for this function:

◆ rfc2047_encode()

void rfc2047_encode ( char ** pd,
const char * specials,
int col,
const struct Slist * charsets )

RFC-2047-encode a string.

Parameters
[in,out]pdString to be encoded, and resulting encoded string
[in]specialsSpecial characters to be encoded
[in]colStarting index in string
[in]charsetsList of charsets to choose from

Definition at line 646 of file rfc2047.c.

647{
648 if (!pd || !*pd)
649 return;
650
651 const char *const c_charset = cc_charset();
652 if (!c_charset)
653 return;
654
655 struct Slist *fallback = NULL;
656 if (!charsets)
657 {
658 fallback = slist_parse("utf-8", D_SLIST_SEP_COLON);
659 charsets = fallback;
660 }
661
662 char *e = NULL;
663 size_t elen = 0;
664 encode(*pd, strlen(*pd), col, c_charset, charsets, &e, &elen, specials);
665
666 slist_free(&fallback);
667 FREE(pd);
668 *pd = e;
669}
struct Slist * slist_parse(const char *str, uint32_t flags)
Parse a list of strings into a list.
Definition slist.c:177
void slist_free(struct Slist **ptr)
Free an Slist object.
Definition slist.c:124
static int encode(const char *d, size_t dlen, int col, const char *fromcode, const struct Slist *charsets, char **e, size_t *elen, const char *specials)
RFC2047-encode a string.
Definition rfc2047.c:438
String list.
Definition slist.h:37
#define D_SLIST_SEP_COLON
Slist items are colon-separated.
Definition types.h:112
Here is the call graph for this function:
Here is the caller graph for this function:

◆ rfc2047_decode()

void rfc2047_decode ( char ** pd)

Decode any RFC2047-encoded header fields.

Parameters
[in,out]pdString to be decoded, and resulting decoded string

Try to decode anything that looks like a valid RFC2047 encoded header field, ignoring RFC822 parsing rules. If decoding fails, for example due to an invalid base64 string, the original input is left untouched.

Definition at line 679 of file rfc2047.c.

680{
681 if (!pd || !*pd)
682 return;
683
684 struct Buffer *buf = buf_pool_get(); // Output buffer
685 char *s = *pd; // Read pointer
686 char *beg = NULL; // Begin of encoded word
687 enum ContentEncoding enc = ENC_OTHER; // ENC_BASE64 or ENC_QUOTED_PRINTABLE
688 char *charset = NULL; // Which charset
689 size_t charsetlen = 0; // Length of the charset
690 char *text = NULL; // Encoded text
691 size_t textlen = 0; // Length of encoded text
692
693 /* Keep some state in case the next decoded word is using the same charset
694 * and it happens to be split in the middle of a multibyte character.
695 * See https://github.com/neomutt/neomutt/issues/1015 */
696 struct Buffer *prev = buf_pool_get(); /* Previously decoded word */
697 char *prev_charset = NULL; /* Previously used charset */
698 size_t prev_charsetlen = 0; /* Length of the previously used charset */
699
700 const struct Slist *c_assumed_charset = cc_assumed_charset();
701 const char *c_charset = cc_charset();
702 while (*s)
703 {
704 beg = parse_encoded_word(s, &enc, &charset, &charsetlen, &text, &textlen);
705 if (beg != s)
706 {
707 /* Some non-encoded text was found */
708 size_t holelen = beg ? beg - s : mutt_str_len(s);
709
710 /* Ignore whitespace between encoded words */
711 if (beg && (mutt_str_lws_len(s, holelen) == holelen))
712 {
713 s = beg;
714 continue;
715 }
716
717 /* If we have some previously decoded text, add it now */
718 if (!buf_is_empty(prev))
719 {
720 finalize_chunk(buf, prev, prev_charset, prev_charsetlen);
721 }
722
723 /* Add non-encoded part */
724 if (slist_is_empty(c_assumed_charset))
725 {
726 buf_addstr_n(buf, s, holelen);
727 }
728 else
729 {
730 char *conv = mutt_strn_dup(s, holelen);
731 mutt_ch_convert_nonmime_string(c_assumed_charset, c_charset, &conv);
732 buf_addstr(buf, conv);
733 FREE(&conv);
734 }
735 s += holelen;
736 }
737 if (beg)
738 {
739 /* Some encoded text was found */
740 text[textlen] = '\0';
741 char *decoded = decode_word(text, textlen, enc);
742 if (!decoded)
743 {
744 goto done;
745 }
746 if (!buf_is_empty(prev) && ((prev_charsetlen != charsetlen) ||
747 !mutt_strn_equal(prev_charset, charset, charsetlen)))
748 {
749 /* Different charset, convert the previous chunk and add it to the
750 * final result */
751 finalize_chunk(buf, prev, prev_charset, prev_charsetlen);
752 }
753
754 buf_addstr(prev, decoded);
755 FREE(&decoded);
756 prev_charset = charset;
757 prev_charsetlen = charsetlen;
758 s = text + textlen + 2; /* Skip final ?= */
759 }
760 }
761
762 /* Save the last chunk */
763 if (!buf_is_empty(prev))
764 {
765 finalize_chunk(buf, prev, prev_charset, prev_charsetlen);
766 }
767
768 FREE(pd);
769 *pd = buf_strdup(buf);
770
771done:
772 buf_pool_release(&buf);
773 buf_pool_release(&prev);
774}
size_t buf_addstr_n(struct Buffer *buf, const char *s, size_t len)
Add a string to a Buffer, expanding it if necessary.
Definition buffer.c:109
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:298
const struct Slist * cc_assumed_charset(void)
Get the cached value of $assumed_charset.
ContentEncoding
Content-Transfer-Encoding.
Definition mime.h:47
@ ENC_OTHER
Encoding unknown.
Definition mime.h:48
int mutt_ch_convert_nonmime_string(const struct Slist *const assumed_charset, const char *charset, char **ps)
Try to convert a string using a list of character sets.
Definition charset.c:318
bool slist_is_empty(const struct Slist *list)
Is the slist empty?
Definition slist.c:140
size_t mutt_str_lws_len(const char *s, size_t n)
Measure the linear-white-space at the beginning of a string.
Definition string.c:634
bool mutt_strn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings (to a maximum), safely.
Definition string.c:429
static char * parse_encoded_word(char *str, enum ContentEncoding *enc, char **charset, size_t *charsetlen, char **text, size_t *textlen)
Parse a string and report RFC2047 elements.
Definition rfc2047.c:155
static char * decode_word(const char *s, size_t len, enum ContentEncoding enc)
Decode an RFC2047-encoded string.
Definition rfc2047.c:377
static void finalize_chunk(struct Buffer *res, struct Buffer *buf, char *charset, size_t charsetlen)
Perform charset conversion and filtering.
Definition rfc2047.c:353
Here is the call graph for this function:
Here is the caller graph for this function:

◆ rfc2047_encode_addrlist()

void rfc2047_encode_addrlist ( struct AddressList * al,
const char * tag )

Encode any RFC2047 headers, where required, in an Address list.

Parameters
alAddressList
tagHeader tag (used for wrapping calculation)
Note
rfc2047_encode() may realloc the data pointer it's given, so work on a copy to avoid breaking the Buffer

Definition at line 784 of file rfc2047.c.

785{
786 if (!al)
787 return;
788
789 int col = tag ? strlen(tag) + 2 : 32;
790 struct Address *a = NULL;
791 char *data = NULL;
792 const struct Slist *const c_send_charset = cs_subset_slist(NeoMutt->sub, "send_charset");
793 TAILQ_FOREACH(a, al, entries)
794 {
795 if (a->personal)
796 {
797 data = buf_strdup(a->personal);
798 rfc2047_encode(&data, AddressSpecials, col, c_send_charset);
799 buf_strcpy(a->personal, data);
800 FREE(&data);
801 }
802 else if (a->group && a->mailbox)
803 {
804 data = buf_strdup(a->mailbox);
805 rfc2047_encode(&data, AddressSpecials, col, c_send_charset);
806 buf_strcpy(a->mailbox, data);
807 FREE(&data);
808 }
809 }
810}
const char AddressSpecials[]
Characters with special meaning for email addresses.
Definition address.c:45
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:401
const struct Slist * cs_subset_slist(const struct ConfigSubset *sub, const char *name)
Get a string-list config item by name.
Definition helpers.c:242
#define TAILQ_FOREACH(var, head, field)
Definition queue.h:782
void rfc2047_encode(char **pd, const char *specials, int col, const struct Slist *charsets)
RFC-2047-encode a string.
Definition rfc2047.c:646
An email address.
Definition address.h:35
struct Buffer * personal
Real name of address.
Definition address.h:36
bool group
Group mailbox?
Definition address.h:38
struct Buffer * mailbox
Mailbox and host address.
Definition address.h:37
Container for Accounts, Notifications.
Definition neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
Here is the call graph for this function:
Here is the caller graph for this function:

◆ rfc2047_decode_addrlist()

void rfc2047_decode_addrlist ( struct AddressList * al)

Decode any RFC2047 headers in an Address list.

Parameters
alAddressList
Note
rfc2047_decode() may realloc the data pointer it's given, so work on a copy to avoid breaking the Buffer

Definition at line 819 of file rfc2047.c.

820{
821 if (!al)
822 return;
823
824 const bool assumed = !slist_is_empty(cc_assumed_charset());
825 struct Address *a = NULL;
826 char *data = NULL;
827 TAILQ_FOREACH(a, al, entries)
828 {
829 if (a->personal && ((buf_find_string(a->personal, "=?")) || assumed))
830 {
831 data = buf_strdup(a->personal);
832 rfc2047_decode(&data);
833 buf_strcpy(a->personal, data);
834 FREE(&data);
835 }
836 else if (a->group && a->mailbox && buf_find_string(a->mailbox, "=?"))
837 {
838 data = buf_strdup(a->mailbox);
839 rfc2047_decode(&data);
840 buf_strcpy(a->mailbox, data);
841 FREE(&data);
842 }
843 }
844}
const char * buf_find_string(const struct Buffer *buf, const char *s)
Return a pointer to a substring found in the buffer.
Definition buffer.c:644
void rfc2047_decode(char **pd)
Decode any RFC2047-encoded header fields.
Definition rfc2047.c:679
Here is the call graph for this function:
Here is the caller graph for this function:

◆ rfc2047_decode_envelope()

void rfc2047_decode_envelope ( struct Envelope * env)

Decode the fields of an Envelope.

Parameters
envEnvelope

Definition at line 850 of file rfc2047.c.

851{
852 if (!env)
853 return;
862 rfc2047_decode(&env->x_label);
863
864 char *subj = env->subject;
865 *(char **) &env->subject = NULL;
866 rfc2047_decode(&subj);
867 mutt_env_set_subject(env, subj);
868 FREE(&subj);
869}
void mutt_env_set_subject(struct Envelope *env, const char *subj)
Set both subject and real_subj to subj.
Definition envelope.c:68
void rfc2047_decode_addrlist(struct AddressList *al)
Decode any RFC2047 headers in an Address list.
Definition rfc2047.c:819
struct AddressList return_path
Return path for the Email.
Definition envelope.h:58
char *const subject
Email's subject.
Definition envelope.h:70
struct AddressList to
Email's 'To' list.
Definition envelope.h:60
struct AddressList reply_to
Email's 'reply-to'.
Definition envelope.h:64
struct AddressList mail_followup_to
Email's 'mail-followup-to'.
Definition envelope.h:65
struct AddressList cc
Email's 'Cc' list.
Definition envelope.h:61
struct AddressList sender
Email's sender.
Definition envelope.h:63
struct AddressList bcc
Email's 'Bcc' list.
Definition envelope.h:62
char * x_label
X-Label.
Definition envelope.h:76
struct AddressList from
Email's 'From' list.
Definition envelope.h:59
Here is the call graph for this function:
Here is the caller graph for this function:

◆ rfc2047_encode_envelope()

void rfc2047_encode_envelope ( struct Envelope * env)

Encode the fields of an Envelope.

Parameters
envEnvelope

Definition at line 875 of file rfc2047.c.

876{
877 if (!env)
878 return;
879 rfc2047_encode_addrlist(&env->from, "From");
880 rfc2047_encode_addrlist(&env->to, "To");
881 rfc2047_encode_addrlist(&env->cc, "Cc");
882 rfc2047_encode_addrlist(&env->bcc, "Bcc");
883 rfc2047_encode_addrlist(&env->reply_to, "Reply-To");
884 rfc2047_encode_addrlist(&env->mail_followup_to, "Mail-Followup-To");
885 rfc2047_encode_addrlist(&env->sender, "Sender");
886 const struct Slist *const c_send_charset = cs_subset_slist(NeoMutt->sub, "send_charset");
887 rfc2047_encode(&env->x_label, NULL, sizeof("X-Label:"), c_send_charset);
888
889 char *subj = env->subject;
890 *(char **) &env->subject = NULL;
891 rfc2047_encode(&subj, NULL, sizeof("Subject:"), c_send_charset);
892 mutt_env_set_subject(env, subj);
893 FREE(&subj);
894}
void rfc2047_encode_addrlist(struct AddressList *al, const char *tag)
Encode any RFC2047 headers, where required, in an Address list.
Definition rfc2047.c:784
Here is the call graph for this function:
Here is the caller graph for this function:

Variable Documentation

◆ RFC2047Specials

const char RFC2047Specials[] = "@.,;:<>[]\\\"()?/= \t"
static

Definition at line 72 of file rfc2047.c.