NeoMutt  2023-03-22
Teaching an old dog new tricks
DOXYGEN
rfc2047.c
Go to the documentation of this file.
1
31#include "config.h"
32#include <assert.h>
33#include <ctype.h>
34#include <errno.h>
35#include <iconv.h>
36#include <stdbool.h>
37#include <string.h>
38#include "mutt/lib.h"
39#include "address/lib.h"
40#include "config/lib.h"
41#include "core/lib.h"
42#include "rfc2047.h"
43#include "envelope.h"
44#include "mime.h"
45
46#define ENCWORD_LEN_MAX 75
47#define ENCWORD_LEN_MIN 9 /* strlen ("=?.?.?.?=") */
48
49#define HSPACE(ch) (((ch) == '\0') || ((ch) == ' ') || ((ch) == '\t'))
50
51#define CONTINUATION_BYTE(ch) (((ch) &0xc0) == 0x80)
52
64typedef size_t (*encoder_t)(char *str, const char *buf, size_t buflen, const char *tocode);
65
69static size_t b_encoder(char *str, const char *buf, size_t buflen, const char *tocode)
70{
71 char *s0 = str;
72
73 memcpy(str, "=?", 2);
74 str += 2;
75 memcpy(str, tocode, strlen(tocode));
76 str += strlen(tocode);
77 memcpy(str, "?B?", 3);
78 str += 3;
79
80 while (buflen)
81 {
82 char encoded[11] = { 0 };
83 size_t rc;
84 size_t in_len = MIN(3, buflen);
85
86 rc = mutt_b64_encode(buf, in_len, encoded, sizeof(encoded));
87 for (size_t i = 0; i < rc; i++)
88 *str++ = encoded[i];
89
90 buflen -= in_len;
91 buf += in_len;
92 }
93
94 memcpy(str, "?=", 2);
95 str += 2;
96 return str - s0;
97}
98
102static size_t q_encoder(char *str, const char *buf, size_t buflen, const char *tocode)
103{
104 static const char hex[] = "0123456789ABCDEF";
105 char *s0 = str;
106
107 memcpy(str, "=?", 2);
108 str += 2;
109 memcpy(str, tocode, strlen(tocode));
110 str += strlen(tocode);
111 memcpy(str, "?Q?", 3);
112 str += 3;
113 while (buflen--)
114 {
115 unsigned char c = *buf++;
116 if (c == ' ')
117 *str++ = '_';
118 else if ((c >= 0x7f) || (c < 0x20) || (c == '_') || strchr(MimeSpecials, c))
119 {
120 *str++ = '=';
121 *str++ = hex[(c & 0xf0) >> 4];
122 *str++ = hex[c & 0x0f];
123 }
124 else
125 *str++ = c;
126 }
127 memcpy(str, "?=", 2);
128 str += 2;
129 return str - s0;
130}
131
143static char *parse_encoded_word(char *str, enum ContentEncoding *enc, char **charset,
144 size_t *charsetlen, char **text, size_t *textlen)
145{
146 regmatch_t *match = mutt_prex_capture(PREX_RFC2047_ENCODED_WORD, str);
147 if (!match)
148 return NULL;
149
150 const regmatch_t *mfull = &match[PREX_RFC2047_ENCODED_WORD_MATCH_FULL];
151 const regmatch_t *mcharset = &match[PREX_RFC2047_ENCODED_WORD_MATCH_CHARSET];
152 const regmatch_t *mencoding = &match[PREX_RFC2047_ENCODED_WORD_MATCH_ENCODING];
153 const regmatch_t *mtext = &match[PREX_RFC2047_ENCODED_WORD_MATCH_TEXT];
154
155 /* Charset */
156 *charset = str + mutt_regmatch_start(mcharset);
157 *charsetlen = mutt_regmatch_len(mcharset);
158
159 /* Encoding: either Q or B */
160 *enc = (tolower(str[mutt_regmatch_start(mencoding)]) == 'q') ? ENC_QUOTED_PRINTABLE : ENC_BASE64;
161
162 *text = str + mutt_regmatch_start(mtext);
163 *textlen = mutt_regmatch_len(mtext);
164 return str + mutt_regmatch_start(mfull);
165}
166
186static size_t try_block(const char *d, size_t dlen, const char *fromcode,
187 const char *tocode, encoder_t *encoder, size_t *wlen)
188{
189 char buf[ENCWORD_LEN_MAX - ENCWORD_LEN_MIN + 1];
190 const char *ib = NULL;
191 char *ob = NULL;
192 size_t ibl, obl;
193 int count, len, len_b, len_q;
194
195 if (fromcode)
196 {
197 iconv_t cd = mutt_ch_iconv_open(tocode, fromcode, MUTT_ICONV_NO_FLAGS);
198 assert(cd != (iconv_t) (-1));
199 ib = d;
200 ibl = dlen;
201 ob = buf;
202 obl = sizeof(buf) - strlen(tocode);
203 if ((iconv(cd, (ICONV_CONST char **) &ib, &ibl, &ob, &obl) == (size_t) (-1)) ||
204 (iconv(cd, NULL, NULL, &ob, &obl) == (size_t) (-1)))
205 {
206 assert(errno == E2BIG);
207 iconv_close(cd);
208 assert(ib > d);
209 return ((ib - d) == dlen) ? dlen : ib - d + 1;
210 }
211 iconv_close(cd);
212 }
213 else
214 {
215 if (dlen > (sizeof(buf) - strlen(tocode)))
216 return sizeof(buf) - strlen(tocode) + 1;
217 memcpy(buf, d, dlen);
218 ob = buf + dlen;
219 }
220
221 count = 0;
222 for (char *p = buf; p < ob; p++)
223 {
224 unsigned char c = *p;
225 assert(strchr(MimeSpecials, '?'));
226 if ((c >= 0x7f) || (c < 0x20) || (*p == '_') ||
227 ((c != ' ') && strchr(MimeSpecials, *p)))
228 {
229 count++;
230 }
231 }
232
233 len = ENCWORD_LEN_MIN - 2 + strlen(tocode);
234 len_b = len + (((ob - buf) + 2) / 3) * 4;
235 len_q = len + (ob - buf) + 2 * count;
236
237 /* Apparently RFC1468 says to use B encoding for iso-2022-jp. */
238 if (mutt_istr_equal(tocode, "ISO-2022-JP"))
239 len_q = ENCWORD_LEN_MAX + 1;
240
241 if ((len_b < len_q) && (len_b <= ENCWORD_LEN_MAX))
242 {
243 *encoder = b_encoder;
244 *wlen = len_b;
245 return 0;
246 }
247 else if (len_q <= ENCWORD_LEN_MAX)
248 {
249 *encoder = q_encoder;
250 *wlen = len_q;
251 return 0;
252 }
253 else
254 return dlen;
255}
256
269static size_t encode_block(char *str, char *buf, size_t buflen, const char *fromcode,
270 const char *tocode, encoder_t encoder)
271{
272 if (!fromcode)
273 {
274 return (*encoder)(str, buf, buflen, tocode);
275 }
276
277 const iconv_t cd = mutt_ch_iconv_open(tocode, fromcode, MUTT_ICONV_NO_FLAGS);
278 assert(cd != (iconv_t) (-1));
279 const char *ib = buf;
280 size_t ibl = buflen;
281 char tmp[ENCWORD_LEN_MAX - ENCWORD_LEN_MIN + 1];
282 char *ob = tmp;
283 size_t obl = sizeof(tmp) - strlen(tocode);
284 const size_t n1 = iconv(cd, (ICONV_CONST char **) &ib, &ibl, &ob, &obl);
285 const size_t n2 = iconv(cd, NULL, NULL, &ob, &obl);
286 assert(n1 != (size_t) (-1) && n2 != (size_t) (-1));
287 iconv_close(cd);
288 return (*encoder)(str, tmp, ob - tmp, tocode);
289}
290
307static size_t choose_block(char *d, size_t dlen, int col, const char *fromcode,
308 const char *tocode, encoder_t *encoder, size_t *wlen)
309{
310 const bool utf8 = fromcode && mutt_istr_equal(fromcode, "utf-8");
311
312 size_t n = dlen;
313 while (true)
314 {
315 assert(n > 0);
316 const size_t nn = try_block(d, n, fromcode, tocode, encoder, wlen);
317 if ((nn == 0) && (((col + *wlen) <= (ENCWORD_LEN_MAX + 1)) || (n <= 1)))
318 break;
319 n = ((nn != 0) ? nn : n) - 1;
320 assert(n > 0);
321 if (utf8)
322 while ((n > 1) && CONTINUATION_BYTE(d[n]))
323 n--;
324 }
325 return n;
326}
327
337static void finalize_chunk(struct Buffer *res, struct Buffer *buf, char *charset, size_t charsetlen)
338{
339 if (!charset)
340 return;
341 char end = charset[charsetlen];
342 charset[charsetlen] = '\0';
343 const char *const c_charset = cs_subset_string(NeoMutt->sub, "charset");
344 mutt_ch_convert_string(&buf->data, charset, c_charset, MUTT_ICONV_HOOK_FROM);
345 charset[charsetlen] = end;
347 mutt_buffer_addstr(res, buf->data);
348 FREE(&buf->data);
349 mutt_buffer_init(buf);
350}
351
361static char *decode_word(const char *s, size_t len, enum ContentEncoding enc)
362{
363 const char *it = s;
364 const char *end = s + len;
365
366 if (enc == ENC_QUOTED_PRINTABLE)
367 {
368 struct Buffer buf = mutt_buffer_make(0);
369 for (; it < end; it++)
370 {
371 if (*it == '_')
372 {
373 mutt_buffer_addch(&buf, ' ');
374 }
375 else if ((it[0] == '=') && (!(it[1] & ~127) && (hexval(it[1]) != -1)) &&
376 (!(it[2] & ~127) && (hexval(it[2]) != -1)))
377 {
378 mutt_buffer_addch(&buf, (hexval(it[1]) << 4) | hexval(it[2]));
379 it += 2;
380 }
381 else
382 {
383 mutt_buffer_addch(&buf, *it);
384 }
385 }
386 mutt_buffer_addch(&buf, '\0');
387 return buf.data;
388 }
389 else if (enc == ENC_BASE64)
390 {
391 const int olen = 3 * len / 4 + 1;
392 char *out = mutt_mem_malloc(olen);
393 int dlen = mutt_b64_decode(it, out, olen);
394 if (dlen == -1)
395 {
396 FREE(&out);
397 return NULL;
398 }
399 out[dlen] = '\0';
400 return out;
401 }
402
403 assert(0); /* The enc parameter has an invalid value */
404 return NULL;
405}
406
419static int encode(const char *d, size_t dlen, int col, const char *fromcode,
420 const struct Slist *charsets, char **e, size_t *elen, const char *specials)
421{
422 int rc = 0;
423 char *buf = NULL;
424 size_t bufpos, buflen;
425 char *t0 = NULL, *t1 = NULL, *t = NULL;
426 char *s0 = NULL, *s1 = NULL;
427 size_t ulen, r, wlen = 0;
428 encoder_t encoder = NULL;
429 char *tocode1 = NULL;
430 const char *tocode = NULL;
431 const char *icode = "utf-8";
432
433 /* Try to convert to UTF-8. */
434 char *u = mutt_strn_dup(d, dlen);
435 if (mutt_ch_convert_string(&u, fromcode, icode, MUTT_ICONV_NO_FLAGS) != 0)
436 {
437 rc = 1;
438 icode = 0;
439 }
440 ulen = mutt_str_len(u);
441
442 /* Find earliest and latest things we must encode. */
443 s0 = 0;
444 s1 = 0;
445 t0 = 0;
446 t1 = 0;
447 for (t = u; t < (u + ulen); t++)
448 {
449 if ((*t & 0x80) || ((*t == '=') && (t[1] == '?') && ((t == u) || HSPACE(*(t - 1)))))
450 {
451 if (!t0)
452 t0 = t;
453 t1 = t;
454 }
455 else if (specials && *t && strchr(specials, *t))
456 {
457 if (!s0)
458 s0 = t;
459 s1 = t;
460 }
461 }
462
463 /* If we have something to encode, include RFC822 specials */
464 if (t0 && s0 && (s0 < t0))
465 t0 = s0;
466 if (t1 && s1 && (s1 > t1))
467 t1 = s1;
468
469 if (!t0)
470 {
471 /* No encoding is required. */
472 *e = u;
473 *elen = ulen;
474 return rc;
475 }
476
477 /* Choose target charset. */
478 tocode = fromcode;
479 if (icode)
480 {
481 tocode1 = mutt_ch_choose(icode, charsets, u, ulen, 0, 0);
482 if (tocode1)
483 tocode = tocode1;
484 else
485 {
486 rc = 2;
487 icode = 0;
488 }
489 }
490
491 /* Hack to avoid labelling 8-bit data as us-ascii. */
492 if (!icode && mutt_ch_is_us_ascii(tocode))
493 tocode = "unknown-8bit";
494
495 /* Adjust t0 for maximum length of line. */
496 t = u + (ENCWORD_LEN_MAX + 1) - col - ENCWORD_LEN_MIN;
497 if (t < u)
498 t = u;
499 if (t < t0)
500 t0 = t;
501
502 /* Adjust t0 until we can encode a character after a space. */
503 for (; t0 > u; t0--)
504 {
505 if (!HSPACE(*(t0 - 1)))
506 continue;
507 t = t0 + 1;
508 if (icode)
509 while ((t < (u + ulen)) && CONTINUATION_BYTE(*t))
510 t++;
511 if ((try_block(t0, t - t0, icode, tocode, &encoder, &wlen) == 0) &&
512 ((col + (t0 - u) + wlen) <= (ENCWORD_LEN_MAX + 1)))
513 {
514 break;
515 }
516 }
517
518 /* Adjust t1 until we can encode a character before a space. */
519 for (; t1 < (u + ulen); t1++)
520 {
521 if (!HSPACE(*t1))
522 continue;
523 t = t1 - 1;
524 if (icode)
525 while (CONTINUATION_BYTE(*t))
526 t--;
527 if ((try_block(t, t1 - t, icode, tocode, &encoder, &wlen) == 0) &&
528 ((1 + wlen + (u + ulen - t1)) <= (ENCWORD_LEN_MAX + 1)))
529 {
530 break;
531 }
532 }
533
534 /* We shall encode the region [t0,t1). */
535
536 /* Initialise the output buffer with the us-ascii prefix. */
537 buflen = 2 * ulen;
538 buf = mutt_mem_malloc(buflen);
539 bufpos = t0 - u;
540 memcpy(buf, u, t0 - u);
541
542 col += t0 - u;
543
544 t = t0;
545 while (true)
546 {
547 /* Find how much we can encode. */
548 size_t n = choose_block(t, t1 - t, col, icode, tocode, &encoder, &wlen);
549 if (n == (t1 - t))
550 {
551 /* See if we can fit the us-ascii suffix, too. */
552 if ((col + wlen + (u + ulen - t1)) <= (ENCWORD_LEN_MAX + 1))
553 break;
554 n = t1 - t - 1;
555 if (icode)
556 while (CONTINUATION_BYTE(t[n]))
557 n--;
558 if (n == 0)
559 {
560 /* This should only happen in the really stupid case where the
561 * only word that needs encoding is one character long, but
562 * there is too much us-ascii stuff after it to use a single
563 * encoded word. We add the next word to the encoded region
564 * and try again. */
565 assert(t1 < (u + ulen));
566 for (t1++; (t1 < (u + ulen)) && !HSPACE(*t1); t1++)
567 ; // do nothing
568
569 continue;
570 }
571 n = choose_block(t, n, col, icode, tocode, &encoder, &wlen);
572 }
573
574 /* Add to output buffer. */
575 const char *line_break = "\n\t";
576 const int lb_len = 2; /* strlen(line_break) */
577
578 if ((bufpos + wlen + lb_len) > buflen)
579 {
580 buflen = bufpos + wlen + lb_len;
581 mutt_mem_realloc(&buf, buflen);
582 }
583 r = encode_block(buf + bufpos, t, n, icode, tocode, encoder);
584 assert(r == wlen);
585 bufpos += wlen;
586 memcpy(buf + bufpos, line_break, lb_len);
587 bufpos += lb_len;
588
589 col = 1;
590
591 t += n;
592 }
593
594 /* Add last encoded word and us-ascii suffix to buffer. */
595 buflen = bufpos + wlen + (u + ulen - t1);
596 mutt_mem_realloc(&buf, buflen + 1);
597 r = encode_block(buf + bufpos, t, t1 - t, icode, tocode, encoder);
598 assert(r == wlen);
599 bufpos += wlen;
600 memcpy(buf + bufpos, t1, u + ulen - t1);
601
602 FREE(&tocode1);
603 FREE(&u);
604
605 buf[buflen] = '\0';
606
607 *e = buf;
608 *elen = buflen + 1;
609 return rc;
610}
611
619void rfc2047_encode(char **pd, const char *specials, int col, const struct Slist *charsets)
620{
621 const char *const c_charset = cs_subset_string(NeoMutt->sub, "charset");
622 if (!c_charset || !pd || !*pd)
623 return;
624
625 struct Slist *fallback = NULL;
626 if (!charsets)
627 {
628 fallback = slist_parse("utf-8", SLIST_SEP_COLON);
629 charsets = fallback;
630 }
631
632 char *e = NULL;
633 size_t elen = 0;
634 encode(*pd, strlen(*pd), col, c_charset, charsets, &e, &elen, specials);
635
636 slist_free(&fallback);
637 FREE(pd);
638 *pd = e;
639}
640
649void rfc2047_decode(char **pd)
650{
651 if (!pd || !*pd)
652 return;
653
654 struct Buffer buf = mutt_buffer_make(0); /* Output buffer */
655 char *s = *pd; /* Read pointer */
656 char *beg = NULL; /* Begin of encoded word */
657 enum ContentEncoding enc; /* ENC_BASE64 or ENC_QUOTED_PRINTABLE */
658 char *charset = NULL; /* Which charset */
659 size_t charsetlen; /* Length of the charset */
660 char *text = NULL; /* Encoded text */
661 size_t textlen; /* Length of encoded text */
662
663 /* Keep some state in case the next decoded word is using the same charset
664 * and it happens to be split in the middle of a multibyte character.
665 * See https://github.com/neomutt/neomutt/issues/1015 */
666 struct Buffer prev = mutt_buffer_make(0); /* Previously decoded word */
667 char *prev_charset = NULL; /* Previously used charset */
668 size_t prev_charsetlen = 0; /* Length of the previously used charset */
669
670 while (*s)
671 {
672 beg = parse_encoded_word(s, &enc, &charset, &charsetlen, &text, &textlen);
673 if (beg != s)
674 {
675 /* Some non-encoded text was found */
676 size_t holelen = beg ? beg - s : mutt_str_len(s);
677
678 /* Ignore whitespace between encoded words */
679 if (beg && (mutt_str_lws_len(s, holelen) == holelen))
680 {
681 s = beg;
682 continue;
683 }
684
685 /* If we have some previously decoded text, add it now */
686 if (!mutt_buffer_is_empty(&prev))
687 {
688 finalize_chunk(&buf, &prev, prev_charset, prev_charsetlen);
689 }
690
691 /* Add non-encoded part */
692 {
693 const struct Slist *const c_assumed_charset = cs_subset_slist(NeoMutt->sub, "assumed_charset");
694 if (c_assumed_charset)
695 {
696 char *conv = mutt_strn_dup(s, holelen);
698 mutt_buffer_addstr(&buf, conv);
699 FREE(&conv);
700 }
701 else
702 {
703 mutt_buffer_addstr_n(&buf, s, holelen);
704 }
705 }
706 s += holelen;
707 }
708 if (beg)
709 {
710 /* Some encoded text was found */
711 text[textlen] = '\0';
712 char *decoded = decode_word(text, textlen, enc);
713 if (!decoded)
714 {
716 return;
717 }
718 if (prev.data && ((prev_charsetlen != charsetlen) ||
719 !mutt_strn_equal(prev_charset, charset, charsetlen)))
720 {
721 /* Different charset, convert the previous chunk and add it to the
722 * final result */
723 finalize_chunk(&buf, &prev, prev_charset, prev_charsetlen);
724 }
725
726 mutt_buffer_addstr(&prev, decoded);
727 FREE(&decoded);
728 prev_charset = charset;
729 prev_charsetlen = charsetlen;
730 s = text + textlen + 2; /* Skip final ?= */
731 }
732 }
733
734 /* Save the last chunk */
735 if (prev.data)
736 {
737 finalize_chunk(&buf, &prev, prev_charset, prev_charsetlen);
738 }
739
740 mutt_buffer_addch(&buf, '\0');
741 FREE(pd);
742 *pd = buf.data;
743}
744
750void rfc2047_encode_addrlist(struct AddressList *al, const char *tag)
751{
752 if (!al)
753 return;
754
755 int col = tag ? strlen(tag) + 2 : 32;
756 struct Address *a = NULL;
757 TAILQ_FOREACH(a, al, entries)
758 {
759 const struct Slist *const c_send_charset = cs_subset_slist(NeoMutt->sub, "send_charset");
760 if (a->personal)
761 rfc2047_encode(&a->personal, AddressSpecials, col, c_send_charset);
762 else if (a->group && a->mailbox)
763 rfc2047_encode(&a->mailbox, AddressSpecials, col, c_send_charset);
764 }
765}
766
771void rfc2047_decode_addrlist(struct AddressList *al)
772{
773 if (!al)
774 return;
775
776 struct Address *a = NULL;
777 TAILQ_FOREACH(a, al, entries)
778 {
779 const struct Slist *const c_assumed_charset = cs_subset_slist(NeoMutt->sub, "assumed_charset");
780 if (a->personal && ((strstr(a->personal, "=?")) || c_assumed_charset))
781 {
783 }
784 else if (a->group && a->mailbox && strstr(a->mailbox, "=?"))
786 }
787}
788
794{
795 if (!env)
796 return;
805 rfc2047_decode(&env->x_label);
806 rfc2047_decode(&env->subject);
807}
808
814{
815 if (!env)
816 return;
817 rfc2047_encode_addrlist(&env->from, "From");
818 rfc2047_encode_addrlist(&env->to, "To");
819 rfc2047_encode_addrlist(&env->cc, "Cc");
820 rfc2047_encode_addrlist(&env->bcc, "Bcc");
821 rfc2047_encode_addrlist(&env->reply_to, "Reply-To");
822 rfc2047_encode_addrlist(&env->mail_followup_to, "Mail-Followup-To");
823 rfc2047_encode_addrlist(&env->sender, "Sender");
824 const struct Slist *const c_send_charset = cs_subset_slist(NeoMutt->sub, "send_charset");
825 rfc2047_encode(&env->x_label, NULL, sizeof("X-Label:"), c_send_charset);
826 rfc2047_encode(&env->subject, NULL, sizeof("Subject:"), c_send_charset);
827}
const char AddressSpecials[]
Characters with special meaning for email addresses.
Definition: address.c:42
Email Address Handling.
size_t mutt_b64_encode(const char *in, size_t inlen, char *out, size_t outlen)
Convert raw bytes to null-terminated base64 string.
Definition: base64.c:88
int mutt_b64_decode(const char *in, char *out, size_t olen)
Convert null-terminated base64 string to raw bytes.
Definition: base64.c:136
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:67
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:298
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:347
size_t mutt_buffer_addstr_n(struct Buffer *buf, const char *s, size_t len)
Add a string to a Buffer, expanding it if necessary.
Definition: buffer.c:105
struct Buffer * mutt_buffer_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:52
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:248
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:233
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
const struct Slist * cs_subset_slist(const struct ConfigSubset *sub, const char *name)
Get a string-list config item by name.
Definition: helpers.c:268
Convenience wrapper for the config headers.
Convenience wrapper for the core headers.
Representation of an email header (envelope)
static size_t b_encoder(char *str, const char *buf, size_t buflen, const char *tocode)
Base64 Encode a string - Implements encoder_t -.
Definition: rfc2047.c:69
static size_t q_encoder(char *str, const char *buf, size_t buflen, const char *tocode)
Quoted-printable Encode a string - Implements encoder_t -.
Definition: rfc2047.c:102
int mutt_mb_filter_unprintable(char **s)
Replace unprintable characters.
Definition: mbyte.c:421
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
#define FREE(x)
Definition: memory.h:43
#define MIN(a, b)
Definition: memory.h:31
const char MimeSpecials[]
Characters that need special treatment in MIME.
Definition: mime.c:67
Constants and macros for managing MIME encoding.
ContentEncoding
Content-Transfer-Encoding.
Definition: mime.h:47
@ ENC_BASE64
Base-64 encoded text.
Definition: mime.h:52
@ ENC_QUOTED_PRINTABLE
Quoted-printable text.
Definition: mime.h:51
#define hexval(ch)
Definition: mime.h:80
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:1035
int mutt_ch_convert_string(char **ps, const char *from, const char *to, uint8_t flags)
Convert a string between encodings.
Definition: charset.c:752
int mutt_ch_convert_nonmime_string(char **ps)
Try to convert a string using a list of character sets.
Definition: charset.c:307
iconv_t mutt_ch_iconv_open(const char *tocode, const char *fromcode, uint8_t flags)
Set up iconv for conversions.
Definition: charset.c:564
#define MUTT_ICONV_HOOK_FROM
apply charset-hooks to fromcode
Definition: charset.h:72
#define mutt_ch_is_us_ascii(str)
Definition: charset.h:96
#define MUTT_ICONV_NO_FLAGS
No flags are set.
Definition: charset.h:71
Convenience wrapper for the library headers.
struct Slist * slist_parse(const char *str, uint32_t flags)
Parse a list of strings into a list.
Definition: slist.c:200
void slist_free(struct Slist **list)
Free an Slist object.
Definition: slist.c:162
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition: string.c:451
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:819
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:709
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:496
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:567
regmatch_t * mutt_prex_capture(enum Prex which, const char *str)
Match a precompiled regex against a string.
Definition: prex.c:308
@ PREX_RFC2047_ENCODED_WORD_MATCH_ENCODING
=?utf-8?[Q]?=E8=81...?=
Definition: prex.h:97
@ PREX_RFC2047_ENCODED_WORD_MATCH_TEXT
=?utf-8?Q?[=E8=81...]?=
Definition: prex.h:98
@ PREX_RFC2047_ENCODED_WORD_MATCH_CHARSET
=?[utf-8]?Q?=E8=81...?=
Definition: prex.h:96
@ PREX_RFC2047_ENCODED_WORD_MATCH_FULL
[=?utf-8?Q?=E8=81...?=]
Definition: prex.h:95
@ PREX_RFC2047_ENCODED_WORD
[=?utf-8?Q?=E8=81=AA=E6=98=8E=E7=9A=84?=]
Definition: prex.h:35
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:725
static size_t mutt_regmatch_len(const regmatch_t *match)
Return the length of a match.
Definition: regex3.h:80
static regoff_t mutt_regmatch_start(const regmatch_t *match)
Return the start of a match.
Definition: regex3.h:60
size_t(* encoder_t)(char *str, const char *buf, size_t buflen, const char *tocode)
Definition: rfc2047.c:64
#define CONTINUATION_BYTE(ch)
Definition: rfc2047.c:51
void rfc2047_encode_addrlist(struct AddressList *al, const char *tag)
Encode any RFC2047 headers, where required, in an Address list.
Definition: rfc2047.c:750
void rfc2047_encode(char **pd, const char *specials, int col, const struct Slist *charsets)
RFC-2047-encode a string.
Definition: rfc2047.c:619
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:307
#define ENCWORD_LEN_MIN
Definition: rfc2047.c:47
void rfc2047_decode_envelope(struct Envelope *env)
Decode the fields of an Envelope.
Definition: rfc2047.c:793
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:143
void rfc2047_decode(char **pd)
Decode any RFC2047-encoded header fields.
Definition: rfc2047.c:649
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:269
static char * decode_word(const char *s, size_t len, enum ContentEncoding enc)
Decode an RFC2047-encoded string.
Definition: rfc2047.c:361
void rfc2047_decode_addrlist(struct AddressList *al)
Decode any RFC2047 headers in an Address list.
Definition: rfc2047.c:771
void rfc2047_encode_envelope(struct Envelope *env)
Encode the fields of an Envelope.
Definition: rfc2047.c:813
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:419
#define HSPACE(ch)
Definition: rfc2047.c:49
#define ENCWORD_LEN_MAX
Definition: rfc2047.c:46
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:186
static void finalize_chunk(struct Buffer *res, struct Buffer *buf, char *charset, size_t charsetlen)
Perform charset conversion and filtering.
Definition: rfc2047.c:337
RFC2047 MIME extensions encoding / decoding routines.
#define SLIST_SEP_COLON
Definition: slist.h:35
An email address.
Definition: address.h:36
bool group
Group mailbox?
Definition: address.h:39
char * mailbox
Mailbox and host address.
Definition: address.h:38
char * personal
Real name of address.
Definition: address.h:37
String manipulation buffer.
Definition: buffer.h:34
char * data
Pointer to data.
Definition: buffer.h:35
The header of an Email.
Definition: envelope.h:57
struct AddressList return_path
Return path for the Email.
Definition: envelope.h:58
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
char * subject
Email's subject.
Definition: envelope.h:70
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
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
String list.
Definition: slist.h:47
size_t count
Number of values in list.
Definition: slist.h:49