diff --git a/contrib/testing/smtp-quota-dsn-check.py b/contrib/testing/smtp-quota-dsn-check.py index 8cca8de..50289c5 100755 --- a/contrib/testing/smtp-quota-dsn-check.py +++ b/contrib/testing/smtp-quota-dsn-check.py @@ -288,8 +288,16 @@ def main() -> int: if item not in dsn_message ] if missing: + status_lines = [ + line.decode("utf-8", "replace") + for line in dsn_message.splitlines() + if line.lower().startswith( + (b"final-recipient:", b"action:", b"status:") + ) + ] raise SmtpQuotaTestError( - f"quota DSN lacks required fields: {missing!r}" + f"quota DSN lacks required fields: {missing!r}; " + f"actual delivery status: {status_lines!r}" ) if set(inbox_documents(recipient_store)) != set(recipient_before): diff --git a/src/agents/smtp/smtpd.c b/src/agents/smtp/smtpd.c index 6073a60..3a6f9af 100644 --- a/src/agents/smtp/smtpd.c +++ b/src/agents/smtp/smtpd.c @@ -1581,6 +1581,13 @@ HandleConnection (void *param) goto QuitRcpt; } } + /* + * Preserve the SMTP envelope recipient for DSNs even + * when no explicit ORCPT parameter was supplied. The + * director may rewrite a local address to a bare Store + * username, which is not the final RFC 822 recipient + * address that the submitting client used. + */ ReplyInt = strlen (name); if (SMTPStringRequiresUTF8(name) && @@ -1699,7 +1706,7 @@ HandleConnection (void *param) XplRWReadLockRelease (&ConfigLock); snprintf (Answer, sizeof (Answer), "QSTOR TO %s %s %lu\r\n", To, - Orcpt ? Orcpt : To, + Orcpt ? Orcpt : name, RecipientDSNFlags & DSN_FLAGS); Log(LOG_INFO, "Message from %s at host %s relayed to %s", Client->AuthFrom? @@ -1717,7 +1724,7 @@ HandleConnection (void *param) To); snprintf (Answer, sizeof (Answer), "QSTOR TO %s %s %lu\r\n", To, - Orcpt ? Orcpt : To, + Orcpt ? Orcpt : name, RecipientDSNFlags & DSN_FLAGS); break; } @@ -1730,14 +1737,14 @@ HandleConnection (void *param) XplRWReadLockRelease (&ConfigLock); snprintf (Answer, sizeof (Answer), "QSTOR LOCAL %s %s %lu\r\n", - To, Orcpt ? Orcpt : To, + To, Orcpt ? Orcpt : name, RecipientDSNFlags & DSN_FLAGS); } else { XplRWReadLockRelease (&ConfigLock); if (MsgAuthFindUser(To) == 0) { snprintf (Answer, sizeof (Answer), "QSTOR LOCAL %s %s %lu\r\n", - To, Orcpt ? Orcpt : To, + To, Orcpt ? Orcpt : name, RecipientDSNFlags & DSN_FLAGS); } else { ConnWrite (Client->client.conn, MSG550NOTFOUND, MSG550NOTFOUND_LEN);