fix: leave bounce and read receipt reports out of a forward

This commit is contained in:
Linus Rath
2026-10-31 03:53:02 +01:00
parent a43b55ec25
commit 482e9a2014
2 changed files with 32 additions and 1 deletions
+15
View File
@@ -68,3 +68,18 @@ describe('replyComposeParams: quoted inline images', () => {
expect(replyComposeParams('forward', withImage)?.replyTo?.attachments).toEqual([quotedImage, file]);
});
});
describe('replyComposeParams: report parts', () => {
const file = { blobId: 'b-pdf', type: 'application/pdf', name: 'report.pdf', disposition: 'attachment' };
const reports = [
{ blobId: 'r1', type: 'message/delivery-status', name: null, disposition: 'attachment' },
{ blobId: 'r2', type: 'message/disposition-notification', name: null, disposition: 'attachment' },
{ blobId: 'r3', type: 'message/global-delivery-status', name: null, disposition: 'attachment' },
{ blobId: 'r4', type: 'Message/Global-Disposition-Notification; charset=utf-8', name: null, disposition: 'attachment' },
];
const bounce = { ...source, attachments: [...reports, file] } as unknown as Email;
it('leaves bounce and receipt reports, the global forms too, out of a forward', () => {
expect(replyComposeParams('forward', bounce)?.replyTo?.attachments).toEqual([file]);
});
});
+17 -1
View File
@@ -1,9 +1,25 @@
import type { Attachment, Email } from '../api/types';
import type { RootStackParamList } from '../navigation/types';
import { isReportPart } from './attachment-display';
import { pickEmailBody, plainTextBody } from './email-body';
import { extractCidRefs } from './email-html';
import { stripMessageIdBrackets } from './email-threading';
// Bounce and read-receipt reports (RFC 3464, RFC 8098) and their
// internationalised forms (RFC 6533). They describe the message they came
// with and mean nothing attached to another (webmail
// lib/attachment-visibility.ts, 8a09ba573).
const REPORT_TYPES = new Set([
'message/delivery-status',
'message/disposition-notification',
'message/global-delivery-status',
'message/global-disposition-notification',
]);
function isReport(type?: string | null): boolean {
return isReportPart(type) || REPORT_TYPES.has((type || '').split(';')[0].trim().toLowerCase());
}
/**
* The parts a reply or forward hands the composer. The inline images the
* quoted HTML embeds go along in both modes: the composer fetches them into
@@ -15,7 +31,7 @@ function carriedParts(mode: 'reply' | 'replyAll' | 'forward', source: Email, quo
const quoted = new Set(quoteHtml ? extractCidRefs(quoteHtml) : []);
const isQuoted = (a: Attachment) => !!a.cid && quoted.has(stripMessageIdBrackets(a.cid));
const parts = (source.attachments ?? []).filter((a) =>
isQuoted(a) || (mode === 'forward' && !(a.disposition === 'inline' && a.cid)));
!isReport(a.type) && (isQuoted(a) || (mode === 'forward' && !(a.disposition === 'inline' && a.cid))));
return parts.length > 0 || mode === 'forward' ? parts : undefined;
}