~ubuntu-dev/ubuntu/lucid/dovecot/lucid-201002110912

« back to all changes in this revision

Viewing changes to libsieve/src/lib-sieve/sieve-validator.c

  • Committer: Chuck Short
  • Date: 2010-01-21 20:21:25 UTC
  • mfrom: (4.1.11 squeeze)
  • Revision ID: zulcss@ubuntu.com-20100121202125-pme73o491kfwj5nc
* Merge from debian testing, remaining changes:
  + Add new binary pkg dovecot-postfix that integrates postfix and dovecot
    automatically: (LP: #164837)
  + debian/control:
    - add new binary with short description
    - set Architecture all for dovecot-postfix (LP: #329878)
  + debian/dovecot-postfix.postinst:
    - create initial certificate symlinks to snakeoil.
    - set up postfix with postconf to:
      - use Maildir/ as the default mailbox.
      - use dovecot as the sasl authentication server.
      - use dovecot LDA (deliver).
      - use tls for smtp{d} services.
    - fix certificates paths in postfix' main.cf
    - add reject_unauth_destination to postfix' recipient restrictions
    - add reject_unknown_sender_domain to postfix' sender restriction
    - rename configuration name on remove, delete on purge
    - restart dovecot after linking certificates
    - handle use case when postfix is unconfigurated
  + debian/dovecot-postfix.dirs: create backup directory for postfix's config
    configuration
  + restart postfix and dovecot.
  + debian/dovecot-postfix.postrm:
    - remove all dovecot related configuration from postfix.
    - restart postfix and dovecot.
  + debian/dovecot-common.init:
    - check if /etc/dovecot/dovecot-postfix.conf exists and use it
      as the configuration file if so.
  + debian/patches/warning-ubuntu-postfix.dpatch
    - add warning about dovecot-postfix.conf in dovecot default
      configuration file
  + debian/patches/dovecot-postfix.conf.diff:
    - Ubuntu server custom changes to the default dovecot configuration for
      better interfation with postfix.
    - enable sieve plugin.
    - Ubuntu server custom changes to the default dovecot configuration for
      better integration with postfix:
      - enable imap, pop3, imaps, pop3s and managesieve by default.
      - enable dovecot LDA (deliver).
      - enable SASL auth socket in postfix private directory
   + debian/rules:
     - copy, patch and install dovecot-postfix.conf in /etc/dovecot/.
     - build architecure independent packages too
   + Use Snakeoil SSL certificates by default.
     - debian/control: Depend on ssl-cert.
     - debian/patches/ssl-cert-snakeoil.dpatch: Change default SSL cert
       paths to snakeoil.
     - debian/dovecot-common.postinst: Relax grep for SSL_* a bit.
   + Add autopkgtest to debian/tests/*.
   + Fast TearDown: Update the lsb init header to not stop in level 6.
   + Add ufw integration:
     - Created debian/dovecot-common.ufw.profile.
     - debian/rules: install profile.
     - debian/control: suggest ufw.
   + debian/{control,rules}: enable PIE hardening.
   + dovecot-imapd, dovecot-pop3: Replaces dovecot-common (<< 1:1.1). (LP: #254721)
   + debian/control: Update Vcs-* headers.
   + Add SMTP-AUTH support for Outlook (login auth mechanism)
* New upstream release.
* debian/patches/gold-fix.patch: Removed. Fixed upstream.
* Moved libexec to lib corrections in dovecot-managesieve.patch and
  dovecot-managesieve-dist.patch to dovecot-example.patch
* debian/patches/dovecot-mboxlocking.patch: Regenerated to avoid FTBFS
  when quilt isn't installed.
* debian/patches/quota-mountpoint.patch: Removed. Not needed anymore.
* debian/patches/dovecot-quota.patch: Removed. Quotas aren't properly
  enabled unless mail_plugins = quota imap_quota.
* debian/patches/gold-fix.patch: Fixed configure script to build even
  with binutils-gold or --no-add-needed linker flag (Closes: #554306)
* debian/dovecot-common.init: fixed LSB headers. Thanks to Pascal Volk.
  (Closes: #558040)
* debian/changelog: added CVE references to previous changelog entry.
* debian/rules: checked up the build system. It's not fragile anymore.
  (Closes: 493803)
* debian/dovecot-common.postinst: Now invoking dpkg-reconfigure
  on dovecot-common is enough to generate new certificates
  if the previous ones were removed. (Closes: #545582)
* debian/rules: No longer install convert-tool in /usr/bin.
  It isn't an user utility and it should stay in /usr/lib/dovecot
  like all other similar tool.
* New upstream release. (Closes: #557601)
* [SECURITY] Fixes local information disclosure and denial of service.
  (see: http://www.dovecot.org/list/dovecot-news/2009-November/000143.html
  and CVE-2009-3897)
* Added myself to uploaders.
* Switched to the new source format "3.0 (quilt)":
  - removed dpatch from build-depends
  - removed debian/README.source because now we use only standard
    dpkg features
  - regenerated all patches
* Prepared to switch to multi-origin source:
  - recreated dovecot-libsieve.patch and dovecot-managesieve-dist.patch
    starting from the upstream tarball
  - removed all autotools related build-depends and build-conflict
  - renamed dovecot-libsieve and dovecot-managesieve directories
    to libsieve and managesieve.
* debian/rules: Moved the configuration of libsieve and managesieve from
  the build phase to the configuration phase
* Added dovecot-dbg package  with debugging symbols.  Thanks Stephan Bosch.
  (Closes: #554710)
* Fixed some stray libexec'isms in the default configuration.
* New upstream release.
* debian/dovecot-common.init:
  - use $CONF when starting the daemon. (Closes: #549944)
  - always output start/stop messages. (Closes: #523810)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2002-2009 Dovecot Sieve 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 sieve_validator_register_core_commands
 
27
        (struct sieve_validator *validator);
 
28
static void sieve_validator_register_core_tests
 
29
        (struct sieve_validator *validator);
 
30
        
 
31
/*
 
32
 * Types
 
33
 */
 
34
 
 
35
/* Tag registration */
 
36
 
 
37
struct sieve_tag_registration {
 
38
        const struct sieve_argument *tag;
 
39
        const char *identifier; 
 
40
        int id_code;
 
41
};
 
42
 
 
43
/* Command registration */
 
44
 
 
45
struct sieve_command_registration {
 
46
        const struct sieve_command *command;
 
47
        
 
48
        ARRAY_DEFINE(normal_tags, struct sieve_tag_registration *); 
 
49
        ARRAY_DEFINE(instanced_tags, struct sieve_tag_registration *); 
 
50
        ARRAY_DEFINE(persistent_tags, struct sieve_tag_registration *); 
 
51
};
 
52
 
 
53
/* Default (literal) arguments */
 
54
 
 
55
struct sieve_default_argument {
 
56
        const struct sieve_argument *argument;
 
57
        struct sieve_default_argument *overrides;
 
58
};
 
59
 
 
60
/* 
 
61
 * Validator extension
 
62
 */
 
63
 
 
64
struct sieve_validator_extension_reg {
 
65
        const struct sieve_validator_extension *val_ext;
 
66
        struct sieve_ast_argument *arg;
 
67
        void *context;
 
68
 
 
69
        bool loaded;
 
70
};
 
71
 
 
72
/* 
 
73
 * Validator
 
74
 */
 
75
 
 
76
struct sieve_validator {
 
77
        pool_t pool;
 
78
 
 
79
        struct sieve_ast *ast;
 
80
        struct sieve_script *script;
 
81
        
 
82
        struct sieve_error_handler *ehandler;
 
83
 
 
84
        bool finished_require;
 
85
        
 
86
        /* Registries */
 
87
        
 
88
        struct hash_table *commands;
 
89
        
 
90
        ARRAY_DEFINE(extensions, struct sieve_validator_extension_reg);
 
91
        
 
92
        /* This is currently a wee bit ugly and needs more thought */
 
93
        struct sieve_default_argument default_arguments[SAT_COUNT];
 
94
 
 
95
        /* Default argument processing state (FIXME: ugly) */
 
96
        struct sieve_default_argument *current_defarg;
 
97
        enum sieve_argument_type current_defarg_type;
 
98
        bool current_defarg_constant;
 
99
};
 
100
 
 
101
/* 
 
102
 * Error handling 
 
103
 */
 
104
 
 
105
void sieve_validator_warning
 
106
(struct sieve_validator *validator, unsigned int source_line, 
 
107
        const char *fmt, ...) 
 
108
 
109
        va_list args;
 
110
        
 
111
        va_start(args, fmt);
 
112
        sieve_vwarning(validator->ehandler, 
 
113
                sieve_error_script_location(validator->script, source_line),
 
114
                fmt, args);
 
115
        va_end(args);
 
116
        
 
117
}
 
118
 
 
119
void sieve_validator_error
 
120
(struct sieve_validator *validator, unsigned int source_line, 
 
121
        const char *fmt, ...) 
 
122
{
 
123
        va_list args;
 
124
        
 
125
        va_start(args, fmt);
 
126
        sieve_verror(validator->ehandler, 
 
127
                sieve_error_script_location(validator->script, source_line),
 
128
                fmt, args);
 
129
        va_end(args);
 
130
}
 
131
 
 
132
void sieve_validator_critical
 
133
(struct sieve_validator *validator, unsigned int source_line, 
 
134
        const char *fmt, ...) 
 
135
{
 
136
        va_list args;
 
137
        
 
138
        va_start(args, fmt);
 
139
        sieve_vcritical(validator->ehandler, 
 
140
                sieve_error_script_location(validator->script, source_line),
 
141
                fmt, args);
 
142
        va_end(args);
 
143
}
 
144
 
 
145
/* 
 
146
 * Validator object 
 
147
 */
 
148
 
 
149
struct sieve_validator *sieve_validator_create
 
150
(struct sieve_ast *ast, struct sieve_error_handler *ehandler) 
 
151
{
 
152
        unsigned int i;
 
153
        pool_t pool;
 
154
        struct sieve_validator *validator;
 
155
        
 
156
        pool = pool_alloconly_create("sieve_validator", 8192);  
 
157
        validator = p_new(pool, struct sieve_validator, 1);
 
158
        validator->pool = pool;
 
159
        
 
160
        validator->ehandler = ehandler;
 
161
        sieve_error_handler_ref(ehandler);
 
162
        
 
163
        validator->ast = ast;   
 
164
        validator->script = sieve_ast_script(ast);
 
165
        sieve_ast_ref(ast);
 
166
 
 
167
        /* Setup default arguments */
 
168
        validator->default_arguments[SAT_NUMBER].
 
169
                argument = &number_argument;
 
170
        validator->default_arguments[SAT_VAR_STRING].
 
171
                argument = &string_argument;
 
172
        validator->default_arguments[SAT_CONST_STRING].
 
173
                argument = &string_argument;
 
174
        validator->default_arguments[SAT_STRING_LIST].
 
175
                argument = &string_list_argument;
 
176
 
 
177
        /* Setup storage for extension contexts */              
 
178
        p_array_init(&validator->extensions, pool, sieve_extensions_get_count());
 
179
                
 
180
        /* Setup command registry */
 
181
        validator->commands = hash_table_create
 
182
                (default_pool, pool, 0, strcase_hash, (hash_cmp_callback_t *)strcasecmp);
 
183
        sieve_validator_register_core_commands(validator);
 
184
        sieve_validator_register_core_tests(validator);
 
185
        
 
186
        /* Pre-load core language features implemented as 'extensions' */
 
187
        for ( i = 0; i < sieve_preloaded_extensions_count; i++ ) {
 
188
                const struct sieve_extension *ext = sieve_preloaded_extensions[i];
 
189
                
 
190
                if ( ext->validator_load != NULL )
 
191
                        (void)ext->validator_load(validator);
 
192
        }
 
193
        
 
194
        return validator;
 
195
}
 
196
 
 
197
void sieve_validator_free(struct sieve_validator **validator) 
 
198
{
 
199
        const struct sieve_validator_extension_reg *extrs;
 
200
        unsigned int ext_count, i;
 
201
 
 
202
        hash_table_destroy(&(*validator)->commands);
 
203
        sieve_ast_unref(&(*validator)->ast);
 
204
 
 
205
        sieve_error_handler_unref(&(*validator)->ehandler);
 
206
 
 
207
        /* Signal registered extensions that the validator is being destroyed */
 
208
        extrs = array_get(&(*validator)->extensions, &ext_count);
 
209
        for ( i = 0; i < ext_count; i++ ) {
 
210
                if ( extrs[i].val_ext != NULL && extrs[i].val_ext->free != NULL )
 
211
                        extrs[i].val_ext->free(*validator, extrs[i].context);
 
212
        }
 
213
 
 
214
        pool_unref(&(*validator)->pool);
 
215
 
 
216
        *validator = NULL;
 
217
}
 
218
 
 
219
/*
 
220
 * Accessors
 
221
 */
 
222
 
 
223
pool_t sieve_validator_pool(struct sieve_validator *validator)
 
224
{
 
225
        return validator->pool;
 
226
}
 
227
 
 
228
struct sieve_error_handler *sieve_validator_error_handler
 
229
(struct sieve_validator *validator)
 
230
{
 
231
        return validator->ehandler;
 
232
}
 
233
 
 
234
struct sieve_ast *sieve_validator_ast
 
235
(struct sieve_validator *validator)
 
236
{
 
237
        return validator->ast;
 
238
}
 
239
 
 
240
struct sieve_script *sieve_validator_script
 
241
(struct sieve_validator *validator)
 
242
{
 
243
        return validator->script;
 
244
}
 
245
 
 
246
/* 
 
247
 * Command registry 
 
248
 */
 
249
 
 
250
/* Dummy command object to mark unknown commands in the registry */
 
251
 
 
252
static bool _cmd_unknown_validate
 
253
(struct sieve_validator *validator ATTR_UNUSED, 
 
254
        struct sieve_command_context *cmd ATTR_UNUSED) 
 
255
{
 
256
        i_unreached();
 
257
        return FALSE;
 
258
}
 
259
 
 
260
static const struct sieve_command unknown_command = { 
 
261
        "", SCT_NONE, 0, 0, FALSE, FALSE , 
 
262
        NULL, NULL, _cmd_unknown_validate, NULL, NULL 
 
263
};
 
264
 
 
265
/* Registration of the core commands of the language */
 
266
 
 
267
static void sieve_validator_register_core_tests
 
268
(struct sieve_validator *validator) 
 
269
{
 
270
        unsigned int i;
 
271
        
 
272
        for ( i = 0; i < sieve_core_tests_count; i++ ) {
 
273
                sieve_validator_register_command(validator, sieve_core_tests[i]); 
 
274
        }
 
275
}
 
276
 
 
277
static void sieve_validator_register_core_commands
 
278
(struct sieve_validator *validator) 
 
279
{
 
280
        unsigned int i;
 
281
        
 
282
        for ( i = 0; i < sieve_core_commands_count; i++ ) {
 
283
                sieve_validator_register_command(validator, sieve_core_commands[i]); 
 
284
        }
 
285
}
 
286
 
 
287
/* Registry functions */
 
288
 
 
289
static struct sieve_command_registration *
 
290
sieve_validator_find_command_registration
 
291
(struct sieve_validator *validator, const char *command) 
 
292
{
 
293
        return (struct sieve_command_registration *) 
 
294
                hash_table_lookup(validator->commands, command);
 
295
}
 
296
 
 
297
static struct sieve_command_registration *_sieve_validator_register_command
 
298
(struct sieve_validator *validator, const struct sieve_command *command,
 
299
        const char *identifier) 
 
300
{
 
301
        struct sieve_command_registration *record = 
 
302
                p_new(validator->pool, struct sieve_command_registration, 1);
 
303
        record->command = command;
 
304
        hash_table_insert(validator->commands, (void *) identifier, (void *) record);
 
305
                
 
306
        return record;
 
307
}
 
308
 
 
309
void sieve_validator_register_command
 
310
(struct sieve_validator *validator, const struct sieve_command *command) 
 
311
{
 
312
        struct sieve_command_registration *cmd_reg =
 
313
                sieve_validator_find_command_registration(validator, command->identifier);
 
314
                
 
315
        if ( cmd_reg == NULL ) 
 
316
                cmd_reg = _sieve_validator_register_command
 
317
                        (validator, command, command->identifier);
 
318
        else
 
319
                cmd_reg->command = command;
 
320
        
 
321
        if ( command->registered != NULL ) 
 
322
                command->registered(validator, cmd_reg);
 
323
}
 
324
 
 
325
static void sieve_validator_register_unknown_command
 
326
(struct sieve_validator *validator, const char *command) 
 
327
{
 
328
        (void)_sieve_validator_register_command(validator, &unknown_command, command);          
 
329
}
 
330
 
 
331
const struct sieve_command *sieve_validator_find_command
 
332
(struct sieve_validator *validator, const char *command) 
 
333
{
 
334
  struct sieve_command_registration *record = 
 
335
        sieve_validator_find_command_registration(validator, command);
 
336
  
 
337
  return ( record == NULL ? NULL : record->command );
 
338
}
 
339
 
 
340
/* 
 
341
 * Per-command tagged argument registry 
 
342
 */
 
343
 
 
344
/* Dummy argument object to mark unknown arguments in the registry */
 
345
 
 
346
static bool _unknown_tag_validate
 
347
(struct sieve_validator *validator ATTR_UNUSED, 
 
348
        struct sieve_ast_argument **arg ATTR_UNUSED, 
 
349
        struct sieve_command_context *tst ATTR_UNUSED)
 
350
{
 
351
        i_unreached();
 
352
        return FALSE;
 
353
}
 
354
 
 
355
static const struct sieve_argument _unknown_tag = { 
 
356
        "", 
 
357
        NULL, NULL, 
 
358
        _unknown_tag_validate, 
 
359
        NULL, NULL 
 
360
};
 
361
 
 
362
/* Registry functions */
 
363
 
 
364
static void _sieve_validator_register_tag
 
365
(struct sieve_validator *validator, struct sieve_command_registration *cmd_reg, 
 
366
        const struct sieve_argument *tag, const char *identifier, int id_code) 
 
367
{
 
368
        struct sieve_tag_registration *reg;
 
369
 
 
370
        reg = p_new(validator->pool, struct sieve_tag_registration, 1);
 
371
        reg->tag = tag;
 
372
        reg->id_code = id_code;
 
373
        if ( identifier == NULL )
 
374
                reg->identifier = tag->identifier;
 
375
        else
 
376
                reg->identifier = p_strdup(validator->pool, identifier);
 
377
        
 
378
        if ( !array_is_created(&cmd_reg->normal_tags) )
 
379
                p_array_init(&cmd_reg->normal_tags, validator->pool, 4);
 
380
 
 
381
        array_append(&cmd_reg->normal_tags, &reg, 1);
 
382
}
 
383
 
 
384
void sieve_validator_register_persistent_tag
 
385
(struct sieve_validator *validator, const struct sieve_argument *tag, 
 
386
        const char *command)
 
387
{
 
388
        struct sieve_command_registration *cmd_reg = 
 
389
                sieve_validator_find_command_registration(validator, command);
 
390
        struct sieve_tag_registration *reg = 
 
391
                p_new(validator->pool, struct sieve_tag_registration, 1);
 
392
        
 
393
        reg->tag = tag;
 
394
        reg->id_code = -1;
 
395
        
 
396
        if ( cmd_reg == NULL ) {
 
397
                cmd_reg = _sieve_validator_register_command(validator, NULL, command);
 
398
        }       
 
399
                
 
400
        /* Add the tag to the persistent tags list if necessary */
 
401
        if ( tag->validate_persistent != NULL ) {
 
402
                if ( !array_is_created(&cmd_reg->persistent_tags) ) 
 
403
                        p_array_init(&cmd_reg->persistent_tags, validator->pool, 4);
 
404
                                
 
405
                array_append(&cmd_reg->persistent_tags, &reg, 1);
 
406
        }
 
407
}
 
408
 
 
409
void sieve_validator_register_external_tag
 
410
(struct sieve_validator *validator, const struct sieve_argument *tag, 
 
411
        const char *command, int id_code) 
 
412
{
 
413
        struct sieve_command_registration *cmd_reg = 
 
414
                sieve_validator_find_command_registration(validator, command);
 
415
                
 
416
        if ( cmd_reg == NULL ) {
 
417
                cmd_reg = _sieve_validator_register_command(validator, NULL, command);
 
418
        }
 
419
        
 
420
        _sieve_validator_register_tag
 
421
                (validator, cmd_reg, tag, NULL, id_code);
 
422
}
 
423
 
 
424
void sieve_validator_register_tag
 
425
(struct sieve_validator *validator, struct sieve_command_registration *cmd_reg, 
 
426
        const struct sieve_argument *tag, int id_code) 
 
427
{
 
428
        if ( tag->is_instance_of == NULL )
 
429
                _sieve_validator_register_tag(validator, cmd_reg, tag, NULL, id_code);
 
430
        else {
 
431
                struct sieve_tag_registration *reg = 
 
432
                        p_new(validator->pool, struct sieve_tag_registration, 1);
 
433
                reg->tag = tag;
 
434
                reg->id_code = id_code;
 
435
 
 
436
                if ( !array_is_created(&cmd_reg->instanced_tags) ) 
 
437
                                p_array_init(&cmd_reg->instanced_tags, validator->pool, 4);
 
438
                                
 
439
                array_append(&cmd_reg->instanced_tags, &reg, 1);
 
440
        }
 
441
}
 
442
 
 
443
static void sieve_validator_register_unknown_tag
 
444
(struct sieve_validator *validator, struct sieve_command_registration *cmd_reg, 
 
445
        const char *tag) 
 
446
{
 
447
        _sieve_validator_register_tag(validator, cmd_reg, &_unknown_tag, tag, 0);
 
448
}
 
449
 
 
450
static const struct sieve_argument *sieve_validator_find_tag
 
451
(struct sieve_validator *valdtr, struct sieve_command_context *cmd, 
 
452
        struct sieve_ast_argument *arg, int *id_code) 
 
453
{
 
454
        struct sieve_command_registration *cmd_reg = cmd->cmd_reg;
 
455
        const char *tag = sieve_ast_argument_tag(arg);
 
456
        unsigned int i;
 
457
                        
 
458
        if ( id_code != NULL )
 
459
                *id_code = 0;
 
460
        
 
461
        /* First check normal tags */
 
462
        if ( array_is_created(&cmd_reg->normal_tags) ) {
 
463
                for ( i = 0; i < array_count(&cmd_reg->normal_tags); i++ ) {
 
464
                        struct sieve_tag_registration * const *reg =
 
465
                                array_idx(&cmd_reg->normal_tags, i);
 
466
 
 
467
                        if ( (*reg)->tag != NULL && strcasecmp((*reg)->identifier,tag) == 0) {
 
468
                                if ( id_code != NULL )                          
 
469
                                        *id_code = (*reg)->id_code;
 
470
 
 
471
                                return (*reg)->tag;
 
472
                        }
 
473
                }
 
474
        }       
 
475
  
 
476
        /* Not found so far, try the instanced tags */
 
477
        if ( array_is_created(&cmd_reg->instanced_tags) ) {
 
478
                for ( i = 0; i < array_count(&cmd_reg->instanced_tags); i++ ) {
 
479
                        struct sieve_tag_registration * const *reg = 
 
480
                                array_idx(&cmd_reg->instanced_tags, i);
 
481
        
 
482
                        if ( (*reg)->tag != NULL && 
 
483
                                (*reg)->tag->is_instance_of(valdtr, cmd, arg) ) {
 
484
                                if ( id_code != NULL )
 
485
                                        *id_code = (*reg)->id_code;
 
486
                                
 
487
                                return (*reg)->tag;
 
488
                        }
 
489
                }
 
490
        }
 
491
        
 
492
        return NULL;
 
493
}
 
494
 
 
495
static const struct sieve_argument *sieve_validator_find_tag_by_identifier
 
496
(struct sieve_validator *valdtr, struct sieve_command_context *cmd, 
 
497
        const char *tag) 
 
498
{
 
499
        struct sieve_ast_argument *arg;
 
500
 
 
501
        /* Construct dummy argument */
 
502
        arg = t_new(struct sieve_ast_argument, 1);
 
503
        arg->type = SAAT_TAG;
 
504
        arg->_value.tag = tag; 
 
505
 
 
506
        return sieve_validator_find_tag(valdtr, cmd, arg, NULL);  
 
507
}
 
508
 
 
509
/* 
 
510
 * Extension support 
 
511
 */
 
512
 
 
513
const struct sieve_extension *sieve_validator_extension_load
 
514
(struct sieve_validator *valdtr, struct sieve_command_context *cmd,
 
515
        struct sieve_ast_argument *ext_arg, string_t *ext_name) 
 
516
{
 
517
        int ext_id;
 
518
        struct sieve_validator_extension_reg *reg;
 
519
        const struct sieve_extension *ext;
 
520
        const char *name = str_c(ext_name);
 
521
 
 
522
        if ( str_len(ext_name) > 128 ) {
 
523
                sieve_argument_validate_error(valdtr, ext_arg, 
 
524
                        "%s %s: unknown Sieve capability '%s' (name is impossibly long)",
 
525
                        cmd->command->identifier, sieve_command_type_name(cmd->command),
 
526
                        str_sanitize(name, 128));
 
527
                return NULL;
 
528
        }
 
529
 
 
530
        ext = sieve_extension_get_by_name(name); 
 
531
        
 
532
        if ( ext == NULL ) {
 
533
                unsigned int i;
 
534
                bool core_test = FALSE;
 
535
                bool core_command = FALSE;
 
536
 
 
537
                for ( i = 0; !core_command && i < sieve_core_commands_count; i++ ) {
 
538
                        if ( strcasecmp(sieve_core_commands[i]->identifier, name) == 0 )
 
539
                                core_command = TRUE;
 
540
                }
 
541
 
 
542
                for ( i = 0; !core_test && i < sieve_core_tests_count; i++ ) {
 
543
                        if ( strcasecmp(sieve_core_tests[i]->identifier, name) == 0 )
 
544
                                core_test = TRUE;
 
545
                }
 
546
 
 
547
                if ( core_test || core_command ) {
 
548
                        sieve_argument_validate_error(valdtr, ext_arg,
 
549
                "%s %s: '%s' is not known as a Sieve capability, "
 
550
                                "but it is known as a Sieve %s that is always available",
 
551
                cmd->command->identifier, sieve_command_type_name(cmd->command),
 
552
                name, ( core_test ? "test" : "command" ));
 
553
                } else {
 
554
                        sieve_argument_validate_error(valdtr, ext_arg,
 
555
                                "%s %s: unknown Sieve capability '%s'", 
 
556
                                cmd->command->identifier, sieve_command_type_name(cmd->command),
 
557
                                name);
 
558
                }
 
559
                return NULL;
 
560
        }
 
561
        
 
562
        sieve_ast_extension_link(valdtr->ast, ext);
 
563
 
 
564
        if ( ext->validator_load != NULL && !ext->validator_load(valdtr) ) {
 
565
                sieve_argument_validate_error(valdtr, ext_arg, 
 
566
                        "%s %s: failed to load Sieve capability '%s'",
 
567
                        cmd->command->identifier, sieve_command_type_name(cmd->command),
 
568
                        ext->name);
 
569
                return NULL;
 
570
        }
 
571
 
 
572
        /* Register extension no matter what and store the AST argument registering it */
 
573
        ext_id = SIEVE_EXT_ID(ext);
 
574
        if ( ext_id >= 0 ) {
 
575
                reg = array_idx_modifiable(&valdtr->extensions, (unsigned int) ext_id);
 
576
                reg->arg = ext_arg;
 
577
                reg->loaded = TRUE;
 
578
        }
 
579
 
 
580
        return ext;
 
581
}
 
582
 
 
583
void sieve_validator_extension_register
 
584
(struct sieve_validator *valdtr, 
 
585
        const struct sieve_validator_extension *val_ext, void *context)
 
586
{
 
587
        struct sieve_validator_extension_reg *reg;
 
588
        int ext_id = SIEVE_EXT_ID(val_ext->ext);
 
589
 
 
590
        if ( ext_id < 0 ) return;
 
591
        
 
592
        reg = array_idx_modifiable(&valdtr->extensions, (unsigned int) ext_id);
 
593
        reg->val_ext = val_ext;
 
594
        reg->context = context;
 
595
}
 
596
 
 
597
bool sieve_validator_extension_loaded
 
598
        (struct sieve_validator *valdtr, const struct sieve_extension *ext)
 
599
{
 
600
        int ext_id = SIEVE_EXT_ID(ext);
 
601
        const struct sieve_validator_extension_reg *reg;
 
602
 
 
603
        if ( ext_id < 0 || ext_id >= (int) array_count(&valdtr->extensions))
 
604
                return FALSE;
 
605
 
 
606
        reg = array_idx(&valdtr->extensions, (unsigned int) ext_id);
 
607
 
 
608
        return ( reg->loaded );
 
609
}
 
610
 
 
611
void sieve_validator_extension_set_context
 
612
(struct sieve_validator *valdtr, const struct sieve_extension *ext, 
 
613
        void *context)
 
614
{
 
615
        struct sieve_validator_extension_reg *reg;
 
616
        int ext_id = SIEVE_EXT_ID(ext);
 
617
 
 
618
        if ( ext_id < 0 ) return;
 
619
        
 
620
        reg = array_idx_modifiable(&valdtr->extensions, (unsigned int) ext_id);
 
621
        reg->context = context;
 
622
}
 
623
 
 
624
void *sieve_validator_extension_get_context
 
625
(struct sieve_validator *valdtr, const struct sieve_extension *ext) 
 
626
{
 
627
        int ext_id = SIEVE_EXT_ID(ext);
 
628
        const struct sieve_validator_extension_reg *reg;
 
629
 
 
630
        if  ( ext_id < 0 || ext_id >= (int) array_count(&valdtr->extensions) )
 
631
                return NULL;
 
632
        
 
633
        reg = array_idx(&valdtr->extensions, (unsigned int) ext_id);            
 
634
 
 
635
        return reg->context;
 
636
}
 
637
 
 
638
/* 
 
639
 * Overriding the default literal arguments
 
640
 */
 
641
 
 
642
void sieve_validator_argument_override
 
643
(struct sieve_validator *validator, enum sieve_argument_type type, 
 
644
        const struct sieve_argument *argument)
 
645
{
 
646
        struct sieve_default_argument *arg;
 
647
        
 
648
        if ( validator->default_arguments[type].argument != NULL ) {
 
649
                arg = p_new(validator->pool, struct sieve_default_argument, 1);
 
650
                *arg = validator->default_arguments[type];      
 
651
                
 
652
                validator->default_arguments[type].overrides = arg;
 
653
        }
 
654
        
 
655
        validator->default_arguments[type].argument = argument;
 
656
}
 
657
 
 
658
static bool sieve_validator_argument_default_activate
 
659
(struct sieve_validator *validator, struct sieve_command_context *cmd,
 
660
        struct sieve_default_argument *defarg, struct sieve_ast_argument *arg)
 
661
{
 
662
        bool result = TRUE;
 
663
        struct sieve_default_argument *prev_defarg;
 
664
        
 
665
        prev_defarg = validator->current_defarg;
 
666
        validator->current_defarg = defarg;
 
667
        
 
668
        arg->argument = defarg->argument;
 
669
        if (defarg->argument != NULL && defarg->argument->validate != NULL )
 
670
                result = defarg->argument->validate(validator, &arg, cmd); 
 
671
                
 
672
        validator->current_defarg = prev_defarg;        
 
673
                
 
674
        return result;
 
675
}
 
676
 
 
677
bool sieve_validator_argument_activate_super
 
678
(struct sieve_validator *validator, struct sieve_command_context *cmd, 
 
679
        struct sieve_ast_argument *arg, bool constant ATTR_UNUSED)
 
680
{
 
681
        struct sieve_default_argument *defarg;
 
682
 
 
683
        if ( validator->current_defarg == NULL ||       
 
684
                validator->current_defarg->overrides == NULL )
 
685
                return FALSE;
 
686
        
 
687
        if ( validator->current_defarg->overrides->argument == &string_argument ) {
 
688
                switch ( validator->current_defarg_type) {
 
689
                case SAT_CONST_STRING:
 
690
                        if ( !validator->current_defarg_constant ) {
 
691
                                validator->current_defarg_type = SAT_VAR_STRING;
 
692
                                defarg = &validator->default_arguments[SAT_VAR_STRING];
 
693
                        } else
 
694
                                defarg = validator->current_defarg->overrides;
 
695
                        break;
 
696
                case SAT_VAR_STRING:
 
697
                        defarg = validator->current_defarg->overrides;
 
698
                        break;
 
699
                default:
 
700
                        return FALSE;
 
701
                }
 
702
        } else
 
703
                defarg = validator->current_defarg->overrides;
 
704
        
 
705
        return sieve_validator_argument_default_activate
 
706
                (validator, cmd, defarg, arg);
 
707
}
 
708
 
 
709
/* 
 
710
 * Argument Validation API 
 
711
 */
 
712
 
 
713
bool sieve_validator_argument_activate
 
714
(struct sieve_validator *validator, struct sieve_command_context *cmd, 
 
715
        struct sieve_ast_argument *arg, bool constant)
 
716
{
 
717
        struct sieve_default_argument *defarg;
 
718
        
 
719
        switch ( sieve_ast_argument_type(arg) ) {
 
720
        case SAAT_NUMBER:       
 
721
                validator->current_defarg_type = SAT_NUMBER;
 
722
                break;
 
723
        case SAAT_STRING:
 
724
                validator->current_defarg_type = SAT_CONST_STRING;
 
725
                break;
 
726
        case SAAT_STRING_LIST:
 
727
                validator->current_defarg_type = SAT_STRING_LIST;
 
728
                break;
 
729
        default:
 
730
                return FALSE;
 
731
        }
 
732
 
 
733
        validator->current_defarg_constant = constant;
 
734
        defarg = &validator->default_arguments[validator->current_defarg_type];
 
735
 
 
736
        if ( !constant && defarg->argument == &string_argument ) {
 
737
                validator->current_defarg_type = SAT_VAR_STRING;
 
738
                defarg = &validator->default_arguments[SAT_VAR_STRING];
 
739
        }
 
740
        
 
741
        return sieve_validator_argument_default_activate(validator, cmd, defarg, arg);
 
742
}
 
743
 
 
744
bool sieve_validate_positional_argument
 
745
(struct sieve_validator *validator, struct sieve_command_context *cmd,
 
746
        struct sieve_ast_argument *arg, const char *arg_name, unsigned int arg_pos,
 
747
        enum sieve_ast_argument_type req_type)
 
748
{
 
749
        if ( sieve_ast_argument_type(arg) != req_type && 
 
750
                (sieve_ast_argument_type(arg) != SAAT_STRING || 
 
751
                        req_type != SAAT_STRING_LIST) ) 
 
752
        {
 
753
                sieve_argument_validate_error(validator, arg, 
 
754
                        "the %s %s expects %s as argument %d (%s), but %s was found", 
 
755
                        cmd->command->identifier, sieve_command_type_name(cmd->command), 
 
756
                        sieve_ast_argument_type_name(req_type),
 
757
                        arg_pos, arg_name, sieve_ast_argument_name(arg));
 
758
                return FALSE; 
 
759
        }
 
760
        
 
761
        return TRUE;
 
762
}
 
763
 
 
764
bool sieve_validate_tag_parameter
 
765
(struct sieve_validator *validator, struct sieve_command_context *cmd,
 
766
        struct sieve_ast_argument *tag, struct sieve_ast_argument *param,
 
767
        enum sieve_ast_argument_type req_type)
 
768
{
 
769
        if ( param == NULL ) {
 
770
                sieve_argument_validate_error(validator, tag, 
 
771
                        "the :%s tag for the %s %s requires %s as parameter, "
 
772
                        "but no more arguments were found", sieve_ast_argument_tag(tag), 
 
773
                        cmd->command->identifier, sieve_command_type_name(cmd->command),
 
774
                        sieve_ast_argument_type_name(req_type));
 
775
                return FALSE;   
 
776
        }
 
777
 
 
778
        if ( sieve_ast_argument_type(param) != req_type && 
 
779
                (sieve_ast_argument_type(param) != SAAT_STRING || 
 
780
                        req_type != SAAT_STRING_LIST) ) 
 
781
        {
 
782
                sieve_argument_validate_error(validator, param, 
 
783
                        "the :%s tag for the %s %s requires %s as parameter, "
 
784
                        "but %s was found", sieve_ast_argument_tag(tag), 
 
785
                        cmd->command->identifier, sieve_command_type_name(cmd->command),
 
786
                        sieve_ast_argument_type_name(req_type), sieve_ast_argument_name(param));
 
787
                return FALSE;
 
788
        }
 
789
 
 
790
        param->arg_id_code = tag->arg_id_code;
 
791
 
 
792
        return sieve_validator_argument_activate(validator, cmd, param, FALSE);
 
793
}
 
794
 
 
795
/* 
 
796
 * Command argument validation 
 
797
 */
 
798
 
 
799
static bool sieve_validate_command_arguments
 
800
(struct sieve_validator *validator, struct sieve_command_context *cmd) 
 
801
{
 
802
        int arg_count = cmd->command->positional_arguments;
 
803
        int real_count = 0;
 
804
        struct sieve_ast_argument *arg;
 
805
        struct sieve_command_registration *cmd_reg = cmd->cmd_reg;
 
806
 
 
807
        /* Validate any tags that might be present */
 
808
        arg = sieve_ast_argument_first(cmd->ast_node);
 
809
                
 
810
        /* Visit tagged and optional arguments */
 
811
        while ( sieve_ast_argument_type(arg) == SAAT_TAG ) {
 
812
                int id_code;
 
813
                struct sieve_ast_argument *parg; 
 
814
                const struct sieve_argument *tag = 
 
815
                        sieve_validator_find_tag(validator, cmd, arg, &id_code);
 
816
                
 
817
                if ( tag == NULL ) {
 
818
                        sieve_argument_validate_error(validator, arg, 
 
819
                                "unknown tagged argument ':%s' for the %s %s "
 
820
                                "(reported only once at first occurence)",
 
821
                                sieve_ast_argument_tag(arg), cmd->command->identifier, 
 
822
                                sieve_command_type_name(cmd->command));
 
823
                        sieve_validator_register_unknown_tag
 
824
                                (validator, cmd_reg, sieve_ast_argument_tag(arg));
 
825
                        return FALSE;                                   
 
826
                }
 
827
                
 
828
                /* Check whether previously tagged as unknown */
 
829
                if ( tag->identifier != NULL && *(tag->identifier) == '\0' ) 
 
830
                        return FALSE;
 
831
                
 
832
                /* Assign the tagged argument type to the ast for later reference 
 
833
                 * (in generator) 
 
834
                 */
 
835
                arg->argument = tag;
 
836
                arg->arg_id_code = id_code;  
 
837
                        
 
838
                /* Scan backwards for any duplicates */
 
839
                parg = sieve_ast_argument_prev(arg);
 
840
                while ( parg != NULL ) {
 
841
                        if ( (sieve_ast_argument_type(parg) == SAAT_TAG && parg->argument == tag) 
 
842
                                || (id_code > 0 && parg->arg_id_code == id_code) ) 
 
843
                        {
 
844
                                const char *tag_id = sieve_ast_argument_tag(arg);
 
845
                                const char *tag_desc =
 
846
                                        strcmp(tag->identifier, tag_id) != 0 ?
 
847
                                        t_strdup_printf("%s argument (:%s)", tag->identifier, tag_id) : 
 
848
                                        t_strdup_printf(":%s argument", tag->identifier);        
 
849
                                
 
850
                                sieve_argument_validate_error(validator, arg, 
 
851
                                        "encountered duplicate %s for the %s %s",
 
852
                                        tag_desc, cmd->command->identifier, 
 
853
                                        sieve_command_type_name(cmd->command));
 
854
                                        
 
855
                                return FALSE;   
 
856
                        }
 
857
                        
 
858
                        parg = sieve_ast_argument_prev(parg);
 
859
                }
 
860
                
 
861
                /* Call the validation function for the tag (if present)
 
862
                 *   Fail if the validation fails:
 
863
                 *     Let's not whine multiple times about a single command having multiple 
 
864
                 *     bad arguments...
 
865
                 */ 
 
866
                if ( tag->validate != NULL ) { 
 
867
                        if ( !tag->validate(validator, &arg, cmd) ) 
 
868
                                return FALSE;
 
869
                } else
 
870
                        arg = sieve_ast_argument_next(arg);
 
871
        } 
 
872
        
 
873
        /* Remaining arguments should be positional (tags are not allowed here) */
 
874
        cmd->first_positional = arg;
 
875
        
 
876
        while ( arg != NULL ) {
 
877
                if ( sieve_ast_argument_type(arg) == SAAT_TAG ) {
 
878
                        sieve_argument_validate_error(validator, arg, 
 
879
                                "encountered an unexpected tagged argument ':%s' "
 
880
                                "while validating positional arguments for the %s %s",
 
881
                                sieve_ast_argument_tag(arg), cmd->command->identifier, 
 
882
                                sieve_command_type_name(cmd->command));
 
883
                        return FALSE;
 
884
                }
 
885
                
 
886
                real_count++;
 
887
         
 
888
                arg = sieve_ast_argument_next(arg);
 
889
        }
 
890
        
 
891
        /* Check the required count versus the real number of arguments */
 
892
        if ( arg_count >= 0 && real_count != arg_count ) {
 
893
                sieve_command_validate_error(validator, cmd, 
 
894
                        "the %s %s requires %d positional argument(s), but %d is/are specified",
 
895
                        cmd->command->identifier, sieve_command_type_name(cmd->command), 
 
896
                        arg_count, real_count);
 
897
                return FALSE;
 
898
        }
 
899
        
 
900
        /* Call initial validation for persistent arguments */
 
901
        if ( array_is_created(&cmd_reg->persistent_tags) ) {
 
902
                unsigned int i;
 
903
        
 
904
                for ( i = 0; i < array_count(&cmd_reg->persistent_tags); i++ ) {
 
905
                        struct sieve_tag_registration * const *reg = 
 
906
                                array_idx(&cmd_reg->persistent_tags, i);
 
907
                        const struct sieve_argument *tag = (*reg)->tag;
 
908
  
 
909
                        if ( tag != NULL && tag->validate_persistent != NULL ) { /* To be sure */
 
910
                                if ( !tag->validate_persistent(validator, cmd) )
 
911
                                        return FALSE;
 
912
                        }
 
913
                }
 
914
        }
 
915
 
 
916
        return TRUE;
 
917
}
 
918
 
 
919
static bool sieve_validate_arguments_context
 
920
(struct sieve_validator *validator, struct sieve_command_context *cmd)
 
921
 
922
        struct sieve_ast_argument *arg = 
 
923
                sieve_command_first_argument(cmd);
 
924
        
 
925
        while ( arg != NULL ) {
 
926
                const struct sieve_argument *argument = arg->argument;
 
927
                
 
928
                if ( argument != NULL && argument->validate_context != NULL ) { 
 
929
                        if ( !argument->validate_context(validator, arg, cmd) ) 
 
930
                                return FALSE;
 
931
                }
 
932
                
 
933
                arg = sieve_ast_argument_next(arg);
 
934
        }
 
935
 
 
936
        return TRUE;
 
937
}
 
938
 
 
939
/* 
 
940
 * Command Validation API 
 
941
 */ 
 
942
                 
 
943
static bool sieve_validate_command_subtests
 
944
(struct sieve_validator *valdtr, struct sieve_command_context *cmd, 
 
945
        const unsigned int count) 
 
946
{
 
947
        switch ( count ) {
 
948
        
 
949
        case 0:
 
950
                if ( sieve_ast_test_count(cmd->ast_node) > 0 ) {
 
951
                        /* Unexpected command specified */
 
952
                        enum sieve_command_type ctype = SCT_NONE;
 
953
                        struct sieve_command_registration *cmd_reg;
 
954
                        struct sieve_ast_node *test = sieve_ast_test_first(cmd->ast_node);
 
955
 
 
956
                        cmd_reg = sieve_validator_find_command_registration
 
957
                                (valdtr, test->identifier);
 
958
        
 
959
                        /* First check what we are dealing with */
 
960
                        if ( cmd_reg != NULL && cmd_reg->command != NULL )
 
961
                                ctype = cmd_reg->command->type;
 
962
 
 
963
                        switch ( ctype ) {
 
964
                        case SCT_TEST: /* Spurious test */
 
965
                        case SCT_HYBRID:
 
966
                                sieve_command_validate_error(valdtr, cmd, 
 
967
                                        "the %s %s accepts no sub-tests, but tests are specified", 
 
968
                                        cmd->command->identifier, sieve_command_type_name(cmd->command));
 
969
                                break;
 
970
 
 
971
                        case SCT_NONE: /* Unknown command */
 
972
 
 
973
                                /* Is it perhaps a tag for which the ':' was omitted ? */
 
974
                                if (    sieve_validator_find_tag_by_identifier
 
975
                                        (valdtr, cmd, test->identifier) != NULL ) {
 
976
                                        sieve_command_validate_error(valdtr, cmd, 
 
977
                                                "missing colon ':' before ':%s' tag in %s %s", test->identifier, 
 
978
                                                cmd->command->identifier, sieve_command_type_name(cmd->command));
 
979
                                        break;
 
980
                                } 
 
981
                                /* Fall through */
 
982
                        
 
983
                        case SCT_COMMAND:
 
984
                                sieve_command_validate_error(valdtr, cmd, 
 
985
                                        "missing semicolon ';' after %s %s", 
 
986
                                        cmd->command->identifier, sieve_command_type_name(cmd->command));
 
987
                                break;
 
988
                        }
 
989
                        return FALSE;
 
990
                }
 
991
                break;
 
992
        case 1:
 
993
                if ( sieve_ast_test_count(cmd->ast_node) == 0 ) {
 
994
                        sieve_command_validate_error(valdtr, cmd, 
 
995
                                "the %s %s requires one sub-test, but none is specified", 
 
996
                                cmd->command->identifier, sieve_command_type_name(cmd->command));
 
997
                                
 
998
                        return FALSE;
 
999
                        
 
1000
                } else if ( sieve_ast_test_count(cmd->ast_node) > 1 || 
 
1001
                        cmd->ast_node->test_list ) {
 
1002
                        
 
1003
                        sieve_command_validate_error(valdtr, cmd, 
 
1004
                                "the %s %s requires one sub-test, but a list of tests is specified", 
 
1005
                                cmd->command->identifier, sieve_command_type_name(cmd->command));
 
1006
                                
 
1007
                        return FALSE;
 
1008
                }
 
1009
                break;
 
1010
                
 
1011
        default:
 
1012
                if ( sieve_ast_test_count(cmd->ast_node) == 0 ) {
 
1013
                        sieve_command_validate_error(valdtr, cmd, 
 
1014
                                "the %s %s requires a list of sub-tests, but none is specified", 
 
1015
                                cmd->command->identifier, sieve_command_type_name(cmd->command));
 
1016
                        
 
1017
                        return FALSE;
 
1018
                        
 
1019
                } else if ( sieve_ast_test_count(cmd->ast_node) == 1 && 
 
1020
                        !cmd->ast_node->test_list ) {
 
1021
                        
 
1022
                        sieve_command_validate_error(valdtr, cmd, 
 
1023
                                "the %s %s requires a list of sub-tests, "
 
1024
                                "but a single test is specified", 
 
1025
                                cmd->command->identifier, sieve_command_type_name(cmd->command) );
 
1026
                        
 
1027
                        return FALSE;
 
1028
                }
 
1029
                break;          
 
1030
        }
 
1031
 
 
1032
        return TRUE;
 
1033
}
 
1034
 
 
1035
static bool sieve_validate_command_block
 
1036
(struct sieve_validator *validator, struct sieve_command_context *cmd, 
 
1037
        bool block_allowed, bool block_required) 
 
1038
{
 
1039
        i_assert( cmd->ast_node->type == SAT_COMMAND );
 
1040
        
 
1041
        if ( block_required ) {
 
1042
                if ( !cmd->ast_node->block ) {
 
1043
                        sieve_command_validate_error(validator, cmd, 
 
1044
                                "the %s command requires a command block, but it is missing", 
 
1045
                                cmd->command->identifier);
 
1046
                        
 
1047
                        return FALSE;
 
1048
                }
 
1049
        } else if ( !block_allowed && cmd->ast_node->block ) {
 
1050
                sieve_command_validate_error(validator, cmd, 
 
1051
                        "the %s command does not accept a command block, "
 
1052
                        "but one is specified anyway", 
 
1053
                        cmd->command->identifier );
 
1054
                
 
1055
                return FALSE;
 
1056
        }
 
1057
        
 
1058
        return TRUE;
 
1059
 
1060
 
 
1061
/* 
 
1062
 * AST Validation 
 
1063
 */
 
1064
 
 
1065
static bool sieve_validate_test_list
 
1066
        (struct sieve_validator *validator, struct sieve_ast_node *test_list); 
 
1067
static bool sieve_validate_block
 
1068
        (struct sieve_validator *validator, struct sieve_ast_node *block);
 
1069
static bool sieve_validate_command
 
1070
        (struct sieve_validator *validator, struct sieve_ast_node *cmd_node);
 
1071
        
 
1072
static bool sieve_validate_command_context
 
1073
(struct sieve_validator *valdtr, struct sieve_ast_node *cmd_node) 
 
1074
{
 
1075
        enum sieve_ast_type ast_type = sieve_ast_node_type(cmd_node);
 
1076
        struct sieve_command_registration *cmd_reg;
 
1077
        
 
1078
        i_assert( ast_type == SAT_TEST || ast_type == SAT_COMMAND );
 
1079
        
 
1080
        /* Verify the command specified by this node */
 
1081
        
 
1082
        cmd_reg = sieve_validator_find_command_registration
 
1083
                (valdtr, cmd_node->identifier);
 
1084
        
 
1085
        if ( cmd_reg != NULL && cmd_reg->command != NULL ) {
 
1086
                const struct sieve_command *command = cmd_reg->command;
 
1087
 
 
1088
                /* Identifier = "" when the command was previously marked as unknown */
 
1089
                if ( *(command->identifier) != '\0' ) {
 
1090
                        if ( (command->type == SCT_COMMAND && ast_type == SAT_TEST)
 
1091
                                || (command->type == SCT_TEST && ast_type == SAT_COMMAND) ) {
 
1092
                                sieve_validator_error(
 
1093
                                        valdtr, cmd_node->source_line, "attempted to use %s '%s' as %s", 
 
1094
                                        sieve_command_type_name(command), cmd_node->identifier,
 
1095
                                        sieve_ast_type_name(ast_type));
 
1096
                        
 
1097
                                return FALSE;
 
1098
                        } 
 
1099
                         
 
1100
                        struct sieve_command_context *ctx = 
 
1101
                                sieve_command_context_create(cmd_node, command, cmd_reg); 
 
1102
                        cmd_node->context = ctx;
 
1103
 
 
1104
                } else {
 
1105
                        return FALSE;
 
1106
                }
 
1107
 
 
1108
        }       else {
 
1109
                sieve_validator_error(
 
1110
                        valdtr, cmd_node->source_line, 
 
1111
                        "unknown %s '%s' (only reported once at first occurence)", 
 
1112
                        sieve_ast_type_name(ast_type), cmd_node->identifier);
 
1113
                        
 
1114
                sieve_validator_register_unknown_command(valdtr, cmd_node->identifier);
 
1115
                
 
1116
                return FALSE;
 
1117
        }
 
1118
 
 
1119
        return TRUE;
 
1120
}
 
1121
 
 
1122
static bool sieve_validate_command
 
1123
(struct sieve_validator *valdtr, struct sieve_ast_node *cmd_node) 
 
1124
{
 
1125
        enum sieve_ast_type ast_type = sieve_ast_node_type(cmd_node);
 
1126
        struct sieve_command_context *ctx = cmd_node->context;
 
1127
        const struct sieve_command *command = ( ctx != NULL ? ctx->command : NULL );
 
1128
        bool result = TRUE;
 
1129
        
 
1130
        i_assert( ast_type == SAT_TEST || ast_type == SAT_COMMAND );
 
1131
 
 
1132
        if ( command != NULL && *(command->identifier) != '\0' ) {
 
1133
                
 
1134
                if ( command->pre_validate == NULL 
 
1135
                        || command->pre_validate(valdtr, ctx) ) {
 
1136
        
 
1137
                        /* Check argument syntax */
 
1138
                        if ( !sieve_validate_command_arguments(valdtr, ctx) ) {
 
1139
                                result = FALSE;
 
1140
 
 
1141
                                /* A missing ':' causes a tag to become a test. This can be the cause
 
1142
                                 * of the arguments validation failing. Therefore we must produce an
 
1143
                                 * error for the sub-tests as well if appropriate.
 
1144
                                 */
 
1145
                                (void)sieve_validate_command_subtests(valdtr, ctx, command->subtests);
 
1146
 
 
1147
                        } else if (
 
1148
                                !sieve_validate_command_subtests(valdtr, ctx, command->subtests) || 
 
1149
                                (ast_type == SAT_COMMAND && !sieve_validate_command_block
 
1150
                                        (valdtr, ctx, command->block_allowed, command->block_required)) ) {
 
1151
 
 
1152
                                result = FALSE;
 
1153
 
 
1154
                        } else {
 
1155
                                /* Call command validation function if specified */
 
1156
                                if ( command->validate != NULL )
 
1157
                                        result = command->validate(valdtr, ctx) && result;
 
1158
                        }
 
1159
                } else {
 
1160
                        /* If pre-validation fails, don't bother to validate further 
 
1161
                         * as context might be missing and doing so is not very useful for 
 
1162
                         * further error reporting anyway
 
1163
                         */
 
1164
                        return FALSE;
 
1165
                }
 
1166
                        
 
1167
                result = result && sieve_validate_arguments_context(valdtr, ctx);
 
1168
                                                                
 
1169
        }
 
1170
 
 
1171
        /*  
 
1172
         * Descend further into the AST 
 
1173
         */
 
1174
        
 
1175
        if ( command != NULL ) {
 
1176
                /* Tests */
 
1177
                if ( command->subtests > 0 && 
 
1178
                        (result || sieve_errors_more_allowed(valdtr->ehandler)) )
 
1179
                        result = sieve_validate_test_list(valdtr, cmd_node) && result;
 
1180
 
 
1181
                /* Command block */
 
1182
                if ( command->block_allowed && ast_type == SAT_COMMAND && 
 
1183
                        (result || sieve_errors_more_allowed(valdtr->ehandler)) )
 
1184
                        result = sieve_validate_block(valdtr, cmd_node) && result;
 
1185
        }
 
1186
        
 
1187
        return result;
 
1188
}
 
1189
 
 
1190
static bool sieve_validate_test_list
 
1191
(struct sieve_validator *valdtr, struct sieve_ast_node *test_list) 
 
1192
{
 
1193
        bool result = TRUE;
 
1194
        struct sieve_ast_node *test;
 
1195
 
 
1196
        test = sieve_ast_test_first(test_list);
 
1197
        while ( test != NULL 
 
1198
                && (result || sieve_errors_more_allowed(valdtr->ehandler)) ) {
 
1199
        
 
1200
                result = 
 
1201
                        sieve_validate_command_context(valdtr, test) && 
 
1202
                        sieve_validate_command(valdtr, test) &&
 
1203
                        result; 
 
1204
                
 
1205
                test = sieve_ast_test_next(test);
 
1206
        }               
 
1207
        
 
1208
        return result;
 
1209
}
 
1210
 
 
1211
static bool sieve_validate_block
 
1212
(struct sieve_validator *valdtr, struct sieve_ast_node *block) 
 
1213
{
 
1214
        bool result = TRUE, fatal = FALSE;
 
1215
        struct sieve_ast_node *command, *next;
 
1216
 
 
1217
        T_BEGIN {       
 
1218
                command = sieve_ast_command_first(block);
 
1219
                while ( !fatal &&  command != NULL
 
1220
                        && (result || sieve_errors_more_allowed(valdtr->ehandler)) ) {  
 
1221
                        bool command_success;
 
1222
 
 
1223
                        next = sieve_ast_command_next(command);
 
1224
                        command_success = sieve_validate_command_context(valdtr, command);
 
1225
                        result = command_success && result;     
 
1226
 
 
1227
                        /* Check if this is the first non-require command */
 
1228
                        if ( command_success && sieve_ast_node_type(block) == SAT_ROOT
 
1229
                                && !valdtr->finished_require && command->context != NULL
 
1230
                                && command->context->command != &cmd_require ) {
 
1231
                                const struct sieve_validator_extension_reg *extrs;
 
1232
                                unsigned int ext_count, i;
 
1233
 
 
1234
                                valdtr->finished_require = TRUE;
 
1235
 
 
1236
                                /* Validate all 'require'd extensions */
 
1237
                                extrs = array_get(&valdtr->extensions, &ext_count);
 
1238
                                for ( i = 0; i < ext_count; i++ ) {
 
1239
                                        if ( extrs[i].val_ext != NULL 
 
1240
                                                && extrs[i].val_ext->validate != NULL ) {
 
1241
 
 
1242
                                                if ( !extrs[i].val_ext->validate
 
1243
                                                        (valdtr, extrs[i].context, extrs[i].arg) )
 
1244
                                                fatal = TRUE;
 
1245
                                                break;
 
1246
                                        } 
 
1247
                                }
 
1248
                        }
 
1249
 
 
1250
                        result = !fatal && sieve_validate_command(valdtr, command) && result;
 
1251
                        
 
1252
                        command = next;
 
1253
                }               
 
1254
        } T_END;
 
1255
        
 
1256
        return result && !fatal;
 
1257
}
 
1258
 
 
1259
bool sieve_validator_run(struct sieve_validator *validator) 
 
1260
{       
 
1261
        return sieve_validate_block(validator, sieve_ast_root(validator->ast));
 
1262
}
 
1263
 
 
1264
/*
 
1265
 * Validator object registry
 
1266
 */
 
1267
 
 
1268
struct sieve_validator_object_registry {
 
1269
        struct sieve_validator *validator;
 
1270
        ARRAY_DEFINE(registrations, const struct sieve_object *);
 
1271
};
 
1272
 
 
1273
struct sieve_validator_object_registry *sieve_validator_object_registry_get
 
1274
(struct sieve_validator *validator, const struct sieve_extension *ext)
 
1275
{
 
1276
        return (struct sieve_validator_object_registry *) 
 
1277
                sieve_validator_extension_get_context(validator, ext);
 
1278
}
 
1279
 
 
1280
void sieve_validator_object_registry_add
 
1281
(struct sieve_validator_object_registry *regs, 
 
1282
        const struct sieve_object *object) 
 
1283
{
 
1284
    array_append(&regs->registrations, &object, 1);
 
1285
}
 
1286
 
 
1287
const struct sieve_object *sieve_validator_object_registry_find
 
1288
(struct sieve_validator_object_registry *regs, const char *identifier) 
 
1289
{
 
1290
        unsigned int i;
 
1291
 
 
1292
        for ( i = 0; i < array_count(&regs->registrations); i++ ) {
 
1293
                const struct sieve_object * const *obj = array_idx(&regs->registrations, i);
 
1294
 
 
1295
                if ( strcasecmp((*obj)->identifier, identifier) == 0)
 
1296
                        return *obj;
 
1297
        }
 
1298
 
 
1299
        return NULL;
 
1300
}
 
1301
 
 
1302
struct sieve_validator_object_registry *sieve_validator_object_registry_create
 
1303
(struct sieve_validator *validator)
 
1304
{
 
1305
        pool_t pool = validator->pool;
 
1306
        struct sieve_validator_object_registry *regs = 
 
1307
                p_new(pool, struct sieve_validator_object_registry, 1);
 
1308
        
 
1309
        /* Setup registry */        
 
1310
        p_array_init(&regs->registrations, validator->pool, 4);
 
1311
 
 
1312
        regs->validator = validator;
 
1313
 
 
1314
        return regs;
 
1315
}
 
1316
 
 
1317
struct sieve_validator_object_registry *sieve_validator_object_registry_init
 
1318
(struct sieve_validator *validator, const struct sieve_extension *ext)
 
1319
{
 
1320
        struct sieve_validator_object_registry *regs = 
 
1321
                sieve_validator_object_registry_create(validator);
 
1322
        
 
1323
        sieve_validator_extension_set_context(validator, ext, regs);
 
1324
        return regs;
 
1325
}
 
1326
 
 
1327