Fixes to import code.
[FIXED] Don't recalculate key for each entry in import list. [FIXED] Use correct fetch request to find user entity for import. [FIXED] Properly schedule all use of MOC with performBlock* [ADDED] Use undoManager to revert failed import changes.
This commit is contained in:
parent
b0b6dcc56b
commit
647235616e
@ -197,7 +197,7 @@
|
|||||||
inf(@"Importing sites.");
|
inf(@"Importing sites.");
|
||||||
|
|
||||||
static NSRegularExpression *headerPattern, *sitePattern;
|
static NSRegularExpression *headerPattern, *sitePattern;
|
||||||
__autoreleasing NSError *error;
|
__autoreleasing __block NSError *error;
|
||||||
if (!headerPattern) {
|
if (!headerPattern) {
|
||||||
headerPattern = [[NSRegularExpression alloc]
|
headerPattern = [[NSRegularExpression alloc]
|
||||||
initWithPattern:@"^#[[:space:]]*([^:]+): (.*)"
|
initWithPattern:@"^#[[:space:]]*([^:]+): (.*)"
|
||||||
@ -216,7 +216,7 @@
|
|||||||
return MPImportResultInternalError;
|
return MPImportResultInternalError;
|
||||||
|
|
||||||
MPKey *key = nil;
|
MPKey *key = nil;
|
||||||
MPUserEntity *user = nil;
|
__block MPUserEntity *user = nil;
|
||||||
NSString *bundleVersion = nil, *keyIDHex = nil, *userName = nil;
|
NSString *bundleVersion = nil, *keyIDHex = nil, *userName = nil;
|
||||||
BOOL headerStarted = NO, headerEnded = NO, clearText = NO;
|
BOOL headerStarted = NO, headerEnded = NO, clearText = NO;
|
||||||
NSArray *importedSiteLines = [importedSitesString componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
|
NSArray *importedSiteLines = [importedSitesString componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
|
||||||
@ -252,7 +252,10 @@
|
|||||||
|
|
||||||
NSFetchRequest *userFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([MPUserEntity class])];
|
NSFetchRequest *userFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([MPUserEntity class])];
|
||||||
userFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", userName];
|
userFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", userName];
|
||||||
user = [[self.managedObjectContext executeFetchRequest:fetchRequest error:&error] lastObject];
|
[self.managedObjectContext performBlockAndWait:^{
|
||||||
|
user = [[self.managedObjectContext executeFetchRequest:userFetchRequest error:&error] lastObject];
|
||||||
|
}];
|
||||||
|
dbg(@"Found user: %@", [user debugDescription]);
|
||||||
}
|
}
|
||||||
if ([headerName isEqualToString:@"Key ID"])
|
if ([headerName isEqualToString:@"Key ID"])
|
||||||
keyIDHex = headerValue;
|
keyIDHex = headerValue;
|
||||||
@ -269,9 +272,11 @@
|
|||||||
continue;
|
continue;
|
||||||
if (!keyIDHex || ![userName length])
|
if (!keyIDHex || ![userName length])
|
||||||
return MPImportResultMalformedInput;
|
return MPImportResultMalformedInput;
|
||||||
|
if (!key) {
|
||||||
key = [MPAlgorithmDefaultForBundleVersion(bundleVersion) keyForPassword:password ofUserNamed:userName];
|
key = [MPAlgorithmDefaultForBundleVersion(bundleVersion) keyForPassword:password ofUserNamed:userName];
|
||||||
if (![keyIDHex isEqualToString:[key.keyID encodeHex]])
|
if (![keyIDHex isEqualToString:[key.keyID encodeHex]])
|
||||||
return MPImportResultInvalidPassword;
|
return MPImportResultInvalidPassword;
|
||||||
|
}
|
||||||
if (![importedSiteLine length])
|
if (![importedSiteLine length])
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -292,13 +297,16 @@
|
|||||||
// Find existing site.
|
// Find existing site.
|
||||||
if (user) {
|
if (user) {
|
||||||
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND user == %@", name, user];
|
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND user == %@", name, user];
|
||||||
NSArray *existingSites = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
|
__block NSArray *existingSites = nil;
|
||||||
if (error)
|
[self.managedObjectContext performBlockAndWait:^{
|
||||||
err(@"Couldn't search existing sites: %@", error);
|
existingSites = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
|
||||||
|
}];
|
||||||
if (!existingSites) {
|
if (!existingSites) {
|
||||||
err(@"Lookup of existing sites failed for site: %@, user: %@", name, user.userID);
|
err(@"Lookup of existing sites failed for site: %@, user: %@, error: %@", name, user.userID, error);
|
||||||
return MPImportResultInternalError;
|
return MPImportResultInternalError;
|
||||||
}
|
} else
|
||||||
|
if (existingSites.count)
|
||||||
|
dbg(@"Existing sites: %@", existingSites);
|
||||||
|
|
||||||
[elementsToDelete addObjectsFromArray:existingSites];
|
[elementsToDelete addObjectsFromArray:existingSites];
|
||||||
[importedSiteElements addObject:[NSArray arrayWithObjects:lastUsed, uses, type, version, name, exportContent, nil]];
|
[importedSiteElements addObject:[NSArray arrayWithObjects:lastUsed, uses, type, version, name, exportContent, nil]];
|
||||||
@ -314,19 +322,29 @@
|
|||||||
return MPImportResultCancelled;
|
return MPImportResultCancelled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOL success = NO;
|
||||||
|
[self.managedObjectContext.undoManager beginUndoGrouping];
|
||||||
|
@try {
|
||||||
|
|
||||||
// Delete existing sites.
|
// Delete existing sites.
|
||||||
|
if (elementsToDelete.count)
|
||||||
|
[self.managedObjectContext performBlockAndWait:^{
|
||||||
[elementsToDelete enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
|
[elementsToDelete enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
|
||||||
inf(@"Deleting site: %@, it will be replaced by an imported site.", [obj name]);
|
inf(@"Deleting site: %@, it will be replaced by an imported site.", [obj name]);
|
||||||
|
dbg(@"Deleted Element: %@", [obj debugDescription]);
|
||||||
[self.managedObjectContext deleteObject:obj];
|
[self.managedObjectContext deleteObject:obj];
|
||||||
}];
|
}];
|
||||||
[self saveContext];
|
}];
|
||||||
|
|
||||||
// Import new sites.
|
// Import new sites.
|
||||||
if (!user) {
|
if (!user) {
|
||||||
|
[self.managedObjectContext performBlockAndWait:^{
|
||||||
user = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([MPUserEntity class])
|
user = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([MPUserEntity class])
|
||||||
inManagedObjectContext:self.managedObjectContext];
|
inManagedObjectContext:self.managedObjectContext];
|
||||||
user.name = userName;
|
user.name = userName;
|
||||||
user.keyID = [keyIDHex decodeHex];
|
user.keyID = [keyIDHex decodeHex];
|
||||||
|
}];
|
||||||
|
dbg(@"Created User: %@", [user debugDescription]);
|
||||||
}
|
}
|
||||||
for (NSArray *siteElements in importedSiteElements) {
|
for (NSArray *siteElements in importedSiteElements) {
|
||||||
NSDate *lastUsed = [[NSDateFormatter rfc3339DateFormatter] dateFromString:[siteElements objectAtIndex:0]];
|
NSDate *lastUsed = [[NSDateFormatter rfc3339DateFormatter] dateFromString:[siteElements objectAtIndex:0]];
|
||||||
@ -337,6 +355,7 @@
|
|||||||
NSString *exportContent = [siteElements objectAtIndex:5];
|
NSString *exportContent = [siteElements objectAtIndex:5];
|
||||||
|
|
||||||
// Create new site.
|
// Create new site.
|
||||||
|
[self.managedObjectContext performBlockAndWait:^{
|
||||||
MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:[key.algorithm classNameOfType:type]
|
MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:[key.algorithm classNameOfType:type]
|
||||||
inManagedObjectContext:self.managedObjectContext];
|
inManagedObjectContext:self.managedObjectContext];
|
||||||
element.name = name;
|
element.name = name;
|
||||||
@ -351,9 +370,12 @@
|
|||||||
else
|
else
|
||||||
[element importProtectedContent:exportContent];
|
[element importProtectedContent:exportContent];
|
||||||
}
|
}
|
||||||
|
dbg(@"Created Element: %@", [element debugDescription]);
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
[self saveContext];
|
|
||||||
|
|
||||||
|
[self saveContext];
|
||||||
|
success = YES;
|
||||||
inf(@"Import completed successfully.");
|
inf(@"Import completed successfully.");
|
||||||
#ifdef TESTFLIGHT_SDK_VERSION
|
#ifdef TESTFLIGHT_SDK_VERSION
|
||||||
[TestFlight passCheckpoint:MPCheckpointSitesImported];
|
[TestFlight passCheckpoint:MPCheckpointSitesImported];
|
||||||
@ -362,6 +384,13 @@
|
|||||||
|
|
||||||
return MPImportResultSuccess;
|
return MPImportResultSuccess;
|
||||||
}
|
}
|
||||||
|
@finally {
|
||||||
|
[self.managedObjectContext.undoManager endUndoGrouping];
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
[self.managedObjectContext.undoManager undoNestedGroup];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- (NSString *)exportSitesShowingPasswords:(BOOL)showPasswords {
|
- (NSString *)exportSitesShowingPasswords:(BOOL)showPasswords {
|
||||||
|
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="1171" systemVersion="11E53" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
|
<model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="1487" systemVersion="12A269" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
|
||||||
<entity name="MPElementEntity" representedClassName="MPElementEntity" isAbstract="YES" syncable="YES">
|
<entity name="MPElementEntity" representedClassName="MPElementEntity" isAbstract="YES" syncable="YES">
|
||||||
<attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/>
|
<attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/>
|
||||||
<attribute name="lastUsed" attributeType="Date" syncable="YES"/>
|
<attribute name="lastUsed" attributeType="Date" indexed="YES" syncable="YES"/>
|
||||||
<attribute name="name" attributeType="String" minValueString="1" indexed="YES" syncable="YES"/>
|
<attribute name="name" attributeType="String" minValueString="1" indexed="YES" syncable="YES"/>
|
||||||
<attribute name="requiresExplicitMigration_" attributeType="Boolean" defaultValueString="NO"/>
|
<attribute name="requiresExplicitMigration_" attributeType="Boolean" defaultValueString="NO">
|
||||||
|
<userInfo/>
|
||||||
|
</attribute>
|
||||||
<attribute name="type_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/>
|
<attribute name="type_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/>
|
||||||
<attribute name="userName" optional="YES" attributeType="String" syncable="YES"/>
|
<attribute name="userName" optional="YES" attributeType="String" syncable="YES"/>
|
||||||
<attribute name="uses_" attributeType="Integer 16" defaultValueString="0" syncable="YES"/>
|
<attribute name="uses_" attributeType="Integer 16" defaultValueString="0" indexed="YES" syncable="YES"/>
|
||||||
<attribute name="version_" attributeType="Integer 16" minValueString="0" defaultValueString="0" syncable="YES"/>
|
<attribute name="version_" attributeType="Integer 16" minValueString="0" defaultValueString="0" syncable="YES"/>
|
||||||
<relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="elements" inverseEntity="MPUserEntity" syncable="YES"/>
|
<relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="elements" inverseEntity="MPUserEntity" syncable="YES"/>
|
||||||
</entity>
|
</entity>
|
||||||
@ -24,7 +26,9 @@
|
|||||||
<attribute name="lastUsed" optional="YES" attributeType="Date" syncable="YES"/>
|
<attribute name="lastUsed" optional="YES" attributeType="Date" syncable="YES"/>
|
||||||
<attribute name="name" attributeType="String" syncable="YES"/>
|
<attribute name="name" attributeType="String" syncable="YES"/>
|
||||||
<attribute name="requiresExplicitMigration_" transient="YES" attributeType="Boolean" defaultValueString="NO" syncable="YES"/>
|
<attribute name="requiresExplicitMigration_" transient="YES" attributeType="Boolean" defaultValueString="NO" syncable="YES"/>
|
||||||
<attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO"/>
|
<attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO">
|
||||||
|
<userInfo/>
|
||||||
|
</attribute>
|
||||||
<relationship name="elements" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPElementEntity" inverseName="user" inverseEntity="MPElementEntity" syncable="YES"/>
|
<relationship name="elements" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPElementEntity" inverseName="user" inverseEntity="MPElementEntity" syncable="YES"/>
|
||||||
</entity>
|
</entity>
|
||||||
<elements>
|
<elements>
|
||||||
|
@ -275,14 +275,19 @@
|
|||||||
|
|
||||||
- (void)didSelectNewUserAvatar:(UIButton *)newUserAvatar {
|
- (void)didSelectNewUserAvatar:(UIButton *)newUserAvatar {
|
||||||
|
|
||||||
MPUserEntity *newUser = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([MPUserEntity class])
|
__block MPUserEntity *newUser = nil;
|
||||||
|
[[MPAppDelegate managedObjectContext] performBlockAndWait:^{
|
||||||
|
newUser = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([MPUserEntity class])
|
||||||
inManagedObjectContext:[MPAppDelegate managedObjectContext]];
|
inManagedObjectContext:[MPAppDelegate managedObjectContext]];
|
||||||
|
}];
|
||||||
|
|
||||||
[self showNewUserNameAlertFor:newUser completion:^(BOOL finished) {
|
[self showNewUserNameAlertFor:newUser completion:^(BOOL finished) {
|
||||||
newUserAvatar.selected = NO;
|
newUserAvatar.selected = NO;
|
||||||
if (!finished)
|
if (!finished)
|
||||||
|
[[MPAppDelegate managedObjectContext] performBlock:^{
|
||||||
[[MPAppDelegate managedObjectContext] deleteObject:newUser];
|
[[MPAppDelegate managedObjectContext] deleteObject:newUser];
|
||||||
}];
|
}];
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)showNewUserNameAlertFor:(MPUserEntity *)newUser completion:(void (^)(BOOL finished))completion {
|
- (void)showNewUserNameAlertFor:(MPUserEntity *)newUser completion:(void (^)(BOOL finished))completion {
|
||||||
@ -715,7 +720,9 @@
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (buttonIndex == [sheet destructiveButtonIndex]) {
|
if (buttonIndex == [sheet destructiveButtonIndex]) {
|
||||||
|
[[MPAppDelegate get].managedObjectContext performBlockAndWait:^{
|
||||||
[[MPAppDelegate get].managedObjectContext deleteObject:targetedUser];
|
[[MPAppDelegate get].managedObjectContext deleteObject:targetedUser];
|
||||||
|
}];
|
||||||
[[MPAppDelegate get] saveContext];
|
[[MPAppDelegate get] saveContext];
|
||||||
[self updateUsers];
|
[self updateUsers];
|
||||||
} else
|
} else
|
||||||
@ -724,7 +731,10 @@
|
|||||||
[[self avatarForUser:targetedUser] setSelected:YES];
|
[[self avatarForUser:targetedUser] setSelected:YES];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
[[MPAppDelegate get] saveContext];
|
||||||
} cancelTitle:[PearlStrings get].commonButtonCancel destructiveTitle:@"Delete User" otherTitles:@"Reset Password",
|
} cancelTitle:[PearlStrings get].commonButtonCancel destructiveTitle:@"Delete User" otherTitles:@"Reset Password",
|
||||||
|
@"Save",
|
||||||
nil];
|
nil];
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
Loading…
Reference in New Issue
Block a user