Coverage Report

Created: 2026-05-16 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/lib-sieve/plugins/ihave/tst-ihave.c
Line
Count
Source
1
/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
2
 */
3
4
#include "lib.h"
5
#include "array.h"
6
7
#include "sieve-common.h"
8
#include "sieve-stringlist.h"
9
#include "sieve-extensions.h"
10
#include "sieve-code.h"
11
#include "sieve-commands.h"
12
#include "sieve-validator.h"
13
#include "sieve-generator.h"
14
#include "sieve-interpreter.h"
15
16
#include "ext-ihave-common.h"
17
18
/*
19
 * Ihave test
20
 *
21
 * Syntax:
22
 *   ihave <capabilities: string-list>
23
 */
24
25
static bool
26
tst_ihave_validate(struct sieve_validator *valdtr, struct sieve_command *tst);
27
static bool
28
tst_ihave_validate_const(struct sieve_validator *valdtr,
29
       struct sieve_command *tst, int *const_current,
30
       int const_next);
31
static bool
32
tst_ihave_generate(const struct sieve_codegen_env *cgenv,
33
       struct sieve_command *tst);
34
35
const struct sieve_command_def ihave_test = {
36
  .identifier = "ihave",
37
  .type = SCT_TEST,
38
  .positional_args = 1,
39
  .subtests = 0,
40
  .block_allowed = FALSE,
41
  .block_required = FALSE,
42
  .validate = tst_ihave_validate,
43
  .validate_const = tst_ihave_validate_const,
44
  .generate = tst_ihave_generate,
45
};
46
47
/*
48
 * Ihave operation
49
 */
50
51
static bool
52
tst_ihave_operation_dump(const struct sieve_dumptime_env *denv,
53
       sieve_size_t *address);
54
static int
55
tst_ihave_operation_execute(const struct sieve_runtime_env *renv,
56
          sieve_size_t *address);
57
58
const struct sieve_operation_def tst_ihave_operation = {
59
  .mnemonic = "IHAVE",
60
  .ext_def = &ihave_extension,
61
  .code = EXT_IHAVE_OPERATION_IHAVE,
62
  .dump = tst_ihave_operation_dump,
63
  .execute = tst_ihave_operation_execute,
64
};
65
66
/*
67
 * Code validation
68
 */
69
70
static bool
71
tst_ihave_validate(struct sieve_validator *valdtr, struct sieve_command *tst)
72
0
{
73
0
  struct _capability {
74
0
    const struct sieve_extension *ext;
75
0
    struct sieve_ast_argument *arg;
76
0
  };
77
0
  struct sieve_ast_argument *arg = tst->first_positional;
78
0
  struct sieve_ast_argument *stritem;
79
0
  enum sieve_compile_flags cpflags =
80
0
    sieve_validator_compile_flags(valdtr);
81
0
  bool no_global = ((cpflags & SIEVE_COMPILE_FLAG_NOGLOBAL) != 0);
82
0
  ARRAY(struct _capability) capabilities;
83
0
  struct _capability capability;
84
0
  const struct _capability *caps;
85
0
  unsigned int i, count;
86
0
  bool all_known = TRUE;
87
88
0
  t_array_init(&capabilities, 64);
89
90
0
  tst->data = (void *)FALSE;
91
92
  /* Check stringlist argument */
93
0
  if (!sieve_validate_positional_argument(valdtr, tst, arg,
94
0
            "capabilities", 1,
95
0
            SAAT_STRING_LIST))
96
0
    return FALSE;
97
98
0
  switch (sieve_ast_argument_type(arg)) {
99
0
  case SAAT_STRING:
100
    /* Single string */
101
0
    capability.arg = arg;
102
0
    capability.ext = sieve_extension_get_by_name(
103
0
      tst->ext->svinst, sieve_ast_argument_strc(arg));
104
105
0
    if (capability.ext == NULL ||
106
0
        (no_global && capability.ext->global)) {
107
0
      all_known = FALSE;
108
109
0
      ext_ihave_ast_add_missing_extension(
110
0
        tst->ext, tst->ast_node->ast,
111
0
        sieve_ast_argument_strc(arg));
112
0
    } else {
113
0
      array_append(&capabilities, &capability, 1);
114
0
    }
115
116
0
    break;
117
118
0
  case SAAT_STRING_LIST:
119
    /* String list */
120
0
    stritem = sieve_ast_strlist_first(arg);
121
122
0
    while (stritem != NULL) {
123
0
      capability.arg = stritem;
124
0
      capability.ext = sieve_extension_get_by_name(
125
0
        tst->ext->svinst,
126
0
        sieve_ast_argument_strc(stritem));
127
128
0
      if (capability.ext == NULL ||
129
0
          (no_global && capability.ext->global)) {
130
0
        all_known = FALSE;
131
132
0
        ext_ihave_ast_add_missing_extension(
133
0
          tst->ext, tst->ast_node->ast,
134
0
          sieve_ast_argument_strc(stritem));
135
0
      } else {
136
0
        array_append(&capabilities, &capability, 1);
137
0
      }
138
0
      stritem = sieve_ast_strlist_next(stritem);
139
0
    }
140
141
0
    break;
142
0
  default:
143
0
    i_unreached();
144
0
  }
145
146
0
  if (!all_known)
147
0
    return TRUE;
148
149
  /* RFC 5463, Section 4, page 4:
150
151
     The "ihave" extension is designed to be used with other extensions
152
     that add tests, actions, comparators, or arguments.  Implementations
153
     MUST NOT allow it to be used with extensions that change the
154
     underlying Sieve grammar, or extensions like encoded-character
155
     [RFC5228], or variables [RFC5229] that change how the content of
156
     Sieve scripts are interpreted.  The test MUST fail and the extension
157
     MUST NOT be enabled if such usage is attempted.
158
159
     FIXME: current implementation of this restriction is hardcoded and
160
     therefore highly inflexible
161
   */
162
0
  caps = array_get(&capabilities, &count);
163
0
  for (i = 0; i < count; i++) {
164
0
    if (sieve_extension_name_is(caps[i].ext, "variables") ||
165
0
        sieve_extension_name_is(caps[i].ext, "encoded-character"))
166
0
      return TRUE;
167
0
  }
168
169
  /* Load all extensions */
170
0
  caps = array_get(&capabilities, &count);
171
0
  for (i = 0; i < count; i++) {
172
0
    if (!sieve_validator_extension_load(valdtr, tst, caps[i].arg,
173
0
                caps[i].ext, FALSE))
174
0
      return FALSE;
175
0
  }
176
177
0
  if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE))
178
0
    return FALSE;
179
180
0
  tst->data = (void *)TRUE;
181
0
  return TRUE;
182
0
}
183
184
static bool
185
tst_ihave_validate_const(struct sieve_validator *valdtr ATTR_UNUSED,
186
       struct sieve_command *tst, int *const_current,
187
       int const_next ATTR_UNUSED)
188
0
{
189
0
  if ((bool)tst->data == TRUE)
190
0
    *const_current = -1;
191
0
  else
192
0
    *const_current = 0;
193
0
  return TRUE;
194
0
}
195
196
/*
197
 * Code generation
198
 */
199
200
bool tst_ihave_generate(const struct sieve_codegen_env *cgenv,
201
      struct sieve_command *tst)
202
0
{
203
  /* Emit opcode */
204
0
  sieve_operation_emit(cgenv->sblock, tst->ext, &tst_ihave_operation);
205
206
  /* Generate arguments */
207
0
  return sieve_generate_arguments(cgenv, tst, NULL);
208
0
}
209
210
/*
211
 * Code dump
212
 */
213
214
static bool
215
tst_ihave_operation_dump(const struct sieve_dumptime_env *denv,
216
       sieve_size_t *address)
217
0
{
218
0
  sieve_code_dumpf(denv, "IHAVE");
219
0
  sieve_code_descend(denv);
220
221
0
  return sieve_opr_stringlist_dump(denv, address, "capabilities");
222
0
}
223
224
/*
225
 * Code execution
226
 */
227
228
static int
229
tst_ihave_operation_execute(const struct sieve_runtime_env *renv,
230
          sieve_size_t *address)
231
0
{
232
0
  const struct sieve_execute_env *eenv = renv->exec_env;
233
0
  struct sieve_instance *svinst = eenv->svinst;
234
0
  struct sieve_stringlist *capabilities;
235
0
  string_t *cap_item;
236
0
  bool matched;
237
0
  int ret;
238
239
  /*
240
   * Read operands
241
   */
242
243
  /* Read capabilities */
244
0
  if ((ret = sieve_opr_stringlist_read(renv, address, "capabilities",
245
0
               &capabilities)) <= 0)
246
0
    return ret;
247
248
  /*
249
   * Perform test
250
   */
251
252
  /* Perform the test */
253
0
  sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "ihave test");
254
0
  sieve_runtime_trace_descend(renv);
255
256
0
  cap_item = NULL;
257
0
  matched = TRUE;
258
0
  while (matched &&
259
0
         (ret = sieve_stringlist_next_item(capabilities,
260
0
             &cap_item)) > 0) {
261
0
    const struct sieve_extension *ext;
262
0
    int sret;
263
264
0
    ext = sieve_extension_get_by_name(svinst, str_c(cap_item));
265
0
    if (ext == NULL) {
266
0
      sieve_runtime_trace_error(
267
0
        renv, "ihave: invalid extension name");
268
0
      return SIEVE_EXEC_BIN_CORRUPT;
269
0
    }
270
0
    sret = sieve_interpreter_extension_start(renv->interp, ext);
271
0
    if (sret == SIEVE_EXEC_FAILURE) {
272
0
      sieve_runtime_trace(
273
0
        renv, SIEVE_TRLVL_TESTS,
274
0
        "extension '%s' not available",
275
0
        sieve_extension_name(ext));
276
0
      matched = FALSE;
277
0
    } else if (sret == SIEVE_EXEC_OK) {
278
0
      sieve_runtime_trace(
279
0
        renv, SIEVE_TRLVL_TESTS,
280
0
        "extension '%s' available",
281
0
        sieve_extension_name(ext));
282
0
    } else {
283
0
      return sret;
284
0
    }
285
0
  }
286
0
  if (ret < 0) {
287
0
    sieve_runtime_trace_error(renv, "invalid capabilities item");
288
0
    return SIEVE_EXEC_BIN_CORRUPT;
289
0
  }
290
291
  /* Set test result for subsequent conditional jump */
292
0
  sieve_interpreter_set_test_result(renv->interp, matched);
293
0
  return SIEVE_EXEC_OK;
294
0
}