summaryrefslogtreecommitdiffstats
path: root/pigeonhole/src/lib-sieve/tst-size.c
diff options
context:
space:
mode:
Diffstat (limited to 'pigeonhole/src/lib-sieve/tst-size.c')
-rw-r--r--pigeonhole/src/lib-sieve/tst-size.c318
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;
+}