/* 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] * */ 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 * / :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 */ 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; }