Coverage Report

Created: 2026-07-30 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/lib-sieve/tst-exists.c
Line
Count
Source
1
/* Copyright (c) Pigeonhole authors, see top-level COPYING file */
2
3
#include "lib.h"
4
#include "str-sanitize.h"
5
6
#include "sieve-common.h"
7
#include "sieve-commands.h"
8
#include "sieve-stringlist.h"
9
#include "sieve-code.h"
10
#include "sieve-message.h"
11
#include "sieve-validator.h"
12
#include "sieve-generator.h"
13
#include "sieve-interpreter.h"
14
#include "sieve-code-dumper.h"
15
16
/*
17
 * Exists test
18
 *
19
 * Syntax:
20
 *    exists <header-names: string-list>
21
 */
22
23
static bool
24
tst_exists_validate(struct sieve_validator *valdtr, struct sieve_command *tst);
25
static bool
26
tst_exists_generate(const struct sieve_codegen_env *cgenv,
27
        struct sieve_command *tst);
28
29
const struct sieve_command_def tst_exists = {
30
  .identifier = "exists",
31
  .type = SCT_TEST,
32
  .positional_args = 1,
33
  .subtests = 0,
34
  .block_allowed = FALSE,
35
  .block_required = FALSE,
36
  .validate = tst_exists_validate,
37
  .generate = tst_exists_generate
38
};
39
40
/*
41
 * Exists operation
42
 */
43
44
static bool
45
tst_exists_operation_dump(const struct sieve_dumptime_env *denv,
46
        sieve_size_t *address);
47
static int
48
tst_exists_operation_execute(const struct sieve_runtime_env *renv,
49
           sieve_size_t *address);
50
51
const struct sieve_operation_def tst_exists_operation = {
52
  .mnemonic = "EXISTS",
53
  .code = SIEVE_OPERATION_EXISTS,
54
  .dump = tst_exists_operation_dump,
55
  .execute = tst_exists_operation_execute
56
};
57
58
/*
59
 * Validation
60
 */
61
62
static bool
63
tst_exists_validate(struct sieve_validator *valdtr, struct sieve_command *tst)
64
0
{
65
0
  struct sieve_ast_argument *arg = tst->first_positional;
66
67
0
  if (!sieve_validate_positional_argument(valdtr, tst, arg,
68
0
            "header names", 1,
69
0
            SAAT_STRING_LIST))
70
0
    return FALSE;
71
72
0
  if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE))
73
0
    return FALSE;
74
75
0
  return sieve_command_verify_headers_argument(valdtr, arg);
76
0
}
77
78
/*
79
 * Code generation
80
 */
81
82
static bool
83
tst_exists_generate(const struct sieve_codegen_env *cgenv,
84
        struct sieve_command *tst)
85
0
{
86
0
  sieve_operation_emit(cgenv->sblock, NULL, &tst_exists_operation);
87
88
  /* Generate arguments */
89
0
  return sieve_generate_arguments(cgenv, tst, NULL);
90
0
}
91
92
/*
93
 * Code dump
94
 */
95
96
static bool
97
tst_exists_operation_dump(const struct sieve_dumptime_env *denv,
98
        sieve_size_t *address)
99
0
{
100
0
  sieve_code_dumpf(denv, "EXISTS");
101
0
  sieve_code_descend(denv);
102
103
  /* Optional operands */
104
0
  if (sieve_message_opr_optional_dump(denv, address, NULL) != 0)
105
0
    return FALSE;
106
107
0
  return sieve_opr_stringlist_dump(denv, address, "header names");
108
0
}
109
110
/*
111
 * Code execution
112
 */
113
114
static int
115
tst_exists_operation_execute(const struct sieve_runtime_env *renv,
116
           sieve_size_t *address)
117
0
{
118
0
  struct sieve_stringlist *hdr_list;
119
0
  ARRAY_TYPE(sieve_message_override) svmos;
120
0
  string_t *hdr_item;
121
0
  bool matched;
122
0
  int ret;
123
124
  /*
125
   * Read operands
126
   */
127
128
  /* Optional operands */
129
0
  i_zero(&svmos);
130
0
  if (sieve_message_opr_optional_read(renv, address, NULL, &ret,
131
0
              NULL, NULL, NULL, &svmos) < 0)
132
0
    return ret;
133
134
  /* Read header-list */
135
0
  if ((ret = sieve_opr_stringlist_read(renv, address, "header-list",
136
0
               &hdr_list)) <= 0)
137
0
    return ret;
138
139
  /*
140
   * Perfrom test
141
   */
142
143
0
  sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "exists test");
144
0
  sieve_runtime_trace_descend(renv);
145
146
  /* Iterate through all requested headers to match (must find all
147
     specified) */
148
0
  hdr_item = NULL;
149
0
  matched = TRUE;
150
0
  while (matched &&
151
0
         (ret = sieve_stringlist_next_item(hdr_list, &hdr_item)) > 0) {
152
0
    struct sieve_stringlist *field_names, *value_list;
153
0
    string_t *dummy;
154
155
    /* Get header */
156
0
    field_names = sieve_single_stringlist_create(renv, hdr_item, FALSE);
157
0
    ret = sieve_message_get_header_fields(renv, field_names,
158
0
                  &svmos, FALSE, &value_list);
159
0
    if (ret <= 0)
160
0
      return ret;
161
162
0
    ret = sieve_stringlist_next_item(value_list, &dummy);
163
0
    if (ret < 0)
164
0
      return value_list->exec_status;
165
0
    if (ret == 0)
166
0
      matched = FALSE;
167
168
0
    sieve_runtime_trace(
169
0
      renv, SIEVE_TRLVL_MATCHING,
170
0
      "header '%s' %s", str_sanitize(str_c(hdr_item), 80),
171
0
      (matched ? "exists" : "is missing"));
172
0
  }
173
174
0
  if (matched) {
175
0
    sieve_runtime_trace(renv, SIEVE_TRLVL_MATCHING,
176
0
            "all headers exist");
177
0
  } else {
178
0
    sieve_runtime_trace(renv, SIEVE_TRLVL_MATCHING,
179
0
            "headers are missing");
180
0
  }
181
182
  /* Set test result for subsequent conditional jump */
183
0
  if (ret >= 0) {
184
0
    sieve_interpreter_set_test_result(renv->interp, matched);
185
0
    return SIEVE_EXEC_OK;
186
0
  }
187
188
0
  sieve_runtime_trace_error(renv, "invalid header-list item");
189
0
  return SIEVE_EXEC_BIN_CORRUPT;
190
0
}