bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch#include "lib.h"
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch#include "llist.h"
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch#include "array.h"
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch#include "safe-memset.h"
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch#include "ioloop.h"
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch#include "net.h"
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch#include "base64.h"
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch#include "istream.h"
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch#include "ostream.h"
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch#include "ostream-dot.h"
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch#include "iostream-rawlog.h"
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch#include "iostream-ssl.h"
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch#include "str.h"
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch#include "dsasl-client.h"
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch#include "dns-lookup.h"
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch#include "smtp-syntax.h"
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch#include "smtp-reply-parser.h"
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch#include "smtp-client-private.h"
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch#include <ctype.h>
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschconst char *const smtp_client_connection_state_names[] = {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "disconnected",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "connecting",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "handshaking",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "authenticating",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "ready",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "transaction"
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch};
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic int
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_ssl_init(struct smtp_client_connection *conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const char **error_r);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic void
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_handshake(struct smtp_client_connection *conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic void
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_established(struct smtp_client_connection *conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic void
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_start_transaction(struct smtp_client_connection *conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch/*
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch * Capabilities
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschenum smtp_capability
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_get_capabilities(struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return conn->capabilities;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschuoff_t smtp_client_connection_get_size_capability(
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return conn->cap_size;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch/*
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch * Logging
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschconst char *
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmpt_client_connection_label(struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->label == NULL) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->label = i_strdup_printf("%s:%u [%u]",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->host, conn->port, conn->id);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return conn->label;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic inline void ATTR_FORMAT(2, 3)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_debug(struct smtp_client_connection *conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const char *format, ...)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch va_list args;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->set.debug) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch va_start(args, format);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_debug("%s-client: conn %s: %s",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_protocol_name(conn->protocol),
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smpt_client_connection_label(conn),
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch t_strdup_vprintf(format, args));
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch va_end(args);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic inline void ATTR_FORMAT(2, 3)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_warning(struct smtp_client_connection *conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const char *format, ...)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch va_list args;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch va_start(args, format);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_warning("%s-client: conn %s: %s",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_protocol_name(conn->protocol),
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smpt_client_connection_label(conn),
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch t_strdup_vprintf(format, args));
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch va_end(args);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic inline void ATTR_FORMAT(2, 3)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_error(struct smtp_client_connection *conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const char *format, ...)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch va_list args;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch va_start(args, format);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_error("%s-client: conn %s: %s",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_protocol_name(conn->protocol),
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smpt_client_connection_label(conn),
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch t_strdup_vprintf(format, args));
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch va_end(args);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch/*
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch *
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic void
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_commands_abort(struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
2dd20833b86743fe1e45c5af06d9c5f2549126e3Stephan Bosch smtp_client_commands_list_abort(conn->cmd_wait_list_head,
2dd20833b86743fe1e45c5af06d9c5f2549126e3Stephan Bosch conn->cmd_wait_list_count);
2dd20833b86743fe1e45c5af06d9c5f2549126e3Stephan Bosch smtp_client_commands_list_abort(conn->cmd_send_queue_head,
2dd20833b86743fe1e45c5af06d9c5f2549126e3Stephan Bosch conn->cmd_send_queue_count);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic void
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_commands_fail_reply(struct smtp_client_connection *conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const struct smtp_reply *reply)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
bd06411e6f2ffc9e0122824ba4edb774bb40c26fStephan Bosch smtp_client_commands_list_fail_reply(conn->cmd_wait_list_head,
bd06411e6f2ffc9e0122824ba4edb774bb40c26fStephan Bosch conn->cmd_wait_list_count, reply);
bd06411e6f2ffc9e0122824ba4edb774bb40c26fStephan Bosch smtp_client_commands_list_fail_reply(conn->cmd_send_queue_head,
bd06411e6f2ffc9e0122824ba4edb774bb40c26fStephan Bosch conn->cmd_send_queue_count, reply);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic void
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_commands_fail(struct smtp_client_connection *conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch unsigned int status, const char *error)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_reply reply;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_reply_init(&reply, status, error);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch reply.enhanced_code.x = 9;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_commands_fail_reply(conn, &reply);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic void
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_login_callback(struct smtp_client_connection *conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const struct smtp_reply *reply)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_command_callback_t *callback = conn->login_callback;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch void *context = conn->login_context;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->login_callback = NULL;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->login_context = NULL;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->closed)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (callback != NULL)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch callback(reply, context);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic void
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_login_fail(struct smtp_client_connection *conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch unsigned int status, const char *error)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_reply reply;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_reply_init(&reply, status, error);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch reply.enhanced_code.x = 9;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_login_callback(conn, &reply);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic void
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_set_state(struct smtp_client_connection *conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch enum smtp_client_connection_state state)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->state = state;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschvoid smtp_client_connection_cork(struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->corked = TRUE;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->conn.output != NULL)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch o_stream_cork(conn->conn.output);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschvoid smtp_client_connection_uncork(struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
326301f0d478de2d0dd4bf40dda7099ef015ee3eStephan Bosch conn->corked = FALSE;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->conn.output != NULL) {
326301f0d478de2d0dd4bf40dda7099ef015ee3eStephan Bosch if (o_stream_uncork_flush(conn->conn.output) < 0) {
326301f0d478de2d0dd4bf40dda7099ef015ee3eStephan Bosch smtp_client_connection_handle_output_error(conn);
326301f0d478de2d0dd4bf40dda7099ef015ee3eStephan Bosch return;
326301f0d478de2d0dd4bf40dda7099ef015ee3eStephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_trigger_output(conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschenum smtp_client_connection_state
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_get_state(struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return conn->state;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic void
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_command_timeout(struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_ref(conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_error(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Command timed out, disconnecting");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_fail(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_COMMAND_ERROR_TIMED_OUT,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Command timed out");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_unref(&conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschvoid smtp_client_connection_start_cmd_timeout(
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
7501ff318cd45e2b1fc63bafa713c2f0974780f3Stephan Bosch unsigned int msecs = conn->set.command_timeout_msecs;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->state != SMTP_CLIENT_CONNECTION_STATE_READY) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* pre-login uses connect timeout */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (msecs == 0) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* no timeout configured */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch timeout_remove(&conn->to_commands);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->cmd_wait_list_head == NULL &&
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->cmd_send_queue_head == NULL) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* no commands pending */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch timeout_remove(&conn->to_commands);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn, "Start timeout");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->to_commands == NULL) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->to_commands = timeout_add
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch (msecs, smtp_client_command_timeout, conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschvoid smtp_client_connection_update_cmd_timeout(
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
7501ff318cd45e2b1fc63bafa713c2f0974780f3Stephan Bosch unsigned int msecs = conn->set.command_timeout_msecs;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->state != SMTP_CLIENT_CONNECTION_STATE_READY) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* pre-login uses connect timeout */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (msecs == 0) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* no timeout configured */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch timeout_remove(&conn->to_commands);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->cmd_wait_list_head == NULL &&
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->cmd_send_queue_head == NULL) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->to_commands != NULL) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "No commands pending; stop timeout");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch timeout_remove(&conn->to_commands);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch } else if (conn->to_commands != NULL) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Reset timeout");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch timeout_reset(conn->to_commands);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch } else {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_start_cmd_timeout(conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic void
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_fail_reply(struct smtp_client_connection *conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const struct smtp_reply *reply)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_client_transaction *trans;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Connection failed: %s", smtp_reply_log(reply));
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_ref(conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_login_callback(conn, reply);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_commands_fail_reply(conn, reply);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_disconnect(conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch trans = conn->transactions_head;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch while (trans != NULL) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_client_transaction *trans_next = trans->next;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_transaction_connection_result(trans, reply);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch trans = trans_next;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_unref(&conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschvoid smtp_client_connection_fail(struct smtp_client_connection *conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch unsigned int status, const char *error)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_reply reply;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const char *text_lines[] = {error, NULL};
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_zero(&reply);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch reply.status = status;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch reply.text_lines = text_lines;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch reply.enhanced_code.x = 9;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_fail_reply(conn, &reply);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
a02b8d9eed3636518cc5ffce8d1045f9808f24e4Stephan Boschvoid smtp_client_connection_handle_output_error(
a02b8d9eed3636518cc5ffce8d1045f9808f24e4Stephan Bosch struct smtp_client_connection *conn)
a02b8d9eed3636518cc5ffce8d1045f9808f24e4Stephan Bosch{
a02b8d9eed3636518cc5ffce8d1045f9808f24e4Stephan Bosch struct ostream *output = conn->conn.output;
a02b8d9eed3636518cc5ffce8d1045f9808f24e4Stephan Bosch
a02b8d9eed3636518cc5ffce8d1045f9808f24e4Stephan Bosch if (output->stream_errno != EPIPE &&
a02b8d9eed3636518cc5ffce8d1045f9808f24e4Stephan Bosch output->stream_errno != ECONNRESET) {
a02b8d9eed3636518cc5ffce8d1045f9808f24e4Stephan Bosch smtp_client_connection_error(conn,
a02b8d9eed3636518cc5ffce8d1045f9808f24e4Stephan Bosch "Connection lost: write(%s) failed: %s",
a02b8d9eed3636518cc5ffce8d1045f9808f24e4Stephan Bosch o_stream_get_name(conn->conn.output),
a02b8d9eed3636518cc5ffce8d1045f9808f24e4Stephan Bosch o_stream_get_error(conn->conn.output));
a02b8d9eed3636518cc5ffce8d1045f9808f24e4Stephan Bosch smtp_client_connection_fail(conn,
a02b8d9eed3636518cc5ffce8d1045f9808f24e4Stephan Bosch SMTP_CLIENT_COMMAND_ERROR_CONNECTION_LOST,
a02b8d9eed3636518cc5ffce8d1045f9808f24e4Stephan Bosch "Lost connection to remote server: "
a02b8d9eed3636518cc5ffce8d1045f9808f24e4Stephan Bosch "Write failure");
a02b8d9eed3636518cc5ffce8d1045f9808f24e4Stephan Bosch } else {
a02b8d9eed3636518cc5ffce8d1045f9808f24e4Stephan Bosch smtp_client_connection_error(conn,
a02b8d9eed3636518cc5ffce8d1045f9808f24e4Stephan Bosch "Connection lost: Remote disconnected");
a02b8d9eed3636518cc5ffce8d1045f9808f24e4Stephan Bosch smtp_client_connection_fail(conn,
a02b8d9eed3636518cc5ffce8d1045f9808f24e4Stephan Bosch SMTP_CLIENT_COMMAND_ERROR_CONNECTION_LOST,
a02b8d9eed3636518cc5ffce8d1045f9808f24e4Stephan Bosch "Lost connection to remote server: "
a02b8d9eed3636518cc5ffce8d1045f9808f24e4Stephan Bosch "Remote closed connection unexpectedly");
a02b8d9eed3636518cc5ffce8d1045f9808f24e4Stephan Bosch }
a02b8d9eed3636518cc5ffce8d1045f9808f24e4Stephan Bosch}
a02b8d9eed3636518cc5ffce8d1045f9808f24e4Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic void stmp_client_connection_ready(struct smtp_client_connection *conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const struct smtp_reply *reply)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch timeout_remove(&conn->to_connect);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_set_state(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_CONNECTION_STATE_READY);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->reset_needed = FALSE;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn, "Connection ready");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_login_callback(conn, reply);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_update_cmd_timeout(conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_start_transaction(conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic void
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_clear_password(struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->set.remember_password)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->password == NULL)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch safe_memset(conn->password, 0, strlen(conn->password));
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->set.password = NULL;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->password = NULL;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic void
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_auth_cb(const struct smtp_reply *reply,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (reply->status == 334) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const unsigned char *sasl_output;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch size_t sasl_output_len, input_len;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch buffer_t *buf;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const char *error;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (reply->text_lines[1] != NULL) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_error(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Authentication failed: "
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Server returned multi-line reply: %s",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_reply_log(reply));
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_fail(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_COMMAND_ERROR_AUTH_FAILED,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Authentication protocol error");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch input_len = strlen(reply->text_lines[0]);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch buf = buffer_create_dynamic(pool_datastack_create(),
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch MAX_BASE64_DECODED_SIZE(input_len));
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (base64_decode
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch (reply->text_lines[0], input_len, NULL, buf) < 0) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_error(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Authentication failed: "
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Server sent non-base64 input for AUTH: %s",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch reply->text_lines[0]);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch } else if (dsasl_client_input(conn->sasl_client,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch buf->data, buf->used, &error) < 0) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_error(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Authentication failed: %s", error);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch } else if (dsasl_client_output(conn->sasl_client,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch &sasl_output, &sasl_output_len, &error) < 0) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_error(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Authentication failed: %s", error);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch } else {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch string_t *smtp_output = t_str_new(
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch MAX_BASE64_ENCODED_SIZE(sasl_output_len)+2);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch base64_encode(sasl_output,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch sasl_output_len, smtp_output);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch str_append(smtp_output, "\r\n");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch o_stream_nsend(conn->conn.output,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch str_data(smtp_output), str_len(smtp_output));
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_fail(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_COMMAND_ERROR_AUTH_FAILED,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Authentication failed");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if ((reply->status / 100) != 2) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_error(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Authentication failed: %s",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_reply_log(reply));
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_fail_reply(conn, reply);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_clear_password(conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->set.debug) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Authenticated successfully");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch dsasl_client_free(&conn->sasl_client);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->to_connect != NULL)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch timeout_reset(conn->to_connect);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->authenticated = TRUE;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_handshake(conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic int
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_get_sasl_mech(struct smtp_client_connection *conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const struct dsasl_client_mech **mech_r,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const char **error_r)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const struct smtp_client_settings *set = &conn->set;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const char *const *mechanisms;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (set->sasl_mech != NULL) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const char *mech = dsasl_client_mech_get_name(set->sasl_mech);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (!str_array_icase_find(conn->cap_auth_mechanisms, mech)) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch *error_r = t_strdup_printf(
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Server doesn't support `%s' SASL mechanism",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch mech);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return -1;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch *mech_r = set->sasl_mech;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return 0;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (set->sasl_mechanisms == NULL ||
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch set->sasl_mechanisms[0] == '\0') {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch *mech_r = &dsasl_client_mech_plain;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return 0;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* find one of the specified SASL mechanisms */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch mechanisms = t_strsplit_spaces(set->sasl_mechanisms, ", ");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch for (; *mechanisms != NULL; mechanisms++) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (str_array_icase_find(conn->cap_auth_mechanisms,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch *mechanisms)) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch *mech_r = dsasl_client_mech_find(*mechanisms);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (*mech_r != NULL)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return 0;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch *error_r = t_strdup_printf(
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Support for SASL mechanism `%s' is missing",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch *mechanisms);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return -1;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch *error_r = t_strdup_printf(
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Server doesn't support any of "
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "the requested SASL mechanisms: %s",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch set->sasl_mechanisms);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return -1;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic bool
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_authenticate(struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const struct smtp_client_settings *set = &conn->set;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct dsasl_client_settings sasl_set;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const struct dsasl_client_mech *sasl_mech = NULL;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_client_command *cmd;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const unsigned char *sasl_output;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch size_t sasl_output_len;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch string_t *sasl_output_base64;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const char *init_resp, *error;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->authenticated)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return TRUE;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (set->username == NULL && set->sasl_mech == NULL)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return TRUE;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if ((conn->capabilities & SMTP_CAPABILITY_AUTH) == 0) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_fail(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_COMMAND_ERROR_AUTH_FAILED,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Authentication not supported");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return FALSE;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->set.debug) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (set->master_user != NULL) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Authenticating as %s for user %s",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch set->master_user, set->username);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch } else if (set->username == NULL) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Authenticating");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch } else {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Authenticating as %s", set->username);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (smtp_client_connection_get_sasl_mech(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch &sasl_mech, &error) < 0) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_error(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Authentication failed: %s", error);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_fail(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_COMMAND_ERROR_AUTH_FAILED,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Server authentication mechanisms incompatible");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return FALSE;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_zero(&sasl_set);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (set->master_user == NULL)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch sasl_set.authid = set->username;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch else {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch sasl_set.authid = set->master_user;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch sasl_set.authzid = set->username;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch sasl_set.password = set->password;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->sasl_client = dsasl_client_new(sasl_mech, &sasl_set);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (dsasl_client_output(conn->sasl_client, &sasl_output,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch &sasl_output_len, &error) < 0) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_error(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Failed to create initial %s SASL reply: %s",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch dsasl_client_mech_get_name(sasl_mech), error);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_fail(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_COMMAND_ERROR_AUTH_FAILED,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Internal authentication failure");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return FALSE;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch sasl_output_base64 = t_str_new(
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch MAX_BASE64_ENCODED_SIZE(sasl_output_len));
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch base64_encode(sasl_output, sasl_output_len, sasl_output_base64);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* RFC 4954, Section 4:
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch If the client is transmitting an initial response of zero
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch length, it MUST instead transmit the response as a single
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch equals sign ("="). This indicates that the response is
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch present, but contains no data. */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch init_resp = (str_len(sasl_output_base64) == 0 ?
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "=" : str_c(sasl_output_base64));
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch cmd = smtp_client_command_new(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_COMMAND_FLAG_PRELOGIN,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_auth_cb, conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_command_printf(cmd, "AUTH %s %s",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch dsasl_client_mech_get_name(sasl_mech), init_resp);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_command_submit(cmd);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_set_state(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_CONNECTION_STATE_AUTHENTICATING);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return FALSE;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic void
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_xclient_cb(const struct smtp_reply *reply,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->set.debug) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Received XCLIENT handshake reply: %s",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_reply_log(reply));
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (reply->status == 421) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_fail_reply(conn, reply);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->state == SMTP_CLIENT_CONNECTION_STATE_DISCONNECTED)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->to_connect != NULL)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch timeout_reset(conn->to_connect);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_handshake(conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschvoid
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_send_xclient(struct smtp_client_connection *conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_proxy_data *xclient)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const char **xclient_args = conn->cap_xclient_args;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch unsigned int empty_len;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch string_t *str;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (!conn->set.peer_trusted)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
fbd0a83db9f2c380dd281e243ecfff40b7acfcfdStephan Bosch if ((conn->capabilities & SMTP_CAPABILITY_XCLIENT) == 0 ||
fbd0a83db9f2c380dd281e243ecfff40b7acfcfdStephan Bosch conn->cap_xclient_args == NULL)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch str = t_str_new(64);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch str_append(str, "XCLIENT");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch empty_len = str_len(str);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (xclient->source_ip.family != 0 &&
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch str_array_icase_find(xclient_args, "ADDR")) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* Older versions of Dovecot LMTP don't quite follow Postfix'
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch specification of the XCLIENT command regarding IPv6
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch addresses: the "IPV6:" prefix is omitted. For now, we
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch maintain this deviation for LMTP. Newer versions of Dovecot
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch LMTP can work with or without the prefix. */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->protocol != SMTP_PROTOCOL_LMTP &&
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch xclient->source_ip.family == AF_INET6)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch str_append(str, " ADDR=IPV6:");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch else
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch str_append(str, " ADDR=");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch str_append(str, net_ip2addr(&xclient->source_ip));
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (xclient->source_port != 0 &&
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch str_array_icase_find(xclient_args, "PORT"))
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch str_printfa(str, " PORT=%u", xclient->source_port);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (xclient->helo != NULL &&
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch str_array_icase_find(xclient_args, "HELO")) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch str_append(str, " HELO=");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_xtext_encode_cstr(str, xclient->helo);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (str_array_icase_find(xclient_args, "PROTO")) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch switch (xclient->proto) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch case SMTP_PROXY_PROTOCOL_SMTP:
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch str_printfa(str, " PROTO=SMTP");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch break;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch case SMTP_PROXY_PROTOCOL_ESMTP:
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch str_printfa(str, " PROTO=ESMTP");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch break;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch case SMTP_PROXY_PROTOCOL_LMTP:
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch str_printfa(str, " PROTO=LMTP");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch break;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch default:
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch break;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (xclient->login != NULL &&
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch str_array_icase_find(xclient_args, "LOGIN")) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch str_append(str, " LOGIN=");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_xtext_encode_cstr(str, xclient->login);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (xclient->ttl_plus_1 > 0 &&
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch str_array_icase_find(xclient_args, "TTL"))
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch str_printfa(str, " TTL=%u", xclient->ttl_plus_1-1);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (xclient->timeout_secs > 0 &&
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch str_array_icase_find(xclient_args, "TIMEOUT"))
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch str_printfa(str, " TIMEOUT=%u", xclient->timeout_secs);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (str_len(str) > empty_len) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_client_command *cmd;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch enum smtp_client_command_flags flags;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Sending XCLIENT handshake");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch flags = SMTP_CLIENT_COMMAND_FLAG_PRELOGIN |
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_COMMAND_FLAG_PRIORITY;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch cmd = smtp_client_command_new(conn, flags,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_xclient_cb, conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_command_write(cmd, str_c(str));
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_command_submit(cmd);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic bool
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_init_xclient(struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (!conn->initial_xclient_sent) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->initial_xclient_sent = TRUE;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_send_xclient(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch &conn->set.proxy_data);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return smtp_client_connection_authenticate(conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic void
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_starttls_cb(const struct smtp_reply *reply,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const char *error;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->set.debug) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Received STARTTLS reply: %s",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_reply_log(reply));
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if ((reply->status / 100) != 2) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_fail_reply(conn, reply);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (smtp_client_connection_ssl_init(conn, &error) < 0) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_fail(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_COMMAND_ERROR_CONNECT_FAILED, error);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch } else {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->to_connect != NULL)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch timeout_reset(conn->to_connect);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_handshake(conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic bool
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_starttls(struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_client_command *cmd;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->ssl_mode == SMTP_CLIENT_SSL_MODE_STARTTLS &&
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->ssl_iostream == NULL) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if ((conn->capabilities & SMTP_CAPABILITY_STARTTLS) == 0) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_error(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Requested STARTTLS, "
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "but server doesn't support it");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_fail(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_COMMAND_ERROR_CONNECT_FAILED,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "STARTTLS not supported");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return FALSE;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn, "Starting TLS");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch cmd = smtp_client_command_new(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_COMMAND_FLAG_PRELOGIN,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_starttls_cb, conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_command_write(cmd, "STARTTLS");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_command_submit(cmd);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return FALSE;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return smtp_client_connection_init_xclient(conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic void
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_handshake_cb(const struct smtp_reply *reply,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const char *const *lines;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch unsigned int j;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn, "Received handshake reply");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* check reply status */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if ((reply->status / 100) != 2) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* RFC 5321, Section 3.2:
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch For a particular connection attempt, if the server returns a
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "command not recognized" response to EHLO, the client SHOULD
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch be able to fall back and send HELO. */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->protocol == SMTP_PROTOCOL_SMTP && !conn->old_smtp &&
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch (reply->status == 500 || reply->status == 502)) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* try HELO */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->old_smtp = TRUE;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_handshake(conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* failed */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_fail_reply(conn, reply);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* reset capabilities */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch p_clear(conn->cap_pool);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->capabilities = 0;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->cap_xclient_args = NULL;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->cap_auth_mechanisms = NULL;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->cap_size = 0;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch lines = reply->text_lines;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (*lines == NULL) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_fail(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_COMMAND_ERROR_BAD_REPLY,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Invalid handshake reply");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* greeting line */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch lines++;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* capability lines */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch while (*lines != NULL) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const struct smtp_capability_name *cap = NULL;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const char *const *params;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const char *cap_name, *error;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (smtp_ehlo_line_parse(*lines,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch &cap_name, &params, &error) <= 0) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_warning(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Received invalid EHLO response line: %s",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch error);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch lines++;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch continue;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch for (j = 0; smtp_capability_names[j].name != NULL; j++) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch cap = &smtp_capability_names[j];
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (strcasecmp(cap_name, cap->name) == 0)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch break;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch cap = NULL;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (cap != NULL) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch switch (cap->capability) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch case SMTP_CAPABILITY_AUTH:
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->cap_auth_mechanisms =
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch p_strarray_dup(conn->cap_pool, params);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch break;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch case SMTP_CAPABILITY_SIZE:
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (params == NULL || *params == NULL)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch break;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (str_to_uoff(*params, &conn->cap_size) < 0) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_warning(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Received invalid SIZE capability "
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "in EHLO response line");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch cap = NULL;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch break;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch case SMTP_CAPABILITY_XCLIENT:
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->cap_xclient_args =
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch p_strarray_dup(conn->cap_pool, params);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch break;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch default:
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch break;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (cap != NULL)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->capabilities |= cap->capability;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch lines++;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn, "Received server capabilities");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->to_connect != NULL)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch timeout_reset(conn->to_connect);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (smtp_client_connection_starttls(conn)) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch stmp_client_connection_ready(conn, reply);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic void
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_handshake(struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_client_command *cmd;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch enum smtp_client_command_flags flags;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const char *command;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch flags = SMTP_CLIENT_COMMAND_FLAG_PRELOGIN |
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_COMMAND_FLAG_PRIORITY;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch switch (conn->protocol) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch case SMTP_PROTOCOL_SMTP:
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch command = (conn->old_smtp ? "HELO" : "EHLO");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch break;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch case SMTP_PROTOCOL_LMTP:
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch command = "LHLO";
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch break;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch default:
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_unreached();
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Sending %s handshake", command);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch cmd = smtp_client_command_new(conn, flags,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_handshake_cb, conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_command_write(cmd, command);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_command_write(cmd, " ");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_command_write(cmd, conn->set.my_hostname);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_command_submit(cmd);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_set_state(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_CONNECTION_STATE_HANDSHAKING);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic int
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_input_reply(struct smtp_client_connection *conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const struct smtp_reply *reply)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch int ret;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* initial greeting? */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->state == SMTP_CLIENT_CONNECTION_STATE_CONNECTING) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->set.debug) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Received greeting from server: %s",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_reply_log(reply));
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (reply->status != 220) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (smtp_reply_is_success(reply)) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_fail(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_COMMAND_ERROR_BAD_REPLY,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Received inappropriate greeting");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch } else {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_fail_reply(conn, reply);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return -1;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_handshake(conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return 1;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* unexpected reply? */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->cmd_wait_list_head == NULL) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Unexpected reply: %s", smtp_reply_log(reply));
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (reply->status ==
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_COMMAND_ERROR_CONNECTION_CLOSED) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_fail_reply(conn, reply);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch } else {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_fail(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_COMMAND_ERROR_BAD_REPLY,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Got unexpected reply");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return -1;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* command reply */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch ret = smtp_client_command_input_reply(conn->cmd_wait_list_head, reply);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->state == SMTP_CLIENT_CONNECTION_STATE_DISCONNECTED ||
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->conn.output == NULL)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return -1;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return ret;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic void smtp_client_connection_input(struct connection *_conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_client_connection *conn =
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch (struct smtp_client_connection *)_conn;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch bool enhanced_codes = ((conn->capabilities &
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CAPABILITY_ENHANCEDSTATUSCODES) != 0);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_reply *reply;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const char *error = NULL;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch int ret;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->ssl_iostream != NULL &&
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch !ssl_iostream_is_handshaked(conn->ssl_iostream)) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* finish SSL negotiation by reading from input stream */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch while ((ret=i_stream_read(conn->conn.input)) > 0 || ret == -2) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (ssl_iostream_is_handshaked(conn->ssl_iostream))
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch break;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (ret < 0) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* failed somehow */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_assert(ret != -2);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch error = t_strdup_printf(
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "SSL handshaking with %s failed: "
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "read(%s) failed: %s", _conn->name,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_stream_get_name(conn->conn.input),
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_stream_get_error(conn->conn.input));
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "connect(%s) failed: %s", _conn->name, error);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_fail(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_COMMAND_ERROR_CONNECT_FAILED, error);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (!ssl_iostream_is_handshaked(conn->ssl_iostream)) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* not finished */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_assert(ret == 0);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* ready for SMTP handshake */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_established(conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_ref(conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch o_stream_cork(conn->conn.output);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch for (;;) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->cmd_wait_list_head != NULL &&
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->cmd_wait_list_head->ehlo) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if ((ret=smtp_reply_parse_ehlo(conn->reply_parser,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch &reply, &error)) <= 0)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch break;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch } else {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if ((ret=smtp_reply_parse_next(conn->reply_parser,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch enhanced_codes, &reply, &error)) <= 0)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch break;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch T_BEGIN {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch ret = smtp_client_connection_input_reply(conn, reply);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch } T_END;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (ret < 0) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->conn.output != NULL && !conn->corked)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch o_stream_uncork(conn->conn.output);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_unref(&conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (ret < 0 || (ret == 0 && conn->conn.input->eof)) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->conn.input->stream_errno == ENOBUFS) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_fail(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_COMMAND_ERROR_BAD_REPLY,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Command reply line too long");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch } else if (conn->conn.input->stream_errno != 0) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_error(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "read(%s) failed: %s",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_stream_get_name(conn->conn.input),
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_stream_get_error(conn->conn.input));
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_fail(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_COMMAND_ERROR_CONNECTION_LOST,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Lost connection to remote server "
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "(read failure)");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch } else if (!i_stream_have_bytes_left(conn->conn.input)) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_fail(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_COMMAND_ERROR_CONNECTION_LOST,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Lost connection to remote server "
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "(disconnected in input)");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch } else {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_assert(error != NULL);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_fail(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_COMMAND_ERROR_BAD_REPLY,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch t_strdup_printf("Invalid command reply: %s",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch error));
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
326301f0d478de2d0dd4bf40dda7099ef015ee3eStephan Bosch if (ret >= 0 && conn->conn.output != NULL && !conn->corked) {
326301f0d478de2d0dd4bf40dda7099ef015ee3eStephan Bosch if (o_stream_uncork_flush(conn->conn.output) < 0)
326301f0d478de2d0dd4bf40dda7099ef015ee3eStephan Bosch smtp_client_connection_handle_output_error(conn);
326301f0d478de2d0dd4bf40dda7099ef015ee3eStephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_unref(&conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic int smtp_client_connection_output(struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch int ret;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->to_connect != NULL)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch timeout_reset(conn->to_connect);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if ((ret=o_stream_flush(conn->conn.output)) <= 0) {
a02b8d9eed3636518cc5ffce8d1045f9808f24e4Stephan Bosch if (ret < 0)
a02b8d9eed3636518cc5ffce8d1045f9808f24e4Stephan Bosch smtp_client_connection_handle_output_error(conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return ret;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_ref(conn);
ce19176ce2fcb6db22d2d2f650d16fcc3e53a868Stephan Bosch o_stream_cork(conn->conn.output);
52cb7a947c02678218c5e4d859e03aa35d54cda9Stephan Bosch if (smtp_client_command_send_more(conn) < 0)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch ret = -1;
ce19176ce2fcb6db22d2d2f650d16fcc3e53a868Stephan Bosch if (ret >= 0 && conn->conn.output != NULL && !conn->corked) {
ce19176ce2fcb6db22d2d2f650d16fcc3e53a868Stephan Bosch if (o_stream_uncork_flush(conn->conn.output) < 0)
ce19176ce2fcb6db22d2d2f650d16fcc3e53a868Stephan Bosch smtp_client_connection_handle_output_error(conn);
ce19176ce2fcb6db22d2d2f650d16fcc3e53a868Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_unref(&conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return ret;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschvoid smtp_client_connection_trigger_output(struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->conn.output != NULL)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch o_stream_set_flush_pending(conn->conn.output, TRUE);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic void smtp_client_connection_destroy(struct connection *_conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_client_connection *conn =
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch (struct smtp_client_connection *)_conn;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const char *error;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch switch (_conn->disconnect_reason) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch case CONNECTION_DISCONNECT_NOT:
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch break;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch case CONNECTION_DISCONNECT_DEINIT:
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Connection deinit");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_commands_abort(conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_close(&conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch break;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch case CONNECTION_DISCONNECT_CONNECT_TIMEOUT:
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_error(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "connect(%s) failed: Connection timed out",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch _conn->name);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_fail(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_COMMAND_ERROR_CONNECT_FAILED,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Connect timed out");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch break;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch default:
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch case CONNECTION_DISCONNECT_CONN_CLOSED:
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->connect_failed)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch break;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch error = _conn->input == NULL ? "Connection lost" :
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch t_strdup_printf("Connection lost: %s",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_stream_get_error(_conn->input));
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_fail(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_COMMAND_ERROR_CONNECTION_LOST, error);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch break;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic void
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_established(struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->to_connect != NULL)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch timeout_reset(conn->to_connect);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* set flush callback */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch o_stream_set_flush_callback(conn->conn.output,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_output, conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic int
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_ssl_handshaked(const char **error_r, void *context)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_client_connection *conn = context;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const char *error, *host = conn->host;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (ssl_iostream_check_cert_validity(conn->ssl_iostream,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch host, &error) == 0) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "SSL handshake successful");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch } else if (conn->set.ssl->allow_invalid_cert) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "SSL handshake successful, "
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "ignoring invalid certificate: %s", error);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch } else {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch *error_r = error;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return -1;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return 0;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
3da7cb6c60e6a82661c9b763383d3944a911f6a4Stephan Boschstatic void
3da7cb6c60e6a82661c9b763383d3944a911f6a4Stephan Boschsmtp_client_connection_streams_changed(struct smtp_client_connection *conn)
3da7cb6c60e6a82661c9b763383d3944a911f6a4Stephan Bosch{
3da7cb6c60e6a82661c9b763383d3944a911f6a4Stephan Bosch struct stat st;
3da7cb6c60e6a82661c9b763383d3944a911f6a4Stephan Bosch
3da7cb6c60e6a82661c9b763383d3944a911f6a4Stephan Bosch if (conn->set.rawlog_dir != NULL &&
3da7cb6c60e6a82661c9b763383d3944a911f6a4Stephan Bosch stat(conn->set.rawlog_dir, &st) == 0) {
3da7cb6c60e6a82661c9b763383d3944a911f6a4Stephan Bosch iostream_rawlog_create(conn->set.rawlog_dir,
3da7cb6c60e6a82661c9b763383d3944a911f6a4Stephan Bosch &conn->conn.input, &conn->conn.output);
3da7cb6c60e6a82661c9b763383d3944a911f6a4Stephan Bosch }
3da7cb6c60e6a82661c9b763383d3944a911f6a4Stephan Bosch
3da7cb6c60e6a82661c9b763383d3944a911f6a4Stephan Bosch if (conn->reply_parser == NULL) {
3da7cb6c60e6a82661c9b763383d3944a911f6a4Stephan Bosch conn->reply_parser = smtp_reply_parser_init(
3da7cb6c60e6a82661c9b763383d3944a911f6a4Stephan Bosch conn->conn.input, conn->set.max_reply_size);
3da7cb6c60e6a82661c9b763383d3944a911f6a4Stephan Bosch } else {
3da7cb6c60e6a82661c9b763383d3944a911f6a4Stephan Bosch smtp_reply_parser_set_stream(conn->reply_parser,
3da7cb6c60e6a82661c9b763383d3944a911f6a4Stephan Bosch conn->conn.input);
3da7cb6c60e6a82661c9b763383d3944a911f6a4Stephan Bosch }
1a145e6ad48c860a2a174996299489b6669e410bTimo Sirainen
1a145e6ad48c860a2a174996299489b6669e410bTimo Sirainen connection_streams_changed(&conn->conn);
3da7cb6c60e6a82661c9b763383d3944a911f6a4Stephan Bosch}
3da7cb6c60e6a82661c9b763383d3944a911f6a4Stephan Bosch
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Boschstatic int
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Boschsmtp_client_connection_init_ssl_ctx(struct smtp_client_connection *conn,
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch const char **error_r)
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch{
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch struct smtp_client *client = conn->client;
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch const char *error;
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch if (conn->ssl_ctx != NULL)
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch return 0;
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch if (conn->set.ssl == client->set.ssl) {
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch if (smtp_client_init_ssl_ctx(client, error_r) < 0)
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch return -1;
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch conn->ssl_ctx = client->ssl_ctx;
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch ssl_iostream_context_ref(conn->ssl_ctx);
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch return 0;
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch }
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch if (conn->set.ssl == NULL) {
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch *error_r = "Requested SSL connection, but no SSL settings given";
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch return -1;
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch }
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch if (ssl_iostream_client_context_cache_get(conn->set.ssl,
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch &conn->ssl_ctx, &error) < 0) {
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch *error_r = t_strdup_printf("Couldn't initialize SSL context: %s",
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch error);
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch return -1;
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch }
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch return 0;
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch}
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic int
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_ssl_init(struct smtp_client_connection *conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const char **error_r)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const char *error;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch if (smtp_client_connection_init_ssl_ctx(conn, &error) < 0) {
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch *error_r = t_strdup_printf(
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch "Failed to initialize SSL: %s", error);
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch return -1;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->set.debug)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn, "Starting SSL handshake");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->raw_input != conn->conn.input) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* recreate rawlog after STARTTLS */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_stream_ref(conn->raw_input);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch o_stream_ref(conn->raw_output);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_stream_destroy(&conn->conn.input);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch o_stream_destroy(&conn->conn.output);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->conn.input = conn->raw_input;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->conn.output = conn->raw_output;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b2feec56441867a6ffa2e024f97ef9c591055de1Stephan Bosch connection_input_halt(&conn->conn);
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch if (io_stream_create_ssl_client(conn->ssl_ctx,
1dd30824d32f02372cd775b3e54b3b7780c2c4ddStephan Bosch conn->host, conn->set.ssl,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch &conn->conn.input, &conn->conn.output,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch &conn->ssl_iostream, &error) < 0) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch *error_r = t_strdup_printf(
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Couldn't initialize SSL client for %s: %s",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->conn.name, error);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return -1;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b2feec56441867a6ffa2e024f97ef9c591055de1Stephan Bosch connection_input_resume(&conn->conn);
3da7cb6c60e6a82661c9b763383d3944a911f6a4Stephan Bosch smtp_client_connection_streams_changed(conn);
3da7cb6c60e6a82661c9b763383d3944a911f6a4Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch ssl_iostream_set_handshake_callback(conn->ssl_iostream,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_ssl_handshaked, conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (ssl_iostream_handshake(conn->ssl_iostream) < 0) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch *error_r = t_strdup_printf("SSL handshake to %s failed: %s",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->conn.name,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch ssl_iostream_get_last_error(conn->ssl_iostream));
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return -1;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (ssl_iostream_is_handshaked(conn->ssl_iostream)) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_established(conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch } else {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* wait for handshake to complete; connection input handler
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch does the rest by reading from the input stream */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch o_stream_set_flush_callback(conn->conn.output,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_output, conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return 0;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic void
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_delayed_connect_failure(
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn, "Delayed connect failure");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_assert(conn->to_connect != NULL);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch timeout_remove(&conn->to_connect);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_fail(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_COMMAND_ERROR_CONNECT_FAILED,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Failed to connect to remote server");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic void
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_connected(struct connection *_conn, bool success)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_client_connection *conn =
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch (struct smtp_client_connection *)_conn;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const struct smtp_client_settings *set = &conn->set;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const char *error;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (!success) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_error(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "connect(%s) failed: %m", _conn->name);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->connect_failed = TRUE;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch timeout_remove(&conn->to_connect);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->to_connect = timeout_add_short(0,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_delayed_connect_failure, conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn, "Connected");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch (void)net_set_tcp_nodelay(_conn->fd_out, TRUE);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (set->socket_send_buffer_size > 0) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (net_set_send_buffer_size(_conn->fd_out,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch set->socket_send_buffer_size) < 0)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_error("net_set_send_buffer_size(%"PRIuSIZE_T") failed: %m",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch set->socket_send_buffer_size);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (set->socket_recv_buffer_size > 0) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (net_set_recv_buffer_size(_conn->fd_in,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch set->socket_recv_buffer_size) < 0)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_error("net_set_recv_buffer_size(%"PRIuSIZE_T") failed: %m",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch set->socket_recv_buffer_size);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->raw_input = conn->conn.input;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->raw_output = conn->conn.output;
3da7cb6c60e6a82661c9b763383d3944a911f6a4Stephan Bosch smtp_client_connection_streams_changed(conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->ssl_mode == SMTP_CLIENT_SSL_MODE_IMMEDIATE) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (smtp_client_connection_ssl_init(conn, &error) < 0) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_error(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "connect(%s) failed: %s", _conn->name, error);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch timeout_remove(&conn->to_connect);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->to_connect = timeout_add_short(0,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_delayed_connect_failure,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch } else {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_established(conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_input(_conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic void
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_connect_timeout(struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch switch (conn->state) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch case SMTP_CLIENT_CONNECTION_STATE_CONNECTING:
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_error(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "connect(%s) failed: "
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Connection timed out after %u seconds",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->conn.name,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->set.connect_timeout_msecs/1000);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_fail(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_COMMAND_ERROR_CONNECT_FAILED,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Connect timed out");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch break;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch case SMTP_CLIENT_CONNECTION_STATE_HANDSHAKING:
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_error(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "SMTP handshake timed out after %u seconds",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->set.connect_timeout_msecs/1000);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_fail(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_COMMAND_ERROR_CONNECT_FAILED,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Handshake timed out");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch break;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch case SMTP_CLIENT_CONNECTION_STATE_AUTHENTICATING:
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_error(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Authentication timed out after %u seconds",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->set.connect_timeout_msecs/1000);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_fail(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_COMMAND_ERROR_AUTH_FAILED,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Authentication timed out");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch break;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch default:
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_unreached();
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic void
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_do_connect(struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch unsigned int msecs;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->connect_failed = FALSE;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->handshake_failed = FALSE;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (connection_client_connect(&conn->conn) < 0) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_connected(&conn->conn, FALSE);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* don't use connection.h timeout because we want this timeout
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch to include also the SSL handshake */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch msecs = conn->set.connect_timeout_msecs;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (msecs == 0)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch msecs = conn->set.command_timeout_msecs;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_assert(conn->to_connect == NULL);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (msecs > 0) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->to_connect = timeout_add(msecs,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_connect_timeout, conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic void
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_connect_next_ip(struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b136a381ac64c99b7341830ae664ae70c726c08fStephan Bosch const struct ip_addr *ip, *my_ip = &conn->set.my_ip;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch timeout_remove(&conn->to_connect);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->prev_connect_idx = (conn->prev_connect_idx+1) % conn->ips_count;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch ip = &conn->ips[conn->prev_connect_idx];
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b136a381ac64c99b7341830ae664ae70c726c08fStephan Bosch if (my_ip->family != 0) {
b136a381ac64c99b7341830ae664ae70c726c08fStephan Bosch smtp_client_connection_debug(conn,
b136a381ac64c99b7341830ae664ae70c726c08fStephan Bosch "Connecting to %s:%u (from %s)",
b136a381ac64c99b7341830ae664ae70c726c08fStephan Bosch net_ip2addr(ip), conn->port, net_ip2addr(my_ip));
b136a381ac64c99b7341830ae664ae70c726c08fStephan Bosch } else {
b136a381ac64c99b7341830ae664ae70c726c08fStephan Bosch smtp_client_connection_debug(conn,
b136a381ac64c99b7341830ae664ae70c726c08fStephan Bosch "Connecting to %s:%u",
b136a381ac64c99b7341830ae664ae70c726c08fStephan Bosch net_ip2addr(ip), conn->port);
b136a381ac64c99b7341830ae664ae70c726c08fStephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b136a381ac64c99b7341830ae664ae70c726c08fStephan Bosch connection_init_client_ip_from(conn->client->conn_list,
b136a381ac64c99b7341830ae664ae70c726c08fStephan Bosch &conn->conn, ip, conn->port, my_ip);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_do_connect(conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic void
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_delayed_host_lookup_failure(
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Delayed host lookup failure");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_assert(conn->to_connect != NULL);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch timeout_remove(&conn->to_connect);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_fail(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_COMMAND_ERROR_HOST_LOOKUP_FAILED,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Failed to lookup remote server");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic void
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_dns_callback(const struct dns_lookup_result *result,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->dns_lookup = NULL;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (result->ret != 0) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_error(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "dns_lookup(%s) failed: %s",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->host, result->error);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch timeout_remove(&conn->to_connect);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->to_connect = timeout_add_short(0,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_delayed_host_lookup_failure, conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "DNS lookup successful; got %d IPs",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch result->ips_count);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_assert(result->ips_count > 0);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->ips_count = result->ips_count;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->ips = i_new(struct ip_addr, conn->ips_count);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch memcpy(conn->ips, result->ips, sizeof(*conn->ips) * conn->ips_count);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->prev_connect_idx = conn->ips_count - 1;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_connect_next_ip(conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschvoid smtp_client_connection_connect(struct smtp_client_connection *conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_command_callback_t login_callback, void *login_context)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct dns_lookup_settings dns_set;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct ip_addr ip, *ips;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch unsigned int ips_count;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch int ret;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->state != SMTP_CLIENT_CONNECTION_STATE_DISCONNECTED) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_assert(login_callback == NULL);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_assert(conn->login_callback == NULL);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->login_callback = login_callback;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->login_context = login_context;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn, "Looking up IP address");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_set_state(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_CONNECTION_STATE_CONNECTING);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->ips_count == 0 &&
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch net_addr2ip(conn->host, &ip) == 0) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* IP address */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->ips_count = 1;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->ips = i_new(struct ip_addr, conn->ips_count);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->ips[0] = ip;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch } else if (conn->set.dns_client != NULL) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Performing asynchronous DNS lookup");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch (void)dns_client_lookup(conn->set.dns_client, conn->host,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_dns_callback, conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch &conn->dns_lookup);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch } else if (conn->set.dns_client_socket_path != NULL) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_zero(&dns_set);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch dns_set.dns_client_socket_path =
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->set.dns_client_socket_path;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch dns_set.timeout_msecs = conn->set.connect_timeout_msecs;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Performing asynchronous DNS lookup");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch (void)dns_lookup(conn->host, &dns_set,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_dns_callback, conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch &conn->dns_lookup);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch } else {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* no dns-conn, use blocking lookup */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch ret = net_gethostbyname(conn->host, &ips, &ips_count);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (ret != 0) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_error(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "net_gethostbyname(%s) failed: %s",
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->host, net_gethosterror(ret));
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch timeout_remove(&conn->to_connect);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->to_connect = timeout_add_short(0,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_delayed_host_lookup_failure, conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "DNS lookup successful; got %d IPs", ips_count);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->ips_count = ips_count;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->ips = i_new(struct ip_addr, ips_count);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch memcpy(conn->ips, ips, ips_count * sizeof(*ips));
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->ips_count == 0)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* always work asynchronously */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch timeout_remove(&conn->to_connect);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->to_connect = timeout_add(0,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_connect_next_ip, conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic const struct connection_settings smtp_client_connection_set = {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch .input_max_size = (size_t)-1,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch .output_max_size = (size_t)-1,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch .client = TRUE
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch};
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic const struct connection_vfuncs smtp_client_connection_vfuncs = {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch .destroy = smtp_client_connection_destroy,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch .input = smtp_client_connection_input,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch .client_connected = smtp_client_connection_connected
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch};
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstruct connection_list *
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_list_init(void)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return connection_list_init
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch (&smtp_client_connection_set, &smtp_client_connection_vfuncs);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschvoid smtp_client_connection_disconnect(struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->state == SMTP_CLIENT_CONNECTION_STATE_DISCONNECTED)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn, "Disconnected");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_clear_password(conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->conn.output != NULL && !conn->sent_quit &&
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch !conn->sending_command) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* Close the connection gracefully if possible */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch o_stream_uncork(conn->conn.output);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch o_stream_send_str(conn->conn.output, "QUIT\r\n");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->dns_lookup != NULL)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch dns_lookup_abort(&conn->dns_lookup);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch timeout_remove(&conn->to_connect);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch timeout_remove(&conn->to_trans);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch timeout_remove(&conn->to_commands);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->ssl_iostream != NULL)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch ssl_iostream_unref(&conn->ssl_iostream);
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch if (conn->ssl_ctx != NULL)
5db9891a9dee031ee3e4dfb74c95d5136c5f771aStephan Bosch ssl_iostream_context_unref(&conn->ssl_ctx);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->sasl_client != NULL)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch dsasl_client_free(&conn->sasl_client);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch o_stream_destroy(&conn->dot_output);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch connection_disconnect(&conn->conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_set_state(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_CONNECTION_STATE_DISCONNECTED);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_login_fail(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_COMMAND_ERROR_ABORTED,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Disconnected from server");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_commands_fail(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_COMMAND_ERROR_ABORTED,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Disconnected from server");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstruct smtp_client_connection *
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_create(struct smtp_client *client,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch enum smtp_protocol protocol, const char *host, in_port_t port,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch enum smtp_client_connection_ssl_mode ssl_mode,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const struct smtp_client_settings *set)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch static unsigned int id = 0;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_client_connection *conn;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch pool_t pool;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch pool = pool_alloconly_create("smtp client connection", 512);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn = p_new(pool, struct smtp_client_connection, 1);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->refcount = 1;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->pool = pool;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->client = client;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->id = id++;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->protocol = protocol;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->host = p_strdup(conn->pool, host);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->port = port;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->ssl_mode = ssl_mode;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->conn.name = p_strdup_printf(conn->pool, "%s:%u", host, port);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->set = client->set;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (set != NULL) {
b136a381ac64c99b7341830ae664ae70c726c08fStephan Bosch if (set->my_ip.family != 0)
b136a381ac64c99b7341830ae664ae70c726c08fStephan Bosch conn->set.my_ip = set->my_ip;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (set->my_hostname != NULL && *set->my_hostname != '\0')
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->set.my_hostname = p_strdup(pool, set->my_hostname);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (set->rawlog_dir != NULL && *set->rawlog_dir != '\0')
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->set.rawlog_dir = p_strdup_empty(pool, set->rawlog_dir);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (set->ssl != NULL)
68d51078686e3d0b305f91e400c29e581343aeb1Stephan Bosch conn->set.ssl = ssl_iostream_settings_dup(pool, set->ssl);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (set->master_user != NULL && *set->master_user != '\0')
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->set.master_user = p_strdup_empty(pool, set->master_user);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (set->username != NULL && *set->username != '\0')
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->set.username = p_strdup_empty(pool, set->username);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (set->password != NULL && *set->password != '\0') {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->password = p_strdup(pool, set->password);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->set.password = conn->password;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (set->sasl_mech != NULL)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->set.sasl_mech = set->sasl_mech;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch else if (set->sasl_mechanisms != NULL &&
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch *set->sasl_mechanisms != '\0') {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->set.sasl_mechanisms =
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch p_strdup(pool, set->sasl_mechanisms);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->set.remember_password = set->remember_password;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (set->command_timeout_msecs > 0)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->set.command_timeout_msecs = set->command_timeout_msecs;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (set->connect_timeout_msecs > 0)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->set.connect_timeout_msecs = set->connect_timeout_msecs;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (set->max_reply_size > 0)
c26c060e415eb0047b6eb71470ac4ec2e0736b31Stephan Bosch conn->set.max_reply_size = set->max_reply_size;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (set->max_data_chunk_size > 0)
c26c060e415eb0047b6eb71470ac4ec2e0736b31Stephan Bosch conn->set.max_data_chunk_size = set->max_data_chunk_size;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (set->max_data_chunk_pipeline > 0)
c26c060e415eb0047b6eb71470ac4ec2e0736b31Stephan Bosch conn->set.max_data_chunk_pipeline = set->max_data_chunk_pipeline;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (set->socket_send_buffer_size > 0)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->set.socket_send_buffer_size = set->socket_send_buffer_size;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (set->socket_recv_buffer_size > 0)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->set.socket_recv_buffer_size = set->socket_recv_buffer_size;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->set.debug = conn->set.debug || set->debug;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (set->proxy_data.source_ip.family != 0) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->set.proxy_data.source_ip = set->proxy_data.source_ip;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->set.proxy_data.source_port = set->proxy_data.source_port;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->set.proxy_data.ttl_plus_1 = set->proxy_data.ttl_plus_1;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->set.proxy_data.timeout_secs = set->proxy_data.timeout_secs;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->set.proxy_data.helo =
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch p_strdup_empty(pool, set->proxy_data.helo);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->set.proxy_data.login =
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch p_strdup_empty(pool, set->proxy_data.login);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->set.peer_trusted = set->peer_trusted;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_assert(conn->set.my_hostname != NULL &&
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch *conn->set.my_hostname != '\0');
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->cap_pool = pool_alloconly_create
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch ("smtp client connection capabilities", 128);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch connection_init(conn->client->conn_list, &conn->conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn, "Connection created");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return conn;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschvoid smtp_client_connection_ref(struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_assert(conn->refcount >= 0);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->refcount++;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschvoid smtp_client_connection_unref(struct smtp_client_connection **_conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_client_connection *conn = *_conn;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch *_conn = NULL;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_assert(conn->refcount > 0);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (--conn->refcount > 0)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->destroying)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->destroying = TRUE;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_clear_password(conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_disconnect(conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
a56840e419fb37d98df754153118ff6f213f8233Stephan Bosch /* could have been created while already disconnected */
a56840e419fb37d98df754153118ff6f213f8233Stephan Bosch timeout_remove(&conn->to_commands);
a56840e419fb37d98df754153118ff6f213f8233Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn, "Destroy");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->reply_parser != NULL)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_reply_parser_deinit(&conn->reply_parser);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch connection_deinit(&conn->conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_free(conn->ips);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_free(conn->label);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch pool_unref(&conn->cap_pool);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch pool_unref(&conn->pool);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschvoid smtp_client_connection_close(struct smtp_client_connection **_conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_client_connection *conn = *_conn;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch *_conn = NULL;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->closed)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->closed = TRUE;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_disconnect(conn);
a56840e419fb37d98df754153118ff6f213f8233Stephan Bosch
a56840e419fb37d98df754153118ff6f213f8233Stephan Bosch /* could have been created while already disconnected */
a56840e419fb37d98df754153118ff6f213f8233Stephan Bosch timeout_remove(&conn->to_commands);
a56840e419fb37d98df754153118ff6f213f8233Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_unref(&conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschvoid smtp_client_connection_switch_ioloop(struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_client_transaction *trans;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->to_connect != NULL)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->to_connect = io_loop_move_timeout(&conn->to_connect);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->to_trans != NULL)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->to_trans = io_loop_move_timeout(&conn->to_trans);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->to_commands != NULL)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->to_commands = io_loop_move_timeout(&conn->to_commands);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch connection_switch_ioloop(&conn->conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch trans = conn->transactions_head;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch while (trans != NULL) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_transaction_switch_ioloop(trans);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch trans = trans->next;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic void
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_rset_dummy_cb(
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch const struct smtp_reply *reply ATTR_UNUSED,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_client_connection *conn ATTR_UNUSED)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* nothing */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic void
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_reset(struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch "Submitting RSET command");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->reset_needed = FALSE;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch (void)smtp_client_command_rset_submit(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_COMMAND_FLAG_PRIORITY,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_rset_dummy_cb, conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschstatic void
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschsmtp_client_connection_start_transaction(struct smtp_client_connection *conn)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_reply reply;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch timeout_remove(&conn->to_trans);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->transactions_head == NULL)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->state != SMTP_CLIENT_CONNECTION_STATE_READY)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->reset_needed)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_reset(conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_set_state(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_CONNECTION_STATE_TRANSACTION);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_debug(conn, "Start next transaction");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_reply_init(&reply, 200, "Connection ready");
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_transaction_connection_result(
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->transactions_head, &reply);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschvoid smtp_client_connection_add_transaction(
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_client_connection *conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_client_transaction *trans)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch DLLIST2_APPEND(&conn->transactions_head,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch &conn->transactions_tail, trans);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_connect(conn, NULL, NULL);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->state == SMTP_CLIENT_CONNECTION_STATE_READY) {
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->to_trans = timeout_add_short(0,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_start_transaction, conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch }
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschvoid smtp_client_connection_abort_transaction(
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_client_connection *conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_client_transaction *trans)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch bool was_first = (trans == conn->transactions_head);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch DLLIST2_REMOVE(&conn->transactions_head,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch &conn->transactions_tail, trans);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (!was_first)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_assert(conn->state != SMTP_CLIENT_CONNECTION_STATE_READY);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->state != SMTP_CLIENT_CONNECTION_STATE_TRANSACTION)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch /* transaction messed up; protocol state needs to be reset for
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch next transaction */
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->reset_needed = TRUE;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_set_state(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_CONNECTION_STATE_READY);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->to_trans = timeout_add_short(0,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_start_transaction, conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Boschvoid smtp_client_connection_next_transaction(
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_client_connection *conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch struct smtp_client_transaction *trans)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch{
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_assert(trans == conn->transactions_head);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch DLLIST2_REMOVE(&conn->transactions_head,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch &conn->transactions_tail, trans);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch i_assert(conn->state != SMTP_CLIENT_CONNECTION_STATE_READY);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch if (conn->state != SMTP_CLIENT_CONNECTION_STATE_TRANSACTION)
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch return;
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_set_state(conn,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch SMTP_CLIENT_CONNECTION_STATE_READY);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch conn->to_trans = timeout_add_short(0,
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch smtp_client_connection_start_transaction, conn);
b3888944586654b4aa069e0db31f998e0ed8b414Stephan Bosch}