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/sieve-commands.c
Line
Count
Source
1
/* Copyright (c) Pigeonhole authors, see top-level COPYING file */
2
3
#include "lib.h"
4
#include "str.h"
5
#include "str-sanitize.h"
6
7
#include "rfc2822.h"
8
9
#include "sieve-common.h"
10
#include "sieve-ast.h"
11
#include "sieve-validator.h"
12
#include "sieve-generator.h"
13
#include "sieve-binary.h"
14
#include "sieve-commands.h"
15
#include "sieve-code.h"
16
#include "sieve-interpreter.h"
17
18
/*
19
 * Literal arguments
20
 */
21
22
/* Forward declarations */
23
24
static bool
25
arg_number_generate(const struct sieve_codegen_env *cgenv,
26
        struct sieve_ast_argument *arg,
27
        struct sieve_command *context);
28
static bool
29
arg_string_generate(const struct sieve_codegen_env *cgenv,
30
        struct sieve_ast_argument *arg,
31
        struct sieve_command *context);
32
static bool
33
arg_string_list_validate(struct sieve_validator *valdtr,
34
       struct sieve_ast_argument **arg,
35
       struct sieve_command *context);
36
static bool
37
arg_string_list_generate(const struct sieve_codegen_env *cgenv,
38
       struct sieve_ast_argument *arg,
39
       struct sieve_command *context);
40
41
/* Argument objects */
42
43
const struct sieve_argument_def number_argument = {
44
  .identifier = "@number",
45
  .generate = arg_number_generate
46
};
47
48
const struct sieve_argument_def string_argument = {
49
  .identifier = "@string",
50
  .generate = arg_string_generate
51
};
52
53
const struct sieve_argument_def string_list_argument = {
54
  .identifier = "@string-list",
55
  .validate = arg_string_list_validate,
56
  .generate = arg_string_list_generate
57
};
58
59
/* Argument implementations */
60
61
static bool
62
arg_number_generate(const struct sieve_codegen_env *cgenv,
63
        struct sieve_ast_argument *arg,
64
        struct sieve_command *cmd ATTR_UNUSED)
65
0
{
66
0
  sieve_opr_number_emit(cgenv->sblock, sieve_ast_argument_number(arg));
67
0
  return TRUE;
68
0
}
69
70
static bool
71
arg_string_generate(const struct sieve_codegen_env *cgenv,
72
        struct sieve_ast_argument *arg,
73
        struct sieve_command *cmd ATTR_UNUSED)
74
0
{
75
0
  sieve_opr_string_emit(cgenv->sblock, sieve_ast_argument_str(arg));
76
0
  return TRUE;
77
0
}
78
79
static bool
80
arg_string_list_validate(struct sieve_validator *valdtr,
81
       struct sieve_ast_argument **arg,
82
       struct sieve_command *cmd)
83
0
{
84
0
  struct sieve_ast_argument *stritem;
85
86
0
  stritem = sieve_ast_strlist_first(*arg);
87
0
  while (stritem != NULL) {
88
0
    if (!sieve_validator_argument_activate(valdtr, cmd,
89
0
                   stritem, FALSE))
90
0
      return FALSE;
91
92
0
    stritem = sieve_ast_strlist_next(stritem);
93
0
  }
94
0
  return TRUE;
95
0
}
96
97
static bool
98
emit_string_list_operand(const struct sieve_codegen_env *cgenv,
99
       const struct sieve_ast_argument *strlist,
100
       struct sieve_command *cmd)
101
0
{
102
0
  void *list_context;
103
0
  struct sieve_ast_argument *stritem;
104
105
0
  sieve_opr_stringlist_emit_start(cgenv->sblock,
106
0
          sieve_ast_strlist_count(strlist),
107
0
          &list_context);
108
109
0
  stritem = sieve_ast_strlist_first(strlist);
110
0
  while (stritem != NULL) {
111
0
    if (!sieve_generate_argument(cgenv, stritem, cmd))
112
0
      return FALSE;
113
114
0
    stritem = sieve_ast_strlist_next(stritem);
115
0
  }
116
117
0
  sieve_opr_stringlist_emit_end(cgenv->sblock, list_context);
118
0
  return TRUE;
119
0
}
120
121
static bool
122
arg_string_list_generate(const struct sieve_codegen_env *cgenv,
123
       struct sieve_ast_argument *arg,
124
       struct sieve_command *cmd)
125
0
{
126
0
  if (sieve_ast_argument_type(arg) == SAAT_STRING) {
127
0
    return sieve_generate_argument(cgenv, arg, cmd);
128
0
  } else if (sieve_ast_argument_type(arg) == SAAT_STRING_LIST) {
129
0
    bool result = TRUE;
130
131
0
    if (sieve_ast_strlist_count(arg) == 1) {
132
0
      return sieve_generate_argument(
133
0
        cgenv, sieve_ast_strlist_first(arg), cmd);
134
0
    } else T_BEGIN {
135
0
      result = emit_string_list_operand(cgenv, arg, cmd);
136
0
    } T_END;
137
138
0
    return result;
139
0
  }
140
0
  return FALSE;
141
0
}
142
143
/*
144
 * Abstract arguments
145
 *
146
 *   (Generated by processing and not by parsing the grammar)
147
 */
148
149
/* Catenated string */
150
151
struct sieve_arg_catenated_string {
152
  struct sieve_ast_arg_list *str_parts;
153
};
154
155
struct sieve_arg_catenated_string *
156
sieve_arg_catenated_string_create(struct sieve_ast_argument *orig_arg)
157
0
{
158
0
  pool_t pool = sieve_ast_pool(orig_arg->ast);
159
0
  struct sieve_ast_arg_list *arglist;
160
0
  struct sieve_arg_catenated_string *catstr;
161
162
0
  arglist = sieve_ast_arg_list_create(pool);
163
164
0
  catstr = p_new(pool, struct sieve_arg_catenated_string, 1);
165
0
  catstr->str_parts = arglist;
166
0
  (orig_arg)->argument->data = catstr;
167
168
0
  return catstr;
169
0
}
170
171
void sieve_arg_catenated_string_add_element(
172
  struct sieve_arg_catenated_string *catstr,
173
  struct sieve_ast_argument *element)
174
0
{
175
0
  i_assert(catstr->str_parts != NULL);
176
0
  sieve_ast_arg_list_add(catstr->str_parts, element);
177
0
}
178
179
0
#define _cat_string_first(catstr) __AST_LIST_FIRST((catstr)->str_parts)
180
0
#define _cat_string_count(catstr) __AST_LIST_COUNT((catstr)->str_parts)
181
0
#define _cat_string_next(item) __AST_LIST_NEXT(item)
182
183
bool sieve_arg_catenated_string_generate(const struct sieve_codegen_env *cgenv,
184
           struct sieve_ast_argument *arg,
185
           struct sieve_command *cmd)
186
0
{
187
0
  struct sieve_arg_catenated_string *catstr =
188
0
    (struct sieve_arg_catenated_string *)arg->argument->data;
189
0
  struct sieve_ast_argument *strpart;
190
191
0
  if (_cat_string_count(catstr) == 1)
192
0
    sieve_generate_argument(cgenv, _cat_string_first(catstr), cmd);
193
0
  else {
194
0
    sieve_opr_catenated_string_emit(cgenv->sblock,
195
0
            _cat_string_count(catstr));
196
197
0
    strpart = _cat_string_first(catstr);
198
0
    while (strpart != NULL) {
199
0
      if (!sieve_generate_argument(cgenv, strpart, cmd))
200
0
        return FALSE;
201
202
0
      strpart = _cat_string_next(strpart);
203
0
    }
204
0
  }
205
0
  return TRUE;
206
0
}
207
208
/*
209
 * Argument creation
210
 */
211
212
struct sieve_argument *
213
sieve_argument_create(struct sieve_ast *ast,
214
          const struct sieve_argument_def *def,
215
          const struct sieve_extension *ext, int id_code)
216
0
{
217
0
  struct sieve_argument *arg;
218
0
  pool_t pool;
219
220
0
  pool = sieve_ast_pool(ast);
221
0
  arg = p_new(pool, struct sieve_argument, 1);
222
0
  arg->def = def;
223
0
  arg->ext = ext;
224
0
  arg->id_code = id_code;
225
226
0
  return arg;
227
0
}
228
229
/*
230
 * Core tests and commands
231
 */
232
233
const struct sieve_command_def *sieve_core_tests[] = {
234
  &tst_false, &tst_true,
235
  &tst_not, &tst_anyof, &tst_allof,
236
  &tst_address, &tst_header, &tst_exists, &tst_size
237
};
238
239
const unsigned int sieve_core_tests_count = N_ELEMENTS(sieve_core_tests);
240
241
const struct sieve_command_def *sieve_core_commands[] = {
242
  &cmd_require,
243
  &cmd_stop, &cmd_if, &cmd_elsif, &cmd_else,
244
  &cmd_keep, &cmd_discard, &cmd_redirect
245
};
246
247
const unsigned int sieve_core_commands_count = N_ELEMENTS(sieve_core_commands);
248
249
/*
250
 * Command context
251
 */
252
253
struct sieve_command *sieve_command_prev(struct sieve_command *cmd)
254
0
{
255
0
  struct sieve_ast_node *node = sieve_ast_node_prev(cmd->ast_node);
256
257
0
  if (node != NULL)
258
0
    return node->command;
259
0
  return NULL;
260
0
}
261
262
struct sieve_command *sieve_command_parent(struct sieve_command *cmd)
263
0
{
264
0
  struct sieve_ast_node *node = sieve_ast_node_parent(cmd->ast_node);
265
266
0
  return (node != NULL ? node->command : NULL);
267
0
}
268
269
struct sieve_command *
270
sieve_command_create(struct sieve_ast_node *cmd_node,
271
         const struct sieve_extension *ext,
272
         const struct sieve_command_def *cmd_def,
273
         struct sieve_command_registration *cmd_reg)
274
0
{
275
0
  struct sieve_command *cmd;
276
277
0
  cmd = p_new(sieve_ast_node_pool(cmd_node), struct sieve_command, 1);
278
279
0
  cmd->ast_node = cmd_node;
280
0
  cmd->def = cmd_def;
281
0
  cmd->ext = ext;
282
0
  cmd->reg = cmd_reg;
283
284
0
  cmd->block_exit_command = NULL;
285
286
0
  return cmd;
287
0
}
288
289
const char *sieve_command_def_type_name(const struct sieve_command_def *cmd_def)
290
0
{
291
0
  switch (cmd_def->type) {
292
0
  case SCT_NONE:
293
0
    return "command of unspecified type (bug)";
294
0
  case SCT_TEST:
295
0
    return "test";
296
0
  case SCT_COMMAND:
297
0
    return "command";
298
0
  case SCT_HYBRID:
299
0
    return "command or test";
300
0
  default:
301
0
    break;
302
0
  }
303
0
  return "??COMMAND-TYPE??";
304
0
}
305
306
const char *sieve_command_type_name(const struct sieve_command *cmd)
307
0
{
308
0
  switch (cmd->def->type) {
309
0
  case SCT_NONE:
310
0
    return "command of unspecified type (bug)";
311
0
  case SCT_TEST:
312
0
    return "test";
313
0
  case SCT_COMMAND:
314
0
    return "command";
315
0
  case SCT_HYBRID:
316
0
    if (cmd->ast_node->type == SAT_TEST)
317
0
      return "test";
318
0
    return "command";
319
0
  default:
320
0
    break;
321
0
  }
322
0
  return "??COMMAND-TYPE??";
323
0
}
324
325
struct sieve_ast_argument *
326
sieve_command_add_dynamic_tag(struct sieve_command *cmd,
327
            const struct sieve_extension *ext,
328
            const struct sieve_argument_def *tag,
329
            int id_code)
330
0
{
331
0
  struct sieve_ast_argument *arg;
332
333
0
  if (cmd->first_positional != NULL) {
334
0
    arg = sieve_ast_argument_tag_insert(cmd->first_positional,
335
0
                tag->identifier,
336
0
                cmd->ast_node->source_line);
337
0
  } else {
338
0
    arg = sieve_ast_argument_tag_create(cmd->ast_node,
339
0
                tag->identifier,
340
0
                cmd->ast_node->source_line);
341
0
  }
342
343
0
  arg->argument = sieve_argument_create(cmd->ast_node->ast, tag, ext,
344
0
                id_code);
345
0
  return arg;
346
0
}
347
348
struct sieve_ast_argument *
349
sieve_command_find_argument(struct sieve_command *cmd,
350
          const struct sieve_argument_def *arg_def)
351
0
{
352
0
  struct sieve_ast_argument *arg =
353
0
    sieve_ast_argument_first(cmd->ast_node);
354
355
  /* Visit tagged and optional arguments */
356
0
  while (arg != NULL) {
357
0
    if (arg->argument != NULL && arg->argument->def == arg_def)
358
0
      return arg;
359
360
0
    arg = sieve_ast_argument_next(arg);
361
0
  }
362
0
  return arg;
363
0
}
364
365
/* Use this function with caution. The command commits to exiting the block.
366
   When it for some reason does not, the interpretation will break later on,
367
   because exiting jumps are not generated when they would otherwise be
368
   necessary.
369
 */
370
void sieve_command_exit_block_unconditionally(struct sieve_command *cmd)
371
0
{
372
0
  struct sieve_command *parent = sieve_command_parent(cmd);
373
374
  /* Only the first unconditional exit is of importance */
375
0
  if (parent != NULL && parent->block_exit_command == NULL)
376
0
    parent->block_exit_command = cmd;
377
0
}
378
379
bool sieve_command_block_exits_unconditionally(struct sieve_command *cmd)
380
0
{
381
0
  return ( cmd->block_exit_command != NULL );
382
0
}
383
384
/*
385
 * Command utility functions
386
 */
387
388
/* NOTE: this may be moved */
389
390
static int
391
_verify_header_name_item(void *context, struct sieve_ast_argument *header)
392
0
{
393
0
  struct sieve_validator *valdtr = (struct sieve_validator *)context;
394
0
  string_t *name = sieve_ast_argument_str(header);
395
396
0
  if (sieve_argument_is_string_literal(header) &&
397
0
      !rfc2822_header_field_name_verify(str_c(name), str_len(name))) {
398
0
    sieve_argument_validate_warning(
399
0
      valdtr, header,
400
0
      "specified header field name '%s' is invalid",
401
0
      str_sanitize(str_c(name), 80));
402
0
    return 0;
403
0
  }
404
0
  return 1;
405
0
}
406
407
bool sieve_command_verify_headers_argument(struct sieve_validator *valdtr,
408
             struct sieve_ast_argument *headers)
409
0
{
410
0
  return (sieve_ast_stringlist_map(&headers, valdtr,
411
0
           _verify_header_name_item) >= 0);
412
0
}