NeoMutt  2025-12-11-980-ge38c27
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
rfc2047.c
Go to the documentation of this file.
1
26
32
33#include "config.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
47#define ENCWORD_LEN_MAX 75
49#define ENCWORD_LEN_MIN 9 /* strlen ("=?.?.?.?=") */
50
52#define HSPACE(ch) (((ch) == '\0') || ((ch) == ' ') || ((ch) == '\t'))
53
55#define CONTINUATION_BYTE(ch) (((ch) & 0xc0) == 0x80)
56
68typedef size_t (*encoder_t)(char *res, const char *buf, size_t buflen, const char *tocode);
69
70/* RFC2047 specials are similar to MimeSpecials, but must include '.'
71 * for encoded-words used outside quoted MIME parameters. */
72static const char RFC2047Specials[] = "@.,;:<>[]\\\"()?/= \t";
73
77static size_t b_encoder(char *res, const char *src, size_t srclen, const char *tocode)
78{
79 char *s0 = res;
80
81 memcpy(res, "=?", 2);
82 res += 2;
83 memcpy(res, tocode, strlen(tocode));
84 res += strlen(tocode);
85 memcpy(res, "?B?", 3);
86 res += 3;
87
88 while (srclen)
89 {
90 char encoded[11] = { 0 };
91 size_t rc;
92 size_t in_len = MIN(3, srclen);
93
94 rc = mutt_b64_encode(src, in_len, encoded, sizeof(encoded));
95 for (size_t i = 0; i < rc; i++)
96 *res++ = encoded[i];
97
98 srclen -= in_len;
99 src += in_len;
100 }
101
102 memcpy(res, "?=", 2);
103 res += 2;
104 return res - s0;
105}
106
110static size_t q_encoder(char *res, const char *src, size_t srclen, const char *tocode)
111{
112 static const char hex[] = "0123456789ABCDEF";
113 char *s0 = res;
114
115 memcpy(res, "=?", 2);
116 res += 2;
117 memcpy(res, tocode, strlen(tocode));
118 res += strlen(tocode);
119 memcpy(res, "?Q?", 3);
120 res += 3;
121 while (srclen--)
122 {
123 unsigned char c = *src++;
124 if (c == ' ')
125 {
126 *res++ = '_';
127 }
128 else if ((c >= 0x7f) || (c < 0x20) || (c == '_') || strchr(RFC2047Specials, c))
129 {
130 *res++ = '=';
131 *res++ = hex[(c & 0xf0) >> 4];
132 *res++ = hex[c & 0x0f];
133 }
134 else
135 {
136 *res++ = c;
137 }
138 }
139 memcpy(res, "?=", 2);
140 res += 2;
141 return res - s0;
142}
143
155static char *parse_encoded_word(char *str, enum ContentEncoding *enc, char **charset,
156 size_t *charsetlen, char **text, size_t *textlen)
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}
179
199static size_t try_block(const char *d, size_t dlen, const char *fromcode,
200 const char *tocode, encoder_t *encoder, size_t *wlen)
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}
273
286static size_t encode_block(char *str, char *buf, size_t buflen, const char *fromcode,
287 const char *tocode, encoder_t encoder)
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}
306
323static size_t choose_block(char *d, size_t dlen, int col, const char *fromcode,
324 const char *tocode, encoder_t *encoder, size_t *wlen)
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}
343
353static void finalize_chunk(struct Buffer *res, struct Buffer *buf, char *charset, size_t charsetlen)
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}
366
377static char *decode_word(const char *s, size_t len, enum ContentEncoding enc)
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}
425
438static int encode(const char *d, size_t dlen, int col, const char *fromcode,
439 const struct Slist *charsets, char **e, size_t *elen, const char *specials)
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}
638
646void rfc2047_encode(char **pd, const char *specials, int col, const struct Slist *charsets)
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}
670
679void rfc2047_decode(char **pd)
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}
775
784void rfc2047_encode_addrlist(struct AddressList *al, const char *tag)
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}
811
819void rfc2047_decode_addrlist(struct AddressList *al)
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}
845
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}
870
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}
const char AddressSpecials[]
Characters with special meaning for email addresses.
Definition address.c:45
Email Address Handling.
size_t mutt_b64_encode(const char *in, size_t inlen, char *out, size_t outlen)
Convert raw bytes to a base64 string.
Definition base64.c:148
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_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
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
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:298
struct Buffer * buf_init(struct Buffer *buf)
Initialise a new Buffer.
Definition buffer.c:74
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition buffer.c:248
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:233
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:401
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition buffer.c:577
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
Convenience wrapper for the config headers.
const char * cc_charset(void)
Get the cached value of $charset.
const struct Slist * cc_assumed_charset(void)
Get the cached value of $assumed_charset.
Convenience wrapper for the core headers.
int mutt_tolower(int arg)
Wrapper for tolower(3)
Definition ctype.c:126
void mutt_env_set_subject(struct Envelope *env, const char *subj)
Set both subject and real_subj to subj.
Definition envelope.c:68
Representation of an email header (envelope)
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
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
#define MIN(a, b)
Return the minimum of two values.
Definition memory.h:40
#define MUTT_MEM_REALLOC(pptr, n, type)
Definition memory.h:55
#define MUTT_MEM_MALLOC(n, type)
Definition memory.h:53
Constants and macros for managing MIME encoding.
ContentEncoding
Content-Transfer-Encoding.
Definition mime.h:47
@ ENC_OTHER
Encoding unknown.
Definition mime.h:48
@ ENC_BASE64
Base-64 encoded text.
Definition mime.h:52
@ ENC_QUOTED_PRINTABLE
Quoted-printable text.
Definition mime.h:51
#define hexval(ch)
Convert hexadecimal character to its integer value.
Definition mime.h:82
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
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
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
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 MUTT_ICONV_HOOK_FROM
apply charset-hooks to fromcode
Definition charset.h:67
#define mutt_ch_is_us_ascii(str)
Definition charset.h:108
#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
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:177
bool slist_is_empty(const struct Slist *list)
Is the slist empty?
Definition slist.c:140
void slist_free(struct Slist **ptr)
Free an Slist object.
Definition slist.c:124
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition string.c:384
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition string.c:678
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
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:503
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
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
#define TAILQ_FOREACH(var, head, field)
Definition queue.h:782
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
#define CONTINUATION_BYTE(ch)
Check if a byte is a UTF-8 continuation byte (10xxxxxx pattern)
Definition rfc2047.c:55
void rfc2047_encode_addrlist(struct AddressList *al, const char *tag)
Encode any RFC2047 headers, where required, in an Address list.
Definition rfc2047.c:784
void rfc2047_encode(char **pd, const char *specials, int col, const struct Slist *charsets)
RFC-2047-encode a string.
Definition rfc2047.c:646
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
#define ENCWORD_LEN_MIN
Minimum length of an RFC2047 encoded word ("=?.?.?.?=" = 9 chars)
Definition rfc2047.c:49
void rfc2047_decode_envelope(struct Envelope *env)
Decode the fields of an Envelope.
Definition rfc2047.c:850
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
size_t(* encoder_t)(char *res, const char *buf, size_t buflen, const char *tocode)
Definition rfc2047.c:68
void rfc2047_decode(char **pd)
Decode any RFC2047-encoded header fields.
Definition rfc2047.c:679
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
static const char RFC2047Specials[]
Definition rfc2047.c:72
static char * decode_word(const char *s, size_t len, enum ContentEncoding enc)
Decode an RFC2047-encoded string.
Definition rfc2047.c:377
void rfc2047_decode_addrlist(struct AddressList *al)
Decode any RFC2047 headers in an Address list.
Definition rfc2047.c:819
void rfc2047_encode_envelope(struct Envelope *env)
Encode the fields of an Envelope.
Definition rfc2047.c:875
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
#define HSPACE(ch)
Check if character is horizontal whitespace (space, tab, or null)
Definition rfc2047.c:52
#define ENCWORD_LEN_MAX
Maximum length of an RFC2047 encoded word (75 chars per RFC2047)
Definition rfc2047.c:47
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
static void finalize_chunk(struct Buffer *res, struct Buffer *buf, char *charset, size_t charsetlen)
Perform charset conversion and filtering.
Definition rfc2047.c:353
RFC2047 MIME extensions encoding / decoding routines.
#define ASSERT(COND)
Definition signal2.h:59
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
String manipulation buffer.
Definition buffer.h:36
char * data
Pointer to data.
Definition buffer.h:37
The header of an Email.
Definition envelope.h:57
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
Container for Accounts, Notifications.
Definition neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
String list.
Definition slist.h:37
size_t count
Number of values in list.
Definition slist.h:39
#define D_SLIST_SEP_COLON
Slist items are colon-separated.
Definition types.h:112