• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

neomutt / neomutt / 12868279274

20 Jan 2025 12:16PM UTC coverage: 48.988% (+0.06%) from 48.925%
12868279274

push

github

flatcap
query: unify NeoMutt -D and -Q

Reduce the code duplication between:

- NeoMutt -D       # Dump config options
- NeoMutt -Q opt   # Query config option

3 of 3 new or added lines in 1 file covered. (100.0%)

288 existing lines in 6 files now uncovered.

8785 of 17933 relevant lines covered (48.99%)

239.65 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/pattern/exec.c
1
/**
2
 * @file
3
 * Execute a Pattern
4
 *
5
 * @authors
6
 * Copyright (C) 2020 Romeu Vieira <romeu.bizz@gmail.com>
7
 * Copyright (C) 2020-2024 Richard Russon <rich@flatcap.org>
8
 * Copyright (C) 2021-2023 Pietro Cerutti <gahr@gahr.ch>
9
 * Copyright (C) 2023 Leon Philman
10
 * Copyright (C) 2024 Dennis Schön <mail@dennis-schoen.de>
11
 *
12
 * @copyright
13
 * This program is free software: you can redistribute it and/or modify it under
14
 * the terms of the GNU General Public License as published by the Free Software
15
 * Foundation, either version 2 of the License, or (at your option) any later
16
 * version.
17
 *
18
 * This program is distributed in the hope that it will be useful, but WITHOUT
19
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
20
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
21
 * details.
22
 *
23
 * You should have received a copy of the GNU General Public License along with
24
 * this program.  If not, see <http://www.gnu.org/licenses/>.
25
 */
26

27
/**
28
 * @page pattern_exec Execute a Pattern
29
 *
30
 * Execute a Pattern
31
 */
32

33
#include "config.h"
34
#include <stdarg.h> // IWYU pragma: keep
35
#include <stdbool.h>
36
#include <stdio.h>
37
#include <string.h>
38
#include <unistd.h>
39
#include "private.h"
40
#include "mutt/lib.h"
41
#include "address/lib.h"
42
#include "config/lib.h"
43
#include "email/lib.h"
44
#include "core/lib.h"
45
#include "alias/alias.h" // IWYU pragma: keep
46
#include "alias/gui.h"   // IWYU pragma: keep
47
#include "alias/lib.h"
48
#include "mutt.h"
49
#include "lib.h"
50
#include "attach/lib.h"
51
#include "ncrypt/lib.h"
52
#include "send/lib.h"
53
#include "copy.h"
54
#include "handler.h"
55
#include "maillist.h"
56
#include "mx.h"
57
#ifndef USE_FMEMOPEN
58
#include <sys/stat.h>
59
#endif
60

61
static bool pattern_exec(struct Pattern *pat, PatternExecFlags flags,
62
                         struct Mailbox *m, struct Email *e,
63
                         struct Message *msg, struct PatternCache *cache);
64

65
/**
66
 * patmatch - Compare a string to a Pattern
67
 * @param pat Pattern to use
68
 * @param buf String to compare
69
 * @retval true  Match
70
 * @retval false No match
71
 */
72
static bool patmatch(const struct Pattern *pat, const char *buf)
×
73
{
74
  if (pat->is_multi)
×
75
    return (mutt_list_find(&pat->p.multi_cases, buf) != NULL);
×
76
  if (pat->string_match)
×
77
    return pat->ign_case ? mutt_istr_find(buf, pat->p.str) : strstr(buf, pat->p.str);
×
78
  if (pat->group_match)
×
79
    return mutt_group_match(pat->p.group, buf);
×
80
  return (regexec(pat->p.regex, buf, 0, NULL, 0) == 0);
×
81
}
82

83
/**
84
 * print_crypt_pattern_op_error - Print an error for a disabled crypto pattern
85
 * @param op Operation, e.g. #MUTT_PAT_CRYPT_SIGN
86
 */
87
static void print_crypt_pattern_op_error(int op)
88
{
89
  const struct PatternFlags *entry = lookup_op(op);
90
  if (entry)
91
  {
92
    /* L10N: One of the crypt pattern operators: ~g, ~G, ~k, ~V
93
       was invoked when NeoMutt was compiled without crypto support.
94
       %c is the pattern character, i.e. "g".  */
95
    mutt_error(_("Pattern operator '~%c' is disabled"), entry->tag);
96
  }
97
  else
98
  {
99
    /* L10N: An unknown pattern operator was somehow invoked.
100
       This shouldn't be possible unless there is a bug.  */
101
    mutt_error(_("error: unknown op %d (report this error)"), op);
102
  }
103
}
104

105
/**
106
 * msg_search - Search an email
107
 * @param pat   Pattern to find
108
 * @param e   Email
109
 * @param msg Message
110
 * @retval true Pattern found
111
 * @retval false Error or pattern not found
112
 */
113
static bool msg_search(struct Pattern *pat, struct Email *e, struct Message *msg)
×
114
{
115
  ASSERT(msg);
×
116

117
  bool match = false;
118

119
  FILE *fp = NULL;
×
120
  long len = 0;
121
#ifdef USE_FMEMOPEN
122
  char *temp = NULL;
123
  size_t tempsize = 0;
124
#else
125
  struct stat st = { 0 };
×
126
#endif
127

128
  const bool needs_head = (pat->op == MUTT_PAT_HEADER) || (pat->op == MUTT_PAT_WHOLE_MSG);
×
129
  const bool needs_body = (pat->op == MUTT_PAT_BODY) || (pat->op == MUTT_PAT_WHOLE_MSG);
×
130
  const bool c_thorough_search = cs_subset_bool(NeoMutt->sub, "thorough_search");
×
131
  if (c_thorough_search)
×
132
  {
133
    /* decode the header / body */
134
    struct State state = { 0 };
×
135
    state.fp_in = msg->fp;
×
136
    state.flags = STATE_CHARCONV;
×
137
#ifdef USE_FMEMOPEN
138
    state.fp_out = open_memstream(&temp, &tempsize);
139
    if (!state.fp_out)
140
    {
141
      mutt_perror(_("Error opening 'memory stream'"));
142
      return false;
143
    }
144
#else
145
    state.fp_out = mutt_file_mkstemp();
×
146
    if (!state.fp_out)
×
147
    {
148
      mutt_perror(_("Can't create temporary file"));
×
149
      return false;
×
150
    }
151
#endif
152

153
    if (needs_head)
×
154
    {
155
      mutt_copy_header(msg->fp, e, state.fp_out, CH_FROM | CH_DECODE, NULL, 0);
×
156
    }
157

158
    if (needs_body)
×
159
    {
160
      mutt_parse_mime_message(e, msg->fp);
×
161

162
      if ((WithCrypto != 0) && (e->security & SEC_ENCRYPT) &&
×
163
          !crypt_valid_passphrase(e->security))
×
164
      {
165
        if (state.fp_out)
×
166
        {
167
          mutt_file_fclose(&state.fp_out);
×
168
#ifdef USE_FMEMOPEN
169
          FREE(&temp);
170
#endif
171
        }
172
        return false;
×
173
      }
174

175
      if (!mutt_file_seek(msg->fp, e->offset, SEEK_SET))
×
176
      {
177
#ifdef USE_FMEMOPEN
178
        FREE(&temp);
179
#endif
UNCOV
180
        mutt_file_fclose(&state.fp_out);
×
UNCOV
181
        return false;
×
182
      }
UNCOV
183
      mutt_body_handler(e->body, &state);
×
184
    }
185

186
#ifdef USE_FMEMOPEN
187
    mutt_file_fclose(&state.fp_out);
188
    len = tempsize;
189

190
    if (tempsize != 0)
191
    {
192
      fp = fmemopen(temp, tempsize, "r");
193
      if (!fp)
194
      {
195
        mutt_perror(_("Error re-opening 'memory stream'"));
196
        FREE(&temp);
197
        return false;
198
      }
199
    }
200
    else
201
    { /* fmemopen can't handle empty buffers */
202
      fp = mutt_file_fopen("/dev/null", "r");
203
      if (!fp)
204
      {
205
        mutt_perror(_("Error opening /dev/null"));
206
        FREE(&temp);
207
        return false;
208
      }
209
    }
210
#else
211
    fp = state.fp_out;
×
212
    fflush(fp);
×
UNCOV
213
    if (!mutt_file_seek(fp, 0, SEEK_SET) || fstat(fileno(fp), &st))
×
214
    {
215
      mutt_perror(_("Error checking length of temporary file"));
×
216
      mutt_file_fclose(&fp);
×
UNCOV
217
      return false;
×
218
    }
UNCOV
219
    len = (long) st.st_size;
×
220
#endif
221
  }
222
  else
223
  {
224
    /* raw header / body */
225
    fp = msg->fp;
×
UNCOV
226
    if (needs_head)
×
227
    {
UNCOV
228
      if (!mutt_file_seek(fp, e->offset, SEEK_SET))
×
229
      {
230
        return false;
231
      }
UNCOV
232
      len = e->body->offset - e->offset;
×
233
    }
UNCOV
234
    if (needs_body)
×
235
    {
UNCOV
236
      if (pat->op == MUTT_PAT_BODY)
×
237
      {
UNCOV
238
        if (!mutt_file_seek(fp, e->body->offset, SEEK_SET))
×
239
        {
240
          return false;
241
        }
242
      }
UNCOV
243
      len += e->body->length;
×
244
    }
245
  }
246

247
  /* search the file "fp" */
UNCOV
248
  if (pat->op == MUTT_PAT_HEADER)
×
249
  {
250
    struct Buffer *buf = buf_pool_get();
×
UNCOV
251
    while (len > 0)
×
252
    {
UNCOV
253
      if (mutt_rfc822_read_line(fp, buf) == 0)
×
254
      {
255
        break;
256
      }
257
      len -= buf_len(buf);
×
UNCOV
258
      if (patmatch(pat, buf_string(buf)))
×
259
      {
260
        match = true;
261
        break;
262
      }
263
    }
UNCOV
264
    buf_pool_release(&buf);
×
265
  }
266
  else
267
  {
268
    char buf[1024] = { 0 };
×
UNCOV
269
    while (len > 0)
×
270
    {
UNCOV
271
      if (!fgets(buf, sizeof(buf), fp))
×
272
      {
273
        break; /* don't loop forever */
274
      }
275
      len -= mutt_str_len(buf);
×
UNCOV
276
      if (patmatch(pat, buf))
×
277
      {
278
        match = true;
279
        break;
280
      }
281
    }
282
  }
283

284
  if (c_thorough_search)
×
UNCOV
285
    mutt_file_fclose(&fp);
×
286

287
#ifdef USE_FMEMOPEN
288
  FREE(&temp);
289
#endif
290

291
  return match;
292
}
293

294
/**
295
 * perform_and - Perform a logical AND on a set of Patterns
296
 * @param pat   Patterns to test
297
 * @param flags Optional flags, e.g. #MUTT_MATCH_FULL_ADDRESS
298
 * @param m   Mailbox
299
 * @param e   Email
300
 * @param msg Message
301
 * @param cache Cached Patterns
302
 * @retval true ALL of the Patterns evaluates to true
303
 */
UNCOV
304
static bool perform_and(struct PatternList *pat, PatternExecFlags flags,
×
305
                        struct Mailbox *m, struct Email *e, struct Message *msg,
306
                        struct PatternCache *cache)
307
{
308
  struct Pattern *p = NULL;
309

UNCOV
310
  SLIST_FOREACH(p, pat, entries)
×
311
  {
UNCOV
312
    if (!pattern_exec(p, flags, m, e, msg, cache))
×
313
    {
314
      return false;
315
    }
316
  }
317
  return true;
318
}
319

320
/**
321
 * perform_alias_and - Perform a logical AND on a set of Patterns
322
 * @param pat   Patterns to test
323
 * @param flags Optional flags, e.g. #MUTT_MATCH_FULL_ADDRESS
324
 * @param av    AliasView
325
 * @param cache Cached Patterns
326
 * @retval true ALL of the Patterns evaluate to true
327
 */
UNCOV
328
static bool perform_alias_and(struct PatternList *pat, PatternExecFlags flags,
×
329
                              struct AliasView *av, struct PatternCache *cache)
330
{
331
  struct Pattern *p = NULL;
332

UNCOV
333
  SLIST_FOREACH(p, pat, entries)
×
334
  {
UNCOV
335
    if (!mutt_pattern_alias_exec(p, flags, av, cache))
×
336
    {
337
      return false;
338
    }
339
  }
340
  return true;
341
}
342

343
/**
344
 * perform_or - Perform a logical OR on a set of Patterns
345
 * @param pat   Patterns to test
346
 * @param flags Optional flags, e.g. #MUTT_MATCH_FULL_ADDRESS
347
 * @param m   Mailbox
348
 * @param e   Email
349
 * @param msg Message
350
 * @param cache Cached Patterns
351
 * @retval true ONE (or more) of the Patterns evaluates to true
352
 */
UNCOV
353
static int perform_or(struct PatternList *pat, PatternExecFlags flags,
×
354
                      struct Mailbox *m, struct Email *e, struct Message *msg,
355
                      struct PatternCache *cache)
356
{
357
  struct Pattern *p = NULL;
358

UNCOV
359
  SLIST_FOREACH(p, pat, entries)
×
360
  {
UNCOV
361
    if (pattern_exec(p, flags, m, e, msg, cache))
×
362
    {
363
      return true;
364
    }
365
  }
366
  return false;
367
}
368

369
/**
370
 * perform_alias_or - Perform a logical OR on a set of Patterns
371
 * @param pat   Patterns to test
372
 * @param flags Optional flags, e.g. #MUTT_MATCH_FULL_ADDRESS
373
 * @param av    AliasView
374
 * @param cache Cached Patterns
375
 * @retval true ONE (or more) of the Patterns evaluates to true
376
 */
UNCOV
377
static int perform_alias_or(struct PatternList *pat, PatternExecFlags flags,
×
378
                            struct AliasView *av, struct PatternCache *cache)
379
{
380
  struct Pattern *p = NULL;
381

UNCOV
382
  SLIST_FOREACH(p, pat, entries)
×
383
  {
UNCOV
384
    if (mutt_pattern_alias_exec(p, flags, av, cache))
×
385
    {
386
      return true;
387
    }
388
  }
389
  return false;
390
}
391

392
/**
393
 * match_tags - match a pattern against a tags list
394
 * @param pat  pattern to find
395
 * @param tags tags list
396
 * @retval true if any tag match
397
 */
UNCOV
398
static bool match_tags(struct Pattern *pat, struct TagList *tags)
×
399
{
400
  struct Tag *tag = NULL;
401
  bool matched = false;
UNCOV
402
  STAILQ_FOREACH(tag, tags, entries)
×
403
  {
UNCOV
404
    matched |= patmatch(pat, tag->name);
×
405
  }
UNCOV
406
  return pat->pat_not ^ matched;
×
407
}
408

409
/**
410
 * match_addrlist - match a pattern against an address list
411
 * @param pat            pattern to find
412
 * @param match_personal if true, also match the pattern against the real name
413
 * @param n              number of addresses supplied
414
 * @param ...            variable number of addresses
415
 * @retval true
416
 * - one address matches (all_addr is false)
417
 * - all the addresses match (all_addr is true)
418
 */
UNCOV
419
static int match_addrlist(struct Pattern *pat, bool match_personal, int n, ...)
×
420
{
421
  va_list ap;
422

423
  va_start(ap, n);
×
UNCOV
424
  while (n-- > 0)
×
425
  {
UNCOV
426
    struct AddressList *al = va_arg(ap, struct AddressList *);
×
427
    struct Address *a = NULL;
UNCOV
428
    TAILQ_FOREACH(a, al, entries)
×
429
    {
430
      if (pat->all_addr ^
×
431
          ((!pat->is_alias || alias_reverse_lookup(a)) &&
×
432
           ((a->mailbox && patmatch(pat, buf_string(a->mailbox))) ||
×
UNCOV
433
            (match_personal && a->personal && patmatch(pat, buf_string(a->personal))))))
×
434
      {
435
        va_end(ap);
×
UNCOV
436
        return !pat->all_addr; /* Found match, or non-match if all_addr */
×
437
      }
438
    }
439
  }
440
  va_end(ap);
×
UNCOV
441
  return pat->all_addr; /* No matches, or all matches if all_addr */
×
442
}
443

444
/**
445
 * match_reference - Match references against a Pattern
446
 * @param pat  Pattern to match
447
 * @param refs List of References
448
 * @retval true One of the references matches
449
 */
UNCOV
450
static bool match_reference(struct Pattern *pat, struct ListHead *refs)
×
451
{
452
  struct ListNode *np = NULL;
UNCOV
453
  STAILQ_FOREACH(np, refs, entries)
×
454
  {
UNCOV
455
    if (patmatch(pat, np->data))
×
456
      return true;
457
  }
458
  return false;
459
}
460

461
/**
462
 * mutt_is_predicate_recipient - Test an Envelopes Addresses using a predicate function
463
 * @param all_addr If true, ALL Addresses must match
464
 * @param env     Envelope
465
 * @param p       Predicate function, e.g. mutt_is_subscribed_list()
466
 * @retval true
467
 * - One Address matches (all_addr is false)
468
 * - All the Addresses match (all_addr is true)
469
 *
470
 * Test the 'To' and 'Cc' fields of an Address using a test function (the predicate).
471
 */
UNCOV
472
static bool mutt_is_predicate_recipient(bool all_addr, struct Envelope *env, addr_predicate_t p)
×
473
{
474
  struct AddressList *als[] = { &env->to, &env->cc };
×
UNCOV
475
  for (size_t i = 0; i < mutt_array_size(als); i++)
×
476
  {
UNCOV
477
    struct AddressList *al = als[i];
×
478
    struct Address *a = NULL;
UNCOV
479
    TAILQ_FOREACH(a, al, entries)
×
480
    {
481
      if (all_addr ^ p(a))
×
UNCOV
482
        return !all_addr;
×
483
    }
484
  }
485
  return all_addr;
486
}
487

488
/**
489
 * mutt_is_subscribed_list_recipient - Matches subscribed mailing lists
490
 * @param all_addr If true, ALL Addresses must be on the subscribed list
491
 * @param env     Envelope
492
 * @retval true
493
 * - One Address is subscribed (all_addr is false)
494
 * - All the Addresses are subscribed (all_addr is true)
495
 */
UNCOV
496
bool mutt_is_subscribed_list_recipient(bool all_addr, struct Envelope *env)
×
497
{
UNCOV
498
  return mutt_is_predicate_recipient(all_addr, env, &mutt_is_subscribed_list);
×
499
}
500

501
/**
502
 * mutt_is_list_recipient - Matches known mailing lists
503
 * @param all_addr If true, ALL Addresses must be mailing lists
504
 * @param env     Envelope
505
 * @retval true
506
 * - One Address is a mailing list (all_addr is false)
507
 * - All the Addresses are mailing lists (all_addr is true)
508
 */
UNCOV
509
bool mutt_is_list_recipient(bool all_addr, struct Envelope *env)
×
510
{
UNCOV
511
  return mutt_is_predicate_recipient(all_addr, env, &mutt_is_mail_list);
×
512
}
513

514
/**
515
 * match_user - Matches the user's email Address
516
 * @param all_addr If true, ALL Addresses must refer to the user
517
 * @param n       number of AddressLists supplied
518
 * @param ...     Variable number of AddressLists
519
 * @retval true
520
 * - One Address refers to the user (all_addr is false)
521
 * - All the Addresses refer to the user (all_addr is true)
522
 */
UNCOV
523
static int match_user(bool all_addr, int n, ...)
×
524
{
525
  va_list ap;
526

527
  va_start(ap, n);
×
UNCOV
528
  while (n-- > 0)
×
529
  {
UNCOV
530
    struct AddressList *al = va_arg(ap, struct AddressList *);
×
531
    struct Address *a = NULL;
UNCOV
532
    TAILQ_FOREACH(a, al, entries)
×
533
    {
UNCOV
534
      if (all_addr ^ mutt_addr_is_user(a))
×
535
      {
536
        va_end(ap);
×
UNCOV
537
        return !all_addr;
×
538
      }
539
    }
540
  }
541
  va_end(ap);
×
UNCOV
542
  return all_addr;
×
543
}
544

545
/**
546
 * match_threadcomplete - Match a Pattern against an email thread
547
 * @param pat   Pattern to match
548
 * @param flags Flags, e.g. #MUTT_MATCH_FULL_ADDRESS
549
 * @param m   Mailbox
550
 * @param t     Email thread
551
 * @param left  Navigate to the previous email
552
 * @param up    Navigate to the email's parent
553
 * @param right Navigate to the next email
554
 * @param down  Navigate to the email's children
555
 * @retval 1  Success, match found
556
 * @retval 0  No match
557
 */
UNCOV
558
static int match_threadcomplete(struct PatternList *pat, PatternExecFlags flags,
×
559
                                struct Mailbox *m, struct MuttThread *t,
560
                                int left, int up, int right, int down)
561
{
UNCOV
562
  if (!t)
×
563
    return 0;
564

565
  int a;
566
  struct Email *e = t->message;
×
567
  if (e)
×
UNCOV
568
    if (mutt_pattern_exec(SLIST_FIRST(pat), flags, m, e, NULL))
×
569
      return 1;
570

UNCOV
571
  if (up && (a = match_threadcomplete(pat, flags, m, t->parent, 1, 1, 1, 0)))
×
572
    return a;
UNCOV
573
  if (right && t->parent && (a = match_threadcomplete(pat, flags, m, t->next, 0, 0, 1, 1)))
×
574
  {
575
    return a;
576
  }
UNCOV
577
  if (left && t->parent && (a = match_threadcomplete(pat, flags, m, t->prev, 1, 0, 0, 1)))
×
578
  {
579
    return a;
580
  }
UNCOV
581
  if (down && (a = match_threadcomplete(pat, flags, m, t->child, 1, 0, 1, 1)))
×
582
    return a;
583
  return 0;
584
}
585

586
/**
587
 * match_threadparent - Match Pattern against an email's parent
588
 * @param pat   Pattern to match
589
 * @param flags Flags, e.g. #MUTT_MATCH_FULL_ADDRESS
590
 * @param m   Mailbox
591
 * @param t     Thread of email
592
 * @retval  1 Success, pattern matched
593
 * @retval  0 Pattern did not match
594
 * @retval -1 Error
595
 */
UNCOV
596
static int match_threadparent(struct PatternList *pat, PatternExecFlags flags,
×
597
                              struct Mailbox *m, struct MuttThread *t)
598
{
UNCOV
599
  if (!t || !t->parent || !t->parent->message)
×
600
    return 0;
601

UNCOV
602
  return mutt_pattern_exec(SLIST_FIRST(pat), flags, m, t->parent->message, NULL);
×
603
}
604

605
/**
606
 * match_threadchildren - Match Pattern against an email's children
607
 * @param pat   Pattern to match
608
 * @param flags Flags, e.g. #MUTT_MATCH_FULL_ADDRESS
609
 * @param m   Mailbox
610
 * @param t     Thread of email
611
 * @retval  1 Success, pattern matched
612
 * @retval  0 Pattern did not match
613
 * @retval -1 Error
614
 */
UNCOV
615
static int match_threadchildren(struct PatternList *pat, PatternExecFlags flags,
×
616
                                struct Mailbox *m, struct MuttThread *t)
617
{
UNCOV
618
  if (!t || !t->child)
×
619
    return 0;
620

621
  for (t = t->child; t; t = t->next)
×
UNCOV
622
    if (t->message && mutt_pattern_exec(SLIST_FIRST(pat), flags, m, t->message, NULL))
×
623
      return 1;
624

625
  return 0;
626
}
627

628
/**
629
 * match_content_type - Match a Pattern against an Attachment's Content-Type
630
 * @param pat   Pattern to match
631
 * @param b     Attachment
632
 * @retval true  Success, pattern matched
633
 * @retval false Pattern did not match
634
 */
UNCOV
635
static bool match_content_type(const struct Pattern *pat, struct Body *b)
×
636
{
UNCOV
637
  if (!b)
×
638
    return false;
639

640
  char buf[256] = { 0 };
×
UNCOV
641
  snprintf(buf, sizeof(buf), "%s/%s", TYPE(b), b->subtype);
×
642

UNCOV
643
  if (patmatch(pat, buf))
×
644
    return true;
UNCOV
645
  if (match_content_type(pat, b->parts))
×
646
    return true;
UNCOV
647
  if (match_content_type(pat, b->next))
×
648
    return true;
649
  return false;
650
}
651

652
/**
653
 * match_mime_content_type - Match a Pattern against an email's Content-Type
654
 * @param pat Pattern to match
655
 * @param e   Email
656
 * @param fp  Message file
657
 * @retval true  Success, pattern matched
658
 * @retval false Pattern did not match
659
 */
UNCOV
660
static bool match_mime_content_type(const struct Pattern *pat, struct Email *e, FILE *fp)
×
661
{
662
  mutt_parse_mime_message(e, fp);
×
UNCOV
663
  return match_content_type(pat, e->body);
×
664
}
665

666
/**
667
 * match_update_dynamic_date - Update a dynamic date pattern
668
 * @param pat Pattern to modify
669
 * @retval true  Pattern valid and updated
670
 * @retval false Pattern invalid
671
 */
UNCOV
672
static bool match_update_dynamic_date(struct Pattern *pat)
×
673
{
UNCOV
674
  struct Buffer *err = buf_pool_get();
×
675

676
  bool rc = eval_date_minmax(pat, pat->p.str, err);
×
UNCOV
677
  buf_pool_release(&err);
×
678

UNCOV
679
  return rc;
×
680
}
681

682
/**
683
 * set_pattern_cache_value - Sets a value in the PatternCache cache entry
684
 * @param cache_entry Cache entry to update
685
 * @param value       Value to set
686
 *
687
 * Normalizes the "true" value to 2.
688
 */
689
static void set_pattern_cache_value(int *cache_entry, int value)
690
{
691
  *cache_entry = (value != 0) ? 2 : 1;
×
UNCOV
692
}
×
693

694
/**
695
 * get_pattern_cache_value - Get pattern cache value
696
 * @param cache_entry Cache entry to get
697
 * @retval 1 The cache value is set and has a true value
698
 * @retval 0 otherwise (even if unset!)
699
 */
700
static bool get_pattern_cache_value(int cache_entry)
701
{
UNCOV
702
  return cache_entry == 2;
×
703
}
704

705
/**
706
 * is_pattern_cache_set - Is a given Pattern cached?
707
 * @param cache_entry Cache entry to check
708
 * @retval true Pattern is cached
709
 */
710
static int is_pattern_cache_set(int cache_entry)
711
{
712
  return cache_entry != 0;
713
}
714

715
/**
716
 * msg_search_sendmode - Search in send-mode
717
 * @param e   Email to search
718
 * @param pat Pattern to find
719
 * @retval  1 Success, pattern matched
720
 * @retval  0 Pattern did not match
721
 * @retval -1 Error
722
 */
UNCOV
723
static int msg_search_sendmode(struct Email *e, struct Pattern *pat)
×
724
{
725
  bool match = false;
726
  char *buf = NULL;
×
727
  size_t blen = 0;
×
UNCOV
728
  FILE *fp = NULL;
×
729

UNCOV
730
  if ((pat->op == MUTT_PAT_HEADER) || (pat->op == MUTT_PAT_WHOLE_MSG))
×
731
  {
732
    struct Buffer *tempfile = buf_pool_get();
×
733
    buf_mktemp(tempfile);
×
734
    fp = mutt_file_fopen(buf_string(tempfile), "w+");
×
UNCOV
735
    if (!fp)
×
736
    {
737
      mutt_perror("%s", buf_string(tempfile));
×
738
      buf_pool_release(&tempfile);
×
UNCOV
739
      return 0;
×
740
    }
741

742
    mutt_rfc822_write_header(fp, e->env, e->body, MUTT_WRITE_HEADER_POSTPONE,
×
743
                             false, false, NeoMutt->sub);
×
744
    fflush(fp);
×
UNCOV
745
    if (mutt_file_seek(fp, 0, SEEK_SET))
×
746
    {
UNCOV
747
      while ((buf = mutt_file_read_line(buf, &blen, fp, NULL, MUTT_RL_NO_FLAGS)) != NULL)
×
748
      {
UNCOV
749
        if (patmatch(pat, buf) == 0)
×
750
        {
751
          match = true;
752
          break;
753
        }
754
      }
755
    }
756

757
    FREE(&buf);
×
758
    mutt_file_fclose(&fp);
×
759
    unlink(buf_string(tempfile));
×
UNCOV
760
    buf_pool_release(&tempfile);
×
761

UNCOV
762
    if (match)
×
763
      return match;
764
  }
765

UNCOV
766
  if ((pat->op == MUTT_PAT_BODY) || (pat->op == MUTT_PAT_WHOLE_MSG))
×
767
  {
768
    fp = mutt_file_fopen(e->body->filename, "r");
×
UNCOV
769
    if (!fp)
×
770
    {
771
      mutt_perror("%s", e->body->filename);
×
UNCOV
772
      return 0;
×
773
    }
774

UNCOV
775
    while ((buf = mutt_file_read_line(buf, &blen, fp, NULL, MUTT_RL_NO_FLAGS)) != NULL)
×
776
    {
UNCOV
777
      if (patmatch(pat, buf) == 0)
×
778
      {
779
        match = true;
780
        break;
781
      }
782
    }
783

784
    FREE(&buf);
×
UNCOV
785
    mutt_file_fclose(&fp);
×
786
  }
787

UNCOV
788
  return match;
×
789
}
790

791
/**
792
 * pattern_needs_msg - Check whether a pattern needs a full message
793
 * @param m Mailbox
794
 * @param pat Pattern
795
 * @retval true The pattern needs a full message
796
 * @retval false The pattern does not need a full message
797
 */
UNCOV
798
static bool pattern_needs_msg(const struct Mailbox *m, const struct Pattern *pat)
×
799
{
UNCOV
800
  if (!m)
×
801
  {
802
    return false;
803
  }
804

UNCOV
805
  if ((pat->op == MUTT_PAT_MIMETYPE) || (pat->op == MUTT_PAT_MIMEATTACH))
×
806
  {
807
    return true;
808
  }
809

UNCOV
810
  if ((pat->op == MUTT_PAT_WHOLE_MSG) || (pat->op == MUTT_PAT_BODY) || (pat->op == MUTT_PAT_HEADER))
×
811
  {
UNCOV
812
    return !((m->type == MUTT_IMAP) && pat->string_match);
×
813
  }
814

UNCOV
815
  if ((pat->op == MUTT_PAT_AND) || (pat->op == MUTT_PAT_OR))
×
816
  {
817
    struct Pattern *p = NULL;
UNCOV
818
    SLIST_FOREACH(p, pat->child, entries)
×
819
    {
UNCOV
820
      if (pattern_needs_msg(m, p))
×
821
      {
822
        return true;
823
      }
824
    }
825
  }
826

827
  return false;
828
}
829

830
/**
831
 * pattern_exec - Match a pattern against an email header
832
 * @param pat   Pattern to match
833
 * @param flags Flags, e.g. #MUTT_MATCH_FULL_ADDRESS
834
 * @param m     Mailbox
835
 * @param e     Email
836
 * @param msg   MEssage
837
 * @param cache Cache for common Patterns
838
 * @retval true Success, pattern matched
839
 * @retval false Pattern did not match
840
 *
841
 * flags: MUTT_MATCH_FULL_ADDRESS: match both personal and machine address
842
 * cache: For repeated matches against the same Header, passing in non-NULL will
843
 *        store some of the cacheable pattern matches in this structure.
844
 */
UNCOV
845
static bool pattern_exec(struct Pattern *pat, PatternExecFlags flags,
×
846
                         struct Mailbox *m, struct Email *e,
847
                         struct Message *msg, struct PatternCache *cache)
848
{
UNCOV
849
  switch (pat->op)
×
850
  {
851
    case MUTT_PAT_AND:
×
852
      return pat->pat_not ^ (perform_and(pat->child, flags, m, e, msg, cache) > 0);
×
853
    case MUTT_PAT_OR:
×
854
      return pat->pat_not ^ (perform_or(pat->child, flags, m, e, msg, cache) > 0);
×
855
    case MUTT_PAT_THREAD:
×
856
      return pat->pat_not ^
×
857
             match_threadcomplete(pat->child, flags, m, e->thread, 1, 1, 1, 1);
×
858
    case MUTT_PAT_PARENT:
×
859
      return pat->pat_not ^ match_threadparent(pat->child, flags, m, e->thread);
×
860
    case MUTT_PAT_CHILDREN:
×
861
      return pat->pat_not ^ match_threadchildren(pat->child, flags, m, e->thread);
×
862
    case MUTT_ALL:
×
863
      return !pat->pat_not;
×
864
    case MUTT_EXPIRED:
×
865
      return pat->pat_not ^ e->expired;
×
866
    case MUTT_SUPERSEDED:
×
867
      return pat->pat_not ^ e->superseded;
×
868
    case MUTT_FLAG:
×
869
      return pat->pat_not ^ e->flagged;
×
870
    case MUTT_TAG:
×
871
      return pat->pat_not ^ e->tagged;
×
872
    case MUTT_NEW:
×
873
      return pat->pat_not ? e->old || e->read : !(e->old || e->read);
×
874
    case MUTT_UNREAD:
×
875
      return pat->pat_not ? e->read : !e->read;
×
876
    case MUTT_REPLIED:
×
877
      return pat->pat_not ^ e->replied;
×
878
    case MUTT_OLD:
×
879
      return pat->pat_not ? (!e->old || e->read) : (e->old && !e->read);
×
880
    case MUTT_READ:
×
881
      return pat->pat_not ^ e->read;
×
882
    case MUTT_DELETED:
×
883
      return pat->pat_not ^ e->deleted;
×
884
    case MUTT_PAT_MESSAGE:
×
885
      return pat->pat_not ^
×
886
             ((email_msgno(e) >= pat->min) && (email_msgno(e) <= pat->max));
×
887
    case MUTT_PAT_DATE:
×
888
      if (pat->dynamic)
×
889
        match_update_dynamic_date(pat);
×
890
      return pat->pat_not ^ ((e->date_sent >= pat->min) && (e->date_sent <= pat->max));
×
891
    case MUTT_PAT_DATE_RECEIVED:
×
892
      if (pat->dynamic)
×
893
        match_update_dynamic_date(pat);
×
894
      return pat->pat_not ^ ((e->received >= pat->min) && (e->received <= pat->max));
×
UNCOV
895
    case MUTT_PAT_BODY:
×
896
    case MUTT_PAT_HEADER:
897
    case MUTT_PAT_WHOLE_MSG:
UNCOV
898
      if (pat->sendmode)
×
899
      {
UNCOV
900
        if (!e->body || !e->body->filename)
×
901
          return false;
UNCOV
902
        return pat->pat_not ^ msg_search_sendmode(e, pat);
×
903
      }
904
      /* m can be NULL in certain cases, such as when replying to a message
905
       * from the attachment menu and the user has a reply-hook using "~e".
906
       * This is also the case when message scoring.  */
UNCOV
907
      if (!m)
×
908
        return false;
909
      /* IMAP search sets e->matched at search compile time */
910
      if ((m->type == MUTT_IMAP) && pat->string_match)
×
911
        return e->matched;
×
912
      return pat->pat_not ^ msg_search(pat, e, msg);
×
913
    case MUTT_PAT_SERVERSEARCH:
×
UNCOV
914
      if (!m)
×
915
        return false;
UNCOV
916
      if (m->type == MUTT_IMAP)
×
917
      {
UNCOV
918
        return (pat->string_match) ? e->matched : false;
×
919
      }
920
      mutt_error(_("error: server custom search only supported with IMAP"));
×
921
      return false;
×
922
    case MUTT_PAT_SENDER:
×
UNCOV
923
      if (!e->env)
×
924
        return false;
UNCOV
925
      return pat->pat_not ^ match_addrlist(pat, (flags & MUTT_MATCH_FULL_ADDRESS),
×
926
                                           1, &e->env->sender);
927
    case MUTT_PAT_FROM:
×
UNCOV
928
      if (!e->env)
×
929
        return false;
930
      return pat->pat_not ^
×
931
             match_addrlist(pat, (flags & MUTT_MATCH_FULL_ADDRESS), 1, &e->env->from);
×
932
    case MUTT_PAT_TO:
×
UNCOV
933
      if (!e->env)
×
934
        return false;
935
      return pat->pat_not ^
×
936
             match_addrlist(pat, (flags & MUTT_MATCH_FULL_ADDRESS), 1, &e->env->to);
×
937
    case MUTT_PAT_CC:
×
UNCOV
938
      if (!e->env)
×
939
        return false;
940
      return pat->pat_not ^
×
941
             match_addrlist(pat, (flags & MUTT_MATCH_FULL_ADDRESS), 1, &e->env->cc);
×
942
    case MUTT_PAT_BCC:
×
UNCOV
943
      if (!e->env)
×
944
        return false;
945
      return pat->pat_not ^
×
946
             match_addrlist(pat, (flags & MUTT_MATCH_FULL_ADDRESS), 1, &e->env->bcc);
×
947
    case MUTT_PAT_SUBJECT:
×
UNCOV
948
      if (!e->env)
×
949
        return false;
950
      return pat->pat_not ^ (e->env->subject && patmatch(pat, e->env->subject));
×
UNCOV
951
    case MUTT_PAT_ID:
×
952
    case MUTT_PAT_ID_EXTERNAL:
UNCOV
953
      if (!e->env)
×
954
        return false;
955
      return pat->pat_not ^ (e->env->message_id && patmatch(pat, e->env->message_id));
×
956
    case MUTT_PAT_SCORE:
×
957
      return pat->pat_not ^ (e->score >= pat->min &&
×
958
                             (pat->max == MUTT_MAXRANGE || e->score <= pat->max));
×
959
    case MUTT_PAT_SIZE:
×
960
      return pat->pat_not ^ (e->body->length >= pat->min &&
×
961
                             (pat->max == MUTT_MAXRANGE || e->body->length <= pat->max));
×
962
    case MUTT_PAT_REFERENCE:
×
UNCOV
963
      if (!e->env)
×
964
        return false;
965
      return pat->pat_not ^ (match_reference(pat, &e->env->references) ||
×
966
                             match_reference(pat, &e->env->in_reply_to));
×
967
    case MUTT_PAT_ADDRESS:
×
UNCOV
968
      if (!e->env)
×
969
        return false;
UNCOV
970
      return pat->pat_not ^ match_addrlist(pat, (flags & MUTT_MATCH_FULL_ADDRESS),
×
971
                                           5, &e->env->from, &e->env->sender,
972
                                           &e->env->to, &e->env->cc, &e->env->bcc);
973
    case MUTT_PAT_RECIPIENT:
×
UNCOV
974
      if (!e->env)
×
975
        return false;
UNCOV
976
      return pat->pat_not ^ match_addrlist(pat, (flags & MUTT_MATCH_FULL_ADDRESS), 3,
×
977
                                           &e->env->to, &e->env->cc, &e->env->bcc);
UNCOV
978
    case MUTT_PAT_LIST: /* known list, subscribed or not */
×
979
    {
UNCOV
980
      if (!e->env)
×
981
        return false;
982

983
      bool result;
UNCOV
984
      if (cache)
×
985
      {
986
        int *cache_entry = pat->all_addr ? &cache->list_all : &cache->list_one;
×
UNCOV
987
        if (!is_pattern_cache_set(*cache_entry))
×
988
        {
989
          set_pattern_cache_value(cache_entry,
UNCOV
990
                                  mutt_is_list_recipient(pat->all_addr, e->env));
×
991
        }
UNCOV
992
        result = get_pattern_cache_value(*cache_entry);
×
993
      }
994
      else
995
      {
UNCOV
996
        result = mutt_is_list_recipient(pat->all_addr, e->env);
×
997
      }
UNCOV
998
      return pat->pat_not ^ result;
×
999
    }
UNCOV
1000
    case MUTT_PAT_SUBSCRIBED_LIST:
×
1001
    {
UNCOV
1002
      if (!e->env)
×
1003
        return false;
1004

1005
      bool result;
UNCOV
1006
      if (cache)
×
1007
      {
1008
        int *cache_entry = pat->all_addr ? &cache->sub_all : &cache->sub_one;
×
UNCOV
1009
        if (!is_pattern_cache_set(*cache_entry))
×
1010
        {
1011
          set_pattern_cache_value(cache_entry,
UNCOV
1012
                                  mutt_is_subscribed_list_recipient(pat->all_addr, e->env));
×
1013
        }
UNCOV
1014
        result = get_pattern_cache_value(*cache_entry);
×
1015
      }
1016
      else
1017
      {
UNCOV
1018
        result = mutt_is_subscribed_list_recipient(pat->all_addr, e->env);
×
1019
      }
UNCOV
1020
      return pat->pat_not ^ result;
×
1021
    }
UNCOV
1022
    case MUTT_PAT_PERSONAL_RECIP:
×
1023
    {
UNCOV
1024
      if (!e->env)
×
1025
        return false;
1026

1027
      bool result;
UNCOV
1028
      if (cache)
×
1029
      {
1030
        int *cache_entry = pat->all_addr ? &cache->pers_recip_all : &cache->pers_recip_one;
×
UNCOV
1031
        if (!is_pattern_cache_set(*cache_entry))
×
1032
        {
1033
          set_pattern_cache_value(cache_entry,
×
UNCOV
1034
                                  match_user(pat->all_addr, 3, &e->env->to,
×
1035
                                             &e->env->cc, &e->env->bcc));
1036
        }
UNCOV
1037
        result = get_pattern_cache_value(*cache_entry);
×
1038
      }
1039
      else
1040
      {
UNCOV
1041
        result = match_user(pat->all_addr, 3, &e->env->to, &e->env->cc, &e->env->bcc);
×
1042
      }
UNCOV
1043
      return pat->pat_not ^ result;
×
1044
    }
UNCOV
1045
    case MUTT_PAT_PERSONAL_FROM:
×
1046
    {
UNCOV
1047
      if (!e->env)
×
1048
        return false;
1049

1050
      bool result;
UNCOV
1051
      if (cache)
×
1052
      {
1053
        int *cache_entry = pat->all_addr ? &cache->pers_from_all : &cache->pers_from_one;
×
UNCOV
1054
        if (!is_pattern_cache_set(*cache_entry))
×
1055
        {
1056
          set_pattern_cache_value(cache_entry,
×
UNCOV
1057
                                  match_user(pat->all_addr, 1, &e->env->from));
×
1058
        }
UNCOV
1059
        result = get_pattern_cache_value(*cache_entry);
×
1060
      }
1061
      else
1062
      {
UNCOV
1063
        result = match_user(pat->all_addr, 1, &e->env->from);
×
1064
      }
UNCOV
1065
      return pat->pat_not ^ result;
×
1066
    }
1067
    case MUTT_PAT_COLLAPSED:
×
UNCOV
1068
      return pat->pat_not ^ (e->collapsed && e->num_hidden > 1);
×
1069
    case MUTT_PAT_CRYPT_SIGN:
1070
      if (!WithCrypto)
1071
      {
1072
        print_crypt_pattern_op_error(pat->op);
1073
        return false;
1074
      }
UNCOV
1075
      return pat->pat_not ^ ((e->security & SEC_SIGN) ? 1 : 0);
×
1076
    case MUTT_PAT_CRYPT_VERIFIED:
1077
      if (!WithCrypto)
1078
      {
1079
        print_crypt_pattern_op_error(pat->op);
1080
        return false;
1081
      }
UNCOV
1082
      return pat->pat_not ^ ((e->security & SEC_GOODSIGN) ? 1 : 0);
×
1083
    case MUTT_PAT_CRYPT_ENCRYPT:
1084
      if (!WithCrypto)
1085
      {
1086
        print_crypt_pattern_op_error(pat->op);
1087
        return false;
1088
      }
UNCOV
1089
      return pat->pat_not ^ ((e->security & SEC_ENCRYPT) ? 1 : 0);
×
1090
    case MUTT_PAT_PGP_KEY:
1091
      if (!(WithCrypto & APPLICATION_PGP))
1092
      {
1093
        print_crypt_pattern_op_error(pat->op);
1094
        return false;
1095
      }
1096
      return pat->pat_not ^ ((e->security & PGP_KEY) == PGP_KEY);
×
1097
    case MUTT_PAT_XLABEL:
×
UNCOV
1098
      if (!e->env)
×
1099
        return false;
1100
      return pat->pat_not ^ (e->env->x_label && patmatch(pat, e->env->x_label));
×
UNCOV
1101
    case MUTT_PAT_DRIVER_TAGS:
×
1102
    {
UNCOV
1103
      return match_tags(pat, &e->tags);
×
1104
    }
1105
    case MUTT_PAT_HORMEL:
×
UNCOV
1106
      if (!e->env)
×
1107
        return false;
1108
      return pat->pat_not ^ (e->env->spam.data && patmatch(pat, e->env->spam.data));
×
1109
    case MUTT_PAT_DUPLICATED:
×
1110
      return pat->pat_not ^ (e->thread && e->thread->duplicate_thread);
×
UNCOV
1111
    case MUTT_PAT_MIMEATTACH:
×
1112
    {
1113
      int count = msg ? mutt_count_body_parts(e, msg->fp) : 0;
×
1114
      return pat->pat_not ^
×
UNCOV
1115
             (count >= pat->min && (pat->max == MUTT_MAXRANGE || count <= pat->max));
×
1116
    }
1117
    case MUTT_PAT_MIMETYPE:
×
UNCOV
1118
      if (!m || !msg)
×
1119
        return false;
1120
      return pat->pat_not ^ match_mime_content_type(pat, e, msg->fp);
×
1121
    case MUTT_PAT_UNREFERENCED:
×
1122
      return pat->pat_not ^ (e->thread && !e->thread->child);
×
1123
    case MUTT_PAT_BROKEN:
×
1124
      return pat->pat_not ^ (e->thread && e->thread->fake_thread);
×
1125
    case MUTT_PAT_NEWSGROUPS:
×
UNCOV
1126
      if (!e->env)
×
1127
        return false;
UNCOV
1128
      return pat->pat_not ^ (e->env->newsgroups && patmatch(pat, e->env->newsgroups));
×
1129
  }
1130
  mutt_error(_("error: unknown op %d (report this error)"), pat->op);
×
UNCOV
1131
  return false;
×
1132
}
1133

1134
/**
1135
 * mutt_pattern_exec - Match a pattern against an email header
1136
 * @param pat   Pattern to match
1137
 * @param flags Flags, e.g. #MUTT_MATCH_FULL_ADDRESS
1138
 * @param m     Mailbox
1139
 * @param e     Email
1140
 * @param cache Cache for common Patterns
1141
 * @retval true Success, pattern matched
1142
 * @retval false Pattern did not match
1143
 *
1144
 * flags: MUTT_MATCH_FULL_ADDRESS: match both personal and machine address
1145
 * cache: For repeated matches against the same Header, passing in non-NULL will
1146
 *        store some of the cacheable pattern matches in this structure.
1147
 */
UNCOV
1148
bool mutt_pattern_exec(struct Pattern *pat, PatternExecFlags flags,
×
1149
                       struct Mailbox *m, struct Email *e, struct PatternCache *cache)
1150
{
1151
  const bool needs_msg = pattern_needs_msg(m, pat);
×
1152
  struct Message *msg = needs_msg ? mx_msg_open(m, e) : NULL;
×
UNCOV
1153
  if (needs_msg && !msg)
×
1154
  {
1155
    return false;
1156
  }
1157
  const bool matched = pattern_exec(pat, flags, m, e, msg, cache);
×
1158
  mx_msg_close(m, &msg);
×
UNCOV
1159
  return matched;
×
1160
}
1161

1162
/**
1163
 * mutt_pattern_alias_exec - Match a pattern against an alias
1164
 * @param pat   Pattern to match
1165
 * @param flags Flags, e.g. #MUTT_MATCH_FULL_ADDRESS
1166
 * @param av    AliasView
1167
 * @param cache Cache for common Patterns
1168
 * @retval true Success, pattern matched
1169
 * @retval false Pattern did not match
1170
 *
1171
 * flags: MUTT_MATCH_FULL_ADDRESS: match both personal and machine address
1172
 * cache: For repeated matches against the same Alias, passing in non-NULL will
1173
 *        store some of the cacheable pattern matches in this structure.
1174
 */
UNCOV
1175
bool mutt_pattern_alias_exec(struct Pattern *pat, PatternExecFlags flags,
×
1176
                             struct AliasView *av, struct PatternCache *cache)
1177
{
UNCOV
1178
  switch (pat->op)
×
1179
  {
1180
    case MUTT_PAT_FROM: /* alias */
×
UNCOV
1181
      if (!av->alias)
×
1182
        return false;
1183
      return pat->pat_not ^ (av->alias->name && patmatch(pat, av->alias->name));
×
1184
    case MUTT_PAT_CC: /* comment */
×
UNCOV
1185
      if (!av->alias)
×
1186
        return false;
1187
      return pat->pat_not ^ (av->alias->comment && patmatch(pat, av->alias->comment));
×
1188
    case MUTT_PAT_TO: /* alias address list */
×
UNCOV
1189
      if (!av->alias)
×
1190
        return false;
UNCOV
1191
      return pat->pat_not ^ match_addrlist(pat, (flags & MUTT_MATCH_FULL_ADDRESS),
×
1192
                                           1, &av->alias->addr);
1193
    case MUTT_PAT_DRIVER_TAGS:
×
UNCOV
1194
      if (!av->alias)
×
1195
        return false;
1196
      return match_tags(pat, &av->alias->tags);
×
1197
    case MUTT_PAT_AND:
×
1198
      return pat->pat_not ^ (perform_alias_and(pat->child, flags, av, cache) > 0);
×
1199
    case MUTT_PAT_OR:
×
UNCOV
1200
      return pat->pat_not ^ (perform_alias_or(pat->child, flags, av, cache) > 0);
×
1201
  }
1202

1203
  return false;
1204
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc