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