Coverage Report

Created: 2026-09-14 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/lib-sieve/plugins/variables/tst-string.c
Line
Count
Source
1
/* Copyright (c) Pigeonhole authors, see top-level COPYING file */
2
3
#include "sieve-common.h"
4
#include "sieve-commands.h"
5
#include "sieve-stringlist.h"
6
#include "sieve-code.h"
7
#include "sieve-comparators.h"
8
#include "sieve-match-types.h"
9
#include "sieve-validator.h"
10
#include "sieve-generator.h"
11
#include "sieve-interpreter.h"
12
#include "sieve-dump.h"
13
#include "sieve-match.h"
14
15
#include "ext-variables-common.h"
16
17
/*
18
 * String test
19
 *
20
 * Syntax:
21
 *   string [COMPARATOR] [MATCH-TYPE]
22
 *     <source: string-list> <key-list: string-list>
23
 */
24
25
static bool
26
tst_string_registered(struct sieve_validator *valdtr,
27
          const struct sieve_extension *ext,
28
          struct sieve_command_registration *cmd_reg);
29
static bool
30
tst_string_validate(struct sieve_validator *valdtr, struct sieve_command *tst);
31
static bool
32
tst_string_generate(const struct sieve_codegen_env *cgenv,
33
        struct sieve_command *ctx);
34
35
const struct sieve_command_def tst_string = {
36
  .identifier = "string",
37
  .type = SCT_TEST,
38
  .positional_args = 2,
39
  .subtests = 0,
40
  .block_allowed = FALSE,
41
  .block_required = FALSE,
42
  .registered = tst_string_registered,
43
  .validate = tst_string_validate,
44
  .generate = tst_string_generate,
45
};
46
47
/*
48
 * String operation
49
 */
50
51
static bool
52
tst_string_operation_dump(const struct sieve_dumptime_env *denv,
53
        sieve_size_t *address);
54
static int
55
tst_string_operation_execute(const struct sieve_runtime_env *renv,
56
           sieve_size_t *address);
57
58
const struct sieve_operation_def tst_string_operation = {
59
  .mnemonic = "STRING",
60
  .ext_def = &variables_extension,
61
  .code = EXT_VARIABLES_OPERATION_STRING,
62
  .dump = tst_string_operation_dump,
63
  .execute = tst_string_operation_execute,
64
};
65
66
/*
67
 * Optional arguments
68
 */
69
70
enum tst_string_optional {
71
  OPT_END,
72
  OPT_COMPARATOR,
73
  OPT_MATCH_TYPE
74
};
75
76
/*
77
 * Test registration
78
 */
79
80
static bool
81
tst_string_registered(struct sieve_validator *valdtr,
82
          const struct sieve_extension *ext ATTR_UNUSED,
83
          struct sieve_command_registration *cmd_reg)
84
0
{
85
  /* The order of these is not significant */
86
0
  sieve_comparators_link_tag(valdtr, cmd_reg, OPT_COMPARATOR);
87
0
  sieve_match_types_link_tags(valdtr, cmd_reg, OPT_MATCH_TYPE);
88
89
0
  return TRUE;
90
0
}
91
92
/*
93
 * Test validation
94
 */
95
96
static bool
97
tst_string_validate(struct sieve_validator *valdtr, struct sieve_command *tst)
98
0
{
99
0
  struct sieve_ast_argument *arg = tst->first_positional;
100
0
  const struct sieve_match_type mcht_default =
101
0
    SIEVE_MATCH_TYPE_DEFAULT(is_match_type);
102
0
  const struct sieve_comparator cmp_default =
103
0
    SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator);
104
105
0
  if (!sieve_validate_positional_argument(valdtr, tst, arg, "source",
106
0
            1, SAAT_STRING_LIST))
107
0
    return FALSE;
108
0
  if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE))
109
0
    return FALSE;
110
111
0
  arg = sieve_ast_argument_next(arg);
112
113
0
  if (!sieve_validate_positional_argument(valdtr, tst, arg, "key list",
114
0
            2, SAAT_STRING_LIST))
115
0
    return FALSE;
116
0
  if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE))
117
0
    return FALSE;
118
119
  /* Validate the key argument to a specified match type */
120
0
  return sieve_match_type_validate(valdtr, tst, arg,
121
0
           &mcht_default, &cmp_default);
122
0
}
123
124
/*
125
 * Test generation
126
 */
127
128
static bool
129
tst_string_generate(const struct sieve_codegen_env *cgenv,
130
        struct sieve_command *cmd)
131
0
{
132
0
  sieve_operation_emit(cgenv->sblock, cmd->ext, &tst_string_operation);
133
134
  /* Generate arguments */
135
0
  if (!sieve_generate_arguments(cgenv, cmd, NULL))
136
0
    return FALSE;
137
0
  return TRUE;
138
0
}
139
140
/*
141
 * Code dump
142
 */
143
144
static bool
145
tst_string_operation_dump(const struct sieve_dumptime_env *denv,
146
        sieve_size_t *address)
147
0
{
148
0
  sieve_code_dumpf(denv, "STRING-TEST");
149
0
  sieve_code_descend(denv);
150
151
  /* Optional operands */
152
0
  if (sieve_match_opr_optional_dump(denv, address, NULL) != 0)
153
0
    return FALSE;
154
155
0
  return (sieve_opr_stringlist_dump(denv, address, "source") &&
156
0
    sieve_opr_stringlist_dump(denv, address, "key list"));
157
0
}
158
159
/*
160
 * Code execution
161
 */
162
163
static int
164
tst_string_stringlist_next_item(struct sieve_stringlist *_strlist,
165
        string_t **str_r);
166
static void tst_string_stringlist_reset(struct sieve_stringlist *_strlist);
167
static int tst_string_stringlist_get_length(struct sieve_stringlist *_strlist);
168
169
struct tst_string_stringlist {
170
  struct sieve_stringlist strlist;
171
172
  struct sieve_stringlist *value_list;
173
};
174
175
static struct sieve_stringlist *
176
tst_string_stringlist_create(const struct sieve_runtime_env *renv,
177
           struct sieve_stringlist *value_list)
178
0
{
179
0
  struct tst_string_stringlist *strlist;
180
181
0
  strlist = t_new(struct tst_string_stringlist, 1);
182
0
  strlist->strlist.runenv = renv;
183
0
  strlist->strlist.exec_status = SIEVE_EXEC_OK;
184
0
  strlist->strlist.next_item = tst_string_stringlist_next_item;
185
0
  strlist->strlist.reset = tst_string_stringlist_reset;
186
0
  strlist->strlist.get_length = tst_string_stringlist_get_length;
187
0
  strlist->value_list = value_list;
188
189
0
  return &strlist->strlist;
190
0
}
191
192
static int
193
tst_string_stringlist_next_item(struct sieve_stringlist *_strlist,
194
        string_t **str_r)
195
0
{
196
0
  struct tst_string_stringlist *strlist =
197
0
    (struct tst_string_stringlist *)_strlist;
198
199
0
  return sieve_stringlist_next_item(strlist->value_list, str_r);
200
0
}
201
202
static void tst_string_stringlist_reset(struct sieve_stringlist *_strlist)
203
0
{
204
0
  struct tst_string_stringlist *strlist =
205
0
    (struct tst_string_stringlist *)_strlist;
206
207
0
  sieve_stringlist_reset(strlist->value_list);
208
0
}
209
210
static int tst_string_stringlist_get_length(struct sieve_stringlist *_strlist)
211
0
{
212
0
  struct tst_string_stringlist *strlist =
213
0
    (struct tst_string_stringlist *)_strlist;
214
0
  string_t *item;
215
0
  int length = 0;
216
0
  int ret;
217
218
0
  while ((ret = sieve_stringlist_next_item(strlist->value_list,
219
0
             &item)) > 0) {
220
0
    if (str_len(item) > 0)
221
0
      length++;
222
0
  }
223
0
  return (ret < 0 ? -1 : length);
224
0
}
225
226
static int
227
tst_string_operation_execute(const struct sieve_runtime_env *renv,
228
           sieve_size_t *address)
229
0
{
230
0
  struct sieve_match_type mcht =
231
0
    SIEVE_MATCH_TYPE_DEFAULT(is_match_type);
232
0
  struct sieve_comparator cmp =
233
0
    SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator);
234
0
  struct sieve_stringlist *source, *value_list, *key_list;
235
0
  int match, ret;
236
237
  /*
238
   * Read operands
239
   */
240
241
  /* Handle match-type and comparator operands */
242
0
  if (sieve_match_opr_optional_read(renv, address, NULL,
243
0
            &ret, &cmp, &mcht) < 0)
244
0
    return ret;
245
246
  /* Read source */
247
0
  ret = sieve_opr_stringlist_read(renv, address, "source", &source);
248
0
  if (ret <= 0)
249
0
    return ret;
250
251
  /* Read key-list */
252
0
  ret = sieve_opr_stringlist_read(renv, address, "key-list", &key_list);
253
0
  if (ret <= 0)
254
0
    return ret;
255
256
  /*
257
   * Perform operation
258
   */
259
260
0
  sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "string test");
261
262
  /* Create wrapper string list wich does not count empty string items */
263
0
  value_list = tst_string_stringlist_create(renv, source);
264
265
  /* Perform match */
266
0
  match = sieve_match(renv, &mcht, &cmp, value_list, key_list, &ret);
267
0
  if (match < 0)
268
0
    return ret;
269
270
  /* Set test result for subsequent conditional jump */
271
0
  sieve_interpreter_set_test_result(renv->interp, match > 0);
272
0
  return SIEVE_EXEC_OK;
273
0
}