]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lmtp: Ported to use lib-smtp/server.
authorStephan Bosch <stephan.bosch@dovecot.fi>
Wed, 23 Nov 2016 09:43:53 +0000 (10:43 +0100)
committerStephan Bosch <stephan.bosch@dovecot.fi>
Fri, 8 Dec 2017 18:41:03 +0000 (19:41 +0100)
Implicitly fixes handling of multi-line replies from proxy backend.
Implicitly adds support for mixing local and proxy recipients.
Implicitly adds support for SMTP CHUNKING.
RCPT failures are reported back to the client immediately, rather than waiting for the DATA command.

TODO
src/lmtp/client.c
src/lmtp/client.h
src/lmtp/commands.c
src/lmtp/commands.h
src/lmtp/lmtp-local.c
src/lmtp/lmtp-local.h
src/lmtp/lmtp-proxy.c
src/lmtp/lmtp-proxy.h
src/lmtp/main.c
src/lmtp/main.h

diff --git a/TODO b/TODO
index f47a6393734699f38cb4a8d117008bd4656ddaa4..d3cf6745b8a4795005d0fead1e301be216d8ad90 100644 (file)
--- a/TODO
+++ b/TODO
@@ -12,8 +12,6 @@
  - quota: maybe check quota once more at commit time to make sure the whole
    transaction fits. avoids multiple parallel slow COPY commands from being
    able to go over quota
- - lmtp: Calculate incoming mail's hash, forward it via proxying, have the
-   final delivery code verify that it's correct
  - METADATA: quota, NOTIFY interaction, METADATA-SERVER capability
  - fts: if SEARCH X-MAILBOX is used on virtual/all folder, it doesn't update
    any indexes. (and it should skip those physical mailboxes that don't
@@ -95,8 +93,6 @@
  - after reading whole message text, update has_nul-state to cache
  - FIFOs maybe should be counted as connections, but unlisten should
    unlink+reopen it in master?
- - lmtp client/proxy: Handle multiline replies better
- - lmtp: support DSN extension (especially ORCPT)
  - recreate mailbox -> existing sessions log "indexid changed" error
  - add message/mime limits
  - imapc:
           multiple services can coexist independently on the same HTTP server.
         - Implement support for `Range:' requests.
     - Review compliance with RFC 7230 and RFC 7231
+
+ - lmtp:
+    - Implement parallel pipelined RCPT TO: verification (requires auth API
+      changes).
+    - Improve efficiency and security by splitting lmtp up into a protocol
+      handler and a one-user local delivery service.
+    - Fully support DSN extension (especially ORCPT)
+    - Calculate incoming mail's hash, forward it via proxying, have the
+      final delivery code verify that it's correct
index 200b1cba0b1b2d4cc64bb71da165448e10b7a0ee..a24eb4e91f5c2870d4badd8e409cf4aab7e8f8f9 100644 (file)
@@ -12,6 +12,7 @@
 #include "process-title.h"
 #include "var-expand.h"
 #include "settings-parser.h"
+#include "smtp-server.h"
 #include "master-service.h"
 #include "master-service-ssl.h"
 #include "master-service-settings.h"
 #include <unistd.h>
 
 #define CLIENT_IDLE_TIMEOUT_MSECS (1000*60*5)
-#define CLIENT_MAX_INPUT_SIZE 4096
 
 static struct client *clients = NULL;
-unsigned int clients_count = 0;
+static unsigned int clients_count = 0;
 
 static bool verbose_proctitle = FALSE;
 
+static const struct smtp_server_callbacks lmtp_callbacks;
+
 const char *client_remote_id(struct client *client)
 {
        const char *addr;
@@ -48,12 +50,11 @@ const char *client_remote_id(struct client *client)
        return addr;
 }
 
-void client_state_set(struct client *client, const char *name, const char *args)
+static void refresh_proctitle(void)
 {
+       struct client *client;
        string_t *title;
 
-       client->state.name = name;
-
        if (!verbose_proctitle)
                return;
 
@@ -64,11 +65,10 @@ void client_state_set(struct client *client, const char *name, const char *args)
                str_append(title, "idling");
                break;
        case 1:
+               client = clients;
                str_append(title, client_remote_id(client));
                str_append_c(title, ' ');
-               str_append(title, client->state.name);
-               if (args[0] != '\0')
-                       str_printfa(title, " %s", args);
+               str_append(title, client_state_get_name(client));
                break;
        default:
                str_printfa(title, "%u connections", clients_count);
@@ -78,13 +78,6 @@ void client_state_set(struct client *client, const char *name, const char *args)
        process_title_set(str_c(title));
 }
 
-static void client_idle_timeout(struct client *client)
-{
-       client_destroy(client,
-                      t_strdup_printf("421 4.4.2 %s", client->my_domain),
-                      "Disconnected client for inactivity");
-}
-
 static void client_raw_user_create(struct client *client)
 {
        void **sets;
@@ -125,71 +118,58 @@ static void client_read_settings(struct client *client)
        client->unexpanded_lda_set = lda_set;
 }
 
-unsigned int client_get_rcpt_count(struct client *client)
-{
-       return lmtp_local_rcpt_count(client) + lmtp_proxy_rcpt_count(client);
-}
-
-static void client_generate_session_id(struct client *client)
-{
-       guid_128_t guid;
-       string_t *id = t_str_new(30);
-
-       guid_128_generate(guid);
-       base64_encode(guid, sizeof(guid), id);
-       i_assert(str_c(id)[str_len(id)-2] == '=');
-       str_truncate(id, str_len(id)-2); /* drop trailing "==" */
-       client->state.session_id = p_strdup(client->state_pool, str_c(id));
-}
-
-struct client *client_create(int fd_in, int fd_out,
+struct client *client_create(int fd_in, int fd_out, bool ssl_start,
                             const struct master_service_connection *conn)
 {
+       struct smtp_server_settings lmtp_set;
        struct client *client;
        pool_t pool;
 
-       /* always use nonblocking I/O */
-       net_set_nonblock(fd_in, TRUE);
-       net_set_nonblock(fd_out, TRUE);
-
        pool = pool_alloconly_create("lmtp client", 2048);
        client = p_new(pool, struct client, 1);
        client->pool = pool;
-       client->fd_in = fd_in;
-       client->fd_out = fd_out;
        client->remote_ip = conn->remote_ip;
        client->remote_port = conn->remote_port;
        client->local_ip = conn->local_ip;
        client->local_port = conn->local_port;
 
-       client->input = i_stream_create_fd(fd_in, CLIENT_MAX_INPUT_SIZE);
-       client->output = o_stream_create_fd(fd_out, (size_t)-1);
-       o_stream_set_no_error_handling(client->output, TRUE);
-
-       client_io_reset(client);
        client->state_pool = pool_alloconly_create("client state", 4096);
        client->state.mail_data_fd = -1;
+
        client_read_settings(client);
        client_raw_user_create(client);
-       client_generate_session_id(client);
        client->my_domain = client->unexpanded_lda_set->hostname;
-       client->lhlo = i_strdup("missing");
-       client->proxy_ttl = LMTP_PROXY_DEFAULT_TTL;
 
        if (client->service_set->verbose_proctitle)
                verbose_proctitle = TRUE;
 
+       i_zero(&lmtp_set);
+       lmtp_set.capabilities =
+               SMTP_CAPABILITY_PIPELINING |
+               SMTP_CAPABILITY_ENHANCEDSTATUSCODES |
+               SMTP_CAPABILITY_8BITMIME |
+               SMTP_CAPABILITY_CHUNKING;
+       if (!ssl_start && master_service_ssl_is_enabled(master_service))
+               lmtp_set.capabilities |= SMTP_CAPABILITY_STARTTLS;
+       lmtp_set.hostname = client->unexpanded_lda_set->hostname;
+       lmtp_set.rcpt_domain_optional = TRUE;
+       lmtp_set.max_client_idle_time_msecs = CLIENT_IDLE_TIMEOUT_MSECS;
+
+       client->conn = smtp_server_connection_create
+               (lmtp_server, fd_in, fd_out,
+                       &conn->remote_ip, conn->remote_port,
+                       &lmtp_set, &lmtp_callbacks, client);
+
        DLLIST_PREPEND(&clients, client);
        clients_count++;
 
-       client_state_set(client, "banner", "");
-       client_send_line(client, "220 %s %s", client->my_domain,
-                        client->lmtp_set->login_greeting);
+       smtp_server_connection_start(client->conn, ssl_start);
        i_info("Connect from %s", client_remote_id(client));
+       refresh_proctitle();
        return client;
 }
 
-void client_state_reset(struct client *client, const char *state_name)
+void client_state_reset(struct client *client)
 {
        if (client->local != NULL)
                lmtp_local_deinit(&client->local);
@@ -203,77 +183,112 @@ void client_state_reset(struct client *client, const char *state_name)
        i_zero(&client->state);
        p_clear(client->state_pool);
        client->state.mail_data_fd = -1;
-
-       client_generate_session_id(client);
-       client_state_set(client, state_name, "");
 }
 
-void client_destroy(struct client *client, const char *prefix,
+void client_destroy(struct client *client, const char *enh_code,
                    const char *reason)
 {
-       client_disconnect(client, prefix, reason);
-       o_stream_uncork(client->output);
+       if (client->destroyed)
+               return;
+       client->destroyed = TRUE;
+
+       client_disconnect(client, enh_code, reason);
 
        clients_count--;
        DLLIST_REMOVE(&clients, client);
 
-       client_state_set(client, "destroyed", "");
-
        if (client->raw_mail_user != NULL)
                mail_user_unref(&client->raw_mail_user);
-       if (client->local != NULL)
-               lmtp_local_deinit(&client->local);
-       if (client->proxy != NULL)
-               lmtp_proxy_deinit(&client->proxy);
-       io_remove(&client->io);
-       timeout_remove(&client->to_idle);
-       if (client->ssl_iostream != NULL)
-               ssl_iostream_destroy(&client->ssl_iostream);
-       i_stream_destroy(&client->input);
-       o_stream_destroy(&client->output);
-
-       fd_close_maybe_stdio(&client->fd_in, &client->fd_out);
-       client_state_reset(client, "destroyed");
-       i_free(client->lhlo);
+
+       client_state_reset(client);
        pool_unref(&client->state_pool);
        pool_unref(&client->pool);
 
        master_service_client_connection_destroyed(master_service);
 }
 
-static const char *client_get_disconnect_reason(struct client *client)
+const char *client_state_get_name(struct client *client)
 {
-       const char *err;
-
-       if (client->ssl_iostream != NULL &&
-           !ssl_iostream_is_handshaked(client->ssl_iostream)) {
-               err = ssl_iostream_get_last_error(client->ssl_iostream);
-               if (err != NULL) {
-                       return t_strdup_printf("TLS handshaking failed: %s",
-                                              err);
-               }
-       }
-       return io_stream_get_disconnect_reason(client->input, client->output);
+       enum smtp_server_state state;
+
+       if (client->conn == NULL)
+               state = client->last_state;
+       else
+               state = smtp_server_connection_get_state(client->conn);
+       return smtp_server_state_names[state];
 }
 
-void client_disconnect(struct client *client, const char *prefix,
+void client_disconnect(struct client *client, const char *enh_code,
                       const char *reason)
 {
+       struct smtp_server_connection *conn = client->conn;
+
        if (client->disconnected)
                return;
+       client->disconnected = TRUE;
 
-       if (reason != NULL)
-               client_send_line(client, "%s %s", prefix, reason);
-       else
-               reason = client_get_disconnect_reason(client);
-       i_info("Disconnect from %s: %s (in %s)", client_remote_id(client),
-              reason, client->state.name);
+       if (reason == NULL)
+               reason = "Connection closed";
+       i_info("Disconnect from %s: %s (state = %s)", client_remote_id(client),
+              reason, client_state_get_name(client));
 
-       client->disconnected = TRUE;
+       if (conn != NULL) {
+               client->last_state = smtp_server_connection_get_state(conn);
+               smtp_server_connection_terminate(&conn,
+                       (enh_code == NULL ? "4.0.0" : enh_code), reason);
+       }
+}
+
+static void
+client_connection_trans_free(void *context,
+                            struct smtp_server_transaction *trans ATTR_UNUSED)
+{
+       struct client *client = (struct client *)context;
+
+       client_state_reset(client);
+}
+
+static void
+client_connection_state_changed(void *context ATTR_UNUSED,
+       enum smtp_server_state newstate ATTR_UNUSED)
+{
+       if (clients_count == 1)
+               refresh_proctitle();
+}
+
+static void
+client_connection_proxy_data_updated(void *context,
+                                    const struct smtp_proxy_data *data)
+{
+       struct client *client = (struct client *)context;
+
+       client->remote_ip = data->source_ip;
+       client->remote_port = data->source_port;
+
+       if (clients_count == 1)
+               refresh_proctitle();
+}
+
+static void client_connection_disconnect(void *context, const char *reason)
+{
+       struct client *client = (struct client *)context;
+       struct smtp_server_connection *conn = client->conn;
+
+       if (conn != NULL)
+               client->last_state = smtp_server_connection_get_state(conn);
+       client_disconnect(client, NULL, reason);
 }
 
-bool client_is_trusted(struct client *client)
+static void client_connection_destroy(void *context)
 {
+       struct client *client = (struct client *)context;
+
+       client_destroy(client, NULL, NULL);
+}
+
+static bool client_connection_is_trusted(void *context)
+{
+       struct client *client = (struct client *)context;
        const char *const *net;
        struct ip_addr net_ip;
        unsigned int bits;
@@ -298,133 +313,33 @@ bool client_is_trusted(struct client *client)
 void clients_destroy(void)
 {
        while (clients != NULL) {
-               client_destroy(clients,
-                       t_strdup_printf("421 4.3.2 %s", clients->my_domain),
-                       "Shutting down");
+               client_destroy(clients, "4.3.2", "Shutting down");
        }
 }
 
-/*
- * Input handling
- */
+static const struct smtp_server_callbacks lmtp_callbacks = {
+       .conn_cmd_mail = cmd_mail,
+       .conn_cmd_rcpt = cmd_rcpt,
+       .conn_cmd_data_begin = cmd_data_begin,
+       .conn_cmd_data_continue = cmd_data_continue,
 
-static int client_input_line(struct client *client, const char *line)
-{
-       const char *cmd, *args;
-
-       args = strchr(line, ' ');
-       if (args == NULL) {
-               cmd = line;
-               args = "";
-       } else {
-               cmd = t_strdup_until(line, args);
-               args++;
-       }
-       cmd = t_str_ucase(cmd);
-
-       if (strcmp(cmd, "LHLO") == 0)
-               return cmd_lhlo(client, args);
-       if (strcmp(cmd, "STARTTLS") == 0 &&
-           master_service_ssl_is_enabled(master_service))
-               return cmd_starttls(client);
-       if (strcmp(cmd, "MAIL") == 0)
-               return cmd_mail(client, args);
-       if (strcmp(cmd, "RCPT") == 0)
-               return cmd_rcpt(client, args);
-       if (strcmp(cmd, "DATA") == 0)
-               return cmd_data(client, args);
-       if (strcmp(cmd, "QUIT") == 0)
-               return cmd_quit(client, args);
-       if (strcmp(cmd, "VRFY") == 0)
-               return cmd_vrfy(client, args);
-       if (strcmp(cmd, "RSET") == 0)
-               return cmd_rset(client, args);
-       if (strcmp(cmd, "NOOP") == 0)
-               return cmd_noop(client, args);
-       if (strcmp(cmd, "XCLIENT") == 0)
-               return cmd_xclient(client, args);
-
-       client_send_line(client, "502 5.5.2 Unknown command");
-       return 0;
-}
+       .conn_trans_free = client_connection_trans_free,
 
-int client_input_read(struct client *client)
-{
-       client->last_input = ioloop_time;
-       timeout_reset(client->to_idle);
-
-       switch (i_stream_read(client->input)) {
-       case -2:
-               /* buffer full */
-               client_destroy(client, "502 5.5.2",
-                              "Disconnected: Input buffer full");
-               return -1;
-       case -1:
-               /* disconnected */
-               client_destroy(client, NULL, NULL);
-               return -1;
-       case 0:
-               /* nothing new read */
-               return 0;
-       default:
-               /* something was read */
-               return 0;
-       }
-}
+       .conn_state_changed = client_connection_state_changed,
 
-void client_input_handle(struct client *client)
-{
-       struct ostream *output;
-       const char *line;
-       int ret;
-
-       output = client->output;
-       o_stream_ref(output);
-       while ((line = i_stream_next_line(client->input)) != NULL) {
-               T_BEGIN {
-                       o_stream_cork(output);
-                       ret = client_input_line(client, line);
-                       o_stream_uncork(output);
-               } T_END;
-               if (ret < 0)
-                       break;
-       }
-       o_stream_unref(&output);
-}
+       .conn_proxy_data_updated = client_connection_proxy_data_updated,
 
-static void client_input(struct client *client)
-{
-       if (client_input_read(client) < 0)
-               return;
-       client_input_handle(client);
-}
+       .conn_disconnect = client_connection_disconnect,
+       .conn_destroy = client_connection_destroy,
 
-void client_io_reset(struct client *client)
-{
-       io_remove(&client->io);
-       timeout_remove(&client->to_idle);
-       client->io = io_add(client->fd_in, IO_READ, client_input, client);
-        client->last_input = ioloop_time;
-       client->to_idle = timeout_add(CLIENT_IDLE_TIMEOUT_MSECS,
-                                     client_idle_timeout, client);
-}
+       .conn_is_trusted = client_connection_is_trusted
+};
+
+/*
+ * Input handling
+ */
 
 /*
  * Output handling
  */
 
-void client_send_line(struct client *client, const char *fmt, ...)
-{
-       va_list args;
-
-       va_start(args, fmt);
-       T_BEGIN {
-               string_t *str;
-
-               str = t_str_new(256);
-               str_vprintfa(str, fmt, args);
-               str_append(str, "\r\n");
-               o_stream_nsend(client->output, str_data(str), str_len(str));
-       } T_END;
-       va_end(args);
-}
index 57edf3c4df319a751a9eecd7c397a0a38ba22d7a..c640266a23f3b6c655ec6b1b3cf586438ce13a9a 100644 (file)
@@ -2,35 +2,33 @@
 #define CLIENT_H
 
 #include "net.h"
-#include "smtp-params.h"
+#include "smtp-server.h"
 
 #define CLIENT_MAIL_DATA_MAX_INMEMORY_SIZE (1024*128)
 
 struct lmtp_recipient {
        struct client *client;
-       const char *session_id;
 
-       struct smtp_address *address;
-       const char *detail; /* +detail part is also in address */
-       struct smtp_params_rcpt params;
+       struct smtp_address *path;
+       struct smtp_server_cmd_ctx *rcpt_cmd;
+       struct smtp_server_recipient *rcpt;
+       unsigned int index;
 };
 
 struct client_state {
        const char *name;
-       const char *session_id;
-       struct smtp_address *mail_from;
-       struct smtp_params_mail mail_params;
+       unsigned int session_id_seq;
 
-       unsigned int data_end_idx;
+       struct timeval data_end_timeval;
 
        /* Initially we start writing to mail_data. If it grows too large,
           start using mail_data_fd. */
        buffer_t *mail_data;
        int mail_data_fd;
        struct ostream *mail_data_output;
-       const char *added_headers;
 
-       struct timeval mail_from_timeval, data_end_timeval;
+       const char *added_headers_local;
+       const char *added_headers_proxy;
 };
 
 struct client {
@@ -41,55 +39,37 @@ struct client {
        const struct lda_settings *unexpanded_lda_set;
        const struct lmtp_settings *lmtp_set;
        const struct master_service_settings *service_set;
-       int fd_in, fd_out;
-       struct io *io;
-       struct istream *input;
-       struct ostream *output;
-       struct ssl_iostream *ssl_iostream;
 
-       struct timeout *to_idle;
-       time_t last_input;
+       struct smtp_server_connection *conn;
+       enum smtp_server_state last_state;
 
        struct ip_addr remote_ip, local_ip;
        in_port_t remote_port, local_port;
 
        struct mail_user *raw_mail_user;
        const char *my_domain;
-       char *lhlo;
 
        pool_t state_pool;
        struct client_state state;
        struct istream *dot_input;
        struct lmtp_local *local;
        struct lmtp_proxy *proxy;
-       unsigned int proxy_ttl;
-       unsigned int proxy_timeout_secs;
 
        bool disconnected:1;
+       bool destroyed:1;
 };
 
-extern unsigned int clients_count;
-
-struct client *client_create(int fd_in, int fd_out,
+struct client *client_create(int fd_in, int fd_out, bool ssl_start,
                             const struct master_service_connection *conn);
-void client_destroy(struct client *client, const char *prefix,
+void client_destroy(struct client *client, const char *enh_code,
                    const char *reason) ATTR_NULL(2, 3);
-void client_disconnect(struct client *client, const char *prefix,
-                      const char *reason);
-unsigned int client_get_rcpt_count(struct client *client);
-void client_state_reset(struct client *client, const char *state_name);
-void client_state_set(struct client *client, const char *name, const char *args);
+void client_disconnect(struct client *client, const char *enh_code,
+                      const char *reason) ATTR_NULL(2, 3);
 const char *client_remote_id(struct client *client);
 
-bool client_is_trusted(struct client *client);
+const char *client_state_get_name(struct client *client);
+void client_state_reset(struct client *client);
 
 void clients_destroy(void);
 
-void client_input_handle(struct client *client);
-int client_input_read(struct client *client);
-void client_io_reset(struct client *client);
-
-void client_send_line(struct client *client, const char *fmt, ...)
-       ATTR_FORMAT(2, 3);
-
 #endif
index 7064fcfa8121bcb329a2342b8e0492d99df3e321..61245709b4ca8798b8a54698f990ed426538720d 100644 (file)
 /* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
-#include "ioloop.h"
 #include "array.h"
 #include "str.h"
 #include "istream.h"
 #include "istream-concat.h"
 #include "ostream.h"
-#include "istream-dot.h"
 #include "safe-mkstemp.h"
-#include "anvil-client.h"
-#include "master-service.h"
-#include "master-service-ssl.h"
-#include "iostream-ssl.h"
-#include "rfc822-parser.h"
-#include "message-date.h"
-#include "mail-storage-service.h"
 #include "index/raw/raw-storage.h"
+#include "master-service.h"
+#include "settings-parser.h"
 #include "lda-settings.h"
 #include "lmtp-settings.h"
+#include "smtp-address.h"
+#include "smtp-server.h"
+#include "lmtp-proxy.h"
 #include "lmtp-local.h"
 #include "mail-deliver.h"
-#include "message-address.h"
+#include "mail-error.h"
 #include "main.h"
 #include "client.h"
 #include "commands.h"
-#include "lmtp-proxy.h"
-
-#define ERRSTR_TEMP_MAILBOX_FAIL "451 4.3.0 <%s> Temporary internal error"
 
 /*
  * EHLO command
  */
 
-int cmd_lhlo(struct client *client, const char *args)
-{
-       struct rfc822_parser_context parser;
-       string_t *domain = t_str_new(128);
-       const char *p;
-       int ret = 0;
-
-       if (*args == '\0') {
-               client_send_line(client, "501 Missing hostname");
-               return 0;
-       }
-
-       /* domain / address-literal */
-       rfc822_parser_init(&parser, (const unsigned char *)args, strlen(args),
-                          NULL);
-       if (*args != '[')
-               ret = rfc822_parse_dot_atom(&parser, domain);
-       else {
-               for (p = args+1; *p != ']'; p++) {
-                       if (*p == '\\' || *p == '[')
-                               break;
-               }
-               if (strcmp(p, "]") != 0)
-                       ret = -1;
-       }
-       if (ret < 0) {
-               str_truncate(domain, 0);
-               str_append(domain, "invalid");
-       }
-
-       client_state_reset(client, "LHLO");
-       client_send_line(client, "250-%s", client->my_domain);
-       if (master_service_ssl_is_enabled(master_service) &&
-           client->ssl_iostream == NULL)
-               client_send_line(client, "250-STARTTLS");
-       if (client_is_trusted(client))
-               client_send_line(client, "250-XCLIENT ADDR PORT TTL TIMEOUT");
-       client_send_line(client, "250-8BITMIME");
-       client_send_line(client, "250-ENHANCEDSTATUSCODES");
-       client_send_line(client, "250 PIPELINING");
-
-       i_free(client->lhlo);
-       client->lhlo = i_strdup(str_c(domain));
-       client_state_set(client, "LHLO", "");
-       return 0;
-}
-
 /*
  * STARTTLS command
  */
 
-int cmd_starttls(struct client *client)
-{
-       struct ostream *plain_output = client->output;
-       const char *error;
-
-       if (client->ssl_iostream != NULL) {
-               o_stream_nsend_str(client->output,
-                                  "443 5.5.1 TLS is already active.\r\n");
-               return 0;
-       }
-
-       if (master_service_ssl_init(master_service,
-                                   &client->input, &client->output,
-                                   &client->ssl_iostream, &error) < 0) {
-               i_error("TLS initialization failed: %s", error);
-               o_stream_nsend_str(client->output,
-                       "454 4.7.0 Internal error, TLS not available.\r\n");
-               return 0;
-       }
-       o_stream_nsend_str(plain_output,
-                          "220 2.0.0 Begin TLS negotiation now.\r\n");
-       if (ssl_iostream_handshake(client->ssl_iostream) < 0) {
-               client_destroy(client, NULL, NULL);
-               return -1;
-       }
-       return 0;
-}
-
 /*
  * MAIL command
  */
 
-int cmd_mail(struct client *client, const char *args)
+int cmd_mail(void *conn_ctx,
+            struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
+            struct smtp_server_cmd_mail *data ATTR_UNUSED)
 {
-       struct smtp_address *address;
-       enum smtp_param_parse_error pperror;
-       const char *error;
-
-       if (client->state.mail_from != NULL) {
-               client_send_line(client, "503 5.5.1 MAIL already given");
-               return 0;
-       }
-
-       if (strncasecmp(args, "FROM:", 5) != 0) {
-               client_send_line(client, "501 5.5.4 Invalid parameters");
-               return 0;
-       }
-       if (smtp_address_parse_path_full(pool_datastack_create(), args + 5,
-                                        SMTP_ADDRESS_PARSE_FLAG_ALLOW_EMPTY,
-                                        &address, &error, &args) < 0) {
-               client_send_line(client, "501 5.5.4 Invalid FROM: %s", error);
-               return 0;
-       }
-       if (*args == ' ')
-               args++;
-       else if (*args != '\0') {
-               client_send_line(client, "501 5.5.4 Invalid FROM: "
-                       "Invalid character in path");
-               return 0;
-       }
-
-       /* [SP Mail-parameters] */
-       if (smtp_params_mail_parse(client->state_pool, args,
-               SMTP_CAPABILITY_8BITMIME, FALSE,
-               &client->state.mail_params, &pperror, &error) < 0) {
-               switch (pperror) {
-               case SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX:
-                       client_send_line(client, "501 5.5.4 %s", error);
-                       break;
-               case SMTP_PARAM_PARSE_ERROR_NOT_SUPPORTED:
-                       client_send_line(client, "555 5.5.4 %s", error);
-                       break;
-               default:
-                       i_unreached();
-               }
-               return 0;
-       }
-
-       client->state.mail_from =
-               smtp_address_clone(client->state_pool, address);
-       client_send_line(client, "250 2.1.0 OK");
-       client_state_set(client, "MAIL FROM",
-               smtp_address_encode(address));
+       struct client *client = (struct client *)conn_ctx;
 
        if (client->lmtp_set->lmtp_user_concurrency_limit > 0) {
                /* connect to anvil before dropping privileges */
                lmtp_anvil_init();
        }
-
-       client->state.mail_from_timeval = ioloop_timeval;
-       return 0;
+       return 1;
 }
 
 /*
  * RCPT command
  */
 
-int cmd_rcpt(struct client *client, const char *args)
+int cmd_rcpt(void *conn_ctx, struct smtp_server_cmd_ctx *cmd,
+            struct smtp_server_cmd_rcpt *data)
 {
-       struct smtp_address *address;
+       struct client *client = (struct client *)conn_ctx;
        const char *username, *detail;
-       struct smtp_params_rcpt params;
-       enum smtp_param_parse_error pperror;
-       const char *error = NULL;
        char delim = '\0';
-
-       if (client->state.mail_from == NULL) {
-               client_send_line(client, "503 5.5.1 MAIL needed first");
-               return 0;
-       }
-
-       if (strncasecmp(args, "TO:", 3) != 0) {
-               client_send_line(client, "501 5.5.4 Invalid parameters");
-               return 0;
-       }
-       if (smtp_address_parse_path_full(pool_datastack_create(), args + 3,
-                                        SMTP_ADDRESS_PARSE_FLAG_ALLOW_LOCALPART,
-                                        &address, &error, &args) < 0) {
-               client_send_line(client, "501 5.5.4 Invalid TO: %s", error);
-               return 0;
-       }
-       if (*args == ' ')
-               args++;
-       else if (*args != '\0') {
-               client_send_line(client, "501 5.5.4 Invalid TO: "
-                       "Invalid character in path");
-               return 0;
-       }
-
-       /* [SP Rcpt-parameters] */
-       if (smtp_params_rcpt_parse(client->state_pool, args,
-               SMTP_CAPABILITY_DSN, FALSE,
-               &params, &pperror, &error) < 0) {
-               switch (pperror) {
-               case SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX:
-                       client_send_line(client, "501 5.5.4 %s", error);
-                       break;
-               case SMTP_PARAM_PARSE_ERROR_NOT_SUPPORTED:
-                       client_send_line(client, "555 5.5.4 %s", error);
-                       break;
-               default:
-                       i_unreached();
-               }
-               return 0;
-       }
+       int ret;
 
        smtp_address_detail_parse_temp(
                client->unexpanded_lda_set->recipient_delimiter,
-               address, &username, &delim, &detail);
-
-       client_state_set(client, "RCPT TO",
-               smtp_address_encode(address));
-
+               data->path, &username, &delim, &detail);
        if (client->lmtp_set->lmtp_proxy) {
-               if (lmtp_proxy_rcpt(client, address, username, detail, delim,
-                                     &params) != 0)
-                       return 0;
+               /* proxied? */
+               if ((ret=lmtp_proxy_rcpt(client, cmd, data,
+                                        username, detail, delim)) != 0)
+                       return (ret < 0 ? -1 : 0);
+               /* no */
        }
 
-       return lmtp_local_rcpt(client, address, username, detail,
-                              &params);
+       /* local delivery */
+       return lmtp_local_rcpt(client, cmd, data, username, detail);
 }
 
 /*
  * QUIT command
  */
 
-int cmd_quit(struct client *client, const char *args ATTR_UNUSED)
-{
-       client_send_line(client, "221 2.0.0 OK");
-       /* don't log the (state name) for successful QUITs */
-       i_info("Disconnect from %s: Successful quit", client_remote_id(client));
-       client->disconnected = TRUE;
-       client_destroy(client, NULL, NULL);
-       return -1;
-}
-
 /*
  * VRFY command
  */
 
-int cmd_vrfy(struct client *client, const char *args ATTR_UNUSED)
-{
-       client_send_line(client, "252 2.3.3 Try RCPT instead");
-       return 0;
-}
-
 /*
  * RSET command
  */
 
-int cmd_rset(struct client *client, const char *args ATTR_UNUSED)
-{
-       client_state_reset(client, "RSET");
-       client_send_line(client, "250 2.0.0 OK");
-       return 0;
-}
-
 /*
  * NOOP command
  */
 
-int cmd_noop(struct client *client, const char *args ATTR_UNUSED)
-{
-       client_send_line(client, "250 2.0.0 OK");
-       return 0;
-}
-
 /*
  * DATA command
  */
@@ -299,96 +97,43 @@ int cmd_noop(struct client *client, const char *args ATTR_UNUSED)
 static struct istream *cmd_data_get_input(struct client *client)
 {
        struct client_state *state = &client->state;
-       struct istream *cinput, *inputs[3];
-
-       inputs[0] = i_stream_create_from_data(state->added_headers,
-                                             strlen(state->added_headers));
+       struct istream *input;
 
        if (state->mail_data_output != NULL) {
                o_stream_unref(&state->mail_data_output);
-               inputs[1] = i_stream_create_fd(state->mail_data_fd,
-                                              MAIL_READ_FULL_BLOCK_SIZE);
-               i_stream_set_init_buffer_size(inputs[1],
+               input = i_stream_create_fd(state->mail_data_fd,
+                                          MAIL_READ_FULL_BLOCK_SIZE);
+               i_stream_set_init_buffer_size(input,
                                              MAIL_READ_FULL_BLOCK_SIZE);
        } else {
-               inputs[1] = i_stream_create_from_data(state->mail_data->data,
-                                                     state->mail_data->used);
+               input = i_stream_create_from_data(state->mail_data->data,
+                                                 state->mail_data->used);
        }
-       inputs[2] = NULL;
-
-       cinput = i_stream_create_concat(inputs);
-       i_stream_set_name(cinput, "<lmtp DATA>");
-       i_stream_unref(&inputs[0]);
-       i_stream_unref(&inputs[1]);
-       return cinput;
+       return input;
 }
 
-static void client_input_data_finish(struct client *client)
+static void
+cmd_data_create_added_headers(struct client *client,
+                             struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
+                             struct smtp_server_transaction *trans)
 {
-       client_io_reset(client);
-       client_state_reset(client, "DATA finished");
-       if (i_stream_have_bytes_left(client->input))
-               client_input_handle(client);
-}
+       size_t proxy_offset = 0;
+       string_t *str;
 
-static void client_proxy_finish(void *context)
-{
-       struct client *client = context;
-
-       lmtp_proxy_deinit(&client->proxy);
-       client_input_data_finish(client);
-}
-
-static const char *client_get_added_headers(struct client *client)
-{
-       string_t *str = t_str_new(200);
-       const char *host, *rcpt_to = NULL;
+       str = t_str_new(512);
 
        /* headers for local deliveries only */
        if (client->local != NULL)
-               lmtp_local_add_headers(client->local, str);
-
-       str_printfa(str, "Received: from %s", client->lhlo);
-       host = net_ip2addr(&client->remote_ip);
-       if (host[0] != '\0')
-               str_printfa(str, " ([%s])", host);
-       str_append(str, "\r\n");
-       if (client->ssl_iostream != NULL) {
-               str_printfa(str, "\t(using %s)\r\n",
-                           ssl_iostream_get_security_string(client->ssl_iostream));
-       }
-       str_printfa(str, "\tby %s with LMTP id %s",
-                   client->my_domain, client->state.session_id);
-
-       str_append(str, "\r\n\t");
-       if (rcpt_to != NULL)
-               str_printfa(str, "for <%s>", rcpt_to);
-       str_printfa(str, "; %s\r\n", message_date_create(ioloop_time));
-       return str_c(str);
-}
+               lmtp_local_add_headers(client->local, trans, str);
 
-static void client_input_data_write(struct client *client)
-{
-       struct istream *input;
+       /* headers for local and proxied messages */
+       proxy_offset = str_len(str);
+       smtp_server_transaction_write_trace_record(str, trans);
 
-       /* stop handling client input until saving/proxying is finished */
-       timeout_remove(&client->to_idle);
-       io_remove(&client->io);
-       i_stream_destroy(&client->dot_input);
-
-       client->state.data_end_timeval = ioloop_timeval;
-
-       input = cmd_data_get_input(client);
-       if (lmtp_local_rcpt_count(client) != 0)
-               lmtp_local_data(client, input);
-       if (client->proxy != NULL) {
-               client_state_set(client, "DATA", "proxying");
-               lmtp_proxy_start(client->proxy, input,
-                                client_proxy_finish, client);
-       } else {
-               client_input_data_finish(client);
-       }
-       i_stream_unref(&input);
+       client->state.added_headers_local =
+               p_strdup(client->state_pool, str_c(str));
+       client->state.added_headers_proxy =
+               client->state.added_headers_local + proxy_offset;
 }
 
 static int
@@ -453,129 +198,112 @@ cmd_data_input_add(struct client *client,
        }
 }
 
-static void client_input_data_handle(struct client *client)
+static int
+cmd_data_finish(struct client *client,
+               struct smtp_server_cmd_ctx *cmd,
+               struct smtp_server_transaction *trans)
 {
-       struct istream *data_input = client->dot_input;
+       struct client_state *state = &client->state;
+       struct istream *input_msg, *input_local, *input_proxy;
+       struct istream *inputs[3];
+
+       client->state.data_end_timeval = ioloop_timeval;
+
+       /* finish the message */
+       input_msg = cmd_data_get_input(client);
+
+       /* formulate prepended headers for both local and proxy delivery */
+       cmd_data_create_added_headers(client, cmd, trans);
+
+       /* construct message streams for local and proxy delivery */
+       input_local = input_proxy = NULL;
+       if (client->local != NULL) {
+               inputs[0] = i_stream_create_from_data(
+                       state->added_headers_local,
+                       strlen(state->added_headers_local));
+               inputs[1] = input_msg;
+               inputs[2] = NULL;
+
+               input_local = i_stream_create_concat(inputs);
+               i_stream_set_name(input_local, "<lmtp DATA local>");
+               i_stream_unref(&inputs[0]);
+       }
+       if (client->proxy != NULL) {
+               inputs[0] = i_stream_create_from_data(
+                       state->added_headers_proxy,
+                       strlen(state->added_headers_proxy));
+               inputs[1] = input_msg;
+               inputs[2] = NULL;
+
+               input_proxy = i_stream_create_concat(inputs);
+               i_stream_set_name(input_proxy, "<lmtp DATA proxy>");
+               i_stream_unref(&inputs[0]);
+       }
+
+       i_stream_unref(&input_msg);
+
+       /* local delivery */
+       if (client->local != NULL) {
+               lmtp_local_data(client, cmd, trans, input_local);
+               i_stream_unref(&input_local);
+       }
+       /* proxy delivery */
+       if (client->proxy != NULL) {
+               lmtp_proxy_data(client, cmd, trans, input_proxy);
+               i_stream_unref(&input_proxy);
+       }
+       return 0;
+}
+
+int cmd_data_continue(void *conn_ctx, struct smtp_server_cmd_ctx *cmd,
+                     struct smtp_server_transaction *trans)
+{
+       struct client *client = (struct client *)conn_ctx;
+       struct istream *data_input = (struct istream *)trans->context;
        const unsigned char *data;
        size_t size;
        ssize_t ret;
 
+       i_assert(client->state.mail_data_output != NULL);
+
        while ((ret = i_stream_read(data_input)) > 0 || ret == -2) {
                data = i_stream_get_data(data_input, &size);
                if (cmd_data_input_add(client, data, size) < 0) {
-                       client_destroy(client, "451 4.3.0",
-                                      "Temporary internal failure");
-                       return;
+                       smtp_server_reply(cmd, 451, "4.3.0",
+                               "Temporary internal failure");
+                       return -1;
                }
+
                i_stream_skip(data_input, size);
        }
-       if (ret == 0)
-               return;
 
-       if (data_input->stream_errno != 0) {
+       if (ret == 0)
+               return 0;
+       if (ret < 0 && data_input->stream_errno != 0) {
                /* client probably disconnected */
-               client_destroy(client, NULL, NULL);
-               return;
+               return -1;
        }
 
-       /* the ending "." line was seen. begin saving the mail. */
-       client_input_data_write(client);
-}
-
-static void client_input_data(struct client *client)
-{
-       if (client_input_read(client) < 0)
-               return;
-
-       client_input_data_handle(client);
+       /* the ending "." line was seen. finish delivery. */
+       return cmd_data_finish(client, cmd, trans);
 }
 
-int cmd_data(struct client *client, const char *args ATTR_UNUSED)
+int cmd_data_begin(void *conn_ctx,
+                  struct smtp_server_cmd_ctx *cmd,
+                  struct smtp_server_transaction *trans,
+                  struct istream *data_input)
 {
-       if (client->state.mail_from == NULL) {
-               client_send_line(client, "503 5.5.1 MAIL needed first");
-               return 0;
-       }
-       if ((lmtp_local_rcpt_count(client) +
-            lmtp_proxy_rcpt_count(client)) == 0) {
-               client_send_line(client, "554 5.5.1 No valid recipients");
-               return 0;
-       }
-
-       client->state.added_headers =
-               p_strdup(client->state_pool, client_get_added_headers(client));
+       struct client *client = (struct client *)conn_ctx;
 
        i_assert(client->state.mail_data == NULL);
        client->state.mail_data = buffer_create_dynamic(default_pool, 1024*64);
 
-       i_assert(client->dot_input == NULL);
-       client->dot_input = i_stream_create_dot(client->input, TRUE);
-       client_send_line(client, "354 OK");
-       /* send the DATA reply immediately before we start handling any data */
-       o_stream_uncork(client->output);
-
-       io_remove(&client->io);
-       client_state_set(client, "DATA", "");
-       client->io = io_add(client->fd_in, IO_READ, client_input_data, client);
-       client_input_data_handle(client);
-       return -1;
+       cmd->context = (void*)client;
+
+       trans->context = (void*)data_input;
+       return 0;
 }
 
 /*
  * XCLIENT command
  */
-
-int cmd_xclient(struct client *client, const char *args)
-{
-       const char *const *tmp;
-       struct ip_addr remote_ip;
-       in_port_t remote_port = 0;
-       unsigned int ttl = UINT_MAX, timeout_secs = 0;
-       bool args_ok = TRUE;
-
-       if (!client_is_trusted(client)) {
-               client_send_line(client, "550 You are not from trusted IP");
-               return 0;
-       }
-       remote_ip.family = 0;
-       for (tmp = t_strsplit(args, " "); *tmp != NULL; tmp++) {
-               if (strncasecmp(*tmp, "ADDR=", 5) == 0) {
-                       const char *addr = *tmp + 5;
-                       bool ipv6 = FALSE;
-
-                       if (strncasecmp(addr, "IPV6:", 5) == 0) {
-                               addr += 5;
-                               ipv6 = TRUE;
-                       }
-                       if (net_addr2ip(addr, &remote_ip) < 0 ||
-                           (ipv6 && remote_ip.family != AF_INET6))
-                               args_ok = FALSE;
-               } else if (strncasecmp(*tmp, "PORT=", 5) == 0) {
-                       if (net_str2port(*tmp + 5, &remote_port) < 0)
-                               args_ok = FALSE;
-               } else if (strncasecmp(*tmp, "TTL=", 4) == 0) {
-                       if (str_to_uint(*tmp + 4, &ttl) < 0)
-                               args_ok = FALSE;
-               } else if (strncasecmp(*tmp, "TIMEOUT=", 8) == 0) {
-                       if (str_to_uint(*tmp + 8, &timeout_secs) < 0)
-                               args_ok = FALSE;
-               }
-       }
-       if (!args_ok) {
-               client_send_line(client, "501 Invalid parameters");
-               return 0;
-       }
-
-       /* args ok, set them and reset the state */
-       client_state_reset(client, "XCLIENT");
-       if (remote_ip.family != 0)
-               client->remote_ip = remote_ip;
-       if (remote_port != 0)
-               client->remote_port = remote_port;
-       if (ttl != UINT_MAX)
-               client->proxy_ttl = ttl;
-       client->proxy_timeout_secs = timeout_secs;
-       client_send_line(client, "220 %s %s", client->my_domain,
-                        client->lmtp_set->login_greeting);
-       return 0;
-}
index 56310e39fe11271985e0e2e2d79a399269755a74..f93c4953dcee37ad37768986fcbc0bd0c84baa26 100644 (file)
@@ -2,16 +2,17 @@
 #define COMMANDS_H
 
 struct client;
+struct smtp_server_cmd_ctx;
+struct smtp_server_cmd_helo;
 
-int cmd_lhlo(struct client *client, const char *args);
-int cmd_starttls(struct client *client);
-int cmd_mail(struct client *client, const char *args);
-int cmd_rcpt(struct client *client, const char *args);
-int cmd_quit(struct client *client, const char *args);
-int cmd_vrfy(struct client *client, const char *args);
-int cmd_rset(struct client *client, const char *args);
-int cmd_noop(struct client *client, const char *args);
-int cmd_data(struct client *client, const char *args);
-int cmd_xclient(struct client *client, const char *args);
+int cmd_mail(void *conn_ctx, struct smtp_server_cmd_ctx *cmd,
+       struct smtp_server_cmd_mail *data);
+int cmd_rcpt(void *conn_ctx, struct smtp_server_cmd_ctx *cmd,
+       struct smtp_server_cmd_rcpt *data);
+int cmd_data_continue(void *conn_ctx, struct smtp_server_cmd_ctx *cmd,
+       struct smtp_server_transaction *trans);
+int cmd_data_begin(void *conn_ctx,
+       struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
+       struct smtp_server_transaction *trans, struct istream *data_input);
 
 #endif
index 3414509b9c6ce10999940e9b40180a6eb91ce58a..7b969e15ff043a50455d7b8d83fd9be7ac89534b 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "lib.h"
 #include "str.h"
+#include "istream.h"
 #include "strescape.h"
 #include "array.h"
 #include "time-util.h"
 #include "mail-autoexpunge.h"
 #include "index/raw/raw-storage.h"
 #include "master-service.h"
+#include "smtp-common.h"
+#include "smtp-params.h"
 #include "smtp-address.h"
 #include "smtp-submit-settings.h"
+#include "smtp-server.h"
 #include "lda-settings.h"
 #include "lmtp-settings.h"
 #include "client.h"
 #include "main.h"
+#include "lmtp-settings.h"
 #include "lmtp-local.h"
 
-#define ERRSTR_TEMP_MAILBOX_FAIL "451 4.3.0 <%s> Temporary internal error"
-#define ERRSTR_TEMP_USERDB_FAIL_PREFIX "451 4.3.0 <%s> "
-
 struct lmtp_local_recipient {
        struct lmtp_recipient rcpt;
        char *session_id;
@@ -99,13 +101,6 @@ void lmtp_local_deinit(struct lmtp_local **_local)
  * Recipient
  */
 
-unsigned int lmtp_local_rcpt_count(struct client *client)
-{
-       if (client->local == NULL)
-               return 0;
-       return array_count(&client->local->rcpt_to);
-}
-
 static void
 lmtp_local_rcpt_anvil_disconnect(struct lmtp_local_recipient *rcpt)
 {
@@ -113,6 +108,7 @@ lmtp_local_rcpt_anvil_disconnect(struct lmtp_local_recipient *rcpt)
 
        if (!rcpt->anvil_connect_sent)
                return;
+       rcpt->anvil_connect_sent = FALSE;
 
        input = mail_storage_service_user_get_input(rcpt->service_user);
        master_service_anvil_send(master_service, t_strconcat(
@@ -120,7 +116,8 @@ lmtp_local_rcpt_anvil_disconnect(struct lmtp_local_recipient *rcpt)
                "/", input->username, "\n", NULL));
 }
 
-void lmtp_local_rcpt_deinit(struct lmtp_local_recipient *rcpt)
+static void
+lmtp_local_rcpt_deinit(struct lmtp_local_recipient *rcpt)
 {
        if (rcpt->anvil_query != NULL)
                anvil_client_query_abort(anvil, &rcpt->anvil_query);
@@ -136,23 +133,41 @@ static void
 lmtp_local_rcpt_reply_overquota(struct lmtp_local_recipient *rcpt,
                                const char *error)
 {
-       struct client *client = rcpt->rcpt.client;
+       struct smtp_address *address = rcpt->rcpt.rcpt->path;
        struct lda_settings *lda_set =
                mail_storage_service_user_get_set(rcpt->service_user)[2];
 
-       client_send_line(client, "%s <%s> %s",
-               lda_set->quota_full_tempfail ? "452 4.2.2" : "552 5.2.2",
-               smtp_address_encode(rcpt->rcpt.address), error);
+       if (lda_set->quota_full_tempfail) {
+               smtp_server_reply(rcpt->rcpt.rcpt_cmd,
+                       452, "4.2.2", "<%s> %s",
+                       smtp_address_encode(address), error);
+       } else {
+               smtp_server_reply(rcpt->rcpt.rcpt_cmd,
+                       552, "5.2.2", "<%s> %s",
+                       smtp_address_encode(address), error);
+       }
 }
 
-static void
-lmtp_local_rcpt_fail_all(struct lmtp_local *local)
+static void ATTR_FORMAT(5,6)
+lmtp_local_rcpt_fail_all(struct lmtp_local *local,
+       struct smtp_server_cmd_ctx *cmd,
+       unsigned int status, const char *enh_code,
+       const char *fmt, ...)
 {
-       struct lmtp_local_recipient *const *rcptp;
+       struct lmtp_local_recipient *const *rcpts;
+       const char *msg;
+       unsigned int count, i;
+       va_list args;
 
-       array_foreach(&local->rcpt_to, rcptp) {
-               client_send_line(local->client, ERRSTR_TEMP_MAILBOX_FAIL,
-                                smtp_address_encode((*rcptp)->rcpt.address));
+       va_start(args, fmt);
+       msg = t_strdup_vprintf(fmt, args);
+       va_end(args);
+
+       rcpts = array_get(&local->rcpt_to, &count);
+       for (i = 0; i < count; i++) {
+               smtp_server_reply_index(cmd, rcpts[i]->rcpt.index,
+                       status, enh_code, "<%s> %s",
+                       smtp_address_encode(rcpts[i]->rcpt.rcpt->path), msg);
        }
 }
 
@@ -164,7 +179,7 @@ static int
 lmtp_local_rcpt_check_quota(struct lmtp_local_recipient *rcpt)
 {
        struct client *client = rcpt->rcpt.client;
-       struct smtp_address *address = rcpt->rcpt.address;
+       struct smtp_address *address = rcpt->rcpt.path;
        struct mail_user *user;
        struct mail_namespace *ns;
        struct mailbox *box;
@@ -207,6 +222,7 @@ lmtp_local_rcpt_check_quota(struct lmtp_local_recipient *rcpt)
                                        "failed: %s",
                                        mailbox_get_vname(box),
                                        mailbox_get_last_internal_error(box, NULL));
+                               ret = -1;
                        }
                }
                mailbox_free(&box);
@@ -214,32 +230,62 @@ lmtp_local_rcpt_check_quota(struct lmtp_local_recipient *rcpt)
        }
 
        if (ret < 0) {
-               client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
-                                smtp_address_encode(address));
+               smtp_server_reply(rcpt->rcpt.rcpt_cmd,
+                       451, "4.3.0", "<%s> Temporary internal error",
+                       smtp_address_encode(address));
        }
        return ret;
 }
 
+static void lmtp_local_rcpt_finished(
+       struct smtp_server_cmd_ctx *cmd,
+       struct smtp_server_transaction *trans ATTR_UNUSED,
+       struct smtp_server_recipient *trcpt,
+       unsigned int index)
+{
+       struct lmtp_local_recipient *rcpt =
+               (struct lmtp_local_recipient *)cmd->context;
+       struct client *client = rcpt->rcpt.client;
+
+       if (!smtp_server_command_replied_success(cmd->cmd)) {
+               /* failed in RCPT command; clean up early */
+               lmtp_local_rcpt_deinit(rcpt);
+               return;
+       }
+
+       trcpt->context = (void *)rcpt;
+
+       /* add to local recipients */
+       array_append(&client->local->rcpt_to, &rcpt, 1);
+
+       rcpt->rcpt.rcpt = trcpt;
+       rcpt->rcpt.index = index;
+       rcpt->rcpt.rcpt_cmd = NULL;
+}
+
 static bool
 lmtp_local_rcpt_anvil_finish(struct lmtp_local_recipient *rcpt)
 {
-       struct client *client = rcpt->rcpt.client;
+       struct smtp_server_cmd_ctx *cmd = rcpt->rcpt.rcpt_cmd;
        int ret;
 
        if ((ret = lmtp_local_rcpt_check_quota(rcpt)) < 0) {
-               mail_storage_service_user_unref(&rcpt->service_user);
+               lmtp_local_rcpt_deinit(rcpt);
                return FALSE;
        }
-       array_append(&client->local->rcpt_to, &rcpt, 1);
-       client_send_line(client, "250 2.1.5 OK");
+
+       smtp_server_reply(cmd, 250, "2.1.5", "OK");
        return TRUE;
 }
 
 static void
 lmtp_local_rcpt_anvil_cb(const char *reply, void *context)
 {
-       struct lmtp_local_recipient *rcpt = context;
+       struct lmtp_local_recipient *rcpt =
+               (struct lmtp_local_recipient *)context;
+       struct smtp_server_cmd_ctx *cmd = rcpt->rcpt.rcpt_cmd;
        struct client *client = rcpt->rcpt.client;
+       struct smtp_address *address = rcpt->rcpt.path;
        const struct mail_storage_service_input *input;
        unsigned int parallel_count = 0;
 
@@ -251,10 +297,9 @@ lmtp_local_rcpt_anvil_cb(const char *reply, void *context)
        }
 
        if (parallel_count >= client->lmtp_set->lmtp_user_concurrency_limit) {
-               client_send_line(client, ERRSTR_TEMP_USERDB_FAIL_PREFIX
-                                "Too many concurrent deliveries for user",
-                                smtp_address_encode(rcpt->rcpt.address));
-               mail_storage_service_user_unref(&rcpt->service_user);
+               smtp_server_reply(cmd, 451, "4.3.0",
+                       "<%s> Too many concurrent deliveries for user",
+                       smtp_address_encode(address));
        } else if (lmtp_local_rcpt_anvil_finish(rcpt)) {
                rcpt->anvil_connect_sent = TRUE;
                input = mail_storage_service_user_get_input(rcpt->service_user);
@@ -262,30 +307,33 @@ lmtp_local_rcpt_anvil_cb(const char *reply, void *context)
                        "CONNECT\t", my_pid, "\t", master_service_get_name(master_service),
                        "/", input->username, "\n", NULL));
        }
-
-       client_io_reset(client);
-       client_input_handle(client);
 }
 
 int lmtp_local_rcpt(struct client *client,
-       struct smtp_address *address,
-       const char *username, const char *detail,
-       const struct smtp_params_rcpt *params)
+       struct smtp_server_cmd_ctx *cmd,
+       struct smtp_server_cmd_rcpt *data,
+       const char *username, const char *detail)
 {
+       struct smtp_server_connection *conn = cmd->conn;
+       const struct smtp_address *address = data->path;
+       struct smtp_server_transaction *trans;
        struct lmtp_local_recipient *rcpt;
        struct mail_storage_service_input input;
        struct mail_storage_service_user *service_user;
        const char *session_id, *error = NULL;
-       int ret;
+       int ret = 0;
+
+       trans = smtp_server_connection_get_transaction(conn);
+       i_assert(trans != NULL); /* MAIL command is synchronous */
 
        /* Use a unique session_id for each mail delivery. This is especially
           important for stats process to not see duplicate sessions. */
-       if (client_get_rcpt_count(client) == 0)
-               session_id = client->state.session_id;
+       client->state.session_id_seq++;
+       if (client->state.session_id_seq == 1)
+               session_id = trans->id;
        else {
-               session_id =
-                       t_strdup_printf("%s:%u", client->state.session_id,
-                                       client_get_rcpt_count(client)+1);
+               session_id = t_strdup_printf("%s:%u",
+                       trans->id, client->state.session_id_seq);
        }
 
        i_zero(&input);
@@ -299,28 +347,18 @@ int lmtp_local_rcpt(struct client *client,
 
        ret = mail_storage_service_lookup(storage_service, &input,
                                          &service_user, &error);
-
        if (ret < 0) {
                i_error("Failed to lookup user %s: %s", username, error);
-               client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
+               smtp_server_reply(cmd, 451, "4.3.0",
+                       "<%s> Temporary internal error",
                        smtp_address_encode(address));
-               return 0;
+               return -1;
        }
        if (ret == 0) {
-               client_send_line(client,
-                                "550 5.1.1 <%s> User doesn't exist: %s",
-                                smtp_address_encode(address), username);
-               return 0;
-       }
-       if (client->proxy != NULL) {
-               /* NOTE: if this restriction is ever removed, we'll also need
-                  to send different message bodies to local and proxy
-                  (with and without Return-Path: header) */
-               client_send_line(client, "451 4.3.0 <%s> "
-                       "Can't handle mixed proxy/non-proxy destinations",
-                       smtp_address_encode(address));
-               mail_storage_service_user_unref(&service_user);
-               return 0;
+               smtp_server_reply(cmd, 550, "5.1.1",
+                       "<%s> User doesn't exist: %s",
+                       smtp_address_encode(address), username);
+               return -1;
        }
 
        if (client->local == NULL)
@@ -328,15 +366,17 @@ int lmtp_local_rcpt(struct client *client,
 
        rcpt = i_new(struct lmtp_local_recipient, 1);
        rcpt->rcpt.client = client;
-       rcpt->rcpt.address = smtp_address_clone(client->state_pool, address); 
-       smtp_params_rcpt_copy(client->state_pool, &rcpt->rcpt.params, params);
+       rcpt->rcpt.path = data->path;
+       rcpt->rcpt.rcpt_cmd = cmd;
        rcpt->detail = i_strdup(detail);
        rcpt->service_user = service_user;
        rcpt->session_id = i_strdup(session_id);
 
+       cmd->context = (void*)rcpt;
+       data->hook_finished = lmtp_local_rcpt_finished;
+
        if (client->lmtp_set->lmtp_user_concurrency_limit == 0) {
                (void)lmtp_local_rcpt_anvil_finish(rcpt);
-               return 0;
        } else {
                /* NOTE: username may change as the result of the userdb
                   lookup. Look up the new one via service_user. */
@@ -345,13 +385,12 @@ int lmtp_local_rcpt(struct client *client,
                const char *query = t_strconcat("LOOKUP\t",
                        master_service_get_name(master_service),
                        "/", str_tabescape(input->username), NULL);
-               io_remove(&client->io);
                rcpt->anvil_query = anvil_client_query(anvil, query,
                        lmtp_local_rcpt_anvil_cb, rcpt);
-               /* stop processing further commands while anvil query is
-                  pending */
-               return rcpt->anvil_query == NULL ? 0 : -1;
+               return 0;
        }
+
+       return 1;
 }
 
 /*
@@ -359,9 +398,9 @@ int lmtp_local_rcpt(struct client *client,
  */
 
 void lmtp_local_add_headers(struct lmtp_local *local,
+                           struct smtp_server_transaction *trans,
                            string_t *headers)
 {
-       struct client *client = local->client;
        struct lmtp_local_recipient *const *rcpts;
        const struct lmtp_settings *lmtp_set;
        const struct smtp_address *rcpt_to = NULL;
@@ -369,7 +408,7 @@ void lmtp_local_add_headers(struct lmtp_local *local,
        void **sets;
 
        str_printfa(headers, "Return-Path: <%s>\r\n",
-                   smtp_address_encode(client->state.mail_from));
+                   smtp_address_encode(trans->mail_from));
 
        rcpts = array_get(&local->rcpt_to, &count);
        if (count == 1) {
@@ -380,10 +419,10 @@ void lmtp_local_add_headers(struct lmtp_local *local,
                case LMTP_HDR_DELIVERY_ADDRESS_NONE:
                        break;
                case LMTP_HDR_DELIVERY_ADDRESS_FINAL:
-                       rcpt_to = rcpts[0]->rcpt.address;
+                       rcpt_to = rcpts[0]->rcpt.rcpt->path;
                        break;
                case LMTP_HDR_DELIVERY_ADDRESS_ORIGINAL:
-                       rcpt_to = rcpts[0]->rcpt.params.orcpt.addr;
+                       rcpt_to = rcpts[0]->rcpt.rcpt->params.orcpt.addr;
                        break;
                }
        }
@@ -395,11 +434,17 @@ void lmtp_local_add_headers(struct lmtp_local *local,
 
 static int
 lmtp_local_deliver(struct lmtp_local *local,
+                  struct smtp_server_cmd_ctx *cmd,
+                  struct smtp_server_transaction *trans,
                   struct lmtp_local_recipient *rcpt,
                   struct mail *src_mail,
                   struct mail_deliver_session *session)
 {
        struct client *client = local->client;
+       struct smtp_address *rcpt_to = rcpt->rcpt.rcpt->path;
+       unsigned int rcpt_idx = rcpt->rcpt.index;
+       const struct smtp_server_recipient *trcpt =
+               *array_idx(&trans->rcpt_to, rcpt_idx);
        struct mail_storage_service_user *service_user = rcpt->service_user;
        struct mail_deliver_context dctx;
        struct mail_user *rcpt_user;
@@ -407,6 +452,7 @@ lmtp_local_deliver(struct lmtp_local *local,
        const struct mail_storage_service_input *input;
        const struct mail_storage_settings *mail_set;
        struct smtp_submit_settings *smtp_set;
+       struct smtp_proxy_data proxy_data;
        struct lda_settings *lda_set;
        struct mail_namespace *ns;
        struct setting_parser_context *set_parser;
@@ -423,16 +469,19 @@ lmtp_local_deliver(struct lmtp_local *local,
 
        mail_set = mail_storage_service_user_get_mail_set(service_user);
        set_parser = mail_storage_service_user_get_settings_parser(service_user);
-       if (client->proxy_timeout_secs > 0 &&
+
+       smtp_server_connection_get_proxy_data
+               (client->conn, &proxy_data);
+       if (proxy_data.timeout_secs > 0 &&
            (mail_set->mail_max_lock_timeout == 0 ||
-            mail_set->mail_max_lock_timeout > client->proxy_timeout_secs)) {
+            mail_set->mail_max_lock_timeout > proxy_data.timeout_secs)) {
                /* set lock timeout waits to be less than when proxy has
                   advertised that it's going to timeout the connection.
                   this avoids duplicate deliveries in case the delivery
                   succeeds after the proxy has already disconnected from us. */
                line = t_strdup_printf("mail_max_lock_timeout=%us",
-                                      client->proxy_timeout_secs <= 1 ? 1 :
-                                      client->proxy_timeout_secs-1);
+                                      proxy_data.timeout_secs <= 1 ? 1 :
+                                      proxy_data.timeout_secs-1);
                if (settings_parse_line(set_parser, line) < 0)
                        i_unreached();
        }
@@ -441,13 +490,13 @@ lmtp_local_deliver(struct lmtp_local *local,
        io_loop_time_refresh();
        delivery_time_started = ioloop_timeval;
 
-       client_state_set(client, "DATA", username);
        i_set_failure_prefix("lmtp(%s, %s): ", my_pid, username);
        if (mail_storage_service_next(storage_service, service_user,
                                      &rcpt_user, &error) < 0) {
                i_error("Failed to initialize user: %s", error);
-               client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
-                                smtp_address_encode(rcpt->rcpt.address));
+               smtp_server_reply_index(cmd, rcpt_idx, 451, "4.3.0",
+                       "<%s> Temporary internal error",
+                       smtp_address_encode(rcpt_to));
                return -1;
        }
        local->rcpt_user = rcpt_user;
@@ -468,8 +517,9 @@ lmtp_local_deliver(struct lmtp_local *local,
        }
        if (ret <= 0) {
                i_error("Failed to expand settings: %s", error);
-               client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
-                                smtp_address_encode(rcpt->rcpt.address));
+               smtp_server_reply_index(cmd, rcpt_idx, 451, "4.3.0",
+                       "<%s> Temporary internal error",
+                       smtp_address_encode(rcpt_to));
                return -1;
        }
 
@@ -479,8 +529,9 @@ lmtp_local_deliver(struct lmtp_local *local,
                                  rcpt_user, &error) <= 0) {
                i_error("Failed to expand mail_log_prefix=%s: %s",
                        rcpt_user->set->mail_log_prefix, error);
-               client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
-                                smtp_address_encode(rcpt->rcpt.address));
+               smtp_server_reply_index(cmd, rcpt_idx, 451, "4.3.0",
+                       "<%s> Temporary internal error",
+                       smtp_address_encode(rcpt_to));
                return -1;
        }
        i_set_failure_prefix("%s", str_c(str));
@@ -494,21 +545,23 @@ lmtp_local_deliver(struct lmtp_local *local,
        dctx.src_mail = src_mail;
 
        /* MAIL FROM */
-       dctx.mail_from = client->state.mail_from;
-       dctx.mail_params = client->state.mail_params;
+       dctx.mail_from = trans->mail_from;
+       smtp_params_mail_copy(dctx.pool,
+               &dctx.mail_params, &trans->params);
 
        /* RCPT TO */
        dctx.rcpt_user = rcpt_user;
-       dctx.rcpt_params = rcpt->rcpt.params;
-       if (dctx.rcpt_params.orcpt.addr != NULL) {
-               /* used ORCPT */
-       } else if (*dctx.set->lda_original_recipient_header != '\0') {
-               dctx.rcpt_params.orcpt.addr = mail_deliver_get_address(src_mail,
+       smtp_params_rcpt_copy(dctx.pool,
+               &dctx.rcpt_params, &trcpt->params);
+       if (dctx.rcpt_params.orcpt.addr == NULL &&
+               *dctx.set->lda_original_recipient_header != '\0') {
+               dctx.rcpt_params.orcpt.addr =
+                       mail_deliver_get_address(src_mail,
                                dctx.set->lda_original_recipient_header);
        }
        if (dctx.rcpt_params.orcpt.addr == NULL)
-               dctx.rcpt_params.orcpt.addr = rcpt->rcpt.address;
-       dctx.rcpt_to = rcpt->rcpt.address;
+               dctx.rcpt_params.orcpt.addr = rcpt_to;
+       dctx.rcpt_to = rcpt_to;
        if (*rcpt->detail == '\0' ||
            !client->lmtp_set->lmtp_save_to_detail_mailbox)
                dctx.rcpt_default_mailbox = "INBOX";
@@ -518,12 +571,12 @@ lmtp_local_deliver(struct lmtp_local *local,
                        t_strconcat(ns->prefix, rcpt->detail, NULL);
        }
 
-       dctx.save_dest_mail = array_count(&local->rcpt_to) > 1 &&
+       dctx.save_dest_mail = array_count(&trans->rcpt_to) > 1 &&
                local->first_saved_mail == NULL;
 
        dctx.session_time_msecs =
                timeval_diff_msecs(&client->state.data_end_timeval,
-                                  &client->state.mail_from_timeval);
+                                  &trans->timestamp);
        dctx.delivery_time_started = delivery_time_started;
 
        if (mail_deliver(&dctx, &storage) == 0) {
@@ -531,39 +584,44 @@ lmtp_local_deliver(struct lmtp_local *local,
                        i_assert(local->first_saved_mail == NULL);
                        local->first_saved_mail = dctx.dest_mail;
                }
-               client_send_line(client, "250 2.0.0 <%s> %s Saved",
-                                smtp_address_encode(rcpt->rcpt.address),
-                                rcpt->session_id);
+               smtp_server_reply_index(cmd, rcpt_idx,
+                       250, "2.0.0", "<%s> %s Saved",
+                       smtp_address_encode(rcpt_to), rcpt->session_id);
                ret = 0;
        } else if (dctx.tempfail_error != NULL) {
-               client_send_line(client, "451 4.2.0 <%s> %s",
-                                smtp_address_encode(rcpt->rcpt.address),
-                                dctx.tempfail_error);
+               smtp_server_reply_index(cmd, rcpt_idx,
+                       451, "4.2.0", "<%s> %s",
+                       smtp_address_encode(rcpt_to),
+                       dctx.tempfail_error);
                ret = -1;
        } else if (storage != NULL) {
                error = mail_storage_get_last_error(storage, &mail_error);
                if (mail_error == MAIL_ERROR_NOQUOTA) {
                        lmtp_local_rcpt_reply_overquota(rcpt, error);
                } else {
-                       client_send_line(client, "451 4.2.0 <%s> %s",
-                                        smtp_address_encode(rcpt->rcpt.address), error);
+                       smtp_server_reply_index(cmd, rcpt_idx,
+                               451, "4.2.0", "<%s> %s",
+                               smtp_address_encode(rcpt_to), error);
                }
                ret = -1;
        } else {
                /* This shouldn't happen */
                i_error("BUG: Saving failed to unknown storage");
-               client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
-                                smtp_address_encode(rcpt->rcpt.address));
+               smtp_server_reply_index(cmd, rcpt_idx, 451, "4.3.0",
+                       "<%s> Temporary internal error",
+                       smtp_address_encode(rcpt_to));
                ret = -1;
        }
+       lmtp_local_rcpt_anvil_disconnect(rcpt);
        return ret;
 }
 
 static uid_t
 lmtp_local_deliver_to_rcpts(struct lmtp_local *local,
+                           struct smtp_server_cmd_ctx *cmd,
+                           struct smtp_server_transaction *trans,
                            struct mail_deliver_session *session)
 {
-       struct client *client = local->client;
        uid_t first_uid = (uid_t)-1;
        struct mail *src_mail;
        struct lmtp_local_recipient *const *rcpts;
@@ -571,14 +629,12 @@ lmtp_local_deliver_to_rcpts(struct lmtp_local *local,
        int ret;
 
        src_mail = local->raw_mail;
-
        rcpts = array_get(&local->rcpt_to, &count);
        for (i = 0; i < count; i++) {
                struct lmtp_local_recipient *rcpt = rcpts[i];
 
-               ret = lmtp_local_deliver(local, rcpt,
-                                    src_mail, session);
-               client_state_set(client, "DATA", "");
+               ret = lmtp_local_deliver(local, cmd,
+                       trans, rcpt, src_mail, session);
                i_set_failure_prefix("lmtp(%s): ", my_pid);
 
                /* succeeded and mail_user is not saved in first_saved_mail */
@@ -606,6 +662,8 @@ lmtp_local_deliver_to_rcpts(struct lmtp_local *local,
 
 static int
 lmtp_local_open_raw_mail(struct lmtp_local *local,
+                        struct smtp_server_cmd_ctx *cmd,
+                        struct smtp_server_transaction *trans,
                         struct istream *input)
 {
        static const char *wanted_headers[] = {
@@ -619,12 +677,13 @@ lmtp_local_open_raw_mail(struct lmtp_local *local,
        enum mail_error error;
 
        if (raw_mailbox_alloc_stream(client->raw_mail_user, input,
-                                    (time_t)-1, smtp_address_encode(client->state.mail_from),
+                                    (time_t)-1, smtp_address_encode(trans->mail_from),
                                     &box) < 0) {
                i_error("Can't open delivery mail as raw: %s",
                        mailbox_get_last_internal_error(box, &error));
                mailbox_free(&box);
-               lmtp_local_rcpt_fail_all(local);
+               lmtp_local_rcpt_fail_all(local, cmd,
+                       451, "4.3.0", "Temporary internal error");
                return -1;
        }
 
@@ -637,18 +696,21 @@ lmtp_local_open_raw_mail(struct lmtp_local *local,
        return 0;
 }
 
-void lmtp_local_data(struct client *client, struct istream *input)
+void lmtp_local_data(struct client *client,
+                    struct smtp_server_cmd_ctx *cmd,
+                    struct smtp_server_transaction *trans,
+                    struct istream *input)
 {
        struct lmtp_local *local = client->local;
        struct mail_deliver_session *session;
        uid_t old_uid, first_uid;
 
-       if (lmtp_local_open_raw_mail(local, input) < 0)
+       if (lmtp_local_open_raw_mail(local, cmd, trans, input) < 0)
                return;
 
        session = mail_deliver_session_init();
        old_uid = geteuid();
-       first_uid = lmtp_local_deliver_to_rcpts(local, session);
+       first_uid = lmtp_local_deliver_to_rcpts(local, cmd, trans, session);
        mail_deliver_session_deinit(&session);
 
        if (local->first_saved_mail != NULL) {
index 66bc7da7b2bb21130f0669bb6b52777eccf310f1..edd6156f7f9c3977e3faa6d425566d266ac9137a 100644 (file)
@@ -1,24 +1,28 @@
 #ifndef LMTP_LOCAL_H
 #define LMTP_LOCAL_H
 
-struct smtp_address;
-struct smtp_params_rcpt;
-struct lmtp_recipient;
+#include "net.h"
+
+struct mail_deliver_session;
+struct smtp_server_cmd_ctx;
+struct smtp_server_cmd_rcpt;
 struct lmtp_local;
 struct client;
 
-unsigned int lmtp_local_rcpt_count(struct client *client);
-
 void lmtp_local_deinit(struct lmtp_local **_local);
 
 int lmtp_local_rcpt(struct client *client,
-       struct smtp_address *address,
-       const char *username, const char *detail,
-       const struct smtp_params_rcpt *params);
+                   struct smtp_server_cmd_ctx *cmd,
+                   struct smtp_server_cmd_rcpt *data,
+                   const char *username, const char *detail);
 
 void lmtp_local_add_headers(struct lmtp_local *local,
+                           struct smtp_server_transaction *trans,
                            string_t *headers);
 
-void lmtp_local_data(struct client *client, struct istream *input);
+void lmtp_local_data(struct client *client,
+                    struct smtp_server_cmd_ctx *cmd,
+                    struct smtp_server_transaction *trans,
+                    struct istream *input);
 
 #endif
index 21aad788ded2cab8aeb91a39e169128a9014d646..82cf7953b441396b746be4bbcbb675e9f9c163da 100644 (file)
@@ -8,24 +8,26 @@
 #include "ostream.h"
 #include "str.h"
 #include "time-util.h"
-#include "smtp-reply.h"
+#include "smtp-common.h"
+#include "smtp-params.h"
+#include "smtp-address.h"
+#include "smtp-server.h"
 #include "smtp-client.h"
 #include "smtp-client-connection.h"
 #include "smtp-client-transaction.h"
 #include "auth-master.h"
+#include "settings-parser.h"
 #include "master-service.h"
 #include "mail-storage-service.h"
+#include "lda-settings.h"
 #include "client.h"
 #include "main.h"
+#include "lmtp-settings.h"
 #include "lmtp-proxy.h"
 
-#define LMTP_MAX_LINE_LEN 1024
+#define LMTP_MAX_REPLY_SIZE 4096
 #define LMTP_PROXY_DEFAULT_TIMEOUT_MSECS (1000*125)
 
-#define ERRSTR_TEMP_USERDB_FAIL_PREFIX "451 4.3.0 <%s> "
-#define ERRSTR_TEMP_USERDB_FAIL \
-       ERRSTR_TEMP_USERDB_FAIL_PREFIX "Temporary user lookup failure"
-
 struct lmtp_proxy_rcpt_settings {
        enum smtp_protocol protocol;
        const char *host;
@@ -36,10 +38,8 @@ struct lmtp_proxy_rcpt_settings {
 };
 
 struct lmtp_proxy_recipient {
+       struct lmtp_recipient rcpt;
        struct lmtp_proxy_connection *conn;
-       struct smtp_address *address;
-       char *reply;
-       unsigned int idx;
 
        bool rcpt_to_failed:1;
        bool data_reply_received:1;
@@ -74,13 +74,11 @@ struct lmtp_proxy {
 
        unsigned int max_timeout_msecs;
 
-       lmtp_proxy_finish_callback_t *finish_callback;
-       void *finish_context;
+       struct smtp_server_cmd_ctx *pending_data_cmd;
 
        bool finished:1;
 };
 
-static void lmtp_proxy_try_finish(struct lmtp_proxy *proxy);
 static void
 lmtp_proxy_data_cb(const struct smtp_reply *reply,
                   struct lmtp_proxy_recipient *rcpt);
@@ -90,29 +88,32 @@ lmtp_proxy_data_cb(const struct smtp_reply *reply,
  */
 
 static struct lmtp_proxy *
-lmtp_proxy_init(struct client *client)
+lmtp_proxy_init(struct client *client,
+               struct smtp_server_transaction *trans)
 {
        struct smtp_client_settings lmtp_set;
        struct lmtp_proxy *proxy;
 
-       i_assert(client->proxy_ttl > 1);
-
        proxy = i_new(struct lmtp_proxy, 1);
        proxy->client = client;
+       proxy->trans = trans;
        i_array_init(&proxy->rcpt_to, 32);
        i_array_init(&proxy->connections, 32);
 
        i_zero(&lmtp_set);
        lmtp_set.my_hostname = client->my_domain;
        lmtp_set.dns_client_socket_path = dns_client_socket_path;
+       lmtp_set.max_reply_size = LMTP_MAX_REPLY_SIZE;
 
+       smtp_server_connection_get_proxy_data(client->conn,
+                                             &lmtp_set.proxy_data);
        lmtp_set.proxy_data.source_ip = client->remote_ip;
        lmtp_set.proxy_data.source_port = client->remote_port;
-       lmtp_set.proxy_data.ttl_plus_1 = client->proxy_ttl + 1;
        if (lmtp_set.proxy_data.ttl_plus_1 == 0)
                lmtp_set.proxy_data.ttl_plus_1 = LMTP_PROXY_DEFAULT_TTL + 1;
        else
                lmtp_set.proxy_data.ttl_plus_1--;
+       lmtp_set.peer_trusted = TRUE;
 
        proxy->lmtp_client = smtp_client_init(&lmtp_set);
 
@@ -122,8 +123,6 @@ lmtp_proxy_init(struct client *client)
 static void
 lmtp_proxy_recipient_deinit(struct lmtp_proxy_recipient *rcpt)
 {
-       i_free(rcpt->address);
-       i_free(rcpt->reply);
        i_free(rcpt);
 }
 
@@ -170,20 +169,16 @@ lmtp_proxy_mail_cb(const struct smtp_reply *proxy_reply ATTR_UNUSED,
 static void
 lmtp_proxy_connection_finish(struct lmtp_proxy_connection *conn)
 {
-       struct lmtp_proxy *proxy = conn->proxy;
-
        conn->finished = TRUE;
        conn->lmtp_trans = NULL;
-
-       lmtp_proxy_try_finish(proxy);
 }
 
 static struct lmtp_proxy_connection *
 lmtp_proxy_get_connection(struct lmtp_proxy *proxy,
                          const struct lmtp_proxy_rcpt_settings *set)
 {
-       struct client *client = proxy->client;
        struct smtp_client_connection *lmtp_conn;
+       struct smtp_server_transaction *trans = proxy->trans;
        struct lmtp_proxy_connection *const *conns, *conn;
        const char *host = (set->hostip.family == 0 ?
                set->host : net_ip2addr(&set->hostip));
@@ -215,7 +210,7 @@ lmtp_proxy_get_connection(struct lmtp_proxy *proxy,
        smtp_client_connection_connect(lmtp_conn, NULL, NULL);
 
        conn->lmtp_trans = smtp_client_transaction_create(lmtp_conn,
-               client->state.mail_from, &client->state.mail_params,
+               trans->mail_from, &trans->params,
                lmtp_proxy_connection_finish, conn);
        smtp_client_connection_unref(&lmtp_conn);
 
@@ -227,97 +222,55 @@ lmtp_proxy_get_connection(struct lmtp_proxy *proxy,
        return conn;
 }
 
-static bool lmtp_proxy_send_data_replies(struct lmtp_proxy *proxy)
+static bool
+lmtp_proxy_handle_reply(struct smtp_server_cmd_ctx *cmd,
+                       const struct smtp_reply *reply,
+                       struct smtp_reply *reply_r)
 {
-       struct client *client = proxy->client;
-       struct lmtp_proxy_recipient *const *rcpt;
-       unsigned int i, count;
-
-       o_stream_cork(client->output);
-       rcpt = array_get(&proxy->rcpt_to, &count);
-       for (i = proxy->next_data_reply_idx; i < count; i++) {
-               if (!(rcpt[i]->rcpt_to_failed || rcpt[i]->data_reply_received))
-                       break;
-               o_stream_nsend_str(client->output,
-                                  t_strconcat(rcpt[i]->reply, "\r\n", NULL));
-       }
-       o_stream_uncork(client->output);
-       proxy->next_data_reply_idx = i;
-
-       return i == count;
-}
+       *reply_r = *reply;
 
-static void lmtp_proxy_finish_timeout(struct lmtp_proxy *proxy)
-{
-       i_assert(!proxy->finished);
+       if (!smtp_reply_is_remote(reply)) {
+               const char *detail = "";
 
-       timeout_remove(&proxy->to_finish);
-       proxy->finished = TRUE;
-       proxy->finish_callback(proxy->finish_context);
-}
-
-static void lmtp_proxy_try_finish(struct lmtp_proxy *proxy)
-{
-       if (proxy->finish_callback == NULL) {
-               /* DATA command hasn't been sent yet */
-               return;
-       }
-       if (!lmtp_proxy_send_data_replies(proxy)) {
-               /* we can't received reply from all clients yet */
-               return;
-       }
-       /* do the actual finishing in a timeout handler, since the finish
-          callback causes the proxy to be destroyed and the code leading up
-          to this function can be called from many different places. it's
-          easier this way rather than having all the callers check if the
-          proxy was already destroyed. */
-       if (proxy->to_finish == NULL) {
-               proxy->to_finish = timeout_add(0, lmtp_proxy_finish_timeout,
-                                              proxy);
-       }
-}
-
-static void
-lmtp_proxy_write_reply(string_t *reply, const struct smtp_reply *proxy_reply)
-{
-       if (smtp_reply_is_remote(proxy_reply)) {
-               smtp_reply_write_one_line(reply, proxy_reply);
-       } else {
-               str_append(reply, "451 4.4.0 Remote server not answering");
-               switch (proxy_reply->status) {
+               switch (reply->status) {
+               case SMTP_CLIENT_COMMAND_ERROR_ABORTED:
+                       break;
                case SMTP_CLIENT_COMMAND_ERROR_HOST_LOOKUP_FAILED:
-                       str_append(reply, " (DNS lookup)");
+                       detail = " (DNS lookup)";
                        break;
                case SMTP_CLIENT_COMMAND_ERROR_CONNECT_FAILED:
                case SMTP_CLIENT_COMMAND_ERROR_AUTH_FAILED:
-                       str_append(reply, " (connect)");
+                       detail = " (connect)";
                        break;
                case SMTP_CLIENT_COMMAND_ERROR_CONNECTION_LOST:
-                       str_append(reply, " (connection lost)");
+                       detail = " (connection lost)";
                        break;
                case SMTP_CLIENT_COMMAND_ERROR_BAD_REPLY:
-                       str_append(reply, " (bad reply)");
-                       break;                  
+                       detail = " (bad reply)";
+                       break;
                case SMTP_CLIENT_COMMAND_ERROR_TIMED_OUT:
-                       str_append(reply, " (timed out)");
+                       detail = " (timed out)";
                        break;
                default:
                        break;
                }
+
+               smtp_server_command_fail(cmd->cmd, 451, "4.4.0",
+                       "Remote server not answering%s", detail);
+               return FALSE;
        }
+
+       if (!smtp_reply_has_enhanced_code(reply)) {
+               reply_r->enhanced_code =
+                       SMTP_REPLY_ENH_CODE(reply->status / 100, 0, 0);
+       }
+       return TRUE;
 }
 
 /*
  * RCPT command
  */
 
-unsigned int lmtp_proxy_rcpt_count(struct client *client)
-{
-       if (client->proxy == NULL)
-               return 0;
-       return array_count(&client->proxy->rcpt_to);
-}
-
 static bool
 lmtp_proxy_rcpt_parse_fields(struct lmtp_proxy_rcpt_settings *set,
                             const char *const *args, const char **address)
@@ -404,37 +357,89 @@ lmtp_proxy_is_ourself(const struct client *client,
        return TRUE;
 }
 
+static void
+lmtp_proxy_rcpt_destroy(struct smtp_server_cmd_ctx *cmd)
+{
+       struct lmtp_proxy_recipient *rcpt =
+               (struct lmtp_proxy_recipient *)cmd->context;
+
+       lmtp_proxy_recipient_deinit(rcpt);
+}
+
+static void
+lmtp_proxy_rcpt_finished(struct smtp_server_cmd_ctx *cmd,
+                        struct smtp_server_transaction *trans ATTR_UNUSED,
+                        struct smtp_server_recipient *trcpt,
+                        unsigned int index)
+{
+       struct lmtp_proxy_recipient *rcpt =
+               (struct lmtp_proxy_recipient *)cmd->context;
+       struct client *client = rcpt->rcpt.client;
+
+       if (!smtp_server_command_replied_success(cmd->cmd)) {
+               /* failed in RCPT command; clean up early */
+               lmtp_proxy_recipient_deinit(rcpt);
+               return;
+       }
+
+       cmd->hook_destroy = NULL;
+
+       /* copy to transaction */
+       trcpt->context = (void *)rcpt;
+
+       /* add to local recipients */
+       array_append(&client->proxy->rcpt_to, &rcpt, 1);
+
+       rcpt->rcpt.rcpt = trcpt;
+       rcpt->rcpt.index = index;
+       rcpt->rcpt.rcpt_cmd = NULL;
+}
+
 static void
 lmtp_proxy_rcpt_cb(const struct smtp_reply *proxy_reply,
                   struct lmtp_proxy_recipient *rcpt)
 {
-       string_t *reply;
+       struct smtp_server_cmd_ctx *cmd = rcpt->rcpt.rcpt_cmd;
+       struct smtp_reply reply;
+
+       if (!lmtp_proxy_handle_reply(cmd, proxy_reply, &reply))
+               return;
 
-       i_assert(rcpt->reply == NULL);
+       if (smtp_reply_is_success(proxy_reply)) {
+               /* if backend accepts it, we accept it too */
 
-       reply = t_str_new(128);
-       lmtp_proxy_write_reply(reply, proxy_reply);
+               /* the default 2.0.0 code won't do */
+               if (!smtp_reply_has_enhanced_code(proxy_reply))
+                       reply.enhanced_code = SMTP_REPLY_ENH_CODE(2, 1, 0);
+       }
 
-       rcpt->reply = i_strdup(str_c(reply));
-       rcpt->rcpt_to_failed = !smtp_reply_is_success(proxy_reply);
+       /* forward reply */
+       smtp_server_reply_forward(cmd, &reply);
 }
 
 int lmtp_proxy_rcpt(struct client *client,
-                   struct smtp_address *address,
-                   const char *username, const char *detail, char delim,
-                   struct smtp_params_rcpt *params)
+                   struct smtp_server_cmd_ctx *cmd,
+                   struct smtp_server_cmd_rcpt *data,
+                   const char *username, const char *detail,
+                   char delim)
 {
        struct auth_master_connection *auth_conn;
        struct lmtp_proxy_rcpt_settings set;
        struct lmtp_proxy_connection *conn;
        struct lmtp_proxy_recipient *rcpt;
+       struct smtp_server_transaction *trans;
+       struct smtp_address *address = data->path;
        struct auth_user_info info;
        struct mail_storage_service_input input;
        const char *const *fields, *errstr, *orig_username = username;
+       struct smtp_proxy_data proxy_data;
        struct smtp_address *user;
        pool_t auth_pool;
        int ret;
 
+       trans = smtp_server_connection_get_transaction(cmd->conn);
+       i_assert(trans != NULL); /* MAIL command is synchronous */
+
        i_zero(&input);
        input.module = input.service = "lmtp";
        mail_storage_service_init_settings(storage_service, &input);
@@ -446,20 +451,21 @@ int lmtp_proxy_rcpt(struct client *client,
        info.local_port = client->local_port;
        info.remote_port = client->remote_port;
 
+       // FIXME: make this async
        auth_pool = pool_alloconly_create("auth lookup", 1024);
        auth_conn = mail_storage_service_get_auth_conn(storage_service);
        ret = auth_master_pass_lookup(auth_conn, username, &info,
                                      auth_pool, &fields);
        if (ret <= 0) {
-               errstr = ret < 0 && fields[0] != NULL ? t_strdup(fields[0]) :
-                       t_strdup_printf(ERRSTR_TEMP_USERDB_FAIL,
-                               smtp_address_encode(address));
+               errstr = ret < 0 && fields[0] != NULL ?
+                       t_strdup(fields[0]) : "Temporary user lookup failure";
                pool_unref(&auth_pool);
                if (ret < 0) {
-                       client_send_line(client, "%s", errstr);
+                       smtp_server_reply(cmd, 451, "4.3.0", "<%s> %s",
+                               smtp_address_encode(address), errstr);
                        return -1;
                } else {
-                       /* user not found from passdb. try userdb also. */
+                       /* user not found from passdb. revert to local delivery */
                        return 0;
                }
        }
@@ -468,7 +474,6 @@ int lmtp_proxy_rcpt(struct client *client,
        set.port = client->local_port;
        set.protocol = SMTP_PROTOCOL_LMTP;
        set.timeout_msecs = LMTP_PROXY_DEFAULT_TIMEOUT_MSECS;
-       set.params = *params;
 
        if (!lmtp_proxy_rcpt_parse_fields(&set, fields, &username)) {
                /* not proxying this user */
@@ -480,7 +485,7 @@ int lmtp_proxy_rcpt(struct client *client,
                                                username, &user, &errstr) < 0) {
                        i_error("%s: Username `%s' returned by passdb lookup is not a valid SMTP address",
                                orig_username, username);
-                       client_send_line(client, "550 5.3.5 <%s> "
+                       smtp_server_reply(cmd, 550, "5.3.5", "<%s> "
                                "Internal user lookup failure",
                                smtp_address_encode(address));
                        pool_unref(&auth_pool);
@@ -494,54 +499,45 @@ int lmtp_proxy_rcpt(struct client *client,
                }
        } else if (lmtp_proxy_is_ourself(client, &set)) {
                i_error("Proxying to <%s> loops to itself", username);
-               client_send_line(client, "554 5.4.6 <%s> "
-                                "Proxying loops to itself",
-                                smtp_address_encode(address));
+               smtp_server_reply(cmd, 554, "5.4.6",
+                       "<%s> Proxying loops to itself",
+                       smtp_address_encode(address));
                pool_unref(&auth_pool);
                return -1;
        }
 
-       if (client->proxy_ttl <= 1) {
+       smtp_server_connection_get_proxy_data(cmd->conn, &proxy_data);
+       if (proxy_data.ttl_plus_1 == 1) {
                i_error("Proxying to <%s> appears to be looping (TTL=0)",
                        username);
-               client_send_line(client, "554 5.4.6 <%s> "
-                                "Proxying appears to be looping (TTL=0)",
-                                username);
-               pool_unref(&auth_pool);
-               return -1;
-       }
-       if (client_get_rcpt_count(client) >
-               lmtp_proxy_rcpt_count(client)) {
-               client_send_line(client, "451 4.3.0 <%s> "
-                       "Can't handle mixed proxy/non-proxy destinations",
+               smtp_server_reply(cmd, 554, "5.4.6",
+                       "<%s> Proxying appears to be looping (TTL=0)",
                        smtp_address_encode(address));
                pool_unref(&auth_pool);
                return -1;
        }
 
        if (client->proxy == NULL)
-               client->proxy = lmtp_proxy_init(client);
+               client->proxy = lmtp_proxy_init(client, trans);
+
+       data->path = smtp_address_clone(cmd->pool, address);
 
        conn = lmtp_proxy_get_connection(client->proxy, &set);
-       if (conn->failed) {
-               client_send_line(client,
-                       "451 4.4.0 Remote server not answering");
-               pool_unref(&auth_pool);
-               return -1;
-       }
+       pool_unref(&auth_pool);
 
        rcpt = i_new(struct lmtp_proxy_recipient, 1);
-       rcpt->idx = array_count(&client->proxy->rcpt_to);
+       rcpt->rcpt.client = client;
+       rcpt->rcpt.rcpt_cmd = cmd;
+       rcpt->rcpt.path = data->path;
        rcpt->conn = conn;
-       rcpt->address = smtp_address_clone(default_pool, address);
-       array_append(&client->proxy->rcpt_to, &rcpt, 1);
+
+       cmd->context = (void*)rcpt;
+       cmd->hook_destroy = lmtp_proxy_rcpt_destroy;
+       data->hook_finished = lmtp_proxy_rcpt_finished;
 
        smtp_client_transaction_add_rcpt(conn->lmtp_trans,
-               address, &set.params,
+               address, &data->params,
                lmtp_proxy_rcpt_cb, lmtp_proxy_data_cb, rcpt);
-
-       client_send_line(client, "250 2.1.5 OK");
-       pool_unref(&auth_pool);
        return 1;
 }
 
@@ -554,46 +550,60 @@ lmtp_proxy_data_cb(const struct smtp_reply *proxy_reply,
                   struct lmtp_proxy_recipient *rcpt)
 {
        struct lmtp_proxy_connection *conn = rcpt->conn;
-       struct client *client = conn->proxy->client;
+       struct lmtp_proxy *proxy = conn->proxy;
+       struct smtp_server_cmd_ctx *cmd = proxy->pending_data_cmd;
+       struct smtp_server_transaction *trans = proxy->trans;
+       struct smtp_address *address = rcpt->rcpt.rcpt->path;
        const struct smtp_client_transaction_times *times =
                smtp_client_transaction_get_times(conn->lmtp_trans);
-       string_t *reply;
+       unsigned int rcpt_index = rcpt->rcpt.index;
+       struct smtp_reply reply;
        string_t *msg;
 
-       i_assert(!rcpt->rcpt_to_failed);
-       i_assert(rcpt->reply != NULL);
-
-       /* reset timeout in case there are a lot of RCPT TOs */
-       if (conn->to != NULL)
-               timeout_reset(conn->to);
-
-       reply = t_str_new(128);
-       lmtp_proxy_write_reply(reply, proxy_reply);
-
-       rcpt->reply = i_strdup(str_c(reply));
-       rcpt->data_reply_received = TRUE;
-
+       /* compose log message */
        msg = t_str_new(128);
-       str_printfa(msg, "%s: ", client->state.session_id);
+       str_printfa(msg, "%s: ", trans->id);
        if (smtp_reply_is_success(proxy_reply))
                str_append(msg, "Sent message to");
        else
                str_append(msg, "Failed to send message to");
        str_printfa(msg, " <%s> at %s:%u: %s (%u/%u at %u ms)",
-                   smtp_address_encode(rcpt->address), conn->set.host,
-                   conn->set.port, str_c(reply),
-                   rcpt->idx + 1, array_count(&conn->proxy->rcpt_to),
+                   smtp_address_encode(address),
+                   conn->set.host, conn->set.port,
+                   smtp_reply_log(proxy_reply),
+                   rcpt_index + 1, array_count(&trans->rcpt_to),
                    timeval_diff_msecs(&ioloop_timeval, &times->started));
-       if (smtp_reply_is_success(proxy_reply) ||
-               smtp_reply_is_remote(proxy_reply)) {
-               /* the problem isn't with the proxy, it's with the remote side.
-                  so the remote side will log an error, while for us this is
-                  just an info event */
+
+       /* handle reply */
+       if (smtp_reply_is_success(proxy_reply)) {
+               /* if backend accepts it, we accept it too */
                i_info("%s", str_c(msg));
+
+               /* substitute our own success message */
+               smtp_reply_printf(&reply, 250, "<%s> %s Saved",
+                       smtp_address_encode(address), trans->id);
+               /* do let the enhanced code through */
+               if (!smtp_reply_has_enhanced_code(proxy_reply))
+                       reply.enhanced_code = SMTP_REPLY_ENH_CODE(2, 0, 0);
+               else
+                       reply.enhanced_code = proxy_reply->enhanced_code;
+
        } else {
-               i_error("%s", str_c(msg));
+               if (smtp_reply_is_remote(proxy_reply)) {
+                       /* The problem isn't with the proxy, it's with the
+                          remote side. so the remote side will log an error,
+                          while for us this is just an info event */
+                       i_info("%s", str_c(msg));
+               } else {
+                       i_error("%s", str_c(msg));
+               }
+
+               if (!lmtp_proxy_handle_reply(cmd, proxy_reply, &reply))
+                       return;
        }
-       lmtp_proxy_try_finish(conn->proxy);
+
+       /* forward reply */
+       smtp_server_reply_index_forward(cmd, rcpt_index, &reply);
 }
 
 static void
@@ -603,17 +613,19 @@ lmtp_proxy_data_dummy_cb(const struct smtp_reply *proxy_reply ATTR_UNUSED,
        /* nothing */
 }
 
-void lmtp_proxy_start(struct lmtp_proxy *proxy, struct istream *data_input,
-                     lmtp_proxy_finish_callback_t *callback, void *context)
+void lmtp_proxy_data(struct client *client,
+                    struct smtp_server_cmd_ctx *cmd,
+                    struct smtp_server_transaction *trans ATTR_UNUSED,
+                    struct istream *data_input)
 {
+       struct lmtp_proxy *proxy = client->proxy;
        struct lmtp_proxy_connection *const *conns;
        uoff_t size;
 
        i_assert(data_input->seekable);
        i_assert(proxy->data_input == NULL);
 
-       proxy->finish_callback = callback;
-       proxy->finish_context = context;
+       proxy->pending_data_cmd = cmd;
        proxy->data_input = data_input;
        i_stream_ref(proxy->data_input);
        if (i_stream_get_size(proxy->data_input, TRUE, &size) < 0) {
@@ -642,14 +654,15 @@ void lmtp_proxy_start(struct lmtp_proxy *proxy, struct istream *data_input,
        array_foreach(&proxy->connections, conns) {
                struct lmtp_proxy_connection *conn = *conns;
 
+               if (conn->finished) {
+                       /* this connection had already failed */
+                       continue;
+               }
+
                smtp_client_transaction_set_timeout(conn->lmtp_trans,
                        proxy->max_timeout_msecs);
-               if (conn->data_input != NULL) {
-                       smtp_client_transaction_send(conn->lmtp_trans,
-                               conn->data_input,
-                               lmtp_proxy_data_dummy_cb, conn);
-               }
+               smtp_client_transaction_send(conn->lmtp_trans,
+                       conn->data_input,
+                       lmtp_proxy_data_dummy_cb, conn);
        }
-       /* finish if all of the connections have already failed */
-       lmtp_proxy_try_finish(proxy);
 }
index 6e7ff10446dabc35ca15caf64fe02fec8ea308de..ee6fc2e4a50037c2c7d0faa1bd77b058e861735a 100644 (file)
@@ -2,31 +2,28 @@
 #define LMTP_PROXY_H
 
 #include "net.h"
-#include "smtp-address.h"
+
+#include "smtp-common.h"
 #include "smtp-params.h"
 #include "smtp-client.h"
 
 #define LMTP_PROXY_DEFAULT_TTL 5
 
-struct smtp_address;
+struct smtp_server_cmd_ctx;
+struct smtp_server_cmd_rcpt;
 struct lmtp_proxy;
 struct client;
 
-typedef void lmtp_proxy_finish_callback_t(void *context);
-
 void lmtp_proxy_deinit(struct lmtp_proxy **proxy);
 
-unsigned int lmtp_proxy_rcpt_count(struct client *client);
-
 int lmtp_proxy_rcpt(struct client *client,
-                   struct smtp_address *address,
-                   const char *username, const char *detail, char delim,
-                   struct smtp_params_rcpt *params);
-
-/* Start proxying */
-void lmtp_proxy_start(struct lmtp_proxy *proxy, struct istream *data_input,
-                     lmtp_proxy_finish_callback_t *callback, void *context)
-       ATTR_NULL(3);
-
+                   struct smtp_server_cmd_ctx *cmd,
+                   struct smtp_server_cmd_rcpt *data,
+                   const char *username, const char *detail, char delim);
+
+void lmtp_proxy_data(struct client *client,
+                    struct smtp_server_cmd_ctx *cmd,
+                    struct smtp_server_transaction *trans ATTR_UNUSED,
+                    struct istream *data_input);
 
 #endif
index 283ec7a439a8a497fd0485d6ef42c278f29e46d4..a499eed5f69247428971710146c6b42c6a4ef7fc 100644 (file)
@@ -30,6 +30,8 @@ char *dns_client_socket_path, *base_dir;
 struct mail_storage_service_ctx *storage_service;
 struct anvil_client *anvil;
 
+struct smtp_server *lmtp_server;
+
 void lmtp_anvil_init(void)
 {
        if (anvil == NULL) {
@@ -41,7 +43,7 @@ void lmtp_anvil_init(void)
 static void client_connected(struct master_service_connection *conn)
 {
        master_service_client_connection_accept(conn);
-       (void)client_create(conn->fd, conn->fd, conn);
+       (void)client_create(conn->fd, conn->fd, conn->ssl, conn);
 }
 
 static void drop_privileges(void)
@@ -67,10 +69,18 @@ static void drop_privileges(void)
 static void main_init(void)
 {
        struct master_service_connection conn;
+       struct smtp_server_settings lmtp_set;
+
+       i_zero(&lmtp_set);
+       lmtp_set.protocol = SMTP_PROTOCOL_LMTP;
+       lmtp_set.auth_optional = TRUE;
+       lmtp_set.rcpt_domain_optional = TRUE;
+
+       lmtp_server = smtp_server_init(&lmtp_set);
 
        if (IS_STANDALONE()) {
                i_zero(&conn);
-               (void)client_create(STDIN_FILENO, STDOUT_FILENO, &conn);
+               (void)client_create(STDIN_FILENO, STDOUT_FILENO, FALSE, &conn);
        }
 
        const char *error, *tmp_socket_path;
@@ -88,6 +98,7 @@ static void main_deinit(void)
                anvil_client_deinit(&anvil);
        i_free(dns_client_socket_path);
        i_free(base_dir);
+       smtp_server_deinit(&lmtp_server);
 }
 
 int main(int argc, char *argv[])
index 00683421f14420f09b56e371c0ed2d62f173a800..f15b5b00f866aafc9338ddd40696636fa7cdc098 100644 (file)
@@ -5,6 +5,8 @@ extern char *dns_client_socket_path, *base_dir;
 extern struct mail_storage_service_ctx *storage_service;
 extern struct anvil_client *anvil;
 
+extern struct smtp_server *lmtp_server;
+
 void lmtp_anvil_init(void);
 
 void listener_client_destroyed(void);