diff options
Diffstat (limited to 'pigeonhole/src/lib-sieve/plugins/body/tst-body.c')
-rw-r--r-- | pigeonhole/src/lib-sieve/plugins/body/tst-body.c | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/pigeonhole/src/lib-sieve/plugins/body/tst-body.c b/pigeonhole/src/lib-sieve/plugins/body/tst-body.c new file mode 100644 index 0000000..f08dd54 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/body/tst-body.c @@ -0,0 +1,385 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-stringlist.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-binary.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-match.h" + +#include "ext-body-common.h" + +/* + * Body test + * + * Syntax + * body [COMPARATOR] [MATCH-TYPE] [BODY-TRANSFORM] + * <key-list: string-list> + */ + +static bool tst_body_registered + (struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool tst_body_validate + (struct sieve_validator *valdtr, struct sieve_command *tst); +static bool tst_body_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx); + +const struct sieve_command_def body_test = { + .identifier = "body", + .type = SCT_TEST, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = tst_body_registered, + .validate = tst_body_validate, + .generate = tst_body_generate +}; + +/* + * Body operation + */ + +static bool ext_body_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int ext_body_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +const struct sieve_operation_def body_operation = { + .mnemonic = "body", + .ext_def = &body_extension, + .dump = ext_body_operation_dump, + .execute = ext_body_operation_execute +}; + +/* + * Optional operands + */ + +enum tst_body_optional { + OPT_BODY_TRANSFORM = SIEVE_MATCH_OPT_LAST +}; + +/* + * Tagged arguments + */ + +/* Forward declarations */ + +static bool tag_body_transform_validate + (struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd); +static bool tag_body_transform_generate + (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *cmd); + +/* Argument objects */ + +static const struct sieve_argument_def body_raw_tag = { + .identifier = "raw", + .validate = tag_body_transform_validate, + .generate = tag_body_transform_generate +}; + +static const struct sieve_argument_def body_content_tag = { + .identifier = "content", + .validate = tag_body_transform_validate, + .generate = tag_body_transform_generate +}; + +static const struct sieve_argument_def body_text_tag = { + .identifier = "text", + .validate = tag_body_transform_validate, + .generate = tag_body_transform_generate +}; + +/* Argument implementation */ + +static bool tag_body_transform_validate +(struct sieve_validator *valdtr, struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + enum tst_body_transform transform; + struct sieve_ast_argument *tag = *arg; + + /* BODY-TRANSFORM: + * :raw + * / :content <content-types: string-list> + * / :text + */ + if ( (bool) cmd->data ) { + sieve_argument_validate_error(valdtr, *arg, + "the :raw, :content and :text arguments for the body test are mutually " + "exclusive, but more than one was specified"); + return FALSE; + } + + /* Skip tag */ + *arg = sieve_ast_argument_next(*arg); + + /* :content tag has a string-list argument */ + if ( sieve_argument_is(tag, body_raw_tag) ) + transform = TST_BODY_TRANSFORM_RAW; + + else if ( sieve_argument_is(tag, body_text_tag) ) + transform = TST_BODY_TRANSFORM_TEXT; + + else if ( sieve_argument_is(tag, body_content_tag) ) { + /* Check syntax: + * :content <content-types: string-list> + */ + if ( !sieve_validate_tag_parameter + (valdtr, cmd, tag, *arg, NULL, 0, SAAT_STRING_LIST, FALSE) ) { + return FALSE; + } + + /* Assign tag parameters */ + tag->parameters = *arg; + *arg = sieve_ast_arguments_detach(*arg,1); + + transform = TST_BODY_TRANSFORM_CONTENT; + } else + return FALSE; + + /* Signal the presence of this tag */ + cmd->data = (void *) TRUE; + + /* Assign context data */ + tag->argument->data = (void *) transform; + + return TRUE; +} + +/* + * Command Registration + */ + +static bool tst_body_registered +(struct sieve_validator *valdtr, const struct sieve_extension *ext, + 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_validator_register_tag + (valdtr, cmd_reg, ext, &body_raw_tag, OPT_BODY_TRANSFORM); + sieve_validator_register_tag + (valdtr, cmd_reg, ext, &body_content_tag, OPT_BODY_TRANSFORM); + sieve_validator_register_tag + (valdtr, cmd_reg, ext, &body_text_tag, OPT_BODY_TRANSFORM); + + return TRUE; +} + +/* + * Validation + */ + +static bool tst_body_validate +(struct sieve_validator *valdtr, struct sieve_command *tst) +{ + struct sieve_ast_argument *arg = tst->first_positional; + const struct sieve_match_type mcht_default = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + const struct sieve_comparator cmp_default = + SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); + + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "key list", 1, 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_body_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) +{ + (void)sieve_operation_emit(cgenv->sblock, cmd->ext, &body_operation); + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, cmd, NULL); +} + +static bool tag_body_transform_generate +(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, + struct sieve_command *cmd ATTR_UNUSED) +{ + enum tst_body_transform transform = + POINTER_CAST_TO(arg->argument->data, enum tst_body_transform); + + sieve_binary_emit_byte(cgenv->sblock, transform); + sieve_generate_argument_parameters(cgenv, cmd, arg); + + return TRUE; +} + +/* + * Code dump + */ + +static bool ext_body_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + unsigned int transform; + int opt_code = 0; + + sieve_code_dumpf(denv, "BODY"); + sieve_code_descend(denv); + + /* Handle any optional arguments */ + for (;;) { + int opt; + + if ( (opt=sieve_match_opr_optional_dump(denv, address, &opt_code)) + < 0 ) + return FALSE; + + if ( opt == 0 ) break; + + switch ( opt_code ) { + case OPT_BODY_TRANSFORM: + if ( !sieve_binary_read_byte(denv->sblock, address, &transform) ) + return FALSE; + + switch ( transform ) { + case TST_BODY_TRANSFORM_RAW: + sieve_code_dumpf(denv, "BODY-TRANSFORM: RAW"); + break; + case TST_BODY_TRANSFORM_TEXT: + sieve_code_dumpf(denv, "BODY-TRANSFORM: TEXT"); + break; + case TST_BODY_TRANSFORM_CONTENT: + sieve_code_dumpf(denv, "BODY-TRANSFORM: CONTENT"); + + sieve_code_descend(denv); + if ( !sieve_opr_stringlist_dump(denv, address, "content types") ) + return FALSE; + sieve_code_ascend(denv); + break; + default: + return FALSE; + } + break; + default: + return FALSE; + } + }; + + return sieve_opr_stringlist_dump(denv, address, "key list"); +} + +/* + * Interpretation + */ + +static int ext_body_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + int opt_code = 0; + struct sieve_comparator cmp = + SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); + struct sieve_match_type mcht = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + unsigned int transform = TST_BODY_TRANSFORM_TEXT; + struct sieve_stringlist *ctype_list, *value_list, *key_list; + bool mvalues_active; + const char * const *content_types = NULL; + int match, ret; + + /* + * Read operands + */ + + /* Optional operands */ + + ctype_list = NULL; + for (;;) { + int opt; + + if ( (opt=sieve_match_opr_optional_read + (renv, address, &opt_code, &ret, &cmp, &mcht)) < 0 ) + return ret; + + if ( opt == 0 ) break; + + switch ( opt_code ) { + case OPT_BODY_TRANSFORM: + if ( !sieve_binary_read_byte(renv->sblock, address, &transform) || + transform > TST_BODY_TRANSFORM_TEXT ) { + sieve_runtime_trace_error(renv, "invalid body transform type"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + if ( transform == TST_BODY_TRANSFORM_CONTENT && + (ret=sieve_opr_stringlist_read + (renv, address, "content-type-list", &ctype_list)) <= 0 ) + return ret; + + break; + + default: + sieve_runtime_trace_error(renv, "unknown optional operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + } + + /* Read key-list */ + + if ( (ret=sieve_opr_stringlist_read(renv, address, "key-list", &key_list)) + <= 0 ) + return ret; + + if ( ctype_list != NULL && sieve_stringlist_read_all + (ctype_list, pool_datastack_create(), &content_types) < 0 ) { + sieve_runtime_trace_error(renv, "failed to read content-type-list operand"); + return ctype_list->exec_status; + } + + /* + * Perform operation + */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "body test"); + + /* Extract requested parts */ + if ( (ret=ext_body_get_part_list(renv, + (enum tst_body_transform) transform, content_types,&value_list)) <= 0 ) + return ret; + + /* Disable match values processing as required by RFC */ + mvalues_active = sieve_match_values_set_enabled(renv, FALSE); + + /* Perform match */ + match = sieve_match(renv, &mcht, &cmp, value_list, key_list, &ret); + + /* Restore match values processing */ + (void)sieve_match_values_set_enabled(renv, mvalues_active); + + if ( match < 0 ) + return ret; + + /* Set test result for subsequent conditional jump */ + sieve_interpreter_set_test_result(renv->interp, match > 0); + return SIEVE_EXEC_OK; +} |