Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3526075
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
36 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
Mime Type
text/x-diff
Expires
Wed, Dec 25, 7:29 PM (19 h, 8 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2700897
Default Alt Text
(36 KB)
Attached To
Mode
rCOMM Comm
Attached
Detach File
Event Timeline
Log In to Comment