summaryrefslogtreecommitdiffstats
path: root/pigeonhole/src/lib-sieve/plugins/special-use/tst-specialuse-exists.c
diff options
context:
space:
mode:
Diffstat (limited to 'pigeonhole/src/lib-sieve/plugins/special-use/tst-specialuse-exists.c')
-rw-r--r--pigeonhole/src/lib-sieve/plugins/special-use/tst-specialuse-exists.c525
1 files changed, 525 insertions, 0 deletions
diff --git a/pigeonhole/src/lib-sieve/plugins/special-use/tst-specialuse-exists.c b/pigeonhole/src/lib-sieve/plugins/special-use/tst-specialuse-exists.c
new file mode 100644
index 0000000..a1aa878
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/plugins/special-use/tst-specialuse-exists.c
@@ -0,0 +1,525 @@
+/* Copyright (c) 2019 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str-sanitize.h"
+#include "mail-storage.h"
+#include "mail-namespace.h"
+
+#include "sieve-common.h"
+#include "sieve-actions.h"
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-stringlist.h"
+#include "sieve-code.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+
+#include "ext-special-use-common.h"
+
+/*
+ * specialuse_exists command
+ *
+ * Syntax:
+ * specialuse_exists [<mailbox: string>]
+ * <special-use-flags: string-list>
+ */
+
+static bool
+tst_specialuse_exists_validate(struct sieve_validator *valdtr,
+ struct sieve_command *tst);
+static bool
+tst_specialuse_exists_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_command *ctx);
+
+const struct sieve_command_def specialuse_exists_test = {
+ .identifier = "specialuse_exists",
+ .type = SCT_TEST,
+ .positional_args = -1, /* We check positional arguments ourselves */
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .validate = tst_specialuse_exists_validate,
+ .generate = tst_specialuse_exists_generate,
+};
+
+/*
+ * Mailboxexists operation
+ */
+
+static bool
+tst_specialuse_exists_operation_dump(const struct sieve_dumptime_env *denv,
+ sieve_size_t *address);
+static int
+tst_specialuse_exists_operation_execute(const struct sieve_runtime_env *renv,
+ sieve_size_t *address);
+
+const struct sieve_operation_def specialuse_exists_operation = {
+ .mnemonic = "SPECIALUSE_EXISTS",
+ .ext_def = &special_use_extension,
+ .dump = tst_specialuse_exists_operation_dump,
+ .execute = tst_specialuse_exists_operation_execute,
+};
+
+/*
+ * Test validation
+ */
+
+struct _validate_context {
+ struct sieve_validator *valdtr;
+ struct sieve_command *tst;
+};
+
+static int
+tst_specialuse_exists_flag_validate(void *context,
+ struct sieve_ast_argument *arg)
+{
+ struct _validate_context *valctx = (struct _validate_context *)context;
+
+ if (sieve_argument_is_string_literal(arg)) {
+ const char *flag = sieve_ast_argument_strc(arg);
+
+ if (!ext_special_use_flag_valid(flag)) {
+ sieve_argument_validate_error(
+ valctx->valdtr, arg, "%s test: "
+ "invalid special-use flag `%s' specified",
+ sieve_command_identifier(valctx->tst),
+ str_sanitize(flag, 64));
+ }
+ }
+
+ return 1;
+}
+
+static bool
+tst_specialuse_exists_validate(struct sieve_validator *valdtr,
+ struct sieve_command *tst)
+{
+ struct sieve_ast_argument *arg = tst->first_positional;
+ struct sieve_ast_argument *arg2;
+ struct sieve_ast_argument *aarg;
+ struct _validate_context valctx;
+
+ if (arg == NULL) {
+ sieve_command_validate_error(
+ valdtr, tst, "the %s %s expects at least one argument, "
+ "but none was found",
+ sieve_command_identifier(tst),
+ sieve_command_type_name(tst));
+ return FALSE;
+ }
+
+ if (sieve_ast_argument_type(arg) != SAAT_STRING &&
+ sieve_ast_argument_type(arg) != SAAT_STRING_LIST) {
+ sieve_argument_validate_error(
+ valdtr, arg,
+ "the %s %s expects either a string (mailbox) or "
+ "a string-list (special-use flags) as first argument, "
+ "but %s was found",
+ sieve_command_identifier(tst),
+ sieve_command_type_name(tst),
+ sieve_ast_argument_name(arg));
+ return FALSE;
+ }
+
+ arg2 = sieve_ast_argument_next(arg);
+ if (arg2 != NULL) {
+ /* First, check syntax sanity */
+ if (sieve_ast_argument_type(arg) != SAAT_STRING) {
+ sieve_argument_validate_error(
+ valdtr, arg,
+ "if a second argument is specified for the %s %s, "
+ "the first must be a string (mailbox), "
+ "but %s was found",
+ sieve_command_identifier(tst),
+ sieve_command_type_name(tst),
+ sieve_ast_argument_name(arg));
+ return FALSE;
+ }
+ if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE))
+ return FALSE;
+
+ /* Check name validity when mailbox argument is not a variable */
+ if (sieve_argument_is_string_literal(arg)) {
+ const char *mailbox = sieve_ast_argument_strc(arg);
+ const char *error;
+
+ if (!sieve_mailbox_check_name(mailbox, &error)) {
+ sieve_argument_validate_warning(
+ valdtr, arg, "%s test: "
+ "invalid mailbox name `%s' specified: %s",
+ sieve_command_identifier(tst),
+ str_sanitize(mailbox, 256), error);
+ }
+ }
+
+ if (sieve_ast_argument_type(arg2) != SAAT_STRING &&
+ sieve_ast_argument_type(arg2) != SAAT_STRING_LIST) {
+ sieve_argument_validate_error(
+ valdtr, arg2,
+ "the %s %s expects a string list (special-use flags) as "
+ "second argument when two arguments are specified, "
+ "but %s was found",
+ sieve_command_identifier(tst),
+ sieve_command_type_name(tst),
+ sieve_ast_argument_name(arg2));
+ return FALSE;
+ }
+ } else
+ arg2 = arg;
+
+ if (!sieve_validator_argument_activate(valdtr, tst, arg2, FALSE))
+ return FALSE;
+
+ aarg = arg2;
+ memset(&valctx, 0, sizeof(valctx));
+ valctx.valdtr = valdtr;
+ valctx.tst = tst;
+
+ return (sieve_ast_stringlist_map(
+ &aarg, (void*)&valctx,
+ tst_specialuse_exists_flag_validate) >= 0);
+}
+
+/*
+ * Test generation
+ */
+
+static bool
+tst_specialuse_exists_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_command *tst)
+{
+ struct sieve_ast_argument *arg = tst->first_positional;
+ struct sieve_ast_argument *arg2;
+
+ sieve_operation_emit(cgenv->sblock,
+ tst->ext, &specialuse_exists_operation);
+
+ /* Generate arguments */
+ arg2 = sieve_ast_argument_next(arg);
+ if (arg2 != NULL) {
+ if (!sieve_generate_argument(cgenv, arg, tst))
+ return FALSE;
+ } else {
+ sieve_opr_omitted_emit(cgenv->sblock);
+ arg2 = arg;
+ }
+ return sieve_generate_argument(cgenv, arg2, tst);
+}
+
+/*
+ * Code dump
+ */
+
+static bool
+tst_specialuse_exists_operation_dump(const struct sieve_dumptime_env *denv,
+ sieve_size_t *address)
+{
+ struct sieve_operand oprnd;
+
+ sieve_code_dumpf(denv, "SPECIALUSE_EXISTS");
+ sieve_code_descend(denv);
+
+ sieve_code_mark(denv);
+ if (!sieve_operand_read(denv->sblock, address, NULL, &oprnd)) {
+ sieve_code_dumpf(denv, "ERROR: INVALID OPERAND");
+ return FALSE;
+ }
+
+ if (!sieve_operand_is_omitted(&oprnd)) {
+ return (sieve_opr_string_dump_data(denv, &oprnd,
+ address, "mailbox") &&
+ sieve_opr_stringlist_dump(denv, address,
+ "special-use-flags"));
+ }
+
+ return sieve_opr_stringlist_dump(denv, address, "special-use-flags");
+}
+
+/*
+ * Code execution
+ */
+
+static int
+tst_specialuse_find_mailbox(const struct sieve_runtime_env *renv,
+ const char *mailbox, struct mailbox **box_r)
+{
+ const struct sieve_execute_env *eenv = renv->exec_env;
+ struct mail_user *user = eenv->scriptenv->user;
+ struct mailbox *box;
+ bool trace = sieve_runtime_trace_active(renv, SIEVE_TRLVL_MATCHING);
+ enum mail_error error_code;
+ const char *error;
+
+ *box_r = NULL;
+
+ if (user == NULL)
+ return 0;
+
+ /* Open the box */
+ box = mailbox_alloc_for_user(user, mailbox, MAILBOX_FLAG_POST_SESSION);
+ if (mailbox_open(box) < 0) {
+ error = mailbox_get_last_internal_error(box, &error_code);
+
+ if (trace) {
+ sieve_runtime_trace(
+ renv, 0, "mailbox `%s' cannot be opened: %s",
+ str_sanitize(mailbox, 256), error);
+ }
+
+ mailbox_free(&box);
+
+ if (error_code == MAIL_ERROR_TEMP) {
+ sieve_runtime_error(
+ renv, NULL, "specialuse_exists test: "
+ "failed to open mailbox `%s': %s",
+ str_sanitize(mailbox, 256), error);
+ return -1;
+ }
+ return 0;
+ }
+
+ /* Also fail when it is readonly */
+ if (mailbox_is_readonly(box)) {
+ if (trace) {
+ sieve_runtime_trace(
+ renv, 0, "mailbox `%s' is read-only",
+ str_sanitize(mailbox, 256));
+ }
+
+ mailbox_free(&box);
+ return 0;
+ }
+
+ *box_r = box;
+ return 1;
+}
+
+static int
+tst_specialuse_find_specialuse(const struct sieve_runtime_env *renv,
+ const char *special_use)
+{
+ const struct sieve_execute_env *eenv = renv->exec_env;
+ struct mail_user *user = eenv->scriptenv->user;
+ struct mailbox *box;
+ bool trace = sieve_runtime_trace_active(renv, SIEVE_TRLVL_MATCHING);
+ enum mail_error error_code;
+ const char *error;
+
+ if (user == NULL)
+ return 0;
+
+ /* Open the box */
+ box = mailbox_alloc_for_user(user, special_use,
+ (MAILBOX_FLAG_POST_SESSION |
+ MAILBOX_FLAG_SPECIAL_USE));
+ if (mailbox_open(box) < 0) {
+ error = mailbox_get_last_internal_error(box, &error_code);
+
+ if (trace) {
+ sieve_runtime_trace(
+ renv, 0, "mailbox with special-use flag `%s' "
+ "cannot be opened: %s",
+ str_sanitize(special_use, 64), error);
+ }
+
+ mailbox_free(&box);
+
+ if (error_code == MAIL_ERROR_TEMP) {
+ sieve_runtime_error(
+ renv, NULL, "specialuse_exists test: "
+ "failed to open mailbox with special-use flag`%s': %s",
+ str_sanitize(special_use, 64), error);
+ return -1;
+ }
+ return 0;
+ }
+
+ /* Also fail when it is readonly */
+ if (mailbox_is_readonly(box)) {
+ if (trace) {
+ sieve_runtime_trace(
+ renv, 0,
+ "mailbox with special-use flag `%s' is read-only",
+ str_sanitize(special_use, 64));
+ }
+
+ mailbox_free(&box);
+ return 0;
+ }
+
+ mailbox_free(&box);
+ return 1;
+}
+
+static int
+tst_specialuse_exists_check_flag(const struct sieve_runtime_env *renv,
+ struct mailbox *box, const char *use_flag,
+ bool trace, bool *all_exist_r)
+{
+ int ret;
+
+ if (!ext_special_use_flag_valid(use_flag)) {
+ sieve_runtime_error(
+ renv, NULL, "specialuse_exists test: "
+ "invalid special-use flag `%s' specified",
+ str_sanitize(use_flag, 64));
+ return SIEVE_EXEC_FAILURE;
+ }
+
+ if (box != NULL) {
+ /* Mailbox has this SPECIAL-USE flag? */
+ if (!mailbox_has_special_use(box, use_flag)) {
+ *all_exist_r = FALSE;
+ return SIEVE_EXEC_OK;
+ }
+ } else {
+ /* Is there mailbox with this SPECIAL-USE flag? */
+ ret = tst_specialuse_find_specialuse(renv, use_flag);
+ if (ret < 0)
+ return SIEVE_EXEC_TEMP_FAILURE;
+ if (ret == 0) {
+ *all_exist_r = FALSE;
+ return SIEVE_EXEC_OK;
+ }
+ }
+
+ if (trace) {
+ sieve_runtime_trace(
+ renv, 0, "special-use flag `%s' exists",
+ str_sanitize(use_flag, 80));
+ }
+
+ return SIEVE_EXEC_OK;
+}
+
+static int
+tst_specialuse_exists_operation_execute(const struct sieve_runtime_env *renv,
+ sieve_size_t *address)
+{
+ struct sieve_operand oprnd;
+ struct sieve_stringlist *special_use_flags;
+ string_t *mailbox, *special_use_flag;
+ struct mailbox *box = NULL;
+ const char *error;
+ bool trace = FALSE, all_exist = TRUE;
+ int ret;
+
+ /*
+ * Read operands
+ */
+
+ /* Read bare operand (two types possible) */
+ ret = sieve_operand_runtime_read(renv, address, NULL, &oprnd);
+ if (ret <= 0)
+ return ret;
+
+ /* Mailbox operand (optional) */
+ mailbox = NULL;
+ if (!sieve_operand_is_omitted(&oprnd)) {
+ /* Read the mailbox operand */
+ ret = sieve_opr_string_read_data(renv, &oprnd, address,
+ "mailbox", &mailbox);
+ if (ret <= 0)
+ return ret;
+
+ /* Read flag list */
+ ret = sieve_opr_stringlist_read(renv, address,
+ "special-use-flags",
+ &special_use_flags);
+ if (ret <= 0)
+ return ret;
+
+ /* Flag-list operand */
+ } else {
+ /* Read flag list */
+ ret = sieve_opr_stringlist_read(renv, address,
+ "special-use-flags",
+ &special_use_flags);
+ if (ret <= 0)
+ return ret;
+ }
+
+ /*
+ * Perform operation
+ */
+
+ if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_TESTS)) {
+ sieve_runtime_trace(renv, 0, "specialuse_exists test");
+ sieve_runtime_trace_descend(renv);
+
+ trace = sieve_runtime_trace_active(renv, SIEVE_TRLVL_MATCHING);
+ }
+
+ if (mailbox != NULL) {
+ if (!sieve_mailbox_check_name(str_c(mailbox), &error)) {
+ sieve_runtime_warning(
+ renv, NULL, "specialuse_exists test: "
+ "invalid mailbox name `%s' specified: %s",
+ str_sanitize(str_c(mailbox), 256), error);
+ sieve_interpreter_set_test_result(renv->interp, FALSE);
+ return SIEVE_EXEC_OK;
+ }
+
+ if (tst_specialuse_find_mailbox(renv, str_c(mailbox), &box) < 0)
+ return SIEVE_EXEC_TEMP_FAILURE;
+ }
+
+ if (box == NULL && mailbox != NULL) {
+ sieve_runtime_trace(
+ renv, 0, "mailbox `%s' is not accessible",
+ str_sanitize(str_c(mailbox), 80));
+ sieve_interpreter_set_test_result(renv->interp, FALSE);
+ return SIEVE_EXEC_OK;
+ }
+
+ if (mailbox != NULL) {
+ sieve_runtime_trace(
+ renv, 0, "mailbox `%s' is accessible",
+ str_sanitize(str_c(mailbox), 80));
+ }
+
+ ret = 0;
+ special_use_flag = NULL;
+ while (all_exist &&
+ (ret = sieve_stringlist_next_item(
+ special_use_flags, &special_use_flag)) > 0) {
+ const char *use_flag = str_c(special_use_flag);
+
+ ret = tst_specialuse_exists_check_flag(
+ renv, box, use_flag, trace, &all_exist);
+ if (ret <= 0) {
+ if (box != NULL) {
+ /* Close mailbox */
+ mailbox_free(&box);
+ }
+ return ret;
+ }
+ }
+
+ if (box != NULL) {
+ /* Close mailbox */
+ mailbox_free(&box);
+ }
+
+ if (ret < 0) {
+ sieve_runtime_trace_error(
+ renv, "invalid special-use flag item");
+ return SIEVE_EXEC_BIN_CORRUPT;
+ }
+
+ if (trace) {
+ if (all_exist) {
+ sieve_runtime_trace(
+ renv, 0, "all special-use flags are set");
+ } else {
+ sieve_runtime_trace(
+ renv, 0, "some special-use are not set");
+ }
+ }
+
+ sieve_interpreter_set_test_result(renv->interp, all_exist);
+ return SIEVE_EXEC_OK;
+}