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

neomutt / neomutt / 21124756832

18 Jan 2026 09:18PM UTC coverage: 42.051% (-3.9%) from 45.967%
21124756832

push

github

flatcap
refactor command parsing: mailboxes

Refactor `parse_mailboxes()` into three parts:

- `parse_mailboxes()`
  Command handler, which calls:
- `parse_mailboxes_args()`
  Split the string into tokens
- `parse_mailboxes_exec()`
  Perform the actions of the `mailboxes` command

29 of 30 new or added lines in 1 file covered. (96.67%)

2607 existing lines in 19 files now uncovered.

11739 of 27916 relevant lines covered (42.05%)

445.43 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
 * Copyright (C) 2025 Alejandro Colomar <alx@kernel.org>
12
 *
13
 * @copyright
14
 * This program is free software: you can redistribute it and/or modify it under
15
 * the terms of the GNU General Public License as published by the Free Software
16
 * Foundation, either version 2 of the License, or (at your option) any later
17
 * version.
18
 *
19
 * This program is distributed in the hope that it will be useful, but WITHOUT
20
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
21
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
22
 * details.
23
 *
24
 * You should have received a copy of the GNU General Public License along with
25
 * this program.  If not, see <http://www.gnu.org/licenses/>.
26
 */
27

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

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

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

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

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

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

115
  bool match = false;
116

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

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

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

156
    if (needs_body)
×
157
    {
UNCOV
158
      mutt_parse_mime_message(e, msg->fp);
×
159

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

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

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

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

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

UNCOV
282
  if (c_thorough_search)
×
UNCOV
283
    mutt_file_fclose(&fp);
×
284

285
#ifdef USE_FMEMOPEN
286
  FREE(&temp);
287
#endif
288

289
  return match;
290
}
291

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

600
  return mutt_pattern_exec(SLIST_FIRST(pat), flags, m, t->parent->message, NULL);
×
601
}
602

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

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

623
  return 0;
624
}
625

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

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

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

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

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

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

677
  return rc;
×
678
}
679

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

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

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

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

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

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

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

760
    if (match)
×
761
      return match;
762
  }
763

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

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

UNCOV
782
    FREE(&buf);
×
UNCOV
783
    mutt_file_fclose(&fp);
×
784
  }
785

786
  return match;
×
787
}
788

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

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

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

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

825
  return false;
826
}
827

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

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

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

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

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

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

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

1201
  return false;
1202
}
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