NeoMutt  2023-03-22
Teaching an old dog new tricks
DOXYGEN
parse.c
Go to the documentation of this file.
1
30#include "config.h"
31#include <ctype.h>
32#include <string.h>
33#include <time.h>
34#include "mutt/lib.h"
35#include "address/lib.h"
36#include "config/lib.h"
37#include "core/lib.h"
38#include "mutt.h"
39#include "parse.h"
40#include "body.h"
41#include "email.h"
42#include "envelope.h"
43#include "from.h"
44#include "globals.h"
45#include "mime.h"
46#include "parameter.h"
47#include "rfc2047.h"
48#include "rfc2231.h"
49#include "url.h"
50#ifdef USE_AUTOCRYPT
51#include "autocrypt/lib.h"
52#endif
53
54/* If the 'Content-Length' is bigger than 1GiB, then it's clearly wrong.
55 * Cap the value to prevent overflow of Body.length */
56#define CONTENT_TOO_BIG (1 << 30)
57
58static void parse_part(FILE *fp, struct Body *b, int *counter);
59static struct Body *rfc822_parse_message(FILE *fp, struct Body *parent, int *counter);
60static struct Body *parse_multipart(FILE *fp, const char *boundary,
61 LOFF_T end_off, bool digest, int *counter);
62
67void mutt_auto_subscribe(const char *mailto)
68{
69 if (!mailto)
70 return;
71
74
76 return;
77
79
80 struct Envelope *lpenv = mutt_env_new(); /* parsed envelope from the List-Post mailto: URL */
81
82 if (mutt_parse_mailto(lpenv, NULL, mailto) && !TAILQ_EMPTY(&lpenv->to))
83 {
84 const char *mailbox = TAILQ_FIRST(&lpenv->to)->mailbox;
85 if (mailbox && !mutt_regexlist_match(&SubscribedLists, mailbox) &&
88 {
89 /* mutt_regexlist_add() detects duplicates, so it is safe to
90 * try to add here without any checks. */
91 mutt_regexlist_add(&MailLists, mailbox, REG_ICASE, NULL);
92 mutt_regexlist_add(&SubscribedLists, mailbox, REG_ICASE, NULL);
93 }
94 }
95
96 mutt_env_free(&lpenv);
97}
98
110static void parse_parameters(struct ParameterList *pl, const char *s, bool allow_value_spaces)
111{
112 struct Parameter *pnew = NULL;
113 const char *p = NULL;
114 size_t i;
115
116 struct Buffer *buf = mutt_buffer_pool_get();
117 /* allow_value_spaces, especially with autocrypt keydata, can result
118 * in quite large parameter values. avoid frequent reallocs by
119 * pre-sizing */
120 if (allow_value_spaces)
122
123 mutt_debug(LL_DEBUG2, "'%s'\n", s);
124
125 while (*s)
126 {
128
129 p = strpbrk(s, "=;");
130 if (!p)
131 {
132 mutt_debug(LL_DEBUG1, "malformed parameter: %s\n", s);
133 goto bail;
134 }
135
136 /* if we hit a ; now the parameter has no value, just skip it */
137 if (*p != ';')
138 {
139 i = p - s;
140 /* remove whitespace from the end of the attribute name */
141 while ((i > 0) && mutt_str_is_email_wsp(s[i - 1]))
142 i--;
143
144 /* the check for the missing parameter token is here so that we can skip
145 * over any quoted value that may be present. */
146 if (i == 0)
147 {
148 mutt_debug(LL_DEBUG1, "missing attribute: %s\n", s);
149 pnew = NULL;
150 }
151 else
152 {
153 pnew = mutt_param_new();
154 pnew->attribute = mutt_strn_dup(s, i);
155 }
156
157 do
158 {
159 s = mutt_str_skip_email_wsp(p + 1); /* skip over the =, or space if we loop */
160
161 if (*s == '"')
162 {
163 bool state_ascii = true;
164 s++;
165 for (; *s; s++)
166 {
167 const struct Slist *const c_assumed_charset =
168 cs_subset_slist(NeoMutt->sub, "assumed_charset");
169 if (c_assumed_charset)
170 {
171 // As iso-2022-* has a character of '"' with non-ascii state, ignore it
172 if (*s == 0x1b)
173 {
174 if ((s[1] == '(') && ((s[2] == 'B') || (s[2] == 'J')))
175 state_ascii = true;
176 else
177 state_ascii = false;
178 }
179 }
180 if (state_ascii && (*s == '"'))
181 break;
182 if (*s == '\\')
183 {
184 if (s[1])
185 {
186 s++;
187 /* Quote the next character */
188 mutt_buffer_addch(buf, *s);
189 }
190 }
191 else
192 mutt_buffer_addch(buf, *s);
193 }
194 if (*s)
195 s++; /* skip over the " */
196 }
197 else
198 {
199 for (; *s && *s != ' ' && *s != ';'; s++)
200 mutt_buffer_addch(buf, *s);
201 }
202
203 p = s;
204 } while (allow_value_spaces && (*s == ' '));
205
206 /* if the attribute token was missing, 'new' will be NULL */
207 if (pnew)
208 {
209 pnew->value = mutt_buffer_strdup(buf);
210
211 mutt_debug(LL_DEBUG2, "parse_parameter: '%s' = '%s'\n",
212 pnew->attribute ? pnew->attribute : "", pnew->value ? pnew->value : "");
213
214 /* Add this parameter to the list */
215 TAILQ_INSERT_HEAD(pl, pnew, entries);
216 }
217 }
218 else
219 {
220 mutt_debug(LL_DEBUG1, "parameter with no value: %s\n", s);
221 s = p;
222 }
223
224 /* Find the next parameter */
225 if ((*s != ';') && !(s = strchr(s, ';')))
226 break; /* no more parameters */
227
228 do
229 {
230 /* Move past any leading whitespace. the +1 skips over the semicolon */
231 s = mutt_str_skip_email_wsp(s + 1);
232 } while (*s == ';'); /* skip empty parameters */
233 }
234
235bail:
236
239}
240
248static void parse_content_disposition(const char *s, struct Body *ct)
249{
250 struct ParameterList pl = TAILQ_HEAD_INITIALIZER(pl);
251
252 if (mutt_istr_startswith(s, "inline"))
254 else if (mutt_istr_startswith(s, "form-data"))
256 else
258
259 /* Check to see if a default filename was given */
260 s = strchr(s, ';');
261 if (s)
262 {
263 s = mutt_str_skip_email_wsp(s + 1);
264 parse_parameters(&pl, s, false);
265 s = mutt_param_get(&pl, "filename");
266 if (s)
267 mutt_str_replace(&ct->filename, s);
268 s = mutt_param_get(&pl, "name");
269 if (s)
271 mutt_param_free(&pl);
272 }
273}
274
280static void parse_references(struct ListHead *head, const char *s)
281{
282 if (!head)
283 return;
284
285 char *m = NULL;
286 for (size_t off = 0; (m = mutt_extract_message_id(s, &off)); s += off)
287 {
288 mutt_list_insert_head(head, m);
289 }
290}
291
297static void parse_content_language(const char *s, struct Body *ct)
298{
299 if (!s || !ct)
300 return;
301
302 mutt_debug(LL_DEBUG2, "RFC8255 >> Content-Language set to %s\n", s);
303 mutt_str_replace(&ct->language, s);
304}
305
313bool mutt_matches_ignore(const char *s)
314{
315 return mutt_list_match(s, &Ignore) && !mutt_list_match(s, &UnIgnore);
316}
317
324{
325 if (mutt_istr_equal("text", s))
326 return TYPE_TEXT;
327 if (mutt_istr_equal("multipart", s))
328 return TYPE_MULTIPART;
329#ifdef SUN_ATTACHMENT
330 if (mutt_istr_equal("x-sun-attachment", s))
331 return TYPE_MULTIPART;
332#endif
333 if (mutt_istr_equal("application", s))
334 return TYPE_APPLICATION;
335 if (mutt_istr_equal("message", s))
336 return TYPE_MESSAGE;
337 if (mutt_istr_equal("image", s))
338 return TYPE_IMAGE;
339 if (mutt_istr_equal("audio", s))
340 return TYPE_AUDIO;
341 if (mutt_istr_equal("video", s))
342 return TYPE_VIDEO;
343 if (mutt_istr_equal("model", s))
344 return TYPE_MODEL;
345 if (mutt_istr_equal("*", s))
346 return TYPE_ANY;
347 if (mutt_istr_equal(".*", s))
348 return TYPE_ANY;
349
350 return TYPE_OTHER;
351}
352
360char *mutt_extract_message_id(const char *s, size_t *len)
361{
362 if (!s || (*s == '\0'))
363 return NULL;
364
365 char *decoded = mutt_str_dup(s);
366 rfc2047_decode(&decoded);
367
368 char *res = NULL;
369
370 for (const char *p = decoded, *beg = NULL; *p; p++)
371 {
372 if (*p == '<')
373 {
374 beg = p;
375 continue;
376 }
377
378 if (beg && (*p == '>'))
379 {
380 if (len)
381 *len = p - decoded + 1;
382 res = mutt_strn_dup(beg, (p + 1) - beg);
383 break;
384 }
385 }
386
387 FREE(&decoded);
388 return res;
389}
390
396int mutt_check_encoding(const char *c)
397{
398 if (mutt_istr_startswith(c, "7bit"))
399 return ENC_7BIT;
400 if (mutt_istr_startswith(c, "8bit"))
401 return ENC_8BIT;
402 if (mutt_istr_startswith(c, "binary"))
403 return ENC_BINARY;
404 if (mutt_istr_startswith(c, "quoted-printable"))
406 if (mutt_istr_startswith(c, "base64"))
407 return ENC_BASE64;
408 if (mutt_istr_startswith(c, "x-uuencode"))
409 return ENC_UUENCODED;
410#ifdef SUN_ATTACHMENT
411 if (mutt_istr_startswith(c, "uuencode"))
412 return ENC_UUENCODED;
413#endif
414 return ENC_OTHER;
415}
416
424void mutt_parse_content_type(const char *s, struct Body *ct)
425{
426 if (!s || !ct)
427 return;
428
429 FREE(&ct->subtype);
431
432 /* First extract any existing parameters */
433 char *pc = strchr(s, ';');
434 if (pc)
435 {
436 *pc++ = 0;
437 while (*pc && IS_SPACE(*pc))
438 pc++;
439 parse_parameters(&ct->parameter, pc, false);
440
441 /* Some pre-RFC1521 gateways still use the "name=filename" convention,
442 * but if a filename has already been set in the content-disposition,
443 * let that take precedence, and don't set it here */
444 pc = mutt_param_get(&ct->parameter, "name");
445 if (pc && !ct->filename)
446 ct->filename = mutt_str_dup(pc);
447
448#ifdef SUN_ATTACHMENT
449 /* this is deep and utter perversion */
450 pc = mutt_param_get(&ct->parameter, "conversions");
451 if (pc)
453#endif
454 }
455
456 /* Now get the subtype */
457 char *subtype = strchr(s, '/');
458 if (subtype)
459 {
460 *subtype++ = '\0';
461 for (pc = subtype; *pc && !IS_SPACE(*pc) && (*pc != ';'); pc++)
462 ; // do nothing
463
464 *pc = '\0';
465 mutt_str_replace(&ct->subtype, subtype);
466 }
467
468 /* Finally, get the major type */
469 ct->type = mutt_check_mime_type(s);
470
471#ifdef SUN_ATTACHMENT
472 if (mutt_istr_equal("x-sun-attachment", s))
473 mutt_str_replace(&ct->subtype, "x-sun-attachment");
474#endif
475
476 if (ct->type == TYPE_OTHER)
477 {
478 mutt_str_replace(&ct->xtype, s);
479 }
480
481 if (!ct->subtype)
482 {
483 /* Some older non-MIME mailers (i.e., mailtool, elm) have a content-type
484 * field, so we can attempt to convert the type to Body here. */
485 if (ct->type == TYPE_TEXT)
486 ct->subtype = mutt_str_dup("plain");
487 else if (ct->type == TYPE_AUDIO)
488 ct->subtype = mutt_str_dup("basic");
489 else if (ct->type == TYPE_MESSAGE)
490 ct->subtype = mutt_str_dup("rfc822");
491 else if (ct->type == TYPE_OTHER)
492 {
493 char buf[128] = { 0 };
494
496 snprintf(buf, sizeof(buf), "x-%s", s);
497 ct->subtype = mutt_str_dup(buf);
498 }
499 else
500 ct->subtype = mutt_str_dup("x-unknown");
501 }
502
503 /* Default character set for text types. */
504 if (ct->type == TYPE_TEXT)
505 {
506 pc = mutt_param_get(&ct->parameter, "charset");
507 if (pc)
508 {
509 /* Microsoft Outlook seems to think it is necessary to repeat
510 * charset=, strip it off not to confuse ourselves */
511 if (mutt_istrn_equal(pc, "charset=", sizeof("charset=") - 1))
512 mutt_param_set(&ct->parameter, "charset", pc + (sizeof("charset=") - 1));
513 }
514 else
515 {
516 mutt_param_set(&ct->parameter, "charset",
517 (const char *) mutt_ch_get_default_charset());
518 }
519 }
520}
521
522#ifdef USE_AUTOCRYPT
529static struct AutocryptHeader *parse_autocrypt(struct AutocryptHeader *head, const char *s)
530{
531 struct AutocryptHeader *autocrypt = mutt_autocrypthdr_new();
532 autocrypt->next = head;
533
534 struct ParameterList pl = TAILQ_HEAD_INITIALIZER(pl);
535 parse_parameters(&pl, s, true);
536 if (TAILQ_EMPTY(&pl))
537 {
538 autocrypt->invalid = true;
539 goto cleanup;
540 }
541
542 struct Parameter *p = NULL;
543 TAILQ_FOREACH(p, &pl, entries)
544 {
545 if (mutt_istr_equal(p->attribute, "addr"))
546 {
547 if (autocrypt->addr)
548 {
549 autocrypt->invalid = true;
550 goto cleanup;
551 }
552 autocrypt->addr = p->value;
553 p->value = NULL;
554 }
555 else if (mutt_istr_equal(p->attribute, "prefer-encrypt"))
556 {
557 if (mutt_istr_equal(p->value, "mutual"))
558 autocrypt->prefer_encrypt = true;
559 }
560 else if (mutt_istr_equal(p->attribute, "keydata"))
561 {
562 if (autocrypt->keydata)
563 {
564 autocrypt->invalid = true;
565 goto cleanup;
566 }
567 autocrypt->keydata = p->value;
568 p->value = NULL;
569 }
570 else if (p->attribute && (p->attribute[0] != '_'))
571 {
572 autocrypt->invalid = true;
573 goto cleanup;
574 }
575 }
576
577 /* Checking the addr against From, and for multiple valid headers
578 * occurs later, after all the headers are parsed. */
579 if (!autocrypt->addr || !autocrypt->keydata)
580 autocrypt->invalid = true;
581
582cleanup:
583 mutt_param_free(&pl);
584 return autocrypt;
585}
586#endif
587
593static char *rfc2369_first_mailto(const char *body)
594{
595 for (const char *beg = body, *end = NULL; beg; beg = strchr(end, ','))
596 {
597 beg = strchr(beg, '<');
598 if (!beg)
599 {
600 break;
601 }
602 beg++;
603 end = strchr(beg, '>');
604 if (!end)
605 {
606 break;
607 }
608
609 char *mlist = mutt_strn_dup(beg, end - beg);
610 if (url_check_scheme(mlist) == U_MAILTO)
611 {
612 return mlist;
613 }
614 FREE(&mlist);
615 }
616 return NULL;
617}
618
634int mutt_rfc822_parse_line(struct Envelope *env, struct Email *e, const char *name,
635 const char *body, bool user_hdrs, bool weed, bool do_2047)
636{
637 if (!env || !name)
638 return 0;
639
640 bool matched = false;
641
642 switch (tolower(name[0]))
643 {
644 case 'a':
645 if (mutt_istr_equal(name + 1, "pparently-to"))
646 {
647 mutt_addrlist_parse(&env->to, body);
648 matched = true;
649 }
650 else if (mutt_istr_equal(name + 1, "pparently-from"))
651 {
652 mutt_addrlist_parse(&env->from, body);
653 matched = true;
654 }
655#ifdef USE_AUTOCRYPT
656 else if (mutt_istr_equal(name + 1, "utocrypt"))
657 {
658 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
659 if (c_autocrypt)
660 {
661 env->autocrypt = parse_autocrypt(env->autocrypt, body);
662 matched = true;
663 }
664 }
665 else if (mutt_istr_equal(name + 1, "utocrypt-gossip"))
666 {
667 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
668 if (c_autocrypt)
669 {
671 matched = true;
672 }
673 }
674#endif
675 break;
676
677 case 'b':
678 if (mutt_istr_equal(name + 1, "cc"))
679 {
680 mutt_addrlist_parse(&env->bcc, body);
681 matched = true;
682 }
683 break;
684
685 case 'c':
686 if (mutt_istr_equal(name + 1, "c"))
687 {
688 mutt_addrlist_parse(&env->cc, body);
689 matched = true;
690 }
691 else
692 {
693 size_t plen = mutt_istr_startswith(name + 1, "ontent-");
694 if (plen != 0)
695 {
696 if (mutt_istr_equal(name + 1 + plen, "type"))
697 {
698 if (e)
700 matched = true;
701 }
702 else if (mutt_istr_equal(name + 1 + plen, "language"))
703 {
704 if (e)
706 matched = true;
707 }
708 else if (mutt_istr_equal(name + 1 + plen, "transfer-encoding"))
709 {
710 if (e)
712 matched = true;
713 }
714 else if (mutt_istr_equal(name + 1 + plen, "length"))
715 {
716 if (e)
717 {
718 unsigned long len = 0;
719 e->body->length = mutt_str_atoul(body, &len) ? MIN(len, CONTENT_TOO_BIG) : -1;
720 }
721 matched = true;
722 }
723 else if (mutt_istr_equal(name + 1 + plen, "description"))
724 {
725 if (e)
726 {
729 }
730 matched = true;
731 }
732 else if (mutt_istr_equal(name + 1 + plen, "disposition"))
733 {
734 if (e)
736 matched = true;
737 }
738 }
739 }
740 break;
741
742 case 'd':
743 if (!mutt_istr_equal("ate", name + 1))
744 break;
745
746 mutt_str_replace(&env->date, body);
747 if (e)
748 {
749 struct Tz tz;
750 e->date_sent = mutt_date_parse_date(body, &tz);
751 if (e->date_sent > 0)
752 {
753 e->zhours = tz.zhours;
754 e->zminutes = tz.zminutes;
755 e->zoccident = tz.zoccident;
756 }
757 }
758 matched = true;
759 break;
760
761 case 'e':
762 if (mutt_istr_equal("xpires", name + 1) && e &&
763 (mutt_date_parse_date(body, NULL) < mutt_date_now()))
764 {
765 e->expired = true;
766 }
767 break;
768
769 case 'f':
770 if (mutt_istr_equal("rom", name + 1))
771 {
772 mutt_addrlist_parse(&env->from, body);
773 matched = true;
774 }
775#ifdef USE_NNTP
776 else if (mutt_istr_equal(name + 1, "ollowup-to"))
777 {
778 if (!env->followup_to)
779 {
782 }
783 matched = true;
784 }
785#endif
786 break;
787
788 case 'i':
789 if (!mutt_istr_equal(name + 1, "n-reply-to"))
790 break;
791
793 parse_references(&env->in_reply_to, body);
794 matched = true;
795 break;
796
797 case 'l':
798 if (mutt_istr_equal(name + 1, "ines"))
799 {
800 if (e)
801 {
802 unsigned int ui = 0; // we don't want a negative number of lines
803 mutt_str_atoui(body, &ui);
804 e->lines = ui;
805 }
806
807 matched = true;
808 }
809 else if (mutt_istr_equal(name + 1, "ist-Post"))
810 {
811 /* RFC2369 */
812 if (!mutt_strn_equal(mutt_str_skip_whitespace(body), "NO", 2))
813 {
814 char *mailto = rfc2369_first_mailto(body);
815 if (mailto)
816 {
817 FREE(&env->list_post);
818 env->list_post = mailto;
819 const bool c_auto_subscribe = cs_subset_bool(NeoMutt->sub, "auto_subscribe");
820 if (c_auto_subscribe)
822 }
823 }
824 matched = true;
825 }
826 else if (mutt_istr_equal(name + 1, "ist-Subscribe"))
827 {
828 /* RFC2369 */
829 char *mailto = rfc2369_first_mailto(body);
830 if (mailto)
831 {
832 FREE(&env->list_subscribe);
833 env->list_subscribe = mailto;
834 }
835 matched = true;
836 }
837 else if (mutt_istr_equal(name + 1, "ist-Unsubscribe"))
838 {
839 /* RFC2369 */
840 char *mailto = rfc2369_first_mailto(body);
841 if (mailto)
842 {
843 FREE(&env->list_unsubscribe);
844 env->list_unsubscribe = mailto;
845 }
846 matched = true;
847 }
848 break;
849
850 case 'm':
851 if (mutt_istr_equal(name + 1, "ime-version"))
852 {
853 if (e)
854 e->mime = true;
855 matched = true;
856 }
857 else if (mutt_istr_equal(name + 1, "essage-id"))
858 {
859 /* We add a new "Message-ID:" when building a message */
860 FREE(&env->message_id);
861 env->message_id = mutt_extract_message_id(body, NULL);
862 matched = true;
863 }
864 else
865 {
866 size_t plen = mutt_istr_startswith(name + 1, "ail-");
867 if (plen != 0)
868 {
869 if (mutt_istr_equal(name + 1 + plen, "reply-to"))
870 {
871 /* override the Reply-To: field */
873 mutt_addrlist_parse(&env->reply_to, body);
874 matched = true;
875 }
876 else if (mutt_istr_equal(name + 1 + plen, "followup-to"))
877 {
879 matched = true;
880 }
881 }
882 }
883 break;
884
885#ifdef USE_NNTP
886 case 'n':
887 if (mutt_istr_equal(name + 1, "ewsgroups"))
888 {
889 FREE(&env->newsgroups);
892 matched = true;
893 }
894 break;
895#endif
896
897 case 'o':
898 /* field 'Organization:' saves only for pager! */
899 if (mutt_istr_equal(name + 1, "rganization"))
900 {
901 if (!env->organization && !mutt_istr_equal(body, "unknown"))
902 env->organization = mutt_str_dup(body);
903 }
904 break;
905
906 case 'r':
907 if (mutt_istr_equal(name + 1, "eferences"))
908 {
910 parse_references(&env->references, body);
911 matched = true;
912 }
913 else if (mutt_istr_equal(name + 1, "eply-to"))
914 {
915 mutt_addrlist_parse(&env->reply_to, body);
916 matched = true;
917 }
918 else if (mutt_istr_equal(name + 1, "eturn-path"))
919 {
920 mutt_addrlist_parse(&env->return_path, body);
921 matched = true;
922 }
923 else if (mutt_istr_equal(name + 1, "eceived"))
924 {
925 if (e && (e->received == 0))
926 {
927 char *d = strrchr(body, ';');
928 if (d)
929 {
930 d = mutt_str_skip_email_wsp(d + 1);
931 e->received = mutt_date_parse_date(d, NULL);
932 }
933 }
934 }
935 break;
936
937 case 's':
938 if (mutt_istr_equal(name + 1, "ubject"))
939 {
940 if (!env->subject)
941 env->subject = mutt_str_dup(body);
942 matched = true;
943 }
944 else if (mutt_istr_equal(name + 1, "ender"))
945 {
946 mutt_addrlist_parse(&env->sender, body);
947 matched = true;
948 }
949 else if (mutt_istr_equal(name + 1, "tatus"))
950 {
951 if (e)
952 {
953 while (*body)
954 {
955 switch (*body)
956 {
957 case 'O':
958 {
959 e->old = true;
960 break;
961 }
962 case 'R':
963 e->read = true;
964 break;
965 case 'r':
966 e->replied = true;
967 break;
968 }
969 body++;
970 }
971 }
972 matched = true;
973 }
974 else if (e && (mutt_istr_equal("upersedes", name + 1) ||
975 mutt_istr_equal("upercedes", name + 1)))
976 {
977 FREE(&env->supersedes);
978 env->supersedes = mutt_str_dup(body);
979 }
980 break;
981
982 case 't':
983 if (mutt_istr_equal(name + 1, "o"))
984 {
985 mutt_addrlist_parse(&env->to, body);
986 matched = true;
987 }
988 break;
989
990 case 'x':
991 if (mutt_istr_equal(name + 1, "-status"))
992 {
993 if (e)
994 {
995 while (*body)
996 {
997 switch (*body)
998 {
999 case 'A':
1000 e->replied = true;
1001 break;
1002 case 'D':
1003 e->deleted = true;
1004 break;
1005 case 'F':
1006 e->flagged = true;
1007 break;
1008 default:
1009 break;
1010 }
1011 body++;
1012 }
1013 }
1014 matched = true;
1015 }
1016 else if (mutt_istr_equal(name + 1, "-label"))
1017 {
1018 FREE(&env->x_label);
1019 env->x_label = mutt_str_dup(body);
1020 matched = true;
1021 }
1022#ifdef USE_NNTP
1023 else if (mutt_istr_equal(name + 1, "-comment-to"))
1024 {
1025 if (!env->x_comment_to)
1026 env->x_comment_to = mutt_str_dup(body);
1027 matched = true;
1028 }
1029 else if (mutt_istr_equal(name + 1, "ref"))
1030 {
1031 if (!env->xref)
1032 env->xref = mutt_str_dup(body);
1033 matched = true;
1034 }
1035#endif
1036 else if (mutt_istr_equal(name + 1, "-original-to"))
1037 {
1039 matched = true;
1040 }
1041 break;
1042
1043 default:
1044 break;
1045 }
1046
1047 /* Keep track of the user-defined headers */
1048 if (!matched && user_hdrs)
1049 {
1050 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
1051 char *dup = NULL;
1052 mutt_str_asprintf(&dup, "%s: %s", name, body);
1053
1054 if (!weed || !c_weed || !mutt_matches_ignore(dup))
1055 {
1056 struct ListNode *np = mutt_list_insert_tail(&env->userhdrs, dup);
1057 if (do_2047)
1058 {
1059 rfc2047_decode(&np->data);
1060 }
1061 }
1062 else
1063 {
1064 FREE(&dup);
1065 }
1066 }
1067
1068 return matched;
1069}
1070
1082char *mutt_rfc822_read_line(FILE *fp, char *line, size_t *linelen)
1083{
1084 if (!fp || !line || !linelen)
1085 return NULL;
1086
1087 char *buf = line;
1088 int ch;
1089 size_t offset = 0;
1090
1091 while (true)
1092 {
1093 if (!fgets(buf, *linelen - offset, fp) || /* end of file or */
1094 (IS_SPACE(*line) && !offset)) /* end of headers */
1095 {
1096 *line = '\0';
1097 return line;
1098 }
1099
1100 const size_t len = mutt_str_len(buf);
1101 if (len == 0)
1102 return line;
1103
1104 buf += len - 1;
1105 if (*buf == '\n')
1106 {
1107 /* we did get a full line. remove trailing space */
1108 while (IS_SPACE(*buf))
1109 {
1110 *buf-- = '\0'; /* we can't come beyond line's beginning because
1111 * it begins with a non-space */
1112 }
1113
1114 /* check to see if the next line is a continuation line */
1115 ch = fgetc(fp);
1116 if ((ch != ' ') && (ch != '\t'))
1117 {
1118 ungetc(ch, fp);
1119 return line; /* next line is a separate header field or EOH */
1120 }
1121
1122 /* eat tabs and spaces from the beginning of the continuation line */
1123 while (((ch = fgetc(fp)) == ' ') || (ch == '\t'))
1124 ; // do nothing
1125
1126 ungetc(ch, fp);
1127 *++buf = ' '; /* string is still terminated because we removed
1128 at least one whitespace char above */
1129 }
1130
1131 buf++;
1132 offset = buf - line;
1133 if (*linelen < (offset + 256))
1134 {
1135 /* grow the buffer */
1136 *linelen += 256;
1137 mutt_mem_realloc(&line, *linelen);
1138 buf = line + offset;
1139 }
1140 }
1141 /* not reached */
1142}
1143
1157struct Envelope *mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
1158{
1159 if (!fp)
1160 return NULL;
1161
1162 struct Envelope *env = mutt_env_new();
1163 char *p = NULL;
1164 LOFF_T loc;
1165 size_t linelen = 1024;
1166 char *line = mutt_mem_malloc(linelen);
1167 char buf[linelen + 1];
1168
1169 if (e)
1170 {
1171 if (!e->body)
1172 {
1173 e->body = mutt_body_new();
1174
1175 /* set the defaults from RFC1521 */
1176 e->body->type = TYPE_TEXT;
1177 e->body->subtype = mutt_str_dup("plain");
1178 e->body->encoding = ENC_7BIT;
1179 e->body->length = -1;
1180
1181 /* RFC2183 says this is arbitrary */
1183 }
1184 }
1185
1186 while ((loc = ftello(fp)) != -1)
1187 {
1188 line = mutt_rfc822_read_line(fp, line, &linelen);
1189 if (*line == '\0')
1190 break;
1191 p = strpbrk(line, ": \t");
1192 if (!p || (*p != ':'))
1193 {
1194 char return_path[1024] = { 0 };
1195 time_t t = 0;
1196
1197 /* some bogus MTAs will quote the original "From " line */
1198 if (mutt_str_startswith(line, ">From "))
1199 continue; /* just ignore */
1200 else if (is_from(line, return_path, sizeof(return_path), &t))
1201 {
1202 /* MH sometimes has the From_ line in the middle of the header! */
1203 if (e && (e->received == 0))
1204 e->received = t - mutt_date_local_tz(t);
1205 continue;
1206 }
1207
1208 (void) mutt_file_seek(fp, loc, SEEK_SET);
1209 break; /* end of header */
1210 }
1211
1212 *buf = '\0';
1213
1214 if (mutt_replacelist_match(&SpamList, buf, sizeof(buf), line))
1215 {
1216 if (!mutt_regexlist_match(&NoSpamList, line))
1217 {
1218 /* if spam tag already exists, figure out how to amend it */
1219 if ((!mutt_buffer_is_empty(&env->spam)) && (*buf != '\0'))
1220 {
1221 /* If `$spam_separator` defined, append with separator */
1222 const char *const c_spam_separator = cs_subset_string(NeoMutt->sub, "spam_separator");
1223 if (c_spam_separator)
1224 {
1225 mutt_buffer_addstr(&env->spam, c_spam_separator);
1226 mutt_buffer_addstr(&env->spam, buf);
1227 }
1228 else /* overwrite */
1229 {
1230 mutt_buffer_reset(&env->spam);
1231 mutt_buffer_addstr(&env->spam, buf);
1232 }
1233 }
1234
1235 /* spam tag is new, and match expr is non-empty; copy */
1236 else if (mutt_buffer_is_empty(&env->spam) && (*buf != '\0'))
1237 {
1238 mutt_buffer_addstr(&env->spam, buf);
1239 }
1240
1241 /* match expr is empty; plug in null string if no existing tag */
1242 else if (mutt_buffer_is_empty(&env->spam))
1243 {
1244 mutt_buffer_addstr(&env->spam, "");
1245 }
1246
1247 if (!mutt_buffer_is_empty(&env->spam))
1248 mutt_debug(LL_DEBUG5, "spam = %s\n", env->spam.data);
1249 }
1250 }
1251
1252 *p = '\0';
1253 p = mutt_str_skip_email_wsp(p + 1);
1254 if (*p == '\0')
1255 continue; /* skip empty header fields */
1256
1257 mutt_rfc822_parse_line(env, e, line, p, user_hdrs, weed, true);
1258 }
1259
1260 FREE(&line);
1261
1262 if (e)
1263 {
1264 e->body->hdr_offset = e->offset;
1265 e->body->offset = ftello(fp);
1266
1268
1269 if (env->subject)
1270 {
1271 regmatch_t pmatch[1];
1272
1273 const struct Regex *c_reply_regex = cs_subset_regex(NeoMutt->sub, "reply_regex");
1274 if (mutt_regex_capture(c_reply_regex, env->subject, 1, pmatch))
1275 {
1276 env->real_subj = env->subject + pmatch[0].rm_eo;
1277 if (env->real_subj[0] == '\0')
1278 env->real_subj = NULL;
1279 }
1280 else
1281 env->real_subj = env->subject;
1282 }
1283
1284 if (e->received < 0)
1285 {
1286 mutt_debug(LL_DEBUG1, "resetting invalid received time to 0\n");
1287 e->received = 0;
1288 }
1289
1290 /* check for missing or invalid date */
1291 if (e->date_sent <= 0)
1292 {
1293 mutt_debug(LL_DEBUG1, "no date found, using received time from msg separator\n");
1294 e->date_sent = e->received;
1295 }
1296
1297#ifdef USE_AUTOCRYPT
1298 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
1299 if (c_autocrypt)
1300 {
1302 /* No sense in taking up memory after the header is processed */
1304 }
1305#endif
1306 }
1307
1308 return env;
1309}
1310
1317struct Body *mutt_read_mime_header(FILE *fp, bool digest)
1318{
1319 if (!fp)
1320 return NULL;
1321
1322 struct Body *p = mutt_body_new();
1323 struct Envelope *env = mutt_env_new();
1324 char *c = NULL;
1325 size_t linelen = 1024;
1326 char *line = mutt_mem_malloc(linelen);
1327 bool matched = false;
1328
1329 p->hdr_offset = ftello(fp);
1330
1331 p->encoding = ENC_7BIT; /* default from RFC1521 */
1332 p->type = digest ? TYPE_MESSAGE : TYPE_TEXT;
1334
1335 while (*(line = mutt_rfc822_read_line(fp, line, &linelen)) != 0)
1336 {
1337 /* Find the value of the current header */
1338 c = strchr(line, ':');
1339 if (c)
1340 {
1341 *c = '\0';
1342 c = mutt_str_skip_email_wsp(c + 1);
1343 if (*c == '\0')
1344 {
1345 mutt_debug(LL_DEBUG1, "skipping empty header field: %s\n", line);
1346 continue;
1347 }
1348 }
1349 else
1350 {
1351 mutt_debug(LL_DEBUG1, "bogus MIME header: %s\n", line);
1352 break;
1353 }
1354
1355 size_t plen = mutt_istr_startswith(line, "content-");
1356 if (plen != 0)
1357 {
1358 if (mutt_istr_equal("type", line + plen))
1360 else if (mutt_istr_equal("language", line + plen))
1362 else if (mutt_istr_equal("transfer-encoding", line + plen))
1364 else if (mutt_istr_equal("disposition", line + plen))
1366 else if (mutt_istr_equal("description", line + plen))
1367 {
1370 }
1371 else if (mutt_istr_equal("id", line + plen))
1372 {
1373 // strip <angle braces> from Content-ID: header
1374 char *id = c;
1375 int cid_len = mutt_str_len(c);
1376 if (cid_len > 2)
1377 {
1378 if (id[0] == '<')
1379 {
1380 id++;
1381 cid_len--;
1382 }
1383 if (id[cid_len - 1] == '>')
1384 id[cid_len - 1] = '\0';
1385 }
1386 mutt_param_set(&p->parameter, "content-id", id);
1387 }
1388 }
1389#ifdef SUN_ATTACHMENT
1390 else if ((plen = mutt_istr_startswith(line, "x-sun-")))
1391 {
1392 if (mutt_istr_equal("data-type", line + plen))
1394 else if (mutt_istr_equal("encoding-info", line + plen))
1396 else if (mutt_istr_equal("content-lines", line + plen))
1397 mutt_param_set(&p->parameter, "content-lines", c);
1398 else if (mutt_istr_equal("data-description", line + plen))
1399 {
1402 }
1403 }
1404#endif
1405 else
1406 {
1407 if (mutt_rfc822_parse_line(env, NULL, line, c, false, false, false))
1408 {
1409 matched = true;
1410 }
1411 }
1412 }
1413 p->offset = ftello(fp); /* Mark the start of the real data */
1414 if ((p->type == TYPE_TEXT) && !p->subtype)
1415 p->subtype = mutt_str_dup("plain");
1416 else if ((p->type == TYPE_MESSAGE) && !p->subtype)
1417 p->subtype = mutt_str_dup("rfc822");
1418
1419 FREE(&line);
1420
1421 if (matched)
1422 {
1423 p->mime_headers = env;
1425 }
1426 else
1427 {
1428 mutt_env_free(&env);
1429 }
1430
1431 return p;
1432}
1433
1441bool mutt_is_message_type(int type, const char *subtype)
1442{
1443 if (type != TYPE_MESSAGE)
1444 return false;
1445
1446 subtype = NONULL(subtype);
1447 return (mutt_istr_equal(subtype, "rfc822") ||
1448 mutt_istr_equal(subtype, "news") || mutt_istr_equal(subtype, "global"));
1449}
1450
1457static void parse_part(FILE *fp, struct Body *b, int *counter)
1458{
1459 if (!fp || !b)
1460 return;
1461
1462 const char *bound = NULL;
1463 static unsigned short recurse_level = 0;
1464
1465 if (recurse_level >= MUTT_MIME_MAX_DEPTH)
1466 {
1467 mutt_debug(LL_DEBUG1, "recurse level too deep. giving up.\n");
1468 return;
1469 }
1470 recurse_level++;
1471
1472 switch (b->type)
1473 {
1474 case TYPE_MULTIPART:
1475#ifdef SUN_ATTACHMENT
1476 if (mutt_istr_equal(b->subtype, "x-sun-attachment"))
1477 bound = "--------";
1478 else
1479#endif
1480 bound = mutt_param_get(&b->parameter, "boundary");
1481
1482 if (!mutt_file_seek(fp, b->offset, SEEK_SET))
1483 {
1484 goto bail;
1485 }
1486 b->parts = parse_multipart(fp, bound, b->offset + b->length,
1487 mutt_istr_equal("digest", b->subtype), counter);
1488 break;
1489
1490 case TYPE_MESSAGE:
1491 if (!b->subtype)
1492 break;
1493
1494 if (!mutt_file_seek(fp, b->offset, SEEK_SET))
1495 {
1496 goto bail;
1497 }
1498 if (mutt_is_message_type(b->type, b->subtype))
1499 b->parts = rfc822_parse_message(fp, b, counter);
1500 else if (mutt_istr_equal(b->subtype, "external-body"))
1501 b->parts = mutt_read_mime_header(fp, 0);
1502 else
1503 goto bail;
1504 break;
1505
1506 default:
1507 goto bail;
1508 }
1509
1510 /* try to recover from parsing error */
1511 if (!b->parts)
1512 {
1513 b->type = TYPE_TEXT;
1514 mutt_str_replace(&b->subtype, "plain");
1515 }
1516bail:
1517 recurse_level--;
1518}
1519
1530static struct Body *parse_multipart(FILE *fp, const char *boundary,
1531 LOFF_T end_off, bool digest, int *counter)
1532{
1533 if (!fp)
1534 return NULL;
1535
1536 if (!boundary)
1537 {
1538 mutt_error(_("multipart message has no boundary parameter"));
1539 return NULL;
1540 }
1541
1542 char buf[1024] = { 0 };
1543 struct Body *head = NULL, *last = NULL, *new_body = NULL;
1544 bool final = false; /* did we see the ending boundary? */
1545
1546 const size_t blen = mutt_str_len(boundary);
1547 while ((ftello(fp) < end_off) && fgets(buf, sizeof(buf), fp))
1548 {
1549 const size_t len = mutt_str_len(buf);
1550
1551 const size_t crlf = ((len > 1) && (buf[len - 2] == '\r')) ? 1 : 0;
1552
1553 if ((buf[0] == '-') && (buf[1] == '-') && mutt_str_startswith(buf + 2, boundary))
1554 {
1555 if (last)
1556 {
1557 last->length = ftello(fp) - last->offset - len - 1 - crlf;
1558 if (last->parts && (last->parts->length == 0))
1559 last->parts->length = ftello(fp) - last->parts->offset - len - 1 - crlf;
1560 /* if the body is empty, we can end up with a -1 length */
1561 if (last->length < 0)
1562 last->length = 0;
1563 }
1564
1565 if (len > 0)
1566 {
1567 /* Remove any trailing whitespace, up to the length of the boundary */
1568 for (size_t i = len - 1; IS_SPACE(buf[i]) && (i >= (blen + 2)); i--)
1569 buf[i] = '\0';
1570 }
1571
1572 /* Check for the end boundary */
1573 if (mutt_str_equal(buf + blen + 2, "--"))
1574 {
1575 final = true;
1576 break; /* done parsing */
1577 }
1578 else if (buf[2 + blen] == '\0')
1579 {
1580 new_body = mutt_read_mime_header(fp, digest);
1581
1582#ifdef SUN_ATTACHMENT
1583 if (mutt_param_get(&new_body->parameter, "content-lines"))
1584 {
1585 int lines = 0;
1586 mutt_str_atoi(mutt_param_get(&new_body->parameter, "content-lines"), &lines);
1587 for (; lines > 0; lines--)
1588 if ((ftello(fp) >= end_off) || !fgets(buf, sizeof(buf), fp))
1589 break;
1590 }
1591#endif
1592 /* Consistency checking - catch bad attachment end boundaries */
1593 if (new_body->offset > end_off)
1594 {
1595 mutt_body_free(&new_body);
1596 break;
1597 }
1598 if (head)
1599 {
1600 last->next = new_body;
1601 last = new_body;
1602 }
1603 else
1604 {
1605 last = new_body;
1606 head = new_body;
1607 }
1608
1609 /* It seems more intuitive to add the counter increment to
1610 * parse_part(), but we want to stop the case where a multipart
1611 * contains thousands of tiny parts before the memory and data
1612 * structures are allocated. */
1613 if (++(*counter) >= MUTT_MIME_MAX_PARTS)
1614 break;
1615 }
1616 }
1617 }
1618
1619 /* in case of missing end boundary, set the length to something reasonable */
1620 if (last && (last->length == 0) && !final)
1621 last->length = end_off - last->offset;
1622
1623 /* parse recursive MIME parts */
1624 for (last = head; last; last = last->next)
1625 parse_part(fp, last, counter);
1626
1627 return head;
1628}
1629
1639static struct Body *rfc822_parse_message(FILE *fp, struct Body *parent, int *counter)
1640{
1641 if (!fp || !parent)
1642 return NULL;
1643
1644 parent->email = email_new();
1645 parent->email->offset = ftello(fp);
1646 parent->email->env = mutt_rfc822_read_header(fp, parent->email, false, false);
1647 struct Body *msg = parent->email->body;
1648
1649 /* ignore the length given in the content-length since it could be wrong
1650 * and we already have the info to calculate the correct length */
1651 /* if (msg->length == -1) */
1652 msg->length = parent->length - (msg->offset - parent->offset);
1653
1654 /* if body of this message is empty, we can end up with a negative length */
1655 if (msg->length < 0)
1656 msg->length = 0;
1657
1658 parse_part(fp, msg, counter);
1659 return msg;
1660}
1661
1670bool mutt_parse_mailto(struct Envelope *env, char **body, const char *src)
1671{
1672 if (!env || !src)
1673 return false;
1674
1675 struct Url *url = url_parse(src);
1676 if (!url)
1677 return false;
1678
1679 if (url->host)
1680 {
1681 /* this is not a path-only URL */
1682 url_free(&url);
1683 return false;
1684 }
1685
1686 mutt_addrlist_parse(&env->to, url->path);
1687
1688 struct UrlQuery *np;
1689 STAILQ_FOREACH(np, &url->query_strings, entries)
1690 {
1691 const char *tag = np->name;
1692 char *value = np->value;
1693 /* Determine if this header field is on the allowed list. Since NeoMutt
1694 * interprets some header fields specially (such as
1695 * "Attach: ~/.gnupg/secring.gpg"), care must be taken to ensure that
1696 * only safe fields are allowed.
1697 *
1698 * RFC2368, "4. Unsafe headers"
1699 * The user agent interpreting a mailto URL SHOULD choose not to create
1700 * a message if any of the headers are considered dangerous; it may also
1701 * choose to create a message with only a subset of the headers given in
1702 * the URL. */
1703 if (mutt_list_match(tag, &MailToAllow))
1704 {
1705 if (mutt_istr_equal(tag, "body"))
1706 {
1707 if (body)
1708 mutt_str_replace(body, value);
1709 }
1710 else
1711 {
1712 char *scratch = NULL;
1713 size_t taglen = mutt_str_len(tag);
1714
1715 mutt_str_asprintf(&scratch, "%s: %s", tag, value);
1716 scratch[taglen] = 0; /* overwrite the colon as mutt_rfc822_parse_line expects */
1717 value = mutt_str_skip_email_wsp(&scratch[taglen + 1]);
1718 mutt_rfc822_parse_line(env, NULL, scratch, value, true, false, true);
1719 FREE(&scratch);
1720 }
1721 }
1722 }
1723
1724 /* RFC2047 decode after the RFC822 parsing */
1726
1727 url_free(&url);
1728 return true;
1729}
1730
1736void mutt_parse_part(FILE *fp, struct Body *b)
1737{
1738 int counter = 0;
1739
1740 parse_part(fp, b, &counter);
1741}
1742
1751struct Body *mutt_rfc822_parse_message(FILE *fp, struct Body *parent)
1752{
1753 int counter = 0;
1754
1755 return rfc822_parse_message(fp, parent, &counter);
1756}
1757
1767struct Body *mutt_parse_multipart(FILE *fp, const char *boundary, LOFF_T end_off, bool digest)
1768{
1769 int counter = 0;
1770
1771 return parse_multipart(fp, boundary, end_off, digest, &counter);
1772}
void mutt_addrlist_clear(struct AddressList *al)
Unlink and free all Address in an AddressList.
Definition: address.c:1435
int mutt_addrlist_parse(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition: address.c:458
Email Address Handling.
const char * mutt_str_atoul(const char *str, unsigned long *dst)
Convert ASCII string to an unsigned long.
Definition: atoi.c:227
const char * mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition: atoi.c:203
const char * mutt_str_atoi(const char *str, int *dst)
Convert ASCII string to an integer.
Definition: atoi.c:179
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:265
void mutt_buffer_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:313
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:298
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
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:85
char * mutt_buffer_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition: buffer.c:485
const struct Regex * cs_subset_regex(const struct ConfigSubset *sub, const char *name)
Get a regex config item by name.
Definition: helpers.c:243
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
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
Convenience wrapper for the config headers.
Convenience wrapper for the core headers.
int mutt_date_local_tz(time_t t)
Calculate the local timezone in seconds east of UTC.
Definition: date.c:206
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:428
time_t mutt_date_parse_date(const char *s, struct Tz *tz_out)
Parse a date string in RFC822 format.
Definition: date.c:457
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition: body.c:57
struct Body * mutt_body_new(void)
Create a new Body.
Definition: body.c:43
struct Email * email_new(void)
Create a new Email.
Definition: email.c:78
struct ReplaceList SpamList
List of regexes and patterns to match spam emails.
Definition: globals.c:34
struct RegexList SubscribedLists
List of regexes to match subscribed mailing lists.
Definition: globals.c:43
struct HashTable * AutoSubscribeCache
Hash Table of auto-subscribed mailing lists.
Definition: globals.c:38
struct RegexList UnSubscribedLists
List of regexes to exclude false matches in SubscribedLists.
Definition: globals.c:40
struct RegexList UnMailLists
List of regexes to exclude false matches in MailLists.
Definition: globals.c:42
struct RegexList MailLists
List of regexes to match mailing lists.
Definition: globals.c:41
struct ListHead MailToAllow
List of permitted fields in a mailto: url.
Definition: globals.c:37
struct ListHead Ignore
List of header patterns to ignore.
Definition: globals.c:35
struct RegexList NoSpamList
List of regexes to identify non-spam emails.
Definition: globals.c:33
struct ListHead UnIgnore
List of header patterns to unignore (see)
Definition: globals.c:36
Representation of an email.
void mutt_env_free(struct Envelope **ptr)
Free an Envelope.
Definition: envelope.c:97
struct Envelope * mutt_env_new(void)
Create a new Envelope.
Definition: envelope.c:43
void mutt_autocrypthdr_free(struct AutocryptHeader **p)
Free an AutocryptHeader.
Definition: envelope.c:75
struct AutocryptHeader * mutt_autocrypthdr_new(void)
Create a new AutocryptHeader.
Definition: envelope.c:66
Representation of an email header (envelope)
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition: file.c:706
bool is_from(const char *s, char *path, size_t pathlen, time_t *tp)
Is a string a 'From' header line?
Definition: from.c:48
Determine who the email is from.
Global variables.
#define mutt_error(...)
Definition: logging.h:87
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
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:335
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:362
struct HashTable * mutt_hash_new(size_t num_elems, HashFlags flags)
Create a new Hash Table (with string keys)
Definition: hash.c:259
#define MUTT_HASH_STRDUP_KEYS
make a copy of the keys
Definition: hash.h:110
#define MUTT_HASH_STRCASECMP
use strcasecmp() to compare keys
Definition: hash.h:109
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:64
struct ListNode * mutt_list_insert_head(struct ListHead *h, char *s)
Insert a string at the beginning of a List.
Definition: list.c:45
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition: list.c:122
bool mutt_list_match(const char *s, struct ListHead *h)
Is the string in the list (see notes)
Definition: list.c:195
@ LL_DEBUG5
Log at debug level 5.
Definition: logging.h:44
@ LL_DEBUG2
Log at debug level 2.
Definition: logging.h:41
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
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
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
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
Definition: mime.h:70
char * mutt_ch_get_default_charset(void)
Get the default character set.
Definition: charset.c:439
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:135
bool mutt_regex_capture(const struct Regex *regex, const char *str, size_t nmatch, regmatch_t matches[])
Match a regex against a string, with provided options.
Definition: regex.c:614
bool mutt_replacelist_match(struct ReplaceList *rl, char *buf, size_t buflen, const char *str)
Does a string match a pattern?
Definition: regex.c:495
bool mutt_regexlist_match(struct RegexList *rl, const char *str)
Does a string match any Regex in the list?
Definition: regex.c:195
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition: string.c:451
void mutt_str_remove_trailing_ws(char *s)
Trim trailing whitespace from a string.
Definition: string.c:636
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:819
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:250
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:1031
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition: string.c:679
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:807
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_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:227
char * mutt_str_skip_whitespace(const char *p)
Find the first non-whitespace character in a string.
Definition: string.c:622
bool mutt_str_is_email_wsp(char c)
Is this a whitespace character (for an email header)
Definition: string.c:695
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:567
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:239
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:524
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:326
Many unsorted constants and some structs.
char * mutt_param_get(const struct ParameterList *pl, const char *s)
Find a matching Parameter.
Definition: parameter.c:84
void mutt_param_set(struct ParameterList *pl, const char *attribute, const char *value)
Set a Parameter.
Definition: parameter.c:110
void mutt_param_free(struct ParameterList *pl)
Free a ParameterList.
Definition: parameter.c:61
struct Parameter * mutt_param_new(void)
Create a new Parameter.
Definition: parameter.c:39
Store attributes associated with a MIME part.
char * mutt_rfc822_read_line(FILE *fp, char *line, size_t *linelen)
Read a header line from a file.
Definition: parse.c:1082
void mutt_parse_part(FILE *fp, struct Body *b)
Parse a MIME part.
Definition: parse.c:1736
void mutt_auto_subscribe(const char *mailto)
Check if user is subscribed to mailing list.
Definition: parse.c:67
struct Body * mutt_read_mime_header(FILE *fp, bool digest)
Parse a MIME header.
Definition: parse.c:1317
void mutt_parse_content_type(const char *s, struct Body *ct)
Parse a content type.
Definition: parse.c:424
static struct AutocryptHeader * parse_autocrypt(struct AutocryptHeader *head, const char *s)
Parse an Autocrypt header line.
Definition: parse.c:529
static void parse_references(struct ListHead *head, const char *s)
Parse references from an email header.
Definition: parse.c:280
bool mutt_matches_ignore(const char *s)
Does the string match the ignore list.
Definition: parse.c:313
int mutt_rfc822_parse_line(struct Envelope *env, struct Email *e, const char *name, const char *body, bool user_hdrs, bool weed, bool do_2047)
Parse an email header.
Definition: parse.c:634
static char * rfc2369_first_mailto(const char *body)
Extract the first mailto: URL from a RFC2369 list.
Definition: parse.c:593
enum ContentType mutt_check_mime_type(const char *s)
Check a MIME type string.
Definition: parse.c:323
static struct Body * rfc822_parse_message(FILE *fp, struct Body *parent, int *counter)
Parse a Message/RFC822 body.
Definition: parse.c:1639
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
Parses an RFC822 header.
Definition: parse.c:1157
static void parse_content_language(const char *s, struct Body *ct)
Read the content's language.
Definition: parse.c:297
bool mutt_parse_mailto(struct Envelope *env, char **body, const char *src)
Parse a mailto:// url.
Definition: parse.c:1670
int mutt_check_encoding(const char *c)
Check the encoding type.
Definition: parse.c:396
static void parse_content_disposition(const char *s, struct Body *ct)
Parse a content disposition.
Definition: parse.c:248
struct Body * mutt_parse_multipart(FILE *fp, const char *boundary, LOFF_T end_off, bool digest)
Parse a multipart structure.
Definition: parse.c:1767
char * mutt_extract_message_id(const char *s, size_t *len)
Find a message-id.
Definition: parse.c:360
struct Body * mutt_rfc822_parse_message(FILE *fp, struct Body *parent)
Parse a Message/RFC822 body.
Definition: parse.c:1751
static void parse_part(FILE *fp, struct Body *b, int *counter)
Parse a MIME part.
Definition: parse.c:1457
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:1530
bool mutt_is_message_type(int type, const char *subtype)
Determine if a mime type matches a message or not.
Definition: parse.c:1441
static void parse_parameters(struct ParameterList *pl, const char *s, bool allow_value_spaces)
Parse a list of Parameters.
Definition: parse.c:110
#define CONTENT_TOO_BIG
Definition: parse.c:56
Miscellaneous email parsing routines.
static size_t plen
Length of cached packet.
Definition: pgppacket.c:39
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:725
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
#define TAILQ_FIRST(head)
Definition: queue.h:723
#define TAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:637
#define TAILQ_EMPTY(head)
Definition: queue.h:721
#define TAILQ_INSERT_HEAD(head, elm, field)
Definition: queue.h:796
void rfc2047_decode_envelope(struct Envelope *env)
Decode the fields of an Envelope.
Definition: rfc2047.c:793
void rfc2047_decode(char **pd)
Decode any RFC2047-encoded header fields.
Definition: rfc2047.c:649
RFC2047 MIME extensions encoding / decoding routines.
void rfc2231_decode_parameters(struct ParameterList *pl)
Decode a Parameter list.
Definition: rfc2231.c:236
RFC2231 MIME Charset routines.
Convenience wrapper for the send headers.
#define NONULL(x)
Definition: string2.h:37
#define IS_SPACE(ch)
Definition: string2.h:38
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:77
struct Body * parts
parts of a multipart or message/rfc822
Definition: body.h:72
LOFF_T offset
offset where the actual data begins
Definition: body.h:52
char * xtype
content-type if x-unknown
Definition: body.h:61
struct Envelope * mime_headers
Memory hole protected headers.
Definition: body.h:75
LOFF_T length
length (in bytes) of attachment
Definition: body.h:53
struct ParameterList parameter
Parameters of the content-type.
Definition: body.h:62
struct Email * email
header information for message/rfc822
Definition: body.h:73
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:71
char * subtype
content-type subtype
Definition: body.h:60
unsigned int encoding
content-transfer-encoding, ContentEncoding
Definition: body.h:41
long hdr_offset
Offset in stream where the headers begin.
Definition: body.h:80
char * form_name
Content-Disposition form-data name param.
Definition: body.h:59
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:58
String manipulation buffer.
Definition: buffer.h:34
char * data
Pointer to data.
Definition: buffer.h:35
The envelope/body of an email.
Definition: email.h:37
bool read
Email is read.
Definition: email.h:48
unsigned int zminutes
Minutes away from UTC.
Definition: email.h:55
struct Envelope * env
Envelope information.
Definition: email.h:66
bool mime
Has a MIME-Version header?
Definition: email.h:46
int lines
How many lines in the body of this message?
Definition: email.h:60
struct Body * body
List of MIME parts.
Definition: email.h:67
bool old
Email is seen, but unread.
Definition: email.h:47
bool zoccident
True, if west of UTC, False if east.
Definition: email.h:56
LOFF_T offset
Where in the stream does this message begin?
Definition: email.h:69
bool flagged
Marked important?
Definition: email.h:45
unsigned int zhours
Hours away from UTC.
Definition: email.h:54
time_t date_sent
Time when the message was sent (UTC)
Definition: email.h:58
bool replied
Email has been replied to.
Definition: email.h:49
bool expired
Already expired?
Definition: email.h:44
bool deleted
Email is deleted.
Definition: email.h:76
time_t received
Time when the message was placed in the mailbox.
Definition: email.h:59
The header of an Email.
Definition: envelope.h:57
struct ListHead userhdrs
user defined headers
Definition: envelope.h:87
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
struct AddressList to
Email's 'To' list.
Definition: envelope.h:60
char * followup_to
List of 'followup-to' fields.
Definition: envelope.h:81
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:82
struct AddressList x_original_to
Email's 'X-Orig-to'.
Definition: envelope.h:66
struct AutocryptHeader * autocrypt_gossip
Autocrypt Gossip header.
Definition: envelope.h:90
char * newsgroups
List of newsgroups.
Definition: envelope.h:79
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:85
struct AutocryptHeader * autocrypt
Autocrypt header.
Definition: envelope.h:89
struct Buffer spam
Spam header.
Definition: envelope.h:84
struct ListHead in_reply_to
in-reply-to header content
Definition: envelope.h:86
char * subject
Email's subject.
Definition: envelope.h:70
struct AddressList bcc
Email's 'Bcc' list.
Definition: envelope.h:62
char * xref
List of cross-references.
Definition: envelope.h:80
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 * real_subj
Offset of the real subject.
Definition: envelope.h:71
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:35
char * data
String.
Definition: list.h:36
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
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
Cached regular expression.
Definition: regex3.h:89
String list.
Definition: slist.h:47
List of recognised Timezones.
Definition: date.h:43
unsigned char zminutes
Minutes away from UTC.
Definition: date.h:46
bool zoccident
True if west of UTC, False if East.
Definition: date.h:47
unsigned char zhours
Hours away from UTC.
Definition: date.h:45
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:234
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition: url.c:123
enum UrlScheme url_check_scheme(const char *str)
Check the protocol of a URL.
Definition: url.c:221
Parse and identify different URL schemes.
@ U_MAILTO
Url is mailto://.
Definition: url.h:45