--- dovecot-2.2.33.2.original/src/lib-mail/mail-user-hash.c 2017-10-05 18:10:44.000000000 +0100 +++ dovecot-2.2.33.2/src/lib-mail/mail-user-hash.c 2017-10-31 16:21:20.424866755 +0000 @@ -33,9 +33,13 @@ tab = t_malloc(sizeof(static_tab)); memcpy(tab, static_tab, sizeof(static_tab)); tab[0].value = username; - tab[1].value = t_strcut(username, '@'); - tab[2].value = strchr(username, '@'); - if (tab[2].value != NULL) tab[2].value++; + tab[2].value = strrchr(username, '@'); + if (tab[2].value != NULL) { + tab[1].value = t_strndup(username, tab[2].value - username); + tab[2].value++; + } else { + tab[1].value = username; + } var_expand(str, format, tab); md5_get_digest(str_data(str), str_len(str), md5); --- dovecot-2.2.33.2.original/src/lib-mail/message-address.c 2017-10-05 18:10:44.000000000 +0100 +++ dovecot-2.2.33.2/src/lib-mail/message-address.c 2017-10-31 10:12:50.185866755 +0000 @@ -540,7 +540,7 @@ if (*delimiter_string == '\0') return; - domain = strchr(address, '@'); + domain = strrchr(address, '@'); p = strstr(address, delimiter_string); if (p != NULL && (domain == NULL || p < domain)) { /* user+detail@domain */ --- dovecot-2.2.33.2.original/src/lib-smtp/Makefile.am 2017-10-05 18:10:44.000000000 +0100 +++ dovecot-2.2.33.2/src/lib-smtp/Makefile.am 2017-11-01 16:05:07.393866755 +0000 @@ -2,7 +2,8 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ - -I$(top_srcdir)/src/lib-dns + -I$(top_srcdir)/src/lib-dns \ + -I$(top_srcdir)/src/lib/mail libsmtp_la_SOURCES = \ lmtp-client.c --- dovecot-2.2.33.2.original/src/lib-smtp/lmtp-client.c 2017-10-05 18:10:44.000000000 +0100 +++ dovecot-2.2.33.2/src/lib-smtp/lmtp-client.c 2017-11-01 16:09:28.384866755 +0000 @@ -8,6 +8,7 @@ #include "ostream.h" #include "str.h" #include "dns-lookup.h" +#include "rfc822-parser.h" #include "lmtp-client.h" #include @@ -778,6 +779,73 @@ client->data_header = p_strdup(client->pool, str); } +/* similar to (private) str_append_maybe_escape() in lib-mail */ +static void lmtp_client_rfc822_escape_address(string_t *dest_r, const char *str) +{ + const char *p; + const char *domain; + + /* no strrchrnul */ + domain = strrchr(str, '@'); + if (domain == NULL) { + domain = str; + while (*domain != '\0') + domain++; + } + + /* see if we need to quote local part */ + for (p=str; p < domain; p++) { + if (!IS_ATEXT(*p)) + break; + } + + /* local part is atext */ + if (p == domain) { + str_append(dest_r, str); + return; + } + + /* need to quote the local part */ + str_append_c(dest_r, '"'); + + /* see if we need to escape */ + for (p=str; p < domain; p++) { + if (strchr("\t\"\r\n\\", *p)) + break; + } + + if (p == domain) { + /* only quote */ + str_append_data(dest_r, str, (size_t)(p - str)); + } else { + /* escape local-part */ + for (p=str; p < domain; p++) { + switch (*p) { + case '\t': + str_append(dest_r, "\\t"); + break; + case '"': + str_append(dest_r, "\\\""); + break; + case '\r': + str_append(dest_r, "\\r"); + break; + case '\n': + str_append(dest_r, "\\n"); + break; + case '\\': + str_append(dest_r, "\\\\"); + break; + default: + str_append_c(dest_r, *p); + } + } + } + + str_append_c(dest_r, '"'); + str_append(dest_r, domain); +} + static void lmtp_append_xtext(string_t *dest, const char *str) { unsigned int i; @@ -800,7 +868,9 @@ rcpt = array_get(&client->recipients, &count); for (i = client->rcpt_next_send_idx; i < count; i++) { str_truncate(str, 0); - str_printfa(str, "RCPT TO:<%s>", rcpt[i].address); + str_append(str, "RCPT TO:<"); + lmtp_client_rfc822_escape_address(str, rcpt[i].address); + str_append_c(str, '>'); if (rcpt->params.dsn_orcpt != NULL) { str_append(str, " ORCPT="); lmtp_append_xtext(str, rcpt->params.dsn_orcpt); --- dovecot-2.2.33.2.original/src/lmtp/commands.c 2017-10-05 18:10:44.000000000 +0100 +++ dovecot-2.2.33.2/src/lmtp/commands.c 2017-11-01 13:07:23.614866755 +0000 @@ -441,34 +441,18 @@ static const char *lmtp_unescape_address(const char *name) { + struct rfc822_parser_context parser; 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. */ + /* decode quoted-string local-part */ 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; + rfc822_parser_init(&parser, (const unsigned char*)name, strlen(name), NULL); + rfc822_parse_quoted_string(&parser, str); + str_append(str, (const char*)parser.data); - str_append(str, p); return str_c(str); }