From 4cdeab42561bf72e9b890ecd5aa3dff22a276177 Mon Sep 17 00:00:00 2001 From: Maarten Billemont Date: Sat, 4 Aug 2012 10:16:58 +0200 Subject: [PATCH] Don't block the MOC lookup. [IMPROVED] Don't block when MOC is not yet ready, just return nil. [IMPROVED] Outdated tip links to info, icon links to site search. [IMPROVED] Minor improvements to error handling during import. --- .gitignore | 3 + External/iCloudStoreManager | 2 +- MasterPassword/MPAppDelegate_Store.h | 4 +- MasterPassword/MPAppDelegate_Store.m | 81 ++++++++++--------- MasterPassword/iOS/MPMainViewController.h | 1 + MasterPassword/iOS/MPMainViewController.m | 17 ++-- MasterPassword/iOS/MPSearchDelegate.m | 2 +- MasterPassword/iOS/MPUnlockViewController.m | 31 ++++--- .../iOS/MainStoryboard_iPhone.storyboard | 31 ++++--- 9 files changed, 100 insertions(+), 72 deletions(-) diff --git a/.gitignore b/.gitignore index b96f8dbd..6c92b4f3 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,6 @@ Press/MasterPassword_PressKit/MasterPassword_pressrelease_*.pdf # IPA /sendipa/* !/sendipa/sendipa.conf + +# Java +MasterPassword/Java/**/target diff --git a/External/iCloudStoreManager b/External/iCloudStoreManager index 1614e652..a7b028ff 160000 --- a/External/iCloudStoreManager +++ b/External/iCloudStoreManager @@ -1 +1 @@ -Subproject commit 1614e65222a543a75cdf4e5f757f06e39b5979aa +Subproject commit a7b028ff80cd4768be686a9ef2067dfd31989ce0 diff --git a/MasterPassword/MPAppDelegate_Store.h b/MasterPassword/MPAppDelegate_Store.h index 00aa509b..7a5b8f9e 100644 --- a/MasterPassword/MPAppDelegate_Store.h +++ b/MasterPassword/MPAppDelegate_Store.h @@ -20,9 +20,9 @@ typedef enum { @interface MPAppDelegate_Shared (Store) -+ (NSManagedObjectContext *)managedObjectContext; ++ (NSManagedObjectContext *)managedObjectContextIfReady; + (NSManagedObjectModel *)managedObjectModel; -- (NSManagedObjectContext *)managedObjectContext; +- (NSManagedObjectContext *)managedObjectContextIfReady; - (NSManagedObjectModel *)managedObjectModel; - (UbiquityStoreManager *)storeManager; diff --git a/MasterPassword/MPAppDelegate_Store.m b/MasterPassword/MPAppDelegate_Store.m index 99fc967f..4c8bafc5 100644 --- a/MasterPassword/MPAppDelegate_Store.m +++ b/MasterPassword/MPAppDelegate_Store.m @@ -13,9 +13,9 @@ #pragma mark - Core Data setup -+ (NSManagedObjectContext *)managedObjectContext { ++ (NSManagedObjectContext *)managedObjectContextIfReady { - return [[self get] managedObjectContext]; + return [[self get] managedObjectContextIfReady]; } + (NSManagedObjectModel *)managedObjectModel { @@ -33,7 +33,10 @@ return managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]; } -- (NSManagedObjectContext *)managedObjectContext { +- (NSManagedObjectContext *)managedObjectContextIfReady { + + if (![self storeManager].isReady) + return nil; static NSManagedObjectContext *managedObjectContext = nil; if (managedObjectContext) @@ -41,25 +44,14 @@ managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; [managedObjectContext performBlockAndWait:^{ - managedObjectContext.persistentStoreCoordinator = [self persistentStoreCoordinator]; managedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy; + managedObjectContext.persistentStoreCoordinator = [self storeManager].persistentStoreCoordinator; + managedObjectContext.undoManager = [NSUndoManager new]; }]; return managedObjectContext; } -- (NSPersistentStoreCoordinator *)persistentStoreCoordinator { - - // Start loading the store. - [self storeManager]; - - // Wait until the storeManager is ready. - while (![self storeManager].isReady) - [NSThread sleepForTimeInterval:0.1]; - - return [self storeManager].persistentStoreCoordinator; -} - - (UbiquityStoreManager *)storeManager { static UbiquityStoreManager *storeManager = nil; @@ -112,10 +104,10 @@ - (void)saveContext { - [self.managedObjectContext performBlock:^{ + [self.managedObjectContextIfReady performBlock:^{ NSError *error = nil; - if ([self.managedObjectContext hasChanges]) - if (![self.managedObjectContext save:&error]) + if ([self.managedObjectContextIfReady hasChanges]) + if (![self.managedObjectContextIfReady save:&error]) err(@"While saving context: %@", error); }]; } @@ -124,7 +116,7 @@ - (NSManagedObjectContext *)managedObjectContextForUbiquityStoreManager:(UbiquityStoreManager *)usm { - return self.managedObjectContext; + return self.managedObjectContextIfReady; } - (void)ubiquityStoreManager:(UbiquityStoreManager *)manager log:(NSString *)message { @@ -197,18 +189,16 @@ inf(@"Importing sites."); static NSRegularExpression *headerPattern, *sitePattern; - __autoreleasing __block NSError *error; + __block NSError *error = nil; if (!headerPattern) { - headerPattern = [[NSRegularExpression alloc] - initWithPattern:@"^#[[:space:]]*([^:]+): (.*)" - options:0 error:&error]; + headerPattern = [[NSRegularExpression alloc] initWithPattern:@"^#[[:space:]]*([^:]+): (.*)" + options:0 error:&error]; if (error) err(@"Error loading the header pattern: %@", error); } if (!sitePattern) { - sitePattern = [[NSRegularExpression alloc] - initWithPattern:@"^([^[:space:]]+)[[:space:]]+([[:digit:]]+)[[:space:]]+([[:digit:]]+)(:[[:digit:]]+)?[[:space:]]+([^\t]+)\t(.*)" - options:0 error:&error]; + sitePattern = [[NSRegularExpression alloc] initWithPattern:@"^([^[:space:]]+)[[:space:]]+([[:digit:]]+)[[:space:]]+([[:digit:]]+)(:[[:digit:]]+)?[[:space:]]+([^\t]+)\t(.*)" + options:0 error:&error]; if (error) err(@"Error loading the site pattern: %@", error); } @@ -252,9 +242,20 @@ NSFetchRequest *userFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([MPUserEntity class])]; userFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", userName]; - [self.managedObjectContext performBlockAndWait:^{ - user = [[self.managedObjectContext executeFetchRequest:userFetchRequest error:&error] lastObject]; + __block NSArray *users = nil; + [self.managedObjectContextIfReady performBlockAndWait:^{ + users = [self.managedObjectContextIfReady executeFetchRequest:userFetchRequest error:&error]; }]; + if (!users) { + err(@"While looking for user: %@, error: %@", userName, error); + return MPImportResultInternalError; + } + if ([users count] > 1) { + err(@"While looking for user: %@, found more than one: %u", userName, [users count]); + return MPImportResultInternalError; + } + + user = [users count]? [users lastObject]: nil; dbg(@"Found user: %@", [user debugDescription]); } if ([headerName isEqualToString:@"Key ID"]) @@ -298,8 +299,8 @@ if (user) { fetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND user == %@", name, user]; __block NSArray *existingSites = nil; - [self.managedObjectContext performBlockAndWait:^{ - existingSites = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error]; + [self.managedObjectContextIfReady performBlockAndWait:^{ + existingSites = [self.managedObjectContextIfReady executeFetchRequest:fetchRequest error:&error]; }]; if (!existingSites) { err(@"Lookup of existing sites failed for site: %@, user: %@, error: %@", name, user.userID, error); @@ -323,24 +324,24 @@ } BOOL success = NO; - [self.managedObjectContext.undoManager beginUndoGrouping]; + [self.managedObjectContextIfReady.undoManager beginUndoGrouping]; @try { // Delete existing sites. if (elementsToDelete.count) - [self.managedObjectContext performBlockAndWait:^{ + [self.managedObjectContextIfReady performBlockAndWait:^{ [elementsToDelete enumerateObjectsUsingBlock:^(id obj, BOOL *stop) { inf(@"Deleting site: %@, it will be replaced by an imported site.", [obj name]); dbg(@"Deleted Element: %@", [obj debugDescription]); - [self.managedObjectContext deleteObject:obj]; + [self.managedObjectContextIfReady deleteObject:obj]; }]; }]; // Import new sites. if (!user) { - [self.managedObjectContext performBlockAndWait:^{ + [self.managedObjectContextIfReady performBlockAndWait:^{ user = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([MPUserEntity class]) - inManagedObjectContext:self.managedObjectContext]; + inManagedObjectContext:self.managedObjectContextIfReady]; user.name = userName; user.keyID = [keyIDHex decodeHex]; }]; @@ -355,9 +356,9 @@ NSString *exportContent = [siteElements objectAtIndex:5]; // Create new site. - [self.managedObjectContext performBlockAndWait:^{ + [self.managedObjectContextIfReady performBlockAndWait:^{ MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:[key.algorithm classNameOfType:type] - inManagedObjectContext:self.managedObjectContext]; + inManagedObjectContext:self.managedObjectContextIfReady]; element.name = name; element.user = user; element.type = type; @@ -385,10 +386,10 @@ return MPImportResultSuccess; } @finally { - [self.managedObjectContext.undoManager endUndoGrouping]; + [self.managedObjectContextIfReady.undoManager endUndoGrouping]; if (!success) - [self.managedObjectContext.undoManager undoNestedGroup]; + [self.managedObjectContextIfReady.undoManager undoNestedGroup]; } } diff --git a/MasterPassword/iOS/MPMainViewController.h b/MasterPassword/iOS/MPMainViewController.h index 1c151fa3..b9884e09 100644 --- a/MasterPassword/iOS/MPMainViewController.h +++ b/MasterPassword/iOS/MPMainViewController.h @@ -60,6 +60,7 @@ - (IBAction)upgradePassword; - (IBAction)action:(UIBarButtonItem *)sender; - (IBAction)toggleUser; +- (IBAction)searchOutdatedElements; - (IBAction)closeOutdatedAlert; - (IBAction)infoOutdatedAlert; diff --git a/MasterPassword/iOS/MPMainViewController.m b/MasterPassword/iOS/MPMainViewController.m index 42476daa..61baa9c3 100644 --- a/MasterPassword/iOS/MPMainViewController.m +++ b/MasterPassword/iOS/MPMainViewController.m @@ -86,7 +86,7 @@ action:@selector(editUserName:)]]; [self.userNameContainer addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(copyUserName:)]]; [self.outdatedAlertBack addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self - action:@selector(searchOutdatedElements:)]]; + action:@selector(infoOutdatedAlert)]]; self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"ui_background"]]; @@ -137,12 +137,13 @@ [self updateAnimated:animated]; if ([MPAppDelegate get].activeUser) - [[MPAppDelegate get].managedObjectContext performBlock:^void() { + [[MPAppDelegate get].managedObjectContextIfReady performBlock:^void() { NSError *error = nil; NSFetchRequest *migrationRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([MPElementEntity class])]; migrationRequest.predicate = [NSPredicate predicateWithFormat:@"version_ < %d", MPAlgorithmDefaultVersion]; - NSArray *migrationElements = [[MPAppDelegate get].managedObjectContext executeFetchRequest:migrationRequest error:&error]; + NSArray *migrationElements = [[MPAppDelegate get].managedObjectContextIfReady executeFetchRequest:migrationRequest + error:&error]; if (!migrationElements) { err(@"While looking for elements to migrate: %@", error); return; @@ -652,7 +653,7 @@ }]; } -- (IBAction)searchOutdatedElements:(UITapGestureRecognizer *)sender { +- (IBAction)searchOutdatedElements { self.searchDisplayController.searchBar.selectedScopeButtonIndex = MPSearchScopeOutdated; self.searchDisplayController.searchBar.searchResultsButtonSelected = YES; @@ -824,16 +825,16 @@ // Update password type. if ([self.activeElement.algorithm classOfType:type] != self.activeElement.typeClass) // Type requires a different class of element. Recreate the element. - [[MPAppDelegate managedObjectContext] performBlockAndWait:^{ + [[MPAppDelegate managedObjectContextIfReady] performBlockAndWait:^{ MPElementEntity *newElement = [NSEntityDescription insertNewObjectForEntityForName:[self.activeElement.algorithm classNameOfType:type] - inManagedObjectContext:[MPAppDelegate managedObjectContext]]; + inManagedObjectContext:[MPAppDelegate managedObjectContextIfReady]]; newElement.name = self.activeElement.name; newElement.user = self.activeElement.user; newElement.uses = self.activeElement.uses; newElement.lastUsed = self.activeElement.lastUsed; newElement.version = self.activeElement.version; - [[MPAppDelegate managedObjectContext] deleteObject:self.activeElement]; + [[MPAppDelegate managedObjectContextIfReady] deleteObject:self.activeElement]; self.activeElement = newElement; }]; @@ -941,7 +942,7 @@ if (navigationType == UIWebViewNavigationTypeLinkClicked) { if ([[[request URL] query] isEqualToString:@"outdated"]) { - [self searchOutdatedElements:nil]; + [self searchOutdatedElements]; return NO; } diff --git a/MasterPassword/iOS/MPSearchDelegate.m b/MasterPassword/iOS/MPSearchDelegate.m index 1d2f4feb..e6500610 100644 --- a/MasterPassword/iOS/MPSearchDelegate.m +++ b/MasterPassword/iOS/MPSearchDelegate.m @@ -38,7 +38,7 @@ NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([MPElementEntity class])]; fetchRequest.sortDescriptors = [NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:@"uses_" ascending:NO]]; self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest - managedObjectContext:[MPAppDelegate managedObjectContext] + managedObjectContext:[MPAppDelegate managedObjectContextIfReady] sectionNameKeyPath:nil cacheName:nil]; self.fetchedResultsController.delegate = self; diff --git a/MasterPassword/iOS/MPUnlockViewController.m b/MasterPassword/iOS/MPUnlockViewController.m index 594e3d56..d9c64be9 100644 --- a/MasterPassword/iOS/MPUnlockViewController.m +++ b/MasterPassword/iOS/MPUnlockViewController.m @@ -44,7 +44,7 @@ @synthesize wordList = _wordList; -- (void)initAvatarAlert:(UIAlertView *)alert forUser:(MPUserEntity *)user { +- (void)initializeAvatarAlert:(UIAlertView *)alert forUser:(MPUserEntity *)user { UIScrollView *alertAvatarScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(12, 30, 260, 150)]; alertAvatarScrollView.indicatorStyle = UIScrollViewIndicatorStyleWhite; @@ -88,7 +88,7 @@ [alertAvatarScrollView setContentOffset:selectedOffset animated:YES]; } -- (void)initConfirmationAlert:(UIAlertView *)alert forUser:(MPUserEntity *)user { +- (void)initializeConfirmationAlert:(UIAlertView *)alert forUser:(MPUserEntity *)user { UIView *container = [[UIView alloc] initWithFrame:CGRectMake(12, 70, 260, 110)]; [alert addSubview:container]; @@ -145,6 +145,15 @@ [self initializeWordLabel:wordLabel]; } recurse:NO]; + [[NSNotificationCenter defaultCenter] addObserverForName:PersistentStoreDidChange object:nil queue:nil usingBlock: + ^(NSNotification *note) { + [self updateUsers]; + }]; + [[NSNotificationCenter defaultCenter] addObserverForName:PersistentStoreDidMergeChanges object:nil queue:nil usingBlock: + ^(NSNotification *note) { + [self updateUsers]; + }]; + [super viewDidLoad]; } @@ -195,7 +204,7 @@ NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([MPUserEntity class])]; fetchRequest.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO]]; - NSArray *users = [[MPAppDelegate managedObjectContext] executeFetchRequest:fetchRequest error:nil]; + NSArray *users = [[MPAppDelegate managedObjectContextIfReady] executeFetchRequest:fetchRequest error:nil]; // Clean up avatars. for (UIView *subview in [self.avatarsView subviews]) @@ -276,16 +285,16 @@ - (void)didSelectNewUserAvatar:(UIButton *)newUserAvatar { __block MPUserEntity *newUser = nil; - [[MPAppDelegate managedObjectContext] performBlockAndWait:^{ + [[MPAppDelegate managedObjectContextIfReady] performBlockAndWait:^{ newUser = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([MPUserEntity class]) - inManagedObjectContext:[MPAppDelegate managedObjectContext]]; + inManagedObjectContext:[MPAppDelegate managedObjectContextIfReady]]; }]; [self showNewUserNameAlertFor:newUser completion:^(BOOL finished) { newUserAvatar.selected = NO; if (!finished) - [[MPAppDelegate managedObjectContext] performBlock:^{ - [[MPAppDelegate managedObjectContext] deleteObject:newUser]; + [[MPAppDelegate managedObjectContextIfReady] performBlock:^{ + [[MPAppDelegate managedObjectContextIfReady] deleteObject:newUser]; }]; }]; } @@ -318,7 +327,7 @@ [PearlAlert showAlertWithTitle:@"Choose Your Avatar" message:@"\n\n\n\n\n\n" viewStyle:UIAlertViewStyleDefault initAlert:^(UIAlertView *_alert, UITextField *_firstField) { - [self initAvatarAlert:_alert forUser:newUser]; + [self initializeAvatarAlert:_alert forUser:newUser]; } tappedButtonBlock:^(UIAlertView *_alert, NSInteger _buttonIndex) { @@ -335,7 +344,7 @@ @"\n\n\n\n\n\n" viewStyle:UIAlertViewStyleDefault initAlert:^void(UIAlertView *__alert, UITextField *__firstField) { - [self initConfirmationAlert:__alert forUser:newUser]; + [self initializeConfirmationAlert:__alert forUser:newUser]; } tappedButtonBlock:^void(UIAlertView *__alert, NSInteger __buttonIndex) { if (__buttonIndex == [__alert cancelButtonIndex]) { @@ -720,8 +729,8 @@ return; if (buttonIndex == [sheet destructiveButtonIndex]) { - [[MPAppDelegate get].managedObjectContext performBlockAndWait:^{ - [[MPAppDelegate get].managedObjectContext deleteObject:targetedUser]; + [[MPAppDelegate get].managedObjectContextIfReady performBlockAndWait:^{ + [[MPAppDelegate get].managedObjectContextIfReady deleteObject:targetedUser]; }]; [[MPAppDelegate get] saveContext]; [self updateUsers]; diff --git a/MasterPassword/iOS/MainStoryboard_iPhone.storyboard b/MasterPassword/iOS/MainStoryboard_iPhone.storyboard index b07e7028..40915bb3 100644 --- a/MasterPassword/iOS/MainStoryboard_iPhone.storyboard +++ b/MasterPassword/iOS/MainStoryboard_iPhone.storyboard @@ -1,7 +1,6 @@ - @@ -564,7 +563,7 @@ Your passwords will be AES-encrypted with your master password. - + @@ -814,9 +813,9 @@ L4m3P4sSw0rD - Some of your sites have outdated passwords. Tap this alert to see them. + Some of your sites have outdated passwords. Tap this alert for more info or to find them. -You should upgrade these sites and update their account's passwords as soon as convenient. +You should upgrade these sites and update their account's passwords as soon as is convenient. @@ -842,7 +841,7 @@ You should upgrade these sites and update their account's passwords as soon as c - + @@ -850,7 +849,23 @@ You should upgrade these sites and update their account's passwords as soon as c - + + + + @@ -1798,7 +1813,7 @@ You could use the word wall for inspiration in finding a memorable master passw - + @@ -1833,13 +1848,11 @@ You could use the word wall for inspiration in finding a memorable master passw - -