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