diff options
Diffstat (limited to 'pigeonhole/src/lib-sieve/tst-size.c')
-rw-r--r-- | pigeonhole/src/lib-sieve/tst-size.c | 318 |
1 files changed, 318 insertions, 0 deletions
diff --git a/pigeonhole/src/lib-sieve/tst-size.c b/pigeonhole/src/lib-sieve/tst-size.c new file mode 100644 index 0000000..1ed101b --- /dev/null +++ b/pigeonhole/src/lib-sieve/tst-size.c @@ -0,0 +1,318 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "mail-storage.h" + +#include "sieve-common.h" +#include "sieve-code.h" +#include "sieve-message.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +/* + * Size test + * + * Syntax: + * size <":over" / ":under"> <limit: number> + */ + +static bool +tst_size_registered(struct sieve_validator *valdtr, + const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool +tst_size_pre_validate(struct sieve_validator *valdtr, + struct sieve_command *tst); +static bool +tst_size_validate(struct sieve_validator *valdtr, struct sieve_command *tst); +static bool +tst_size_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *ctx); + +const struct sieve_command_def tst_size = { + .identifier = "size", + .type = SCT_TEST, + .positional_args = 1, + .subtests = 0, + .block_allowed = FALSE, + .block_required = FALSE, + .registered = tst_size_registered, + .pre_validate = tst_size_pre_validate, + .validate = tst_size_validate, + .generate = tst_size_generate +}; + +/* + * Size operations + */ + +static bool +tst_size_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address); +static int +tst_size_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address); + +const struct sieve_operation_def tst_size_over_operation = { + .mnemonic = "SIZE-OVER", + .code = SIEVE_OPERATION_SIZE_OVER, + .dump = tst_size_operation_dump, + .execute = tst_size_operation_execute +}; + +const struct sieve_operation_def tst_size_under_operation = { + .mnemonic = "SIZE-UNDER", + .code = SIEVE_OPERATION_SIZE_UNDER, + .dump = tst_size_operation_dump, + .execute = tst_size_operation_execute +}; + +/* + * Context data + */ + +struct tst_size_context_data { + enum { SIZE_UNASSIGNED, SIZE_UNDER, SIZE_OVER } type; +}; + +#define TST_SIZE_ERROR_DUP_TAG \ + "exactly one of the ':under' or ':over' tags must be specified " \ + "for the size test, but more were found" + +/* + * Tag validation + */ + +static bool +tst_size_validate_over_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg, + struct sieve_command *tst) +{ + struct tst_size_context_data *ctx_data = + (struct tst_size_context_data *)tst->data; + + if (ctx_data->type != SIZE_UNASSIGNED) { + sieve_argument_validate_error(valdtr, *arg, + TST_SIZE_ERROR_DUP_TAG); + return FALSE; + } + + ctx_data->type = SIZE_OVER; + + /* Delete this tag */ + *arg = sieve_ast_arguments_detach(*arg, 1); + + return TRUE; +} + +static bool +tst_size_validate_under_tag(struct sieve_validator *valdtr, + struct sieve_ast_argument **arg ATTR_UNUSED, + struct sieve_command *tst) +{ + struct tst_size_context_data *ctx_data = + (struct tst_size_context_data *)tst->data; + + if (ctx_data->type != SIZE_UNASSIGNED) { + sieve_argument_validate_error(valdtr, *arg, + TST_SIZE_ERROR_DUP_TAG); + return FALSE; + } + + ctx_data->type = SIZE_UNDER; + + /* Delete this tag */ + *arg = sieve_ast_arguments_detach(*arg, 1); + + return TRUE; +} + +/* + * Test registration + */ + +static const struct sieve_argument_def size_over_tag = { + .identifier = "over", + .validate = tst_size_validate_over_tag +}; + +static const struct sieve_argument_def size_under_tag = { + .identifier = "under", + .validate = tst_size_validate_under_tag, +}; + +static bool +tst_size_registered(struct sieve_validator *valdtr, + const struct sieve_extension *ext ATTR_UNUSED, + struct sieve_command_registration *cmd_reg) +{ + /* Register our tags */ + sieve_validator_register_tag(valdtr, cmd_reg, NULL, &size_over_tag, 0); + sieve_validator_register_tag(valdtr, cmd_reg, NULL, &size_under_tag, 0); + + return TRUE; +} + +/* + * Test validation + */ + +static bool +tst_size_pre_validate(struct sieve_validator *valdtr ATTR_UNUSED, + struct sieve_command *tst) +{ + struct tst_size_context_data *ctx_data; + + /* Assign context */ + ctx_data = p_new(sieve_command_pool(tst), + struct tst_size_context_data, 1); + ctx_data->type = SIZE_UNASSIGNED; + tst->data = ctx_data; + + return TRUE; +} + +static bool +tst_size_validate(struct sieve_validator *valdtr, struct sieve_command *tst) +{ + struct tst_size_context_data *ctx_data = + (struct tst_size_context_data *)tst->data; + struct sieve_ast_argument *arg = tst->first_positional; + + if (ctx_data->type == SIZE_UNASSIGNED) { + sieve_command_validate_error(valdtr, tst, + "the size test requires either the :under or the :over tag " + "to be specified"); + return FALSE; + } + + if (!sieve_validate_positional_argument(valdtr, tst, arg, "limit", 1, + SAAT_NUMBER)) + return FALSE; + + return sieve_validator_argument_activate(valdtr, tst, arg, FALSE); +} + +/* + * Code generation + */ + +bool tst_size_generate(const struct sieve_codegen_env *cgenv, + struct sieve_command *tst) +{ + struct tst_size_context_data *ctx_data = + (struct tst_size_context_data *)tst->data; + + if (ctx_data->type == SIZE_OVER) { + sieve_operation_emit(cgenv->sblock, NULL, + &tst_size_over_operation); + } else { + sieve_operation_emit(cgenv->sblock, NULL, + &tst_size_under_operation); + } + + /* Generate arguments */ + if (!sieve_generate_arguments(cgenv, tst, NULL)) + return FALSE; + + return TRUE; +} + +/* + * Code dump + */ + +static bool +tst_size_operation_dump(const struct sieve_dumptime_env *denv, + sieve_size_t *address) +{ + sieve_code_dumpf(denv, "%s", sieve_operation_mnemonic(denv->oprtn)); + sieve_code_descend(denv); + + return sieve_opr_number_dump(denv, address, "limit"); +} + +/* + * Code execution + */ + +static inline bool +tst_size_get(const struct sieve_runtime_env *renv, sieve_number_t *size) +{ + struct mail *mail = sieve_message_get_mail(renv->msgctx); + uoff_t psize; + + if (mail_get_physical_size(mail, &psize) < 0) + return FALSE; + + *size = psize; + return TRUE; +} + +static int +tst_size_operation_execute(const struct sieve_runtime_env *renv, + sieve_size_t *address) +{ + sieve_number_t mail_size, limit; + int ret; + + /* + * Read operands + */ + + /* Read size limit */ + if ((ret = sieve_opr_number_read(renv, address, "limit", &limit)) <= 0) + return ret; + + /* + * Perform test + */ + + /* Get the size of the message */ + if (!tst_size_get(renv, &mail_size)) { + /* FIXME: improve this error */ + e_error(renv->event, "failed to assess message size"); + return SIEVE_EXEC_FAILURE; + } + + /* Perform the test */ + if (sieve_operation_is(renv->oprtn, tst_size_over_operation)) { + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "size :over test"); + + if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_MATCHING)) { + sieve_runtime_trace_descend(renv); + + sieve_runtime_trace( + renv, 0, "comparing message size %llu", + (unsigned long long)mail_size); + sieve_runtime_trace( + renv, 0, "with upper limit %llu", + (unsigned long long)limit); + } + + sieve_interpreter_set_test_result(renv->interp, + (mail_size > limit)); + } else { + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "size :under test"); + + if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_MATCHING)) { + sieve_runtime_trace_descend(renv); + + sieve_runtime_trace( + renv, 0, "comparing message size %llu", + (unsigned long long)mail_size); + sieve_runtime_trace( + renv, 0, "with lower limit %llu", + (unsigned long long)limit); + } + + sieve_interpreter_set_test_result(renv->interp, + (mail_size < limit)); + } + return SIEVE_EXEC_OK; +} |