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