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/sieve-validator.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
#include "buffer.h"
8
#include "mempool.h"
9
#include "hash.h"
10
11
#include "sieve-common.h"
12
#include "sieve-extensions.h"
13
#include "sieve-script.h"
14
#include "sieve-ast.h"
15
#include "sieve-commands.h"
16
#include "sieve-validator.h"
17
18
#include "sieve-comparators.h"
19
#include "sieve-address-parts.h"
20
21
/*
22
 * Forward declarations
23
 */
24
25
static void
26
sieve_validator_register_core_commands(struct sieve_validator *valdtr);
27
static void
28
sieve_validator_register_core_tests(struct sieve_validator *valdtr);
29
30
/*
31
 * Types
32
 */
33
34
/* Tag registration */
35
36
struct sieve_tag_registration {
37
  const struct sieve_argument_def *tag_def;
38
  const struct sieve_extension *ext;
39
40
  const char *identifier;
41
  int id_code;
42
};
43
44
/* Command registration */
45
46
struct sieve_command_registration {
47
  const struct sieve_command_def *cmd_def;
48
  const struct sieve_extension *ext;
49
50
  ARRAY(struct sieve_tag_registration *) normal_tags;
51
  ARRAY(struct sieve_tag_registration *) instanced_tags;
52
  ARRAY(struct sieve_tag_registration *) persistent_tags;
53
};
54
55
/* Default (literal) arguments */
56
57
struct sieve_default_argument {
58
  const struct sieve_argument_def *arg_def;
59
  const struct sieve_extension *ext;
60
61
  struct sieve_default_argument *overrides;
62
};
63
64
/*
65
 * Validator extension
66
 */
67
68
struct sieve_validator_extension_reg {
69
  const struct sieve_validator_extension *valext;
70
  const struct sieve_extension *ext;
71
  struct sieve_ast_argument *arg;
72
  void *context;
73
74
  bool loaded:1;
75
  bool required:1;
76
};
77
78
/*
79
 * Validator
80
 */
81
82
struct sieve_validator {
83
  pool_t pool;
84
85
  struct sieve_instance *svinst;
86
  struct sieve_ast *ast;
87
  struct sieve_script *script;
88
  enum sieve_compile_flags flags;
89
90
  struct sieve_error_handler *ehandler;
91
92
  bool finished_require;
93
94
  /* Registries */
95
96
  HASH_TABLE(const char *, struct sieve_command_registration *) commands;
97
98
  ARRAY(struct sieve_validator_extension_reg) extensions;
99
100
  /* This is currently a wee bit ugly and needs more thought */
101
  struct sieve_default_argument default_arguments[SAT_COUNT];
102
103
  /* Default argument processing state (FIXME: ugly) */
104
  struct sieve_default_argument *current_defarg;
105
  enum sieve_argument_type current_defarg_type;
106
  bool current_defarg_constant;
107
108
  bool failed:1;
109
};
110
111
/*
112
 * Validator object
113
 */
114
115
struct sieve_validator *
116
sieve_validator_create(struct sieve_ast *ast,
117
           struct sieve_error_handler *ehandler,
118
           enum sieve_compile_flags flags)
119
0
{
120
0
  pool_t pool;
121
0
  struct sieve_validator *valdtr;
122
0
  const struct sieve_extension *const *ext_preloaded;
123
0
  unsigned int i, ext_count;
124
125
0
  pool = pool_alloconly_create("sieve_validator", 16384);
126
0
  valdtr = p_new(pool, struct sieve_validator, 1);
127
0
  valdtr->pool = pool;
128
129
0
  valdtr->ehandler = ehandler;
130
0
  sieve_error_handler_ref(ehandler);
131
132
0
  valdtr->ast = ast;
133
0
  sieve_ast_ref(ast);
134
135
0
  valdtr->script = sieve_ast_script(ast);
136
0
  valdtr->svinst = sieve_script_svinst(valdtr->script);
137
0
  valdtr->flags = flags;
138
139
  /* Setup default arguments */
140
0
  valdtr->default_arguments[SAT_NUMBER].arg_def = &number_argument;
141
0
  valdtr->default_arguments[SAT_NUMBER].ext = NULL;
142
0
  valdtr->default_arguments[SAT_VAR_STRING].arg_def = &string_argument;
143
0
  valdtr->default_arguments[SAT_VAR_STRING].ext = NULL;
144
0
  valdtr->default_arguments[SAT_CONST_STRING].arg_def = &string_argument;
145
0
  valdtr->default_arguments[SAT_CONST_STRING].ext = NULL;
146
0
  valdtr->default_arguments[SAT_STRING_LIST].arg_def = &string_list_argument;
147
0
  valdtr->default_arguments[SAT_STRING_LIST].ext = NULL;
148
149
  /* Setup storage for extension contexts */
150
0
  p_array_init(&valdtr->extensions, pool,
151
0
         sieve_extensions_get_count(valdtr->svinst));
152
153
  /* Setup command registry */
154
0
  hash_table_create(&valdtr->commands, pool, 0, strcase_hash, strcasecmp);
155
0
  sieve_validator_register_core_commands(valdtr);
156
0
  sieve_validator_register_core_tests(valdtr);
157
158
  /* Pre-load core language features implemented as 'extensions' */
159
0
  ext_preloaded =
160
0
    sieve_extensions_get_preloaded(valdtr->svinst, &ext_count);
161
0
  for (i = 0; i < ext_count; i++) {
162
0
    const struct sieve_extension_def *ext_def =
163
0
      ext_preloaded[i]->def;
164
165
0
    if (ext_def != NULL && ext_def->validator_load != NULL)
166
0
      (void)ext_def->validator_load(ext_preloaded[i], valdtr);
167
0
  }
168
169
0
  return valdtr;
170
0
}
171
172
void sieve_validator_free(struct sieve_validator **valdtr)
173
0
{
174
0
  const struct sieve_validator_extension_reg *extrs;
175
0
  unsigned int ext_count, i;
176
177
0
  hash_table_destroy(&(*valdtr)->commands);
178
0
  sieve_ast_unref(&(*valdtr)->ast);
179
180
0
  sieve_error_handler_unref(&(*valdtr)->ehandler);
181
182
  /* Signal registered extensions that the validator is being destroyed */
183
0
  extrs = array_get(&(*valdtr)->extensions, &ext_count);
184
0
  for (i = 0; i < ext_count; i++) {
185
0
    if (extrs[i].valext != NULL && extrs[i].valext->free != NULL)
186
0
      extrs[i].valext->free(extrs[i].ext, *valdtr,
187
0
                extrs[i].context);
188
0
  }
189
190
0
  pool_unref(&(*valdtr)->pool);
191
192
0
  *valdtr = NULL;
193
0
}
194
195
/*
196
 * Accessors
197
 */
198
199
// FIXME: build validate environment
200
201
pool_t sieve_validator_pool(struct sieve_validator *valdtr)
202
0
{
203
0
  return valdtr->pool;
204
0
}
205
206
struct sieve_error_handler *
207
sieve_validator_error_handler(struct sieve_validator *valdtr)
208
0
{
209
0
  return valdtr->ehandler;
210
0
}
211
212
struct sieve_ast *sieve_validator_ast(struct sieve_validator *valdtr)
213
0
{
214
0
  return valdtr->ast;
215
0
}
216
217
struct sieve_script *sieve_validator_script(struct sieve_validator *valdtr)
218
0
{
219
0
  return valdtr->script;
220
0
}
221
222
const char *sieve_validator_script_cause(struct sieve_validator *valdtr)
223
0
{
224
0
  return sieve_script_cause(valdtr->script);
225
0
}
226
227
struct sieve_instance *sieve_validator_svinst(struct sieve_validator *valdtr)
228
0
{
229
0
  return valdtr->svinst;
230
0
}
231
232
enum sieve_compile_flags
233
sieve_validator_compile_flags(struct sieve_validator *valdtr)
234
0
{
235
0
  return valdtr->flags;
236
0
}
237
238
bool sieve_validator_failed(struct sieve_validator *valdtr)
239
0
{
240
0
  return valdtr->failed;
241
0
}
242
243
/*
244
 * Command registry
245
 */
246
247
/* Dummy command object to mark unknown commands in the registry */
248
249
static bool _cmd_unknown_validate(struct sieve_validator *valdtr ATTR_UNUSED,
250
          struct sieve_command *cmd ATTR_UNUSED)
251
0
{
252
0
  i_unreached();
253
0
}
254
255
static const struct sieve_command_def unknown_command = {
256
  .identifier = "",
257
  .type = SCT_NONE,
258
  .positional_args = 0,
259
  .subtests = 0,
260
  .block_allowed = FALSE,
261
  .block_required = FALSE,
262
  .validate = _cmd_unknown_validate
263
};
264
265
/* Registration of the core commands of the language */
266
267
static void
268
sieve_validator_register_core_tests(struct sieve_validator *valdtr)
269
0
{
270
0
  unsigned int i;
271
272
0
  for (i = 0; i < sieve_core_tests_count; i++) {
273
0
    sieve_validator_register_command(valdtr, NULL,
274
0
             sieve_core_tests[i]);
275
0
  }
276
0
}
277
278
static void
279
sieve_validator_register_core_commands(struct sieve_validator *valdtr)
280
0
{
281
0
  unsigned int i;
282
283
0
  for (i = 0; i < sieve_core_commands_count; i++) {
284
0
    sieve_validator_register_command(valdtr, NULL,
285
0
             sieve_core_commands[i]);
286
0
  }
287
0
}
288
289
/* Registry functions */
290
291
static struct sieve_command_registration *
292
sieve_validator_find_command_registration(struct sieve_validator *valdtr,
293
            const char *command)
294
0
{
295
0
  return hash_table_lookup(valdtr->commands, command);
296
0
}
297
298
static struct sieve_command_registration *
299
_sieve_validator_register_command(struct sieve_validator *valdtr,
300
          const struct sieve_extension *ext,
301
          const struct sieve_command_def *cmd_def,
302
          const char *identifier)
303
0
{
304
0
  struct sieve_command_registration *cmd_reg =
305
0
    p_new(valdtr->pool, struct sieve_command_registration, 1);
306
307
0
  cmd_reg->cmd_def = cmd_def;
308
0
  cmd_reg->ext = ext;
309
310
0
  hash_table_insert(valdtr->commands, identifier, cmd_reg);
311
312
0
  return cmd_reg;
313
0
}
314
315
void sieve_validator_register_command(struct sieve_validator *valdtr,
316
              const struct sieve_extension *ext,
317
              const struct sieve_command_def *cmd_def)
318
0
{
319
0
  struct sieve_command_registration *cmd_reg =
320
0
    sieve_validator_find_command_registration(
321
0
      valdtr, cmd_def->identifier);
322
323
0
  if (cmd_reg == NULL) {
324
0
    cmd_reg = _sieve_validator_register_command(
325
0
      valdtr, ext, cmd_def, cmd_def->identifier);
326
0
  } else {
327
0
    cmd_reg->cmd_def = cmd_def;
328
0
    cmd_reg->ext = ext;
329
0
  }
330
331
0
  if (cmd_def->registered != NULL)
332
0
    cmd_def->registered(valdtr, ext, cmd_reg);
333
0
}
334
335
static void
336
sieve_validator_register_unknown_command(struct sieve_validator *valdtr,
337
           const char *command)
338
0
{
339
0
  struct sieve_command_registration *cmd_reg =
340
0
    sieve_validator_find_command_registration(valdtr, command);
341
342
0
  if (cmd_reg == NULL) {
343
0
    (void)_sieve_validator_register_command(
344
0
      valdtr, NULL, &unknown_command, command);
345
0
  } else {
346
0
    i_assert(cmd_reg->cmd_def == NULL);
347
0
    cmd_reg->cmd_def = &unknown_command;
348
0
  }
349
0
}
350
351
/*const struct sieve_command *sieve_validator_find_command
352
(struct sieve_validator *valdtr, const char *command)
353
{
354
  struct sieve_command_registration *cmd_reg =
355
    sieve_validator_find_command_registration(valdtr, command);
356
357
  return ( record == NULL ? NULL : record->command );
358
}*/
359
360
/*
361
 * Per-command tagged argument registry
362
 */
363
364
/* Dummy argument object to mark unknown arguments in the registry */
365
366
static bool
367
_unknown_tag_validate(struct sieve_validator *valdtr ATTR_UNUSED,
368
          struct sieve_ast_argument **arg ATTR_UNUSED,
369
          struct sieve_command *tst ATTR_UNUSED)
370
0
{
371
0
  i_unreached();
372
0
}
373
374
static const struct sieve_argument_def _unknown_tag = {
375
  .identifier = "",
376
  .validate = _unknown_tag_validate,
377
};
378
379
static inline bool
380
_tag_registration_is_unknown(struct sieve_tag_registration *tag_reg)
381
0
{
382
0
  return (tag_reg != NULL && tag_reg->tag_def == &_unknown_tag);
383
0
}
384
385
/* Registry functions */
386
387
static void
388
_sieve_validator_register_tag(struct sieve_validator *valdtr,
389
            struct sieve_command_registration *cmd_reg,
390
            const struct sieve_extension *ext,
391
            const struct sieve_argument_def *tag_def,
392
            const char *identifier, int id_code)
393
0
{
394
0
  struct sieve_tag_registration *reg;
395
396
0
  reg = p_new(valdtr->pool, struct sieve_tag_registration, 1);
397
0
  reg->ext = ext;
398
0
  reg->tag_def = tag_def;
399
0
  reg->id_code = id_code;
400
0
  if (identifier == NULL)
401
0
    reg->identifier = tag_def->identifier;
402
0
  else
403
0
    reg->identifier = p_strdup(valdtr->pool, identifier);
404
405
0
  if (!array_is_created(&cmd_reg->normal_tags))
406
0
    p_array_init(&cmd_reg->normal_tags, valdtr->pool, 4);
407
408
0
  array_append(&cmd_reg->normal_tags, &reg, 1);
409
0
}
410
411
void sieve_validator_register_persistent_tag(
412
  struct sieve_validator *valdtr, const char *command,
413
  const struct sieve_extension *ext,
414
  const struct sieve_argument_def *tag_def)
415
0
{
416
  /* Add the tag to the persistent tags list if necessary */
417
0
  if (tag_def->validate_persistent != NULL) {
418
0
    struct sieve_command_registration *cmd_reg =
419
0
      sieve_validator_find_command_registration(
420
0
        valdtr, command);
421
422
0
    if (cmd_reg == NULL) {
423
0
      cmd_reg = _sieve_validator_register_command(
424
0
        valdtr, NULL, NULL, command);
425
0
    }
426
427
0
    struct sieve_tag_registration *reg;
428
429
0
    if (!array_is_created(&cmd_reg->persistent_tags)) {
430
0
      p_array_init(&cmd_reg->persistent_tags,
431
0
             valdtr->pool, 4);
432
0
    } else {
433
0
      struct sieve_tag_registration *reg_idx;
434
435
      /* Avoid dupplicate registration */
436
0
      array_foreach_elem(&cmd_reg->persistent_tags, reg_idx) {
437
0
        if (reg_idx->tag_def == tag_def)
438
0
          return;
439
0
      }
440
0
    }
441
442
0
    reg = p_new(valdtr->pool, struct sieve_tag_registration, 1);
443
0
    reg->ext = ext;
444
0
    reg->tag_def = tag_def;
445
0
    reg->id_code = -1;
446
447
0
    array_append(&cmd_reg->persistent_tags, &reg, 1);
448
0
  }
449
0
}
450
451
void sieve_validator_register_external_tag(
452
  struct sieve_validator *valdtr, const char *command,
453
  const struct sieve_extension *ext,
454
  const struct sieve_argument_def *tag_def, int id_code)
455
0
{
456
0
  struct sieve_command_registration *cmd_reg =
457
0
    sieve_validator_find_command_registration(valdtr, command);
458
459
0
  if (cmd_reg == NULL) {
460
0
    cmd_reg = _sieve_validator_register_command(
461
0
      valdtr, NULL, NULL, command);
462
0
  }
463
464
0
  _sieve_validator_register_tag(valdtr, cmd_reg, ext, tag_def,
465
0
              NULL, id_code);
466
0
}
467
468
void sieve_validator_register_tag(
469
  struct sieve_validator *valdtr,
470
  struct sieve_command_registration *cmd_reg,
471
  const struct sieve_extension *ext,
472
  const struct sieve_argument_def *tag_def, int id_code)
473
0
{
474
0
  if (tag_def->is_instance_of == NULL) {
475
0
    _sieve_validator_register_tag(valdtr, cmd_reg, ext, tag_def,
476
0
                NULL, id_code);
477
0
  } else {
478
0
    struct sieve_tag_registration *reg =
479
0
      p_new(valdtr->pool, struct sieve_tag_registration, 1);
480
0
    reg->ext = ext;
481
0
    reg->tag_def = tag_def;
482
0
    reg->id_code = id_code;
483
484
0
    if (!array_is_created(&cmd_reg->instanced_tags))
485
0
      p_array_init(&cmd_reg->instanced_tags, valdtr->pool, 4);
486
487
0
    array_append(&cmd_reg->instanced_tags, &reg, 1);
488
0
  }
489
0
}
490
491
static void
492
sieve_validator_register_unknown_tag(struct sieve_validator *valdtr,
493
             struct sieve_command_registration *cmd_reg,
494
             const char *tag)
495
0
{
496
0
  _sieve_validator_register_tag(valdtr, cmd_reg, NULL,
497
0
              &_unknown_tag, tag, 0);
498
0
}
499
500
static struct sieve_tag_registration *
501
_sieve_validator_command_tag_get(struct sieve_validator *valdtr,
502
         struct sieve_command *cmd,
503
         const char *tag, void **data)
504
0
{
505
0
  struct sieve_command_registration *cmd_reg = cmd->reg;
506
0
  struct sieve_tag_registration *const *regs;
507
0
  unsigned int i, reg_count;
508
509
  /* First check normal tags */
510
0
  if (array_is_created(&cmd_reg->normal_tags)) {
511
0
    regs = array_get(&cmd_reg->normal_tags, &reg_count);
512
513
0
    for (i = 0; i < reg_count; i++) {
514
0
      if (regs[i]->tag_def != NULL &&
515
0
          strcasecmp(regs[i]->identifier, tag) == 0) {
516
517
0
        return regs[i];
518
0
      }
519
0
    }
520
0
  }
521
522
  /* Not found so far, try the instanced tags */
523
0
  if (array_is_created(&cmd_reg->instanced_tags)) {
524
0
    regs = array_get(&cmd_reg->instanced_tags, &reg_count);
525
526
0
    for (i = 0; i < reg_count; i++) {
527
0
      if (regs[i]->tag_def != NULL) {
528
0
        if (regs[i]->tag_def->is_instance_of(
529
0
          valdtr, cmd, regs[i]->ext, tag, data))
530
0
          return regs[i];
531
0
      }
532
0
    }
533
0
  }
534
535
0
  return NULL;
536
0
}
537
538
static bool
539
sieve_validator_command_tag_exists(struct sieve_validator *valdtr,
540
           struct sieve_command *cmd, const char *tag)
541
0
{
542
0
  return (_sieve_validator_command_tag_get(valdtr, cmd,
543
0
             tag, NULL) != NULL);
544
0
}
545
546
static struct sieve_tag_registration *
547
sieve_validator_command_tag_get(struct sieve_validator *valdtr,
548
        struct sieve_command *cmd,
549
        struct sieve_ast_argument *arg, void **data)
550
0
{
551
0
  const char *tag = sieve_ast_argument_tag(arg);
552
553
0
  return _sieve_validator_command_tag_get(valdtr, cmd, tag, data);
554
0
}
555
556
/*
557
 * Extension support
558
 */
559
560
static bool
561
sieve_validator_extensions_check_conficts(struct sieve_validator *valdtr,
562
            struct sieve_ast_argument *ext_arg,
563
            const struct sieve_extension *ext)
564
0
{
565
0
  struct sieve_validator_extension_reg *ext_reg;
566
0
  struct sieve_validator_extension_reg *regs;
567
0
  unsigned int count, i;
568
569
0
  if (ext->id < 0)
570
0
    return TRUE;
571
572
0
  ext_reg = array_idx_get_space(&valdtr->extensions,
573
0
              (unsigned int) ext->id);
574
575
0
  regs = array_get_modifiable(&valdtr->extensions, &count);
576
0
  for (i = 0; i < count; i++) {
577
0
    bool required = ext_reg->required && regs[i].required;
578
579
0
    if (regs[i].ext == NULL)
580
0
      continue;
581
0
    if (regs[i].ext == ext)
582
0
      continue;
583
0
    if (!regs[i].loaded)
584
0
      continue;
585
586
    /* Check this extension vs other extension */
587
0
    if (ext_reg->valext != NULL &&
588
0
        ext_reg->valext->check_conflict != NULL) {
589
0
      struct sieve_ast_argument *this_ext_arg =
590
0
        (ext_arg == NULL ? regs[i].arg : ext_arg);
591
592
0
      if (!ext_reg->valext->check_conflict(
593
0
        ext, valdtr, ext_reg->context, this_ext_arg,
594
0
        regs[i].ext, required))
595
0
        return FALSE;
596
0
    }
597
598
    /* Check other extension vs this extension */
599
0
    if (regs[i].valext != NULL &&
600
0
        regs[i].valext->check_conflict != NULL) {
601
0
      if (!regs[i].valext->check_conflict(
602
0
        regs[i].ext, valdtr, regs[i].context,
603
0
        regs[i].arg, ext, required))
604
0
        return FALSE;
605
0
    }
606
0
  }
607
0
  return TRUE;
608
0
}
609
610
bool sieve_validator_extension_load(struct sieve_validator *valdtr,
611
            struct sieve_command *cmd,
612
            struct sieve_ast_argument *ext_arg,
613
            const struct sieve_extension *ext,
614
            bool required)
615
0
{
616
0
  const struct sieve_extension_def *extdef = ext->def;
617
0
  struct sieve_validator_extension_reg *reg = NULL;
618
619
0
  if (ext->global &&
620
0
      (valdtr->flags & SIEVE_COMPILE_FLAG_NOGLOBAL) != 0) {
621
0
    const char *cmd_prefix = (cmd == NULL ? "" :
622
0
      t_strdup_printf("%s %s: ",
623
0
          sieve_command_identifier(cmd),
624
0
          sieve_command_type_name(cmd)));
625
0
    sieve_argument_validate_error(
626
0
      valdtr, ext_arg,
627
0
      "%sfailed to load Sieve capability '%s': "
628
0
      "its use is restricted to global scripts",
629
0
      cmd_prefix, sieve_extension_name(ext));
630
0
    return FALSE;
631
0
  }
632
633
  /* Register extension no matter what and store the
634
   * AST argument registering it */
635
0
  if (ext->id >= 0) {
636
0
    reg = array_idx_get_space(&valdtr->extensions,
637
0
            (unsigned int)ext->id);
638
0
    i_assert(reg->ext == NULL || reg->ext == ext);
639
0
    reg->ext = ext;
640
0
    reg->required = reg->required || required;
641
0
    if (reg->arg == NULL)
642
0
      reg->arg = ext_arg;
643
644
0
    if (reg->loaded) {
645
      /* Already loaded successfully before, e.g. because the
646
         script requires the same extension more than once.
647
         Loading it again would duplicate the validator state
648
         it registers, such as the default argument override
649
         chain, which is walked recursively while validating
650
         arguments. */
651
0
      sieve_ast_extension_link(valdtr->ast, ext,
652
0
             reg->required);
653
0
      return TRUE;
654
0
    }
655
0
  }
656
657
0
  if (extdef->validator_load != NULL &&
658
0
      !extdef->validator_load(ext, valdtr)) {
659
0
    const char *cmd_prefix = (cmd == NULL ? "" :
660
0
      t_strdup_printf("%s %s: ",
661
0
          sieve_command_identifier(cmd),
662
0
          sieve_command_type_name(cmd)));
663
0
    sieve_argument_validate_error(
664
0
      valdtr, ext_arg,
665
0
      "%sfailed to load Sieve capability '%s'",
666
0
      cmd_prefix, sieve_extension_name(ext));
667
0
    return FALSE;
668
0
  }
669
670
  /* Check conflicts with other extensions */
671
0
  if (!sieve_validator_extensions_check_conficts(valdtr, ext_arg, ext))
672
0
    return FALSE;
673
674
  /* Link extension to AST for use at code generation */
675
0
  if (reg != NULL) {
676
0
    sieve_ast_extension_link(valdtr->ast, ext, reg->required);
677
0
    reg->loaded = TRUE;
678
0
  }
679
680
0
  return TRUE;
681
0
}
682
683
const struct sieve_extension *
684
sieve_validator_extension_load_by_name(struct sieve_validator *valdtr,
685
               struct sieve_command *cmd,
686
               struct sieve_ast_argument *ext_arg,
687
               const char *ext_name)
688
0
{
689
0
  const struct sieve_extension *ext;
690
691
0
  ext = sieve_extension_get_by_name(valdtr->svinst, ext_name);
692
693
0
  if (ext == NULL || ext->def == NULL || !ext->enabled) {
694
0
    unsigned int i;
695
0
    bool core_test = FALSE;
696
0
    bool core_command = FALSE;
697
698
0
    for (i = 0; !core_command && i < sieve_core_commands_count;
699
0
         i++) {
700
0
      if (strcasecmp(sieve_core_commands[i]->identifier,
701
0
               ext_name) == 0)
702
0
        core_command = TRUE;
703
0
    }
704
705
0
    for (i = 0; !core_test && i < sieve_core_tests_count; i++) {
706
0
      if (strcasecmp(sieve_core_tests[i]->identifier,
707
0
               ext_name) == 0)
708
0
        core_test = TRUE;
709
0
    }
710
711
0
    if (core_test || core_command) {
712
0
      sieve_argument_validate_error(
713
0
        valdtr, ext_arg,
714
0
        "%s %s: '%s' is not known as a Sieve capability, "
715
0
        "but it is known as a Sieve %s that is always available",
716
0
        sieve_command_identifier(cmd),
717
0
        sieve_command_type_name(cmd),
718
0
        str_sanitize(ext_name, 128),
719
0
        (core_test ? "test" : "command"));
720
0
    } else {
721
0
      sieve_argument_validate_error(
722
0
        valdtr, ext_arg,
723
0
        "%s %s: unknown Sieve capability '%s'",
724
0
        sieve_command_identifier(cmd),
725
0
        sieve_command_type_name(cmd),
726
0
        str_sanitize(ext_name, 128));
727
0
    }
728
0
    return NULL;
729
0
  }
730
731
0
  if (!sieve_validator_extension_load(valdtr, cmd, ext_arg, ext, TRUE))
732
0
    return NULL;
733
734
0
  return ext;
735
0
}
736
737
const struct sieve_extension *
738
sieve_validator_extension_load_implicit(struct sieve_validator *valdtr,
739
          const char *ext_name)
740
0
{
741
0
  const struct sieve_extension *ext;
742
743
0
  ext = sieve_extension_get_by_name(valdtr->svinst, ext_name);
744
745
0
  if (ext == NULL || ext->def == NULL)
746
0
    return NULL;
747
748
0
  if (!sieve_validator_extension_load(valdtr, NULL, NULL, ext, TRUE))
749
0
    return NULL;
750
751
0
  return ext;
752
0
}
753
754
void sieve_validator_extension_register(
755
  struct sieve_validator *valdtr, const struct sieve_extension *ext,
756
  const struct sieve_validator_extension *valext, void *context)
757
0
{
758
0
  struct sieve_validator_extension_reg *reg;
759
760
0
  if (ext->id < 0)
761
0
    return;
762
763
0
  reg = array_idx_get_space(&valdtr->extensions, (unsigned int) ext->id);
764
0
  i_assert(reg->ext == NULL || reg->ext == ext);
765
0
  reg->ext = ext;
766
0
  reg->valext = valext;
767
0
  reg->context = context;
768
0
}
769
770
bool sieve_validator_extension_loaded(struct sieve_validator *valdtr,
771
              const struct sieve_extension *ext)
772
0
{
773
0
  const struct sieve_validator_extension_reg *reg;
774
775
0
  if (ext->id < 0 || ext->id >= (int) array_count(&valdtr->extensions))
776
0
    return FALSE;
777
778
0
  reg = array_idx(&valdtr->extensions, (unsigned int) ext->id);
779
780
0
  return (reg->loaded);
781
0
}
782
783
void sieve_validator_extension_set_context(struct sieve_validator *valdtr,
784
             const struct sieve_extension *ext,
785
             void *context)
786
0
{
787
0
  struct sieve_validator_extension_reg *reg;
788
789
0
  if (ext->id < 0)
790
0
    return;
791
792
0
  reg = array_idx_get_space(&valdtr->extensions, (unsigned int) ext->id);
793
0
  reg->context = context;
794
0
}
795
796
void *sieve_validator_extension_get_context(struct sieve_validator *valdtr,
797
              const struct sieve_extension *ext)
798
0
{
799
0
  const struct sieve_validator_extension_reg *reg;
800
801
0
  if (ext->id < 0 || ext->id >= (int) array_count(&valdtr->extensions))
802
0
    return NULL;
803
804
0
  reg = array_idx(&valdtr->extensions, (unsigned int) ext->id);
805
806
0
  return reg->context;
807
0
}
808
809
/*
810
 * Overriding the default literal arguments
811
 */
812
813
void sieve_validator_argument_override(struct sieve_validator *valdtr,
814
               enum sieve_argument_type type,
815
               const struct sieve_extension *ext,
816
               const struct sieve_argument_def *arg_def)
817
0
{
818
0
  struct sieve_default_argument *arg;
819
820
0
  if (valdtr->default_arguments[type].arg_def == arg_def &&
821
0
      valdtr->default_arguments[type].ext == ext) {
822
    /* Already the active override; don't extend the overrides
823
       chain with a duplicate. */
824
0
    return;
825
0
  }
826
827
0
  if (valdtr->default_arguments[type].arg_def != NULL) {
828
0
    arg = p_new(valdtr->pool, struct sieve_default_argument, 1);
829
0
    *arg = valdtr->default_arguments[type];
830
831
0
    valdtr->default_arguments[type].overrides = arg;
832
0
  }
833
834
0
  valdtr->default_arguments[type].arg_def = arg_def;
835
0
  valdtr->default_arguments[type].ext = ext;
836
0
}
837
838
static bool
839
sieve_validator_argument_default_activate(struct sieve_validator *valdtr,
840
            struct sieve_command *cmd,
841
            struct sieve_default_argument *defarg,
842
            struct sieve_ast_argument *arg)
843
0
{
844
0
  bool result = TRUE;
845
0
  struct sieve_default_argument *prev_defarg;
846
847
0
  prev_defarg = valdtr->current_defarg;
848
0
  valdtr->current_defarg = defarg;
849
850
0
  if (arg->argument == NULL) {
851
0
    arg->argument = sieve_argument_create(arg->ast, defarg->arg_def,
852
0
                  defarg->ext, 0);
853
0
  } else {
854
0
    arg->argument->def = defarg->arg_def;
855
0
    arg->argument->ext = defarg->ext;
856
0
  }
857
858
0
  if (defarg->arg_def != NULL && defarg->arg_def->validate != NULL)
859
0
    result = defarg->arg_def->validate(valdtr, &arg, cmd);
860
861
0
  valdtr->current_defarg = prev_defarg;
862
863
0
  return result;
864
0
}
865
866
bool sieve_validator_argument_activate_super(struct sieve_validator *valdtr,
867
               struct sieve_command *cmd,
868
               struct sieve_ast_argument *arg,
869
               bool constant ATTR_UNUSED)
870
0
{
871
0
  struct sieve_default_argument *defarg;
872
873
0
  if (valdtr->current_defarg == NULL ||
874
0
      valdtr->current_defarg->overrides == NULL)
875
0
    return FALSE;
876
877
0
  if (valdtr->current_defarg->overrides->arg_def == &string_argument) {
878
0
    switch (valdtr->current_defarg_type) {
879
0
    case SAT_CONST_STRING:
880
0
      if (!valdtr->current_defarg_constant) {
881
0
        valdtr->current_defarg_type = SAT_VAR_STRING;
882
0
        defarg = &valdtr->default_arguments[SAT_VAR_STRING];
883
0
      } else {
884
0
        defarg = valdtr->current_defarg->overrides;
885
0
      }
886
0
      break;
887
0
    case SAT_VAR_STRING:
888
0
      defarg = valdtr->current_defarg->overrides;
889
0
      break;
890
0
    default:
891
0
      return FALSE;
892
0
    }
893
0
  } else {
894
0
    defarg = valdtr->current_defarg->overrides;
895
0
  }
896
897
0
  return sieve_validator_argument_default_activate(valdtr, cmd,
898
0
               defarg, arg);
899
0
}
900
901
/*
902
 * Argument Validation API
903
 */
904
905
bool sieve_validator_argument_activate(struct sieve_validator *valdtr,
906
               struct sieve_command *cmd,
907
               struct sieve_ast_argument *arg,
908
               bool constant)
909
0
{
910
0
  struct sieve_default_argument *defarg;
911
912
0
  switch (sieve_ast_argument_type(arg)) {
913
0
  case SAAT_NUMBER:
914
0
    valdtr->current_defarg_type = SAT_NUMBER;
915
0
    break;
916
0
  case SAAT_STRING:
917
0
    valdtr->current_defarg_type = SAT_CONST_STRING;
918
0
    break;
919
0
  case SAAT_STRING_LIST:
920
0
    valdtr->current_defarg_type = SAT_STRING_LIST;
921
0
    break;
922
0
  default:
923
0
    return FALSE;
924
0
  }
925
926
0
  valdtr->current_defarg_constant = constant;
927
0
  defarg = &valdtr->default_arguments[valdtr->current_defarg_type];
928
929
0
  if (!constant && defarg->arg_def == &string_argument) {
930
0
    valdtr->current_defarg_type = SAT_VAR_STRING;
931
0
    defarg = &valdtr->default_arguments[SAT_VAR_STRING];
932
0
  }
933
934
0
  return sieve_validator_argument_default_activate(valdtr, cmd,
935
0
               defarg, arg);
936
0
}
937
938
bool sieve_validate_positional_argument(struct sieve_validator *valdtr,
939
          struct sieve_command *cmd,
940
          struct sieve_ast_argument *arg,
941
          const char *arg_name,
942
          unsigned int arg_pos,
943
          enum sieve_ast_argument_type req_type)
944
0
{
945
0
  i_assert(arg != NULL);
946
947
0
  if (sieve_ast_argument_type(arg) != req_type &&
948
0
      (sieve_ast_argument_type(arg) != SAAT_STRING ||
949
0
       req_type != SAAT_STRING_LIST))
950
0
  {
951
0
    sieve_argument_validate_error(
952
0
      valdtr, arg,
953
0
      "the %s %s expects %s as argument %d (%s), "
954
0
      "but %s was found",
955
0
      sieve_command_identifier(cmd),
956
0
      sieve_command_type_name(cmd),
957
0
      sieve_ast_argument_type_name(req_type),
958
0
      arg_pos, arg_name, sieve_ast_argument_name(arg));
959
0
    return FALSE;
960
0
  }
961
962
0
  return TRUE;
963
0
}
964
965
bool sieve_validate_tag_parameter(struct sieve_validator *valdtr,
966
          struct sieve_command *cmd,
967
          struct sieve_ast_argument *tag,
968
          struct sieve_ast_argument *param,
969
          const char *arg_name, unsigned int arg_pos,
970
          enum sieve_ast_argument_type req_type,
971
          bool constant)
972
0
{
973
0
  i_assert(tag != NULL);
974
975
0
  if (param == NULL) {
976
0
    const char *position = (arg_pos == 0 ? "" :
977
0
      t_strdup_printf(" %d (%s)", arg_pos, arg_name));
978
979
0
    sieve_argument_validate_error(
980
0
      valdtr, tag,
981
0
      "the :%s tag for the %s %s requires %s as parameter%s, "
982
0
      "but no parameters were found",
983
0
      sieve_ast_argument_tag(tag),
984
0
      sieve_command_identifier(cmd),
985
0
      sieve_command_type_name(cmd),
986
0
      sieve_ast_argument_type_name(req_type), position);
987
0
    return FALSE;
988
0
  }
989
990
0
  if (sieve_ast_argument_type(param) != req_type &&
991
0
      (sieve_ast_argument_type(param) != SAAT_STRING ||
992
0
       req_type != SAAT_STRING_LIST))
993
0
  {
994
0
    const char *position = (arg_pos == 0 ? "" :
995
0
      t_strdup_printf(" %d (%s)", arg_pos, arg_name));
996
997
0
    sieve_argument_validate_error(
998
0
      valdtr, param,
999
0
      "the :%s tag for the %s %s requires %s as parameter%s, "
1000
0
      "but %s was found",
1001
0
      sieve_ast_argument_tag(tag),
1002
0
      sieve_command_identifier(cmd),
1003
0
      sieve_command_type_name(cmd),
1004
0
      sieve_ast_argument_type_name(req_type), position,
1005
0
      sieve_ast_argument_name(param));
1006
0
    return FALSE;
1007
0
  }
1008
1009
0
  if (!sieve_validator_argument_activate(valdtr, cmd, param, constant))
1010
0
    return FALSE;
1011
1012
0
  param->argument->id_code = tag->argument->id_code;
1013
1014
0
  return TRUE;
1015
0
}
1016
1017
/*
1018
 * Command argument validation
1019
 */
1020
1021
static bool
1022
sieve_validate_command_arguments(struct sieve_validator *valdtr,
1023
         struct sieve_command *cmd)
1024
0
{
1025
0
  int arg_count = cmd->def->positional_args;
1026
0
  int real_count = 0;
1027
0
  struct sieve_ast_argument *arg;
1028
0
  struct sieve_command_registration *cmd_reg = cmd->reg;
1029
1030
  /* Resolve tagged arguments */
1031
0
  arg = sieve_ast_argument_first(cmd->ast_node);
1032
0
  while (arg != NULL) {
1033
0
    void *arg_data = NULL;
1034
0
    struct sieve_tag_registration *tag_reg;
1035
0
    const struct sieve_argument_def *tag_def;
1036
1037
0
    if (sieve_ast_argument_type(arg) != SAAT_TAG) {
1038
0
      arg = sieve_ast_argument_next(arg);
1039
0
      continue;
1040
0
    }
1041
1042
0
    tag_reg = sieve_validator_command_tag_get(valdtr, cmd,
1043
0
                arg, &arg_data);
1044
1045
0
    if (tag_reg == NULL) {
1046
0
      sieve_argument_validate_error(
1047
0
        valdtr, arg,
1048
0
        "unknown tagged argument ':%s' for the %s %s "
1049
0
        "(reported only once at first occurrence)",
1050
0
        sieve_ast_argument_tag(arg),
1051
0
        sieve_command_identifier(cmd),
1052
0
        sieve_command_type_name(cmd));
1053
0
      sieve_validator_register_unknown_tag(
1054
0
        valdtr, cmd_reg, sieve_ast_argument_tag(arg));
1055
0
      return FALSE;
1056
0
    }
1057
1058
    /* Check whether previously tagged as unknown */
1059
0
    if (_tag_registration_is_unknown(tag_reg))
1060
0
      return FALSE;
1061
1062
0
    tag_def = tag_reg->tag_def;
1063
1064
    /* Assign the tagged argument type to the ast for later
1065
       reference */
1066
0
    arg->argument = sieve_argument_create(
1067
0
      arg->ast, tag_def, tag_reg->ext, tag_reg->id_code);
1068
0
    arg->argument->data = arg_data;
1069
1070
0
    arg = sieve_ast_argument_next(arg);
1071
0
  }
1072
1073
  /* Validate tagged arguments */
1074
0
  arg = sieve_ast_argument_first(cmd->ast_node);
1075
0
  while (arg != NULL && sieve_ast_argument_type(arg) == SAAT_TAG) {
1076
0
    const struct sieve_argument_def *tag_def = arg->argument->def;
1077
0
    struct sieve_ast_argument *parg;
1078
1079
    /* Scan backwards for any duplicates */
1080
0
    if ((tag_def->flags & SIEVE_ARGUMENT_FLAG_MULTIPLE) == 0) {
1081
0
      parg = sieve_ast_argument_prev(arg);
1082
0
      while (parg != NULL) {
1083
0
        if ((sieve_ast_argument_type(parg) == SAAT_TAG &&
1084
0
             parg->argument->def == tag_def) ||
1085
0
            (arg->argument->id_code > 0 &&
1086
0
             parg->argument != NULL &&
1087
0
             parg->argument->id_code == arg->argument->id_code))
1088
0
        {
1089
0
          const char *tag_id = sieve_ast_argument_tag(arg);
1090
0
          const char *tag_desc =
1091
0
            strcmp(tag_def->identifier, tag_id) != 0 ?
1092
0
            t_strdup_printf("%s argument (:%s)",
1093
0
                      tag_def->identifier, tag_id) :
1094
0
            t_strdup_printf(":%s argument",
1095
0
                tag_def->identifier);
1096
1097
0
          sieve_argument_validate_error(
1098
0
            valdtr, arg,
1099
0
            "encountered duplicate %s for the %s %s",
1100
0
            tag_desc, sieve_command_identifier(cmd),
1101
0
            sieve_command_type_name(cmd));
1102
1103
0
          return FALSE;
1104
0
        }
1105
1106
0
        parg = sieve_ast_argument_prev(parg);
1107
0
      }
1108
0
    }
1109
1110
    /* Call the validation function for the tag (if present)
1111
         Fail if the validation fails:
1112
           Let's not whine multiple times about a single command
1113
           having multiple bad arguments...
1114
     */
1115
0
    if (tag_def->validate != NULL) {
1116
0
      if (!tag_def->validate(valdtr, &arg, cmd))
1117
0
        return FALSE;
1118
0
    } else {
1119
0
      arg = sieve_ast_argument_next(arg);
1120
0
    }
1121
0
  }
1122
1123
  /* Remaining arguments should be positional (tags are not allowed
1124
     here) */
1125
0
  cmd->first_positional = arg;
1126
1127
0
  while (arg != NULL) {
1128
0
    if (sieve_ast_argument_type(arg) == SAAT_TAG) {
1129
0
      sieve_argument_validate_error(
1130
0
        valdtr, arg,
1131
0
        "encountered an unexpected tagged argument ':%s' "
1132
0
        "while validating positional arguments for the %s %s",
1133
0
        sieve_ast_argument_tag(arg),
1134
0
        sieve_command_identifier(cmd),
1135
0
        sieve_command_type_name(cmd));
1136
0
      return FALSE;
1137
0
    }
1138
1139
0
    real_count++;
1140
1141
0
    arg = sieve_ast_argument_next(arg);
1142
0
  }
1143
1144
  /* Check the required count versus the real number of arguments */
1145
0
  if (arg_count >= 0 && real_count != arg_count) {
1146
0
    sieve_command_validate_error(
1147
0
      valdtr, cmd,
1148
0
      "the %s %s requires %d positional argument(s), "
1149
0
      "but %d is/are specified",
1150
0
      sieve_command_identifier(cmd),
1151
0
      sieve_command_type_name(cmd),
1152
0
      arg_count, real_count);
1153
0
    return FALSE;
1154
0
  }
1155
1156
  /* Call initial validation for persistent arguments */
1157
0
  if (array_is_created(&cmd_reg->persistent_tags)) {
1158
0
    struct sieve_tag_registration *const *regs;
1159
0
    unsigned int i, reg_count;
1160
1161
0
    regs = array_get(&cmd_reg->persistent_tags, &reg_count);
1162
0
    for (i = 0; i < reg_count; i++) {
1163
0
      const struct sieve_argument_def *tag_def =
1164
0
        regs[i]->tag_def;
1165
1166
0
      if (tag_def != NULL &&
1167
0
          tag_def->validate_persistent != NULL) {
1168
        /* To be sure */
1169
0
        if (!tag_def->validate_persistent(
1170
0
          valdtr, cmd, regs[i]->ext))
1171
0
            return FALSE;
1172
0
      }
1173
0
    }
1174
0
  }
1175
1176
0
  return TRUE;
1177
0
}
1178
1179
static bool
1180
sieve_validate_arguments_context(struct sieve_validator *valdtr,
1181
         struct sieve_command *cmd)
1182
0
{
1183
0
  struct sieve_ast_argument *arg =
1184
0
    sieve_command_first_argument(cmd);
1185
1186
0
  while (arg != NULL) {
1187
0
    const struct sieve_argument *argument = arg->argument;
1188
1189
0
    if (argument != NULL && argument->def != NULL &&
1190
0
        argument->def->validate_context != NULL) {
1191
1192
0
      if (!argument->def->validate_context(valdtr, arg, cmd))
1193
0
        return FALSE;
1194
0
    }
1195
1196
0
    arg = sieve_ast_argument_next(arg);
1197
0
  }
1198
1199
0
  return TRUE;
1200
0
}
1201
1202
/*
1203
 * Command Validation API
1204
 */
1205
1206
static bool
1207
sieve_validate_command_subtests(struct sieve_validator *valdtr,
1208
        struct sieve_command *cmd,
1209
        const unsigned int count)
1210
0
{
1211
0
  switch (count) {
1212
0
  case 0:
1213
0
    if (sieve_ast_test_count(cmd->ast_node) > 0) {
1214
      /* Unexpected command specified */
1215
0
      enum sieve_command_type ctype = SCT_NONE;
1216
0
      struct sieve_command_registration *cmd_reg;
1217
0
      struct sieve_ast_node *test =
1218
0
        sieve_ast_test_first(cmd->ast_node);
1219
1220
0
      cmd_reg = sieve_validator_find_command_registration(
1221
0
        valdtr, test->identifier);
1222
1223
      /* First check what we are dealing with */
1224
0
      if (cmd_reg != NULL && cmd_reg->cmd_def != NULL)
1225
0
        ctype = cmd_reg->cmd_def->type;
1226
1227
0
      switch (ctype) {
1228
0
      case SCT_TEST: /* Spurious test */
1229
0
      case SCT_HYBRID:
1230
0
        sieve_command_validate_error(
1231
0
          valdtr, cmd,
1232
0
          "the %s %s accepts no sub-tests, "
1233
0
          "but tests are specified",
1234
0
          sieve_command_identifier(cmd),
1235
0
          sieve_command_type_name(cmd));
1236
0
        break;
1237
0
      case SCT_NONE: /* Unknown command */
1238
        /* Is it perhaps a tag for which the ':' was
1239
           omitted ? */
1240
0
        if (sieve_validator_command_tag_exists(
1241
0
          valdtr, cmd, test->identifier)) {
1242
0
          sieve_command_validate_error(
1243
0
            valdtr, cmd,
1244
0
            "missing colon ':' before ':%s' tag in %s %s",
1245
0
            test->identifier,
1246
0
            sieve_command_identifier(cmd),
1247
0
            sieve_command_type_name(cmd));
1248
0
          break;
1249
0
        }
1250
        /* Fall through */
1251
0
      case SCT_COMMAND:
1252
0
        sieve_command_validate_error(
1253
0
          valdtr, cmd,
1254
0
          "missing semicolon ';' after %s %s",
1255
0
          sieve_command_identifier(cmd),
1256
0
          sieve_command_type_name(cmd));
1257
0
        break;
1258
0
      }
1259
0
      return FALSE;
1260
0
    }
1261
0
    break;
1262
0
  case 1:
1263
0
    if (sieve_ast_test_count(cmd->ast_node) == 0) {
1264
0
      sieve_command_validate_error(
1265
0
        valdtr, cmd,
1266
0
        "the %s %s requires one sub-test, "
1267
0
        "but none is specified",
1268
0
        sieve_command_identifier(cmd),
1269
0
        sieve_command_type_name(cmd));
1270
0
      return FALSE;
1271
1272
0
    } else if (sieve_ast_test_count(cmd->ast_node) > 1 ||
1273
0
         cmd->ast_node->test_list) {
1274
0
      sieve_command_validate_error(
1275
0
        valdtr, cmd,
1276
0
        "the %s %s requires one sub-test, "
1277
0
        "but a list of tests is specified",
1278
0
        sieve_command_identifier(cmd),
1279
0
        sieve_command_type_name(cmd));
1280
0
      return FALSE;
1281
0
    }
1282
0
    break;
1283
0
  default:
1284
0
    if (sieve_ast_test_count(cmd->ast_node) == 0) {
1285
0
      sieve_command_validate_error(
1286
0
        valdtr, cmd,
1287
0
        "the %s %s requires a list of sub-tests, "
1288
0
        "but none is specified",
1289
0
        sieve_command_identifier(cmd),
1290
0
        sieve_command_type_name(cmd));
1291
0
      return FALSE;
1292
0
    } else if (sieve_ast_test_count(cmd->ast_node) == 1 &&
1293
0
         !cmd->ast_node->test_list) {
1294
0
      sieve_command_validate_error(
1295
0
        valdtr, cmd,
1296
0
        "the %s %s requires a list of sub-tests, "
1297
0
        "but a single test is specified",
1298
0
        sieve_command_identifier(cmd),
1299
0
        sieve_command_type_name(cmd));
1300
0
      return FALSE;
1301
0
    }
1302
0
    break;
1303
0
  }
1304
1305
0
  return TRUE;
1306
0
}
1307
1308
static bool
1309
sieve_validate_command_block(struct sieve_validator *valdtr,
1310
           struct sieve_command *cmd, bool block_allowed,
1311
           bool block_required)
1312
0
{
1313
0
  i_assert(cmd->ast_node->type == SAT_COMMAND);
1314
1315
0
  if (block_required) {
1316
0
    if (!cmd->ast_node->block) {
1317
0
      sieve_command_validate_error(
1318
0
        valdtr, cmd,
1319
0
        "the %s command requires a command block, "
1320
0
        "but it is missing",
1321
0
        sieve_command_identifier(cmd));
1322
0
      return FALSE;
1323
0
    }
1324
0
  } else if (!block_allowed && cmd->ast_node->block) {
1325
0
    sieve_command_validate_error(
1326
0
      valdtr, cmd,
1327
0
      "the %s command does not accept a command block, "
1328
0
      "but one is specified anyway",
1329
0
      sieve_command_identifier(cmd));
1330
0
    return FALSE;
1331
0
  }
1332
1333
0
  return TRUE;
1334
0
}
1335
1336
/*
1337
 * AST Validation
1338
 */
1339
1340
static bool
1341
sieve_validate_test_list(struct sieve_validator *valdtr,
1342
       struct sieve_ast_node *test_list, int *const_r);
1343
static bool
1344
sieve_validate_block(struct sieve_validator *valdtr,
1345
         struct sieve_ast_node *block);
1346
static bool
1347
sieve_validate_command(struct sieve_validator *valdtr,
1348
           struct sieve_ast_node *cmd_node, int *const_r);
1349
1350
static bool
1351
sieve_validate_command_context(struct sieve_validator *valdtr,
1352
             struct sieve_ast_node *cmd_node)
1353
0
{
1354
0
  enum sieve_ast_type ast_type = sieve_ast_node_type(cmd_node);
1355
0
  struct sieve_command_registration *cmd_reg;
1356
1357
0
  i_assert(ast_type == SAT_TEST || ast_type == SAT_COMMAND);
1358
1359
  /* Verify the command specified by this node */
1360
0
  cmd_reg = sieve_validator_find_command_registration(
1361
0
    valdtr, cmd_node->identifier);
1362
1363
0
  if (cmd_reg != NULL && cmd_reg->cmd_def != NULL) {
1364
0
    const struct sieve_command_def *cmd_def = cmd_reg->cmd_def;
1365
1366
    /* Identifier = "" when the command was previously marked as
1367
       unknown */
1368
0
    if (*(cmd_def->identifier) != '\0') {
1369
0
      if ((cmd_def->type == SCT_COMMAND && ast_type == SAT_TEST) ||
1370
0
          (cmd_def->type == SCT_TEST && ast_type == SAT_COMMAND)) {
1371
0
        sieve_validator_error(
1372
0
          valdtr, cmd_node->source_line,
1373
0
          "attempted to use %s '%s' as %s",
1374
0
          sieve_command_def_type_name(cmd_def),
1375
0
          cmd_node->identifier,
1376
0
          sieve_ast_type_name(ast_type));
1377
0
        return FALSE;
1378
0
      }
1379
1380
0
      cmd_node->command = sieve_command_create(
1381
0
        cmd_node, cmd_reg->ext, cmd_def, cmd_reg);
1382
0
    } else {
1383
0
      return FALSE;
1384
0
    }
1385
0
  } else {
1386
0
    sieve_validator_error(
1387
0
      valdtr, cmd_node->source_line,
1388
0
      "unknown %s '%s' (only reported once at first occurrence)",
1389
0
      sieve_ast_type_name(ast_type), cmd_node->identifier);
1390
1391
0
    sieve_validator_register_unknown_command(
1392
0
      valdtr, cmd_node->identifier);
1393
0
    return FALSE;
1394
0
  }
1395
1396
0
  return TRUE;
1397
0
}
1398
1399
static bool
1400
sieve_validate_command(struct sieve_validator *valdtr,
1401
           struct sieve_ast_node *cmd_node, int *const_r)
1402
0
{
1403
0
  enum sieve_ast_type ast_type = sieve_ast_node_type(cmd_node);
1404
0
  struct sieve_command *cmd =
1405
0
    (cmd_node == NULL ? NULL : cmd_node->command);
1406
0
  const struct sieve_command_def *cmd_def =
1407
0
    (cmd != NULL ? cmd->def : NULL);
1408
0
  bool result = TRUE;
1409
1410
0
  i_assert(ast_type == SAT_TEST || ast_type == SAT_COMMAND);
1411
1412
0
  if (cmd_def != NULL && *(cmd_def->identifier) != '\0') {
1413
0
    if (cmd_def->pre_validate == NULL ||
1414
0
        cmd_def->pre_validate(valdtr, cmd)) {
1415
      /* Check argument syntax */
1416
0
      if (!sieve_validate_command_arguments(valdtr, cmd)) {
1417
0
        result = FALSE;
1418
1419
        /* A missing ':' causes a tag to become a test.
1420
           This can be the cause of the arguments
1421
           validation failing. Therefore we must produce
1422
           an error for the sub-tests as well if
1423
           appropriate. */
1424
0
        (void)sieve_validate_command_subtests(
1425
0
          valdtr, cmd, cmd_def->subtests);
1426
0
      } else if (!sieve_validate_command_subtests(
1427
0
        valdtr, cmd, cmd_def->subtests) ||
1428
0
        (ast_type == SAT_COMMAND &&
1429
0
         !sieve_validate_command_block(
1430
0
          valdtr, cmd, cmd_def->block_allowed,
1431
0
          cmd_def->block_required))) {
1432
0
        result = FALSE;
1433
0
      } else {
1434
        /* Call command validation function if specified
1435
         */
1436
0
        if (cmd_def->validate != NULL) {
1437
0
          result = cmd_def->validate(valdtr, cmd) &&
1438
0
            result;
1439
0
        }
1440
0
      }
1441
0
    } else {
1442
      /* If pre-validation fails, don't bother to validate
1443
         further as context might be missing and doing so is
1444
         not very useful for further error reporting anyway */
1445
0
      valdtr->failed = TRUE;
1446
0
      return FALSE;
1447
0
    }
1448
1449
0
    result = result && sieve_validate_arguments_context(valdtr, cmd);
1450
0
  }
1451
0
  if (!result)
1452
0
    valdtr->failed = TRUE;
1453
1454
  /*
1455
   * Descend further into the AST
1456
   */
1457
1458
0
  if (cmd_def != NULL) {
1459
    /* Tests */
1460
0
    if (cmd_def->subtests > 0) {
1461
0
      if (result ||
1462
0
          sieve_errors_more_allowed(valdtr->ehandler)) {
1463
0
        result = sieve_validate_test_list(
1464
0
          valdtr, cmd_node, const_r) && result;
1465
0
      }
1466
0
    } else if (result) {
1467
0
      if (cmd_def->validate_const != NULL) {
1468
0
        (void)cmd_def->validate_const(
1469
0
          valdtr, cmd, const_r, -1);
1470
0
      } else {
1471
0
        *const_r = -1;
1472
0
      }
1473
0
    }
1474
1475
    /* Skip block if result of test is const FALSE */
1476
0
    if (result && *const_r == 0)
1477
0
      return TRUE;
1478
1479
    /* Command block */
1480
0
    if (cmd_def->block_allowed && ast_type == SAT_COMMAND &&
1481
0
        (result || sieve_errors_more_allowed(valdtr->ehandler))) {
1482
0
      result = sieve_validate_block(valdtr, cmd_node) &&
1483
0
        result;
1484
0
    }
1485
0
  }
1486
1487
0
  return result;
1488
0
}
1489
1490
static bool
1491
sieve_validate_test_list(struct sieve_validator *valdtr,
1492
       struct sieve_ast_node *test_node, int *const_r)
1493
0
{
1494
0
  struct sieve_command *tst = test_node->command;
1495
0
  const struct sieve_command_def *tst_def =
1496
0
    (tst != NULL ? tst->def : NULL);
1497
0
  struct sieve_ast_node *test;
1498
0
  bool result = TRUE;
1499
1500
0
  if (tst_def != NULL && tst_def->validate_const != NULL) {
1501
0
    if (!tst_def->validate_const(valdtr, tst, const_r, -2))
1502
0
      return TRUE;
1503
0
  }
1504
1505
0
  test = sieve_ast_test_first(test_node);
1506
0
  while (test != NULL &&
1507
0
         (result || sieve_errors_more_allowed(valdtr->ehandler))) {
1508
0
    int const_value = -2;
1509
1510
0
    result = sieve_validate_command_context(valdtr, test) &&
1511
0
      sieve_validate_command(valdtr, test, &const_value) &&
1512
0
      result;
1513
1514
0
    if (!result)
1515
0
      valdtr->failed = TRUE;
1516
0
    else {
1517
0
      if (tst_def != NULL &&
1518
0
          tst_def->validate_const != NULL) {
1519
0
        if (!tst_def->validate_const(
1520
0
          valdtr, tst, const_r, const_value))
1521
0
          return TRUE;
1522
0
      } else {
1523
0
        *const_r = -1;
1524
0
      }
1525
0
    }
1526
1527
0
    if (result && const_value >= 0)
1528
0
      test = sieve_ast_node_detach(test);
1529
0
    else
1530
0
      test = sieve_ast_test_next(test);
1531
0
  }
1532
1533
0
  return result;
1534
0
}
1535
1536
static bool
1537
sieve_validate_block(struct sieve_validator *valdtr,
1538
         struct sieve_ast_node *block)
1539
0
{
1540
0
  bool result = TRUE, fatal = FALSE, already_failed = valdtr->failed;
1541
0
  struct sieve_ast_node *cmd_node, *next;
1542
1543
0
  T_BEGIN {
1544
0
    cmd_node = sieve_ast_command_first(block);
1545
0
    while (!fatal && cmd_node != NULL &&
1546
0
           (result ||
1547
0
      sieve_errors_more_allowed(valdtr->ehandler))) {
1548
0
      bool command_success;
1549
0
      int const_value = -2;
1550
1551
0
      next = sieve_ast_command_next(cmd_node);
1552
1553
      /* Check if this is the first non-require command */
1554
0
      if (sieve_ast_node_type(block) == SAT_ROOT &&
1555
0
          !valdtr->finished_require &&
1556
0
          strcasecmp(cmd_node->identifier,
1557
0
               cmd_require.identifier) != 0) {
1558
0
        const struct sieve_validator_extension_reg *extrs;
1559
0
        const struct sieve_extension *const *exts;
1560
0
        unsigned int ext_count, i;
1561
1562
0
        valdtr->finished_require = TRUE;
1563
1564
        /* Load implicit extensions */
1565
0
        exts = sieve_extensions_get_all(valdtr->svinst, &ext_count);
1566
0
        for (i = 0; i < ext_count; i++) {
1567
0
          if (exts[i]->implicit) {
1568
0
            (void)sieve_validator_extension_load(
1569
0
              valdtr, NULL, NULL, exts[i], TRUE);
1570
0
          }
1571
0
        }
1572
1573
        /* Validate all 'require'd extensions */
1574
0
        extrs = array_get(&valdtr->extensions, &ext_count);
1575
0
        for (i = 0; i < ext_count; i++) {
1576
0
          if (extrs[i].loaded && extrs[i].valext != NULL &&
1577
0
              extrs[i].valext->validate != NULL) {
1578
0
            if (!extrs[i].valext->validate(
1579
0
              extrs[i].ext, valdtr,
1580
0
              extrs[i].context, extrs[i].arg,
1581
0
              extrs[i].required)) {
1582
0
              fatal = TRUE;
1583
0
              break;
1584
0
            }
1585
0
          }
1586
0
        }
1587
0
      }
1588
1589
0
      command_success =
1590
0
        sieve_validate_command_context(valdtr, cmd_node);
1591
0
      result = command_success && result;
1592
1593
0
      if (!result || fatal)
1594
0
        valdtr->failed = TRUE;
1595
1596
0
      result = !fatal &&
1597
0
        sieve_validate_command(valdtr, cmd_node,
1598
0
                   &const_value) && result;
1599
1600
0
      cmd_node = next;
1601
0
    }
1602
0
  } T_END;
1603
1604
  /* Assert that this function returns FALSE if failure is caused here. */
1605
0
  i_assert(!valdtr->failed || already_failed || !result || fatal);
1606
0
  return result && !fatal;
1607
0
}
1608
1609
bool sieve_validator_run(struct sieve_validator *valdtr)
1610
0
{
1611
0
  return sieve_validate_block(valdtr, sieve_ast_root(valdtr->ast));
1612
0
}
1613
1614
/*
1615
 * Validator object registry
1616
 */
1617
1618
struct sieve_validator_object_reg {
1619
  const struct sieve_object_def *obj_def;
1620
  const struct sieve_extension *ext;
1621
};
1622
1623
struct sieve_validator_object_registry {
1624
  struct sieve_validator *valdtr;
1625
  ARRAY(struct sieve_validator_object_reg) registrations;
1626
};
1627
1628
struct sieve_validator_object_registry *
1629
sieve_validator_object_registry_get(struct sieve_validator *valdtr,
1630
            const struct sieve_extension *ext)
1631
0
{
1632
0
  return (struct sieve_validator_object_registry *)
1633
0
    sieve_validator_extension_get_context(valdtr, ext);
1634
0
}
1635
1636
void sieve_validator_object_registry_add(
1637
  struct sieve_validator_object_registry *regs,
1638
  const struct sieve_extension *ext,
1639
  const struct sieve_object_def *obj_def)
1640
0
{
1641
0
  struct sieve_validator_object_reg *reg;
1642
1643
0
  reg = array_append_space(&regs->registrations);
1644
0
  reg->ext = ext;
1645
0
  reg->obj_def = obj_def;
1646
0
}
1647
1648
bool sieve_validator_object_registry_find(
1649
  struct sieve_validator_object_registry *regs, const char *identifier,
1650
  struct sieve_object *obj)
1651
0
{
1652
0
  unsigned int i;
1653
1654
0
  for (i = 0; i < array_count(&regs->registrations); i++) {
1655
0
    const struct sieve_validator_object_reg *reg =
1656
0
      array_idx(&regs->registrations, i);
1657
1658
0
    if (strcasecmp(reg->obj_def->identifier, identifier) == 0) {
1659
0
      if (obj != NULL) {
1660
0
        obj->def = reg->obj_def;
1661
0
        obj->ext = reg->ext;
1662
0
      }
1663
0
      return TRUE;
1664
0
    }
1665
0
  }
1666
1667
0
  return FALSE;
1668
0
}
1669
1670
struct sieve_validator_object_registry *
1671
sieve_validator_object_registry_create(struct sieve_validator *valdtr)
1672
0
{
1673
0
  pool_t pool = valdtr->pool;
1674
0
  struct sieve_validator_object_registry *regs =
1675
0
    p_new(pool, struct sieve_validator_object_registry, 1);
1676
1677
  /* Setup registry */
1678
0
  p_array_init(&regs->registrations, valdtr->pool, 4);
1679
1680
0
  regs->valdtr = valdtr;
1681
1682
0
  return regs;
1683
0
}
1684
1685
struct sieve_validator_object_registry *
1686
sieve_validator_object_registry_init(struct sieve_validator *valdtr,
1687
             const struct sieve_extension *ext)
1688
0
{
1689
0
  struct sieve_validator_object_registry *regs =
1690
0
    sieve_validator_object_registry_create(valdtr);
1691
1692
0
  sieve_validator_extension_set_context(valdtr, ext, regs);
1693
0
  return regs;
1694
0
}
1695
1696
/*
1697
 * Error handling
1698
 */
1699
1700
#undef sieve_validator_error
1701
void sieve_validator_error(struct sieve_validator *valdtr,
1702
         const char *csrc_filename, unsigned int csrc_linenum,
1703
         unsigned int source_line, const char *fmt, ...)
1704
0
{
1705
0
  struct sieve_error_params params = {
1706
0
    .log_type = LOG_TYPE_ERROR,
1707
0
    .csrc = {
1708
0
      .filename = csrc_filename,
1709
0
      .linenum = csrc_linenum,
1710
0
    },
1711
0
  };
1712
0
  va_list args;
1713
1714
0
  params.location =
1715
0
    sieve_error_script_location(valdtr->script, source_line);
1716
1717
0
  va_start(args, fmt);
1718
0
  sieve_logv(valdtr->ehandler, &params, fmt, args);
1719
0
  va_end(args);
1720
0
}
1721
1722
#undef sieve_validator_warning
1723
void sieve_validator_warning(struct sieve_validator *valdtr,
1724
           const char *csrc_filename,
1725
           unsigned int csrc_linenum,
1726
           unsigned int source_line, const char *fmt, ...)
1727
0
{
1728
0
  struct sieve_error_params params = {
1729
0
    .log_type = LOG_TYPE_WARNING,
1730
0
    .csrc = {
1731
0
      .filename = csrc_filename,
1732
0
      .linenum = csrc_linenum,
1733
0
    },
1734
0
  };
1735
0
  va_list args;
1736
1737
0
  params.location =
1738
0
    sieve_error_script_location(valdtr->script, source_line);
1739
1740
0
  va_start(args, fmt);
1741
0
  sieve_logv(valdtr->ehandler, &params, fmt, args);
1742
0
  va_end(args);
1743
1744
0
}