diff options
Diffstat (limited to 'pigeonhole/src/plugins/sieve-extprograms/cmd-execute.c')
-rw-r--r-- | pigeonhole/src/plugins/sieve-extprograms/cmd-execute.c | 488 |
1 files changed, 488 insertions, 0 deletions
diff --git a/pigeonhole/src/plugins/sieve-extprograms/cmd-execute.c b/pigeonhole/src/plugins/sieve-extprograms/cmd-execute.c new file mode 100644 index 0000000..79f6663 --- /dev/null +++ b/pigeonhole/src/plugins/sieve-extprograms/cmd-execute.c @@ -0,0 +1,488 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "array.h" +#include "buffer.h" +#include "str.h" +#include "str-sanitize.h" +#include "istream.h" +#include "ostream.h" + +#include "sieve-common.h" +#include "sieve-stringlist.h" +#include "sieve-binary.h" +#include "sieve-code.h" +#include "sieve-message.h" +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-actions.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-result.h" + +#include "sieve-ext-variables.h" + +#include "sieve-extprograms-common.h" + +/* Execute command + * + * Syntax: + * "execute" [":input" <input-data: string> / ":pipe"] + * [":output" <varname: string>] + * <program-name: string> [<arguments: string-list>] + * + */ + +static bool +cmd_execute_registered(struct sieve_validator *valdtr, + const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool +cmd_execute_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *ctx); + +const struct sieve_command_def sieve_cmd_execute = { + .identifier = "execute", + .type = SCT_HYBRID, + .positional_args = -1, /* We check positional arguments ourselves */ + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = cmd_execute_registered, + .validate = sieve_extprogram_command_validate, + .generate = cmd_execute_generate, +}; + +/* + * Tagged arguments + */ + +static bool +cmd_execute_validate_input_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd); +static bool +cmd_execute_generate_input_tag(const struct sieve_codegen_env *cgenv, + struct sieve_ast_argument *arg, + struct sieve_command *cmd); + +static bool +cmd_execute_validate_output_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd); + +static const struct sieve_argument_def execute_input_tag = { + .identifier = "input", + .validate = cmd_execute_validate_input_tag, + .generate = cmd_execute_generate_input_tag +}; + +static const struct sieve_argument_def execute_pipe_tag = { + .identifier = "pipe", + .validate = cmd_execute_validate_input_tag, + .generate = cmd_execute_generate_input_tag +}; + +static const struct sieve_argument_def execute_output_tag = { + .identifier = "output", + .validate = cmd_execute_validate_output_tag, +}; + + +/* + * Execute operation + */ + +static bool +cmd_execute_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +cmd_execute_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_operation_def sieve_opr_execute = { + .mnemonic = "EXECUTE", + .ext_def = &sieve_ext_vnd_execute, + .dump = cmd_execute_operation_dump, + .execute = cmd_execute_operation_execute +}; + +/* Codes for optional operands */ + +enum cmd_execute_optional { + OPT_END, + OPT_INPUT, + OPT_OUTPUT +}; + +/* + * Tag validation + */ + +static bool +cmd_execute_validate_input_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *tag = *arg; + + if ((bool)cmd->data) { + sieve_argument_validate_error( + valdtr, *arg, + "multiple :input or :pipe arguments specified for the %s %s", + sieve_command_identifier(cmd), + sieve_command_type_name(cmd)); + return FALSE; + } + + cmd->data = (void *)TRUE; + + /* Skip tag */ + *arg = sieve_ast_argument_next(*arg); + + if (sieve_argument_is(tag, execute_input_tag)) { + /* Check syntax: + * :input <input-data: string> + */ + if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, + 0, SAAT_STRING, FALSE)) + return FALSE; + + /* Assign tag parameters */ + tag->parameters = *arg; + *arg = sieve_ast_arguments_detach(*arg,1); + } + + return TRUE; +} + +static bool +cmd_execute_validate_output_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *cmd) +{ + struct sieve_ast_argument *tag = *arg; + struct sieve_extprograms_config *ext_config = + (struct sieve_extprograms_config *)cmd->ext->context; + + if (ext_config == NULL || ext_config->var_ext == NULL || + !sieve_ext_variables_is_active(ext_config->var_ext, valdtr)) { + sieve_argument_validate_error( + valdtr,*arg, + "the %s %s only allows for the specification of an " + ":output argument when the variables extension is active", + sieve_command_identifier(cmd), + sieve_command_type_name(cmd)); + return FALSE; + } + + /* Detach the tag itself */ + *arg = sieve_ast_arguments_detach(*arg, 1); + + if (!sieve_variable_argument_activate(ext_config->var_ext, + ext_config->var_ext, valdtr, + cmd, *arg, TRUE)) + return FALSE; + + (*arg)->argument->id_code = tag->argument->id_code; + + /* Skip parameter */ + *arg = sieve_ast_argument_next(*arg); + + return TRUE; +} + +/* + * Command registration + */ + +static bool +cmd_execute_registered(struct sieve_validator *valdtr, + const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg) +{ + sieve_validator_register_tag(valdtr, cmd_reg, ext, + &execute_input_tag, OPT_INPUT); + sieve_validator_register_tag(valdtr, cmd_reg, ext, + &execute_pipe_tag, OPT_INPUT); + sieve_validator_register_tag(valdtr, cmd_reg, ext, + &execute_output_tag, OPT_OUTPUT); + return TRUE; +} + +/* + * Code generation + */ + +static bool +cmd_execute_generate_input_tag(const struct sieve_codegen_env *cgenv, + struct sieve_ast_argument *arg, + struct sieve_command *cmd) +{ + if (arg->parameters == NULL) { + sieve_opr_omitted_emit(cgenv->sblock); + return TRUE; + } + + return sieve_generate_argument_parameters(cgenv, cmd, arg); +} + +static bool +cmd_execute_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *cmd) +{ + sieve_operation_emit(cgenv->sblock, cmd->ext, &sieve_opr_execute); + + /* Emit is_test flag */ + sieve_binary_emit_byte(cgenv->sblock, + (uint8_t)(cmd->ast_node->type == SAT_TEST ? + 1 : 0)); + + /* Generate arguments */ + if (!sieve_generate_arguments(cgenv, cmd, NULL)) + return FALSE; + + /* Emit a placeholder when the <arguments> argument is missing */ + if (sieve_ast_argument_next(cmd->first_positional) == NULL) + sieve_opr_omitted_emit(cgenv->sblock); + + return TRUE; +} + +/* + * Code dump + */ + +static bool +cmd_execute_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + int opt_code = 0; + unsigned int is_test = 0; + + /* Read is_test flag */ + if (!sieve_binary_read_byte(denv->sblock, address, &is_test)) + return FALSE; + + sieve_code_dumpf(denv, "EXECUTE (%s)", + (is_test > 0 ? "test" : "command")); + sieve_code_descend(denv); + + /* Dump optional operands */ + for (;;) { + int opt; + bool opok = TRUE; + + if ((opt = sieve_action_opr_optional_dump(denv, address, + &opt_code)) < 0) + return FALSE; + + if (opt == 0) + break; + + switch (opt_code) { + case OPT_INPUT: + opok = sieve_opr_string_dump_ex(denv, address, + "input", "PIPE"); + break; + case OPT_OUTPUT: + opok = sieve_opr_string_dump(denv, address, "output"); + break; + default: + return FALSE; + } + + if (!opok) + return FALSE; + } + + if (!sieve_opr_string_dump(denv, address, "program-name")) + return FALSE; + + return sieve_opr_stringlist_dump_ex(denv, address, "arguments", ""); +} + +/* + * Code execution + */ + +static int +cmd_execute_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address) +{ + const struct sieve_execute_env *eenv = renv->exec_env; + const struct sieve_extension *this_ext = renv->oprtn->ext; + struct sieve_side_effects_list *slist = NULL; + int opt_code = 0; + unsigned int is_test = 0; + struct sieve_stringlist *args_list = NULL; + string_t *pname = NULL, *input = NULL; + struct sieve_variable_storage *var_storage = NULL; + unsigned int var_index; + bool have_input = FALSE; + const char *program_name = NULL; + const char *const *args = NULL; + enum sieve_error error = SIEVE_ERROR_NONE; + buffer_t *outbuf = NULL; + struct sieve_extprogram *sprog = NULL; + int ret; + + /* + * Read operands + */ + + /* The is_test flag */ + if (!sieve_binary_read_byte(renv->sblock, address, &is_test)) { + sieve_runtime_trace_error(renv, "invalid is_test flag"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + /* Optional operands */ + + for (;;) { + int opt; + + if ((opt = sieve_action_opr_optional_read( + renv, address, &opt_code, &ret, &slist)) < 0) + return ret; + + if (opt == 0) + break; + + switch (opt_code) { + case OPT_INPUT: + ret = sieve_opr_string_read_ex(renv, address, "input", + TRUE, &input, NULL); + have_input = TRUE; + break; + case OPT_OUTPUT: + ret = sieve_variable_operand_read( + renv, address, "output", &var_storage, + &var_index); + break; + default: + sieve_runtime_trace_error( + renv, "unknown optional operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + if (ret <= 0) + return ret; + } + + /* Fixed operands */ + + if ((ret = sieve_extprogram_command_read_operands( + renv, address, &pname, &args_list)) <= 0) + return ret; + + program_name = str_c(pname); + if (args_list != NULL && + sieve_stringlist_read_all(args_list, pool_datastack_create(), + &args) < 0) { + sieve_runtime_trace_error(renv, "failed to read args operand"); + return args_list->exec_status; + } + + /* + * Perform operation + */ + + /* Trace */ + + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, "execute action"); + sieve_runtime_trace_descend(renv); + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, + "execute program `%s'", + str_sanitize(program_name, 128)); + + sprog = sieve_extprogram_create(this_ext, eenv->scriptenv, + eenv->msgdata, "execute", + program_name, args, &error); + if (sprog != NULL) { + if (var_storage != NULL) { + // FIXME: limit output size + struct ostream *outdata; + + outbuf = buffer_create_dynamic(default_pool, 1024); + outdata = o_stream_create_buffer(outbuf); + sieve_extprogram_set_output(sprog, outdata); + o_stream_unref(&outdata); + } + + if (input == NULL && have_input) { + struct mail *mail = sieve_message_get_mail(renv->msgctx); + + if (sieve_extprogram_set_input_mail(sprog, mail) < 0) { + sieve_extprogram_destroy(&sprog); + if (outbuf != NULL) + buffer_free(&outbuf); + return sieve_runtime_mail_error( + renv, mail, "execute action: " + "failed to read input message"); + } + ret = 1; + } else if (input != NULL) { + struct istream *indata = + i_stream_create_from_data(str_data(input), + str_len(input)); + sieve_extprogram_set_input(sprog, indata); + i_stream_unref(&indata); + ret = 1; + } + + if (ret >= 0) + ret = sieve_extprogram_run(sprog); + sieve_extprogram_destroy(&sprog); + } else { + ret = -1; + } + + if (ret > 0) { + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, + "executed program successfully"); + + if (var_storage != NULL) { + string_t *var; + + if (sieve_variable_get_modifiable(var_storage, + var_index, &var)) { + str_truncate(var, 0); + str_append_str(var, outbuf); + + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, + "assigned output variable"); + } // FIXME: handle failure + } + + } else if (ret < 0) { + if (error == SIEVE_ERROR_NOT_FOUND) { + sieve_runtime_error( + renv, NULL, + "execute action: program `%s' not found", + str_sanitize(program_name, 80)); + } else { + sieve_extprogram_exec_error( + renv->ehandler, + sieve_runtime_get_full_command_location(renv), + "execute action: failed to execute to program `%s'", + str_sanitize(program_name, 80)); + } + } else { + sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, + "execute action: " + "program indicated false result"); + } + + if (outbuf != NULL) + buffer_free(&outbuf); + + if (is_test > 0) { + sieve_interpreter_set_test_result(renv->interp, (ret > 0)); + return SIEVE_EXEC_OK; + } + return (ret >= 0 ? SIEVE_EXEC_OK : SIEVE_EXEC_FAILURE); +} |