The Dovecot 2.1 LMTP server currently always strips the address extension
from a recipient address (if recipient_delimiter is set), meaning user IDs
cannot contain the recipient delimiter character, e.g. "user+foo" is not
supported.

This was surprising for me, as Postfix behaves differently in this regard:
It first looks up "user+foo", and only then "user".

The attached patch works for me and brings Dovecot's behavior in line with
Postfix. Please let me know what you think about it.
diff -r 1ab8e0e699f7 src/lmtp/commands.c
--- a/src/lmtp/commands.c	Wed Jan 09 07:01:41 2013 +0200
+++ b/src/lmtp/commands.c	Sat Jan 12 17:25:27 2013 +0100
@@ -480,12 +480,39 @@
 	return ret;
 }
 
+static int rcpt_user_lookup(struct client *client, struct mail_recipient *rcpt,
+		     const char *username)
+{
+	struct mail_storage_service_input input;
+	const char *prefix;
+	const char *error = NULL;
+	int ret;
+
+	memset(&input, 0, sizeof(input));
+	input.module = input.service = "lmtp";
+	input.username = username;
+	input.local_ip = client->local_ip;
+	input.remote_ip = client->remote_ip;
+	input.local_port = client->local_port;
+	input.remote_port = client->remote_port;
+
+	ret = mail_storage_service_lookup(storage_service, &input,
+					  &rcpt->service_user, &error);
+
+	if (ret < 0) {
+		prefix = t_strdup_printf(ERRSTR_TEMP_USERDB_FAIL_PREFIX,
+					 username);
+		client_send_line(client, "%s%s", prefix, error);
+		return -1;
+	}
+
+	return ret;
+}
+
 int cmd_rcpt(struct client *client, const char *args)
 {
 	struct mail_recipient rcpt;
-	struct mail_storage_service_input input;
-	const char *address, *username, *detail, *prefix;
-	const char *error = NULL;
+	const char *address, *username, *detail;
 	int ret = 0;
 
 	client->state.name = "RCPT TO";
@@ -508,36 +535,34 @@
 		client_send_line(client, "501 5.5.4 Unsupported options");
 		return 0;
 	}
-	rcpt_address_parse(client, address, &username, &detail);
 
 	if (client->lmtp_set->lmtp_proxy) {
+		rcpt_address_parse(client, address, &username, &detail);
 		if (client_proxy_rcpt(client, address, username, detail))
 			return 0;
 	}
 
-	memset(&input, 0, sizeof(input));
-	input.module = input.service = "lmtp";
-	input.username = username;
-	input.local_ip = client->local_ip;
-	input.remote_ip = client->remote_ip;
-	input.local_port = client->local_port;
-	input.remote_port = client->remote_port;
-
-	ret = mail_storage_service_lookup(storage_service, &input,
-					  &rcpt.service_user, &error);
-
-	if (ret < 0) {
-		prefix = t_strdup_printf(ERRSTR_TEMP_USERDB_FAIL_PREFIX,
-					 username);
-		client_send_line(client, "%s%s", prefix, error);
+	/* First, try looking up with the whole address as the username */
+	ret = rcpt_user_lookup(client, &rcpt, address);
+	if (ret > 0) {
+		username = address;
+		detail = "";
+	}
+	/* User not found, remove extension and try again */
+	if (ret == 0) {
+		rcpt_address_parse(client, address, &username, &detail);
+		/* Avoid second userdb lookup when unneccessary */
+		if (*detail != '\0')
+			ret = rcpt_user_lookup(client, &rcpt, username);
+		if (ret == 0) {
+			client_send_line(client,
+					"550 5.1.1 <%s> User doesn't exist: %s",
+					address, username);
+			return 0;
+		}
+	}
+	if (ret == -1)
 		return 0;
-	}
-	if (ret == 0) {
-		client_send_line(client,
-				 "550 5.1.1 <%s> User doesn't exist: %s",
-				 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

Reply via email to