Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3509035
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
18 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h
index 8d68165b8..94e73fb9b 100644
--- a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h
+++ b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h
@@ -1,71 +1,72 @@
#pragma once
#include "../CryptoTools/Persist.h"
#include "DatabaseQueryExecutor.h"
#include "entities/Draft.h"
#include <mutex>
#include <string>
namespace comm {
class SQLiteQueryExecutor : public DatabaseQueryExecutor {
void migrate() const;
static void assign_encryption_key();
static auto &getStorage();
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;
static std::once_flag initialized;
static int sqlcipherEncryptionKeySize;
- static std::string secureStoreEncryptionKeyID;
public:
static std::string sqliteFilePath;
static std::string encryptionKey;
+ static std::string secureStoreEncryptionKeyID;
+
SQLiteQueryExecutor();
static void initialize(std::string &databasePath);
std::unique_ptr<Thread> 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<Draft> getAllDrafts() const override;
void removeAllDrafts() const override;
void removeAllMessages() const override;
std::vector<std::pair<Message, std::vector<Media>>>
getAllMessages() const override;
void removeMessages(const std::vector<std::string> &ids) const override;
void removeMessagesForThreads(
const std::vector<std::string> &threadIDs) const override;
void replaceMessage(const Message &message) const override;
void rekeyMessage(std::string from, std::string to) const override;
void removeAllMedia() const override;
void removeMediaForMessages(
const std::vector<std::string> &msg_ids) const override;
void removeMediaForMessage(std::string msg_id) const override;
void removeMediaForThreads(
const std::vector<std::string> &thread_ids) const override;
void replaceMedia(const Media &media) const override;
void rekeyMediaContainers(std::string from, std::string to) const override;
std::vector<Thread> getAllThreads() const override;
void removeThreads(std::vector<std::string> ids) const override;
void replaceThread(const Thread &thread) const override;
void removeAllThreads() const override;
void beginTransaction() const override;
void commitTransaction() const override;
void rollbackTransaction() const override;
std::vector<OlmPersistSession> getOlmPersistSessionsData() const override;
folly::Optional<std::string> getOlmPersistAccountData() const override;
void storeOlmPersistData(crypto::Persist persist) const override;
void setNotifyToken(std::string token) const override;
void clearNotifyToken() const override;
void setCurrentUserID(std::string userID) const override;
std::string getCurrentUserID() const override;
void setDeviceID(std::string deviceID) const override;
std::string getDeviceID() const override;
void clearSensitiveData() const override;
};
} // namespace comm
diff --git a/native/ios/Comm/AppDelegate.mm b/native/ios/Comm/AppDelegate.mm
index 5436d2fbf..e53a28677 100644
--- a/native/ios/Comm/AppDelegate.mm
+++ b/native/ios/Comm/AppDelegate.mm
@@ -1,294 +1,301 @@
#import "AppDelegate.h"
#import "Orientation.h"
#import "RNNotifications.h"
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTConvert.h>
#import <React/RCTRootView.h>
#import <React/RCTBridge+Private.h>
#import <React/RCTCxxBridgeDelegate.h>
#import <React/RCTJSIExecutorRuntimeInstaller.h>
#import <cxxreact/JSExecutor.h>
#import <jsireact/JSIExecutor.h>
#import <reacthermes/HermesExecutorFactory.h>
#import "CommCoreModule.h"
+#import "CommSecureStoreIOSWrapper.h"
#import "GlobalDBSingleton.h"
#import "Logger.h"
#import "MessageOperationsUtilities.h"
#import "SQLiteQueryExecutor.h"
#import "TemporaryMessageStorage.h"
#import "ThreadOperations.h"
#import "Tools.h"
#import <cstdio>
#import <stdexcept>
#import <string>
#ifdef FB_SONARKIT_ENABLED
#import <FlipperKit/FlipperClient.h>
#import <FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h>
#import <FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h>
#import <FlipperKitReactPlugin/FlipperKitReactPlugin.h>
#import <FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h>
#import <SKIOSNetworkPlugin/SKIOSNetworkAdapter.h>
static void InitializeFlipper(UIApplication *application) {
FlipperClient *client = [FlipperClient sharedClient];
SKDescriptorMapper *layoutDescriptorMapper =
[[SKDescriptorMapper alloc] initWithDefaults];
[client addPlugin:[[FlipperKitLayoutPlugin alloc]
initWithRootNode:application
withDescriptorMapper:layoutDescriptorMapper]];
[client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]];
[client addPlugin:[FlipperKitReactPlugin new]];
[client addPlugin:[[FlipperKitNetworkPlugin alloc]
initWithNetworkAdapter:[SKIOSNetworkAdapter new]]];
[client start];
}
#endif
#import <ReactCommon/RCTTurboModuleManager.h>
#import <RNReanimated/REAInitializer.h>
#import <UserNotifications/UserNotifications.h>
NSString *const backgroundNotificationTypeKey = @"backgroundNotifType";
NSString *const setUnreadStatusKey = @"setUnreadStatus";
@interface AppDelegate () <
RCTCxxBridgeDelegate,
RCTTurboModuleManagerDelegate> {
}
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
willFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+ NSString *secureStoreEncryptionKeyID = [NSString
+ stringWithUTF8String:
+ (comm::SQLiteQueryExecutor::secureStoreEncryptionKeyID.c_str())];
+ [[CommSecureStoreIOSWrapper sharedInstance]
+ migrateOptionsForKey:secureStoreEncryptionKeyID
+ withVersion:@"0"];
[self attemptDatabaseInitialization];
return YES;
}
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
#ifdef FB_SONARKIT_ENABLED
InitializeFlipper(application);
#endif
[self moveMessagesToDatabase];
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self
launchOptions:launchOptions];
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:@"Comm"
initialProperties:nil];
if (@available(iOS 13.0, *)) {
rootView.backgroundColor = [UIColor systemBackgroundColor];
} else {
rootView.backgroundColor = [UIColor whiteColor];
}
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
[super application:application didFinishLaunchingWithOptions:launchOptions];
// This prevents a very small flicker from occurring before expo-splash-screen
// is able to display
UIView *launchScreenView =
[[UIStoryboard storyboardWithName:@"SplashScreen"
bundle:nil] instantiateInitialViewController]
.view;
launchScreenView.frame = self.window.bounds;
rootView.loadingView = launchScreenView;
rootView.loadingViewFadeDelay = 0;
rootView.loadingViewFadeDuration = 0.001;
return YES;
}
- (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge {
// If you'd like to export some custom RCTBridgeModules that are not Expo
// modules, add them here!
return @[];
}
- (void)application:(UIApplication *)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
[RNNotifications
didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
- (void)application:(UIApplication *)application
didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
[RNNotifications didFailToRegisterForRemoteNotificationsWithError:error];
}
// Required for the notification event. You must call the completion handler
// after handling the remote notification.
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)notification
fetchCompletionHandler:
(void (^)(UIBackgroundFetchResult))completionHandler {
BOOL handled = NO;
if (notification[@"aps"][@"content-available"] &&
notification[backgroundNotificationTypeKey]) {
handled = [self handleBackgroundNotification:notification
fetchCompletionHandler:completionHandler];
}
if (handled) {
return;
}
[RNNotifications didReceiveRemoteNotification:notification
fetchCompletionHandler:completionHandler];
}
- (BOOL)handleBackgroundNotification:(NSDictionary *)notification
fetchCompletionHandler:
(void (^)(UIBackgroundFetchResult))completionHandler {
if ([notification[backgroundNotificationTypeKey] isEqualToString:@"CLEAR"]) {
if (notification[setUnreadStatusKey] && notification[@"threadID"]) {
std::string threadID =
std::string([notification[@"threadID"] UTF8String]);
// this callback may be called from inactive state so we need
// to initialize the database
[self attemptDatabaseInitialization];
comm::GlobalDBSingleton::instance.scheduleOrRun([threadID]() mutable {
comm::ThreadOperations::updateSQLiteUnreadStatus(threadID, false);
});
}
[[UNUserNotificationCenter currentNotificationCenter]
getDeliveredNotificationsWithCompletionHandler:^(
NSArray<UNNotification *> *notifications) {
for (UNNotification *notif in notifications) {
if ([notification[@"notificationId"]
isEqual:notif.request.content.userInfo[@"id"]]) {
NSArray *identifiers =
[NSArray arrayWithObjects:notif.request.identifier, nil];
[[UNUserNotificationCenter currentNotificationCenter]
removeDeliveredNotificationsWithIdentifiers:identifiers];
}
}
dispatch_async(dispatch_get_main_queue(), ^{
completionHandler(UIBackgroundFetchResultNewData);
});
}];
return YES;
}
return NO;
}
// Required for the localNotification event.
- (void)application:(UIApplication *)application
didReceiveLocalNotification:(UILocalNotification *)notification {
[RNNotifications didReceiveLocalNotification:notification];
}
- (UIInterfaceOrientationMask)application:(UIApplication *)application
supportedInterfaceOrientationsForWindow:(UIWindow *)window {
return [Orientation getOrientation];
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge {
#if DEBUG
return
[[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"
fallbackResource:nil];
#else
return [[NSBundle mainBundle] URLForResource:@"main"
withExtension:@"jsbundle"];
#endif
}
using JSExecutorFactory = facebook::react::JSExecutorFactory;
using HermesExecutorFactory = facebook::react::HermesExecutorFactory;
using Runtime = facebook::jsi::Runtime;
- (std::unique_ptr<JSExecutorFactory>)jsExecutorFactoryForBridge:
(RCTBridge *)bridge {
__weak __typeof(self) weakSelf = self;
const auto commRuntimeInstaller = [weakSelf,
bridge](facebook::jsi::Runtime &rt) {
if (!bridge) {
return;
}
__typeof(self) strongSelf = weakSelf;
if (strongSelf) {
std::shared_ptr<comm::CommCoreModule> nativeModule =
std::make_shared<comm::CommCoreModule>(bridge.jsCallInvoker);
rt.global().setProperty(
rt,
facebook::jsi::PropNameID::forAscii(rt, "CommCoreModule"),
facebook::jsi::Object::createFromHostObject(rt, nativeModule));
}
};
const auto installer =
reanimated::REAJSIExecutorRuntimeInstaller(bridge, commRuntimeInstaller);
return std::make_unique<HermesExecutorFactory>(
facebook::react::RCTJSIExecutorRuntimeInstaller(installer),
JSIExecutor::defaultTimeoutInvoker,
makeRuntimeConfig(3072));
}
- (void)attemptDatabaseInitialization {
std::string sqliteFilePath =
std::string([[Tools getSQLiteFilePath] UTF8String]);
// Previous Comm versions used app group location for SQLite
// database, so that NotificationService was able to acces it directly.
// Unfortunately it caused errores related to system locks. The code
// below re-migrates SQLite from app group to app specific location
// on devices where previous Comm version was installed.
NSString *appGroupSQLiteFilePath = [Tools getAppGroupSQLiteFilePath];
if ([NSFileManager.defaultManager fileExistsAtPath:appGroupSQLiteFilePath] &&
std::rename(
std::string([appGroupSQLiteFilePath UTF8String]).c_str(),
sqliteFilePath.c_str())) {
throw std::runtime_error(
"Failed to move SQLite database from app group to default location");
}
comm::SQLiteQueryExecutor::initialize(sqliteFilePath);
}
- (void)moveMessagesToDatabase {
TemporaryMessageStorage *temporaryStorage =
[[TemporaryMessageStorage alloc] init];
NSArray<NSString *> *messages = [temporaryStorage readAndClearMessages];
for (NSString *message in messages) {
std::string messageInfos = std::string([message UTF8String]);
comm::GlobalDBSingleton::instance.scheduleOrRun([messageInfos]() mutable {
comm::MessageOperationsUtilities::storeMessageInfos(messageInfos);
});
}
}
// Copied from
// ReactAndroid/src/main/java/com/facebook/hermes/reactexecutor/OnLoad.cpp
static ::hermes::vm::RuntimeConfig
makeRuntimeConfig(::hermes::vm::gcheapsize_t heapSizeMB) {
namespace vm = ::hermes::vm;
auto gcConfigBuilder =
vm::GCConfig::Builder()
.withName("RN")
// For the next two arguments: avoid GC before TTI by initializing the
// runtime to allocate directly in the old generation, but revert to
// normal operation when we reach the (first) TTI point.
.withAllocInYoung(false)
.withRevertToYGAtTTI(true);
if (heapSizeMB > 0) {
gcConfigBuilder.withMaxHeapSize(heapSizeMB << 20);
}
return vm::RuntimeConfig::Builder()
.withGCConfig(gcConfigBuilder.build())
.build();
}
@end
diff --git a/native/ios/Comm/CommSecureStoreIOSWrapper.h b/native/ios/Comm/CommSecureStoreIOSWrapper.h
index ef7b40166..9406e5b9e 100644
--- a/native/ios/Comm/CommSecureStoreIOSWrapper.h
+++ b/native/ios/Comm/CommSecureStoreIOSWrapper.h
@@ -1,12 +1,13 @@
#pragma once
#import <EXSecureStore/EXSecureStore.h>
#import <Foundation/Foundation.h>
@interface CommSecureStoreIOSWrapper : NSObject
+ (id)sharedInstance;
- (void)set:(NSString *)key value:(NSString *)value;
- (NSString *)get:(NSString *)key;
+- (void)migrateOptionsForKey:(NSString *)key withVersion:(NSString *)version;
@end
diff --git a/native/ios/Comm/CommSecureStoreIOSWrapper.mm b/native/ios/Comm/CommSecureStoreIOSWrapper.mm
index c309fd23a..c8a5f8ce4 100644
--- a/native/ios/Comm/CommSecureStoreIOSWrapper.mm
+++ b/native/ios/Comm/CommSecureStoreIOSWrapper.mm
@@ -1,67 +1,99 @@
#import "CommSecureStoreIOSWrapper.h"
#import "CommSecureStoreIOSWrapper.h"
#import <ExpoModulesCore/EXModuleRegistryProvider.h>
@interface CommSecureStoreIOSWrapper ()
@property(nonatomic, strong) EXSecureStore *secureStore;
@property(nonatomic, strong) NSDictionary *options;
@end
@interface EXSecureStore (CommEXSecureStore)
- (BOOL)_setValue:(NSString *)value
withKey:(NSString *)key
withOptions:(NSDictionary *)options
error:(NSError **)error;
- (NSString *)_getValueWithKey:(NSString *)key
withOptions:(NSDictionary *)options
error:(NSError **)error;
+- (void)_deleteValueWithKey:(NSString *)key withOptions:(NSDictionary *)options;
@end
@implementation CommSecureStoreIOSWrapper
#pragma mark Singleton Methods
+ (id)sharedInstance {
static CommSecureStoreIOSWrapper *shared = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
shared = [[self alloc] init];
EXModuleRegistryProvider *moduleRegistryProvider =
[[EXModuleRegistryProvider alloc] init];
EXSecureStore *secureStore =
(EXSecureStore *)[[moduleRegistryProvider moduleRegistry]
getExportedModuleOfClass:EXSecureStore.class];
shared.secureStore = secureStore;
+ shared.options =
+ @{@"keychainAccessible" : @(EXSecureStoreAccessibleAfterFirstUnlock)};
});
return shared;
}
- (void)set:(NSString *)key value:(NSString *)value {
if ([self secureStore] == nil) {
[NSException raise:@"secure store error"
format:@"secure store has not been initialized"];
}
NSError *error;
[[self secureStore] _setValue:value
withKey:key
withOptions:[self options]
error:&error];
if (error != nil) {
[NSException raise:@"secure store error"
format:@"error occured when setting data"];
}
}
- (NSString *)get:(NSString *)key {
if ([self secureStore] == nil) {
[NSException raise:@"secure store error"
format:@"secure store has not been initialized"];
}
NSError *error;
return [[self secureStore] _getValueWithKey:key
withOptions:[self options]
error:&error];
}
+- (void)migrateOptionsForKey:(NSString *)key withVersion:(NSString *)version {
+ NSString *secureStoreKeyVersionID = [key stringByAppendingString:@".version"];
+ NSString *failureProtectionCopyKey = [key stringByAppendingString:@".copy"];
+
+ NSString *secureStoreKeyVersion = [self get:secureStoreKeyVersionID];
+ if (secureStoreKeyVersion &&
+ [secureStoreKeyVersion isEqualToString:version]) {
+ return;
+ }
+
+ NSString *value = [self get:key];
+ NSString *valueCopy = [self get:failureProtectionCopyKey];
+
+ if (value) {
+ [self set:failureProtectionCopyKey value:value];
+ [[self secureStore] _deleteValueWithKey:key withOptions:[self options]];
+ } else if (valueCopy) {
+ value = valueCopy;
+ } else {
+ [self set:secureStoreKeyVersionID value:version];
+ return;
+ }
+
+ [self set:key value:value];
+ [self set:secureStoreKeyVersionID value:version];
+ [[self secureStore] _deleteValueWithKey:failureProtectionCopyKey
+ withOptions:[self options]];
+}
+
@end
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Dec 23, 12:38 AM (3 h, 17 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2675865
Default Alt Text
(18 KB)
Attached To
Mode
rCOMM Comm
Attached
Detach File
Event Timeline
Log In to Comment