diff --git a/lib/handlers/db-ops-handler.react.js b/lib/handlers/db-ops-handler.react.js index 504ea4bc6..a2db344c6 100644 --- a/lib/handlers/db-ops-handler.react.js +++ b/lib/handlers/db-ops-handler.react.js @@ -1,61 +1,61 @@ // @flow import * as React from 'react'; import { opsProcessingFinishedActionType } from '../actions/db-ops-actions.js'; import { useTunnelbroker } from '../tunnelbroker/tunnelbroker-context.js'; import type { DBOpsEntry } from '../types/db-ops-types.js'; import type { StoreOperations } from '../types/store-ops-types.js'; import { type MessageProcessed, peerToPeerMessageTypes, } from '../types/tunnelbroker/peer-to-peer-message-types.js'; import { getConfig } from '../utils/config.js'; import { useDispatch, useSelector } from '../utils/redux-utils.js'; type Props = { +processDBStoreOperations: StoreOperations => Promise, }; function DBOpsHandler(props: Props): React.Node { const { sqliteAPI } = getConfig(); const { processDBStoreOperations } = props; const queueFront = useSelector(state => state.dbOpsStore.queuedOps[0]); const prevQueueFront = React.useRef(null); const { sendMessage } = useTunnelbroker(); const dispatch = useDispatch(); React.useEffect(() => { if (!queueFront || prevQueueFront.current === queueFront) { return; } prevQueueFront.current = queueFront; const { ops, messageSourceMetadata } = queueFront; void (async () => { if (ops) { await processDBStoreOperations(ops); } dispatch({ type: opsProcessingFinishedActionType, }); if (messageSourceMetadata) { const { messageID, senderDeviceID } = messageSourceMetadata; const message: MessageProcessed = { type: peerToPeerMessageTypes.MESSAGE_PROCESSED, messageID, }; await sendMessage({ deviceID: senderDeviceID, payload: JSON.stringify(message), }); - await sqliteAPI.removeReceivedMessagesToDevice([messageID]); + await sqliteAPI.removeInboundP2PMessages([messageID]); } })(); }, [queueFront, dispatch, processDBStoreOperations, sendMessage, sqliteAPI]); return null; } export { DBOpsHandler }; diff --git a/lib/types/sqlite-types.js b/lib/types/sqlite-types.js index ad57f6a66..a34e1c390 100644 --- a/lib/types/sqlite-types.js +++ b/lib/types/sqlite-types.js @@ -1,25 +1,23 @@ // @flow import type { StoreOperations } from './store-ops-types.js'; -export type ReceivedMessageToDevice = { +export type InboundP2PMessage = { +messageID: string, +senderDeviceID: string, +plaintext: string, +status: string, }; export type SQLiteAPI = { // read operations - +getAllReceivedMessageToDevice: () => Promise, + +getAllInboundP2PMessage: () => Promise, // write operations - +removeReceivedMessagesToDevice: ( - ids: $ReadOnlyArray, - ) => Promise, + +removeInboundP2PMessages: (ids: $ReadOnlyArray) => Promise, +processDBStoreOperations: ( operations: StoreOperations, userID?: ?string, ) => Promise, }; diff --git a/lib/utils/__mocks__/config.js b/lib/utils/__mocks__/config.js index ec6929575..6bcbb3837 100644 --- a/lib/utils/__mocks__/config.js +++ b/lib/utils/__mocks__/config.js @@ -1,38 +1,38 @@ // @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(), 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(), }, sqliteAPI: { - getAllReceivedMessageToDevice: jest.fn(), - removeReceivedMessagesToDevice: jest.fn(), + getAllInboundP2PMessage: jest.fn(), + removeInboundP2PMessages: jest.fn(), processDBStoreOperations: jest.fn(), }, }); const hasConfig = (): boolean => true; export { getConfig, hasConfig }; diff --git a/native/cpp/CommonCpp/DatabaseManagers/DatabaseQueryExecutor.h b/native/cpp/CommonCpp/DatabaseManagers/DatabaseQueryExecutor.h index 014d9b236..a827a77bd 100644 --- a/native/cpp/CommonCpp/DatabaseManagers/DatabaseQueryExecutor.h +++ b/native/cpp/CommonCpp/DatabaseManagers/DatabaseQueryExecutor.h @@ -1,165 +1,163 @@ #pragma once #include "../CryptoTools/Persist.h" #include "entities/AuxUserInfo.h" #include "entities/CommunityInfo.h" #include "entities/Draft.h" +#include "entities/InboundP2PMessage.h" #include "entities/IntegrityThreadHash.h" #include "entities/KeyserverInfo.h" #include "entities/Message.h" #include "entities/MessageStoreThread.h" #include "entities/MessageToDevice.h" #include "entities/OlmPersistAccount.h" #include "entities/OlmPersistSession.h" #include "entities/PersistItem.h" -#include "entities/ReceivedMessageToDevice.h" #include "entities/Report.h" #include "entities/SyncedMetadataEntry.h" #include "entities/Thread.h" #include "entities/ThreadActivityEntry.h" #include "entities/UserInfo.h" #include namespace comm { /** * if any initialization/cleaning up steps are required for specific * database managers they should appear in constructors/destructors * following the RAII pattern */ class DatabaseQueryExecutor { public: virtual std::string getDraft(std::string key) const = 0; virtual std::unique_ptr getThread(std::string threadID) const = 0; virtual void updateDraft(std::string key, std::string text) const = 0; virtual bool moveDraft(std::string oldKey, std::string newKey) const = 0; virtual std::vector getAllDrafts() const = 0; virtual void removeAllDrafts() const = 0; virtual void removeDrafts(const std::vector &ids) const = 0; virtual void removeAllMessages() const = 0; virtual std::vector>> getAllMessages() const = 0; virtual void removeMessages(const std::vector &ids) const = 0; virtual void removeMessagesForThreads(const std::vector &threadIDs) const = 0; virtual void replaceMessage(const Message &message) const = 0; virtual void rekeyMessage(std::string from, std::string to) const = 0; virtual void removeAllMedia() const = 0; virtual void replaceMessageStoreThreads( const std::vector &threads) const = 0; virtual void removeMessageStoreThreads(const std::vector &ids) const = 0; virtual void removeAllMessageStoreThreads() const = 0; virtual std::vector getAllMessageStoreThreads() const = 0; virtual void removeMediaForMessages(const std::vector &msg_ids) const = 0; virtual void removeMediaForMessage(std::string msg_id) const = 0; virtual void removeMediaForThreads(const std::vector &thread_ids) const = 0; virtual void replaceMedia(const Media &media) const = 0; virtual void rekeyMediaContainers(std::string from, std::string to) const = 0; virtual std::vector getAllThreads() const = 0; virtual void removeThreads(std::vector ids) const = 0; virtual void replaceThread(const Thread &thread) const = 0; virtual void removeAllThreads() const = 0; virtual void replaceReport(const Report &report) const = 0; virtual void removeReports(const std::vector &ids) const = 0; virtual void removeAllReports() const = 0; virtual std::vector getAllReports() const = 0; virtual void setPersistStorageItem(std::string key, std::string item) const = 0; virtual void removePersistStorageItem(std::string key) const = 0; virtual std::string getPersistStorageItem(std::string key) const = 0; virtual void replaceUser(const UserInfo &user_info) const = 0; virtual void removeUsers(const std::vector &ids) const = 0; virtual void removeAllUsers() const = 0; virtual std::vector getAllUsers() const = 0; virtual void replaceKeyserver(const KeyserverInfo &keyserver_info) const = 0; virtual void removeKeyservers(const std::vector &ids) const = 0; virtual void removeAllKeyservers() const = 0; virtual std::vector getAllKeyservers() const = 0; virtual void replaceCommunity(const CommunityInfo &community_info) const = 0; virtual void removeCommunities(const std::vector &ids) const = 0; virtual void removeAllCommunities() const = 0; virtual std::vector getAllCommunities() const = 0; virtual void replaceIntegrityThreadHashes( const std::vector &thread_hashes) const = 0; virtual void removeIntegrityThreadHashes(const std::vector &ids) const = 0; virtual void removeAllIntegrityThreadHashes() const = 0; virtual std::vector getAllIntegrityThreadHashes() const = 0; virtual void replaceSyncedMetadataEntry( const SyncedMetadataEntry &synced_metadata_entry) const = 0; virtual void removeSyncedMetadata(const std::vector &names) const = 0; virtual void removeAllSyncedMetadata() const = 0; virtual std::vector getAllSyncedMetadata() const = 0; virtual void replaceAuxUserInfo(const AuxUserInfo &aux_user_info) const = 0; virtual void removeAuxUserInfos(const std::vector &ids) const = 0; virtual void removeAllAuxUserInfos() const = 0; virtual std::vector getAllAuxUserInfos() const = 0; virtual void replaceThreadActivityEntry( const ThreadActivityEntry &thread_activity_entry) const = 0; virtual void removeThreadActivityEntries(const std::vector &ids) const = 0; virtual void removeAllThreadActivityEntries() const = 0; virtual std::vector getAllThreadActivityEntries() const = 0; virtual void beginTransaction() const = 0; virtual void commitTransaction() const = 0; virtual void rollbackTransaction() const = 0; virtual int getContentAccountID() const = 0; virtual int getNotifsAccountID() const = 0; virtual std::vector getOlmPersistSessionsData() const = 0; virtual std::optional getOlmPersistAccountData(int accountID) const = 0; virtual void storeOlmPersistSession(const OlmPersistSession &session) const = 0; virtual void storeOlmPersistAccount( int accountID, const std::string &accountData) const = 0; virtual void storeOlmPersistData(int accountID, crypto::Persist persist) const = 0; virtual void setNotifyToken(std::string token) const = 0; virtual void clearNotifyToken() const = 0; virtual void stampSQLiteDBUserID(std::string userID) const = 0; virtual std::string getSQLiteStampedUserID() const = 0; virtual void setMetadata(std::string entry_name, std::string data) const = 0; virtual void clearMetadata(std::string entry_name) const = 0; virtual std::string getMetadata(std::string entry_name) const = 0; virtual void restoreFromMainCompaction( std::string mainCompactionPath, std::string mainCompactionEncryptionKey) const = 0; virtual void restoreFromBackupLog(const std::vector &backupLog) const = 0; virtual void addMessagesToDevice( const std::vector &messages) const = 0; virtual std::vector getAllMessagesToDevice(const std::string &deviceID) const = 0; virtual void removeMessagesToDeviceOlderThan( const ClientMessageToDevice &lastConfirmedMessage) const = 0; virtual void removeAllMessagesForDevice(const std::string &deviceID) const = 0; + virtual void addInboundP2PMessage(InboundP2PMessage message) const = 0; + virtual std::vector getAllInboundP2PMessage() const = 0; virtual void - addReceivedMessageToDevice(ReceivedMessageToDevice message) const = 0; - virtual std::vector - getAllReceivedMessageToDevice() const = 0; - virtual void - removeReceivedMessagesToDevice(const std::vector &ids) const = 0; + removeInboundP2PMessages(const std::vector &ids) const = 0; #ifdef EMSCRIPTEN virtual std::vector getAllThreadsWeb() const = 0; virtual void replaceThreadWeb(const WebThread &thread) const = 0; virtual std::vector getAllMessagesWeb() const = 0; virtual void replaceMessageWeb(const WebMessage &message) const = 0; virtual NullableString getOlmPersistAccountDataWeb(int accountID) const = 0; #else virtual void createMainCompaction(std::string backupID) const = 0; virtual void captureBackupLogs() const = 0; #endif }; } // namespace comm diff --git a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp index 9a24a977a..ff3259a84 100644 --- a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp +++ b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp @@ -1,2544 +1,2544 @@ #include "SQLiteQueryExecutor.h" #include "Logger.h" #include "entities/CommunityInfo.h" #include "entities/EntityQueryHelpers.h" #include "entities/IntegrityThreadHash.h" #include "entities/KeyserverInfo.h" #include "entities/Metadata.h" #include "entities/SyncedMetadataEntry.h" #include "entities/UserInfo.h" #include #include #include #ifndef EMSCRIPTEN #include "../CryptoTools/CryptoModule.h" #include "CommSecureStore.h" #include "PlatformSpecificTools.h" #include "StaffUtils.h" #endif const int CONTENT_ACCOUNT_ID = 1; const int NOTIFS_ACCOUNT_ID = 2; namespace comm { std::string SQLiteQueryExecutor::sqliteFilePath; std::string SQLiteQueryExecutor::encryptionKey; std::once_flag SQLiteQueryExecutor::initialized; int SQLiteQueryExecutor::sqlcipherEncryptionKeySize = 64; // Should match constant defined in `native_rust_library/src/constants.rs` std::string SQLiteQueryExecutor::secureStoreEncryptionKeyID = "comm.encryptionKey"; int SQLiteQueryExecutor::backupLogsEncryptionKeySize = 32; std::string SQLiteQueryExecutor::secureStoreBackupLogsEncryptionKeyID = "comm.backupLogsEncryptionKey"; std::string SQLiteQueryExecutor::backupLogsEncryptionKey; #ifndef EMSCRIPTEN NativeSQLiteConnectionManager SQLiteQueryExecutor::connectionManager; std::unordered_set SQLiteQueryExecutor::backedUpTablesBlocklist = { "olm_persist_account", "olm_persist_sessions", "metadata", "messages_to_device", "integrity_store", "persist_storage", "keyservers", }; #else SQLiteConnectionManager SQLiteQueryExecutor::connectionManager; #endif bool create_table(sqlite3 *db, std::string query, std::string tableName) { char *error; sqlite3_exec(db, query.c_str(), nullptr, nullptr, &error); if (!error) { return true; } std::ostringstream stringStream; stringStream << "Error creating '" << tableName << "' table: " << error; Logger::log(stringStream.str()); sqlite3_free(error); return false; } bool create_drafts_table(sqlite3 *db) { std::string query = "CREATE TABLE IF NOT EXISTS drafts (threadID TEXT UNIQUE PRIMARY KEY, " "text TEXT);"; return create_table(db, query, "drafts"); } bool rename_threadID_to_key(sqlite3 *db) { sqlite3_stmt *key_column_stmt; sqlite3_prepare_v2( db, "SELECT name AS col_name FROM pragma_table_xinfo ('drafts') WHERE " "col_name='key';", -1, &key_column_stmt, nullptr); sqlite3_step(key_column_stmt); auto num_bytes = sqlite3_column_bytes(key_column_stmt, 0); sqlite3_finalize(key_column_stmt); if (num_bytes) { return true; } char *error; sqlite3_exec( db, "ALTER TABLE drafts RENAME COLUMN `threadID` TO `key`;", nullptr, nullptr, &error); if (error) { std::ostringstream stringStream; stringStream << "Error occurred renaming threadID column in drafts table " << "to key: " << error; Logger::log(stringStream.str()); sqlite3_free(error); return false; } return true; } bool create_persist_account_table(sqlite3 *db) { std::string query = "CREATE TABLE IF NOT EXISTS olm_persist_account(" "id INTEGER UNIQUE PRIMARY KEY NOT NULL, " "account_data TEXT NOT NULL);"; return create_table(db, query, "olm_persist_account"); } bool create_persist_sessions_table(sqlite3 *db) { std::string query = "CREATE TABLE IF NOT EXISTS olm_persist_sessions(" "target_user_id TEXT UNIQUE PRIMARY KEY NOT NULL, " "session_data TEXT NOT NULL);"; return create_table(db, query, "olm_persist_sessions"); } bool drop_messages_table(sqlite3 *db) { char *error; sqlite3_exec(db, "DROP TABLE IF EXISTS messages;", nullptr, nullptr, &error); if (!error) { return true; } std::ostringstream stringStream; stringStream << "Error dropping 'messages' table: " << error; Logger::log(stringStream.str()); sqlite3_free(error); return false; } bool recreate_messages_table(sqlite3 *db) { std::string query = "CREATE TABLE IF NOT EXISTS messages ( " "id TEXT UNIQUE PRIMARY KEY NOT NULL, " "local_id TEXT, " "thread TEXT NOT NULL, " "user TEXT NOT NULL, " "type INTEGER NOT NULL, " "future_type INTEGER, " "content TEXT, " "time INTEGER NOT NULL);"; return create_table(db, query, "messages"); } bool create_messages_idx_thread_time(sqlite3 *db) { char *error; sqlite3_exec( db, "CREATE INDEX IF NOT EXISTS messages_idx_thread_time " "ON messages (thread, time);", nullptr, nullptr, &error); if (!error) { return true; } std::ostringstream stringStream; stringStream << "Error creating (thread, time) index on messages table: " << error; Logger::log(stringStream.str()); sqlite3_free(error); return false; } bool create_media_table(sqlite3 *db) { std::string query = "CREATE TABLE IF NOT EXISTS media ( " "id TEXT UNIQUE PRIMARY KEY NOT NULL, " "container TEXT NOT NULL, " "thread TEXT NOT NULL, " "uri TEXT NOT NULL, " "type TEXT NOT NULL, " "extras TEXT NOT NULL);"; return create_table(db, query, "media"); } bool create_media_idx_container(sqlite3 *db) { char *error; sqlite3_exec( db, "CREATE INDEX IF NOT EXISTS media_idx_container " "ON media (container);", nullptr, nullptr, &error); if (!error) { return true; } std::ostringstream stringStream; stringStream << "Error creating (container) index on media table: " << error; Logger::log(stringStream.str()); sqlite3_free(error); return false; } bool create_threads_table(sqlite3 *db) { std::string query = "CREATE TABLE IF NOT EXISTS threads ( " "id TEXT UNIQUE PRIMARY KEY NOT NULL, " "type INTEGER NOT NULL, " "name TEXT, " "description TEXT, " "color TEXT NOT NULL, " "creation_time BIGINT NOT NULL, " "parent_thread_id TEXT, " "containing_thread_id TEXT, " "community TEXT, " "members TEXT NOT NULL, " "roles TEXT NOT NULL, " "current_user TEXT NOT NULL, " "source_message_id TEXT, " "replies_count INTEGER NOT NULL);"; return create_table(db, query, "threads"); } bool update_threadID_for_pending_threads_in_drafts(sqlite3 *db) { char *error; sqlite3_exec( db, "UPDATE drafts SET key = " "REPLACE(REPLACE(REPLACE(REPLACE(key, 'type4/', '')," "'type5/', ''),'type6/', ''),'type7/', '')" "WHERE key LIKE 'pending/%'", nullptr, nullptr, &error); if (!error) { return true; } std::ostringstream stringStream; stringStream << "Error update pending threadIDs on drafts table: " << error; Logger::log(stringStream.str()); sqlite3_free(error); return false; } bool enable_write_ahead_logging_mode(sqlite3 *db) { char *error; sqlite3_exec(db, "PRAGMA journal_mode=wal;", nullptr, nullptr, &error); if (!error) { return true; } std::ostringstream stringStream; stringStream << "Error enabling write-ahead logging mode: " << error; Logger::log(stringStream.str()); sqlite3_free(error); return false; } bool create_metadata_table(sqlite3 *db) { std::string query = "CREATE TABLE IF NOT EXISTS metadata ( " "name TEXT UNIQUE PRIMARY KEY NOT NULL, " "data TEXT);"; return create_table(db, query, "metadata"); } bool add_not_null_constraint_to_drafts(sqlite3 *db) { char *error; sqlite3_exec( db, "CREATE TABLE IF NOT EXISTS temporary_drafts (" "key TEXT UNIQUE PRIMARY KEY NOT NULL, " "text TEXT NOT NULL);" "INSERT INTO temporary_drafts SELECT * FROM drafts " "WHERE key IS NOT NULL AND text IS NOT NULL;" "DROP TABLE drafts;" "ALTER TABLE temporary_drafts RENAME TO drafts;", nullptr, nullptr, &error); if (!error) { return true; } std::ostringstream stringStream; stringStream << "Error adding NOT NULL constraint to drafts table: " << error; Logger::log(stringStream.str()); sqlite3_free(error); return false; } bool add_not_null_constraint_to_metadata(sqlite3 *db) { char *error; sqlite3_exec( db, "CREATE TABLE IF NOT EXISTS temporary_metadata (" "name TEXT UNIQUE PRIMARY KEY NOT NULL, " "data TEXT NOT NULL);" "INSERT INTO temporary_metadata SELECT * FROM metadata " "WHERE data IS NOT NULL;" "DROP TABLE metadata;" "ALTER TABLE temporary_metadata RENAME TO metadata;", nullptr, nullptr, &error); if (!error) { return true; } std::ostringstream stringStream; stringStream << "Error adding NOT NULL constraint to metadata table: " << error; Logger::log(stringStream.str()); sqlite3_free(error); return false; } bool add_avatar_column_to_threads_table(sqlite3 *db) { char *error; sqlite3_exec( db, "ALTER TABLE threads ADD COLUMN avatar TEXT;", nullptr, nullptr, &error); if (!error) { return true; } std::ostringstream stringStream; stringStream << "Error adding avatar column to threads table: " << error; Logger::log(stringStream.str()); sqlite3_free(error); return false; } bool add_pinned_count_column_to_threads(sqlite3 *db) { sqlite3_stmt *pinned_column_stmt; sqlite3_prepare_v2( db, "SELECT name AS col_name FROM pragma_table_xinfo ('threads') WHERE " "col_name='pinned_count';", -1, &pinned_column_stmt, nullptr); sqlite3_step(pinned_column_stmt); auto num_bytes = sqlite3_column_bytes(pinned_column_stmt, 0); sqlite3_finalize(pinned_column_stmt); if (num_bytes) { return true; } char *error; sqlite3_exec( db, "ALTER TABLE threads ADD COLUMN pinned_count INTEGER NOT NULL DEFAULT 0;", nullptr, nullptr, &error); if (!error) { return true; } std::ostringstream stringStream; stringStream << "Error adding pinned_count column to threads table: " << error; Logger::log(stringStream.str()); sqlite3_free(error); return false; } bool create_message_store_threads_table(sqlite3 *db) { std::string query = "CREATE TABLE IF NOT EXISTS message_store_threads (" " id TEXT UNIQUE PRIMARY KEY NOT NULL," " start_reached INTEGER NOT NULL," " last_navigated_to BIGINT NOT NULL," " last_pruned BIGINT NOT NULL" ");"; return create_table(db, query, "message_store_threads"); } bool create_reports_table(sqlite3 *db) { std::string query = "CREATE TABLE IF NOT EXISTS reports (" " id TEXT UNIQUE PRIMARY KEY NOT NULL," " report TEXT NOT NULL" ");"; return create_table(db, query, "reports"); } bool create_persist_storage_table(sqlite3 *db) { std::string query = "CREATE TABLE IF NOT EXISTS persist_storage (" " key TEXT UNIQUE PRIMARY KEY NOT NULL," " item TEXT NOT NULL" ");"; return create_table(db, query, "persist_storage"); } bool recreate_message_store_threads_table(sqlite3 *db) { char *errMsg = 0; // 1. Create table without `last_navigated_to` or `last_pruned`. std::string create_new_table_query = "CREATE TABLE IF NOT EXISTS temp_message_store_threads (" " id TEXT UNIQUE PRIMARY KEY NOT NULL," " start_reached INTEGER NOT NULL" ");"; if (sqlite3_exec(db, create_new_table_query.c_str(), NULL, NULL, &errMsg) != SQLITE_OK) { Logger::log( "Error creating temp_message_store_threads: " + std::string{errMsg}); sqlite3_free(errMsg); return false; } // 2. Dump data from existing `message_store_threads` table into temp table. std::string copy_data_query = "INSERT INTO temp_message_store_threads (id, start_reached)" "SELECT id, start_reached FROM message_store_threads;"; if (sqlite3_exec(db, copy_data_query.c_str(), NULL, NULL, &errMsg) != SQLITE_OK) { Logger::log( "Error dumping data from existing message_store_threads to " "temp_message_store_threads: " + std::string{errMsg}); sqlite3_free(errMsg); return false; } // 3. Drop the existing `message_store_threads` table. std::string drop_old_table_query = "DROP TABLE message_store_threads;"; if (sqlite3_exec(db, drop_old_table_query.c_str(), NULL, NULL, &errMsg) != SQLITE_OK) { Logger::log( "Error dropping message_store_threads table: " + std::string{errMsg}); sqlite3_free(errMsg); return false; } // 4. Rename the temp table back to `message_store_threads`. std::string rename_table_query = "ALTER TABLE temp_message_store_threads RENAME TO message_store_threads;"; if (sqlite3_exec(db, rename_table_query.c_str(), NULL, NULL, &errMsg) != SQLITE_OK) { Logger::log( "Error renaming temp_message_store_threads to message_store_threads: " + std::string{errMsg}); sqlite3_free(errMsg); return false; } return true; } bool create_users_table(sqlite3 *db) { std::string query = "CREATE TABLE IF NOT EXISTS users (" " id TEXT UNIQUE PRIMARY KEY NOT NULL," " user_info TEXT NOT NULL" ");"; return create_table(db, query, "users"); } bool create_keyservers_table(sqlite3 *db) { std::string query = "CREATE TABLE IF NOT EXISTS keyservers (" " id TEXT UNIQUE PRIMARY KEY NOT NULL," " keyserver_info TEXT NOT NULL" ");"; return create_table(db, query, "keyservers"); } bool enable_rollback_journal_mode(sqlite3 *db) { char *error; sqlite3_exec(db, "PRAGMA journal_mode=DELETE;", nullptr, nullptr, &error); if (!error) { return true; } std::stringstream error_message; error_message << "Error disabling write-ahead logging mode: " << error; Logger::log(error_message.str()); sqlite3_free(error); return false; } bool create_communities_table(sqlite3 *db) { std::string query = "CREATE TABLE IF NOT EXISTS communities (" " id TEXT UNIQUE PRIMARY KEY NOT NULL," " community_info TEXT NOT NULL" ");"; return create_table(db, query, "communities"); } bool create_messages_to_device_table(sqlite3 *db) { std::string query = "CREATE TABLE IF NOT EXISTS messages_to_device (" " message_id TEXT NOT NULL," " device_id TEXT NOT NULL," " user_id TEXT NOT NULL," " timestamp BIGINT NOT NULL," " plaintext TEXT NOT NULL," " ciphertext TEXT NOT NULL," " PRIMARY KEY (message_id, device_id)" ");" "CREATE INDEX IF NOT EXISTS messages_to_device_idx_id_timestamp" " ON messages_to_device (device_id, timestamp);"; return create_table(db, query, "messages_to_device"); } bool create_integrity_table(sqlite3 *db) { std::string query = "CREATE TABLE IF NOT EXISTS integrity_store (" " id TEXT UNIQUE PRIMARY KEY NOT NULL," " thread_hash TEXT NOT NULL" ");"; return create_table(db, query, "integrity_store"); } bool create_synced_metadata_table(sqlite3 *db) { std::string query = "CREATE TABLE IF NOT EXISTS synced_metadata (" " name TEXT UNIQUE PRIMARY KEY NOT NULL," " data TEXT NOT NULL" ");"; return create_table(db, query, "synced_metadata"); } bool create_keyservers_synced(sqlite3 *db) { std::string query = "CREATE TABLE IF NOT EXISTS keyservers_synced (" " id TEXT UNIQUE PRIMARY KEY NOT NULL," " keyserver_info TEXT NOT NULL" ");"; bool success = create_table(db, query, "keyservers_synced"); if (!success) { return false; } std::string copyData = "INSERT INTO keyservers_synced (id, keyserver_info)" "SELECT id, keyserver_info " "FROM keyservers;"; char *error; sqlite3_exec(db, copyData.c_str(), nullptr, nullptr, &error); if (error) { return false; } return true; } bool create_aux_user_table(sqlite3 *db) { std::string query = "CREATE TABLE IF NOT EXISTS aux_users (" " id TEXT UNIQUE PRIMARY KEY NOT NULL," " aux_user_info TEXT NOT NULL" ");"; return create_table(db, query, "aux_users"); } bool add_version_column_to_olm_persist_sessions_table(sqlite3 *db) { char *error; sqlite3_exec( db, "ALTER TABLE olm_persist_sessions " " RENAME COLUMN `target_user_id` TO `target_device_id`; " "ALTER TABLE olm_persist_sessions " " ADD COLUMN version INTEGER NOT NULL DEFAULT 1;", nullptr, nullptr, &error); if (!error) { return true; } std::ostringstream stringStream; stringStream << "Error updating olm_persist_sessions table: " << error; Logger::log(stringStream.str()); sqlite3_free(error); return false; } bool create_thread_activity_table(sqlite3 *db) { std::string query = "CREATE TABLE IF NOT EXISTS thread_activity (" " id TEXT UNIQUE PRIMARY KEY NOT NULL," " thread_activity_store_entry TEXT NOT NULL" ");"; return create_table(db, query, "thread_activity"); } bool create_received_messages_to_device(sqlite3 *db) { std::string query = "CREATE TABLE IF NOT EXISTS received_messages_to_device (" " id INTEGER PRIMARY KEY," " message_id TEXT NOT NULL," " sender_device_id TEXT NOT NULL," " plaintext TEXT NOT NULL," " status TEXT NOT NULL" ");"; return create_table(db, query, "received_messages_to_device"); } bool create_schema(sqlite3 *db) { char *error; sqlite3_exec( db, "CREATE TABLE IF NOT EXISTS drafts (" " key TEXT UNIQUE PRIMARY KEY NOT NULL," " text TEXT NOT NULL" ");" "CREATE TABLE IF NOT EXISTS messages (" " id TEXT UNIQUE PRIMARY KEY NOT NULL," " local_id TEXT," " thread TEXT NOT NULL," " user TEXT NOT NULL," " type INTEGER NOT NULL," " future_type INTEGER," " content TEXT," " time INTEGER NOT NULL" ");" "CREATE TABLE IF NOT EXISTS olm_persist_account (" " id INTEGER UNIQUE PRIMARY KEY NOT NULL," " account_data TEXT NOT NULL" ");" "CREATE TABLE IF NOT EXISTS olm_persist_sessions (" " target_device_id TEXT UNIQUE PRIMARY KEY NOT NULL," " session_data TEXT NOT NULL," " version INTEGER NOT NULL DEFAULT 1" ");" "CREATE TABLE IF NOT EXISTS media (" " id TEXT UNIQUE PRIMARY KEY NOT NULL," " container TEXT NOT NULL," " thread TEXT NOT NULL," " uri TEXT NOT NULL," " type TEXT NOT NULL," " extras TEXT NOT NULL" ");" "CREATE TABLE IF NOT EXISTS threads (" " id TEXT UNIQUE PRIMARY KEY NOT NULL," " type INTEGER NOT NULL," " name TEXT," " description TEXT," " color TEXT NOT NULL," " creation_time BIGINT NOT NULL," " parent_thread_id TEXT," " containing_thread_id TEXT," " community TEXT," " members TEXT NOT NULL," " roles TEXT NOT NULL," " current_user TEXT NOT NULL," " source_message_id TEXT," " replies_count INTEGER NOT NULL," " avatar TEXT," " pinned_count INTEGER NOT NULL DEFAULT 0" ");" "CREATE TABLE IF NOT EXISTS metadata (" " name TEXT UNIQUE PRIMARY KEY NOT NULL," " data TEXT NOT NULL" ");" "CREATE TABLE IF NOT EXISTS message_store_threads (" " id TEXT UNIQUE PRIMARY KEY NOT NULL," " start_reached INTEGER NOT NULL" ");" "CREATE TABLE IF NOT EXISTS reports (" " id TEXT UNIQUE PRIMARY KEY NOT NULL," " report TEXT NOT NULL" ");" "CREATE TABLE IF NOT EXISTS persist_storage (" " key TEXT UNIQUE PRIMARY KEY NOT NULL," " item TEXT NOT NULL" ");" "CREATE TABLE IF NOT EXISTS users (" " id TEXT UNIQUE PRIMARY KEY NOT NULL," " user_info TEXT NOT NULL" ");" "CREATE TABLE IF NOT EXISTS keyservers (" " id TEXT UNIQUE PRIMARY KEY NOT NULL," " keyserver_info TEXT NOT NULL" ");" "CREATE TABLE IF NOT EXISTS keyservers_synced (" " id TEXT UNIQUE PRIMARY KEY NOT NULL," " keyserver_info TEXT NOT NULL" ");" "CREATE TABLE IF NOT EXISTS communities (" " id TEXT UNIQUE PRIMARY KEY NOT NULL," " community_info TEXT NOT NULL" ");" "CREATE TABLE IF NOT EXISTS messages_to_device (" " message_id TEXT NOT NULL," " device_id TEXT NOT NULL," " user_id TEXT NOT NULL," " timestamp BIGINT NOT NULL," " plaintext TEXT NOT NULL," " ciphertext TEXT NOT NULL," " PRIMARY KEY (message_id, device_id)" ");" "CREATE TABLE IF NOT EXISTS integrity_store (" " id TEXT UNIQUE PRIMARY KEY NOT NULL," " thread_hash TEXT NOT NULL" ");" "CREATE TABLE IF NOT EXISTS synced_metadata (" " name TEXT UNIQUE PRIMARY KEY NOT NULL," " data TEXT NOT NULL" ");" "CREATE TABLE IF NOT EXISTS aux_users (" " id TEXT UNIQUE PRIMARY KEY NOT NULL," " aux_user_info TEXT NOT NULL" ");" "CREATE TABLE IF NOT EXISTS thread_activity (" " id TEXT UNIQUE PRIMARY KEY NOT NULL," " thread_activity_store_entry TEXT NOT NULL" ");" "CREATE TABLE IF NOT EXISTS received_messages_to_device (" " id INTEGER PRIMARY KEY," " message_id TEXT NOT NULL," " sender_device_id TEXT NOT NULL," " plaintext TEXT NOT NULL," " status TEXT NOT NULL" ");" "CREATE INDEX IF NOT EXISTS media_idx_container" " ON media (container);" "CREATE INDEX IF NOT EXISTS messages_idx_thread_time" " ON messages (thread, time);" "CREATE INDEX IF NOT EXISTS messages_to_device_idx_id_timestamp" " ON messages_to_device (device_id, timestamp);", nullptr, nullptr, &error); if (!error) { return true; } std::ostringstream stringStream; stringStream << "Error creating tables: " << error; Logger::log(stringStream.str()); sqlite3_free(error); return false; } void set_encryption_key( sqlite3 *db, const std::string &encryptionKey = SQLiteQueryExecutor::encryptionKey) { std::string set_encryption_key_query = "PRAGMA key = \"x'" + encryptionKey + "'\";"; char *error_set_key; sqlite3_exec( db, set_encryption_key_query.c_str(), nullptr, nullptr, &error_set_key); if (error_set_key) { std::ostringstream error_message; error_message << "Failed to set encryption key: " << error_set_key; throw std::system_error( ECANCELED, std::generic_category(), error_message.str()); } } int get_database_version(sqlite3 *db) { sqlite3_stmt *user_version_stmt; sqlite3_prepare_v2( db, "PRAGMA user_version;", -1, &user_version_stmt, nullptr); sqlite3_step(user_version_stmt); int current_user_version = sqlite3_column_int(user_version_stmt, 0); sqlite3_finalize(user_version_stmt); return current_user_version; } bool set_database_version(sqlite3 *db, int db_version) { std::stringstream update_version; update_version << "PRAGMA user_version=" << db_version << ";"; auto update_version_str = update_version.str(); char *error; sqlite3_exec(db, update_version_str.c_str(), nullptr, nullptr, &error); if (!error) { return true; } std::ostringstream errorStream; errorStream << "Error setting database version to " << db_version << ": " << error; Logger::log(errorStream.str()); sqlite3_free(error); return false; } // We don't want to run `PRAGMA key = ...;` // on main web database. The context is here: // https://linear.app/comm/issue/ENG-6398/issues-with-sqlcipher-on-web void default_on_db_open_callback(sqlite3 *db) { #ifndef EMSCRIPTEN set_encryption_key(db); #endif } // This is a temporary solution. In future we want to keep // a separate table for blob hashes. Tracked on Linear: // https://linear.app/comm/issue/ENG-6261/introduce-blob-hash-table std::string blob_hash_from_blob_service_uri(const std::string &media_uri) { static const std::string blob_service_prefix = "comm-blob-service://"; return media_uri.substr(blob_service_prefix.size()); } bool file_exists(const std::string &file_path) { std::ifstream file(file_path.c_str()); return file.good(); } void attempt_delete_file( const std::string &file_path, const char *error_message) { if (std::remove(file_path.c_str())) { throw std::system_error(errno, std::generic_category(), error_message); } } void attempt_rename_file( const std::string &old_path, const std::string &new_path, const char *error_message) { if (std::rename(old_path.c_str(), new_path.c_str())) { throw std::system_error(errno, std::generic_category(), error_message); } } bool is_database_queryable( sqlite3 *db, bool use_encryption_key, const std::string &path = SQLiteQueryExecutor::sqliteFilePath, const std::string &encryptionKey = SQLiteQueryExecutor::encryptionKey) { char *err_msg; sqlite3_open(path.c_str(), &db); // According to SQLCipher documentation running some SELECT is the only way to // check for key validity if (use_encryption_key) { set_encryption_key(db, encryptionKey); } sqlite3_exec( db, "SELECT COUNT(*) FROM sqlite_master;", nullptr, nullptr, &err_msg); sqlite3_close(db); return !err_msg; } void validate_encryption() { std::string temp_encrypted_db_path = SQLiteQueryExecutor::sqliteFilePath + "_temp_encrypted"; bool temp_encrypted_exists = file_exists(temp_encrypted_db_path); bool default_location_exists = file_exists(SQLiteQueryExecutor::sqliteFilePath); if (temp_encrypted_exists && default_location_exists) { Logger::log( "Previous encryption attempt failed. Repeating encryption process from " "the beginning."); attempt_delete_file( temp_encrypted_db_path, "Failed to delete corrupted encrypted database."); } else if (temp_encrypted_exists && !default_location_exists) { Logger::log( "Moving temporary encrypted database to default location failed in " "previous encryption attempt. Repeating rename step."); attempt_rename_file( temp_encrypted_db_path, SQLiteQueryExecutor::sqliteFilePath, "Failed to move encrypted database to default location."); return; } else if (!default_location_exists) { Logger::log( "Database not present yet. It will be created encrypted under default " "path."); return; } sqlite3 *db; if (is_database_queryable(db, true)) { Logger::log( "Database exists under default path and it is correctly encrypted."); return; } if (!is_database_queryable(db, false)) { Logger::log( "Database exists but it is encrypted with key that was lost. " "Attempting database deletion. New encrypted one will be created."); attempt_delete_file( SQLiteQueryExecutor::sqliteFilePath.c_str(), "Failed to delete database encrypted with lost key."); return; } else { Logger::log( "Database exists but it is not encrypted. Attempting encryption " "process."); } sqlite3_open(SQLiteQueryExecutor::sqliteFilePath.c_str(), &db); std::string createEncryptedCopySQL = "ATTACH DATABASE '" + temp_encrypted_db_path + "' AS encrypted_comm " "KEY \"x'" + SQLiteQueryExecutor::encryptionKey + "'\";" "SELECT sqlcipher_export('encrypted_comm');" "DETACH DATABASE encrypted_comm;"; char *encryption_error; sqlite3_exec( db, createEncryptedCopySQL.c_str(), nullptr, nullptr, &encryption_error); if (encryption_error) { throw std::system_error( ECANCELED, std::generic_category(), "Failed to create encrypted copy of the original database."); } sqlite3_close(db); attempt_delete_file( SQLiteQueryExecutor::sqliteFilePath, "Failed to delete unencrypted database."); attempt_rename_file( temp_encrypted_db_path, SQLiteQueryExecutor::sqliteFilePath, "Failed to move encrypted database to default location."); Logger::log("Encryption completed successfully."); } typedef bool ShouldBeInTransaction; typedef std::function MigrateFunction; typedef std::pair SQLiteMigration; std::vector> migrations{ {{1, {create_drafts_table, true}}, {2, {rename_threadID_to_key, true}}, {4, {create_persist_account_table, true}}, {5, {create_persist_sessions_table, true}}, {15, {create_media_table, true}}, {16, {drop_messages_table, true}}, {17, {recreate_messages_table, true}}, {18, {create_messages_idx_thread_time, true}}, {19, {create_media_idx_container, true}}, {20, {create_threads_table, true}}, {21, {update_threadID_for_pending_threads_in_drafts, true}}, {22, {enable_write_ahead_logging_mode, false}}, {23, {create_metadata_table, true}}, {24, {add_not_null_constraint_to_drafts, true}}, {25, {add_not_null_constraint_to_metadata, true}}, {26, {add_avatar_column_to_threads_table, true}}, {27, {add_pinned_count_column_to_threads, true}}, {28, {create_message_store_threads_table, true}}, {29, {create_reports_table, true}}, {30, {create_persist_storage_table, true}}, {31, {recreate_message_store_threads_table, true}}, {32, {create_users_table, true}}, {33, {create_keyservers_table, true}}, {34, {enable_rollback_journal_mode, false}}, {35, {create_communities_table, true}}, {36, {create_messages_to_device_table, true}}, {37, {create_integrity_table, true}}, {38, {[](sqlite3 *) { return true; }, false}}, {39, {create_synced_metadata_table, true}}, {40, {create_keyservers_synced, true}}, {41, {create_aux_user_table, true}}, {42, {add_version_column_to_olm_persist_sessions_table, true}}, {43, {create_thread_activity_table, true}}, {44, {create_received_messages_to_device, true}}}}; enum class MigrationResult { SUCCESS, FAILURE, NOT_APPLIED }; MigrationResult applyMigrationWithTransaction( sqlite3 *db, const MigrateFunction &migrate, int index) { sqlite3_exec(db, "BEGIN TRANSACTION;", nullptr, nullptr, nullptr); auto db_version = get_database_version(db); if (index <= db_version) { sqlite3_exec(db, "ROLLBACK;", nullptr, nullptr, nullptr); return MigrationResult::NOT_APPLIED; } auto rc = migrate(db); if (!rc) { sqlite3_exec(db, "ROLLBACK;", nullptr, nullptr, nullptr); return MigrationResult::FAILURE; } auto database_version_set = set_database_version(db, index); if (!database_version_set) { sqlite3_exec(db, "ROLLBACK;", nullptr, nullptr, nullptr); return MigrationResult::FAILURE; } sqlite3_exec(db, "END TRANSACTION;", nullptr, nullptr, nullptr); return MigrationResult::SUCCESS; } MigrationResult applyMigrationWithoutTransaction( sqlite3 *db, const MigrateFunction &migrate, int index) { auto db_version = get_database_version(db); if (index <= db_version) { return MigrationResult::NOT_APPLIED; } auto rc = migrate(db); if (!rc) { return MigrationResult::FAILURE; } sqlite3_exec(db, "BEGIN TRANSACTION;", nullptr, nullptr, nullptr); auto inner_db_version = get_database_version(db); if (index <= inner_db_version) { sqlite3_exec(db, "ROLLBACK;", nullptr, nullptr, nullptr); return MigrationResult::NOT_APPLIED; } auto database_version_set = set_database_version(db, index); if (!database_version_set) { sqlite3_exec(db, "ROLLBACK;", nullptr, nullptr, nullptr); return MigrationResult::FAILURE; } sqlite3_exec(db, "END TRANSACTION;", nullptr, nullptr, nullptr); return MigrationResult::SUCCESS; } bool set_up_database(sqlite3 *db) { sqlite3_exec(db, "BEGIN TRANSACTION;", nullptr, nullptr, nullptr); auto db_version = get_database_version(db); auto latest_version = migrations.back().first; if (db_version == latest_version) { sqlite3_exec(db, "ROLLBACK;", nullptr, nullptr, nullptr); return true; } if (db_version != 0 || !create_schema(db) || !set_database_version(db, latest_version)) { sqlite3_exec(db, "ROLLBACK;", nullptr, nullptr, nullptr); return false; } sqlite3_exec(db, "END TRANSACTION;", nullptr, nullptr, nullptr); return true; } void SQLiteQueryExecutor::migrate() { // We don't want to run `PRAGMA key = ...;` // on main web database. The context is here: // https://linear.app/comm/issue/ENG-6398/issues-with-sqlcipher-on-web #ifndef EMSCRIPTEN validate_encryption(); #endif sqlite3 *db; sqlite3_open(SQLiteQueryExecutor::sqliteFilePath.c_str(), &db); default_on_db_open_callback(db); std::stringstream db_path; db_path << "db path: " << SQLiteQueryExecutor::sqliteFilePath.c_str() << std::endl; Logger::log(db_path.str()); auto db_version = get_database_version(db); std::stringstream version_msg; version_msg << "db version: " << db_version << std::endl; Logger::log(version_msg.str()); if (db_version == 0) { auto db_created = set_up_database(db); if (!db_created) { sqlite3_close(db); Logger::log("Database structure creation error."); throw std::runtime_error("Database structure creation error"); } Logger::log("Database structure created."); sqlite3_close(db); return; } for (const auto &[idx, migration] : migrations) { const auto &[applyMigration, shouldBeInTransaction] = migration; MigrationResult migrationResult; if (shouldBeInTransaction) { migrationResult = applyMigrationWithTransaction(db, applyMigration, idx); } else { migrationResult = applyMigrationWithoutTransaction(db, applyMigration, idx); } if (migrationResult == MigrationResult::NOT_APPLIED) { continue; } std::stringstream migration_msg; if (migrationResult == MigrationResult::FAILURE) { migration_msg << "migration " << idx << " failed." << std::endl; Logger::log(migration_msg.str()); sqlite3_close(db); throw std::runtime_error(migration_msg.str()); } if (migrationResult == MigrationResult::SUCCESS) { migration_msg << "migration " << idx << " succeeded." << std::endl; Logger::log(migration_msg.str()); } } sqlite3_close(db); } SQLiteQueryExecutor::SQLiteQueryExecutor() { SQLiteQueryExecutor::migrate(); #ifndef EMSCRIPTEN SQLiteQueryExecutor::initializeTablesForLogMonitoring(); std::string currentBackupID = this->getMetadata("backupID"); if (!StaffUtils::isStaffRelease() || !currentBackupID.size()) { return; } SQLiteQueryExecutor::connectionManager.setLogsMonitoring(true); #endif } SQLiteQueryExecutor::SQLiteQueryExecutor(std::string sqliteFilePath) { SQLiteQueryExecutor::sqliteFilePath = sqliteFilePath; SQLiteQueryExecutor::migrate(); } sqlite3 *SQLiteQueryExecutor::getConnection() { if (SQLiteQueryExecutor::connectionManager.getConnection()) { return SQLiteQueryExecutor::connectionManager.getConnection(); } SQLiteQueryExecutor::connectionManager.initializeConnection( SQLiteQueryExecutor::sqliteFilePath, default_on_db_open_callback); return SQLiteQueryExecutor::connectionManager.getConnection(); } void SQLiteQueryExecutor::closeConnection() { SQLiteQueryExecutor::connectionManager.closeConnection(); } SQLiteQueryExecutor::~SQLiteQueryExecutor() { SQLiteQueryExecutor::closeConnection(); } std::string SQLiteQueryExecutor::getDraft(std::string key) const { static std::string getDraftByPrimaryKeySQL = "SELECT * " "FROM drafts " "WHERE key = ?;"; std::unique_ptr draft = getEntityByPrimaryKey( SQLiteQueryExecutor::getConnection(), getDraftByPrimaryKeySQL, key); return (draft == nullptr) ? "" : draft->text; } std::unique_ptr SQLiteQueryExecutor::getThread(std::string threadID) const { static std::string getThreadByPrimaryKeySQL = "SELECT * " "FROM threads " "WHERE id = ?;"; return getEntityByPrimaryKey( SQLiteQueryExecutor::getConnection(), getThreadByPrimaryKeySQL, threadID); } void SQLiteQueryExecutor::updateDraft(std::string key, std::string text) const { static std::string replaceDraftSQL = "REPLACE INTO drafts (key, text) " "VALUES (?, ?);"; Draft draft = {key, text}; replaceEntity( SQLiteQueryExecutor::getConnection(), replaceDraftSQL, draft); } bool SQLiteQueryExecutor::moveDraft(std::string oldKey, std::string newKey) const { std::string draftText = this->getDraft(oldKey); if (!draftText.size()) { return false; } static std::string rekeyDraftSQL = "UPDATE OR REPLACE drafts " "SET key = ? " "WHERE key = ?;"; rekeyAllEntities( SQLiteQueryExecutor::getConnection(), rekeyDraftSQL, oldKey, newKey); return true; } std::vector SQLiteQueryExecutor::getAllDrafts() const { static std::string getAllDraftsSQL = "SELECT * " "FROM drafts;"; return getAllEntities( SQLiteQueryExecutor::getConnection(), getAllDraftsSQL); } void SQLiteQueryExecutor::removeAllDrafts() const { static std::string removeAllDraftsSQL = "DELETE FROM drafts;"; removeAllEntities(SQLiteQueryExecutor::getConnection(), removeAllDraftsSQL); } void SQLiteQueryExecutor::removeDrafts( const std::vector &ids) const { if (!ids.size()) { return; } std::stringstream removeDraftsByKeysSQLStream; removeDraftsByKeysSQLStream << "DELETE FROM drafts " "WHERE key IN " << getSQLStatementArray(ids.size()) << ";"; removeEntitiesByKeys( SQLiteQueryExecutor::getConnection(), removeDraftsByKeysSQLStream.str(), ids); } void SQLiteQueryExecutor::removeAllMessages() const { static std::string removeAllMessagesSQL = "DELETE FROM messages;"; removeAllEntities(SQLiteQueryExecutor::getConnection(), removeAllMessagesSQL); } std::vector>> SQLiteQueryExecutor::getAllMessages() const { static std::string getAllMessagesSQL = "SELECT * " "FROM messages " "LEFT JOIN media " " ON messages.id = media.container " "ORDER BY messages.id;"; SQLiteStatementWrapper preparedSQL( SQLiteQueryExecutor::getConnection(), getAllMessagesSQL, "Failed to retrieve all messages."); std::string prevMsgIdx{}; std::vector>> allMessages; for (int stepResult = sqlite3_step(preparedSQL); stepResult == SQLITE_ROW; stepResult = sqlite3_step(preparedSQL)) { Message message = Message::fromSQLResult(preparedSQL, 0); if (message.id == prevMsgIdx) { allMessages.back().second.push_back(Media::fromSQLResult(preparedSQL, 8)); } else { prevMsgIdx = message.id; std::vector mediaForMsg; if (sqlite3_column_type(preparedSQL, 8) != SQLITE_NULL) { mediaForMsg.push_back(Media::fromSQLResult(preparedSQL, 8)); } allMessages.push_back(std::make_pair(std::move(message), mediaForMsg)); } } return allMessages; } void SQLiteQueryExecutor::removeMessages( const std::vector &ids) const { if (!ids.size()) { return; } std::stringstream removeMessagesByKeysSQLStream; removeMessagesByKeysSQLStream << "DELETE FROM messages " "WHERE id IN " << getSQLStatementArray(ids.size()) << ";"; removeEntitiesByKeys( SQLiteQueryExecutor::getConnection(), removeMessagesByKeysSQLStream.str(), ids); } void SQLiteQueryExecutor::removeMessagesForThreads( const std::vector &threadIDs) const { if (!threadIDs.size()) { return; } std::stringstream removeMessagesByKeysSQLStream; removeMessagesByKeysSQLStream << "DELETE FROM messages " "WHERE thread IN " << getSQLStatementArray(threadIDs.size()) << ";"; removeEntitiesByKeys( SQLiteQueryExecutor::getConnection(), removeMessagesByKeysSQLStream.str(), threadIDs); } void SQLiteQueryExecutor::replaceMessage(const Message &message) const { static std::string replaceMessageSQL = "REPLACE INTO messages " "(id, local_id, thread, user, type, future_type, content, time) " "VALUES (?, ?, ?, ?, ?, ?, ?, ?);"; replaceEntity( SQLiteQueryExecutor::getConnection(), replaceMessageSQL, message); } void SQLiteQueryExecutor::rekeyMessage(std::string from, std::string to) const { static std::string rekeyMessageSQL = "UPDATE OR REPLACE messages " "SET id = ? " "WHERE id = ?"; rekeyAllEntities( SQLiteQueryExecutor::getConnection(), rekeyMessageSQL, from, to); } void SQLiteQueryExecutor::removeAllMedia() const { static std::string removeAllMediaSQL = "DELETE FROM media;"; removeAllEntities(SQLiteQueryExecutor::getConnection(), removeAllMediaSQL); } void SQLiteQueryExecutor::removeMediaForMessages( const std::vector &msg_ids) const { if (!msg_ids.size()) { return; } std::stringstream removeMediaByKeysSQLStream; removeMediaByKeysSQLStream << "DELETE FROM media " "WHERE container IN " << getSQLStatementArray(msg_ids.size()) << ";"; removeEntitiesByKeys( SQLiteQueryExecutor::getConnection(), removeMediaByKeysSQLStream.str(), msg_ids); } void SQLiteQueryExecutor::removeMediaForMessage(std::string msg_id) const { static std::string removeMediaByKeySQL = "DELETE FROM media " "WHERE container IN (?);"; std::vector keys = {msg_id}; removeEntitiesByKeys( SQLiteQueryExecutor::getConnection(), removeMediaByKeySQL, keys); } void SQLiteQueryExecutor::removeMediaForThreads( const std::vector &thread_ids) const { if (!thread_ids.size()) { return; } std::stringstream removeMediaByKeysSQLStream; removeMediaByKeysSQLStream << "DELETE FROM media " "WHERE thread IN " << getSQLStatementArray(thread_ids.size()) << ";"; removeEntitiesByKeys( SQLiteQueryExecutor::getConnection(), removeMediaByKeysSQLStream.str(), thread_ids); } void SQLiteQueryExecutor::replaceMedia(const Media &media) const { static std::string replaceMediaSQL = "REPLACE INTO media " "(id, container, thread, uri, type, extras) " "VALUES (?, ?, ?, ?, ?, ?)"; replaceEntity( SQLiteQueryExecutor::getConnection(), replaceMediaSQL, media); } void SQLiteQueryExecutor::rekeyMediaContainers(std::string from, std::string to) const { static std::string rekeyMediaContainersSQL = "UPDATE media SET container = ? WHERE container = ?;"; rekeyAllEntities( SQLiteQueryExecutor::getConnection(), rekeyMediaContainersSQL, from, to); } void SQLiteQueryExecutor::replaceMessageStoreThreads( const std::vector &threads) const { static std::string replaceMessageStoreThreadSQL = "REPLACE INTO message_store_threads " "(id, start_reached) " "VALUES (?, ?);"; for (auto &thread : threads) { replaceEntity( SQLiteQueryExecutor::getConnection(), replaceMessageStoreThreadSQL, thread); } } void SQLiteQueryExecutor::removeAllMessageStoreThreads() const { static std::string removeAllMessageStoreThreadsSQL = "DELETE FROM message_store_threads;"; removeAllEntities( SQLiteQueryExecutor::getConnection(), removeAllMessageStoreThreadsSQL); } void SQLiteQueryExecutor::removeMessageStoreThreads( const std::vector &ids) const { if (!ids.size()) { return; } std::stringstream removeMessageStoreThreadsByKeysSQLStream; removeMessageStoreThreadsByKeysSQLStream << "DELETE FROM message_store_threads " "WHERE id IN " << getSQLStatementArray(ids.size()) << ";"; removeEntitiesByKeys( SQLiteQueryExecutor::getConnection(), removeMessageStoreThreadsByKeysSQLStream.str(), ids); } std::vector SQLiteQueryExecutor::getAllMessageStoreThreads() const { static std::string getAllMessageStoreThreadsSQL = "SELECT * " "FROM message_store_threads;"; return getAllEntities( SQLiteQueryExecutor::getConnection(), getAllMessageStoreThreadsSQL); } std::vector SQLiteQueryExecutor::getAllThreads() const { static std::string getAllThreadsSQL = "SELECT * " "FROM threads;"; return getAllEntities( SQLiteQueryExecutor::getConnection(), getAllThreadsSQL); }; void SQLiteQueryExecutor::removeThreads(std::vector ids) const { if (!ids.size()) { return; } std::stringstream removeThreadsByKeysSQLStream; removeThreadsByKeysSQLStream << "DELETE FROM threads " "WHERE id IN " << getSQLStatementArray(ids.size()) << ";"; removeEntitiesByKeys( SQLiteQueryExecutor::getConnection(), removeThreadsByKeysSQLStream.str(), ids); }; void SQLiteQueryExecutor::replaceThread(const Thread &thread) const { static std::string replaceThreadSQL = "REPLACE INTO threads (" " id, type, name, description, color, creation_time, parent_thread_id," " containing_thread_id, community, members, roles, current_user," " source_message_id, replies_count, avatar, pinned_count) " "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"; replaceEntity( SQLiteQueryExecutor::getConnection(), replaceThreadSQL, thread); }; void SQLiteQueryExecutor::removeAllThreads() const { static std::string removeAllThreadsSQL = "DELETE FROM threads;"; removeAllEntities(SQLiteQueryExecutor::getConnection(), removeAllThreadsSQL); }; void SQLiteQueryExecutor::replaceReport(const Report &report) const { static std::string replaceReportSQL = "REPLACE INTO reports (id, report) " "VALUES (?, ?);"; replaceEntity( SQLiteQueryExecutor::getConnection(), replaceReportSQL, report); } void SQLiteQueryExecutor::removeAllReports() const { static std::string removeAllReportsSQL = "DELETE FROM reports;"; removeAllEntities(SQLiteQueryExecutor::getConnection(), removeAllReportsSQL); } void SQLiteQueryExecutor::removeReports( const std::vector &ids) const { if (!ids.size()) { return; } std::stringstream removeReportsByKeysSQLStream; removeReportsByKeysSQLStream << "DELETE FROM reports " "WHERE id IN " << getSQLStatementArray(ids.size()) << ";"; removeEntitiesByKeys( SQLiteQueryExecutor::getConnection(), removeReportsByKeysSQLStream.str(), ids); } std::vector SQLiteQueryExecutor::getAllReports() const { static std::string getAllReportsSQL = "SELECT * " "FROM reports;"; return getAllEntities( SQLiteQueryExecutor::getConnection(), getAllReportsSQL); } void SQLiteQueryExecutor::setPersistStorageItem( std::string key, std::string item) const { static std::string replacePersistStorageItemSQL = "REPLACE INTO persist_storage (key, item) " "VALUES (?, ?);"; PersistItem entry{ key, item, }; replaceEntity( SQLiteQueryExecutor::getConnection(), replacePersistStorageItemSQL, entry); } void SQLiteQueryExecutor::removePersistStorageItem(std::string key) const { static std::string removePersistStorageItemByKeySQL = "DELETE FROM persist_storage " "WHERE key IN (?);"; std::vector keys = {key}; removeEntitiesByKeys( SQLiteQueryExecutor::getConnection(), removePersistStorageItemByKeySQL, keys); } std::string SQLiteQueryExecutor::getPersistStorageItem(std::string key) const { static std::string getPersistStorageItemByPrimaryKeySQL = "SELECT * " "FROM persist_storage " "WHERE key = ?;"; std::unique_ptr entry = getEntityByPrimaryKey( SQLiteQueryExecutor::getConnection(), getPersistStorageItemByPrimaryKeySQL, key); return (entry == nullptr) ? "" : entry->item; } void SQLiteQueryExecutor::replaceUser(const UserInfo &user_info) const { static std::string replaceUserSQL = "REPLACE INTO users (id, user_info) " "VALUES (?, ?);"; replaceEntity( SQLiteQueryExecutor::getConnection(), replaceUserSQL, user_info); } void SQLiteQueryExecutor::removeAllUsers() const { static std::string removeAllUsersSQL = "DELETE FROM users;"; removeAllEntities(SQLiteQueryExecutor::getConnection(), removeAllUsersSQL); } void SQLiteQueryExecutor::removeUsers( const std::vector &ids) const { if (!ids.size()) { return; } std::stringstream removeUsersByKeysSQLStream; removeUsersByKeysSQLStream << "DELETE FROM users " "WHERE id IN " << getSQLStatementArray(ids.size()) << ";"; removeEntitiesByKeys( SQLiteQueryExecutor::getConnection(), removeUsersByKeysSQLStream.str(), ids); } void SQLiteQueryExecutor::replaceKeyserver( const KeyserverInfo &keyserver_info) const { static std::string replaceKeyserverSQL = "REPLACE INTO keyservers (id, keyserver_info) " "VALUES (:id, :keyserver_info);"; replaceEntity( SQLiteQueryExecutor::getConnection(), replaceKeyserverSQL, keyserver_info); static std::string replaceKeyserverSyncedSQL = "REPLACE INTO keyservers_synced (id, keyserver_info) " "VALUES (:id, :synced_keyserver_info);"; replaceEntity( SQLiteQueryExecutor::getConnection(), replaceKeyserverSyncedSQL, keyserver_info); } void SQLiteQueryExecutor::removeAllKeyservers() const { static std::string removeAllKeyserversSQL = "DELETE FROM keyservers;"; removeAllEntities( SQLiteQueryExecutor::getConnection(), removeAllKeyserversSQL); static std::string removeAllKeyserversSyncedSQL = "DELETE FROM keyservers_synced;"; removeAllEntities( SQLiteQueryExecutor::getConnection(), removeAllKeyserversSyncedSQL); } void SQLiteQueryExecutor::removeKeyservers( const std::vector &ids) const { if (!ids.size()) { return; } auto idArray = getSQLStatementArray(ids.size()); std::stringstream removeKeyserversByKeysSQLStream; removeKeyserversByKeysSQLStream << "DELETE FROM keyservers " "WHERE id IN " << idArray << ";"; removeEntitiesByKeys( SQLiteQueryExecutor::getConnection(), removeKeyserversByKeysSQLStream.str(), ids); std::stringstream removeKeyserversSyncedByKeysSQLStream; removeKeyserversSyncedByKeysSQLStream << "DELETE FROM keyservers_synced " "WHERE id IN " << idArray << ";"; removeEntitiesByKeys( SQLiteQueryExecutor::getConnection(), removeKeyserversSyncedByKeysSQLStream.str(), ids); } std::vector SQLiteQueryExecutor::getAllKeyservers() const { static std::string getAllKeyserversSQL = "SELECT " " synced.id, " " COALESCE(keyservers.keyserver_info, ''), " " synced.keyserver_info " "FROM keyservers_synced synced " "LEFT JOIN keyservers " " ON synced.id = keyservers.id;"; return getAllEntities( SQLiteQueryExecutor::getConnection(), getAllKeyserversSQL); } std::vector SQLiteQueryExecutor::getAllUsers() const { static std::string getAllUsersSQL = "SELECT * " "FROM users;"; return getAllEntities( SQLiteQueryExecutor::getConnection(), getAllUsersSQL); } void SQLiteQueryExecutor::replaceCommunity( const CommunityInfo &community_info) const { static std::string replaceCommunitySQL = "REPLACE INTO communities (id, community_info) " "VALUES (?, ?);"; replaceEntity( SQLiteQueryExecutor::getConnection(), replaceCommunitySQL, community_info); } void SQLiteQueryExecutor::removeAllCommunities() const { static std::string removeAllCommunitiesSQL = "DELETE FROM communities;"; removeAllEntities( SQLiteQueryExecutor::getConnection(), removeAllCommunitiesSQL); } void SQLiteQueryExecutor::removeCommunities( const std::vector &ids) const { if (!ids.size()) { return; } std::stringstream removeCommunitiesByKeysSQLStream; removeCommunitiesByKeysSQLStream << "DELETE FROM communities " "WHERE id IN " << getSQLStatementArray(ids.size()) << ";"; removeEntitiesByKeys( SQLiteQueryExecutor::getConnection(), removeCommunitiesByKeysSQLStream.str(), ids); } std::vector SQLiteQueryExecutor::getAllCommunities() const { static std::string getAllCommunitiesSQL = "SELECT * " "FROM communities;"; return getAllEntities( SQLiteQueryExecutor::getConnection(), getAllCommunitiesSQL); } void SQLiteQueryExecutor::replaceIntegrityThreadHashes( const std::vector &threadHashes) const { static std::string replaceIntegrityThreadHashSQL = "REPLACE INTO integrity_store (id, thread_hash) " "VALUES (?, ?);"; for (const IntegrityThreadHash &integrityThreadHash : threadHashes) { replaceEntity( SQLiteQueryExecutor::getConnection(), replaceIntegrityThreadHashSQL, integrityThreadHash); } } void SQLiteQueryExecutor::removeAllIntegrityThreadHashes() const { static std::string removeAllIntegrityThreadHashesSQL = "DELETE FROM integrity_store;"; removeAllEntities( SQLiteQueryExecutor::getConnection(), removeAllIntegrityThreadHashesSQL); } void SQLiteQueryExecutor::removeIntegrityThreadHashes( const std::vector &ids) const { if (!ids.size()) { return; } std::stringstream removeIntegrityThreadHashesByKeysSQLStream; removeIntegrityThreadHashesByKeysSQLStream << "DELETE FROM integrity_store " "WHERE id IN " << getSQLStatementArray(ids.size()) << ";"; removeEntitiesByKeys( SQLiteQueryExecutor::getConnection(), removeIntegrityThreadHashesByKeysSQLStream.str(), ids); } std::vector SQLiteQueryExecutor::getAllIntegrityThreadHashes() const { static std::string getAllIntegrityThreadHashesSQL = "SELECT * " "FROM integrity_store;"; return getAllEntities( SQLiteQueryExecutor::getConnection(), getAllIntegrityThreadHashesSQL); } void SQLiteQueryExecutor::replaceSyncedMetadataEntry( const SyncedMetadataEntry &synced_metadata_entry) const { static std::string replaceSyncedMetadataEntrySQL = "REPLACE INTO synced_metadata (name, data) " "VALUES (?, ?);"; replaceEntity( SQLiteQueryExecutor::getConnection(), replaceSyncedMetadataEntrySQL, synced_metadata_entry); } void SQLiteQueryExecutor::removeAllSyncedMetadata() const { static std::string removeAllSyncedMetadataSQL = "DELETE FROM synced_metadata;"; removeAllEntities( SQLiteQueryExecutor::getConnection(), removeAllSyncedMetadataSQL); } void SQLiteQueryExecutor::removeSyncedMetadata( const std::vector &names) const { if (!names.size()) { return; } std::stringstream removeSyncedMetadataByNamesSQLStream; removeSyncedMetadataByNamesSQLStream << "DELETE FROM synced_metadata " "WHERE name IN " << getSQLStatementArray(names.size()) << ";"; removeEntitiesByKeys( SQLiteQueryExecutor::getConnection(), removeSyncedMetadataByNamesSQLStream.str(), names); } std::vector SQLiteQueryExecutor::getAllSyncedMetadata() const { static std::string getAllSyncedMetadataSQL = "SELECT * " "FROM synced_metadata;"; return getAllEntities( SQLiteQueryExecutor::getConnection(), getAllSyncedMetadataSQL); } void SQLiteQueryExecutor::replaceAuxUserInfo( const AuxUserInfo &aux_user_info) const { static std::string replaceAuxUserInfoSQL = "REPLACE INTO aux_users (id, aux_user_info) " "VALUES (?, ?);"; replaceEntity( SQLiteQueryExecutor::getConnection(), replaceAuxUserInfoSQL, aux_user_info); } void SQLiteQueryExecutor::removeAllAuxUserInfos() const { static std::string removeAllAuxUserInfosSQL = "DELETE FROM aux_users;"; removeAllEntities( SQLiteQueryExecutor::getConnection(), removeAllAuxUserInfosSQL); } void SQLiteQueryExecutor::removeAuxUserInfos( const std::vector &ids) const { if (!ids.size()) { return; } std::stringstream removeAuxUserInfosByKeysSQLStream; removeAuxUserInfosByKeysSQLStream << "DELETE FROM aux_users " "WHERE id IN " << getSQLStatementArray(ids.size()) << ";"; removeEntitiesByKeys( SQLiteQueryExecutor::getConnection(), removeAuxUserInfosByKeysSQLStream.str(), ids); } std::vector SQLiteQueryExecutor::getAllAuxUserInfos() const { static std::string getAllAuxUserInfosSQL = "SELECT * " "FROM aux_users;"; return getAllEntities( SQLiteQueryExecutor::getConnection(), getAllAuxUserInfosSQL); } void SQLiteQueryExecutor::replaceThreadActivityEntry( const ThreadActivityEntry &thread_activity_entry) const { static std::string replaceThreadActivityEntrySQL = "REPLACE INTO thread_activity (id, thread_activity_store_entry) " "VALUES (?, ?);"; replaceEntity( SQLiteQueryExecutor::getConnection(), replaceThreadActivityEntrySQL, thread_activity_entry); } void SQLiteQueryExecutor::removeAllThreadActivityEntries() const { static std::string removeAllThreadActivityEntriesSQL = "DELETE FROM thread_activity;"; removeAllEntities( SQLiteQueryExecutor::getConnection(), removeAllThreadActivityEntriesSQL); } void SQLiteQueryExecutor::removeThreadActivityEntries( const std::vector &ids) const { if (!ids.size()) { return; } std::stringstream removeThreadActivityEntriesByKeysSQLStream; removeThreadActivityEntriesByKeysSQLStream << "DELETE FROM thread_activity " "WHERE id IN " << getSQLStatementArray(ids.size()) << ";"; removeEntitiesByKeys( SQLiteQueryExecutor::getConnection(), removeThreadActivityEntriesByKeysSQLStream.str(), ids); } std::vector SQLiteQueryExecutor::getAllThreadActivityEntries() const { static std::string getAllThreadActivityEntriesSQL = "SELECT * " "FROM thread_activity;"; return getAllEntities( SQLiteQueryExecutor::getConnection(), getAllThreadActivityEntriesSQL); } void SQLiteQueryExecutor::beginTransaction() const { executeQuery(SQLiteQueryExecutor::getConnection(), "BEGIN TRANSACTION;"); } void SQLiteQueryExecutor::commitTransaction() const { executeQuery(SQLiteQueryExecutor::getConnection(), "COMMIT;"); } void SQLiteQueryExecutor::rollbackTransaction() const { executeQuery(SQLiteQueryExecutor::getConnection(), "ROLLBACK;"); } int SQLiteQueryExecutor::getContentAccountID() const { return CONTENT_ACCOUNT_ID; } int SQLiteQueryExecutor::getNotifsAccountID() const { return NOTIFS_ACCOUNT_ID; } std::vector SQLiteQueryExecutor::getOlmPersistSessionsData() const { static std::string getAllOlmPersistSessionsSQL = "SELECT * " "FROM olm_persist_sessions;"; return getAllEntities( SQLiteQueryExecutor::getConnection(), getAllOlmPersistSessionsSQL); } std::optional SQLiteQueryExecutor::getOlmPersistAccountData(int accountID) const { static std::string getOlmPersistAccountSQL = "SELECT * " "FROM olm_persist_account " "WHERE id = ?;"; std::unique_ptr result = getEntityByIntegerPrimaryKey( SQLiteQueryExecutor::getConnection(), getOlmPersistAccountSQL, accountID); if (result == nullptr) { return std::nullopt; } return result->account_data; } void SQLiteQueryExecutor::storeOlmPersistAccount( int accountID, const std::string &accountData) const { static std::string replaceOlmPersistAccountSQL = "REPLACE INTO olm_persist_account (id, account_data) " "VALUES (?, ?);"; OlmPersistAccount persistAccount = {accountID, accountData}; replaceEntity( SQLiteQueryExecutor::getConnection(), replaceOlmPersistAccountSQL, persistAccount); } void SQLiteQueryExecutor::storeOlmPersistSession( const OlmPersistSession &session) const { static std::string replaceOlmPersistSessionSQL = "REPLACE INTO olm_persist_sessions " "(target_device_id, session_data, version) " "VALUES (?, ?, ?);"; replaceEntity( SQLiteQueryExecutor::getConnection(), replaceOlmPersistSessionSQL, session); } void SQLiteQueryExecutor::storeOlmPersistData( int accountID, crypto::Persist persist) const { if (accountID != CONTENT_ACCOUNT_ID && persist.sessions.size() > 0) { throw std::runtime_error( "Attempt to store notifications sessions in SQLite. Notifications " "sessions must be stored in storage shared with NSE."); } std::string accountData = std::string(persist.account.begin(), persist.account.end()); this->storeOlmPersistAccount(accountID, accountData); for (auto it = persist.sessions.begin(); it != persist.sessions.end(); it++) { OlmPersistSession persistSession = { it->first, std::string(it->second.buffer.begin(), it->second.buffer.end()), it->second.version}; this->storeOlmPersistSession(persistSession); } } void SQLiteQueryExecutor::setNotifyToken(std::string token) const { this->setMetadata("notify_token", token); } void SQLiteQueryExecutor::clearNotifyToken() const { this->clearMetadata("notify_token"); } void SQLiteQueryExecutor::stampSQLiteDBUserID(std::string userID) const { this->setMetadata("current_user_id", userID); } std::string SQLiteQueryExecutor::getSQLiteStampedUserID() const { return this->getMetadata("current_user_id"); } void SQLiteQueryExecutor::setMetadata(std::string entry_name, std::string data) const { std::string replaceMetadataSQL = "REPLACE INTO metadata (name, data) " "VALUES (?, ?);"; Metadata entry{ entry_name, data, }; replaceEntity( SQLiteQueryExecutor::getConnection(), replaceMetadataSQL, entry); } void SQLiteQueryExecutor::clearMetadata(std::string entry_name) const { static std::string removeMetadataByKeySQL = "DELETE FROM metadata " "WHERE name IN (?);"; std::vector keys = {entry_name}; removeEntitiesByKeys( SQLiteQueryExecutor::getConnection(), removeMetadataByKeySQL, keys); } std::string SQLiteQueryExecutor::getMetadata(std::string entry_name) const { std::string getMetadataByPrimaryKeySQL = "SELECT * " "FROM metadata " "WHERE name = ?;"; std::unique_ptr entry = getEntityByPrimaryKey( SQLiteQueryExecutor::getConnection(), getMetadataByPrimaryKeySQL, entry_name); return (entry == nullptr) ? "" : entry->data; } void SQLiteQueryExecutor::addMessagesToDevice( const std::vector &messages) const { static std::string addMessageToDevice = "REPLACE INTO messages_to_device (" " message_id, device_id, user_id, timestamp, plaintext, ciphertext) " "VALUES (?, ?, ?, ?, ?, ?);"; for (const ClientMessageToDevice &clientMessage : messages) { MessageToDevice message = clientMessage.toMessageToDevice(); replaceEntity( SQLiteQueryExecutor::getConnection(), addMessageToDevice, message); } } std::vector SQLiteQueryExecutor::getAllMessagesToDevice(const std::string &deviceID) const { std::string query = "SELECT * FROM messages_to_device " "WHERE device_id = ? " "ORDER BY timestamp;"; SQLiteStatementWrapper preparedSQL( SQLiteQueryExecutor::getConnection(), query, "Failed to get all messages to device"); sqlite3_bind_text(preparedSQL, 1, deviceID.c_str(), -1, SQLITE_TRANSIENT); std::vector messages; for (int stepResult = sqlite3_step(preparedSQL); stepResult == SQLITE_ROW; stepResult = sqlite3_step(preparedSQL)) { messages.emplace_back( ClientMessageToDevice(MessageToDevice::fromSQLResult(preparedSQL, 0))); } return messages; } void SQLiteQueryExecutor::removeMessagesToDeviceOlderThan( const ClientMessageToDevice &lastConfirmedMessageClient) const { static std::string query = "DELETE FROM messages_to_device " "WHERE timestamp <= ? AND device_id IN (?);"; MessageToDevice lastConfirmedMessage = lastConfirmedMessageClient.toMessageToDevice(); comm::SQLiteStatementWrapper preparedSQL( SQLiteQueryExecutor::getConnection(), query, "Failed to remove messages to device"); sqlite3_bind_int64(preparedSQL, 1, lastConfirmedMessage.timestamp); sqlite3_bind_text( preparedSQL, 2, lastConfirmedMessage.device_id.c_str(), -1, SQLITE_TRANSIENT); int result = sqlite3_step(preparedSQL); if (result != SQLITE_DONE) { throw std::runtime_error( "Failed to execute removeMessagesToDeviceOlderThan statement: " + std::string(sqlite3_errmsg(SQLiteQueryExecutor::getConnection()))); } } void SQLiteQueryExecutor::removeAllMessagesForDevice( const std::string &deviceID) const { static std::string removeMessagesSQL = "DELETE FROM messages_to_device " "WHERE device_id IN (?);"; std::vector keys = {deviceID}; removeEntitiesByKeys( SQLiteQueryExecutor::getConnection(), removeMessagesSQL, keys); } -void SQLiteQueryExecutor::addReceivedMessageToDevice( - ReceivedMessageToDevice message) const { +void SQLiteQueryExecutor::addInboundP2PMessage( + InboundP2PMessage message) const { static std::string addMessage = "REPLACE INTO received_messages_to_device (" " message_id, sender_device_id, plaintext, status)" "VALUES (?, ?, ?, ?);"; - replaceEntity( + replaceEntity( SQLiteQueryExecutor::getConnection(), addMessage, message); } -std::vector -SQLiteQueryExecutor::getAllReceivedMessageToDevice() const { +std::vector +SQLiteQueryExecutor::getAllInboundP2PMessage() const { static std::string query = "SELECT message_id, sender_device_id, plaintext, status " "FROM received_messages_to_device;"; - return getAllEntities( + return getAllEntities( SQLiteQueryExecutor::getConnection(), query); } -void SQLiteQueryExecutor::removeReceivedMessagesToDevice( +void SQLiteQueryExecutor::removeInboundP2PMessages( const std::vector &ids) const { if (!ids.size()) { return; } std::stringstream removeMessagesSQLStream; removeMessagesSQLStream << "DELETE FROM received_messages_to_device " "WHERE message_id IN " << getSQLStatementArray(ids.size()) << ";"; removeEntitiesByKeys( SQLiteQueryExecutor::getConnection(), removeMessagesSQLStream.str(), ids); } #ifdef EMSCRIPTEN std::vector SQLiteQueryExecutor::getAllThreadsWeb() const { auto threads = this->getAllThreads(); std::vector webThreads; webThreads.reserve(threads.size()); for (const auto &thread : threads) { webThreads.emplace_back(thread); } return webThreads; }; void SQLiteQueryExecutor::replaceThreadWeb(const WebThread &thread) const { this->replaceThread(thread.toThread()); }; std::vector SQLiteQueryExecutor::getAllMessagesWeb() const { auto allMessages = this->getAllMessages(); std::vector allMessageWithMedias; for (auto &messageWitMedia : allMessages) { allMessageWithMedias.push_back( {std::move(messageWitMedia.first), messageWitMedia.second}); } return allMessageWithMedias; } void SQLiteQueryExecutor::replaceMessageWeb(const WebMessage &message) const { this->replaceMessage(message.toMessage()); }; NullableString SQLiteQueryExecutor::getOlmPersistAccountDataWeb(int accountID) const { std::optional accountData = this->getOlmPersistAccountData(accountID); if (!accountData.has_value()) { return NullableString(); } return std::make_unique(accountData.value()); } #else void SQLiteQueryExecutor::clearSensitiveData() { SQLiteQueryExecutor::closeConnection(); if (file_exists(SQLiteQueryExecutor::sqliteFilePath) && std::remove(SQLiteQueryExecutor::sqliteFilePath.c_str())) { std::ostringstream errorStream; errorStream << "Failed to delete database file. Details: " << strerror(errno); throw std::system_error(errno, std::generic_category(), errorStream.str()); } SQLiteQueryExecutor::generateFreshEncryptionKey(); SQLiteQueryExecutor::migrate(); } void SQLiteQueryExecutor::initialize(std::string &databasePath) { std::call_once(SQLiteQueryExecutor::initialized, [&databasePath]() { SQLiteQueryExecutor::sqliteFilePath = databasePath; folly::Optional maybeEncryptionKey = CommSecureStore::get(SQLiteQueryExecutor::secureStoreEncryptionKeyID); folly::Optional maybeBackupLogsEncryptionKey = CommSecureStore::get( SQLiteQueryExecutor::secureStoreBackupLogsEncryptionKeyID); if (file_exists(databasePath) && maybeEncryptionKey && maybeBackupLogsEncryptionKey) { SQLiteQueryExecutor::encryptionKey = maybeEncryptionKey.value(); SQLiteQueryExecutor::backupLogsEncryptionKey = maybeBackupLogsEncryptionKey.value(); return; } else if (file_exists(databasePath) && maybeEncryptionKey) { SQLiteQueryExecutor::encryptionKey = maybeEncryptionKey.value(); SQLiteQueryExecutor::generateFreshBackupLogsEncryptionKey(); return; } SQLiteQueryExecutor::generateFreshEncryptionKey(); }); } void SQLiteQueryExecutor::initializeTablesForLogMonitoring() { sqlite3 *db; sqlite3_open(SQLiteQueryExecutor::sqliteFilePath.c_str(), &db); default_on_db_open_callback(db); std::vector tablesToMonitor; { SQLiteStatementWrapper preparedSQL( db, "SELECT name FROM sqlite_master WHERE type='table';", "Failed to get all database tables"); for (int stepResult = sqlite3_step(preparedSQL); stepResult == SQLITE_ROW; stepResult = sqlite3_step(preparedSQL)) { std::string table_name = reinterpret_cast(sqlite3_column_text(preparedSQL, 0)); if (SQLiteQueryExecutor::backedUpTablesBlocklist.find(table_name) == SQLiteQueryExecutor::backedUpTablesBlocklist.end()) { tablesToMonitor.emplace_back(table_name); } } // Runs preparedSQL destructor which finalizes the sqlite statement } sqlite3_close(db); SQLiteQueryExecutor::connectionManager.tablesToMonitor = tablesToMonitor; } void SQLiteQueryExecutor::createMainCompaction(std::string backupID) const { std::string finalBackupPath = PlatformSpecificTools::getBackupFilePath(backupID, false); std::string finalAttachmentsPath = PlatformSpecificTools::getBackupFilePath(backupID, true); std::string tempBackupPath = finalBackupPath + "_tmp"; std::string tempAttachmentsPath = finalAttachmentsPath + "_tmp"; if (file_exists(tempBackupPath)) { Logger::log( "Attempting to delete temporary backup file from previous backup " "attempt."); attempt_delete_file( tempBackupPath, "Failed to delete temporary backup file from previous backup attempt."); } if (file_exists(tempAttachmentsPath)) { Logger::log( "Attempting to delete temporary attachments file from previous backup " "attempt."); attempt_delete_file( tempAttachmentsPath, "Failed to delete temporary attachments file from previous backup " "attempt."); } sqlite3 *backupDB; sqlite3_open(tempBackupPath.c_str(), &backupDB); set_encryption_key(backupDB); sqlite3_backup *backupObj = sqlite3_backup_init( backupDB, "main", SQLiteQueryExecutor::getConnection(), "main"); if (!backupObj) { std::stringstream error_message; error_message << "Failed to init backup for main compaction. Details: " << sqlite3_errmsg(backupDB) << std::endl; sqlite3_close(backupDB); throw std::runtime_error(error_message.str()); } int backupResult = sqlite3_backup_step(backupObj, -1); sqlite3_backup_finish(backupObj); if (backupResult == SQLITE_BUSY || backupResult == SQLITE_LOCKED) { sqlite3_close(backupDB); throw std::runtime_error( "Programmer error. Database in transaction during backup attempt."); } else if (backupResult != SQLITE_DONE) { sqlite3_close(backupDB); std::stringstream error_message; error_message << "Failed to create database backup. Details: " << sqlite3_errstr(backupResult); throw std::runtime_error(error_message.str()); } if (!SQLiteQueryExecutor::backedUpTablesBlocklist.empty()) { std::string removeDeviceSpecificDataSQL = ""; for (const auto &table_name : SQLiteQueryExecutor::backedUpTablesBlocklist) { removeDeviceSpecificDataSQL.append("DELETE FROM " + table_name + ";\n"); } executeQuery(backupDB, removeDeviceSpecificDataSQL); } executeQuery(backupDB, "VACUUM;"); sqlite3_close(backupDB); attempt_rename_file( tempBackupPath, finalBackupPath, "Failed to rename complete temporary backup file to final backup file."); std::ofstream tempAttachmentsFile(tempAttachmentsPath); if (!tempAttachmentsFile.is_open()) { throw std::runtime_error( "Unable to create attachments file for backup id: " + backupID); } std::string getAllBlobServiceMediaSQL = "SELECT * FROM media WHERE uri LIKE 'comm-blob-service://%';"; std::vector blobServiceMedia = getAllEntities( SQLiteQueryExecutor::getConnection(), getAllBlobServiceMediaSQL); for (const auto &media : blobServiceMedia) { std::string blobServiceURI = media.uri; std::string blobHash = blob_hash_from_blob_service_uri(blobServiceURI); tempAttachmentsFile << blobHash << "\n"; } tempAttachmentsFile.close(); attempt_rename_file( tempAttachmentsPath, finalAttachmentsPath, "Failed to rename complete temporary attachments file to final " "attachments file."); this->setMetadata("backupID", backupID); this->clearMetadata("logID"); if (StaffUtils::isStaffRelease()) { SQLiteQueryExecutor::connectionManager.setLogsMonitoring(true); } } void SQLiteQueryExecutor::generateFreshEncryptionKey() { std::string encryptionKey = comm::crypto::Tools::generateRandomHexString( SQLiteQueryExecutor::sqlcipherEncryptionKeySize); CommSecureStore::set( SQLiteQueryExecutor::secureStoreEncryptionKeyID, encryptionKey); SQLiteQueryExecutor::encryptionKey = encryptionKey; SQLiteQueryExecutor::generateFreshBackupLogsEncryptionKey(); } void SQLiteQueryExecutor::generateFreshBackupLogsEncryptionKey() { std::string backupLogsEncryptionKey = comm::crypto::Tools::generateRandomHexString( SQLiteQueryExecutor::backupLogsEncryptionKeySize); CommSecureStore::set( SQLiteQueryExecutor::secureStoreBackupLogsEncryptionKeyID, backupLogsEncryptionKey); SQLiteQueryExecutor::backupLogsEncryptionKey = backupLogsEncryptionKey; } void SQLiteQueryExecutor::captureBackupLogs() const { std::string backupID = this->getMetadata("backupID"); if (!backupID.size()) { return; } std::string logID = this->getMetadata("logID"); if (!logID.size()) { logID = "1"; } bool newLogCreated = SQLiteQueryExecutor::connectionManager.captureLogs( backupID, logID, SQLiteQueryExecutor::backupLogsEncryptionKey); if (!newLogCreated) { return; } this->setMetadata("logID", std::to_string(std::stoi(logID) + 1)); } #endif void SQLiteQueryExecutor::restoreFromMainCompaction( std::string mainCompactionPath, std::string mainCompactionEncryptionKey) const { if (!file_exists(mainCompactionPath)) { throw std::runtime_error("Restore attempt but backup file does not exist."); } sqlite3 *backupDB; if (!is_database_queryable( backupDB, true, mainCompactionPath, mainCompactionEncryptionKey)) { throw std::runtime_error("Backup file or encryption key corrupted."); } // We don't want to run `PRAGMA key = ...;` // on main web database. The context is here: // https://linear.app/comm/issue/ENG-6398/issues-with-sqlcipher-on-web #ifdef EMSCRIPTEN std::string plaintextBackupPath = mainCompactionPath + "_plaintext"; if (file_exists(plaintextBackupPath)) { attempt_delete_file( plaintextBackupPath, "Failed to delete plaintext backup file from previous backup attempt."); } std::string plaintextMigrationDBQuery = "PRAGMA key = \"x'" + mainCompactionEncryptionKey + "'\";" "ATTACH DATABASE '" + plaintextBackupPath + "' AS plaintext KEY '';" "SELECT sqlcipher_export('plaintext');" "DETACH DATABASE plaintext;"; sqlite3_open(mainCompactionPath.c_str(), &backupDB); char *plaintextMigrationErr; sqlite3_exec( backupDB, plaintextMigrationDBQuery.c_str(), nullptr, nullptr, &plaintextMigrationErr); sqlite3_close(backupDB); if (plaintextMigrationErr) { std::stringstream error_message; error_message << "Failed to migrate backup SQLCipher file to plaintext " "SQLite file. Details" << plaintextMigrationErr << std::endl; std::string error_message_str = error_message.str(); sqlite3_free(plaintextMigrationErr); throw std::runtime_error(error_message_str); } sqlite3_open(plaintextBackupPath.c_str(), &backupDB); #else sqlite3_open(mainCompactionPath.c_str(), &backupDB); set_encryption_key(backupDB, mainCompactionEncryptionKey); #endif sqlite3_backup *backupObj = sqlite3_backup_init( SQLiteQueryExecutor::getConnection(), "main", backupDB, "main"); if (!backupObj) { std::stringstream error_message; error_message << "Failed to init backup for main compaction. Details: " << sqlite3_errmsg(SQLiteQueryExecutor::getConnection()) << std::endl; sqlite3_close(backupDB); throw std::runtime_error(error_message.str()); } int backupResult = sqlite3_backup_step(backupObj, -1); sqlite3_backup_finish(backupObj); sqlite3_close(backupDB); if (backupResult == SQLITE_BUSY || backupResult == SQLITE_LOCKED) { throw std::runtime_error( "Programmer error. Database in transaction during restore attempt."); } else if (backupResult != SQLITE_DONE) { std::stringstream error_message; error_message << "Failed to restore database from backup. Details: " << sqlite3_errstr(backupResult); throw std::runtime_error(error_message.str()); } #ifdef EMSCRIPTEN attempt_delete_file( plaintextBackupPath, "Failed to delete plaintext compaction file after successful restore."); #endif attempt_delete_file( mainCompactionPath, "Failed to delete main compaction file after successful restore."); } void SQLiteQueryExecutor::restoreFromBackupLog( const std::vector &backupLog) const { SQLiteQueryExecutor::connectionManager.restoreFromBackupLog(backupLog); } } // namespace comm diff --git a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h index fac92ac8f..8804d4b48 100644 --- a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h +++ b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h @@ -1,176 +1,174 @@ #pragma once #include "../CryptoTools/Persist.h" #include "DatabaseQueryExecutor.h" #include "NativeSQLiteConnectionManager.h" #include "entities/AuxUserInfo.h" #include "entities/CommunityInfo.h" #include "entities/Draft.h" #include "entities/IntegrityThreadHash.h" #include "entities/KeyserverInfo.h" #include "entities/ThreadActivityEntry.h" #include "entities/UserInfo.h" #include #include #include namespace comm { class SQLiteQueryExecutor : public DatabaseQueryExecutor { static void migrate(); static sqlite3 *getConnection(); static void closeConnection(); static std::once_flag initialized; static int sqlcipherEncryptionKeySize; static std::string secureStoreEncryptionKeyID; static int backupLogsEncryptionKeySize; static std::string secureStoreBackupLogsEncryptionKeyID; static std::string backupLogsEncryptionKey; #ifndef EMSCRIPTEN static NativeSQLiteConnectionManager connectionManager; static std::unordered_set backedUpTablesBlocklist; static void generateFreshEncryptionKey(); static void generateFreshBackupLogsEncryptionKey(); static void initializeTablesForLogMonitoring(); #else static SQLiteConnectionManager connectionManager; #endif public: static std::string sqliteFilePath; static std::string encryptionKey; SQLiteQueryExecutor(); ~SQLiteQueryExecutor(); SQLiteQueryExecutor(std::string sqliteFilePath); std::unique_ptr getThread(std::string threadID) const override; std::string getDraft(std::string key) const override; void updateDraft(std::string key, std::string text) const override; bool moveDraft(std::string oldKey, std::string newKey) const override; std::vector getAllDrafts() const override; void removeAllDrafts() const override; void removeDrafts(const std::vector &ids) const override; void removeAllMessages() const override; std::vector>> getAllMessages() const override; void removeMessages(const std::vector &ids) const override; void removeMessagesForThreads( const std::vector &threadIDs) const override; void replaceMessage(const Message &message) const override; void rekeyMessage(std::string from, std::string to) const override; void replaceMessageStoreThreads( const std::vector &threads) const override; void removeMessageStoreThreads(const std::vector &ids) const override; void removeAllMessageStoreThreads() const override; std::vector getAllMessageStoreThreads() const override; void removeAllMedia() const override; void removeMediaForMessages( const std::vector &msg_ids) const override; void removeMediaForMessage(std::string msg_id) const override; void removeMediaForThreads( const std::vector &thread_ids) const override; void replaceMedia(const Media &media) const override; void rekeyMediaContainers(std::string from, std::string to) const override; std::vector getAllThreads() const override; void removeThreads(std::vector ids) const override; void replaceThread(const Thread &thread) const override; void removeAllThreads() const override; void replaceReport(const Report &report) const override; void removeReports(const std::vector &ids) const override; void removeAllReports() const override; std::vector getAllReports() const override; void setPersistStorageItem(std::string key, std::string item) const override; void removePersistStorageItem(std::string key) const override; std::string getPersistStorageItem(std::string key) const override; void replaceUser(const UserInfo &user_info) const override; void removeUsers(const std::vector &ids) const override; void removeAllUsers() const override; std::vector getAllUsers() const override; void replaceKeyserver(const KeyserverInfo &keyserver_info) const override; void removeKeyservers(const std::vector &ids) const override; void removeAllKeyservers() const override; std::vector getAllKeyservers() const override; void replaceCommunity(const CommunityInfo &community_info) const override; void removeCommunities(const std::vector &ids) const override; void removeAllCommunities() const override; std::vector getAllCommunities() const override; void replaceIntegrityThreadHashes( const std::vector &thread_hashes) const override; void removeIntegrityThreadHashes( const std::vector &ids) const override; void removeAllIntegrityThreadHashes() const override; std::vector getAllIntegrityThreadHashes() const override; void replaceSyncedMetadataEntry( const SyncedMetadataEntry &synced_metadata_entry) const override; void removeSyncedMetadata(const std::vector &names) const override; void removeAllSyncedMetadata() const override; std::vector getAllSyncedMetadata() const override; void replaceAuxUserInfo(const AuxUserInfo &aux_user_info) const override; void removeAuxUserInfos(const std::vector &ids) const override; void removeAllAuxUserInfos() const override; virtual std::vector getAllAuxUserInfos() const override; void replaceThreadActivityEntry( const ThreadActivityEntry &thread_activity_entry) const override; void removeThreadActivityEntries( const std::vector &ids) const override; void removeAllThreadActivityEntries() const override; std::vector getAllThreadActivityEntries() const override; void beginTransaction() const override; void commitTransaction() const override; void rollbackTransaction() const override; int getContentAccountID() const override; int getNotifsAccountID() const override; std::vector getOlmPersistSessionsData() const override; std::optional getOlmPersistAccountData(int accountID) const override; void storeOlmPersistSession(const OlmPersistSession &session) const override; void storeOlmPersistAccount(int accountID, const std::string &accountData) const override; void storeOlmPersistData(int accountID, crypto::Persist persist) const override; void setNotifyToken(std::string token) const override; void clearNotifyToken() const override; void stampSQLiteDBUserID(std::string userID) const override; std::string getSQLiteStampedUserID() const override; void setMetadata(std::string entry_name, std::string data) const override; void clearMetadata(std::string entry_name) const override; std::string getMetadata(std::string entry_name) const override; void restoreFromMainCompaction( std::string mainCompactionPath, std::string mainCompactionEncryptionKey) const override; void restoreFromBackupLog( const std::vector &backupLog) const override; void addMessagesToDevice( const std::vector &messages) const override; std::vector getAllMessagesToDevice(const std::string &deviceID) const override; void removeMessagesToDeviceOlderThan( const ClientMessageToDevice &lastConfirmedMessage) const override; void removeAllMessagesForDevice(const std::string &deviceID) const override; + void addInboundP2PMessage(InboundP2PMessage message) const override; + std::vector getAllInboundP2PMessage() const override; void - addReceivedMessageToDevice(ReceivedMessageToDevice message) const override; - std::vector - getAllReceivedMessageToDevice() const override; - void removeReceivedMessagesToDevice( - const std::vector &ids) const override; + removeInboundP2PMessages(const std::vector &ids) const override; #ifdef EMSCRIPTEN std::vector getAllThreadsWeb() const override; void replaceThreadWeb(const WebThread &thread) const override; std::vector getAllMessagesWeb() const override; void replaceMessageWeb(const WebMessage &message) const override; NullableString getOlmPersistAccountDataWeb(int accountID) const override; #else static void clearSensitiveData(); static void initialize(std::string &databasePath); void createMainCompaction(std::string backupID) const override; void captureBackupLogs() const override; #endif }; } // namespace comm diff --git a/native/cpp/CommonCpp/DatabaseManagers/entities/ReceivedMessageToDevice.h b/native/cpp/CommonCpp/DatabaseManagers/entities/InboundP2PMessage.h similarity index 82% rename from native/cpp/CommonCpp/DatabaseManagers/entities/ReceivedMessageToDevice.h rename to native/cpp/CommonCpp/DatabaseManagers/entities/InboundP2PMessage.h index 7e60059a4..b7ff39493 100644 --- a/native/cpp/CommonCpp/DatabaseManagers/entities/ReceivedMessageToDevice.h +++ b/native/cpp/CommonCpp/DatabaseManagers/entities/InboundP2PMessage.h @@ -1,34 +1,34 @@ #pragma once #include #include #include #include "SQLiteDataConverters.h" namespace comm { -struct ReceivedMessageToDevice { +struct InboundP2PMessage { std::string message_id; std::string sender_device_id; std::string plaintext; std::string status; - static ReceivedMessageToDevice fromSQLResult(sqlite3_stmt *sqlRow, int idx) { - return ReceivedMessageToDevice{ + static InboundP2PMessage fromSQLResult(sqlite3_stmt *sqlRow, int idx) { + return InboundP2PMessage{ getStringFromSQLRow(sqlRow, idx), getStringFromSQLRow(sqlRow, idx + 1), getStringFromSQLRow(sqlRow, idx + 2), getStringFromSQLRow(sqlRow, idx + 3), }; } int bindToSQL(sqlite3_stmt *sql, int idx) const { bindStringToSQL(message_id, sql, idx); bindStringToSQL(sender_device_id, sql, idx + 1); bindStringToSQL(plaintext, sql, idx + 2); return bindStringToSQL(status, sql, idx + 3); } }; } // namespace comm diff --git a/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp b/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp index ffabf2779..02ee02b68 100644 --- a/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp +++ b/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp @@ -1,2223 +1,2221 @@ #include "CommCoreModule.h" #include "../Notifications/BackgroundDataStorage/NotificationsCryptoModule.h" #include "BaseDataStore.h" #include "CommServicesAuthMetadataEmitter.h" #include "DatabaseManager.h" #include "InternalModules/GlobalDBSingleton.h" #include "InternalModules/RustPromiseManager.h" #include "NativeModuleUtils.h" #include "TerminateApp.h" #include #include #include #include #include "JSIRust.h" #include "lib.rs.h" #include #include namespace comm { using namespace facebook::react; jsi::Value CommCoreModule::getDraft(jsi::Runtime &rt, jsi::String key) { std::string keyStr = key.utf8(rt); return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [=, &innerRt]() { std::string error; std::string draftStr; try { draftStr = DatabaseManager::getQueryExecutor().getDraft(keyStr); } catch (std::system_error &e) { error = e.what(); } this->jsInvoker_->invokeAsync([=, &innerRt]() { if (error.size()) { promise->reject(error); return; } jsi::String draft = jsi::String::createFromUtf8(innerRt, draftStr); promise->resolve(std::move(draft)); }); }; GlobalDBSingleton::instance.scheduleOrRunCancellable( job, promise, this->jsInvoker_); }); } jsi::Value CommCoreModule::updateDraft( jsi::Runtime &rt, jsi::String key, jsi::String text) { std::string keyStr = key.utf8(rt); std::string textStr = text.utf8(rt); return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [=]() { std::string error; try { DatabaseManager::getQueryExecutor().updateDraft(keyStr, textStr); } catch (std::system_error &e) { error = e.what(); } this->jsInvoker_->invokeAsync([=]() { if (error.size()) { promise->reject(error); } else { promise->resolve(true); } }); }; GlobalDBSingleton::instance.scheduleOrRunCancellable( job, promise, this->jsInvoker_); }); } jsi::Value CommCoreModule::moveDraft( jsi::Runtime &rt, jsi::String oldKey, jsi::String newKey) { std::string oldKeyStr = oldKey.utf8(rt); std::string newKeyStr = newKey.utf8(rt); return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [=]() { std::string error; bool result = false; try { result = DatabaseManager::getQueryExecutor().moveDraft( oldKeyStr, newKeyStr); } catch (std::system_error &e) { error = e.what(); } this->jsInvoker_->invokeAsync([=]() { if (error.size()) { promise->reject(error); } else { promise->resolve(result); } }); }; GlobalDBSingleton::instance.scheduleOrRunCancellable( job, promise, this->jsInvoker_); }); } jsi::Value CommCoreModule::getClientDBStore(jsi::Runtime &rt) { return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [=, &innerRt]() { std::string error; std::vector draftsVector; std::vector threadsVector; std::vector>> messagesVector; std::vector messageStoreThreadsVector; std::vector reportStoreVector; std::vector userStoreVector; std::vector keyserverStoreVector; std::vector communityStoreVector; std::vector integrityStoreVector; std::vector syncedMetadataStoreVector; std::vector auxUserStoreVector; std::vector threadActivityStoreVector; try { draftsVector = DatabaseManager::getQueryExecutor().getAllDrafts(); messagesVector = DatabaseManager::getQueryExecutor().getAllMessages(); threadsVector = DatabaseManager::getQueryExecutor().getAllThreads(); messageStoreThreadsVector = DatabaseManager::getQueryExecutor().getAllMessageStoreThreads(); reportStoreVector = DatabaseManager::getQueryExecutor().getAllReports(); userStoreVector = DatabaseManager::getQueryExecutor().getAllUsers(); keyserverStoreVector = DatabaseManager::getQueryExecutor().getAllKeyservers(); communityStoreVector = DatabaseManager::getQueryExecutor().getAllCommunities(); integrityStoreVector = DatabaseManager::getQueryExecutor() .getAllIntegrityThreadHashes(); syncedMetadataStoreVector = DatabaseManager::getQueryExecutor().getAllSyncedMetadata(); auxUserStoreVector = DatabaseManager::getQueryExecutor().getAllAuxUserInfos(); threadActivityStoreVector = DatabaseManager::getQueryExecutor() .getAllThreadActivityEntries(); } catch (std::system_error &e) { error = e.what(); } auto draftsVectorPtr = std::make_shared>(std::move(draftsVector)); auto messagesVectorPtr = std::make_shared< std::vector>>>( std::move(messagesVector)); auto threadsVectorPtr = std::make_shared>(std::move(threadsVector)); auto messageStoreThreadsVectorPtr = std::make_shared>( std::move(messageStoreThreadsVector)); auto reportStoreVectorPtr = std::make_shared>( std::move(reportStoreVector)); auto userStoreVectorPtr = std::make_shared>( std::move(userStoreVector)); auto keyserveStoreVectorPtr = std::make_shared>( std::move(keyserverStoreVector)); auto communityStoreVectorPtr = std::make_shared>( std::move(communityStoreVector)); auto integrityStoreVectorPtr = std::make_shared>( std::move(integrityStoreVector)); auto syncedMetadataStoreVectorPtr = std::make_shared>( std::move(syncedMetadataStoreVector)); auto auxUserStoreVectorPtr = std::make_shared>( std::move(auxUserStoreVector)); auto threadActivityStoreVectorPtr = std::make_shared>( std::move(threadActivityStoreVector)); this->jsInvoker_->invokeAsync([&innerRt, draftsVectorPtr, messagesVectorPtr, threadsVectorPtr, messageStoreThreadsVectorPtr, reportStoreVectorPtr, userStoreVectorPtr, keyserveStoreVectorPtr, communityStoreVectorPtr, integrityStoreVectorPtr, syncedMetadataStoreVectorPtr, auxUserStoreVectorPtr, threadActivityStoreVectorPtr, error, promise, draftStore = this->draftStore, threadStore = this->threadStore, messageStore = this->messageStore, reportStore = this->reportStore, userStore = this->userStore, keyserverStore = this->keyserverStore, communityStore = this->communityStore, integrityStore = this->integrityStore, syncedMetadataStore = this->syncedMetadataStore, auxUserStore = this->auxUserStore, threadActivityStore = this->threadActivityStore]() { if (error.size()) { promise->reject(error); return; } jsi::Array jsiDrafts = draftStore.parseDBDataStore(innerRt, draftsVectorPtr); jsi::Array jsiMessages = messageStore.parseDBDataStore(innerRt, messagesVectorPtr); jsi::Array jsiThreads = threadStore.parseDBDataStore(innerRt, threadsVectorPtr); jsi::Array jsiMessageStoreThreads = messageStore.parseDBMessageStoreThreads( innerRt, messageStoreThreadsVectorPtr); jsi::Array jsiReportStore = reportStore.parseDBDataStore(innerRt, reportStoreVectorPtr); jsi::Array jsiUserStore = userStore.parseDBDataStore(innerRt, userStoreVectorPtr); jsi::Array jsiKeyserverStore = keyserverStore.parseDBDataStore( innerRt, keyserveStoreVectorPtr); jsi::Array jsiCommunityStore = communityStore.parseDBDataStore( innerRt, communityStoreVectorPtr); jsi::Array jsiIntegrityStore = integrityStore.parseDBDataStore( innerRt, integrityStoreVectorPtr); jsi::Array jsiSyncedMetadataStore = syncedMetadataStore.parseDBDataStore( innerRt, syncedMetadataStoreVectorPtr); jsi::Array jsiAuxUserStore = auxUserStore.parseDBDataStore(innerRt, auxUserStoreVectorPtr); jsi::Array jsiThreadActivityStore = threadActivityStore.parseDBDataStore( innerRt, threadActivityStoreVectorPtr); auto jsiClientDBStore = jsi::Object(innerRt); jsiClientDBStore.setProperty(innerRt, "messages", jsiMessages); jsiClientDBStore.setProperty(innerRt, "threads", jsiThreads); jsiClientDBStore.setProperty(innerRt, "drafts", jsiDrafts); jsiClientDBStore.setProperty( innerRt, "messageStoreThreads", jsiMessageStoreThreads); jsiClientDBStore.setProperty(innerRt, "reports", jsiReportStore); jsiClientDBStore.setProperty(innerRt, "users", jsiUserStore); jsiClientDBStore.setProperty( innerRt, "keyservers", jsiKeyserverStore); jsiClientDBStore.setProperty( innerRt, "communities", jsiCommunityStore); jsiClientDBStore.setProperty( innerRt, "integrityThreadHashes", jsiIntegrityStore); jsiClientDBStore.setProperty( innerRt, "syncedMetadata", jsiSyncedMetadataStore); jsiClientDBStore.setProperty( innerRt, "auxUserInfos", jsiAuxUserStore); jsiClientDBStore.setProperty( innerRt, "threadActivityEntries", jsiThreadActivityStore); promise->resolve(std::move(jsiClientDBStore)); }); }; GlobalDBSingleton::instance.scheduleOrRunCancellable( job, promise, this->jsInvoker_); }); } jsi::Value CommCoreModule::removeAllDrafts(jsi::Runtime &rt) { return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [=]() { std::string error; try { DatabaseManager::getQueryExecutor().removeAllDrafts(); } catch (std::system_error &e) { error = e.what(); } this->jsInvoker_->invokeAsync([=]() { if (error.size()) { promise->reject(error); return; } promise->resolve(jsi::Value::undefined()); }); }; GlobalDBSingleton::instance.scheduleOrRunCancellable( job, promise, this->jsInvoker_); }); } jsi::Array CommCoreModule::getAllMessagesSync(jsi::Runtime &rt) { auto messagesVector = NativeModuleUtils::runSyncOrThrowJSError< std::vector>>>(rt, []() { return DatabaseManager::getQueryExecutor().getAllMessages(); }); auto messagesVectorPtr = std::make_shared>>>( std::move(messagesVector)); jsi::Array jsiMessages = this->messageStore.parseDBDataStore(rt, messagesVectorPtr); return jsiMessages; } void CommCoreModule::processMessageStoreOperationsSync( jsi::Runtime &rt, jsi::Array operations) { return this->messageStore.processStoreOperationsSync( rt, std::move(operations)); } jsi::Array CommCoreModule::getAllThreadsSync(jsi::Runtime &rt) { auto threadsVector = NativeModuleUtils::runSyncOrThrowJSError>(rt, []() { return DatabaseManager::getQueryExecutor().getAllThreads(); }); auto threadsVectorPtr = std::make_shared>(std::move(threadsVector)); jsi::Array jsiThreads = this->threadStore.parseDBDataStore(rt, threadsVectorPtr); return jsiThreads; } void CommCoreModule::processThreadStoreOperationsSync( jsi::Runtime &rt, jsi::Array operations) { this->threadStore.processStoreOperationsSync(rt, std::move(operations)); } void CommCoreModule::processReportStoreOperationsSync( jsi::Runtime &rt, jsi::Array operations) { this->reportStore.processStoreOperationsSync(rt, std::move(operations)); } template void CommCoreModule::appendDBStoreOps( jsi::Runtime &rt, jsi::Object &operations, const char *key, T &store, std::shared_ptr>> &destination) { auto opsObject = operations.getProperty(rt, key); if (opsObject.isObject()) { auto ops = store.createOperations(rt, opsObject.asObject(rt).asArray(rt)); std::move( std::make_move_iterator(ops.begin()), std::make_move_iterator(ops.end()), std::back_inserter(*destination)); } } jsi::Value CommCoreModule::processDBStoreOperations( jsi::Runtime &rt, jsi::Object operations) { std::string createOperationsError; auto storeOpsPtr = std::make_shared>>(); try { this->appendDBStoreOps( rt, operations, "draftStoreOperations", this->draftStore, storeOpsPtr); this->appendDBStoreOps( rt, operations, "threadStoreOperations", this->threadStore, storeOpsPtr); this->appendDBStoreOps( rt, operations, "messageStoreOperations", this->messageStore, storeOpsPtr); this->appendDBStoreOps( rt, operations, "reportStoreOperations", this->reportStore, storeOpsPtr); this->appendDBStoreOps( rt, operations, "userStoreOperations", this->userStore, storeOpsPtr); this->appendDBStoreOps( rt, operations, "keyserverStoreOperations", this->keyserverStore, storeOpsPtr); this->appendDBStoreOps( rt, operations, "communityStoreOperations", this->communityStore, storeOpsPtr); this->appendDBStoreOps( rt, operations, "integrityStoreOperations", this->integrityStore, storeOpsPtr); this->appendDBStoreOps( rt, operations, "syncedMetadataStoreOperations", this->syncedMetadataStore, storeOpsPtr); this->appendDBStoreOps( rt, operations, "auxUserStoreOperations", this->auxUserStore, storeOpsPtr); this->appendDBStoreOps( rt, operations, "threadActivityStoreOperations", this->threadActivityStore, storeOpsPtr); } catch (std::runtime_error &e) { createOperationsError = e.what(); } return facebook::react::createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [=]() { std::string error = createOperationsError; if (!error.size()) { try { DatabaseManager::getQueryExecutor().beginTransaction(); for (const auto &operation : *storeOpsPtr) { operation->execute(); } DatabaseManager::getQueryExecutor().captureBackupLogs(); DatabaseManager::getQueryExecutor().commitTransaction(); } catch (std::system_error &e) { error = e.what(); DatabaseManager::getQueryExecutor().rollbackTransaction(); } } if (!error.size()) { ::triggerBackupFileUpload(); } this->jsInvoker_->invokeAsync([=]() { if (error.size()) { promise->reject(error); } else { promise->resolve(jsi::Value::undefined()); } }); }; GlobalDBSingleton::instance.scheduleOrRunCancellable( job, promise, this->jsInvoker_); }); } void CommCoreModule::terminate(jsi::Runtime &rt) { TerminateApp::terminate(); } const std::string getAccountDataKey(const std::string secureStoreAccountDataKey) { folly::Optional storedSecretKey = CommSecureStore::get(secureStoreAccountDataKey); if (!storedSecretKey.hasValue()) { storedSecretKey = crypto::Tools::generateRandomString(64); CommSecureStore::set(secureStoreAccountDataKey, storedSecretKey.value()); } return storedSecretKey.value(); } void CommCoreModule::persistCryptoModules( bool persistContentModule, bool persistNotifsModule) { std::string storedSecretKey = getAccountDataKey(secureStoreAccountDataKey); if (!persistContentModule && !persistNotifsModule) { return; } crypto::Persist newContentPersist; if (persistContentModule) { newContentPersist = this->contentCryptoModule->storeAsB64(storedSecretKey); } crypto::Persist newNotifsPersist; if (persistNotifsModule) { newNotifsPersist = this->notifsCryptoModule->storeAsB64(storedSecretKey); } std::promise persistencePromise; std::future persistenceFuture = persistencePromise.get_future(); GlobalDBSingleton::instance.scheduleOrRunCancellable( [=, &persistencePromise]() { try { DatabaseManager::getQueryExecutor().beginTransaction(); if (persistContentModule) { DatabaseManager::getQueryExecutor().storeOlmPersistData( DatabaseManager::getQueryExecutor().getContentAccountID(), newContentPersist); } if (persistNotifsModule) { DatabaseManager::getQueryExecutor().storeOlmPersistData( DatabaseManager::getQueryExecutor().getNotifsAccountID(), newNotifsPersist); } DatabaseManager::getQueryExecutor().commitTransaction(); persistencePromise.set_value(); } catch (std::system_error &e) { DatabaseManager::getQueryExecutor().rollbackTransaction(); persistencePromise.set_exception(std::make_exception_ptr(e)); } }); persistenceFuture.get(); } jsi::Value CommCoreModule::initializeCryptoAccount(jsi::Runtime &rt) { folly::Optional storedSecretKey = CommSecureStore::get(this->secureStoreAccountDataKey); if (!storedSecretKey.hasValue()) { storedSecretKey = crypto::Tools::generateRandomString(64); CommSecureStore::set( this->secureStoreAccountDataKey, storedSecretKey.value()); } return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [=]() { crypto::Persist contentPersist; crypto::Persist notifsPersist; std::string error; try { std::optional contentAccountData = DatabaseManager::getQueryExecutor().getOlmPersistAccountData( DatabaseManager::getQueryExecutor().getContentAccountID()); if (contentAccountData.has_value()) { contentPersist.account = crypto::OlmBuffer( contentAccountData->begin(), contentAccountData->end()); // handle sessions data std::vector sessionsData = DatabaseManager::getQueryExecutor() .getOlmPersistSessionsData(); for (OlmPersistSession &sessionsDataItem : sessionsData) { crypto::OlmBuffer sessionDataBuffer( sessionsDataItem.session_data.begin(), sessionsDataItem.session_data.end()); crypto::SessionPersist sessionPersist{ sessionDataBuffer, sessionsDataItem.version}; contentPersist.sessions.insert(std::make_pair( sessionsDataItem.target_device_id, sessionPersist)); } } std::optional notifsAccountData = DatabaseManager::getQueryExecutor().getOlmPersistAccountData( DatabaseManager::getQueryExecutor().getNotifsAccountID()); if (notifsAccountData.has_value()) { notifsPersist.account = crypto::OlmBuffer( notifsAccountData->begin(), notifsAccountData->end()); } } catch (std::system_error &e) { error = e.what(); } this->cryptoThread->scheduleTask([=]() { std::string error; this->contentCryptoModule.reset(new crypto::CryptoModule( this->publicCryptoAccountID, storedSecretKey.value(), contentPersist)); this->notifsCryptoModule.reset(new crypto::CryptoModule( this->notifsCryptoAccountID, storedSecretKey.value(), notifsPersist)); try { this->persistCryptoModules( contentPersist.isEmpty(), notifsPersist.isEmpty()); } catch (const std::exception &e) { error = e.what(); } this->jsInvoker_->invokeAsync([=]() { if (error.size()) { promise->reject(error); return; } }); this->jsInvoker_->invokeAsync( [=]() { promise->resolve(jsi::Value::undefined()); }); }); }; GlobalDBSingleton::instance.scheduleOrRunCancellable( job, promise, this->jsInvoker_); }); } jsi::Value CommCoreModule::getUserPublicKey(jsi::Runtime &rt) { return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [=, &innerRt]() { std::string error; std::string primaryKeysResult; std::string notificationsKeysResult; if (this->contentCryptoModule == nullptr || this->notifsCryptoModule == nullptr) { error = "user has not been initialized"; } else { primaryKeysResult = this->contentCryptoModule->getIdentityKeys(); notificationsKeysResult = this->notifsCryptoModule->getIdentityKeys(); } std::string notificationsCurve25519Cpp, notificationsEd25519Cpp, blobPayloadCpp, signatureCpp, primaryCurve25519Cpp, primaryEd25519Cpp; if (!error.size()) { folly::dynamic parsedPrimary; try { parsedPrimary = folly::parseJson(primaryKeysResult); } catch (const folly::json::parse_error &e) { error = "parsing identity keys failed with: " + std::string(e.what()); } if (!error.size()) { primaryCurve25519Cpp = parsedPrimary["curve25519"].asString(); primaryEd25519Cpp = parsedPrimary["ed25519"].asString(); folly::dynamic parsedNotifications; try { parsedNotifications = folly::parseJson(notificationsKeysResult); } catch (const folly::json::parse_error &e) { error = "parsing notifications keys failed with: " + std::string(e.what()); } if (!error.size()) { notificationsCurve25519Cpp = parsedNotifications["curve25519"].asString(); notificationsEd25519Cpp = parsedNotifications["ed25519"].asString(); folly::dynamic blobPayloadJSON = folly::dynamic::object( "primaryIdentityPublicKeys", folly::dynamic::object("ed25519", primaryEd25519Cpp)( "curve25519", primaryCurve25519Cpp))( "notificationIdentityPublicKeys", folly::dynamic::object("ed25519", notificationsEd25519Cpp)( "curve25519", notificationsCurve25519Cpp)); blobPayloadCpp = folly::toJson(blobPayloadJSON); signatureCpp = this->contentCryptoModule->signMessage(blobPayloadCpp); } } } this->jsInvoker_->invokeAsync([=, &innerRt]() { if (error.size()) { promise->reject(error); return; } auto primaryCurve25519{ jsi::String::createFromUtf8(innerRt, primaryCurve25519Cpp)}; auto primaryEd25519{ jsi::String::createFromUtf8(innerRt, primaryEd25519Cpp)}; auto jsiPrimaryIdentityPublicKeys = jsi::Object(innerRt); jsiPrimaryIdentityPublicKeys.setProperty( innerRt, "ed25519", primaryEd25519); jsiPrimaryIdentityPublicKeys.setProperty( innerRt, "curve25519", primaryCurve25519); auto notificationsCurve25519{jsi::String::createFromUtf8( innerRt, notificationsCurve25519Cpp)}; auto notificationsEd25519{ jsi::String::createFromUtf8(innerRt, notificationsEd25519Cpp)}; auto jsiNotificationIdentityPublicKeys = jsi::Object(innerRt); jsiNotificationIdentityPublicKeys.setProperty( innerRt, "ed25519", notificationsEd25519); jsiNotificationIdentityPublicKeys.setProperty( innerRt, "curve25519", notificationsCurve25519); auto blobPayload{ jsi::String::createFromUtf8(innerRt, blobPayloadCpp)}; auto signature{jsi::String::createFromUtf8(innerRt, signatureCpp)}; auto jsiClientPublicKeys = jsi::Object(innerRt); jsiClientPublicKeys.setProperty( innerRt, "primaryIdentityPublicKeys", jsiPrimaryIdentityPublicKeys); jsiClientPublicKeys.setProperty( innerRt, "notificationIdentityPublicKeys", jsiNotificationIdentityPublicKeys); jsiClientPublicKeys.setProperty( innerRt, "blobPayload", blobPayload); jsiClientPublicKeys.setProperty(innerRt, "signature", signature); promise->resolve(std::move(jsiClientPublicKeys)); }); }; this->cryptoThread->scheduleTask(job); }); } jsi::Object parseOLMOneTimeKeys(jsi::Runtime &rt, std::string oneTimeKeysBlob) { folly::dynamic parsedOneTimeKeys = folly::parseJson(oneTimeKeysBlob); auto jsiOneTimeKeysInner = jsi::Object(rt); for (auto &kvPair : parsedOneTimeKeys["curve25519"].items()) { jsiOneTimeKeysInner.setProperty( rt, kvPair.first.asString().c_str(), jsi::String::createFromUtf8(rt, kvPair.second.asString())); } auto jsiOneTimeKeys = jsi::Object(rt); jsiOneTimeKeys.setProperty(rt, "curve25519", jsiOneTimeKeysInner); return jsiOneTimeKeys; } std::string parseOLMPrekey(std::string prekeyBlob) { folly::dynamic parsedPrekey; try { parsedPrekey = folly::parseJson(prekeyBlob); } catch (const folly::json::parse_error &e) { throw std::runtime_error( "parsing prekey failed with: " + std::string(e.what())); } folly::dynamic innerObject = parsedPrekey["curve25519"]; if (!innerObject.isObject()) { throw std::runtime_error("parsing prekey failed: inner object malformed"); } if (innerObject.values().begin() == innerObject.values().end()) { throw std::runtime_error("parsing prekey failed: prekey missing"); } return parsedPrekey["curve25519"].values().begin()->asString(); } jsi::Object parseOneTimeKeysResult( jsi::Runtime &rt, std::string contentOneTimeKeysBlob, std::string notifOneTimeKeysBlob) { auto contentOneTimeKeys = parseOLMOneTimeKeys(rt, contentOneTimeKeysBlob); auto notifOneTimeKeys = parseOLMOneTimeKeys(rt, notifOneTimeKeysBlob); auto jsiOneTimeKeysResult = jsi::Object(rt); jsiOneTimeKeysResult.setProperty( rt, "contentOneTimeKeys", contentOneTimeKeys); jsiOneTimeKeysResult.setProperty( rt, "notificationsOneTimeKeys", notifOneTimeKeys); return jsiOneTimeKeysResult; } jsi::Value CommCoreModule::getOneTimeKeys(jsi::Runtime &rt, double oneTimeKeysAmount) { return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [=, &innerRt]() { std::string error; std::string contentResult; std::string notifResult; if (this->contentCryptoModule == nullptr || this->notifsCryptoModule == nullptr) { this->jsInvoker_->invokeAsync([=, &innerRt]() { promise->reject("user has not been initialized"); }); return; } try { contentResult = this->contentCryptoModule->getOneTimeKeysForPublishing( oneTimeKeysAmount); notifResult = this->notifsCryptoModule->getOneTimeKeysForPublishing( oneTimeKeysAmount); this->persistCryptoModules(true, true); } catch (const std::exception &e) { error = e.what(); } this->jsInvoker_->invokeAsync([=, &innerRt]() { if (error.size()) { promise->reject(error); return; } promise->resolve( parseOneTimeKeysResult(innerRt, contentResult, notifResult)); }); }; this->cryptoThread->scheduleTask(job); }); } jsi::Value CommCoreModule::validateAndUploadPrekeys( jsi::Runtime &rt, jsi::String authUserID, jsi::String authDeviceID, jsi::String authAccessToken) { auto authUserIDRust = jsiStringToRustString(authUserID, rt); auto authDeviceIDRust = jsiStringToRustString(authDeviceID, rt); auto authAccessTokenRust = jsiStringToRustString(authAccessToken, rt); return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [=, &innerRt]() { std::string error; std::optional maybeContentPrekeyToUpload; std::optional maybeNotifsPrekeyToUpload; if (this->contentCryptoModule == nullptr || this->notifsCryptoModule == nullptr) { this->jsInvoker_->invokeAsync([=, &innerRt]() { promise->reject("user has not been initialized"); }); return; } try { maybeContentPrekeyToUpload = this->contentCryptoModule->validatePrekey(); maybeNotifsPrekeyToUpload = this->notifsCryptoModule->validatePrekey(); this->persistCryptoModules(true, true); if (!maybeContentPrekeyToUpload.has_value()) { maybeContentPrekeyToUpload = this->contentCryptoModule->getUnpublishedPrekey(); } if (!maybeNotifsPrekeyToUpload.has_value()) { maybeNotifsPrekeyToUpload = this->notifsCryptoModule->getUnpublishedPrekey(); } } catch (const std::exception &e) { error = e.what(); } if (error.size()) { this->jsInvoker_->invokeAsync( [=, &innerRt]() { promise->reject(error); }); return; } if (!maybeContentPrekeyToUpload.has_value() && !maybeNotifsPrekeyToUpload.has_value()) { this->jsInvoker_->invokeAsync( [=]() { promise->resolve(jsi::Value::undefined()); }); return; } std::string contentPrekeyToUpload; if (maybeContentPrekeyToUpload.has_value()) { contentPrekeyToUpload = maybeContentPrekeyToUpload.value(); } else { contentPrekeyToUpload = this->contentCryptoModule->getPrekey(); } std::string notifsPrekeyToUpload; if (maybeNotifsPrekeyToUpload.has_value()) { notifsPrekeyToUpload = maybeNotifsPrekeyToUpload.value(); } else { notifsPrekeyToUpload = this->notifsCryptoModule->getPrekey(); } std::string prekeyUploadError; try { std::string contentPrekeySignature = this->contentCryptoModule->getPrekeySignature(); std::string notifsPrekeySignature = this->notifsCryptoModule->getPrekeySignature(); try { std::promise prekeyPromise; std::future prekeyFuture = prekeyPromise.get_future(); RustPromiseManager::CPPPromiseInfo promiseInfo = { std::move(prekeyPromise)}; auto currentID = RustPromiseManager::instance.addPromise( std::move(promiseInfo)); auto contentPrekeyToUploadRust = rust::String(parseOLMPrekey(contentPrekeyToUpload)); auto prekeySignatureRust = rust::string(contentPrekeySignature); auto notifsPrekeyToUploadRust = rust::String(parseOLMPrekey(notifsPrekeyToUpload)); auto notificationsPrekeySignatureRust = rust::string(notifsPrekeySignature); ::identityRefreshUserPrekeys( authUserIDRust, authDeviceIDRust, authAccessTokenRust, contentPrekeyToUploadRust, prekeySignatureRust, notifsPrekeyToUploadRust, notificationsPrekeySignatureRust, currentID); prekeyFuture.get(); } catch (const std::exception &e) { prekeyUploadError = e.what(); } if (!prekeyUploadError.size()) { this->contentCryptoModule->markPrekeyAsPublished(); this->notifsCryptoModule->markPrekeyAsPublished(); this->persistCryptoModules(true, true); } } catch (std::exception &e) { error = e.what(); } this->jsInvoker_->invokeAsync([=]() { if (error.size()) { promise->reject(error); return; } if (prekeyUploadError.size()) { promise->reject(prekeyUploadError); return; } promise->resolve(jsi::Value::undefined()); }); }; this->cryptoThread->scheduleTask(job); }); } jsi::Value CommCoreModule::validateAndGetPrekeys(jsi::Runtime &rt) { return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [=, &innerRt]() { std::string error; std::string contentPrekey, notifPrekey, contentPrekeySignature, notifPrekeySignature; std::optional contentPrekeyBlob; std::optional notifPrekeyBlob; if (this->contentCryptoModule == nullptr || this->notifsCryptoModule == nullptr) { this->jsInvoker_->invokeAsync([=, &innerRt]() { promise->reject("user has not been initialized"); }); return; } try { contentPrekeyBlob = this->contentCryptoModule->validatePrekey(); if (!contentPrekeyBlob) { contentPrekeyBlob = this->contentCryptoModule->getUnpublishedPrekey(); } if (!contentPrekeyBlob) { contentPrekeyBlob = this->contentCryptoModule->getPrekey(); } notifPrekeyBlob = this->notifsCryptoModule->validatePrekey(); if (!notifPrekeyBlob) { notifPrekeyBlob = this->notifsCryptoModule->getUnpublishedPrekey(); } if (!notifPrekeyBlob) { notifPrekeyBlob = this->notifsCryptoModule->getPrekey(); } this->persistCryptoModules(true, true); contentPrekeySignature = this->contentCryptoModule->getPrekeySignature(); notifPrekeySignature = this->notifsCryptoModule->getPrekeySignature(); contentPrekey = parseOLMPrekey(contentPrekeyBlob.value()); notifPrekey = parseOLMPrekey(notifPrekeyBlob.value()); } catch (const std::exception &e) { error = e.what(); } this->jsInvoker_->invokeAsync([=, &innerRt]() { if (error.size()) { promise->reject(error); return; } auto contentPrekeyJSI = jsi::String::createFromUtf8(innerRt, contentPrekey); auto contentPrekeySignatureJSI = jsi::String::createFromUtf8(innerRt, contentPrekeySignature); auto notifPrekeyJSI = jsi::String::createFromUtf8(innerRt, notifPrekey); auto notifPrekeySignatureJSI = jsi::String::createFromUtf8(innerRt, notifPrekeySignature); auto signedPrekeysJSI = jsi::Object(innerRt); signedPrekeysJSI.setProperty( innerRt, "contentPrekey", contentPrekeyJSI); signedPrekeysJSI.setProperty( innerRt, "contentPrekeySignature", contentPrekeySignatureJSI); signedPrekeysJSI.setProperty( innerRt, "notifPrekey", notifPrekeyJSI); signedPrekeysJSI.setProperty( innerRt, "notifPrekeySignature", notifPrekeySignatureJSI); promise->resolve(std::move(signedPrekeysJSI)); }); }; this->cryptoThread->scheduleTask(job); }); } jsi::Value CommCoreModule::initializeNotificationsSession( jsi::Runtime &rt, jsi::String identityKeys, jsi::String prekey, jsi::String prekeySignature, jsi::String oneTimeKey, jsi::String keyserverID) { auto identityKeysCpp{identityKeys.utf8(rt)}; auto prekeyCpp{prekey.utf8(rt)}; auto prekeySignatureCpp{prekeySignature.utf8(rt)}; auto oneTimeKeyCpp{oneTimeKey.utf8(rt)}; auto keyserverIDCpp{keyserverID.utf8(rt)}; return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [=, &innerRt]() { std::string error; crypto::EncryptedData result; try { this->notifsCryptoModule->initializeOutboundForSendingSession( keyserverIDCpp, std::vector( identityKeysCpp.begin(), identityKeysCpp.end()), std::vector(prekeyCpp.begin(), prekeyCpp.end()), std::vector( prekeySignatureCpp.begin(), prekeySignatureCpp.end()), std::vector( oneTimeKeyCpp.begin(), oneTimeKeyCpp.end())); result = this->notifsCryptoModule->encrypt( keyserverIDCpp, NotificationsCryptoModule::initialEncryptedMessageContent); std::shared_ptr keyserverNotificationsSession = this->notifsCryptoModule->getSessionByDeviceId(keyserverIDCpp); NotificationsCryptoModule::persistNotificationsSession( keyserverIDCpp, keyserverNotificationsSession); this->notifsCryptoModule->removeSessionByDeviceId(keyserverIDCpp); this->persistCryptoModules(false, true); } catch (const std::exception &e) { error = e.what(); } this->jsInvoker_->invokeAsync([=, &innerRt]() { if (error.size()) { promise->reject(error); return; } promise->resolve(jsi::String::createFromUtf8( innerRt, std::string{result.message.begin(), result.message.end()})); }); }; this->cryptoThread->scheduleTask(job); }); } jsi::Value CommCoreModule::isNotificationsSessionInitialized(jsi::Runtime &rt) { return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [=, &innerRt]() { std::string error; bool result; try { result = NotificationsCryptoModule::isNotificationsSessionInitialized( "Comm"); } catch (const std::exception &e) { error = e.what(); } this->jsInvoker_->invokeAsync([=, &innerRt]() { if (error.size()) { promise->reject(error); return; } promise->resolve(result); }); }; this->cryptoThread->scheduleTask(job); }); } jsi::Value CommCoreModule::updateKeyserverDataInNotifStorage( jsi::Runtime &rt, jsi::Array keyserversData) { std::vector> keyserversDataCpp; for (auto idx = 0; idx < keyserversData.size(rt); idx++) { auto data = keyserversData.getValueAtIndex(rt, idx).asObject(rt); std::string keyserverID = data.getProperty(rt, "id").asString(rt).utf8(rt); std::string keyserverUnreadCountKey = "KEYSERVER." + keyserverID + ".UNREAD_COUNT"; int unreadCount = data.getProperty(rt, "unreadCount").asNumber(); keyserversDataCpp.push_back({keyserverUnreadCountKey, unreadCount}); } return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { std::string error; try { for (const auto &keyserverData : keyserversDataCpp) { CommMMKV::setInt(keyserverData.first, keyserverData.second); } } catch (const std::exception &e) { error = e.what(); } this->jsInvoker_->invokeAsync([=, &innerRt]() { if (error.size()) { promise->reject(error); return; } promise->resolve(jsi::Value::undefined()); }); }); } jsi::Value CommCoreModule::removeKeyserverDataFromNotifStorage( jsi::Runtime &rt, jsi::Array keyserverIDsToDelete) { std::vector keyserverIDsToDeleteCpp{}; for (auto idx = 0; idx < keyserverIDsToDelete.size(rt); idx++) { std::string keyserverID = keyserverIDsToDelete.getValueAtIndex(rt, idx).asString(rt).utf8(rt); std::string keyserverUnreadCountKey = "KEYSERVER." + keyserverID + ".UNREAD_COUNT"; keyserverIDsToDeleteCpp.push_back(keyserverUnreadCountKey); } return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { std::string error; try { CommMMKV::removeKeys(keyserverIDsToDeleteCpp); } catch (const std::exception &e) { error = e.what(); } this->jsInvoker_->invokeAsync([=, &innerRt]() { if (error.size()) { promise->reject(error); return; } promise->resolve(jsi::Value::undefined()); }); }); } jsi::Value CommCoreModule::getKeyserverDataFromNotifStorage( jsi::Runtime &rt, jsi::Array keyserverIDs) { std::vector keyserverIDsCpp{}; for (auto idx = 0; idx < keyserverIDs.size(rt); idx++) { std::string keyserverID = keyserverIDs.getValueAtIndex(rt, idx).asString(rt).utf8(rt); keyserverIDsCpp.push_back(keyserverID); } return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { std::string error; std::vector> keyserversDataVector{}; try { for (const auto &keyserverID : keyserverIDsCpp) { std::string keyserverUnreadCountKey = "KEYSERVER." + keyserverID + ".UNREAD_COUNT"; std::optional unreadCount = CommMMKV::getInt(keyserverUnreadCountKey, -1); if (!unreadCount.has_value()) { continue; } keyserversDataVector.push_back({keyserverID, unreadCount.value()}); } } catch (const std::exception &e) { error = e.what(); } auto keyserversDataVectorPtr = std::make_shared>>( std::move(keyserversDataVector)); this->jsInvoker_->invokeAsync( [&innerRt, keyserversDataVectorPtr, error, promise]() { if (error.size()) { promise->reject(error); return; } size_t numKeyserversData = keyserversDataVectorPtr->size(); jsi::Array jsiKeyserversData = jsi::Array(innerRt, numKeyserversData); size_t writeIdx = 0; for (const auto &keyserverData : *keyserversDataVectorPtr) { jsi::Object jsiKeyserverData = jsi::Object(innerRt); jsiKeyserverData.setProperty( innerRt, "id", keyserverData.first); jsiKeyserverData.setProperty( innerRt, "unreadCount", keyserverData.second); jsiKeyserversData.setValueAtIndex( innerRt, writeIdx++, jsiKeyserverData); } promise->resolve(std::move(jsiKeyserversData)); }); }); } jsi::Value CommCoreModule::initializeContentOutboundSession( jsi::Runtime &rt, jsi::String identityKeys, jsi::String prekey, jsi::String prekeySignature, jsi::String oneTimeKey, jsi::String deviceID) { auto identityKeysCpp{identityKeys.utf8(rt)}; auto prekeyCpp{prekey.utf8(rt)}; auto prekeySignatureCpp{prekeySignature.utf8(rt)}; auto oneTimeKeyCpp{oneTimeKey.utf8(rt)}; auto deviceIDCpp{deviceID.utf8(rt)}; return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [=, &innerRt]() { std::string error; crypto::EncryptedData initialEncryptedData; int sessionVersion; try { sessionVersion = this->contentCryptoModule->initializeOutboundForSendingSession( deviceIDCpp, std::vector( identityKeysCpp.begin(), identityKeysCpp.end()), std::vector(prekeyCpp.begin(), prekeyCpp.end()), std::vector( prekeySignatureCpp.begin(), prekeySignatureCpp.end()), std::vector( oneTimeKeyCpp.begin(), oneTimeKeyCpp.end())); const std::string initMessage = "{\"type\": \"init\"}"; initialEncryptedData = contentCryptoModule->encrypt(deviceIDCpp, initMessage); this->persistCryptoModules(true, false); } catch (const std::exception &e) { error = e.what(); } this->jsInvoker_->invokeAsync([=, &innerRt]() { if (error.size()) { promise->reject(error); return; } auto initialEncryptedDataJSI = jsi::Object(innerRt); auto message = std::string{ initialEncryptedData.message.begin(), initialEncryptedData.message.end()}; auto messageJSI = jsi::String::createFromUtf8(innerRt, message); initialEncryptedDataJSI.setProperty(innerRt, "message", messageJSI); initialEncryptedDataJSI.setProperty( innerRt, "messageType", static_cast(initialEncryptedData.messageType)); auto outboundSessionCreationResultJSI = jsi::Object(innerRt); outboundSessionCreationResultJSI.setProperty( innerRt, "encryptedData", initialEncryptedDataJSI); outboundSessionCreationResultJSI.setProperty( innerRt, "sessionVersion", sessionVersion); promise->resolve(std::move(outboundSessionCreationResultJSI)); }); }; this->cryptoThread->scheduleTask(job); }); } jsi::Value CommCoreModule::initializeContentInboundSession( jsi::Runtime &rt, jsi::String identityKeys, jsi::Object encryptedDataJSI, jsi::String deviceID, double sessionVersion, bool overwrite) { auto identityKeysCpp{identityKeys.utf8(rt)}; size_t messageType = std::lround(encryptedDataJSI.getProperty(rt, "messageType").asNumber()); std::string encryptedMessageCpp = encryptedDataJSI.getProperty(rt, "message").asString(rt).utf8(rt); auto deviceIDCpp{deviceID.utf8(rt)}; return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [=, &innerRt]() { std::string error; std::string decryptedMessage; try { this->contentCryptoModule->initializeInboundForReceivingSession( deviceIDCpp, std::vector( encryptedMessageCpp.begin(), encryptedMessageCpp.end()), std::vector( identityKeysCpp.begin(), identityKeysCpp.end()), static_cast(sessionVersion), overwrite); crypto::EncryptedData encryptedData{ std::vector( encryptedMessageCpp.begin(), encryptedMessageCpp.end()), messageType}; decryptedMessage = this->contentCryptoModule->decrypt(deviceIDCpp, encryptedData); this->persistCryptoModules(true, false); } catch (const std::exception &e) { error = e.what(); } this->jsInvoker_->invokeAsync([=, &innerRt]() { if (error.size()) { promise->reject(error); return; } promise->resolve( jsi::String::createFromUtf8(innerRt, decryptedMessage)); }); }; this->cryptoThread->scheduleTask(job); }); } jsi::Value CommCoreModule::encrypt( jsi::Runtime &rt, jsi::String message, jsi::String deviceID) { auto messageCpp{message.utf8(rt)}; auto deviceIDCpp{deviceID.utf8(rt)}; return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [=, &innerRt]() { std::string error; crypto::EncryptedData encryptedMessage; try { encryptedMessage = contentCryptoModule->encrypt(deviceIDCpp, messageCpp); this->persistCryptoModules(true, false); } catch (const std::exception &e) { error = e.what(); } this->jsInvoker_->invokeAsync([=, &innerRt]() { if (error.size()) { promise->reject(error); return; } auto encryptedDataJSI = jsi::Object(innerRt); auto message = std::string{ encryptedMessage.message.begin(), encryptedMessage.message.end()}; auto messageJSI = jsi::String::createFromUtf8(innerRt, message); encryptedDataJSI.setProperty(innerRt, "message", messageJSI); encryptedDataJSI.setProperty( innerRt, "messageType", static_cast(encryptedMessage.messageType)); promise->resolve(std::move(encryptedDataJSI)); }); }; this->cryptoThread->scheduleTask(job); }); } jsi::Value CommCoreModule::decrypt( jsi::Runtime &rt, jsi::Object encryptedDataJSI, jsi::String deviceID) { size_t messageType = std::lround(encryptedDataJSI.getProperty(rt, "messageType").asNumber()); std::string message = encryptedDataJSI.getProperty(rt, "message").asString(rt).utf8(rt); auto deviceIDCpp{deviceID.utf8(rt)}; return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [=, &innerRt]() { std::string error; std::string decryptedMessage; try { crypto::EncryptedData encryptedData{ std::vector(message.begin(), message.end()), messageType}; decryptedMessage = this->contentCryptoModule->decrypt(deviceIDCpp, encryptedData); this->persistCryptoModules(true, false); } catch (const std::exception &e) { error = e.what(); } this->jsInvoker_->invokeAsync([=, &innerRt]() { if (error.size()) { promise->reject(error); return; } promise->resolve( jsi::String::createFromUtf8(innerRt, decryptedMessage)); }); }; this->cryptoThread->scheduleTask(job); }); } jsi::Value CommCoreModule::decryptSequentialAndPersist( jsi::Runtime &rt, jsi::Object encryptedDataJSI, jsi::String deviceID, jsi::String messageID) { size_t messageType = std::lround(encryptedDataJSI.getProperty(rt, "messageType").asNumber()); std::string message = encryptedDataJSI.getProperty(rt, "message").asString(rt).utf8(rt); auto deviceIDCpp{deviceID.utf8(rt)}; auto messageIDCpp{messageID.utf8(rt)}; return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [=, &innerRt]() { std::string error; std::string decryptedMessage; try { crypto::EncryptedData encryptedData{ std::vector(message.begin(), message.end()), messageType}; decryptedMessage = this->contentCryptoModule->decryptSequential( deviceIDCpp, encryptedData); std::string storedSecretKey = getAccountDataKey(secureStoreAccountDataKey); crypto::Persist newContentPersist = this->contentCryptoModule->storeAsB64(storedSecretKey); std::promise persistencePromise; std::future persistenceFuture = persistencePromise.get_future(); GlobalDBSingleton::instance.scheduleOrRunCancellable( [=, &persistencePromise]() { try { - ReceivedMessageToDevice message{ + InboundP2PMessage message{ messageIDCpp, deviceIDCpp, decryptedMessage, "decrypted"}; DatabaseManager::getQueryExecutor().beginTransaction(); - DatabaseManager::getQueryExecutor() - .addReceivedMessageToDevice(message); + DatabaseManager::getQueryExecutor().addInboundP2PMessage( + message); DatabaseManager::getQueryExecutor().storeOlmPersistData( DatabaseManager::getQueryExecutor() .getContentAccountID(), newContentPersist); DatabaseManager::getQueryExecutor().commitTransaction(); persistencePromise.set_value(); } catch (std::system_error &e) { DatabaseManager::getQueryExecutor().rollbackTransaction(); persistencePromise.set_exception( std::make_exception_ptr(e)); } }); persistenceFuture.get(); } catch (const std::exception &e) { error = e.what(); } this->jsInvoker_->invokeAsync([=, &innerRt]() { if (error.size()) { promise->reject(error); return; } promise->resolve( jsi::String::createFromUtf8(innerRt, decryptedMessage)); }); }; this->cryptoThread->scheduleTask(job); }); } jsi::Value CommCoreModule::signMessage(jsi::Runtime &rt, jsi::String message) { std::string messageStr = message.utf8(rt); return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [=, &innerRt]() { std::string error; std::string signature; try { signature = this->contentCryptoModule->signMessage(messageStr); } catch (const std::exception &e) { error = "signing message failed with: " + std::string(e.what()); } this->jsInvoker_->invokeAsync([=, &innerRt]() { if (error.size()) { promise->reject(error); return; } auto jsiSignature{jsi::String::createFromUtf8(innerRt, signature)}; promise->resolve(std::move(jsiSignature)); }); }; this->cryptoThread->scheduleTask(job); }); } jsi::Value CommCoreModule::verifySignature( jsi::Runtime &rt, jsi::String publicKey, jsi::String message, jsi::String signature) { std::string keyStr = publicKey.utf8(rt); std::string messageStr = message.utf8(rt); std::string signatureStr = signature.utf8(rt); return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [=, &innerRt]() { std::string error; try { crypto::CryptoModule::verifySignature( keyStr, messageStr, signatureStr); } catch (const std::exception &e) { error = "verifying signature failed with: " + std::string(e.what()); } this->jsInvoker_->invokeAsync([=, &innerRt]() { if (error.size()) { promise->reject(error); return; } promise->resolve(jsi::Value::undefined()); }); }; this->cryptoThread->scheduleTask(job); }); } CommCoreModule::CommCoreModule( std::shared_ptr jsInvoker) : facebook::react::CommCoreModuleSchemaCxxSpecJSI(jsInvoker), cryptoThread(std::make_unique("crypto")), draftStore(jsInvoker), threadStore(jsInvoker), messageStore(jsInvoker), reportStore(jsInvoker), userStore(jsInvoker), keyserverStore(jsInvoker), communityStore(jsInvoker), integrityStore(jsInvoker), syncedMetadataStore(jsInvoker), auxUserStore(jsInvoker), threadActivityStore(jsInvoker) { GlobalDBSingleton::instance.enableMultithreading(); } double CommCoreModule::getCodeVersion(jsi::Runtime &rt) { return this->codeVersion; } jsi::Value CommCoreModule::setNotifyToken(jsi::Runtime &rt, jsi::String token) { auto notifyToken{token.utf8(rt)}; return createPromiseAsJSIValue( rt, [this, notifyToken](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [this, notifyToken, promise]() { std::string error; try { DatabaseManager::getQueryExecutor().setNotifyToken(notifyToken); } catch (std::system_error &e) { error = e.what(); } this->jsInvoker_->invokeAsync([error, promise]() { if (error.size()) { promise->reject(error); } else { promise->resolve(jsi::Value::undefined()); } }); }; GlobalDBSingleton::instance.scheduleOrRunCancellable( job, promise, this->jsInvoker_); }); } jsi::Value CommCoreModule::clearNotifyToken(jsi::Runtime &rt) { return createPromiseAsJSIValue( rt, [this](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [this, promise]() { std::string error; try { DatabaseManager::getQueryExecutor().clearNotifyToken(); } catch (std::system_error &e) { error = e.what(); } this->jsInvoker_->invokeAsync([error, promise]() { if (error.size()) { promise->reject(error); } else { promise->resolve(jsi::Value::undefined()); } }); }; GlobalDBSingleton::instance.scheduleOrRunCancellable( job, promise, this->jsInvoker_); }); }; jsi::Value CommCoreModule::stampSQLiteDBUserID(jsi::Runtime &rt, jsi::String userID) { auto currentUserID{userID.utf8(rt)}; return createPromiseAsJSIValue( rt, [this, currentUserID](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [this, promise, currentUserID]() { std::string error; try { DatabaseManager::getQueryExecutor().stampSQLiteDBUserID( currentUserID); } catch (const std::exception &e) { error = e.what(); } this->jsInvoker_->invokeAsync([error, promise]() { if (error.size()) { promise->reject(error); } else { promise->resolve(jsi::Value::undefined()); } }); }; GlobalDBSingleton::instance.scheduleOrRunCancellable( job, promise, this->jsInvoker_); }); } jsi::Value CommCoreModule::getSQLiteStampedUserID(jsi::Runtime &rt) { return createPromiseAsJSIValue( rt, [this](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [this, &innerRt, promise]() { std::string error; std::string result; try { result = DatabaseManager::getQueryExecutor().getSQLiteStampedUserID(); } catch (const std::exception &e) { error = e.what(); } this->jsInvoker_->invokeAsync([&innerRt, error, result, promise]() { if (error.size()) { promise->reject(error); } else { promise->resolve(jsi::String::createFromUtf8(innerRt, result)); } }); }; GlobalDBSingleton::instance.scheduleOrRunCancellable( job, promise, this->jsInvoker_); }); } jsi::Value CommCoreModule::clearSensitiveData(jsi::Runtime &rt) { return createPromiseAsJSIValue( rt, [this](jsi::Runtime &innerRt, std::shared_ptr promise) { GlobalDBSingleton::instance.setTasksCancelled(true); taskType job = [this, promise]() { std::string error; try { this->innerClearCommServicesAuthMetadata(); DatabaseManager::clearSensitiveData(); } catch (const std::exception &e) { error = e.what(); } this->jsInvoker_->invokeAsync([error, promise]() { if (error.size()) { promise->reject(error); } else { promise->resolve(jsi::Value::undefined()); } }); GlobalDBSingleton::instance.scheduleOrRun( []() { GlobalDBSingleton::instance.setTasksCancelled(false); }); }; GlobalDBSingleton::instance.scheduleOrRun(job); }); } bool CommCoreModule::checkIfDatabaseNeedsDeletion(jsi::Runtime &rt) { return DatabaseManager::checkIfDatabaseNeedsDeletion(); } void CommCoreModule::reportDBOperationsFailure(jsi::Runtime &rt) { DatabaseManager::reportDBOperationsFailure(); } jsi::Value CommCoreModule::computeBackupKey( jsi::Runtime &rt, jsi::String password, jsi::String backupID) { std::string passwordStr = password.utf8(rt); std::string backupIDStr = backupID.utf8(rt); return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [=, &innerRt]() { std::string error; std::array<::std::uint8_t, 32> backupKey; try { backupKey = compute_backup_key(passwordStr, backupIDStr); } catch (const std::exception &e) { error = std::string{"Failed to compute backup key: "} + e.what(); } this->jsInvoker_->invokeAsync([=, &innerRt]() { if (error.size()) { promise->reject(error); return; } auto size = backupKey.size(); auto arrayBuffer = innerRt.global() .getPropertyAsFunction(innerRt, "ArrayBuffer") .callAsConstructor(innerRt, {static_cast(size)}) .asObject(innerRt) .getArrayBuffer(innerRt); auto bufferPtr = arrayBuffer.data(innerRt); memcpy(bufferPtr, backupKey.data(), size); promise->resolve(std::move(arrayBuffer)); }); }; this->cryptoThread->scheduleTask(job); }); } jsi::Value CommCoreModule::generateRandomString(jsi::Runtime &rt, double size) { return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [=, &innerRt]() { std::string error; std::string randomString; try { randomString = crypto::Tools::generateRandomString(static_cast(size)); } catch (const std::exception &e) { error = "Failed to generate random string for size " + std::to_string(size) + ": " + e.what(); } this->jsInvoker_->invokeAsync( [&innerRt, error, randomString, promise]() { if (error.size()) { promise->reject(error); } else { jsi::String jsiRandomString = jsi::String::createFromUtf8(innerRt, randomString); promise->resolve(std::move(jsiRandomString)); } }); }; this->cryptoThread->scheduleTask(job); }); } jsi::Value CommCoreModule::setCommServicesAuthMetadata( jsi::Runtime &rt, jsi::String userID, jsi::String deviceID, jsi::String accessToken) { auto userIDStr{userID.utf8(rt)}; auto deviceIDStr{deviceID.utf8(rt)}; auto accessTokenStr{accessToken.utf8(rt)}; return createPromiseAsJSIValue( rt, [this, userIDStr, deviceIDStr, accessTokenStr]( jsi::Runtime &innerRt, std::shared_ptr promise) { std::string error; try { this->innerSetCommServicesAuthMetadata( userIDStr, deviceIDStr, accessTokenStr); } catch (const std::exception &e) { error = e.what(); } this->jsInvoker_->invokeAsync([error, promise]() { if (error.size()) { promise->reject(error); } else { promise->resolve(jsi::Value::undefined()); } }); }); } void CommCoreModule::innerSetCommServicesAuthMetadata( std::string userID, std::string deviceID, std::string accessToken) { CommSecureStore::set(CommSecureStore::userID, userID); CommSecureStore::set(CommSecureStore::deviceID, deviceID); CommSecureStore::set(CommSecureStore::commServicesAccessToken, accessToken); CommServicesAuthMetadataEmitter::sendAuthMetadataToJS(accessToken, userID); } jsi::Value CommCoreModule::getCommServicesAuthMetadata(jsi::Runtime &rt) { return createPromiseAsJSIValue( rt, [this](jsi::Runtime &innerRt, std::shared_ptr promise) { std::string error; std::string userID; std::string deviceID; std::string accessToken; try { folly::Optional userIDOpt = CommSecureStore::get(CommSecureStore::userID); if (userIDOpt.hasValue()) { userID = userIDOpt.value(); } folly::Optional deviceIDOpt = CommSecureStore::get(CommSecureStore::deviceID); if (deviceIDOpt.hasValue()) { deviceID = deviceIDOpt.value(); } folly::Optional accessTokenOpt = CommSecureStore::get(CommSecureStore::commServicesAccessToken); if (accessTokenOpt.hasValue()) { accessToken = accessTokenOpt.value(); } } catch (const std::exception &e) { error = e.what(); } this->jsInvoker_->invokeAsync( [&innerRt, error, userID, deviceID, accessToken, promise]() { if (error.size()) { promise->reject(error); } else { auto authMetadata = jsi::Object(innerRt); if (!userID.empty()) { authMetadata.setProperty( innerRt, "userID", jsi::String::createFromUtf8(innerRt, userID)); } if (!deviceID.empty()) { authMetadata.setProperty( innerRt, "deviceID", jsi::String::createFromUtf8(innerRt, deviceID)); } if (!accessToken.empty()) { authMetadata.setProperty( innerRt, "accessToken", jsi::String::createFromUtf8(innerRt, accessToken)); } promise->resolve(std::move(authMetadata)); } }); }); } jsi::Value CommCoreModule::clearCommServicesAuthMetadata(jsi::Runtime &rt) { return this->setCommServicesAuthMetadata( rt, jsi::String::createFromUtf8(rt, ""), jsi::String::createFromUtf8(rt, ""), jsi::String::createFromUtf8(rt, "")); } void CommCoreModule::innerClearCommServicesAuthMetadata() { return this->innerSetCommServicesAuthMetadata("", "", ""); } jsi::Value CommCoreModule::setCommServicesAccessToken( jsi::Runtime &rt, jsi::String accessToken) { auto accessTokenStr{accessToken.utf8(rt)}; return createPromiseAsJSIValue( rt, [this, accessTokenStr]( jsi::Runtime &innerRt, std::shared_ptr promise) { std::string error; try { CommSecureStore::set( CommSecureStore::commServicesAccessToken, accessTokenStr); } catch (const std::exception &e) { error = e.what(); } this->jsInvoker_->invokeAsync([error, promise]() { if (error.size()) { promise->reject(error); } else { promise->resolve(jsi::Value::undefined()); } }); }); } jsi::Value CommCoreModule::clearCommServicesAccessToken(jsi::Runtime &rt) { return createPromiseAsJSIValue( rt, [this](jsi::Runtime &innerRt, std::shared_ptr promise) { std::string error; try { CommSecureStore::set(CommSecureStore::commServicesAccessToken, ""); } catch (const std::exception &e) { error = e.what(); } this->jsInvoker_->invokeAsync([error, promise]() { if (error.size()) { promise->reject(error); } else { promise->resolve(jsi::Value::undefined()); } }); }); } void CommCoreModule::startBackupHandler(jsi::Runtime &rt) { try { ::startBackupHandler(); } catch (const std::exception &e) { throw jsi::JSError(rt, e.what()); } } void CommCoreModule::stopBackupHandler(jsi::Runtime &rt) { try { ::stopBackupHandler(); } catch (const std::exception &e) { throw jsi::JSError(rt, e.what()); } } jsi::Value CommCoreModule::createNewBackupInternal( jsi::Runtime &rt, std::string backupSecret, std::string backupMessage) { return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { this->cryptoThread->scheduleTask([=, &innerRt]() { std::string error; std::string backupID; try { backupID = crypto::Tools::generateRandomString(32); } catch (const std::exception &e) { error = "Failed to generate backupID"; } std::string pickleKey; std::string pickledAccount; if (!error.size()) { try { pickleKey = crypto::Tools::generateRandomString(64); crypto::Persist persist = this->contentCryptoModule->storeAsB64(pickleKey); pickledAccount = std::string(persist.account.begin(), persist.account.end()); } catch (const std::exception &e) { error = "Failed to pickle crypto account"; } } if (!error.size()) { auto currentID = RustPromiseManager::instance.addPromise( {promise, this->jsInvoker_, innerRt}); ::createBackup( rust::string(backupID), rust::string(backupSecret), rust::string(pickleKey), rust::string(pickledAccount), rust::string(backupMessage), currentID); } else { this->jsInvoker_->invokeAsync( [=, &innerRt]() { promise->reject(error); }); } }); }); } jsi::Value CommCoreModule::createNewBackup(jsi::Runtime &rt, jsi::String backupSecret) { std::string backupSecretStr = backupSecret.utf8(rt); return createNewBackupInternal(rt, backupSecretStr, ""); } jsi::Value CommCoreModule::createNewSIWEBackup( jsi::Runtime &rt, jsi::String backupSecret, jsi::String siweBackupMsg) { std::string backupSecretStr = backupSecret.utf8(rt); std::string siweBackupMsgStr = siweBackupMsg.utf8(rt); return createNewBackupInternal(rt, backupSecretStr, siweBackupMsgStr); } jsi::Value CommCoreModule::restoreBackup(jsi::Runtime &rt, jsi::String backupSecret) { std::string backupSecretStr = backupSecret.utf8(rt); return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { auto currentID = RustPromiseManager::instance.addPromise( {promise, this->jsInvoker_, innerRt}); ::restoreBackup(rust::string(backupSecretStr), currentID); }); } jsi::Value CommCoreModule::restoreBackupData( jsi::Runtime &rt, jsi::String backupID, jsi::String backupDataKey, jsi::String backupLogDataKey) { std::string backupIDStr = backupID.utf8(rt); std::string backupDataKeyStr = backupDataKey.utf8(rt); std::string backupLogDataKeyStr = backupLogDataKey.utf8(rt); return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { auto currentID = RustPromiseManager::instance.addPromise( {promise, this->jsInvoker_, innerRt}); ::restoreBackupData( rust::string(backupIDStr), rust::string(backupDataKeyStr), rust::string(backupLogDataKeyStr), currentID); }); } jsi::Value CommCoreModule::retrieveBackupKeys(jsi::Runtime &rt, jsi::String backupSecret) { std::string backupSecretStr = backupSecret.utf8(rt); return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { auto currentID = RustPromiseManager::instance.addPromise( {promise, this->jsInvoker_, innerRt}); ::retrieveBackupKeys(rust::string(backupSecretStr), currentID); }); } jsi::Value CommCoreModule::retrieveLatestSIWEBackupData(jsi::Runtime &rt) { return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { auto currentID = RustPromiseManager::instance.addPromise( {promise, this->jsInvoker_, innerRt}); ::retrieveLatestSIWEBackupData(currentID); }); } jsi::Value CommCoreModule::setSIWEBackupSecrets( jsi::Runtime &rt, jsi::Object siweBackupSecrets) { std::string message = siweBackupSecrets.getProperty(rt, "message").asString(rt).utf8(rt); std::string signature = siweBackupSecrets.getProperty(rt, "signature").asString(rt).utf8(rt); folly::dynamic backupSecretsJSON = folly::dynamic::object("message", message)("signature", signature); std::string backupSecrets = folly::toJson(backupSecretsJSON); return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [this, promise, backupSecrets]() { std::string error; try { DatabaseManager::getQueryExecutor().setMetadata( "siweBackupSecrets", backupSecrets); } catch (const std::exception &e) { error = e.what(); } this->jsInvoker_->invokeAsync([error, promise]() { if (error.size()) { promise->reject(error); } else { promise->resolve(jsi::Value::undefined()); } }); }; GlobalDBSingleton::instance.scheduleOrRunCancellable( job, promise, this->jsInvoker_); }); } jsi::Value CommCoreModule::getSIWEBackupSecrets(jsi::Runtime &rt) { return createPromiseAsJSIValue( rt, [this](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [this, &innerRt, promise]() { std::string error; std::string backupSecrets; try { backupSecrets = DatabaseManager::getQueryExecutor().getMetadata( "siweBackupSecrets"); } catch (const std::exception &e) { error = e.what(); } this->jsInvoker_->invokeAsync( [&innerRt, error, backupSecrets, promise]() { if (error.size()) { promise->reject(error); } else if (!backupSecrets.size()) { promise->resolve(jsi::Value::undefined()); } else { folly::dynamic backupSecretsJSON = folly::parseJson(backupSecrets); std::string message = backupSecretsJSON["message"].asString(); std::string signature = backupSecretsJSON["signature"].asString(); auto siweBackupSecrets = jsi::Object(innerRt); siweBackupSecrets.setProperty( innerRt, "message", jsi::String::createFromUtf8(innerRt, message)); siweBackupSecrets.setProperty( innerRt, "signature", jsi::String::createFromUtf8(innerRt, signature)); promise->resolve(std::move(siweBackupSecrets)); } }); }; GlobalDBSingleton::instance.scheduleOrRunCancellable( job, promise, this->jsInvoker_); }); } -jsi::Value CommCoreModule::getAllReceivedMessageToDevice(jsi::Runtime &rt) { +jsi::Value CommCoreModule::getAllInboundP2PMessage(jsi::Runtime &rt) { return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [=, &innerRt]() { std::string error; - std::vector messages; + std::vector messages; try { - messages = DatabaseManager::getQueryExecutor() - .getAllReceivedMessageToDevice(); + messages = + DatabaseManager::getQueryExecutor().getAllInboundP2PMessage(); } catch (std::system_error &e) { error = e.what(); } - auto messagesPtr = - std::make_shared>( - std::move(messages)); + auto messagesPtr = std::make_shared>( + std::move(messages)); this->jsInvoker_->invokeAsync( [&innerRt, messagesPtr, error, promise]() { if (error.size()) { promise->reject(error); return; } jsi::Array jsiMessages = jsi::Array(innerRt, messagesPtr->size()); size_t writeIdx = 0; - for (const ReceivedMessageToDevice &msg : *messagesPtr) { + for (const InboundP2PMessage &msg : *messagesPtr) { jsi::Object jsiMsg = jsi::Object(innerRt); jsiMsg.setProperty(innerRt, "messageID", msg.message_id); jsiMsg.setProperty( innerRt, "senderDeviceID", msg.sender_device_id); jsiMsg.setProperty(innerRt, "plaintext", msg.plaintext); jsiMsg.setProperty(innerRt, "status", msg.status); jsiMessages.setValueAtIndex(innerRt, writeIdx++, jsiMsg); } promise->resolve(std::move(jsiMessages)); }); }; GlobalDBSingleton::instance.scheduleOrRunCancellable( job, promise, this->jsInvoker_); }); } -jsi::Value CommCoreModule::removeReceivedMessagesToDevice( - jsi::Runtime &rt, - jsi::Array ids) { +jsi::Value +CommCoreModule::removeInboundP2PMessages(jsi::Runtime &rt, jsi::Array ids) { std::vector msgIDsCPP{}; for (auto idx = 0; idx < ids.size(rt); idx++) { std::string msgID = ids.getValueAtIndex(rt, idx).asString(rt).utf8(rt); msgIDsCPP.push_back(msgID); } return createPromiseAsJSIValue( rt, [this, msgIDsCPP](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [this, promise, msgIDsCPP]() { std::string error; try { - DatabaseManager::getQueryExecutor().removeReceivedMessagesToDevice( + DatabaseManager::getQueryExecutor().removeInboundP2PMessages( msgIDsCPP); } catch (std::system_error &e) { error = e.what(); } this->jsInvoker_->invokeAsync([error, promise]() { if (error.size()) { promise->reject(error); } else { promise->resolve(jsi::Value::undefined()); } }); }; GlobalDBSingleton::instance.scheduleOrRunCancellable( job, promise, this->jsInvoker_); }); } } // namespace comm diff --git a/native/cpp/CommonCpp/NativeModules/CommCoreModule.h b/native/cpp/CommonCpp/NativeModules/CommCoreModule.h index 17c736b46..7f7bf9203 100644 --- a/native/cpp/CommonCpp/NativeModules/CommCoreModule.h +++ b/native/cpp/CommonCpp/NativeModules/CommCoreModule.h @@ -1,207 +1,207 @@ #pragma once #include "../CryptoTools/CryptoModule.h" #include "../Tools/CommMMKV.h" #include "../Tools/CommSecureStore.h" #include "../Tools/WorkerThread.h" #include "../_generated/commJSI.h" #include "PersistentStorageUtilities/DataStores/AuxUserStore.h" #include "PersistentStorageUtilities/DataStores/CommunityStore.h" #include "PersistentStorageUtilities/DataStores/DraftStore.h" #include "PersistentStorageUtilities/DataStores/IntegrityStore.h" #include "PersistentStorageUtilities/DataStores/KeyserverStore.h" #include "PersistentStorageUtilities/DataStores/MessageStore.h" #include "PersistentStorageUtilities/DataStores/ReportStore.h" #include "PersistentStorageUtilities/DataStores/SyncedMetadataStore.h" #include "PersistentStorageUtilities/DataStores/ThreadActivityStore.h" #include "PersistentStorageUtilities/DataStores/ThreadStore.h" #include "PersistentStorageUtilities/DataStores/UserStore.h" #include #include #include #include namespace comm { namespace jsi = facebook::jsi; class CommCoreModule : public facebook::react::CommCoreModuleSchemaCxxSpecJSI { const int codeVersion{344}; std::unique_ptr cryptoThread; const std::string secureStoreAccountDataKey = "cryptoAccountDataKey"; const std::string publicCryptoAccountID = "publicCryptoAccountID"; std::unique_ptr contentCryptoModule; const std::string notifsCryptoAccountID = "notifsCryptoAccountID"; std::unique_ptr notifsCryptoModule; DraftStore draftStore; ThreadStore threadStore; MessageStore messageStore; ReportStore reportStore; UserStore userStore; KeyserverStore keyserverStore; CommunityStore communityStore; IntegrityStore integrityStore; SyncedMetadataStore syncedMetadataStore; AuxUserStore auxUserStore; ThreadActivityStore threadActivityStore; void persistCryptoModules(bool persistContentModule, bool persistNotifsModule); jsi::Value createNewBackupInternal( jsi::Runtime &rt, std::string backupSecret, std::string backupMessage); virtual jsi::Value getDraft(jsi::Runtime &rt, jsi::String key) override; virtual jsi::Value updateDraft(jsi::Runtime &rt, jsi::String key, jsi::String text) override; virtual jsi::Value moveDraft(jsi::Runtime &rt, jsi::String oldKey, jsi::String newKey) override; virtual jsi::Value getClientDBStore(jsi::Runtime &rt) override; virtual jsi::Value removeAllDrafts(jsi::Runtime &rt) override; virtual jsi::Array getAllMessagesSync(jsi::Runtime &rt) override; virtual void processReportStoreOperationsSync( jsi::Runtime &rt, jsi::Array operations) override; virtual void processMessageStoreOperationsSync( jsi::Runtime &rt, jsi::Array operations) override; virtual jsi::Array getAllThreadsSync(jsi::Runtime &rt) override; virtual void processThreadStoreOperationsSync( jsi::Runtime &rt, jsi::Array operations) override; virtual jsi::Value processDBStoreOperations(jsi::Runtime &rt, jsi::Object operations) override; template void appendDBStoreOps( jsi::Runtime &rt, jsi::Object &operations, const char *key, T &store, std::shared_ptr>> &destination); virtual jsi::Value initializeCryptoAccount(jsi::Runtime &rt) override; virtual jsi::Value getUserPublicKey(jsi::Runtime &rt) override; virtual jsi::Value getOneTimeKeys(jsi::Runtime &rt, double oneTimeKeysAmount) override; virtual jsi::Value validateAndUploadPrekeys( jsi::Runtime &rt, jsi::String authUserID, jsi::String authDeviceID, jsi::String authAccessToken) override; virtual jsi::Value validateAndGetPrekeys(jsi::Runtime &rt) override; virtual jsi::Value initializeNotificationsSession( jsi::Runtime &rt, jsi::String identityKeys, jsi::String prekey, jsi::String prekeySignature, jsi::String oneTimeKeys, jsi::String keyserverID) override; virtual jsi::Value isNotificationsSessionInitialized(jsi::Runtime &rt) override; virtual jsi::Value updateKeyserverDataInNotifStorage( jsi::Runtime &rt, jsi::Array keyserversData) override; virtual jsi::Value removeKeyserverDataFromNotifStorage( jsi::Runtime &rt, jsi::Array keyserverIDsToDelete) override; virtual jsi::Value getKeyserverDataFromNotifStorage( jsi::Runtime &rt, jsi::Array keyserverIDs) override; virtual jsi::Value initializeContentOutboundSession( jsi::Runtime &rt, jsi::String identityKeys, jsi::String prekey, jsi::String prekeySignature, jsi::String oneTimeKeys, jsi::String deviceID) override; virtual jsi::Value initializeContentInboundSession( jsi::Runtime &rt, jsi::String identityKeys, jsi::Object encryptedDataJSI, jsi::String deviceID, double sessionVersion, bool overwrite) override; virtual jsi::Value encrypt(jsi::Runtime &rt, jsi::String message, jsi::String deviceID) override; virtual jsi::Value decrypt( jsi::Runtime &rt, jsi::Object encryptedDataJSI, jsi::String deviceID) override; virtual jsi::Value decryptSequentialAndPersist( jsi::Runtime &rt, jsi::Object encryptedDataJSI, jsi::String deviceID, jsi::String messageID) override; virtual jsi::Value signMessage(jsi::Runtime &rt, jsi::String message) override; virtual jsi::Value verifySignature( jsi::Runtime &rt, jsi::String publicKey, jsi::String message, jsi::String signature) override; virtual void terminate(jsi::Runtime &rt) override; virtual double getCodeVersion(jsi::Runtime &rt) override; virtual jsi::Value setNotifyToken(jsi::Runtime &rt, jsi::String token) override; virtual jsi::Value clearNotifyToken(jsi::Runtime &rt) override; virtual jsi::Value stampSQLiteDBUserID(jsi::Runtime &rt, jsi::String userID) override; virtual jsi::Value getSQLiteStampedUserID(jsi::Runtime &rt) override; virtual jsi::Value clearSensitiveData(jsi::Runtime &rt) override; virtual bool checkIfDatabaseNeedsDeletion(jsi::Runtime &rt) override; virtual void reportDBOperationsFailure(jsi::Runtime &rt) override; virtual jsi::Value computeBackupKey( jsi::Runtime &rt, jsi::String password, jsi::String backupID) override; virtual jsi::Value generateRandomString(jsi::Runtime &rt, double size) override; virtual jsi::Value setCommServicesAuthMetadata( jsi::Runtime &rt, jsi::String userID, jsi::String deviceID, jsi::String accessToken) override; virtual void innerSetCommServicesAuthMetadata( std::string userID, std::string deviceID, std::string accessToken); virtual jsi::Value getCommServicesAuthMetadata(jsi::Runtime &rt) override; virtual jsi::Value clearCommServicesAuthMetadata(jsi::Runtime &rt) override; virtual void innerClearCommServicesAuthMetadata(); virtual jsi::Value setCommServicesAccessToken( jsi::Runtime &rt, jsi::String accessToken) override; virtual jsi::Value clearCommServicesAccessToken(jsi::Runtime &rt) override; virtual void startBackupHandler(jsi::Runtime &rt) override; virtual void stopBackupHandler(jsi::Runtime &rt) override; virtual jsi::Value createNewBackup(jsi::Runtime &rt, jsi::String backupSecret) override; virtual jsi::Value createNewSIWEBackup( jsi::Runtime &rt, jsi::String backupSecret, jsi::String siweBackupMsg) override; virtual jsi::Value restoreBackup(jsi::Runtime &rt, jsi::String backupSecret) override; virtual jsi::Value restoreBackupData( jsi::Runtime &rt, jsi::String backupID, jsi::String backupDataKey, jsi::String backupLogDataKey) override; virtual jsi::Value retrieveBackupKeys(jsi::Runtime &rt, jsi::String backupSecret) override; virtual jsi::Value setSIWEBackupSecrets( jsi::Runtime &rt, jsi::Object siweBackupSecrets) override; virtual jsi::Value retrieveLatestSIWEBackupData(jsi::Runtime &rt) override; virtual jsi::Value getSIWEBackupSecrets(jsi::Runtime &rt) override; - virtual jsi::Value getAllReceivedMessageToDevice(jsi::Runtime &rt) override; + virtual jsi::Value getAllInboundP2PMessage(jsi::Runtime &rt) override; virtual jsi::Value - removeReceivedMessagesToDevice(jsi::Runtime &rt, jsi::Array ids) override; + removeInboundP2PMessages(jsi::Runtime &rt, jsi::Array ids) override; public: CommCoreModule(std::shared_ptr jsInvoker); }; } // namespace comm diff --git a/native/cpp/CommonCpp/_generated/commJSI-generated.cpp b/native/cpp/CommonCpp/_generated/commJSI-generated.cpp index c5fa15101..c341d7210 100644 --- a/native/cpp/CommonCpp/_generated/commJSI-generated.cpp +++ b/native/cpp/CommonCpp/_generated/commJSI-generated.cpp @@ -1,253 +1,253 @@ /** * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). * * Do not edit this file as changes may cause incorrect behavior and will be lost * once the code is regenerated. * * @generated by codegen project: GenerateModuleH.js */ #include "commJSI.h" namespace facebook { namespace react { static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getDraft(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->getDraft(rt, args[0].asString(rt)); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_updateDraft(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->updateDraft(rt, args[0].asString(rt), args[1].asString(rt)); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_moveDraft(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->moveDraft(rt, args[0].asString(rt), args[1].asString(rt)); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getClientDBStore(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->getClientDBStore(rt); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_removeAllDrafts(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->removeAllDrafts(rt); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getAllMessagesSync(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->getAllMessagesSync(rt); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_processMessageStoreOperationsSync(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { static_cast(&turboModule)->processMessageStoreOperationsSync(rt, args[0].asObject(rt).asArray(rt)); return jsi::Value::undefined(); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getAllThreadsSync(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->getAllThreadsSync(rt); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_processReportStoreOperationsSync(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { static_cast(&turboModule)->processReportStoreOperationsSync(rt, args[0].asObject(rt).asArray(rt)); return jsi::Value::undefined(); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_processThreadStoreOperationsSync(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { static_cast(&turboModule)->processThreadStoreOperationsSync(rt, args[0].asObject(rt).asArray(rt)); return jsi::Value::undefined(); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_processDBStoreOperations(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->processDBStoreOperations(rt, args[0].asObject(rt)); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_initializeCryptoAccount(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->initializeCryptoAccount(rt); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getUserPublicKey(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->getUserPublicKey(rt); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getOneTimeKeys(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->getOneTimeKeys(rt, args[0].asNumber()); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_validateAndGetPrekeys(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->validateAndGetPrekeys(rt); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_validateAndUploadPrekeys(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->validateAndUploadPrekeys(rt, args[0].asString(rt), args[1].asString(rt), args[2].asString(rt)); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_initializeNotificationsSession(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->initializeNotificationsSession(rt, args[0].asString(rt), args[1].asString(rt), args[2].asString(rt), args[3].asString(rt), args[4].asString(rt)); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_isNotificationsSessionInitialized(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->isNotificationsSessionInitialized(rt); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_updateKeyserverDataInNotifStorage(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->updateKeyserverDataInNotifStorage(rt, args[0].asObject(rt).asArray(rt)); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_removeKeyserverDataFromNotifStorage(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->removeKeyserverDataFromNotifStorage(rt, args[0].asObject(rt).asArray(rt)); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getKeyserverDataFromNotifStorage(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->getKeyserverDataFromNotifStorage(rt, args[0].asObject(rt).asArray(rt)); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_initializeContentOutboundSession(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->initializeContentOutboundSession(rt, args[0].asString(rt), args[1].asString(rt), args[2].asString(rt), args[3].asString(rt), args[4].asString(rt)); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_initializeContentInboundSession(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->initializeContentInboundSession(rt, args[0].asString(rt), args[1].asObject(rt), args[2].asString(rt), args[3].asNumber(), args[4].asBool()); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_encrypt(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->encrypt(rt, args[0].asString(rt), args[1].asString(rt)); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_decrypt(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->decrypt(rt, args[0].asObject(rt), args[1].asString(rt)); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_decryptSequentialAndPersist(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->decryptSequentialAndPersist(rt, args[0].asObject(rt), args[1].asString(rt), args[2].asString(rt)); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_signMessage(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->signMessage(rt, args[0].asString(rt)); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_verifySignature(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->verifySignature(rt, args[0].asString(rt), args[1].asString(rt), args[2].asString(rt)); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getCodeVersion(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->getCodeVersion(rt); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_terminate(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { static_cast(&turboModule)->terminate(rt); return jsi::Value::undefined(); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_setNotifyToken(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->setNotifyToken(rt, args[0].asString(rt)); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_clearNotifyToken(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->clearNotifyToken(rt); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_stampSQLiteDBUserID(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->stampSQLiteDBUserID(rt, args[0].asString(rt)); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getSQLiteStampedUserID(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->getSQLiteStampedUserID(rt); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_clearSensitiveData(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->clearSensitiveData(rt); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_checkIfDatabaseNeedsDeletion(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->checkIfDatabaseNeedsDeletion(rt); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_reportDBOperationsFailure(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { static_cast(&turboModule)->reportDBOperationsFailure(rt); return jsi::Value::undefined(); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_computeBackupKey(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->computeBackupKey(rt, args[0].asString(rt), args[1].asString(rt)); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_generateRandomString(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->generateRandomString(rt, args[0].asNumber()); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_setCommServicesAuthMetadata(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->setCommServicesAuthMetadata(rt, args[0].asString(rt), args[1].asString(rt), args[2].asString(rt)); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getCommServicesAuthMetadata(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->getCommServicesAuthMetadata(rt); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_clearCommServicesAuthMetadata(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->clearCommServicesAuthMetadata(rt); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_setCommServicesAccessToken(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->setCommServicesAccessToken(rt, args[0].asString(rt)); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_clearCommServicesAccessToken(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->clearCommServicesAccessToken(rt); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_startBackupHandler(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { static_cast(&turboModule)->startBackupHandler(rt); return jsi::Value::undefined(); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_stopBackupHandler(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { static_cast(&turboModule)->stopBackupHandler(rt); return jsi::Value::undefined(); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_createNewBackup(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->createNewBackup(rt, args[0].asString(rt)); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_createNewSIWEBackup(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->createNewSIWEBackup(rt, args[0].asString(rt), args[1].asString(rt)); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_restoreBackup(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->restoreBackup(rt, args[0].asString(rt)); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_restoreBackupData(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->restoreBackupData(rt, args[0].asString(rt), args[1].asString(rt), args[2].asString(rt)); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_retrieveBackupKeys(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->retrieveBackupKeys(rt, args[0].asString(rt)); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_retrieveLatestSIWEBackupData(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->retrieveLatestSIWEBackupData(rt); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_setSIWEBackupSecrets(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->setSIWEBackupSecrets(rt, args[0].asObject(rt)); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getSIWEBackupSecrets(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->getSIWEBackupSecrets(rt); } -static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getAllReceivedMessageToDevice(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { - return static_cast(&turboModule)->getAllReceivedMessageToDevice(rt); +static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getAllInboundP2PMessage(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { + return static_cast(&turboModule)->getAllInboundP2PMessage(rt); } -static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_removeReceivedMessagesToDevice(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { - return static_cast(&turboModule)->removeReceivedMessagesToDevice(rt, args[0].asObject(rt).asArray(rt)); +static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_removeInboundP2PMessages(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { + return static_cast(&turboModule)->removeInboundP2PMessages(rt, args[0].asObject(rt).asArray(rt)); } CommCoreModuleSchemaCxxSpecJSI::CommCoreModuleSchemaCxxSpecJSI(std::shared_ptr jsInvoker) : TurboModule("CommTurboModule", jsInvoker) { methodMap_["getDraft"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getDraft}; methodMap_["updateDraft"] = MethodMetadata {2, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_updateDraft}; methodMap_["moveDraft"] = MethodMetadata {2, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_moveDraft}; methodMap_["getClientDBStore"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getClientDBStore}; methodMap_["removeAllDrafts"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_removeAllDrafts}; methodMap_["getAllMessagesSync"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getAllMessagesSync}; methodMap_["processMessageStoreOperationsSync"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_processMessageStoreOperationsSync}; methodMap_["getAllThreadsSync"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getAllThreadsSync}; methodMap_["processReportStoreOperationsSync"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_processReportStoreOperationsSync}; methodMap_["processThreadStoreOperationsSync"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_processThreadStoreOperationsSync}; methodMap_["processDBStoreOperations"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_processDBStoreOperations}; methodMap_["initializeCryptoAccount"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_initializeCryptoAccount}; methodMap_["getUserPublicKey"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getUserPublicKey}; methodMap_["getOneTimeKeys"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getOneTimeKeys}; methodMap_["validateAndGetPrekeys"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_validateAndGetPrekeys}; methodMap_["validateAndUploadPrekeys"] = MethodMetadata {3, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_validateAndUploadPrekeys}; methodMap_["initializeNotificationsSession"] = MethodMetadata {5, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_initializeNotificationsSession}; methodMap_["isNotificationsSessionInitialized"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_isNotificationsSessionInitialized}; methodMap_["updateKeyserverDataInNotifStorage"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_updateKeyserverDataInNotifStorage}; methodMap_["removeKeyserverDataFromNotifStorage"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_removeKeyserverDataFromNotifStorage}; methodMap_["getKeyserverDataFromNotifStorage"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getKeyserverDataFromNotifStorage}; methodMap_["initializeContentOutboundSession"] = MethodMetadata {5, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_initializeContentOutboundSession}; methodMap_["initializeContentInboundSession"] = MethodMetadata {5, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_initializeContentInboundSession}; methodMap_["encrypt"] = MethodMetadata {2, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_encrypt}; methodMap_["decrypt"] = MethodMetadata {2, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_decrypt}; methodMap_["decryptSequentialAndPersist"] = MethodMetadata {3, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_decryptSequentialAndPersist}; methodMap_["signMessage"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_signMessage}; methodMap_["verifySignature"] = MethodMetadata {3, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_verifySignature}; methodMap_["getCodeVersion"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getCodeVersion}; methodMap_["terminate"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_terminate}; methodMap_["setNotifyToken"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_setNotifyToken}; methodMap_["clearNotifyToken"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_clearNotifyToken}; methodMap_["stampSQLiteDBUserID"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_stampSQLiteDBUserID}; methodMap_["getSQLiteStampedUserID"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getSQLiteStampedUserID}; methodMap_["clearSensitiveData"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_clearSensitiveData}; methodMap_["checkIfDatabaseNeedsDeletion"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_checkIfDatabaseNeedsDeletion}; methodMap_["reportDBOperationsFailure"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_reportDBOperationsFailure}; methodMap_["computeBackupKey"] = MethodMetadata {2, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_computeBackupKey}; methodMap_["generateRandomString"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_generateRandomString}; methodMap_["setCommServicesAuthMetadata"] = MethodMetadata {3, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_setCommServicesAuthMetadata}; methodMap_["getCommServicesAuthMetadata"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getCommServicesAuthMetadata}; methodMap_["clearCommServicesAuthMetadata"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_clearCommServicesAuthMetadata}; methodMap_["setCommServicesAccessToken"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_setCommServicesAccessToken}; methodMap_["clearCommServicesAccessToken"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_clearCommServicesAccessToken}; methodMap_["startBackupHandler"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_startBackupHandler}; methodMap_["stopBackupHandler"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_stopBackupHandler}; methodMap_["createNewBackup"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_createNewBackup}; methodMap_["createNewSIWEBackup"] = MethodMetadata {2, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_createNewSIWEBackup}; methodMap_["restoreBackup"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_restoreBackup}; methodMap_["restoreBackupData"] = MethodMetadata {3, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_restoreBackupData}; methodMap_["retrieveBackupKeys"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_retrieveBackupKeys}; methodMap_["retrieveLatestSIWEBackupData"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_retrieveLatestSIWEBackupData}; methodMap_["setSIWEBackupSecrets"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_setSIWEBackupSecrets}; methodMap_["getSIWEBackupSecrets"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getSIWEBackupSecrets}; - methodMap_["getAllReceivedMessageToDevice"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getAllReceivedMessageToDevice}; - methodMap_["removeReceivedMessagesToDevice"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_removeReceivedMessagesToDevice}; + methodMap_["getAllInboundP2PMessage"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getAllInboundP2PMessage}; + methodMap_["removeInboundP2PMessages"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_removeInboundP2PMessages}; } } // namespace react } // namespace facebook diff --git a/native/cpp/CommonCpp/_generated/commJSI.h b/native/cpp/CommonCpp/_generated/commJSI.h index e3d5e89ac..2daa439bd 100644 --- a/native/cpp/CommonCpp/_generated/commJSI.h +++ b/native/cpp/CommonCpp/_generated/commJSI.h @@ -1,557 +1,557 @@ /** * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). * * Do not edit this file as changes may cause incorrect behavior and will be lost * once the code is regenerated. * * @generated by codegen project: GenerateModuleH.js */ #pragma once #include #include namespace facebook { namespace react { class JSI_EXPORT CommCoreModuleSchemaCxxSpecJSI : public TurboModule { protected: CommCoreModuleSchemaCxxSpecJSI(std::shared_ptr jsInvoker); public: virtual jsi::Value getDraft(jsi::Runtime &rt, jsi::String key) = 0; virtual jsi::Value updateDraft(jsi::Runtime &rt, jsi::String key, jsi::String text) = 0; virtual jsi::Value moveDraft(jsi::Runtime &rt, jsi::String oldKey, jsi::String newKey) = 0; virtual jsi::Value getClientDBStore(jsi::Runtime &rt) = 0; virtual jsi::Value removeAllDrafts(jsi::Runtime &rt) = 0; virtual jsi::Array getAllMessagesSync(jsi::Runtime &rt) = 0; virtual void processMessageStoreOperationsSync(jsi::Runtime &rt, jsi::Array operations) = 0; virtual jsi::Array getAllThreadsSync(jsi::Runtime &rt) = 0; virtual void processReportStoreOperationsSync(jsi::Runtime &rt, jsi::Array operations) = 0; virtual void processThreadStoreOperationsSync(jsi::Runtime &rt, jsi::Array operations) = 0; virtual jsi::Value processDBStoreOperations(jsi::Runtime &rt, jsi::Object operations) = 0; virtual jsi::Value initializeCryptoAccount(jsi::Runtime &rt) = 0; virtual jsi::Value getUserPublicKey(jsi::Runtime &rt) = 0; virtual jsi::Value getOneTimeKeys(jsi::Runtime &rt, double oneTimeKeysAmount) = 0; virtual jsi::Value validateAndGetPrekeys(jsi::Runtime &rt) = 0; virtual jsi::Value validateAndUploadPrekeys(jsi::Runtime &rt, jsi::String authUserID, jsi::String authDeviceID, jsi::String authAccessToken) = 0; virtual jsi::Value initializeNotificationsSession(jsi::Runtime &rt, jsi::String identityKeys, jsi::String prekey, jsi::String prekeySignature, jsi::String oneTimeKey, jsi::String keyserverID) = 0; virtual jsi::Value isNotificationsSessionInitialized(jsi::Runtime &rt) = 0; virtual jsi::Value updateKeyserverDataInNotifStorage(jsi::Runtime &rt, jsi::Array keyserversData) = 0; virtual jsi::Value removeKeyserverDataFromNotifStorage(jsi::Runtime &rt, jsi::Array keyserverIDsToDelete) = 0; virtual jsi::Value getKeyserverDataFromNotifStorage(jsi::Runtime &rt, jsi::Array keyserverIDs) = 0; virtual jsi::Value initializeContentOutboundSession(jsi::Runtime &rt, jsi::String identityKeys, jsi::String prekey, jsi::String prekeySignature, jsi::String oneTimeKey, jsi::String deviceID) = 0; virtual jsi::Value initializeContentInboundSession(jsi::Runtime &rt, jsi::String identityKeys, jsi::Object encryptedContent, jsi::String deviceID, double sessionVersion, bool overwrite) = 0; virtual jsi::Value encrypt(jsi::Runtime &rt, jsi::String message, jsi::String deviceID) = 0; virtual jsi::Value decrypt(jsi::Runtime &rt, jsi::Object encryptedData, jsi::String deviceID) = 0; virtual jsi::Value decryptSequentialAndPersist(jsi::Runtime &rt, jsi::Object encryptedData, jsi::String deviceID, jsi::String messageID) = 0; virtual jsi::Value signMessage(jsi::Runtime &rt, jsi::String message) = 0; virtual jsi::Value verifySignature(jsi::Runtime &rt, jsi::String publicKey, jsi::String message, jsi::String signature) = 0; virtual double getCodeVersion(jsi::Runtime &rt) = 0; virtual void terminate(jsi::Runtime &rt) = 0; virtual jsi::Value setNotifyToken(jsi::Runtime &rt, jsi::String token) = 0; virtual jsi::Value clearNotifyToken(jsi::Runtime &rt) = 0; virtual jsi::Value stampSQLiteDBUserID(jsi::Runtime &rt, jsi::String userID) = 0; virtual jsi::Value getSQLiteStampedUserID(jsi::Runtime &rt) = 0; virtual jsi::Value clearSensitiveData(jsi::Runtime &rt) = 0; virtual bool checkIfDatabaseNeedsDeletion(jsi::Runtime &rt) = 0; virtual void reportDBOperationsFailure(jsi::Runtime &rt) = 0; virtual jsi::Value computeBackupKey(jsi::Runtime &rt, jsi::String password, jsi::String backupID) = 0; virtual jsi::Value generateRandomString(jsi::Runtime &rt, double size) = 0; virtual jsi::Value setCommServicesAuthMetadata(jsi::Runtime &rt, jsi::String userID, jsi::String deviceID, jsi::String accessToken) = 0; virtual jsi::Value getCommServicesAuthMetadata(jsi::Runtime &rt) = 0; virtual jsi::Value clearCommServicesAuthMetadata(jsi::Runtime &rt) = 0; virtual jsi::Value setCommServicesAccessToken(jsi::Runtime &rt, jsi::String accessToken) = 0; virtual jsi::Value clearCommServicesAccessToken(jsi::Runtime &rt) = 0; virtual void startBackupHandler(jsi::Runtime &rt) = 0; virtual void stopBackupHandler(jsi::Runtime &rt) = 0; virtual jsi::Value createNewBackup(jsi::Runtime &rt, jsi::String backupSecret) = 0; virtual jsi::Value createNewSIWEBackup(jsi::Runtime &rt, jsi::String backupSecret, jsi::String siweBackupMsg) = 0; virtual jsi::Value restoreBackup(jsi::Runtime &rt, jsi::String backupSecret) = 0; virtual jsi::Value restoreBackupData(jsi::Runtime &rt, jsi::String backupID, jsi::String backupDataKey, jsi::String backupLogDataKey) = 0; virtual jsi::Value retrieveBackupKeys(jsi::Runtime &rt, jsi::String backupSecret) = 0; virtual jsi::Value retrieveLatestSIWEBackupData(jsi::Runtime &rt) = 0; virtual jsi::Value setSIWEBackupSecrets(jsi::Runtime &rt, jsi::Object siweBackupSecrets) = 0; virtual jsi::Value getSIWEBackupSecrets(jsi::Runtime &rt) = 0; - virtual jsi::Value getAllReceivedMessageToDevice(jsi::Runtime &rt) = 0; - virtual jsi::Value removeReceivedMessagesToDevice(jsi::Runtime &rt, jsi::Array ids) = 0; + virtual jsi::Value getAllInboundP2PMessage(jsi::Runtime &rt) = 0; + virtual jsi::Value removeInboundP2PMessages(jsi::Runtime &rt, jsi::Array ids) = 0; }; template class JSI_EXPORT CommCoreModuleSchemaCxxSpec : public TurboModule { public: jsi::Value get(jsi::Runtime &rt, const jsi::PropNameID &propName) override { return delegate_.get(rt, propName); } protected: CommCoreModuleSchemaCxxSpec(std::shared_ptr jsInvoker) : TurboModule("CommTurboModule", jsInvoker), delegate_(static_cast(this), jsInvoker) {} private: class Delegate : public CommCoreModuleSchemaCxxSpecJSI { public: Delegate(T *instance, std::shared_ptr jsInvoker) : CommCoreModuleSchemaCxxSpecJSI(std::move(jsInvoker)), instance_(instance) {} jsi::Value getDraft(jsi::Runtime &rt, jsi::String key) override { static_assert( bridging::getParameterCount(&T::getDraft) == 2, "Expected getDraft(...) to have 2 parameters"); return bridging::callFromJs( rt, &T::getDraft, jsInvoker_, instance_, std::move(key)); } jsi::Value updateDraft(jsi::Runtime &rt, jsi::String key, jsi::String text) override { static_assert( bridging::getParameterCount(&T::updateDraft) == 3, "Expected updateDraft(...) to have 3 parameters"); return bridging::callFromJs( rt, &T::updateDraft, jsInvoker_, instance_, std::move(key), std::move(text)); } jsi::Value moveDraft(jsi::Runtime &rt, jsi::String oldKey, jsi::String newKey) override { static_assert( bridging::getParameterCount(&T::moveDraft) == 3, "Expected moveDraft(...) to have 3 parameters"); return bridging::callFromJs( rt, &T::moveDraft, jsInvoker_, instance_, std::move(oldKey), std::move(newKey)); } jsi::Value getClientDBStore(jsi::Runtime &rt) override { static_assert( bridging::getParameterCount(&T::getClientDBStore) == 1, "Expected getClientDBStore(...) to have 1 parameters"); return bridging::callFromJs( rt, &T::getClientDBStore, jsInvoker_, instance_); } jsi::Value removeAllDrafts(jsi::Runtime &rt) override { static_assert( bridging::getParameterCount(&T::removeAllDrafts) == 1, "Expected removeAllDrafts(...) to have 1 parameters"); return bridging::callFromJs( rt, &T::removeAllDrafts, jsInvoker_, instance_); } jsi::Array getAllMessagesSync(jsi::Runtime &rt) override { static_assert( bridging::getParameterCount(&T::getAllMessagesSync) == 1, "Expected getAllMessagesSync(...) to have 1 parameters"); return bridging::callFromJs( rt, &T::getAllMessagesSync, jsInvoker_, instance_); } void processMessageStoreOperationsSync(jsi::Runtime &rt, jsi::Array operations) override { static_assert( bridging::getParameterCount(&T::processMessageStoreOperationsSync) == 2, "Expected processMessageStoreOperationsSync(...) to have 2 parameters"); return bridging::callFromJs( rt, &T::processMessageStoreOperationsSync, jsInvoker_, instance_, std::move(operations)); } jsi::Array getAllThreadsSync(jsi::Runtime &rt) override { static_assert( bridging::getParameterCount(&T::getAllThreadsSync) == 1, "Expected getAllThreadsSync(...) to have 1 parameters"); return bridging::callFromJs( rt, &T::getAllThreadsSync, jsInvoker_, instance_); } void processReportStoreOperationsSync(jsi::Runtime &rt, jsi::Array operations) override { static_assert( bridging::getParameterCount(&T::processReportStoreOperationsSync) == 2, "Expected processReportStoreOperationsSync(...) to have 2 parameters"); return bridging::callFromJs( rt, &T::processReportStoreOperationsSync, jsInvoker_, instance_, std::move(operations)); } void processThreadStoreOperationsSync(jsi::Runtime &rt, jsi::Array operations) override { static_assert( bridging::getParameterCount(&T::processThreadStoreOperationsSync) == 2, "Expected processThreadStoreOperationsSync(...) to have 2 parameters"); return bridging::callFromJs( rt, &T::processThreadStoreOperationsSync, jsInvoker_, instance_, std::move(operations)); } jsi::Value processDBStoreOperations(jsi::Runtime &rt, jsi::Object operations) override { static_assert( bridging::getParameterCount(&T::processDBStoreOperations) == 2, "Expected processDBStoreOperations(...) to have 2 parameters"); return bridging::callFromJs( rt, &T::processDBStoreOperations, jsInvoker_, instance_, std::move(operations)); } jsi::Value initializeCryptoAccount(jsi::Runtime &rt) override { static_assert( bridging::getParameterCount(&T::initializeCryptoAccount) == 1, "Expected initializeCryptoAccount(...) to have 1 parameters"); return bridging::callFromJs( rt, &T::initializeCryptoAccount, jsInvoker_, instance_); } jsi::Value getUserPublicKey(jsi::Runtime &rt) override { static_assert( bridging::getParameterCount(&T::getUserPublicKey) == 1, "Expected getUserPublicKey(...) to have 1 parameters"); return bridging::callFromJs( rt, &T::getUserPublicKey, jsInvoker_, instance_); } jsi::Value getOneTimeKeys(jsi::Runtime &rt, double oneTimeKeysAmount) override { static_assert( bridging::getParameterCount(&T::getOneTimeKeys) == 2, "Expected getOneTimeKeys(...) to have 2 parameters"); return bridging::callFromJs( rt, &T::getOneTimeKeys, jsInvoker_, instance_, std::move(oneTimeKeysAmount)); } jsi::Value validateAndGetPrekeys(jsi::Runtime &rt) override { static_assert( bridging::getParameterCount(&T::validateAndGetPrekeys) == 1, "Expected validateAndGetPrekeys(...) to have 1 parameters"); return bridging::callFromJs( rt, &T::validateAndGetPrekeys, jsInvoker_, instance_); } jsi::Value validateAndUploadPrekeys(jsi::Runtime &rt, jsi::String authUserID, jsi::String authDeviceID, jsi::String authAccessToken) override { static_assert( bridging::getParameterCount(&T::validateAndUploadPrekeys) == 4, "Expected validateAndUploadPrekeys(...) to have 4 parameters"); return bridging::callFromJs( rt, &T::validateAndUploadPrekeys, jsInvoker_, instance_, std::move(authUserID), std::move(authDeviceID), std::move(authAccessToken)); } jsi::Value initializeNotificationsSession(jsi::Runtime &rt, jsi::String identityKeys, jsi::String prekey, jsi::String prekeySignature, jsi::String oneTimeKey, jsi::String keyserverID) override { static_assert( bridging::getParameterCount(&T::initializeNotificationsSession) == 6, "Expected initializeNotificationsSession(...) to have 6 parameters"); return bridging::callFromJs( rt, &T::initializeNotificationsSession, jsInvoker_, instance_, std::move(identityKeys), std::move(prekey), std::move(prekeySignature), std::move(oneTimeKey), std::move(keyserverID)); } jsi::Value isNotificationsSessionInitialized(jsi::Runtime &rt) override { static_assert( bridging::getParameterCount(&T::isNotificationsSessionInitialized) == 1, "Expected isNotificationsSessionInitialized(...) to have 1 parameters"); return bridging::callFromJs( rt, &T::isNotificationsSessionInitialized, jsInvoker_, instance_); } jsi::Value updateKeyserverDataInNotifStorage(jsi::Runtime &rt, jsi::Array keyserversData) override { static_assert( bridging::getParameterCount(&T::updateKeyserverDataInNotifStorage) == 2, "Expected updateKeyserverDataInNotifStorage(...) to have 2 parameters"); return bridging::callFromJs( rt, &T::updateKeyserverDataInNotifStorage, jsInvoker_, instance_, std::move(keyserversData)); } jsi::Value removeKeyserverDataFromNotifStorage(jsi::Runtime &rt, jsi::Array keyserverIDsToDelete) override { static_assert( bridging::getParameterCount(&T::removeKeyserverDataFromNotifStorage) == 2, "Expected removeKeyserverDataFromNotifStorage(...) to have 2 parameters"); return bridging::callFromJs( rt, &T::removeKeyserverDataFromNotifStorage, jsInvoker_, instance_, std::move(keyserverIDsToDelete)); } jsi::Value getKeyserverDataFromNotifStorage(jsi::Runtime &rt, jsi::Array keyserverIDs) override { static_assert( bridging::getParameterCount(&T::getKeyserverDataFromNotifStorage) == 2, "Expected getKeyserverDataFromNotifStorage(...) to have 2 parameters"); return bridging::callFromJs( rt, &T::getKeyserverDataFromNotifStorage, jsInvoker_, instance_, std::move(keyserverIDs)); } jsi::Value initializeContentOutboundSession(jsi::Runtime &rt, jsi::String identityKeys, jsi::String prekey, jsi::String prekeySignature, jsi::String oneTimeKey, jsi::String deviceID) override { static_assert( bridging::getParameterCount(&T::initializeContentOutboundSession) == 6, "Expected initializeContentOutboundSession(...) to have 6 parameters"); return bridging::callFromJs( rt, &T::initializeContentOutboundSession, jsInvoker_, instance_, std::move(identityKeys), std::move(prekey), std::move(prekeySignature), std::move(oneTimeKey), std::move(deviceID)); } jsi::Value initializeContentInboundSession(jsi::Runtime &rt, jsi::String identityKeys, jsi::Object encryptedContent, jsi::String deviceID, double sessionVersion, bool overwrite) override { static_assert( bridging::getParameterCount(&T::initializeContentInboundSession) == 6, "Expected initializeContentInboundSession(...) to have 6 parameters"); return bridging::callFromJs( rt, &T::initializeContentInboundSession, jsInvoker_, instance_, std::move(identityKeys), std::move(encryptedContent), std::move(deviceID), std::move(sessionVersion), std::move(overwrite)); } jsi::Value encrypt(jsi::Runtime &rt, jsi::String message, jsi::String deviceID) override { static_assert( bridging::getParameterCount(&T::encrypt) == 3, "Expected encrypt(...) to have 3 parameters"); return bridging::callFromJs( rt, &T::encrypt, jsInvoker_, instance_, std::move(message), std::move(deviceID)); } jsi::Value decrypt(jsi::Runtime &rt, jsi::Object encryptedData, jsi::String deviceID) override { static_assert( bridging::getParameterCount(&T::decrypt) == 3, "Expected decrypt(...) to have 3 parameters"); return bridging::callFromJs( rt, &T::decrypt, jsInvoker_, instance_, std::move(encryptedData), std::move(deviceID)); } jsi::Value decryptSequentialAndPersist(jsi::Runtime &rt, jsi::Object encryptedData, jsi::String deviceID, jsi::String messageID) override { static_assert( bridging::getParameterCount(&T::decryptSequentialAndPersist) == 4, "Expected decryptSequentialAndPersist(...) to have 4 parameters"); return bridging::callFromJs( rt, &T::decryptSequentialAndPersist, jsInvoker_, instance_, std::move(encryptedData), std::move(deviceID), std::move(messageID)); } jsi::Value signMessage(jsi::Runtime &rt, jsi::String message) override { static_assert( bridging::getParameterCount(&T::signMessage) == 2, "Expected signMessage(...) to have 2 parameters"); return bridging::callFromJs( rt, &T::signMessage, jsInvoker_, instance_, std::move(message)); } jsi::Value verifySignature(jsi::Runtime &rt, jsi::String publicKey, jsi::String message, jsi::String signature) override { static_assert( bridging::getParameterCount(&T::verifySignature) == 4, "Expected verifySignature(...) to have 4 parameters"); return bridging::callFromJs( rt, &T::verifySignature, jsInvoker_, instance_, std::move(publicKey), std::move(message), std::move(signature)); } double getCodeVersion(jsi::Runtime &rt) override { static_assert( bridging::getParameterCount(&T::getCodeVersion) == 1, "Expected getCodeVersion(...) to have 1 parameters"); return bridging::callFromJs( rt, &T::getCodeVersion, jsInvoker_, instance_); } void terminate(jsi::Runtime &rt) override { static_assert( bridging::getParameterCount(&T::terminate) == 1, "Expected terminate(...) to have 1 parameters"); return bridging::callFromJs( rt, &T::terminate, jsInvoker_, instance_); } jsi::Value setNotifyToken(jsi::Runtime &rt, jsi::String token) override { static_assert( bridging::getParameterCount(&T::setNotifyToken) == 2, "Expected setNotifyToken(...) to have 2 parameters"); return bridging::callFromJs( rt, &T::setNotifyToken, jsInvoker_, instance_, std::move(token)); } jsi::Value clearNotifyToken(jsi::Runtime &rt) override { static_assert( bridging::getParameterCount(&T::clearNotifyToken) == 1, "Expected clearNotifyToken(...) to have 1 parameters"); return bridging::callFromJs( rt, &T::clearNotifyToken, jsInvoker_, instance_); } jsi::Value stampSQLiteDBUserID(jsi::Runtime &rt, jsi::String userID) override { static_assert( bridging::getParameterCount(&T::stampSQLiteDBUserID) == 2, "Expected stampSQLiteDBUserID(...) to have 2 parameters"); return bridging::callFromJs( rt, &T::stampSQLiteDBUserID, jsInvoker_, instance_, std::move(userID)); } jsi::Value getSQLiteStampedUserID(jsi::Runtime &rt) override { static_assert( bridging::getParameterCount(&T::getSQLiteStampedUserID) == 1, "Expected getSQLiteStampedUserID(...) to have 1 parameters"); return bridging::callFromJs( rt, &T::getSQLiteStampedUserID, jsInvoker_, instance_); } jsi::Value clearSensitiveData(jsi::Runtime &rt) override { static_assert( bridging::getParameterCount(&T::clearSensitiveData) == 1, "Expected clearSensitiveData(...) to have 1 parameters"); return bridging::callFromJs( rt, &T::clearSensitiveData, jsInvoker_, instance_); } bool checkIfDatabaseNeedsDeletion(jsi::Runtime &rt) override { static_assert( bridging::getParameterCount(&T::checkIfDatabaseNeedsDeletion) == 1, "Expected checkIfDatabaseNeedsDeletion(...) to have 1 parameters"); return bridging::callFromJs( rt, &T::checkIfDatabaseNeedsDeletion, jsInvoker_, instance_); } void reportDBOperationsFailure(jsi::Runtime &rt) override { static_assert( bridging::getParameterCount(&T::reportDBOperationsFailure) == 1, "Expected reportDBOperationsFailure(...) to have 1 parameters"); return bridging::callFromJs( rt, &T::reportDBOperationsFailure, jsInvoker_, instance_); } jsi::Value computeBackupKey(jsi::Runtime &rt, jsi::String password, jsi::String backupID) override { static_assert( bridging::getParameterCount(&T::computeBackupKey) == 3, "Expected computeBackupKey(...) to have 3 parameters"); return bridging::callFromJs( rt, &T::computeBackupKey, jsInvoker_, instance_, std::move(password), std::move(backupID)); } jsi::Value generateRandomString(jsi::Runtime &rt, double size) override { static_assert( bridging::getParameterCount(&T::generateRandomString) == 2, "Expected generateRandomString(...) to have 2 parameters"); return bridging::callFromJs( rt, &T::generateRandomString, jsInvoker_, instance_, std::move(size)); } jsi::Value setCommServicesAuthMetadata(jsi::Runtime &rt, jsi::String userID, jsi::String deviceID, jsi::String accessToken) override { static_assert( bridging::getParameterCount(&T::setCommServicesAuthMetadata) == 4, "Expected setCommServicesAuthMetadata(...) to have 4 parameters"); return bridging::callFromJs( rt, &T::setCommServicesAuthMetadata, jsInvoker_, instance_, std::move(userID), std::move(deviceID), std::move(accessToken)); } jsi::Value getCommServicesAuthMetadata(jsi::Runtime &rt) override { static_assert( bridging::getParameterCount(&T::getCommServicesAuthMetadata) == 1, "Expected getCommServicesAuthMetadata(...) to have 1 parameters"); return bridging::callFromJs( rt, &T::getCommServicesAuthMetadata, jsInvoker_, instance_); } jsi::Value clearCommServicesAuthMetadata(jsi::Runtime &rt) override { static_assert( bridging::getParameterCount(&T::clearCommServicesAuthMetadata) == 1, "Expected clearCommServicesAuthMetadata(...) to have 1 parameters"); return bridging::callFromJs( rt, &T::clearCommServicesAuthMetadata, jsInvoker_, instance_); } jsi::Value setCommServicesAccessToken(jsi::Runtime &rt, jsi::String accessToken) override { static_assert( bridging::getParameterCount(&T::setCommServicesAccessToken) == 2, "Expected setCommServicesAccessToken(...) to have 2 parameters"); return bridging::callFromJs( rt, &T::setCommServicesAccessToken, jsInvoker_, instance_, std::move(accessToken)); } jsi::Value clearCommServicesAccessToken(jsi::Runtime &rt) override { static_assert( bridging::getParameterCount(&T::clearCommServicesAccessToken) == 1, "Expected clearCommServicesAccessToken(...) to have 1 parameters"); return bridging::callFromJs( rt, &T::clearCommServicesAccessToken, jsInvoker_, instance_); } void startBackupHandler(jsi::Runtime &rt) override { static_assert( bridging::getParameterCount(&T::startBackupHandler) == 1, "Expected startBackupHandler(...) to have 1 parameters"); return bridging::callFromJs( rt, &T::startBackupHandler, jsInvoker_, instance_); } void stopBackupHandler(jsi::Runtime &rt) override { static_assert( bridging::getParameterCount(&T::stopBackupHandler) == 1, "Expected stopBackupHandler(...) to have 1 parameters"); return bridging::callFromJs( rt, &T::stopBackupHandler, jsInvoker_, instance_); } jsi::Value createNewBackup(jsi::Runtime &rt, jsi::String backupSecret) override { static_assert( bridging::getParameterCount(&T::createNewBackup) == 2, "Expected createNewBackup(...) to have 2 parameters"); return bridging::callFromJs( rt, &T::createNewBackup, jsInvoker_, instance_, std::move(backupSecret)); } jsi::Value createNewSIWEBackup(jsi::Runtime &rt, jsi::String backupSecret, jsi::String siweBackupMsg) override { static_assert( bridging::getParameterCount(&T::createNewSIWEBackup) == 3, "Expected createNewSIWEBackup(...) to have 3 parameters"); return bridging::callFromJs( rt, &T::createNewSIWEBackup, jsInvoker_, instance_, std::move(backupSecret), std::move(siweBackupMsg)); } jsi::Value restoreBackup(jsi::Runtime &rt, jsi::String backupSecret) override { static_assert( bridging::getParameterCount(&T::restoreBackup) == 2, "Expected restoreBackup(...) to have 2 parameters"); return bridging::callFromJs( rt, &T::restoreBackup, jsInvoker_, instance_, std::move(backupSecret)); } jsi::Value restoreBackupData(jsi::Runtime &rt, jsi::String backupID, jsi::String backupDataKey, jsi::String backupLogDataKey) override { static_assert( bridging::getParameterCount(&T::restoreBackupData) == 4, "Expected restoreBackupData(...) to have 4 parameters"); return bridging::callFromJs( rt, &T::restoreBackupData, jsInvoker_, instance_, std::move(backupID), std::move(backupDataKey), std::move(backupLogDataKey)); } jsi::Value retrieveBackupKeys(jsi::Runtime &rt, jsi::String backupSecret) override { static_assert( bridging::getParameterCount(&T::retrieveBackupKeys) == 2, "Expected retrieveBackupKeys(...) to have 2 parameters"); return bridging::callFromJs( rt, &T::retrieveBackupKeys, jsInvoker_, instance_, std::move(backupSecret)); } jsi::Value retrieveLatestSIWEBackupData(jsi::Runtime &rt) override { static_assert( bridging::getParameterCount(&T::retrieveLatestSIWEBackupData) == 1, "Expected retrieveLatestSIWEBackupData(...) to have 1 parameters"); return bridging::callFromJs( rt, &T::retrieveLatestSIWEBackupData, jsInvoker_, instance_); } jsi::Value setSIWEBackupSecrets(jsi::Runtime &rt, jsi::Object siweBackupSecrets) override { static_assert( bridging::getParameterCount(&T::setSIWEBackupSecrets) == 2, "Expected setSIWEBackupSecrets(...) to have 2 parameters"); return bridging::callFromJs( rt, &T::setSIWEBackupSecrets, jsInvoker_, instance_, std::move(siweBackupSecrets)); } jsi::Value getSIWEBackupSecrets(jsi::Runtime &rt) override { static_assert( bridging::getParameterCount(&T::getSIWEBackupSecrets) == 1, "Expected getSIWEBackupSecrets(...) to have 1 parameters"); return bridging::callFromJs( rt, &T::getSIWEBackupSecrets, jsInvoker_, instance_); } - jsi::Value getAllReceivedMessageToDevice(jsi::Runtime &rt) override { + jsi::Value getAllInboundP2PMessage(jsi::Runtime &rt) override { static_assert( - bridging::getParameterCount(&T::getAllReceivedMessageToDevice) == 1, - "Expected getAllReceivedMessageToDevice(...) to have 1 parameters"); + bridging::getParameterCount(&T::getAllInboundP2PMessage) == 1, + "Expected getAllInboundP2PMessage(...) to have 1 parameters"); return bridging::callFromJs( - rt, &T::getAllReceivedMessageToDevice, jsInvoker_, instance_); + rt, &T::getAllInboundP2PMessage, jsInvoker_, instance_); } - jsi::Value removeReceivedMessagesToDevice(jsi::Runtime &rt, jsi::Array ids) override { + jsi::Value removeInboundP2PMessages(jsi::Runtime &rt, jsi::Array ids) override { static_assert( - bridging::getParameterCount(&T::removeReceivedMessagesToDevice) == 2, - "Expected removeReceivedMessagesToDevice(...) to have 2 parameters"); + bridging::getParameterCount(&T::removeInboundP2PMessages) == 2, + "Expected removeInboundP2PMessages(...) to have 2 parameters"); return bridging::callFromJs( - rt, &T::removeReceivedMessagesToDevice, jsInvoker_, instance_, std::move(ids)); + rt, &T::removeInboundP2PMessages, jsInvoker_, instance_, std::move(ids)); } private: T *instance_; }; Delegate delegate_; }; } // namespace react } // namespace facebook diff --git a/native/database/sqlite-api.js b/native/database/sqlite-api.js index 4b0011a05..d035918b0 100644 --- a/native/database/sqlite-api.js +++ b/native/database/sqlite-api.js @@ -1,18 +1,18 @@ // @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 - getAllReceivedMessageToDevice: commCoreModule.getAllReceivedMessageToDevice, + getAllInboundP2PMessage: commCoreModule.getAllInboundP2PMessage, // write operations - removeReceivedMessagesToDevice: commCoreModule.removeReceivedMessagesToDevice, + removeInboundP2PMessages: commCoreModule.removeInboundP2PMessages, processDBStoreOperations, }; export { sqliteAPI }; diff --git a/native/schema/CommCoreModuleSchema.js b/native/schema/CommCoreModuleSchema.js index 0f76449bb..8ede13465 100644 --- a/native/schema/CommCoreModuleSchema.js +++ b/native/schema/CommCoreModuleSchema.js @@ -1,176 +1,174 @@ // @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 { ReceivedMessageToDevice } from 'lib/types/sqlite-types.js'; +import type { InboundP2PMessage } 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; +updateDraft: (key: string, text: string) => Promise; +moveDraft: (oldKey: string, newKey: string) => Promise; +getClientDBStore: () => Promise; +removeAllDrafts: () => Promise; +getAllMessagesSync: () => $ReadOnlyArray; +processMessageStoreOperationsSync: ( operations: $ReadOnlyArray, ) => void; +getAllThreadsSync: () => $ReadOnlyArray; +processReportStoreOperationsSync: ( operations: $ReadOnlyArray, ) => void; +processThreadStoreOperationsSync: ( operations: $ReadOnlyArray, ) => void; +processDBStoreOperations: (operations: Object) => Promise; +initializeCryptoAccount: () => Promise; +getUserPublicKey: () => Promise; +getOneTimeKeys: (oneTimeKeysAmount: number) => Promise; +validateAndGetPrekeys: () => Promise; +validateAndUploadPrekeys: ( authUserID: string, authDeviceID: string, authAccessToken: string, ) => Promise; +initializeNotificationsSession: ( identityKeys: string, prekey: string, prekeySignature: string, oneTimeKey: string, keyserverID: string, ) => Promise; +isNotificationsSessionInitialized: () => Promise; +updateKeyserverDataInNotifStorage: ( keyserversData: $ReadOnlyArray<{ +id: string, +unreadCount: number }>, ) => Promise; +removeKeyserverDataFromNotifStorage: ( keyserverIDsToDelete: $ReadOnlyArray, ) => Promise; +getKeyserverDataFromNotifStorage: ( keyserverIDs: $ReadOnlyArray, ) => Promise<$ReadOnlyArray<{ +id: string, +unreadCount: number }>>; +initializeContentOutboundSession: ( identityKeys: string, prekey: string, prekeySignature: string, oneTimeKey: string, deviceID: string, ) => Promise; +initializeContentInboundSession: ( identityKeys: string, encryptedContent: Object, deviceID: string, sessionVersion: number, overwrite: boolean, ) => Promise; +encrypt: (message: string, deviceID: string) => Promise; +decrypt: (encryptedData: Object, deviceID: string) => Promise; +decryptSequentialAndPersist: ( encryptedData: Object, deviceID: string, messageID: string, ) => Promise; +signMessage: (message: string) => Promise; +verifySignature: ( publicKey: string, message: string, signature: string, ) => Promise; +getCodeVersion: () => number; +terminate: () => void; +setNotifyToken: (token: string) => Promise; +clearNotifyToken: () => Promise; +stampSQLiteDBUserID: (userID: string) => Promise; +getSQLiteStampedUserID: () => Promise; +clearSensitiveData: () => Promise; +checkIfDatabaseNeedsDeletion: () => boolean; +reportDBOperationsFailure: () => void; +computeBackupKey: (password: string, backupID: string) => Promise; +generateRandomString: (size: number) => Promise; +setCommServicesAuthMetadata: ( userID: string, deviceID: string, accessToken: string, ) => Promise; +getCommServicesAuthMetadata: () => Promise; +clearCommServicesAuthMetadata: () => Promise; +setCommServicesAccessToken: (accessToken: string) => Promise; +clearCommServicesAccessToken: () => Promise; +startBackupHandler: () => void; +stopBackupHandler: () => void; +createNewBackup: (backupSecret: string) => Promise; +createNewSIWEBackup: ( backupSecret: string, siweBackupMsg: string, ) => Promise; +restoreBackup: (backupSecret: string) => Promise; +restoreBackupData: ( backupID: string, backupDataKey: string, backupLogDataKey: string, ) => Promise; +retrieveBackupKeys: (backupSecret: string) => Promise; +retrieveLatestSIWEBackupData: () => Promise; +setSIWEBackupSecrets: (siweBackupSecrets: Object) => Promise; +getSIWEBackupSecrets: () => Promise; - +getAllReceivedMessageToDevice: () => Promise; - +removeReceivedMessagesToDevice: ( - ids: $ReadOnlyArray, - ) => Promise; + +getAllInboundP2PMessage: () => Promise; + +removeInboundP2PMessages: (ids: $ReadOnlyArray) => Promise; } export interface CoreModuleSpec extends Spec { +computeBackupKey: ( password: string, backupID: string, ) => Promise; +decrypt: (encryptedData: EncryptedData, deviceID: string) => Promise; +decryptSequentialAndPersist: ( encryptedData: EncryptedData, deviceID: string, messageID: string, ) => Promise; +initializeContentInboundSession: ( identityKeys: string, encryptedContent: EncryptedData, deviceID: string, sessionVersion: number, overwrite: boolean, ) => Promise; +setSIWEBackupSecrets: ( siweBackupSecrets: SIWEBackupSecrets, ) => Promise; +getSIWEBackupSecrets: () => Promise; +processDBStoreOperations: ( operations: ClientDBStoreOperations, ) => Promise; } export default (TurboModuleRegistry.getEnforcing( 'CommTurboModule', ): Spec); diff --git a/web/cpp/SQLiteQueryExecutorBindings.cpp b/web/cpp/SQLiteQueryExecutorBindings.cpp index 6a11dc43a..90db117f3 100644 --- a/web/cpp/SQLiteQueryExecutorBindings.cpp +++ b/web/cpp/SQLiteQueryExecutorBindings.cpp @@ -1,320 +1,319 @@ #include "SQLiteQueryExecutor.cpp" +#include "entities/InboundP2PMessage.h" #include "entities/MessageToDevice.h" #include "entities/Nullable.h" -#include "entities/ReceivedMessageToDevice.h" #include #include namespace comm { using namespace emscripten; std::string getExceptionMessage(int exceptionPtr) { if (exceptionPtr == 0) { return std::string("Exception pointer value was null"); } std::exception *e = reinterpret_cast(exceptionPtr); if (e) { return std::string(e->what()); } return std::string("Pointer to exception was invalid"); } EMSCRIPTEN_BINDINGS(SQLiteQueryExecutor) { function("getExceptionMessage", &getExceptionMessage); value_object("NullableString") .field("value", &NullableString::value) .field("isNull", &NullableString::isNull); value_object("NullableInt") .field("value", &NullableInt::value) .field("isNull", &NullableInt::isNull); value_object("Draft") .field("key", &Draft::key) .field("text", &Draft::text); value_object("Report") .field("id", &Report::id) .field("report", &Report::report); value_object("PersistItem") .field("key", &PersistItem::key) .field("item", &PersistItem::item); value_object("UserInfo") .field("id", &UserInfo::id) .field("userInfo", &UserInfo::user_info); value_object("KeyserverInfo") .field("id", &KeyserverInfo::id) .field("keyserverInfo", &KeyserverInfo::keyserver_info) .field("syncedKeyserverInfo", &KeyserverInfo::synced_keyserver_info); value_object("MessageStoreThreads") .field("id", &MessageStoreThread::id) .field("startReached", &MessageStoreThread::start_reached); value_object("CommunityInfo") .field("id", &CommunityInfo::id) .field("communityInfo", &CommunityInfo::community_info); value_object("IntegrityThreadHash") .field("id", &IntegrityThreadHash::id) .field("threadHash", &IntegrityThreadHash::thread_hash); value_object("SyncedMetadataEntry") .field("name", &SyncedMetadataEntry::name) .field("data", &SyncedMetadataEntry::data); value_object("AuxUserInfo") .field("id", &AuxUserInfo::id) .field("auxUserInfo", &AuxUserInfo::aux_user_info); value_object("ThreadActivityEntry") .field("id", &ThreadActivityEntry::id) .field( "threadActivityStoreEntry", &ThreadActivityEntry::thread_activity_store_entry); value_object("WebThread") .field("id", &WebThread::id) .field("type", &WebThread::type) .field("name", &WebThread::name) .field("description", &WebThread::description) .field("color", &WebThread::color) .field("creationTime", &WebThread::creation_time) .field("parentThreadID", &WebThread::parent_thread_id) .field("containingThreadID", &WebThread::containing_thread_id) .field("community", &WebThread::community) .field("members", &WebThread::members) .field("roles", &WebThread::roles) .field("currentUser", &WebThread::current_user) .field("sourceMessageID", &WebThread::source_message_id) .field("repliesCount", &WebThread::replies_count) .field("avatar", &WebThread::avatar) .field("pinnedCount", &WebThread::pinned_count); value_object("WebMessage") .field("id", &WebMessage::id) .field("localID", &WebMessage::local_id) .field("thread", &WebMessage::thread) .field("user", &WebMessage::user) .field("type", &WebMessage::type) .field("futureType", &WebMessage::future_type) .field("content", &WebMessage::content) .field("time", &WebMessage::time); value_object("Media") .field("id", &Media::id) .field("container", &Media::container) .field("thread", &Media::thread) .field("uri", &Media::uri) .field("type", &Media::type) .field("extras", &Media::extras); value_object("MessageWithMedias") .field("message", &MessageWithMedias::message) .field("medias", &MessageWithMedias::medias); value_object("OlmPersistSession") .field("targetDeviceID", &OlmPersistSession::target_device_id) .field("sessionData", &OlmPersistSession::session_data) .field("version", &OlmPersistSession::version); value_object("ClientMessageToDevice") .field("messageID", &ClientMessageToDevice::message_id) .field("deviceID", &ClientMessageToDevice::device_id) .field("userID", &ClientMessageToDevice::user_id) .field("timestamp", &ClientMessageToDevice::timestamp) .field("plaintext", &ClientMessageToDevice::plaintext) .field("ciphertext", &ClientMessageToDevice::ciphertext); - value_object("ReceivedMessageToDevice") - .field("messageID", &ReceivedMessageToDevice::message_id) - .field("senderDeviceID", &ReceivedMessageToDevice::sender_device_id) - .field("plaintext", &ReceivedMessageToDevice::plaintext) - .field("status", &ReceivedMessageToDevice::status); + value_object("InboundP2PMessage") + .field("messageID", &InboundP2PMessage::message_id) + .field("senderDeviceID", &InboundP2PMessage::sender_device_id) + .field("plaintext", &InboundP2PMessage::plaintext) + .field("status", &InboundP2PMessage::status); class_("SQLiteQueryExecutor") .constructor() .function("updateDraft", &SQLiteQueryExecutor::updateDraft) .function("moveDraft", &SQLiteQueryExecutor::moveDraft) .function("getAllDrafts", &SQLiteQueryExecutor::getAllDrafts) .function("removeAllDrafts", &SQLiteQueryExecutor::removeAllDrafts) .function("removeDrafts", &SQLiteQueryExecutor::removeDrafts) .function("getAllMessagesWeb", &SQLiteQueryExecutor::getAllMessagesWeb) .function("removeAllMessages", &SQLiteQueryExecutor::removeAllMessages) .function("removeMessages", &SQLiteQueryExecutor::removeMessages) .function( "removeMessagesForThreads", &SQLiteQueryExecutor::removeMessagesForThreads) .function("replaceMessageWeb", &SQLiteQueryExecutor::replaceMessageWeb) .function("rekeyMessage", &SQLiteQueryExecutor::rekeyMessage) .function("removeAllMedia", &SQLiteQueryExecutor::removeAllMedia) .function( "removeMediaForThreads", &SQLiteQueryExecutor::removeMediaForThreads) .function( "removeMediaForMessage", &SQLiteQueryExecutor::removeMediaForMessage) .function( "removeMediaForMessages", &SQLiteQueryExecutor::removeMediaForMessages) .function("replaceMedia", &SQLiteQueryExecutor::replaceMedia) .function( "rekeyMediaContainers", &SQLiteQueryExecutor::rekeyMediaContainers) .function( "replaceMessageStoreThreads", &SQLiteQueryExecutor::replaceMessageStoreThreads) .function( "removeMessageStoreThreads", &SQLiteQueryExecutor::removeMessageStoreThreads) .function( "getAllMessageStoreThreads", &SQLiteQueryExecutor::getAllMessageStoreThreads) .function( "removeAllMessageStoreThreads", &SQLiteQueryExecutor::removeAllMessageStoreThreads) .function("setMetadata", &SQLiteQueryExecutor::setMetadata) .function("clearMetadata", &SQLiteQueryExecutor::clearMetadata) .function("getMetadata", &SQLiteQueryExecutor::getMetadata) .function("replaceReport", &SQLiteQueryExecutor::replaceReport) .function("removeReports", &SQLiteQueryExecutor::removeReports) .function("removeAllReports", &SQLiteQueryExecutor::removeAllReports) .function("getAllReports", &SQLiteQueryExecutor::getAllReports) .function( "setPersistStorageItem", &SQLiteQueryExecutor::setPersistStorageItem) .function( "removePersistStorageItem", &SQLiteQueryExecutor::removePersistStorageItem) .function( "getPersistStorageItem", &SQLiteQueryExecutor::getPersistStorageItem) .function("replaceUser", &SQLiteQueryExecutor::replaceUser) .function("removeUsers", &SQLiteQueryExecutor::removeUsers) .function("removeAllUsers", &SQLiteQueryExecutor::removeAllUsers) .function("getAllUsers", &SQLiteQueryExecutor::getAllUsers) .function("replaceThreadWeb", &SQLiteQueryExecutor::replaceThreadWeb) .function("getAllThreadsWeb", &SQLiteQueryExecutor::getAllThreadsWeb) .function("removeAllThreads", &SQLiteQueryExecutor::removeAllThreads) .function("removeThreads", &SQLiteQueryExecutor::removeThreads) .function("replaceKeyserver", &SQLiteQueryExecutor::replaceKeyserver) .function("removeKeyservers", &SQLiteQueryExecutor::removeKeyservers) .function( "removeAllKeyservers", &SQLiteQueryExecutor::removeAllKeyservers) .function("getAllKeyservers", &SQLiteQueryExecutor::getAllKeyservers) .function("replaceCommunity", &SQLiteQueryExecutor::replaceCommunity) .function("removeCommunities", &SQLiteQueryExecutor::removeCommunities) .function( "removeAllCommunities", &SQLiteQueryExecutor::removeAllCommunities) .function("getAllCommunities", &SQLiteQueryExecutor::getAllCommunities) .function( "replaceIntegrityThreadHashes", &SQLiteQueryExecutor::replaceIntegrityThreadHashes) .function( "removeIntegrityThreadHashes", &SQLiteQueryExecutor::removeIntegrityThreadHashes) .function( "removeAllIntegrityThreadHashes", &SQLiteQueryExecutor::removeAllIntegrityThreadHashes) .function( "getAllIntegrityThreadHashes", &SQLiteQueryExecutor::getAllIntegrityThreadHashes) .function( "replaceSyncedMetadataEntry", &SQLiteQueryExecutor::replaceSyncedMetadataEntry) .function( "removeSyncedMetadata", &SQLiteQueryExecutor::removeSyncedMetadata) .function( "removeAllSyncedMetadata", &SQLiteQueryExecutor::removeAllSyncedMetadata) .function( "getAllSyncedMetadata", &SQLiteQueryExecutor::getAllSyncedMetadata) .function("replaceAuxUserInfo", &SQLiteQueryExecutor::replaceAuxUserInfo) .function("removeAuxUserInfos", &SQLiteQueryExecutor::removeAuxUserInfos) .function( "removeAllAuxUserInfos", &SQLiteQueryExecutor::removeAllAuxUserInfos) .function("getAllAuxUserInfos", &SQLiteQueryExecutor::getAllAuxUserInfos) .function( "replaceThreadActivityEntry", &SQLiteQueryExecutor::replaceThreadActivityEntry) .function( "removeThreadActivityEntries", &SQLiteQueryExecutor::removeThreadActivityEntries) .function( "removeAllThreadActivityEntries", &SQLiteQueryExecutor::removeAllThreadActivityEntries) .function( "getAllThreadActivityEntries", &SQLiteQueryExecutor::getAllThreadActivityEntries) .function("beginTransaction", &SQLiteQueryExecutor::beginTransaction) .function("commitTransaction", &SQLiteQueryExecutor::commitTransaction) .function( "getContentAccountID", &SQLiteQueryExecutor::getContentAccountID) .function("getNotifsAccountID", &SQLiteQueryExecutor::getNotifsAccountID) .function( "getOlmPersistSessionsData", &SQLiteQueryExecutor::getOlmPersistSessionsData) .function( "getOlmPersistAccountDataWeb", &SQLiteQueryExecutor::getOlmPersistAccountDataWeb) .function( "storeOlmPersistSession", &SQLiteQueryExecutor::storeOlmPersistSession) .function( "storeOlmPersistAccount", &SQLiteQueryExecutor::storeOlmPersistAccount) .function( "rollbackTransaction", &SQLiteQueryExecutor::rollbackTransaction) .function( "restoreFromMainCompaction", &SQLiteQueryExecutor::restoreFromMainCompaction) .function( "restoreFromBackupLog", &SQLiteQueryExecutor::restoreFromBackupLog) .function( "addMessagesToDevice", &SQLiteQueryExecutor::addMessagesToDevice) .function( "removeMessagesToDeviceOlderThan", &SQLiteQueryExecutor::removeMessagesToDeviceOlderThan) .function( "removeAllMessagesForDevice", &SQLiteQueryExecutor::removeAllMessagesForDevice) .function( "getAllMessagesToDevice", &SQLiteQueryExecutor::getAllMessagesToDevice) .function( - "addReceivedMessageToDevice", - &SQLiteQueryExecutor::addReceivedMessageToDevice) + "addInboundP2PMessage", &SQLiteQueryExecutor::addInboundP2PMessage) .function( - "getAllReceivedMessageToDevice", - &SQLiteQueryExecutor::getAllReceivedMessageToDevice) + "getAllInboundP2PMessage", + &SQLiteQueryExecutor::getAllInboundP2PMessage) .function( - "removeReceivedMessagesToDevice", - &SQLiteQueryExecutor::removeReceivedMessagesToDevice); + "removeInboundP2PMessages", + &SQLiteQueryExecutor::removeInboundP2PMessages); } } // namespace comm namespace emscripten { namespace internal { template struct BindingType> { using ValBinding = BindingType; using WireType = ValBinding::WireType; static WireType toWireType(const std::vector &vec) { std::vector valVec(vec.begin(), vec.end()); return BindingType::toWireType(val::array(valVec)); } static std::vector fromWireType(WireType value) { return vecFromJSArray(ValBinding::fromWireType(value)); } }; template struct TypeID< T, typename std::enable_if_t::type, std::vector< typename Canonicalized::type::value_type, typename Canonicalized::type::allocator_type>>::value>> { static constexpr TYPEID get() { return TypeID::get(); } }; } // namespace internal } // namespace emscripten diff --git a/web/database/sqlite-api.js b/web/database/sqlite-api.js index 6bd3f9568..c1c671548 100644 --- a/web/database/sqlite-api.js +++ b/web/database/sqlite-api.js @@ -1,39 +1,34 @@ // @flow -import type { - SQLiteAPI, - ReceivedMessageToDevice, -} from 'lib/types/sqlite-types.js'; +import type { SQLiteAPI, InboundP2PMessage } 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 getAllReceivedMessageToDevice(): Promise { + async getAllInboundP2PMessage(): Promise { const sharedWorker = await getCommSharedWorker(); const data = await sharedWorker.schedule({ - type: workerRequestMessageTypes.GET_RECEIVED_MESSAGES_TO_DEVICE, + type: workerRequestMessageTypes.GET_INBOUND_P2P_MESSAGES, }); - const messages: ?$ReadOnlyArray = data?.messages; + const messages: ?$ReadOnlyArray = data?.messages; return messages ? [...messages] : []; }, // write operations - async removeReceivedMessagesToDevice( - ids: $ReadOnlyArray, - ): Promise { + async removeInboundP2PMessages(ids: $ReadOnlyArray): Promise { const sharedWorker = await getCommSharedWorker(); await sharedWorker.schedule({ - type: workerRequestMessageTypes.REMOVE_RECEIVED_MESSAGES_TO_DEVICE, + type: workerRequestMessageTypes.REMOVE_INBOUND_P2P_MESSAGES, ids, }); }, processDBStoreOperations, }; export { sqliteAPI }; diff --git a/web/shared-worker/_generated/comm_query_executor.wasm b/web/shared-worker/_generated/comm_query_executor.wasm index f1b4aedd0..cc61fdd1b 100755 Binary files a/web/shared-worker/_generated/comm_query_executor.wasm and b/web/shared-worker/_generated/comm_query_executor.wasm differ diff --git a/web/shared-worker/queries/received-messages-to-device-queries.test.js b/web/shared-worker/queries/inbound-p2p-message-queries.test.js similarity index 70% rename from web/shared-worker/queries/received-messages-to-device-queries.test.js rename to web/shared-worker/queries/inbound-p2p-message-queries.test.js index cff790459..3350631ee 100644 --- a/web/shared-worker/queries/received-messages-to-device-queries.test.js +++ b/web/shared-worker/queries/inbound-p2p-message-queries.test.js @@ -1,77 +1,77 @@ // @flow -import type { ReceivedMessageToDevice } from 'lib/types/sqlite-types.js'; +import type { InboundP2PMessage } from 'lib/types/sqlite-types.js'; import { getDatabaseModule } from '../db-module.js'; import type { EmscriptenModule } from '../types/module.js'; import { type SQLiteQueryExecutor } from '../types/sqlite-query-executor.js'; import { clearSensitiveData } from '../utils/db-utils.js'; const FILE_PATH = 'test.sqlite'; const device1 = 'device-1'; const device2 = 'device-2'; -const TEST_MSG_1: ReceivedMessageToDevice = { +const TEST_MSG_1: InboundP2PMessage = { messageID: 'id-1', senderDeviceID: device1, plaintext: 'decrypted-1', status: 'none', }; -const TEST_MSG_2: ReceivedMessageToDevice = { +const TEST_MSG_2: InboundP2PMessage = { messageID: 'id-2', senderDeviceID: device2, plaintext: 'decrypted-2', status: 'none', }; -const TEST_MSG_3: ReceivedMessageToDevice = { +const TEST_MSG_3: InboundP2PMessage = { messageID: 'id-3', senderDeviceID: device1, plaintext: 'decrypted-3', status: 'none', }; const messagesOrdered = [TEST_MSG_1, TEST_MSG_2, TEST_MSG_3]; -describe('Received messages to device queries', () => { +describe('Inbound P2P message queries', () => { let queryExecutor: ?SQLiteQueryExecutor = null; let dbModule: ?EmscriptenModule = null; beforeAll(async () => { dbModule = getDatabaseModule(); }); beforeEach(() => { if (!dbModule) { throw new Error('Database module is missing'); } queryExecutor = new dbModule.SQLiteQueryExecutor(FILE_PATH); if (!queryExecutor) { throw new Error('SQLiteQueryExecutor is missing'); } - queryExecutor?.addReceivedMessageToDevice(TEST_MSG_1); - queryExecutor?.addReceivedMessageToDevice(TEST_MSG_2); - queryExecutor?.addReceivedMessageToDevice(TEST_MSG_3); + queryExecutor?.addInboundP2PMessage(TEST_MSG_1); + queryExecutor?.addInboundP2PMessage(TEST_MSG_2); + queryExecutor?.addInboundP2PMessage(TEST_MSG_3); }); afterEach(() => { if (!dbModule || !queryExecutor) { return; } clearSensitiveData(dbModule, FILE_PATH, queryExecutor); }); it('should return all messages', () => { - const messages = queryExecutor?.getAllReceivedMessageToDevice() ?? []; + const messages = queryExecutor?.getAllInboundP2PMessage() ?? []; expect(messages.length).toBe(3); expect(messages).toStrictEqual(messagesOrdered); }); it('should remove messages', () => { - queryExecutor?.removeReceivedMessagesToDevice([TEST_MSG_2.messageID]); - const messages = queryExecutor?.getAllReceivedMessageToDevice() ?? []; + queryExecutor?.removeInboundP2PMessages([TEST_MSG_2.messageID]); + const messages = queryExecutor?.getAllInboundP2PMessage() ?? []; expect(messages.length).toBe(2); expect(messages).toStrictEqual([TEST_MSG_1, TEST_MSG_3]); }); }); diff --git a/web/shared-worker/types/sqlite-query-executor.js b/web/shared-worker/types/sqlite-query-executor.js index c792bc8c1..5666a676d 100644 --- a/web/shared-worker/types/sqlite-query-executor.js +++ b/web/shared-worker/types/sqlite-query-executor.js @@ -1,184 +1,184 @@ // @flow import type { ClientDBAuxUserInfo } from 'lib/ops/aux-user-store-ops.js'; import type { ClientDBCommunityInfo } from 'lib/ops/community-store-ops.js'; import type { ClientDBIntegrityThreadHash } from 'lib/ops/integrity-store-ops.js'; import type { ClientDBKeyserverInfo } from 'lib/ops/keyserver-store-ops.js'; import type { ClientDBReport } from 'lib/ops/report-store-ops.js'; import type { ClientDBSyncedMetadataEntry } from 'lib/ops/synced-metadata-store-ops.js'; import type { ClientDBThreadActivityEntry } from 'lib/ops/thread-activity-store-ops.js'; import type { ClientDBUserInfo } from 'lib/ops/user-store-ops.js'; import type { ClientDBDraftInfo } from 'lib/types/draft-types.js'; -import type { ReceivedMessageToDevice } from 'lib/types/sqlite-types.js'; +import type { InboundP2PMessage } from 'lib/types/sqlite-types.js'; import { type WebClientDBThreadInfo, type NullableString, type NullableInt, } from './entities.js'; export type WebMessage = { +id: string, +localID: NullableString, +thread: string, +user: string, +type: number, +futureType: NullableInt, +content: NullableString, +time: string, }; export type Media = { +id: string, +container: string, +thread: string, +uri: string, +type: 'photo' | 'video', +extras: string, }; export type OlmPersistSession = { +targetDeviceID: string, +sessionData: string, +version: number, }; export type ClientMessageToDevice = { +messageID: string, +deviceID: string, +userID: string, +timestamp: string, +plaintext: string, +ciphertext: string, }; declare export class SQLiteQueryExecutor { constructor(sqliteFilePath: string): void; updateDraft(key: string, text: string): void; moveDraft(oldKey: string, newKey: string): boolean; getAllDrafts(): ClientDBDraftInfo[]; removeAllDrafts(): void; removeDrafts(ids: $ReadOnlyArray): void; getAllMessagesWeb(): $ReadOnlyArray<{ +message: WebMessage, +medias: $ReadOnlyArray, }>; removeAllMessages(): void; removeMessages(ids: $ReadOnlyArray): void; removeMessagesForThreads(threadIDs: $ReadOnlyArray): void; replaceMessageWeb(message: WebMessage): void; rekeyMessage(from: string, to: string): void; removeAllMedia(): void; removeMediaForThreads(threadIDs: $ReadOnlyArray): void; removeMediaForMessages(msgIDs: $ReadOnlyArray): void; removeMediaForMessage(msgID: string): void; replaceMedia(media: Media): void; rekeyMediaContainers(from: string, to: string): void; replaceMessageStoreThreads( threads: $ReadOnlyArray<{ +id: string, +startReached: number }>, ): void; removeMessageStoreThreads($ReadOnlyArray): void; getAllMessageStoreThreads(): $ReadOnlyArray<{ +id: string, +startReached: number, }>; removeAllMessageStoreThreads(): void; setMetadata(entryName: string, data: string): void; clearMetadata(entryName: string): void; getMetadata(entryName: string): string; replaceReport(report: ClientDBReport): void; removeReports(ids: $ReadOnlyArray): void; removeAllReports(): void; getAllReports(): ClientDBReport[]; setPersistStorageItem(key: string, item: string): void; removePersistStorageItem(key: string): void; getPersistStorageItem(key: string): string; replaceUser(userInfo: ClientDBUserInfo): void; removeUsers(ids: $ReadOnlyArray): void; removeAllUsers(): void; getAllUsers(): ClientDBUserInfo[]; replaceThreadWeb(thread: WebClientDBThreadInfo): void; removeThreads(ids: $ReadOnlyArray): void; removeAllThreads(): void; getAllThreadsWeb(): WebClientDBThreadInfo[]; replaceKeyserver(keyserverInfo: ClientDBKeyserverInfo): void; removeKeyservers(ids: $ReadOnlyArray): void; removeAllKeyservers(): void; getAllKeyservers(): ClientDBKeyserverInfo[]; replaceCommunity(communityInfo: ClientDBCommunityInfo): void; removeCommunities(ids: $ReadOnlyArray): void; removeAllCommunities(): void; getAllCommunities(): ClientDBCommunityInfo[]; replaceIntegrityThreadHashes( threadHashes: $ReadOnlyArray, ): void; removeIntegrityThreadHashes(ids: $ReadOnlyArray): void; removeAllIntegrityThreadHashes(): void; getAllIntegrityThreadHashes(): ClientDBIntegrityThreadHash[]; replaceSyncedMetadataEntry( syncedMetadataEntry: ClientDBSyncedMetadataEntry, ): void; removeSyncedMetadata(names: $ReadOnlyArray): void; removeAllSyncedMetadata(): void; getAllSyncedMetadata(): ClientDBSyncedMetadataEntry[]; replaceAuxUserInfo(auxUserInfo: ClientDBAuxUserInfo): void; removeAuxUserInfos(ids: $ReadOnlyArray): void; removeAllAuxUserInfos(): void; getAllAuxUserInfos(): ClientDBAuxUserInfo[]; replaceThreadActivityEntry( threadActivityEntry: ClientDBThreadActivityEntry, ): void; removeThreadActivityEntries(ids: $ReadOnlyArray): void; removeAllThreadActivityEntries(): void; getAllThreadActivityEntries(): ClientDBThreadActivityEntry[]; beginTransaction(): void; commitTransaction(): void; rollbackTransaction(): void; getContentAccountID(): number; getNotifsAccountID(): number; getOlmPersistAccountDataWeb(accountID: number): NullableString; getOlmPersistSessionsData(): $ReadOnlyArray; storeOlmPersistAccount(accountID: number, accountData: string): void; storeOlmPersistSession(session: OlmPersistSession): void; restoreFromMainCompaction( mainCompactionPath: string, mainCompactionEncryptionKey: string, ): void; restoreFromBackupLog(backupLog: Uint8Array): void; addMessagesToDevice(messages: $ReadOnlyArray): void; removeMessagesToDeviceOlderThan( lastConfirmedMessage: ClientMessageToDevice, ): void; removeAllMessagesForDevice(deviceID: string): void; getAllMessagesToDevice( deviceID: string, ): $ReadOnlyArray; - addReceivedMessageToDevice(message: ReceivedMessageToDevice): void; - getAllReceivedMessageToDevice(): $ReadOnlyArray; - removeReceivedMessagesToDevice(ids: $ReadOnlyArray): void; + addInboundP2PMessage(message: InboundP2PMessage): void; + getAllInboundP2PMessage(): $ReadOnlyArray; + removeInboundP2PMessages(ids: $ReadOnlyArray): void; // method is provided to manually signal that a C++ object // is no longer needed and can be deleted delete(): void; } export type SQLiteQueryExecutorType = typeof SQLiteQueryExecutor; diff --git a/web/shared-worker/worker/shared-worker.js b/web/shared-worker/worker/shared-worker.js index b6d7bb730..356b52167 100644 --- a/web/shared-worker/worker/shared-worker.js +++ b/web/shared-worker/worker/shared-worker.js @@ -1,365 +1,364 @@ // @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 { 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(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 { // 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_RECEIVED_MESSAGES_TO_DEVICE + message.type === workerRequestMessageTypes.GET_INBOUND_P2P_MESSAGES ) { return { - type: workerResponseMessageTypes.GET_RECEIVED_MESSAGES_TO_DEVICE, - messages: sqliteQueryExecutor.getAllReceivedMessageToDevice(), + type: workerResponseMessageTypes.GET_INBOUND_P2P_MESSAGES, + messages: sqliteQueryExecutor.getAllInboundP2PMessage(), }; } // 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_RECEIVED_MESSAGES_TO_DEVICE + message.type === workerRequestMessageTypes.REMOVE_INBOUND_P2P_MESSAGES ) { - sqliteQueryExecutor.removeReceivedMessagesToDevice(message.ids); + sqliteQueryExecutor.removeInboundP2PMessages(message.ids); } persistNeeded = true; if (!persistInProgress) { void persist(); } return result; } let currentlyProcessedMessage: ?Promise = 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/shared-worker/worker/worker-crypto.js b/web/shared-worker/worker/worker-crypto.js index ef3d1103a..bc0b79870 100644 --- a/web/shared-worker/worker/worker-crypto.js +++ b/web/shared-worker/worker/worker-crypto.js @@ -1,806 +1,806 @@ // @flow import olm, { type Utility } from '@commapp/olm'; import localforage from 'localforage'; import uuid from 'uuid'; import { initialEncryptedMessageContent } from 'lib/shared/crypto-utils.js'; import { hasMinCodeVersion } from 'lib/shared/version-utils.js'; import { type OLMIdentityKeys, type PickledOLMAccount, type IdentityKeysBlob, type SignedIdentityKeysBlob, type OlmAPI, type OneTimeKeysResultValues, type ClientPublicKeys, type NotificationsOlmDataType, type EncryptedData, type OutboundSessionCreationResult, } from 'lib/types/crypto-types.js'; import type { PlatformDetails } from 'lib/types/device-types.js'; import type { IdentityNewDeviceKeyUpload, IdentityExistingDeviceKeyUpload, } from 'lib/types/identity-service-types.js'; import type { OlmSessionInitializationInfo } from 'lib/types/request-types.js'; -import type { ReceivedMessageToDevice } from 'lib/types/sqlite-types.js'; +import type { InboundP2PMessage } from 'lib/types/sqlite-types.js'; import { getMessageForException } from 'lib/utils/errors.js'; import { entries } from 'lib/utils/objects.js'; import { retrieveAccountKeysSet, getAccountOneTimeKeys, getAccountPrekeysSet, shouldForgetPrekey, shouldRotatePrekey, retrieveIdentityKeysAndPrekeys, olmSessionErrors, } from 'lib/utils/olm-utils.js'; import { getIdentityClient } from './identity-client.js'; import { getProcessingStoreOpsExceptionMessage } from './process-operations.js'; import { getDBModule, getSQLiteQueryExecutor, getPlatformDetails, } from './worker-database.js'; import { encryptData, exportKeyToJWK, generateCryptoKey, } from '../../crypto/aes-gcm-crypto-utils.js'; import { getOlmDataContentKeyForCookie, getOlmEncryptionKeyDBLabelForCookie, } from '../../push-notif/notif-crypto-utils.js'; import { type WorkerRequestMessage, type WorkerResponseMessage, workerRequestMessageTypes, workerResponseMessageTypes, type LegacyCryptoStore, } from '../../types/worker-types.js'; import type { OlmPersistSession } from '../types/sqlite-query-executor.js'; import { isDesktopSafari } from '../utils/db-utils.js'; type OlmSession = { +session: olm.Session, +version: number }; type OlmSessions = { [deviceID: string]: OlmSession, }; type WorkerCryptoStore = { +contentAccountPickleKey: string, +contentAccount: olm.Account, +contentSessions: OlmSessions, +notificationAccountPickleKey: string, +notificationAccount: olm.Account, }; let cryptoStore: ?WorkerCryptoStore = null; let olmUtility: ?Utility = null; function clearCryptoStore() { cryptoStore = null; } function persistCryptoStore(withoutTransaction: boolean = false) { const sqliteQueryExecutor = getSQLiteQueryExecutor(); const dbModule = getDBModule(); if (!sqliteQueryExecutor || !dbModule) { throw new Error( "Couldn't persist crypto store because database is not initialized", ); } if (!cryptoStore) { throw new Error("Couldn't persist crypto store because it doesn't exist"); } const { contentAccountPickleKey, contentAccount, contentSessions, notificationAccountPickleKey, notificationAccount, } = cryptoStore; const pickledContentAccount: PickledOLMAccount = { picklingKey: contentAccountPickleKey, pickledAccount: contentAccount.pickle(contentAccountPickleKey), }; const pickledContentSessions: OlmPersistSession[] = entries( contentSessions, ).map(([targetDeviceID, sessionData]) => ({ targetDeviceID, sessionData: sessionData.session.pickle(contentAccountPickleKey), version: sessionData.version, })); const pickledNotificationAccount: PickledOLMAccount = { picklingKey: notificationAccountPickleKey, pickledAccount: notificationAccount.pickle(notificationAccountPickleKey), }; try { if (!withoutTransaction) { sqliteQueryExecutor.beginTransaction(); } sqliteQueryExecutor.storeOlmPersistAccount( sqliteQueryExecutor.getContentAccountID(), JSON.stringify(pickledContentAccount), ); for (const pickledSession of pickledContentSessions) { sqliteQueryExecutor.storeOlmPersistSession(pickledSession); } sqliteQueryExecutor.storeOlmPersistAccount( sqliteQueryExecutor.getNotifsAccountID(), JSON.stringify(pickledNotificationAccount), ); if (!withoutTransaction) { sqliteQueryExecutor.commitTransaction(); } } catch (err) { if (!withoutTransaction) { sqliteQueryExecutor.rollbackTransaction(); } throw new Error(getProcessingStoreOpsExceptionMessage(err, dbModule)); } } function getOrCreateOlmAccount(accountIDInDB: number): { +picklingKey: string, +account: olm.Account, } { const sqliteQueryExecutor = getSQLiteQueryExecutor(); const dbModule = getDBModule(); if (!sqliteQueryExecutor || !dbModule) { throw new Error('Database not initialized'); } const account = new olm.Account(); let picklingKey; let accountDBString; try { accountDBString = sqliteQueryExecutor.getOlmPersistAccountDataWeb(accountIDInDB); } catch (err) { throw new Error(getProcessingStoreOpsExceptionMessage(err, dbModule)); } if (accountDBString.isNull) { picklingKey = uuid.v4(); account.create(); } else { const dbAccount: PickledOLMAccount = JSON.parse(accountDBString.value); picklingKey = dbAccount.picklingKey; account.unpickle(picklingKey, dbAccount.pickledAccount); } return { picklingKey, account }; } function getOlmSessions(picklingKey: string): OlmSessions { const sqliteQueryExecutor = getSQLiteQueryExecutor(); const dbModule = getDBModule(); if (!sqliteQueryExecutor || !dbModule) { throw new Error( "Couldn't get olm sessions because database is not initialized", ); } let dbSessionsData; try { dbSessionsData = sqliteQueryExecutor.getOlmPersistSessionsData(); } catch (err) { throw new Error(getProcessingStoreOpsExceptionMessage(err, dbModule)); } const sessionsData: OlmSessions = {}; for (const persistedSession: OlmPersistSession of dbSessionsData) { const { sessionData, version } = persistedSession; const session = new olm.Session(); session.unpickle(picklingKey, sessionData); sessionsData[persistedSession.targetDeviceID] = { session, version, }; } return sessionsData; } function unpickleInitialCryptoStoreAccount( account: PickledOLMAccount, ): olm.Account { const { picklingKey, pickledAccount } = account; const olmAccount = new olm.Account(); olmAccount.unpickle(picklingKey, pickledAccount); return olmAccount; } async function initializeCryptoAccount( olmWasmPath: string, initialCryptoStore: ?LegacyCryptoStore, ) { const sqliteQueryExecutor = getSQLiteQueryExecutor(); if (!sqliteQueryExecutor) { throw new Error('Database not initialized'); } await olm.init({ locateFile: () => olmWasmPath }); olmUtility = new olm.Utility(); if (initialCryptoStore) { cryptoStore = { contentAccountPickleKey: initialCryptoStore.primaryAccount.picklingKey, contentAccount: unpickleInitialCryptoStoreAccount( initialCryptoStore.primaryAccount, ), contentSessions: {}, notificationAccountPickleKey: initialCryptoStore.notificationAccount.picklingKey, notificationAccount: unpickleInitialCryptoStoreAccount( initialCryptoStore.notificationAccount, ), }; persistCryptoStore(); return; } await olmAPI.initializeCryptoAccount(); } async function processAppOlmApiRequest( message: WorkerRequestMessage, ): Promise { if (message.type === workerRequestMessageTypes.INITIALIZE_CRYPTO_ACCOUNT) { await initializeCryptoAccount( message.olmWasmPath, message.initialCryptoStore, ); } else if (message.type === workerRequestMessageTypes.CALL_OLM_API_METHOD) { const method: (...$ReadOnlyArray) => mixed = (olmAPI[ message.method ]: any); // Flow doesn't allow us to bind the (stringified) method name with // the argument types so we need to pass the args as mixed. const result = await method(...message.args); return { type: workerResponseMessageTypes.CALL_OLM_API_METHOD, result, }; } return undefined; } function getSignedIdentityKeysBlob(): SignedIdentityKeysBlob { if (!cryptoStore) { throw new Error('Crypto account not initialized'); } const { contentAccount, notificationAccount } = cryptoStore; const identityKeysBlob: IdentityKeysBlob = { notificationIdentityPublicKeys: JSON.parse( notificationAccount.identity_keys(), ), primaryIdentityPublicKeys: JSON.parse(contentAccount.identity_keys()), }; const payloadToBeSigned: string = JSON.stringify(identityKeysBlob); const signedIdentityKeysBlob: SignedIdentityKeysBlob = { payload: payloadToBeSigned, signature: contentAccount.sign(payloadToBeSigned), }; return signedIdentityKeysBlob; } function getNewDeviceKeyUpload(): IdentityNewDeviceKeyUpload { if (!cryptoStore) { throw new Error('Crypto account not initialized'); } const { contentAccount, notificationAccount } = cryptoStore; const signedIdentityKeysBlob = getSignedIdentityKeysBlob(); const primaryAccountKeysSet = retrieveAccountKeysSet(contentAccount); const notificationAccountKeysSet = retrieveAccountKeysSet(notificationAccount); persistCryptoStore(); return { keyPayload: signedIdentityKeysBlob.payload, keyPayloadSignature: signedIdentityKeysBlob.signature, contentPrekey: primaryAccountKeysSet.prekey, contentPrekeySignature: primaryAccountKeysSet.prekeySignature, notifPrekey: notificationAccountKeysSet.prekey, notifPrekeySignature: notificationAccountKeysSet.prekeySignature, contentOneTimeKeys: primaryAccountKeysSet.oneTimeKeys, notifOneTimeKeys: notificationAccountKeysSet.oneTimeKeys, }; } function getExistingDeviceKeyUpload(): IdentityExistingDeviceKeyUpload { if (!cryptoStore) { throw new Error('Crypto account not initialized'); } const { contentAccount, notificationAccount } = cryptoStore; const signedIdentityKeysBlob = getSignedIdentityKeysBlob(); const { prekey: contentPrekey, prekeySignature: contentPrekeySignature } = retrieveIdentityKeysAndPrekeys(contentAccount); const { prekey: notifPrekey, prekeySignature: notifPrekeySignature } = retrieveIdentityKeysAndPrekeys(notificationAccount); persistCryptoStore(); return { keyPayload: signedIdentityKeysBlob.payload, keyPayloadSignature: signedIdentityKeysBlob.signature, contentPrekey, contentPrekeySignature, notifPrekey, notifPrekeySignature, }; } function getNotifsPersistenceKeys( cookie: ?string, keyserverID: string, platformDetails: PlatformDetails, ) { if (hasMinCodeVersion(platformDetails, { majorDesktop: 12 })) { return { notifsOlmDataEncryptionKeyDBLabel: getOlmEncryptionKeyDBLabelForCookie( cookie, keyserverID, ), notifsOlmDataContentKey: getOlmDataContentKeyForCookie( cookie, keyserverID, ), }; } else { return { notifsOlmDataEncryptionKeyDBLabel: getOlmEncryptionKeyDBLabelForCookie(cookie), notifsOlmDataContentKey: getOlmDataContentKeyForCookie(cookie), }; } } async function reassignLocalForageItem(source: string, destination: string) { const value = await localforage.getItem(source); if (!value) { return; } const valueAtDestination = await localforage.getItem(destination); if (!valueAtDestination) { await localforage.setItem(destination, value); } await localforage.removeItem(source); } const olmAPI: OlmAPI = { async initializeCryptoAccount(): Promise { const sqliteQueryExecutor = getSQLiteQueryExecutor(); if (!sqliteQueryExecutor) { throw new Error('Database not initialized'); } const contentAccountResult = getOrCreateOlmAccount( sqliteQueryExecutor.getContentAccountID(), ); const notificationAccountResult = getOrCreateOlmAccount( sqliteQueryExecutor.getNotifsAccountID(), ); const contentSessions = getOlmSessions(contentAccountResult.picklingKey); cryptoStore = { contentAccountPickleKey: contentAccountResult.picklingKey, contentAccount: contentAccountResult.account, contentSessions, notificationAccountPickleKey: notificationAccountResult.picklingKey, notificationAccount: notificationAccountResult.account, }; persistCryptoStore(); }, async getUserPublicKey(): Promise { if (!cryptoStore) { throw new Error('Crypto account not initialized'); } const { contentAccount, notificationAccount } = cryptoStore; const { payload, signature } = getSignedIdentityKeysBlob(); return { primaryIdentityPublicKeys: JSON.parse(contentAccount.identity_keys()), notificationIdentityPublicKeys: JSON.parse( notificationAccount.identity_keys(), ), blobPayload: payload, signature, }; }, async encrypt(content: string, deviceID: string): Promise { if (!cryptoStore) { throw new Error('Crypto account not initialized'); } const olmSession = cryptoStore.contentSessions[deviceID]; if (!olmSession) { throw new Error(`No session for deviceID: ${deviceID}`); } const encryptedContent = olmSession.session.encrypt(content); persistCryptoStore(); return { message: encryptedContent.body, messageType: encryptedContent.type, }; }, async decrypt( encryptedData: EncryptedData, deviceID: string, ): Promise { if (!cryptoStore) { throw new Error('Crypto account not initialized'); } const olmSession = cryptoStore.contentSessions[deviceID]; if (!olmSession) { throw new Error(`No session for deviceID: ${deviceID}`); } const result = olmSession.session.decrypt( encryptedData.messageType, encryptedData.message, ); persistCryptoStore(); return result; }, async decryptSequentialAndPersist( encryptedData: EncryptedData, deviceID: string, messageID: string, ): Promise { if (!cryptoStore) { throw new Error('Crypto account not initialized'); } const olmSession = cryptoStore.contentSessions[deviceID]; if (!olmSession) { throw new Error(`No session for deviceID: ${deviceID}`); } const result = olmSession.session.decrypt_sequential( encryptedData.messageType, encryptedData.message, ); const sqliteQueryExecutor = getSQLiteQueryExecutor(); const dbModule = getDBModule(); if (!sqliteQueryExecutor || !dbModule) { throw new Error( "Couldn't persist crypto store because database is not initialized", ); } - const receivedMessage: ReceivedMessageToDevice = { + const receivedMessage: InboundP2PMessage = { messageID, senderDeviceID: deviceID, plaintext: result, status: 'decrypted', }; sqliteQueryExecutor.beginTransaction(); try { - sqliteQueryExecutor.addReceivedMessageToDevice(receivedMessage); + sqliteQueryExecutor.addInboundP2PMessage(receivedMessage); persistCryptoStore(true); sqliteQueryExecutor.commitTransaction(); } catch (e) { sqliteQueryExecutor.rollbackTransaction(); throw e; } return result; }, async contentInboundSessionCreator( contentIdentityKeys: OLMIdentityKeys, initialEncryptedData: EncryptedData, sessionVersion: number, overwrite: boolean, ): Promise { if (!cryptoStore) { throw new Error('Crypto account not initialized'); } const { contentAccount, contentSessions } = cryptoStore; const existingSession = contentSessions[contentIdentityKeys.ed25519]; if (existingSession) { if (!overwrite && existingSession.version > sessionVersion) { throw new Error(olmSessionErrors.alreadyCreated); } else if (!overwrite && existingSession.version === sessionVersion) { throw new Error(olmSessionErrors.raceCondition); } } const session = new olm.Session(); session.create_inbound_from( contentAccount, contentIdentityKeys.curve25519, initialEncryptedData.message, ); contentAccount.remove_one_time_keys(session); const initialEncryptedMessage = session.decrypt( initialEncryptedData.messageType, initialEncryptedData.message, ); contentSessions[contentIdentityKeys.ed25519] = { session, version: sessionVersion, }; persistCryptoStore(); return initialEncryptedMessage; }, async contentOutboundSessionCreator( contentIdentityKeys: OLMIdentityKeys, contentInitializationInfo: OlmSessionInitializationInfo, ): Promise { if (!cryptoStore) { throw new Error('Crypto account not initialized'); } const { contentAccount, contentSessions } = cryptoStore; const existingSession = contentSessions[contentIdentityKeys.ed25519]; const session = new olm.Session(); session.create_outbound( contentAccount, contentIdentityKeys.curve25519, contentIdentityKeys.ed25519, contentInitializationInfo.prekey, contentInitializationInfo.prekeySignature, contentInitializationInfo.oneTimeKey, ); const initialEncryptedData = session.encrypt( JSON.stringify(initialEncryptedMessageContent), ); const newSessionVersion = existingSession ? existingSession.version + 1 : 1; contentSessions[contentIdentityKeys.ed25519] = { session, version: newSessionVersion, }; persistCryptoStore(); const encryptedData: EncryptedData = { message: initialEncryptedData.body, messageType: initialEncryptedData.type, }; return { encryptedData, sessionVersion: newSessionVersion }; }, async notificationsSessionCreator( cookie: ?string, notificationsIdentityKeys: OLMIdentityKeys, notificationsInitializationInfo: OlmSessionInitializationInfo, keyserverID: string, ): Promise { const platformDetails = getPlatformDetails(); if (!platformDetails) { throw new Error('Worker not initialized'); } if (!cryptoStore) { throw new Error('Crypto account not initialized'); } const { notificationAccountPickleKey, notificationAccount } = cryptoStore; const encryptionKey = await generateCryptoKey({ extractable: isDesktopSafari, }); const notificationsPrekey = notificationsInitializationInfo.prekey; const session = new olm.Session(); session.create_outbound( notificationAccount, notificationsIdentityKeys.curve25519, notificationsIdentityKeys.ed25519, notificationsPrekey, notificationsInitializationInfo.prekeySignature, notificationsInitializationInfo.oneTimeKey, ); const { body: initialNotificationsEncryptedMessage } = session.encrypt( JSON.stringify(initialEncryptedMessageContent), ); const mainSession = session.pickle(notificationAccountPickleKey); const notificationsOlmData: NotificationsOlmDataType = { mainSession, pendingSessionUpdate: mainSession, updateCreationTimestamp: Date.now(), picklingKey: notificationAccountPickleKey, }; const encryptedOlmData = await encryptData( new TextEncoder().encode(JSON.stringify(notificationsOlmData)), encryptionKey, ); const { notifsOlmDataContentKey, notifsOlmDataEncryptionKeyDBLabel } = getNotifsPersistenceKeys(cookie, keyserverID, platformDetails); const persistEncryptionKeyPromise = (async () => { let cryptoKeyPersistentForm; if (isDesktopSafari) { // Safari doesn't support structured clone algorithm in service // worker context so we have to store CryptoKey as JSON cryptoKeyPersistentForm = await exportKeyToJWK(encryptionKey); } else { cryptoKeyPersistentForm = encryptionKey; } await localforage.setItem( notifsOlmDataEncryptionKeyDBLabel, cryptoKeyPersistentForm, ); })(); await Promise.all([ localforage.setItem(notifsOlmDataContentKey, encryptedOlmData), persistEncryptionKeyPromise, ]); return initialNotificationsEncryptedMessage; }, async reassignNotificationsSession( prevCookie: ?string, newCookie: ?string, keyserverID: string, ): Promise { const platformDetails = getPlatformDetails(); if (!platformDetails) { throw new Error('Worker not initialized'); } const prevPersistenceKeys = getNotifsPersistenceKeys( prevCookie, keyserverID, platformDetails, ); const newPersistenceKeys = getNotifsPersistenceKeys( newCookie, keyserverID, platformDetails, ); await Promise.all([ reassignLocalForageItem( prevPersistenceKeys.notifsOlmDataContentKey, newPersistenceKeys.notifsOlmDataContentKey, ), reassignLocalForageItem( prevPersistenceKeys.notifsOlmDataEncryptionKeyDBLabel, newPersistenceKeys.notifsOlmDataEncryptionKeyDBLabel, ), ]); }, async getOneTimeKeys(numberOfKeys: number): Promise { if (!cryptoStore) { throw new Error('Crypto account not initialized'); } const { contentAccount, notificationAccount } = cryptoStore; const contentOneTimeKeys = getAccountOneTimeKeys( contentAccount, numberOfKeys, ); contentAccount.mark_keys_as_published(); const notificationsOneTimeKeys = getAccountOneTimeKeys( notificationAccount, numberOfKeys, ); notificationAccount.mark_keys_as_published(); persistCryptoStore(); return { contentOneTimeKeys, notificationsOneTimeKeys }; }, async validateAndUploadPrekeys(authMetadata): Promise { const { userID, deviceID, accessToken } = authMetadata; if (!userID || !deviceID || !accessToken) { return; } const identityClient = getIdentityClient(); if (!identityClient) { throw new Error('Identity client not initialized'); } if (!cryptoStore) { throw new Error('Crypto account not initialized'); } const { contentAccount, notificationAccount } = cryptoStore; // Content and notification accounts' keys are always rotated at the same // time so we only need to check one of them. if (shouldRotatePrekey(contentAccount)) { contentAccount.generate_prekey(); notificationAccount.generate_prekey(); } if (shouldForgetPrekey(contentAccount)) { contentAccount.forget_old_prekey(); notificationAccount.forget_old_prekey(); } persistCryptoStore(); if (!contentAccount.unpublished_prekey()) { return; } const { prekey: notifPrekey, prekeySignature: notifPrekeySignature } = getAccountPrekeysSet(notificationAccount); const { prekey: contentPrekey, prekeySignature: contentPrekeySignature } = getAccountPrekeysSet(contentAccount); if (!notifPrekeySignature || !contentPrekeySignature) { throw new Error('Prekey signature is missing'); } await identityClient.publishWebPrekeys({ contentPrekey, contentPrekeySignature, notifPrekey, notifPrekeySignature, }); contentAccount.mark_prekey_as_published(); notificationAccount.mark_prekey_as_published(); persistCryptoStore(); }, async signMessage(message: string): Promise { if (!cryptoStore) { throw new Error('Crypto account not initialized'); } const { contentAccount } = cryptoStore; return contentAccount.sign(message); }, async verifyMessage( message: string, signature: string, signingPublicKey: string, ): Promise { if (!olmUtility) { throw new Error('Crypto account not initialized'); } try { olmUtility.ed25519_verify(signingPublicKey, message, signature); return true; } catch (err) { const isSignatureInvalid = getMessageForException(err)?.includes('BAD_MESSAGE_MAC'); if (isSignatureInvalid) { return false; } throw err; } }, }; export { clearCryptoStore, processAppOlmApiRequest, getSignedIdentityKeysBlob, getNewDeviceKeyUpload, getExistingDeviceKeyUpload, }; diff --git a/web/types/worker-types.js b/web/types/worker-types.js index 2ce484104..11675c874 100644 --- a/web/types/worker-types.js +++ b/web/types/worker-types.js @@ -1,257 +1,257 @@ // @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 { ReceivedMessageToDevice } from 'lib/types/sqlite-types.js'; +import type { InboundP2PMessage } 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_RECEIVED_MESSAGES_TO_DEVICE: 16, - REMOVE_RECEIVED_MESSAGES_TO_DEVICE: 17, + GET_INBOUND_P2P_MESSAGES: 16, + REMOVE_INBOUND_P2P_MESSAGES: 17, }); export const workerWriteRequests: $ReadOnlyArray = [ 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_RECEIVED_MESSAGES_TO_DEVICE, + workerRequestMessageTypes.REMOVE_INBOUND_P2P_MESSAGES, ]; export const workerOlmAPIRequests: $ReadOnlyArray = [ workerRequestMessageTypes.INITIALIZE_CRYPTO_ACCOUNT, workerRequestMessageTypes.CALL_OLM_API_METHOD, ]; export const workerIdentityClientRequests: $ReadOnlyArray = [ 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, +args: $ReadOnlyArray, }; export type CallOLMApiMethodRequestMessage = { +type: 15, +method: $Keys, +args: $ReadOnlyArray, }; -export type GetReceivedMessagesToDeviceRequestMessage = { +export type GetInboundP2PMessagesRequestMessage = { +type: 16, }; -export type RemoveReceivedMessagesToDeviceRequestMessage = { +export type RemoveInboundP2PMessagesRequestMessage = { +type: 17, +ids: $ReadOnlyArray, }; export type WorkerRequestMessage = | PingWorkerRequestMessage | InitWorkerRequestMessage | GenerateDatabaseEncryptionKeyRequestMessage | ProcessStoreOperationsRequestMessage | GetClientStoreRequestMessage | SetCurrentUserIDRequestMessage | GetCurrentUserIDRequestMessage | GetPersistStorageItemRequestMessage | SetPersistStorageItemRequestMessage | RemovePersistStorageItemRequestMessage | ClearSensitiveDataRequestMessage | BackupRestoreRequestMessage | InitializeCryptoAccountRequestMessage | CreateIdentityServiceClientRequestMessage | CallIdentityClientMethodRequestMessage | CallOLMApiMethodRequestMessage - | GetReceivedMessagesToDeviceRequestMessage - | RemoveReceivedMessagesToDeviceRequestMessage; + | GetInboundP2PMessagesRequestMessage + | RemoveInboundP2PMessagesRequestMessage; 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_RECEIVED_MESSAGES_TO_DEVICE: 6, + GET_INBOUND_P2P_MESSAGES: 6, }); 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 GetReceivedMessagesToDeviceResponseMessage = { +export type GetInboundP2PMessagesResponseMessage = { +type: 6, - +messages: $ReadOnlyArray, + +messages: $ReadOnlyArray, }; export type WorkerResponseMessage = | PongWorkerResponseMessage | ClientStoreResponseMessage | GetCurrentUserIDResponseMessage | GetPersistStorageItemResponseMessage | CallIdentityClientMethodResponseMessage | CallOLMApiMethodResponseMessage - | GetReceivedMessagesToDeviceResponseMessage; + | GetInboundP2PMessagesResponseMessage; export type WorkerResponseProxyMessage = { +id?: number, +message?: WorkerResponseMessage, +error?: string, }; // SharedWorker types export type SharedWorkerMessageEvent = MessageEvent & { +ports: $ReadOnlyArray, ... };