Antidote/local_pod_repo/objcTox/Classes/Private/Manager/OCTManagerImpl.m
2024-02-22 21:43:11 +02:00

348 lines
9.3 KiB
Objective-C

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#import <objc/runtime.h>
#import "OCTManagerImpl.h"
#import "OCTTox.h"
#import "OCTToxEncryptSave.h"
#import "OCTManagerConfiguration.h"
#import "OCTManagerFactory.h"
#import "OCTSubmanagerBootstrapImpl.h"
#import "OCTSubmanagerCallsImpl.h"
#import "OCTSubmanagerChatsImpl.h"
#import "OCTSubmanagerFilesImpl.h"
#import "OCTSubmanagerFriendsImpl.h"
#import "OCTSubmanagerObjectsImpl.h"
#import "OCTSubmanagerUserImpl.h"
#import "OCTRealmManager.h"
@interface OCTManagerImpl () <OCTToxDelegate, OCTSubmanagerDataSource>
@property (copy, nonatomic, readonly) OCTManagerConfiguration *currentConfiguration;
@property (strong, nonatomic, readonly) OCTTox *tox;
@property (strong, nonatomic, readonly) NSObject *toxSaveFileLock;
@property (strong, nonatomic, nonnull) OCTToxEncryptSave *encryptSave;
@property (strong, nonatomic, readonly) OCTRealmManager *realmManager;
@property (strong, atomic) NSNotificationCenter *notificationCenter;
@property (strong, nonatomic, readwrite) OCTSubmanagerBootstrapImpl *bootstrap;
@property (strong, nonatomic, readwrite) OCTSubmanagerCallsImpl *calls;
@property (strong, nonatomic, readwrite) OCTSubmanagerChatsImpl *chats;
@property (strong, nonatomic, readwrite) OCTSubmanagerFilesImpl *files;
@property (strong, nonatomic, readwrite) OCTSubmanagerFriendsImpl *friends;
@property (strong, nonatomic, readwrite) OCTSubmanagerObjectsImpl *objects;
@property (strong, nonatomic, readwrite) OCTSubmanagerUserImpl *user;
@end
@implementation OCTManagerImpl
#pragma mark - Lifecycle
- (instancetype)initWithConfiguration:(OCTManagerConfiguration *)configuration
tox:(OCTTox *)tox
toxEncryptSave:(OCTToxEncryptSave *)toxEncryptSave
realmManager:(OCTRealmManager *)realmManager
{
self = [super init];
if (! self) {
return nil;
}
_currentConfiguration = [configuration copy];
_tox = tox;
_tox.delegate = self;
_toxSaveFileLock = [NSObject new];
_encryptSave = toxEncryptSave;
_realmManager = realmManager;
_notificationCenter = [[NSNotificationCenter alloc] init];
[_tox start];
[self saveTox];
[self createSubmanagers];
return self;
}
- (void)dealloc
{
[self killSubmanagers];
[self.tox stop];
}
#pragma mark - Public
- (OCTManagerConfiguration *)configuration
{
return [self.currentConfiguration copy];
}
- (NSString *)exportToxSaveFileAndReturnError:(NSError **)error
{
@synchronized(self.toxSaveFileLock) {
NSString *savedDataPath = self.currentConfiguration.fileStorage.pathForToxSaveFile;
NSString *tempPath = self.currentConfiguration.fileStorage.pathForTemporaryFilesDirectory;
tempPath = [tempPath stringByAppendingPathComponent:[savedDataPath lastPathComponent]];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:tempPath]) {
[fileManager removeItemAtPath:tempPath error:error];
}
if (! [fileManager copyItemAtPath:savedDataPath toPath:tempPath error:error]) {
return nil;
}
return tempPath;
}
}
- (BOOL)changeEncryptPassword:(nonnull NSString *)newPassword oldPassword:(nonnull NSString *)oldPassword
{
OCTToxEncryptSave *encryptSave = [self changeToxPassword:newPassword oldPassword:oldPassword];
if (encryptSave == nil) {
return NO;
}
if (! [self changeDatabasePassword:newPassword oldPassword:oldPassword]) {
return NO;
}
self.encryptSave = encryptSave;
[self saveTox];
return YES;
}
- (BOOL)isManagerEncryptedWithPassword:(nonnull NSString *)password
{
NSString *toxFilePath = self.currentConfiguration.fileStorage.pathForToxSaveFile;
return [self isDataAtPath:toxFilePath encryptedWithPassword:password];
}
#pragma mark - OCTSubmanagerDataSource
- (OCTTox *)managerGetTox
{
return self.tox;
}
- (BOOL)managerIsToxConnected
{
return (self.user.connectionStatus != OCTToxConnectionStatusNone);
}
- (void)managerSaveTox
{
return [self saveTox];
}
- (OCTRealmManager *)managerGetRealmManager
{
return self.realmManager;
}
- (id<OCTFileStorageProtocol>)managerGetFileStorage
{
return self.currentConfiguration.fileStorage;
}
- (NSNotificationCenter *)managerGetNotificationCenter
{
return self.notificationCenter;
}
- (BOOL)managerUseFauxOfflineMessaging
{
return self.currentConfiguration.useFauxOfflineMessaging;
}
#pragma mark - Private
- (NSData *)getSavedDataFromPath:(NSString *)path
{
return [[NSFileManager defaultManager] fileExistsAtPath:path] ?
([NSData dataWithContentsOfFile:path]) :
nil;
}
- (BOOL)isDataAtPath:(NSString *)path encryptedWithPassword:(NSString *)password
{
NSData *savedData = [self getSavedDataFromPath:path];
if (! savedData) {
return NO;
}
if ([OCTToxEncryptSave isDataEncrypted:savedData]) {
return [OCTToxEncryptSave decryptData:savedData withPassphrase:password error:nil] != nil;
}
return NO;
}
- (void)createSubmanagers
{
_bootstrap = [self createSubmanagerWithClass:[OCTSubmanagerBootstrapImpl class]];
_chats = [self createSubmanagerWithClass:[OCTSubmanagerChatsImpl class]];
_files = [self createSubmanagerWithClass:[OCTSubmanagerFilesImpl class]];
_friends = [self createSubmanagerWithClass:[OCTSubmanagerFriendsImpl class]];
_objects = [self createSubmanagerWithClass:[OCTSubmanagerObjectsImpl class]];
_user = [self createSubmanagerWithClass:[OCTSubmanagerUserImpl class]];
OCTSubmanagerCallsImpl *calls = [[OCTSubmanagerCallsImpl alloc] initWithTox:_tox];
calls.dataSource = self;
_calls = calls;
[_calls setupAndReturnError:nil];
}
- (void)killSubmanagers
{
self.bootstrap = nil;
self.calls = nil;
self.chats = nil;
self.files = nil;
self.friends = nil;
self.objects = nil;
self.user = nil;
}
- (id)createSubmanagerWithClass:(Class)class
{
id<OCTSubmanagerProtocol> submanager = [[class alloc] init];
submanager.dataSource = self;
if ([submanager respondsToSelector:@selector(configure)]) {
[submanager configure];
}
return submanager;
}
- (BOOL)respondsToSelector:(SEL)aSelector
{
id submanager = [self forwardingTargetForSelector:aSelector];
if (submanager) {
return YES;
}
return [super respondsToSelector:aSelector];
}
- (id)forwardingTargetForSelector:(SEL)aSelector
{
struct objc_method_description description = protocol_getMethodDescription(@protocol(OCTToxDelegate), aSelector, NO, YES);
if (description.name == NULL) {
// We forward methods only from OCTToxDelegate protocol.
return nil;
}
NSArray *submanagers = @[
self.bootstrap,
self.chats,
self.files,
self.friends,
self.objects,
self.user,
];
for (id delegate in submanagers) {
if ([delegate respondsToSelector:aSelector]) {
return delegate;
}
}
return nil;
}
- (void)saveTox
{
@synchronized(self.toxSaveFileLock) {
void (^throwException)(NSError *) = ^(NSError *error) {
NSDictionary *userInfo = nil;
if (error) {
userInfo = @{ @"NSError" : error };
}
@throw [NSException exceptionWithName:@"saveToxException" reason:error.debugDescription userInfo:userInfo];
};
NSData *data = [self.tox save];
NSError *error;
data = [self.encryptSave encryptData:data error:&error];
if (! data) {
throwException(error);
}
if (! [data writeToFile:self.currentConfiguration.fileStorage.pathForToxSaveFile options:NSDataWritingAtomic error:&error]) {
throwException(error);
}
}
}
// On success returns encryptSave with new password.
- (OCTToxEncryptSave *)changeToxPassword:(NSString *)newPassword oldPassword:(NSString *)oldPassword
{
NSString *toxFilePath = self.currentConfiguration.fileStorage.pathForToxSaveFile;
if (! [self isDataAtPath:toxFilePath encryptedWithPassword:oldPassword]) {
return nil;
}
__block OCTToxEncryptSave *newEncryptSave;
@synchronized(self.toxSaveFileLock) {
// Passing nil as tox data as we are setting new password.
newEncryptSave = [[OCTToxEncryptSave alloc] initWithPassphrase:newPassword toxData:nil error:nil];
}
return newEncryptSave;
}
- (BOOL)changeDatabasePassword:(NSString *)newPassword oldPassword:(NSString *)oldPassword
{
NSParameterAssert(newPassword);
NSParameterAssert(oldPassword);
NSString *encryptedKeyPath = self.currentConfiguration.fileStorage.pathForDatabaseEncryptionKey;
NSData *encryptedKey = [NSData dataWithContentsOfFile:encryptedKeyPath];
if (! encryptedKey) {
return NO;
}
NSData *key = [OCTToxEncryptSave decryptData:encryptedKey withPassphrase:oldPassword error:nil];
if (! key) {
return NO;
}
NSData *newEncryptedKey = [OCTToxEncryptSave encryptData:key withPassphrase:newPassword error:nil];
if (! newEncryptedKey) {
return NO;
}
return [newEncryptedKey writeToFile:encryptedKeyPath options:NSDataWritingAtomic error:nil];
}
@end