summaryrefslogtreecommitdiffstats
path: root/src/lib-smtp/smtp-server-recipient.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib-smtp/smtp-server-recipient.c')
-rw-r--r--src/lib-smtp/smtp-server-recipient.c361
1 files changed, 361 insertions, 0 deletions
diff --git a/src/lib-smtp/smtp-server-recipient.c b/src/lib-smtp/smtp-server-recipient.c
new file mode 100644
index 0000000..fbae663
--- /dev/null
+++ b/src/lib-smtp/smtp-server-recipient.c
@@ -0,0 +1,361 @@
+/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "llist.h"
+#include "str-sanitize.h"
+#include "smtp-address.h"
+#include "smtp-reply.h"
+
+#include "smtp-server-private.h"
+
+static void
+smtp_server_recipient_update_event(struct smtp_server_recipient_private *prcpt)
+{
+ struct event *event = prcpt->rcpt.event;
+ const char *path = smtp_address_encode(prcpt->rcpt.path);
+
+ event_add_str(event, "rcpt_to", path);
+ smtp_params_rcpt_add_to_event(&prcpt->rcpt.params, event);
+ event_set_append_log_prefix(
+ event, t_strdup_printf("rcpt %s: ", str_sanitize(path, 128)));
+}
+
+static void
+smtp_server_recipient_create_event(struct smtp_server_recipient_private *prcpt)
+{
+ struct smtp_server_recipient *rcpt = &prcpt->rcpt;
+ struct smtp_server_connection *conn = rcpt->conn;
+
+ if (rcpt->event != NULL)
+ return;
+
+ if (conn->state.trans == NULL) {
+ /* Create event for the transaction early. */
+ if (conn->next_trans_event == NULL) {
+ conn->next_trans_event = event_create(conn->event);
+ event_set_append_log_prefix(conn->next_trans_event,
+ "trans: ");
+ }
+ rcpt->event = event_create(conn->next_trans_event);
+ } else {
+ /* Use existing transaction event. */
+ rcpt->event = event_create(conn->state.trans->event);
+ }
+ /* Drop transaction log prefix so that the connection event prefix
+ remains. */
+ event_drop_parent_log_prefixes(rcpt->event, 1);
+
+ smtp_server_recipient_update_event(prcpt);
+}
+
+struct smtp_server_recipient *
+smtp_server_recipient_create(struct smtp_server_cmd_ctx *cmd,
+ const struct smtp_address *rcpt_to,
+ const struct smtp_params_rcpt *params)
+{
+ struct smtp_server_recipient_private *prcpt;
+ pool_t pool;
+
+ pool = pool_alloconly_create("smtp server recipient", 512);
+ prcpt = p_new(pool, struct smtp_server_recipient_private, 1);
+ prcpt->refcount = 1;
+ prcpt->rcpt.pool = pool;
+ prcpt->rcpt.conn = cmd->conn;
+ prcpt->rcpt.cmd = cmd;
+ prcpt->rcpt.path = smtp_address_clone(pool, rcpt_to);
+ smtp_params_rcpt_copy(pool, &prcpt->rcpt.params, params);
+
+ smtp_server_recipient_create_event(prcpt);
+
+ return &prcpt->rcpt;
+}
+
+void smtp_server_recipient_ref(struct smtp_server_recipient *rcpt)
+{
+ struct smtp_server_recipient_private *prcpt =
+ (struct smtp_server_recipient_private *)rcpt;
+
+ if (prcpt->destroying)
+ return;
+ i_assert(prcpt->refcount > 0);
+ prcpt->refcount++;
+}
+
+bool smtp_server_recipient_unref(struct smtp_server_recipient **_rcpt)
+{
+ struct smtp_server_recipient *rcpt = *_rcpt;
+ struct smtp_server_recipient_private *prcpt =
+ (struct smtp_server_recipient_private *)rcpt;
+
+ *_rcpt = NULL;
+
+ if (rcpt == NULL)
+ return FALSE;
+ if (prcpt->destroying)
+ return FALSE;
+
+ i_assert(prcpt->refcount > 0);
+ if (--prcpt->refcount > 0)
+ return TRUE;
+ prcpt->destroying = TRUE;
+
+ if (!smtp_server_recipient_call_hooks(
+ &rcpt, SMTP_SERVER_RECIPIENT_HOOK_DESTROY))
+ i_unreached();
+
+ if (!rcpt->finished) {
+ smtp_server_recipient_create_event(prcpt);
+
+ struct event_passthrough *e =
+ event_create_passthrough(rcpt->event)->
+ set_name("smtp_server_transaction_rcpt_finished");
+ e->add_int("status_code", 9000);
+ e->add_str("enhanced_code", "9.0.0");
+ e->add_str("error", "Aborted");
+
+ e_debug(e->event(), "Aborted");
+ }
+
+ event_unref(&rcpt->event);
+ pool_unref(&rcpt->pool);
+ return FALSE;
+}
+
+void smtp_server_recipient_destroy(struct smtp_server_recipient **_rcpt)
+{
+ smtp_server_recipient_unref(_rcpt);
+}
+
+const struct smtp_address *
+smtp_server_recipient_get_original(struct smtp_server_recipient *rcpt)
+{
+ if (rcpt->params.orcpt.addr == NULL)
+ return rcpt->path;
+ return rcpt->params.orcpt.addr;
+}
+
+bool smtp_server_recipient_approved(struct smtp_server_recipient **_rcpt)
+{
+ struct smtp_server_recipient *rcpt = *_rcpt;
+ struct smtp_server_transaction *trans = rcpt->conn->state.trans;
+
+ i_assert(trans != NULL);
+ i_assert(rcpt->event != NULL);
+
+ e_debug(rcpt->event, "Approved");
+
+ rcpt->cmd = NULL;
+ smtp_server_transaction_add_rcpt(trans, rcpt);
+
+ return smtp_server_recipient_call_hooks(
+ _rcpt, SMTP_SERVER_RECIPIENT_HOOK_APPROVED);
+}
+
+void smtp_server_recipient_denied(struct smtp_server_recipient *rcpt,
+ const struct smtp_server_reply *reply)
+{
+ i_assert(!rcpt->finished);
+ i_assert(rcpt->event != NULL);
+
+ rcpt->finished = TRUE;
+
+ struct event_passthrough *e =
+ event_create_passthrough(rcpt->event)->
+ set_name("smtp_server_transaction_rcpt_finished");
+ smtp_server_reply_add_to_event(reply, e);
+
+ e_debug(e->event(), "Denied");
+}
+
+void smtp_server_recipient_data_command(struct smtp_server_recipient *rcpt,
+ struct smtp_server_cmd_ctx *cmd)
+{
+ rcpt->cmd = cmd;
+}
+
+void smtp_server_recipient_data_replied(struct smtp_server_recipient *rcpt)
+{
+ if (rcpt->replied)
+ return;
+ if (smtp_server_recipient_get_reply(rcpt) == NULL)
+ return;
+ rcpt->replied = TRUE;
+ if (!smtp_server_recipient_call_hooks(
+ &rcpt, SMTP_SERVER_RECIPIENT_HOOK_DATA_REPLIED)) {
+ /* Nothing to do */
+ }
+}
+
+struct smtp_server_reply *
+smtp_server_recipient_get_reply(struct smtp_server_recipient *rcpt)
+{
+ if (!HAS_ALL_BITS(rcpt->trans->flags,
+ SMTP_SERVER_TRANSACTION_FLAG_REPLY_PER_RCPT))
+ return smtp_server_command_get_reply(rcpt->cmd->cmd, 0);
+ return smtp_server_command_get_reply(rcpt->cmd->cmd, rcpt->index);
+}
+
+bool smtp_server_recipient_is_replied(struct smtp_server_recipient *rcpt)
+{
+ i_assert(rcpt->cmd != NULL);
+
+ return smtp_server_command_is_replied(rcpt->cmd->cmd);
+}
+
+void smtp_server_recipient_replyv(struct smtp_server_recipient *rcpt,
+ unsigned int status, const char *enh_code,
+ const char *fmt, va_list args)
+{
+ i_assert(rcpt->cmd != NULL);
+
+ if (smtp_server_command_is_rcpt(rcpt->cmd) && (status / 100) == 2) {
+ smtp_server_reply_indexv(rcpt->cmd, rcpt->index,
+ status, enh_code, fmt, args);
+ return;
+ }
+
+ smtp_server_reply_index(rcpt->cmd, rcpt->index, status, enh_code,
+ "<%s> %s", smtp_address_encode(rcpt->path),
+ t_strdup_vprintf(fmt, args));
+}
+
+void smtp_server_recipient_reply(struct smtp_server_recipient *rcpt,
+ unsigned int status, const char *enh_code,
+ const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ smtp_server_recipient_replyv(rcpt, status, enh_code, fmt, args);
+ va_end(args);
+}
+
+void smtp_server_recipient_reply_forward(struct smtp_server_recipient *rcpt,
+ const struct smtp_reply *from)
+{
+ bool add_path = (!smtp_server_command_is_rcpt(rcpt->cmd) ||
+ !smtp_reply_is_success(from));
+ struct smtp_server_reply *reply;
+
+ reply = smtp_server_reply_create_forward(rcpt->cmd->cmd, rcpt->index,
+ from);
+ smtp_server_reply_replace_path(reply, rcpt->path, add_path);
+ smtp_server_reply_submit(reply);
+}
+
+void smtp_server_recipient_reset(struct smtp_server_recipient *rcpt)
+{
+ i_assert(!rcpt->finished);
+ rcpt->finished = TRUE;
+
+ struct event_passthrough *e =
+ event_create_passthrough(rcpt->event)->
+ set_name("smtp_server_transaction_rcpt_finished");
+ e->add_int("status_code", 9000);
+ e->add_str("enhanced_code", "9.0.0");
+ e->add_str("error", "Reset");
+
+ e_debug(e->event(), "Reset");
+}
+
+void smtp_server_recipient_finished(struct smtp_server_recipient *rcpt,
+ const struct smtp_server_reply *reply)
+{
+ i_assert(!rcpt->finished);
+ rcpt->finished = TRUE;
+
+ struct event_passthrough *e =
+ event_create_passthrough(rcpt->event)->
+ set_name("smtp_server_transaction_rcpt_finished");
+ smtp_server_reply_add_to_event(reply, e);
+
+ e_debug(e->event(), "Finished");
+}
+
+#undef smtp_server_recipient_add_hook
+void smtp_server_recipient_add_hook(struct smtp_server_recipient *rcpt,
+ enum smtp_server_recipient_hook_type type,
+ smtp_server_rcpt_func_t func, void *context)
+{
+ struct smtp_server_recipient_private *prcpt =
+ (struct smtp_server_recipient_private *)rcpt;
+ struct smtp_server_recipient_hook *hook;
+
+ i_assert(func != NULL);
+
+ hook = prcpt->hooks_head;
+ while (hook != NULL) {
+ /* No double registrations */
+ i_assert(hook->type != type || hook->func != func);
+
+ hook = hook->next;
+ }
+
+ hook = p_new(rcpt->pool, struct smtp_server_recipient_hook, 1);
+ hook->type = type;
+ hook->func = func;
+ hook->context = context;
+
+ DLLIST2_APPEND(&prcpt->hooks_head, &prcpt->hooks_tail, hook);
+}
+
+#undef smtp_server_recipient_remove_hook
+void smtp_server_recipient_remove_hook(
+ struct smtp_server_recipient *rcpt,
+ enum smtp_server_recipient_hook_type type,
+ smtp_server_rcpt_func_t *func)
+{
+ struct smtp_server_recipient_private *prcpt =
+ (struct smtp_server_recipient_private *)rcpt;
+ struct smtp_server_recipient_hook *hook;
+ bool found = FALSE;
+
+ hook = prcpt->hooks_head;
+ while (hook != NULL) {
+ struct smtp_server_recipient_hook *hook_next = hook->next;
+
+ if (hook->type == type && hook->func == func) {
+ DLLIST2_REMOVE(&prcpt->hooks_head, &prcpt->hooks_tail,
+ hook);
+ found = TRUE;
+ break;
+ }
+
+ hook = hook_next;
+ }
+ i_assert(found);
+}
+
+bool smtp_server_recipient_call_hooks(
+ struct smtp_server_recipient **_rcpt,
+ enum smtp_server_recipient_hook_type type)
+{
+ struct smtp_server_recipient *rcpt = *_rcpt;
+ struct smtp_server_recipient_private *prcpt =
+ (struct smtp_server_recipient_private *)rcpt;
+ struct smtp_server_recipient_hook *hook;
+
+ if (type != SMTP_SERVER_RECIPIENT_HOOK_DESTROY)
+ smtp_server_recipient_ref(rcpt);
+
+ hook = prcpt->hooks_head;
+ while (hook != NULL) {
+ struct smtp_server_recipient_hook *hook_next = hook->next;
+
+ if (hook->type == type) {
+ DLLIST2_REMOVE(&prcpt->hooks_head, &prcpt->hooks_tail,
+ hook);
+ hook->func(rcpt, hook->context);
+ }
+
+ hook = hook_next;
+ }
+
+ if (type != SMTP_SERVER_RECIPIENT_HOOK_DESTROY) {
+ if (!smtp_server_recipient_unref(&rcpt)) {
+ *_rcpt = NULL;
+ return FALSE;
+ }
+ }
+ return TRUE;
+}