Coverage Report

Created: 2026-05-30 06:37

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