Coverage Report

Created: 2026-04-12 07:00

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