From 009b1ff996a788d0660a353ea16a975a46c47870 Mon Sep 17 00:00:00 2001 From: Maarten Billemont Date: Sat, 14 Sep 2013 14:04:06 -0400 Subject: [PATCH] Fixes to cloud store switching. [UPDATED] Log out when user is no longer available. [UPDATED] Re-enabled cloud store switching using new USM store enumeration facilities. [UPDATED] Lower log level for test and crash logs so we can see why stuff fails. --- External/UbiquityStoreManager | 2 +- MasterPassword/ObjC/MPAppDelegate_Shared.m | 7 +- .../ObjC/iOS/MPLogsViewController.m | 148 +++++++++--------- MasterPassword/ObjC/iOS/MPiOSAppDelegate.m | 8 +- .../ObjC/iOS/MainStoryboard_iPhone.storyboard | 82 +++------- .../project.pbxproj | 11 +- 6 files changed, 106 insertions(+), 152 deletions(-) diff --git a/External/UbiquityStoreManager b/External/UbiquityStoreManager index 0202de6c..0c13b407 160000 --- a/External/UbiquityStoreManager +++ b/External/UbiquityStoreManager @@ -1 +1 @@ -Subproject commit 0202de6cf5b1f2847f4726af0788f6fce9683b32 +Subproject commit 0c13b407e1ca5113bbed5c233215ff61ed77f41a diff --git a/MasterPassword/ObjC/MPAppDelegate_Shared.m b/MasterPassword/ObjC/MPAppDelegate_Shared.m index 9ba72cbf..369c150a 100644 --- a/MasterPassword/ObjC/MPAppDelegate_Shared.m +++ b/MasterPassword/ObjC/MPAppDelegate_Shared.m @@ -8,6 +8,7 @@ #import "MPAppDelegate_Shared.h" #import "MPAppDelegate_Store.h" +#import "MPAppDelegate_Key.h" @implementation MPAppDelegate_Shared { NSManagedObjectID *_activeUserOID; @@ -36,8 +37,10 @@ NSError *error; MPUserEntity *activeUser = (MPUserEntity *)[moc existingObjectWithID:_activeUserOID error:&error]; - if (!activeUser) - err(@"Failed to retrieve active user: %@", error); + if (!activeUser) { + [self signOutAnimated:YES]; + err(@"Failed to retrieve active user: %@", error); + } return activeUser; } diff --git a/MasterPassword/ObjC/iOS/MPLogsViewController.m b/MasterPassword/ObjC/iOS/MPLogsViewController.m index 82c1f301..bce3229d 100644 --- a/MasterPassword/ObjC/iOS/MPLogsViewController.m +++ b/MasterPassword/ObjC/iOS/MPLogsViewController.m @@ -74,83 +74,77 @@ - (void)switchCloudStore { -// NSError *error = nil; -// NSURL *cloudStoreDirectory = [[MPiOSAppDelegate get].storeManager URLForCloudStoreDirectory]; -// NSURL *cloudContentDirectory = [[MPiOSAppDelegate get].storeManager URLForCloudContentDirectory]; -// NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:cloudContentDirectory includingPropertiesForKeys:nil -// options:NSDirectoryEnumerationSkipsHiddenFiles error:&error]; -// if (!contents) -// err(@"While enumerating cloud contents: %@", error); -// -// BOOL directory; -// NSMutableDictionary *stores = [NSMutableDictionary dictionaryWithCapacity:[contents count]]; -// NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil]; -// NSPersistentStoreCoordinator *storePSC = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; -// NSFetchRequest *usersFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )]; -// NSFetchRequest *sitesFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )]; -// for (NSURL *content in contents) -// if ([[NSFileManager defaultManager] fileExistsAtPath:content.path isDirectory:&directory] && directory) { -// NSString *contentString = [content lastPathComponent]; -// NSUInteger firstDash = [contentString rangeOfString:@"-" options:0].location; -// NSString *storeDescription = firstDash == NSNotFound? contentString: [contentString substringToIndex:firstDash]; -// NSPersistentStore *store = nil; -// @try { -// NSURL *storeURL = [[cloudStoreDirectory -// URLByAppendingPathComponent:[content lastPathComponent] isDirectory:NO] -// URLByAppendingPathExtension:@"sqlite"]; -// if (!(store = [storePSC addPersistentStoreWithType:NSSQLiteStoreType configuration:nil -// URL:storeURL options:@{ -// NSPersistentStoreUbiquitousContentNameKey : [[MPiOSAppDelegate get].storeManager valueForKey:@"contentName"], -// NSPersistentStoreUbiquitousContentURLKey : content, -// NSMigratePersistentStoresAutomaticallyOption : @YES, -// NSInferMappingModelAutomaticallyOption : @YES, -// NSPersistentStoreFileProtectionKey : NSFileProtectionComplete -// } error:&error])) { -// wrn(@"Couldn't describe store opening %@: %@", [content lastPathComponent], error); -// continue; -// } -// -// NSUInteger userCount, siteCount; -// NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; -// moc.persistentStoreCoordinator = storePSC; -// if ((userCount = [moc countForFetchRequest:usersFetchRequest error:&error]) == NSNotFound) { -// wrn(@"Couldn't describe store userCount %@: %@", [content lastPathComponent], error); -// continue; -// } -// if ((siteCount = [moc countForFetchRequest:sitesFetchRequest error:&error]) == NSNotFound) { -// wrn(@"Couldn't describe store siteCount %@: %@", [content lastPathComponent], error); -// continue; -// } -// -// storeDescription = PearlString( @"%@: %dU, %dS", storeDescription, userCount, siteCount ); -// } -// @catch (NSException *exception) { -// wrn(@"Couldn't describe store %@: exception %@", [content lastPathComponent], exception); -// } -// @finally { -// if (store) if (![storePSC removePersistentStore:store error:&error]) -// wrn(@"Couldn't remove store %@: %@", [content lastPathComponent], error); -// [stores setObject:storeDescription forKey:[content lastPathComponent]]; -// } -// } -// -// NSString *storeUUID = [[MPiOSAppDelegate get].storeManager valueForKey:@"storeUUID_ThreadSafe"]; -// NSUInteger firstDash = [storeUUID rangeOfString:@"-" options:0].location; -// PearlArrayTVC *vc = [[PearlArrayTVC alloc] initWithStyle:UITableViewStylePlain]; -// vc.title = PearlString( @"Current: %@", firstDash == NSNotFound? storeUUID: [storeUUID substringToIndex:firstDash] ); -// [stores enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { -// [vc addRowWithName:obj style:PearlArrayTVCRowStyleLink toggled:NO toSection:@"Cloud Stores" -// activationBlock:^BOOL(BOOL wasToggled) { -// [[MPiOSAppDelegate get].storeManager setValue:key forKey:@"storeUUID"]; -// [[MPiOSAppDelegate get].storeManager reloadStore]; -// [[MPiOSAppDelegate get] signOutAnimated:YES]; -// return YES; -// }]; -// }]; -// dispatch_async( dispatch_get_main_queue(), ^{ -// [switchCloudStoreProgress cancelAlertAnimated:YES]; -// [self.navigationController pushViewController:vc animated:YES]; -// } ); + NSDictionary *cloudStores = [[MPiOSAppDelegate get].storeManager enumerateCloudStores]; + if (!cloudStores) + wrn(@"Failed enumerating cloud stores."); + + NSString *currentStoreUUID = nil; + NSMutableDictionary *stores = [NSMutableDictionary dictionary]; + NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil]; + NSPersistentStoreCoordinator *storePSC = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; + NSFetchRequest *usersFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )]; + NSFetchRequest *sitesFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )]; + for (NSURL *cloudStoreURL in cloudStores) { + NSString *storeUUID = [[cloudStoreURL URLByDeletingPathExtension] lastPathComponent]; + for (NSDictionary *cloudStoreOptions in [cloudStores objectForKey:cloudStoreURL]) { + NSError *error = nil; + NSPersistentStore *store = nil; + NSUInteger firstDash = [storeUUID rangeOfString:@"-" options:0].location; + NSString *storeDescription = PearlString( @"%@ v%@", + firstDash == NSNotFound? storeUUID: [storeUUID substringToIndex:firstDash], + cloudStoreOptions[USMCloudVersionKey] ); + if ([cloudStoreOptions[USMCloudCurrentKey] boolValue]) + currentStoreUUID = storeUUID; + @try { + if (!(store = [storePSC addPersistentStoreWithType:NSSQLiteStoreType configuration:nil + URL:cloudStoreURL options:cloudStoreOptions error:&error])) { + wrn(@"Couldn't describe store %@. While opening: %@", storeDescription, error); + continue; + } + + NSUInteger userCount, siteCount; + NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; + moc.persistentStoreCoordinator = storePSC; + if ((userCount = [moc countForFetchRequest:usersFetchRequest error:&error]) == NSNotFound) { + wrn(@"Couldn't describe store %@. While determining userCount: %@", storeDescription, error); + continue; + } + if ((siteCount = [moc countForFetchRequest:sitesFetchRequest error:&error]) == NSNotFound) { + wrn(@"Couldn't describe store %@. While determining siteCount: %@", storeDescription, error); + continue; + } + + storeDescription = PearlString( @"%@: %dU, %dS", storeDescription, userCount, siteCount ); + } + @catch (NSException *exception) { + wrn(@"Couldn't describe store %@: %@", storeDescription, exception); + } + @finally { + if (store && ![storePSC removePersistentStore:store error:&error]) { + wrn(@"Couldn't remove store %@: %@", storeDescription, error); + storePSC = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; + } + + [stores setObject:cloudStoreOptions forKey:storeDescription]; + } + } + } + + PearlArrayTVC *vc = [[PearlArrayTVC alloc] initWithStyle:UITableViewStylePlain]; + NSUInteger firstDash = [currentStoreUUID rangeOfString:@"-" options:0].location; + vc.title = PearlString( @"Active: %@", firstDash == NSNotFound? currentStoreUUID: [currentStoreUUID substringToIndex:firstDash] ); + [stores enumerateKeysAndObjectsUsingBlock:^(id storeDescription, id cloudStoreOptions, BOOL *stop) { + [vc addRowWithName:storeDescription style:PearlArrayTVCRowStyleLink toggled:[cloudStoreOptions[USMCloudCurrentKey] boolValue] + toSection:@"Cloud Stores" activationBlock:^BOOL(BOOL wasToggled) { + [[MPiOSAppDelegate get].storeManager switchToCloudStoreWithOptions:cloudStoreOptions]; + [self.navigationController popToRootViewControllerAnimated:YES]; + return YES; + }]; + }]; + dispatch_async( dispatch_get_main_queue(), ^{ + [switchCloudStoreProgress cancelAlertAnimated:YES]; + [self.navigationController pushViewController:vc animated:YES]; + } ); } - (IBAction)toggleLevelControl:(UISegmentedControl *)sender { diff --git a/MasterPassword/ObjC/iOS/MPiOSAppDelegate.m b/MasterPassword/ObjC/iOS/MPiOSAppDelegate.m index c972db55..a4b2b204 100644 --- a/MasterPassword/ObjC/iOS/MPiOSAppDelegate.m +++ b/MasterPassword/ObjC/iOS/MPiOSAppDelegate.m @@ -49,9 +49,7 @@ }]; [TestFlight takeOff:testFlightToken]; [[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) { - PearlLogLevel level = PearlLogLevelWarn; - if ([[MPiOSConfig get].sendInfo boolValue]) - level = PearlLogLevelInfo; + PearlLogLevel level = PearlLogLevelDebug; if (message.level >= level) TFLog( @"%@", [message messageDescription] ); @@ -80,9 +78,9 @@ [Crashlytics setObjectValue:@"Anonymous" forKey:@"username"]; [Crashlytics startWithAPIKey:crashlyticsAPIKey]; [[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) { - PearlLogLevel level = PearlLogLevelWarn; + PearlLogLevel level = PearlLogLevelInfo; if ([[MPiOSConfig get].sendInfo boolValue]) - level = PearlLogLevelInfo; + level = PearlLogLevelDebug; if (message.level >= level) CLSLog( @"%@", [message messageDescription] ); diff --git a/MasterPassword/ObjC/iOS/MainStoryboard_iPhone.storyboard b/MasterPassword/ObjC/iOS/MainStoryboard_iPhone.storyboard index 1636ff5e..b9639c7a 100644 --- a/MasterPassword/ObjC/iOS/MainStoryboard_iPhone.storyboard +++ b/MasterPassword/ObjC/iOS/MainStoryboard_iPhone.storyboard @@ -545,6 +545,7 @@ Your passwords will be AES-encrypted with your master password. + @@ -1167,17 +1168,17 @@ L4m3P4sSw0rD - + - + - + - + @@ -1336,7 +1337,7 @@ L4m3P4sSw0rD - + - + @@ -1549,15 +1550,15 @@ You can use the words in the background for inspiration in finding a memorable m - @@ -1771,6 +1771,7 @@ You can use the words in the background for inspiration in finding a memorable m + @@ -1850,7 +1851,7 @@ If you set a custom password, it will be encrypted before it is saved to the clo - + @@ -1944,6 +1945,7 @@ If you set a custom password, it will be encrypted before it is saved to the clo + @@ -2512,7 +2514,7 @@ You can make passwords for anything, like email addresses, sites or real-world t - + @@ -2813,7 +2815,6 @@ You can make passwords for anything, like email addresses, sites or real-world t - @@ -2908,36 +2909,21 @@ However, it means that anyone who finds your device unlocked can do the same. - - - - - - - - - - - - - - - @@ -2945,39 +2931,15 @@ However, it means that anyone who finds your device unlocked can do the same. - - - - - - - - - - - - - - - - - - - - - - - - @@ -2989,9 +2951,9 @@ However, it means that anyone who finds your device unlocked can do the same. - - - - + + + + \ No newline at end of file diff --git a/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj b/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj index 8c84e868..7cd21505 100644 --- a/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj +++ b/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj @@ -3709,12 +3709,10 @@ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; CLANG_ENABLE_OBJC_ARC = YES; CODE_SIGN_ENTITLEMENTS = MasterPassword.entitlements; - CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: Maarten Billemont (DWGU95U4ZD)"; EXCLUDED_SOURCE_FILE_NAMES = libTestFlight.a; GCC_PREFIX_HEADER = "MasterPassword-Prefix.pch"; INFOPLIST_FILE = "MasterPassword-Info.plist"; - PROVISIONING_PROFILE = ""; "PROVISIONING_PROFILE[sdk=iphoneos*]" = "EBD2A2E4-AEC3-4EEA-8FF0-5F1A0D9FFA1C"; SKIP_INSTALL = NO; TARGETED_DEVICE_FAMILY = 1; @@ -3728,12 +3726,11 @@ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; CLANG_ENABLE_OBJC_ARC = YES; CODE_SIGN_ENTITLEMENTS = MasterPassword.entitlements; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution: Maarten Billemont"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution: Maarten Billemont (HL3Q45LX9N)"; EXCLUDED_SOURCE_FILE_NAMES = ""; GCC_PREFIX_HEADER = "MasterPassword-Prefix.pch"; INFOPLIST_FILE = "MasterPassword-Info.plist"; - PROVISIONING_PROFILE = ""; - "PROVISIONING_PROFILE[sdk=iphoneos*]" = "E0204B5E-CCA7-447B-828F-53912F920A0C"; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = "4CBD21E7-DB60-4F7F-80F8-98DFA83D2CE0"; SKIP_INSTALL = NO; TARGETED_DEVICE_FAMILY = 1; }; @@ -3844,10 +3841,10 @@ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; CLANG_ENABLE_OBJC_ARC = YES; CODE_SIGN_ENTITLEMENTS = MasterPassword.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution: Maarten Billemont (HL3Q45LX9N)"; EXCLUDED_SOURCE_FILE_NAMES = libTestFlight.a; GCC_PREFIX_HEADER = "MasterPassword-Prefix.pch"; INFOPLIST_FILE = "MasterPassword-Info.plist"; - PROVISIONING_PROFILE = ""; "PROVISIONING_PROFILE[sdk=iphoneos*]" = "6C6B84DD-9D8F-4321-BD77-5F737DBE1778"; SKIP_INSTALL = NO; TARGETED_DEVICE_FAMILY = 1;