diff --git a/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp b/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp index 51bb20656..86ef4738a 100644 --- a/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp +++ b/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp @@ -1,1306 +1,1306 @@ #include "CommCoreModule.h" #include "../Notifications/BackgroundDataStorage/NotificationsCryptoModule.h" #include "BaseDataStore.h" #include "DatabaseManager.h" #include "InternalModules/GlobalDBSingleton.h" #include "InternalModules/RustPromiseManager.h" #include "NativeModuleUtils.h" #include "TerminateApp.h" #include #include #include #include #include "lib.rs.h" #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; 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(); } 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)); this->jsInvoker_->invokeAsync([&innerRt, draftsVectorPtr, messagesVectorPtr, threadsVectorPtr, messageStoreThreadsVectorPtr, reportStoreVectorPtr, userStoreVectorPtr, error, promise, draftStore = this->draftStore, threadStore = this->threadStore, messageStore = this->messageStore, reportStore = this->reportStore, userStore = this->userStore]() { 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); 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); 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; } jsi::Value CommCoreModule::processDraftStoreOperations( jsi::Runtime &rt, jsi::Array operations) { return this->draftStore.processStoreOperations(rt, std::move(operations)); } jsi::Value CommCoreModule::processMessageStoreOperations( jsi::Runtime &rt, jsi::Array operations) { return this->messageStore.processStoreOperations(rt, std::move(operations)); } 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; } jsi::Value CommCoreModule::processThreadStoreOperations( jsi::Runtime &rt, jsi::Array operations) { return this->threadStore.processStoreOperations(rt, std::move(operations)); } void CommCoreModule::processThreadStoreOperationsSync( jsi::Runtime &rt, jsi::Array operations) { this->threadStore.processStoreOperationsSync(rt, std::move(operations)); } jsi::Value CommCoreModule::processReportStoreOperations( jsi::Runtime &rt, jsi::Array operations) { return this->reportStore.processStoreOperations(rt, std::move(operations)); } void CommCoreModule::processReportStoreOperationsSync( jsi::Runtime &rt, jsi::Array operations) { this->reportStore.processStoreOperationsSync(rt, std::move(operations)); } jsi::Value CommCoreModule::processUserStoreOperations( jsi::Runtime &rt, jsi::Array operations) { return this->userStore.processStoreOperations(rt, std::move(operations)); } void CommCoreModule::terminate(jsi::Runtime &rt) { TerminateApp::terminate(); } void CommCoreModule::persistCryptoModule() { folly::Optional storedSecretKey = CommSecureStore::get(this->secureStoreAccountDataKey); if (!storedSecretKey.hasValue()) { storedSecretKey = crypto::Tools::generateRandomString(64); CommSecureStore::set( this->secureStoreAccountDataKey, storedSecretKey.value()); } crypto::Persist newPersist = this->cryptoModule->storeAsB64(storedSecretKey.value()); std::promise persistencePromise; std::future persistenceFuture = persistencePromise.get_future(); GlobalDBSingleton::instance.scheduleOrRunCancellable( [=, &persistencePromise]() { try { DatabaseManager::getQueryExecutor().storeOlmPersistData(newPersist); persistencePromise.set_value(); } catch (std::system_error &e) { 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 persist; std::string error; try { std::optional accountData = DatabaseManager::getQueryExecutor().getOlmPersistAccountData(); if (accountData.has_value()) { persist.account = crypto::OlmBuffer(accountData->begin(), accountData->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()); persist.sessions.insert(std::make_pair( sessionsDataItem.target_user_id, sessionDataBuffer)); } } } catch (std::system_error &e) { error = e.what(); } this->cryptoThread->scheduleTask([=]() { std::string error; this->cryptoModule.reset(new crypto::CryptoModule( this->publicCryptoAccountID, storedSecretKey.value(), persist)); if (persist.isEmpty()) { crypto::Persist newPersist = this->cryptoModule->storeAsB64(storedSecretKey.value()); GlobalDBSingleton::instance.scheduleOrRunCancellable( [=]() { std::string error; try { DatabaseManager::getQueryExecutor().storeOlmPersistData( newPersist); } catch (std::system_error &e) { error = e.what(); } this->jsInvoker_->invokeAsync([=]() { if (error.size()) { promise->reject(error); return; } }); }, promise, this->jsInvoker_); } else { this->cryptoModule->restoreFromB64( storedSecretKey.value(), persist); this->jsInvoker_->invokeAsync([=]() { if (error.size()) { promise->reject(error); return; } }); } try { NotificationsCryptoModule::initializeNotificationsCryptoAccount( "Comm"); } catch (const std::exception &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::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->cryptoModule == nullptr) { error = "user has not been initialized"; } else { primaryKeysResult = this->cryptoModule->getIdentityKeys(); } try { if (!error.size()) { notificationsKeysResult = NotificationsCryptoModule::getNotificationsIdentityKeys( "Comm"); } } catch (const std::exception &e) { error = e.what(); } 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->cryptoModule->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; } jsi::Value CommCoreModule::getPrimaryOneTimeKeys( jsi::Runtime &rt, double oneTimeKeysAmount) { return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [=, &innerRt]() { std::string error; std::string result; if (this->cryptoModule == nullptr) { error = "user has not been initialized"; } else { result = this->cryptoModule->getOneTimeKeys(oneTimeKeysAmount); this->persistCryptoModule(); } this->jsInvoker_->invokeAsync([=, &innerRt]() { if (error.size()) { promise->reject(error); return; } promise->resolve(parseOLMOneTimeKeys(innerRt, result)); }); }; this->cryptoThread->scheduleTask(job); }); } jsi::Value CommCoreModule::getNotificationsOneTimeKeys( jsi::Runtime &rt, double oneTimeKeysAmount) { return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [=, &innerRt]() { std::string error; std::string result; try { result = NotificationsCryptoModule::getNotificationsOneTimeKeys( oneTimeKeysAmount, "Comm"); } catch (const std::exception &e) { error = e.what(); } this->jsInvoker_->invokeAsync([=, &innerRt]() { if (error.size()) { promise->reject(error); return; } promise->resolve(parseOLMOneTimeKeys(innerRt, result)); }); }; this->cryptoThread->scheduleTask(job); }); } jsi::Value CommCoreModule::generateAndGetPrekeys(jsi::Runtime &rt) { return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [=, &innerRt]() { std::string error; std::string contentPrekey, contentPrekeySignature, notifPrekey, notifPrekeySignature; if (this->cryptoModule == nullptr) { error = "user has not been initialized"; } else { try { contentPrekey = this->cryptoModule->generateAndGetPrekey(); contentPrekeySignature = this->cryptoModule->getPrekeySignature(); this->persistCryptoModule(); notifPrekey = NotificationsCryptoModule::generateAndGetNotificationsPrekey( "Comm"); notifPrekeySignature = NotificationsCryptoModule::getNotificationsPrekeySignature( "Comm"); } 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 oneTimeKeys) { auto identityKeysCpp{identityKeys.utf8(rt)}; auto prekeyCpp{prekey.utf8(rt)}; auto prekeySignatureCpp{prekeySignature.utf8(rt)}; auto oneTimeKeysCpp{oneTimeKeys.utf8(rt)}; return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [=, &innerRt]() { std::string error; crypto::EncryptedData result; try { result = NotificationsCryptoModule::initializeNotificationsSession( identityKeysCpp, prekeyCpp, prekeySignatureCpp, oneTimeKeysCpp, "Comm"); } 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::initializeContentOutboundSession( jsi::Runtime &rt, jsi::String identityKeys, jsi::String prekey, jsi::String prekeySignature, jsi::String oneTimeKeys, jsi::String deviceID) { auto identityKeysCpp{identityKeys.utf8(rt)}; auto prekeyCpp{prekey.utf8(rt)}; auto prekeySignatureCpp{prekeySignature.utf8(rt)}; auto oneTimeKeysCpp{oneTimeKeys.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 initialEncryptedMessage; try { this->cryptoModule->initializeOutboundForSendingSession( deviceIDCpp, std::vector( identityKeysCpp.begin(), identityKeysCpp.end()), std::vector(prekeyCpp.begin(), prekeyCpp.end()), std::vector( prekeySignatureCpp.begin(), prekeySignatureCpp.end()), std::vector( oneTimeKeysCpp.begin(), oneTimeKeysCpp.end())); const std::string initMessage = "{\"type\": \"init\"}"; initialEncryptedMessage = cryptoModule->encrypt(deviceIDCpp, initMessage); this->persistCryptoModule(); } 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{ initialEncryptedMessage.message.begin(), initialEncryptedMessage.message.end()})); }); }; this->cryptoThread->scheduleTask(job); }); } jsi::Value CommCoreModule::initializeContentInboundSession( jsi::Runtime &rt, jsi::String identityKeys, jsi::String encryptedMessage, jsi::String deviceID) { auto identityKeysCpp{identityKeys.utf8(rt)}; auto encryptedMessageCpp{encryptedMessage.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->cryptoModule->initializeInboundForReceivingSession( deviceIDCpp, std::vector( encryptedMessageCpp.begin(), encryptedMessageCpp.end()), std::vector( identityKeysCpp.begin(), identityKeysCpp.end())); crypto::EncryptedData encryptedData{std::vector( encryptedMessageCpp.begin(), encryptedMessageCpp.end())}; decryptedMessage = cryptoModule->decrypt(deviceIDCpp, encryptedData); this->persistCryptoModule(); } 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 = cryptoModule->encrypt(deviceIDCpp, messageCpp); this->persistCryptoModule(); } 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{ encryptedMessage.message.begin(), encryptedMessage.message.end()})); }); }; this->cryptoThread->scheduleTask(job); }); } jsi::Value CommCoreModule::decrypt( 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; std::string decryptedMessage; try { crypto::EncryptedData encryptedData{ std::vector(messageCpp.begin(), messageCpp.end()), ENCRYPTED_MESSAGE_TYPE}; decryptedMessage = cryptoModule->decrypt(deviceIDCpp, encryptedData); this->persistCryptoModule(); } 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); }); } 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) { 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::setCurrentUserID(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().setCurrentUserID(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::getCurrentUserID(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().getCurrentUserID(); } 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 { 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) { taskType job = [this, promise, userIDStr, deviceIDStr, accessTokenStr]() { std::string error; try { CommSecureStore::set(CommSecureStore::userID, userIDStr); CommSecureStore::set(CommSecureStore::deviceID, deviceIDStr); 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()); } }); }; GlobalDBSingleton::instance.scheduleOrRunCancellable( job, promise, this->jsInvoker_); }); } jsi::Value CommCoreModule::getCommServicesAuthMetadata(jsi::Runtime &rt) { return createPromiseAsJSIValue( rt, [this](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [this, &innerRt, 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)); } }); }; GlobalDBSingleton::instance.scheduleOrRunCancellable( job, promise, this->jsInvoker_); }); } 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) { taskType job = [this, promise, accessTokenStr]() { 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()); } }); }; GlobalDBSingleton::instance.scheduleOrRunCancellable( job, promise, this->jsInvoker_); }); } jsi::Value CommCoreModule::clearCommServicesAccessToken(jsi::Runtime &rt) { return createPromiseAsJSIValue( rt, [this](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [this, 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()); } }); }; GlobalDBSingleton::instance.scheduleOrRunCancellable( job, promise, this->jsInvoker_); }); } jsi::Value CommCoreModule::createNewBackup( jsi::Runtime &rt, jsi::String backupSecret, jsi::String userData) { std::string backupSecretStr = backupSecret.utf8(rt); std::string userDataStr = userData.utf8(rt); 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->cryptoModule->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); + {promise, this->jsInvoker_, innerRt}); ::createBackup( rust::string(backupID), rust::string(backupSecretStr), rust::string(pickleKey), rust::string(pickledAccount), rust::string(userDataStr), currentID); } else { this->jsInvoker_->invokeAsync( [=, &innerRt]() { promise->reject(error); }); } }); }); } 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); + {promise, this->jsInvoker_, innerRt}); ::restoreBackup(rust::string(backupSecretStr), currentID); }); } } // namespace comm diff --git a/native/cpp/CommonCpp/NativeModules/CommRustModule.cpp b/native/cpp/CommonCpp/NativeModules/CommRustModule.cpp index 8e294d7b6..b25d03360 100644 --- a/native/cpp/CommonCpp/NativeModules/CommRustModule.cpp +++ b/native/cpp/CommonCpp/NativeModules/CommRustModule.cpp @@ -1,411 +1,411 @@ #include "CommRustModule.h" #include "InternalModules/RustPromiseManager.h" #include "JSIRust.h" #include "lib.rs.h" #include namespace comm { using namespace facebook::react; CommRustModule::CommRustModule(std::shared_ptr jsInvoker) : CommRustModuleSchemaCxxSpecJSI(jsInvoker) { } jsi::Value CommRustModule::generateNonce(jsi::Runtime &rt) { return createPromiseAsJSIValue( rt, [this](jsi::Runtime &innerRt, std::shared_ptr promise) { std::string error; try { auto currentID = RustPromiseManager::instance.addPromise( - promise, this->jsInvoker_, innerRt); + {promise, this->jsInvoker_, innerRt}); identityGenerateNonce(currentID); } catch (const std::exception &e) { error = e.what(); }; if (!error.empty()) { this->jsInvoker_->invokeAsync( [error, promise]() { promise->reject(error); }); } }); } jsi::Value CommRustModule::registerUser( jsi::Runtime &rt, jsi::String username, jsi::String password, jsi::String keyPayload, jsi::String keyPayloadSignature, jsi::String contentPrekey, jsi::String contentPrekeySignature, jsi::String notifPrekey, jsi::String notifPrekeySignature, jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys) { auto usernameRust = jsiStringToRustString(username, rt); auto passwordRust = jsiStringToRustString(password, rt); auto keyPayloadRust = jsiStringToRustString(keyPayload, rt); auto keyPayloadSignatureRust = jsiStringToRustString(keyPayloadSignature, rt); auto contentPrekeyRust = jsiStringToRustString(contentPrekey, rt); auto contentPrekeySignatureRust = jsiStringToRustString(contentPrekeySignature, rt); auto notifPrekeyRust = jsiStringToRustString(notifPrekey, rt); auto notifPrekeySignatureRust = jsiStringToRustString(notifPrekeySignature, rt); auto contentOneTimeKeysRust = jsiStringArrayToRustVec(contentOneTimeKeys, rt); auto notifOneTimeKeysRust = jsiStringArrayToRustVec(notifOneTimeKeys, rt); return createPromiseAsJSIValue( rt, [=, this](jsi::Runtime &innerRt, std::shared_ptr promise) { std::string error; try { auto currentID = RustPromiseManager::instance.addPromise( - promise, this->jsInvoker_, innerRt); + {promise, this->jsInvoker_, innerRt}); identityRegisterUser( usernameRust, passwordRust, keyPayloadRust, keyPayloadSignatureRust, contentPrekeyRust, contentPrekeySignatureRust, notifPrekeyRust, notifPrekeySignatureRust, contentOneTimeKeysRust, notifOneTimeKeysRust, currentID); } catch (const std::exception &e) { error = e.what(); }; if (!error.empty()) { this->jsInvoker_->invokeAsync( [error, promise]() { promise->reject(error); }); } }); } jsi::Value CommRustModule::loginPasswordUser( jsi::Runtime &rt, jsi::String username, jsi::String password, jsi::String keyPayload, jsi::String keyPayloadSignature, jsi::String contentPrekey, jsi::String contentPrekeySignature, jsi::String notifPrekey, jsi::String notifPrekeySignature, jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys) { auto usernameRust = jsiStringToRustString(username, rt); auto passwordRust = jsiStringToRustString(password, rt); auto keyPayloadRust = jsiStringToRustString(keyPayload, rt); auto keyPayloadSignatureRust = jsiStringToRustString(keyPayloadSignature, rt); auto contentPrekeyRust = jsiStringToRustString(contentPrekey, rt); auto contentPrekeySignatureRust = jsiStringToRustString(contentPrekeySignature, rt); auto notifPrekeyRust = jsiStringToRustString(notifPrekey, rt); auto notifPrekeySignatureRust = jsiStringToRustString(notifPrekeySignature, rt); auto contentOneTimeKeysRust = jsiStringArrayToRustVec(contentOneTimeKeys, rt); auto notifOneTimeKeysRust = jsiStringArrayToRustVec(notifOneTimeKeys, rt); return createPromiseAsJSIValue( rt, [=, this](jsi::Runtime &innerRt, std::shared_ptr promise) { std::string error; try { auto currentID = RustPromiseManager::instance.addPromise( - promise, this->jsInvoker_, innerRt); + {promise, this->jsInvoker_, innerRt}); identityLoginPasswordUser( usernameRust, passwordRust, keyPayloadRust, keyPayloadSignatureRust, contentPrekeyRust, contentPrekeySignatureRust, notifPrekeyRust, notifPrekeySignatureRust, contentOneTimeKeysRust, notifOneTimeKeysRust, currentID); } catch (const std::exception &e) { error = e.what(); }; if (!error.empty()) { this->jsInvoker_->invokeAsync( [error, promise]() { promise->reject(error); }); } }); } jsi::Value CommRustModule::loginWalletUser( jsi::Runtime &rt, jsi::String siweMessage, jsi::String siweSignature, jsi::String keyPayload, jsi::String keyPayloadSignature, jsi::String contentPrekey, jsi::String contentPrekeySignature, jsi::String notifPrekey, jsi::String notifPrekeySignature, jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys, jsi::String socialProof) { auto siweMessageRust = jsiStringToRustString(siweMessage, rt); auto siweSignatureRust = jsiStringToRustString(siweSignature, rt); auto keyPayloadRust = jsiStringToRustString(keyPayload, rt); auto keyPayloadSignatureRust = jsiStringToRustString(keyPayloadSignature, rt); auto contentPrekeyRust = jsiStringToRustString(contentPrekey, rt); auto contentPrekeySignatureRust = jsiStringToRustString(contentPrekeySignature, rt); auto notifPrekeyRust = jsiStringToRustString(notifPrekey, rt); auto notifPrekeySignatureRust = jsiStringToRustString(notifPrekeySignature, rt); auto contentOneTimeKeysRust = jsiStringArrayToRustVec(contentOneTimeKeys, rt); auto notifOneTimeKeysRust = jsiStringArrayToRustVec(notifOneTimeKeys, rt); auto socialProofRust = jsiStringToRustString(socialProof, rt); return createPromiseAsJSIValue( rt, [=, this](jsi::Runtime &innerRt, std::shared_ptr promise) { std::string error; try { auto currentID = RustPromiseManager::instance.addPromise( - promise, this->jsInvoker_, innerRt); + {promise, this->jsInvoker_, innerRt}); identityLoginWalletUser( siweMessageRust, siweSignatureRust, keyPayloadRust, keyPayloadSignatureRust, contentPrekeyRust, contentPrekeySignatureRust, notifPrekeyRust, notifPrekeySignatureRust, contentOneTimeKeysRust, notifOneTimeKeysRust, socialProofRust, currentID); } catch (const std::exception &e) { error = e.what(); }; if (!error.empty()) { this->jsInvoker_->invokeAsync( [error, promise]() { promise->reject(error); }); } }); } jsi::Value CommRustModule::updatePassword( jsi::Runtime &rt, jsi::String userID, jsi::String deviceID, jsi::String accessToken, jsi::String password) { auto userIDRust = jsiStringToRustString(userID, rt); auto deviceIDRust = jsiStringToRustString(deviceID, rt); auto accessTokenRust = jsiStringToRustString(accessToken, rt); auto passwordRust = jsiStringToRustString(password, rt); return createPromiseAsJSIValue( rt, [=, this](jsi::Runtime &innerRt, std::shared_ptr promise) { std::string error; try { auto currentID = RustPromiseManager::instance.addPromise( - promise, this->jsInvoker_, innerRt); + {promise, this->jsInvoker_, innerRt}); identityUpdateUserPassword( userIDRust, deviceIDRust, accessTokenRust, passwordRust, currentID); } catch (const std::exception &e) { error = e.what(); }; if (!error.empty()) { this->jsInvoker_->invokeAsync( [error, promise]() { promise->reject(error); }); } }); } jsi::Value CommRustModule::deleteUser( jsi::Runtime &rt, jsi::String userID, jsi::String deviceID, jsi::String accessToken) { auto userIDRust = jsiStringToRustString(userID, rt); auto deviceIDRust = jsiStringToRustString(deviceID, rt); auto accessTokenRust = jsiStringToRustString(accessToken, rt); return createPromiseAsJSIValue( rt, [=, this](jsi::Runtime &innerRt, std::shared_ptr promise) { std::string error; try { auto currentID = RustPromiseManager::instance.addPromise( - promise, this->jsInvoker_, innerRt); + {promise, this->jsInvoker_, innerRt}); identityDeleteUser( userIDRust, deviceIDRust, accessTokenRust, currentID); } catch (const std::exception &e) { error = e.what(); }; if (!error.empty()) { this->jsInvoker_->invokeAsync( [error, promise]() { promise->reject(error); }); } }); } jsi::Value CommRustModule::getOutboundKeysForUser( jsi::Runtime &rt, jsi::String authUserID, jsi::String authDeviceID, jsi::String authAccessToken, jsi::String userID) { auto authUserIDRust = jsiStringToRustString(authUserID, rt); auto authDeviceIDRust = jsiStringToRustString(authDeviceID, rt); auto authAccessTokenRust = jsiStringToRustString(authAccessToken, rt); auto userIDRust = jsiStringToRustString(userID, rt); return createPromiseAsJSIValue( rt, [=, this](jsi::Runtime &innerRt, std::shared_ptr promise) { std::string error; try { auto currentID = RustPromiseManager::instance.addPromise( - promise, this->jsInvoker_, innerRt); + {promise, this->jsInvoker_, innerRt}); identityGetOutboundKeysForUser( authUserIDRust, authDeviceIDRust, authAccessTokenRust, userIDRust, currentID); } catch (const std::exception &e) { error = e.what(); }; if (!error.empty()) { this->jsInvoker_->invokeAsync( [error, promise]() { promise->reject(error); }); } }); } jsi::Value CommRustModule::getInboundKeysForUser( jsi::Runtime &rt, jsi::String authUserID, jsi::String authDeviceID, jsi::String authAccessToken, jsi::String userID) { auto authUserIDRust = jsiStringToRustString(authUserID, rt); auto authDeviceIDRust = jsiStringToRustString(authDeviceID, rt); auto authAccessTokenRust = jsiStringToRustString(authAccessToken, rt); auto userIDRust = jsiStringToRustString(userID, rt); return createPromiseAsJSIValue( rt, [=, this](jsi::Runtime &innerRt, std::shared_ptr promise) { std::string error; try { auto currentID = RustPromiseManager::instance.addPromise( - promise, this->jsInvoker_, innerRt); + {promise, this->jsInvoker_, innerRt}); identityGetInboundKeysForUser( authUserIDRust, authDeviceIDRust, authAccessTokenRust, userIDRust, currentID); } catch (const std::exception &e) { error = e.what(); }; if (!error.empty()) { this->jsInvoker_->invokeAsync( [error, promise]() { promise->reject(error); }); } }); } jsi::Value CommRustModule::versionSupported(jsi::Runtime &rt) { return createPromiseAsJSIValue( rt, [=, this](jsi::Runtime &innerRt, std::shared_ptr promise) { std::string error; try { auto currentID = RustPromiseManager::instance.addPromise( - promise, this->jsInvoker_, innerRt); + {promise, this->jsInvoker_, innerRt}); identityVersionSupported(currentID); } catch (const std::exception &e) { error = e.what(); }; if (!error.empty()) { this->jsInvoker_->invokeAsync( [error, promise]() { promise->reject(error); }); } }); } jsi::Value CommRustModule::uploadOneTimeKeys( jsi::Runtime &rt, jsi::String authUserID, jsi::String authDeviceID, jsi::String authAccessToken, jsi::Array contentOneTimePreKeys, jsi::Array notifOneTimePreKeys) { auto authUserIDRust = jsiStringToRustString(authUserID, rt); auto authDeviceIDRust = jsiStringToRustString(authDeviceID, rt); auto authAccessTokenRust = jsiStringToRustString(authAccessToken, rt); auto contentOneTimePreKeysRust = jsiStringArrayToRustVec(contentOneTimePreKeys, rt); auto notifOneTimePreKeysRust = jsiStringArrayToRustVec(notifOneTimePreKeys, rt); return createPromiseAsJSIValue( rt, [=, this](jsi::Runtime &innerRt, std::shared_ptr promise) { std::string error; try { auto currentID = RustPromiseManager::instance.addPromise( - promise, this->jsInvoker_, innerRt); + {promise, this->jsInvoker_, innerRt}); identityUploadOneTimeKeys( authUserIDRust, authDeviceIDRust, authAccessTokenRust, contentOneTimePreKeysRust, notifOneTimePreKeysRust, currentID); } catch (const std::exception &e) { error = e.what(); }; if (!error.empty()) { this->jsInvoker_->invokeAsync( [error, promise]() { promise->reject(error); }); } }); } jsi::Value CommRustModule::getKeyserverKeys( jsi::Runtime &rt, jsi::String authUserID, jsi::String authDeviceID, jsi::String authAccessToken, jsi::String keyserverID) { auto authUserIDRust = jsiStringToRustString(authUserID, rt); auto authDeviceIDRust = jsiStringToRustString(authDeviceID, rt); auto authAccessTokenRust = jsiStringToRustString(authAccessToken, rt); auto keyserverIDRust = jsiStringToRustString(keyserverID, rt); return createPromiseAsJSIValue( rt, [=, this](jsi::Runtime &innerRt, std::shared_ptr promise) { std::string error; try { auto currentID = RustPromiseManager::instance.addPromise( - promise, this->jsInvoker_, innerRt); + {promise, this->jsInvoker_, innerRt}); identityGetKeyserverKeys( authUserIDRust, authDeviceIDRust, authAccessTokenRust, keyserverIDRust, currentID); } catch (const std::exception &e) { error = e.what(); }; if (!error.empty()) { this->jsInvoker_->invokeAsync( [error, promise]() { promise->reject(error); }); } }); } } // namespace comm diff --git a/native/cpp/CommonCpp/NativeModules/InternalModules/RustPromiseManager.cpp b/native/cpp/CommonCpp/NativeModules/InternalModules/RustPromiseManager.cpp index be63cc59d..ae16dfbd0 100644 --- a/native/cpp/CommonCpp/NativeModules/InternalModules/RustPromiseManager.cpp +++ b/native/cpp/CommonCpp/NativeModules/InternalModules/RustPromiseManager.cpp @@ -1,65 +1,78 @@ #include "RustPromiseManager.h" +#include namespace comm { RustPromiseManager RustPromiseManager::instance; -RustPromiseManager::RustPromiseManager(){}; +RustPromiseManager::RustPromiseManager() { +} + +uint32_t RustPromiseManager::addPromise(JSIPromiseInfo info) { + if (!info.jsInvoker) { + throw std::invalid_argument("jsInvoker is missing"); + } -uint32_t RustPromiseManager::addPromise( - std::shared_ptr promise, - std::shared_ptr jsInvoker, - facebook::jsi::Runtime &rt) { uint32_t id = getNextID(); - PromiseInfo info = {promise, jsInvoker, rt}; // Acquire a lock for writing std::unique_lock lock(mutex); - promises.insert({id, info}); + promises.insert({id, std::move(info)}); + return id; +} + +uint32_t RustPromiseManager::addPromise(CPPPromiseInfo info) { + auto id = getNextID(); + std::unique_lock lock(mutex); + promises.insert({id, std::move(info)}); return id; } void RustPromiseManager::removePromise(uint32_t id) { // Acquire a lock for writing std::unique_lock lock(mutex); promises.erase(id); } void RustPromiseManager::resolvePromise(uint32_t id, folly::dynamic ret) { // Acquire a shared lock for reading std::shared_lock lock(mutex); auto it = promises.find(id); if (it == promises.end()) { return; } // Release the shared lock lock.unlock(); - auto promiseInfo = it->second; - if (promiseInfo.jsInvoker) { - promiseInfo.jsInvoker->invokeAsync([promiseInfo, ret]() { + + if (auto jsiPtr = std::get_if(&it->second)) { + jsiPtr->jsInvoker->invokeAsync([promiseInfo = *jsiPtr, ret]() { promiseInfo.promise->resolve(valueFromDynamic(promiseInfo.rt, ret)); }); - } else { - promiseInfo.promise->resolve(valueFromDynamic(promiseInfo.rt, ret)); + + } else if (auto cppPtr = std::get_if(&it->second)) { + cppPtr->promise.set_value(ret); } removePromise(id); } void RustPromiseManager::rejectPromise(uint32_t id, const std::string &error) { // Acquire a shared lock for reading std::shared_lock lock(mutex); auto it = promises.find(id); if (it == promises.end()) { return; } // Release the shared lock lock.unlock(); - if (it->second.jsInvoker) { - it->second.jsInvoker->invokeAsync( - [promise = it->second.promise, error]() { promise->reject(error); }); - } else { - it->second.promise->reject(error); + + if (auto jsiPtr = std::get_if(&it->second)) { + jsiPtr->jsInvoker->invokeAsync( + [promise = jsiPtr->promise, error]() { promise->reject(error); }); + + } else if (auto cppPtr = std::get_if(&it->second)) { + cppPtr->promise.set_exception( + std::make_exception_ptr(std::runtime_error(error))); } removePromise(id); } } // namespace comm diff --git a/native/cpp/CommonCpp/NativeModules/InternalModules/RustPromiseManager.h b/native/cpp/CommonCpp/NativeModules/InternalModules/RustPromiseManager.h index ad3f51bba..6ca855f99 100644 --- a/native/cpp/CommonCpp/NativeModules/InternalModules/RustPromiseManager.h +++ b/native/cpp/CommonCpp/NativeModules/InternalModules/RustPromiseManager.h @@ -1,40 +1,49 @@ #pragma once #include #include #include #include +#include #include +#include namespace comm { class RustPromiseManager { std::atomic id{0}; RustPromiseManager(); uint32_t getNextID() { return this->id++; }; public: static RustPromiseManager instance; - uint32_t addPromise( - std::shared_ptr promise, - std::shared_ptr jsInvoker, - facebook::jsi::Runtime &rt); - void removePromise(uint32_t id); - void resolvePromise(uint32_t id, folly::dynamic ret); - void rejectPromise(uint32_t id, const std::string &error); - struct PromiseInfo { + struct JSIPromiseInfo { std::shared_ptr promise; std::shared_ptr jsInvoker; facebook::jsi::Runtime &rt; }; + + struct CPPPromiseInfo { + std::promise promise; + }; + + using PromiseInfo = std::variant; + + uint32_t addPromise(JSIPromiseInfo info); + uint32_t addPromise(CPPPromiseInfo info); + + void removePromise(uint32_t id); + void resolvePromise(uint32_t id, folly::dynamic ret); + void rejectPromise(uint32_t id, const std::string &error); + std::unordered_map promises; std::shared_mutex mutex; }; } // namespace comm