Page MenuHomePhabricator

No OneTemporary

diff --git a/lib/types/sqlite-types.js b/lib/types/sqlite-types.js
index 2dfb9206b..29a1c6996 100644
--- a/lib/types/sqlite-types.js
+++ b/lib/types/sqlite-types.js
@@ -1,62 +1,67 @@
// @flow
import type { ClientDBMessageInfo } from './message-types.js';
import type { StoreOperations } from './store-ops-types.js';
export const outboundP2PMessageStatuses = Object.freeze({
// The message was prepared to be sent to other peers, but it's not encrypted.
// It was inserted into DB in the same transaction as making changes to
// the store.
persisted: 'persisted',
// Encryption is done in the same transaction as persisting the CryptoModule,
// and message order is also tracked on the client side,
// which means the message can be sent.
encrypted: 'encrypted',
// The message was sent to another peer (Tunnelbroker owns it),
// waiting for the confirmation (handled in `peerToPeerMessageHandler`).
sent: 'sent',
});
export type OutboundP2PMessageStatuses = $Values<
typeof outboundP2PMessageStatuses,
>;
export type InboundP2PMessage = {
+messageID: string,
+senderDeviceID: string,
+plaintext: string,
+status: string,
};
export type OutboundP2PMessage = {
+messageID: string,
+deviceID: string,
+userID: string,
+timestamp: string,
+plaintext: string,
+ciphertext: string,
+status: OutboundP2PMessageStatuses,
+supportsAutoRetry: boolean,
};
export type SQLiteAPI = {
// read operations
- +getAllInboundP2PMessage: () => Promise<InboundP2PMessage[]>,
- +getAllOutboundP2PMessage: () => Promise<OutboundP2PMessage[]>,
- +getRelatedMessages: (messageID: string) => Promise<ClientDBMessageInfo[]>,
+ +getAllInboundP2PMessage: () => Promise<Array<InboundP2PMessage>>,
+ +getAllOutboundP2PMessage: () => Promise<Array<OutboundP2PMessage>>,
+ +getRelatedMessages: (
+ messageID: string,
+ ) => Promise<Array<ClientDBMessageInfo>>,
+ +getOutboundP2PMessagesByID: (
+ ids: $ReadOnlyArray<string>,
+ ) => Promise<Array<OutboundP2PMessage>>,
// write operations
+removeInboundP2PMessages: (ids: $ReadOnlyArray<string>) => Promise<void>,
+markOutboundP2PMessageAsSent: (
messageID: string,
deviceID: string,
) => Promise<void>,
+removeOutboundP2PMessagesOlderThan: (
messageID: string,
deviceID: string,
) => Promise<void>,
+processDBStoreOperations: (
operations: StoreOperations,
userID?: ?string,
) => Promise<void>,
};
diff --git a/lib/utils/__mocks__/config.js b/lib/utils/__mocks__/config.js
index 2de245d37..b35cad26f 100644
--- a/lib/utils/__mocks__/config.js
+++ b/lib/utils/__mocks__/config.js
@@ -1,44 +1,45 @@
// @flow
import { type Config } from '../config.js';
const getConfig = (): Config => ({
resolveKeyserverSessionInvalidationUsingNativeCredentials: null,
setSessionIDOnRequest: true,
calendarRangeInactivityLimit: null,
platformDetails: {
platform: 'web',
codeVersion: 70,
stateVersion: 50,
},
authoritativeKeyserverID: '123',
olmAPI: {
initializeCryptoAccount: jest.fn(),
getUserPublicKey: jest.fn(),
encrypt: jest.fn(),
encryptAndPersist: jest.fn(),
decrypt: jest.fn(),
decryptSequentialAndPersist: jest.fn(),
contentInboundSessionCreator: jest.fn(),
contentOutboundSessionCreator: jest.fn(),
notificationsSessionCreator: jest.fn(),
getOneTimeKeys: jest.fn(),
validateAndUploadPrekeys: jest.fn(),
signMessage: jest.fn(),
verifyMessage: jest.fn(),
markPrekeysAsPublished: jest.fn(),
},
sqliteAPI: {
getAllInboundP2PMessage: jest.fn(),
removeInboundP2PMessages: jest.fn(),
processDBStoreOperations: jest.fn(),
getAllOutboundP2PMessage: jest.fn(),
markOutboundP2PMessageAsSent: jest.fn(),
removeOutboundP2PMessagesOlderThan: jest.fn(),
getRelatedMessages: jest.fn(),
+ getOutboundP2PMessagesByID: jest.fn(),
},
});
const hasConfig = (): boolean => true;
export { getConfig, hasConfig };
diff --git a/native/database/sqlite-api.js b/native/database/sqlite-api.js
index d3c67ab63..ceaabd521 100644
--- a/native/database/sqlite-api.js
+++ b/native/database/sqlite-api.js
@@ -1,23 +1,24 @@
// @flow
import type { SQLiteAPI } from 'lib/types/sqlite-types.js';
import { commCoreModule } from '../native-modules.js';
import { processDBStoreOperations } from '../redux/redux-utils.js';
const sqliteAPI: SQLiteAPI = {
// read operations
getAllInboundP2PMessage: commCoreModule.getAllInboundP2PMessage,
getAllOutboundP2PMessage: commCoreModule.getAllOutboundP2PMessage,
getRelatedMessages: commCoreModule.getRelatedMessages,
+ getOutboundP2PMessagesByID: commCoreModule.getOutboundP2PMessagesByID,
// write operations
removeInboundP2PMessages: commCoreModule.removeInboundP2PMessages,
markOutboundP2PMessageAsSent: commCoreModule.markOutboundP2PMessageAsSent,
removeOutboundP2PMessagesOlderThan:
commCoreModule.removeOutboundP2PMessagesOlderThan,
processDBStoreOperations,
};
export { sqliteAPI };
diff --git a/native/schema/CommCoreModuleSchema.js b/native/schema/CommCoreModuleSchema.js
index 94a30ad0c..de1de3622 100644
--- a/native/schema/CommCoreModuleSchema.js
+++ b/native/schema/CommCoreModuleSchema.js
@@ -1,203 +1,203 @@
// @flow
'use strict';
import { TurboModuleRegistry } from 'react-native';
import type { TurboModule } from 'react-native/Libraries/TurboModule/RCTExport.js';
import type { ClientDBMessageStoreOperation } from 'lib/ops/message-store-ops.js';
import type { ClientDBReportStoreOperation } from 'lib/ops/report-store-ops.js';
import type { ClientDBThreadStoreOperation } from 'lib/ops/thread-store-ops.js';
import type {
OneTimeKeysResult,
SignedPrekeys,
ClientPublicKeys,
EncryptedData,
OutboundSessionCreationResult,
} from 'lib/types/crypto-types.js';
import type { ClientDBMessageInfo } from 'lib/types/message-types.js';
import type { SIWEBackupSecrets } from 'lib/types/siwe-types.js';
import type {
InboundP2PMessage,
OutboundP2PMessage,
} from 'lib/types/sqlite-types.js';
import type {
ClientDBStore,
ClientDBStoreOperations,
} from 'lib/types/store-ops-types';
import type { ClientDBThreadInfo } from 'lib/types/thread-types.js';
type CommServicesAuthMetadata = {
+userID?: ?string,
+deviceID?: ?string,
+accessToken?: ?string,
};
interface Spec extends TurboModule {
+getDraft: (key: string) => Promise<string>;
+updateDraft: (key: string, text: string) => Promise<boolean>;
+moveDraft: (oldKey: string, newKey: string) => Promise<boolean>;
+getClientDBStore: () => Promise<ClientDBStore>;
+removeAllDrafts: () => Promise<void>;
+getAllMessagesSync: () => $ReadOnlyArray<ClientDBMessageInfo>;
+processMessageStoreOperationsSync: (
operations: $ReadOnlyArray<ClientDBMessageStoreOperation>,
) => void;
+getAllThreadsSync: () => $ReadOnlyArray<ClientDBThreadInfo>;
+processReportStoreOperationsSync: (
operations: $ReadOnlyArray<ClientDBReportStoreOperation>,
) => void;
+processThreadStoreOperationsSync: (
operations: $ReadOnlyArray<ClientDBThreadStoreOperation>,
) => void;
+processDBStoreOperations: (operations: Object) => Promise<void>;
+initializeCryptoAccount: () => Promise<string>;
+getUserPublicKey: () => Promise<ClientPublicKeys>;
+getOneTimeKeys: (oneTimeKeysAmount: number) => Promise<OneTimeKeysResult>;
+validateAndGetPrekeys: () => Promise<SignedPrekeys>;
+validateAndUploadPrekeys: (
authUserID: string,
authDeviceID: string,
authAccessToken: string,
) => Promise<void>;
+initializeNotificationsSession: (
identityKeys: string,
prekey: string,
prekeySignature: string,
oneTimeKey: ?string,
keyserverID: string,
) => Promise<string>;
+isNotificationsSessionInitialized: () => Promise<boolean>;
+updateKeyserverDataInNotifStorage: (
keyserversData: $ReadOnlyArray<{ +id: string, +unreadCount: number }>,
) => Promise<void>;
+removeKeyserverDataFromNotifStorage: (
keyserverIDsToDelete: $ReadOnlyArray<string>,
) => Promise<void>;
+getKeyserverDataFromNotifStorage: (
keyserverIDs: $ReadOnlyArray<string>,
) => Promise<$ReadOnlyArray<{ +id: string, +unreadCount: number }>>;
+initializeContentOutboundSession: (
identityKeys: string,
prekey: string,
prekeySignature: string,
oneTimeKey: ?string,
deviceID: string,
) => Promise<OutboundSessionCreationResult>;
+initializeContentInboundSession: (
identityKeys: string,
encryptedContent: Object,
deviceID: string,
sessionVersion: number,
overwrite: boolean,
) => Promise<string>;
+encrypt: (message: string, deviceID: string) => Promise<EncryptedData>;
+encryptAndPersist: (
message: string,
deviceID: string,
messageID: string,
) => Promise<EncryptedData>;
+decrypt: (encryptedData: Object, deviceID: string) => Promise<string>;
+decryptSequentialAndPersist: (
encryptedData: Object,
deviceID: string,
messageID: string,
) => Promise<string>;
+signMessage: (message: string) => Promise<string>;
+verifySignature: (
publicKey: string,
message: string,
signature: string,
) => Promise<void>;
+getCodeVersion: () => number;
+terminate: () => void;
+setNotifyToken: (token: string) => Promise<void>;
+clearNotifyToken: () => Promise<void>;
+stampSQLiteDBUserID: (userID: string) => Promise<void>;
+getSQLiteStampedUserID: () => Promise<string>;
+clearSensitiveData: () => Promise<void>;
+checkIfDatabaseNeedsDeletion: () => boolean;
+reportDBOperationsFailure: () => void;
+computeBackupKey: (password: string, backupID: string) => Promise<Object>;
+generateRandomString: (size: number) => Promise<string>;
+setCommServicesAuthMetadata: (
userID: string,
deviceID: string,
accessToken: string,
) => Promise<void>;
+getCommServicesAuthMetadata: () => Promise<CommServicesAuthMetadata>;
+clearCommServicesAuthMetadata: () => Promise<void>;
+setCommServicesAccessToken: (accessToken: string) => Promise<void>;
+clearCommServicesAccessToken: () => Promise<void>;
+startBackupHandler: () => void;
+stopBackupHandler: () => void;
+createNewBackup: (backupSecret: string) => Promise<void>;
+createNewSIWEBackup: (
backupSecret: string,
siweBackupMsg: string,
) => Promise<void>;
+restoreBackup: (backupSecret: string, maxVersion: string) => Promise<string>;
+restoreSIWEBackup: (
backupSecret: string,
backupID: string,
maxVersion: string,
) => Promise<string>;
+restoreBackupData: (
backupID: string,
backupDataKey: string,
backupLogDataKey: string,
maxVersion: string,
) => Promise<void>;
+retrieveBackupKeys: (backupSecret: string) => Promise<string>;
+retrieveLatestSIWEBackupData: () => Promise<string>;
+setSIWEBackupSecrets: (siweBackupSecrets: Object) => Promise<void>;
+getSIWEBackupSecrets: () => Promise<?Object>;
+getAllInboundP2PMessage: () => Promise<InboundP2PMessage[]>;
+removeInboundP2PMessages: (ids: $ReadOnlyArray<string>) => Promise<void>;
+getOutboundP2PMessagesByID: (
ids: $ReadOnlyArray<string>,
- ) => Promise<$ReadOnlyArray<OutboundP2PMessage>>;
+ ) => Promise<Array<OutboundP2PMessage>>;
+getAllOutboundP2PMessage: () => Promise<OutboundP2PMessage[]>;
+markOutboundP2PMessageAsSent: (
messageID: string,
deviceID: string,
) => Promise<void>;
+removeOutboundP2PMessagesOlderThan: (
messageID: string,
deviceID: string,
) => Promise<void>;
+getSyncedDatabaseVersion: () => Promise<string>;
+markPrekeysAsPublished: () => Promise<void>;
+getRelatedMessages: (messageID: string) => Promise<ClientDBMessageInfo[]>;
}
export interface CoreModuleSpec extends Spec {
+computeBackupKey: (
password: string,
backupID: string,
) => Promise<ArrayBuffer>;
+decrypt: (encryptedData: EncryptedData, deviceID: string) => Promise<string>;
+decryptSequentialAndPersist: (
encryptedData: EncryptedData,
deviceID: string,
messageID: string,
) => Promise<string>;
+initializeContentInboundSession: (
identityKeys: string,
encryptedContent: EncryptedData,
deviceID: string,
sessionVersion: number,
overwrite: boolean,
) => Promise<string>;
+setSIWEBackupSecrets: (
siweBackupSecrets: SIWEBackupSecrets,
) => Promise<void>;
+getSIWEBackupSecrets: () => Promise<?SIWEBackupSecrets>;
+processDBStoreOperations: (
operations: ClientDBStoreOperations,
) => Promise<void>;
}
export default (TurboModuleRegistry.getEnforcing<Spec>(
'CommTurboModule',
): Spec);
diff --git a/web/database/sqlite-api.js b/web/database/sqlite-api.js
index a0a317099..037ad3860 100644
--- a/web/database/sqlite-api.js
+++ b/web/database/sqlite-api.js
@@ -1,88 +1,102 @@
// @flow
import type { ClientDBMessageInfo } from 'lib/types/message-types.js';
import type {
SQLiteAPI,
InboundP2PMessage,
OutboundP2PMessage,
} from 'lib/types/sqlite-types.js';
import { getCommSharedWorker } from '../shared-worker/shared-worker-provider.js';
import { processDBStoreOperations } from '../shared-worker/utils/store.js';
import { workerRequestMessageTypes } from '../types/worker-types.js';
const sqliteAPI: SQLiteAPI = {
// read operations
async getAllInboundP2PMessage(): Promise<InboundP2PMessage[]> {
const sharedWorker = await getCommSharedWorker();
const data = await sharedWorker.schedule({
type: workerRequestMessageTypes.GET_INBOUND_P2P_MESSAGES,
});
const messages: ?$ReadOnlyArray<InboundP2PMessage> =
data?.inboundP2PMessages;
return messages ? [...messages] : [];
},
async getAllOutboundP2PMessage(): Promise<OutboundP2PMessage[]> {
const sharedWorker = await getCommSharedWorker();
const data = await sharedWorker.schedule({
type: workerRequestMessageTypes.GET_OUTBOUND_P2P_MESSAGES,
});
const messages: ?$ReadOnlyArray<OutboundP2PMessage> =
data?.outboundP2PMessages;
return messages ? [...messages] : [];
},
async getRelatedMessages(messageID: string): Promise<ClientDBMessageInfo[]> {
const sharedWorker = await getCommSharedWorker();
const data = await sharedWorker.schedule({
type: workerRequestMessageTypes.GET_RELATED_MESSAGES,
messageID,
});
const messages: ?$ReadOnlyArray<ClientDBMessageInfo> = data?.messages;
return messages ? [...messages] : [];
},
+ async getOutboundP2PMessagesByID(
+ ids: $ReadOnlyArray<string>,
+ ): Promise<Array<OutboundP2PMessage>> {
+ const sharedWorker = await getCommSharedWorker();
+
+ const data = await sharedWorker.schedule({
+ type: workerRequestMessageTypes.GET_OUTBOUND_P2P_MESSAGES_BY_ID,
+ messageIDs: ids,
+ });
+ const messages: ?$ReadOnlyArray<OutboundP2PMessage> =
+ data?.outboundP2PMessages;
+ return messages ? [...messages] : [];
+ },
+
// write operations
async removeInboundP2PMessages(ids: $ReadOnlyArray<string>): Promise<void> {
const sharedWorker = await getCommSharedWorker();
await sharedWorker.schedule({
type: workerRequestMessageTypes.REMOVE_INBOUND_P2P_MESSAGES,
ids,
});
},
async markOutboundP2PMessageAsSent(
messageID: string,
deviceID: string,
): Promise<void> {
const sharedWorker = await getCommSharedWorker();
await sharedWorker.schedule({
type: workerRequestMessageTypes.MARK_OUTBOUND_P2P_MESSAGE_AS_SENT,
messageID,
deviceID,
});
},
async removeOutboundP2PMessagesOlderThan(
messageID: string,
deviceID: string,
): Promise<void> {
const sharedWorker = await getCommSharedWorker();
await sharedWorker.schedule({
type: workerRequestMessageTypes.REMOVE_OUTBOUND_P2P_MESSAGES,
messageID,
deviceID,
});
},
processDBStoreOperations,
};
export { sqliteAPI };
diff --git a/web/shared-worker/worker/shared-worker.js b/web/shared-worker/worker/shared-worker.js
index 113020c3a..5f67494ca 100644
--- a/web/shared-worker/worker/shared-worker.js
+++ b/web/shared-worker/worker/shared-worker.js
@@ -1,394 +1,403 @@
// @flow
import localforage from 'localforage';
import { getMessageForException } from 'lib/utils/errors.js';
import { restoreBackup } from './backup.js';
import { processAppIdentityClientRequest } from './identity-client.js';
import {
getClientStoreFromQueryExecutor,
processDBStoreOperations,
} from './process-operations.js';
import { clearCryptoStore, processAppOlmApiRequest } from './worker-crypto.js';
import {
getDBModule,
getSQLiteQueryExecutor,
setDBModule,
setSQLiteQueryExecutor,
getPlatformDetails,
setPlatformDetails,
} from './worker-database.js';
import initBackupClientModule from '../../backup-client-wasm/wasm/backup-client-wasm.js';
import {
decryptData,
encryptData,
generateCryptoKey,
importJWKKey,
type EncryptedData,
} from '../../crypto/aes-gcm-crypto-utils.js';
import {
type SharedWorkerMessageEvent,
type WorkerRequestMessage,
type WorkerResponseMessage,
workerRequestMessageTypes,
workerResponseMessageTypes,
type WorkerRequestProxyMessage,
workerWriteRequests,
workerOlmAPIRequests,
} from '../../types/worker-types.js';
import { workerIdentityClientRequests } from '../../types/worker-types.js';
import { getDatabaseModule } from '../db-module.js';
import { webMessageToClientDBMessageInfo } from '../types/entities.js';
import {
COMM_SQLITE_DATABASE_PATH,
SQLITE_STAMPED_USER_ID_KEY,
localforageConfig,
SQLITE_CONTENT,
SQLITE_ENCRYPTION_KEY,
DEFAULT_BACKUP_CLIENT_FILENAME,
} from '../utils/constants.js';
import {
clearSensitiveData,
exportDatabaseContent,
importDatabaseContent,
} from '../utils/db-utils.js';
localforage.config(localforageConfig);
let encryptionKey: ?CryptoKey = null;
let persistNeeded: boolean = false;
let persistInProgress: boolean = false;
async function initDatabase(
webworkerModulesFilePath: string,
commQueryExecutorFilename: ?string,
encryptionKeyJWK?: ?SubtleCrypto$JsonWebKey,
) {
const dbModule = getDBModule();
const sqliteQueryExecutor = getSQLiteQueryExecutor();
if (!!dbModule && !!sqliteQueryExecutor) {
console.log('Database already initialized');
return;
}
const newModule = dbModule
? dbModule
: getDatabaseModule(commQueryExecutorFilename, webworkerModulesFilePath);
if (!dbModule) {
setDBModule(newModule);
}
if (encryptionKeyJWK) {
encryptionKey = await importJWKKey(encryptionKeyJWK);
} else {
encryptionKey = await localforage.getItem(SQLITE_ENCRYPTION_KEY);
if (!encryptionKey) {
const cryptoKey = await generateCryptoKey({ extractable: false });
await localforage.setItem(SQLITE_ENCRYPTION_KEY, cryptoKey);
}
}
const encryptedContent =
await localforage.getItem<EncryptedData>(SQLITE_CONTENT);
let dbContent = null;
try {
if (encryptionKey && encryptedContent) {
dbContent = await decryptData(encryptedContent, encryptionKey);
}
} catch (e) {
console.error('Error while decrypting content, clearing database content');
await localforage.removeItem(SQLITE_CONTENT);
}
if (dbContent) {
importDatabaseContent(dbContent, newModule, COMM_SQLITE_DATABASE_PATH);
console.info(
'Database exists and is properly encrypted, using persisted data',
);
} else {
console.info('Creating fresh database');
}
setSQLiteQueryExecutor(
new newModule.SQLiteQueryExecutor(COMM_SQLITE_DATABASE_PATH),
);
}
async function initBackupClient(
webworkerModulesFilePath: string,
backupClientFilename: ?string,
) {
let modulePath;
if (backupClientFilename) {
modulePath = `${webworkerModulesFilePath}/${backupClientFilename}`;
} else {
modulePath = `${webworkerModulesFilePath}/${DEFAULT_BACKUP_CLIENT_FILENAME}`;
}
await initBackupClientModule(modulePath);
}
async function persist() {
persistInProgress = true;
const sqliteQueryExecutor = getSQLiteQueryExecutor();
const dbModule = getDBModule();
if (!sqliteQueryExecutor || !dbModule) {
persistInProgress = false;
throw new Error(
'Database not initialized while persisting database content',
);
}
if (!encryptionKey) {
encryptionKey = await localforage.getItem(SQLITE_ENCRYPTION_KEY);
}
while (persistNeeded) {
persistNeeded = false;
const dbData = exportDatabaseContent(dbModule, COMM_SQLITE_DATABASE_PATH);
if (!encryptionKey) {
persistInProgress = false;
throw new Error('Encryption key is missing');
}
const encryptedData = await encryptData(dbData, encryptionKey);
await localforage.setItem(SQLITE_CONTENT, encryptedData);
}
persistInProgress = false;
}
async function processAppRequest(
message: WorkerRequestMessage,
): Promise<?WorkerResponseMessage> {
// non-database operations
if (message.type === workerRequestMessageTypes.PING) {
return {
type: workerResponseMessageTypes.PONG,
text: 'PONG',
};
} else if (
message.type === workerRequestMessageTypes.GENERATE_DATABASE_ENCRYPTION_KEY
) {
const cryptoKey = await generateCryptoKey({ extractable: false });
await localforage.setItem(SQLITE_ENCRYPTION_KEY, cryptoKey);
return undefined;
}
const sqliteQueryExecutor = getSQLiteQueryExecutor();
const dbModule = getDBModule();
// database operations
if (message.type === workerRequestMessageTypes.INIT) {
setPlatformDetails(message.platformDetails);
const promises = [
initDatabase(
message.webworkerModulesFilePath,
message.commQueryExecutorFilename,
message.encryptionKey,
),
];
if (message.backupClientFilename !== undefined) {
promises.push(
initBackupClient(
message.webworkerModulesFilePath,
message.backupClientFilename,
),
);
}
await Promise.all(promises);
return undefined;
} else if (message.type === workerRequestMessageTypes.CLEAR_SENSITIVE_DATA) {
clearCryptoStore();
encryptionKey = null;
await localforage.clear();
if (dbModule && sqliteQueryExecutor) {
clearSensitiveData(
dbModule,
COMM_SQLITE_DATABASE_PATH,
sqliteQueryExecutor,
);
}
setSQLiteQueryExecutor(null);
return undefined;
}
if (!sqliteQueryExecutor) {
throw new Error(
`Database not initialized, unable to process request type: ${message.type}`,
);
}
// read-only operations
if (message.type === workerRequestMessageTypes.GET_CLIENT_STORE) {
return {
type: workerResponseMessageTypes.CLIENT_STORE,
store: getClientStoreFromQueryExecutor(sqliteQueryExecutor),
};
} else if (
message.type === workerRequestMessageTypes.GET_SQLITE_STAMPED_USER_ID
) {
return {
type: workerResponseMessageTypes.GET_SQLITE_STAMPED_USER_ID,
userID: sqliteQueryExecutor.getMetadata(SQLITE_STAMPED_USER_ID_KEY),
};
} else if (
message.type === workerRequestMessageTypes.GET_PERSIST_STORAGE_ITEM
) {
return {
type: workerResponseMessageTypes.GET_PERSIST_STORAGE_ITEM,
item: sqliteQueryExecutor.getPersistStorageItem(message.key),
};
} else if (
message.type === workerRequestMessageTypes.GET_INBOUND_P2P_MESSAGES
) {
return {
type: workerResponseMessageTypes.GET_INBOUND_P2P_MESSAGES,
inboundP2PMessages: sqliteQueryExecutor.getAllInboundP2PMessage(),
};
} else if (
message.type === workerRequestMessageTypes.GET_OUTBOUND_P2P_MESSAGES
) {
return {
type: workerResponseMessageTypes.GET_OUTBOUND_P2P_MESSAGES,
outboundP2PMessages: sqliteQueryExecutor.getAllOutboundP2PMessages(),
};
} else if (message.type === workerRequestMessageTypes.GET_RELATED_MESSAGES) {
const webMessageEntities = sqliteQueryExecutor.getRelatedMessagesWeb(
message.messageID,
);
return {
type: workerResponseMessageTypes.GET_RELATED_MESSAGES,
messages: webMessageEntities.map(webMessageToClientDBMessageInfo),
};
+ } else if (
+ message.type === workerRequestMessageTypes.GET_OUTBOUND_P2P_MESSAGES_BY_ID
+ ) {
+ return {
+ type: workerResponseMessageTypes.GET_OUTBOUND_P2P_MESSAGES,
+ outboundP2PMessages: sqliteQueryExecutor.getOutboundP2PMessagesByID(
+ message.messageIDs,
+ ),
+ };
}
// write operations
const isOlmAPIRequest = workerOlmAPIRequests.includes(message.type);
const isIdentityClientRequest = workerIdentityClientRequests.includes(
message.type,
);
if (
!workerWriteRequests.includes(message.type) &&
!isOlmAPIRequest &&
!isIdentityClientRequest
) {
throw new Error(`Request type ${message.type} not supported`);
}
if (!sqliteQueryExecutor || !dbModule) {
throw new Error(
`Database not initialized, unable to process request type: ${message.type}`,
);
}
let result;
if (isOlmAPIRequest) {
result = await processAppOlmApiRequest(message);
} else if (isIdentityClientRequest) {
const platformDetails = getPlatformDetails();
if (!platformDetails) {
throw new Error(
'Platform details not set, unable to process identity client request',
);
}
result = await processAppIdentityClientRequest(
sqliteQueryExecutor,
dbModule,
platformDetails,
message,
);
} else if (
message.type === workerRequestMessageTypes.PROCESS_STORE_OPERATIONS
) {
processDBStoreOperations(
sqliteQueryExecutor,
message.storeOperations,
dbModule,
);
} else if (
message.type === workerRequestMessageTypes.STAMP_SQLITE_DB_USER_ID
) {
sqliteQueryExecutor.setMetadata(SQLITE_STAMPED_USER_ID_KEY, message.userID);
} else if (
message.type === workerRequestMessageTypes.SET_PERSIST_STORAGE_ITEM
) {
sqliteQueryExecutor.setPersistStorageItem(message.key, message.item);
} else if (
message.type === workerRequestMessageTypes.REMOVE_PERSIST_STORAGE_ITEM
) {
sqliteQueryExecutor.removePersistStorageItem(message.key);
} else if (message.type === workerRequestMessageTypes.BACKUP_RESTORE) {
await restoreBackup(
sqliteQueryExecutor,
dbModule,
message.authMetadata,
message.backupID,
message.backupDataKey,
message.backupLogDataKey,
);
} else if (
message.type === workerRequestMessageTypes.REMOVE_INBOUND_P2P_MESSAGES
) {
sqliteQueryExecutor.removeInboundP2PMessages(message.ids);
} else if (
message.type === workerRequestMessageTypes.MARK_OUTBOUND_P2P_MESSAGE_AS_SENT
) {
sqliteQueryExecutor.markOutboundP2PMessageAsSent(
message.messageID,
message.deviceID,
);
} else if (
message.type === workerRequestMessageTypes.REMOVE_OUTBOUND_P2P_MESSAGES
) {
sqliteQueryExecutor.removeOutboundP2PMessagesOlderThan(
message.messageID,
message.deviceID,
);
}
persistNeeded = true;
if (!persistInProgress) {
void persist();
}
return result;
}
let currentlyProcessedMessage: ?Promise<void> = null;
function connectHandler(event: SharedWorkerMessageEvent) {
if (!event.ports.length) {
return;
}
const port: MessagePort = event.ports[0];
console.log('Web database worker alive!');
port.onmessage = async function (messageEvent: MessageEvent) {
const data: WorkerRequestProxyMessage = (messageEvent.data: any);
const { id, message } = data;
if (!id) {
port.postMessage({
error: 'Request without identifier',
});
}
currentlyProcessedMessage = (async () => {
await currentlyProcessedMessage;
try {
const result = await processAppRequest(message);
port.postMessage({
id,
message: result,
});
} catch (e) {
port.postMessage({
id,
error: getMessageForException(e),
});
}
})();
};
}
self.addEventListener('connect', connectHandler);
diff --git a/web/types/worker-types.js b/web/types/worker-types.js
index 576a4dd7c..50981ee2e 100644
--- a/web/types/worker-types.js
+++ b/web/types/worker-types.js
@@ -1,306 +1,313 @@
// @flow
import type { AuthMetadata } from 'lib/shared/identity-client-context.js';
import type {
PickledOLMAccount,
OLMIdentityKeys,
OlmAPI,
} from 'lib/types/crypto-types.js';
import type { PlatformDetails } from 'lib/types/device-types.js';
import type {
IdentityServiceClient,
IdentityServiceAuthLayer,
} from 'lib/types/identity-service-types.js';
import type { ClientDBMessageInfo } from 'lib/types/message-types.js';
import type {
InboundP2PMessage,
OutboundP2PMessage,
} from 'lib/types/sqlite-types.js';
import type {
ClientDBStore,
ClientDBStoreOperations,
} from 'lib/types/store-ops-types.js';
// The types of messages sent from app to worker
export const workerRequestMessageTypes = Object.freeze({
PING: 0,
INIT: 1,
GENERATE_DATABASE_ENCRYPTION_KEY: 2,
PROCESS_STORE_OPERATIONS: 3,
GET_CLIENT_STORE: 4,
STAMP_SQLITE_DB_USER_ID: 5,
GET_SQLITE_STAMPED_USER_ID: 6,
GET_PERSIST_STORAGE_ITEM: 7,
SET_PERSIST_STORAGE_ITEM: 8,
REMOVE_PERSIST_STORAGE_ITEM: 9,
CLEAR_SENSITIVE_DATA: 10,
BACKUP_RESTORE: 11,
INITIALIZE_CRYPTO_ACCOUNT: 12,
CREATE_IDENTITY_SERVICE_CLIENT: 13,
CALL_IDENTITY_CLIENT_METHOD: 14,
CALL_OLM_API_METHOD: 15,
GET_INBOUND_P2P_MESSAGES: 16,
REMOVE_INBOUND_P2P_MESSAGES: 17,
GET_OUTBOUND_P2P_MESSAGES: 18,
MARK_OUTBOUND_P2P_MESSAGE_AS_SENT: 19,
REMOVE_OUTBOUND_P2P_MESSAGES: 20,
GET_RELATED_MESSAGES: 21,
+ GET_OUTBOUND_P2P_MESSAGES_BY_ID: 22,
});
export const workerWriteRequests: $ReadOnlyArray<number> = [
workerRequestMessageTypes.PROCESS_STORE_OPERATIONS,
workerRequestMessageTypes.STAMP_SQLITE_DB_USER_ID,
workerRequestMessageTypes.SET_PERSIST_STORAGE_ITEM,
workerRequestMessageTypes.REMOVE_PERSIST_STORAGE_ITEM,
workerRequestMessageTypes.BACKUP_RESTORE,
workerRequestMessageTypes.INITIALIZE_CRYPTO_ACCOUNT,
workerRequestMessageTypes.REMOVE_INBOUND_P2P_MESSAGES,
workerRequestMessageTypes.MARK_OUTBOUND_P2P_MESSAGE_AS_SENT,
workerRequestMessageTypes.REMOVE_OUTBOUND_P2P_MESSAGES,
];
export const workerOlmAPIRequests: $ReadOnlyArray<number> = [
workerRequestMessageTypes.INITIALIZE_CRYPTO_ACCOUNT,
workerRequestMessageTypes.CALL_OLM_API_METHOD,
];
export const workerIdentityClientRequests: $ReadOnlyArray<number> = [
workerRequestMessageTypes.CREATE_IDENTITY_SERVICE_CLIENT,
workerRequestMessageTypes.CALL_IDENTITY_CLIENT_METHOD,
];
export type PingWorkerRequestMessage = {
+type: 0,
+text: string,
};
export type InitWorkerRequestMessage = {
+type: 1,
+platformDetails: PlatformDetails,
+webworkerModulesFilePath: string,
+commQueryExecutorFilename: ?string,
+encryptionKey?: ?SubtleCrypto$JsonWebKey,
+backupClientFilename?: ?string,
};
export type GenerateDatabaseEncryptionKeyRequestMessage = {
+type: 2,
};
export type ProcessStoreOperationsRequestMessage = {
+type: 3,
+storeOperations: ClientDBStoreOperations,
};
export type GetClientStoreRequestMessage = {
+type: 4,
};
export type SetCurrentUserIDRequestMessage = {
+type: 5,
+userID: string,
};
export type GetCurrentUserIDRequestMessage = {
+type: 6,
};
export type GetPersistStorageItemRequestMessage = {
+type: 7,
+key: string,
};
export type SetPersistStorageItemRequestMessage = {
+type: 8,
+key: string,
+item: string,
};
export type RemovePersistStorageItemRequestMessage = {
+type: 9,
+key: string,
};
export type ClearSensitiveDataRequestMessage = {
+type: 10,
};
export type BackupRestoreRequestMessage = {
+type: 11,
+authMetadata: AuthMetadata,
+backupID: string,
+backupDataKey: string,
+backupLogDataKey: string,
};
// Previously used on web in redux. Now only used
// in a migration to the shared worker.
export type LegacyCryptoStore = {
+primaryAccount: PickledOLMAccount,
+primaryIdentityKeys: OLMIdentityKeys,
+notificationAccount: PickledOLMAccount,
+notificationIdentityKeys: OLMIdentityKeys,
};
export type InitializeCryptoAccountRequestMessage = {
+type: 12,
+olmWasmPath: string,
+initialCryptoStore?: LegacyCryptoStore,
};
export type CreateIdentityServiceClientRequestMessage = {
+type: 13,
+opaqueWasmPath: string,
+authLayer: ?IdentityServiceAuthLayer,
};
export type CallIdentityClientMethodRequestMessage = {
+type: 14,
+method: $Keys<IdentityServiceClient>,
+args: $ReadOnlyArray<mixed>,
};
export type CallOLMApiMethodRequestMessage = {
+type: 15,
+method: $Keys<OlmAPI>,
+args: $ReadOnlyArray<mixed>,
};
export type GetInboundP2PMessagesRequestMessage = {
+type: 16,
};
export type RemoveInboundP2PMessagesRequestMessage = {
+type: 17,
+ids: $ReadOnlyArray<string>,
};
export type GetOutboundP2PMessagesRequestMessage = {
+type: 18,
};
export type MarkOutboundP2PMessageAsSentRequestMessage = {
+type: 19,
+messageID: string,
+deviceID: string,
};
export type RemoveOutboundP2PMessagesRequestMessage = {
+type: 20,
+messageID: string,
+deviceID: string,
};
export type GetRelatedMessagesRequestMessage = {
+type: 21,
+messageID: string,
};
+export type GetOutboundP2PMessagesByIDRequestMessage = {
+ +type: 22,
+ +messageIDs: $ReadOnlyArray<string>,
+};
+
export type WorkerRequestMessage =
| PingWorkerRequestMessage
| InitWorkerRequestMessage
| GenerateDatabaseEncryptionKeyRequestMessage
| ProcessStoreOperationsRequestMessage
| GetClientStoreRequestMessage
| SetCurrentUserIDRequestMessage
| GetCurrentUserIDRequestMessage
| GetPersistStorageItemRequestMessage
| SetPersistStorageItemRequestMessage
| RemovePersistStorageItemRequestMessage
| ClearSensitiveDataRequestMessage
| BackupRestoreRequestMessage
| InitializeCryptoAccountRequestMessage
| CreateIdentityServiceClientRequestMessage
| CallIdentityClientMethodRequestMessage
| CallOLMApiMethodRequestMessage
| GetInboundP2PMessagesRequestMessage
| RemoveInboundP2PMessagesRequestMessage
| GetOutboundP2PMessagesRequestMessage
| MarkOutboundP2PMessageAsSentRequestMessage
| RemoveOutboundP2PMessagesRequestMessage
- | GetRelatedMessagesRequestMessage;
+ | GetRelatedMessagesRequestMessage
+ | GetOutboundP2PMessagesByIDRequestMessage;
export type WorkerRequestProxyMessage = {
+id: number,
+message: WorkerRequestMessage,
};
// The types of messages sent from worker to app
export const workerResponseMessageTypes = Object.freeze({
PONG: 0,
CLIENT_STORE: 1,
GET_SQLITE_STAMPED_USER_ID: 2,
GET_PERSIST_STORAGE_ITEM: 3,
CALL_IDENTITY_CLIENT_METHOD: 4,
CALL_OLM_API_METHOD: 5,
GET_INBOUND_P2P_MESSAGES: 6,
GET_OUTBOUND_P2P_MESSAGES: 7,
GET_RELATED_MESSAGES: 8,
});
export type PongWorkerResponseMessage = {
+type: 0,
+text: string,
};
export type ClientStoreResponseMessage = {
+type: 1,
+store: ClientDBStore,
};
export type GetCurrentUserIDResponseMessage = {
+type: 2,
+userID: ?string,
};
export type GetPersistStorageItemResponseMessage = {
+type: 3,
+item: string,
};
export type CallIdentityClientMethodResponseMessage = {
+type: 4,
+result: mixed,
};
export type CallOLMApiMethodResponseMessage = {
+type: 5,
+result: mixed,
};
export type GetInboundP2PMessagesResponseMessage = {
+type: 6,
+inboundP2PMessages: $ReadOnlyArray<InboundP2PMessage>,
};
export type GetOutboundP2PMessagesResponseMessage = {
+type: 7,
+outboundP2PMessages: $ReadOnlyArray<OutboundP2PMessage>,
};
export type GetRelatedMessagesResponseMessage = {
+type: 8,
+messages: $ReadOnlyArray<ClientDBMessageInfo>,
};
export type WorkerResponseMessage =
| PongWorkerResponseMessage
| ClientStoreResponseMessage
| GetCurrentUserIDResponseMessage
| GetPersistStorageItemResponseMessage
| CallIdentityClientMethodResponseMessage
| CallOLMApiMethodResponseMessage
| GetInboundP2PMessagesResponseMessage
| GetOutboundP2PMessagesResponseMessage
| GetRelatedMessagesResponseMessage;
export type WorkerResponseProxyMessage = {
+id?: number,
+message?: WorkerResponseMessage,
+error?: string,
};
// SharedWorker types
export type SharedWorkerMessageEvent = MessageEvent & {
+ports: $ReadOnlyArray<MessagePort>,
...
};

File Metadata

Mime Type
text/x-diff
Expires
Wed, Dec 25, 7:29 PM (1 d, 8 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2700897
Default Alt Text
(36 KB)

Event Timeline