diff --git a/ts/services/backups/export.ts b/ts/services/backups/export.ts index aa8e4ae5364..af3c0f04c3a 100644 --- a/ts/services/backups/export.ts +++ b/ts/services/backups/export.ts @@ -47,7 +47,7 @@ import { isConversationUnregistered } from '../../util/isConversationUnregistere import { uuidToBytes } from '../../util/uuidToBytes'; import { assertDev, strictAssert } from '../../util/assert'; import { getSafeLongFromTimestamp } from '../../util/timestampLongUtils'; -import { MINUTE, SECOND, DurationInSeconds } from '../../util/durations'; +import { DAY, MINUTE, SECOND, DurationInSeconds } from '../../util/durations'; import { PhoneNumberDiscoverability, parsePhoneNumberDiscoverability, @@ -111,6 +111,7 @@ import { import type { CoreAttachmentBackupJobType } from '../../types/AttachmentBackup'; import { AttachmentBackupManager } from '../../jobs/AttachmentBackupManager'; import { getBackupCdnInfo } from './util/mediaId'; +import { calculateExpirationTimestamp } from '../../util/expirationTimer'; import { ReadStatus } from '../../messages/MessageReadStatus'; const MAX_CONCURRENCY = 10; @@ -174,7 +175,10 @@ type NonBubbleResultType = Readonly< >; export class BackupExportStream extends Readable { - private readonly backupTimeMs = getSafeLongFromTimestamp(Date.now()); + // Shared between all methods for consistency. + private now = Date.now(); + + private readonly backupTimeMs = getSafeLongFromTimestamp(this.now); private readonly convoIdToRecipientId = new Map(); private attachmentBackupJobs: Array = []; private buffers = new Array(); @@ -825,6 +829,12 @@ export class BackupExportStream extends Readable { return undefined; } + const expirationTimestamp = calculateExpirationTimestamp(message); + if (expirationTimestamp != null && expirationTimestamp <= this.now + DAY) { + // Message expires too soon + return undefined; + } + let authorId: Long | undefined; const isOutgoing = message.type === 'outgoing'; diff --git a/ts/test-electron/backup/bubble_test.ts b/ts/test-electron/backup/bubble_test.ts index 17182ba80bd..94c7ca264cb 100644 --- a/ts/test-electron/backup/bubble_test.ts +++ b/ts/test-electron/backup/bubble_test.ts @@ -15,6 +15,7 @@ import { ReadStatus } from '../../messages/MessageReadStatus'; import { SeenStatus } from '../../MessageSeenStatus'; import { loadCallsHistory } from '../../services/callHistoryLoader'; import { ID_V1_LENGTH } from '../../groups'; +import { DurationInSeconds, WEEK } from '../../util/durations'; import { setupBasics, asymmetricRoundtripHarness, @@ -423,4 +424,49 @@ describe('backup/bubble messages', () => { [] ); }); + + it('drops messages that expire soon', async () => { + await asymmetricRoundtripHarness( + [ + { + conversationId: contactA.id, + id: generateGuid(), + type: 'incoming', + received_at: 3, + received_at_ms: 3, + sent_at: 3, + timestamp: 3, + sourceServiceId: CONTACT_A, + body: 'd', + readStatus: ReadStatus.Unread, + seenStatus: SeenStatus.Unseen, + unidentifiedDeliveryReceived: true, + expirationStartTimestamp: Date.now(), + expireTimer: DurationInSeconds.fromSeconds(1), + }, + ], + [] + ); + }); + + it('does not drop messages that expire far in the future', async () => { + await symmetricRoundtripHarness([ + { + conversationId: contactA.id, + id: generateGuid(), + type: 'incoming', + received_at: 3, + received_at_ms: 3, + sent_at: 3, + timestamp: 3, + sourceServiceId: CONTACT_A, + body: 'd', + readStatus: ReadStatus.Unread, + seenStatus: SeenStatus.Unseen, + unidentifiedDeliveryReceived: true, + expirationStartTimestamp: Date.now(), + expireTimer: DurationInSeconds.fromMillis(WEEK), + }, + ]); + }); });