diff options
Diffstat (limited to 'pigeonhole/src/lib-sieve/ext-envelope.c')
-rw-r--r-- | pigeonhole/src/lib-sieve/ext-envelope.c | 732 |
1 files changed, 732 insertions, 0 deletions
diff --git a/pigeonhole/src/lib-sieve/ext-envelope.c b/pigeonhole/src/lib-sieve/ext-envelope.c new file mode 100644 index 0000000..e889cff --- /dev/null +++ b/pigeonhole/src/lib-sieve/ext-envelope.c @@ -0,0 +1,732 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +/* Extension envelope + * ------------------ + * + * Authors: Stephan Bosch + * Specification: RFC 5228 + * Implementation: full + * Status: testing + * + */ + +#include "lib.h" +#include "str-sanitize.h" +#include "array.h" + +#include "sieve-common.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-address.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-address-parts.h" +#include "sieve-message.h" + +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-match.h" + +/* + * Forward declarations + */ + +static const struct sieve_command_def envelope_test; +const struct sieve_operation_def envelope_operation; +const struct sieve_extension_def envelope_extension; + +/* + * Extension + */ + +static bool +ext_envelope_validator_load(const struct sieve_extension *ext, + struct sieve_validator *valdtr); +static bool +ext_envelope_interpreter_load(const struct sieve_extension *ext, + const struct sieve_runtime_env *renv, + sieve_size_t *address); + +static bool +ext_envelope_validator_validate(const struct sieve_extension *ext, + struct sieve_validator *valdtr, void *context, + struct sieve_ast_argument *require_arg, + bool required); +static int +ext_envelope_interpreter_run(const struct sieve_extension *this_ext, + const struct sieve_runtime_env *renv, + void *context, bool deferred); + +const struct sieve_extension_def envelope_extension = { + .name = "envelope", + .interpreter_load = ext_envelope_interpreter_load, + .validator_load = ext_envelope_validator_load, + SIEVE_EXT_DEFINE_OPERATION(envelope_operation) +}; +const struct sieve_validator_extension +envelope_validator_extension = { + .ext = &envelope_extension, + .validate = ext_envelope_validator_validate +}; +const struct sieve_interpreter_extension +envelope_interpreter_extension = { + .ext_def = &envelope_extension, + .run = ext_envelope_interpreter_run +}; + +static bool +ext_envelope_validator_load(const struct sieve_extension *ext, + struct sieve_validator *valdtr) +{ + /* Register new test */ + sieve_validator_register_command(valdtr, ext, &envelope_test); + + sieve_validator_extension_register(valdtr, ext, + &envelope_validator_extension, NULL); + return TRUE; +} + +static bool +ext_envelope_interpreter_load(const struct sieve_extension *ext, + const struct sieve_runtime_env *renv, + sieve_size_t *address ATTR_UNUSED) +{ + sieve_interpreter_extension_register(renv->interp, ext, + &envelope_interpreter_extension, + NULL); + return TRUE; +} + +static bool +ext_envelope_validator_validate(const struct sieve_extension *ext, + struct sieve_validator *valdtr, + void *context ATTR_UNUSED, + struct sieve_ast_argument *require_arg, + bool required) +{ + if (required) { + enum sieve_compile_flags flags = + sieve_validator_compile_flags(valdtr); + + if ((flags & SIEVE_COMPILE_FLAG_NO_ENVELOPE) != 0) { + sieve_argument_validate_error( + valdtr, require_arg, + "the %s extension cannot be used in this context " + "(needs access to message envelope)", + sieve_extension_name(ext)); + return FALSE; + } + } + return TRUE; +} + +static int +ext_envelope_interpreter_run(const struct sieve_extension *ext, + const struct sieve_runtime_env *renv, + void *context ATTR_UNUSED, bool deferred) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + + if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) != 0) { + if (!deferred) { + sieve_runtime_error( + renv, NULL, + "the %s extension cannot be used in this context " + "(needs access to message envelope)", + sieve_extension_name(ext)); + } + return SIEVE_EXEC_FAILURE; + } + return SIEVE_EXEC_OK; +} + +/* + * Envelope test + * + * Syntax + * envelope [COMPARATOR] [ADDRESS-PART] [MATCH-TYPE] + * <envelope-part: string-list> <key-list: string-list> + */ + +static bool +tst_envelope_registered(struct sieve_validator *valdtr, + const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool +tst_envelope_validate(struct sieve_validator *valdtr, + struct sieve_command *tst); +static bool +tst_envelope_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *ctx); + +static const struct sieve_command_def envelope_test = { + .identifier = "envelope", + .type = SCT_TEST, + .positional_args= 2, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = tst_envelope_registered, + .validate = tst_envelope_validate, + .generate = tst_envelope_generate +}; + +/* + * Envelope operation + */ + +static bool +ext_envelope_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +ext_envelope_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_operation_def envelope_operation = { + .mnemonic = "ENVELOPE", + .ext_def = &envelope_extension, + .dump = ext_envelope_operation_dump, + .execute = ext_envelope_operation_execute +}; + +/* + * Envelope parts + * + * FIXME: not available to extensions + */ + +struct sieve_envelope_part { + const char *identifier; + + const struct smtp_address *const *(*get_addresses) + (const struct sieve_runtime_env *renv); + const char * const *(*get_values) + (const struct sieve_runtime_env *renv); +}; + +static const struct smtp_address *const * +_from_part_get_addresses(const struct sieve_runtime_env *renv); +static const char *const * +_from_part_get_values(const struct sieve_runtime_env *renv); +static const struct smtp_address *const * +_to_part_get_addresses(const struct sieve_runtime_env *renv); +static const char *const * +_to_part_get_values(const struct sieve_runtime_env *renv); +static const char *const * +_auth_part_get_values(const struct sieve_runtime_env *renv); + +static const struct sieve_envelope_part _from_part = { + "from", + _from_part_get_addresses, + _from_part_get_values, +}; + +static const struct sieve_envelope_part _to_part = { + "to", + _to_part_get_addresses, + _to_part_get_values, +}; + +static const struct sieve_envelope_part _auth_part = { + "auth", + NULL, + _auth_part_get_values, +}; + +static const struct sieve_envelope_part *_envelope_parts[] = { + /* Required */ + &_from_part, &_to_part, + + /* Non-standard */ + &_auth_part +}; + +static unsigned int _envelope_part_count = N_ELEMENTS(_envelope_parts); + +static const struct sieve_envelope_part * +_envelope_part_find(const char *identifier) +{ + unsigned int i; + + for (i = 0; i < _envelope_part_count; i++) { + if (strcasecmp(_envelope_parts[i]->identifier, + identifier) == 0) + return _envelope_parts[i]; + } + + return NULL; +} + +/* Envelope parts implementation */ + +static const struct smtp_address *const * +_from_part_get_addresses(const struct sieve_runtime_env *renv) +{ + ARRAY(const struct smtp_address *) envelope_values; + const struct smtp_address *address = + sieve_message_get_sender(renv->msgctx); + + t_array_init(&envelope_values, 2); + + if (address == NULL) + address = smtp_address_create_temp(NULL, NULL); + array_append(&envelope_values, &address, 1); + + (void)array_append_space(&envelope_values); + return array_idx(&envelope_values, 0); +} + +static const char *const * +_from_part_get_values(const struct sieve_runtime_env *renv) +{ + ARRAY(const char *)envelope_values; + const struct smtp_address *address = + sieve_message_get_sender(renv->msgctx); + const char *value; + + t_array_init(&envelope_values, 2); + + value = ""; + if (!smtp_address_isnull(address)) + value = smtp_address_encode(address); + array_append(&envelope_values, &value, 1); + + (void)array_append_space(&envelope_values); + + return array_idx(&envelope_values, 0); +} + +static const struct smtp_address *const * +_to_part_get_addresses(const struct sieve_runtime_env *renv) +{ + ARRAY(const struct smtp_address *) envelope_values; + const struct smtp_address *address = + sieve_message_get_orig_recipient(renv->msgctx); + + if (address != NULL && address->localpart != NULL) { + t_array_init(&envelope_values, 2); + + array_append(&envelope_values, &address, 1); + + (void)array_append_space(&envelope_values); + return array_idx(&envelope_values, 0); + } + return NULL; +} + +static const char *const * +_to_part_get_values(const struct sieve_runtime_env *renv) +{ + ARRAY(const char *) envelope_values; + const struct smtp_address *address = + sieve_message_get_orig_recipient(renv->msgctx); + + t_array_init(&envelope_values, 2); + + if (address != NULL && address->localpart != NULL) { + const char *value = smtp_address_encode(address); + array_append(&envelope_values, &value, 1); + } + + (void)array_append_space(&envelope_values); + + return array_idx(&envelope_values, 0); +} + +static const char *const * +_auth_part_get_values(const struct sieve_runtime_env *renv) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + ARRAY(const char *) envelope_values; + + t_array_init(&envelope_values, 2); + + if (eenv->msgdata->auth_user != NULL) + array_append(&envelope_values, &eenv->msgdata->auth_user, 1); + + (void)array_append_space(&envelope_values); + + return array_idx(&envelope_values, 0); +} + +/* + * Envelope address list + */ + +/* Forward declarations */ + +static int +sieve_envelope_address_list_next_string_item(struct sieve_stringlist *_strlist, + string_t **str_r); +static int +sieve_envelope_address_list_next_item(struct sieve_address_list *_addrlist, + struct smtp_address *addr_r, + string_t **unparsed_r); +static void +sieve_envelope_address_list_reset(struct sieve_stringlist *_strlist); + +/* Stringlist object */ + +struct sieve_envelope_address_list { + struct sieve_address_list addrlist; + + struct sieve_stringlist *env_parts; + + const struct smtp_address *const *cur_addresses; + const char * const *cur_values; + + int value_index; +}; + +static struct sieve_address_list * +sieve_envelope_address_list_create(const struct sieve_runtime_env *renv, + struct sieve_stringlist *env_parts) +{ + struct sieve_envelope_address_list *addrlist; + + addrlist = t_new(struct sieve_envelope_address_list, 1); + addrlist->addrlist.strlist.runenv = renv; + addrlist->addrlist.strlist.exec_status = SIEVE_EXEC_OK; + addrlist->addrlist.strlist.next_item = + sieve_envelope_address_list_next_string_item; + addrlist->addrlist.strlist.reset = sieve_envelope_address_list_reset; + addrlist->addrlist.next_item = sieve_envelope_address_list_next_item; + addrlist->env_parts = env_parts; + + return &addrlist->addrlist; +} + +static int +sieve_envelope_address_list_next_item(struct sieve_address_list *_addrlist, + struct smtp_address *addr_r, + string_t **unparsed_r) +{ + struct sieve_envelope_address_list *addrlist = + (struct sieve_envelope_address_list *)_addrlist; + const struct sieve_runtime_env *renv = _addrlist->strlist.runenv; + + if (addr_r != NULL) + smtp_address_init(addr_r, NULL, NULL); + if (unparsed_r != NULL) *unparsed_r = NULL; + + while (addrlist->cur_addresses == NULL && + addrlist->cur_values == NULL) { + const struct sieve_envelope_part *epart; + string_t *envp_item = NULL; + int ret; + + /* Read next header value from source list */ + if ((ret = sieve_stringlist_next_item(addrlist->env_parts, + &envp_item)) <= 0) + return ret; + + if (_addrlist->strlist.trace) { + sieve_runtime_trace( + _addrlist->strlist.runenv, 0, + "getting `%s' part from message envelope", + str_sanitize(str_c(envp_item), 80)); + } + + if ((epart=_envelope_part_find(str_c(envp_item))) != NULL) { + addrlist->value_index = 0; + + if (epart->get_addresses != NULL) { + /* Field contains addresses */ + addrlist->cur_addresses = + epart->get_addresses(renv); + + /* Drop empty list */ + if (addrlist->cur_addresses != NULL && + addrlist->cur_addresses[0] == NULL) + addrlist->cur_addresses = NULL; + } + + if (addrlist->cur_addresses == NULL && + epart->get_values != NULL) { + /* Field contains something else */ + addrlist->cur_values = epart->get_values(renv); + + /* Drop empty list */ + if (addrlist->cur_values != NULL && + addrlist->cur_values[0] == NULL) + addrlist->cur_values = NULL; + } + } + } + + /* Return next item */ + if (addrlist->cur_addresses != NULL) { + const struct smtp_address *addr = + addrlist->cur_addresses[addrlist->value_index]; + + if (addr->localpart == NULL) { + /* Null path <> */ + if (unparsed_r != NULL) + *unparsed_r = t_str_new_const("", 0); + } else { + if (addr_r != NULL) + *addr_r = *addr; + } + + /* Advance to next value */ + addrlist->value_index++; + if (addrlist->cur_addresses[addrlist->value_index] == NULL) { + addrlist->cur_addresses = NULL; + addrlist->value_index = 0; + } + } else { + if (unparsed_r != NULL) { + const char *value = + addrlist->cur_values[addrlist->value_index]; + + *unparsed_r = t_str_new_const(value, strlen(value)); + } + + /* Advance to next value */ + addrlist->value_index++; + if (addrlist->cur_values[addrlist->value_index] == NULL) { + addrlist->cur_values = NULL; + addrlist->value_index = 0; + } + } + + return 1; +} + +static int +sieve_envelope_address_list_next_string_item(struct sieve_stringlist *_strlist, + string_t **str_r) +{ + struct sieve_address_list *addrlist = + (struct sieve_address_list *)_strlist; + struct smtp_address addr; + int ret; + + if ((ret=sieve_envelope_address_list_next_item(addrlist, &addr, + str_r)) <= 0) + return ret; + + if (addr.localpart != NULL) { + const char *addr_str = smtp_address_encode(&addr); + if (str_r != NULL) + *str_r = t_str_new_const(addr_str, strlen(addr_str)); + } + return 1; +} + +static void +sieve_envelope_address_list_reset(struct sieve_stringlist *_strlist) +{ + struct sieve_envelope_address_list *addrlist = + (struct sieve_envelope_address_list *)_strlist; + + sieve_stringlist_reset(addrlist->env_parts); + addrlist->cur_addresses = NULL; + addrlist->cur_values = NULL; + addrlist->value_index = 0; +} + +/* + * Command Registration + */ + +static bool +tst_envelope_registered(struct sieve_validator *valdtr, + const struct sieve_extension *ext ATTR_UNUSED, + struct sieve_command_registration *cmd_reg) +{ + /* The order of these is not significant */ + 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_address_parts_link_tags(valdtr, cmd_reg, SIEVE_AM_OPT_ADDRESS_PART); + return TRUE; +} + +/* + * Validation + */ + +static int +_envelope_part_is_supported(void *context, struct sieve_ast_argument *arg) +{ + const struct sieve_envelope_part **not_address = + (const struct sieve_envelope_part **) context; + + if (sieve_argument_is_string_literal(arg)) { + const struct sieve_envelope_part *epart; + + if ((epart=_envelope_part_find( + sieve_ast_strlist_strc(arg))) != NULL) { + if (epart->get_addresses == NULL) { + if (*not_address == NULL) + *not_address = epart; + } + return 1; + } + return 0; + } + return 1; /* Can't check at compile time */ +} + +static bool +tst_envelope_validate(struct sieve_validator *valdtr, struct sieve_command *tst) +{ + struct sieve_ast_argument *arg = tst->first_positional; + struct sieve_ast_argument *epart; + struct sieve_comparator cmp_default = + SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); + struct sieve_match_type mcht_default = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + const struct sieve_envelope_part *not_address = NULL; + + if (!sieve_validate_positional_argument(valdtr, tst, arg, + "envelope part", 1, + SAAT_STRING_LIST)) { + return FALSE; + } + + if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE)) + return FALSE; + + /* Check whether supplied envelope parts are supported + * FIXME: verify dynamic envelope parts at runtime + */ + epart = arg; + if (sieve_ast_stringlist_map(&epart, (void *) ¬_address, + _envelope_part_is_supported) <= 0) { + i_assert(epart != NULL); + sieve_argument_validate_error( + valdtr, epart, + "specified envelope part '%s' is not supported by the envelope test", + str_sanitize(sieve_ast_strlist_strc(epart), 64)); + return FALSE; + } + + if (not_address != NULL) { + struct sieve_ast_argument *addrp_arg = + sieve_command_find_argument(tst, &address_part_tag); + + if (addrp_arg != NULL) { + sieve_argument_validate_error( + valdtr, addrp_arg, + "address part ':%s' specified while non-address envelope part '%s' " + "is tested with the envelope test", + sieve_ast_argument_tag(addrp_arg), + not_address->identifier); + return FALSE; + } + } + + arg = sieve_ast_argument_next(arg); + + if (!sieve_validate_positional_argument(valdtr, tst, arg, "key list", 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, + &mcht_default, &cmp_default); +} + +/* + * Code generation + */ + +static bool +tst_envelope_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd) +{ + (void)sieve_operation_emit(cgenv->sblock, cmd->ext, + &envelope_operation); + + /* Generate arguments */ + if (!sieve_generate_arguments(cgenv, cmd, NULL)) + return FALSE; + return TRUE; +} + +/* + * Code dump + */ + +static bool +ext_envelope_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + sieve_code_dumpf(denv, "ENVELOPE"); + sieve_code_descend(denv); + + /* Handle any optional arguments */ + if (sieve_addrmatch_opr_optional_dump(denv, address, NULL) != 0) + return FALSE; + + return (sieve_opr_stringlist_dump(denv, address, "envelope part") && + sieve_opr_stringlist_dump(denv, address, "key list")); +} + +/* + * Interpretation + */ + +static int +ext_envelope_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address) +{ + struct sieve_comparator cmp = + SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); + struct sieve_match_type mcht = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + struct sieve_address_part addrp = + SIEVE_ADDRESS_PART_DEFAULT(all_address_part); + struct sieve_stringlist *env_part_list, *value_list, *key_list; + struct sieve_address_list *addr_list; + int match, ret; + + /* + * Read operands + */ + + /* Read optional operands */ + if (sieve_addrmatch_opr_optional_read(renv, address, NULL, &ret, + &addrp, &mcht, &cmp) < 0) + return ret; + + /* Read envelope-part */ + if ((ret = sieve_opr_stringlist_read(renv, address, "envelope-part", + &env_part_list)) <= 0) + return ret; + + /* Read key-list */ + if ((ret = sieve_opr_stringlist_read(renv, address, "key-list", + &key_list)) <= 0) + return ret; + + /* + * Perform test + */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "envelope test"); + + /* Create value stringlist */ + addr_list = sieve_envelope_address_list_create(renv, env_part_list); + value_list = sieve_address_part_stringlist_create(renv, &addrp, + addr_list); + + /* Perform match */ + if ((match = sieve_match(renv, &mcht, &cmp, + value_list, key_list, &ret)) < 0) + return ret; + + /* Set test result for subsequent conditional jump */ + sieve_interpreter_set_test_result(renv->interp, match > 0); + return SIEVE_EXEC_OK; +} + |