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