// Copyright 2018 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only // The idea with this file is to make it webpackable for the style guide import * as Crypto from './Crypto'; import * as Curve from './Curve'; import { start as conversationControllerStart } from './ConversationController'; import Data from './sql/Client'; import * as Groups from './groups'; import OS from './util/os/osMain'; import * as RemoteConfig from './RemoteConfig'; // Components import { ConfirmationDialog } from './components/ConfirmationDialog'; // State import { createApp } from './state/roots/createApp'; import { createSafetyNumberViewer } from './state/roots/createSafetyNumberViewer'; import { createStore } from './state/createStore'; // Types import * as TypesAttachment from './types/Attachment'; import * as VisualAttachment from './types/VisualAttachment'; import * as MessageType from './types/Message2'; import { Address } from './types/Address'; import { QualifiedAddress } from './types/QualifiedAddress'; // Processes / Services import { initializeGroupCredentialFetcher } from './services/groupCredentialFetcher'; import { initializeNetworkObserver } from './services/networkObserver'; import { initializeUpdateListener } from './services/updateListener'; import { calling } from './services/calling'; import * as storage from './services/storage'; import type { LoggerType } from './types/Logging'; import type { AttachmentType, AttachmentWithHydratedData, } from './types/Attachment'; import type { MessageAttributesType, QuotedMessageType } from './model-types.d'; import type { SignalCoreType } from './window.d'; import type { EmbeddedContactType, EmbeddedContactWithHydratedAvatar, } from './types/EmbeddedContact'; import type { LinkPreviewType, LinkPreviewWithHydratedData, } from './types/message/LinkPreviews'; import type { StickerType, StickerWithHydratedData } from './types/Stickers'; type MigrationsModuleType = { attachmentsPath: string; copyIntoAttachmentsDirectory: ( path: string ) => Promise<{ path: string; size: number }>; copyIntoTempDirectory: ( path: string ) => Promise<{ path: string; size: number }>; deleteAttachmentData: (path: string) => Promise; deleteAvatar: (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; getAbsoluteDraftPath: (path: string) => string; getAbsoluteStickerPath: (path: string) => string; getAbsoluteTempPath: (path: string) => string; loadAttachmentData: ( attachment: Pick ) => Promise; loadContactData: ( contact: Array | undefined ) => Promise | undefined>; loadMessage: ( message: MessageAttributesType ) => Promise; loadPreviewData: ( preview: Array | undefined ) => Promise>; loadQuoteData: ( quote: QuotedMessageType | null | undefined ) => Promise; loadStickerData: ( sticker: StickerType | undefined ) => Promise; readAttachmentData: (path: string) => Promise; readDraftData: (path: string) => Promise; readStickerData: (path: string) => Promise; readTempData: (path: string) => Promise; saveAttachmentToDisk: (options: { data: Uint8Array; name: string; }) => Promise; processNewAttachment: (attachment: AttachmentType) => Promise; processNewSticker: (stickerData: Uint8Array) => Promise<{ path: string; width: number; height: number; }>; processNewEphemeralSticker: (stickerData: Uint8Array) => Promise<{ path: string; width: number; height: number; }>; upgradeMessageSchema: ( attributes: MessageAttributesType, options?: { maxVersion?: number } ) => Promise; writeMessageAttachments: ( message: MessageAttributesType ) => Promise; writeNewAttachmentData: (data: Uint8Array) => Promise; writeNewDraftData: (data: Uint8Array) => Promise; writeNewAvatarData: (data: Uint8Array) => Promise; writeNewBadgeImageFileData: (data: Uint8Array) => Promise; writeNewTempData: (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, createReader, createWriterForExisting, createWriterForNew, createDoesExist, getAvatarsPath, getDraftPath, getPath, getStickersPath, getBadgesPath, getTempPath, saveAttachmentToDisk, } = Attachments; const { getImageDimensions, makeImageThumbnail, makeObjectUrl, makeVideoScreenshot, revokeObjectUrl, } = VisualType; const attachmentsPath = getPath(userDataPath); const readAttachmentData = createReader(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 deleteOnDisk = Attachments.createDeleter(attachmentsPath); const writeExistingAttachmentData = createWriterForExisting(attachmentsPath); const writeNewAttachmentData = createWriterForNew(attachmentsPath); const copyIntoAttachmentsDirectory = Attachments.copyIntoAttachmentsDirectory(attachmentsPath); const doesAttachmentExist = createDoesExist(attachmentsPath); const stickersPath = getStickersPath(userDataPath); const writeNewStickerData = createWriterForNew(stickersPath); const getAbsoluteStickerPath = createAbsolutePathGetter(stickersPath); const deleteSticker = Attachments.createDeleter(stickersPath); const readStickerData = createReader(stickersPath); const badgesPath = getBadgesPath(userDataPath); const getAbsoluteBadgeImageFilePath = createAbsolutePathGetter(badgesPath); const writeNewBadgeImageFileData = createWriterForNew(badgesPath, '.svg'); const tempPath = getTempPath(userDataPath); const getAbsoluteTempPath = createAbsolutePathGetter(tempPath); const writeNewTempData = createWriterForNew(tempPath); const deleteTempFile = Attachments.createDeleter(tempPath); const readTempData = createReader(tempPath); const copyIntoTempDirectory = Attachments.copyIntoAttachmentsDirectory(tempPath); const draftPath = getDraftPath(userDataPath); const getAbsoluteDraftPath = createAbsolutePathGetter(draftPath); const writeNewDraftData = createWriterForNew(draftPath); const deleteDraftFile = Attachments.createDeleter(draftPath); const readDraftData = createReader(draftPath); const avatarsPath = getAvatarsPath(userDataPath); const getAbsoluteAvatarPath = createAbsolutePathGetter(avatarsPath); const writeNewAvatarData = createWriterForNew(avatarsPath); const deleteAvatar = Attachments.createDeleter(avatarsPath); return { attachmentsPath, copyIntoAttachmentsDirectory, copyIntoTempDirectory, deleteAttachmentData: deleteOnDisk, deleteAvatar, deleteDraftFile, deleteExternalMessageFiles: MessageType.deleteAllExternalFiles({ deleteAttachmentData: Type.deleteData(deleteOnDisk), deleteOnDisk, }), deleteSticker, deleteTempFile, doesAttachmentExist, getAbsoluteAttachmentPath, getAbsoluteAvatarPath, getAbsoluteBadgeImageFilePath, getAbsoluteDraftPath, getAbsoluteStickerPath, getAbsoluteTempPath, loadAttachmentData, loadContactData, loadMessage: MessageType.createAttachmentLoader(loadAttachmentData), loadPreviewData, loadQuoteData, loadStickerData, readAttachmentData, readDraftData, readStickerData, readTempData, saveAttachmentToDisk, processNewAttachment: (attachment: AttachmentType) => MessageType.processNewAttachment(attachment, { writeNewAttachmentData, getAbsoluteAttachmentPath, makeObjectUrl, revokeObjectUrl, getImageDimensions, makeImageThumbnail, makeVideoScreenshot, logger, }), processNewSticker: (stickerData: Uint8Array) => MessageType.processNewSticker(stickerData, { writeNewStickerData, getAbsoluteStickerPath, getImageDimensions, logger, }), processNewEphemeralSticker: (stickerData: Uint8Array) => MessageType.processNewSticker(stickerData, { writeNewStickerData: writeNewTempData, getAbsoluteStickerPath: getAbsoluteTempPath, getImageDimensions, logger, }), upgradeMessageSchema: ( message: MessageAttributesType, options: { maxVersion?: number } = {} ) => { const { maxVersion } = options; return MessageType.upgradeSchema(message, { writeNewAttachmentData, getRegionCode, getAbsoluteAttachmentPath, makeObjectUrl, revokeObjectUrl, getImageDimensions, makeImageThumbnail, makeVideoScreenshot, logger, maxVersion, getAbsoluteStickerPath, writeNewStickerData, }); }, writeMessageAttachments: MessageType.createAttachmentDataWriter({ writeExistingAttachmentData, logger, }), writeNewAttachmentData: createWriterForNew(attachmentsPath), writeNewAvatarData, writeNewDraftData, writeNewBadgeImageFileData, writeNewTempData, }; } type StringGetterType = (basePath: string) => string; type AttachmentsModuleType = { getAvatarsPath: StringGetterType; getBadgesPath: StringGetterType; getDraftPath: StringGetterType; getPath: StringGetterType; getStickersPath: StringGetterType; getTempPath: StringGetterType; getUpdateCachePath: StringGetterType; createDeleter: (root: string) => (relativePath: string) => Promise; createReader: (root: string) => (relativePath: string) => Promise; getRelativePath: (name: string) => string; createName: (suffix?: string) => string; copyIntoAttachmentsDirectory: ( root: string ) => (sourcePath: string) => Promise<{ path: string; size: number }>; createWriterForNew: ( root: string, suffix?: string ) => (bytes: Uint8Array) => Promise; createWriterForExisting: ( root: string ) => (options: { data?: Uint8Array; path?: string }) => Promise; createAbsolutePathGetter: ( rootPath: string ) => (relativePath: string) => string; createDoesExist: (root: string) => (relativePath: string) => Promise; saveAttachmentToDisk: ({ data, name, }: { data: Uint8Array; name: 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, }); const Components = { ConfirmationDialog, }; const Roots = { createApp, createSafetyNumberViewer, }; const Services = { calling, initializeGroupCredentialFetcher, initializeNetworkObserver, initializeUpdateListener, // Testing storage, }; const State = { createStore, Roots, }; const Types = { Message: MessageType, // Mostly for debugging Address, QualifiedAddress, }; return { Components, Crypto, Curve, // Note: used in test/index.html, and not type-checked! conversationControllerStart, Data, Groups, Migrations, OS, RemoteConfig, Services, State, Types, }; };