NeoMutt  2025-12-11-435-g4ac674
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
parse.c
Go to the documentation of this file.
1
29
35
36#include "config.h"
37#include <errno.h>
38#include <string.h>
39#include <time.h>
40#include "mutt/lib.h"
41#include "address/lib.h"
42#include "config/lib.h"
43#include "core/lib.h"
44#include "mutt.h"
45#include "parse.h"
46#include "body.h"
47#include "email.h"
48#include "envelope.h"
49#include "from.h"
50#include "mime.h"
51#include "module_data.h"
52#include "parameter.h"
53#include "rfc2047.h"
54#include "rfc2231.h"
55#include "url.h"
56#ifdef USE_AUTOCRYPT
57#include "autocrypt/lib.h"
58#endif
59
61#define CONTENT_TOO_BIG (1 << 30)
62
63static void parse_part(FILE *fp, struct Body *b, int *counter);
64static struct Body *rfc822_parse_message(FILE *fp, struct Body *parent, int *counter);
65static struct Body *parse_multipart(FILE *fp, const char *boundary,
66 LOFF_T end_off, bool digest, int *counter);
67
73{
74 if (!header)
75 return;
76
77 for (; (*header != '\0'); header++)
78 {
79 if ((*header < 33) || (*header > 126) || (*header == ':'))
80 *header = '?';
81 }
82}
83
93{
94 if (!header)
95 return;
96
97 for (; (*header != '\0'); header++)
98 {
99 if ((*header == '\n') || (*header == '\r'))
100 *header = ' ';
101 }
102}
103
108void mutt_auto_subscribe(const char *mailto)
109{
110 if (!mailto)
111 return;
112
114 ASSERT(md);
115
116 if (!md->auto_subscribe_cache)
118
119 if (mutt_hash_find(md->auto_subscribe_cache, mailto))
120 return;
121
123
124 struct Envelope *lpenv = mutt_env_new(); /* parsed envelope from the List-Post mailto: URL */
125
126 if (mutt_parse_mailto(lpenv, NULL, mailto) && !TAILQ_EMPTY(&lpenv->to))
127 {
128 const char *mailbox = buf_string(TAILQ_FIRST(&lpenv->to)->mailbox);
129 if (mailbox && !mutt_regexlist_match(&md->subscribed, mailbox) &&
130 !mutt_regexlist_match(&md->unmail, mailbox) &&
131 !mutt_regexlist_match(&md->unsubscribed, mailbox))
132 {
133 /* mutt_regexlist_add() detects duplicates, so it is safe to
134 * try to add here without any checks. */
135 mutt_regexlist_add(&md->mail, mailbox, REG_ICASE, NULL);
136 mutt_regexlist_add(&md->subscribed, mailbox, REG_ICASE, NULL);
137 }
138 }
139
140 mutt_env_free(&lpenv);
141}
142
154static void parse_parameters(struct ParameterList *pl, const char *s, bool allow_value_spaces)
155{
156 struct Parameter *pnew = NULL;
157 const char *p = NULL;
158 size_t i;
159
160 struct Buffer *buf = buf_pool_get();
161 /* allow_value_spaces, especially with autocrypt keydata, can result
162 * in quite large parameter values. avoid frequent reallocs by
163 * pre-sizing */
164 if (allow_value_spaces)
165 buf_alloc(buf, mutt_str_len(s));
166
167 mutt_debug(LL_DEBUG2, "'%s'\n", s);
168
169 const bool assumed = !slist_is_empty(cc_assumed_charset());
170 while (*s)
171 {
172 buf_reset(buf);
173
174 p = strpbrk(s, "=;");
175 if (!p)
176 {
177 mutt_debug(LL_DEBUG1, "malformed parameter: %s\n", s);
178 goto bail;
179 }
180
181 /* if we hit a ; now the parameter has no value, just skip it */
182 if (*p != ';')
183 {
184 i = p - s;
185 /* remove whitespace from the end of the attribute name */
186 while ((i > 0) && mutt_str_is_email_wsp(s[i - 1]))
187 i--;
188
189 /* the check for the missing parameter token is here so that we can skip
190 * over any quoted value that may be present. */
191 if (i == 0)
192 {
193 mutt_debug(LL_DEBUG1, "missing attribute: %s\n", s);
194 pnew = NULL;
195 }
196 else
197 {
198 pnew = mutt_param_new();
199 pnew->attribute = mutt_strn_dup(s, i);
200 }
201
202 do
203 {
204 s = mutt_str_skip_email_wsp(p + 1); /* skip over the =, or space if we loop */
205
206 if (*s == '"')
207 {
208 bool state_ascii = true;
209 s++;
210 for (; *s; s++)
211 {
212 if (assumed)
213 {
214 // As iso-2022-* has a character of '"' with non-ascii state, ignore it
215 if (*s == 0x1b)
216 {
217 if ((s[1] == '(') && ((s[2] == 'B') || (s[2] == 'J')))
218 state_ascii = true;
219 else
220 state_ascii = false;
221 }
222 }
223 if (state_ascii && (*s == '"'))
224 break;
225 if (*s == '\\')
226 {
227 if (s[1])
228 {
229 s++;
230 /* Quote the next character */
231 buf_addch(buf, *s);
232 }
233 }
234 else
235 {
236 buf_addch(buf, *s);
237 }
238 }
239 if (*s)
240 s++; /* skip over the " */
241 }
242 else
243 {
244 for (; *s && *s != ' ' && *s != ';'; s++)
245 buf_addch(buf, *s);
246 }
247
248 p = s;
249 } while (allow_value_spaces && (*s == ' '));
250
251 /* if the attribute token was missing, 'new' will be NULL */
252 if (pnew)
253 {
254 pnew->value = buf_strdup(buf);
255
256 mutt_debug(LL_DEBUG2, "parse_parameter: '%s' = '%s'\n",
257 pnew->attribute ? pnew->attribute : "", pnew->value ? pnew->value : "");
258
259 /* Add this parameter to the list */
260 TAILQ_INSERT_HEAD(pl, pnew, entries);
261 }
262 }
263 else
264 {
265 mutt_debug(LL_DEBUG1, "parameter with no value: %s\n", s);
266 s = p;
267 }
268
269 /* Find the next parameter */
270 if ((*s != ';') && !(s = strchr(s, ';')))
271 break; /* no more parameters */
272
273 do
274 {
275 /* Move past any leading whitespace. the +1 skips over the semicolon */
276 s = mutt_str_skip_email_wsp(s + 1);
277 } while (*s == ';'); /* skip empty parameters */
278 }
279
280bail:
281
283 buf_pool_release(&buf);
284}
285
293static void parse_content_disposition(const char *s, struct Body *b)
294{
295 struct ParameterList pl = TAILQ_HEAD_INITIALIZER(pl);
296
297 if (mutt_istr_startswith(s, "inline"))
299 else if (mutt_istr_startswith(s, "form-data"))
301 else
303
304 /* Check to see if a default filename was given */
305 s = strchr(s, ';');
306 if (s)
307 {
308 s = mutt_str_skip_email_wsp(s + 1);
309 parse_parameters(&pl, s, false);
310 s = mutt_param_get(&pl, "filename");
311 if (s)
313 s = mutt_param_get(&pl, "name");
314 if (s)
316 mutt_param_free(&pl);
317 }
318}
319
325static void parse_references(struct ListHead *head, const char *s)
326{
327 if (!head)
328 return;
329
330 char *m = NULL;
331 for (size_t off = 0; (m = mutt_extract_message_id(s, &off)); s += off)
332 {
333 mutt_list_insert_head(head, m);
334 }
335}
336
342static void parse_content_language(const char *s, struct Body *b)
343{
344 if (!s || !b)
345 return;
346
347 mutt_debug(LL_DEBUG2, "RFC8255 >> Content-Language set to %s\n", s);
349}
350
358bool mutt_matches_ignore(const char *s)
359{
361 ASSERT(md);
362
363 return mutt_list_match(s, &md->ignore) && !mutt_list_match(s, &md->unignore);
364}
365
372{
373 if (mutt_istr_equal("text", s))
374 return TYPE_TEXT;
375 if (mutt_istr_equal("multipart", s))
376 return TYPE_MULTIPART;
377 if (mutt_istr_equal("x-sun-attachment", s))
378 return TYPE_MULTIPART;
379 if (mutt_istr_equal("application", s))
380 return TYPE_APPLICATION;
381 if (mutt_istr_equal("message", s))
382 return TYPE_MESSAGE;
383 if (mutt_istr_equal("image", s))
384 return TYPE_IMAGE;
385 if (mutt_istr_equal("audio", s))
386 return TYPE_AUDIO;
387 if (mutt_istr_equal("video", s))
388 return TYPE_VIDEO;
389 if (mutt_istr_equal("model", s))
390 return TYPE_MODEL;
391 if (mutt_istr_equal("*", s))
392 return TYPE_ANY;
393 if (mutt_istr_equal(".*", s))
394 return TYPE_ANY;
395
396 return TYPE_OTHER;
397}
398
406char *mutt_extract_message_id(const char *s, size_t *len)
407{
408 if (!s || (*s == '\0'))
409 return NULL;
410
411 char *decoded = mutt_str_dup(s);
412 rfc2047_decode(&decoded);
413
414 char *res = NULL;
415
416 for (const char *p = decoded, *beg = NULL; *p; p++)
417 {
418 if (*p == '<')
419 {
420 beg = p;
421 continue;
422 }
423
424 if (beg && (*p == '>'))
425 {
426 if (len)
427 *len = p - decoded + 1;
428 res = mutt_strn_dup(beg, (p + 1) - beg);
429 break;
430 }
431 }
432
433 FREE(&decoded);
434 return res;
435}
436
442int mutt_check_encoding(const char *c)
443{
444 if (mutt_istr_startswith(c, "7bit"))
445 return ENC_7BIT;
446 if (mutt_istr_startswith(c, "8bit"))
447 return ENC_8BIT;
448 if (mutt_istr_startswith(c, "binary"))
449 return ENC_BINARY;
450 if (mutt_istr_startswith(c, "quoted-printable"))
452 if (mutt_istr_startswith(c, "base64"))
453 return ENC_BASE64;
454 if (mutt_istr_startswith(c, "x-uuencode"))
455 return ENC_UUENCODED;
456 if (mutt_istr_startswith(c, "uuencode"))
457 return ENC_UUENCODED;
458 return ENC_OTHER;
459}
460
468void mutt_parse_content_type(const char *s, struct Body *b)
469{
470 if (!s || !b)
471 return;
472
473 FREE(&b->subtype);
475
476 /* First extract any existing parameters */
477 char *pc = strchr(s, ';');
478 if (pc)
479 {
480 *pc++ = 0;
481 while (*pc && mutt_isspace(*pc))
482 pc++;
483 parse_parameters(&b->parameter, pc, false);
484
485 /* Some pre-RFC1521 gateways still use the "name=filename" convention,
486 * but if a filename has already been set in the content-disposition,
487 * let that take precedence, and don't set it here */
488 pc = mutt_param_get(&b->parameter, "name");
489 if (pc && !b->filename)
490 b->filename = mutt_str_dup(pc);
491
492 /* this is deep and utter perversion */
493 pc = mutt_param_get(&b->parameter, "conversions");
494 if (pc)
496 }
497
498 /* Now get the subtype */
499 char *subtype = strchr(s, '/');
500 if (subtype)
501 {
502 *subtype++ = '\0';
503 for (pc = subtype; *pc && !mutt_isspace(*pc) && (*pc != ';'); pc++)
504 ; // do nothing
505
506 *pc = '\0';
507 mutt_str_replace(&b->subtype, subtype);
508 }
509
510 /* Finally, get the major type */
512
513 if (mutt_istr_equal("x-sun-attachment", s))
514 mutt_str_replace(&b->subtype, "x-sun-attachment");
515
516 if (b->type == TYPE_OTHER)
517 {
518 mutt_str_replace(&b->xtype, s);
519 }
520
521 if (!b->subtype)
522 {
523 /* Some older non-MIME mailers (i.e., mailtool, elm) have a content-type
524 * field, so we can attempt to convert the type to Body here. */
525 if (b->type == TYPE_TEXT)
526 {
527 b->subtype = mutt_str_dup("plain");
528 }
529 else if (b->type == TYPE_AUDIO)
530 {
531 b->subtype = mutt_str_dup("basic");
532 }
533 else if (b->type == TYPE_MESSAGE)
534 {
535 b->subtype = mutt_str_dup("rfc822");
536 }
537 else if (b->type == TYPE_OTHER)
538 {
539 char buf[128] = { 0 };
540
542 snprintf(buf, sizeof(buf), "x-%s", s);
543 b->subtype = mutt_str_dup(buf);
544 }
545 else
546 {
547 b->subtype = mutt_str_dup("x-unknown");
548 }
549 }
550
551 /* Default character set for text types. */
552 if (b->type == TYPE_TEXT)
553 {
554 pc = mutt_param_get(&b->parameter, "charset");
555 if (pc)
556 {
557 /* Microsoft Outlook seems to think it is necessary to repeat
558 * charset=, strip it off not to confuse ourselves */
559 if (mutt_istrn_equal(pc, "charset=", sizeof("charset=") - 1))
560 mutt_param_set(&b->parameter, "charset", pc + (sizeof("charset=") - 1));
561 }
562 else
563 {
564 mutt_param_set(&b->parameter, "charset",
566 }
567 }
568}
569
570#ifdef USE_AUTOCRYPT
577static struct AutocryptHeader *parse_autocrypt(struct AutocryptHeader *head, const char *s)
578{
579 struct AutocryptHeader *autocrypt = mutt_autocrypthdr_new();
580 autocrypt->next = head;
581
582 struct ParameterList pl = TAILQ_HEAD_INITIALIZER(pl);
583 parse_parameters(&pl, s, true);
584 if (TAILQ_EMPTY(&pl))
585 {
586 autocrypt->invalid = true;
587 goto cleanup;
588 }
589
590 struct Parameter *p = NULL;
591 TAILQ_FOREACH(p, &pl, entries)
592 {
593 if (mutt_istr_equal(p->attribute, "addr"))
594 {
595 if (autocrypt->addr)
596 {
597 autocrypt->invalid = true;
598 goto cleanup;
599 }
600 autocrypt->addr = p->value;
601 p->value = NULL;
602 }
603 else if (mutt_istr_equal(p->attribute, "prefer-encrypt"))
604 {
605 if (mutt_istr_equal(p->value, "mutual"))
606 autocrypt->prefer_encrypt = true;
607 }
608 else if (mutt_istr_equal(p->attribute, "keydata"))
609 {
610 if (autocrypt->keydata)
611 {
612 autocrypt->invalid = true;
613 goto cleanup;
614 }
615 autocrypt->keydata = p->value;
616 p->value = NULL;
617 }
618 else if (p->attribute && (p->attribute[0] != '_'))
619 {
620 autocrypt->invalid = true;
621 goto cleanup;
622 }
623 }
624
625 /* Checking the addr against From, and for multiple valid headers
626 * occurs later, after all the headers are parsed. */
627 if (!autocrypt->addr || !autocrypt->keydata)
628 autocrypt->invalid = true;
629
630cleanup:
631 mutt_param_free(&pl);
632 return autocrypt;
633}
634#endif
635
641static char *rfc2369_first_mailto(const char *body)
642{
643 for (const char *beg = body, *end = NULL; beg; beg = strchr(end, ','))
644 {
645 beg = strchr(beg, '<');
646 if (!beg)
647 {
648 break;
649 }
650 beg++;
651 end = strchr(beg, '>');
652 if (!end)
653 {
654 break;
655 }
656
657 char *mlist = mutt_strn_dup(beg, end - beg);
658 if (url_check_scheme(mlist) == U_MAILTO)
659 {
660 return mlist;
661 }
662 FREE(&mlist);
663 }
664 return NULL;
665}
666
683int mutt_rfc822_parse_line(struct Envelope *env, struct Email *e,
684 const char *name, size_t name_len, const char *body,
685 bool user_hdrs, bool weed, bool do_2047)
686{
687 if (!env || !name)
688 return 0;
689
690 bool matched = false;
691
692 switch (name[0] | 0x20)
693 {
694 case 'a':
695 if ((name_len == 13) && eqi12(name + 1, "pparently-to"))
696 {
697 mutt_addrlist_parse(&env->to, body);
698 matched = true;
699 }
700 else if ((name_len == 15) && eqi14(name + 1, "pparently-from"))
701 {
702 mutt_addrlist_parse(&env->from, body);
703 matched = true;
704 }
705#ifdef USE_AUTOCRYPT
706 else if ((name_len == 9) && eqi8(name + 1, "utocrypt"))
707 {
708 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
709 if (c_autocrypt)
710 {
711 env->autocrypt = parse_autocrypt(env->autocrypt, body);
712 matched = true;
713 }
714 }
715 else if ((name_len == 16) && eqi15(name + 1, "utocrypt-gossip"))
716 {
717 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
718 if (c_autocrypt)
719 {
721 matched = true;
722 }
723 }
724#endif
725 break;
726
727 case 'b':
728 if ((name_len == 3) && eqi2(name + 1, "cc"))
729 {
730 mutt_addrlist_parse(&env->bcc, body);
731 matched = true;
732 }
733 break;
734
735 case 'c':
736 if ((name_len == 2) && eqi1(name + 1, "c"))
737 {
738 mutt_addrlist_parse(&env->cc, body);
739 matched = true;
740 }
741 else
742 {
743 if ((name_len >= 12) && eqi8(name, "content-"))
744 {
745 if ((name_len == 12) && eqi4(name + 8, "type"))
746 {
747 if (e)
749 matched = true;
750 }
751 else if ((name_len == 16) && eqi8(name + 8, "language"))
752 {
753 if (e)
755 matched = true;
756 }
757 else if ((name_len == 25) && eqi17(name + 8, "transfer-encoding"))
758 {
759 if (e)
761 matched = true;
762 }
763 else if ((name_len == 14) && eqi8(name + 6, "t-length"))
764 {
765 if (e)
766 {
767 unsigned long len = 0;
768 e->body->length = mutt_str_atoul(body, &len) ? MIN(len, CONTENT_TOO_BIG) : -1;
769 }
770 matched = true;
771 }
772 else if ((name_len == 19) && eqi11(name + 8, "description"))
773 {
774 if (e)
775 {
778 }
779 matched = true;
780 }
781 else if ((name_len == 19) && eqi11(name + 8, "disposition"))
782 {
783 if (e)
785 matched = true;
786 }
787 }
788 }
789 break;
790
791 case 'd':
792 if ((name_len != 4) || !eqi4(name, "date"))
793 break;
794
795 mutt_str_replace(&env->date, body);
796 if (e)
797 {
798 struct Tz tz = { 0 };
799 // the caller will check e->date_sent for -1
800 e->date_sent = mutt_date_parse_date(body, &tz);
801 if (e->date_sent > 0)
802 {
803 e->zhours = tz.zhours;
804 e->zminutes = tz.zminutes;
805 e->zoccident = tz.zoccident;
806 }
807 }
808 matched = true;
809 break;
810
811 case 'e':
812 if ((name_len == 7) && eqi6(name + 1, "xpires") && e)
813 {
814 const time_t expired = mutt_date_parse_date(body, NULL);
815 if ((expired != -1) && (expired < mutt_date_now()))
816 {
817 e->expired = true;
818 }
819 }
820 break;
821
822 case 'f':
823 if ((name_len == 4) && eqi4(name, "from"))
824 {
825 mutt_addrlist_parse(&env->from, body);
826 matched = true;
827 }
828 else if ((name_len == 11) && eqi10(name + 1, "ollowup-to"))
829 {
830 if (!env->followup_to)
831 {
834 }
835 matched = true;
836 }
837 break;
838
839 case 'i':
840 if ((name_len != 11) || !eqi10(name + 1, "n-reply-to"))
841 break;
842
844 char *body2 = mutt_str_dup(body); // Create a mutable copy
846 parse_references(&env->in_reply_to, body2);
847 FREE(&body2);
848 matched = true;
849 break;
850
851 case 'l':
852 if ((name_len == 5) && eqi4(name + 1, "ines"))
853 {
854 if (e)
855 {
856 unsigned int ui = 0; // we don't want a negative number of lines
857 mutt_str_atoui(body, &ui);
858 e->lines = ui;
859 }
860
861 matched = true;
862 }
863 else if ((name_len == 9) && eqi8(name + 1, "ist-post"))
864 {
865 /* RFC2369 */
866 if (!mutt_strn_equal(mutt_str_skip_whitespace(body), "NO", 2))
867 {
868 char *mailto = rfc2369_first_mailto(body);
869 if (mailto)
870 {
871 FREE(&env->list_post);
872 env->list_post = mailto;
873 const bool c_auto_subscribe = cs_subset_bool(NeoMutt->sub, "auto_subscribe");
874 if (c_auto_subscribe)
876 }
877 }
878 matched = true;
879 }
880 else if ((name_len == 14) && eqi13(name + 1, "ist-subscribe"))
881 {
882 /* RFC2369 */
883 char *mailto = rfc2369_first_mailto(body);
884 if (mailto)
885 {
886 FREE(&env->list_subscribe);
887 env->list_subscribe = mailto;
888 }
889 matched = true;
890 }
891 else if ((name_len == 16) && eqi15(name + 1, "ist-unsubscribe"))
892 {
893 /* RFC2369 */
894 char *mailto = rfc2369_first_mailto(body);
895 if (mailto)
896 {
897 FREE(&env->list_unsubscribe);
898 env->list_unsubscribe = mailto;
899 }
900 matched = true;
901 }
902 break;
903
904 case 'm':
905 if ((name_len == 12) && eqi11(name + 1, "ime-version"))
906 {
907 if (e)
908 e->mime = true;
909 matched = true;
910 }
911 else if ((name_len == 10) && eqi9(name + 1, "essage-id"))
912 {
913 /* We add a new "Message-ID:" when building a message */
914 FREE(&env->message_id);
915 env->message_id = mutt_extract_message_id(body, NULL);
916 matched = true;
917 }
918 else
919 {
920 if ((name_len >= 13) && eqi4(name + 1, "ail-"))
921 {
922 if ((name_len == 13) && eqi8(name + 5, "reply-to"))
923 {
924 /* override the Reply-To: field */
926 mutt_addrlist_parse(&env->reply_to, body);
927 matched = true;
928 }
929 else if ((name_len == 16) && eqi11(name + 5, "followup-to"))
930 {
932 matched = true;
933 }
934 }
935 }
936 break;
937
938 case 'n':
939 if ((name_len == 10) && eqi9(name + 1, "ewsgroups"))
940 {
941 FREE(&env->newsgroups);
944 matched = true;
945 }
946 break;
947
948 case 'o':
949 /* field 'Organization:' saves only for pager! */
950 if ((name_len == 12) && eqi11(name + 1, "rganization"))
951 {
952 if (!env->organization && !mutt_istr_equal(body, "unknown"))
953 env->organization = mutt_str_dup(body);
954 }
955 break;
956
957 case 'r':
958 if ((name_len == 10) && eqi9(name + 1, "eferences"))
959 {
961 parse_references(&env->references, body);
962 matched = true;
963 }
964 else if ((name_len == 8) && eqi8(name, "reply-to"))
965 {
966 mutt_addrlist_parse(&env->reply_to, body);
967 matched = true;
968 }
969 else if ((name_len == 11) && eqi10(name + 1, "eturn-path"))
970 {
971 mutt_addrlist_parse(&env->return_path, body);
972 matched = true;
973 }
974 else if ((name_len == 8) && eqi8(name, "received"))
975 {
976 if (e && (e->received == 0))
977 {
978 char *d = strrchr(body, ';');
979 if (d)
980 {
981 d = mutt_str_skip_email_wsp(d + 1);
982 // the caller will check e->received for -1
983 e->received = mutt_date_parse_date(d, NULL);
984 }
985 }
986 }
987 break;
988
989 case 's':
990 if ((name_len == 7) && eqi6(name + 1, "ubject"))
991 {
992 if (!env->subject)
993 mutt_env_set_subject(env, body);
994 matched = true;
995 }
996 else if ((name_len == 6) && eqi5(name + 1, "ender"))
997 {
998 mutt_addrlist_parse(&env->sender, body);
999 matched = true;
1000 }
1001 else if ((name_len == 6) && eqi5(name + 1, "tatus"))
1002 {
1003 if (e)
1004 {
1005 while (*body)
1006 {
1007 switch (*body)
1008 {
1009 case 'O':
1010 {
1011 e->old = true;
1012 break;
1013 }
1014 case 'R':
1015 e->read = true;
1016 break;
1017 case 'r':
1018 e->replied = true;
1019 break;
1020 }
1021 body++;
1022 }
1023 }
1024 matched = true;
1025 }
1026 else if (e && (name_len == 10) && eqi1(name + 1, "u") &&
1027 (eqi8(name + 2, "persedes") || eqi8(name + 2, "percedes")))
1028 {
1029 FREE(&env->supersedes);
1030 env->supersedes = mutt_str_dup(body);
1031 }
1032 break;
1033
1034 case 't':
1035 if ((name_len == 2) && eqi1(name + 1, "o"))
1036 {
1037 mutt_addrlist_parse(&env->to, body);
1038 matched = true;
1039 }
1040 break;
1041
1042 case 'x':
1043 if ((name_len == 8) && eqi8(name, "x-status"))
1044 {
1045 if (e)
1046 {
1047 while (*body)
1048 {
1049 switch (*body)
1050 {
1051 case 'A':
1052 e->replied = true;
1053 break;
1054 case 'D':
1055 e->deleted = true;
1056 break;
1057 case 'F':
1058 e->flagged = true;
1059 break;
1060 default:
1061 break;
1062 }
1063 body++;
1064 }
1065 }
1066 matched = true;
1067 }
1068 else if ((name_len == 7) && eqi6(name + 1, "-label"))
1069 {
1070 FREE(&env->x_label);
1071 env->x_label = mutt_str_dup(body);
1072 matched = true;
1073 }
1074 else if ((name_len == 12) && eqi11(name + 1, "-comment-to"))
1075 {
1076 if (!env->x_comment_to)
1077 env->x_comment_to = mutt_str_dup(body);
1078 matched = true;
1079 }
1080 else if ((name_len == 4) && eqi4(name, "xref"))
1081 {
1082 if (!env->xref)
1083 env->xref = mutt_str_dup(body);
1084 matched = true;
1085 }
1086 else if ((name_len == 13) && eqi12(name + 1, "-original-to"))
1087 {
1089 matched = true;
1090 }
1091 break;
1092
1093 default:
1094 break;
1095 }
1096
1097 /* Keep track of the user-defined headers */
1098 if (!matched && user_hdrs)
1099 {
1100 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
1101 char *dup = NULL;
1102 mutt_str_asprintf(&dup, "%s: %s", name, body);
1103
1104 if (!weed || !c_weed || !mutt_matches_ignore(dup))
1105 {
1106 struct ListNode *np = mutt_list_insert_tail(&env->userhdrs, dup);
1107 if (do_2047)
1108 {
1109 rfc2047_decode(&np->data);
1110 }
1111 }
1112 else
1113 {
1114 FREE(&dup);
1115 }
1116 }
1117
1118 return matched;
1119}
1120
1130size_t mutt_rfc822_read_line(FILE *fp, struct Buffer *buf)
1131{
1132 if (!fp || !buf)
1133 return 0;
1134
1135 size_t read = 0;
1136 char line[1024] = { 0 }; /* RFC2822 specifies a maximum line length of 998 */
1137
1138 buf_reset(buf);
1139 while (true)
1140 {
1141 if (!fgets(line, sizeof(line), fp))
1142 {
1143 return 0;
1144 }
1145
1146 const size_t linelen = mutt_str_len(line);
1147 if (linelen == 0)
1148 {
1149 break;
1150 }
1151
1152 if (mutt_str_is_email_wsp(line[0]) && buf_is_empty(buf))
1153 {
1154 read = linelen;
1155 break;
1156 }
1157
1158 read += linelen;
1159
1160 size_t off = linelen - 1;
1161 if (line[off] == '\n')
1162 {
1163 /* We did get a full line: remove trailing space */
1164 do
1165 {
1166 line[off] = '\0';
1167 } while (off && mutt_str_is_email_wsp(line[--off]));
1168
1169 /* check to see if the next line is a continuation line */
1170 int ch = fgetc(fp);
1171 if ((ch != ' ') && (ch != '\t'))
1172 {
1173 /* next line is a separate header field or EOH */
1174 ungetc(ch, fp);
1175 buf_addstr(buf, line);
1176 break;
1177 }
1178 read++;
1179
1180 /* eat tabs and spaces from the beginning of the continuation line */
1181 while (((ch = fgetc(fp)) == ' ') || (ch == '\t'))
1182 {
1183 read++;
1184 }
1185
1186 ungetc(ch, fp);
1187 line[off + 1] = ' '; /* string is still terminated because we removed
1188 at least one whitespace char above */
1189 }
1190
1191 buf_addstr(buf, line);
1192 }
1193
1194 return read;
1195}
1196
1210struct Envelope *mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
1211{
1212 if (!fp)
1213 return NULL;
1214
1215 struct Envelope *env = mutt_env_new();
1216 char *p = NULL;
1217 LOFF_T loc = e ? e->offset : ftello(fp);
1218 if (loc < 0)
1219 {
1220 mutt_debug(LL_DEBUG1, "ftello: %s (errno %d)\n", strerror(errno), errno);
1221 loc = 0;
1222 }
1223
1224 struct Buffer *line = buf_pool_get();
1225
1226 if (e)
1227 {
1228 if (!e->body)
1229 {
1230 e->body = mutt_body_new();
1231
1232 /* set the defaults from RFC1521 */
1233 e->body->type = TYPE_TEXT;
1234 e->body->subtype = mutt_str_dup("plain");
1235 e->body->encoding = ENC_7BIT;
1236 e->body->length = -1;
1237
1238 /* RFC2183 says this is arbitrary */
1240 }
1241 }
1242
1244 ASSERT(md);
1245
1246 while (true)
1247 {
1248 LOFF_T line_start_loc = loc;
1249 size_t len = mutt_rfc822_read_line(fp, line);
1250 if (buf_is_empty(line))
1251 {
1252 break;
1253 }
1254 loc += len;
1255 const char *lines = buf_string(line);
1256 p = strpbrk(lines, ": \t");
1257 if (!p || (*p != ':'))
1258 {
1259 char return_path[1024] = { 0 };
1260 time_t t = 0;
1261
1262 /* some bogus MTAs will quote the original "From " line */
1263 if (mutt_str_startswith(lines, ">From "))
1264 {
1265 continue; /* just ignore */
1266 }
1267 else if (is_from(lines, return_path, sizeof(return_path), &t))
1268 {
1269 /* MH sometimes has the From_ line in the middle of the header! */
1270 if (e && (e->received == 0))
1271 e->received = t - mutt_date_local_tz(t);
1272 continue;
1273 }
1274
1275 /* We need to seek back to the start of the body. Note that we
1276 * keep track of loc ourselves, since calling ftello() incurs
1277 * a syscall, which can be expensive to do for every single line */
1278 (void) mutt_file_seek(fp, line_start_loc, SEEK_SET);
1279 break; /* end of header */
1280 }
1281 size_t name_len = p - lines;
1282
1283 char buf[1024] = { 0 };
1284 if (mutt_replacelist_match(&md->spam, buf, sizeof(buf), lines))
1285 {
1286 if (!mutt_regexlist_match(&md->no_spam, lines))
1287 {
1288 /* if spam tag already exists, figure out how to amend it */
1289 if ((!buf_is_empty(&env->spam)) && (*buf != '\0'))
1290 {
1291 /* If `$spam_separator` defined, append with separator */
1292 const char *const c_spam_separator = cs_subset_string(NeoMutt->sub, "spam_separator");
1293 if (c_spam_separator)
1294 {
1295 buf_addstr(&env->spam, c_spam_separator);
1296 buf_addstr(&env->spam, buf);
1297 }
1298 else /* overwrite */
1299 {
1300 buf_reset(&env->spam);
1301 buf_addstr(&env->spam, buf);
1302 }
1303 }
1304 else if (buf_is_empty(&env->spam) && (*buf != '\0'))
1305 {
1306 /* spam tag is new, and match expr is non-empty; copy */
1307 buf_addstr(&env->spam, buf);
1308 }
1309 else if (buf_is_empty(&env->spam))
1310 {
1311 /* match expr is empty; plug in null string if no existing tag */
1312 buf_addstr(&env->spam, "");
1313 }
1314
1315 if (!buf_is_empty(&env->spam))
1316 mutt_debug(LL_DEBUG5, "spam = %s\n", env->spam.data);
1317 }
1318 }
1319
1320 *p = '\0';
1321 p = mutt_str_skip_email_wsp(p + 1);
1322 if (*p == '\0')
1323 continue; /* skip empty header fields */
1324
1325 mutt_rfc822_parse_line(env, e, lines, name_len, p, user_hdrs, weed, true);
1326 }
1327
1328 buf_pool_release(&line);
1329
1330 if (e)
1331 {
1332 e->body->hdr_offset = e->offset;
1333 e->body->offset = ftello(fp);
1334
1336
1337 if (e->received < 0)
1338 {
1339 mutt_debug(LL_DEBUG1, "resetting invalid received time to 0\n");
1340 e->received = 0;
1341 }
1342
1343 /* check for missing or invalid date */
1344 if (e->date_sent <= 0)
1345 {
1346 mutt_debug(LL_DEBUG1, "no date found, using received time from msg separator\n");
1347 e->date_sent = e->received;
1348 }
1349
1350#ifdef USE_AUTOCRYPT
1351 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
1352 if (c_autocrypt)
1353 {
1355 /* No sense in taking up memory after the header is processed */
1357 }
1358#endif
1359 }
1360
1361 return env;
1362}
1363
1370struct Body *mutt_read_mime_header(FILE *fp, bool digest)
1371{
1372 if (!fp)
1373 return NULL;
1374
1375 struct Body *b = mutt_body_new();
1376 struct Envelope *env = mutt_env_new();
1377 char *c = NULL;
1378 struct Buffer *buf = buf_pool_get();
1379 bool matched = false;
1380
1381 b->hdr_offset = ftello(fp);
1382
1383 b->encoding = ENC_7BIT; /* default from RFC1521 */
1384 b->type = digest ? TYPE_MESSAGE : TYPE_TEXT;
1386
1387 while (mutt_rfc822_read_line(fp, buf) != 0)
1388 {
1389 const char *line = buf_string(buf);
1390 /* Find the value of the current header */
1391 c = strchr(line, ':');
1392 if (c)
1393 {
1394 *c = '\0';
1395 c = mutt_str_skip_email_wsp(c + 1);
1396 if (*c == '\0')
1397 {
1398 mutt_debug(LL_DEBUG1, "skipping empty header field: %s\n", line);
1399 continue;
1400 }
1401 }
1402 else
1403 {
1404 mutt_debug(LL_DEBUG1, "bogus MIME header: %s\n", line);
1405 break;
1406 }
1407
1408 size_t plen = mutt_istr_startswith(line, "content-");
1409 if (plen != 0)
1410 {
1411 if (mutt_istr_equal("type", line + plen))
1412 {
1414 }
1415 else if (mutt_istr_equal("language", line + plen))
1416 {
1418 }
1419 else if (mutt_istr_equal("transfer-encoding", line + plen))
1420 {
1422 }
1423 else if (mutt_istr_equal("disposition", line + plen))
1424 {
1426 }
1427 else if (mutt_istr_equal("description", line + plen))
1428 {
1431 }
1432 else if (mutt_istr_equal("id", line + plen))
1433 {
1434 // strip <angle braces> from Content-ID: header
1435 char *id = c;
1436 int cid_len = mutt_str_len(c);
1437 if (cid_len > 2)
1438 {
1439 if (id[0] == '<')
1440 {
1441 id++;
1442 cid_len--;
1443 }
1444 if (id[cid_len - 1] == '>')
1445 id[cid_len - 1] = '\0';
1446 }
1448 }
1449 }
1450 else if ((plen = mutt_istr_startswith(line, "x-sun-")))
1451 {
1452 if (mutt_istr_equal("data-type", line + plen))
1453 {
1455 }
1456 else if (mutt_istr_equal("encoding-info", line + plen))
1457 {
1459 }
1460 else if (mutt_istr_equal("content-lines", line + plen))
1461 {
1462 mutt_param_set(&b->parameter, "content-lines", c);
1463 }
1464 else if (mutt_istr_equal("data-description", line + plen))
1465 {
1468 }
1469 }
1470 else
1471 {
1472 if (mutt_rfc822_parse_line(env, NULL, line, strlen(line), c, false, false, false))
1473 {
1474 matched = true;
1475 }
1476 }
1477 }
1478 b->offset = ftello(fp); /* Mark the start of the real data */
1479 if ((b->type == TYPE_TEXT) && !b->subtype)
1480 b->subtype = mutt_str_dup("plain");
1481 else if ((b->type == TYPE_MESSAGE) && !b->subtype)
1482 b->subtype = mutt_str_dup("rfc822");
1483
1484 buf_pool_release(&buf);
1485
1486 if (matched)
1487 {
1488 b->mime_headers = env;
1490 }
1491 else
1492 {
1493 mutt_env_free(&env);
1494 }
1495
1496 return b;
1497}
1498
1506bool mutt_is_message_type(int type, const char *subtype)
1507{
1508 if (type != TYPE_MESSAGE)
1509 return false;
1510
1511 subtype = NONULL(subtype);
1512 return (mutt_istr_equal(subtype, "rfc822") ||
1513 mutt_istr_equal(subtype, "news") || mutt_istr_equal(subtype, "global"));
1514}
1515
1522static void parse_part(FILE *fp, struct Body *b, int *counter)
1523{
1524 if (!fp || !b)
1525 return;
1526
1527 const char *bound = NULL;
1528 static unsigned short recurse_level = 0;
1529
1530 if (recurse_level >= MUTT_MIME_MAX_DEPTH)
1531 {
1532 mutt_debug(LL_DEBUG1, "recurse level too deep. giving up\n");
1533 return;
1534 }
1535 recurse_level++;
1536
1537 switch (b->type)
1538 {
1539 case TYPE_MULTIPART:
1540 if (mutt_istr_equal(b->subtype, "x-sun-attachment"))
1541 bound = "--------";
1542 else
1543 bound = mutt_param_get(&b->parameter, "boundary");
1544
1545 if (!mutt_file_seek(fp, b->offset, SEEK_SET))
1546 {
1547 goto bail;
1548 }
1549 b->parts = parse_multipart(fp, bound, b->offset + b->length,
1550 mutt_istr_equal("digest", b->subtype), counter);
1551 break;
1552
1553 case TYPE_MESSAGE:
1554 if (!b->subtype)
1555 break;
1556
1557 if (!mutt_file_seek(fp, b->offset, SEEK_SET))
1558 {
1559 goto bail;
1560 }
1561 if (mutt_is_message_type(b->type, b->subtype))
1562 b->parts = rfc822_parse_message(fp, b, counter);
1563 else if (mutt_istr_equal(b->subtype, "external-body"))
1564 b->parts = mutt_read_mime_header(fp, 0);
1565 else
1566 goto bail;
1567 break;
1568
1569 default:
1570 goto bail;
1571 }
1572
1573 /* try to recover from parsing error */
1574 if (!b->parts)
1575 {
1576 b->type = TYPE_TEXT;
1577 mutt_str_replace(&b->subtype, "plain");
1578 }
1579bail:
1580 recurse_level--;
1581}
1582
1593static struct Body *parse_multipart(FILE *fp, const char *boundary,
1594 LOFF_T end_off, bool digest, int *counter)
1595{
1596 if (!fp)
1597 return NULL;
1598
1599 if (!boundary)
1600 {
1601 mutt_error(_("multipart message has no boundary parameter"));
1602 return NULL;
1603 }
1604
1605 char buf[1024] = { 0 };
1606 struct Body *head = NULL, *last = NULL, *new_body = NULL;
1607 bool final = false; /* did we see the ending boundary? */
1608
1609 const size_t blen = mutt_str_len(boundary);
1610 while ((ftello(fp) < end_off) && fgets(buf, sizeof(buf), fp))
1611 {
1612 const size_t len = mutt_str_len(buf);
1613
1614 const size_t crlf = ((len > 1) && (buf[len - 2] == '\r')) ? 1 : 0;
1615
1616 if ((buf[0] == '-') && (buf[1] == '-') && mutt_str_startswith(buf + 2, boundary))
1617 {
1618 if (last)
1619 {
1620 last->length = ftello(fp) - last->offset - len - 1 - crlf;
1621 if (last->parts && (last->parts->length == 0))
1622 last->parts->length = ftello(fp) - last->parts->offset - len - 1 - crlf;
1623 /* if the body is empty, we can end up with a -1 length */
1624 if (last->length < 0)
1625 last->length = 0;
1626 }
1627
1628 if (len > 0)
1629 {
1630 /* Remove any trailing whitespace, up to the length of the boundary */
1631 for (size_t i = len - 1; mutt_isspace(buf[i]) && (i >= (blen + 2)); i--)
1632 buf[i] = '\0';
1633 }
1634
1635 /* Check for the end boundary */
1636 if (mutt_str_equal(buf + blen + 2, "--"))
1637 {
1638 final = true;
1639 break; /* done parsing */
1640 }
1641 else if (buf[2 + blen] == '\0')
1642 {
1643 new_body = mutt_read_mime_header(fp, digest);
1644 if (!new_body)
1645 break;
1646
1647 if (mutt_param_get(&new_body->parameter, "content-lines"))
1648 {
1649 int lines = 0;
1650 mutt_str_atoi(mutt_param_get(&new_body->parameter, "content-lines"), &lines);
1651 for (; lines > 0; lines--)
1652 if ((ftello(fp) >= end_off) || !fgets(buf, sizeof(buf), fp))
1653 break;
1654 }
1655
1656 /* Consistency checking - catch bad attachment end boundaries */
1657 if (new_body->offset > end_off)
1658 {
1659 mutt_body_free(&new_body);
1660 break;
1661 }
1662 if (head)
1663 {
1664 last->next = new_body;
1665 last = new_body;
1666 }
1667 else
1668 {
1669 last = new_body;
1670 head = new_body;
1671 }
1672
1673 /* It seems more intuitive to add the counter increment to
1674 * parse_part(), but we want to stop the case where a multipart
1675 * contains thousands of tiny parts before the memory and data
1676 * structures are allocated. */
1677 if (++(*counter) >= MUTT_MIME_MAX_PARTS)
1678 break;
1679 }
1680 }
1681 }
1682
1683 /* in case of missing end boundary, set the length to something reasonable */
1684 if (last && (last->length == 0) && !final)
1685 last->length = end_off - last->offset;
1686
1687 /* parse recursive MIME parts */
1688 for (last = head; last; last = last->next)
1689 parse_part(fp, last, counter);
1690
1691 return head;
1692}
1693
1703static struct Body *rfc822_parse_message(FILE *fp, struct Body *parent, int *counter)
1704{
1705 if (!fp || !parent)
1706 return NULL;
1707
1708 parent->email = email_new();
1709 parent->email->offset = ftello(fp);
1710 parent->email->env = mutt_rfc822_read_header(fp, parent->email, false, false);
1711 struct Body *msg = parent->email->body;
1712
1713 /* ignore the length given in the content-length since it could be wrong
1714 * and we already have the info to calculate the correct length */
1715 /* if (msg->length == -1) */
1716 msg->length = parent->length - (msg->offset - parent->offset);
1717
1718 /* if body of this message is empty, we can end up with a negative length */
1719 if (msg->length < 0)
1720 msg->length = 0;
1721
1722 parse_part(fp, msg, counter);
1723 return msg;
1724}
1725
1740static bool mailto_header_allowed(const char *s, struct ListHead *h)
1741{
1742 if (!h)
1743 return false;
1744
1745 struct ListNode *np = NULL;
1746 STAILQ_FOREACH(np, h, entries)
1747 {
1748 if ((*np->data == '*') || mutt_istr_equal(s, np->data))
1749 return true;
1750 }
1751 return false;
1752}
1753
1762bool mutt_parse_mailto(struct Envelope *env, char **body, const char *src)
1763{
1764 if (!env || !src)
1765 return false;
1766
1767 struct Url *url = url_parse(src);
1768 if (!url)
1769 return false;
1770
1771 if (url->host)
1772 {
1773 /* this is not a path-only URL */
1774 url_free(&url);
1775 return false;
1776 }
1777
1778 mutt_addrlist_parse(&env->to, url->path);
1779
1781 ASSERT(md);
1782
1783 struct UrlQuery *np;
1784 STAILQ_FOREACH(np, &url->query_strings, entries)
1785 {
1787 const char *tag = np->name;
1788 char *value = np->value;
1789 /* Determine if this header field is on the allowed list. Since NeoMutt
1790 * interprets some header fields specially (such as
1791 * "Attach: ~/.gnupg/secring.gpg"), care must be taken to ensure that
1792 * only safe fields are allowed.
1793 *
1794 * RFC2368, "4. Unsafe headers"
1795 * The user agent interpreting a mailto URL SHOULD choose not to create
1796 * a message if any of the headers are considered dangerous; it may also
1797 * choose to create a message with only a subset of the headers given in
1798 * the URL. */
1799 if (mailto_header_allowed(tag, &md->mail_to_allow))
1800 {
1801 if (mutt_istr_equal(tag, "body"))
1802 {
1803 if (body)
1804 mutt_str_replace(body, value);
1805 }
1806 else
1807 {
1808 char *scratch = NULL;
1809 size_t taglen = mutt_str_len(tag);
1810
1812 mutt_str_asprintf(&scratch, "%s: %s", tag, value);
1813 scratch[taglen] = 0; /* overwrite the colon as mutt_rfc822_parse_line expects */
1814 value = mutt_str_skip_email_wsp(&scratch[taglen + 1]);
1815 mutt_rfc822_parse_line(env, NULL, scratch, taglen, value, true, false, true);
1816 FREE(&scratch);
1817 }
1818 }
1819 }
1820
1821 /* RFC2047 decode after the RFC822 parsing */
1823
1824 url_free(&url);
1825 return true;
1826}
1827
1833void mutt_parse_part(FILE *fp, struct Body *b)
1834{
1835 int counter = 0;
1836
1837 parse_part(fp, b, &counter);
1838}
1839
1848struct Body *mutt_rfc822_parse_message(FILE *fp, struct Body *b)
1849{
1850 int counter = 0;
1851
1852 return rfc822_parse_message(fp, b, &counter);
1853}
1854
1864struct Body *mutt_parse_multipart(FILE *fp, const char *boundary, LOFF_T end_off, bool digest)
1865{
1866 int counter = 0;
1867
1868 return parse_multipart(fp, boundary, end_off, digest, &counter);
1869}
void mutt_addrlist_clear(struct AddressList *al)
Unlink and free all Address in an AddressList.
Definition address.c:1464
int mutt_addrlist_parse(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition address.c:480
Email Address Handling.
const char * mutt_str_atoul(const char *str, unsigned long *dst)
Convert ASCII string to an unsigned long.
Definition atoi.c:243
const char * mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition atoi.c:217
const char * mutt_str_atoi(const char *str, int *dst)
Convert ASCII string to an integer.
Definition atoi.c:191
Autocrypt end-to-end encryption.
int mutt_autocrypt_process_autocrypt_header(struct Email *e, struct Envelope *env)
Parse an Autocrypt email header.
Definition autocrypt.c:256
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:76
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:291
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition buffer.c:241
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:226
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition buffer.c:571
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition buffer.c:337
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition helpers.c:291
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
Convenience wrapper for the config headers.
const struct Slist * cc_assumed_charset(void)
Get the cached value of $assumed_charset.
Convenience wrapper for the core headers.
bool mutt_isspace(int arg)
Wrapper for isspace(3)
Definition ctype.c:96
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition body.c:58
struct Body * mutt_body_new(void)
Create a new Body.
Definition body.c:44
Representation of the body of an email.
struct Email * email_new(void)
Create a new Email.
Definition email.c:77
Email private Module data.
int mutt_rfc822_parse_line(struct Envelope *env, struct Email *e, const char *name, size_t name_len, const char *body, bool user_hdrs, bool weed, bool do_2047)
Parse an email header.
Definition parse.c:683
struct Body * mutt_rfc822_parse_message(FILE *fp, struct Body *b)
Parse a Message/RFC822 body.
Definition parse.c:1848
void mutt_parse_part(FILE *fp, struct Body *b)
Parse a MIME part.
Definition parse.c:1833
void mutt_parse_content_type(const char *s, struct Body *b)
Parse a content type.
Definition parse.c:468
void mutt_auto_subscribe(const char *mailto)
Check if user is subscribed to mailing list.
Definition parse.c:108
size_t mutt_rfc822_read_line(FILE *fp, struct Buffer *buf)
Read a header line from a file.
Definition parse.c:1130
struct Body * mutt_read_mime_header(FILE *fp, bool digest)
Parse a MIME header.
Definition parse.c:1370
static struct AutocryptHeader * parse_autocrypt(struct AutocryptHeader *head, const char *s)
Parse an Autocrypt header line.
Definition parse.c:577
static void parse_references(struct ListHead *head, const char *s)
Parse references from an email header.
Definition parse.c:325
bool mutt_matches_ignore(const char *s)
Does the string match the ignore list.
Definition parse.c:358
static char * rfc2369_first_mailto(const char *body)
Extract the first mailto: URL from a RFC2369 list.
Definition parse.c:641
enum ContentType mutt_check_mime_type(const char *s)
Check a MIME type string.
Definition parse.c:371
static struct Body * rfc822_parse_message(FILE *fp, struct Body *parent, int *counter)
Parse a Message/RFC822 body.
Definition parse.c:1703
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
Parses an RFC822 header.
Definition parse.c:1210
static void parse_content_language(const char *s, struct Body *b)
Read the content's language.
Definition parse.c:342
static bool mailto_header_allowed(const char *s, struct ListHead *h)
Is the string in the list.
Definition parse.c:1740
bool mutt_parse_mailto(struct Envelope *env, char **body, const char *src)
Parse a mailto:// url.
Definition parse.c:1762
int mutt_check_encoding(const char *c)
Check the encoding type.
Definition parse.c:442
static void parse_content_disposition(const char *s, struct Body *b)
Parse a content disposition.
Definition parse.c:293
struct Body * mutt_parse_multipart(FILE *fp, const char *boundary, LOFF_T end_off, bool digest)
Parse a multipart structure.
Definition parse.c:1864
void mutt_filter_commandline_header_tag(char *header)
Sanitise characters in a header tag.
Definition parse.c:72
char * mutt_extract_message_id(const char *s, size_t *len)
Find a message-id.
Definition parse.c:406
static void parse_part(FILE *fp, struct Body *b, int *counter)
Parse a MIME part.
Definition parse.c:1522
static struct Body * parse_multipart(FILE *fp, const char *boundary, LOFF_T end_off, bool digest, int *counter)
Parse a multipart structure.
Definition parse.c:1593
bool mutt_is_message_type(int type, const char *subtype)
Determine if a mime type matches a message or not.
Definition parse.c:1506
void mutt_filter_commandline_header_value(char *header)
Sanitise characters in a header value.
Definition parse.c:92
static void parse_parameters(struct ParameterList *pl, const char *s, bool allow_value_spaces)
Parse a list of Parameters.
Definition parse.c:154
#define CONTENT_TOO_BIG
Maximum reasonable Content-Length value (1 GiB) to prevent overflow.
Definition parse.c:61
Miscellaneous email parsing routines.
Representation of an email.
void mutt_env_free(struct Envelope **ptr)
Free an Envelope.
Definition envelope.c:125
struct Envelope * mutt_env_new(void)
Create a new Envelope.
Definition envelope.c:45
void mutt_env_set_subject(struct Envelope *env, const char *subj)
Set both subject and real_subj to subj.
Definition envelope.c:68
struct AutocryptHeader * mutt_autocrypthdr_new(void)
Create a new AutocryptHeader.
Definition envelope.c:94
void mutt_autocrypthdr_free(struct AutocryptHeader **ptr)
Free an AutocryptHeader.
Definition envelope.c:103
Representation of an email header (envelope)
static bool eqi17(const char *a, const char b[17])
eqi17 - Compare two 17-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:207
static bool eqi9(const char *a, const char b[9])
eqi9 - Compare two 9-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:159
static bool eqi10(const char *a, const char b[10])
eqi10 - Compare two 10-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:165
static bool eqi8(const char *a, const char b[8])
Compare two 8-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons.
Definition eqi.h:124
static bool eqi11(const char *a, const char b[11])
eqi11 - Compare two 11-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:171
static bool eqi6(const char *a, const char b[6])
eqi6 - Compare two 6-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:151
static bool eqi14(const char *a, const char b[14])
eqi14 - Compare two 14-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:189
static bool eqi13(const char *a, const char b[13])
eqi13 - Compare two 13-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:183
static bool eqi4(const char *a, const char b[4])
Compare two 4-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons.
Definition eqi.h:106
static bool eqi5(const char *a, const char b[5])
eqi5 - Compare two 5-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:145
static bool eqi12(const char *a, const char b[12])
eqi12 - Compare two 12-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:177
static bool eqi15(const char *a, const char b[15])
eqi15 - Compare two 15-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:195
static bool eqi1(const char *a, const char b[1])
Compare two 1-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons.
Definition eqi.h:78
static bool eqi2(const char *a, const char b[2])
Compare two 2-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons.
Definition eqi.h:90
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition file.c:652
bool is_from(const char *s, char *path, size_t pathlen, time_t *tp)
Is a string a 'From' header line?
Definition from.c:49
Determine who the email is from.
#define mutt_error(...)
Definition logging2.h:94
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
struct HashElem * mutt_hash_insert(struct HashTable *table, const char *strkey, void *data)
Add a new element to the Hash Table (with string keys)
Definition hash.c:337
void * mutt_hash_find(const struct HashTable *table, const char *strkey)
Find the HashElem data in a Hash Table element using a key.
Definition hash.c:364
struct HashTable * mutt_hash_new(size_t num_elems, HashFlags flags)
Create a new Hash Table (with string keys)
Definition hash.c:261
#define MUTT_HASH_STRDUP_KEYS
make a copy of the keys
Definition hash.h:113
#define MUTT_HASH_STRCASECMP
use strcasecmp() to compare keys
Definition hash.h:112
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition list.c:65
struct ListNode * mutt_list_insert_head(struct ListHead *h, char *s)
Insert a string at the beginning of a List.
Definition list.c:46
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition list.c:123
bool mutt_list_match(const char *s, struct ListHead *h)
Is the string in the list (see notes)
Definition list.c:194
@ LL_DEBUG5
Log at debug level 5.
Definition logging2.h:49
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:46
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
#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
Constants and macros for managing MIME encoding.
@ ENC_7BIT
7-bit text
Definition mime.h:49
@ ENC_UUENCODED
UUEncoded text.
Definition mime.h:54
@ ENC_OTHER
Encoding unknown.
Definition mime.h:48
@ ENC_BINARY
Binary.
Definition mime.h:53
@ ENC_BASE64
Base-64 encoded text.
Definition mime.h:52
@ ENC_8BIT
8-bit text
Definition mime.h:50
@ ENC_QUOTED_PRINTABLE
Quoted-printable text.
Definition mime.h:51
#define MUTT_MIME_MAX_DEPTH
Maximum nesting depth for MIME parts to prevent stack overflow.
Definition mime.h:69
ContentType
Content-Type.
Definition mime.h:30
@ TYPE_AUDIO
Type: 'audio/*'.
Definition mime.h:32
@ TYPE_IMAGE
Type: 'image/*'.
Definition mime.h:34
@ TYPE_OTHER
Unknown Content-Type.
Definition mime.h:31
@ TYPE_MESSAGE
Type: 'message/*'.
Definition mime.h:35
@ TYPE_MODEL
Type: 'model/*'.
Definition mime.h:36
@ TYPE_MULTIPART
Type: 'multipart/*'.
Definition mime.h:37
@ TYPE_APPLICATION
Type: 'application/*'.
Definition mime.h:33
@ TYPE_TEXT
Type: 'text/*'.
Definition mime.h:38
@ TYPE_ANY
Type: '' or '.'.
Definition mime.h:40
@ TYPE_VIDEO
Type: 'video/*'.
Definition mime.h:39
@ DISP_ATTACH
Content is attached.
Definition mime.h:63
@ DISP_INLINE
Content is inline.
Definition mime.h:62
@ DISP_FORM_DATA
Content is form-data.
Definition mime.h:64
#define MUTT_MIME_MAX_PARTS
Maximum number of MIME parts to process to prevent DoS.
Definition mime.h:71
@ MODULE_ID_EMAIL
ModuleEmail, Email code
Definition module_api.h:64
const char * mutt_ch_get_default_charset(const struct Slist *const assumed_charset)
Get the default character set.
Definition charset.c:451
int mutt_date_local_tz(time_t t)
Calculate the local timezone in seconds east of UTC.
Definition date.c:220
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition date.c:457
time_t mutt_date_parse_date(const char *s, struct Tz *tz_out)
Parse a date string in RFC822 format.
Definition date.c:717
Convenience wrapper for the library headers.
#define _(a)
Definition message.h:28
int mutt_regexlist_add(struct RegexList *rl, const char *str, uint16_t flags, struct Buffer *err)
Compile a regex string and add it to a list.
Definition regex.c:140
bool mutt_replacelist_match(struct ReplaceList *rl, char *buf, size_t buflen, const char *str)
Does a string match a pattern?
Definition regex.c:478
bool mutt_regexlist_match(struct RegexList *rl, const char *str)
Does a string match any Regex in the list?
Definition regex.c:200
bool slist_is_empty(const struct Slist *list)
Is the slist empty?
Definition slist.c:140
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition string.c:384
void mutt_str_remove_trailing_ws(char *s)
Trim trailing whitespace from a string.
Definition string.c:567
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition string.c:674
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition string.c:805
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition string.c:610
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:662
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_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition string.c:234
char * mutt_str_skip_whitespace(const char *p)
Find the first non-whitespace character in a string.
Definition string.c:553
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:500
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition string.c:246
bool mutt_istrn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings ignoring case (to a maximum), safely.
Definition string.c:457
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition string.c:284
Many unsorted constants and some structs.
void * neomutt_get_module_data(struct NeoMutt *n, enum ModuleId id)
Get the private data for a Module.
Definition neomutt.c:585
char * mutt_param_get(const struct ParameterList *pl, const char *s)
Find a matching Parameter.
Definition parameter.c:85
void mutt_param_set(struct ParameterList *pl, const char *attribute, const char *value)
Set a Parameter.
Definition parameter.c:111
void mutt_param_free(struct ParameterList *pl)
Free a ParameterList.
Definition parameter.c:62
struct Parameter * mutt_param_new(void)
Create a new Parameter.
Definition parameter.c:40
Store attributes associated with a MIME part.
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
#define TAILQ_FOREACH(var, head, field)
Definition queue.h:782
#define STAILQ_FOREACH(var, head, field)
Definition queue.h:390
#define TAILQ_FIRST(head)
Definition queue.h:780
#define TAILQ_HEAD_INITIALIZER(head)
Definition queue.h:694
#define TAILQ_EMPTY(head)
Definition queue.h:778
#define TAILQ_INSERT_HEAD(head, elm, field)
Definition queue.h:853
void rfc2047_decode_envelope(struct Envelope *env)
Decode the fields of an Envelope.
Definition rfc2047.c:836
void rfc2047_decode(char **pd)
Decode any RFC2047-encoded header fields.
Definition rfc2047.c:665
RFC2047 MIME extensions encoding / decoding routines.
void rfc2231_decode_parameters(struct ParameterList *pl)
Decode a Parameter list.
Definition rfc2231.c:241
RFC2231 MIME Charset routines.
#define ASSERT(COND)
Definition signal2.h:59
#define NONULL(x)
Definition string2.h:44
static bool mutt_str_is_email_wsp(char c)
Is this a whitespace character (for an email header)
Definition string2.h:111
Parse Autocrypt header info.
Definition envelope.h:44
bool invalid
Header is invalid.
Definition envelope.h:48
struct AutocryptHeader * next
Linked list.
Definition envelope.h:49
char * keydata
PGP Key data.
Definition envelope.h:46
bool prefer_encrypt
User prefers encryption.
Definition envelope.h:47
char * addr
Email address.
Definition envelope.h:45
The body of an email.
Definition body.h:36
char * language
content-language (RFC8255)
Definition body.h:78
char * content_id
Content-Id (RFC2392)
Definition body.h:58
struct Body * parts
parts of a multipart or message/rfc822
Definition body.h:73
LOFF_T offset
offset where the actual data begins
Definition body.h:52
char * xtype
content-type if x-unknown
Definition body.h:62
struct Envelope * mime_headers
Memory hole protected headers.
Definition body.h:76
LOFF_T length
length (in bytes) of attachment
Definition body.h:53
struct ParameterList parameter
Parameters of the content-type.
Definition body.h:63
struct Email * email
header information for message/rfc822
Definition body.h:74
char * description
content-description
Definition body.h:55
unsigned int disposition
content-disposition, ContentDisposition
Definition body.h:42
struct Body * next
next attachment in the list
Definition body.h:72
char * subtype
content-type subtype
Definition body.h:61
unsigned int encoding
content-transfer-encoding, ContentEncoding
Definition body.h:41
long hdr_offset
Offset in stream where the headers begin.
Definition body.h:81
char * form_name
Content-Disposition form-data name param.
Definition body.h:60
unsigned int type
content-type primary type, ContentType
Definition body.h:40
char * filename
When sending a message, this is the file to which this structure refers.
Definition body.h:59
String manipulation buffer.
Definition buffer.h:36
char * data
Pointer to data.
Definition buffer.h:37
Email private Module data.
Definition module_data.h:32
struct RegexList unsubscribed
Regexes to exclude false matches in subscribed.
Definition module_data.h:47
struct RegexList subscribed
Regexes to match subscribed mailing lists.
Definition module_data.h:42
struct ListHead unignore
Header patterns to unignore.
Definition module_data.h:45
struct ListHead ignore
Header patterns to ignore.
Definition module_data.h:37
struct ReplaceList spam
Regexes and patterns to match spam emails.
Definition module_data.h:41
struct RegexList unmail
Regexes to exclude false matches in mail.
Definition module_data.h:46
struct ListHead mail_to_allow
Permitted fields in a mailto: url.
Definition module_data.h:39
struct RegexList no_spam
Regexes to identify non-spam emails.
Definition module_data.h:40
struct HashTable * auto_subscribe_cache
Hash Table: "mailto:" (no value)
Definition module_data.h:34
struct RegexList mail
Regexes to match mailing lists.
Definition module_data.h:38
The envelope/body of an email.
Definition email.h:39
bool read
Email is read.
Definition email.h:50
unsigned int zminutes
Minutes away from UTC.
Definition email.h:57
struct Envelope * env
Envelope information.
Definition email.h:68
bool mime
Has a MIME-Version header?
Definition email.h:48
int lines
How many lines in the body of this message?
Definition email.h:62
struct Body * body
List of MIME parts.
Definition email.h:69
bool old
Email is seen, but unread.
Definition email.h:49
bool zoccident
True, if west of UTC, False if east.
Definition email.h:58
LOFF_T offset
Where in the stream does this message begin?
Definition email.h:71
bool flagged
Marked important?
Definition email.h:47
unsigned int zhours
Hours away from UTC.
Definition email.h:56
time_t date_sent
Time when the message was sent (UTC)
Definition email.h:60
bool replied
Email has been replied to.
Definition email.h:51
bool expired
Already expired?
Definition email.h:46
bool deleted
Email is deleted.
Definition email.h:78
time_t received
Time when the message was placed in the mailbox.
Definition email.h:61
The header of an Email.
Definition envelope.h:57
struct ListHead userhdrs
user defined headers
Definition envelope.h:85
char * supersedes
Supersedes header.
Definition envelope.h:74
char * list_subscribe
This stores a mailto URL, or nothing.
Definition envelope.h:68
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
char * followup_to
List of 'followup-to' fields.
Definition envelope.h:80
struct AddressList reply_to
Email's 'reply-to'.
Definition envelope.h:64
char * message_id
Message ID.
Definition envelope.h:73
char * x_comment_to
List of 'X-comment-to' fields.
Definition envelope.h:81
struct AddressList x_original_to
Email's 'X-Original-to'.
Definition envelope.h:66
struct AutocryptHeader * autocrypt_gossip
Autocrypt Gossip header.
Definition envelope.h:88
char * newsgroups
List of newsgroups.
Definition envelope.h:78
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 ListHead references
message references (in reverse order)
Definition envelope.h:83
struct AutocryptHeader * autocrypt
Autocrypt header.
Definition envelope.h:87
struct Buffer spam
Spam header.
Definition envelope.h:82
struct ListHead in_reply_to
in-reply-to header content
Definition envelope.h:84
struct AddressList bcc
Email's 'Bcc' list.
Definition envelope.h:62
char * xref
List of cross-references.
Definition envelope.h:79
char * organization
Organisation header.
Definition envelope.h:77
char * x_label
X-Label.
Definition envelope.h:76
char * list_post
This stores a mailto URL, or nothing.
Definition envelope.h:67
char * date
Sent date.
Definition envelope.h:75
char * list_unsubscribe
This stores a mailto URL, or nothing.
Definition envelope.h:69
struct AddressList from
Email's 'From' list.
Definition envelope.h:59
A List node for strings.
Definition list.h:37
char * data
String.
Definition list.h:38
Container for Accounts, Notifications.
Definition neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
Attribute associated with a MIME part.
Definition parameter.h:33
char * attribute
Parameter name.
Definition parameter.h:34
char * value
Parameter value.
Definition parameter.h:35
List of recognised Timezones.
Definition date.h:53
unsigned char zminutes
Minutes away from UTC.
Definition date.h:56
bool zoccident
True if west of UTC, False if East.
Definition date.h:57
unsigned char zhours
Hours away from UTC.
Definition date.h:55
Parsed Query String.
Definition url.h:58
char * name
Query name.
Definition url.h:59
char * value
Query value.
Definition url.h:60
A parsed URL proto://user:password@host:port/path?a=1&b=2
Definition url.h:69
struct UrlQueryList query_strings
List of query strings.
Definition url.h:76
char * host
Host.
Definition url.h:73
char * src
Raw URL string.
Definition url.h:77
char * path
Path.
Definition url.h:75
struct Url * url_parse(const char *src)
Fill in Url.
Definition url.c:239
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition url.c:124
enum UrlScheme url_check_scheme(const char *str)
Check the protocol of a URL.
Definition url.c:226
Parse and identify different URL schemes.
@ U_MAILTO
Url is mailto://.
Definition url.h:45