fix(reply): honour an external Reply-To even on a self-sent message

The self-sent handling from #703 (reply to your own thread message ->
address the original recipients, ignore Reply-To) was entered whenever
From matched one of the user's identities. Contact-form / helpdesk mail
commonly arrives From the user's own address with the real correspondent
in Reply-To, so that branch ignored the Reply-To and looped the reply
back to the user's own To.

Only take the self-sent branch when there is no EXTERNAL Reply-To: a
Reply-To resolving to a non-own address is an explicit RFC 5322 directive
and now wins even for self-sent messages (#776). A Reply-To that is only
our own address stays ignored for self-sent (#703 preserved).

Closes #776.
This commit is contained in:
Stefan Hildebrandt
2026-08-16 21:12:21 +02:00
parent 5ab5749306
commit ffdc51ef70
2 changed files with 29 additions and 3 deletions
+23
View File
@@ -81,6 +81,29 @@ describe('buildReplyRecipients', () => {
expect(emails(to)).toEqual(['bob@other.com']);
});
it('honours an EXTERNAL Reply-To even on a self-sent message (#776)', () => {
// Contact-form / helpdesk pattern: the mail arrives From our own address
// (so it looks self-sent) but Reply-To carries the real correspondent.
// The reply must reach them, not loop back to our own To.
const { to } = buildReplyRecipients(
{ ...sent, to: [{ email: 'me@example.com' }], replyToAddresses: [{ email: 'customer@external.com' }] },
'reply',
OWN,
);
expect(emails(to)).toEqual(['customer@external.com']);
});
it('reply-all with an external Reply-To routes To to the Reply-To (#776)', () => {
const { to } = buildReplyRecipients(
{ ...sent, replyToAddresses: [{ email: 'customer@external.com' }] },
'replyAll',
OWN,
);
// Reply-To leads; the other original recipients (bob) are still included,
// our own address dropped.
expect(emails(to)).toEqual(['customer@external.com', 'bob@other.com']);
});
it('keeps a self-addressed recipient we chose ourselves', () => {
const { to } = buildReplyRecipients(
{ ...sent, to: [{ email: 'info@example.com' }] },
+6 -3
View File
@@ -80,7 +80,10 @@ export function buildReplyRecipients(
const withEmail = (list: ReplyAddress[] | undefined) => (list ?? []).filter((r) => Boolean(r.email));
if (isSelfSent(source, ownEmails)) {
const replyToList = withEmail(source.replyToAddresses);
const externalReplyTo = replyToList.filter((r) => !isOwnAddress(r.email, ownEmails));
if (!externalReplyTo.length && isSelfSent(source, ownEmails)) {
const originalTo = withEmail(source.to);
if (originalTo.length > 0) {
return {
@@ -90,8 +93,8 @@ export function buildReplyRecipients(
}
}
const replyTarget = withEmail(source.replyToAddresses).length
? withEmail(source.replyToAddresses)
const replyTarget = replyToList.length
? replyToList
: (source.from?.[0]?.email ? [source.from[0]] : []);
if (mode === 'reply') {