diff --git a/ChatSecure.xcodeproj/project.pbxproj b/ChatSecure.xcodeproj/project.pbxproj index 3251dc220..368315b48 100644 --- a/ChatSecure.xcodeproj/project.pbxproj +++ b/ChatSecure.xcodeproj/project.pbxproj @@ -130,6 +130,7 @@ 633AF2FA1A7C3DBC0030A3FF /* OTRAudioSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 633AF2F91A7C3DBC0030A3FF /* OTRAudioSessionManager.m */; }; 6353AC2D1AD5E32000753B83 /* OTRAudioTrashView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6353AC2C1AD5E32000753B83 /* OTRAudioTrashView.m */; }; 6354BBE41A96C67400E8EBAC /* OTRMediaFileManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 6354BBE31A96C67400E8EBAC /* OTRMediaFileManager.m */; }; + 638FC09D1B0BE46100B37454 /* NSFileManager+ChatSecure.m in Sources */ = {isa = PBXBuildFile; fileRef = 638FC09C1B0BE46100B37454 /* NSFileManager+ChatSecure.m */; }; 6396AFA01A169D54009F3E6C /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6396AF9F1A169D54009F3E6C /* main.m */; }; 6396AFBA1A169D54009F3E6C /* ChatSecureTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6396AFB91A169D54009F3E6C /* ChatSecureTests.m */; }; 639CDD8E1AD71BC4009BAABC /* OTRCircleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 639CDD8D1AD71BC4009BAABC /* OTRCircleView.m */; }; @@ -470,6 +471,8 @@ 6353AC2C1AD5E32000753B83 /* OTRAudioTrashView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTRAudioTrashView.m; sourceTree = ""; }; 6354BBE21A96C67400E8EBAC /* OTRMediaFileManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTRMediaFileManager.h; sourceTree = ""; }; 6354BBE31A96C67400E8EBAC /* OTRMediaFileManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTRMediaFileManager.m; sourceTree = ""; }; + 638FC09B1B0BE46100B37454 /* NSFileManager+ChatSecure.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSFileManager+ChatSecure.h"; sourceTree = ""; }; + 638FC09C1B0BE46100B37454 /* NSFileManager+ChatSecure.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSFileManager+ChatSecure.m"; sourceTree = ""; }; 6396AF9A1A169D54009F3E6C /* ChatSecure.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ChatSecure.app; sourceTree = BUILT_PRODUCTS_DIR; }; 6396AF9E1A169D54009F3E6C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 6396AF9F1A169D54009F3E6C /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; @@ -666,6 +669,8 @@ 63FABC8D1A410EBF009BF681 /* JSQMessagesCollectionViewCell+ChatSecure.m */, 63FABC8F1A41218E009BF681 /* NSString+ChatSecure.h */, 63FABC901A41218E009BF681 /* NSString+ChatSecure.m */, + 638FC09B1B0BE46100B37454 /* NSFileManager+ChatSecure.h */, + 638FC09C1B0BE46100B37454 /* NSFileManager+ChatSecure.m */, ); path = Categories; sourceTree = ""; @@ -1589,6 +1594,7 @@ 633620991A76E87B006E8739 /* OTRImageItem.m in Sources */, 633107081A16D1A300C17BAE /* OTRNewAccountViewController.m in Sources */, 6336209C1A76E88C006E8739 /* OTRVideoItem.m in Sources */, + 638FC09D1B0BE46100B37454 /* NSFileManager+ChatSecure.m in Sources */, 633106F01A16D1A300C17BAE /* OTRLoginViewController.m in Sources */, 633106C81A16D1A300C17BAE /* OTRSettingsGroup.m in Sources */, 633107271A16D1A400C17BAE /* OTRSocialButtonsView.m in Sources */, diff --git a/ChatSecure/Classes/Categories/NSFileManager+ChatSecure.h b/ChatSecure/Classes/Categories/NSFileManager+ChatSecure.h new file mode 100644 index 000000000..ec9273961 --- /dev/null +++ b/ChatSecure/Classes/Categories/NSFileManager+ChatSecure.h @@ -0,0 +1,18 @@ +// +// NSFileManager+ChatSecure.h +// ChatSecure +// +// Created by David Chiles on 5/19/15. +// Copyright (c) 2015 Chris Ballinger. All rights reserved. +// + +#import + +@interface NSFileManager (ChatSecure) + + +- (void)otr_enumerateFilesInDirectory:(NSString *)directory block:(void (^)(NSString *fullPath,BOOL *stop))enumerateBlock; +- (BOOL)otr_setFileProtection:(NSString *)fileProtection forFilesInDirectory:(NSString *)directory; +- (BOOL)otr_excudeFromBackUpFilesInDirectory:(NSString *)directory; + +@end diff --git a/ChatSecure/Classes/Categories/NSFileManager+ChatSecure.m b/ChatSecure/Classes/Categories/NSFileManager+ChatSecure.m new file mode 100644 index 000000000..e5e588bcd --- /dev/null +++ b/ChatSecure/Classes/Categories/NSFileManager+ChatSecure.m @@ -0,0 +1,49 @@ +// +// NSFileManager+ChatSecure.m +// ChatSecure +// +// Created by David Chiles on 5/19/15. +// Copyright (c) 2015 Chris Ballinger. All rights reserved. +// + +#import "NSFileManager+ChatSecure.h" + +@implementation NSFileManager (ChatSecure) + +- (void)otr_enumerateFilesInDirectory:(NSString *)directory block:(void (^)(NSString *fullPath,BOOL *stop))enumerateBlock +{ + BOOL isDirecotry = NO; + BOOL exists = [self fileExistsAtPath:directory isDirectory:&isDirecotry]; + if (enumerateBlock && isDirecotry && exists) { + NSDirectoryEnumerator *directoryEnumerator = [self enumeratorAtPath:directory]; + NSString *file = nil; + BOOL stop = NO; + while ((file = [directoryEnumerator nextObject]) && !stop) { + NSString *path = [NSString pathWithComponents:@[directory,file]]; + enumerateBlock(path,&stop); + } + } +} + +- (BOOL)otr_setFileProtection:(NSString *)fileProtection forFilesInDirectory:(NSString *)directory +{ + __block BOOL success = YES; + [self otr_enumerateFilesInDirectory:directory block:^(NSString *fullPath, BOOL *stop) { + success = [self setAttributes:@{NSFileProtectionKey:fileProtection} + ofItemAtPath:fullPath error:nil]; + *stop = !success; + }]; + return success; +} + +- (BOOL)otr_excudeFromBackUpFilesInDirectory:(NSString *)directory +{ + __block BOOL success = YES; + [self otr_enumerateFilesInDirectory:directory block:^(NSString *fullPath, BOOL *stop) { + success = [self setAttributes:@{NSURLIsExcludedFromBackupKey:@(YES)} ofItemAtPath:fullPath error:nil]; + *stop = !success; + }]; + return success; +} + +@end diff --git a/ChatSecure/Classes/Controllers/OTRDatabaseManager.m b/ChatSecure/Classes/Controllers/OTRDatabaseManager.m index 2179e824c..0fb38ca07 100644 --- a/ChatSecure/Classes/Controllers/OTRDatabaseManager.m +++ b/ChatSecure/Classes/Controllers/OTRDatabaseManager.m @@ -29,6 +29,7 @@ #import "OTRMessage.h" #import "OTRMediaFileManager.h" #import "IOCipher.h" +#import "NSFileManager+ChatSecure.h" NSString *const OTRYapDatabaseRelationshipName = @"OTRYapDatabaseRelationshipName"; NSString *const OTRYapDatabseMessageIdSecondaryIndex = @"OTRYapDatabseMessageIdSecondaryIndex"; @@ -53,7 +54,13 @@ - (BOOL) setupDatabaseWithName:(NSString*)databaseName { [self migrateCoreDataToYapDatabase]; success = YES; } - success = [self setupSecureMediaStorage]; + if (success) success = [self setupSecureMediaStorage]; + + NSString *databaseDirectory = [OTRDatabaseManager yapDatabaseDirectory]; + //Enumerate all files in yap database directory and exclude from backup + if (success) success = [[NSFileManager defaultManager] otr_excudeFromBackUpFilesInDirectory:databaseDirectory]; + //fix file protection on existing files + if (success) success = [[NSFileManager defaultManager] otr_setFileProtection:NSFileProtectionCompleteUntilFirstUserAuthentication forFilesInDirectory:databaseDirectory]; return success; } @@ -200,6 +207,7 @@ - (BOOL)setupYapDatabaseWithName:(NSString *)name } NSString *databasePath = [[self class] yapDatabasePathWithName:name]; + self.database = [[YapDatabase alloc] initWithPath:databasePath serializer:nil deserializer:nil @@ -227,22 +235,6 @@ - (BOOL)setupYapDatabaseWithName:(NSString *)name if (success) success = [OTRDatabaseView registerUnreadMessagesView]; if (success) success = [self setupSecondaryIndexes]; - - - //Enumerate all files in yap database directory and exclude from backup - if (success) { - NSError *error = nil; - NSDirectoryEnumerator *directoryEnumerator = [[NSFileManager defaultManager] enumeratorAtPath:databaseDirectory]; - id file; - while ((file = [directoryEnumerator nextObject]) && success && !error) { - if([file isKindOfClass:[NSString class]]) { - NSString *fileName = file; - NSURL *url = [NSURL fileURLWithPath:[databaseDirectory stringByAppendingPathComponent:fileName]]; - success = [url setResourceValue: @(YES) forKey: NSURLIsExcludedFromBackupKey error: &error]; - } - } - } - if (self.database && success) { return YES; } diff --git a/ChatSecure/Classes/Controllers/OTRProtocolManager.m b/ChatSecure/Classes/Controllers/OTRProtocolManager.m index bd471e36f..bcdfbff74 100644 --- a/ChatSecure/Classes/Controllers/OTRProtocolManager.m +++ b/ChatSecure/Classes/Controllers/OTRProtocolManager.m @@ -30,7 +30,7 @@ #import "OTRDatabaseManager.h" #import "YapDatabaseConnection.h" #import "YapDatabaseTransaction.h" - +#import #import "OTRLog.h" static OTRProtocolManager *sharedManager = nil; @@ -41,7 +41,7 @@ @interface OTRProtocolManager () @property (nonatomic) NSUInteger numberOfConnectingProtocols; @property (nonatomic, strong) OTRPushManager *pushManager; @property (nonatomic, strong) NSMutableDictionary * protocolManagerDictionary; - +@property (nonatomic, strong) FBKVOController *KVOController; @property (nonatomic) dispatch_queue_t internalQueue; @end @@ -56,7 +56,8 @@ -(id)init self.numberOfConnectedProtocols = 0; self.numberOfConnectingProtocols = 0; self.encryptionManager = [[OTREncryptionManager alloc] init]; - self.protocolManagerDictionary = [NSMutableDictionary new]; + self.protocolManagerDictionary = [[NSMutableDictionary alloc] init]; + self.KVOController = [FBKVOController controllerWithObserver:self]; } return self; } @@ -64,10 +65,6 @@ -(id)init - (void)removeProtocolForAccount:(OTRAccount *)account { @synchronized(self.protocolManagerDictionary) { - id protocol = self.protocolManagerDictionary[account.uniqueId]; - if (protocol) { - [protocol removeObserver:self forKeyPath:NSStringFromSelector(@selector(connectionStatus))]; - } [self.protocolManagerDictionary removeObjectForKey:account.uniqueId]; } } @@ -76,7 +73,7 @@ - (void)addProtocol:(id)protocol forAccount:(OTRAccount *)account { @synchronized(self.protocolManagerDictionary){ [self.protocolManagerDictionary setObject:protocol forKey:account.uniqueId]; - [protocol addObserver:self forKeyPath:NSStringFromSelector(@selector(connectionStatus)) options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL]; + [self.KVOController observe:protocol keyPath:NSStringFromSelector(@selector(connectionStatus)) options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld action:@selector(protocolDidChange:)]; } } @@ -175,39 +172,42 @@ - (void)disconnectAllAccounts } } --(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +- (void)protocolDidChange:(NSDictionary *)change { - if ([keyPath isEqualToString:NSStringFromSelector(@selector(connectionStatus))]) { - OTRProtocolConnectionStatus newStatus = [[change objectForKey:NSKeyValueChangeNewKey] integerValue]; - OTRProtocolConnectionStatus oldStatus = [[change objectForKey:NSKeyValueChangeOldKey] integerValue]; - NSInteger connectedInt = 0; - NSInteger connectingInt = 0; - - switch (oldStatus) { - case OTRProtocolConnectionStatusConnected: - connectedInt = -1; - break; - case OTRProtocolConnectionStatusConnecting: - connectingInt = -1; - default: - break; - } - - switch (newStatus) { - case OTRProtocolConnectionStatusConnected: - connectedInt = 1; - break; - case OTRProtocolConnectionStatusConnecting: - connectedInt = 1; - default: - break; - } - - - self.numberOfConnectedProtocols += connectedInt; - self.numberOfConnectingProtocols += connectingInt; + + OTRProtocolConnectionStatus newStatus = [[change objectForKey:NSKeyValueChangeNewKey] integerValue]; + OTRProtocolConnectionStatus oldStatus = [[change objectForKey:NSKeyValueChangeOldKey] integerValue]; + + if (oldStatus == newStatus) { + return; } - + + NSInteger connectedInt = 0; + NSInteger connectingInt = 0; + + switch (oldStatus) { + case OTRProtocolConnectionStatusConnected: + connectedInt = -1; + break; + case OTRProtocolConnectionStatusConnecting: + connectingInt = -1; + default: + break; + } + + switch (newStatus) { + case OTRProtocolConnectionStatusConnected: + connectedInt = 1; + break; + case OTRProtocolConnectionStatusConnecting: + connectingInt = 1; + default: + break; + } + + + self.numberOfConnectedProtocols += connectedInt; + self.numberOfConnectingProtocols += connectingInt; } -(BOOL)isAccountConnected:(OTRAccount *)account; diff --git a/ChatSecure/Classes/Views/OTRPlayPauseProgressView.m b/ChatSecure/Classes/Views/OTRPlayPauseProgressView.m index ffd2601c2..c2361ada1 100644 --- a/ChatSecure/Classes/Views/OTRPlayPauseProgressView.m +++ b/ChatSecure/Classes/Views/OTRPlayPauseProgressView.m @@ -160,7 +160,9 @@ - (void)setStatus:(OTRPlayPauseProgressViewStatus)status - (void)removePlayView { [self.playView removeFromSuperview]; - [self removeConstraints:self.playViewConstraints]; + if ([self.playViewConstraints count]) { + [self removeConstraints:self.playViewConstraints]; + } self.playViewConstraints = nil; } diff --git a/Podfile b/Podfile index 552592c60..02547488f 100755 --- a/Podfile +++ b/Podfile @@ -35,6 +35,7 @@ pod 'TUSafariActivity', '~> 1.0' pod 'ARChromeActivity', '~> 1.0' pod 'CocoaAsyncSocket', '~> 7.4' pod 'JTSImageViewController', '~> 1.4' +pod 'KVOController', '~> 1.0' # Local Podspecs pod 'gtm-http-fetcher', :podspec => 'Podspecs/gtm-http-fetcher.podspec.json' diff --git a/Podfile.lock b/Podfile.lock index eaa548a61..0cd17bce6 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -58,6 +58,7 @@ PODS: - JTSImageViewController (1.4) - JTTargetActionBlock (1.0.0) - JVFloatLabeledTextField (1.0.2) + - KVOController (1.0.3) - libsqlfs/common (1.3) - libsqlfs/SQLCipher (1.3): - libsqlfs/common @@ -264,6 +265,7 @@ DEPENDENCIES: - JTSImageViewController (~> 1.4) - JTTargetActionBlock (~> 1.0) - JVFloatLabeledTextField (~> 1.0) + - KVOController (~> 1.0) - MagicalRecord (~> 2.2) - Mantle (~> 1.4) - MBProgressHUD (~> 0.9) @@ -344,6 +346,7 @@ SPEC CHECKSUMS: JTSImageViewController: 6a367b144e257db02cdfacdba78d684377d3777b JTTargetActionBlock: 6e9201e5fa88651deb354c91b7c9c6487a1abe55 JVFloatLabeledTextField: 58a3a32cfb800e5b224f676987e7c13abf50a14d + KVOController: 9fd8f0343670994e4b6f9f0b31f5a45663f3e1cf libsqlfs: 179e4fb40b8c07fd0ee7933ec4c0b9f4815cbd0e MagicalRecord: f8a56bb87ab6552f20c4bb8681a1958a197ea3cd Mantle: 60acd0cd363e27d945bda0a6b8e90577112d28f3 @@ -365,4 +368,4 @@ SPEC CHECKSUMS: XMPPFramework: 239fe709baff208fff5970708469b939387105ee YapDatabase: 13566c87dc9226a0f15d755edba08e8ad94cac9f -COCOAPODS: 0.36.4 +COCOAPODS: 0.37.1