Coverage Report

Created: 2026-08-08 06:43

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