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