Coverage Report

Created: 2026-04-12 07:00

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