summaryrefslogtreecommitdiffstats
path: root/src/lib-smtp/smtp-server-transaction.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/lib-smtp/smtp-server-transaction.c361
1 files changed, 361 insertions, 0 deletions
diff --git a/src/lib-smtp/smtp-server-transaction.c b/src/lib-smtp/smtp-server-transaction.c
new file mode 100644
index 0000000..7d9b839
--- /dev/null
+++ b/src/lib-smtp/smtp-server-transaction.c
@@ -0,0 +1,361 @@
+/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "str.h"
+#include "strfuncs.h"
+#include "message-date.h"
+#include "smtp-address.h"
+#include "smtp-params.h"
+
+#include "smtp-server-private.h"
+
+static void
+smtp_server_transaction_update_event(struct smtp_server_transaction *trans)
+{
+ struct event *event = trans->event;
+
+ event_add_str(event, "transaction_id", trans->id);
+ event_add_str(event, "session", trans->id);
+ event_add_str(event, "mail_from",
+ smtp_address_encode(trans->mail_from));
+ event_add_str(event, "mail_from_raw",
+ smtp_address_encode_raw(trans->mail_from));
+ smtp_params_mail_add_to_event(&trans->params, event);
+ event_set_append_log_prefix(event,
+ t_strdup_printf("trans <%s>: ", trans->id));
+}
+
+struct smtp_server_transaction *
+smtp_server_transaction_create(struct smtp_server_connection *conn,
+ const struct smtp_server_cmd_mail *mail_data)
+{
+ struct smtp_server_transaction *trans;
+ pool_t pool;
+
+ /* create new transaction */
+ pool = pool_alloconly_create("smtp server transaction", 4096);
+ trans = p_new(pool, struct smtp_server_transaction, 1);
+ trans->pool = pool;
+ trans->conn = conn;
+
+ /* generate transaction ID */
+ if (conn->transaction_seq++ == 0)
+ trans->id = conn->session_id;
+ else {
+ trans->id = p_strdup_printf(pool, "%s:T%u", conn->session_id,
+ conn->transaction_seq);
+ }
+
+ trans->flags = mail_data->flags;
+ trans->mail_from = smtp_address_clone(trans->pool, mail_data->path);
+ smtp_params_mail_copy(pool, &trans->params, &mail_data->params);
+ trans->timestamp = mail_data->timestamp;
+
+ if (conn->next_trans_event == NULL)
+ trans->event = event_create(conn->event);
+ else {
+ trans->event = conn->next_trans_event;
+ conn->next_trans_event = NULL;
+ }
+ smtp_server_transaction_update_event(trans);
+
+ struct event_passthrough *e =
+ event_create_passthrough(trans->event)->
+ set_name("smtp_server_transaction_started");
+
+ e_debug(e->event(), "Start");
+
+ if (conn->callbacks != NULL &&
+ conn->callbacks->conn_trans_start != NULL)
+ conn->callbacks->conn_trans_start(conn->context, trans);
+
+ return trans;
+}
+
+void smtp_server_transaction_free(struct smtp_server_transaction **_trans)
+{
+ struct smtp_server_transaction *trans = *_trans;
+ struct smtp_server_connection *conn = trans->conn;
+ struct smtp_server_recipient **rcpts;
+ unsigned int rcpts_total, rcpts_aborted, rcpts_failed;
+ unsigned int rcpts_count, i;
+
+ *_trans = NULL;
+
+ if (conn->callbacks != NULL &&
+ conn->callbacks->conn_trans_free != NULL)
+ conn->callbacks->conn_trans_free(conn->context, trans);
+
+ rcpts_count = 0;
+ if (array_is_created(&trans->rcpt_to))
+ rcpts = array_get_modifiable(&trans->rcpt_to, &rcpts_count);
+
+ rcpts_aborted = rcpts_count + conn->state.pending_rcpt_cmds;
+ rcpts_failed = conn->state.denied_rcpt_cmds;
+ rcpts_total = rcpts_aborted + rcpts_failed;
+
+ for (i = 0; i < rcpts_count; i++)
+ smtp_server_recipient_destroy(&rcpts[i]);
+
+ if (!trans->finished) {
+ struct event_passthrough *e =
+ event_create_passthrough(trans->event)->
+ set_name("smtp_server_transaction_finished")->
+ add_int("recipients", rcpts_total)->
+ add_int("recipients_denied", rcpts_failed)->
+ add_int("recipients_aborted", rcpts_aborted)->
+ add_int("recipients_failed", rcpts_failed)->
+ add_int("recipients_succeeded", 0);
+ 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(&trans->event);
+ pool_unref(&trans->pool);
+}
+
+struct smtp_server_recipient *
+smtp_server_transaction_find_rcpt_duplicate(
+ struct smtp_server_transaction *trans,
+ struct smtp_server_recipient *rcpt)
+{
+ struct smtp_server_recipient *drcpt;
+
+ i_assert(array_is_created(&trans->rcpt_to));
+ array_foreach_elem(&trans->rcpt_to, drcpt) {
+ if (drcpt == rcpt)
+ continue;
+ if (smtp_address_equals(drcpt->path, rcpt->path) &&
+ smtp_params_rcpt_equal(&drcpt->params, &rcpt->params))
+ return drcpt;
+ }
+ return NULL;
+}
+
+void smtp_server_transaction_add_rcpt(struct smtp_server_transaction *trans,
+ struct smtp_server_recipient *rcpt)
+{
+ if (!array_is_created(&trans->rcpt_to))
+ p_array_init(&trans->rcpt_to, trans->pool, 8);
+
+ rcpt->trans = trans;
+ rcpt->index = array_count(&trans->rcpt_to);
+
+ array_push_back(&trans->rcpt_to, &rcpt);
+}
+
+bool smtp_server_transaction_has_rcpt(struct smtp_server_transaction *trans)
+{
+ return (array_is_created(&trans->rcpt_to) &&
+ array_count(&trans->rcpt_to) > 0);
+}
+
+unsigned int
+smtp_server_transaction_rcpt_count(struct smtp_server_transaction *trans)
+{
+ if (!array_is_created(&trans->rcpt_to))
+ return 0;
+ return array_count(&trans->rcpt_to);
+}
+
+void smtp_server_transaction_data_command(struct smtp_server_transaction *trans,
+ struct smtp_server_cmd_ctx *cmd)
+{
+ struct smtp_server_recipient *rcpt;
+
+ trans->cmd = cmd;
+
+ if (!array_is_created(&trans->rcpt_to))
+ return;
+ array_foreach_elem(&trans->rcpt_to, rcpt)
+ smtp_server_recipient_data_command(rcpt, cmd);
+}
+
+void smtp_server_transaction_received(struct smtp_server_transaction *trans,
+ uoff_t data_size)
+{
+ event_add_int(trans->event, "data_size", data_size);
+}
+
+void smtp_server_transaction_reset(struct smtp_server_transaction *trans)
+{
+ struct smtp_server_connection *conn = trans->conn;
+ struct smtp_server_recipient *const *rcpts = NULL;
+ unsigned int rcpts_total, rcpts_failed, rcpts_aborted;
+ unsigned int rcpts_count, i;
+
+ i_assert(!trans->finished);
+ trans->finished = TRUE;
+
+ rcpts_count = 0;
+ if (array_is_created(&trans->rcpt_to))
+ rcpts = array_get(&trans->rcpt_to, &rcpts_count);
+
+ rcpts_aborted = rcpts_count + conn->state.pending_rcpt_cmds;
+ rcpts_failed = conn->state.denied_rcpt_cmds;
+ rcpts_total = rcpts_aborted + rcpts_failed;
+
+ for (i = 0; i < rcpts_count; i++)
+ smtp_server_recipient_reset(rcpts[i]);
+
+ struct event_passthrough *e =
+ event_create_passthrough(trans->event)->
+ set_name("smtp_server_transaction_finished")->
+ add_int("recipients", rcpts_total)->
+ add_int("recipients_denied", rcpts_failed)->
+ add_int("recipients_aborted", rcpts_aborted)->
+ add_int("recipients_failed", rcpts_failed)->
+ add_int("recipients_succeeded", 0)->
+ add_str("is_reset", "yes");
+ e_debug(e->event(), "Finished");
+}
+
+void smtp_server_transaction_finished(struct smtp_server_transaction *trans,
+ struct smtp_server_cmd_ctx *cmd)
+{
+ struct smtp_server_connection *conn = trans->conn;
+ struct smtp_server_recipient *const *rcpts = NULL;
+ const struct smtp_server_reply *trans_reply = NULL;
+ unsigned int rcpts_total, rcpts_denied, rcpts_failed, rcpts_succeeded;
+ unsigned int rcpts_count, i;
+
+ i_assert(conn->state.pending_rcpt_cmds == 0);
+ i_assert(!trans->finished);
+ trans->finished = TRUE;
+
+ rcpts_count = 0;
+ if (array_is_created(&trans->rcpt_to))
+ rcpts = array_get(&trans->rcpt_to, &rcpts_count);
+
+ rcpts_succeeded = 0;
+ rcpts_denied = conn->state.denied_rcpt_cmds;
+ rcpts_failed = conn->state.denied_rcpt_cmds;
+ rcpts_total = rcpts_count + conn->state.denied_rcpt_cmds;
+ for (i = 0; i < rcpts_count; i++) {
+ struct smtp_server_reply *reply;
+
+ if ((trans->flags &
+ SMTP_SERVER_TRANSACTION_FLAG_REPLY_PER_RCPT) != 0)
+ reply = smtp_server_command_get_reply(cmd->cmd, i);
+ else
+ reply = smtp_server_command_get_reply(cmd->cmd, 0);
+ smtp_server_recipient_finished(rcpts[i], reply);
+
+ if (smtp_server_reply_is_success(reply))
+ rcpts_succeeded++;
+ else {
+ rcpts_failed++;
+ if (trans_reply == NULL)
+ trans_reply = reply;
+ }
+ }
+
+ if (trans_reply == NULL) {
+ /* record first success reply in transaction */
+ trans_reply = smtp_server_command_get_reply(cmd->cmd, 0);
+ }
+
+ struct event_passthrough *e =
+ event_create_passthrough(trans->event)->
+ set_name("smtp_server_transaction_finished")->
+ add_int("recipients", rcpts_total)->
+ add_int("recipients_denied", rcpts_denied)->
+ add_int("recipients_aborted", 0)->
+ add_int("recipients_failed", rcpts_failed)->
+ add_int("recipients_succeeded", rcpts_succeeded);
+ smtp_server_reply_add_to_event(trans_reply, e);
+
+ e_debug(e->event(), "Finished");
+}
+
+void smtp_server_transaction_fail_data(struct smtp_server_transaction *trans,
+ struct smtp_server_cmd_ctx *data_cmd,
+ unsigned int status, const char *enh_code,
+ const char *fmt, va_list args)
+{
+ struct smtp_server_recipient *const *rcpts;
+ const char *msg;
+ unsigned int count, i;
+
+ msg = t_strdup_vprintf(fmt, args);
+ rcpts = array_get(&trans->rcpt_to, &count);
+ for (i = 0; i < count; i++) {
+ smtp_server_reply_index(data_cmd, i,
+ status, enh_code, "<%s> %s",
+ smtp_address_encode(rcpts[i]->path), msg);
+ }
+}
+
+void smtp_server_transaction_write_trace_record(
+ string_t *str, struct smtp_server_transaction *trans,
+ enum smtp_server_trace_rcpt_to_address rcpt_to_address)
+{
+ struct smtp_server_connection *conn = trans->conn;
+ const struct smtp_server_helo_data *helo_data = &conn->helo;
+ const char *host, *secstr, *rcpt_to = NULL;
+
+ if (array_count(&trans->rcpt_to) == 1) {
+ struct smtp_server_recipient *const *rcpts =
+ array_front(&trans->rcpt_to);
+
+ switch (rcpt_to_address) {
+ case SMTP_SERVER_TRACE_RCPT_TO_ADDRESS_NONE:
+ break;
+ case SMTP_SERVER_TRACE_RCPT_TO_ADDRESS_FINAL:
+ rcpt_to = smtp_address_encode(rcpts[0]->path);
+ break;
+ case SMTP_SERVER_TRACE_RCPT_TO_ADDRESS_ORIGINAL:
+ rcpt_to = smtp_address_encode(
+ smtp_server_recipient_get_original(rcpts[0]));
+ break;
+ }
+ }
+
+ /* from */
+ str_append(str, "Received: from ");
+ if (helo_data->domain_valid)
+ str_append(str, helo_data->domain);
+ else
+ str_append(str, "unknown");
+ host = "";
+ if (conn->conn.remote_ip.family != 0)
+ host = net_ip2addr(&conn->conn.remote_ip);
+ if (host[0] != '\0') {
+ str_append(str, " ([");
+ str_append(str, host);
+ str_append(str, "])");
+ }
+ /* (using) */
+ secstr = smtp_server_connection_get_security_string(conn);
+ if (secstr != NULL) {
+ str_append(str, "\r\n\t(using ");
+ str_append(str, secstr);
+ str_append(str, ")");
+ }
+ /* by, with */
+ str_append(str, "\r\n\tby ");
+ str_append(str, conn->set.hostname);
+ str_append(str, " with ");
+ str_append(str, smtp_server_connection_get_protocol_name(conn));
+ /* id */
+ str_append(str, "\r\n\tid ");
+ str_append(str, trans->id);
+ /* (envelope-from) */
+ str_append(str, "\r\n\t(envelope-from <");
+ smtp_address_write(str, trans->mail_from);
+ str_append(str, ">)");
+ /* for */
+ if (rcpt_to != NULL) {
+ str_append(str, "\r\n\tfor <");
+ str_append(str, rcpt_to);
+ str_append(str, ">");
+ }
+ str_append(str, "; ");
+ /* date */
+ str_append(str, message_date_create(trans->timestamp.tv_sec));
+ str_printfa(str, "\r\n");
+}