Coverage Report

Created: 2026-06-09 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-arguments.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
#include "array.h"
8
9
#include "sieve-common.h"
10
#include "sieve-ast.h"
11
#include "sieve-commands.h"
12
#include "sieve-code.h"
13
#include "sieve-validator.h"
14
#include "sieve-generator.h"
15
#include "sieve-dump.h"
16
17
#include "ext-variables-common.h"
18
#include "ext-variables-limits.h"
19
#include "ext-variables-name.h"
20
#include "ext-variables-operands.h"
21
#include "ext-variables-namespaces.h"
22
#include "ext-variables-arguments.h"
23
24
/*
25
 * Variable argument implementation
26
 */
27
28
static bool
29
arg_variable_generate(const struct sieve_codegen_env *cgenv,
30
          struct sieve_ast_argument *arg,
31
          struct sieve_command *context);
32
33
const struct sieve_argument_def variable_argument = {
34
  .identifier = "@variable",
35
  .generate = arg_variable_generate,
36
};
37
38
static bool
39
ext_variables_variable_argument_activate(const struct sieve_extension *var_ext,
40
           const struct sieve_extension *this_ext,
41
           struct sieve_validator *valdtr,
42
           struct sieve_ast_argument *arg,
43
           const char *variable)
44
0
{
45
0
  struct sieve_ast *ast = arg->ast;
46
0
  struct sieve_variable *var;
47
48
0
  var = ext_variables_validator_declare_variable(
49
0
    this_ext, valdtr, variable);
50
51
0
  if (var == NULL) {
52
0
    sieve_argument_validate_error(
53
0
      valdtr, arg,
54
0
      "(implicit) declaration of new variable '%s' exceeds the limit "
55
0
      "(max variables: %u)", variable,
56
0
      sieve_variables_get_max_scope_count(var_ext));
57
0
    return FALSE;
58
0
  }
59
60
0
  arg->argument = sieve_argument_create(ast, &variable_argument,
61
0
                this_ext, 0);
62
0
  arg->argument->data = var;
63
0
  return TRUE;
64
0
}
65
66
static struct sieve_ast_argument *
67
ext_variables_variable_argument_create(const struct sieve_extension *this_ext,
68
               struct sieve_validator *valdtr,
69
               struct sieve_ast_argument *parent_arg,
70
               const char *variable)
71
0
{
72
0
  struct sieve_ast *ast = parent_arg->ast;
73
0
  struct sieve_ast_argument *new_arg;
74
75
0
  new_arg = sieve_ast_argument_create(
76
0
    ast, sieve_ast_argument_line(parent_arg));
77
0
  new_arg->type = SAAT_STRING;
78
79
0
  if (!ext_variables_variable_argument_activate(
80
0
    this_ext, this_ext, valdtr, new_arg, variable))
81
0
    return NULL;
82
83
0
  return new_arg;
84
0
}
85
86
static bool
87
arg_variable_generate(const struct sieve_codegen_env *cgenv,
88
          struct sieve_ast_argument *arg,
89
          struct sieve_command *context ATTR_UNUSED)
90
0
{
91
0
  struct sieve_argument *argument = arg->argument;
92
0
  struct sieve_variable *var = (struct sieve_variable *) argument->data;
93
94
0
  sieve_variables_opr_variable_emit(cgenv->sblock, argument->ext, var);
95
0
  return TRUE;
96
0
}
97
98
/*
99
 * Match value argument implementation
100
 */
101
102
static bool
103
arg_match_value_generate(const struct sieve_codegen_env *cgenv,
104
       struct sieve_ast_argument *arg,
105
       struct sieve_command *context ATTR_UNUSED);
106
107
const struct sieve_argument_def match_value_argument = {
108
  .identifier = "@match_value",
109
  .generate = arg_match_value_generate,
110
};
111
112
static bool
113
ext_variables_match_value_argument_activate(
114
  const struct sieve_extension *this_ext,
115
  struct sieve_validator *valdtr, struct sieve_ast_argument *arg,
116
  unsigned int index, bool assignment)
117
0
{
118
0
  struct sieve_ast *ast = arg->ast;
119
120
0
  if (assignment) {
121
0
    sieve_argument_validate_error(
122
0
      valdtr, arg, "cannot assign to match variable");
123
0
    return FALSE;
124
0
  }
125
126
0
  if (index > EXT_VARIABLES_MAX_MATCH_INDEX) {
127
0
    sieve_argument_validate_error(
128
0
      valdtr, arg, "match value index %u out of range "
129
0
      "(max: %u)", index, EXT_VARIABLES_MAX_MATCH_INDEX);
130
0
    return FALSE;
131
0
  }
132
133
0
  arg->argument = sieve_argument_create(ast, &match_value_argument,
134
0
                this_ext, 0);
135
0
  arg->argument->data = POINTER_CAST(index);
136
0
  return TRUE;
137
0
}
138
139
static struct sieve_ast_argument *
140
ext_variables_match_value_argument_create(
141
  const struct sieve_extension *this_ext, struct sieve_validator *valdtr,
142
  struct sieve_ast_argument *parent_arg, unsigned int index)
143
0
{
144
0
  struct sieve_ast *ast = parent_arg->ast;
145
0
  struct sieve_ast_argument *new_arg;
146
147
0
  new_arg = sieve_ast_argument_create(
148
0
    ast, sieve_ast_argument_line(parent_arg));
149
0
  new_arg->type = SAAT_STRING;
150
151
0
  if (!ext_variables_match_value_argument_activate(
152
0
    this_ext, valdtr, new_arg, index, FALSE))
153
0
    return NULL;
154
0
  return new_arg;
155
0
}
156
157
static bool
158
arg_match_value_generate(const struct sieve_codegen_env *cgenv,
159
       struct sieve_ast_argument *arg,
160
       struct sieve_command *context ATTR_UNUSED)
161
0
{
162
0
  struct sieve_argument *argument = arg->argument;
163
0
  unsigned int index = POINTER_CAST_TO(argument->data, unsigned int);
164
165
0
  sieve_variables_opr_match_value_emit(cgenv->sblock,
166
0
               argument->ext, index);
167
0
  return TRUE;
168
0
}
169
170
/*
171
 * Variable string argument implementation
172
 */
173
174
static bool
175
arg_variable_string_validate(struct sieve_validator *valdtr,
176
           struct sieve_ast_argument **arg,
177
           struct sieve_command *cmd);
178
179
const struct sieve_argument_def variable_string_argument = {
180
  .identifier = "@variable-string",
181
  .validate = arg_variable_string_validate,
182
  .generate = sieve_arg_catenated_string_generate,
183
};
184
185
static bool
186
arg_variable_string_validate(struct sieve_validator *valdtr,
187
           struct sieve_ast_argument **arg,
188
           struct sieve_command *cmd)
189
0
{
190
0
  const struct sieve_extension *this_ext = (*arg)->argument->ext;
191
0
  enum { ST_NONE, ST_OPEN, ST_VARIABLE, ST_CLOSE } state = ST_NONE;
192
0
  pool_t pool = sieve_ast_pool((*arg)->ast);
193
0
  struct sieve_arg_catenated_string *catstr = NULL;
194
0
  string_t *str = sieve_ast_argument_str(*arg);
195
0
  const char *p, *strstart, *substart = NULL;
196
0
  const char *strval = (const char *) str_data(str);
197
0
  const char *strend = strval + str_len(str);
198
0
  bool result = TRUE;
199
0
  ARRAY_TYPE(sieve_variable_name) substitution;
200
0
  int nelements = 0;
201
202
0
  T_BEGIN {
203
    /* Initialize substitution structure */
204
0
    t_array_init(&substitution, 2);
205
206
0
    p = strval;
207
0
    strstart = p;
208
0
    while (result && p < strend) {
209
0
      switch (state) {
210
      /* Nothing found yet */
211
0
      case ST_NONE:
212
0
        if (*p == '$') {
213
0
          substart = p;
214
0
          state = ST_OPEN;
215
0
        }
216
0
        p++;
217
0
        break;
218
      /* Got '$' */
219
0
      case ST_OPEN:
220
0
        if (*p == '{') {
221
0
          state = ST_VARIABLE;
222
0
          p++;
223
0
        } else
224
0
          state = ST_NONE;
225
0
        break;
226
      /* Got '${' */
227
0
      case ST_VARIABLE:
228
0
        nelements = ext_variable_name_parse(
229
0
          &substitution, &p, strend);
230
231
0
        if (nelements < 0)
232
0
          state = ST_NONE;
233
0
        else
234
0
          state = ST_CLOSE;
235
0
        break;
236
      /* Finished parsing name, expecting '}' */
237
0
      case ST_CLOSE:
238
0
        if (*p == '}') {
239
0
          struct sieve_ast_argument *strarg;
240
241
          /* We now know that the substitution is valid */
242
243
0
          if (catstr == NULL)
244
0
            catstr = sieve_arg_catenated_string_create(*arg);
245
246
          /* Add the substring that is before the substitution to the
247
             variable-string AST.
248
249
             FIXME: For efficiency, if the variable is not found we should
250
             coalesce this substring with the one after the substitution.
251
           */
252
0
          if (substart > strstart) {
253
0
            string_t *newstr = str_new(pool, substart - strstart);
254
0
            str_append_data(newstr, strstart, substart - strstart);
255
256
0
            strarg = sieve_ast_argument_string_create_raw(
257
0
              (*arg)->ast, newstr, (*arg)->source_line);
258
0
            sieve_arg_catenated_string_add_element(catstr, strarg);
259
260
            /* Give other substitution extensions a chance to do
261
               their work.
262
             */
263
0
            if (!sieve_validator_argument_activate_super(
264
0
              valdtr, cmd, strarg, FALSE)) {
265
0
              result = FALSE;
266
0
              break;
267
0
            }
268
0
          }
269
270
          /* Find the variable */
271
0
          if (nelements == 1) {
272
0
            const struct sieve_variable_name *cur_element =
273
0
              array_idx(&substitution, 0);
274
275
0
            if (cur_element->num_variable == -1) {
276
              /* Add variable argument '${identifier}' */
277
0
              strarg = ext_variables_variable_argument_create(
278
0
                this_ext, valdtr, *arg,
279
0
                str_c(cur_element->identifier));
280
281
0
            } else {
282
              /* Add match value argument '${000}' */
283
0
              strarg = ext_variables_match_value_argument_create(
284
0
                this_ext, valdtr, *arg,
285
0
                cur_element->num_variable);
286
0
            }
287
0
          } else {
288
0
            strarg = ext_variables_namespace_argument_create(
289
0
              this_ext, valdtr, *arg, cmd, &substitution);
290
0
          }
291
292
0
          if (strarg != NULL)
293
0
            sieve_arg_catenated_string_add_element(catstr, strarg);
294
295
0
          strstart = p + 1;
296
0
          substart = strstart;
297
298
0
          p++;
299
0
        }
300
301
        /* Finished, reset for the next substitution */
302
0
        state = ST_NONE;
303
0
      }
304
0
    }
305
0
  } T_END;
306
307
  /* Bail out early if substitution is invalid */
308
0
  if (!result)
309
0
    return FALSE;
310
311
  /* Check whether any substitutions were found */
312
0
  if (catstr == NULL) {
313
    /* No substitutions in this string, pass it on to any other
314
       substution extension.
315
     */
316
0
    return sieve_validator_argument_activate_super(
317
0
      valdtr, cmd, *arg, TRUE);
318
0
  }
319
320
  /* Add the final substring that comes after the last substitution to the
321
     variable-string AST.
322
   */
323
0
  if (strend > strstart) {
324
0
    struct sieve_ast_argument *strarg;
325
0
    string_t *newstr = str_new(pool, strend - strstart);
326
0
    str_append_data(newstr, strstart, strend - strstart);
327
328
0
    strarg = sieve_ast_argument_string_create_raw(
329
0
      (*arg)->ast, newstr, (*arg)->source_line);
330
0
    sieve_arg_catenated_string_add_element(catstr, strarg);
331
332
    /* Give other substitution extensions a chance to do their work.
333
     */
334
0
    if (!sieve_validator_argument_activate_super(
335
0
      valdtr, cmd, strarg, FALSE))
336
0
      return FALSE;
337
0
  }
338
339
0
  return TRUE;
340
0
}
341
342
/*
343
 * Variable argument interface
344
 */
345
346
static bool
347
_sieve_variable_argument_activate(const struct sieve_extension *var_ext,
348
          const struct sieve_extension *this_ext,
349
          struct sieve_validator *valdtr,
350
          struct sieve_command *cmd,
351
          struct sieve_ast_argument *arg,
352
          bool assignment)
353
0
{
354
0
  bool result = FALSE;
355
0
  string_t *variable;
356
0
  const char *varstr, *varend;
357
0
  ARRAY_TYPE(sieve_variable_name) vname;
358
0
  int nelements = 0;
359
360
0
  T_BEGIN {
361
0
    t_array_init(&vname, 2);
362
363
0
    variable = sieve_ast_argument_str(arg);
364
0
    varstr = str_c(variable);
365
0
    varend = PTR_OFFSET(varstr, str_len(variable));
366
0
    nelements = ext_variable_name_parse(&vname, &varstr, varend);
367
368
    /* Check whether name parsing succeeded */
369
0
    if (nelements <= 0 || varstr != varend) {
370
      /* Parse failed */
371
0
      sieve_argument_validate_error(
372
0
        valdtr, arg,
373
0
        "invalid variable name '%s'",
374
0
        str_sanitize(str_c(variable),80));
375
0
    } else if (nelements == 1) {
376
      /* Normal (match) variable */
377
378
0
      const struct sieve_variable_name *cur_element =
379
0
        array_idx(&vname, 0);
380
381
0
      if (cur_element->num_variable < 0) {
382
        /* Variable */
383
0
        result = ext_variables_variable_argument_activate(
384
0
          var_ext, this_ext, valdtr, arg,
385
0
          str_c(cur_element->identifier));
386
387
0
      } else {
388
        /* Match value */
389
0
        result = ext_variables_match_value_argument_activate(
390
0
          this_ext, valdtr, arg,
391
0
          cur_element->num_variable, assignment);
392
0
      }
393
394
0
    } else {
395
      /* Namespace variable */
396
0
      result = ext_variables_namespace_argument_activate(
397
0
        this_ext, valdtr, arg, cmd, &vname, assignment);
398
0
    }
399
0
  } T_END;
400
401
0
  return result;
402
0
}
403
404
bool sieve_variable_argument_activate(const struct sieve_extension *var_ext,
405
              const struct sieve_extension *this_ext,
406
              struct sieve_validator *valdtr,
407
              struct sieve_command *cmd,
408
              struct sieve_ast_argument *arg,
409
              bool assignment)
410
0
{
411
0
  if (sieve_ast_argument_type(arg) == SAAT_STRING) {
412
    /* Single string */
413
0
    return _sieve_variable_argument_activate(
414
0
      var_ext, this_ext, valdtr, cmd, arg, assignment);
415
0
  } else if (sieve_ast_argument_type(arg) == SAAT_STRING_LIST) {
416
    /* String list */
417
0
    struct sieve_ast_argument *stritem;
418
419
0
    i_assert (!assignment);
420
421
0
    stritem = sieve_ast_strlist_first(arg);
422
0
    while (stritem != NULL) {
423
0
      if (!_sieve_variable_argument_activate(
424
0
        var_ext, this_ext, valdtr, cmd, stritem,
425
0
        assignment))
426
0
        return FALSE;
427
428
0
      stritem = sieve_ast_strlist_next(stritem);
429
0
    }
430
431
0
    arg->argument = sieve_argument_create(
432
0
      arg->ast, &string_list_argument, NULL, 0);
433
434
0
    return TRUE;
435
0
  }
436
437
0
  return FALSE;
438
0
}