diff options
Diffstat (limited to 'pigeonhole/src/lib-sieve/plugins/ihave/tst-ihave.c')
-rw-r--r-- | pigeonhole/src/lib-sieve/plugins/ihave/tst-ihave.c | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/pigeonhole/src/lib-sieve/plugins/ihave/tst-ihave.c b/pigeonhole/src/lib-sieve/plugins/ihave/tst-ihave.c new file mode 100644 index 0000000..c6c8e8f --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/ihave/tst-ihave.c @@ -0,0 +1,294 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" + +#include "sieve-common.h" +#include "sieve-stringlist.h" +#include "sieve-extensions.h" +#include "sieve-code.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" + +#include "ext-ihave-common.h" + +/* + * Ihave test + * + * Syntax: + * ihave <capabilities: string-list> + */ + +static bool +tst_ihave_validate(struct sieve_validator *valdtr, struct sieve_command *tst); +static bool +tst_ihave_validate_const(struct sieve_validator *valdtr, + struct sieve_command *tst, int *const_current, + int const_next); +static bool +tst_ihave_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *tst); + +const struct sieve_command_def ihave_test = { + .identifier = "ihave", + .type = SCT_TEST, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .validate = tst_ihave_validate, + .validate_const = tst_ihave_validate_const, + .generate = tst_ihave_generate +}; + +/* + * Ihave operation + */ + +static bool +tst_ihave_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +tst_ihave_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_operation_def tst_ihave_operation = { + .mnemonic = "IHAVE", + .ext_def = &ihave_extension, + .code = EXT_IHAVE_OPERATION_IHAVE, + .dump = tst_ihave_operation_dump, + .execute = tst_ihave_operation_execute +}; + +/* + * Code validation + */ + +static bool +tst_ihave_validate(struct sieve_validator *valdtr, struct sieve_command *tst) +{ + struct _capability { + const struct sieve_extension *ext; + struct sieve_ast_argument *arg; + }; + struct sieve_ast_argument *arg = tst->first_positional; + struct sieve_ast_argument *stritem; + enum sieve_compile_flags cpflags = + sieve_validator_compile_flags(valdtr); + bool no_global = ((cpflags & SIEVE_COMPILE_FLAG_NOGLOBAL) != 0); + ARRAY(struct _capability) capabilities; + struct _capability capability; + const struct _capability *caps; + unsigned int i, count; + bool all_known = TRUE; + + t_array_init(&capabilities, 64); + + tst->data = (void *)FALSE; + + /* Check stringlist argument */ + if (!sieve_validate_positional_argument(valdtr, tst, arg, + "capabilities", 1, + SAAT_STRING_LIST)) + return FALSE; + + switch (sieve_ast_argument_type(arg)) { + case SAAT_STRING: + /* Single string */ + capability.arg = arg; + capability.ext = sieve_extension_get_by_name( + tst->ext->svinst, sieve_ast_argument_strc(arg)); + + if (capability.ext == NULL || + (no_global && capability.ext->global)) { + all_known = FALSE; + + ext_ihave_ast_add_missing_extension( + tst->ext, tst->ast_node->ast, + sieve_ast_argument_strc(arg)); + } else { + array_append(&capabilities, &capability, 1); + } + + break; + + case SAAT_STRING_LIST: + /* String list */ + stritem = sieve_ast_strlist_first(arg); + + while (stritem != NULL) { + capability.arg = stritem; + capability.ext = sieve_extension_get_by_name( + tst->ext->svinst, + sieve_ast_argument_strc(stritem)); + + if (capability.ext == NULL || + (no_global && capability.ext->global)) { + all_known = FALSE; + + ext_ihave_ast_add_missing_extension( + tst->ext, tst->ast_node->ast, + sieve_ast_argument_strc(stritem)); + } else { + array_append(&capabilities, &capability, 1); + } + stritem = sieve_ast_strlist_next(stritem); + } + + break; + default: + i_unreached(); + } + + if (!all_known) + return TRUE; + + /* RFC 5463, Section 4, page 4: + + The "ihave" extension is designed to be used with other extensions + that add tests, actions, comparators, or arguments. Implementations + MUST NOT allow it to be used with extensions that change the + underlying Sieve grammar, or extensions like encoded-character + [RFC5228], or variables [RFC5229] that change how the content of + Sieve scripts are interpreted. The test MUST fail and the extension + MUST NOT be enabled if such usage is attempted. + + FIXME: current implementation of this restriction is hardcoded and + therefore highly inflexible + */ + caps = array_get(&capabilities, &count); + for (i = 0; i < count; i++) { + if (sieve_extension_name_is(caps[i].ext, "variables") || + sieve_extension_name_is(caps[i].ext, "encoded-character")) + return TRUE; + } + + /* Load all extensions */ + caps = array_get(&capabilities, &count); + for (i = 0; i < count; i++) { + if (!sieve_validator_extension_load(valdtr, tst, caps[i].arg, + caps[i].ext, FALSE)) + return FALSE; + } + + if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE)) + return FALSE; + + tst->data = (void *)TRUE; + return TRUE; +} + +static bool +tst_ihave_validate_const(struct sieve_validator *valdtr ATTR_UNUSED, + struct sieve_command *tst, int *const_current, + int const_next ATTR_UNUSED) +{ + if ((bool)tst->data == TRUE) + *const_current = -1; + else + *const_current = 0; + return TRUE; +} + +/* + * Code generation + */ + +bool tst_ihave_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *tst) +{ + /* Emit opcode */ + sieve_operation_emit(cgenv->sblock, tst->ext, &tst_ihave_operation); + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, tst, NULL); +} + +/* + * Code dump + */ + +static bool +tst_ihave_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + sieve_code_dumpf(denv, "IHAVE"); + sieve_code_descend(denv); + + return sieve_opr_stringlist_dump(denv, address, "capabilities"); +} + +/* + * Code execution + */ + +static int +tst_ihave_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + struct sieve_instance *svinst = eenv->svinst; + struct sieve_stringlist *capabilities; + string_t *cap_item; + bool matched; + int ret; + + /* + * Read operands + */ + + /* Read capabilities */ + if ((ret = sieve_opr_stringlist_read(renv, address, "capabilities", + &capabilities)) <= 0) + return ret; + + /* + * Perform test + */ + + /* Perform the test */ + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "ihave test"); + sieve_runtime_trace_descend(renv); + + cap_item = NULL; + matched = TRUE; + while (matched && + (ret = sieve_stringlist_next_item(capabilities, + &cap_item)) > 0) { + const struct sieve_extension *ext; + int sret; + + ext = sieve_extension_get_by_name(svinst, str_c(cap_item)); + if (ext == NULL) { + sieve_runtime_trace_error( + renv, "ihave: invalid extension name"); + return SIEVE_EXEC_BIN_CORRUPT; + } + sret = sieve_interpreter_extension_start(renv->interp, ext); + if (sret == SIEVE_EXEC_FAILURE) { + sieve_runtime_trace( + renv, SIEVE_TRLVL_TESTS, + "extension `%s' not available", + sieve_extension_name(ext)); + matched = FALSE; + } else if (sret == SIEVE_EXEC_OK) { + sieve_runtime_trace( + renv, SIEVE_TRLVL_TESTS, + "extension `%s' available", + sieve_extension_name(ext)); + } else { + return sret; + } + } + if (ret < 0) { + sieve_runtime_trace_error(renv, "invalid capabilities item"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + /* Set test result for subsequent conditional jump */ + sieve_interpreter_set_test_result(renv->interp, matched); + return SIEVE_EXEC_OK; +} |