diff --git a/ts/ConversationController.ts b/ts/ConversationController.ts index 0c5a0f0d82..556cb550c9 100644 --- a/ts/ConversationController.ts +++ b/ts/ConversationController.ts @@ -18,6 +18,10 @@ import { isGroupV1, isGroupV2, } from './util/whatTypeOfConversation.js'; +import { + doesAttachmentExist, + deleteAttachmentData, +} from './util/migrations.js'; import { isServiceIdString, normalizePni, @@ -1610,8 +1614,6 @@ export class ConversationController { if (avatarPath || profileAvatarPath) { drop( (async () => { - const { doesAttachmentExist, deleteAttachmentData } = - window.Signal.Migrations; if (avatarPath && (await doesAttachmentExist(avatarPath))) { await deleteAttachmentData(avatarPath); } diff --git a/ts/badges/badgeImageFileDownloader.ts b/ts/badges/badgeImageFileDownloader.ts index d4cf004bb6..b6db0bcfab 100644 --- a/ts/badges/badgeImageFileDownloader.ts +++ b/ts/badges/badgeImageFileDownloader.ts @@ -7,6 +7,7 @@ import { createLogger } from '../logging/log.js'; import { MINUTE } from '../util/durations/index.js'; import { missingCaseError } from '../util/missingCaseError.js'; import { waitForOnline } from '../util/waitForOnline.js'; +import { writeNewBadgeImageFileData } from '../util/migrations.js'; import { getBadgeImageFile, isOnline } from '../textsecure/WebAPI.js'; const log = createLogger('badgeImageFileDownloader'); @@ -92,8 +93,7 @@ async function downloadBadgeImageFile(url: string): Promise { await waitForOnline({ server: { isOnline }, timeout: 1 * MINUTE }); const imageFileData = await getBadgeImageFile(url); - const localPath = - await window.Signal.Migrations.writeNewBadgeImageFileData(imageFileData); + const localPath = await writeNewBadgeImageFileData(imageFileData); await DataWriter.badgeImageFileDownloaded(url, localPath); diff --git a/ts/groups.ts b/ts/groups.ts index 09132ddd56..41f8288db5 100644 --- a/ts/groups.ts +++ b/ts/groups.ts @@ -17,6 +17,11 @@ import { isMoreRecentThan } from './util/timestamp.js'; import { MINUTE, DurationInSeconds, SECOND } from './util/durations/index.js'; import { drop } from './util/drop.js'; import { dropNull } from './util/dropNull.js'; +import { + writeNewAttachmentData, + readAttachmentData, + deleteAttachmentData, +} from './util/migrations.js'; import type { ConversationAttributesType, GroupV2MemberType, @@ -1822,9 +1827,7 @@ export async function createGroupV2( try { avatarAttribute = { url: uploadedAvatar.key, - ...(await window.Signal.Migrations.writeNewAttachmentData( - uploadedAvatar.data - )), + ...(await writeNewAttachmentData(uploadedAvatar.data)), hash: uploadedAvatar.hash, }; } catch (err) { @@ -2232,8 +2235,7 @@ export async function initiateMigrationToGroupV2( const { avatar: currentAvatar } = conversation.attributes; if (currentAvatar?.path) { - const avatarData = - await window.Signal.Migrations.readAttachmentData(currentAvatar); + const avatarData = await readAttachmentData(currentAvatar); const { hash, key } = await uploadAvatar({ logId, publicParams, @@ -5597,9 +5599,7 @@ export async function applyNewAvatar( // Avatar has been dropped if (!newAvatarUrl && attributes.avatar) { if (attributes.avatar.path) { - await window.Signal.Migrations.deleteAttachmentData( - attributes.avatar.path - ); + await deleteAttachmentData(attributes.avatar.path); } result.avatar = undefined; } @@ -5646,11 +5646,9 @@ export async function applyNewAvatar( } if (attributes.avatar?.path) { - await window.Signal.Migrations.deleteAttachmentData( - attributes.avatar.path - ); + await deleteAttachmentData(attributes.avatar.path); } - const local = await window.Signal.Migrations.writeNewAttachmentData(data); + const local = await writeNewAttachmentData(data); result.avatar = { url: avatarUrlToUse, ...local, @@ -5663,7 +5661,7 @@ export async function applyNewAvatar( Errors.toLogFormat(error) ); if (result.avatar && result.avatar.path) { - await window.Signal.Migrations.deleteAttachmentData(result.avatar.path); + await deleteAttachmentData(result.avatar.path); } result.avatar = undefined; } diff --git a/ts/groups/joinViaLink.ts b/ts/groups/joinViaLink.ts index ff689d53ed..1015be1d14 100644 --- a/ts/groups/joinViaLink.ts +++ b/ts/groups/joinViaLink.ts @@ -27,6 +27,7 @@ import { } from '../groups.js'; import { createGroupV2JoinModal } from '../state/roots/createGroupV2JoinModal.js'; import { explodePromise } from '../util/explodePromise.js'; +import { deleteAttachmentData } from '../util/migrations.js'; import { isAccessControlEnabled } from './util.js'; import { isGroupV1 } from '../util/whatTypeOfConversation.js'; import { longRunningTaskWrapper } from '../util/longRunningTaskWrapper.js'; @@ -222,9 +223,7 @@ export async function joinViaLink(value: string): Promise { localAvatar?.loadingState === LoadingState.Loaded && localAvatar.value?.path ) { - await window.Signal.Migrations.deleteAttachmentData( - localAvatar.value.path - ); + await deleteAttachmentData(localAvatar.value.path); } resolve(); } catch (error) { @@ -428,9 +427,7 @@ export async function joinViaLink(value: string): Promise { // Dialog has been dismissed; we'll delete the unneeeded avatar if (!groupV2InfoRoot) { - await window.Signal.Migrations.deleteAttachmentData( - attributes.avatar.path - ); + await deleteAttachmentData(attributes.avatar.path); return; } } else { diff --git a/ts/jobs/AttachmentBackupManager.ts b/ts/jobs/AttachmentBackupManager.ts index 6dfb0b6b3f..e2f6776f2f 100644 --- a/ts/jobs/AttachmentBackupManager.ts +++ b/ts/jobs/AttachmentBackupManager.ts @@ -17,6 +17,7 @@ import { type JobManagerJobResultType, } from './JobManager.js'; import { strictAssert } from '../util/assert.js'; +import { getAbsoluteAttachmentPath as doGetAbsoluteAttachmentPath } from '../util/migrations.js'; import { type BackupsService, backupsService, @@ -185,7 +186,7 @@ class AttachmentPermanentlyMissingError extends Error {} class FileNotFoundOnTransitTierError extends Error {} type RunAttachmentBackupJobDependenciesType = { - getAbsoluteAttachmentPath: typeof window.Signal.Migrations.getAbsoluteAttachmentPath; + getAbsoluteAttachmentPath: typeof doGetAbsoluteAttachmentPath; backupMediaBatch?: typeof doBackupMediaBatch; backupsService: BackupsService; encryptAndUploadAttachment: typeof encryptAndUploadAttachment; @@ -199,8 +200,7 @@ export async function runAttachmentBackupJob( abortSignal: AbortSignal; }, dependencies: RunAttachmentBackupJobDependenciesType = { - getAbsoluteAttachmentPath: - window.Signal.Migrations.getAbsoluteAttachmentPath, + getAbsoluteAttachmentPath: doGetAbsoluteAttachmentPath, backupsService, backupMediaBatch: doBackupMediaBatch, encryptAndUploadAttachment, diff --git a/ts/jobs/AttachmentDownloadManager.ts b/ts/jobs/AttachmentDownloadManager.ts index 73d191dbea..1ecb7f5058 100644 --- a/ts/jobs/AttachmentDownloadManager.ts +++ b/ts/jobs/AttachmentDownloadManager.ts @@ -18,6 +18,10 @@ import { downloadAttachment as downloadAttachmentUtil, isIncrementalMacVerificationError, } from '../util/downloadAttachment.js'; +import { + deleteDownloadData as doDeleteDownloadData, + processNewAttachment as doProcessNewAttachment, +} from '../util/migrations.js'; import { DataReader, DataWriter } from '../sql/Client.js'; import { getValue } from '../RemoteConfig.js'; @@ -487,9 +491,9 @@ export class AttachmentDownloadManager extends JobManager { const message = await DataReader.getMessageById(messageId); if (!message) { log?.warn('Message not found; attempting to delete download path.'); - await window.Signal.Migrations.deleteDownloadData(downloadPath); + await deleteDownloadData(downloadPath); return undefined; } @@ -85,7 +86,7 @@ export class DeleteDownloadsJobQueue extends JobQueue { log?.warn( 'Target attachment not found; attempting to delete download path.' ); - await window.Signal.Migrations.deleteDownloadData(downloadPath); + await deleteDownloadData(downloadPath); return undefined; } @@ -96,7 +97,7 @@ export class DeleteDownloadsJobQueue extends JobQueue { throw new Error('Attachment still downloading'); } - await window.Signal.Migrations.deleteDownloadData(downloadPath); + await deleteDownloadData(downloadPath); const updatedMessage = { ...message, diff --git a/ts/jobs/helpers/sendNormalMessage.ts b/ts/jobs/helpers/sendNormalMessage.ts index 2cf85ea4e7..fd2038aa17 100644 --- a/ts/jobs/helpers/sendNormalMessage.ts +++ b/ts/jobs/helpers/sendNormalMessage.ts @@ -15,6 +15,13 @@ import { getSendOptions } from '../../util/getSendOptions.js'; import { handleMessageSend } from '../../util/handleMessageSend.js'; import { findAndFormatContact } from '../../util/findAndFormatContact.js'; import { uploadAttachment } from '../../util/uploadAttachment.js'; +import { + loadAttachmentData, + loadQuoteData, + loadPreviewData, + loadStickerData, + loadContactData, +} from '../../util/migrations.js'; import type { CallbackResultType } from '../../textsecure/Types.d.ts'; import { isSent } from '../../messages/MessageSendState.js'; import { isOutgoing, canReact } from '../../state/selectors/message.js'; @@ -744,8 +751,6 @@ async function uploadSingleAttachment({ message: MessageModel; targetTimestamp: number; }): Promise { - const { loadAttachmentData } = window.Signal.Migrations; - const withData = await loadAttachmentData(attachment); const uploaded = await uploadAttachment(withData); @@ -800,8 +805,6 @@ async function uploadLongMessageAttachment({ message: MessageModel; targetTimestamp: number; }): Promise { - const { loadAttachmentData } = window.Signal.Migrations; - const withData = await loadAttachmentData(attachment); const uploaded = await uploadAttachment(withData); @@ -846,8 +849,6 @@ async function uploadMessageQuote({ targetTimestamp: number; uploadQueue: PQueue; }): Promise { - const { loadQuoteData } = window.Signal.Migrations; - // We don't update the caches here because (1) we expect the caches to be populated // on initial send, so they should be there in the 99% case (2) if you're retrying // a failed message across restarts, we don't touch the cache for simplicity. If @@ -990,8 +991,6 @@ async function uploadMessagePreviews({ targetTimestamp: number; uploadQueue: PQueue; }): Promise | undefined> { - const { loadPreviewData } = window.Signal.Migrations; - // See uploadMessageQuote for comment on how we do caching for these // attachments. const startingPreview = getPropForTimestamp({ @@ -1078,8 +1077,6 @@ async function uploadMessageSticker( message: MessageModel, uploadQueue: PQueue ): Promise { - const { loadStickerData } = window.Signal.Migrations; - // See uploadMessageQuote for comment on how we do caching for these // attachments. const startingSticker = message.get('sticker'); @@ -1125,8 +1122,6 @@ async function uploadMessageContacts( message: MessageModel, uploadQueue: PQueue ): Promise | undefined> { - const { loadContactData } = window.Signal.Migrations; - // See uploadMessageQuote for comment on how we do caching for these // attachments. const contacts = await loadContactData(message.get('contact')); diff --git a/ts/jobs/helpers/sendStory.ts b/ts/jobs/helpers/sendStory.ts index 3974c518da..379e4a64cd 100644 --- a/ts/jobs/helpers/sendStory.ts +++ b/ts/jobs/helpers/sendStory.ts @@ -32,6 +32,7 @@ import { getSendOptions, getSendOptionsForRecipients, } from '../../util/getSendOptions.js'; +import { loadPreviewData, loadAttachmentData } from '../../util/migrations.js'; import { handleMessageSend } from '../../util/handleMessageSend.js'; import { handleMultipleSendErrors } from './handleMultipleSendErrors.js'; import { isGroupV2, isMe } from '../../util/whatTypeOfConversation.js'; @@ -164,9 +165,7 @@ export async function sendStory( }; } else { const hydratedPreview = ( - await window.Signal.Migrations.loadPreviewData([ - localAttachment.preview, - ]) + await loadPreviewData([localAttachment.preview]) )[0]; textAttachment = { @@ -180,8 +179,7 @@ export async function sendStory( }; } } else { - const hydratedAttachment = - await window.Signal.Migrations.loadAttachmentData(attachment); + const hydratedAttachment = await loadAttachmentData(attachment); fileAttachment = await uploadAttachment(hydratedAttachment); } diff --git a/ts/messageModifiers/AttachmentDownloads.ts b/ts/messageModifiers/AttachmentDownloads.ts index aa746805fd..03672b0446 100644 --- a/ts/messageModifiers/AttachmentDownloads.ts +++ b/ts/messageModifiers/AttachmentDownloads.ts @@ -10,6 +10,10 @@ import { doAttachmentsOnSameMessageMatch, isDownloaded, } from '../util/Attachment.js'; +import { + loadAttachmentData, + deleteAttachmentData, +} from '../util/migrations.js'; import { getMessageById } from '../messages/getMessageById.js'; import { trimMessageWhitespace } from '../types/BodyRange.js'; @@ -84,8 +88,7 @@ export async function addAttachmentToMessage( try { if (attachment.path) { - const loaded = - await window.Signal.Migrations.loadAttachmentData(attachment); + const loaded = await loadAttachmentData(attachment); attachmentData = loaded.data; } @@ -161,7 +164,7 @@ export async function addAttachmentToMessage( }); } finally { if (attachment.path) { - await window.Signal.Migrations.deleteAttachmentData(attachment.path); + await deleteAttachmentData(attachment.path); } if (!handledAnywhere) { log.warn( diff --git a/ts/messageModifiers/DeletesForMe.ts b/ts/messageModifiers/DeletesForMe.ts index db755ae492..398c76dbd4 100644 --- a/ts/messageModifiers/DeletesForMe.ts +++ b/ts/messageModifiers/DeletesForMe.ts @@ -11,6 +11,10 @@ import type { ConversationIdentifier, AddressableMessage, } from '../textsecure/messageReceiverEvents.js'; +import { + deleteAttachmentData, + deleteDownloadData, +} from '../util/migrations.js'; import { deleteAttachmentFromMessage, deleteMessage, @@ -111,9 +115,8 @@ export async function onDelete(item: DeleteForMeAttributesType): Promise { item.message, item.deleteAttachmentData, { - deleteAttachmentOnDisk: - window.Signal.Migrations.deleteAttachmentData, - deleteDownloadOnDisk: window.Signal.Migrations.deleteDownloadData, + deleteAttachmentOnDisk: deleteAttachmentData, + deleteDownloadOnDisk: deleteDownloadData, logId, } ); diff --git a/ts/messages/handleDataMessage.ts b/ts/messages/handleDataMessage.ts index 5dd84110e6..e9382d4b3b 100644 --- a/ts/messages/handleDataMessage.ts +++ b/ts/messages/handleDataMessage.ts @@ -14,6 +14,7 @@ import { messageHasPaymentEvent } from './payments.js'; import { getMessageIdForLogging } from '../util/idForLogging.js'; import { getSenderIdentifier } from '../util/getSenderIdentifier.js'; import { isNormalNumber } from '../util/isNormalNumber.js'; +import { upgradeMessageSchema } from '../util/migrations.js'; import { getOwn } from '../util/getOwn.js'; import { SendActionType, @@ -87,7 +88,6 @@ export async function handleDataMessage( options: { data?: SentEventData } = {} ): Promise { const { data } = options; - const { upgradeMessageSchema } = window.Signal.Migrations; // This function is called from the background script in a few scenarios: // 1. on an incoming message diff --git a/ts/messages/migrateMessageData.ts b/ts/messages/migrateMessageData.ts index 78697105aa..b2fe8f8903 100644 --- a/ts/messages/migrateMessageData.ts +++ b/ts/messages/migrateMessageData.ts @@ -13,6 +13,7 @@ import type { AciString } from '../types/ServiceId.js'; import * as Errors from '../types/errors.js'; import { DataReader, DataWriter } from '../sql/Client.js'; import { postSaveUpdates } from '../util/cleanup.js'; +import { upgradeMessageSchema as doUpgradeMessageSchema } from '../util/migrations.js'; import { createLogger } from '../logging/log.js'; import { itemStorage } from '../textsecure/Storage.js'; @@ -166,7 +167,7 @@ export async function migrateBatchOfMessages({ return migrationQueue.add(() => _migrateMessageData({ numMessagesPerBatch, - upgradeMessageSchema: window.Signal.Migrations.upgradeMessageSchema, + upgradeMessageSchema: doUpgradeMessageSchema, getMessagesNeedingUpgrade: DataReader.getMessagesNeedingUpgrade, saveMessagesIndividually: DataWriter.saveMessagesIndividually, incrementMessagesMigrationAttempts: diff --git a/ts/models/conversations.ts b/ts/models/conversations.ts index e8d9adf837..bb68040196 100644 --- a/ts/models/conversations.ts +++ b/ts/models/conversations.ts @@ -17,6 +17,16 @@ import type { } from '../model-types.d.ts'; import { DataReader, DataWriter } from '../sql/Client.js'; import { getConversation } from '../util/getConversation.js'; +import { + copyAttachmentIntoTempDirectory, + deleteAttachmentData, + doesAttachmentExist, + getAbsoluteAttachmentPath, + getAbsoluteTempPath, + readStickerData, + upgradeMessageSchema, + writeNewAttachmentData, +} from '../util/migrations.js'; import { drop } from '../util/drop.js'; import { isShallowEqual } from '../util/isShallowEqual.js'; import { getInitials } from '../util/getInitials.js'; @@ -3840,8 +3850,6 @@ export class ConversationModel { } async sendStickerMessage(packId: string, stickerId: number): Promise { - const { readStickerData } = window.Signal.Migrations; - const packData = Stickers.getStickerPack(packId); const stickerData = Stickers.getSticker(packId, stickerId); if (!stickerData || !packData) { @@ -4025,9 +4033,6 @@ export class ConversationModel { extraReduxActions?: () => void; } = {} ): Promise { - const { deleteAttachmentData, upgradeMessageSchema } = - window.Signal.Migrations; - if (this.isGroupV1AndDisabled()) { return; } @@ -4962,12 +4967,6 @@ export class ConversationModel { decryptionKey?: Uint8Array | null | undefined; forceFetch?: boolean; }): Promise { - const { - deleteAttachmentData, - doesAttachmentExist, - writeNewAttachmentData, - } = window.Signal.Migrations; - const { avatarUrl, decryptionKey, forceFetch } = options; if (isMe(this.attributes)) { if (avatarUrl) { @@ -5471,8 +5470,6 @@ export class ConversationModel { url: string; absolutePath?: string; }> { - const { getAbsoluteTempPath } = window.Signal.Migrations; - const saveToDisk = shouldSaveNotificationAvatarToDisk(); const avatarUrl = getLocalAvatarUrl(this.attributes); if (avatarUrl) { @@ -5494,13 +5491,6 @@ export class ConversationModel { } async #getTemporaryAvatarPath(): Promise { - const { - copyAttachmentIntoTempDirectory, - deleteAttachmentData, - getAbsoluteAttachmentPath, - getAbsoluteTempPath, - } = window.Signal.Migrations; - const avatar = getAvatar(this.attributes); if (avatar?.path == null) { return undefined; diff --git a/ts/services/LinkPreview.ts b/ts/services/LinkPreview.ts index f983f661b5..46228fd4bb 100644 --- a/ts/services/LinkPreview.ts +++ b/ts/services/LinkPreview.ts @@ -20,6 +20,7 @@ import * as Bytes from '../Bytes.js'; import { sha256 } from '../Crypto.js'; import * as LinkPreview from '../types/LinkPreview.js'; import { getLinkPreviewSetting } from '../util/Settings.js'; +import { readTempData, readStickerData } from '../util/migrations.js'; import * as Stickers from '../types/Stickers.js'; import * as VisualAttachment from '../types/VisualAttachment.js'; import { createLogger } from '../logging/log.js'; @@ -472,8 +473,8 @@ async function getStickerPackPreview( const sticker = pack.stickers[coverStickerId]; const data = pack.status === 'ephemeral' - ? await window.Signal.Migrations.readTempData(sticker) - : await window.Signal.Migrations.readStickerData(sticker); + ? await readTempData(sticker) + : await readStickerData(sticker); if (abortSignal.aborted) { return null; diff --git a/ts/services/MessageCache.ts b/ts/services/MessageCache.ts index 4bec429661..c32e9c7ec3 100644 --- a/ts/services/MessageCache.ts +++ b/ts/services/MessageCache.ts @@ -9,6 +9,7 @@ import { MessageModel } from '../models/messages.js'; import { DataReader, DataWriter } from '../sql/Client.js'; import { getMessageConversation } from '../util/getMessageConversation.js'; import { getSenderIdentifier } from '../util/getSenderIdentifier.js'; +import { upgradeMessageSchema } from '../util/migrations.js'; import { isNotNil } from '../util/isNotNil.js'; import { isStory } from '../messages/helpers.js'; import { getStoryDataFromMessageAttributes } from './storyLoader.js'; @@ -155,8 +156,7 @@ export class MessageCache { return; } const startingAttributes = message.attributes; - const upgradedAttributes = - await window.Signal.Migrations.upgradeMessageSchema(startingAttributes); + const upgradedAttributes = await upgradeMessageSchema(startingAttributes); if (startingAttributes !== upgradedAttributes) { message.set(upgradedAttributes); } diff --git a/ts/services/backups/import.ts b/ts/services/backups/import.ts index 0c6f056e1f..1fbb9a35c4 100644 --- a/ts/services/backups/import.ts +++ b/ts/services/backups/import.ts @@ -60,6 +60,7 @@ import type { QuotedMessageType, } from '../../model-types.d.ts'; import { assertDev, strictAssert } from '../../util/assert.js'; +import { upgradeMessageSchema } from '../../util/migrations.js'; import { getCheckedTimestampFromLong, getCheckedTimestampOrUndefinedFromLong, @@ -590,7 +591,7 @@ export class BackupImportStream extends Writable { attributes: MessageAttributesType ): Promise { try { - return await window.Signal.Migrations.upgradeMessageSchema(attributes); + return await upgradeMessageSchema(attributes); } catch (error) { log.error( `${this.#logId}: failed to migrate a message ${attributes.sent_at}, ` + diff --git a/ts/services/backups/index.ts b/ts/services/backups/index.ts index 93b9d0d231..3b4458f6d6 100644 --- a/ts/services/backups/index.ts +++ b/ts/services/backups/index.ts @@ -22,6 +22,10 @@ import * as Bytes from '../../Bytes.js'; import { strictAssert } from '../../util/assert.js'; import { drop } from '../../util/drop.js'; import { TEMP_PATH } from '../../util/basePaths.js'; +import { + getAbsoluteDownloadsPath, + saveAttachmentToDisk, +} from '../../util/migrations.js'; import { waitForAllBatchers } from '../../util/batcher.js'; import { flushAllWaitBatchers } from '../../util/waitBatcher.js'; import { DelimitedStream } from '../../util/DelimitedStream.js'; @@ -210,8 +214,7 @@ export class BackupsService { const ephemeralKey = itemStorage.get('backupEphemeralKey'); - const absoluteDownloadPath = - window.Signal.Migrations.getAbsoluteDownloadsPath(backupDownloadPath); + const absoluteDownloadPath = getAbsoluteDownloadsPath(backupDownloadPath); let hasBackup = false; // eslint-disable-next-line no-constant-condition @@ -521,8 +524,6 @@ export class BackupsService { public async exportWithDialog(): Promise { const { data } = await this.exportBackupData(); - const { saveAttachmentToDisk } = window.Signal.Migrations; - await saveAttachmentToDisk({ name: 'backup.bin', data, diff --git a/ts/services/backups/util/filePointers.ts b/ts/services/backups/util/filePointers.ts index fce39b680b..80afa31a6e 100644 --- a/ts/services/backups/util/filePointers.ts +++ b/ts/services/backups/util/filePointers.ts @@ -9,6 +9,7 @@ import { } from '../../../types/MIME.js'; import { createLogger } from '../../../logging/log.js'; import type { AttachmentType } from '../../../types/Attachment.js'; +import { getAbsoluteAttachmentPath } from '../../../util/migrations.js'; import { hasRequiredInformationForBackup, hasRequiredInformationToDownloadFromTransitTier, @@ -399,9 +400,7 @@ function getLocatorInfoForAttachment({ if (isLocalBackup && isBackupable) { const attachmentExistsLocally = attachment.path != null && - existsSync( - window.Signal.Migrations.getAbsoluteAttachmentPath(attachment.path) - ); + existsSync(getAbsoluteAttachmentPath(attachment.path)); if (attachmentExistsLocally && attachment.localKey) { locatorInfo.localKey = Bytes.fromBase64(attachment.localKey); diff --git a/ts/services/calling.ts b/ts/services/calling.ts index a3145bec99..fe99597717 100644 --- a/ts/services/calling.ts +++ b/ts/services/calling.ts @@ -63,6 +63,7 @@ import type { import type { ConversationType } from '../state/ducks/conversations.js'; import { getConversationCallMode } from '../state/ducks/conversations.js'; import { isMe } from '../util/whatTypeOfConversation.js'; +import { getAbsoluteTempPath } from '../util/migrations.js'; import type { AvailableIODevicesType, CallEndedReason, @@ -2505,7 +2506,7 @@ export class CallingClass { ); url = result.url; absolutePath = result.path - ? window.Signal.Migrations.getAbsoluteTempPath(result.path) + ? getAbsoluteTempPath(result.path) : undefined; } else { const conversation = window.ConversationController.get(conversationId); diff --git a/ts/services/contactSync.ts b/ts/services/contactSync.ts index 06f3aa03f4..30187641d1 100644 --- a/ts/services/contactSync.ts +++ b/ts/services/contactSync.ts @@ -20,6 +20,11 @@ import * as Errors from '../types/errors.js'; import type { ValidateConversationType } from '../model-types.d.ts'; import type { ConversationModel } from '../models/conversations.js'; import { validateConversation } from '../util/validateConversation.js'; +import { + writeNewAttachmentData, + deleteAttachmentData, + doesAttachmentExist, +} from '../util/migrations.js'; import { isDirectConversation, isMe } from '../util/whatTypeOfConversation.js'; import { createLogger } from '../logging/log.js'; import { dropNull } from '../util/dropNull.js'; @@ -55,8 +60,6 @@ async function updateConversationFromContactSync( sentAt: number ): Promise { const logId = `updateConversationFromContactSync(${conversation.idForLogging()}`; - const { writeNewAttachmentData, deleteAttachmentData, doesAttachmentExist } = - window.Signal.Migrations; conversation.set({ name: dropNull(details.name), @@ -137,7 +140,7 @@ async function downloadAndParseContactAttachment( }); } finally { if (downloaded?.path) { - await window.Signal.Migrations.deleteAttachmentData(downloaded.path); + await deleteAttachmentData(downloaded.path); } } } diff --git a/ts/services/releaseNotesFetcher.ts b/ts/services/releaseNotesFetcher.ts index 8e95d38505..b191f777d2 100644 --- a/ts/services/releaseNotesFetcher.ts +++ b/ts/services/releaseNotesFetcher.ts @@ -11,6 +11,10 @@ import { createLogger } from '../logging/log.js'; import * as Errors from '../types/errors.js'; import { HTTPError } from '../types/HTTPError.js'; import { drop } from '../util/drop.js'; +import { + writeNewAttachmentData, + processNewAttachment, +} from '../util/migrations.js'; import { strictAssert } from '../util/assert.js'; import type { MessageAttributesType } from '../model-types.js'; import { ReadStatus } from '../messages/MessageReadStatus.js'; @@ -233,18 +237,15 @@ export class ReleaseNotesFetcher { } const localAttachment = - await window.Signal.Migrations.writeNewAttachmentData( - rawAttachmentData - ); + await writeNewAttachmentData(rawAttachmentData); - const processedAttachment = - await window.Signal.Migrations.processNewAttachment( - { - ...localAttachment, - contentType: stringToMIMEType(contentType), - }, - 'attachment' - ); + const processedAttachment = await processNewAttachment( + { + ...localAttachment, + contentType: stringToMIMEType(contentType), + }, + 'attachment' + ); return { hydratedNote, processedAttachment }; } diff --git a/ts/services/writeProfile.ts b/ts/services/writeProfile.ts index a40d3d0410..71e874e464 100644 --- a/ts/services/writeProfile.ts +++ b/ts/services/writeProfile.ts @@ -11,6 +11,10 @@ import { encryptProfileData } from '../util/encryptProfileData.js'; import { getProfile } from '../util/getProfile.js'; import { singleProtoJobQueue } from '../jobs/singleProtoJobQueue.js'; import { strictAssert } from '../util/assert.js'; +import { + writeNewAttachmentData, + deleteAttachmentData, +} from '../util/migrations.js'; import { isWhitespace } from '../util/whitespaceStringUtil.js'; import { imagePathToBytes } from '../util/imagePathToBytes.js'; import { getLocalAvatarUrl } from '../util/avatarUtils.js'; @@ -112,10 +116,8 @@ export async function writeProfile( if (hash !== avatarHash) { log.info('removing old avatar and saving the new one'); const [local] = await Promise.all([ - window.Signal.Migrations.writeNewAttachmentData(newAvatar), - rawAvatarPath - ? window.Signal.Migrations.deleteAttachmentData(rawAvatarPath) - : undefined, + writeNewAttachmentData(newAvatar), + rawAvatarPath ? deleteAttachmentData(rawAvatarPath) : undefined, ]); maybeProfileAvatarUpdate = { profileAvatar: { hash, ...local }, @@ -126,7 +128,7 @@ export async function writeProfile( } else if (rawAvatarPath) { log.info('removing avatar'); await Promise.all([ - window.Signal.Migrations.deleteAttachmentData(rawAvatarPath), + deleteAttachmentData(rawAvatarPath), itemStorage.put('avatarUrl', undefined), ]); diff --git a/ts/signal.ts b/ts/signal.ts index 7b1f82f66a..3a1402859d 100644 --- a/ts/signal.ts +++ b/ts/signal.ts @@ -3,446 +3,19 @@ // The idea with this file is to make it webpackable for the style guide -import type { ReadonlyDeep } from 'type-fest'; - import OS from './util/os/osMain.js'; import { isProduction } from './util/version.js'; import { DataReader, DataWriter } from './sql/Client.js'; -// Types -import * as TypesAttachment from './util/Attachment.js'; -import * as VisualAttachment from './types/VisualAttachment.js'; -import * as MessageType from './types/Message2.js'; - // Processes / Services import { calling } from './services/calling.js'; import * as storage from './services/storage.js'; import { backupsService } from './services/backups/index.js'; import * as donations from './services/donations.js'; -import type { LoggerType } from './types/Logging.js'; -import type { - AttachmentType, - AttachmentWithHydratedData, - AddressableAttachmentType, - LocalAttachmentV2Type, -} from './types/Attachment.js'; -import type { - MessageAttributesType, - QuotedMessageType, -} from './model-types.d.ts'; import type { SignalCoreType } from './window.d.ts'; -import type { - EmbeddedContactType, - EmbeddedContactWithHydratedAvatar, -} from './types/EmbeddedContact.js'; -import type { - LinkPreviewType, - LinkPreviewWithHydratedData, -} from './types/message/LinkPreviews.js'; -import type { StickerType, StickerWithHydratedData } from './types/Stickers.js'; -import type { MessageAttachmentType } from './types/AttachmentDownload.js'; - -type EncryptedReader = ( - attachment: Partial -) => Promise; - -type EncryptedWriter = (data: Uint8Array) => Promise; - -type MigrationsModuleType = { - attachmentsPath: string; - copyStickerIntoAttachmentsDirectory: ( - path: string - ) => Promise<{ path: string; size: number }>; - copyAttachmentIntoTempDirectory: ( - path: string - ) => Promise<{ path: string; size: number }>; - deleteAttachmentData: (path: string) => Promise; - deleteAvatar: (path: string) => Promise; - deleteDownloadData: (path: string) => Promise; - deleteDraftFile: (path: string) => Promise; - deleteExternalMessageFiles: ( - attributes: MessageAttributesType - ) => Promise; - deleteSticker: (path: string) => Promise; - deleteTempFile: (path: string) => Promise; - doesAttachmentExist: (path: string) => Promise; - getAbsoluteAttachmentPath: (path: string) => string; - getAbsoluteAvatarPath: (src: string) => string; - getAbsoluteBadgeImageFilePath: (path: string) => string; - getAbsoluteDownloadsPath: (path: string) => string; - getAbsoluteDraftPath: (path: string) => string; - getAbsoluteStickerPath: (path: string) => string; - getAbsoluteTempPath: (path: string) => string; - getUnusedFilename: (options: { - filename: string; - baseDir?: string; - }) => string; - loadAttachmentData: ( - attachment: Partial - ) => Promise; - loadContactData: ( - contact: ReadonlyArray> | undefined - ) => Promise | undefined>; - loadMessage: ( - message: MessageAttributesType - ) => Promise; - loadPreviewData: ( - preview: ReadonlyArray> | undefined - ) => Promise>; - loadQuoteData: ( - quote: QuotedMessageType | null | undefined - ) => Promise; - loadStickerData: ( - sticker: StickerType | undefined - ) => Promise; - readAttachmentData: EncryptedReader; - readAvatarData: EncryptedReader; - readDraftData: EncryptedReader; - readStickerData: EncryptedReader; - readTempData: EncryptedReader; - saveAttachmentToDisk: (options: { - data: Uint8Array; - name: string; - baseDir?: string; - }) => Promise; - processNewAttachment: ( - attachment: AttachmentType, - attachmentType: MessageAttachmentType - ) => Promise; - processNewSticker: (stickerData: Uint8Array) => Promise< - LocalAttachmentV2Type & { - width: number; - height: number; - } - >; - processNewEphemeralSticker: (stickerData: Uint8Array) => Promise< - LocalAttachmentV2Type & { - width: number; - height: number; - } - >; - upgradeMessageSchema: ( - attributes: MessageAttributesType, - options?: { maxVersion?: number } - ) => Promise; - writeNewAttachmentData: EncryptedWriter; - writeNewDraftData: EncryptedWriter; - writeNewAvatarData: EncryptedWriter; - writeNewStickerData: EncryptedWriter; - writeNewBadgeImageFileData: (data: Uint8Array) => Promise; - writeNewPlaintextTempData: (data: Uint8Array) => Promise; -}; - -export function initializeMigrations({ - getRegionCode, - Attachments, - Type, - VisualType, - logger, - userDataPath, -}: { - getRegionCode: () => string | undefined; - Attachments: AttachmentsModuleType; - Type: typeof TypesAttachment; - VisualType: typeof VisualAttachment; - logger: LoggerType; - userDataPath: string; -}): MigrationsModuleType { - if (!Attachments) { - throw new Error('initializeMigrations: Missing provided attachments!'); - } - const { - createAbsolutePathGetter, - createPlaintextReader, - createWriterForNew, - createDoesExist, - getAvatarsPath, - getDraftPath, - getDownloadsPath, - getPath, - getStickersPath, - getBadgesPath, - getTempPath, - getUnusedFilename, - readAndDecryptDataFromDisk, - saveAttachmentToDisk, - } = Attachments; - const { - getImageDimensions, - makeImageThumbnail, - makeObjectUrl, - makeVideoScreenshot, - revokeObjectUrl, - } = VisualType; - - const attachmentsPath = getPath(userDataPath); - - function createEncryptedReader(basePath: string): EncryptedReader { - const fallbackReader = createPlaintextReader(basePath); - const pathGetter = createAbsolutePathGetter(basePath); - - return async ( - attachment: Partial - ): Promise => { - // In-memory - if (attachment.data != null) { - return attachment.data; - } - - if (attachment.path == null) { - throw new Error('Attachment was not downloaded yet'); - } - - if (attachment.version !== 2) { - return fallbackReader(attachment.path); - } - - if (attachment.localKey == null || attachment.size == null) { - throw new Error('Failed to decrypt v2 attachment'); - } - - const absolutePath = pathGetter(attachment.path); - - return readAndDecryptDataFromDisk({ - absolutePath, - keysBase64: attachment.localKey, - size: attachment.size, - }); - }; - } - - function createEncryptedWriterForNew(basePath: string): EncryptedWriter { - const pathGetter = createAbsolutePathGetter(basePath); - - return data => - Attachments.writeNewAttachmentData({ - data, - getAbsoluteAttachmentPath: pathGetter, - }); - } - - const readAttachmentData = createEncryptedReader(attachmentsPath); - const loadAttachmentData = Type.loadData(readAttachmentData); - const loadContactData = MessageType.loadContactData(loadAttachmentData); - const loadPreviewData = MessageType.loadPreviewData(loadAttachmentData); - const loadQuoteData = MessageType.loadQuoteData(loadAttachmentData); - const loadStickerData = MessageType.loadStickerData(loadAttachmentData); - const getAbsoluteAttachmentPath = createAbsolutePathGetter(attachmentsPath); - const deleteAttachmentOnDisk = Attachments.createDeleter(attachmentsPath); - const writeNewAttachmentData = createEncryptedWriterForNew(attachmentsPath); - const doesAttachmentExist = createDoesExist(attachmentsPath); - - const stickersPath = getStickersPath(userDataPath); - const getAbsoluteStickerPath = createAbsolutePathGetter(stickersPath); - const writeNewStickerData = createEncryptedWriterForNew(stickersPath); - const deleteSticker = Attachments.createDeleter(stickersPath); - const readStickerData = createEncryptedReader(stickersPath); - const copyStickerIntoAttachmentsDirectory = - Attachments.copyIntoAttachmentsDirectory({ - sourceDir: stickersPath, - targetDir: attachmentsPath, - }); - - const badgesPath = getBadgesPath(userDataPath); - const getAbsoluteBadgeImageFilePath = createAbsolutePathGetter(badgesPath); - const writeNewBadgeImageFileData = createWriterForNew(badgesPath, '.svg'); - - const tempPath = getTempPath(userDataPath); - const getAbsoluteTempPath = createAbsolutePathGetter(tempPath); - const writeNewTempData = createEncryptedWriterForNew(tempPath); - const writeNewPlaintextTempData = createWriterForNew(tempPath); - const deleteTempFile = Attachments.createDeleter(tempPath); - const readTempData = createEncryptedReader(tempPath); - const copyAttachmentIntoTempDirectory = - Attachments.copyIntoAttachmentsDirectory({ - sourceDir: attachmentsPath, - targetDir: tempPath, - }); - - const draftPath = getDraftPath(userDataPath); - const getAbsoluteDraftPath = createAbsolutePathGetter(draftPath); - const writeNewDraftData = createEncryptedWriterForNew(draftPath); - const deleteDraftFile = Attachments.createDeleter(draftPath); - const readDraftData = createEncryptedReader(draftPath); - - const downloadsPath = getDownloadsPath(userDataPath); - const getAbsoluteDownloadsPath = createAbsolutePathGetter(downloadsPath); - const deleteDownloadOnDisk = Attachments.createDeleter(downloadsPath); - - const avatarsPath = getAvatarsPath(userDataPath); - const readAvatarData = createEncryptedReader(avatarsPath); - const getAbsoluteAvatarPath = createAbsolutePathGetter(avatarsPath); - const writeNewAvatarData = createEncryptedWriterForNew(avatarsPath); - const deleteAvatar = Attachments.createDeleter(avatarsPath); - - return { - attachmentsPath, - copyStickerIntoAttachmentsDirectory, - copyAttachmentIntoTempDirectory, - deleteAttachmentData: deleteAttachmentOnDisk, - deleteAvatar, - deleteDownloadData: deleteDownloadOnDisk, - deleteDraftFile, - deleteExternalMessageFiles: MessageType.deleteAllExternalFiles({ - deleteAttachmentOnDisk, - deleteDownloadOnDisk, - }), - deleteSticker, - deleteTempFile, - doesAttachmentExist, - getAbsoluteAttachmentPath, - getAbsoluteAvatarPath, - getAbsoluteBadgeImageFilePath, - getAbsoluteDownloadsPath, - getAbsoluteDraftPath, - getAbsoluteStickerPath, - getAbsoluteTempPath, - getUnusedFilename, - loadAttachmentData, - loadContactData, - loadMessage: MessageType.createAttachmentLoader(loadAttachmentData), - loadPreviewData, - loadQuoteData, - loadStickerData, - readAttachmentData, - readAvatarData, - readDraftData, - readStickerData, - readTempData, - saveAttachmentToDisk, - processNewAttachment: ( - attachment: AttachmentType, - attachmentType: MessageAttachmentType - ) => - MessageType.processNewAttachment(attachment, attachmentType, { - writeNewAttachmentData, - makeObjectUrl, - revokeObjectUrl, - getImageDimensions, - makeImageThumbnail, - makeVideoScreenshot, - logger, - }), - processNewSticker: (stickerData: Uint8Array) => - MessageType.processNewSticker(stickerData, false, { - writeNewStickerData, - getImageDimensions, - logger, - }), - processNewEphemeralSticker: (stickerData: Uint8Array) => - MessageType.processNewSticker(stickerData, true, { - writeNewStickerData: writeNewTempData, - getImageDimensions, - logger, - }), - upgradeMessageSchema: ( - message: MessageAttributesType, - options: { maxVersion?: number } = {} - ) => { - const { maxVersion } = options; - - return MessageType.upgradeSchema(message, { - deleteAttachmentOnDisk, - doesAttachmentExist, - getImageDimensions, - getRegionCode, - makeImageThumbnail, - makeObjectUrl, - makeVideoScreenshot, - readAttachmentData, - revokeObjectUrl, - writeNewAttachmentData, - writeNewStickerData, - logger, - maxVersion, - }); - }, - writeNewAttachmentData, - writeNewAvatarData, - writeNewDraftData, - writeNewBadgeImageFileData, - writeNewPlaintextTempData, - writeNewStickerData, - }; -} - -type StringGetterType = (basePath: string) => string; - -type AttachmentsModuleType = { - getAvatarsPath: StringGetterType; - getBadgesPath: StringGetterType; - getDownloadsPath: StringGetterType; - getDraftPath: StringGetterType; - getPath: StringGetterType; - getStickersPath: StringGetterType; - getTempPath: StringGetterType; - getUpdateCachePath: StringGetterType; - - createDeleter: (root: string) => (relativePath: string) => Promise; - - createPlaintextReader: ( - root: string - ) => (relativePath: string) => Promise; - - copyIntoAttachmentsDirectory: (options: { - sourceDir: string; - targetDir: string; - }) => (sourcePath: string) => Promise<{ path: string; size: number }>; - - createWriterForNew: ( - root: string, - suffix?: string - ) => (bytes: Uint8Array) => Promise; - - createAbsolutePathGetter: ( - rootPath: string - ) => (relativePath: string) => string; - - createDoesExist: (root: string) => (relativePath: string) => Promise; - getUnusedFilename: (options: { - filename: string; - baseDir?: string; - }) => string; - saveAttachmentToDisk: ({ - data, - name, - dirName, - }: { - data: Uint8Array; - name: string; - dirName?: string; - }) => Promise; - - readAndDecryptDataFromDisk: (options: { - absolutePath: string; - keysBase64: string; - size: number; - }) => Promise; - - writeNewAttachmentData: (options: { - data: Uint8Array; - getAbsoluteAttachmentPath: (relativePath: string) => string; - }) => Promise; -}; - -export const setup = (options: { - Attachments: AttachmentsModuleType; - getRegionCode: () => string | undefined; - logger: LoggerType; - userDataPath: string; -}): SignalCoreType => { - const { Attachments, getRegionCode, logger, userDataPath } = options; - - const Migrations = initializeMigrations({ - getRegionCode, - Attachments, - Type: TypesAttachment, - VisualType: VisualAttachment, - logger, - userDataPath, - }); +export const setup = (): SignalCoreType => { // Only for testing const Services = { storage, @@ -452,7 +25,6 @@ export const setup = (options: { }; return { - Migrations, OS, ...(isProduction(window.getVersion()) diff --git a/ts/sql/Client.ts b/ts/sql/Client.ts index 08dd857cfc..96323d3827 100644 --- a/ts/sql/Client.ts +++ b/ts/sql/Client.ts @@ -17,6 +17,7 @@ import { deleteExternalFiles } from '../types/Conversation.js'; import { createBatcher } from '../util/batcher.js'; import { assertDev, softAssert } from '../util/assert.js'; import { mapObjectWithSpec } from '../util/mapObjectWithSpec.js'; +import { deleteAttachmentData } from '../util/migrations.js'; import { cleanDataForIpc } from './cleanDataForIpc.js'; import createTaskWithTimeout from '../textsecure/TaskWithTimeout.js'; import { isValidUuid, isValidUuidV7 } from '../util/isValidUuid.js'; @@ -553,7 +554,7 @@ async function removeConversation(id: string): Promise { if (existing) { await writableChannel.removeConversation(id); await deleteExternalFiles(existing, { - deleteAttachmentData: window.Signal.Migrations.deleteAttachmentData, + deleteAttachmentData, }); } } diff --git a/ts/state/ducks/conversations.ts b/ts/state/ducks/conversations.ts index 2f2646e8b8..87bcc14172 100644 --- a/ts/state/ducks/conversations.ts +++ b/ts/state/ducks/conversations.ts @@ -17,6 +17,13 @@ import { retryPlaceholders } from '../../services/retryPlaceholders.js'; import { getOwn } from '../../util/getOwn.js'; import { assertDev, strictAssert } from '../../util/assert.js'; import { drop } from '../../util/drop.js'; +import { + deleteAvatar, + writeNewAvatarData, + getUnusedFilename, + readAttachmentData, + saveAttachmentToDisk, +} from '../../util/migrations.js'; import type { DurationInSeconds } from '../../util/durations/index.js'; import * as universalExpireTimer from '../../util/universalExpireTimer.js'; import * as Attachment from '../../util/Attachment.js'; @@ -1631,7 +1638,7 @@ function deleteAvatarFromDisk( ): ThunkAction { return async (dispatch, getState) => { if (avatarData.imagePath) { - await window.Signal.Migrations.deleteAvatar(avatarData.imagePath); + await deleteAvatar(avatarData.imagePath); } else { log.info( 'No path for avatarData. Removing from userAvatarData, but not disk' @@ -2271,8 +2278,9 @@ function saveAvatarToDisk( strictAssert(conversationId, 'conversationId not provided'); - const { path: imagePath, ...localImage } = - await window.Signal.Migrations.writeNewAvatarData(avatarData.buffer); + const { path: imagePath, ...localImage } = await writeNewAvatarData( + avatarData.buffer + ); const avatars = await getAvatarsAndUpdateConversation( getState().conversations, @@ -2882,8 +2890,9 @@ function composeSaveAvatarToDisk( throw new Error('No avatar Uint8Array provided'); } - const { path: imagePath, ...localImage } = - await window.Signal.Migrations.writeNewAvatarData(avatarData.buffer); + const { path: imagePath, ...localImage } = await writeNewAvatarData( + avatarData.buffer + ); dispatch({ type: COMPOSE_ADD_AVATAR, @@ -2901,7 +2910,7 @@ function composeDeleteAvatarFromDisk( ): ThunkAction { return async dispatch => { if (avatarData.imagePath) { - await window.Signal.Migrations.deleteAvatar(avatarData.imagePath); + await deleteAvatar(avatarData.imagePath); } else { log.info( 'No path for avatarData. Removing from userAvatarData, but not disk' @@ -4105,9 +4114,6 @@ function saveAttachment( return; } - const { getUnusedFilename, readAttachmentData, saveAttachmentToDisk } = - window.Signal.Migrations; - const fullPath = await Attachment.save({ attachment, index: index + 1, @@ -4180,9 +4186,6 @@ function saveAttachments( return; } - const { getUnusedFilename, readAttachmentData, saveAttachmentToDisk } = - window.Signal.Migrations; - let fullPath; let index = 0; try { diff --git a/ts/state/ducks/lightbox.ts b/ts/state/ducks/lightbox.ts index 36a9d55f9a..1cb92248e4 100644 --- a/ts/state/ducks/lightbox.ts +++ b/ts/state/ducks/lightbox.ts @@ -31,6 +31,11 @@ import { getLocalAttachmentUrl, AttachmentDisposition, } from '../../util/getLocalAttachmentUrl.js'; +import { + deleteTempFile, + copyAttachmentIntoTempDirectory, + getAbsoluteAttachmentPath, +} from '../../util/migrations.js'; import { isTapToView, getPropsForAttachment } from '../selectors/message.js'; import { SHOW_TOAST } from './toast.js'; import { ToastType } from '../../types/Toast.js'; @@ -130,7 +135,7 @@ function closeLightbox(): ThunkAction< if (!item.attachment.path) { return; } - void window.Signal.Migrations.deleteTempFile(item.attachment.path); + void deleteTempFile(item.attachment.path); }); } @@ -194,9 +199,6 @@ function showLightboxForViewOnceMedia( ); } - const { copyAttachmentIntoTempDirectory, getAbsoluteAttachmentPath } = - window.Signal.Migrations; - const absolutePath = getAbsoluteAttachmentPath(firstAttachment.path); const { path: tempPath } = await copyAttachmentIntoTempDirectory(absolutePath); diff --git a/ts/state/selectors/badges.ts b/ts/state/selectors/badges.ts index 554f0bed75..749e8cf6e4 100644 --- a/ts/state/selectors/badges.ts +++ b/ts/state/selectors/badges.ts @@ -8,6 +8,7 @@ import type { StateType } from '../reducer.js'; import type { BadgesStateType } from '../ducks/badges.js'; import type { BadgeType } from '../../badges/types.js'; import { getOwn } from '../../util/getOwn.js'; +import { getAbsoluteBadgeImageFilePath } from '../../util/migrations.js'; import type { ConversationType } from '../ducks/conversations.js'; const { mapValues } = lodash; @@ -25,9 +26,7 @@ export const getBadgesById = createSelector(getBadgeState, state => imageFile.localPath ? { ...imageFile, - localPath: window.Signal.Migrations.getAbsoluteBadgeImageFilePath( - imageFile.localPath - ), + localPath: getAbsoluteBadgeImageFilePath(imageFile.localPath), } : imageFile ) diff --git a/ts/state/smart/Preferences.tsx b/ts/state/smart/Preferences.tsx index 0fafd24b59..2f9f05b7a3 100644 --- a/ts/state/smart/Preferences.tsx +++ b/ts/state/smart/Preferences.tsx @@ -29,6 +29,7 @@ import { } from '../../textsecure/WebAPI.js'; import { DEFAULT_CONVERSATION_COLOR } from '../../types/Colors.js'; import { isBackupFeatureEnabled } from '../../util/isBackupEnabled.js'; +import { saveAttachmentToDisk } from '../../util/migrations.js'; import { format } from '../../types/PhoneNumber.js'; import { getIntl, @@ -924,7 +925,7 @@ export function SmartPreferences(): JSX.Element | null { zoomFactor={zoomFactor} donationReceipts={donationReceipts} internalAddDonationReceipt={internalAddDonationReceipt} - saveAttachmentToDisk={window.Signal.Migrations.saveAttachmentToDisk} + saveAttachmentToDisk={saveAttachmentToDisk} generateDonationReceiptBlob={generateDonationReceiptBlob} __dangerouslyRunAbitraryReadOnlySqlQuery={ __dangerouslyRunAbitraryReadOnlySqlQuery diff --git a/ts/state/smart/PreferencesDonations.tsx b/ts/state/smart/PreferencesDonations.tsx index 951bf239b2..397a9b5223 100644 --- a/ts/state/smart/PreferencesDonations.tsx +++ b/ts/state/smart/PreferencesDonations.tsx @@ -20,6 +20,7 @@ import { getCachedSubscriptionConfiguration, } from '../../util/subscriptionConfiguration.js'; import { drop } from '../../util/drop.js'; +import { saveAttachmentToDisk } from '../../util/migrations.js'; import type { OneTimeDonationHumanAmounts } from '../../types/Donations.js'; import { ONE_TIME_DONATION_CONFIG_ID, @@ -80,7 +81,6 @@ export const SmartPreferencesDonations = memo( (state: StateType) => state.donations.receipts ); - const { saveAttachmentToDisk } = window.Signal.Migrations; const { updateOrCreate } = useBadgesActions(); // Function to fetch donation badge data diff --git a/ts/test-electron/ContactsParser_test.ts b/ts/test-electron/ContactsParser_test.ts index 63ba2eeed4..c8e7dd4772 100644 --- a/ts/test-electron/ContactsParser_test.ts +++ b/ts/test-electron/ContactsParser_test.ts @@ -12,6 +12,11 @@ import protobuf from '../protobuf/wrap.js'; import { createLogger } from '../logging/log.js'; import * as Bytes from '../Bytes.js'; import * as Errors from '../types/errors.js'; +import { + getAbsoluteAttachmentPath, + deleteAttachmentData, + readAttachmentData, +} from '../util/migrations.js'; import { APPLICATION_OCTET_STREAM } from '../types/MIME.js'; import { type AciString, generateAci } from '../types/ServiceId.js'; import { SignalService as Proto } from '../protobuf/index.js'; @@ -54,8 +59,7 @@ describe('ContactsParser', () => { ({ path } = await encryptAttachmentV2ToDisk({ keys, - getAbsoluteAttachmentPath: - window.Signal.Migrations.getAbsoluteAttachmentPath, + getAbsoluteAttachmentPath, needIncrementalMac: false, plaintext: { data }, })); @@ -73,7 +77,7 @@ describe('ContactsParser', () => { await Promise.all(contacts.map(contact => verifyContact(contact))); } finally { if (path) { - await window.Signal.Migrations.deleteAttachmentData(path); + await deleteAttachmentData(path); } } }); @@ -225,10 +229,8 @@ async function verifyContact( strictAssert(contact.avatar?.path, 'Avatar needs path'); - const avatarBytes = await window.Signal.Migrations.readAttachmentData( - contact.avatar - ); - await window.Signal.Migrations.deleteAttachmentData(contact.avatar.path); + const avatarBytes = await readAttachmentData(contact.avatar); + await deleteAttachmentData(contact.avatar.path); for (let j = 0; j < 255; j += 1) { assert.strictEqual(avatarBytes[j], j); diff --git a/ts/test-electron/Crypto_test.ts b/ts/test-electron/Crypto_test.ts index 25a069a527..504d150523 100644 --- a/ts/test-electron/Crypto_test.ts +++ b/ts/test-electron/Crypto_test.ts @@ -51,6 +51,7 @@ import { } from '../AttachmentCrypto.js'; import type { AciString, PniString } from '../types/ServiceId.js'; import { createTempDir, deleteTempDir } from '../updater/common.js'; +import { getAbsoluteAttachmentPath } from '../util/migrations.js'; import { uuidToBytes, bytesToUuid } from '../util/uuidToBytes.js'; import { getAesCbcCiphertextSize, @@ -604,12 +605,9 @@ describe('Crypto', () => { }, theirIncrementalMac: undefined, theirChunkSize: undefined, - getAbsoluteAttachmentPath: - window.Signal.Migrations.getAbsoluteAttachmentPath, + getAbsoluteAttachmentPath, }); - plaintextPath = window.Signal.Migrations.getAbsoluteAttachmentPath( - decryptedAttachment.path - ); + plaintextPath = getAbsoluteAttachmentPath(decryptedAttachment.path); const plaintext = readFileSync(plaintextPath); assert.isTrue(constantTimeEqual(FILE_CONTENTS, plaintext)); @@ -634,8 +632,7 @@ describe('Crypto', () => { const encryptedAttachment = await encryptAttachmentV2ToDisk({ keys, plaintext: { data: FILE_CONTENTS }, - getAbsoluteAttachmentPath: - window.Signal.Migrations.getAbsoluteAttachmentPath, + getAbsoluteAttachmentPath, needIncrementalMac: true, }); @@ -643,10 +640,9 @@ describe('Crypto', () => { decryptAttachmentV2ToSink( { type: 'standard', - ciphertextPath: - window.Signal.Migrations.getAbsoluteAttachmentPath( - encryptedAttachment.path - ), + ciphertextPath: getAbsoluteAttachmentPath( + encryptedAttachment.path + ), idForLogging: 'test', ...splitKeys(keys), size: FILE_CONTENTS.byteLength, @@ -668,8 +664,7 @@ describe('Crypto', () => { const encryptedAttachment = await encryptAttachmentV2ToDisk({ keys, plaintext: { data: FILE_CONTENTS }, - getAbsoluteAttachmentPath: - window.Signal.Migrations.getAbsoluteAttachmentPath, + getAbsoluteAttachmentPath, needIncrementalMac: true, }); @@ -677,10 +672,9 @@ describe('Crypto', () => { decryptAttachmentV2ToSink( { type: 'standard', - ciphertextPath: - window.Signal.Migrations.getAbsoluteAttachmentPath( - encryptedAttachment.path - ), + ciphertextPath: getAbsoluteAttachmentPath( + encryptedAttachment.path + ), idForLogging: 'test', ...splitKeys(keys), size: FILE_CONTENTS.byteLength, @@ -722,14 +716,11 @@ describe('Crypto', () => { const encryptedAttachment = await encryptAttachmentV2ToDisk({ keys, plaintext: path ? { absolutePath: path } : { data }, - getAbsoluteAttachmentPath: - window.Signal.Migrations.getAbsoluteAttachmentPath, + getAbsoluteAttachmentPath, needIncrementalMac: true, }); - ciphertextPath = window.Signal.Migrations.getAbsoluteAttachmentPath( - encryptedAttachment.path - ); + ciphertextPath = getAbsoluteAttachmentPath(encryptedAttachment.path); const macLength = encryptedAttachment.incrementalMac?.length; if ( @@ -771,13 +762,10 @@ describe('Crypto', () => { }, theirIncrementalMac: encryptedAttachment.incrementalMac, theirChunkSize: encryptedAttachment.chunkSize, - getAbsoluteAttachmentPath: - window.Signal.Migrations.getAbsoluteAttachmentPath, + getAbsoluteAttachmentPath, }); - plaintextPath = window.Signal.Migrations.getAbsoluteAttachmentPath( - decryptedAttachment.path - ); + plaintextPath = getAbsoluteAttachmentPath(decryptedAttachment.path); const plaintext = readFileSync(plaintextPath); @@ -893,13 +881,10 @@ describe('Crypto', () => { const encryptedAttachment = await encryptAttachmentV2ToDisk({ keys, plaintext: { absolutePath: FILE_PATH }, - getAbsoluteAttachmentPath: - window.Signal.Migrations.getAbsoluteAttachmentPath, + getAbsoluteAttachmentPath, needIncrementalMac: false, }); - ciphertextPath = window.Signal.Migrations.getAbsoluteAttachmentPath( - encryptedAttachment.path - ); + ciphertextPath = getAbsoluteAttachmentPath(encryptedAttachment.path); const ciphertext = readFileSync(ciphertextPath); @@ -950,13 +935,10 @@ describe('Crypto', () => { keys, plaintext: { absolutePath: FILE_PATH }, _testOnlyDangerousIv: dangerousTestOnlyIv, - getAbsoluteAttachmentPath: - window.Signal.Migrations.getAbsoluteAttachmentPath, + getAbsoluteAttachmentPath, needIncrementalMac: false, }); - ciphertextPath = window.Signal.Migrations.getAbsoluteAttachmentPath( - encryptedAttachmentV2.path - ); + ciphertextPath = getAbsoluteAttachmentPath(encryptedAttachmentV2.path); const ciphertextV2 = readFileSync(ciphertextPath); @@ -987,29 +969,25 @@ describe('Crypto', () => { innerEncryptedAttachment = await encryptAttachmentV2ToDisk({ keys: innerKeys, plaintext: { absolutePath: plaintextAbsolutePath }, - getAbsoluteAttachmentPath: - window.Signal.Migrations.getAbsoluteAttachmentPath, + getAbsoluteAttachmentPath, needIncrementalMac: true, }); - innerCiphertextPath = - window.Signal.Migrations.getAbsoluteAttachmentPath( - innerEncryptedAttachment.path - ); + innerCiphertextPath = getAbsoluteAttachmentPath( + innerEncryptedAttachment.path + ); const outerEncryptedAttachment = await encryptAttachmentV2ToDisk({ keys: outerKeys, plaintext: { absolutePath: innerCiphertextPath }, // We (and the server!) don't pad the second layer _testOnlyDangerousSkipPadding: true, - getAbsoluteAttachmentPath: - window.Signal.Migrations.getAbsoluteAttachmentPath, + getAbsoluteAttachmentPath, needIncrementalMac: false, }); - outerCiphertextPath = - window.Signal.Migrations.getAbsoluteAttachmentPath( - outerEncryptedAttachment.path - ); + outerCiphertextPath = getAbsoluteAttachmentPath( + outerEncryptedAttachment.path + ); } finally { if (innerCiphertextPath) { unlinkSync(innerCiphertextPath); @@ -1049,13 +1027,10 @@ describe('Crypto', () => { encryptResult.innerEncryptedAttachment.incrementalMac, theirChunkSize: encryptResult.innerEncryptedAttachment.chunkSize, outerEncryption: splitKeys(outerKeys), - getAbsoluteAttachmentPath: - window.Signal.Migrations.getAbsoluteAttachmentPath, + getAbsoluteAttachmentPath, }); - plaintextPath = window.Signal.Migrations.getAbsoluteAttachmentPath( - decryptedAttachment.path - ); + plaintextPath = getAbsoluteAttachmentPath(decryptedAttachment.path); const plaintext = readFileSync(plaintextPath); assert.isTrue(constantTimeEqual(FILE_CONTENTS, plaintext)); assert.strictEqual( @@ -1111,12 +1086,9 @@ describe('Crypto', () => { encryptResult.innerEncryptedAttachment.incrementalMac, theirChunkSize: encryptResult.innerEncryptedAttachment.chunkSize, outerEncryption: splitKeys(outerKeys), - getAbsoluteAttachmentPath: - window.Signal.Migrations.getAbsoluteAttachmentPath, + getAbsoluteAttachmentPath, }); - plaintextPath = window.Signal.Migrations.getAbsoluteAttachmentPath( - decryptedAttachment.path - ); + plaintextPath = getAbsoluteAttachmentPath(decryptedAttachment.path); const plaintext = readFileSync(plaintextPath); assert.isTrue(constantTimeEqual(data, plaintext)); } finally { @@ -1170,8 +1142,7 @@ describe('Crypto', () => { aesKey: splitKeys(outerKeys).aesKey, macKey: splitKeys(innerKeys).macKey, // wrong mac! }, - getAbsoluteAttachmentPath: - window.Signal.Migrations.getAbsoluteAttachmentPath, + getAbsoluteAttachmentPath, }), /Bad outer encryption MAC/ ); diff --git a/ts/test-electron/backup/attachments_test.ts b/ts/test-electron/backup/attachments_test.ts index 81e28869eb..686c0efcc8 100644 --- a/ts/test-electron/backup/attachments_test.ts +++ b/ts/test-electron/backup/attachments_test.ts @@ -4,8 +4,6 @@ import { v4 as generateGuid } from 'uuid'; import { BackupLevel } from '@signalapp/libsignal-client/zkgroup.js'; import lodash from 'lodash'; -import * as sinon from 'sinon'; -import { join } from 'node:path'; import { assert } from 'chai'; import type { ConversationModel } from '../../models/conversations.js'; @@ -51,7 +49,6 @@ const CONTACT_A = generateAci(); const NON_ROUNDTRIPPED_FIELDS = ['path', 'thumbnail', 'screenshot', 'localKey']; describe('backup/attachments', () => { - let sandbox: sinon.SinonSandbox; let contactA: ConversationModel; beforeEach(async () => { @@ -69,27 +66,10 @@ describe('backup/attachments', () => { ); await loadAllAndReinitializeRedux(); - - sandbox = sinon.createSandbox(); - const getAbsoluteAttachmentPath = sandbox.stub( - window.Signal.Migrations, - 'getAbsoluteAttachmentPath' - ); - getAbsoluteAttachmentPath.callsFake(path => { - if (path === 'path/to/sticker') { - return join(__dirname, '../../../fixtures/kitten-3-64-64.jpg'); - } - if (path === 'path/to/thumbnail') { - return join(__dirname, '../../../fixtures/kitten-3-64-64.jpg'); - } - return getAbsoluteAttachmentPath.wrappedMethod(path); - }); }); afterEach(async () => { await DataWriter.removeAll(); - - sandbox.restore(); }); function composeAttachment( diff --git a/ts/test-electron/cleanupOrphanedAttachments_test.ts b/ts/test-electron/cleanupOrphanedAttachments_test.ts index 7472843d48..9ed68414c9 100644 --- a/ts/test-electron/cleanupOrphanedAttachments_test.ts +++ b/ts/test-electron/cleanupOrphanedAttachments_test.ts @@ -9,6 +9,11 @@ import { dirname } from 'node:path'; import { DataWriter } from '../sql/Client.js'; import { missingCaseError } from '../util/missingCaseError.js'; +import { + getAbsoluteAttachmentPath, + getAbsoluteDownloadsPath, + getAbsoluteDraftPath, +} from '../util/migrations.js'; import { getDownloadsPath, getDraftPath, @@ -27,11 +32,11 @@ function getAbsolutePath( ) { switch (type) { case 'attachment': - return window.Signal.Migrations.getAbsoluteAttachmentPath(path); + return getAbsoluteAttachmentPath(path); case 'download': - return window.Signal.Migrations.getAbsoluteDownloadsPath(path); + return getAbsoluteDownloadsPath(path); case 'draft': - return window.Signal.Migrations.getAbsoluteDraftPath(path); + return getAbsoluteDraftPath(path); default: throw missingCaseError(type); } diff --git a/ts/test-electron/deleteMessageAttachments_test.ts b/ts/test-electron/deleteMessageAttachments_test.ts index ddff4b7621..0e8395ed7c 100644 --- a/ts/test-electron/deleteMessageAttachments_test.ts +++ b/ts/test-electron/deleteMessageAttachments_test.ts @@ -14,6 +14,14 @@ import { IMAGE_JPEG, LONG_MESSAGE } from '../types/MIME.js'; import type { MessageAttributesType } from '../model-types.js'; import type { AttachmentType } from '../types/Attachment.js'; import { deleteAllAttachmentFilesOnDisk } from '../util/Attachment.js'; +import { + getAbsoluteAttachmentPath, + getAbsoluteDownloadsPath, + getAbsoluteDraftPath, + deleteAttachmentData, + deleteDownloadData, + deleteExternalMessageFiles, +} from '../util/migrations.js'; import { strictAssert } from '../util/assert.js'; const { emptyDir, ensureFile } = fsExtra; @@ -24,11 +32,11 @@ function getAbsolutePath( ) { switch (type) { case 'attachment': - return window.Signal.Migrations.getAbsoluteAttachmentPath(path); + return getAbsoluteAttachmentPath(path); case 'download': - return window.Signal.Migrations.getAbsoluteDownloadsPath(path); + return getAbsoluteDownloadsPath(path); case 'draft': - return window.Signal.Migrations.getAbsoluteDraftPath(path); + return getAbsoluteDraftPath(path); default: throw missingCaseError(type); } @@ -118,8 +126,8 @@ describe('Attachment deletion', () => { await writeFiles(3, 'download'); await deleteAllAttachmentFilesOnDisk({ - deleteAttachmentOnDisk: window.Signal.Migrations.deleteAttachmentData, - deleteDownloadOnDisk: window.Signal.Migrations.deleteDownloadData, + deleteAttachmentOnDisk: deleteAttachmentData, + deleteDownloadOnDisk: deleteDownloadData, })(composeAttachment()); assert.strictEqual(attachmentIndex, 4); @@ -135,8 +143,8 @@ describe('Attachment deletion', () => { attachment.copied = true; await deleteAllAttachmentFilesOnDisk({ - deleteAttachmentOnDisk: window.Signal.Migrations.deleteAttachmentData, - deleteDownloadOnDisk: window.Signal.Migrations.deleteDownloadData, + deleteAttachmentOnDisk: deleteAttachmentData, + deleteDownloadOnDisk: deleteDownloadData, })(attachment); assert.sameDeepMembers(listFiles('attachment'), [ @@ -241,7 +249,7 @@ describe('Attachment deletion', () => { await writeFiles(NUM_DOWNLOAD_FILES_IN_MESSAGE + 3, 'download'); const message = composeMessageWithAllAttachments(); - await window.Signal.Migrations.deleteExternalMessageFiles(message); + await deleteExternalMessageFiles(message); assert.strictEqual(attachmentIndex, NUM_ATTACHMENT_FILES_IN_MESSAGE); assert.strictEqual(downloadIndex, NUM_DOWNLOAD_FILES_IN_MESSAGE); @@ -268,7 +276,7 @@ describe('Attachment deletion', () => { strictAssert(quotedThumbnail, 'thumbnail exists'); quotedThumbnail.copied = true; - await window.Signal.Migrations.deleteExternalMessageFiles(message); + await deleteExternalMessageFiles(message); assert.strictEqual(attachmentIndex, NUM_ATTACHMENT_FILES_IN_MESSAGE); assert.strictEqual(downloadIndex, NUM_DOWNLOAD_FILES_IN_MESSAGE); diff --git a/ts/test-electron/services/AttachmentBackupManager_test.ts b/ts/test-electron/services/AttachmentBackupManager_test.ts index 167f53d8ad..6405a32b5d 100644 --- a/ts/test-electron/services/AttachmentBackupManager_test.ts +++ b/ts/test-electron/services/AttachmentBackupManager_test.ts @@ -23,6 +23,7 @@ import { DataWriter } from '../../sql/Client.js'; import { getRandomBytes } from '../../Crypto.js'; import { APPLICATION_OCTET_STREAM, VIDEO_MP4 } from '../../types/MIME.js'; import { createName, getRelativePath } from '../../util/attachmentPath.js'; +import { getAbsoluteAttachmentPath } from '../../util/migrations.js'; import { encryptAttachmentV2, generateKeys } from '../../AttachmentCrypto.js'; import { SECOND } from '../../util/durations/index.js'; import { HTTPError } from '../../types/HTTPError.js'; @@ -99,7 +100,6 @@ describe('AttachmentBackupManager/JobManager', function attachmentBackupManager( } before(async () => { - const { getAbsoluteAttachmentPath } = window.Signal.Migrations; const absolutePath = getAbsoluteAttachmentPath(RELATIVE_ATTACHMENT_PATH); await ensureFile(absolutePath); await DataWriter.ensureFilePermissions(); @@ -142,7 +142,6 @@ describe('AttachmentBackupManager/JobManager', function attachmentBackupManager( }); const decryptAttachmentV2ToSink = sinon.stub(); - const { getAbsoluteAttachmentPath } = window.Signal.Migrations; const abortController = new AbortController(); runJob = sandbox.stub().callsFake((job: AttachmentBackupJobType) => { return runAttachmentBackupJob( diff --git a/ts/test-electron/services/ReleaseNotesFetcher_test.ts b/ts/test-electron/services/ReleaseNotesFetcher_test.ts index fc7552d66f..a33c77d47f 100644 --- a/ts/test-electron/services/ReleaseNotesFetcher_test.ts +++ b/ts/test-electron/services/ReleaseNotesFetcher_test.ts @@ -156,17 +156,6 @@ describe('ReleaseNotesFetcher', () => { sandbox.stub(window.SignalContext, 'getI18nLocale').returns('en-US'); sandbox.stub(window, 'getVersion').returns(currentVersion); - sandbox.stub(window.Signal, 'Migrations').value({ - writeNewAttachmentData: sandbox - .stub() - .resolves({ path: 'path/to/attachment' }), - processNewAttachment: sandbox.stub().resolves({ - path: 'processed/path', - contentType: 'image/png', - size: 123, - }), - }); - // Mock Whisper events const fakeWhisperEvents = new EventEmitter(); sandbox.stub(window.Whisper, 'events').value(fakeWhisperEvents); diff --git a/ts/test-electron/util/migrateMessageData_test.ts b/ts/test-electron/util/migrateMessageData_test.ts index 309266cf06..58701151f8 100644 --- a/ts/test-electron/util/migrateMessageData_test.ts +++ b/ts/test-electron/util/migrateMessageData_test.ts @@ -8,6 +8,7 @@ import type { MessageAttributesType } from '../../model-types.js'; import { DataReader, DataWriter } from '../../sql/Client.js'; import { generateAci } from '../../types/ServiceId.js'; import { postSaveUpdates } from '../../util/cleanup.js'; +import { upgradeMessageSchema } from '../../util/migrations.js'; import { itemStorage } from '../../textsecure/Storage.js'; function composeMessage(timestamp: number): MessageAttributesType { @@ -51,7 +52,7 @@ describe('utils/migrateMessageData', async () => { if (message.id === CANNOT_UPGRADE_MESSAGE_ID) { throw new Error('upgrade failed'); } - return window.Signal.Migrations.upgradeMessageSchema(message, ...rest); + return upgradeMessageSchema(message, ...rest); }, getMessagesNeedingUpgrade: async (...args) => { const messagesToUpgrade = await DataReader.getMessagesNeedingUpgrade( diff --git a/ts/textsecure/ContactsParser.ts b/ts/textsecure/ContactsParser.ts index e7fabbea35..5edbd9320e 100644 --- a/ts/textsecure/ContactsParser.ts +++ b/ts/textsecure/ContactsParser.ts @@ -7,6 +7,11 @@ import { createLogger } from '../logging/log.js'; import { SignalService as Proto } from '../protobuf/index.js'; import protobuf from '../protobuf/wrap.js'; import { DurationInSeconds } from '../util/durations/index.js'; +import { + getAbsoluteAttachmentPath, + writeNewAttachmentData, + deleteAttachmentData, +} from '../util/migrations.js'; import type { ContactAvatarType } from '../types/Avatar.js'; import type { AttachmentType } from '../types/Attachment.js'; import type { AciString } from '../types/ServiceId.js'; @@ -61,9 +66,7 @@ export async function parseContactsV2( { idForLogging: 'parseContactsV2', - ciphertextPath: window.Signal.Migrations.getAbsoluteAttachmentPath( - attachment.path - ), + ciphertextPath: getAbsoluteAttachmentPath(attachment.path), keysBase64: attachment.localKey, size: attachment.size, type: 'local', @@ -155,7 +158,7 @@ export class ParseContactsTransform extends Transform { const local = // eslint-disable-next-line no-await-in-loop - await window.Signal.Migrations.writeNewAttachmentData(avatarData); + await writeNewAttachmentData(avatarData); const contentType = this.activeContact.avatar?.contentType; const prepared = prepareContact(this.activeContact, { @@ -170,7 +173,7 @@ export class ParseContactsTransform extends Transform { this.contacts.push(prepared); } else { // eslint-disable-next-line no-await-in-loop - await window.Signal.Migrations.deleteAttachmentData(local.path); + await deleteAttachmentData(local.path); } this.activeContact = undefined; diff --git a/ts/textsecure/downloadAttachment.ts b/ts/textsecure/downloadAttachment.ts index dc6c09a860..4528de2614 100644 --- a/ts/textsecure/downloadAttachment.ts +++ b/ts/textsecure/downloadAttachment.ts @@ -12,6 +12,10 @@ import fsExtra from 'fs-extra'; import { createLogger } from '../logging/log.js'; import * as Errors from '../types/errors.js'; import { strictAssert } from '../util/assert.js'; +import { + getAbsoluteDownloadsPath, + getAbsoluteAttachmentPath, +} from '../util/migrations.js'; import { hasRequiredInformationForBackup } from '../util/Attachment.js'; import { AttachmentSizeError, @@ -153,7 +157,7 @@ export async function downloadAttachment( : undefined; const absoluteDownloadPath = downloadPath - ? window.Signal.Migrations.getAbsoluteDownloadsPath(downloadPath) + ? getAbsoluteDownloadsPath(downloadPath) : undefined; let downloadOffset = 0; @@ -313,8 +317,7 @@ export async function downloadAttachment( mediaTier === 'backup' ? getBackupMediaOuterEncryptionKeyMaterial(attachment) : undefined, - getAbsoluteAttachmentPath: - window.Signal.Migrations.getAbsoluteAttachmentPath, + getAbsoluteAttachmentPath, }); } case AttachmentVariant.ThumbnailFromBackup: { @@ -335,8 +338,7 @@ export async function downloadAttachment( ...thumbnailEncryptionKeys, outerEncryption: getBackupThumbnailOuterEncryptionKeyMaterial(attachment), - getAbsoluteAttachmentPath: - window.Signal.Migrations.getAbsoluteAttachmentPath, + getAbsoluteAttachmentPath, }); } default: { @@ -364,10 +366,8 @@ async function downloadToDisk({ expectedCiphertextSize: number; }): Promise<{ absolutePath: string; downloadSize: number }> { const absoluteTargetPath = downloadPath - ? window.Signal.Migrations.getAbsoluteDownloadsPath(downloadPath) - : window.Signal.Migrations.getAbsoluteAttachmentPath( - getRelativePath(createName()) - ); + ? getAbsoluteDownloadsPath(downloadPath) + : getAbsoluteAttachmentPath(getRelativePath(createName())); await ensureFile(absoluteTargetPath); let writeStream: Writable; if (downloadPath) { diff --git a/ts/types/Stickers.ts b/ts/types/Stickers.ts index 76f51884a5..7a807e0d41 100644 --- a/ts/types/Stickers.ts +++ b/ts/types/Stickers.ts @@ -30,6 +30,17 @@ import { SignalService as Proto } from '../protobuf/index.js'; import { createLogger } from '../logging/log.js'; import type { StickersStateType } from '../state/ducks/stickers.js'; import { MINUTE } from '../util/durations/index.js'; +import { + processNewEphemeralSticker, + processNewSticker, + deleteTempFile, + getAbsoluteStickerPath, + copyStickerIntoAttachmentsDirectory, + readAttachmentData, + deleteSticker, + readStickerData, + writeNewStickerData, +} from '../util/migrations.js'; import { drop } from '../util/drop.js'; import { isNotNil } from '../util/isNotNil.js'; import { encryptLegacyAttachment } from '../util/encryptLegacyAttachment.js'; @@ -432,8 +443,8 @@ async function downloadSticker( const plaintext = decryptSticker(packKey, ciphertext); const sticker = ephemeral - ? await window.Signal.Migrations.processNewEphemeralSticker(plaintext) - : await window.Signal.Migrations.processNewSticker(plaintext); + ? await processNewEphemeralSticker(plaintext) + : await processNewSticker(plaintext); return { id, @@ -493,7 +504,7 @@ export async function removeEphemeralPack(packId: string): Promise { const stickers = values(existing.stickers); const paths = stickers.map(sticker => sticker.path); - await pMap(paths, window.Signal.Migrations.deleteTempFile, { + await pMap(paths, deleteTempFile, { concurrency: 3, }); @@ -1065,12 +1076,9 @@ export async function copyStickerToAttachments( } const { path: stickerPath } = sticker; - const absolutePath = - window.Signal.Migrations.getAbsoluteStickerPath(stickerPath); + const absolutePath = getAbsoluteStickerPath(stickerPath); const { path, size } = - await window.Signal.Migrations.copyStickerIntoAttachmentsDirectory( - absolutePath - ); + await copyStickerIntoAttachmentsDirectory(absolutePath); const newSticker: AttachmentType = { ...sticker, @@ -1080,7 +1088,7 @@ export async function copyStickerToAttachments( // Fall-back contentType: IMAGE_WEBP, }; - const data = await window.Signal.Migrations.readAttachmentData(newSticker); + const data = await readAttachmentData(newSticker); const sniffedMimeType = sniffImageMimeType(data); if (sniffedMimeType) { @@ -1131,7 +1139,7 @@ export async function deletePackReference( const { removeStickerPack } = getReduxStickerActions(); removeStickerPack(packId); - await pMap(paths, window.Signal.Migrations.deleteSticker, { + await pMap(paths, deleteSticker, { concurrency: 3, }); } @@ -1150,7 +1158,7 @@ async function deletePack(packId: string): Promise { const { removeStickerPack } = getReduxStickerActions(); removeStickerPack(packId); - await pMap(paths, window.Signal.Migrations.deleteSticker, { + await pMap(paths, deleteSticker, { concurrency: 3, }); } @@ -1207,9 +1215,6 @@ async function encryptLegacySticker( ): Promise< { sticker: StickerFromDBType; cleanup: () => Promise } | undefined > { - const { deleteSticker, readStickerData, writeNewStickerData } = - window.Signal.Migrations; - const updated = await encryptLegacyAttachment(sticker, { logId: 'sticker', readAttachmentData: readStickerData, diff --git a/ts/util/avatarUtils.ts b/ts/util/avatarUtils.ts index 0377eb31ae..be73df99c3 100644 --- a/ts/util/avatarUtils.ts +++ b/ts/util/avatarUtils.ts @@ -6,6 +6,7 @@ import type { ContactAvatarType } from '../types/Avatar.js'; import { isMe } from './whatTypeOfConversation.js'; import { isSignalConversation } from './isSignalConversation.js'; import { getLocalAttachmentUrl } from './getLocalAttachmentUrl.js'; +import { getAbsoluteAttachmentPath } from './migrations.js'; import { itemStorage } from '../textsecure/Storage.js'; export function hasAvatar( @@ -64,7 +65,6 @@ export function getRawAvatarPath( return avatar.path; } - const { getAbsoluteAttachmentPath } = window.Signal.Migrations; return getAbsoluteAttachmentPath(avatar.path); } diff --git a/ts/util/basePaths.ts b/ts/util/basePaths.ts index 342b276982..44d40d3a2e 100644 --- a/ts/util/basePaths.ts +++ b/ts/util/basePaths.ts @@ -6,7 +6,10 @@ import { getDraftPath, getStickersPath, getTempPath, -} from '../../app/attachments.js'; + getBadgesPath, + getAvatarsPath, + getDownloadsPath, +} from '../windows/main/attachments.js'; const userDataPath = window.SignalContext.getPath('userData'); @@ -14,3 +17,6 @@ export const ATTACHMENTS_PATH = getPath(userDataPath); export const DRAFT_PATH = getDraftPath(userDataPath); export const STICKERS_PATH = getStickersPath(userDataPath); export const TEMP_PATH = getTempPath(userDataPath); +export const BADGES_PATH = getBadgesPath(userDataPath); +export const AVATARS_PATH = getAvatarsPath(userDataPath); +export const DOWNLOADS_PATH = getDownloadsPath(userDataPath); diff --git a/ts/util/cleanup.ts b/ts/util/cleanup.ts index 1dd4883c4e..f6f6995b2f 100644 --- a/ts/util/cleanup.ts +++ b/ts/util/cleanup.ts @@ -25,6 +25,7 @@ import { getMessageIdForLogging } from './idForLogging.js'; import { singleProtoJobQueue } from '../jobs/singleProtoJobQueue.js'; import { MINUTE } from './durations/index.js'; import { drop } from './drop.js'; +import { deleteExternalMessageFiles } from './migrations.js'; import { hydrateStoryContext } from './hydrateStoryContext.js'; import { update as updateExpiringMessagesService } from '../services/expiringMessagesDeletion.js'; import { tapToViewMessagesDeletionService } from '../services/tapToViewMessagesDeletionService.js'; @@ -208,7 +209,7 @@ async function cleanupStoryReplies( export async function deleteMessageData( message: MessageAttributesType ): Promise { - await window.Signal.Migrations.deleteExternalMessageFiles(message); + await deleteExternalMessageFiles(message); if (isStory(message)) { await cleanupStoryReplies(message); diff --git a/ts/util/createIdenticon.tsx b/ts/util/createIdenticon.tsx index 27e514530a..d08a55a291 100644 --- a/ts/util/createIdenticon.tsx +++ b/ts/util/createIdenticon.tsx @@ -13,6 +13,7 @@ import { // eslint-disable-next-line import/no-restricted-paths } from '../components/IdenticonSVG.js'; import { missingCaseError } from './missingCaseError.js'; +import { writeNewPlaintextTempData } from './migrations.js'; const TARGET_MIME = 'image/png'; @@ -118,8 +119,7 @@ export function createIdenticon( } const data = new Uint8Array(arrayBuffer); - const path = - await window.Signal.Migrations.writeNewPlaintextTempData(data); + const path = await writeNewPlaintextTempData(data); resolve({ url, path }); }); reader.readAsArrayBuffer(blob); diff --git a/ts/util/deleteDraftAttachment.ts b/ts/util/deleteDraftAttachment.ts index f6e0b70c30..c79189a9ca 100644 --- a/ts/util/deleteDraftAttachment.ts +++ b/ts/util/deleteDraftAttachment.ts @@ -2,14 +2,15 @@ // SPDX-License-Identifier: AGPL-3.0-only import type { AttachmentType } from '../types/Attachment.js'; +import { deleteDraftFile } from './migrations.js'; export async function deleteDraftAttachment( attachment: Pick ): Promise { if (attachment.screenshotPath) { - await window.Signal.Migrations.deleteDraftFile(attachment.screenshotPath); + await deleteDraftFile(attachment.screenshotPath); } if (attachment.path) { - await window.Signal.Migrations.deleteDraftFile(attachment.path); + await deleteDraftFile(attachment.path); } } diff --git a/ts/util/downloadAttachmentFromLocalBackup.ts b/ts/util/downloadAttachmentFromLocalBackup.ts index 29adafe6b1..5fccdf64fd 100644 --- a/ts/util/downloadAttachmentFromLocalBackup.ts +++ b/ts/util/downloadAttachmentFromLocalBackup.ts @@ -9,6 +9,7 @@ import { type ReencryptedAttachmentV2, } from '../AttachmentCrypto.js'; import { strictAssert } from './assert.js'; +import { getAbsoluteAttachmentPath } from './migrations.js'; const { isNumber } = lodash; @@ -50,7 +51,6 @@ async function doDownloadFromLocalBackup( idForLogging: logId, keysBase64: localKey, size, - getAbsoluteAttachmentPath: - window.Signal.Migrations.getAbsoluteAttachmentPath, + getAbsoluteAttachmentPath, }); } diff --git a/ts/util/downloadOnboardingStory.ts b/ts/util/downloadOnboardingStory.ts index 2d520e04fd..01894330f9 100644 --- a/ts/util/downloadOnboardingStory.ts +++ b/ts/util/downloadOnboardingStory.ts @@ -11,6 +11,7 @@ import { ReadStatus } from '../messages/MessageReadStatus.js'; import { SeenStatus } from '../MessageSeenStatus.js'; import { findAndDeleteOnboardingStoryIfExists } from './findAndDeleteOnboardingStoryIfExists.js'; import { saveNewMessageBatcher } from './messageBatcher.js'; +import { writeNewAttachmentData, processNewAttachment } from './migrations.js'; import { incrementMessageCounter } from './incrementMessageCounter.js'; import { getOnboardingStoryManifest, @@ -65,16 +66,13 @@ export async function downloadOnboardingStory(): Promise { const attachments: Array = await Promise.all( imageBuffers.map(async data => { - const local = await window.Signal.Migrations.writeNewAttachmentData(data); + const local = await writeNewAttachmentData(data); const attachment: AttachmentType = { contentType: IMAGE_JPEG, ...local, }; - return window.Signal.Migrations.processNewAttachment( - attachment, - 'attachment' - ); + return processNewAttachment(attachment, 'attachment'); }) ); diff --git a/ts/util/encryptConversationAttachments.ts b/ts/util/encryptConversationAttachments.ts index 366420dfe5..09b3dab9b6 100644 --- a/ts/util/encryptConversationAttachments.ts +++ b/ts/util/encryptConversationAttachments.ts @@ -9,6 +9,17 @@ import type { ConversationAttributesType } from '../model-types.d.ts'; import { encryptLegacyAttachment } from './encryptLegacyAttachment.js'; import { AttachmentDisposition } from './getLocalAttachmentUrl.js'; import { isNotNil } from './isNotNil.js'; +import { + deleteAttachmentData, + deleteAvatar, + deleteDraftFile, + readAttachmentData, + readAvatarData, + readDraftData, + writeNewAttachmentData, + writeNewAvatarData, + writeNewDraftData, +} from './migrations.js'; import { isSignalConversation } from './isSignalConversation.js'; import { getConversationIdForLogging } from './idForLogging.js'; import { itemStorage } from '../textsecure/Storage.js'; @@ -75,18 +86,6 @@ async function encryptOne(attributes: ConversationAttributesType): Promise< const logId = getConversationIdForLogging(attributes); const result = { ...attributes }; - const { - deleteAttachmentData, - deleteAvatar, - deleteDraftFile, - readAttachmentData, - readAvatarData, - readDraftData, - writeNewAttachmentData, - writeNewAvatarData, - writeNewDraftData, - } = window.Signal.Migrations; - const cleanup: CleanupType = []; if (attributes.profileAvatar?.path) { diff --git a/ts/util/handleEditMessage.ts b/ts/util/handleEditMessage.ts index 2a0bbd7593..c43601a964 100644 --- a/ts/util/handleEditMessage.ts +++ b/ts/util/handleEditMessage.ts @@ -13,6 +13,7 @@ import { createLogger } from '../logging/log.js'; import { ReadStatus } from '../messages/MessageReadStatus.js'; import { DataWriter } from '../sql/Client.js'; import { drop } from './drop.js'; +import { upgradeMessageSchema } from './migrations.js'; import { cacheAttachmentBySignature, getCachedAttachmentBySignature, @@ -129,8 +130,9 @@ export async function handleEditMessage( return; } - const upgradedEditedMessageData = - await window.Signal.Migrations.upgradeMessageSchema(editAttributes.message); + const upgradedEditedMessageData = await upgradeMessageSchema( + editAttributes.message + ); // Copies over the attachments from the main message if they're the same // and they have already been downloaded. diff --git a/ts/util/makeQuote.ts b/ts/util/makeQuote.ts index 2fb8d63ff9..1f761b6006 100644 --- a/ts/util/makeQuote.ts +++ b/ts/util/makeQuote.ts @@ -16,6 +16,7 @@ import { isGiftBadge, isTapToView } from '../state/selectors/message.js'; import { createLogger } from '../logging/log.js'; import { map, take, collect } from './iterables.js'; import { strictAssert } from './assert.js'; +import { loadAttachmentData } from './migrations.js'; import { getMessageSentTimestamp } from './getMessageSentTimestamp.js'; import { getLocalAttachmentUrl } from './getLocalAttachmentUrl.js'; import type { QuotedMessageForComposerType } from '../state/ducks/composer.js'; @@ -61,8 +62,6 @@ export async function getQuoteAttachment( preview?: ReadonlyArray, sticker?: StickerType ): Promise> { - const { loadAttachmentData } = window.Signal.Migrations; - if (attachments && attachments.length) { const attachmentsToUse = Array.from(take(attachments, 1)); const isGIFQuote = isGIF(attachmentsToUse); diff --git a/ts/util/maybeForwardMessages.ts b/ts/util/maybeForwardMessages.ts index 79852007f4..b1d60a411a 100644 --- a/ts/util/maybeForwardMessages.ts +++ b/ts/util/maybeForwardMessages.ts @@ -18,6 +18,12 @@ import type { EmbeddedContactWithHydratedAvatar } from '../types/EmbeddedContact import type { DraftBodyRanges } from '../types/BodyRange.js'; import type { StickerWithHydratedData } from '../types/Stickers.js'; import { drop } from './drop.js'; +import { + loadAttachmentData, + loadContactData, + loadPreviewData, + loadStickerData, +} from './migrations.js'; import { toLogFormat } from '../types/errors.js'; import { sortByMessageOrder, @@ -77,13 +83,6 @@ export async function maybeForwardMessages( const sendMessageOptions = { dontClearDraft: true }; const baseTimestamp = Date.now(); - const { - loadAttachmentData, - loadContactData, - loadPreviewData, - loadStickerData, - } = window.Signal.Migrations; - let timestampOffset = 0; // load any sticker data, attachments, or link previews that we need to diff --git a/ts/util/migrations.ts b/ts/util/migrations.ts new file mode 100644 index 0000000000..76eb419774 --- /dev/null +++ b/ts/util/migrations.ts @@ -0,0 +1,220 @@ +// Copyright 2025 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import { + createAbsolutePathGetter, + createPlaintextReader, + createWriterForNew, + createDoesExist, + getUnusedFilename, + readAndDecryptDataFromDisk, + saveAttachmentToDisk, + writeNewAttachmentData as doWriteNewAttachmentData, + createDeleter, + copyIntoAttachmentsDirectory, +} from '../windows/main/attachments.js'; +import { + getImageDimensions, + makeImageThumbnail, + makeObjectUrl, + makeVideoScreenshot, + revokeObjectUrl, +} from '../types/VisualAttachment.js'; +import { loadData } from './Attachment.js'; +import { + loadContactData as doLoadContactData, + loadPreviewData as doLoadPreviewData, + loadQuoteData as doLoadQuoteData, + loadStickerData as doLoadStickerData, + processNewAttachment as doProcessNewAttachment, + processNewSticker as doProcessNewSticker, + deleteAllExternalFiles, + createAttachmentLoader, + upgradeSchema, +} from '../types/Message2.js'; +import type { + AttachmentType, + AddressableAttachmentType, + LocalAttachmentV2Type, +} from '../types/Attachment.js'; +import type { MessageAttachmentType } from '../types/AttachmentDownload.js'; +import type { MessageAttributesType } from '../model-types.d.ts'; +import { createLogger } from '../logging/log.js'; +import { itemStorage } from '../textsecure/Storage.js'; +import { + ATTACHMENTS_PATH, + STICKERS_PATH, + BADGES_PATH, + DRAFT_PATH, + TEMP_PATH, + AVATARS_PATH, + DOWNLOADS_PATH, +} from './basePaths.js'; + +const logger = createLogger('migrations'); + +type EncryptedReader = ( + attachment: Partial +) => Promise; + +type EncryptedWriter = (data: Uint8Array) => Promise; + +function createEncryptedReader(basePath: string): EncryptedReader { + const fallbackReader = createPlaintextReader(basePath); + const pathGetter = createAbsolutePathGetter(basePath); + + return async ( + attachment: Partial + ): Promise => { + // In-memory + if (attachment.data != null) { + return attachment.data; + } + + if (attachment.path == null) { + throw new Error('Attachment was not downloaded yet'); + } + + if (attachment.version !== 2) { + return fallbackReader(attachment.path); + } + + if (attachment.localKey == null || attachment.size == null) { + throw new Error('Failed to decrypt v2 attachment'); + } + + const absolutePath = pathGetter(attachment.path); + + return readAndDecryptDataFromDisk({ + absolutePath, + keysBase64: attachment.localKey, + size: attachment.size, + }); + }; +} + +function createEncryptedWriterForNew(basePath: string): EncryptedWriter { + const pathGetter = createAbsolutePathGetter(basePath); + + return data => + doWriteNewAttachmentData({ + data, + getAbsoluteAttachmentPath: pathGetter, + }); +} + +export const readAttachmentData = createEncryptedReader(ATTACHMENTS_PATH); +export const loadAttachmentData = loadData(readAttachmentData); +export const loadContactData = doLoadContactData(loadAttachmentData); +export const loadPreviewData = doLoadPreviewData(loadAttachmentData); +export const loadQuoteData = doLoadQuoteData(loadAttachmentData); +export const loadStickerData = doLoadStickerData(loadAttachmentData); +export const getAbsoluteAttachmentPath = + createAbsolutePathGetter(ATTACHMENTS_PATH); +export const deleteAttachmentData = createDeleter(ATTACHMENTS_PATH); +export const writeNewAttachmentData = + createEncryptedWriterForNew(ATTACHMENTS_PATH); +export const doesAttachmentExist = createDoesExist(ATTACHMENTS_PATH); + +export const getAbsoluteStickerPath = createAbsolutePathGetter(STICKERS_PATH); +export const writeNewStickerData = createEncryptedWriterForNew(STICKERS_PATH); +export const deleteSticker = createDeleter(STICKERS_PATH); +export const readStickerData = createEncryptedReader(STICKERS_PATH); +export const copyStickerIntoAttachmentsDirectory = copyIntoAttachmentsDirectory( + { + sourceDir: STICKERS_PATH, + targetDir: ATTACHMENTS_PATH, + } +); + +export const getAbsoluteBadgeImageFilePath = + createAbsolutePathGetter(BADGES_PATH); +export const writeNewBadgeImageFileData = createWriterForNew( + BADGES_PATH, + '.svg' +); + +export const getAbsoluteTempPath = createAbsolutePathGetter(TEMP_PATH); +const writeNewTempData = createEncryptedWriterForNew(TEMP_PATH); +export const writeNewPlaintextTempData = createWriterForNew(TEMP_PATH); +export const deleteTempFile = createDeleter(TEMP_PATH); +export const readTempData = createEncryptedReader(TEMP_PATH); +export const copyAttachmentIntoTempDirectory = copyIntoAttachmentsDirectory({ + sourceDir: ATTACHMENTS_PATH, + targetDir: TEMP_PATH, +}); + +export const getAbsoluteDraftPath = createAbsolutePathGetter(DRAFT_PATH); +export const writeNewDraftData = createEncryptedWriterForNew(DRAFT_PATH); +export const deleteDraftFile = createDeleter(DRAFT_PATH); +export const readDraftData = createEncryptedReader(DRAFT_PATH); + +export const getAbsoluteDownloadsPath = + createAbsolutePathGetter(DOWNLOADS_PATH); +export const deleteDownloadData = createDeleter(DOWNLOADS_PATH); + +export const readAvatarData = createEncryptedReader(AVATARS_PATH); +export const getAbsoluteAvatarPath = createAbsolutePathGetter(AVATARS_PATH); +export const writeNewAvatarData = createEncryptedWriterForNew(AVATARS_PATH); +export const deleteAvatar = createDeleter(AVATARS_PATH); + +export const deleteExternalMessageFiles = deleteAllExternalFiles({ + deleteAttachmentOnDisk: deleteAttachmentData, + deleteDownloadOnDisk: deleteDownloadData, +}); +export const loadMessage = createAttachmentLoader(loadAttachmentData); + +export const processNewAttachment = ( + attachment: AttachmentType, + attachmentType: MessageAttachmentType +): ReturnType => + doProcessNewAttachment(attachment, attachmentType, { + writeNewAttachmentData, + makeObjectUrl, + revokeObjectUrl, + getImageDimensions, + makeImageThumbnail, + makeVideoScreenshot, + logger, + }); +export const processNewSticker = ( + stickerData: Uint8Array +): ReturnType => + doProcessNewSticker(stickerData, false, { + writeNewStickerData, + getImageDimensions, + logger, + }); +export const processNewEphemeralSticker = ( + stickerData: Uint8Array +): ReturnType => + doProcessNewSticker(stickerData, true, { + writeNewStickerData: writeNewTempData, + getImageDimensions, + logger, + }); + +export const upgradeMessageSchema = ( + message: MessageAttributesType, + options: { maxVersion?: number } = {} +): Promise => { + const { maxVersion } = options; + + return upgradeSchema(message, { + deleteAttachmentOnDisk: deleteAttachmentData, + doesAttachmentExist, + getImageDimensions, + getRegionCode: () => itemStorage.get('regionCode'), + makeImageThumbnail, + makeObjectUrl, + makeVideoScreenshot, + readAttachmentData, + revokeObjectUrl, + writeNewAttachmentData, + writeNewStickerData, + logger, + maxVersion, + }); +}; + +export { getUnusedFilename, saveAttachmentToDisk }; diff --git a/ts/util/modifyTargetMessage.ts b/ts/util/modifyTargetMessage.ts index 440a36af6b..42291aeec1 100644 --- a/ts/util/modifyTargetMessage.ts +++ b/ts/util/modifyTargetMessage.ts @@ -33,6 +33,7 @@ import { getSourceServiceId } from '../messages/sources.js'; import { missingCaseError } from './missingCaseError.js'; import { reduce } from './iterables.js'; import { strictAssert } from './assert.js'; +import { deleteAttachmentData, deleteDownloadData } from './migrations.js'; import { applyDeleteAttachmentFromMessage, applyDeleteMessage, @@ -107,9 +108,8 @@ export async function modifyTargetMessage( { logId, shouldSave: false, - deleteAttachmentOnDisk: - window.Signal.Migrations.deleteAttachmentData, - deleteDownloadOnDisk: window.Signal.Migrations.deleteDownloadData, + deleteAttachmentOnDisk: deleteAttachmentData, + deleteDownloadOnDisk: deleteDownloadData, } ); if (result) { diff --git a/ts/util/resolveAttachmentDraftData.ts b/ts/util/resolveAttachmentDraftData.ts index 28ad3bf201..d93f0f0161 100644 --- a/ts/util/resolveAttachmentDraftData.ts +++ b/ts/util/resolveAttachmentDraftData.ts @@ -3,6 +3,7 @@ import { createLogger } from '../logging/log.js'; import type { AttachmentType } from '../types/Attachment.js'; +import { readDraftData } from './migrations.js'; const log = createLogger('resolveAttachmentDraftData'); @@ -17,7 +18,7 @@ export async function resolveAttachmentDraftData( return; } - const data = await window.Signal.Migrations.readDraftData(attachment); + const data = await readDraftData(attachment); if (data.byteLength !== attachment.size) { log.error( `Attachment size from disk ${data.byteLength} did not match attachment size ${attachment.size}` diff --git a/ts/util/resolveDraftAttachmentOnDisk.ts b/ts/util/resolveDraftAttachmentOnDisk.ts index c5ef79256c..87279fef98 100644 --- a/ts/util/resolveDraftAttachmentOnDisk.ts +++ b/ts/util/resolveDraftAttachmentOnDisk.ts @@ -8,6 +8,7 @@ import { getLocalAttachmentUrl, AttachmentDisposition, } from './getLocalAttachmentUrl.js'; +import { getAbsoluteDraftPath } from './migrations.js'; const log = createLogger('resolveDraftAttachmentOnDisk'); @@ -21,9 +22,7 @@ export function resolveDraftAttachmentOnDisk( if (attachment.screenshotPath) { // Legacy - url = window.Signal.Migrations.getAbsoluteDraftPath( - attachment.screenshotPath - ); + url = getAbsoluteDraftPath(attachment.screenshotPath); } else if (attachment.screenshot?.path) { url = getLocalAttachmentUrl(attachment.screenshot, { disposition: AttachmentDisposition.Draft, diff --git a/ts/util/sendStoryMessage.ts b/ts/util/sendStoryMessage.ts index ca3362b11e..6bb7e4d280 100644 --- a/ts/util/sendStoryMessage.ts +++ b/ts/util/sendStoryMessage.ts @@ -28,6 +28,7 @@ import { incrementMessageCounter } from './incrementMessageCounter.js'; import { isGroupV2 } from './whatTypeOfConversation.js'; import { isNotNil } from './isNotNil.js'; import { collect } from './iterables.js'; +import { loadPreviewData, upgradeMessageSchema } from './migrations.js'; import { DurationInSeconds } from './durations/index.js'; import { sanitizeLinkPreview } from '../services/LinkPreview.js'; import type { DraftBodyRanges } from '../types/BodyRange.js'; @@ -139,7 +140,6 @@ export async function sendStoryMessage( const attachments: Array = [attachment]; const linkPreview = attachment?.textAttachment?.preview; - const { loadPreviewData } = window.Signal.Migrations; const sanitizedLinkPreview = linkPreview ? sanitizeLinkPreview((await loadPreviewData([linkPreview]))[0]) : undefined; @@ -166,7 +166,7 @@ export async function sendStoryMessage( // Note: we use the same sent_at for these messages because we want de-duplication // on the receiver side. - return window.Signal.Migrations.upgradeMessageSchema({ + return upgradeMessageSchema({ attachments, bodyRanges, conversationId: ourConversation.id, @@ -272,27 +272,26 @@ export async function sendStoryMessage( } ); - const messageAttributes = - await window.Signal.Migrations.upgradeMessageSchema({ - attachments, - bodyRanges, - canReplyToStory: true, - conversationId: group.id, - expireTimer: DurationInSeconds.DAY, - expirationStartTimestamp: Date.now(), - id: generateUuid(), - readStatus: ReadStatus.Read, - received_at: incrementMessageCounter(), - received_at_ms: groupTimestamp, - seenStatus: SeenStatus.NotApplicable, - sendStateByConversationId, - sent_at: groupTimestamp, - source: itemStorage.user.getNumber(), - sourceServiceId: itemStorage.user.getAci(), - sourceDevice: itemStorage.user.getDeviceId(), - timestamp: groupTimestamp, - type: 'story', - }); + const messageAttributes = await upgradeMessageSchema({ + attachments, + bodyRanges, + canReplyToStory: true, + conversationId: group.id, + expireTimer: DurationInSeconds.DAY, + expirationStartTimestamp: Date.now(), + id: generateUuid(), + readStatus: ReadStatus.Read, + received_at: incrementMessageCounter(), + received_at_ms: groupTimestamp, + seenStatus: SeenStatus.NotApplicable, + sendStateByConversationId, + sent_at: groupTimestamp, + source: itemStorage.user.getNumber(), + sourceServiceId: itemStorage.user.getAci(), + sourceDevice: itemStorage.user.getDeviceId(), + timestamp: groupTimestamp, + type: 'story', + }); groupV2MessagesByConversationId.set(group.id, messageAttributes); }) diff --git a/ts/util/uploadAttachment.ts b/ts/util/uploadAttachment.ts index 3df76af89c..88e0f2e716 100644 --- a/ts/util/uploadAttachment.ts +++ b/ts/util/uploadAttachment.ts @@ -26,6 +26,7 @@ import { import { missingCaseError } from './missingCaseError.js'; import { uuidToBytes } from './uuidToBytes.js'; import { isVisualMedia } from './Attachment.js'; +import { getAbsoluteAttachmentPath } from './migrations.js'; const CDNS_SUPPORTING_TUS = new Set([3]); @@ -103,16 +104,13 @@ export async function encryptAndUploadAttachment({ } const encrypted = await encryptAttachmentV2ToDisk({ - getAbsoluteAttachmentPath: - window.Signal.Migrations.getAbsoluteAttachmentPath, + getAbsoluteAttachmentPath, keys, needIncrementalMac, plaintext, }); - absoluteCiphertextPath = window.Signal.Migrations.getAbsoluteAttachmentPath( - encrypted.path - ); + absoluteCiphertextPath = getAbsoluteAttachmentPath(encrypted.path); await uploadFile({ absoluteCiphertextPath, diff --git a/ts/util/writeDraftAttachment.ts b/ts/util/writeDraftAttachment.ts index 83503ebabc..91a243f980 100644 --- a/ts/util/writeDraftAttachment.ts +++ b/ts/util/writeDraftAttachment.ts @@ -14,6 +14,7 @@ import { getLocalAttachmentUrl, AttachmentDisposition, } from './getLocalAttachmentUrl.js'; +import { writeNewDraftData } from './migrations.js'; import { createLogger } from '../logging/log.js'; const { omit } = lodash; @@ -27,14 +28,10 @@ export async function writeDraftAttachment( throw new Error('writeDraftAttachment: Cannot write pending attachment'); } - const local = await window.Signal.Migrations.writeNewDraftData( - attachment.data - ); + const local = await writeNewDraftData(attachment.data); const localScreenshot = attachment.screenshotData - ? await window.Signal.Migrations.writeNewDraftData( - attachment.screenshotData - ) + ? await writeNewDraftData(attachment.screenshotData) : undefined; let dimensions: { width?: number; height?: number } = {}; diff --git a/ts/window.d.ts b/ts/window.d.ts index 484319cb0b..7b75c524ab 100644 --- a/ts/window.d.ts +++ b/ts/window.d.ts @@ -28,7 +28,6 @@ import type { StateType } from './state/reducer.js'; import type { CIType } from './CI.js'; import type { IPCEventsType } from './util/createIPCEvents.js'; import type { SignalContextType } from './windows/context.js'; -import type { initializeMigrations } from './signal.js'; import type { PropsPreloadType as PreferencesPropsType } from './components/Preferences.js'; import type { WindowsNotificationData } from './services/notifications.js'; import type { QueryStatsOptions } from './sql/main.js'; @@ -129,7 +128,6 @@ export type SignalCoreType = { SettingsWindowProps?: SettingsWindowPropsType; OS: OSType; - Migrations: ReturnType; // Only for debugging in Dev Tools Services?: { diff --git a/ts/windows/main/phase2-dependencies.ts b/ts/windows/main/phase2-dependencies.ts index 70c689baf8..67abb13939 100644 --- a/ts/windows/main/phase2-dependencies.ts +++ b/ts/windows/main/phase2-dependencies.ts @@ -14,12 +14,7 @@ import { STICKERS_PATH, DRAFT_PATH, } from '../../util/basePaths.js'; -import { createLogger } from '../../logging/log.js'; import { SignalContext } from '../context.js'; -import * as Attachments from './attachments.js'; -import { itemStorage } from '../../textsecure/Storage.js'; - -const log = createLogger('phase2-dependencies'); initializeLogging(); @@ -53,9 +48,4 @@ if (SignalContext.config.disableIPv6) { } dns.setFallback(SignalContext.config.dnsFallback); -window.Signal = setup({ - Attachments, - getRegionCode: () => itemStorage.get('regionCode'), - logger: log, - userDataPath: window.SignalContext.getPath('userData'), -}); +window.Signal = setup();