diff --git a/native/android/app/src/main/java/app/comm/android/commservices/CommAndroidBlobClient.java b/native/android/app/src/main/java/app/comm/android/commservices/CommAndroidBlobClient.java index fe9af2862..482daa39a 100644 --- a/native/android/app/src/main/java/app/comm/android/commservices/CommAndroidBlobClient.java +++ b/native/android/app/src/main/java/app/comm/android/commservices/CommAndroidBlobClient.java @@ -1,106 +1,107 @@ package app.comm.android.commservices; import android.content.Context; import androidx.work.Constraints; import androidx.work.Data; import androidx.work.NetworkType; import androidx.work.OneTimeWorkRequest; import androidx.work.WorkManager; import androidx.work.WorkRequest; import app.comm.android.BuildConfig; import app.comm.android.fbjni.CommSecureStore; import java.io.IOException; import java.lang.OutOfMemoryError; import java.util.Base64; import java.util.concurrent.TimeUnit; import okhttp3.Call; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; import org.json.JSONException; import org.json.JSONObject; public class CommAndroidBlobClient { // The FirebaseMessagingService docs state that message // processing should complete within at most 20 seconds // window. Therefore we limit http time call to 15 seconds // https://firebase.google.com/docs/cloud-messaging/android/receive#handling_messages private static final int NOTIF_PROCESSING_TIME_LIMIT_SECONDS = 15; // OkHttp docs advise to share OkHttpClient instances // https://square.github.io/okhttp/4.x/okhttp/okhttp3/-ok-http-client/#okhttpclients-should-be-shared public static final OkHttpClient httpClient = new OkHttpClient.Builder() .callTimeout(NOTIF_PROCESSING_TIME_LIMIT_SECONDS, TimeUnit.SECONDS) .build(); public static final String BLOB_SERVICE_URL = BuildConfig.DEBUG ? "https://blob.staging.commtechnologies.org" : "https://blob.commtechnologies.org"; public static final String BLOB_HASH_KEY = "blob_hash"; public static final String BLOB_HOLDER_KEY = "holder"; public byte[] getBlobSync(String blobHash) throws IOException, JSONException { String authToken = getAuthToken(); Request request = new Request.Builder() .get() .url(BLOB_SERVICE_URL + "/blob/" + blobHash) .header("Authorization", authToken) .build(); Response response = httpClient.newCall(request).execute(); if (!response.isSuccessful()) { throw new RuntimeException( "Failed to download blob from blob service. Response error code: " + response); } return response.body().bytes(); } public void scheduleDeferredBlobDeletion( String blobHash, String blobHolder, Context context) { Constraints constraints = new Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) .build(); WorkRequest deleteBlobWorkRequest = new OneTimeWorkRequest.Builder(CommAndroidDeleteBlobWork.class) .setConstraints(constraints) .setInitialDelay( NOTIF_PROCESSING_TIME_LIMIT_SECONDS, TimeUnit.SECONDS) .setInputData(new Data.Builder() .putString(BLOB_HASH_KEY, blobHash) .putString(BLOB_HOLDER_KEY, blobHolder) .build()) .build(); WorkManager.getInstance(context).enqueue(deleteBlobWorkRequest); } public static String getAuthToken() throws JSONException { // Authentication data are retrieved on every request // since they might change while CommNotificationsHandler // thread is running so we should not rely on caching // them in memory. String userID = CommSecureStore.get("userID"); String accessToken = CommSecureStore.get("accessToken"); String deviceID = CommSecureStore.get("deviceID"); - userID = userID == null ? "" : userID; - accessToken = accessToken == null ? "" : accessToken; - deviceID = deviceID == null ? "" : deviceID; + if (userID == null || accessToken == null || deviceID == null) { + throw new RuntimeException( + "Unable to query blob service due to missing CSAT."); + } String authObjectJsonBody = new JSONObject() .put("userID", userID) .put("accessToken", accessToken) .put("deviceID", deviceID) .toString(); String encodedAuthObjectJsonBody = Base64.getEncoder().encodeToString(authObjectJsonBody.getBytes()); return "Bearer " + encodedAuthObjectJsonBody; } } diff --git a/native/ios/Comm/CommIOSServices/CommIOSBlobClient.mm b/native/ios/Comm/CommIOSServices/CommIOSBlobClient.mm index 55846845d..13d5687c3 100644 --- a/native/ios/Comm/CommIOSServices/CommIOSBlobClient.mm +++ b/native/ios/Comm/CommIOSServices/CommIOSBlobClient.mm @@ -1,297 +1,305 @@ #import "CommIOSBlobClient.h" #import "CommMMKV.h" #import "CommSecureStore.h" #import "Logger.h" #ifdef DEBUG NSString const *blobServiceAddress = @"https://blob.staging.commtechnologies.org"; #else NSString const *blobServiceAddress = @"https://blob.commtechnologies.org"; #endif int const blobServiceQueryTimeLimit = 15; const std::string mmkvBlobHolderPrefix = "BLOB_HOLDER."; // The blob service expects slightly different keys in // delete reuqest payload than we use in notif payload NSString *const blobServiceHashKey = @"blob_hash"; NSString *const blobServiceHolderKey = @"holder"; @interface CommIOSBlobClient () @property(nonatomic, strong) NSURLSession *sharedBlobServiceSession; @end @implementation CommIOSBlobClient + (id)sharedInstance { static CommIOSBlobClient *sharedBlobServiceClient = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSURLSessionConfiguration *config = [NSURLSessionConfiguration ephemeralSessionConfiguration]; [config setTimeoutIntervalForRequest:blobServiceQueryTimeLimit]; NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:nil delegateQueue:[NSOperationQueue mainQueue]]; sharedBlobServiceClient = [[self alloc] init]; sharedBlobServiceClient.sharedBlobServiceSession = session; }); return sharedBlobServiceClient; } - (NSData *)getBlobSync:(NSString *)blobHash orSetError:(NSError **)error { NSError *authTokenError = nil; NSString *authToken = [CommIOSBlobClient _getAuthTokenOrSetError:&authTokenError]; if (authTokenError) { *error = authTokenError; return nil; } NSString *blobUrlStr = [blobServiceAddress stringByAppendingString:[@"/blob/" stringByAppendingString:blobHash]]; NSURL *blobUrl = [NSURL URLWithString:blobUrlStr]; NSMutableURLRequest *blobRequest = [NSMutableURLRequest requestWithURL:blobUrl]; // This is slightly against Apple docs: // https://developer.apple.com/documentation/foundation/nsurlrequest?language=objc#1776617 // but apparently there is no other way to // do this and even Apple staff members // advice to set this field manually to // achieve token based authentication: // https://developer.apple.com/forums/thread/89811 [blobRequest setValue:authToken forHTTPHeaderField:@"Authorization"]; __block NSError *requestError = nil; __block NSData *blobContent = nil; dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); NSURLSessionDataTask *task = [self.sharedBlobServiceSession dataTaskWithRequest:blobRequest completionHandler:^( NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) { @try { NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; if (httpResponse.statusCode > 299) { NSString *errorMessage = [@"Fetching blob failed with the following reason: " stringByAppendingString:[NSHTTPURLResponse localizedStringForStatusCode: httpResponse.statusCode]]; requestError = [NSError errorWithDomain:@"app.comm" code:httpResponse.statusCode userInfo:@{NSLocalizedDescriptionKey : errorMessage}]; return; } if (error) { requestError = error; return; } blobContent = data; } @catch (NSException *exception) { comm::Logger::log( "Received exception when fetching blob. Details: " + std::string([exception.reason UTF8String])); } @finally { dispatch_semaphore_signal(semaphore); } }]; [task resume]; dispatch_semaphore_wait( semaphore, dispatch_time( DISPATCH_TIME_NOW, (int64_t)(blobServiceQueryTimeLimit * NSEC_PER_SEC))); if (requestError) { *error = requestError; return nil; } return blobContent; } - (void)deleteBlobAsyncWithHash:(NSString *)blobHash andHolder:(NSString *)blobHolder withSuccessHandler:(void (^)())successHandler andFailureHandler:(void (^)(NSError *))failureHandler { NSError *authTokenError = nil; NSString *authToken = [CommIOSBlobClient _getAuthTokenOrSetError:&authTokenError]; if (authTokenError) { comm::Logger::log( "Failed to create blob service auth token. Reason: " + std::string([authTokenError.localizedDescription UTF8String])); return; } NSString *blobUrlStr = [blobServiceAddress stringByAppendingString:@"/blob"]; NSURL *blobUrl = [NSURL URLWithString:blobUrlStr]; NSMutableURLRequest *deleteRequest = [NSMutableURLRequest requestWithURL:blobUrl]; [deleteRequest setValue:authToken forHTTPHeaderField:@"Authorization"]; [deleteRequest setValue:@"application/json" forHTTPHeaderField:@"content-type"]; deleteRequest.HTTPMethod = @"DELETE"; deleteRequest.HTTPBody = [NSJSONSerialization dataWithJSONObject:@{ blobServiceHolderKey : blobHolder, blobServiceHashKey : blobHash } options:0 error:nil]; NSURLSessionDataTask *task = [self.sharedBlobServiceSession dataTaskWithRequest:deleteRequest completionHandler:^( NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) { NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; if (httpResponse.statusCode > 299) { NSString *errorMessage = [@"Deleting blob failed with the following reason: " stringByAppendingString:[NSHTTPURLResponse localizedStringForStatusCode: httpResponse.statusCode]]; failureHandler([NSError errorWithDomain:@"app.comm" code:httpResponse.statusCode userInfo:@{NSLocalizedDescriptionKey : errorMessage}]); return; } if (error) { failureHandler(error); return; } successHandler(); }]; [task resume]; } - (void)storeBlobForDeletionWithHash:(NSString *)blobHash andHolder:(NSString *)blobHolder { std::string blobHashCpp = std::string([blobHash UTF8String]); std::string blobHolderCpp = std::string([blobHolder UTF8String]); std::string mmkvBlobHolderKey = mmkvBlobHolderPrefix + blobHolderCpp; comm::CommMMKV::setString(mmkvBlobHolderKey, blobHashCpp); } - (void)deleteStoredBlobs { std::vector allKeys = comm::CommMMKV::getAllKeys(); NSMutableArray *blobsDataForDeletion = [[NSMutableArray alloc] init]; for (const auto &key : allKeys) { if (key.size() <= mmkvBlobHolderPrefix.size() || key.compare(0, mmkvBlobHolderPrefix.size(), mmkvBlobHolderPrefix)) { continue; } std::optional blobHash = comm::CommMMKV::getString(key); if (!blobHash.has_value()) { continue; } std::string blobHolder = key.substr(mmkvBlobHolderPrefix.size()); NSString *blobHolderObjC = [NSString stringWithCString:blobHolder.c_str() encoding:NSUTF8StringEncoding]; NSString *blobHashObjC = [NSString stringWithCString:blobHash.value().c_str() encoding:NSUTF8StringEncoding]; [self deleteBlobAsyncWithHash:blobHashObjC andHolder:blobHolderObjC withSuccessHandler:^{ std::string mmkvBlobHolderKey = mmkvBlobHolderPrefix + blobHolder; comm::CommMMKV::removeKeys({mmkvBlobHolderKey}); } andFailureHandler:^(NSError *error) { comm::Logger::log( "Failed to delete blob hash " + blobHash.value() + " from blob service. Details: " + std::string([error.localizedDescription UTF8String])); }]; } } - (void)cancelOngoingRequests { [self.sharedBlobServiceSession getAllTasksWithCompletionHandler:^( NSArray<__kindof NSURLSessionTask *> *_Nonnull tasks) { for (NSURLSessionTask *task in tasks) { [task cancel]; } }]; } + (NSString *)_getAuthTokenOrSetError:(NSError **)error { // Authentication data are retrieved on every request // since they might change while NSE process is running // so we should not rely on caching them in memory. auto accessToken = comm::CommSecureStore::get( comm::CommSecureStore::commServicesAccessToken); auto userID = comm::CommSecureStore::get(comm::CommSecureStore::userID); auto deviceID = comm::CommSecureStore::get(comm::CommSecureStore::deviceID); - NSString *userIDObjC = userID.hasValue() - ? [NSString stringWithCString:userID.value().c_str() - encoding:NSUTF8StringEncoding] - : @""; - NSString *accessTokenObjC = accessToken.hasValue() - ? [NSString stringWithCString:accessToken.value().c_str() - encoding:NSUTF8StringEncoding] - : @""; - NSString *deviceIDObjC = deviceID.hasValue() - ? [NSString stringWithCString:deviceID.value().c_str() - encoding:NSUTF8StringEncoding] - : @""; + if (!userID.hasValue() || !accessToken.hasValue() || !deviceID.hasValue()) { + *error = [NSError + errorWithDomain:@"app.comm" + code:NSURLErrorUserAuthenticationRequired + userInfo:@{ + NSLocalizedDescriptionKey : + @"Unable to query blob service due to missing CSAT." + }]; + return nil; + } + + NSString *userIDObjC = [NSString stringWithCString:userID.value().c_str() + encoding:NSUTF8StringEncoding]; + + NSString *accessTokenObjC = + [NSString stringWithCString:accessToken.value().c_str() + encoding:NSUTF8StringEncoding]; + + NSString *deviceIDObjC = [NSString stringWithCString:deviceID.value().c_str() + encoding:NSUTF8StringEncoding]; NSDictionary *jsonAuthObject = @{ @"userID" : userIDObjC, @"accessToken" : accessTokenObjC, @"deviceID" : deviceIDObjC, }; NSData *binaryAuthObject = nil; NSError *jsonError = nil; @try { binaryAuthObject = [NSJSONSerialization dataWithJSONObject:jsonAuthObject options:0 error:&jsonError]; } @catch (NSException *e) { *error = [NSError errorWithDomain:@"app.comm" code:NSFormattingError userInfo:@{NSLocalizedDescriptionKey : e.reason}]; return nil; } if (jsonError) { *error = jsonError; return nil; } return [@"Bearer " stringByAppendingString:[binaryAuthObject base64EncodedStringWithOptions:0]]; } @end diff --git a/web/cpp/SQLiteQueryExecutorBindings.cpp b/web/cpp/SQLiteQueryExecutorBindings.cpp index ce54992be..ee05d8bad 100644 --- a/web/cpp/SQLiteQueryExecutorBindings.cpp +++ b/web/cpp/SQLiteQueryExecutorBindings.cpp @@ -1,342 +1,349 @@ #include "SQLiteQueryExecutor.cpp" #include "entities/InboundP2PMessage.h" #include "entities/Nullable.h" #include "entities/OutboundP2PMessage.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("EntryInfo") .field("id", &EntryInfo::id) .field("entry", &EntryInfo::entry); value_object("LocalMessageInfo") .field("id", &LocalMessageInfo::id) .field("localMessageInfo", &LocalMessageInfo::local_message_info); 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("OutboundP2PMessage") .field("messageID", &OutboundP2PMessage::message_id) .field("deviceID", &OutboundP2PMessage::device_id) .field("userID", &OutboundP2PMessage::user_id) .field("timestamp", &OutboundP2PMessage::timestamp) .field("plaintext", &OutboundP2PMessage::plaintext) .field("ciphertext", &OutboundP2PMessage::ciphertext) .field("status", &OutboundP2PMessage::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("replaceEntry", &SQLiteQueryExecutor::replaceEntry) .function("removeEntries", &SQLiteQueryExecutor::removeEntries) .function("removeAllEntries", &SQLiteQueryExecutor::removeAllEntries) .function("getAllEntries", &SQLiteQueryExecutor::getAllEntries) - .function("replaceMessageStoreLocalMessageInfo", &SQLiteQueryExecutor::replaceMessageStoreLocalMessageInfo) - .function("removeMessageStoreLocalMessageInfos", &SQLiteQueryExecutor::removeMessageStoreLocalMessageInfos) .function( - "removeAllMessageStoreLocalMessageInfos", &SQLiteQueryExecutor::removeAllMessageStoreLocalMessageInfos) - .function("getAllMessageStoreLocalMessageInfos", &SQLiteQueryExecutor::getAllMessageStoreLocalMessageInfos) + "replaceMessageStoreLocalMessageInfo", + &SQLiteQueryExecutor::replaceMessageStoreLocalMessageInfo) + .function( + "removeMessageStoreLocalMessageInfos", + &SQLiteQueryExecutor::removeMessageStoreLocalMessageInfos) + .function( + "removeAllMessageStoreLocalMessageInfos", + &SQLiteQueryExecutor::removeAllMessageStoreLocalMessageInfos) + .function( + "getAllMessageStoreLocalMessageInfos", + &SQLiteQueryExecutor::getAllMessageStoreLocalMessageInfos) .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( "addOutboundP2PMessages", &SQLiteQueryExecutor::addOutboundP2PMessages) .function( "removeOutboundP2PMessagesOlderThan", &SQLiteQueryExecutor::removeOutboundP2PMessagesOlderThan) .function( "removeAllOutboundP2PMessages", &SQLiteQueryExecutor::removeAllOutboundP2PMessages) .function( "getAllOutboundP2PMessages", &SQLiteQueryExecutor::getAllOutboundP2PMessages) .function( "setCiphertextForOutboundP2PMessage", &SQLiteQueryExecutor::setCiphertextForOutboundP2PMessage) .function( "markOutboundP2PMessageAsSent", &SQLiteQueryExecutor::markOutboundP2PMessageAsSent) .function( "addInboundP2PMessage", &SQLiteQueryExecutor::addInboundP2PMessage) .function( "getAllInboundP2PMessage", &SQLiteQueryExecutor::getAllInboundP2PMessage) .function( "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