1
/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file
5
#include "str-sanitize.h"
6
#include "message-date.h"
8
#include "sieve-common.h"
9
#include "sieve-commands.h"
10
#include "sieve-code.h"
11
#include "sieve-comparators.h"
12
#include "sieve-match-types.h"
13
#include "sieve-address-parts.h"
14
#include "sieve-validator.h"
15
#include "sieve-generator.h"
16
#include "sieve-interpreter.h"
17
#include "sieve-dump.h"
18
#include "sieve-match.h"
20
#include "ext-date-common.h"
28
static bool tst_date_validate
29
(struct sieve_validator *valdtr, struct sieve_command_context *tst);
30
static bool tst_date_generate
31
(const struct sieve_codegen_env *cgenv, struct sieve_command_context *ctx);
36
* date [<":zone" <time-zone: string>> / ":originalzone"]
37
* [COMPARATOR] [MATCH-TYPE] <header-name: string>
38
* <date-part: string> <key-list: string-list>
41
static bool tst_date_registered
42
(struct sieve_validator *valdtr, struct sieve_command_registration *cmd_reg);
44
const struct sieve_command date_test = {
58
* currentdate [":zone" <time-zone: string>]
59
* [COMPARATOR] [MATCH-TYPE]
60
* <date-part: string> <key-list: string-list>
63
static bool tst_currentdate_registered
64
(struct sieve_validator *valdtr, struct sieve_command_registration *cmd_reg);
66
const struct sieve_command currentdate_test = {
70
tst_currentdate_registered,
81
/* Forward declarations */
83
static bool tag_zone_validate
84
(struct sieve_validator *validator, struct sieve_ast_argument **arg,
85
struct sieve_command_context *cmd);
86
static bool tag_zone_generate
87
(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg,
88
struct sieve_command_context *cmd);
90
/* Argument objects */
92
static const struct sieve_argument date_zone_tag = {
100
static const struct sieve_argument date_originalzone_tag = {
112
static bool tst_date_operation_dump
113
(const struct sieve_operation *op,
114
const struct sieve_dumptime_env *denv, sieve_size_t *address);
115
static int tst_date_operation_execute
116
(const struct sieve_operation *op,
117
const struct sieve_runtime_env *renv, sieve_size_t *address);
119
const struct sieve_operation date_operation = {
122
EXT_DATE_OPERATION_DATE,
123
tst_date_operation_dump,
124
tst_date_operation_execute
127
const struct sieve_operation currentdate_operation = {
130
EXT_DATE_OPERATION_CURRENTDATE,
131
tst_date_operation_dump,
132
tst_date_operation_execute
139
enum tst_date_optional {
140
OPT_DATE_ZONE = SIEVE_MATCH_OPT_LAST,
148
static bool tag_zone_validate
149
(struct sieve_validator *validator, struct sieve_ast_argument **arg,
150
struct sieve_command_context *cmd)
152
struct sieve_ast_argument *tag = *arg;
154
if ( (bool) cmd->data ) {
155
if ( cmd->command == &date_test ) {
156
sieve_argument_validate_error(validator, *arg,
157
"multiple :zone or :originalzone arguments specified for "
158
"the currentdate test");
160
sieve_argument_validate_error(validator, *arg,
161
"multiple :zone arguments specified for the currentdate test");
167
*arg = sieve_ast_argument_next(*arg);
169
/* :content tag has a string-list argument */
170
if ( tag->argument == &date_zone_tag ) {
173
* :zone <time-zone: string>
175
if ( !sieve_validate_tag_parameter
176
(validator, cmd, tag, *arg, SAAT_STRING) ) {
181
if ( sieve_argument_is_string_literal(*arg) ) {
182
const char *zone = sieve_ast_argument_strc(*arg);
184
if ( !ext_date_parse_timezone(zone, NULL) ) {
185
sieve_argument_validate_warning(validator, *arg,
186
"specified :zone argument '%s' is not a valid timezone",
187
str_sanitize(zone, 40));
191
/* Assign tag parameters */
192
tag->parameters = *arg;
193
*arg = sieve_ast_arguments_detach(*arg,1);
196
cmd->data = (void *) TRUE;
205
static bool tst_date_registered
206
(struct sieve_validator *valdtr, struct sieve_command_registration *cmd_reg)
208
sieve_comparators_link_tag(valdtr, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR);
209
sieve_match_types_link_tags(valdtr, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE);
211
sieve_validator_register_tag
212
(valdtr, cmd_reg, &date_zone_tag, OPT_DATE_ZONE);
213
sieve_validator_register_tag
214
(valdtr, cmd_reg, &date_originalzone_tag, OPT_DATE_ZONE);
219
static bool tst_currentdate_registered
220
(struct sieve_validator *valdtr, struct sieve_command_registration *cmd_reg)
222
sieve_comparators_link_tag(valdtr, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR);
223
sieve_match_types_link_tags(valdtr, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE);
225
sieve_validator_register_tag
226
(valdtr, cmd_reg, &date_zone_tag, OPT_DATE_ZONE);
235
static bool tst_date_validate
236
(struct sieve_validator *valdtr, struct sieve_command_context *tst)
238
struct sieve_ast_argument *arg = tst->first_positional;
239
unsigned int arg_offset = 0 ;
241
/* Check header name */
243
if ( tst->command == &date_test ) {
246
if ( !sieve_validate_positional_argument
247
(valdtr, tst, arg, "header name", 1, SAAT_STRING) ) {
251
if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) )
254
if ( !sieve_command_verify_headers_argument(valdtr, arg) )
257
arg = sieve_ast_argument_next(arg);
260
/* Check date part */
262
if ( !sieve_validate_positional_argument
263
(valdtr, tst, arg, "date part", arg_offset + 1, SAAT_STRING) ) {
267
if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) )
270
arg = sieve_ast_argument_next(arg);
274
if ( !sieve_validate_positional_argument
275
(valdtr, tst, arg, "key list", arg_offset + 2, SAAT_STRING_LIST) ) {
279
if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) )
282
/* Validate the key argument to a specified match type */
283
return sieve_match_type_validate
284
(valdtr, tst, arg, &is_match_type, &i_ascii_casemap_comparator);
291
static bool tst_date_generate
292
(const struct sieve_codegen_env *cgenv, struct sieve_command_context *tst)
294
if ( tst->command == &date_test )
295
sieve_operation_emit_code(cgenv->sbin, &date_operation);
296
else if ( tst->command == ¤tdate_test )
297
sieve_operation_emit_code(cgenv->sbin, ¤tdate_operation);
301
/* Generate arguments */
302
return sieve_generate_arguments(cgenv, tst, NULL);
305
static bool tag_zone_generate
306
(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg,
307
struct sieve_command_context *cmd)
309
struct sieve_ast_argument *param = arg->parameters;
311
if ( param == NULL ) {
312
sieve_opr_omitted_emit(cgenv->sbin);
316
if ( param->argument != NULL && param->argument->generate != NULL )
317
return param->argument->generate(cgenv, param, cmd);
326
static bool tst_date_operation_dump
327
(const struct sieve_operation *op,
328
const struct sieve_dumptime_env *denv, sieve_size_t *address)
331
const struct sieve_operand *operand;
333
sieve_code_dumpf(denv, "%s", op->mnemonic);
334
sieve_code_descend(denv);
336
/* Handle any optional arguments */
338
if ( !sieve_match_dump_optional_operands(denv, address, &opt_code) )
341
switch ( opt_code ) {
342
case SIEVE_MATCH_OPT_END:
345
operand = sieve_operand_read(denv->sbin, address);
346
if ( operand == NULL ) {
347
sieve_code_dumpf(denv, "ERROR: INVALID OPERAND");
351
if ( sieve_operand_is_omitted(operand) ) {
352
sieve_code_dumpf(denv, "zone: ORIGINAL");
354
if ( !sieve_opr_string_dump_data
355
(denv, operand, address, "zone") )
362
} while ( opt_code != SIEVE_MATCH_OPT_END );
364
if ( op == &date_operation &&
365
!sieve_opr_string_dump(denv, address, "header name") )
369
sieve_opr_string_dump(denv, address, "date part") &&
370
sieve_opr_stringlist_dump(denv, address, "key list");
377
static int tst_date_operation_execute
378
(const struct sieve_operation *op,
379
const struct sieve_runtime_env *renv, sieve_size_t *address)
381
bool result = TRUE, zone_specified = FALSE, got_date = FALSE, matched = FALSE;
383
const struct sieve_message_data *msgdata = renv->msgdata;
384
const struct sieve_comparator *cmp = &i_ascii_casemap_comparator;
385
const struct sieve_match_type *mtch = &is_match_type;
386
const struct sieve_operand *operand;
387
struct sieve_match_context *mctx;
388
string_t *header_name = NULL, *date_part = NULL, *zone = NULL;
389
struct sieve_coded_stringlist *key_list;
390
time_t date_value, local_time;
392
const char *part_value;
393
int local_zone = 0, original_zone = 0, wanted_zone = 0;
396
/* Read optional operands */
398
if ( (ret=sieve_match_read_optional_operands
399
(renv, address, &opt_code, &cmp, &mtch)) <= 0 )
402
switch ( opt_code ) {
403
case SIEVE_MATCH_OPT_END:
406
operand = sieve_operand_read(renv->sbin, address);
407
if ( operand == NULL ) {
408
sieve_runtime_trace_error(renv, "invalid operand");
409
return SIEVE_EXEC_BIN_CORRUPT;
412
if ( !sieve_operand_is_omitted(operand) ) {
413
if ( !sieve_opr_string_read_data
414
(renv, operand, address, &zone) ) {
415
sieve_runtime_trace_error(renv, "invalid zone operand");
416
return SIEVE_EXEC_BIN_CORRUPT;
420
zone_specified = TRUE;
423
sieve_runtime_trace_error(renv, "unknown optional operand");
424
return SIEVE_EXEC_BIN_CORRUPT;
426
} while ( opt_code != SIEVE_MATCH_OPT_END );
429
if ( op == &date_operation ) {
430
/* Read header name */
431
if ( !sieve_opr_string_read(renv, address, &header_name) ) {
432
sieve_runtime_trace_error(renv, "invalid header-name operand");
433
return SIEVE_EXEC_BIN_CORRUPT;
438
if ( !sieve_opr_string_read(renv, address, &date_part) ) {
439
sieve_runtime_trace_error(renv, "invalid date-part operand");
440
return SIEVE_EXEC_BIN_CORRUPT;
444
if ( (key_list=sieve_opr_stringlist_read(renv, address)) == NULL ) {
445
sieve_runtime_trace_error(renv, "invalid key-list operand");
446
return SIEVE_EXEC_BIN_CORRUPT;
451
sieve_runtime_trace(renv, "%s test", op->mnemonic);
453
/* Get the date value */
455
local_time = ext_date_get_current_date(renv, &local_zone);
457
if ( op == &date_operation ) {
458
const char *header_value;
459
const char *date_string;
461
/* Get date from the message */
464
* NOTE: need something for index extension to hook into some time.
466
if ( (ret=mail_get_first_header
467
(msgdata->mail, str_c(header_name), &header_value)) > 0 ) {
469
/* Extract the date string value */
470
date_string = strrchr(header_value, ';');
471
if ( date_string == NULL )
472
/* Direct header value */
473
date_string = header_value;
475
/* Delimited by ';', e.g. a Received: header */
479
/* Parse the date value */
480
if ( message_date_parse((const unsigned char *) date_string,
481
strlen(date_string), &date_value, &original_zone) ) {
485
} else if ( op == ¤tdate_operation ) {
486
/* Use time stamp recorded at the time the script first started */
488
date_value = local_time;
489
original_zone = local_zone;
497
/* Apply wanted timezone */
499
if ( !zone_specified )
500
wanted_zone = local_zone;
501
else if ( zone == NULL
502
|| !ext_date_parse_timezone(str_c(zone), &wanted_zone) ) {
504
/* FIXME: warn about parse failures */
505
wanted_zone = original_zone;
508
date_value += wanted_zone * 60;
510
/* Convert timestamp to struct tm */
512
if ( (date_tm=gmtime(&date_value)) == NULL ) {
515
/* Extract the date part */
516
part_value = ext_date_part_extract
517
(str_c(date_part), date_tm, wanted_zone);
521
/* Initialize match */
522
mctx = sieve_match_begin(renv->interp, mtch, cmp, NULL, key_list);
524
if ( got_date && part_value != NULL ) {
526
if ( (ret=sieve_match_value(mctx, part_value, strlen(part_value))) < 0 )
533
if ( (ret=sieve_match_end(&mctx)) < 0 )
536
matched = ( ret > 0 || matched );
538
/* Set test result for subsequent conditional jump */
540
sieve_interpreter_set_test_result(renv->interp, matched);
541
return SIEVE_EXEC_OK;
544
sieve_runtime_trace_error(renv, "invalid string-list item");
545
return SIEVE_EXEC_BIN_CORRUPT;