NeoMutt  2025-12-11-1039-g550ac6
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 "alias/lib.h"
45#include "mutt.h"
46#include "parse.h"
47#include "body.h"
48#include "email.h"
49#include "envelope.h"
50#include "from.h"
51#include "mime.h"
52#include "module_data.h"
53#include "parameter.h"
54#include "rfc2047.h"
55#include "rfc2231.h"
56#include "url.h"
57#ifdef USE_AUTOCRYPT
58#include "autocrypt/lib.h"
59#endif
60
62#define CONTENT_TOO_BIG (1 << 30)
63
64static void parse_part(FILE *fp, struct Body *b, int *counter);
65static struct Body *rfc822_parse_message(FILE *fp, struct Body *parent, int *counter);
66static struct Body *parse_multipart(FILE *fp, const char *boundary,
67 LOFF_T end_off, bool digest, int *counter);
68
74{
75 if (!header)
76 return;
77
78 for (; (*header != '\0'); header++)
79 {
80 if ((*header < 33) || (*header > 126) || (*header == ':'))
81 *header = '?';
82 }
83}
84
94{
95 if (!header)
96 return;
97
98 for (; (*header != '\0'); header++)
99 {
100 if ((*header == '\n') || (*header == '\r'))
101 *header = ' ';
102 }
103}
104
116static void parse_parameters(struct ParameterList *pl, const char *s, bool allow_value_spaces)
117{
118 struct Parameter *pnew = NULL;
119 const char *p = NULL;
120 size_t i;
121
122 struct Buffer *buf = buf_pool_get();
123 /* allow_value_spaces, especially with autocrypt keydata, can result
124 * in quite large parameter values. avoid frequent reallocs by
125 * pre-sizing */
126 if (allow_value_spaces)
127 buf_alloc(buf, mutt_str_len(s));
128
129 mutt_debug(LL_DEBUG2, "'%s'\n", s);
130
131 const bool assumed = !slist_is_empty(cc_assumed_charset());
132 while (*s)
133 {
134 buf_reset(buf);
135
136 p = strpbrk(s, "=;");
137 if (!p)
138 {
139 mutt_debug(LL_DEBUG1, "malformed parameter: %s\n", s);
140 goto bail;
141 }
142
143 /* if we hit a ; now the parameter has no value, just skip it */
144 if (*p != ';')
145 {
146 i = p - s;
147 /* remove whitespace from the end of the attribute name */
148 while ((i > 0) && mutt_str_is_email_wsp(s[i - 1]))
149 i--;
150
151 /* the check for the missing parameter token is here so that we can skip
152 * over any quoted value that may be present. */
153 if (i == 0)
154 {
155 mutt_debug(LL_DEBUG1, "missing attribute: %s\n", s);
156 pnew = NULL;
157 }
158 else
159 {
160 pnew = mutt_param_new();
161 pnew->attribute = mutt_strn_dup(s, i);
162 }
163
164 do
165 {
166 s = mutt_str_skip_email_wsp(p + 1); /* skip over the =, or space if we loop */
167
168 if (*s == '"')
169 {
170 bool state_ascii = true;
171 s++;
172 for (; *s; s++)
173 {
174 if (assumed)
175 {
176 // As iso-2022-* has a character of '"' with non-ascii state, ignore it
177 if (*s == 0x1b)
178 {
179 if ((s[1] == '(') && ((s[2] == 'B') || (s[2] == 'J')))
180 state_ascii = true;
181 else
182 state_ascii = false;
183 }
184 }
185 if (state_ascii && (*s == '"'))
186 break;
187 if (*s == '\\')
188 {
189 if (s[1])
190 {
191 s++;
192 /* Quote the next character */
193 buf_addch(buf, *s);
194 }
195 }
196 else
197 {
198 buf_addch(buf, *s);
199 }
200 }
201 if (*s)
202 s++; /* skip over the " */
203 }
204 else
205 {
206 for (; *s && *s != ' ' && *s != ';'; s++)
207 buf_addch(buf, *s);
208 }
209
210 p = s;
211 } while (allow_value_spaces && (*s == ' '));
212
213 /* if the attribute token was missing, 'new' will be NULL */
214 if (pnew)
215 {
216 pnew->value = buf_strdup(buf);
217
218 mutt_debug(LL_DEBUG2, "parse_parameter: '%s' = '%s'\n",
219 pnew->attribute ? pnew->attribute : "", pnew->value ? pnew->value : "");
220
221 /* Add this parameter to the list */
222 TAILQ_INSERT_HEAD(pl, pnew, entries);
223 }
224 }
225 else
226 {
227 mutt_debug(LL_DEBUG1, "parameter with no value: %s\n", s);
228 s = p;
229 }
230
231 /* Find the next parameter */
232 if ((*s != ';') && !(s = strchr(s, ';')))
233 break; /* no more parameters */
234
235 do
236 {
237 /* Move past any leading whitespace. the +1 skips over the semicolon */
238 s = mutt_str_skip_email_wsp(s + 1);
239 } while (*s == ';'); /* skip empty parameters */
240 }
241
242bail:
243
245 buf_pool_release(&buf);
246}
247
255static void parse_content_disposition(const char *s, struct Body *b)
256{
257 struct ParameterList pl = TAILQ_HEAD_INITIALIZER(pl);
258
259 if (mutt_istr_startswith(s, "inline"))
261 else if (mutt_istr_startswith(s, "form-data"))
263 else
265
266 /* Check to see if a default filename was given */
267 s = strchr(s, ';');
268 if (s)
269 {
270 s = mutt_str_skip_email_wsp(s + 1);
271 parse_parameters(&pl, s, false);
272 s = mutt_param_get(&pl, "filename");
273 if (s)
275 s = mutt_param_get(&pl, "name");
276 if (s)
278 mutt_param_free(&pl);
279 }
280}
281
295static char *extract_message_id(const char *s, size_t *len)
296{
297 if (!s)
298 return NULL;
299
300 for (const char *p = s, *beg = NULL; *p; p++)
301 {
302 if (*p == '<')
303 {
304 beg = p;
305 continue;
306 }
307
308 if (beg && (*p == '>'))
309 {
310 if (len)
311 *len = p - s + 1;
312 return mutt_strn_dup(beg, (p + 1) - beg);
313 }
314 }
315
316 return NULL;
317}
318
324static void parse_references(struct ListHead *head, const char *s)
325{
326 if (!head)
327 return;
328
329 /* Decode once: mutt_extract_message_id() returns an offset into the decoded
330 * text, so the search and the advance must happen on the same string.
331 * Decoding can lengthen the text (e.g. a single-byte charset expanding to
332 * UTF-8), so applying that offset to the raw header would run off the end. */
333 char *decoded = mutt_str_dup(s);
334 rfc2047_decode(&decoded);
335
336 char *m = NULL;
337 const char *p = decoded;
338 for (size_t off = 0; (m = extract_message_id(p, &off)); p += off)
339 {
340 mutt_list_insert_head(head, m);
341 }
342
343 FREE(&decoded);
344}
345
351static void parse_content_language(const char *s, struct Body *b)
352{
353 if (!s || !b)
354 return;
355
356 mutt_debug(LL_DEBUG2, "RFC8255 >> Content-Language set to %s\n", s);
358}
359
367bool mutt_matches_ignore(const char *s)
368{
370 ASSERT(mod_data);
371
372 return mutt_list_match(s, &mod_data->ignore) && !mutt_list_match(s, &mod_data->unignore);
373}
374
381{
382 if (mutt_istr_equal("text", s))
383 return TYPE_TEXT;
384 if (mutt_istr_equal("multipart", s))
385 return TYPE_MULTIPART;
386 if (mutt_istr_equal("x-sun-attachment", s))
387 return TYPE_MULTIPART;
388 if (mutt_istr_equal("application", s))
389 return TYPE_APPLICATION;
390 if (mutt_istr_equal("message", s))
391 return TYPE_MESSAGE;
392 if (mutt_istr_equal("image", s))
393 return TYPE_IMAGE;
394 if (mutt_istr_equal("audio", s))
395 return TYPE_AUDIO;
396 if (mutt_istr_equal("video", s))
397 return TYPE_VIDEO;
398 if (mutt_istr_equal("model", s))
399 return TYPE_MODEL;
400 if (mutt_istr_equal("*", s))
401 return TYPE_ANY;
402 if (mutt_istr_equal(".*", s))
403 return TYPE_ANY;
404
405 return TYPE_OTHER;
406}
407
417char *mutt_extract_message_id(const char *s, size_t *len)
418{
419 if (!s || (*s == '\0'))
420 return NULL;
421
422 char *decoded = mutt_str_dup(s);
423 rfc2047_decode(&decoded);
424
425 char *res = extract_message_id(decoded, len);
426
427 FREE(&decoded);
428 return res;
429}
430
436int mutt_check_encoding(const char *c)
437{
438 if (mutt_istr_startswith(c, "7bit"))
439 return ENC_7BIT;
440 if (mutt_istr_startswith(c, "8bit"))
441 return ENC_8BIT;
442 if (mutt_istr_startswith(c, "binary"))
443 return ENC_BINARY;
444 if (mutt_istr_startswith(c, "quoted-printable"))
446 if (mutt_istr_startswith(c, "base64"))
447 return ENC_BASE64;
448 if (mutt_istr_startswith(c, "x-uuencode"))
449 return ENC_UUENCODED;
450 if (mutt_istr_startswith(c, "uuencode"))
451 return ENC_UUENCODED;
452 return ENC_OTHER;
453}
454
464void mutt_parse_content_type(const char *s, struct Body *b)
465{
466 if (!s || !b)
467 return;
468
469 FREE(&b->subtype);
471
472 /* First extract any existing parameters */
473 char *pc = strchr(s, ';');
474 if (pc)
475 {
476 *pc++ = 0;
477 while (*pc && mutt_isspace(*pc))
478 pc++;
479 parse_parameters(&b->parameter, pc, false);
480
481 /* Some pre-RFC1521 gateways still use the "name=filename" convention,
482 * but if a filename has already been set in the content-disposition,
483 * let that take precedence, and don't set it here */
484 pc = mutt_param_get(&b->parameter, "name");
485 if (pc && !b->filename)
486 b->filename = mutt_str_dup(pc);
487
488 /* this is deep and utter perversion */
489 pc = mutt_param_get(&b->parameter, "conversions");
490 if (pc)
492 }
493
494 /* Now get the subtype */
495 char *subtype = strchr(s, '/');
496 if (subtype)
497 {
498 *subtype++ = '\0';
499 for (pc = subtype; *pc && !mutt_isspace(*pc) && (*pc != ';'); pc++)
500 ; // do nothing
501
502 *pc = '\0';
503 mutt_str_replace(&b->subtype, subtype);
504 }
505
506 /* Finally, get the major type */
508
509 if (mutt_istr_equal("x-sun-attachment", s))
510 mutt_str_replace(&b->subtype, "x-sun-attachment");
511
512 if (b->type == TYPE_OTHER)
513 {
514 mutt_str_replace(&b->xtype, s);
515 }
516
517 if (!b->subtype)
518 {
519 /* Some older non-MIME mailers (i.e., mailtool, elm) have a content-type
520 * field, so we can attempt to convert the type to Body here. */
521 if (b->type == TYPE_TEXT)
522 {
523 b->subtype = mutt_str_dup("plain");
524 }
525 else if (b->type == TYPE_AUDIO)
526 {
527 b->subtype = mutt_str_dup("basic");
528 }
529 else if (b->type == TYPE_MESSAGE)
530 {
531 b->subtype = mutt_str_dup("rfc822");
532 }
533 else if (b->type == TYPE_OTHER)
534 {
535 char buf[128] = { 0 };
536
538 snprintf(buf, sizeof(buf), "x-%s", s);
539 b->subtype = mutt_str_dup(buf);
540 }
541 else
542 {
543 b->subtype = mutt_str_dup("x-unknown");
544 }
545 }
546
547 /* Default character set for text types. */
548 if (b->type == TYPE_TEXT)
549 {
550 pc = mutt_param_get(&b->parameter, "charset");
551 if (pc)
552 {
553 /* Microsoft Outlook seems to think it is necessary to repeat
554 * charset=, strip it off not to confuse ourselves */
555 if (mutt_istrn_equal(pc, "charset=", sizeof("charset=") - 1))
556 mutt_param_set(&b->parameter, "charset", pc + (sizeof("charset=") - 1));
557 }
558 else
559 {
560 mutt_param_set(&b->parameter, "charset",
562 }
563 }
564}
565
566#ifdef USE_AUTOCRYPT
573static struct AutocryptHeader *parse_autocrypt(struct AutocryptHeader *head, const char *s)
574{
575 struct AutocryptHeader *autocrypt = mutt_autocrypthdr_new();
576 autocrypt->next = head;
577
578 struct ParameterList pl = TAILQ_HEAD_INITIALIZER(pl);
579 parse_parameters(&pl, s, true);
580 if (TAILQ_EMPTY(&pl))
581 {
582 autocrypt->invalid = true;
583 goto cleanup;
584 }
585
586 struct Parameter *p = NULL;
587 TAILQ_FOREACH(p, &pl, entries)
588 {
589 if (mutt_istr_equal(p->attribute, "addr"))
590 {
591 if (autocrypt->addr)
592 {
593 autocrypt->invalid = true;
594 goto cleanup;
595 }
596 autocrypt->addr = p->value;
597 p->value = NULL;
598 }
599 else if (mutt_istr_equal(p->attribute, "prefer-encrypt"))
600 {
601 if (mutt_istr_equal(p->value, "mutual"))
602 autocrypt->prefer_encrypt = true;
603 }
604 else if (mutt_istr_equal(p->attribute, "keydata"))
605 {
606 if (autocrypt->keydata)
607 {
608 autocrypt->invalid = true;
609 goto cleanup;
610 }
611 autocrypt->keydata = p->value;
612 p->value = NULL;
613 }
614 else if (p->attribute && (p->attribute[0] != '_'))
615 {
616 autocrypt->invalid = true;
617 goto cleanup;
618 }
619 }
620
621 /* Checking the addr against From, and for multiple valid headers
622 * occurs later, after all the headers are parsed. */
623 if (!autocrypt->addr || !autocrypt->keydata)
624 autocrypt->invalid = true;
625
626cleanup:
627 mutt_param_free(&pl);
628 return autocrypt;
629}
630#endif
631
638static char *rfc2369_first_value(const char *body, bool mailto_only)
639{
640 struct ListHead links = { 0 };
641 char *value = NULL;
642
643 mutt_rfc2369_parse(body, &links);
644
645 struct ListNode *np = NULL;
646 STAILQ_FOREACH(np, &links, entries)
647 {
648 if (mutt_istr_startswith(np->data, "mailto:"))
649 {
650 FREE(&value);
651 value = mutt_str_dup(np->data);
652 break;
653 }
654
655 if (!mailto_only && !value)
656 value = mutt_str_dup(np->data);
657 }
658
659 mutt_list_free(&links);
660 return value;
661}
662
669size_t mutt_rfc2369_parse(const char *body, struct ListHead *links)
670{
671 if (!body || !links)
672 return 0;
673
674 if (!links->stqh_last)
675 STAILQ_INIT(links);
676
677 size_t count = 0;
678 for (const char *beg = body; (beg = strchr(beg, '<')) != NULL; beg++)
679 {
680 beg++;
681
682 const char *end = strchr(beg, '>');
683 if (!end)
684 break;
685
686 if (end > beg)
687 {
688 mutt_list_insert_tail(links, mutt_strn_dup(beg, end - beg));
689 count++;
690 }
691
692 beg = end;
693 }
694
695 return count;
696}
697
703char *mutt_rfc2369_first_mailto(const char *body)
704{
705 return rfc2369_first_value(body, true);
706}
707
713{
714 if (!headers)
715 return;
716
717 if (!headers->archive.stqh_last)
718 STAILQ_INIT(&headers->archive);
719 if (!headers->help.stqh_last)
720 STAILQ_INIT(&headers->help);
721 if (!headers->owner.stqh_last)
722 STAILQ_INIT(&headers->owner);
723 if (!headers->post.stqh_last)
724 STAILQ_INIT(&headers->post);
725 if (!headers->subscribe.stqh_last)
726 STAILQ_INIT(&headers->subscribe);
727 if (!headers->unsubscribe.stqh_last)
728 STAILQ_INIT(&headers->unsubscribe);
729}
730
736{
737 if (!headers)
738 return;
739
741
742 mutt_list_free(&headers->archive);
743 mutt_list_free(&headers->help);
744 mutt_list_free(&headers->owner);
745 mutt_list_free(&headers->post);
746 mutt_list_free(&headers->subscribe);
747 mutt_list_free(&headers->unsubscribe);
748}
749
755void mutt_rfc2369_read_headers(FILE *fp, struct Rfc2369ListHeaders *headers)
756{
757 if (!fp || !headers)
758 return;
759
762
763 struct Buffer *line = buf_pool_get();
764 while (true)
765 {
766 size_t len = mutt_rfc822_read_line(fp, line);
767 if ((len == 0) || buf_is_empty(line))
768 break;
769
770 const char *lines = buf_string(line);
771 const char *p = strpbrk(lines, ": \t");
772 if (!p || (*p != ':'))
773 {
774 time_t t = 0;
775
776 /* Some MTAs quote the original mbox separator. */
777 if (mutt_str_startswith(lines, ">From "))
778 continue;
779 if (is_from(lines, NULL, 0, &t))
780 continue;
781
782 break;
783 }
784
785 const size_t name_len = p - lines;
786 const char *body = mutt_str_skip_whitespace(p + 1);
787 if (*body == '\0')
788 continue;
789
790 struct ListHead *value = NULL;
791 if ((name_len == 12) && eqi12(lines, "List-Archive"))
792 value = &headers->archive;
793 else if ((name_len == 9) && eqi9(lines, "List-Help"))
794 value = &headers->help;
795 else if ((name_len == 10) && eqi10(lines, "List-Owner"))
796 value = &headers->owner;
797 else if ((name_len == 9) && eqi9(lines, "List-Post"))
798 value = &headers->post;
799 else if ((name_len == 14) && eqi14(lines, "List-Subscribe"))
800 value = &headers->subscribe;
801 else if ((name_len == 16) && eqi16(lines, "List-Unsubscribe"))
802 value = &headers->unsubscribe;
803
804 if (!value)
805 continue;
806
807 mutt_list_free(value);
808 mutt_rfc2369_parse(body, value);
809 }
810 buf_pool_release(&line);
811}
812
829int mutt_rfc822_parse_line(struct Envelope *env, struct Email *e,
830 const char *name, size_t name_len, const char *body,
831 bool user_hdrs, bool weed, bool do_2047)
832{
833 if (!env || !name)
834 return 0;
835
836 bool matched = false;
837
838 switch (name[0] | 0x20)
839 {
840 case 'a':
841 if ((name_len == 13) && eqi12(name + 1, "pparently-to"))
842 {
843 mutt_addrlist_parse(&env->to, body);
844 matched = true;
845 }
846 else if ((name_len == 15) && eqi14(name + 1, "pparently-from"))
847 {
848 mutt_addrlist_parse(&env->from, body);
849 matched = true;
850 }
851#ifdef USE_AUTOCRYPT
852 else if ((name_len == 9) && eqi8(name + 1, "utocrypt"))
853 {
854 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
855 if (c_autocrypt)
856 {
857 env->autocrypt = parse_autocrypt(env->autocrypt, body);
858 matched = true;
859 }
860 }
861 else if ((name_len == 16) && eqi15(name + 1, "utocrypt-gossip"))
862 {
863 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
864 if (c_autocrypt)
865 {
867 matched = true;
868 }
869 }
870#endif
871 break;
872
873 case 'b':
874 if ((name_len == 3) && eqi2(name + 1, "cc"))
875 {
876 mutt_addrlist_parse(&env->bcc, body);
877 matched = true;
878 }
879 break;
880
881 case 'c':
882 if ((name_len == 2) && eqi1(name + 1, "c"))
883 {
884 mutt_addrlist_parse(&env->cc, body);
885 matched = true;
886 }
887 else
888 {
889 if ((name_len >= 12) && eqi8(name, "content-"))
890 {
891 if ((name_len == 12) && eqi4(name + 8, "type"))
892 {
893 if (e)
895 matched = true;
896 }
897 else if ((name_len == 16) && eqi8(name + 8, "language"))
898 {
899 if (e)
901 matched = true;
902 }
903 else if ((name_len == 25) && eqi17(name + 8, "transfer-encoding"))
904 {
905 if (e)
907 matched = true;
908 }
909 else if ((name_len == 14) && eqi8(name + 6, "t-length"))
910 {
911 if (e)
912 {
913 unsigned long len = 0;
914 e->body->length = mutt_str_atoul(body, &len) ? MIN(len, CONTENT_TOO_BIG) : -1;
915 }
916 matched = true;
917 }
918 else if ((name_len == 19) && eqi11(name + 8, "description"))
919 {
920 if (e)
921 {
924 }
925 matched = true;
926 }
927 else if ((name_len == 19) && eqi11(name + 8, "disposition"))
928 {
929 if (e)
931 matched = true;
932 }
933 }
934 }
935 break;
936
937 case 'd':
938 if ((name_len != 4) || !eqi4(name, "date"))
939 break;
940
941 mutt_str_replace(&env->date, body);
942 if (e)
943 {
944 struct Tz tz = { 0 };
945 // the caller will check e->date_sent for -1
946 e->date_sent = mutt_date_parse_date(body, &tz);
947 if (e->date_sent > 0)
948 {
949 e->zhours = tz.zhours;
950 e->zminutes = tz.zminutes;
951 e->zoccident = tz.zoccident;
952 }
953 }
954 matched = true;
955 break;
956
957 case 'e':
958 if ((name_len == 7) && eqi6(name + 1, "xpires") && e)
959 {
960 const time_t expired = mutt_date_parse_date(body, NULL);
961 if ((expired != -1) && (expired < mutt_date_now()))
962 {
963 e->expired = true;
964 }
965 }
966 break;
967
968 case 'f':
969 if ((name_len == 4) && eqi4(name, "from"))
970 {
971 mutt_addrlist_parse(&env->from, body);
972 matched = true;
973 }
974 else if ((name_len == 11) && eqi10(name + 1, "ollowup-to"))
975 {
976 if (!env->followup_to)
977 {
980 }
981 matched = true;
982 }
983 break;
984
985 case 'i':
986 if ((name_len != 11) || !eqi10(name + 1, "n-reply-to"))
987 break;
988
990 char *body2 = mutt_str_dup(body); // Create a mutable copy
992 parse_references(&env->in_reply_to, body2);
993 FREE(&body2);
994 matched = true;
995 break;
996
997 case 'l':
998 if ((name_len == 5) && eqi4(name + 1, "ines"))
999 {
1000 if (e)
1001 {
1002 unsigned int ui = 0; // we don't want a negative number of lines
1003 mutt_str_atoui(body, &ui);
1004 e->lines = ui;
1005 }
1006
1007 matched = true;
1008 }
1009 else if ((name_len == 9) && eqi8(name + 1, "ist-post"))
1010 {
1011 /* RFC2369 */
1012 if (!mutt_strn_equal(mutt_str_skip_whitespace(body), "NO", 2))
1013 {
1014 char *mailto = mutt_rfc2369_first_mailto(body);
1015 if (mailto)
1016 {
1017 FREE(&env->list_post);
1018 env->list_post = mailto;
1019 const bool c_auto_subscribe = cs_subset_bool(NeoMutt->sub, "auto_subscribe");
1020 if (c_auto_subscribe)
1022 }
1023 }
1024 matched = true;
1025 }
1026 else if ((name_len == 14) && eqi13(name + 1, "ist-subscribe"))
1027 {
1028 /* RFC2369 */
1029 char *uri = rfc2369_first_value(body, false);
1030 if (uri)
1031 {
1032 FREE(&env->list_subscribe);
1033 env->list_subscribe = uri;
1034 }
1035 matched = true;
1036 }
1037 else if ((name_len == 16) && eqi15(name + 1, "ist-unsubscribe"))
1038 {
1039 /* RFC2369 */
1040 char *uri = rfc2369_first_value(body, false);
1041 if (uri)
1042 {
1043 FREE(&env->list_unsubscribe);
1044 env->list_unsubscribe = uri;
1045 }
1046 matched = true;
1047 }
1048 break;
1049
1050 case 'm':
1051 if ((name_len == 12) && eqi11(name + 1, "ime-version"))
1052 {
1053 if (e)
1054 e->mime = true;
1055 matched = true;
1056 }
1057 else if ((name_len == 10) && eqi9(name + 1, "essage-id"))
1058 {
1059 /* We add a new "Message-ID:" when building a message */
1060 FREE(&env->message_id);
1061 env->message_id = mutt_extract_message_id(body, NULL);
1062 matched = true;
1063 }
1064 else
1065 {
1066 if ((name_len >= 13) && eqi4(name + 1, "ail-"))
1067 {
1068 if ((name_len == 13) && eqi8(name + 5, "reply-to"))
1069 {
1070 /* override the Reply-To: field */
1072 mutt_addrlist_parse(&env->reply_to, body);
1073 matched = true;
1074 }
1075 else if ((name_len == 16) && eqi11(name + 5, "followup-to"))
1076 {
1078 matched = true;
1079 }
1080 }
1081 }
1082 break;
1083
1084 case 'n':
1085 if ((name_len == 10) && eqi9(name + 1, "ewsgroups"))
1086 {
1087 FREE(&env->newsgroups);
1090 matched = true;
1091 }
1092 break;
1093
1094 case 'o':
1095 /* field 'Organization:' saves only for pager! */
1096 if ((name_len == 12) && eqi11(name + 1, "rganization"))
1097 {
1098 if (!env->organization && !mutt_istr_equal(body, "unknown"))
1099 env->organization = mutt_str_dup(body);
1100 }
1101 break;
1102
1103 case 'r':
1104 if ((name_len == 10) && eqi9(name + 1, "eferences"))
1105 {
1107 parse_references(&env->references, body);
1108 matched = true;
1109 }
1110 else if ((name_len == 8) && eqi8(name, "reply-to"))
1111 {
1112 mutt_addrlist_parse(&env->reply_to, body);
1113 matched = true;
1114 }
1115 else if ((name_len == 11) && eqi10(name + 1, "eturn-path"))
1116 {
1117 mutt_addrlist_parse(&env->return_path, body);
1118 matched = true;
1119 }
1120 else if ((name_len == 8) && eqi8(name, "received"))
1121 {
1122 if (e && (e->received == 0))
1123 {
1124 const char *d = strrchr(body, ';');
1125 if (d)
1126 {
1127 d = mutt_str_skip_email_wsp(d + 1);
1128 // the caller will check e->received for -1
1129 e->received = mutt_date_parse_date(d, NULL);
1130 }
1131 }
1132 }
1133 break;
1134
1135 case 's':
1136 if ((name_len == 7) && eqi6(name + 1, "ubject"))
1137 {
1138 if (!env->subject)
1139 mutt_env_set_subject(env, body);
1140 matched = true;
1141 }
1142 else if ((name_len == 6) && eqi5(name + 1, "ender"))
1143 {
1144 mutt_addrlist_parse(&env->sender, body);
1145 matched = true;
1146 }
1147 else if ((name_len == 6) && eqi5(name + 1, "tatus"))
1148 {
1149 if (e)
1150 {
1151 while (*body)
1152 {
1153 switch (*body)
1154 {
1155 case 'O':
1156 {
1157 e->old = true;
1158 break;
1159 }
1160 case 'R':
1161 e->read = true;
1162 break;
1163 case 'r':
1164 e->replied = true;
1165 break;
1166 default:
1167 break;
1168 }
1169 body++;
1170 }
1171 }
1172 matched = true;
1173 }
1174 else if (e && (name_len == 10) && eqi1(name + 1, "u") &&
1175 (eqi8(name + 2, "persedes") || eqi8(name + 2, "percedes")))
1176 {
1177 FREE(&env->supersedes);
1178 env->supersedes = mutt_str_dup(body);
1179 }
1180 break;
1181
1182 case 't':
1183 if ((name_len == 2) && eqi1(name + 1, "o"))
1184 {
1185 mutt_addrlist_parse(&env->to, body);
1186 matched = true;
1187 }
1188 break;
1189
1190 case 'x':
1191 if ((name_len == 8) && eqi8(name, "x-status"))
1192 {
1193 if (e)
1194 {
1195 while (*body)
1196 {
1197 switch (*body)
1198 {
1199 case 'A':
1200 e->replied = true;
1201 break;
1202 case 'D':
1203 e->deleted = true;
1204 break;
1205 case 'F':
1206 e->flagged = true;
1207 break;
1208 default:
1209 break;
1210 }
1211 body++;
1212 }
1213 }
1214 matched = true;
1215 }
1216 else if ((name_len == 7) && eqi6(name + 1, "-label"))
1217 {
1218 FREE(&env->x_label);
1219 env->x_label = mutt_str_dup(body);
1220 matched = true;
1221 }
1222 else if ((name_len == 12) && eqi11(name + 1, "-comment-to"))
1223 {
1224 if (!env->x_comment_to)
1225 env->x_comment_to = mutt_str_dup(body);
1226 matched = true;
1227 }
1228 else if ((name_len == 4) && eqi4(name, "xref"))
1229 {
1230 if (!env->xref)
1231 env->xref = mutt_str_dup(body);
1232 matched = true;
1233 }
1234 else if ((name_len == 13) && eqi12(name + 1, "-original-to"))
1235 {
1237 matched = true;
1238 }
1239 break;
1240
1241 default:
1242 break;
1243 }
1244
1245 /* Keep track of the user-defined headers */
1246 if (!matched && user_hdrs)
1247 {
1248 const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
1249 char *dup = NULL;
1250 mutt_str_asprintf(&dup, "%s: %s", name, body);
1251
1252 if (!weed || !c_weed || !mutt_matches_ignore(dup))
1253 {
1254 struct ListNode *np = mutt_list_insert_tail(&env->userhdrs, dup);
1255 if (do_2047)
1256 {
1257 rfc2047_decode(&np->data);
1258 }
1259 }
1260 else
1261 {
1262 FREE(&dup);
1263 }
1264 }
1265
1266 return matched;
1267}
1268
1278size_t mutt_rfc822_read_line(FILE *fp, struct Buffer *buf)
1279{
1280 if (!fp || !buf)
1281 return 0;
1282
1283 size_t read = 0;
1284 char line[1024] = { 0 }; /* RFC2822 specifies a maximum line length of 998 */
1285
1286 buf_reset(buf);
1287 while (true)
1288 {
1289 if (!fgets(line, sizeof(line), fp))
1290 {
1291 return 0;
1292 }
1293
1294 const size_t linelen = mutt_str_len(line);
1295 if (linelen == 0)
1296 {
1297 break;
1298 }
1299
1300 if (mutt_str_is_email_wsp(line[0]) && buf_is_empty(buf))
1301 {
1302 read = linelen;
1303 break;
1304 }
1305
1306 read += linelen;
1307
1308 size_t off = linelen - 1;
1309 if (line[off] == '\n')
1310 {
1311 /* We did get a full line: remove trailing space */
1312 do
1313 {
1314 line[off] = '\0';
1315 } while (off && mutt_str_is_email_wsp(line[--off]));
1316
1317 /* check to see if the next line is a continuation line */
1318 int ch = fgetc(fp);
1319 if ((ch != ' ') && (ch != '\t'))
1320 {
1321 /* next line is a separate header field or EOH */
1322 ungetc(ch, fp);
1323 buf_addstr(buf, line);
1324 break;
1325 }
1326 read++;
1327
1328 /* eat tabs and spaces from the beginning of the continuation line */
1329 while (((ch = fgetc(fp)) == ' ') || (ch == '\t'))
1330 {
1331 read++;
1332 }
1333
1334 ungetc(ch, fp);
1335 line[off + 1] = ' '; /* string is still terminated because we removed
1336 at least one whitespace char above */
1337 }
1338
1339 buf_addstr(buf, line);
1340 }
1341
1342 return read;
1343}
1344
1358struct Envelope *mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
1359{
1360 if (!fp)
1361 return NULL;
1362
1363 struct Envelope *env = mutt_env_new();
1364 char *p = NULL;
1365 LOFF_T loc = ftello(fp);
1366 if (loc < 0)
1367 {
1368 mutt_debug(LL_DEBUG1, "ftello: %s (errno %d)\n", strerror(errno), errno);
1369 loc = 0;
1370 }
1371
1372 struct Buffer *line = buf_pool_get();
1373
1374 if (e)
1375 {
1376 if (!e->body)
1377 {
1378 e->body = mutt_body_new();
1379
1380 /* set the defaults from RFC1521 */
1381 e->body->type = TYPE_TEXT;
1382 e->body->subtype = mutt_str_dup("plain");
1383 e->body->encoding = ENC_7BIT;
1384 e->body->length = -1;
1385
1386 /* RFC2183 says this is arbitrary */
1388 }
1389 }
1390
1392 ASSERT(mod_data);
1393
1394 while (true)
1395 {
1396 LOFF_T line_start_loc = loc;
1397 size_t len = mutt_rfc822_read_line(fp, line);
1398 if (buf_is_empty(line))
1399 {
1400 break;
1401 }
1402 loc += len;
1403 const char *lines = buf_string(line);
1404 p = strpbrk(lines, ": \t");
1405 if (!p || (*p != ':'))
1406 {
1407 time_t t = 0;
1408
1409 /* some bogus MTAs will quote the original "From " line */
1410 if (mutt_str_startswith(lines, ">From "))
1411 {
1412 continue; /* just ignore */
1413 }
1414 else if (is_from(lines, NULL, 0, &t))
1415 {
1416 /* MH sometimes has the From_ line in the middle of the header! */
1417 if (e && (e->received == 0))
1418 e->received = t - mutt_date_local_tz(t);
1419 continue;
1420 }
1421
1422 /* We need to seek back to the start of the body. Note that we
1423 * keep track of loc ourselves, since calling ftello() incurs
1424 * a syscall, which can be expensive to do for every single line */
1425 (void) mutt_file_seek(fp, line_start_loc, SEEK_SET);
1426 break; /* end of header */
1427 }
1428 size_t name_len = p - lines;
1429
1430 char buf[1024] = { 0 };
1431 if (mutt_replacelist_match(&mod_data->spam, buf, sizeof(buf), lines))
1432 {
1433 if (!mutt_regexlist_match(&mod_data->no_spam, lines))
1434 {
1435 /* if spam tag already exists, figure out how to amend it */
1436 if ((!buf_is_empty(&env->spam)) && (*buf != '\0'))
1437 {
1438 /* If `$spam_separator` defined, append with separator */
1439 const char *const c_spam_separator = cs_subset_string(NeoMutt->sub, "spam_separator");
1440 if (c_spam_separator)
1441 {
1442 buf_addstr(&env->spam, c_spam_separator);
1443 buf_addstr(&env->spam, buf);
1444 }
1445 else /* overwrite */
1446 {
1447 buf_reset(&env->spam);
1448 buf_addstr(&env->spam, buf);
1449 }
1450 }
1451 else if (buf_is_empty(&env->spam) && (*buf != '\0'))
1452 {
1453 /* spam tag is new, and match expr is non-empty; copy */
1454 buf_addstr(&env->spam, buf);
1455 }
1456 else if (buf_is_empty(&env->spam))
1457 {
1458 /* match expr is empty; plug in null string if no existing tag */
1459 buf_addstr(&env->spam, "");
1460 }
1461
1462 if (!buf_is_empty(&env->spam))
1463 mutt_debug(LL_DEBUG5, "spam = %s\n", env->spam.data);
1464 }
1465 }
1466
1467 *p = '\0';
1468 p = mutt_str_skip_email_wsp(p + 1);
1469 if (*p == '\0')
1470 continue; /* skip empty header fields */
1471
1472 mutt_rfc822_parse_line(env, e, lines, name_len, p, user_hdrs, weed, true);
1473 }
1474
1475 buf_pool_release(&line);
1476
1477 if (e)
1478 {
1479 e->body->hdr_offset = e->offset;
1480 e->body->offset = ftello(fp);
1481
1483
1484 if (e->received < 0)
1485 {
1486 mutt_debug(LL_DEBUG1, "resetting invalid received time to 0\n");
1487 e->received = 0;
1488 }
1489
1490 /* check for missing or invalid date */
1491 if (e->date_sent <= 0)
1492 {
1493 mutt_debug(LL_DEBUG1, "no date found, using received time from msg separator\n");
1494 e->date_sent = e->received;
1495 }
1496
1497#ifdef USE_AUTOCRYPT
1498 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
1499 if (c_autocrypt)
1500 {
1502 /* No sense in taking up memory after the header is processed */
1504 }
1505#endif
1506 }
1507
1508 return env;
1509}
1510
1517struct Body *mutt_read_mime_header(FILE *fp, bool digest)
1518{
1519 if (!fp)
1520 return NULL;
1521
1522 struct Body *b = mutt_body_new();
1523 struct Envelope *env = mutt_env_new();
1524 char *c = NULL;
1525 struct Buffer *buf = buf_pool_get();
1526 bool matched = false;
1527
1528 b->hdr_offset = ftello(fp);
1529
1530 b->encoding = ENC_7BIT; /* default from RFC1521 */
1531 b->type = digest ? TYPE_MESSAGE : TYPE_TEXT;
1533
1534 while (mutt_rfc822_read_line(fp, buf) != 0)
1535 {
1536 const char *line = buf_string(buf);
1537 /* Find the value of the current header */
1538 c = strchr(line, ':');
1539 if (c)
1540 {
1541 *c = '\0';
1542 c = mutt_str_skip_email_wsp(c + 1);
1543 if (*c == '\0')
1544 {
1545 mutt_debug(LL_DEBUG1, "skipping empty header field: %s\n", line);
1546 continue;
1547 }
1548 }
1549 else
1550 {
1551 mutt_debug(LL_DEBUG1, "bogus MIME header: %s\n", line);
1552 break;
1553 }
1554
1555 size_t plen = mutt_istr_startswith(line, "content-");
1556 if (plen != 0)
1557 {
1558 if (mutt_istr_equal("type", line + plen))
1559 {
1561 }
1562 else if (mutt_istr_equal("language", line + plen))
1563 {
1565 }
1566 else if (mutt_istr_equal("transfer-encoding", line + plen))
1567 {
1569 }
1570 else if (mutt_istr_equal("disposition", line + plen))
1571 {
1573 }
1574 else if (mutt_istr_equal("description", line + plen))
1575 {
1578 }
1579 else if (mutt_istr_equal("id", line + plen))
1580 {
1581 // strip <angle braces> from Content-ID: header
1582 char *id = c;
1583 int cid_len = mutt_str_len(c);
1584 if (cid_len > 2)
1585 {
1586 if (id[0] == '<')
1587 {
1588 id++;
1589 cid_len--;
1590 }
1591 if (id[cid_len - 1] == '>')
1592 id[cid_len - 1] = '\0';
1593 }
1595 }
1596 }
1597 else if ((plen = mutt_istr_startswith(line, "x-sun-")))
1598 {
1599 if (mutt_istr_equal("data-type", line + plen))
1600 {
1602 }
1603 else if (mutt_istr_equal("encoding-info", line + plen))
1604 {
1606 }
1607 else if (mutt_istr_equal("content-lines", line + plen))
1608 {
1609 mutt_param_set(&b->parameter, "content-lines", c);
1610 }
1611 else if (mutt_istr_equal("data-description", line + plen))
1612 {
1615 }
1616 }
1617 else
1618 {
1619 if (mutt_rfc822_parse_line(env, NULL, line, strlen(line), c, false, false, false))
1620 {
1621 matched = true;
1622 }
1623 }
1624 }
1625 b->offset = ftello(fp); /* Mark the start of the real data */
1626 if ((b->type == TYPE_TEXT) && !b->subtype)
1627 b->subtype = mutt_str_dup("plain");
1628 else if ((b->type == TYPE_MESSAGE) && !b->subtype)
1629 b->subtype = mutt_str_dup("rfc822");
1630
1631 buf_pool_release(&buf);
1632
1633 if (matched)
1634 {
1635 b->mime_headers = env;
1637 }
1638 else
1639 {
1640 mutt_env_free(&env);
1641 }
1642
1643 return b;
1644}
1645
1653bool mutt_is_message_type(int type, const char *subtype)
1654{
1655 if (type != TYPE_MESSAGE)
1656 return false;
1657
1658 subtype = NONULL(subtype);
1659 return (mutt_istr_equal(subtype, "rfc822") ||
1660 mutt_istr_equal(subtype, "news") || mutt_istr_equal(subtype, "global"));
1661}
1662
1669static void parse_part(FILE *fp, struct Body *b, int *counter)
1670{
1671 if (!fp || !b)
1672 return;
1673
1674 const char *bound = NULL;
1675 static unsigned short recurse_level = 0;
1676
1677 if (recurse_level >= MUTT_MIME_MAX_DEPTH)
1678 {
1679 mutt_debug(LL_DEBUG1, "recurse level too deep. giving up\n");
1680 return;
1681 }
1682 recurse_level++;
1683
1684 switch (b->type)
1685 {
1686 case TYPE_MULTIPART:
1687 if (mutt_istr_equal(b->subtype, "x-sun-attachment"))
1688 bound = "--------";
1689 else
1690 bound = mutt_param_get(&b->parameter, "boundary");
1691
1692 if (!mutt_file_seek(fp, b->offset, SEEK_SET))
1693 {
1694 goto bail;
1695 }
1696 b->parts = parse_multipart(fp, bound, b->offset + b->length,
1697 mutt_istr_equal("digest", b->subtype), counter);
1698 break;
1699
1700 case TYPE_MESSAGE:
1701 if (!b->subtype)
1702 break;
1703
1704 if (!mutt_file_seek(fp, b->offset, SEEK_SET))
1705 {
1706 goto bail;
1707 }
1708 if (mutt_is_message_type(b->type, b->subtype))
1709 b->parts = rfc822_parse_message(fp, b, counter);
1710 else if (mutt_istr_equal(b->subtype, "external-body"))
1711 b->parts = mutt_read_mime_header(fp, 0);
1712 else
1713 goto bail;
1714 break;
1715
1716 default:
1717 goto bail;
1718 }
1719
1720 /* try to recover from parsing error */
1721 if (!b->parts)
1722 {
1723 b->type = TYPE_TEXT;
1724 mutt_str_replace(&b->subtype, "plain");
1725 }
1726bail:
1727 recurse_level--;
1728}
1729
1740static struct Body *parse_multipart(FILE *fp, const char *boundary,
1741 LOFF_T end_off, bool digest, int *counter)
1742{
1743 if (!fp)
1744 return NULL;
1745
1746 if (!boundary)
1747 {
1748 mutt_error(_("multipart message has no boundary parameter"));
1749 return NULL;
1750 }
1751
1752 char buf[1024] = { 0 };
1753 struct Body *head = NULL;
1754 struct Body *last = NULL;
1755 struct Body *new_body = NULL;
1756 bool final = false; /* did we see the ending boundary? */
1757
1758 const size_t blen = mutt_str_len(boundary);
1759 while ((ftello(fp) < end_off) && fgets(buf, sizeof(buf), fp))
1760 {
1761 const size_t len = mutt_str_len(buf);
1762
1763 const size_t crlf = ((len > 1) && (buf[len - 2] == '\r')) ? 1 : 0;
1764
1765 if ((buf[0] == '-') && (buf[1] == '-') && mutt_str_startswith(buf + 2, boundary))
1766 {
1767 if (last)
1768 {
1769 last->length = ftello(fp) - last->offset - len - 1 - crlf;
1770 if (last->parts && (last->parts->length == 0))
1771 last->parts->length = ftello(fp) - last->parts->offset - len - 1 - crlf;
1772 /* if the body is empty, we can end up with a -1 length */
1773 if (last->length < 0)
1774 last->length = 0;
1775 }
1776
1777 if (len > 0)
1778 {
1779 /* Remove any trailing whitespace, up to the length of the boundary */
1780 for (size_t i = len - 1; mutt_isspace(buf[i]) && (i >= (blen + 2)); i--)
1781 buf[i] = '\0';
1782 }
1783
1784 /* Check for the end boundary */
1785 if (mutt_str_equal(buf + blen + 2, "--"))
1786 {
1787 final = true;
1788 break; /* done parsing */
1789 }
1790 else if (buf[2 + blen] == '\0')
1791 {
1792 new_body = mutt_read_mime_header(fp, digest);
1793 if (!new_body)
1794 break;
1795
1796 if (mutt_param_get(&new_body->parameter, "content-lines"))
1797 {
1798 int lines = 0;
1799 mutt_str_atoi(mutt_param_get(&new_body->parameter, "content-lines"), &lines);
1800 for (; lines > 0; lines--)
1801 if ((ftello(fp) >= end_off) || !fgets(buf, sizeof(buf), fp))
1802 break;
1803 }
1804
1805 /* Consistency checking - catch bad attachment end boundaries */
1806 if (new_body->offset > end_off)
1807 {
1808 mutt_body_free(&new_body);
1809 break;
1810 }
1811 if (head)
1812 {
1813 last->next = new_body;
1814 last = new_body;
1815 }
1816 else
1817 {
1818 last = new_body;
1819 head = new_body;
1820 }
1821
1822 /* It seems more intuitive to add the counter increment to
1823 * parse_part(), but we want to stop the case where a multipart
1824 * contains thousands of tiny parts before the memory and data
1825 * structures are allocated. */
1826 if (++(*counter) >= MUTT_MIME_MAX_PARTS)
1827 break;
1828 }
1829 }
1830 }
1831
1832 /* in case of missing end boundary, set the length to something reasonable */
1833 if (last && (last->length == 0) && !final)
1834 last->length = end_off - last->offset;
1835
1836 /* parse recursive MIME parts */
1837 for (last = head; last; last = last->next)
1838 parse_part(fp, last, counter);
1839
1840 return head;
1841}
1842
1852static struct Body *rfc822_parse_message(FILE *fp, struct Body *parent, int *counter)
1853{
1854 if (!fp || !parent)
1855 return NULL;
1856
1857 parent->email = email_new();
1858 parent->email->offset = ftello(fp);
1859 parent->email->env = mutt_rfc822_read_header(fp, parent->email, false, false);
1860 struct Body *msg = parent->email->body;
1861
1862 /* ignore the length given in the content-length since it could be wrong
1863 * and we already have the info to calculate the correct length */
1864 /* if (msg->length == -1) */
1865 msg->length = parent->length - (msg->offset - parent->offset);
1866
1867 /* if body of this message is empty, we can end up with a negative length */
1868 if (msg->length < 0)
1869 msg->length = 0;
1870
1871 parse_part(fp, msg, counter);
1872 return msg;
1873}
1874
1889static bool mailto_header_allowed(const char *s, struct ListHead *h)
1890{
1891 if (!h)
1892 return false;
1893
1894 struct ListNode *np = NULL;
1895 STAILQ_FOREACH(np, h, entries)
1896 {
1897 if ((*np->data == '*') || mutt_istr_equal(s, np->data))
1898 return true;
1899 }
1900 return false;
1901}
1902
1911bool mutt_parse_mailto(struct Envelope *env, char **body, const char *src)
1912{
1913 if (!env || !src)
1914 return false;
1915
1916 struct Url *url = url_parse(src);
1917 if (!url)
1918 return false;
1919
1920 if (url->host)
1921 {
1922 /* this is not a path-only URL */
1923 url_free(&url);
1924 return false;
1925 }
1926
1927 mutt_addrlist_parse(&env->to, url->path);
1928
1930 ASSERT(mod_data);
1931
1932 struct UrlQuery *np = NULL;
1933 STAILQ_FOREACH(np, &url->query_strings, entries)
1934 {
1936 const char *tag = np->name;
1937 char *value = np->value;
1938 /* Determine if this header field is on the allowed list. Since NeoMutt
1939 * interprets some header fields specially (such as
1940 * "Attach: ~/.gnupg/secring.gpg"), care must be taken to ensure that
1941 * only safe fields are allowed.
1942 *
1943 * RFC2368, "4. Unsafe headers"
1944 * The user agent interpreting a mailto URL SHOULD choose not to create
1945 * a message if any of the headers are considered dangerous; it may also
1946 * choose to create a message with only a subset of the headers given in
1947 * the URL. */
1948 if (mailto_header_allowed(tag, &mod_data->mail_to_allow))
1949 {
1950 if (mutt_istr_equal(tag, "body"))
1951 {
1952 if (body)
1953 mutt_str_replace(body, value);
1954 }
1955 else
1956 {
1957 char *scratch = NULL;
1958 size_t taglen = mutt_str_len(tag);
1959
1961 mutt_str_asprintf(&scratch, "%s: %s", tag, value);
1962 scratch[taglen] = 0; /* overwrite the colon as mutt_rfc822_parse_line expects */
1963 value = mutt_str_skip_email_wsp(&scratch[taglen + 1]);
1964 mutt_rfc822_parse_line(env, NULL, scratch, taglen, value, true, false, true);
1965 FREE(&scratch);
1966 }
1967 }
1968 }
1969
1970 /* RFC2047 decode after the RFC822 parsing */
1972
1973 url_free(&url);
1974 return true;
1975}
1976
1982void mutt_parse_part(FILE *fp, struct Body *b)
1983{
1984 int counter = 0;
1985
1986 parse_part(fp, b, &counter);
1987}
1988
1997struct Body *mutt_rfc822_parse_message(FILE *fp, struct Body *b)
1998{
1999 int counter = 0;
2000
2001 return rfc822_parse_message(fp, b, &counter);
2002}
2003
2013struct Body *mutt_parse_multipart(FILE *fp, const char *boundary, LOFF_T end_off, bool digest)
2014{
2015 int counter = 0;
2016
2017 return parse_multipart(fp, boundary, end_off, digest, &counter);
2018}
void mutt_addrlist_clear(struct AddressList *al)
Unlink and free all Address in an AddressList.
Definition address.c:1473
int mutt_addrlist_parse(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition address.c:481
Email Address Handling.
void mutt_auto_subscribe(const char *mailto)
Check if user is subscribed to mailing list.
Definition commands.c:49
Email Aliases.
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:263
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:89
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:298
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition buffer.c:248
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:233
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition buffer.c:577
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition buffer.c:342
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:829
struct Body * mutt_rfc822_parse_message(FILE *fp, struct Body *b)
Parse a Message/RFC822 body.
Definition parse.c:1997
void mutt_parse_part(FILE *fp, struct Body *b)
Parse a MIME part.
Definition parse.c:1982
void mutt_parse_content_type(const char *s, struct Body *b)
Parse a content type.
Definition parse.c:464
size_t mutt_rfc822_read_line(FILE *fp, struct Buffer *buf)
Read a header line from a file.
Definition parse.c:1278
char * mutt_rfc2369_first_mailto(const char *body)
Extract the first mailto: URL from an RFC 2369 list.
Definition parse.c:703
struct Body * mutt_read_mime_header(FILE *fp, bool digest)
Parse a MIME header.
Definition parse.c:1517
static char * extract_message_id(const char *s, size_t *len)
Find a message-id in a string.
Definition parse.c:295
static struct AutocryptHeader * parse_autocrypt(struct AutocryptHeader *head, const char *s)
Parse an Autocrypt header line.
Definition parse.c:573
static void parse_references(struct ListHead *head, const char *s)
Parse references from an email header.
Definition parse.c:324
bool mutt_matches_ignore(const char *s)
Does the string match the ignore list.
Definition parse.c:367
enum ContentType mutt_check_mime_type(const char *s)
Check a MIME type string.
Definition parse.c:380
static struct Body * rfc822_parse_message(FILE *fp, struct Body *parent, int *counter)
Parse a Message/RFC822 body.
Definition parse.c:1852
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
Parses an RFC822 header.
Definition parse.c:1358
static void parse_content_language(const char *s, struct Body *b)
Read the content's language.
Definition parse.c:351
static bool mailto_header_allowed(const char *s, struct ListHead *h)
Is the string in the list.
Definition parse.c:1889
bool mutt_parse_mailto(struct Envelope *env, char **body, const char *src)
Parse a mailto:// url.
Definition parse.c:1911
int mutt_check_encoding(const char *c)
Check the encoding type.
Definition parse.c:436
static void parse_content_disposition(const char *s, struct Body *b)
Parse a content disposition.
Definition parse.c:255
struct Body * mutt_parse_multipart(FILE *fp, const char *boundary, LOFF_T end_off, bool digest)
Parse a multipart structure.
Definition parse.c:2013
size_t mutt_rfc2369_parse(const char *body, struct ListHead *links)
Extract URLs from an RFC 2369 list header.
Definition parse.c:669
void mutt_filter_commandline_header_tag(char *header)
Sanitise characters in a header tag.
Definition parse.c:73
char * mutt_extract_message_id(const char *s, size_t *len)
Find a Message-ID.
Definition parse.c:417
static void parse_part(FILE *fp, struct Body *b, int *counter)
Parse a MIME part.
Definition parse.c:1669
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:1740
static void rfc2369_list_headers_ensure_init(struct Rfc2369ListHeaders *headers)
Initialise RFC 2369 header lists.
Definition parse.c:712
bool mutt_is_message_type(int type, const char *subtype)
Determine if a mime type matches a message or not.
Definition parse.c:1653
void mutt_rfc2369_list_headers_free(struct Rfc2369ListHeaders *headers)
Free RFC 2369 mailing-list header values.
Definition parse.c:735
void mutt_filter_commandline_header_value(char *header)
Sanitise characters in a header value.
Definition parse.c:93
static char * rfc2369_first_value(const char *body, bool mailto_only)
Extract the first useful URL from an RFC 2369 list.
Definition parse.c:638
static void parse_parameters(struct ParameterList *pl, const char *s, bool allow_value_spaces)
Parse a list of Parameters.
Definition parse.c:116
void mutt_rfc2369_read_headers(FILE *fp, struct Rfc2369ListHeaders *headers)
Read RFC 2369 mailing-list headers.
Definition parse.c:755
#define CONTENT_TOO_BIG
Maximum reasonable Content-Length value (1 GiB) to prevent overflow.
Definition parse.c:62
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:210
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:162
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:168
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:126
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:174
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:154
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:192
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:186
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:107
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:148
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:180
static bool eqi16(const char *a, const char b[16])
eqi16 - Compare two 16-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons
Definition eqi.h:204
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:198
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:648
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 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:197
@ 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:452
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:459
time_t mutt_date_parse_date(const char *s, struct Tz *tz_out)
Parse a date string in RFC822 format.
Definition date.c:719
Convenience wrapper for the library headers.
#define _(a)
Definition message.h:28
bool mutt_replacelist_match(struct ReplaceList *rl, char *buf, size_t buflen, const char *str)
Does a string match a pattern?
Definition regex.c:481
bool mutt_regexlist_match(struct RegexList *rl, const char *str)
Does a string match any Regex in the list?
Definition regex.c:201
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:571
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition string.c:678
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:809
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition string.c:614
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:666
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:557
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:503
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:666
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_INIT(head)
Definition queue.h:410
#define STAILQ_FOREACH(var, head, field)
Definition queue.h:390
#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:850
void rfc2047_decode(char **pd)
Decode any RFC2047-encoded header fields.
Definition rfc2047.c:679
RFC2047 MIME extensions encoding / decoding routines.
void rfc2231_decode_parameters(struct ParameterList *pl)
Decode a Parameter list.
Definition rfc2231.c:242
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 ListHead unignore
Header patterns to unignore.
Definition module_data.h:43
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:40
struct ListHead mail_to_allow
Permitted fields in a mailto: url.
Definition module_data.h:38
struct RegexList no_spam
Regexes to identify non-spam emails.
Definition module_data.h:39
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 list URI, preferring mailto.
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 list URI, preferring mailto.
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
Mailing-list actions from RFC 2369 headers.
Definition parse.h:41
struct ListHead owner
List-Owner.
Definition parse.h:44
struct ListHead archive
List-Archive.
Definition parse.h:42
struct ListHead post
List-Post.
Definition parse.h:45
struct ListHead unsubscribe
List-Unsubscribe.
Definition parse.h:47
struct ListHead subscribe
List-Subscribe.
Definition parse.h:46
struct ListHead help
List-Help.
Definition parse.h:43
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:242
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition url.c:124
Parse and identify different URL schemes.