summaryrefslogtreecommitdiffstats
path: root/pigeonhole/src/lib-sieve/ext-reject.c
diff options
context:
space:
mode:
Diffstat (limited to 'pigeonhole/src/lib-sieve/ext-reject.c')
-rw-r--r--pigeonhole/src/lib-sieve/ext-reject.c606
1 files changed, 606 insertions, 0 deletions
diff --git a/pigeonhole/src/lib-sieve/ext-reject.c b/pigeonhole/src/lib-sieve/ext-reject.c
new file mode 100644
index 0000000..649c1f2
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/ext-reject.c
@@ -0,0 +1,606 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+/* Extension reject
+ * ----------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: RFC 5429
+ * Implementation: full
+ * Status: testing
+ *
+ */
+
+#include "lib.h"
+#include "ioloop.h"
+#include "hostpid.h"
+#include "str-sanitize.h"
+#include "message-date.h"
+#include "message-size.h"
+#include "istream.h"
+#include "istream-header-filter.h"
+
+#include "rfc2822.h"
+
+#include "sieve-common.h"
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-code.h"
+#include "sieve-actions.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-binary.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+#include "sieve-result.h"
+#include "sieve-message.h"
+#include "sieve-smtp.h"
+
+/*
+ * Forward declarations
+ */
+
+static const struct sieve_command_def reject_command;
+static const struct sieve_operation_def reject_operation;
+
+static const struct sieve_command_def ereject_command;
+static const struct sieve_operation_def ereject_operation;
+
+/*
+ * Extensions
+ */
+
+static bool
+ext_reject_validator_validate(const struct sieve_extension *ext,
+ struct sieve_validator *valdtr, void *context,
+ struct sieve_ast_argument *require_arg,
+ bool required);
+static int
+ext_reject_interpreter_run(const struct sieve_extension *this_ext,
+ const struct sieve_runtime_env *renv,
+ void *context, bool deferred);
+
+/* Reject */
+
+static bool
+ext_reject_validator_load(const struct sieve_extension *ext,
+ struct sieve_validator *valdtr);
+static bool
+ext_reject_interpreter_load(const struct sieve_extension *ext,
+ const struct sieve_runtime_env *renv,
+ sieve_size_t *address);
+
+const struct sieve_extension_def reject_extension = {
+ .name = "reject",
+ .validator_load = ext_reject_validator_load,
+ .interpreter_load = ext_reject_interpreter_load,
+ SIEVE_EXT_DEFINE_OPERATION(reject_operation)
+};
+const struct sieve_validator_extension
+reject_validator_extension = {
+ .ext = &reject_extension,
+ .validate = ext_reject_validator_validate
+};
+const struct sieve_interpreter_extension
+reject_interpreter_extension = {
+ .ext_def = &reject_extension,
+ .run = ext_reject_interpreter_run
+};
+
+static bool
+ext_reject_validator_load(const struct sieve_extension *ext,
+ struct sieve_validator *valdtr)
+{
+ /* Register new command */
+ sieve_validator_register_command(valdtr, ext, &reject_command);
+
+ sieve_validator_extension_register(valdtr, ext,
+ &reject_validator_extension, NULL);
+ return TRUE;
+}
+
+static bool
+ext_reject_interpreter_load(const struct sieve_extension *ext,
+ const struct sieve_runtime_env *renv,
+ sieve_size_t *address ATTR_UNUSED)
+{
+ sieve_interpreter_extension_register(renv->interp, ext,
+ &reject_interpreter_extension,
+ NULL);
+ return TRUE;
+}
+
+/* EReject */
+
+static bool
+ext_ereject_validator_load(const struct sieve_extension *ext,
+ struct sieve_validator *valdtr);
+static bool
+ext_ereject_interpreter_load(const struct sieve_extension *ext,
+ const struct sieve_runtime_env *renv,
+ sieve_size_t *address);
+
+const struct sieve_extension_def ereject_extension = {
+ .name = "ereject",
+ .validator_load = ext_ereject_validator_load,
+ .interpreter_load = ext_ereject_interpreter_load,
+ SIEVE_EXT_DEFINE_OPERATION(ereject_operation)
+};
+const struct sieve_validator_extension
+ereject_validator_extension = {
+ .ext = &ereject_extension,
+ .validate = ext_reject_validator_validate
+};
+const struct sieve_interpreter_extension
+ereject_interpreter_extension = {
+ .ext_def = &ereject_extension,
+ .run = ext_reject_interpreter_run
+};
+
+static bool
+ext_ereject_validator_load(const struct sieve_extension *ext,
+ struct sieve_validator *valdtr)
+{
+ /* Register new command */
+ sieve_validator_register_command(valdtr, ext, &ereject_command);
+
+ sieve_validator_extension_register(valdtr, ext,
+ &ereject_validator_extension, NULL);
+ return TRUE;
+}
+
+static bool
+ext_ereject_interpreter_load(const struct sieve_extension *ext,
+ const struct sieve_runtime_env *renv,
+ sieve_size_t *address ATTR_UNUSED)
+{
+ sieve_interpreter_extension_register(renv->interp, ext,
+ &ereject_interpreter_extension,
+ NULL);
+ return TRUE;
+}
+
+/* Environment checking */
+
+static bool
+ext_reject_validator_validate(const struct sieve_extension *ext,
+ struct sieve_validator *valdtr,
+ void *context ATTR_UNUSED,
+ struct sieve_ast_argument *require_arg,
+ bool required)
+{
+ if (required) {
+ enum sieve_compile_flags flags =
+ sieve_validator_compile_flags(valdtr);
+
+ if ((flags & SIEVE_COMPILE_FLAG_NO_ENVELOPE) != 0) {
+ sieve_argument_validate_error(
+ valdtr, require_arg,
+ "the %s extension cannot be used in this context "
+ "(needs access to message envelope)",
+ sieve_extension_name(ext));
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static int
+ext_reject_interpreter_run(const struct sieve_extension *ext,
+ const struct sieve_runtime_env *renv,
+ void *context ATTR_UNUSED, bool deferred)
+{
+ const struct sieve_execute_env *eenv = renv->exec_env;
+
+ if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) != 0) {
+ if (!deferred) {
+ sieve_runtime_error(
+ renv, NULL,
+ "the %s extension cannot be used in this context "
+ "(needs access to message envelope)",
+ sieve_extension_name(ext));
+ }
+ return SIEVE_EXEC_FAILURE;
+ }
+ return SIEVE_EXEC_OK;
+}
+
+/*
+ * Commands
+ */
+
+/* Forward declarations */
+
+static bool
+cmd_reject_validate(struct sieve_validator *valdtr, struct sieve_command *cmd);
+static bool
+cmd_reject_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_command *cmd);
+
+/* Reject command
+ *
+ * Syntax:
+ * reject <reason: string>
+ */
+
+static const struct sieve_command_def reject_command = {
+ .identifier = "reject",
+ .type = SCT_COMMAND,
+ .positional_args = 1,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .validate = cmd_reject_validate,
+ .generate = cmd_reject_generate
+};
+
+/* EReject command
+ *
+ * Syntax:
+ * ereject <reason: string>
+ */
+
+static const struct sieve_command_def ereject_command = {
+ .identifier = "ereject",
+ .type = SCT_COMMAND,
+ .positional_args = 1,
+ .subtests = 0,
+ .block_allowed = FALSE,
+ .block_required = FALSE,
+ .validate = cmd_reject_validate,
+ .generate = cmd_reject_generate,
+};
+
+/*
+ * Operations
+ */
+
+/* Forward declarations */
+
+static bool
+ext_reject_operation_dump(const struct sieve_dumptime_env *denv,
+ sieve_size_t *address);
+static int
+ext_reject_operation_execute(const struct sieve_runtime_env *renv,
+ sieve_size_t *address);
+
+/* Reject operation */
+
+static const struct sieve_operation_def reject_operation = {
+ .mnemonic = "REJECT",
+ .ext_def = &reject_extension,
+ .dump = ext_reject_operation_dump,
+ .execute = ext_reject_operation_execute
+};
+
+/* EReject operation */
+
+static const struct sieve_operation_def ereject_operation = {
+ .mnemonic = "EREJECT",
+ .ext_def = &ereject_extension,
+ .dump = ext_reject_operation_dump,
+ .execute = ext_reject_operation_execute
+};
+
+/*
+ * Reject action
+ */
+
+static int
+act_reject_check_duplicate(const struct sieve_runtime_env *renv,
+ const struct sieve_action *act,
+ const struct sieve_action *act_other);
+int act_reject_check_conflict(const struct sieve_runtime_env *renv,
+ const struct sieve_action *act,
+ const struct sieve_action *act_other);
+static void
+act_reject_print(const struct sieve_action *action,
+ const struct sieve_result_print_env *rpenv, bool *keep);
+static int
+act_reject_start(const struct sieve_action_exec_env *aenv, void **tr_context);
+static int
+act_reject_execute(const struct sieve_action_exec_env *aenv, void *tr_context,
+ bool *keep);
+static int
+act_reject_commit(const struct sieve_action_exec_env *aenv, void *tr_context);
+
+const struct sieve_action_def act_reject = {
+ .name = "reject",
+ .flags = SIEVE_ACTFLAG_SENDS_RESPONSE,
+ .check_duplicate = act_reject_check_duplicate,
+ .check_conflict = act_reject_check_conflict,
+ .print = act_reject_print,
+ .start = act_reject_start,
+ .execute = act_reject_execute,
+ .commit = act_reject_commit,
+};
+
+struct act_reject_context {
+ const char *reason;
+ bool ereject;
+};
+
+/*
+ * Validation
+ */
+
+static bool
+cmd_reject_validate(struct sieve_validator *valdtr, struct sieve_command *cmd)
+{
+ struct sieve_ast_argument *arg = cmd->first_positional;
+
+ if (!sieve_validate_positional_argument(valdtr, cmd, arg, "reason",
+ 1, SAAT_STRING))
+ return FALSE;
+
+ return sieve_validator_argument_activate(valdtr, cmd, arg, FALSE);
+}
+
+/*
+ * Code generation
+ */
+
+static bool
+cmd_reject_generate(const struct sieve_codegen_env *cgenv,
+ struct sieve_command *cmd)
+{
+ if (sieve_command_is(cmd, reject_command)) {
+ sieve_operation_emit(cgenv->sblock, cmd->ext,
+ &reject_operation);
+ } else {
+ sieve_operation_emit(cgenv->sblock, cmd->ext,
+ &ereject_operation);
+ }
+
+ /* Generate arguments */
+ return sieve_generate_arguments(cgenv, cmd, NULL);
+}
+
+/*
+ * Code dump
+ */
+
+static bool
+ext_reject_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);
+
+ if (sieve_action_opr_optional_dump(denv, address, NULL) != 0)
+ return FALSE;
+
+ return sieve_opr_string_dump(denv, address, "reason");
+}
+
+/*
+ * Interpretation
+ */
+
+static int
+ext_reject_operation_execute(const struct sieve_runtime_env *renv,
+ sieve_size_t *address)
+{
+ const struct sieve_operation *oprtn = renv->oprtn;
+ const struct sieve_extension *this_ext = oprtn->ext;
+ struct sieve_side_effects_list *slist = NULL;
+ struct act_reject_context *act;
+ string_t *reason;
+ pool_t pool;
+ int ret;
+
+ /*
+ * Read data
+ */
+
+ /* Optional operands (side effects only) */
+ if (sieve_action_opr_optional_read(renv, address, NULL,
+ &ret, &slist) != 0)
+ return ret;
+
+ /* Read rejection reason */
+ if ((ret = sieve_opr_string_read(renv, address, "reason",
+ &reason)) <= 0)
+ return ret;
+
+ /*
+ * Perform operation
+ */
+
+ if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_ACTIONS)) {
+ if (sieve_operation_is(oprtn, ereject_operation))
+ sieve_runtime_trace(renv, 0, "ereject action");
+ else
+ sieve_runtime_trace(renv, 0, "reject action");
+
+ sieve_runtime_trace_descend(renv);
+ sieve_runtime_trace(renv, 0, "reject message with reason `%s'",
+ str_sanitize(str_c(reason), 64));
+ }
+
+ /* Add reject action to the result */
+ pool = sieve_result_pool(renv->result);
+ act = p_new(pool, struct act_reject_context, 1);
+ act->reason = p_strdup(pool, str_c(reason));
+ act->ereject = sieve_operation_is(oprtn, ereject_operation);
+
+ if (sieve_result_add_action(renv, this_ext,
+ (act->ereject ? "ereject" : "reject"),
+ &act_reject, slist, (void *)act,
+ 0, FALSE) < 0)
+ return SIEVE_EXEC_FAILURE;
+ return SIEVE_EXEC_OK;
+}
+
+/*
+ * Action implementation
+ */
+
+struct act_reject_transaction {
+ bool ignore_reject:1;
+};
+
+static int
+act_reject_check_duplicate(const struct sieve_runtime_env *renv ATTR_UNUSED,
+ const struct sieve_action *act,
+ const struct sieve_action *act_other)
+{
+ if (!sieve_action_is_executed(act_other, renv->result)) {
+ sieve_runtime_error(
+ renv, act->location,
+ "duplicate reject/ereject action not allowed "
+ "(previously triggered one was here: %s)",
+ act_other->location);
+ return -1;
+ }
+
+ return 1;
+}
+
+int act_reject_check_conflict(const struct sieve_runtime_env *renv,
+ const struct sieve_action *act,
+ const struct sieve_action *act_other)
+{
+ if ((act_other->def->flags & SIEVE_ACTFLAG_TRIES_DELIVER) > 0) {
+ if (!sieve_action_is_executed(act_other, renv->result)) {
+ sieve_runtime_error(
+ renv, act->location,
+ "reject/ereject action conflicts with other action: "
+ "the %s action (%s) tries to deliver the message",
+ act_other->def->name, act_other->location);
+ return -1;
+ }
+ }
+
+ if ((act_other->def->flags & SIEVE_ACTFLAG_SENDS_RESPONSE) > 0) {
+ struct act_reject_context *rj_ctx;
+
+ if (!sieve_action_is_executed(act_other, renv->result)) {
+ sieve_runtime_error(
+ renv, act->location,
+ "reject/ereject action conflicts with other action: "
+ "the %s action (%s) also sends a response to the sender",
+ act_other->def->name, act_other->location);
+ return -1;
+ }
+
+ /* Conflicting action was already executed, transform reject
+ * into discard equivalent.
+ */
+ rj_ctx = (struct act_reject_context *)act->context;
+ rj_ctx->reason = NULL;
+ }
+
+ return 0;
+}
+
+static void
+act_reject_print(const struct sieve_action *action,
+ const struct sieve_result_print_env *rpenv, bool *keep)
+{
+ struct act_reject_context *rj_ctx =
+ (struct act_reject_context *)action->context;
+
+ if (rj_ctx->reason != NULL) {
+ sieve_result_action_printf(
+ rpenv, "reject message with reason: %s",
+ str_sanitize(rj_ctx->reason, 128));
+ } else {
+ sieve_result_action_printf(
+ rpenv,
+ "reject message without sending a response (discard)");
+ }
+
+ *keep = FALSE;
+}
+
+static int
+act_reject_start(const struct sieve_action_exec_env *aenv, void **tr_context)
+{
+ struct act_reject_transaction *trans;
+ pool_t pool = sieve_result_pool(aenv->result);
+
+ /* Create transaction context */
+ trans = p_new(pool, struct act_reject_transaction, 1);
+ *tr_context = trans;
+
+ return SIEVE_EXEC_OK;
+}
+
+static int
+act_reject_execute(const struct sieve_action_exec_env *aenv,
+ void *tr_context, bool *keep)
+{
+ const struct sieve_execute_env *eenv = aenv->exec_env;
+ struct act_reject_context *rj_ctx =
+ (struct act_reject_context *)aenv->action->context;
+ struct act_reject_transaction *trans = tr_context;
+ const struct smtp_address *sender, *recipient;
+
+ sender = sieve_message_get_sender(aenv->msgctx);
+ recipient = sieve_message_get_orig_recipient(aenv->msgctx);
+
+ if ((eenv->flags & SIEVE_EXECUTE_FLAG_SKIP_RESPONSES) != 0) {
+ sieve_result_global_log(
+ aenv, "not sending reject message (skipped)");
+ trans->ignore_reject = TRUE;
+ return SIEVE_EXEC_OK;
+ }
+ if (smtp_address_isnull(recipient)) {
+ sieve_result_global_warning(
+ aenv, "reject action aborted: envelope recipient is <>");
+ trans->ignore_reject = TRUE;
+ return SIEVE_EXEC_OK;
+ }
+ if (rj_ctx->reason == NULL) {
+ sieve_result_global_log(
+ aenv, "not sending reject message "
+ "(would cause second response to sender)");
+ trans->ignore_reject = TRUE;
+ *keep = FALSE;
+ return SIEVE_EXEC_OK;
+ }
+ if (smtp_address_isnull(sender)) {
+ sieve_result_global_log(
+ aenv, "not sending reject message to <>");
+ trans->ignore_reject = TRUE;
+ *keep = FALSE;
+ return SIEVE_EXEC_OK;
+ }
+
+ *keep = FALSE;
+ return SIEVE_EXEC_OK;
+}
+
+static int
+act_reject_commit(const struct sieve_action_exec_env *aenv,
+ void *tr_context ATTR_UNUSED)
+{
+ const struct sieve_execute_env *eenv = aenv->exec_env;
+ struct act_reject_context *rj_ctx =
+ (struct act_reject_context *)aenv->action->context;
+ struct act_reject_transaction *trans = tr_context;
+ const struct smtp_address *sender, *recipient;
+ int ret;
+
+ sender = sieve_message_get_sender(aenv->msgctx);
+ recipient = sieve_message_get_orig_recipient(aenv->msgctx);
+
+ if (trans->ignore_reject)
+ return SIEVE_EXEC_OK;
+
+ if ((ret = sieve_action_reject_mail(aenv, recipient,
+ rj_ctx->reason)) <= 0)
+ return ret;
+
+ eenv->exec_status->significant_action_executed = TRUE;
+
+ struct event_passthrough *e = sieve_action_create_finish_event(aenv);
+
+ sieve_result_event_log(aenv, e->event(),
+ "rejected message from <%s> (%s)",
+ smtp_address_encode(sender),
+ (rj_ctx->ereject ? "ereject" : "reject"));
+
+ return SIEVE_EXEC_OK;
+}