diff --git a/configure.in b/configure.in index 639600760436dcf1c1f4c639681b2a389197d6d4..37db2abded6cee0b2caf1ac916ff454c3f3a6aa0 100644 --- a/configure.in +++ b/configure.in @@ -116,6 +116,7 @@ src/lib-sieve/plugins/enotify/Makefile src/lib-sieve/plugins/notify/Makefile src/lib-sieve/plugins/environment/Makefile src/lib-sieve/plugins/mailbox/Makefile +src/lib-sieve/plugins/date/Makefile src/lib-sieve-tool/Makefile src/plugins/Makefile src/plugins/lda-sieve/Makefile diff --git a/src/lib-sieve/Makefile.am b/src/lib-sieve/Makefile.am index ed89d64c1d0b02497ff6605e4d030e8ecd4498b1..48550d985abd0af7b0bcfb3587c240b99e7d735a 100644 --- a/src/lib-sieve/Makefile.am +++ b/src/lib-sieve/Makefile.am @@ -42,6 +42,11 @@ comparators = \ cmp-i-octet.c \ cmp-i-ascii-casemap.c +if BUILD_UNFINISHED +unfinished_plugins = \ + ./plugins/date/libsieve_ext_date.la +endif + # These are not actual plugins just yet... plugins = \ ./plugins/vacation/libsieve_ext_vacation.la \ @@ -57,8 +62,8 @@ plugins = \ ./plugins/enotify/libsieve_ext_enotify.la \ ./plugins/notify/libsieve_ext_notify.la \ ./plugins/environment/libsieve_ext_environment.la \ - ./plugins/mailbox/libsieve_ext_mailbox.la - + ./plugins/mailbox/libsieve_ext_mailbox.la \ + $(unfinished_plugins) libsieve_la_DEPENDENCIES = $(plugins) libsieve_la_LIBADD = $(plugins) diff --git a/src/lib-sieve/plugins/Makefile.am b/src/lib-sieve/plugins/Makefile.am index c098e06c7dad8e34dca43d117e8a93e1e1643b2d..8b29be1fd4a1d1d21c75f9965e7f915f9b800fc8 100644 --- a/src/lib-sieve/plugins/Makefile.am +++ b/src/lib-sieve/plugins/Makefile.am @@ -1,3 +1,7 @@ +if BUILD_UNFINISHED +UNFINISHED = date +endif + SUBDIRS = \ vacation \ subaddress \ @@ -12,5 +16,6 @@ SUBDIRS = \ enotify \ notify \ environment \ - mailbox + mailbox \ + $(UNFINISHED) diff --git a/src/lib-sieve/plugins/date/Makefile.am b/src/lib-sieve/plugins/date/Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..8d1f731225a554fffcd18709b9eaf8fcfa778c55 --- /dev/null +++ b/src/lib-sieve/plugins/date/Makefile.am @@ -0,0 +1,18 @@ +noinst_LTLIBRARIES = libsieve_ext_date.la + +AM_CPPFLAGS = \ + -I../../ \ + -I$(dovecot_incdir) \ + -I$(dovecot_incdir)/src/lib \ + -I$(dovecot_incdir)/src/lib-mail \ + -I$(dovecot_incdir)/src/lib-storage + +tests = \ + tst-date.c + +libsieve_ext_date_la_SOURCES = \ + $(tests) \ + ext-date.c + +noinst_HEADERS = \ + ext-date-common.h diff --git a/src/lib-sieve/plugins/date/ext-date-common.h b/src/lib-sieve/plugins/date/ext-date-common.h new file mode 100644 index 0000000000000000000000000000000000000000..81001c44ac8a698ba6d575f28360e9c90a8ca73a --- /dev/null +++ b/src/lib-sieve/plugins/date/ext-date-common.h @@ -0,0 +1,32 @@ +/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file + */ + +#ifndef __EXT_DATE_COMMON_H +#define __EXT_DATE_COMMON_H + +/* + * Extension + */ + +extern const struct sieve_extension date_extension; + +/* + * Tests + */ + +extern const struct sieve_command date_test; +extern const struct sieve_command currentdate_test; + +/* + * Operations + */ + +enum ext_date_opcode { + EXT_DATE_OPERATION_DATE, + EXT_DATE_OPERATION_CURRENTDATE +}; + +extern const struct sieve_operation date_operation; +extern const struct sieve_operation currentdate_operation; + +#endif /* __EXT_DATE_COMMON_H */ diff --git a/src/lib-sieve/plugins/date/ext-date.c b/src/lib-sieve/plugins/date/ext-date.c new file mode 100644 index 0000000000000000000000000000000000000000..a899fb0cd199b979c4aa4c3da2aa32e410f16af4 --- /dev/null +++ b/src/lib-sieve/plugins/date/ext-date.c @@ -0,0 +1,65 @@ +/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file + */ + +/* Extension date + * ------------------ + * + * Authors: Stephan Bosch + * Specification: RFC 5260 + * Implementation: skeleton + * Status: under development + * + */ + +#include "lib.h" +#include "array.h" + +#include "sieve-common.h" + +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-address-parts.h" + +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-binary.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "ext-date-common.h" + +/* + * Extension + */ + +static bool ext_date_validator_load(struct sieve_validator *validator); + +int ext_date_my_id = -1; + +const struct sieve_operation *ext_date_operations[] = { + &date_operation, + ¤tdate_operation +}; + +const struct sieve_extension date_extension = { + "date", + &ext_date_my_id, + NULL, NULL, + ext_date_validator_load, + NULL, NULL, NULL, NULL, NULL, + SIEVE_EXT_DEFINE_OPERATIONS(ext_date_operations), + SIEVE_EXT_DEFINE_NO_OPERANDS +}; + +static bool ext_date_validator_load(struct sieve_validator *valdtr) +{ + /* Register new test */ + sieve_validator_register_command(valdtr, &date_test); + sieve_validator_register_command(valdtr, ¤tdate_test); + + return TRUE; +} + + diff --git a/src/lib-sieve/plugins/date/tst-date.c b/src/lib-sieve/plugins/date/tst-date.c new file mode 100644 index 0000000000000000000000000000000000000000..ecc78d18d603fdebc3f0f01fb13c25d7de628393 --- /dev/null +++ b/src/lib-sieve/plugins/date/tst-date.c @@ -0,0 +1,438 @@ +/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file + */ + +#include "lib.h" +#include "str-sanitize.h" + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-code.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-address-parts.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-match.h" + +#include "ext-date-common.h" + +/* + * Tests + */ + +static bool tst_date_validate + (struct sieve_validator *valdtr, struct sieve_command_context *tst); +static bool tst_date_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command_context *ctx); + +/* Address test + * + * Syntax: + * date [<":zone" <time-zone: string>> / ":originalzone"] + * [COMPARATOR] [MATCH-TYPE] <header-name: string> + * <date-part: string> <key-list: string-list> + */ + +static bool tst_date_registered + (struct sieve_validator *valdtr, struct sieve_command_registration *cmd_reg); + +const struct sieve_command date_test = { + "date", + SCT_TEST, + 3, 0, FALSE, FALSE, + tst_date_registered, + NULL, + tst_date_validate, + tst_date_generate, + NULL +}; + +/* Currentdate test + * + * Syntax: + * currentdate [":zone" <time-zone: string>] + * [COMPARATOR] [MATCH-TYPE] + * <date-part: string> <key-list: string-list> + */ + +static bool tst_currentdate_registered + (struct sieve_validator *valdtr, struct sieve_command_registration *cmd_reg); + +const struct sieve_command currentdate_test = { + "currentdate", + SCT_TEST, + 2, 0, FALSE, FALSE, + tst_currentdate_registered, + NULL, + tst_date_validate, + tst_date_generate, + NULL +}; + +/* + * Tagged arguments + */ + +/* Forward declarations */ + +static bool tag_zone_validate + (struct sieve_validator *validator, struct sieve_ast_argument **arg, + struct sieve_command_context *cmd); +static bool tag_zone_generate + (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command_context *cmd); + +/* Argument objects */ + +static const struct sieve_argument date_zone_tag = { + "zone", + NULL, NULL, + tag_zone_validate, + NULL, + tag_zone_generate +}; + +static const struct sieve_argument date_originalzone_tag = { + "originalzone", + NULL, NULL, + tag_zone_validate, + NULL, + tag_zone_generate +}; + +/* + * Address operation + */ + +static bool tst_date_operation_dump + (const struct sieve_operation *op, + const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int tst_date_operation_execute + (const struct sieve_operation *op, + const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation date_operation = { + "DATE", + &date_extension, + EXT_DATE_OPERATION_DATE, + tst_date_operation_dump, + tst_date_operation_execute +}; + +const struct sieve_operation currentdate_operation = { + "CURRENTDATE", + &date_extension, + EXT_DATE_OPERATION_CURRENTDATE, + tst_date_operation_dump, + tst_date_operation_execute +}; + +/* + * Optional operands + */ + +enum tst_date_optional { + OPT_DATE_ZONE = SIEVE_MATCH_OPT_LAST, + OPT_DATE_LAST +}; + +/* + * Tag implementation + */ + +static bool tag_zone_validate +(struct sieve_validator *validator, struct sieve_ast_argument **arg, + struct sieve_command_context *cmd) +{ + struct sieve_ast_argument *tag = *arg; + + if ( (bool) cmd->data ) { + if ( cmd->command == &date_test ) { + sieve_argument_validate_error(validator, *arg, + "multiple :zone or :originalzone arguments specified for " + "the currentdate test"); + } else { + sieve_argument_validate_error(validator, *arg, + "multiple :zone arguments specified for the currentdate test"); + } + return FALSE; + } + + /* Skip tag */ + *arg = sieve_ast_argument_next(*arg); + + /* :content tag has a string-list argument */ + if ( tag->argument == &date_zone_tag ) { + /* Check syntax: + * :zone <time-zone: string> + */ + if ( !sieve_validate_tag_parameter + (validator, cmd, tag, *arg, SAAT_STRING) ) { + return FALSE; + } + + /* Assign tag parameters */ + tag->parameters = *arg; + *arg = sieve_ast_arguments_detach(*arg,1); + } + + cmd->data = (void *) TRUE; + + return TRUE; +} + +/* + * Test registration + */ + +static bool tst_date_registered +(struct sieve_validator *valdtr, struct sieve_command_registration *cmd_reg) +{ + sieve_comparators_link_tag(valdtr, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR); + sieve_match_types_link_tags(valdtr, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE); + + sieve_validator_register_tag + (valdtr, cmd_reg, &date_zone_tag, OPT_DATE_ZONE); + sieve_validator_register_tag + (valdtr, cmd_reg, &date_originalzone_tag, OPT_DATE_ZONE); + + return TRUE; +} + +static bool tst_currentdate_registered +(struct sieve_validator *valdtr, struct sieve_command_registration *cmd_reg) +{ + sieve_comparators_link_tag(valdtr, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR); + sieve_match_types_link_tags(valdtr, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE); + + sieve_validator_register_tag + (valdtr, cmd_reg, &date_zone_tag, OPT_DATE_ZONE); + + return TRUE; +} + +/* + * Validation + */ + +static bool tst_date_validate + (struct sieve_validator *valdtr, struct sieve_command_context *tst) +{ + struct sieve_ast_argument *arg = tst->first_positional; + unsigned int arg_offset = 0 ; + + /* Check header name */ + + if ( tst->command == &date_test ) { + arg_offset = 1; + + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "header name", 1, SAAT_STRING) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) ) + return FALSE; + + if ( !sieve_command_verify_headers_argument(valdtr, arg) ) + return FALSE; + + arg = sieve_ast_argument_next(arg); + } + + /* Check date part */ + + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "date part", arg_offset + 1, SAAT_STRING) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) ) + return FALSE; + + arg = sieve_ast_argument_next(arg); + + /* Check key list */ + + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "key list", arg_offset + 2, SAAT_STRING_LIST) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) ) + return FALSE; + + /* Validate the key argument to a specified match type */ + return sieve_match_type_validate + (valdtr, tst, arg, &is_match_type, &i_ascii_casemap_comparator); +} + +/* + * Code generation + */ + +static bool tst_date_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command_context *tst) +{ + if ( tst->command == &date_test ) + sieve_operation_emit_code(cgenv->sbin, &date_operation); + else if ( tst->command == ¤tdate_test ) + sieve_operation_emit_code(cgenv->sbin, ¤tdate_operation); + else + i_unreached(); + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, tst, NULL); +} + +static bool tag_zone_generate +(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command_context *cmd) +{ + struct sieve_ast_argument *param = arg->parameters; + + if ( param == NULL ) { + sieve_opr_omitted_emit(cgenv->sbin); + return TRUE; + } + + if ( param->argument != NULL && param->argument->generate != NULL ) + return param->argument->generate(cgenv, param, cmd); + + return FALSE; +} + +/* + * Code dump + */ + +static bool tst_date_operation_dump +(const struct sieve_operation *op, + const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + int opt_code = 0; + const struct sieve_operand *operand; + + sieve_code_dumpf(denv, "%s", op->mnemonic); + sieve_code_descend(denv); + + /* Handle any optional arguments */ + do { + + if ( !sieve_match_dump_optional_operands(denv, address, &opt_code) ) + return FALSE; + + switch ( opt_code ) { + case SIEVE_MATCH_OPT_END: + break; + case OPT_DATE_ZONE: + operand = sieve_operand_read(denv->sbin, address); + if ( operand == NULL ) { + sieve_code_dumpf(denv, "ERROR: INVALID OPERAND"); + return FALSE; + } + + if ( sieve_operand_is_omitted(operand) ) { + sieve_code_dumpf(denv, "zone: ORIGINAL"); + } else { + if ( !sieve_opr_string_dump_data + (denv, operand, address, "zone") ) + return FALSE; + } + break; + + default: + return FALSE; + } + } while ( opt_code != SIEVE_MATCH_OPT_END ); + + if ( op == &date_operation && + !sieve_opr_string_dump(denv, address, "header name") ) + return FALSE; + + return + sieve_opr_string_dump(denv, address, "date part") && + sieve_opr_stringlist_dump(denv, address, "key list"); +} + +/* + * Code execution + */ + +static int tst_date_operation_execute +(const struct sieve_operation *op, + const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + bool result = TRUE; + int opt_code = 0; + const struct sieve_comparator *cmp = &i_ascii_casemap_comparator; + const struct sieve_match_type *mtch = &is_match_type; + const struct sieve_operand *operand; + struct sieve_match_context *mctx; + string_t *header_name = NULL, *date_part = NULL, *zone = NULL; + struct sieve_coded_stringlist *key_list; + bool matched = FALSE; + int ret; + + /* Read optional operands */ + do { + if ( (ret=sieve_match_read_optional_operands + (renv, address, &opt_code, &cmp, &mtch)) <= 0 ) + return ret; + + switch ( opt_code ) { + case SIEVE_MATCH_OPT_END: + break; + case OPT_DATE_ZONE: + operand = sieve_operand_read(renv->sbin, address); + if ( operand == NULL ) { + sieve_runtime_trace_error(renv, "invalid operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + if ( !sieve_operand_is_omitted(operand) ) { + if ( !sieve_opr_string_read_data + (renv, operand, address, &zone) ) { + sieve_runtime_trace_error(renv, "invalid zone operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + } + break; + default: + sieve_runtime_trace_error(renv, "unknown optional operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + } while ( opt_code != SIEVE_MATCH_OPT_END ); + + + if ( op == &date_operation ) { + /* Read header name */ + if ( !sieve_opr_string_read(renv, address, &header_name) ) { + sieve_runtime_trace_error(renv, "invalid header-name operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + } + + /* Read date part */ + if ( !sieve_opr_string_read(renv, address, &date_part) ) { + sieve_runtime_trace_error(renv, "invalid date-part operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + /* Read key-list */ + if ( (key_list=sieve_opr_stringlist_read(renv, address)) == NULL ) { + sieve_runtime_trace_error(renv, "invalid key-list operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + /* Perform test */ + + sieve_runtime_trace(renv, "%s test", op->mnemonic); + + /* FIXME: implement */ + + sieve_interpreter_set_test_result(renv->interp, matched); + return SIEVE_EXEC_OK; +} diff --git a/src/lib-sieve/sieve-extensions.c b/src/lib-sieve/sieve-extensions.c index 897f7071f438c936fc01a6c50b93cc74c574fd83..040d86b9e83d05066528eb35cb05b83735c3bc43 100644 --- a/src/lib-sieve/sieve-extensions.c +++ b/src/lib-sieve/sieve-extensions.c @@ -89,6 +89,7 @@ extern const struct sieve_extension mailbox_extension; #ifdef HAVE_SIEVE_UNFINISHED extern const struct sieve_extension ereject_extension; +extern const struct sieve_extension date_extension; #endif @@ -111,6 +112,7 @@ const struct sieve_extension *sieve_core_extensions[] = { #ifdef HAVE_SIEVE_UNFINISHED &ereject_extension, + &date_extension, #endif /* 'Plugins' */