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-common.c
Line
Count
Source
1
/* Copyright (c) Pigeonhole authors, see top-level COPYING file */
2
3
#include "lib.h"
4
#include "hash.h"
5
#include "str.h"
6
#include "array.h"
7
#include "settings.h"
8
9
#include "sieve-common.h"
10
11
#include "sieve-ast.h"
12
#include "sieve-binary.h"
13
#include "sieve-code.h"
14
#include "sieve-objects.h"
15
#include "sieve-match-types.h"
16
17
#include "sieve-commands.h"
18
#include "sieve-validator.h"
19
#include "sieve-generator.h"
20
#include "sieve-dump.h"
21
#include "sieve-interpreter.h"
22
23
#include "ext-variables-common.h"
24
#include "ext-variables-name.h"
25
#include "ext-variables-modifiers.h"
26
27
/*
28
 * Limits
29
 */
30
31
unsigned int
32
sieve_variables_get_max_scope_count(const struct sieve_extension *var_ext)
33
0
{
34
0
  const struct ext_variables_context *extctx =
35
0
    ext_variables_get_context(var_ext);
36
37
0
  return extctx->set->max_scope_count;
38
0
}
39
40
size_t sieve_variables_get_max_value_size(
41
  const struct sieve_extension *var_ext)
42
0
{
43
0
  const struct ext_variables_context *extctx =
44
0
    ext_variables_get_context(var_ext);
45
46
0
  return extctx->set->max_value_size;
47
0
}
48
49
/*
50
 * Extension configuration
51
 */
52
53
int ext_variables_load(const struct sieve_extension *ext, void **context_r)
54
0
{
55
0
  struct sieve_instance *svinst = ext->svinst;
56
0
  const struct ext_variables_settings *set;
57
0
  struct ext_variables_context *extctx;
58
0
  const char *error;
59
60
0
  if (settings_get(svinst->event, &ext_variables_setting_parser_info, 0,
61
0
       &set, &error) < 0) {
62
0
    e_error(svinst->event, "%s", error);
63
0
    return -1;
64
0
  }
65
66
0
  extctx = i_new(struct ext_variables_context, 1);
67
0
  extctx->set = set;
68
69
0
  *context_r = extctx;
70
0
  return 0;
71
0
}
72
73
void ext_variables_unload(const struct sieve_extension *ext)
74
0
{
75
0
  struct ext_variables_context *extctx = ext->context;
76
77
0
  if (extctx == NULL)
78
0
    return;
79
0
  settings_free(extctx->set);
80
0
  i_free(extctx);
81
0
}
82
83
const struct ext_variables_context *
84
ext_variables_get_context(const struct sieve_extension *var_ext)
85
0
{
86
0
  const struct ext_variables_context *extctx = var_ext->context;
87
88
0
  i_assert(var_ext->def == &variables_extension);
89
0
  return extctx;
90
0
}
91
92
/*
93
 * Variable scope
94
 */
95
96
struct sieve_variable_scope {
97
  pool_t pool;
98
  int refcount;
99
100
  struct sieve_instance *svinst;
101
  const struct sieve_extension *var_ext;
102
  const struct sieve_extension *ext;
103
104
  struct sieve_variable *error_var;
105
106
  HASH_TABLE(const char *, struct sieve_variable *) variables;
107
  ARRAY(struct sieve_variable *) variable_index;
108
};
109
110
struct sieve_variable_scope_binary {
111
  struct sieve_variable_scope *scope;
112
113
  unsigned int count;
114
  struct sieve_binary_block *sblock;
115
  sieve_size_t address;
116
};
117
118
struct sieve_variable_scope_iter {
119
  struct sieve_variable_scope *scope;
120
  struct hash_iterate_context *hctx;
121
};
122
123
struct sieve_variable_scope *
124
sieve_variable_scope_create(struct sieve_instance *svinst,
125
          const struct sieve_extension *var_ext,
126
          const struct sieve_extension *ext)
127
0
{
128
0
  struct sieve_variable_scope *scope;
129
0
  pool_t pool;
130
131
0
  i_assert(var_ext->def == &variables_extension);
132
133
0
  pool = pool_alloconly_create("sieve_variable_scope", 4096);
134
0
  scope = p_new(pool, struct sieve_variable_scope, 1);
135
0
  scope->pool = pool;
136
0
  scope->refcount = 1;
137
138
0
  scope->svinst = svinst;
139
0
  scope->var_ext = var_ext;
140
0
  scope->ext = ext;
141
142
0
  hash_table_create(&scope->variables, pool, 0, strcase_hash, strcasecmp);
143
0
  p_array_init(&scope->variable_index, pool, 128);
144
145
0
  return scope;
146
0
}
147
148
void sieve_variable_scope_ref(struct sieve_variable_scope *scope)
149
0
{
150
0
  scope->refcount++;
151
0
}
152
153
void sieve_variable_scope_unref(struct sieve_variable_scope **_scope)
154
0
{
155
0
  struct sieve_variable_scope *scope = *_scope;
156
157
0
  i_assert(scope->refcount > 0);
158
159
0
  if (--scope->refcount != 0)
160
0
    return;
161
162
0
  hash_table_destroy(&scope->variables);
163
164
0
  *_scope = NULL;
165
0
  pool_unref(&scope->pool);
166
0
}
167
168
pool_t sieve_variable_scope_pool(struct sieve_variable_scope *scope)
169
0
{
170
0
  return scope->pool;
171
0
}
172
173
struct sieve_variable *
174
sieve_variable_scope_declare(struct sieve_variable_scope *scope,
175
           const char *identifier)
176
0
{
177
0
  unsigned int max_scope_count;
178
0
  struct sieve_variable *var;
179
180
0
  var = hash_table_lookup(scope->variables, identifier);
181
0
  if (var != NULL)
182
0
    return var;
183
184
0
  max_scope_count = sieve_variables_get_max_scope_count(scope->var_ext);
185
0
  if (array_count(&scope->variable_index) >= max_scope_count) {
186
0
    if (scope->error_var == NULL) {
187
0
      var = p_new(scope->pool, struct sieve_variable, 1);
188
0
      var->identifier = "@ERROR@";
189
0
      var->index = 0;
190
191
0
      scope->error_var = var;
192
0
      return NULL;
193
0
    }
194
195
0
    return scope->error_var;
196
0
  }
197
198
0
  var = p_new(scope->pool, struct sieve_variable, 1);
199
0
  var->ext = scope->ext;
200
0
  var->identifier = p_strdup(scope->pool, identifier);
201
0
  var->index = array_count(&scope->variable_index);
202
203
0
  hash_table_insert(scope->variables, var->identifier, var);
204
0
  array_append(&scope->variable_index, &var, 1);
205
0
  return var;
206
0
}
207
208
struct sieve_variable *
209
sieve_variable_scope_get_variable(struct sieve_variable_scope *scope,
210
          const char *identifier)
211
0
{
212
0
  return hash_table_lookup(scope->variables, identifier);
213
0
}
214
215
struct sieve_variable *
216
sieve_variable_scope_import(struct sieve_variable_scope *scope,
217
          struct sieve_variable *var)
218
0
{
219
0
  struct sieve_variable *old_var, *new_var;
220
221
0
  old_var = sieve_variable_scope_get_variable(scope, var->identifier);
222
0
  if (old_var != NULL) {
223
0
    i_assert(memcmp(old_var, var, sizeof(*var)) == 0);
224
0
    return old_var;
225
0
  }
226
227
0
  new_var = p_new(scope->pool, struct sieve_variable, 1);
228
0
  memcpy(new_var, var, sizeof(*new_var));
229
230
0
  hash_table_insert(scope->variables, new_var->identifier, new_var);
231
232
  /* Not entered into the index because it is an external variable
233
     (This can be done unlimited; only limited by the size of the external
234
     scope)
235
   */
236
0
  return new_var;
237
0
}
238
239
struct sieve_variable_scope_iter *
240
sieve_variable_scope_iterate_init(struct sieve_variable_scope *scope)
241
0
{
242
0
  struct sieve_variable_scope_iter *iter;
243
244
0
  iter = t_new(struct sieve_variable_scope_iter, 1);
245
0
  iter->scope = scope;
246
0
  iter->hctx = hash_table_iterate_init(scope->variables);
247
248
0
  return iter;
249
0
}
250
251
bool sieve_variable_scope_iterate(struct sieve_variable_scope_iter *iter,
252
          struct sieve_variable **var_r)
253
0
{
254
0
  const char *key;
255
256
0
  return hash_table_iterate(iter->hctx, iter->scope->variables,
257
0
          &key, var_r);
258
0
}
259
260
void sieve_variable_scope_iterate_deinit(
261
  struct sieve_variable_scope_iter **iter)
262
0
{
263
0
  hash_table_iterate_deinit(&(*iter)->hctx);
264
0
  *iter = NULL;
265
0
}
266
267
unsigned int
268
sieve_variable_scope_declarations(struct sieve_variable_scope *scope)
269
0
{
270
0
  return hash_table_count(scope->variables);
271
0
}
272
273
unsigned int sieve_variable_scope_size(struct sieve_variable_scope *scope)
274
0
{
275
0
  return array_count(&scope->variable_index);
276
0
}
277
278
struct sieve_variable *const *
279
sieve_variable_scope_get_variables(struct sieve_variable_scope *scope,
280
           unsigned int *size_r)
281
0
{
282
0
  return array_get(&scope->variable_index, size_r);
283
0
}
284
285
struct sieve_variable *
286
sieve_variable_scope_get_indexed(struct sieve_variable_scope *scope,
287
         unsigned int index)
288
0
{
289
0
  struct sieve_variable *const *var;
290
291
0
  if (index >= array_count(&scope->variable_index))
292
0
    return NULL;
293
294
0
  var = array_idx(&scope->variable_index, index);
295
0
  return *var;
296
0
}
297
298
/* Scope binary */
299
300
struct sieve_variable_scope *
301
sieve_variable_scope_binary_dump(struct sieve_instance *svinst,
302
         const struct sieve_extension *var_ext,
303
         const struct sieve_extension *ext,
304
         const struct sieve_dumptime_env *denv,
305
         sieve_size_t *address)
306
0
{
307
0
  struct sieve_variable_scope *local_scope;
308
0
  unsigned int i, scope_size;
309
0
  sieve_size_t pc;
310
0
  sieve_offset_t end_offset;
311
312
  /* Read scope size */
313
0
  sieve_code_mark(denv);
314
0
  if (!sieve_binary_read_unsigned(denv->sblock, address, &scope_size))
315
0
    return NULL;
316
317
  /* Read offset */
318
0
  pc = *address;
319
0
  if (!sieve_binary_read_offset(denv->sblock, address, &end_offset))
320
0
    return NULL;
321
322
  /* Create scope */
323
0
  local_scope = sieve_variable_scope_create(svinst, var_ext, ext);
324
325
  /* Read and dump scope itself */
326
327
0
  sieve_code_dumpf(denv, "VARIABLES SCOPE [%u] (end: %08x)",
328
0
       scope_size, (unsigned int)(pc + end_offset));
329
330
0
  for (i = 0; i < scope_size; i++) {
331
0
    string_t *identifier;
332
333
0
    sieve_code_mark(denv);
334
0
    if (!sieve_binary_read_string(denv->sblock, address,
335
0
                &identifier))
336
0
      return NULL;
337
338
0
    sieve_code_dumpf(denv, "%3d: '%s'", i, str_c(identifier));
339
340
0
    (void)sieve_variable_scope_declare(local_scope,
341
0
               str_c(identifier));
342
0
  }
343
344
0
  return local_scope;
345
0
}
346
347
struct sieve_variable_scope_binary *
348
sieve_variable_scope_binary_create(struct sieve_variable_scope *scope)
349
0
{
350
0
  struct sieve_variable_scope_binary *scpbin;
351
352
0
  scpbin = p_new(scope->pool, struct sieve_variable_scope_binary, 1);
353
0
  scpbin->scope = scope;
354
355
0
  return scpbin;
356
0
}
357
358
void sieve_variable_scope_binary_ref(struct sieve_variable_scope_binary *scpbin)
359
0
{
360
0
  sieve_variable_scope_ref(scpbin->scope);
361
0
}
362
363
void sieve_variable_scope_binary_unref(
364
  struct sieve_variable_scope_binary **scpbin)
365
0
{
366
0
  sieve_variable_scope_unref(&(*scpbin)->scope);
367
0
  *scpbin = NULL;
368
0
}
369
370
struct sieve_variable_scope_binary *
371
sieve_variable_scope_binary_read(struct sieve_instance *svinst,
372
         const struct sieve_extension *var_ext,
373
         const struct sieve_extension *ext,
374
         struct sieve_binary_block *sblock,
375
         sieve_size_t *address)
376
0
{
377
0
  struct sieve_variable_scope *scope;
378
0
  struct sieve_variable_scope_binary *scpbin;
379
0
  unsigned int scope_count, max_scope_count;
380
0
  const char *ext_name = (ext == NULL ? "variables" :
381
0
        sieve_extension_name(ext));
382
0
  sieve_size_t pc;
383
0
  sieve_offset_t end_offset;
384
385
  /* Read scope size */
386
0
  if (!sieve_binary_read_unsigned(sblock, address, &scope_count)) {
387
0
    e_error(svinst->event, "%s: "
388
0
      "variable scope: failed to read count", ext_name);
389
0
    return NULL;
390
0
  }
391
392
  /* Check size limit */
393
0
  max_scope_count = sieve_variables_get_max_scope_count(var_ext);
394
0
  if (scope_count > max_scope_count) {
395
0
    e_error(svinst->event, "%s: "
396
0
      "variable scope: count exceeds the limit (%u > %u)",
397
0
      ext_name, scope_count, max_scope_count);
398
0
    return NULL;
399
0
  }
400
401
  /* Read offset */
402
0
  pc = *address;
403
0
  if (!sieve_binary_read_offset(sblock, address, &end_offset)) {
404
0
    e_error(svinst->event, "%s: "
405
0
      "variable scope: failed to read end offset", ext_name);
406
0
    return NULL;
407
0
  }
408
409
  /* Create scope */
410
0
  scope = sieve_variable_scope_create(svinst, var_ext, ext);
411
412
0
  scpbin = sieve_variable_scope_binary_create(scope);
413
0
  scpbin->count = scope_count;
414
0
  scpbin->sblock = sblock;
415
0
  scpbin->address = *address;
416
417
0
  *address = pc + end_offset;
418
419
0
  return scpbin;
420
0
}
421
422
struct sieve_variable_scope *
423
sieve_variable_scope_binary_get(struct sieve_variable_scope_binary *scpbin)
424
0
{
425
0
  const struct sieve_extension *ext = scpbin->scope->ext;
426
0
  struct sieve_instance *svinst = scpbin->scope->svinst;
427
0
  const char *ext_name = (ext == NULL ? "variables" :
428
0
        sieve_extension_name(ext));
429
0
  unsigned int i;
430
431
0
  if (scpbin->sblock != NULL) {
432
0
    sieve_size_t *address = &scpbin->address;
433
434
    /* Read scope itself */
435
0
    for (i = 0; i < scpbin->count; i++) {
436
0
      struct sieve_variable *var;
437
0
      string_t *identifier;
438
439
0
      if (!sieve_binary_read_string(scpbin->sblock, address,
440
0
                  &identifier)) {
441
0
        e_error(svinst->event, "%s: variable scope: "
442
0
          "failed to read variable name",
443
0
          ext_name);
444
0
        return NULL;
445
0
      }
446
447
0
      var = sieve_variable_scope_declare(scpbin->scope,
448
0
                 str_c(identifier));
449
450
0
      i_assert(var != NULL);
451
0
      i_assert(var->index == i);
452
0
    }
453
454
0
    scpbin->sblock = NULL;
455
0
  }
456
457
0
  return scpbin->scope;
458
0
}
459
460
unsigned int
461
sieve_variable_scope_binary_get_count(
462
  struct sieve_variable_scope_binary *scpbin)
463
0
{
464
0
  if (scpbin->sblock != NULL)
465
0
    return scpbin->count;
466
467
0
  return array_count(&scpbin->scope->variable_index);
468
0
}
469
470
/*
471
 * Variable storage
472
 */
473
474
struct sieve_variable_storage {
475
  pool_t pool;
476
  const struct sieve_extension *var_ext;
477
  struct sieve_variable_scope *scope;
478
  struct sieve_variable_scope_binary *scope_bin;
479
  unsigned int max_count;
480
  ARRAY(string_t *) var_values;
481
};
482
483
struct sieve_variable_storage *
484
sieve_variable_storage_create(const struct sieve_extension *var_ext,
485
            pool_t pool,
486
            struct sieve_variable_scope_binary *scpbin)
487
0
{
488
0
  struct sieve_variable_storage *storage;
489
490
0
  storage = p_new(pool, struct sieve_variable_storage, 1);
491
0
  storage->pool = pool;
492
0
  storage->var_ext = var_ext;
493
0
  storage->scope_bin = scpbin;
494
0
  storage->scope = NULL;
495
496
0
  storage->max_count = sieve_variable_scope_binary_get_count(scpbin);
497
498
0
  p_array_init(&storage->var_values, pool, 4);
499
500
0
  return storage;
501
0
}
502
503
static inline bool
504
sieve_variable_valid(struct sieve_variable_storage *storage,
505
         unsigned int index)
506
0
{
507
0
  if (storage->scope_bin == NULL)
508
0
    return TRUE;
509
510
0
  return (index < storage->max_count);
511
0
}
512
513
bool sieve_variable_get_identifier(struct sieve_variable_storage *storage,
514
           unsigned int index, const char **identifier)
515
0
{
516
0
  struct sieve_variable *const *var;
517
518
0
  *identifier = NULL;
519
520
0
  if (storage->scope_bin == NULL)
521
0
    return TRUE;
522
523
0
  if (storage->scope == NULL) {
524
0
    storage->scope =
525
0
      sieve_variable_scope_binary_get(storage->scope_bin);
526
0
    if (storage->scope == NULL)
527
0
      return FALSE;
528
0
  }
529
530
  /* FIXME: direct invasion of the scope object is a bit ugly */
531
0
  if (index >= array_count(&storage->scope->variable_index))
532
0
    return FALSE;
533
534
0
  var = array_idx(&storage->scope->variable_index, index);
535
0
  if (*var != NULL)
536
0
    *identifier = (*var)->identifier;
537
0
  return TRUE;
538
0
}
539
540
const char *
541
sieve_variable_get_varid(struct sieve_variable_storage *storage,
542
       unsigned int index)
543
0
{
544
0
  if (storage->scope_bin == NULL)
545
0
    return t_strdup_printf("%ld", (long)index);
546
547
0
  if (storage->scope == NULL) {
548
0
    storage->scope =
549
0
      sieve_variable_scope_binary_get(storage->scope_bin);
550
0
    if (storage->scope == NULL)
551
0
      return NULL;
552
0
  }
553
554
0
  return sieve_ext_variables_get_varid(storage->scope->ext, index);
555
0
}
556
557
bool sieve_variable_get(struct sieve_variable_storage *storage,
558
      unsigned int index, string_t **value)
559
0
{
560
0
  *value = NULL;
561
562
0
  if (index < array_count(&storage->var_values)) {
563
0
    string_t *const *varent;
564
565
0
    varent = array_idx(&storage->var_values, index);
566
567
0
    *value = *varent;
568
0
  } else if (!sieve_variable_valid(storage, index)) {
569
0
    return FALSE;
570
0
  }
571
572
0
  return TRUE;
573
0
}
574
575
bool sieve_variable_get_modifiable(struct sieve_variable_storage *storage,
576
           unsigned int index, string_t **value)
577
0
{
578
0
  string_t *dummy;
579
580
0
  if (value == NULL)
581
0
    value = &dummy;
582
583
0
  if (!sieve_variable_get(storage, index, value))
584
0
    return FALSE;
585
586
0
  if (*value == NULL) {
587
0
    *value = str_new(storage->pool, 256);
588
0
    array_idx_set(&storage->var_values, index, value);
589
0
  }
590
0
  return TRUE;
591
0
}
592
593
bool sieve_variable_assign(struct sieve_variable_storage *storage,
594
         unsigned int index, const string_t *value)
595
0
{
596
0
  const struct ext_variables_context *extctx =
597
0
    ext_variables_get_context(storage->var_ext);
598
0
  string_t *varval;
599
600
0
  if (!sieve_variable_get_modifiable(storage, index, &varval))
601
0
    return FALSE;
602
603
0
  str_truncate(varval, 0);
604
0
  str_append_str(varval, value);
605
606
  /* Just a precaution, caller should prevent this in the first place */
607
0
  if (str_len(varval) > extctx->set->max_value_size)
608
0
    str_truncate_utf8(varval, extctx->set->max_value_size);
609
610
0
  return TRUE;
611
0
}
612
613
bool sieve_variable_assign_cstr(struct sieve_variable_storage *storage,
614
        unsigned int index, const char *value)
615
0
{
616
0
  const struct ext_variables_context *extctx =
617
0
    ext_variables_get_context(storage->var_ext);
618
0
  string_t *varval;
619
620
0
  if (!sieve_variable_get_modifiable(storage, index, &varval))
621
0
    return FALSE;
622
623
0
  str_truncate(varval, 0);
624
0
  str_append(varval, value);
625
626
  /* Just a precaution, caller should prevent this in the first place */
627
0
  if (str_len(varval) > extctx->set->max_value_size)
628
0
    str_truncate_utf8(varval, extctx->set->max_value_size);
629
630
0
  return TRUE;
631
0
}
632
633
/*
634
 * AST Context
635
 */
636
637
static void
638
ext_variables_ast_free(const struct sieve_extension *ext ATTR_UNUSED,
639
           struct sieve_ast *ast ATTR_UNUSED, void *context)
640
0
{
641
0
  struct sieve_variable_scope *local_scope =
642
0
    (struct sieve_variable_scope *)context;
643
644
  /* Unreference main variable scope */
645
0
  sieve_variable_scope_unref(&local_scope);
646
0
}
647
648
static const struct sieve_ast_extension variables_ast_extension = {
649
  &variables_extension,
650
  ext_variables_ast_free,
651
};
652
653
static struct sieve_variable_scope *
654
ext_variables_create_local_scope(const struct sieve_extension *this_ext,
655
         struct sieve_ast *ast)
656
0
{
657
0
  struct sieve_variable_scope *scope;
658
659
0
  scope = sieve_variable_scope_create(this_ext->svinst, this_ext, NULL);
660
661
0
  sieve_ast_extension_register(ast, this_ext, &variables_ast_extension,
662
0
             scope);
663
0
  return scope;
664
0
}
665
666
static struct sieve_variable_scope *
667
ext_variables_ast_get_local_scope(const struct sieve_extension *this_ext,
668
          struct sieve_ast *ast)
669
0
{
670
0
  struct sieve_variable_scope *local_scope =
671
0
    (struct sieve_variable_scope *)
672
0
      sieve_ast_extension_get_context(ast, this_ext);
673
674
0
  return local_scope;
675
0
}
676
677
/*
678
 * Validator context
679
 */
680
681
static struct ext_variables_validator_context *
682
ext_variables_validator_context_create(const struct sieve_extension *this_ext,
683
               struct sieve_validator *valdtr)
684
0
{
685
0
  pool_t pool = sieve_validator_pool(valdtr);
686
0
  struct ext_variables_validator_context *ctx;
687
0
  struct sieve_ast *ast = sieve_validator_ast(valdtr);
688
689
0
  ctx = p_new(pool, struct ext_variables_validator_context, 1);
690
0
  ctx->modifiers = sieve_validator_object_registry_create(valdtr);
691
0
  ctx->namespaces = sieve_validator_object_registry_create(valdtr);
692
0
  ctx->local_scope = ext_variables_create_local_scope(this_ext, ast);
693
694
0
  sieve_validator_extension_set_context(valdtr, this_ext, ctx);
695
0
  return ctx;
696
0
}
697
698
struct ext_variables_validator_context *
699
ext_variables_validator_context_get(const struct sieve_extension *this_ext,
700
            struct sieve_validator *valdtr)
701
0
{
702
0
  struct ext_variables_validator_context *ctx;
703
704
0
  i_assert(sieve_extension_is(this_ext, variables_extension));
705
0
  ctx = (struct ext_variables_validator_context *)
706
0
    sieve_validator_extension_get_context(valdtr, this_ext);
707
708
0
  if (ctx == NULL)
709
0
    ctx = ext_variables_validator_context_create(this_ext, valdtr);
710
0
  return ctx;
711
0
}
712
713
void ext_variables_validator_initialize(const struct sieve_extension *this_ext,
714
          struct sieve_validator *valdtr)
715
0
{
716
0
  struct ext_variables_validator_context *ctx;
717
718
  /* Create our context */
719
0
  ctx = ext_variables_validator_context_get(this_ext, valdtr);
720
721
0
  ext_variables_register_core_modifiers(this_ext, ctx);
722
723
0
  ctx->active = TRUE;
724
0
}
725
726
struct sieve_variable *ext_variables_validator_get_variable(
727
  const struct sieve_extension *this_ext,
728
  struct sieve_validator *validator, const char *variable)
729
0
{
730
0
  struct ext_variables_validator_context *ctx =
731
0
    ext_variables_validator_context_get(this_ext, validator);
732
733
0
  return sieve_variable_scope_get_variable(ctx->local_scope, variable);
734
0
}
735
736
struct sieve_variable *
737
ext_variables_validator_declare_variable(const struct sieve_extension *this_ext,
738
           struct sieve_validator *validator,
739
           const char *variable)
740
0
{
741
0
  struct ext_variables_validator_context *ctx =
742
0
    ext_variables_validator_context_get(this_ext, validator);
743
744
0
  return sieve_variable_scope_declare(ctx->local_scope, variable);
745
0
}
746
747
struct sieve_variable_scope *
748
sieve_ext_variables_get_local_scope(const struct sieve_extension *var_ext,
749
            struct sieve_validator *validator)
750
0
{
751
0
  struct ext_variables_validator_context *ctx =
752
0
    ext_variables_validator_context_get(var_ext, validator);
753
754
0
  return ctx->local_scope;
755
0
}
756
757
bool sieve_ext_variables_is_active(const struct sieve_extension *var_ext,
758
           struct sieve_validator *valdtr)
759
0
{
760
0
  struct ext_variables_validator_context *ctx =
761
0
    ext_variables_validator_context_get(var_ext, valdtr);
762
763
0
  return (ctx != NULL && ctx->active);
764
0
}
765
766
/*
767
 * Code generation
768
 */
769
770
bool ext_variables_generator_load(const struct sieve_extension *ext,
771
          const struct sieve_codegen_env *cgenv)
772
0
{
773
0
  struct sieve_variable_scope *local_scope =
774
0
    ext_variables_ast_get_local_scope(ext, cgenv->ast);
775
0
  unsigned int count = sieve_variable_scope_size(local_scope);
776
0
  sieve_size_t jump;
777
778
0
  sieve_binary_emit_unsigned(cgenv->sblock, count);
779
780
0
  jump = sieve_binary_emit_offset(cgenv->sblock, 0);
781
782
0
  if (count > 0) {
783
0
    unsigned int size, i;
784
0
    struct sieve_variable *const *vars =
785
0
      sieve_variable_scope_get_variables(local_scope, &size);
786
787
0
    for (i = 0; i < size; i++) {
788
0
      sieve_binary_emit_cstring(cgenv->sblock,
789
0
              vars[i]->identifier);
790
0
    }
791
0
  }
792
793
0
  sieve_binary_resolve_offset(cgenv->sblock, jump);
794
0
  return TRUE;
795
0
}
796
797
/*
798
 * Interpreter context
799
 */
800
801
struct ext_variables_interpreter_context {
802
  pool_t pool;
803
804
  struct sieve_variable_scope *local_scope;
805
  struct sieve_variable_scope_binary *local_scope_bin;
806
807
  struct sieve_variable_storage *local_storage;
808
  ARRAY(struct sieve_variable_storage *) ext_storages;
809
};
810
811
static void
812
ext_variables_interpreter_free(const struct sieve_extension *ext ATTR_UNUSED,
813
             struct sieve_interpreter *interp ATTR_UNUSED,
814
             void *context)
815
0
{
816
0
  struct ext_variables_interpreter_context *ctx =
817
0
    (struct ext_variables_interpreter_context *)context;
818
819
0
  sieve_variable_scope_binary_unref(&ctx->local_scope_bin);
820
0
}
821
822
static struct sieve_interpreter_extension
823
variables_interpreter_extension = {
824
  .ext_def = &variables_extension,
825
  .free = ext_variables_interpreter_free,
826
};
827
828
static struct ext_variables_interpreter_context *
829
ext_variables_interpreter_context_create(
830
  const struct sieve_extension *this_ext,
831
  struct sieve_interpreter *interp,
832
  struct sieve_variable_scope_binary *scpbin)
833
0
{
834
0
  pool_t pool = sieve_interpreter_pool(interp);
835
0
  struct ext_variables_interpreter_context *ctx;
836
837
0
  ctx = p_new(pool, struct ext_variables_interpreter_context, 1);
838
0
  ctx->pool = pool;
839
0
  ctx->local_scope = NULL;
840
0
  ctx->local_scope_bin = scpbin;
841
0
  ctx->local_storage =
842
0
    sieve_variable_storage_create(this_ext, pool, scpbin);
843
0
  p_array_init(&ctx->ext_storages, pool,
844
0
         sieve_extensions_get_count(this_ext->svinst));
845
846
0
  sieve_interpreter_extension_register(interp, this_ext,
847
0
               &variables_interpreter_extension,
848
0
               ctx);
849
0
  return ctx;
850
0
}
851
852
bool ext_variables_interpreter_load(const struct sieve_extension *ext,
853
            const struct sieve_runtime_env *renv,
854
            sieve_size_t *address)
855
0
{
856
0
  const struct sieve_execute_env *eenv = renv->exec_env;
857
0
  struct sieve_variable_scope_binary *scpbin;
858
859
0
  scpbin = sieve_variable_scope_binary_read(eenv->svinst, ext, NULL,
860
0
              renv->sblock, address);
861
0
  if (scpbin == NULL)
862
0
    return FALSE;
863
864
  /* Create our context */
865
0
  (void)ext_variables_interpreter_context_create(ext, renv->interp,
866
0
                   scpbin);
867
868
  /* Enable support for match values */
869
0
  (void)sieve_match_values_set_enabled(renv, TRUE);
870
871
0
  return TRUE;
872
0
}
873
874
static inline struct ext_variables_interpreter_context *
875
ext_variables_interpreter_context_get(const struct sieve_extension *this_ext,
876
              struct sieve_interpreter *interp)
877
0
{
878
0
  struct ext_variables_interpreter_context *ctx;
879
880
0
  i_assert(sieve_extension_is(this_ext, variables_extension));
881
0
  ctx = (struct ext_variables_interpreter_context *)
882
0
    sieve_interpreter_extension_get_context(interp, this_ext);
883
0
  return ctx;
884
0
}
885
886
struct sieve_variable_storage *
887
sieve_ext_variables_runtime_get_storage(const struct sieve_extension *var_ext,
888
          const struct sieve_runtime_env *renv,
889
          const struct sieve_extension *ext)
890
0
{
891
0
  struct ext_variables_interpreter_context *ctx =
892
0
    ext_variables_interpreter_context_get(var_ext, renv->interp);
893
0
  struct sieve_variable_storage *const *storage;
894
895
0
  if (ext == NULL)
896
0
    return ctx->local_storage;
897
898
0
  if (ext->id >= (int)array_count(&ctx->ext_storages))
899
0
    storage = NULL;
900
0
  else
901
0
    storage = array_idx(&ctx->ext_storages, ext->id);
902
903
0
  if (storage == NULL)
904
0
    return NULL;
905
0
  return *storage;
906
0
}
907
908
void sieve_ext_variables_runtime_set_storage(
909
  const struct sieve_extension *var_ext,
910
  const struct sieve_runtime_env *renv, const struct sieve_extension *ext,
911
  struct sieve_variable_storage *storage)
912
0
{
913
0
  struct ext_variables_interpreter_context *ctx =
914
0
    ext_variables_interpreter_context_get(var_ext, renv->interp);
915
916
0
  if (ctx == NULL || ext == NULL || storage == NULL)
917
0
    return;
918
0
  if (ext->id < 0)
919
0
    return;
920
921
0
  array_idx_set(&ctx->ext_storages, (unsigned int) ext->id, &storage);
922
0
}