NeoMutt  2020-08-07-1-gab41a1
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 "envelope.h"
41 #include "from.h"
42 #include "globals.h"
43 #include "mime.h"
44 #include "mutt_globals.h"
45 #include "parameter.h"
46 #include "rfc2047.h"
47 #include "rfc2231.h"
48 #include "url.h"
49 #ifdef USE_AUTOCRYPT
50 #include "mutt_globals.h"
51 #endif
52 #ifdef USE_AUTOCRYPT
53 #include "autocrypt/lib.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  if (C_AssumedCharset)
170  {
171  // As iso-2022-* has a character of '"' with non-ascii state, ignore it
172  if (*s == 0x1b)
173  {
174  if ((s[1] == '(') && ((s[2] == 'B') || (s[2] == 'J')))
175  state_ascii = true;
176  else
177  state_ascii = false;
178  }
179  }
180  if (state_ascii && (*s == '"'))
181  break;
182  if (*s == '\\')
183  {
184  if (s[1])
185  {
186  s++;
187  /* Quote the next character */
188  mutt_buffer_addch(buf, *s);
189  }
190  }
191  else
192  mutt_buffer_addch(buf, *s);
193  }
194  if (*s)
195  s++; /* skip over the " */
196  }
197  else
198  {
199  for (; *s && *s != ' ' && *s != ';'; s++)
200  mutt_buffer_addch(buf, *s);
201  }
202 
203  p = s;
204  } while (allow_value_spaces && (*s == ' '));
205 
206  /* if the attribute token was missing, 'new' will be NULL */
207  if (pnew)
208  {
209  pnew->value = mutt_buffer_strdup(buf);
210 
211  mutt_debug(LL_DEBUG2, "parse_parameter: '%s' = '%s'\n",
212  pnew->attribute ? pnew->attribute : "", pnew->value ? pnew->value : "");
213 
214  /* Add this parameter to the list */
215  TAILQ_INSERT_HEAD(pl, pnew, entries);
216  }
217  }
218  else
219  {
220  mutt_debug(LL_DEBUG1, "parameter with no value: %s\n", s);
221  s = p;
222  }
223 
224  /* Find the next parameter */
225  if ((*s != ';') && !(s = strchr(s, ';')))
226  break; /* no more parameters */
227 
228  do
229  {
230  /* Move past any leading whitespace. the +1 skips over the semicolon */
231  s = mutt_str_skip_email_wsp(s + 1);
232  } while (*s == ';'); /* skip empty parameters */
233  }
234 
235 bail:
236 
239 }
240 
248 static void parse_content_disposition(const char *s, struct Body *ct)
249 {
250  struct ParameterList pl;
251  TAILQ_INIT(&pl);
252 
253  if (mutt_istr_startswith(s, "inline"))
254  ct->disposition = DISP_INLINE;
255  else if (mutt_istr_startswith(s, "form-data"))
257  else
258  ct->disposition = DISP_ATTACH;
259 
260  /* Check to see if a default filename was given */
261  s = strchr(s, ';');
262  if (s)
263  {
264  s = mutt_str_skip_email_wsp(s + 1);
265  parse_parameters(&pl, s, false);
266  s = mutt_param_get(&pl, "filename");
267  if (s)
268  mutt_str_replace(&ct->filename, s);
269  s = mutt_param_get(&pl, "name");
270  if (s)
271  ct->form_name = mutt_str_dup(s);
272  mutt_param_free(&pl);
273  }
274 }
275 
281 static void parse_references(struct ListHead *head, const char *s)
282 {
283  if (!head)
284  return;
285 
286  char *m = NULL;
287  for (size_t off = 0; (m = mutt_extract_message_id(s, &off)); s += off)
288  {
289  mutt_list_insert_head(head, m);
290  }
291 }
292 
298 static void parse_content_language(const char *s, struct Body *ct)
299 {
300  if (!s || !ct)
301  return;
302 
303  mutt_debug(LL_DEBUG2, "RFC8255 >> Content-Language set to %s\n", s);
304  ct->language = mutt_str_dup(s);
305 }
306 
314 bool mutt_matches_ignore(const char *s)
315 {
316  return mutt_list_match(s, &Ignore) && !mutt_list_match(s, &UnIgnore);
317 }
318 
325 {
326  if (mutt_istr_equal("text", s))
327  return TYPE_TEXT;
328  if (mutt_istr_equal("multipart", s))
329  return TYPE_MULTIPART;
330 #ifdef SUN_ATTACHMENT
331  if (mutt_istr_equal("x-sun-attachment", s))
332  return TYPE_MULTIPART;
333 #endif
334  if (mutt_istr_equal("application", s))
335  return TYPE_APPLICATION;
336  if (mutt_istr_equal("message", s))
337  return TYPE_MESSAGE;
338  if (mutt_istr_equal("image", s))
339  return TYPE_IMAGE;
340  if (mutt_istr_equal("audio", s))
341  return TYPE_AUDIO;
342  if (mutt_istr_equal("video", s))
343  return TYPE_VIDEO;
344  if (mutt_istr_equal("model", s))
345  return TYPE_MODEL;
346  if (mutt_istr_equal("*", s))
347  return TYPE_ANY;
348  if (mutt_istr_equal(".*", s))
349  return TYPE_ANY;
350 
351  return TYPE_OTHER;
352 }
353 
361 char *mutt_extract_message_id(const char *s, size_t *len)
362 {
363  if (!s || (*s == '\0'))
364  return NULL;
365 
366  char *decoded = mutt_str_dup(s);
367  rfc2047_decode(&decoded);
368 
369  char *res = NULL;
370 
371  for (const char *p = decoded, *beg = NULL; *p; p++)
372  {
373  if (*p == '<')
374  {
375  beg = p;
376  continue;
377  }
378 
379  if (beg && (*p == '>'))
380  {
381  if (len)
382  *len = p - decoded + 1;
383  res = mutt_strn_dup(beg, (p + 1) - beg);
384  break;
385  }
386  }
387 
388  FREE(&decoded);
389  return res;
390 }
391 
397 int mutt_check_encoding(const char *c)
398 {
399  if (mutt_istr_startswith(c, "7bit"))
400  return ENC_7BIT;
401  if (mutt_istr_startswith(c, "8bit"))
402  return ENC_8BIT;
403  if (mutt_istr_startswith(c, "binary"))
404  return ENC_BINARY;
405  if (mutt_istr_startswith(c, "quoted-printable"))
406  return ENC_QUOTED_PRINTABLE;
407  if (mutt_istr_startswith(c, "base64"))
408  return ENC_BASE64;
409  if (mutt_istr_startswith(c, "x-uuencode"))
410  return ENC_UUENCODED;
411 #ifdef SUN_ATTACHMENT
412  if (mutt_istr_startswith(c, "uuencode"))
413  return ENC_UUENCODED;
414 #endif
415  return ENC_OTHER;
416 }
417 
425 void mutt_parse_content_type(const char *s, struct Body *ct)
426 {
427  if (!s || !ct)
428  return;
429 
430  FREE(&ct->subtype);
432 
433  /* First extract any existing parameters */
434  char *pc = strchr(s, ';');
435  if (pc)
436  {
437  *pc++ = 0;
438  while (*pc && IS_SPACE(*pc))
439  pc++;
440  parse_parameters(&ct->parameter, pc, false);
441 
442  /* Some pre-RFC1521 gateways still use the "name=filename" convention,
443  * but if a filename has already been set in the content-disposition,
444  * let that take precedence, and don't set it here */
445  pc = mutt_param_get(&ct->parameter, "name");
446  if (pc && !ct->filename)
447  ct->filename = mutt_str_dup(pc);
448 
449 #ifdef SUN_ATTACHMENT
450  /* this is deep and utter perversion */
451  pc = mutt_param_get(&ct->parameter, "conversions");
452  if (pc)
453  ct->encoding = mutt_check_encoding(pc);
454 #endif
455  }
456 
457  /* Now get the subtype */
458  char *subtype = strchr(s, '/');
459  if (subtype)
460  {
461  *subtype++ = '\0';
462  for (pc = subtype; *pc && !IS_SPACE(*pc) && (*pc != ';'); pc++)
463  ; // do nothing
464 
465  *pc = '\0';
466  ct->subtype = mutt_str_dup(subtype);
467  }
468 
469  /* Finally, get the major type */
470  ct->type = mutt_check_mime_type(s);
471 
472 #ifdef SUN_ATTACHMENT
473  if (mutt_istr_equal("x-sun-attachment", s))
474  ct->subtype = mutt_str_dup("x-sun-attachment");
475 #endif
476 
477  if (ct->type == TYPE_OTHER)
478  {
479  mutt_str_replace(&ct->xtype, s);
480  }
481 
482  if (!ct->subtype)
483  {
484  /* Some older non-MIME mailers (i.e., mailtool, elm) have a content-type
485  * field, so we can attempt to convert the type to Body here. */
486  if (ct->type == TYPE_TEXT)
487  ct->subtype = mutt_str_dup("plain");
488  else if (ct->type == TYPE_AUDIO)
489  ct->subtype = mutt_str_dup("basic");
490  else if (ct->type == TYPE_MESSAGE)
491  ct->subtype = mutt_str_dup("rfc822");
492  else if (ct->type == TYPE_OTHER)
493  {
494  char buf[128];
495 
496  ct->type = TYPE_APPLICATION;
497  snprintf(buf, sizeof(buf), "x-%s", s);
498  ct->subtype = mutt_str_dup(buf);
499  }
500  else
501  ct->subtype = mutt_str_dup("x-unknown");
502  }
503 
504  /* Default character set for text types. */
505  if (ct->type == TYPE_TEXT)
506  {
507  pc = mutt_param_get(&ct->parameter, "charset");
508  if (pc)
509  {
510  /* Microsoft Outlook seems to think it is necessary to repeat
511  * charset=, strip it off not to confuse ourselves */
512  if (mutt_istrn_equal(pc, "charset=", sizeof("charset=") - 1))
513  mutt_param_set(&ct->parameter, "charset", pc + (sizeof("charset=") - 1));
514  }
515  else
516  {
517  mutt_param_set(&ct->parameter, "charset",
518  (C_AssumedCharset) ? (const char *) mutt_ch_get_default_charset() :
519  "us-ascii");
520  }
521  }
522 }
523 
524 #ifdef USE_AUTOCRYPT
525 
531 static struct AutocryptHeader *parse_autocrypt(struct AutocryptHeader *head, const char *s)
532 {
533  struct AutocryptHeader *autocrypt = mutt_autocrypthdr_new();
534  autocrypt->next = head;
535 
536  struct ParameterList pl = TAILQ_HEAD_INITIALIZER(pl);
537  parse_parameters(&pl, s, true);
538  if (TAILQ_EMPTY(&pl))
539  {
540  autocrypt->invalid = true;
541  goto cleanup;
542  }
543 
544  struct Parameter *p = NULL;
545  TAILQ_FOREACH(p, &pl, entries)
546  {
547  if (mutt_istr_equal(p->attribute, "addr"))
548  {
549  if (autocrypt->addr)
550  {
551  autocrypt->invalid = true;
552  goto cleanup;
553  }
554  autocrypt->addr = p->value;
555  p->value = NULL;
556  }
557  else if (mutt_istr_equal(p->attribute, "prefer-encrypt"))
558  {
559  if (mutt_istr_equal(p->value, "mutual"))
560  autocrypt->prefer_encrypt = true;
561  }
562  else if (mutt_istr_equal(p->attribute, "keydata"))
563  {
564  if (autocrypt->keydata)
565  {
566  autocrypt->invalid = true;
567  goto cleanup;
568  }
569  autocrypt->keydata = p->value;
570  p->value = NULL;
571  }
572  else if (p->attribute && (p->attribute[0] != '_'))
573  {
574  autocrypt->invalid = true;
575  goto cleanup;
576  }
577  }
578 
579  /* Checking the addr against From, and for multiple valid headers
580  * occurs later, after all the headers are parsed. */
581  if (!autocrypt->addr || !autocrypt->keydata)
582  autocrypt->invalid = true;
583 
584 cleanup:
585  mutt_param_free(&pl);
586  return autocrypt;
587 }
588 #endif
589 
605 int mutt_rfc822_parse_line(struct Envelope *env, struct Email *e, char *line,
606  char *p, bool user_hdrs, bool weed, bool do_2047)
607 {
608  if (!env || !line)
609  return 0;
610 
611  bool matched = false;
612 
613  switch (tolower(line[0]))
614  {
615  case 'a':
616  if (mutt_istr_equal(line + 1, "pparently-to"))
617  {
618  mutt_addrlist_parse(&env->to, p);
619  matched = true;
620  }
621  else if (mutt_istr_equal(line + 1, "pparently-from"))
622  {
623  mutt_addrlist_parse(&env->from, p);
624  matched = true;
625  }
626  break;
627 
628  case 'b':
629  if (mutt_istr_equal(line + 1, "cc"))
630  {
631  mutt_addrlist_parse(&env->bcc, p);
632  matched = true;
633  }
634  break;
635 
636  case 'c':
637  if (mutt_istr_equal(line + 1, "c"))
638  {
639  mutt_addrlist_parse(&env->cc, p);
640  matched = true;
641  }
642  else
643  {
644  size_t plen = mutt_istr_startswith(line + 1, "ontent-");
645  if (plen != 0)
646  {
647  if (mutt_istr_equal(line + 1 + plen, "type"))
648  {
649  if (e)
651  matched = true;
652  }
653  else if (mutt_istr_equal(line + 1 + plen, "language"))
654  {
655  if (e)
657  matched = true;
658  }
659  else if (mutt_istr_equal(line + 1 + plen, "transfer-encoding"))
660  {
661  if (e)
663  matched = true;
664  }
665  else if (mutt_istr_equal(line + 1 + plen, "length"))
666  {
667  if (e)
668  {
669  int rc = mutt_str_atol(p, (long *) &e->body->length);
670  if ((rc < 0) || (e->body->length < 0))
671  e->body->length = -1;
672  if (e->body->length > CONTENT_TOO_BIG)
674  }
675  matched = true;
676  }
677  else if (mutt_istr_equal(line + 1 + plen, "description"))
678  {
679  if (e)
680  {
683  }
684  matched = true;
685  }
686  else if (mutt_istr_equal(line + 1 + plen, "disposition"))
687  {
688  if (e)
690  matched = true;
691  }
692  }
693  }
694  break;
695 
696  case 'd':
697  if (!mutt_istr_equal("ate", line + 1))
698  break;
699 
700  mutt_str_replace(&env->date, p);
701  if (e)
702  {
703  struct Tz tz;
704  e->date_sent = mutt_date_parse_date(p, &tz);
705  if (e->date_sent > 0)
706  {
707  e->zhours = tz.zhours;
708  e->zminutes = tz.zminutes;
709  e->zoccident = tz.zoccident;
710  }
711  }
712  matched = true;
713  break;
714 
715  case 'e':
716  if (mutt_istr_equal("xpires", line + 1) && e &&
717  (mutt_date_parse_date(p, NULL) < mutt_date_epoch()))
718  {
719  e->expired = true;
720  }
721  break;
722 
723  case 'f':
724  if (mutt_istr_equal("rom", line + 1))
725  {
726  mutt_addrlist_parse(&env->from, p);
727  matched = true;
728  }
729 #ifdef USE_NNTP
730  else if (mutt_istr_equal(line + 1, "ollowup-to"))
731  {
732  if (!env->followup_to)
733  {
736  }
737  matched = true;
738  }
739 #endif
740  break;
741 
742  case 'i':
743  if (!mutt_istr_equal(line + 1, "n-reply-to"))
744  break;
745 
747  parse_references(&env->in_reply_to, p);
748  matched = true;
749  break;
750 
751  case 'l':
752  if (mutt_istr_equal(line + 1, "ines"))
753  {
754  if (e)
755  {
756  /* HACK - neomutt has, for a very short time, produced negative
757  * Lines header values. Ignore them. */
758  if ((mutt_str_atoi(p, &e->lines) < 0) || (e->lines < 0))
759  e->lines = 0;
760  }
761 
762  matched = true;
763  }
764  else if (mutt_istr_equal(line + 1, "ist-Post"))
765  {
766  /* RFC2369. FIXME: We should ignore whitespace, but don't. */
767  if (!mutt_strn_equal(p, "NO", 2))
768  {
769  char *beg = NULL, *end = NULL;
770  for (beg = strchr(p, '<'); beg; beg = strchr(end, ','))
771  {
772  beg++;
773  end = strchr(beg, '>');
774  if (!end)
775  break;
776 
777  char *mlist = mutt_strn_dup(beg, end - beg);
778  /* Take the first mailto URL */
779  if (url_check_scheme(mlist) == U_MAILTO)
780  {
781  FREE(&env->list_post);
782  env->list_post = mlist;
783  if (C_AutoSubscribe)
785 
786  break;
787  }
788  FREE(&mlist);
789  }
790  }
791  matched = true;
792  }
793  break;
794 
795  case 'm':
796  if (mutt_istr_equal(line + 1, "ime-version"))
797  {
798  if (e)
799  e->mime = true;
800  matched = true;
801  }
802  else if (mutt_istr_equal(line + 1, "essage-id"))
803  {
804  /* We add a new "Message-ID:" when building a message */
805  FREE(&env->message_id);
806  env->message_id = mutt_extract_message_id(p, NULL);
807  matched = true;
808  }
809  else
810  {
811  size_t plen = mutt_istr_startswith(line + 1, "ail-");
812  if (plen != 0)
813  {
814  if (mutt_istr_equal(line + 1 + plen, "reply-to"))
815  {
816  /* override the Reply-To: field */
818  mutt_addrlist_parse(&env->reply_to, p);
819  matched = true;
820  }
821  else if (mutt_istr_equal(line + 1 + plen, "followup-to"))
822  {
824  matched = true;
825  }
826  }
827  }
828  break;
829 
830 #ifdef USE_NNTP
831  case 'n':
832  if (mutt_istr_equal(line + 1, "ewsgroups"))
833  {
834  FREE(&env->newsgroups);
837  matched = true;
838  }
839  break;
840 #endif
841 
842  case 'o':
843  /* field 'Organization:' saves only for pager! */
844  if (mutt_istr_equal(line + 1, "rganization"))
845  {
846  if (!env->organization && !mutt_istr_equal(p, "unknown"))
847  env->organization = mutt_str_dup(p);
848  }
849  break;
850 
851  case 'r':
852  if (mutt_istr_equal(line + 1, "eferences"))
853  {
854  mutt_list_free(&env->references);
855  parse_references(&env->references, p);
856  matched = true;
857  }
858  else if (mutt_istr_equal(line + 1, "eply-to"))
859  {
860  mutt_addrlist_parse(&env->reply_to, p);
861  matched = true;
862  }
863  else if (mutt_istr_equal(line + 1, "eturn-path"))
864  {
866  matched = true;
867  }
868  else if (mutt_istr_equal(line + 1, "eceived"))
869  {
870  if (e && !e->received)
871  {
872  char *d = strrchr(p, ';');
873  if (d)
874  {
875  d = mutt_str_skip_email_wsp(d + 1);
876  e->received = mutt_date_parse_date(d, NULL);
877  }
878  }
879  }
880  break;
881 
882  case 's':
883  if (mutt_istr_equal(line + 1, "ubject"))
884  {
885  if (!env->subject)
886  env->subject = mutt_str_dup(p);
887  matched = true;
888  }
889  else if (mutt_istr_equal(line + 1, "ender"))
890  {
891  mutt_addrlist_parse(&env->sender, p);
892  matched = true;
893  }
894  else if (mutt_istr_equal(line + 1, "tatus"))
895  {
896  if (e)
897  {
898  while (*p)
899  {
900  switch (*p)
901  {
902  case 'O':
903  e->old = C_MarkOld;
904  break;
905  case 'R':
906  e->read = true;
907  break;
908  case 'r':
909  e->replied = true;
910  break;
911  }
912  p++;
913  }
914  }
915  matched = true;
916  }
917  else if (e && (mutt_istr_equal("upersedes", line + 1) ||
918  mutt_istr_equal("upercedes", line + 1)))
919  {
920  FREE(&env->supersedes);
921  env->supersedes = mutt_str_dup(p);
922  }
923  break;
924 
925  case 't':
926  if (mutt_istr_equal(line + 1, "o"))
927  {
928  mutt_addrlist_parse(&env->to, p);
929  matched = true;
930  }
931 #ifdef USE_AUTOCRYPT
932  else if (mutt_istr_equal(line + 1, "utocrypt"))
933  {
934  if (C_Autocrypt)
935  {
936  env->autocrypt = parse_autocrypt(env->autocrypt, p);
937  matched = true;
938  }
939  }
940  else if (mutt_istr_equal(line + 1, "utocrypt-gossip"))
941  {
942  if (C_Autocrypt)
943  {
945  matched = true;
946  }
947  }
948 #endif
949  break;
950 
951  case 'x':
952  if (mutt_istr_equal(line + 1, "-status"))
953  {
954  if (e)
955  {
956  while (*p)
957  {
958  switch (*p)
959  {
960  case 'A':
961  e->replied = true;
962  break;
963  case 'D':
964  e->deleted = true;
965  break;
966  case 'F':
967  e->flagged = true;
968  break;
969  default:
970  break;
971  }
972  p++;
973  }
974  }
975  matched = true;
976  }
977  else if (mutt_istr_equal(line + 1, "-label"))
978  {
979  FREE(&env->x_label);
980  env->x_label = mutt_str_dup(p);
981  matched = true;
982  }
983 #ifdef USE_NNTP
984  else if (mutt_istr_equal(line + 1, "-comment-to"))
985  {
986  if (!env->x_comment_to)
987  env->x_comment_to = mutt_str_dup(p);
988  matched = true;
989  }
990  else if (mutt_istr_equal(line + 1, "ref"))
991  {
992  if (!env->xref)
993  env->xref = mutt_str_dup(p);
994  matched = true;
995  }
996 #endif
997  else if (mutt_istr_equal(line + 1, "-original-to"))
998  {
1000  matched = true;
1001  }
1002  break;
1003 
1004  default:
1005  break;
1006  }
1007 
1008  /* Keep track of the user-defined headers */
1009  if (!matched && user_hdrs)
1010  {
1011  /* restore the original line */
1012  line[strlen(line)] = ':';
1013 
1014  if (!(weed && C_Weed && mutt_matches_ignore(line)))
1015  {
1016  struct ListNode *np = mutt_list_insert_tail(&env->userhdrs, mutt_str_dup(line));
1017  if (do_2047)
1018  rfc2047_decode(&np->data);
1019  }
1020  }
1021 
1022  return matched;
1023 }
1024 
1036 char *mutt_rfc822_read_line(FILE *fp, char *line, size_t *linelen)
1037 {
1038  if (!fp || !line || !linelen)
1039  return NULL;
1040 
1041  char *buf = line;
1042  int ch;
1043  size_t offset = 0;
1044 
1045  while (true)
1046  {
1047  if (!fgets(buf, *linelen - offset, fp) || /* end of file or */
1048  (IS_SPACE(*line) && !offset)) /* end of headers */
1049  {
1050  *line = '\0';
1051  return line;
1052  }
1053 
1054  const size_t len = mutt_str_len(buf);
1055  if (len == 0)
1056  return line;
1057 
1058  buf += len - 1;
1059  if (*buf == '\n')
1060  {
1061  /* we did get a full line. remove trailing space */
1062  while (IS_SPACE(*buf))
1063  {
1064  *buf-- = '\0'; /* we can't come beyond line's beginning because
1065  * it begins with a non-space */
1066  }
1067 
1068  /* check to see if the next line is a continuation line */
1069  ch = fgetc(fp);
1070  if ((ch != ' ') && (ch != '\t'))
1071  {
1072  ungetc(ch, fp);
1073  return line; /* next line is a separate header field or EOH */
1074  }
1075 
1076  /* eat tabs and spaces from the beginning of the continuation line */
1077  while (((ch = fgetc(fp)) == ' ') || (ch == '\t'))
1078  ; // do nothing
1079 
1080  ungetc(ch, fp);
1081  *++buf = ' '; /* string is still terminated because we removed
1082  at least one whitespace char above */
1083  }
1084 
1085  buf++;
1086  offset = buf - line;
1087  if (*linelen < (offset + 256))
1088  {
1089  /* grow the buffer */
1090  *linelen += 256;
1091  mutt_mem_realloc(&line, *linelen);
1092  buf = line + offset;
1093  }
1094  }
1095  /* not reached */
1096 }
1097 
1111 struct Envelope *mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
1112 {
1113  if (!fp)
1114  return NULL;
1115 
1116  struct Envelope *env = mutt_env_new();
1117  char *p = NULL;
1118  LOFF_T loc;
1119  size_t linelen = 1024;
1120  char *line = mutt_mem_malloc(linelen);
1121  char buf[linelen + 1];
1122 
1123  if (e)
1124  {
1125  if (!e->body)
1126  {
1127  e->body = mutt_body_new();
1128 
1129  /* set the defaults from RFC1521 */
1130  e->body->type = TYPE_TEXT;
1131  e->body->subtype = mutt_str_dup("plain");
1132  e->body->encoding = ENC_7BIT;
1133  e->body->length = -1;
1134 
1135  /* RFC2183 says this is arbitrary */
1137  }
1138  }
1139 
1140  while ((loc = ftello(fp)) != -1)
1141  {
1142  line = mutt_rfc822_read_line(fp, line, &linelen);
1143  if (*line == '\0')
1144  break;
1145  p = strpbrk(line, ": \t");
1146  if (!p || (*p != ':'))
1147  {
1148  char return_path[1024];
1149  time_t t;
1150 
1151  /* some bogus MTAs will quote the original "From " line */
1152  if (mutt_str_startswith(line, ">From "))
1153  continue; /* just ignore */
1154  else if (is_from(line, return_path, sizeof(return_path), &t))
1155  {
1156  /* MH sometimes has the From_ line in the middle of the header! */
1157  if (e && !e->received)
1158  e->received = t - mutt_date_local_tz(t);
1159  continue;
1160  }
1161 
1162  fseeko(fp, loc, SEEK_SET);
1163  break; /* end of header */
1164  }
1165 
1166  *buf = '\0';
1167 
1168  if (mutt_replacelist_match(&SpamList, buf, sizeof(buf), line))
1169  {
1170  if (!mutt_regexlist_match(&NoSpamList, line))
1171  {
1172  /* if spam tag already exists, figure out how to amend it */
1173  if ((!mutt_buffer_is_empty(&env->spam)) && (*buf != '\0'))
1174  {
1175  /* If C_SpamSeparator defined, append with separator */
1176  if (C_SpamSeparator)
1177  {
1179  mutt_buffer_addstr(&env->spam, buf);
1180  }
1181  else /* overwrite */
1182  {
1183  mutt_buffer_reset(&env->spam);
1184  mutt_buffer_addstr(&env->spam, buf);
1185  }
1186  }
1187 
1188  /* spam tag is new, and match expr is non-empty; copy */
1189  else if (mutt_buffer_is_empty(&env->spam) && (*buf != '\0'))
1190  {
1191  mutt_buffer_addstr(&env->spam, buf);
1192  }
1193 
1194  /* match expr is empty; plug in null string if no existing tag */
1195  else if (mutt_buffer_is_empty(&env->spam))
1196  {
1197  mutt_buffer_addstr(&env->spam, "");
1198  }
1199 
1200  if (!mutt_buffer_is_empty(&env->spam))
1201  mutt_debug(LL_DEBUG5, "spam = %s\n", env->spam.data);
1202  }
1203  }
1204 
1205  *p = '\0';
1206  p = mutt_str_skip_email_wsp(p + 1);
1207  if (*p == '\0')
1208  continue; /* skip empty header fields */
1209 
1210  mutt_rfc822_parse_line(env, e, line, p, user_hdrs, weed, true);
1211  }
1212 
1213  FREE(&line);
1214 
1215  if (e)
1216  {
1217  e->body->hdr_offset = e->offset;
1218  e->body->offset = ftello(fp);
1219 
1221 
1222  if (env->subject)
1223  {
1224  regmatch_t pmatch[1];
1225 
1226  if (mutt_regex_capture(C_ReplyRegex, env->subject, 1, pmatch))
1227  {
1228  env->real_subj = env->subject + pmatch[0].rm_eo;
1229  }
1230  else
1231  env->real_subj = env->subject;
1232  }
1233 
1234  if (e->received < 0)
1235  {
1236  mutt_debug(LL_DEBUG1, "resetting invalid received time to 0\n");
1237  e->received = 0;
1238  }
1239 
1240  /* check for missing or invalid date */
1241  if (e->date_sent <= 0)
1242  {
1244  "no date found, using received time from msg separator\n");
1245  e->date_sent = e->received;
1246  }
1247 
1248 #ifdef USE_AUTOCRYPT
1249  if (C_Autocrypt)
1250  {
1252  /* No sense in taking up memory after the header is processed */
1254  }
1255 #endif
1256  }
1257 
1258  return env;
1259 }
1260 
1267 struct Body *mutt_read_mime_header(FILE *fp, bool digest)
1268 {
1269  if (!fp)
1270  return NULL;
1271 
1272  struct Body *p = mutt_body_new();
1273  struct Envelope *env = mutt_env_new();
1274  char *c = NULL;
1275  size_t linelen = 1024;
1276  char *line = mutt_mem_malloc(linelen);
1277 
1278  p->hdr_offset = ftello(fp);
1279 
1280  p->encoding = ENC_7BIT; /* default from RFC1521 */
1281  p->type = digest ? TYPE_MESSAGE : TYPE_TEXT;
1282  p->disposition = DISP_INLINE;
1283 
1284  while (*(line = mutt_rfc822_read_line(fp, line, &linelen)) != 0)
1285  {
1286  /* Find the value of the current header */
1287  c = strchr(line, ':');
1288  if (c)
1289  {
1290  *c = '\0';
1291  c = mutt_str_skip_email_wsp(c + 1);
1292  if (*c == '\0')
1293  {
1294  mutt_debug(LL_DEBUG1, "skipping empty header field: %s\n", line);
1295  continue;
1296  }
1297  }
1298  else
1299  {
1300  mutt_debug(LL_DEBUG1, "bogus MIME header: %s\n", line);
1301  break;
1302  }
1303 
1304  size_t plen = mutt_istr_startswith(line, "content-");
1305  if (plen != 0)
1306  {
1307  if (mutt_istr_equal("type", line + plen))
1309  else if (mutt_istr_equal("language", line + plen))
1310  parse_content_language(c, p);
1311  else if (mutt_istr_equal("transfer-encoding", line + plen))
1312  p->encoding = mutt_check_encoding(c);
1313  else if (mutt_istr_equal("disposition", line + plen))
1315  else if (mutt_istr_equal("description", line + plen))
1316  {
1317  mutt_str_replace(&p->description, c);
1319  }
1320  }
1321 #ifdef SUN_ATTACHMENT
1322  else if ((plen = mutt_istr_startswith(line, "x-sun-")))
1323  {
1324  if (mutt_istr_equal("data-type", line + plen))
1326  else if (mutt_istr_equal("encoding-info", line + plen))
1327  p->encoding = mutt_check_encoding(c);
1328  else if (mutt_istr_equal("content-lines", line + plen))
1329  mutt_param_set(&p->parameter, "content-lines", c);
1330  else if (mutt_istr_equal("data-description", line + plen))
1331  {
1332  mutt_str_replace(&p->description, c);
1334  }
1335  }
1336 #endif
1337  else
1338  {
1339  if (mutt_rfc822_parse_line(env, NULL, line, c, false, false, false))
1340  p->mime_headers = env;
1341  }
1342  }
1343  p->offset = ftello(fp); /* Mark the start of the real data */
1344  if ((p->type == TYPE_TEXT) && !p->subtype)
1345  p->subtype = mutt_str_dup("plain");
1346  else if ((p->type == TYPE_MESSAGE) && !p->subtype)
1347  p->subtype = mutt_str_dup("rfc822");
1348 
1349  FREE(&line);
1350 
1351  if (p->mime_headers)
1353  else
1354  mutt_env_free(&env);
1355 
1356  return p;
1357 }
1358 
1366 bool mutt_is_message_type(int type, const char *subtype)
1367 {
1368  if (type != TYPE_MESSAGE)
1369  return false;
1370 
1371  subtype = NONULL(subtype);
1372  return (mutt_istr_equal(subtype, "rfc822") ||
1373  mutt_istr_equal(subtype, "news") || mutt_istr_equal(subtype, "global"));
1374 }
1375 
1382 static void parse_part(FILE *fp, struct Body *b, int *counter)
1383 {
1384  if (!fp || !b)
1385  return;
1386 
1387  const char *bound = NULL;
1388  static unsigned short recurse_level = 0;
1389 
1390  if (recurse_level >= MUTT_MIME_MAX_DEPTH)
1391  {
1392  mutt_debug(LL_DEBUG1, "recurse level too deep. giving up.\n");
1393  return;
1394  }
1395  recurse_level++;
1396 
1397  switch (b->type)
1398  {
1399  case TYPE_MULTIPART:
1400 #ifdef SUN_ATTACHMENT
1401  if (mutt_istr_equal(b->subtype, "x-sun-attachment"))
1402  bound = "--------";
1403  else
1404 #endif
1405  bound = mutt_param_get(&b->parameter, "boundary");
1406 
1407  fseeko(fp, b->offset, SEEK_SET);
1408  b->parts = parse_multipart(fp, bound, b->offset + b->length,
1409  mutt_istr_equal("digest", b->subtype), counter);
1410  break;
1411 
1412  case TYPE_MESSAGE:
1413  if (!b->subtype)
1414  break;
1415 
1416  fseeko(fp, b->offset, SEEK_SET);
1417  if (mutt_is_message_type(b->type, b->subtype))
1418  b->parts = rfc822_parse_message(fp, b, counter);
1419  else if (mutt_istr_equal(b->subtype, "external-body"))
1420  b->parts = mutt_read_mime_header(fp, 0);
1421  else
1422  goto bail;
1423  break;
1424 
1425  default:
1426  goto bail;
1427  }
1428 
1429  /* try to recover from parsing error */
1430  if (!b->parts)
1431  {
1432  b->type = TYPE_TEXT;
1433  mutt_str_replace(&b->subtype, "plain");
1434  }
1435 bail:
1436  recurse_level--;
1437 }
1438 
1449 static struct Body *parse_multipart(FILE *fp, const char *boundary,
1450  LOFF_T end_off, bool digest, int *counter)
1451 {
1452  if (!fp)
1453  return NULL;
1454 
1455  if (!boundary)
1456  {
1457  mutt_error(_("multipart message has no boundary parameter"));
1458  return NULL;
1459  }
1460 
1461  char buf[1024];
1462  struct Body *head = NULL, *last = NULL, *new_body = NULL;
1463  bool final = false; /* did we see the ending boundary? */
1464 
1465  const size_t blen = mutt_str_len(boundary);
1466  while ((ftello(fp) < end_off) && fgets(buf, sizeof(buf), fp))
1467  {
1468  const size_t len = mutt_str_len(buf);
1469 
1470  const size_t crlf = ((len > 1) && (buf[len - 2] == '\r')) ? 1 : 0;
1471 
1472  if ((buf[0] == '-') && (buf[1] == '-') && mutt_str_startswith(buf + 2, boundary))
1473  {
1474  if (last)
1475  {
1476  last->length = ftello(fp) - last->offset - len - 1 - crlf;
1477  if (last->parts && (last->parts->length == 0))
1478  last->parts->length = ftello(fp) - last->parts->offset - len - 1 - crlf;
1479  /* if the body is empty, we can end up with a -1 length */
1480  if (last->length < 0)
1481  last->length = 0;
1482  }
1483 
1484  if (len > 0)
1485  {
1486  /* Remove any trailing whitespace, up to the length of the boundary */
1487  for (size_t i = len - 1; IS_SPACE(buf[i]) && (i >= (blen + 2)); i--)
1488  buf[i] = '\0';
1489  }
1490 
1491  /* Check for the end boundary */
1492  if (mutt_str_equal(buf + blen + 2, "--"))
1493  {
1494  final = true;
1495  break; /* done parsing */
1496  }
1497  else if (buf[2 + blen] == '\0')
1498  {
1499  new_body = mutt_read_mime_header(fp, digest);
1500 
1501 #ifdef SUN_ATTACHMENT
1502  if (mutt_param_get(&new_body->parameter, "content-lines"))
1503  {
1504  int lines = 0;
1505  if (mutt_str_atoi(
1506  mutt_param_get(&new_body->parameter, "content-lines"), &lines) < 0)
1507  lines = 0;
1508  for (; lines > 0; lines--)
1509  if ((ftello(fp) >= end_off) || !fgets(buf, sizeof(buf), fp))
1510  break;
1511  }
1512 #endif
1513  /* Consistency checking - catch bad attachment end boundaries */
1514  if (new_body->offset > end_off)
1515  {
1516  mutt_body_free(&new_body);
1517  break;
1518  }
1519  if (head)
1520  {
1521  last->next = new_body;
1522  last = new_body;
1523  }
1524  else
1525  {
1526  last = new_body;
1527  head = new_body;
1528  }
1529 
1530  /* It seems more intuitive to add the counter increment to
1531  * parse_part(), but we want to stop the case where a multipart
1532  * contains thousands of tiny parts before the memory and data
1533  * structures are allocated. */
1534  if (++(*counter) >= MUTT_MIME_MAX_PARTS)
1535  break;
1536  }
1537  }
1538  }
1539 
1540  /* in case of missing end boundary, set the length to something reasonable */
1541  if (last && (last->length == 0) && !final)
1542  last->length = end_off - last->offset;
1543 
1544  /* parse recursive MIME parts */
1545  for (last = head; last; last = last->next)
1546  parse_part(fp, last, counter);
1547 
1548  return head;
1549 }
1550 
1560 static struct Body *rfc822_parse_message(FILE *fp, struct Body *parent, int *counter)
1561 {
1562  if (!fp || !parent)
1563  return NULL;
1564 
1565  parent->email = email_new();
1566  parent->email->offset = ftello(fp);
1567  parent->email->env = mutt_rfc822_read_header(fp, parent->email, false, false);
1568  struct Body *msg = parent->email->body;
1569 
1570  /* ignore the length given in the content-length since it could be wrong
1571  * and we already have the info to calculate the correct length */
1572  /* if (msg->length == -1) */
1573  msg->length = parent->length - (msg->offset - parent->offset);
1574 
1575  /* if body of this message is empty, we can end up with a negative length */
1576  if (msg->length < 0)
1577  msg->length = 0;
1578 
1579  parse_part(fp, msg, counter);
1580  return msg;
1581 }
1582 
1591 bool mutt_parse_mailto(struct Envelope *e, char **body, const char *src)
1592 {
1593  if (!e || !src)
1594  return false;
1595 
1596  struct Url *url = url_parse(src);
1597  if (!url)
1598  return false;
1599 
1600  if (url->host)
1601  {
1602  /* this is not a path-only URL */
1603  url_free(&url);
1604  return false;
1605  }
1606 
1607  mutt_addrlist_parse(&e->to, url->path);
1608 
1609  struct UrlQuery *np;
1610  STAILQ_FOREACH(np, &url->query_strings, entries)
1611  {
1612  const char *tag = np->name;
1613  char *value = np->value;
1614  /* Determine if this header field is on the allowed list. Since NeoMutt
1615  * interprets some header fields specially (such as
1616  * "Attach: ~/.gnupg/secring.gpg"), care must be taken to ensure that
1617  * only safe fields are allowed.
1618  *
1619  * RFC2368, "4. Unsafe headers"
1620  * The user agent interpreting a mailto URL SHOULD choose not to create
1621  * a message if any of the headers are considered dangerous; it may also
1622  * choose to create a message with only a subset of the headers given in
1623  * the URL. */
1624  if (mutt_list_match(tag, &MailToAllow))
1625  {
1626  if (mutt_istr_equal(tag, "body"))
1627  {
1628  if (body)
1629  mutt_str_replace(body, value);
1630  }
1631  else
1632  {
1633  char *scratch = NULL;
1634  size_t taglen = mutt_str_len(tag);
1635 
1636  mutt_str_asprintf(&scratch, "%s: %s", tag, value);
1637  scratch[taglen] = 0; /* overwrite the colon as mutt_rfc822_parse_line expects */
1638  value = mutt_str_skip_email_wsp(&scratch[taglen + 1]);
1639  mutt_rfc822_parse_line(e, NULL, scratch, value, true, false, true);
1640  FREE(&scratch);
1641  }
1642  }
1643  }
1644 
1645  /* RFC2047 decode after the RFC822 parsing */
1647 
1648  url_free(&url);
1649  return true;
1650 }
1651 
1657 void mutt_parse_part(FILE *fp, struct Body *b)
1658 {
1659  int counter = 0;
1660 
1661  parse_part(fp, b, &counter);
1662 }
1663 
1672 struct Body *mutt_rfc822_parse_message(FILE *fp, struct Body *parent)
1673 {
1674  int counter = 0;
1675 
1676  return rfc822_parse_message(fp, parent, &counter);
1677 }
1678 
1688 struct Body *mutt_parse_multipart(FILE *fp, const char *boundary, LOFF_T end_off, bool digest)
1689 {
1690  int counter = 0;
1691 
1692  return parse_multipart(fp, boundary, end_off, digest, &counter);
1693 }
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:876
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:314
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
struct RegexList SubscribedLists
List of regexes to match subscribed mailing lists.
Definition: globals.c:52
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:474
char * mutt_str_skip_whitespace(const char *p)
Find the first non-whitespace character in a string.
Definition: string.c:691
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:1382
bool mutt_is_message_type(int type, const char *subtype)
Determine if a mime type matches a message or not.
Definition: parse.c:1366
int mutt_str_atoi(const char *str, int *dst)
Convert ASCII string to an integer.
Definition: string.c:257
bool C_MarkOld
Config: Mark new emails as old when leaving the mailbox.
Definition: globals.c:36
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 AutocryptHeader * autocrypt_gossip
Definition: envelope.h:86
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
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:1449
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:605
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:1468
struct Parameter * mutt_param_new(void)
Create a new Parameter.
Definition: parameter.c:39
char * xref
List of cross-references.
Definition: envelope.h:76
bool C_Autocrypt
Config: Enables the Autocrypt feature.
Definition: config.c:37
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 Regex * C_ReplyRegex
Config: Regex to match message reply subjects like "re: ".
Definition: globals.c:37
static void parse_content_disposition(const char *s, struct Body *ct)
Parse a content disposition.
Definition: parse.c:248
char * mutt_buffer_strdup(struct Buffer *buf)
Copy a Buffer&#39;s string.
Definition: buffer.c:432
struct RegexList UnSubscribedLists
List of regexes to blacklist false matches in SubscribedLists.
Definition: globals.c:49
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
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:375
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:50
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:1688
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:193
void rfc2047_decode_envelope(struct Envelope *env)
Decode the fields of an Envelope.
Definition: rfc2047.c:779
char * value
Query value.
Definition: url.h:58
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.
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
bool C_AutoSubscribe
Config: Automatically check if the user is subscribed to a mailing list.
Definition: globals.c:35
unsigned int disposition
content-disposition, ContentDisposition
Definition: body.h:67
struct HashTable * AutoSubscribeCache
Hash Table of auto-subscribed mailing lists.
Definition: globals.c:48
Email Global Variables.
Email Address Handling.
bool mutt_istrn_equal(const char *a, const char *b, size_t l)
Check for equality of two strings ignoring case (to a maximum), safely.
Definition: string.c:626
struct Body * mutt_body_new(void)
Create a new Body.
Definition: body.c:43
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:1672
bool mutt_parse_mailto(struct Envelope *e, char **body, const char *src)
Parse a mailto:// url.
Definition: parse.c:1591
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:639
enum ContentType mutt_check_mime_type(const char *s)
Check a MIME type string.
Definition: parse.c:324
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:553
bool mime
Has a MIME-Version header?
Definition: email.h:42
struct Envelope * env
Envelope information.
Definition: email.h:90
static struct Body * rfc822_parse_message(FILE *fp, struct Body *parent, int *counter)
parse a Message/RFC822 body
Definition: parse.c:1560
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: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:190
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:45
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:531
static void parse_content_language(const char *s, struct Body *ct)
Read the content&#39;s language.
Definition: parse.c:298
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:888
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:705
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_autocrypt_process_autocrypt_header(struct Email *e, struct Envelope *env)
Parse an Autocrypt email header.
Definition: autocrypt.c:250
bool mutt_str_is_email_wsp(char c)
Is this a whitespace character (for an email header)
Definition: string.c:764
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:397
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:165
#define MUTT_MIME_MAX_DEPTH
Definition: mime.h:69
struct RegexList NoSpamList
List of regexes to whitelist non-spam emails.
Definition: globals.c:43
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
char * mutt_extract_message_id(const char *s, size_t *len)
Find a message-id.
Definition: parse.c:361
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:47
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
char * host
Host.
Definition: url.h:71
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:748
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:177
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:281
char * path
Path.
Definition: url.h:73
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
bool mutt_strn_equal(const char *a, const char *b, size_t l)
Check for equality of two strings (to a maximum), safely.
Definition: string.c:598
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: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:58
struct ReplaceList SpamList
List of regexes and patterns to match spam emails.
Definition: globals.c:44
void mutt_parse_content_type(const char *s, struct Body *ct)
Parse a content type.
Definition: parse.c:425
void mutt_parse_part(FILE *fp, struct Body *b)
Parse a MIME part.
Definition: parse.c:1657
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:636
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:46
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
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:451
#define MUTT_MIME_MAX_PARTS
Definition: mime.h:70
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
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:51
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:440
bool C_Weed
Config: Filter headers when displaying/forwarding/printing/replying.
Definition: globals.c:40
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
Type: &#39;image/*&#39;.
Definition: mime.h:34
struct Email * email_new(void)
Create a new Email.
Definition: email.c:72
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
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:1100
int const char int line
Definition: acutest.h:617
#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:1111
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.
char * C_SpamSeparator
Config: Separator for multiple spam headers.
Definition: globals.c:39
struct Body * mutt_read_mime_header(FILE *fp, bool digest)
Parse a MIME header.
Definition: parse.c:1267
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:1036
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