NeoMutt  2021-02-05-666-ge300cd
Teaching an old dog new tricks
DOXYGEN
exec.c
Go to the documentation of this file.
1 
30 #include "config.h"
31 #include <assert.h>
32 #include <stdarg.h>
33 #include <stdbool.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include "private.h"
38 #include "mutt/lib.h"
39 #include "address/lib.h"
40 #include "config/lib.h"
41 #include "email/lib.h"
42 #include "core/lib.h"
43 #include "alias/alias.h" // IWYU pragma: keep
44 #include "alias/gui.h" // IWYU pragma: keep
45 #include "alias/lib.h"
46 #include "mutt.h"
47 #include "lib.h"
48 #include "ncrypt/lib.h"
49 #include "send/lib.h"
50 #include "attachments.h"
51 #include "copy.h"
52 #include "handler.h"
53 #include "maillist.h"
54 #include "muttlib.h"
55 #include "mx.h"
56 #ifndef USE_FMEMOPEN
57 #include <sys/stat.h>
58 #endif
59 
60 static int pattern_exec(struct Pattern *pat, PatternExecFlags flags,
61  struct Mailbox *m, struct Email *e, struct Message *msg,
62  struct PatternCache *cache);
63 
71 static bool patmatch(const struct Pattern *pat, const char *buf)
72 {
73  if (pat->is_multi)
74  return (mutt_list_find(&pat->p.multi_cases, buf) != NULL);
75  if (pat->string_match)
76  return pat->ign_case ? strcasestr(buf, pat->p.str) : strstr(buf, pat->p.str);
77  if (pat->group_match)
78  return mutt_group_match(pat->p.group, buf);
79  return (regexec(pat->p.regex, buf, 0, NULL, 0) == 0);
80 }
81 
86 static void print_crypt_pattern_op_error(int op)
87 {
88  const struct PatternFlags *entry = lookup_op(op);
89  if (entry)
90  {
91  /* L10N: One of the crypt pattern operators: ~g, ~G, ~k, ~V
92  was invoked when NeoMutt was compiled without crypto support.
93  %c is the pattern character, i.e. "g". */
94  mutt_error(_("Pattern operator '~%c' is disabled"), entry->tag);
95  }
96  else
97  {
98  /* L10N: An unknown pattern operator was somehow invoked.
99  This shouldn't be possible unless there is a bug. */
100  mutt_error(_("error: unknown op %d (report this error)"), op);
101  }
102 }
103 
113 static bool msg_search(struct Pattern *pat, struct Mailbox *m, struct Email *e,
114  struct Message *msg)
115 {
116  assert(msg);
117 
118  bool match = false;
119 
120  FILE *fp = NULL;
121  long len = 0;
122 #ifdef USE_FMEMOPEN
123  char *temp = NULL;
124  size_t tempsize = 0;
125 #else
126  struct stat st;
127 #endif
128 
129  const bool needs_head = (pat->op == MUTT_PAT_HEADER) || (pat->op == MUTT_PAT_WHOLE_MSG);
130  const bool needs_body = (pat->op == MUTT_PAT_BODY) || (pat->op == MUTT_PAT_WHOLE_MSG);
131  const bool c_thorough_search =
132  cs_subset_bool(NeoMutt->sub, "thorough_search");
133  if (c_thorough_search)
134  {
135  /* decode the header / body */
136  struct State s = { 0 };
137  s.fp_in = msg->fp;
138  s.flags = MUTT_CHARCONV;
139 #ifdef USE_FMEMOPEN
140  s.fp_out = open_memstream(&temp, &tempsize);
141  if (!s.fp_out)
142  {
143  mutt_perror(_("Error opening 'memory stream'"));
144  return false;
145  }
146 #else
148  if (!s.fp_out)
149  {
150  mutt_perror(_("Can't create temporary file"));
151  return false;
152  }
153 #endif
154 
155  if (needs_head)
156  {
157  mutt_copy_header(msg->fp, e, s.fp_out, CH_FROM | CH_DECODE, NULL, 0);
158  }
159 
160  if (needs_body)
161  {
162  mutt_parse_mime_message(m, e, msg->fp);
163 
164  if ((WithCrypto != 0) && (e->security & SEC_ENCRYPT) &&
166  {
167  if (s.fp_out)
168  {
170 #ifdef USE_FMEMOPEN
171  FREE(&temp);
172 #endif
173  }
174  return false;
175  }
176 
177  fseeko(msg->fp, e->offset, SEEK_SET);
178  mutt_body_handler(e->body, &s);
179  }
180 
181 #ifdef USE_FMEMOPEN
183  len = tempsize;
184 
185  if (tempsize != 0)
186  {
187  fp = fmemopen(temp, tempsize, "r");
188  if (!fp)
189  {
190  mutt_perror(_("Error re-opening 'memory stream'"));
191  FREE(&temp);
192  return false;
193  }
194  }
195  else
196  { /* fmemopen can't handle empty buffers */
197  fp = mutt_file_fopen("/dev/null", "r");
198  if (!fp)
199  {
200  mutt_perror(_("Error opening /dev/null"));
201  return false;
202  }
203  }
204 #else
205  fp = s.fp_out;
206  fflush(fp);
207  fseek(fp, 0, SEEK_SET);
208  fstat(fileno(fp), &st);
209  len = (long) st.st_size;
210 #endif
211  }
212  else
213  {
214  /* raw header / body */
215  fp = msg->fp;
216  if (needs_head)
217  {
218  fseeko(fp, e->offset, SEEK_SET);
219  len = e->body->offset - e->offset;
220  }
221  if (needs_body)
222  {
223  if (pat->op == MUTT_PAT_BODY)
224  {
225  fseeko(fp, e->body->offset, SEEK_SET);
226  }
227  len += e->body->length;
228  }
229  }
230 
231  size_t blen = 256;
232  char *buf = mutt_mem_malloc(blen);
233 
234  /* search the file "fp" */
235  while (len > 0)
236  {
237  if (pat->op == MUTT_PAT_HEADER)
238  {
239  buf = mutt_rfc822_read_line(fp, buf, &blen);
240  if (*buf == '\0')
241  break;
242  }
243  else if (!fgets(buf, blen - 1, fp))
244  break; /* don't loop forever */
245  if (patmatch(pat, buf))
246  {
247  match = true;
248  break;
249  }
250  len -= mutt_str_len(buf);
251  }
252 
253  FREE(&buf);
254 
255  if (c_thorough_search)
256  mutt_file_fclose(&fp);
257 
258 #ifdef USE_FMEMOPEN
259  FREE(&temp);
260 #endif
261 
262  return match;
263 }
264 
275 static bool perform_and(struct PatternList *pat, PatternExecFlags flags,
276  struct Mailbox *m, struct Email *e, struct Message *msg,
277  struct PatternCache *cache)
278 {
279  struct Pattern *p = NULL;
280 
281  SLIST_FOREACH(p, pat, entries)
282  {
283  if (pattern_exec(p, flags, m, e, msg, cache) <= 0)
284  return false;
285  }
286  return true;
287 }
288 
297 static bool perform_alias_and(struct PatternList *pat, PatternExecFlags flags,
298  struct AliasView *av, struct PatternCache *cache)
299 {
300  struct Pattern *p = NULL;
301 
302  SLIST_FOREACH(p, pat, entries)
303  {
304  if (mutt_pattern_alias_exec(p, flags, av, cache) <= 0)
305  return false;
306  }
307  return true;
308 }
309 
320 static int perform_or(struct PatternList *pat, PatternExecFlags flags,
321  struct Mailbox *m, struct Email *e, struct Message *msg,
322  struct PatternCache *cache)
323 {
324  struct Pattern *p = NULL;
325 
326  SLIST_FOREACH(p, pat, entries)
327  {
328  if (pattern_exec(p, flags, m, e, msg, cache) > 0)
329  return true;
330  }
331  return false;
332 }
333 
342 static int perform_alias_or(struct PatternList *pat, PatternExecFlags flags,
343  struct AliasView *av, struct PatternCache *cache)
344 {
345  struct Pattern *p = NULL;
346 
347  SLIST_FOREACH(p, pat, entries)
348  {
349  if (mutt_pattern_alias_exec(p, flags, av, cache) > 0)
350  return true;
351  }
352  return false;
353 }
354 
365 static int match_addrlist(struct Pattern *pat, bool match_personal, int n, ...)
366 {
367  va_list ap;
368 
369  va_start(ap, n);
370  for (; n; n--)
371  {
372  struct AddressList *al = va_arg(ap, struct AddressList *);
373  struct Address *a = NULL;
374  TAILQ_FOREACH(a, al, entries)
375  {
376  if (pat->all_addr ^ ((!pat->is_alias || alias_reverse_lookup(a)) &&
377  ((a->mailbox && patmatch(pat, a->mailbox)) ||
378  (match_personal && a->personal && patmatch(pat, a->personal)))))
379  {
380  va_end(ap);
381  return !pat->all_addr; /* Found match, or non-match if all_addr */
382  }
383  }
384  }
385  va_end(ap);
386  return pat->all_addr; /* No matches, or all matches if all_addr */
387 }
388 
395 static bool match_reference(struct Pattern *pat, struct ListHead *refs)
396 {
397  struct ListNode *np = NULL;
398  STAILQ_FOREACH(np, refs, entries)
399  {
400  if (patmatch(pat, np->data))
401  return true;
402  }
403  return false;
404 }
405 
417 static int mutt_is_predicate_recipient(bool all_addr, struct Envelope *e, addr_predicate_t p)
418 {
419  struct AddressList *als[] = { &e->to, &e->cc };
420  for (size_t i = 0; i < mutt_array_size(als); ++i)
421  {
422  struct AddressList *al = als[i];
423  struct Address *a = NULL;
424  TAILQ_FOREACH(a, al, entries)
425  {
426  if (all_addr ^ p(a))
427  return !all_addr;
428  }
429  }
430  return all_addr;
431 }
432 
441 int mutt_is_subscribed_list_recipient(bool all_addr, struct Envelope *e)
442 {
444 }
445 
454 int mutt_is_list_recipient(bool all_addr, struct Envelope *e)
455 {
456  return mutt_is_predicate_recipient(all_addr, e, &mutt_is_mail_list);
457 }
458 
468 static int match_user(int all_addr, struct AddressList *al1, struct AddressList *al2)
469 {
470  struct Address *a = NULL;
471  if (al1)
472  {
473  TAILQ_FOREACH(a, al1, entries)
474  {
475  if (all_addr ^ mutt_addr_is_user(a))
476  return !all_addr;
477  }
478  }
479 
480  if (al2)
481  {
482  TAILQ_FOREACH(a, al2, entries)
483  {
484  if (all_addr ^ mutt_addr_is_user(a))
485  return !all_addr;
486  }
487  }
488  return all_addr;
489 }
490 
504 static int match_threadcomplete(struct PatternList *pat, PatternExecFlags flags,
505  struct Mailbox *m, struct MuttThread *t,
506  int left, int up, int right, int down)
507 {
508  if (!t)
509  return 0;
510 
511  int a;
512  struct Email *e = t->message;
513  if (e)
514  if (mutt_pattern_exec(SLIST_FIRST(pat), flags, m, e, NULL))
515  return 1;
516 
517  if (up && (a = match_threadcomplete(pat, flags, m, t->parent, 1, 1, 1, 0)))
518  return a;
519  if (right && t->parent && (a = match_threadcomplete(pat, flags, m, t->next, 0, 0, 1, 1)))
520  {
521  return a;
522  }
523  if (left && t->parent && (a = match_threadcomplete(pat, flags, m, t->prev, 1, 0, 0, 1)))
524  {
525  return a;
526  }
527  if (down && (a = match_threadcomplete(pat, flags, m, t->child, 1, 0, 1, 1)))
528  return a;
529  return 0;
530 }
531 
542 static int match_threadparent(struct PatternList *pat, PatternExecFlags flags,
543  struct Mailbox *m, struct MuttThread *t)
544 {
545  if (!t || !t->parent || !t->parent->message)
546  return 0;
547 
548  return mutt_pattern_exec(SLIST_FIRST(pat), flags, m, t->parent->message, NULL);
549 }
550 
561 static int match_threadchildren(struct PatternList *pat, PatternExecFlags flags,
562  struct Mailbox *m, struct MuttThread *t)
563 {
564  if (!t || !t->child)
565  return 0;
566 
567  for (t = t->child; t; t = t->next)
568  if (t->message && mutt_pattern_exec(SLIST_FIRST(pat), flags, m, t->message, NULL))
569  return 1;
570 
571  return 0;
572 }
573 
581 static bool match_content_type(const struct Pattern *pat, struct Body *b)
582 {
583  if (!b)
584  return false;
585 
586  char buf[256];
587  snprintf(buf, sizeof(buf), "%s/%s", TYPE(b), b->subtype);
588 
589  if (patmatch(pat, buf))
590  return true;
591  if (match_content_type(pat, b->parts))
592  return true;
593  if (match_content_type(pat, b->next))
594  return true;
595  return false;
596 }
597 
607 static bool match_mime_content_type(const struct Pattern *pat,
608  struct Mailbox *m, struct Email *e, FILE *fp)
609 {
610  mutt_parse_mime_message(m, e, fp);
611  return match_content_type(pat, e->body);
612 }
613 
620 static bool match_update_dynamic_date(struct Pattern *pat)
621 {
622  struct Buffer *err = mutt_buffer_pool_get();
623 
624  bool rc = eval_date_minmax(pat, pat->p.str, err);
626 
627  return rc;
628 }
629 
637 static void set_pattern_cache_value(int *cache_entry, int value)
638 {
639  *cache_entry = (value != 0) ? 2 : 1;
640 }
641 
648 static int get_pattern_cache_value(int cache_entry)
649 {
650  return cache_entry == 2;
651 }
652 
658 static int is_pattern_cache_set(int cache_entry)
659 {
660  return cache_entry != 0;
661 }
662 
671 static int msg_search_sendmode(struct Email *e, struct Pattern *pat)
672 {
673  bool match = false;
674  char *buf = NULL;
675  size_t blen = 0;
676  FILE *fp = NULL;
677 
678  if ((pat->op == MUTT_PAT_HEADER) || (pat->op == MUTT_PAT_WHOLE_MSG))
679  {
680  struct Buffer *tempfile = mutt_buffer_pool_get();
681  mutt_buffer_mktemp(tempfile);
682  fp = mutt_file_fopen(mutt_buffer_string(tempfile), "w+");
683  if (!fp)
684  {
685  mutt_perror(mutt_buffer_string(tempfile));
686  mutt_buffer_pool_release(&tempfile);
687  return 0;
688  }
689 
691  false, false, NeoMutt->sub);
692  fflush(fp);
693  fseek(fp, 0, 0);
694 
695  while ((buf = mutt_file_read_line(buf, &blen, fp, NULL, MUTT_RL_NO_FLAGS)) != NULL)
696  {
697  if (patmatch(pat, buf) == 0)
698  {
699  match = true;
700  break;
701  }
702  }
703 
704  FREE(&buf);
705  mutt_file_fclose(&fp);
706  unlink(mutt_buffer_string(tempfile));
707  mutt_buffer_pool_release(&tempfile);
708 
709  if (match)
710  return match;
711  }
712 
713  if ((pat->op == MUTT_PAT_BODY) || (pat->op == MUTT_PAT_WHOLE_MSG))
714  {
715  fp = mutt_file_fopen(e->body->filename, "r");
716  if (!fp)
717  {
719  return 0;
720  }
721 
722  while ((buf = mutt_file_read_line(buf, &blen, fp, NULL, MUTT_RL_NO_FLAGS)) != NULL)
723  {
724  if (patmatch(pat, buf) == 0)
725  {
726  match = true;
727  break;
728  }
729  }
730 
731  FREE(&buf);
732  mutt_file_fclose(&fp);
733  }
734 
735  return match;
736 }
737 
745 static bool pattern_needs_msg(const struct Mailbox *m, const struct Pattern *pat)
746 {
747  if ((pat->op == MUTT_PAT_MIMETYPE) || (pat->op == MUTT_PAT_MIMEATTACH))
748  {
749  return true;
750  }
751 
752  if ((pat->op == MUTT_PAT_WHOLE_MSG) || (pat->op == MUTT_PAT_BODY) || (pat->op == MUTT_PAT_HEADER))
753  {
754 #ifdef USE_IMAP
755  return !((m->type == MUTT_IMAP) && pat->string_match);
756 #else
757  return true;
758 #endif
759  }
760 
761  if ((pat->op == MUTT_PAT_AND) || (pat->op == MUTT_PAT_OR))
762  {
763  struct Pattern *p = NULL;
764  SLIST_FOREACH(p, pat->child, entries)
765  {
766  if (pattern_needs_msg(m, p))
767  {
768  return true;
769  }
770  }
771  }
772 
773  return false;
774 }
775 
792 static int pattern_exec(struct Pattern *pat, PatternExecFlags flags,
793  struct Mailbox *m, struct Email *e, struct Message *msg,
794  struct PatternCache *cache)
795 {
796  switch (pat->op)
797  {
798  case MUTT_PAT_AND:
799  return pat->pat_not ^ (perform_and(pat->child, flags, m, e, msg, cache) > 0);
800  case MUTT_PAT_OR:
801  return pat->pat_not ^ (perform_or(pat->child, flags, m, e, msg, cache) > 0);
802  case MUTT_PAT_THREAD:
803  return pat->pat_not ^
804  match_threadcomplete(pat->child, flags, m, e->thread, 1, 1, 1, 1);
805  case MUTT_PAT_PARENT:
806  return pat->pat_not ^ match_threadparent(pat->child, flags, m, e->thread);
807  case MUTT_PAT_CHILDREN:
808  return pat->pat_not ^ match_threadchildren(pat->child, flags, m, e->thread);
809  case MUTT_ALL:
810  return !pat->pat_not;
811  case MUTT_EXPIRED:
812  return pat->pat_not ^ e->expired;
813  case MUTT_SUPERSEDED:
814  return pat->pat_not ^ e->superseded;
815  case MUTT_FLAG:
816  return pat->pat_not ^ e->flagged;
817  case MUTT_TAG:
818  return pat->pat_not ^ e->tagged;
819  case MUTT_NEW:
820  return pat->pat_not ? e->old || e->read : !(e->old || e->read);
821  case MUTT_UNREAD:
822  return pat->pat_not ? e->read : !e->read;
823  case MUTT_REPLIED:
824  return pat->pat_not ^ e->replied;
825  case MUTT_OLD:
826  return pat->pat_not ? (!e->old || e->read) : (e->old && !e->read);
827  case MUTT_READ:
828  return pat->pat_not ^ e->read;
829  case MUTT_DELETED:
830  return pat->pat_not ^ e->deleted;
831  case MUTT_PAT_MESSAGE:
832  return pat->pat_not ^ ((EMSG(e) >= pat->min) && (EMSG(e) <= pat->max));
833  case MUTT_PAT_DATE:
834  if (pat->dynamic)
836  return pat->pat_not ^ (e->date_sent >= pat->min && e->date_sent <= pat->max);
838  if (pat->dynamic)
840  return pat->pat_not ^ (e->received >= pat->min && e->received <= pat->max);
841  case MUTT_PAT_BODY:
842  case MUTT_PAT_HEADER:
843  case MUTT_PAT_WHOLE_MSG:
844  if (pat->sendmode)
845  {
846  if (!e->body || !e->body->filename)
847  return 0;
848  return pat->pat_not ^ msg_search_sendmode(e, pat);
849  }
850  /* m can be NULL in certain cases, such as when replying to a message
851  * from the attachment menu and the user has a reply-hook using "~e".
852  * This is also the case when message scoring. */
853  if (!m)
854  return 0;
855 #ifdef USE_IMAP
856  /* IMAP search sets e->matched at search compile time */
857  if ((m->type == MUTT_IMAP) && pat->string_match)
858  return e->matched;
859 #endif
860  return pat->pat_not ^ msg_search(pat, m, e, msg);
862 #ifdef USE_IMAP
863  if (!m)
864  return 0;
865  if (m->type == MUTT_IMAP)
866  {
867  if (pat->string_match)
868  return e->matched;
869  return 0;
870  }
871  mutt_error(_("error: server custom search only supported with IMAP"));
872  return 0;
873 #else
874  mutt_error(_("error: server custom search only supported with IMAP"));
875  return -1;
876 #endif
877  case MUTT_PAT_SENDER:
878  if (!e->env)
879  return 0;
880  return pat->pat_not ^ match_addrlist(pat, (flags & MUTT_MATCH_FULL_ADDRESS),
881  1, &e->env->sender);
882  case MUTT_PAT_FROM:
883  if (!e->env)
884  return 0;
885  return pat->pat_not ^
886  match_addrlist(pat, (flags & MUTT_MATCH_FULL_ADDRESS), 1, &e->env->from);
887  case MUTT_PAT_TO:
888  if (!e->env)
889  return 0;
890  return pat->pat_not ^
891  match_addrlist(pat, (flags & MUTT_MATCH_FULL_ADDRESS), 1, &e->env->to);
892  case MUTT_PAT_CC:
893  if (!e->env)
894  return 0;
895  return pat->pat_not ^
896  match_addrlist(pat, (flags & MUTT_MATCH_FULL_ADDRESS), 1, &e->env->cc);
897  case MUTT_PAT_SUBJECT:
898  if (!e->env)
899  return 0;
900  return pat->pat_not ^ (e->env->subject && patmatch(pat, e->env->subject));
901  case MUTT_PAT_ID:
903  if (!e->env)
904  return 0;
905  return pat->pat_not ^ (e->env->message_id && patmatch(pat, e->env->message_id));
906  case MUTT_PAT_SCORE:
907  return pat->pat_not ^ (e->score >= pat->min &&
908  (pat->max == MUTT_MAXRANGE || e->score <= pat->max));
909  case MUTT_PAT_SIZE:
910  return pat->pat_not ^ (e->body->length >= pat->min &&
911  (pat->max == MUTT_MAXRANGE || e->body->length <= pat->max));
912  case MUTT_PAT_REFERENCE:
913  if (!e->env)
914  return 0;
915  return pat->pat_not ^ (match_reference(pat, &e->env->references) ||
916  match_reference(pat, &e->env->in_reply_to));
917  case MUTT_PAT_ADDRESS:
918  if (!e->env)
919  return 0;
920  return pat->pat_not ^ match_addrlist(pat, (flags & MUTT_MATCH_FULL_ADDRESS),
921  4, &e->env->from, &e->env->sender,
922  &e->env->to, &e->env->cc);
923  case MUTT_PAT_RECIPIENT:
924  if (!e->env)
925  return 0;
926  return pat->pat_not ^ match_addrlist(pat, (flags & MUTT_MATCH_FULL_ADDRESS),
927  2, &e->env->to, &e->env->cc);
928  case MUTT_PAT_LIST: /* known list, subscribed or not */
929  {
930  if (!e->env)
931  return 0;
932 
933  int result;
934  if (cache)
935  {
936  int *cache_entry = pat->all_addr ? &cache->list_all : &cache->list_one;
937  if (!is_pattern_cache_set(*cache_entry))
938  {
939  set_pattern_cache_value(cache_entry,
941  }
942  result = get_pattern_cache_value(*cache_entry);
943  }
944  else
945  result = mutt_is_list_recipient(pat->all_addr, e->env);
946  return pat->pat_not ^ result;
947  }
949  {
950  if (!e->env)
951  return 0;
952 
953  int result;
954  if (cache)
955  {
956  int *cache_entry = pat->all_addr ? &cache->sub_all : &cache->sub_one;
957  if (!is_pattern_cache_set(*cache_entry))
958  {
960  cache_entry, mutt_is_subscribed_list_recipient(pat->all_addr, e->env));
961  }
962  result = get_pattern_cache_value(*cache_entry);
963  }
964  else
966  return pat->pat_not ^ result;
967  }
969  {
970  if (!e->env)
971  return 0;
972 
973  int result;
974  if (cache)
975  {
976  int *cache_entry = pat->all_addr ? &cache->pers_recip_all : &cache->pers_recip_one;
977  if (!is_pattern_cache_set(*cache_entry))
978  {
980  cache_entry, match_user(pat->all_addr, &e->env->to, &e->env->cc));
981  }
982  result = get_pattern_cache_value(*cache_entry);
983  }
984  else
985  result = match_user(pat->all_addr, &e->env->to, &e->env->cc);
986  return pat->pat_not ^ result;
987  }
989  {
990  if (!e->env)
991  return 0;
992 
993  int result;
994  if (cache)
995  {
996  int *cache_entry = pat->all_addr ? &cache->pers_from_all : &cache->pers_from_one;
997  if (!is_pattern_cache_set(*cache_entry))
998  {
999  set_pattern_cache_value(cache_entry,
1000  match_user(pat->all_addr, &e->env->from, NULL));
1001  }
1002  result = get_pattern_cache_value(*cache_entry);
1003  }
1004  else
1005  result = match_user(pat->all_addr, &e->env->from, NULL);
1006  return pat->pat_not ^ result;
1007  }
1008  case MUTT_PAT_COLLAPSED:
1009  return pat->pat_not ^ (e->collapsed && e->num_hidden > 1);
1010  case MUTT_PAT_CRYPT_SIGN:
1011  if (!WithCrypto)
1012  {
1014  return 0;
1015  }
1016  return pat->pat_not ^ ((e->security & SEC_SIGN) ? 1 : 0);
1018  if (!WithCrypto)
1019  {
1021  return 0;
1022  }
1023  return pat->pat_not ^ ((e->security & SEC_GOODSIGN) ? 1 : 0);
1025  if (!WithCrypto)
1026  {
1028  return 0;
1029  }
1030  return pat->pat_not ^ ((e->security & SEC_ENCRYPT) ? 1 : 0);
1031  case MUTT_PAT_PGP_KEY:
1032  if (!(WithCrypto & APPLICATION_PGP))
1033  {
1035  return 0;
1036  }
1037  return pat->pat_not ^ ((e->security & PGP_KEY) == PGP_KEY);
1038  case MUTT_PAT_XLABEL:
1039  if (!e->env)
1040  return 0;
1041  return pat->pat_not ^ (e->env->x_label && patmatch(pat, e->env->x_label));
1042  case MUTT_PAT_DRIVER_TAGS:
1043  {
1044  char *tags = driver_tags_get(&e->tags);
1045  bool rc = (pat->pat_not ^ (tags && patmatch(pat, tags)));
1046  FREE(&tags);
1047  return rc;
1048  }
1049  case MUTT_PAT_HORMEL:
1050  if (!e->env)
1051  return 0;
1052  return pat->pat_not ^ (e->env->spam.data && patmatch(pat, e->env->spam.data));
1053  case MUTT_PAT_DUPLICATED:
1054  return pat->pat_not ^ (e->thread && e->thread->duplicate_thread);
1055  case MUTT_PAT_MIMEATTACH:
1056  if (!m)
1057  return 0;
1058  {
1059  int count = mutt_count_body_parts(m, e, msg->fp);
1060  return pat->pat_not ^ (count >= pat->min &&
1061  (pat->max == MUTT_MAXRANGE || count <= pat->max));
1062  }
1063  case MUTT_PAT_MIMETYPE:
1064  if (!m)
1065  return 0;
1066  return pat->pat_not ^ match_mime_content_type(pat, m, e, msg->fp);
1067  case MUTT_PAT_UNREFERENCED:
1068  return pat->pat_not ^ (e->thread && !e->thread->child);
1069  case MUTT_PAT_BROKEN:
1070  return pat->pat_not ^ (e->thread && e->thread->fake_thread);
1071 #ifdef USE_NNTP
1072  case MUTT_PAT_NEWSGROUPS:
1073  if (!e->env)
1074  return 0;
1075  return pat->pat_not ^ (e->env->newsgroups && patmatch(pat, e->env->newsgroups));
1076 #endif
1077  }
1078  mutt_error(_("error: unknown op %d (report this error)"), pat->op);
1079  return 0;
1080 }
1081 
1098  struct Mailbox *m, struct Email *e, struct PatternCache *cache)
1099 {
1100  struct Message *msg = pattern_needs_msg(m, pat) ? mx_msg_open(m, e->msgno) : NULL;
1101  const int rc = pattern_exec(pat, flags, m, e, msg, cache);
1102  mx_msg_close(m, &msg);
1103  return rc;
1104 }
1105 
1121  struct AliasView *av, struct PatternCache *cache)
1122 {
1123  switch (pat->op)
1124  {
1125  case MUTT_PAT_FROM: /* alias */
1126  if (!av->alias)
1127  return 0;
1128  return pat->pat_not ^ (av->alias->name && patmatch(pat, av->alias->name));
1129  case MUTT_PAT_CC: /* comment */
1130  if (!av->alias)
1131  return 0;
1132  return pat->pat_not ^ (av->alias->comment && patmatch(pat, av->alias->comment));
1133  case MUTT_PAT_TO: /* alias address list */
1134  if (!av->alias)
1135  return 0;
1136  return pat->pat_not ^ match_addrlist(pat, (flags & MUTT_MATCH_FULL_ADDRESS),
1137  1, &av->alias->addr);
1138  case MUTT_PAT_AND:
1139  return pat->pat_not ^ (perform_alias_and(pat->child, flags, av, cache) > 0);
1140  case MUTT_PAT_OR:
1141  return pat->pat_not ^ (perform_alias_or(pat->child, flags, av, cache) > 0);
1142  }
1143 
1144  return 0;
1145 }
void mutt_parse_mime_message(struct Mailbox *m, struct Email *e, FILE *fp)
Parse a MIME email.
Definition: attachments.c:591
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
Pattern matches message number.
Definition: lib.h:148
struct PatternList * child
Arguments to logical operation.
Definition: lib.h:82
Deleted messages.
Definition: mutt.h:97
static int match_threadparent(struct PatternList *pat, PatternExecFlags flags, struct Mailbox *m, struct MuttThread *t)
Match Pattern against an email&#39;s parent.
Definition: exec.c:542
char * name
Short name.
Definition: alias.h:35
regex_t * regex
Compiled regex, for non-pattern matching.
Definition: lib.h:84
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:74
char * filename
when sending a message, this is the file to which this structure refers
Definition: body.h:46
enum MailboxType type
Mailbox type.
Definition: mailbox.h:105
Pattern matches email&#39;s Message-Id.
Definition: lib.h:141
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
#define WithCrypto
Definition: lib.h:113
Pattern matches email&#39;s body.
Definition: lib.h:143
bool group_match
Check a group of Addresses.
Definition: lib.h:74
The envelope/body of an email.
Definition: email.h:37
static int get_pattern_cache_value(int cache_entry)
Get pattern cache value.
Definition: exec.c:648
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
static bool match_mime_content_type(const struct Pattern *pat, struct Mailbox *m, struct Email *e, FILE *fp)
Match a Pattern against an email&#39;s Content-Type.
Definition: exec.c:607
Message is signed.
Definition: lib.h:158
A postponed Email, just the envelope info.
Definition: header.h:42
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:725
struct Body * body
List of MIME parts.
Definition: email.h:91
#define MUTT_CHARCONV
Do character set conversions.
Definition: state.h:36
Structs that make up an email.
static int msg_search_sendmode(struct Email *e, struct Pattern *pat)
Search in send-mode.
Definition: exec.c:671
#define mutt_error(...)
Definition: logging.h:88
static int match_user(int all_addr, struct AddressList *al1, struct AddressList *al2)
Matches the user&#39;s email Address.
Definition: exec.c:468
Convenience wrapper for the send headers.
int pers_recip_all
^~p
Definition: lib.h:111
struct MuttThread * thread
Thread of Emails.
Definition: email.h:95
Email is on mailing list.
Definition: lib.h:153
#define SEC_ENCRYPT
Email is encrypted.
Definition: lib.h:75
bool mutt_is_mail_list(const struct Address *addr)
Is this the email address of a mailing list? - Implements addr_predicate_t.
Definition: maillist.c:44
Pattern matches &#39;To:&#39; field.
Definition: lib.h:131
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
static void set_pattern_cache_value(int *cache_entry, int value)
Sets a value in the PatternCache cache entry.
Definition: exec.c:637
static bool pattern_needs_msg(const struct Mailbox *m, const struct Pattern *pat)
Check whether a pattern needs a full message.
Definition: exec.c:745
union Pattern::@3 p
String manipulation buffer.
Definition: buffer.h:33
Flagged messages.
Definition: mutt.h:98
#define EMSG(e)
Definition: private.h:117
Pattern matches date received.
Definition: lib.h:137
LOFF_T offset
offset where the actual data begins
Definition: body.h:44
#define CH_FROM
Retain the "From " message separator?
Definition: copy.h:56
#define _(a)
Definition: message.h:28
bool mutt_is_subscribed_list(const struct Address *addr)
Is this the email address of a user-subscribed mailing list? - Implements addr_predicate_t.
Definition: maillist.c:56
struct Body * next
next attachment in the list
Definition: body.h:53
An email address.
Definition: address.h:35
Pattern matches &#39;References:&#39; or &#39;In-Reply-To:&#39; field.
Definition: lib.h:151
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
FILE * fp_out
File to write to.
Definition: state.h:47
char * mailbox
Mailbox and host address.
Definition: address.h:38
bool is_multi
Multiple case (only for ~I pattern now)
Definition: lib.h:79
GUI data wrapping an Alias.
Definition: gui.h:35
char * mutt_file_read_line(char *line, size_t *size, FILE *fp, int *line_num, ReadLineFlags flags)
Read a line from a file.
Definition: file.c:667
bool mutt_group_match(struct Group *g, const char *s)
Does a string match an entry in a Group?
Definition: group.c:325
bool expired
Already expired?
Definition: email.h:52
static int match_threadcomplete(struct PatternList *pat, PatternExecFlags flags, struct Mailbox *m, struct MuttThread *t, int left, int up, int right, int down)
Match a Pattern against an email thread.
Definition: exec.c:504
#define mutt_perror(...)
Definition: logging.h:89
int mutt_is_subscribed_list_recipient(bool all_addr, struct Envelope *e)
Matches subscribed mailing lists.
Definition: exec.c:441
Container for Accounts, Notifications.
Definition: neomutt.h:36
Message is unreferenced in the thread.
Definition: lib.h:139
FILE * fp_in
File to read from.
Definition: state.h:46
Representation of a single alias to an email address.
static int match_addrlist(struct Pattern *pat, bool match_personal, int n,...)
Match a Pattern against an Address list.
Definition: exec.c:365
Messages that have been replied to.
Definition: mutt.h:91
The body of an email.
Definition: body.h:34
A simple (non-regex) pattern.
Definition: lib.h:68
Convenience wrapper for the config headers.
int sub_all
^~u
Definition: lib.h:109
Pattern matches &#39;From:&#39; field.
Definition: lib.h:135
Email Address Handling.
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
static int perform_or(struct PatternList *pat, PatternExecFlags flags, struct Mailbox *m, struct Email *e, struct Message *msg, struct PatternCache *cache)
Perform a logical OR on a set of Patterns.
Definition: exec.c:320
Pattern matches email&#39;s header.
Definition: lib.h:144
Some miscellaneous functions.
int mutt_count_body_parts(struct Mailbox *m, struct Email *e, FILE *fp)
Count the MIME Body parts.
Definition: attachments.c:253
Server-side pattern matches.
Definition: lib.h:163
#define mutt_array_size(x)
Definition: memory.h:33
bool is_alias
Is there an alias for this Address?
Definition: lib.h:76
Message is encrypted.
Definition: lib.h:160
static bool match_update_dynamic_date(struct Pattern *pat)
Update a dynamic date pattern.
Definition: exec.c:620
StateFlags flags
Flags, e.g. MUTT_DISPLAY.
Definition: state.h:49
int mx_msg_close(struct Mailbox *m, struct Message **msg)
Close a message.
Definition: mx.c:1186
bool crypt_valid_passphrase(SecurityFlags flags)
Check that we have a usable passphrase, ask if not.
Definition: crypt.c:135
All messages.
Definition: mutt.h:87
bool tagged
Email is tagged.
Definition: email.h:44
bool read
Email is read.
Definition: email.h:51
bool dynamic
Evaluate date ranges at run time.
Definition: lib.h:77
Message is crypographically verified.
Definition: lib.h:159
struct ListHead in_reply_to
in-reply-to header content
Definition: envelope.h:82
char * message_id
Message ID.
Definition: envelope.h:69
bool all_addr
All Addresses in the list must match.
Definition: lib.h:72
bool(* addr_predicate_t)(const struct Address *a)
Test an Address for some condition.
Definition: address.h:70
bool eval_date_minmax(struct Pattern *pat, const char *s, struct Buffer *err)
Evaluate a date-range pattern against &#39;now&#39;.
Definition: compile.c:499
struct MuttThread * prev
Previous sibling Thread.
Definition: thread.h:48
static int mutt_is_predicate_recipient(bool all_addr, struct Envelope *e, addr_predicate_t p)
Test an Envelopes Addresses using a predicate function.
Definition: exec.c:417
Many unsorted constants and some structs.
API for mailboxes.
bool old
Email is seen, but unread.
Definition: email.h:50
static bool match_content_type(const struct Pattern *pat, struct Body *b)
Match a Pattern against an Attachment&#39;s Content-Type.
Definition: exec.c:581
struct AddressList from
Email&#39;s &#39;From&#39; list.
Definition: envelope.h:57
static int match_threadchildren(struct PatternList *pat, PatternExecFlags flags, struct Mailbox *m, struct MuttThread *t)
Match Pattern against an email&#39;s children.
Definition: exec.c:561
Pattern matches sender.
Definition: lib.h:147
static bool patmatch(const struct Pattern *pat, const char *buf)
Compare a string to a Pattern.
Definition: exec.c:71
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
Pattern matches keyword/label.
Definition: lib.h:162
Email is addressed to the user.
Definition: lib.h:155
struct Envelope * env
Envelope information.
Definition: email.h:90
Convenience wrapper for the core headers.
Email is from the user.
Definition: lib.h:156
struct ListNode * mutt_list_find(const struct ListHead *h, const char *data)
Find a string in a List.
Definition: list.c:102
Pattern matches newsgroup.
Definition: lib.h:168
struct AddressList cc
Email&#39;s &#39;Cc&#39; list.
Definition: envelope.h:59
struct Address * alias_reverse_lookup(const struct Address *addr)
Does the user have an alias for the given address.
Definition: reverse.c:105
bool pat_not
Pattern should be inverted (not)
Definition: lib.h:71
bool string_match
Check a string for a match.
Definition: lib.h:73
bool superseded
Got superseded?
Definition: email.h:53
Email Aliases.
int min
Minimum for range checks.
Definition: lib.h:80
bool ign_case
Ignore case for local string_match searches.
Definition: lib.h:75
Pattern matches any address field.
Definition: lib.h:157
struct TagList tags
For drivers that support server tagging.
Definition: email.h:109
Message is part of a broken thread.
Definition: lib.h:140
Email is on subscribed mailing list.
Definition: lib.h:154
int score
Message score.
Definition: email.h:89
char * subtype
content-type subtype
Definition: body.h:37
time_t date_sent
Time when the message was sent (UTC)
Definition: email.h:82
Old messages.
Definition: mutt.h:90
int pers_from_one
~P
Definition: lib.h:114
struct MuttThread * child
Child of this Thread.
Definition: thread.h:46
bool duplicate_thread
Duplicated Email in Thread.
Definition: thread.h:37
const struct PatternFlags * lookup_op(int op)
Lookup the Pattern Flags for an op.
Definition: flags.c:210
struct Alias * alias
Alias.
Definition: gui.h:44
int pers_recip_one
~p
Definition: lib.h:112
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition: lib.h:87
#define SLIST_FIRST(head)
Definition: queue.h:229
A local copy of an email.
Definition: mxapi.h:41
LOFF_T length
length (in bytes) of attachment
Definition: body.h:45
Pattern matches MIME type.
Definition: lib.h:166
bool fake_thread
Emails grouped by Subject.
Definition: thread.h:36
A mailbox.
Definition: mailbox.h:81
int mutt_is_list_recipient(bool all_addr, struct Envelope *e)
Matches known mailing lists.
Definition: exec.c:454
int mutt_pattern_alias_exec(struct Pattern *pat, PatternExecFlags flags, struct AliasView *av, struct PatternCache *cache)
Match a pattern against an alias.
Definition: exec.c:1120
struct Message::@1 flags
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
Message-Id is among results from an external query.
Definition: lib.h:142
char * driver_tags_get(struct TagList *list)
Get tags.
Definition: tags.c:145
size_t num_hidden
Number of hidden messages in this view (only valid when collapsed is set)
Definition: email.h:75
static int pattern_exec(struct Pattern *pat, PatternExecFlags flags, struct Mailbox *m, struct Email *e, struct Message *msg, struct PatternCache *cache)
Match a pattern against an email header.
Definition: exec.c:792
Superseded messages.
Definition: mutt.h:103
Tagged messages.
Definition: mutt.h:99
char * data
Pointer to data.
Definition: buffer.h:35
#define CH_DECODE
Do RFC2047 header decoding.
Definition: copy.h:54
struct Body * parts
parts of a multipart or message/rfc822
Definition: body.h:54
int list_all
^~l
Definition: lib.h:107
New messages.
Definition: mutt.h:89
Messages that have been read.
Definition: mutt.h:92
Pattern matches number of attachments.
Definition: lib.h:165
#define mutt_file_mkstemp()
Definition: file.h:108
API for encryption/signing of emails.
char * comment
Free-form comment string.
Definition: alias.h:37
static int is_pattern_cache_set(int cache_entry)
Is a given Pattern cached?
Definition: exec.c:658
bool collapsed
Is this message part of a collapsed thread?
Definition: email.h:73
short op
Operation, e.g. MUTT_PAT_SCORE.
Definition: lib.h:70
#define MUTT_MAXRANGE
Definition: private.h:119
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
SecurityFlags security
bit 0-10: flags, bit 11,12: application, bit 13: traditional pgp See: ncrypt/lib.h pgplib...
Definition: email.h:39
static int perform_alias_or(struct PatternList *pat, PatternExecFlags flags, struct AliasView *av, struct PatternCache *cache)
Perform a logical OR on a set of Patterns.
Definition: exec.c:342
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
Handle mailing lists.
LOFF_T offset
Where in the stream does this message begin?
Definition: email.h:84
Expired messages.
Definition: mutt.h:102
static bool perform_and(struct PatternList *pat, PatternExecFlags flags, struct Mailbox *m, struct Email *e, struct Message *msg, struct PatternCache *cache)
Perform a logical AND on a set of Patterns.
Definition: exec.c:275
int pers_from_all
^~P
Definition: lib.h:113
An Email conversation.
Definition: thread.h:34
#define SEC_SIGN
Email is signed.
Definition: lib.h:76
int mutt_pattern_exec(struct Pattern *pat, PatternExecFlags flags, struct Mailbox *m, struct Email *e, struct PatternCache *cache)
Match a pattern against an email header.
Definition: exec.c:1097
char * personal
Real name of address.
Definition: address.h:37
Unread messages.
Definition: mutt.h:93
#define TYPE(body)
Definition: mime.h:89
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:664
int tag
Character used to represent this operation, e.g. &#39;A&#39; for &#39;~A&#39;.
Definition: private.h:51
Pattern matches &#39;Date:&#39; field.
Definition: lib.h:136
static bool msg_search(struct Pattern *pat, struct Mailbox *m, struct Email *e, struct Message *msg)
Search an email.
Definition: exec.c:113
Thread is collapsed.
Definition: lib.h:133
char * data
String.
Definition: list.h:36
char * subject
Email&#39;s subject.
Definition: envelope.h:66
Pattern matches &#39;Cc:&#39; field.
Definition: lib.h:132
Duplicate message.
Definition: lib.h:138
Duplicate the structure of an entire email.
Both patterns must match.
Definition: lib.h:126
#define PGP_KEY
Definition: lib.h:96
Shared constants/structs that are private to libpattern.
static bool perform_alias_and(struct PatternList *pat, PatternExecFlags flags, struct AliasView *av, struct PatternCache *cache)
Perform a logical AND on a set of Patterns.
Definition: exec.c:297
bool flagged
Marked important?
Definition: email.h:43
char * newsgroups
List of newsgroups.
Definition: envelope.h:75
char * str
String, if string_match is set.
Definition: lib.h:86
bool deleted
Email is deleted.
Definition: email.h:45
Either pattern can match.
Definition: lib.h:127
int mutt_copy_header(FILE *fp_in, struct Email *e, FILE *fp_out, CopyHeaderFlags chflags, const char *prefix, int wraplen)
Copy Email header.
Definition: copy.c:413
bool sendmode
Evaluate searches in send-mode.
Definition: lib.h:78
bool replied
Email has been replied to.
Definition: email.h:54
#define MUTT_RL_NO_FLAGS
No flags are set.
Definition: file.h:38
struct Group * group
Address group if group_match is set.
Definition: lib.h:85
static void print_crypt_pattern_op_error(int op)
Print an error for a disabled crypto pattern.
Definition: exec.c:86
Pattern matches parent.
Definition: lib.h:129
FILE * fp
pointer to the message data
Definition: mxapi.h:43
Pattern matches email&#39;s spam score.
Definition: lib.h:145
User is a recipient of the email.
Definition: lib.h:152
static bool match_reference(struct Pattern *pat, struct ListHead *refs)
Match references against a Pattern.
Definition: exec.c:395
#define FREE(x)
Definition: memory.h:40
bool mutt_addr_is_user(const struct Address *addr)
Does the address belong to the user.
Definition: alias.c:562
int max
Maximum for range checks.
Definition: lib.h:81
Keep track when processing files.
Definition: state.h:44
#define SLIST_FOREACH(var, head, field)
Definition: queue.h:231
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 ListHead multi_cases
Multiple strings for ~I pattern.
Definition: lib.h:87
int mutt_body_handler(struct Body *b, struct State *s)
Handler for the Body of an email.
Definition: handler.c:1604
Pattern matches raw email text.
Definition: lib.h:146
Pattern matches a child email.
Definition: lib.h:130
Mapping between user character and internal constant.
Definition: private.h:49
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
Cache commonly-used patterns.
Definition: lib.h:105
struct AddressList addr
List of Addresses the Alias expands to.
Definition: alias.h:36
Pattern matches message tags.
Definition: lib.h:164
Pattern matches email thread.
Definition: lib.h:128
Convenience wrapper for the library headers.
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:589
struct ListHead references
message references (in reverse order)
Definition: envelope.h:81
A List node for strings.
Definition: list.h:34
Miscellaneous email parsing routines.
Decide how to display email content.
&#39;IMAP&#39; Mailbox type
Definition: mailbox.h:53
char * x_label
X-Label.
Definition: envelope.h:72
Shared code for the Alias and Query Dialogs.
#define SEC_GOODSIGN
Email has a valid signature.
Definition: lib.h:77
int list_one
~l
Definition: lib.h:108
#define MUTT_MATCH_FULL_ADDRESS
Match the full address.
Definition: lib.h:95
struct Message * mx_msg_open(struct Mailbox *m, int msgno)
return a stream pointer for a message
Definition: mx.c:1140
Message has PGP key.
Definition: lib.h:161
int mutt_rfc822_write_header(FILE *fp, struct Envelope *env, struct Body *attach, enum MuttWriteHeaderMode mode, bool privacy, bool hide_protected_subject, struct ConfigSubset *sub)
Write out one RFC822 header line.
Definition: header.c:574
time_t received
Time when the message was placed in the mailbox.
Definition: email.h:83
Pattern matches email&#39;s score.
Definition: lib.h:149
uint8_t PatternExecFlags
Flags for mutt_pattern_exec(), e.g. MUTT_MATCH_FULL_ADDRESS.
Definition: lib.h:93
The header of an Email.
Definition: envelope.h:54
Pattern matches email&#39;s size.
Definition: lib.h:150
struct Buffer spam
Spam header.
Definition: envelope.h:80
bool matched
Search matches this Email.
Definition: email.h:68
int msgno
Number displayed to the user.
Definition: email.h:87
int sub_one
~u
Definition: lib.h:110
Pattern matches &#39;Subject:&#39; field.
Definition: lib.h:134
char * mutt_rfc822_read_line(FILE *fp, char *line, size_t *linelen)
Read a header line from a file.
Definition: parse.c:1049