]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-lda: Changed struct mail_deliver_context address fields to use the new struct...
authorStephan Bosch <stephan.bosch@dovecot.fi>
Sat, 5 Nov 2016 13:47:53 +0000 (14:47 +0100)
committerTimo Sirainen <timo.sirainen@dovecot.fi>
Sun, 26 Nov 2017 20:30:01 +0000 (22:30 +0200)
src/lda/Makefile.am
src/lda/main.c
src/lib-lda/Makefile.am
src/lib-lda/mail-deliver.c
src/lib-lda/mail-deliver.h
src/lib-lda/mail-send.c
src/lmtp/client.h
src/lmtp/commands.c

index a066417ea4ac6968a893a84bb4201d8ba3663121..eb1807972b8a0027c4d398c5bad20206c0969bee 100644 (file)
@@ -10,6 +10,7 @@ AM_CPPFLAGS = \
        -I$(top_srcdir)/src/lib-imap \
        -I$(top_srcdir)/src/lib-index \
        -I$(top_srcdir)/src/lib-master \
+       -I$(top_srcdir)/src/lib-smtp \
        -I$(top_srcdir)/src/lib-lda \
        -I$(top_srcdir)/src/lib-storage \
        -I$(top_srcdir)/src/lib-storage/index \
index ad8ab45e11543639edbcb5e392cb4a9c4e765844..77f89246062e0a4c54d8bd18d8dc59f2c3014471 100644 (file)
@@ -16,6 +16,7 @@
 #include "unichar.h"
 #include "rfc822-parser.h"
 #include "message-address.h"
+#include "smtp-address.h"
 #include "settings-parser.h"
 #include "master-service.h"
 #include "master-service-settings.h"
@@ -31,7 +32,8 @@
 #include <stdio.h>
 #include <sysexits.h>
 
-#define DEFAULT_ENVELOPE_SENDER "MAILER-DAEMON"
+#define DEFAULT_ENVELOPE_SENDER \
+       SMTP_ADDRESS_LITERAL("MAILER-DAEMON", NULL)
 
 /* After buffer grows larger than this, create a temporary file to /tmp
    where to read the mail. */
@@ -42,46 +44,6 @@ static const char *wanted_headers[] = {
        NULL
 };
 
-static const char *escape_local_part(const char *local_part)
-{
-       const char *p;
-
-       /* if local_part isn't dot-atom-text, we need to return quoted-string
-          dot-atom-text = 1*atext *("." 1*atext) */
-       for (p = local_part; *p != '\0'; p++) {
-               if (!IS_ATEXT(*p) && *p != '.')
-                       break;
-       }
-       if (*p != '\0' || *local_part == '.' ||
-           (p != local_part && p[-1] == '.'))
-               local_part = t_strdup_printf("\"%s\"", str_escape(local_part));
-       return local_part;
-}
-
-static const char *address_sanitize(const char *address)
-{
-       struct message_address *addr;
-       const char *ret, *mailbox;
-       pool_t pool;
-
-       pool = pool_alloconly_create("address sanitizer", 256);
-       addr = message_address_parse(pool, (const unsigned char *)address,
-                                    strlen(address), 1, FALSE);
-
-       if (addr == NULL || addr->mailbox == NULL || addr->domain == NULL ||
-           *addr->mailbox == '\0')
-               ret = DEFAULT_ENVELOPE_SENDER;
-       else {
-               mailbox = escape_local_part(addr->mailbox);
-               if (*addr->domain == '\0')
-                       ret = t_strdup(mailbox);
-               else
-                       ret = t_strdup_printf("%s@%s", mailbox, addr->domain);
-       }
-       pool_unref(&pool);
-       return ret;
-}
-
 static int seekable_fd_callback(const char **path_r, void *context)
 {
        struct mail_deliver_context *ctx = context;
@@ -113,6 +75,7 @@ create_raw_stream(struct mail_deliver_context *ctx,
 {
        struct istream *input, *input2, *input_list[2];
        const unsigned char *data;
+       const char *error;
        char *sender = NULL;
        size_t i, size;
        int ret, tz;
@@ -143,9 +106,15 @@ create_raw_stream(struct mail_deliver_context *ctx,
        }
 
        if (sender != NULL && ctx->mail_from == NULL) {
+               struct smtp_address *mail_from = NULL;
                /* use the envelope sender from From_-line, but only if it
                   hasn't been specified with -f already. */
-               ctx->mail_from = p_strdup(ctx->pool, sender);
+               if (smtp_address_parse_mailbox(ctx->pool,
+                       sender, 0, &mail_from, &error) < 0) {
+                       i_warning("Failed to parse address from `From_'-line: %s",
+                                 error);
+               }
+               ctx->mail_from = mail_from;
        }
        i_free(sender);
 
@@ -172,9 +141,9 @@ lda_raw_mail_open(struct mail_deliver_context *ctx, const char *path)
        struct mailbox_transaction_context *t;
        struct mail *mail;
        struct mailbox_header_lookup_ctx *headers_ctx;
+       const struct smtp_address *mail_from;
        struct istream *input;
        void **sets;
-       const char *mail_from;
        time_t mtime;
        int ret;
 
@@ -188,11 +157,11 @@ lda_raw_mail_open(struct mail_deliver_context *ctx, const char *path)
                input = create_raw_stream(ctx, 0, &mtime);
                i_stream_set_name(input, "stdin");
                ret = raw_mailbox_alloc_stream(raw_mail_user, input, mtime,
-                                              mail_from, &box);
+                                              smtp_address_encode(mail_from), &box);
                i_stream_unref(&input);
        } else {
                ret = raw_mailbox_alloc_path(raw_mail_user, path, (time_t)-1,
-                                            mail_from, &box);
+                                            smtp_address_encode(mail_from), &box);
        }
        if (ret < 0) {
                i_fatal("Can't open delivery mail as raw: %s",
@@ -209,27 +178,41 @@ lda_raw_mail_open(struct mail_deliver_context *ctx, const char *path)
 }
 
 static void
-lda_set_rcpt_orig_to(struct mail_deliver_context *ctx, const char *user,
-                 const char *rcpt_to_source)
+lda_set_rcpt_to(struct mail_deliver_context *ctx,
+               const struct smtp_address *rcpt_to, const char *user,
+               const char *rcpt_to_source)
 {
-       if (ctx->rcpt_orig_to == NULL &&
+       const char *error;
+
+       if (rcpt_to == NULL &&
            *ctx->set->lda_original_recipient_header != '\0') {
-               ctx->rcpt_orig_to = mail_deliver_get_address(ctx->src_mail,
+               rcpt_to = mail_deliver_get_address(ctx->src_mail,
                                        ctx->set->lda_original_recipient_header);
                rcpt_to_source = t_strconcat(
                        ctx->set->lda_original_recipient_header, " header", NULL);
        }
-       if (ctx->rcpt_orig_to == NULL) {
-               ctx->rcpt_orig_to = strchr(user, '@') != NULL ? user :
-                       t_strconcat(user, "@", ctx->set->hostname, NULL);
+       if (rcpt_to == NULL) {
+               struct smtp_address *user_addr;
+
+               if (smtp_address_parse_username(ctx->pool, user,
+                       &user_addr, &error) < 0) {
+                       i_fatal_status(EX_USAGE,
+                               "Cannot obtain SMTP address from username `%s': %s",
+                               user, error);
+               }
+               if (user_addr->domain == NULL)
+                       user_addr->domain = ctx->set->hostname;
+               rcpt_to = user_addr;
                rcpt_to_source = "user@hostname";
        }
+
+       ctx->rcpt_orig_to = rcpt_to;
        if (ctx->rcpt_to == NULL)
-               ctx->rcpt_to = ctx->rcpt_orig_to;
+               ctx->rcpt_to = rcpt_to;
 
        if (ctx->rcpt_user->mail_debug) {
                i_debug("Destination address: %s (source: %s)",
-                       ctx->rcpt_orig_to, rcpt_to_source);
+                       smtp_address_encode_path(rcpt_to), rcpt_to_source);
        }
 }
 
@@ -277,6 +260,7 @@ int main(int argc, char *argv[])
        const char *user, *errstr, *path;
        struct lda_settings *lda_set;
        struct smtp_submit_settings *smtp_set;
+       struct smtp_address *rcpt_to, *final_rcpt_to, *mail_from;
        struct mail_storage_service_ctx *storage_service;
        struct mail_storage_service_user *service_user;
        struct mail_storage_service_input service_input;
@@ -321,11 +305,18 @@ int main(int argc, char *argv[])
        path = NULL;
 
        user = getenv("USER");
+       mail_from = final_rcpt_to = rcpt_to = NULL;
        while ((c = master_getopt(master_service)) > 0) {
                switch (c) {
                case 'a':
                        /* original recipient address */
-                       ctx.rcpt_orig_to = optarg;
+                       if (smtp_address_parse_path(ctx.pool, optarg,
+                               SMTP_ADDRESS_PARSE_FLAG_ALLOW_LOCALPART |
+                                       SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL,
+                               &rcpt_to, &errstr) < 0) {
+                               i_fatal_status(EX_USAGE,
+                                       "Invalid -a parameter: %s", errstr);
+                       }
                        rcpt_to_source = "-a parameter";
                        break;
                case 'd':
@@ -338,8 +329,12 @@ int main(int argc, char *argv[])
                        break;
                case 'f':
                        /* envelope sender address */
-                       ctx.mail_from =
-                               p_strdup(ctx.pool, address_sanitize(optarg));
+                       if (smtp_address_parse_path(ctx.pool, optarg,
+                               SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL,
+                               &mail_from, &errstr) < 0) {
+                               i_fatal_status(EX_USAGE,
+                                       "Invalid -f parameter: %s", errstr);
+                       }
                        break;
                case 'm':
                        /* destination mailbox.
@@ -362,7 +357,13 @@ int main(int argc, char *argv[])
                        break;
                case 'r':
                        /* final recipient address */
-                       ctx.rcpt_to = optarg;
+                       if (smtp_address_parse_path(ctx.pool, optarg,
+                               SMTP_ADDRESS_PARSE_FLAG_ALLOW_LOCALPART |
+                               SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL,
+                               &final_rcpt_to, &errstr) < 0) {
+                               i_fatal_status(EX_USAGE,
+                                       "Invalid -a parameter: %s", errstr);
+                       }
                        break;
                default:
                        print_help();
@@ -453,8 +454,13 @@ int main(int argc, char *argv[])
                        user_source);
        }
 
+       if (mail_from == NULL)
+               mail_from = DEFAULT_ENVELOPE_SENDER;
+
        ctx.src_mail = lda_raw_mail_open(&ctx, path);
-       lda_set_rcpt_orig_to(&ctx, user, rcpt_to_source);
+       ctx.mail_from = mail_from;
+       ctx.rcpt_to = final_rcpt_to;
+       lda_set_rcpt_to(&ctx, rcpt_to, user, rcpt_to_source);
 
        if (mail_deliver(&ctx, &storage) < 0) {
                if (ctx.tempfail_error != NULL) {
index 0536080434325fb6876b9def3b302ba318ed398e..25251fc783b1c53c079ee142d6c72fb7a0bb361e 100644 (file)
@@ -6,6 +6,7 @@ AM_CPPFLAGS = \
        -I$(top_srcdir)/src/lib-smtp \
        -I$(top_srcdir)/src/lib-index \
        -I$(top_srcdir)/src/lib-imap \
+       -I$(top_srcdir)/src/lib-smtp \
        -I$(top_srcdir)/src/lib-mail \
        -I$(top_srcdir)/src/lib-storage
 
index 734a41403d85204dbcc7c33680dc09525e0fc26b..d864f8910263a6f09a099cc3be5b948f44ba5818 100644 (file)
@@ -9,6 +9,7 @@
 #include "unichar.h"
 #include "var-expand.h"
 #include "message-address.h"
+#include "smtp-address.h"
 #include "lda-settings.h"
 #include "mail-storage.h"
 #include "mail-namespace.h"
@@ -64,7 +65,8 @@ static MODULE_CONTEXT_DEFINE_INIT(mail_deliver_user_module,
 static MODULE_CONTEXT_DEFINE_INIT(mail_deliver_storage_module,
                                  &mail_storage_module_register);
 
-const char *mail_deliver_get_address(struct mail *mail, const char *header)
+const struct smtp_address *
+mail_deliver_get_address(struct mail *mail, const char *header)
 {
        struct message_address *addr;
        const char *str;
@@ -76,7 +78,7 @@ const char *mail_deliver_get_address(struct mail *mail, const char *header)
                                     strlen(str), 1, FALSE);
        return addr == NULL || addr->mailbox == NULL || addr->domain == NULL ||
                *addr->mailbox == '\0' || *addr->domain == '\0' ?
-               NULL : t_strconcat(addr->mailbox, "@", addr->domain, NULL);
+               NULL : smtp_address_create_from_msg_temp(addr);
 }
 
 static void update_cache(pool_t pool, const char **old_str, const char *new_str)
@@ -106,7 +108,7 @@ mail_deliver_log_update_cache(struct mail_deliver_cache *cache, pool_t pool,
                subject = str_sanitize(subject, 80);
        update_cache(pool, &cache->subject, subject);
 
-       from = str_sanitize(mail_deliver_get_address(mail, "From"), 80);
+       from = smtp_address_encode(mail_deliver_get_address(mail, "From"));
        update_cache(pool, &cache->from, from);
 
        if (mail_get_special(mail, MAIL_FETCH_FROM_ENVELOPE, &from_envelope) > 0)
@@ -149,7 +151,7 @@ mail_deliver_ctx_get_log_var_expand_table(struct mail_deliver_context *ctx,
                { 'w', dec2str(ctx->cache->vsize), "vsize" },
                { '\0', dec2str(delivery_time_msecs), "delivery_time" },
                { '\0', dec2str(ctx->session_time_msecs), "session_time" },
-               { '\0', ctx->rcpt_orig_to, "to_envelope" },
+               { '\0', smtp_address_encode(ctx->rcpt_orig_to), "to_envelope" },
                { '\0', ctx->cache->storage_id, "storage_id" },
                { '\0', NULL, NULL }
        };
@@ -346,8 +348,10 @@ int mail_deliver_save(struct mail_deliver_context *ctx, const char *mailbox,
        kw = str_array_length(keywords) == 0 ? NULL :
                mailbox_keywords_create_valid(box, keywords);
        save_ctx = mailbox_save_alloc(t);
-       if (ctx->mail_from != NULL)
-               mailbox_save_set_from_envelope(save_ctx, ctx->mail_from);
+       if (ctx->mail_from != NULL) {
+               mailbox_save_set_from_envelope(save_ctx,
+                       smtp_address_encode(ctx->mail_from));
+       }
        mailbox_save_set_flags(save_ctx, flags, kw);
 
        headers_ctx = mailbox_header_lookup_init(box, lda_log_wanted_headers);
@@ -398,12 +402,32 @@ int mail_deliver_save(struct mail_deliver_context *ctx, const char *mailbox,
        return ret;
 }
 
-const char *mail_deliver_get_return_address(struct mail_deliver_context *ctx)
+const struct smtp_address *
+mail_deliver_get_return_address(struct mail_deliver_context *ctx)
 {
+       struct smtp_address *address;
+       const char *path, *error;
+       int ret;
+
        if (ctx->mail_from != NULL)
                return ctx->mail_from;
 
-       return mail_deliver_get_address(ctx->src_mail, "Return-Path");
+       if ((ret=mail_get_first_header(ctx->src_mail,
+                                      "Return-Path", &path)) <= 0) {
+               if (ret < 0) {
+                       struct mailbox *box = ctx->src_mail->box;
+                       i_warning("Failed read return-path header: %s",
+                               mailbox_get_last_internal_error(box, NULL));
+               }
+               return NULL;
+       }
+       if (smtp_address_parse_path(ctx->pool, path,
+                                   SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL,
+                                   &address, &error) < 0) {
+               i_warning("Failed to parse return-path header: %s", error);
+               return NULL;
+       }
+       return address;
 }
 
 const char *mail_deliver_get_new_message_id(struct mail_deliver_context *ctx)
index 299b3aea438f1b2621f8305e1d387ed683e1059d..ddb4f04ba8910f0f6789b4a77c0ce12364e5f081 100644 (file)
@@ -7,6 +7,7 @@
 
 #include <sys/time.h>
 
+struct smtp_address;
 struct mail_storage;
 struct mail_save_context;
 struct mailbox;
@@ -35,12 +36,12 @@ struct mail_deliver_context {
        struct mail *src_mail;
 
        /* Envelope sender, if known. */
-       const char *mail_from;
+       const struct smtp_address *mail_from;
 
        /* Envelope recipient (final recipient) */
-       const char *rcpt_to;
+       const struct smtp_address *rcpt_to;
        /* Envelope recipient (original recipient) */
-       const char *rcpt_orig_to;
+       const struct smtp_address *rcpt_orig_to;
        /* Destination user */
        struct mail_user *rcpt_user;
        /* Mailbox where mail should be saved, unless e.g. Sieve does
@@ -85,8 +86,10 @@ mail_deliver_ctx_get_log_var_expand_table(struct mail_deliver_context *ctx,
 void mail_deliver_log(struct mail_deliver_context *ctx, const char *fmt, ...)
        ATTR_FORMAT(2, 3);
 
-const char *mail_deliver_get_address(struct mail *mail, const char *header);
-const char *mail_deliver_get_return_address(struct mail_deliver_context *ctx);
+const struct smtp_address *
+mail_deliver_get_address(struct mail *mail, const char *header);
+const struct smtp_address *
+mail_deliver_get_return_address(struct mail_deliver_context *ctx);
 const char *mail_deliver_get_new_message_id(struct mail_deliver_context *ctx);
 
 struct mail_deliver_session *mail_deliver_session_init(void);
index 9cd427db4584543d249d3928c97190214fa7585e..e53e8718e3fb72b67981f2a9c9155f509fc678a7 100644 (file)
@@ -16,6 +16,7 @@
 #include "mail-storage-settings.h"
 #include "lda-settings.h"
 #include "mail-deliver.h"
+#include "smtp-address.h"
 #include "smtp-submit.h"
 #include "mail-send.h"
 
@@ -53,8 +54,8 @@ int mail_send_rejection(struct mail_deliver_context *ctx, const char *recipient,
        struct istream *input;
        struct smtp_submit *smtp_submit;
        struct ostream *output;
-       const char *return_addr, *hdr;
-       const char *value, *msgid, *orig_msgid, *boundary, *error;
+       const struct smtp_address *return_addr;
+       const char *hdr, *value, *msgid, *orig_msgid, *boundary, *error;
        const struct var_expand_table *vtable;
        string_t *str;
        int ret;
@@ -71,7 +72,7 @@ int mail_send_rejection(struct mail_deliver_context *ctx, const char *recipient,
        }
 
        return_addr = mail_deliver_get_return_address(ctx);
-       if (return_addr == NULL) {
+       if (smtp_address_isnull(return_addr)) {
                i_info("msgid=%s: Return-Path missing, rejection reason: %s",
                        orig_msgid == NULL ? "" : str_sanitize(orig_msgid, 80),
                        str_sanitize(reason, 512));
@@ -80,13 +81,14 @@ int mail_send_rejection(struct mail_deliver_context *ctx, const char *recipient,
 
        if (mailbox_get_settings(mail->box)->mail_debug) {
                i_debug("Sending a rejection to %s: %s",
-                       return_addr, str_sanitize(reason, 512));
+                       smtp_address_encode(return_addr),
+                       str_sanitize(reason, 512));
        }
 
        vtable = get_var_expand_table(mail, reason, recipient);
 
        smtp_submit = smtp_submit_init_simple(ctx->smtp_set, NULL);
-       smtp_submit_add_rcpt(smtp_submit, return_addr);
+       smtp_submit_add_rcpt(smtp_submit, smtp_address_encode(return_addr));
        output = smtp_submit_send(smtp_submit);
 
        msgid = mail_deliver_get_new_message_id(ctx);
@@ -98,7 +100,7 @@ int mail_send_rejection(struct mail_deliver_context *ctx, const char *recipient,
        str_append(str, "From: ");
        message_address_write(str, mail_set->parsed_postmaster_address);
        str_append(str, "\r\n");
-       str_printfa(str, "To: <%s>\r\n", return_addr);
+       str_printfa(str, "To: <%s>\r\n", smtp_address_encode(return_addr));
        str_append(str, "MIME-Version: 1.0\r\n");
        str_printfa(str, "Content-Type: "
                "multipart/report; report-type=%s;\r\n"
index 856a7d468ce74fdf7e157665c46a97c332034bbd..dac1aa59d7cb4683a7b94443699aeddf5e3c8608 100644 (file)
@@ -10,7 +10,7 @@ struct mail_recipient {
        struct client *client;
        const char *session_id;
 
-       const char *address;
+       struct smtp_address *address;
        const char *detail; /* +detail part is also in address */
        struct lmtp_recipient_params params;
 
@@ -22,7 +22,7 @@ struct mail_recipient {
 struct client_state {
        const char *name;
        const char *session_id;
-       const char *mail_from;
+       struct smtp_address *mail_from;
        ARRAY(struct mail_recipient *) rcpt_to;
        unsigned int rcpt_idx;
 
index 4f1460542858ae2b1d91701ddf5485b0e4946f19..ef7d8706a4fdef4d3c3385fbc98fa078a67be5b4 100644 (file)
@@ -21,7 +21,9 @@
 #include "master-service-ssl.h"
 #include "iostream-ssl.h"
 #include "rfc822-parser.h"
+#include "smtp-address.h"
 #include "message-date.h"
+#include "message-address.h"
 #include "auth-master.h"
 #include "mail-storage-service.h"
 #include "index/raw/raw-storage.h"
@@ -119,40 +121,6 @@ int cmd_starttls(struct client *client)
        return 0;
 }
 
-static int parse_address(const char *str, const char **address_r,
-                        const char **rest_r)
-{
-       const char *start;
-
-       if (*str++ != '<')
-               return -1;
-       start = str;
-       if (*str == '"') {
-               /* "quoted-string"@domain */
-               for (str++; *str != '"'; str++) {
-                       if (*str == '\\')
-                               str++;
-                       if (*str == '\0')
-                               return -1;
-               }
-               str++;
-       }
-       for (; *str != '>'; str++) {
-               if (*str == '\0' || *str == ' ')
-                       return -1;
-       }
-       *address_r = t_strdup_until(start, str);
-       if (*str++ != '>')
-               return -1;
-
-       if (*str == ' ')
-               str++;
-       else if (*str != '\0')
-               return -1;
-       *rest_r = str;
-       return 0;
-}
-
 static const char *
 parse_xtext(struct client *client, const char *value)
 {
@@ -190,18 +158,32 @@ static void lmtp_anvil_init(void)
 
 int cmd_mail(struct client *client, const char *args)
 {
-       const char *addr, *const *argv;
+       struct smtp_address *address;
+       const char *const *argv;
+       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 ||
-           parse_address(args + 5, &addr, &args) < 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;
+       }
 
        argv = t_strsplit(args, " ");
        for (; *argv != NULL; argv++) {
@@ -216,10 +198,12 @@ int cmd_mail(struct client *client, const char *args)
                }
        }
 
-       client->state.mail_from = p_strdup(client->state_pool, addr);
+       client->state.mail_from =
+               smtp_address_clone(client->state_pool, address);
        p_array_init(&client->state.rcpt_to, client->state_pool, 64);
        client_send_line(client, "250 2.1.0 OK");
-       client_state_set(client, "MAIL FROM", client->state.mail_from);
+       client_state_set(client, "MAIL FROM",
+               smtp_address_encode(address));
 
        if (client->lmtp_set->lmtp_user_concurrency_limit > 0) {
                /* connect to anvil before dropping privileges */
@@ -316,23 +300,8 @@ client_proxy_is_ourself(const struct client *client,
        return TRUE;
 }
 
-static const char *
-address_add_detail(const char *username, char delim_c,
-                  const char *detail)
-{
-       const char *domain;
-       const char delim[] = {delim_c, '\0'};
-
-       domain = strchr(username, '@');
-       if (domain == NULL)
-               return t_strconcat(username, delim, detail, NULL);
-       else {
-               username = t_strdup_until(username, domain);
-               return t_strconcat(username, delim, detail, domain, NULL);
-       }
-}
-
-static bool client_proxy_rcpt(struct client *client, const char *address,
+static bool client_proxy_rcpt(struct client *client,
+                             struct smtp_address *address,
                              const char *username, const char *detail, char delim,
                              const struct lmtp_recipient_params *params)
 {
@@ -341,6 +310,7 @@ static bool client_proxy_rcpt(struct client *client, const char *address,
        struct auth_user_info info;
        struct mail_storage_service_input input;
        const char *args, *const *fields, *errstr, *orig_username = username;
+       struct smtp_address *user;
        pool_t pool;
        int ret;
 
@@ -361,7 +331,8 @@ static bool client_proxy_rcpt(struct client *client, const char *address,
                                      pool, &fields);
        if (ret <= 0) {
                errstr = ret < 0 && fields[0] != NULL ? t_strdup(fields[0]) :
-                       t_strdup_printf(ERRSTR_TEMP_USERDB_FAIL, address);
+                       t_strdup_printf(ERRSTR_TEMP_USERDB_FAIL,
+                               smtp_address_encode(address));
                pool_unref(&pool);
                if (ret < 0) {
                        client_send_line(client, "%s", errstr);
@@ -384,15 +355,27 @@ static bool client_proxy_rcpt(struct client *client, const char *address,
                return FALSE;
        }
        if (strcmp(username, orig_username) != 0) {
+               if (smtp_address_parse_username(pool_datastack_create(),
+                                               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> "
+                               "Internal user lookup failure",
+                               smtp_address_encode(address));
+                       pool_unref(&pool);
+                       return FALSE;
+               }
                /* username changed. change the address as well */
-               if (*detail == '\0')
-                       address = username;
-               else
-                       address = address_add_detail(username, delim, detail);
+               if (*detail == '\0') {
+                       address = user;
+               } else {
+                       address = smtp_address_add_detail_temp(user, detail, delim);
+               }
        } else if (client_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", address);
+                                "Proxying loops to itself",
+                                smtp_address_encode(address));
                pool_unref(&pool);
                return TRUE;
        }
@@ -409,7 +392,7 @@ static bool client_proxy_rcpt(struct client *client, const char *address,
        if (array_count(&client->state.rcpt_to) != 0) {
                client_send_line(client, "451 4.3.0 <%s> "
                        "Can't handle mixed proxy/non-proxy destinations",
-                       address);
+                       smtp_address_encode(address));
                pool_unref(&pool);
                return TRUE;
        }
@@ -431,10 +414,12 @@ static bool client_proxy_rcpt(struct client *client, const char *address,
                        args = " BODY=7BIT";
                else
                        args = "";
-               lmtp_proxy_mail_from(client->proxy, t_strdup_printf(
-                       "<%s>%s", client->state.mail_from, args));
+               lmtp_proxy_mail_from(client->proxy,
+                       t_strdup_printf("<%s>%s",
+                               smtp_address_encode(client->state.mail_from), args));
        }
-       if (lmtp_proxy_add_rcpt(client->proxy, address, &set) < 0)
+       if (lmtp_proxy_add_rcpt(client->proxy,
+               smtp_address_encode(address), &set) < 0)
                client_send_line(client, ERRSTR_TEMP_REMOTE_FAILURE);
        else
                client_send_line(client, "250 2.1.5 OK");
@@ -442,39 +427,6 @@ static bool client_proxy_rcpt(struct client *client, const char *address,
        return TRUE;
 }
 
-static const char *lmtp_unescape_address(const char *name)
-{
-       string_t *str;
-       const char *p;
-
-       if (*name != '"')
-               return name;
-
-       /* quoted-string local-part. drop the quotes unless there's a
-          '@' character inside or there's an error. */
-       str = t_str_new(128);
-       for (p = name+1; *p != '"'; p++) {
-               if (*p == '\0')
-                       return name;
-               if (*p == '\\') {
-                       if (p[1] == '\0') {
-                               /* error */
-                               return name;
-                       }
-                       p++;
-               }
-               if (*p == '@')
-                       return name;
-               str_append_c(str, *p);
-       }
-       p++;
-       if (*p != '@' && *p != '\0')
-               return name;
-
-       str_append(str, p);
-       return str_c(str);
-}
-
 static void
 client_send_line_overquota(struct client *client,
                           const struct mail_recipient *rcpt, const char *error)
@@ -482,8 +434,9 @@ client_send_line_overquota(struct client *client,
        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", rcpt->address, error);
+       client_send_line(client, "%s <%s> %s",
+               lda_set->quota_full_tempfail ? "452 4.2.2" : "552 5.2.2",
+               smtp_address_encode(rcpt->address), error);
 }
 
 static int
@@ -514,7 +467,8 @@ lmtp_rcpt_to_is_over_quota(struct client *client,
                                                            &user, &errstr);
 
        if (ret < 0) {
-               i_error("Failed to initialize user %s: %s", rcpt->address, errstr);
+               i_error("Failed to initialize user %s: %s",
+                       smtp_address_encode(rcpt->address), errstr);
                return -1;
        }
 
@@ -546,7 +500,7 @@ static bool cmd_rcpt_finish(struct client *client, struct mail_recipient *rcpt)
        if ((ret = lmtp_rcpt_to_is_over_quota(client, rcpt)) != 0) {
                if (ret < 0) {
                        client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
-                                        rcpt->address);
+                                        smtp_address_encode(rcpt->address));
                }
                mail_storage_service_user_unref(&rcpt->service_user);
                return FALSE;
@@ -573,7 +527,7 @@ static void rcpt_anvil_lookup_callback(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",
-                                rcpt->address);
+                                smtp_address_encode(rcpt->address));
                mail_storage_service_user_unref(&rcpt->service_user);
        } else if (cmd_rcpt_finish(client, rcpt)) {
                rcpt->anvil_connect_sent = TRUE;
@@ -591,7 +545,8 @@ int cmd_rcpt(struct client *client, const char *args)
 {
        struct mail_recipient *rcpt;
        struct mail_storage_service_input input;
-       const char *params, *address, *username, *detail;
+       struct smtp_address *address;
+       const char *username, *detail;
        const char *const *argv;
        const char *error = NULL;
        char delim = '\0';
@@ -602,17 +557,28 @@ int cmd_rcpt(struct client *client, const char *args)
                return 0;
        }
 
-       if (strncasecmp(args, "TO:", 3) != 0 ||
-           parse_address(args + 3, &address, &params) < 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;
+       }
 
        rcpt = p_new(client->state_pool, struct mail_recipient, 1);
        rcpt->client = client;
-       address = lmtp_unescape_address(address);
 
-       argv = t_strsplit(params, " ");
+       argv = t_strsplit(args, " ");
        for (; *argv != NULL; argv++) {
                if (strncasecmp(*argv, "ORCPT=", 6) == 0) {
                        rcpt->params.dsn_orcpt = parse_xtext(client, *argv + 6);
@@ -621,10 +587,13 @@ int cmd_rcpt(struct client *client, const char *args)
                        return 0;
                }
        }
-       message_detail_address_parse(client->unexpanded_lda_set->recipient_delimiter,
-                                    address, &username, &delim, &detail);
 
-       client_state_set(client, "RCPT TO", address);
+       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));
 
        if (client->lmtp_set->lmtp_proxy) {
                if (client_proxy_rcpt(client, address, username, detail, delim,
@@ -657,13 +626,14 @@ int cmd_rcpt(struct client *client, const char *args)
 
        if (ret < 0) {
                i_error("Failed to lookup user %s: %s", username, error);
-               client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL, address);
+               client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
+                       smtp_address_encode(address));
                return 0;
        }
        if (ret == 0) {
                client_send_line(client,
                                 "550 5.1.1 <%s> User doesn't exist: %s",
-                                address, username);
+                                smtp_address_encode(address), username);
                return 0;
        }
        if (client->proxy != NULL) {
@@ -672,12 +642,12 @@ int cmd_rcpt(struct client *client, const char *args)
                   (with and without Return-Path: header) */
                client_send_line(client, "451 4.3.0 <%s> "
                        "Can't handle mixed proxy/non-proxy destinations",
-                       address);
+                       smtp_address_encode(address));
                mail_storage_service_user_unref(&rcpt->service_user);
                return 0;
        }
 
-       rcpt->address = p_strdup(client->state_pool, address);
+       rcpt->address = smtp_address_clone(client->state_pool, address);
        rcpt->detail = p_strdup(client->state_pool, detail);
 
        if (client->lmtp_set->lmtp_user_concurrency_limit == 0) {
@@ -729,12 +699,20 @@ int cmd_noop(struct client *client, const char *args ATTR_UNUSED)
        return 0;
 }
 
-static bool orcpt_get_valid_rfc822(const char *orcpt, const char **addr_r)
+static bool orcpt_get_valid_rfc822(const char *orcpt,
+       const struct smtp_address **addr_r)
 {
+       struct message_address *rfc822_addr;
+
        if (orcpt == NULL || strncasecmp(orcpt, "rfc822;", 7) != 0)
                return FALSE;
-       /* FIXME: we should verify the address further */
-       *addr_r = orcpt + 7;
+
+       rfc822_addr = message_address_parse(pool_datastack_create(),
+               (const unsigned char *)orcpt+7, strlen(orcpt+7), 2, FALSE);
+       if (rfc822_addr == NULL || rfc822_addr->invalid_syntax ||
+               rfc822_addr->next != NULL)
+               return FALSE;
+       *addr_r = smtp_address_create_temp(rfc822_addr->mailbox, rfc822_addr->domain);
        return TRUE;
 }
 
@@ -788,7 +766,7 @@ client_deliver(struct client *client, const struct mail_recipient *rcpt,
                                      &dest_user, &error) < 0) {
                i_error("Failed to initialize user: %s", error);
                client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
-                                rcpt->address);
+                                smtp_address_encode(rcpt->address));
                return -1;
        }
        client->state.dest_user = dest_user;
@@ -810,7 +788,7 @@ client_deliver(struct client *client, const struct mail_recipient *rcpt,
        if (ret <= 0) {
                i_error("Failed to expand settings: %s", error);
                client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
-                                rcpt->address);
+                                smtp_address_encode(rcpt->address));
                return -1;
        }
 
@@ -821,7 +799,7 @@ client_deliver(struct client *client, const struct mail_recipient *rcpt,
                i_error("Failed to expand mail_log_prefix=%s: %s",
                        dest_user->set->mail_log_prefix, error);
                client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
-                                rcpt->address);
+                                smtp_address_encode(rcpt->address));
                return -1;
        }
        i_set_failure_prefix("%s", str_c(str));
@@ -867,11 +845,13 @@ client_deliver(struct client *client, const struct mail_recipient *rcpt,
                        client->state.first_saved_mail = dctx.dest_mail;
                }
                client_send_line(client, "250 2.0.0 <%s> %s Saved",
-                                rcpt->address, rcpt->session_id);
+                                smtp_address_encode(rcpt->address),
+                                rcpt->session_id);
                ret = 0;
        } else if (dctx.tempfail_error != NULL) {
                client_send_line(client, "451 4.2.0 <%s> %s",
-                                rcpt->address, dctx.tempfail_error);
+                                smtp_address_encode(rcpt->address),
+                                dctx.tempfail_error);
                ret = -1;
        } else if (storage != NULL) {
                error = mail_storage_get_last_error(storage, &mail_error);
@@ -879,14 +859,14 @@ client_deliver(struct client *client, const struct mail_recipient *rcpt,
                        client_send_line_overquota(client, rcpt, error);
                } else {
                        client_send_line(client, "451 4.2.0 <%s> %s",
-                                        rcpt->address, error);
+                                        smtp_address_encode(rcpt->address), error);
                }
                ret = -1;
        } else {
                /* This shouldn't happen */
                i_error("BUG: Saving failed to unknown storage");
                client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
-                                rcpt->address);
+                                smtp_address_encode(rcpt->address));
                ret = -1;
        }
        return ret;
@@ -946,7 +926,7 @@ static void client_rcpt_fail_all(struct client *client)
 
        array_foreach(&client->state.rcpt_to, rcptp) {
                client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
-                                (*rcptp)->address);
+                                smtp_address_encode((*rcptp)->address));
        }
 }
 
@@ -989,7 +969,7 @@ static int client_open_raw_mail(struct client *client, struct istream *input)
        enum mail_error error;
 
        if (raw_mailbox_alloc_stream(client->raw_mail_user, input,
-                                    (time_t)-1, client->state.mail_from,
+                                    (time_t)-1, smtp_address_encode(client->state.mail_from),
                                     &box) < 0) {
                i_error("Can't open delivery mail as raw: %s",
                        mailbox_get_last_internal_error(box, &error));
@@ -1078,7 +1058,8 @@ static const char *client_get_added_headers(struct client *client)
        string_t *str = t_str_new(200);
        void **sets;
        const struct lmtp_settings *lmtp_set;
-       const char *host, *rcpt_to = NULL;
+       const struct smtp_address *rcpt_to = NULL;
+       const char *host;
 
        if (array_count(&client->state.rcpt_to) == 1) {
                struct mail_recipient *const *rcptp =
@@ -1104,9 +1085,11 @@ static const char *client_get_added_headers(struct client *client)
        /* don't set Return-Path when proxying so it won't get added twice */
        if (array_count(&client->state.rcpt_to) > 0) {
                str_printfa(str, "Return-Path: <%s>\r\n",
-                           client->state.mail_from);
-               if (rcpt_to != NULL)
-                       str_printfa(str, "Delivered-To: %s\r\n", rcpt_to);
+                           smtp_address_encode(client->state.mail_from));
+               if (rcpt_to != NULL) {
+                       str_printfa(str, "Delivered-To: %s\r\n",
+                               smtp_address_encode(rcpt_to));
+               }
        }
 
        str_printfa(str, "Received: from %s", client->lhlo);
@@ -1123,7 +1106,7 @@ static const char *client_get_added_headers(struct client *client)
 
        str_append(str, "\r\n\t");
        if (rcpt_to != NULL)
-               str_printfa(str, "for <%s>", rcpt_to);
+               str_printfa(str, "for <%s>", smtp_address_encode(rcpt_to));
        str_printfa(str, "; %s\r\n", message_date_create(ioloop_time));
        return str_c(str);
 }