From e47e9759e95eca0654f5967e456cd8a96cd4446b Mon Sep 17 00:00:00 2001 From: Frederic Jacobs Date: Sat, 25 Apr 2015 16:59:32 +0200 Subject: [PATCH] Fixing leaky caches. --- Signal/src/AppDelegate.m | 29 +--- Signal/src/environment/VersionMigrations.h | 8 +- Signal/src/environment/VersionMigrations.m | 140 ++++++++++++++++-- .../textsecure/Network/API/TSNetworkManager.m | 1 - .../src/textsecure/Storage/TSStorageManager.m | 14 +- .../view controllers/MessagesViewController.m | 26 ++-- .../UITests/SignalsViewController.m | 2 +- 7 files changed, 157 insertions(+), 63 deletions(-) diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index e2cd5014b..67738de9c 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -25,7 +25,6 @@ #include "TargetConditionals.h" #endif - @interface AppDelegate () @property (nonatomic, retain) UIWindow *blankWindow; @@ -36,32 +35,6 @@ #pragma mark Detect updates - perform migrations -- (void)performUpdateCheck{ - NSString *previousVersion = Environment.preferences.lastRanVersion; - NSString *currentVersion = [Environment.preferences setAndGetCurrentVersion]; - BOOL isCurrentlyMigrating = [VersionMigrations isMigratingTo2Dot0]; - - if (!previousVersion) { - DDLogError(@"No previous version found. Possibly first launch since install."); - } else if(([self isVersion:previousVersion atLeast:@"1.0.2" andLessThan:@"2.0"]) || isCurrentlyMigrating) { - [VersionMigrations migrateFrom1Dot0Dot2ToVersion2Dot0]; - } else if(([self isVersion:previousVersion atLeast:@"2.0.0" andLessThan:@"2.0.18"])) { - [VersionMigrations migrateBloomFilter]; - } -} - --(BOOL) isVersion:(NSString *)thisVersionString atLeast:(NSString *)openLowerBoundVersionString andLessThan:(NSString *)closedUpperBoundVersionString { - return [self isVersion:thisVersionString atLeast:openLowerBoundVersionString] && [self isVersion:thisVersionString lessThan:closedUpperBoundVersionString]; -} - -- (BOOL) isVersion:(NSString *)thisVersionString atLeast:(NSString *)thatVersionString { - return [thisVersionString compare:thatVersionString options:NSNumericSearch] != NSOrderedAscending; -} - -- (BOOL) isVersion:(NSString *)thisVersionString lessThan:(NSString *)thatVersionString { - return [thisVersionString compare:thatVersionString options:NSNumericSearch] == NSOrderedAscending; -} - - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [self setupAppearance]; @@ -104,7 +77,7 @@ [self.window makeKeyAndVisible]; - [self performUpdateCheck]; // this call must be made after environment has been initialized because in general upgrade may depend on environment + [VersionMigrations performUpdateCheck]; // this call must be made after environment has been initialized because in general upgrade may depend on environment //Accept push notification when app is not open NSDictionary *remoteNotif = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]; diff --git a/Signal/src/environment/VersionMigrations.h b/Signal/src/environment/VersionMigrations.h index 3e6675663..21a8e6e28 100644 --- a/Signal/src/environment/VersionMigrations.h +++ b/Signal/src/environment/VersionMigrations.h @@ -12,11 +12,7 @@ @interface VersionMigrations : NSObject -+ (void)migrateBloomFilter; - -+ (void)migrateFrom1Dot0Dot2ToVersion2Dot0; - -+ (BOOL)isMigratingTo2Dot0; - ++ (void)performUpdateCheck; ++ (BOOL)isMigrating; @end diff --git a/Signal/src/environment/VersionMigrations.m b/Signal/src/environment/VersionMigrations.m index 914700d9d..5022d87b3 100644 --- a/Signal/src/environment/VersionMigrations.m +++ b/Signal/src/environment/VersionMigrations.m @@ -21,6 +21,7 @@ #import "TSDatabaseView.h" #define IS_MIGRATING_FROM_1DOT0_TO_LARGER_KEY @"Migrating from 1.0 to Larger" +#define NEEDS_TO_REGISTER_PUSH_KEY @"Register For Push" @@ -32,6 +33,61 @@ @implementation VersionMigrations +#pragma mark Utility methods + ++ (void)performUpdateCheck{ + NSString *previousVersion = Environment.preferences.lastRanVersion; + NSString *currentVersion = [Environment.preferences setAndGetCurrentVersion]; + BOOL isCurrentlyMigrating = [VersionMigrations isMigratingTo2Dot0]; + BOOL needsToRegisterPush = [VersionMigrations needsRegisterPush]; + + if (!previousVersion) { + DDLogError(@"No previous version found. Possibly first launch since install."); + return; + } + + if(([self isVersion:previousVersion atLeast:@"1.0.2" andLessThan:@"2.0"]) || isCurrentlyMigrating) { + [VersionMigrations migrateFrom1Dot0Dot2ToVersion2Dot0]; + } + + if(([self isVersion:previousVersion atLeast:@"2.0.0" andLessThan:@"2.0.18"])) { + [VersionMigrations migrateBloomFilter]; + } + + if ([self isVersion:previousVersion atLeast:@"2.0.0" andLessThan:@"2.0.21"] || needsToRegisterPush) { + [self clearVideoCache]; + [self blockingPushRegistration]; + + } +} + ++ (BOOL)isMigrating{ + return [self isMigratingTo2Dot0]; +} + ++ (BOOL) isVersion:(NSString *)thisVersionString atLeast:(NSString *)openLowerBoundVersionString andLessThan:(NSString *)closedUpperBoundVersionString { + return [self isVersion:thisVersionString atLeast:openLowerBoundVersionString] && [self isVersion:thisVersionString lessThan:closedUpperBoundVersionString]; +} + ++ (BOOL) isVersion:(NSString *)thisVersionString atLeast:(NSString *)thatVersionString { + return [thisVersionString compare:thatVersionString options:NSNumericSearch] != NSOrderedAscending; +} + ++ (BOOL) isVersion:(NSString *)thisVersionString lessThan:(NSString *)thatVersionString { + return [thisVersionString compare:thatVersionString options:NSNumericSearch] == NSOrderedAscending; +} + ++ (void)clearUserDefaults{ + NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier]; + [[NSUserDefaults standardUserDefaults] removePersistentDomainForName:appDomain]; + + [Environment.preferences setAndGetCurrentVersion]; + [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:IS_MIGRATING_FROM_1DOT0_TO_LARGER_KEY]; + [[NSUserDefaults standardUserDefaults] synchronize]; +} + +#pragma mark 2.0.1 + + (void)migrateBloomFilter { // The bloom filter had to be moved to the cache folder after rejection of the 2.0.1 NSString *oldBloomKey = @"Directory Bloom Data"; @@ -39,6 +95,8 @@ return; } +#pragma mark 2.0 + + (void)migrateFrom1Dot0Dot2ToVersion2Dot0 { if (!([self wasRedPhoneRegistered] || [self isMigratingTo2Dot0])) { @@ -60,7 +118,7 @@ [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:waitingController animated:YES completion:nil]; [PushManager.sharedManager registrationAndRedPhoneTokenRequestWithSuccess:^(NSData *pushToken, NSData *voipToken, NSString *signupToken) { - [TSAccountManager registerWithRedPhoneToken:signupToken pushToken:pushToken voipToken:voipToken success:^{ + [TSAccountManager registerWithRedPhoneToken:signupToken pushToken:pushToken voipToken:voipToken success:^{ [UIApplication.sharedApplication setNetworkActivityIndicatorVisible:NO]; [self clearMigrationFlag]; Environment *env = [Environment getCurrent]; @@ -94,7 +152,6 @@ }]; } -#pragma mark helper methods + (void) migrateRecentCallsToVersion2Dot0 { NSUserDefaults *defaults = NSUserDefaults.standardUserDefaults; NSData *encodedData = [defaults objectForKey:RECENT_CALLS_DEFAULT_KEY]; @@ -158,17 +215,76 @@ [UICKeyChainStore removeItemForKey:SIGNALING_EXTRA_KEY]; } -+ (void)clearUserDefaults{ - NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier]; - [[NSUserDefaults standardUserDefaults] removePersistentDomainForName:appDomain]; ++ (BOOL)isMigratingTo2Dot0{ + return [self userDefaultsBoolForKey:IS_MIGRATING_FROM_1DOT0_TO_LARGER_KEY]; +} + ++ (void)clearMigrationFlag{ + [[NSUserDefaults standardUserDefaults] removeObjectForKey:IS_MIGRATING_FROM_1DOT0_TO_LARGER_KEY]; +} + +#pragma mark Upgrading to 2.1 - Needs to register VOIP token + Removing video cache folder + ++ (void)blockingPushRegistration{ + [UIApplication.sharedApplication setNetworkActivityIndicatorVisible:YES]; - [Environment.preferences setAndGetCurrentVersion]; - [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:IS_MIGRATING_FROM_1DOT0_TO_LARGER_KEY]; - [[NSUserDefaults standardUserDefaults] synchronize]; + UIAlertController *waitingController = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Upgrading Signal ...", nil) + message:nil + preferredStyle:UIAlertControllerStyleAlert]; + + [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:waitingController + animated:YES + completion:nil]; + + failedPushRegistrationBlock failure = ^(NSError *error) { + [self refreshPushLock:waitingController]; + }; + + [[PushManager sharedManager] requestPushTokenWithSuccess:^(NSData *pushToken, NSData *voipToken) { + [TSAccountManager registerForPushNotifications:pushToken voipToken:voipToken success:^{ + [[NSUserDefaults standardUserDefaults] removeObjectForKey:NEEDS_TO_REGISTER_PUSH_KEY]; + } failure:failure]; + } failure:failure]; } -+ (BOOL)isMigratingTo2Dot0{ - NSNumber *num = [[NSUserDefaults standardUserDefaults] objectForKey:IS_MIGRATING_FROM_1DOT0_TO_LARGER_KEY]; ++ (void)refreshPushLock:(UIAlertController*)waitingController { + [UIApplication.sharedApplication setNetworkActivityIndicatorVisible:NO]; + [waitingController dismissViewControllerAnimated:NO completion:^{ + UIAlertController *retryController = [UIAlertController alertControllerWithTitle:@"Upgrading Signal failed" + message:@"An error occured while upgrading, please try again." + preferredStyle:UIAlertControllerStyleAlert]; + + [retryController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"REGISTER_FAILED_TRY_AGAIN", nil) + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *action) { + [self blockingPushRegistration]; + }]]; + + [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:retryController + animated:YES + completion:nil]; + }]; +} + ++ (BOOL)needsRegisterPush { + return [self userDefaultsBoolForKey:NEEDS_TO_REGISTER_PUSH_KEY]; +} + ++ (void)clearVideoCache { + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil; + basePath = [basePath stringByAppendingPathComponent:@"videos"]; + + NSError *error; + if([[NSFileManager defaultManager] fileExistsAtPath:basePath]){ + [NSFileManager.defaultManager removeItemAtPath:basePath error:&error]; + } + DDLogError(@"An error occured while removing the videos cache folder from old location: %@", + error.debugDescription); +} + ++ (BOOL)userDefaultsBoolForKey:(NSString*)key { + NSNumber *num = [[NSUserDefaults standardUserDefaults] objectForKey:key]; if (!num) { return NO; @@ -177,8 +293,4 @@ } } -+ (void)clearMigrationFlag{ - [[NSUserDefaults standardUserDefaults] setObject:nil forKey:IS_MIGRATING_FROM_1DOT0_TO_LARGER_KEY]; -} - @end diff --git a/Signal/src/textsecure/Network/API/TSNetworkManager.m b/Signal/src/textsecure/Network/API/TSNetworkManager.m index cb7f1c169..8f36a05f2 100644 --- a/Signal/src/textsecure/Network/API/TSNetworkManager.m +++ b/Signal/src/textsecure/Network/API/TSNetworkManager.m @@ -39,7 +39,6 @@ NSURLSessionConfiguration *sessionConf = NSURLSessionConfiguration.ephemeralSessionConfiguration; self.operationManager = [[AFHTTPSessionManager alloc] initWithBaseURL:[[NSURL alloc] initWithString:textSecureServerURL] sessionConfiguration:sessionConf]; AFSecurityPolicy *policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate]; - policy.allowInvalidCertificates = YES; policy.allowInvalidCertificates = YES; //The certificate is not signed by a CA in the iOS trust store. policy.validatesCertificateChain = NO; //Looking at AFNetworking's implementation of chain checking, we don't need to pin all certs in chain. https://github.com/AFNetworking/AFNetworking/blob/104ce04105098466ea0ea4e337af554d7b9df195/AFNetworking/AFSecurityPolicy.m#L281 Trust to the trusted cert is already vertified before by AFServerTrustIsValid(); NSString *certPath = [NSBundle.mainBundle pathForResource:@"textsecure" ofType:@"cer"]; diff --git a/Signal/src/textsecure/Storage/TSStorageManager.m b/Signal/src/textsecure/Storage/TSStorageManager.m index 5390b5e94..527dad36a 100644 --- a/Signal/src/textsecure/Storage/TSStorageManager.m +++ b/Signal/src/textsecure/Storage/TSStorageManager.m @@ -81,16 +81,24 @@ static NSString * keychainDBPassAccount = @"TSDatabasePass"; - (void)protectSignalFiles{ [self protectFolderAtPath:[TSAttachmentStream attachmentsFolder]]; [self protectFolderAtPath:[self dbPath]]; + [self protectFolderAtPath:[[self dbPath] stringByAppendingString:@"-shm"]]; + [self protectFolderAtPath:[[self dbPath] stringByAppendingString:@"-wal"]]; [self protectFolderAtPath:[[DebugLogger sharedInstance] logsDirectory]]; } - (void)protectFolderAtPath:(NSString*)path { + if (![NSFileManager.defaultManager fileExistsAtPath:path]) { + return; + } + NSError *error; - NSDictionary *attrs = @{NSFileProtectionKey: NSFileProtectionCompleteUntilFirstUserAuthentication, - NSURLIsExcludedFromBackupKey:@YES}; + NSDictionary *fileProtection = @{NSFileProtectionKey:NSFileProtectionCompleteUntilFirstUserAuthentication}; + [[NSFileManager defaultManager] setAttributes:fileProtection ofItemAtPath:path error:&error]; + NSDictionary *resourcesAttrs = @{NSURLIsExcludedFromBackupKey: @YES}; - BOOL success = [NSFileManager.defaultManager setAttributes:attrs ofItemAtPath:path error:&error]; + NSURL *ressourceURL = [NSURL fileURLWithPath:path]; + BOOL success = [ressourceURL setResourceValues:resourcesAttrs error:&error]; if (error || !success) { DDLogError(@"Error while removing files from backup: %@", error.description); diff --git a/Signal/src/view controllers/MessagesViewController.m b/Signal/src/view controllers/MessagesViewController.m index c69f93062..a84658666 100644 --- a/Signal/src/view controllers/MessagesViewController.m +++ b/Signal/src/view controllers/MessagesViewController.m @@ -1196,28 +1196,34 @@ typedef enum : NSUInteger { }]; } --(void)sendQualityAdjustedAttachment:(NSURL*)movieURL { - // TODO: should support anything that is in the videos directory - AVAsset *video = [AVAsset assetWithURL:movieURL]; - AVAssetExportSession *exportSession = [AVAssetExportSession exportSessionWithAsset:video presetName:AVAssetExportPresetMediumQuality]; - exportSession.shouldOptimizeForNetworkUse = YES; - exportSession.outputFileType = AVFileTypeMPEG4; - - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); +- (NSURL*)videoTempFolder { + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil; basePath = [basePath stringByAppendingPathComponent:@"videos"]; if (![[NSFileManager defaultManager] fileExistsAtPath:basePath]) { [[NSFileManager defaultManager] createDirectoryAtPath:basePath withIntermediateDirectories:YES attributes:nil error:nil]; } + return [NSURL fileURLWithPath:basePath]; +} + +-(void)sendQualityAdjustedAttachment:(NSURL*)movieURL { + AVAsset *video = [AVAsset assetWithURL:movieURL]; + AVAssetExportSession *exportSession = [AVAssetExportSession exportSessionWithAsset:video presetName:AVAssetExportPresetMediumQuality]; + exportSession.shouldOptimizeForNetworkUse = YES; + exportSession.outputFileType = AVFileTypeMPEG4; - NSURL *compressedVideoUrl = [NSURL fileURLWithPath:basePath]; double currentTime = [[NSDate date] timeIntervalSince1970]; NSString *strImageName = [NSString stringWithFormat:@"%f",currentTime]; - compressedVideoUrl = [compressedVideoUrl URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.mp4",strImageName]]; + NSURL *compressedVideoUrl = [[self videoTempFolder] URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.mp4",strImageName]]; exportSession.outputURL = compressedVideoUrl; [exportSession exportAsynchronouslyWithCompletionHandler:^{ + NSError *error; [self sendMessageAttachment:[NSData dataWithContentsOfURL:compressedVideoUrl] ofType:@"video/mp4"]; + [[NSFileManager defaultManager] removeItemAtURL:compressedVideoUrl error:&error]; + if (error) { + DDLogWarn(@"Failed to remove cached video file: %@", error.debugDescription); + } }]; } diff --git a/Signal/src/view controllers/UITests/SignalsViewController.m b/Signal/src/view controllers/UITests/SignalsViewController.m index 13a9e0aad..23fa01f42 100644 --- a/Signal/src/view controllers/UITests/SignalsViewController.m +++ b/Signal/src/view controllers/UITests/SignalsViewController.m @@ -85,7 +85,7 @@ static NSString* const kShowSignupFlowSegue = @"showSignupFlow"; [super viewWillAppear:animated]; [self checkIfEmptyView]; - if (![TSAccountManager isRegistered] && ![VersionMigrations isMigratingTo2Dot0]){ + if (![TSAccountManager isRegistered] && ![VersionMigrations isMigrating]){ [self performSegueWithIdentifier:kShowSignupFlowSegue sender:self]; return; }