Import improvements and core data fixes.
[FIXED] Import fixes: - Wait for MOC to become available. - Progress UI while working. - Import files exported with a different master password. - Core Data crashes. [RENAMED] Site's User name -> Login name. [FIXED] Core Data crashes related to using entities from old MOCs.
This commit is contained in:
parent
479d357bd1
commit
75ef15bb4d
Binary file not shown.
Binary file not shown.
2
External/Pearl
vendored
2
External/Pearl
vendored
@ -1 +1 @@
|
||||
Subproject commit cc97c1be4c0f2ee0080af417940244ac8f3e5f47
|
||||
Subproject commit 5a35cdb73c57c65f22959accab2f6ddbeeee0b33
|
@ -17,10 +17,6 @@
|
||||
DA0A1D1515690AF40092735D /* Icon-72@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA0A1D1315690AF30092735D /* Icon-72@2x.png */; };
|
||||
DA0A1D1615690AF40092735D /* Icon-Small-50@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA0A1D1415690AF40092735D /* Icon-Small-50@2x.png */; };
|
||||
DA0E07961577FE490008A67E /* MPEntities.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0E07951577FE490008A67E /* MPEntities.m */; };
|
||||
DA0F9F3315B55397007ED9BC /* MPUserEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0F9F3215B55397007ED9BC /* MPUserEntity.m */; };
|
||||
DA0F9F3615B55397007ED9BC /* MPElementGeneratedEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0F9F3515B55397007ED9BC /* MPElementGeneratedEntity.m */; };
|
||||
DA0F9F3915B55397007ED9BC /* MPElementStoredEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0F9F3815B55397007ED9BC /* MPElementStoredEntity.m */; };
|
||||
DA0F9F3C15B55397007ED9BC /* MPElementEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0F9F3B15B55397007ED9BC /* MPElementEntity.m */; };
|
||||
DA30E9CE15722ECA00A68B4C /* NSBundle+PearlMutableInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = DA30E9CB15722ECA00A68B4C /* NSBundle+PearlMutableInfo.h */; };
|
||||
DA30E9CF15722ECA00A68B4C /* NSBundle+PearlMutableInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = DA30E9CC15722ECA00A68B4C /* NSBundle+PearlMutableInfo.m */; };
|
||||
DA30E9D015722ECA00A68B4C /* Pearl.m in Sources */ = {isa = PBXBuildFile; fileRef = DA30E9CD15722ECA00A68B4C /* Pearl.m */; };
|
||||
@ -95,6 +91,10 @@
|
||||
DA95D5F614DF0B9F008D1B94 /* IASKPSTextFieldSpecifierViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA95D5CC14DF0691008D1B94 /* IASKPSTextFieldSpecifierViewCell.xib */; };
|
||||
DA95D5F714DF0B9F008D1B94 /* IASKPSToggleSwitchSpecifierViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA95D5CD14DF0691008D1B94 /* IASKPSToggleSwitchSpecifierViewCell.xib */; };
|
||||
DA95D5F814DF0B9F008D1B94 /* IASKSpecifierValuesView.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA95D5CE14DF0691008D1B94 /* IASKSpecifierValuesView.xib */; };
|
||||
DAA096FC15E0C59B00912D63 /* MPElementEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DAA096FB15E0C59B00912D63 /* MPElementEntity.m */; };
|
||||
DAA096FF15E0C59B00912D63 /* MPElementGeneratedEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DAA096FE15E0C59B00912D63 /* MPElementGeneratedEntity.m */; };
|
||||
DAA0970215E0C59B00912D63 /* MPElementStoredEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DAA0970115E0C59B00912D63 /* MPElementStoredEntity.m */; };
|
||||
DAA0970515E0C59B00912D63 /* MPUserEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DAA0970415E0C59B00912D63 /* MPUserEntity.m */; };
|
||||
DAB8D45D15036BCF00CED3BC /* MasterPassword.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DAB8D43C15036BCF00CED3BC /* MasterPassword.xcdatamodeld */; };
|
||||
DAB8D45E15036BCF00CED3BC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DAB8D43F15036BCF00CED3BC /* InfoPlist.strings */; };
|
||||
DAB8D45F15036BCF00CED3BC /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB8D44115036BCF00CED3BC /* main.m */; };
|
||||
@ -912,14 +912,6 @@
|
||||
DA0A1D1415690AF40092735D /* Icon-Small-50@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small-50@2x.png"; sourceTree = "<group>"; };
|
||||
DA0E07941577FE490008A67E /* MPEntities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPEntities.h; sourceTree = "<group>"; };
|
||||
DA0E07951577FE490008A67E /* MPEntities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPEntities.m; sourceTree = "<group>"; };
|
||||
DA0F9F3115B55397007ED9BC /* MPUserEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUserEntity.h; sourceTree = "<group>"; };
|
||||
DA0F9F3215B55397007ED9BC /* MPUserEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUserEntity.m; sourceTree = "<group>"; };
|
||||
DA0F9F3415B55397007ED9BC /* MPElementGeneratedEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementGeneratedEntity.h; sourceTree = "<group>"; };
|
||||
DA0F9F3515B55397007ED9BC /* MPElementGeneratedEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementGeneratedEntity.m; sourceTree = "<group>"; };
|
||||
DA0F9F3715B55397007ED9BC /* MPElementStoredEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementStoredEntity.h; sourceTree = "<group>"; };
|
||||
DA0F9F3815B55397007ED9BC /* MPElementStoredEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementStoredEntity.m; sourceTree = "<group>"; };
|
||||
DA0F9F3A15B55397007ED9BC /* MPElementEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementEntity.h; sourceTree = "<group>"; };
|
||||
DA0F9F3B15B55397007ED9BC /* MPElementEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementEntity.m; sourceTree = "<group>"; };
|
||||
DA30E9CB15722ECA00A68B4C /* NSBundle+PearlMutableInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSBundle+PearlMutableInfo.h"; sourceTree = "<group>"; };
|
||||
DA30E9CC15722ECA00A68B4C /* NSBundle+PearlMutableInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSBundle+PearlMutableInfo.m"; sourceTree = "<group>"; };
|
||||
DA30E9CD15722ECA00A68B4C /* Pearl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Pearl.m; sourceTree = "<group>"; };
|
||||
@ -998,6 +990,14 @@
|
||||
DA95D5CD14DF0691008D1B94 /* IASKPSToggleSwitchSpecifierViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = IASKPSToggleSwitchSpecifierViewCell.xib; sourceTree = "<group>"; };
|
||||
DA95D5CE14DF0691008D1B94 /* IASKSpecifierValuesView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = IASKSpecifierValuesView.xib; sourceTree = "<group>"; };
|
||||
DA95D5F014DF0B1E008D1B94 /* MessageUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MessageUI.framework; path = System/Library/Frameworks/MessageUI.framework; sourceTree = SDKROOT; };
|
||||
DAA096FA15E0C59B00912D63 /* MPElementEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementEntity.h; sourceTree = "<group>"; };
|
||||
DAA096FB15E0C59B00912D63 /* MPElementEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementEntity.m; sourceTree = "<group>"; };
|
||||
DAA096FD15E0C59B00912D63 /* MPElementGeneratedEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementGeneratedEntity.h; sourceTree = "<group>"; };
|
||||
DAA096FE15E0C59B00912D63 /* MPElementGeneratedEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementGeneratedEntity.m; sourceTree = "<group>"; };
|
||||
DAA0970015E0C59B00912D63 /* MPElementStoredEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementStoredEntity.h; sourceTree = "<group>"; };
|
||||
DAA0970115E0C59B00912D63 /* MPElementStoredEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementStoredEntity.m; sourceTree = "<group>"; };
|
||||
DAA0970315E0C59B00912D63 /* MPUserEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUserEntity.h; sourceTree = "<group>"; };
|
||||
DAA0970415E0C59B00912D63 /* MPUserEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUserEntity.m; sourceTree = "<group>"; };
|
||||
DAAC35DD156BD77D00C5FD93 /* CoreTelephony.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreTelephony.framework; path = System/Library/Frameworks/CoreTelephony.framework; sourceTree = SDKROOT; };
|
||||
DAB8D43D15036BCF00CED3BC /* MasterPassword 1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 1.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DAB8D44015036BCF00CED3BC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
@ -1737,6 +1737,7 @@
|
||||
DAE4C967157E63BE00EFE047 /* avatar-18.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "avatar-18.png"; sourceTree = "<group>"; };
|
||||
DAE4C968157E63BE00EFE047 /* avatar-18@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "avatar-18@2x.png"; sourceTree = "<group>"; };
|
||||
DAEBC45214F6364500987BF6 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
|
||||
DAF83D6B15E02E04009C8D49 /* MasterPassword 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 3.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DAFE45D815039823003ABA7C /* NSObject+PearlExport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+PearlExport.h"; sourceTree = "<group>"; };
|
||||
DAFE45D915039823003ABA7C /* NSObject+PearlExport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+PearlExport.m"; sourceTree = "<group>"; };
|
||||
DAFE45DA15039823003ABA7C /* NSString+PearlNSArrayFormat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+PearlNSArrayFormat.h"; sourceTree = "<group>"; };
|
||||
@ -2022,14 +2023,14 @@
|
||||
DA0E07941577FE490008A67E /* MPEntities.h */,
|
||||
DA0E07951577FE490008A67E /* MPEntities.m */,
|
||||
DAB8D43C15036BCF00CED3BC /* MasterPassword.xcdatamodeld */,
|
||||
DA0F9F3115B55397007ED9BC /* MPUserEntity.h */,
|
||||
DA0F9F3215B55397007ED9BC /* MPUserEntity.m */,
|
||||
DA0F9F3415B55397007ED9BC /* MPElementGeneratedEntity.h */,
|
||||
DA0F9F3515B55397007ED9BC /* MPElementGeneratedEntity.m */,
|
||||
DA0F9F3715B55397007ED9BC /* MPElementStoredEntity.h */,
|
||||
DA0F9F3A15B55397007ED9BC /* MPElementEntity.h */,
|
||||
DA0F9F3B15B55397007ED9BC /* MPElementEntity.m */,
|
||||
DA0F9F3815B55397007ED9BC /* MPElementStoredEntity.m */,
|
||||
DAA096FA15E0C59B00912D63 /* MPElementEntity.h */,
|
||||
DAA096FB15E0C59B00912D63 /* MPElementEntity.m */,
|
||||
DAA096FD15E0C59B00912D63 /* MPElementGeneratedEntity.h */,
|
||||
DAA096FE15E0C59B00912D63 /* MPElementGeneratedEntity.m */,
|
||||
DAA0970015E0C59B00912D63 /* MPElementStoredEntity.h */,
|
||||
DAA0970115E0C59B00912D63 /* MPElementStoredEntity.m */,
|
||||
DAA0970315E0C59B00912D63 /* MPUserEntity.h */,
|
||||
DAA0970415E0C59B00912D63 /* MPUserEntity.m */,
|
||||
DA600C2415054F3A008E9AB6 /* MPAppDelegate_Key.h */,
|
||||
DA600C2315054F3A008E9AB6 /* MPAppDelegate_Key.m */,
|
||||
DA4426041557C1990052177D /* MPAppDelegate_Shared.h */,
|
||||
@ -4269,10 +4270,10 @@
|
||||
93D390BC6AE7A1C9B91A3668 /* MPKey.m in Sources */,
|
||||
93D394744B5485303B326ECB /* MPAlgorithm.m in Sources */,
|
||||
93D39DC7A7282137B08C8D82 /* MPAlgorithmV1.m in Sources */,
|
||||
DA0F9F3315B55397007ED9BC /* MPUserEntity.m in Sources */,
|
||||
DA0F9F3615B55397007ED9BC /* MPElementGeneratedEntity.m in Sources */,
|
||||
DA0F9F3915B55397007ED9BC /* MPElementStoredEntity.m in Sources */,
|
||||
DA0F9F3C15B55397007ED9BC /* MPElementEntity.m in Sources */,
|
||||
DAA096FC15E0C59B00912D63 /* MPElementEntity.m in Sources */,
|
||||
DAA096FF15E0C59B00912D63 /* MPElementGeneratedEntity.m in Sources */,
|
||||
DAA0970215E0C59B00912D63 /* MPElementStoredEntity.m in Sources */,
|
||||
DAA0970515E0C59B00912D63 /* MPUserEntity.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -5092,10 +5093,11 @@
|
||||
DAB8D43C15036BCF00CED3BC /* MasterPassword.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
children = (
|
||||
DAF83D6B15E02E04009C8D49 /* MasterPassword 3.xcdatamodel */,
|
||||
DA46826C15AB48F100FB09E7 /* MasterPassword 2.xcdatamodel */,
|
||||
DAB8D43D15036BCF00CED3BC /* MasterPassword 1.xcdatamodel */,
|
||||
);
|
||||
currentVersion = DA46826C15AB48F100FB09E7 /* MasterPassword 2.xcdatamodel */;
|
||||
currentVersion = DAF83D6B15E02E04009C8D49 /* MasterPassword 3.xcdatamodel */;
|
||||
path = MasterPassword.xcdatamodeld;
|
||||
sourceTree = "<group>";
|
||||
versionGroupType = wrapper.xcdatamodel;
|
||||
|
@ -50,13 +50,14 @@
|
||||
</MacroExpansion>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Debug"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
enableOpenGLFrameCaptureMode = "0"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableReference
|
||||
|
@ -47,6 +47,9 @@
|
||||
|
||||
- (MPUserEntity *)activeUser {
|
||||
|
||||
if (!self.activeUserID)
|
||||
return nil;
|
||||
|
||||
return (MPUserEntity *)[self.managedObjectContextIfReady objectWithID:self.activeUserID];
|
||||
}
|
||||
|
||||
|
@ -28,8 +28,9 @@ typedef enum {
|
||||
- (UbiquityStoreManager *)storeManager;
|
||||
- (void)saveContext;
|
||||
|
||||
- (MPImportResult)importSites:(NSString *)importedSitesString withPassword:(NSString *)password
|
||||
askConfirmation:(BOOL(^)(NSUInteger importCount, NSUInteger deleteCount))confirmation;
|
||||
- (MPImportResult)importSites:(NSString *)importedSitesString
|
||||
askImportPassword:(NSString *(^)(NSString *userName))importPassword
|
||||
askUserPassword:(NSString *(^)(NSString *userName, NSUInteger importCount, NSUInteger deleteCount))userPassword;
|
||||
- (NSString *)exportSitesShowingPasswords:(BOOL)showPasswords;
|
||||
|
||||
@end
|
||||
|
@ -183,8 +183,12 @@
|
||||
|
||||
#pragma mark - Import / Export
|
||||
|
||||
- (MPImportResult)importSites:(NSString *)importedSitesString withPassword:(NSString *)password
|
||||
askConfirmation:(BOOL(^)(NSUInteger importCount, NSUInteger deleteCount))confirmation {
|
||||
- (MPImportResult)importSites:(NSString *)importedSitesString
|
||||
askImportPassword:(NSString *(^)(NSString *userName))importPassword
|
||||
askUserPassword:(NSString *(^)(NSString *userName, NSUInteger importCount, NSUInteger deleteCount))userPassword {
|
||||
|
||||
while (![self managedObjectContextIfReady])
|
||||
usleep((useconds_t)(USEC_PER_SEC * 0.2));
|
||||
|
||||
inf(@"Importing sites.");
|
||||
|
||||
@ -205,14 +209,15 @@
|
||||
if (!headerPattern || !sitePattern)
|
||||
return MPImportResultInternalError;
|
||||
|
||||
MPKey *key = nil;
|
||||
__block MPUserEntity *user = nil;
|
||||
NSString *bundleVersion = nil, *keyIDHex = nil, *userName = nil;
|
||||
id<MPAlgorithm> importAlgorithm = nil;
|
||||
NSString *importBundleVersion = nil, *importUserName = nil;
|
||||
NSData *importKeyID = nil;
|
||||
BOOL headerStarted = NO, headerEnded = NO, clearText = NO;
|
||||
NSArray *importedSiteLines = [importedSitesString componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
|
||||
NSMutableSet *elementsToDelete = [NSMutableSet set];
|
||||
NSMutableArray *importedSiteElements = [NSMutableArray arrayWithCapacity:[importedSiteLines count]];
|
||||
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([MPElementEntity class])];
|
||||
NSFetchRequest *elementFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([MPElementEntity class])];
|
||||
for (NSString *importedSiteLine in importedSiteLines) {
|
||||
if ([importedSiteLine hasPrefix:@"#"]) {
|
||||
// Comment or header
|
||||
@ -238,20 +243,20 @@
|
||||
NSString *headerName = [importedSiteLine substringWithRange:[headerElements rangeAtIndex:1]];
|
||||
NSString *headerValue = [importedSiteLine substringWithRange:[headerElements rangeAtIndex:2]];
|
||||
if ([headerName isEqualToString:@"User Name"]) {
|
||||
userName = headerValue;
|
||||
importUserName = headerValue;
|
||||
|
||||
NSFetchRequest *userFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([MPUserEntity class])];
|
||||
userFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", userName];
|
||||
userFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", importUserName];
|
||||
__block NSArray *users = nil;
|
||||
[self.managedObjectContextIfReady performBlockAndWait:^{
|
||||
users = [self.managedObjectContextIfReady executeFetchRequest:userFetchRequest error:&error];
|
||||
}];
|
||||
if (!users) {
|
||||
err(@"While looking for user: %@, error: %@", userName, error);
|
||||
err(@"While looking for user: %@, error: %@", importUserName, error);
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
if ([users count] > 1) {
|
||||
err(@"While looking for user: %@, found more than one: %u", userName, [users count]);
|
||||
err(@"While looking for user: %@, found more than one: %u", importUserName, [users count]);
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
|
||||
@ -259,9 +264,11 @@
|
||||
dbg(@"Found user: %@", [user debugDescription]);
|
||||
}
|
||||
if ([headerName isEqualToString:@"Key ID"])
|
||||
keyIDHex = headerValue;
|
||||
if ([headerName isEqualToString:@"Version"])
|
||||
bundleVersion = headerValue;
|
||||
importKeyID = [headerValue decodeHex];
|
||||
if ([headerName isEqualToString:@"Version"]) {
|
||||
importBundleVersion = headerValue;
|
||||
importAlgorithm = MPAlgorithmDefaultForBundleVersion(importBundleVersion);
|
||||
}
|
||||
if ([headerName isEqualToString:@"Passwords"]) {
|
||||
if ([headerValue isEqualToString:@"VISIBLE"])
|
||||
clearText = YES;
|
||||
@ -271,13 +278,8 @@
|
||||
}
|
||||
if (!headerEnded)
|
||||
continue;
|
||||
if (!keyIDHex || ![userName length])
|
||||
if (!importKeyID || ![importUserName length])
|
||||
return MPImportResultMalformedInput;
|
||||
if (!key) {
|
||||
key = [MPAlgorithmDefaultForBundleVersion(bundleVersion) keyForPassword:password ofUserNamed:userName];
|
||||
if (![keyIDHex isEqualToString:[key.keyID encodeHex]])
|
||||
return MPImportResultInvalidPassword;
|
||||
}
|
||||
if (![importedSiteLine length])
|
||||
continue;
|
||||
|
||||
@ -297,10 +299,10 @@
|
||||
|
||||
// Find existing site.
|
||||
if (user) {
|
||||
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND user == %@", name, user];
|
||||
elementFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND user == %@", name, user];
|
||||
__block NSArray *existingSites = nil;
|
||||
[self.managedObjectContextIfReady performBlockAndWait:^{
|
||||
existingSites = [self.managedObjectContextIfReady executeFetchRequest:fetchRequest error:&error];
|
||||
existingSites = [self.managedObjectContextIfReady executeFetchRequest:elementFetchRequest error:&error];
|
||||
}];
|
||||
if (!existingSites) {
|
||||
err(@"Lookup of existing sites failed for site: %@, user: %@, error: %@", name, user.userID, error);
|
||||
@ -314,14 +316,19 @@
|
||||
}
|
||||
}
|
||||
|
||||
inf(@"Importing %u sites, deleting %u sites, for user: %@",
|
||||
[importedSiteElements count], [elementsToDelete count], [MPUserEntity idFor:userName]);
|
||||
|
||||
// Ask for confirmation to import these sites.
|
||||
if (!confirmation([importedSiteElements count], [elementsToDelete count])) {
|
||||
// Ask for confirmation to import these sites and the master password of the user.
|
||||
inf(@"Importing %u sites, deleting %u sites, for user: %@", [importedSiteElements count], [elementsToDelete count], [MPUserEntity idFor:importUserName]);
|
||||
NSString *userMasterPassword = userPassword(user.name, [importedSiteElements count], [elementsToDelete count]);
|
||||
if (!userMasterPassword) {
|
||||
inf(@"Import cancelled.");
|
||||
return MPImportResultCancelled;
|
||||
}
|
||||
MPKey *userKey = [MPAlgorithmDefault keyForPassword:userMasterPassword ofUserNamed:user.name];
|
||||
if (![userKey.keyID isEqualToData:user.keyID])
|
||||
return MPImportResultInvalidPassword;
|
||||
__block MPKey *importKey = userKey;
|
||||
if ([importKey.keyID isEqualToData:importKeyID])
|
||||
importKey = nil;
|
||||
|
||||
BOOL success = NO;
|
||||
[self.managedObjectContextIfReady.undoManager beginUndoGrouping];
|
||||
@ -337,16 +344,19 @@
|
||||
}];
|
||||
}];
|
||||
|
||||
// Import new sites.
|
||||
// Make sure there is a user.
|
||||
if (!user) {
|
||||
[self.managedObjectContextIfReady performBlockAndWait:^{
|
||||
user = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([MPUserEntity class])
|
||||
inManagedObjectContext:self.managedObjectContextIfReady];
|
||||
user.name = userName;
|
||||
user.keyID = [keyIDHex decodeHex];
|
||||
user.name = importUserName;
|
||||
user.keyID = importKeyID;
|
||||
}];
|
||||
dbg(@"Created User: %@", [user debugDescription]);
|
||||
}
|
||||
[self saveContext];
|
||||
|
||||
// Import new sites.
|
||||
for (NSArray *siteElements in importedSiteElements) {
|
||||
NSDate *lastUsed = [[NSDateFormatter rfc3339DateFormatter] dateFromString:[siteElements objectAtIndex:0]];
|
||||
NSUInteger uses = (unsigned)[[siteElements objectAtIndex:1] integerValue];
|
||||
@ -356,8 +366,9 @@
|
||||
NSString *exportContent = [siteElements objectAtIndex:5];
|
||||
|
||||
// Create new site.
|
||||
__block MPImportResult result = MPImportResultSuccess;
|
||||
[self.managedObjectContextIfReady performBlockAndWait:^{
|
||||
MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:[key.algorithm classNameOfType:type]
|
||||
MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:[MPAlgorithmForVersion(version) classNameOfType:type]
|
||||
inManagedObjectContext:self.managedObjectContextIfReady];
|
||||
element.name = name;
|
||||
element.user = user;
|
||||
@ -367,12 +378,23 @@
|
||||
element.version = version;
|
||||
if ([exportContent length]) {
|
||||
if (clearText)
|
||||
[element importClearTextContent:exportContent usingKey:key];
|
||||
else
|
||||
[element importProtectedContent:exportContent];
|
||||
[element importClearTextContent:exportContent usingKey:userKey];
|
||||
else {
|
||||
if (!importKey)
|
||||
importKey = [importAlgorithm keyForPassword:importPassword(user.name) ofUserNamed:user.name];
|
||||
if (![importKey.keyID isEqualToData:importKeyID]) {
|
||||
result = MPImportResultInvalidPassword;
|
||||
return;
|
||||
}
|
||||
|
||||
[element importProtectedContent:exportContent protectedByKey:importKey usingKey:userKey];
|
||||
}
|
||||
}
|
||||
|
||||
dbg(@"Created Element: %@", [element debugDescription]);
|
||||
}];
|
||||
if (result != MPImportResultSuccess)
|
||||
return result;
|
||||
}
|
||||
|
||||
[self saveContext];
|
||||
|
@ -2,7 +2,7 @@
|
||||
// MPElementEntity.h
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 17/07/12.
|
||||
// Created by Maarten Billemont on 2012-08-19.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
@ -15,10 +15,10 @@
|
||||
|
||||
@property (nonatomic, retain) id content;
|
||||
@property (nonatomic, retain) NSDate * lastUsed;
|
||||
@property (nonatomic, retain) NSString * loginName;
|
||||
@property (nonatomic, retain) NSString * name;
|
||||
@property (nonatomic, retain) NSNumber * requiresExplicitMigration_;
|
||||
@property (nonatomic, retain) NSNumber * type_;
|
||||
@property (nonatomic, retain) NSString * userName;
|
||||
@property (nonatomic, retain) NSNumber * uses_;
|
||||
@property (nonatomic, retain) NSNumber * version_;
|
||||
@property (nonatomic, retain) MPUserEntity *user;
|
||||
|
@ -2,7 +2,7 @@
|
||||
// MPElementEntity.m
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 17/07/12.
|
||||
// Created by Maarten Billemont on 2012-08-19.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
@ -14,10 +14,10 @@
|
||||
|
||||
@dynamic content;
|
||||
@dynamic lastUsed;
|
||||
@dynamic loginName;
|
||||
@dynamic name;
|
||||
@dynamic requiresExplicitMigration_;
|
||||
@dynamic type_;
|
||||
@dynamic userName;
|
||||
@dynamic uses_;
|
||||
@dynamic version_;
|
||||
@dynamic user;
|
||||
|
@ -2,7 +2,7 @@
|
||||
// MPElementGeneratedEntity.h
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 17/07/12.
|
||||
// Created by Maarten Billemont on 2012-08-19.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
// MPElementGeneratedEntity.m
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 17/07/12.
|
||||
// Created by Maarten Billemont on 2012-08-19.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
// MPElementStoredEntity.h
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 17/07/12.
|
||||
// Created by Maarten Billemont on 2012-08-19.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
// MPElementStoredEntity.m
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 17/07/12.
|
||||
// Created by Maarten Billemont on 2012-08-19.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
|
@ -29,7 +29,7 @@
|
||||
|
||||
- (NSUInteger)use;
|
||||
- (NSString *)exportContent;
|
||||
- (void)importProtectedContent:(NSString *)protectedContent;
|
||||
- (void)importProtectedContent:(NSString *)protectedContent protectedByKey:(MPKey *)contentProtectionKey usingKey:(MPKey *)key2;
|
||||
- (void)importClearTextContent:(NSString *)clearContent usingKey:(MPKey *)key;
|
||||
- (BOOL)migrateExplicitly:(BOOL)explicit;
|
||||
|
||||
|
@ -92,7 +92,7 @@
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)importProtectedContent:(NSString *)content {
|
||||
- (void)importProtectedContent:(NSString *)protectedContent protectedByKey:(MPKey *)contentProtectionKey usingKey:(MPKey *)key {
|
||||
|
||||
}
|
||||
|
||||
@ -107,9 +107,9 @@
|
||||
|
||||
- (NSString *)debugDescription {
|
||||
|
||||
return PearlString(@"{%@: name=%@, user=%@, type=%d, uses=%d, lastUsed=%@, version=%d, userName=%@, requiresExplicitMigration=%d}",
|
||||
return PearlString(@"{%@: name=%@, user=%@, type=%d, uses=%d, lastUsed=%@, version=%d, loginName=%@, requiresExplicitMigration=%d}",
|
||||
NSStringFromClass([self class]), self.name, self.user.name, self.type, self.uses, self.lastUsed, self.version,
|
||||
self.userName, self.requiresExplicitMigration);
|
||||
self.loginName, self.requiresExplicitMigration);
|
||||
}
|
||||
|
||||
- (BOOL)migrateExplicitly:(BOOL)explicit {
|
||||
@ -185,7 +185,6 @@
|
||||
if (!key)
|
||||
return;
|
||||
|
||||
assert([key.keyID isEqualToData:self.user.keyID]);
|
||||
[self setContent:content usingKey:key];
|
||||
}
|
||||
|
||||
@ -202,17 +201,23 @@
|
||||
|
||||
NSData *decryptedContent = nil;
|
||||
if ([encryptedContent length])
|
||||
decryptedContent = [encryptedContent decryptWithSymmetricKey:[key subKeyOfLength:PearlCryptKeySize].keyData padding:YES];
|
||||
decryptedContent = [self decryptContent:encryptedContent usingKey:key];
|
||||
|
||||
return [[NSString alloc] initWithBytes:decryptedContent.bytes length:decryptedContent.length encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
- (NSData *)decryptContent:(NSData *)encryptedContent usingKey:(MPKey *)key {
|
||||
|
||||
return [encryptedContent decryptWithSymmetricKey:[key subKeyOfLength:PearlCryptKeySize].keyData padding:YES];
|
||||
}
|
||||
|
||||
- (void)setContent:(id)content usingKey:(MPKey *)key {
|
||||
|
||||
assert(self.type & MPElementTypeClassStored);
|
||||
assert(key);
|
||||
assert([key.keyID isEqualToData:self.user.keyID]);
|
||||
|
||||
NSData *encryptedContent = [[content description] encryptWithSymmetricKey:[key subKeyOfLength:PearlCryptKeySize].keyData padding:YES];
|
||||
NSData *encryptedContent = [[[content description] dataUsingEncoding:NSUTF8StringEncoding]
|
||||
encryptWithSymmetricKey:[key subKeyOfLength:PearlCryptKeySize].keyData padding:YES];
|
||||
|
||||
if (self.type & MPElementFeatureDevicePrivate) {
|
||||
[PearlKeyChain addOrUpdateItemForQuery:[MPElementStoredEntity queryForDevicePrivateElementNamed:self.name]
|
||||
@ -233,9 +238,18 @@
|
||||
return [self.contentObject encodeBase64];
|
||||
}
|
||||
|
||||
- (void)importProtectedContent:(NSString *)protectedContent {
|
||||
- (void)importProtectedContent:(NSString *)protectedContent protectedByKey:(MPKey *)contentProtectionKey usingKey:(MPKey *)key {
|
||||
|
||||
if ([contentProtectionKey.keyID isEqualToData:key.keyID])
|
||||
self.contentObject = [protectedContent decodeBase64];
|
||||
|
||||
else {
|
||||
NSString *clearContent = [[NSString alloc] initWithData:[self decryptContent:[protectedContent decodeBase64]
|
||||
usingKey:contentProtectionKey]
|
||||
encoding:NSUTF8StringEncoding];
|
||||
|
||||
[self importClearTextContent:clearContent usingKey:key];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)importClearTextContent:(NSString *)clearContent usingKey:(MPKey *)key {
|
||||
|
@ -43,11 +43,11 @@ typedef enum {
|
||||
#define MPCheckpointAction @"MPCheckpointAction"
|
||||
#define MPCheckpointHelpChapter @"MPCheckpointHelpChapter"
|
||||
#define MPCheckpointCopyToPasteboard @"MPCheckpointCopyToPasteboard"
|
||||
#define MPCheckpointCopyUserNameToPasteboard @"MPCheckpointCopyUserNameToPasteboard"
|
||||
#define MPCheckpointCopyLoginNameToPasteboard @"MPCheckpointCopyLoginNameToPasteboard"
|
||||
#define MPCheckpointResetPasswordCounter @"MPCheckpointResetPasswordCounter"
|
||||
#define MPCheckpointIncrementPasswordCounter @"MPCheckpointIncrementPasswordCounter"
|
||||
#define MPCheckpointEditPassword @"MPCheckpointEditPassword"
|
||||
#define MPCheckpointEditUserName @"MPCheckpointEditUserName"
|
||||
#define MPCheckpointEditLoginName @"MPCheckpointEditLoginName"
|
||||
#define MPCheckpointCloseAlert @"MPCheckpointCloseAlert"
|
||||
#define MPCheckpointCloseOutdatedAlert @"MPCheckpointCloseOutdatedAlert"
|
||||
#define MPCheckpointUseType @"MPCheckpointUseType"
|
||||
|
@ -2,7 +2,7 @@
|
||||
// MPUserEntity.h
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 17/07/12.
|
||||
// Created by Maarten Billemont on 2012-08-19.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
@ -18,8 +18,8 @@
|
||||
@property (nonatomic, retain) NSData * keyID;
|
||||
@property (nonatomic, retain) NSDate * lastUsed;
|
||||
@property (nonatomic, retain) NSString * name;
|
||||
@property (nonatomic, retain) NSNumber * saveKey_;
|
||||
@property (nonatomic, retain) NSNumber * requiresExplicitMigration_;
|
||||
@property (nonatomic, retain) NSNumber * saveKey_;
|
||||
@property (nonatomic, retain) NSSet *elements;
|
||||
@end
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
// MPUserEntity.m
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 17/07/12.
|
||||
// Created by Maarten Billemont on 2012-08-19.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
@ -17,8 +17,8 @@
|
||||
@dynamic keyID;
|
||||
@dynamic lastUsed;
|
||||
@dynamic name;
|
||||
@dynamic saveKey_;
|
||||
@dynamic requiresExplicitMigration_;
|
||||
@dynamic saveKey_;
|
||||
@dynamic elements;
|
||||
|
||||
@end
|
||||
|
@ -3,6 +3,6 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>_XCCurrentVersionName</key>
|
||||
<string>MasterPassword 2.xcdatamodel</string>
|
||||
<string>MasterPassword 3.xcdatamodel</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
@ -32,9 +32,9 @@
|
||||
<relationship name="elements" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPElementEntity" inverseName="user" inverseEntity="MPElementEntity" syncable="YES"/>
|
||||
</entity>
|
||||
<elements>
|
||||
<element name="MPElementEntity" positionX="160" positionY="192" width="128" height="180"/>
|
||||
<element name="MPElementGeneratedEntity" positionX="160" positionY="192" width="128" height="60"/>
|
||||
<element name="MPElementStoredEntity" positionX="160" positionY="192" width="128" height="60"/>
|
||||
<element name="MPUserEntity" positionX="160" positionY="192" width="128" height="150"/>
|
||||
<element name="MPElementEntity" positionX="0" positionY="0" width="0" height="0"/>
|
||||
<element name="MPElementGeneratedEntity" positionX="0" positionY="0" width="0" height="0"/>
|
||||
<element name="MPElementStoredEntity" positionX="0" positionY="0" width="0" height="0"/>
|
||||
<element name="MPUserEntity" positionX="0" positionY="0" width="0" height="0"/>
|
||||
</elements>
|
||||
</model>
|
@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<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">
|
||||
<attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/>
|
||||
<attribute name="lastUsed" attributeType="Date" indexed="YES" syncable="YES"/>
|
||||
<attribute name="loginName" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<attribute name="name" attributeType="String" minValueString="1" indexed="YES" syncable="YES"/>
|
||||
<attribute name="requiresExplicitMigration_" attributeType="Boolean" defaultValueString="NO">
|
||||
<userInfo/>
|
||||
</attribute>
|
||||
<attribute name="type_" attributeType="Integer 16" defaultValueString="17" 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"/>
|
||||
<relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="elements" inverseEntity="MPUserEntity" syncable="YES"/>
|
||||
</entity>
|
||||
<entity name="MPElementGeneratedEntity" representedClassName="MPElementGeneratedEntity" parentEntity="MPElementEntity" syncable="YES">
|
||||
<attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/>
|
||||
</entity>
|
||||
<entity name="MPElementStoredEntity" representedClassName="MPElementStoredEntity" parentEntity="MPElementEntity" syncable="YES">
|
||||
<attribute name="contentObject" optional="YES" attributeType="Transformable" storedInTruthFile="YES" syncable="YES"/>
|
||||
</entity>
|
||||
<entity name="MPUserEntity" representedClassName="MPUserEntity" syncable="YES">
|
||||
<attribute name="avatar_" attributeType="Integer 16" defaultValueString="0" syncable="YES"/>
|
||||
<attribute name="defaultType_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/>
|
||||
<attribute name="keyID" optional="YES" attributeType="Binary" syncable="YES"/>
|
||||
<attribute name="lastUsed" optional="YES" attributeType="Date" syncable="YES"/>
|
||||
<attribute name="name" attributeType="String" syncable="YES"/>
|
||||
<attribute name="requiresExplicitMigration_" transient="YES" attributeType="Boolean" defaultValueString="NO" syncable="YES"/>
|
||||
<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"/>
|
||||
</entity>
|
||||
<elements>
|
||||
<element name="MPElementEntity" positionX="160" positionY="192" width="128" height="180"/>
|
||||
<element name="MPElementGeneratedEntity" positionX="160" positionY="192" width="128" height="60"/>
|
||||
<element name="MPElementStoredEntity" positionX="160" positionY="192" width="128" height="60"/>
|
||||
<element name="MPUserEntity" positionX="160" positionY="192" width="128" height="150"/>
|
||||
</elements>
|
||||
</model>
|
@ -220,48 +220,79 @@
|
||||
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
|
||||
sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
|
||||
|
||||
__autoreleasing NSError *error;
|
||||
__autoreleasing NSURLResponse *response;
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
PearlAlert *activityAlert = [PearlAlert showAlertWithTitle:@"Importing" message:@"\n\n"
|
||||
viewStyle:UIAlertViewStyleDefault initAlert:
|
||||
^(UIAlertView *alert, UITextField *firstField) {
|
||||
UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
|
||||
activityIndicator.center = CGPointMake(140, 90);
|
||||
[activityIndicator startAnimating];
|
||||
[alert addSubview:activityIndicator];
|
||||
}
|
||||
tappedButtonBlock:nil cancelTitle:nil otherTitles:nil];
|
||||
|
||||
NSError *error;
|
||||
NSURLResponse *response;
|
||||
NSData *importedSitesData = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url]
|
||||
returningResponse:&response error:&error];
|
||||
if (error)
|
||||
err(@"While reading imported sites from %@: %@", url, error);
|
||||
if (!importedSitesData)
|
||||
return NO;
|
||||
return;
|
||||
|
||||
NSString *importedSitesString = [[NSString alloc] initWithData:importedSitesData encoding:NSUTF8StringEncoding];
|
||||
[PearlAlert showAlertWithTitle:@"Import Password" message:
|
||||
@"Enter the master password for this export:"
|
||||
viewStyle:UIAlertViewStyleSecureTextInput initAlert:nil tappedButtonBlock:
|
||||
^(UIAlertView *alert, NSInteger buttonIndex) {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
MPImportResult result = [self importSites:importedSitesString withPassword:[alert textFieldAtIndex:0].text
|
||||
askConfirmation:^BOOL(NSUInteger importCount, NSUInteger deleteCount) {
|
||||
__block BOOL confirmation = NO;
|
||||
MPImportResult result = [self importSites:importedSitesString askImportPassword:^NSString *(NSString *userName) {
|
||||
__block NSString *masterPassword = nil;
|
||||
|
||||
dispatch_group_t confirmationGroup = dispatch_group_create();
|
||||
dispatch_group_enter(confirmationGroup);
|
||||
dispatch_group_t importPasswordGroup = dispatch_group_create();
|
||||
dispatch_group_enter(importPasswordGroup);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[PearlAlert showAlertWithTitle:@"Import Sites?"
|
||||
message:PearlString(
|
||||
@"Import %d sites, overwriting %d existing sites?",
|
||||
importCount, deleteCount)
|
||||
viewStyle:UIAlertViewStyleDefault
|
||||
initAlert:nil
|
||||
tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
|
||||
if (buttonIndex_
|
||||
!= [alert_ cancelButtonIndex])
|
||||
confirmation = YES;
|
||||
[PearlAlert showAlertWithTitle:@"Import File's Master Password"
|
||||
message:PearlString(@"%@'s export was done using a different master password.\n"
|
||||
@"Enter that master password to unlock the exported data.", userName)
|
||||
viewStyle:UIAlertViewStyleSecureTextInput
|
||||
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
|
||||
@try {
|
||||
if (buttonIndex_ == [alert_ cancelButtonIndex])
|
||||
return;
|
||||
|
||||
dispatch_group_leave(confirmationGroup);
|
||||
masterPassword = [alert_ textFieldAtIndex:0].text;
|
||||
}
|
||||
cancelTitle:[PearlStrings get].commonButtonCancel
|
||||
otherTitles:@"Import", nil];
|
||||
@finally {
|
||||
dispatch_group_leave(importPasswordGroup);
|
||||
}
|
||||
}
|
||||
cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Unlock Import", nil];
|
||||
});
|
||||
dispatch_group_wait(
|
||||
confirmationGroup, DISPATCH_TIME_FOREVER);
|
||||
dispatch_group_wait(importPasswordGroup, DISPATCH_TIME_FOREVER);
|
||||
|
||||
return confirmation;
|
||||
return masterPassword;
|
||||
} askUserPassword:^NSString *(NSString *userName, NSUInteger importCount, NSUInteger deleteCount) {
|
||||
__block NSString *masterPassword = nil;
|
||||
|
||||
dispatch_group_t userPasswordGroup = dispatch_group_create();
|
||||
dispatch_group_enter(userPasswordGroup);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[PearlAlert showAlertWithTitle:PearlString(@"Master Password for\n%@", userName)
|
||||
message:PearlString(@"Imports %d sites, overwriting %d.", importCount,
|
||||
deleteCount)
|
||||
viewStyle:UIAlertViewStyleSecureTextInput
|
||||
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
|
||||
@try {
|
||||
if (buttonIndex_ == [alert_ cancelButtonIndex])
|
||||
return;
|
||||
|
||||
masterPassword = [alert_ textFieldAtIndex:0].text;
|
||||
}
|
||||
@finally {
|
||||
dispatch_group_leave(userPasswordGroup);
|
||||
}
|
||||
}
|
||||
cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Import", nil];
|
||||
});
|
||||
dispatch_group_wait(userPasswordGroup, DISPATCH_TIME_FOREVER);
|
||||
|
||||
return masterPassword;
|
||||
}];
|
||||
|
||||
switch (result) {
|
||||
@ -278,9 +309,9 @@
|
||||
[PearlAlert showError:@"Incorrect master password for the import sites."];
|
||||
break;
|
||||
}
|
||||
|
||||
[activityAlert dismissAlert];
|
||||
});
|
||||
}
|
||||
cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Unlock File", nil];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
@interface MPMainViewController : UIViewController<MPTypeDelegate, UITextFieldDelegate, MPSearchResultsDelegate, UIWebViewDelegate, MFMailComposeViewControllerDelegate, UIGestureRecognizerDelegate>
|
||||
|
||||
@property (nonatomic, assign) BOOL userNameHidden;
|
||||
@property (nonatomic, assign) BOOL siteInfoHidden;
|
||||
@property (strong, nonatomic) MPElementEntity *activeElement;
|
||||
@property (strong, nonatomic) IBOutlet MPSearchDelegate *searchDelegate;
|
||||
@property (weak, nonatomic) IBOutlet UITextField *contentField;
|
||||
@ -28,20 +28,20 @@
|
||||
@property (weak, nonatomic) IBOutlet UIView *displayContainer;
|
||||
@property (weak, nonatomic) IBOutlet UIView *helpContainer;
|
||||
@property (weak, nonatomic) IBOutlet UIView *contentTipContainer;
|
||||
@property (weak, nonatomic) IBOutlet UIView *userNameTipContainer;
|
||||
@property (weak, nonatomic) IBOutlet UIView *loginNameTipContainer;
|
||||
@property (weak, nonatomic) IBOutlet UIView *alertContainer;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *alertTitle;
|
||||
@property (weak, nonatomic) IBOutlet UITextView *alertBody;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *contentTipBody;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *userNameTipBody;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *loginNameTipBody;
|
||||
@property (weak, nonatomic) IBOutlet UIImageView *toolTipEditIcon;
|
||||
@property (weak, nonatomic) IBOutlet UIView *searchTipContainer;
|
||||
@property (weak, nonatomic) IBOutlet UIView *actionsTipContainer;
|
||||
@property (weak, nonatomic) IBOutlet UIView *typeTipContainer;
|
||||
@property (weak, nonatomic) IBOutlet UIView *toolTipContainer;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *toolTipBody;
|
||||
@property (weak, nonatomic) IBOutlet UIView *userNameContainer;
|
||||
@property (weak, nonatomic) IBOutlet UITextField *userNameField;
|
||||
@property (weak, nonatomic) IBOutlet UIView *loginNameContainer;
|
||||
@property (weak, nonatomic) IBOutlet UITextField *loginNameField;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *passwordUser;
|
||||
@property (weak, nonatomic) IBOutlet UIView *outdatedAlertContainer;
|
||||
@property (weak, nonatomic) IBOutlet UIImageView *outdatedAlertBack;
|
||||
@ -54,7 +54,7 @@
|
||||
- (IBAction)copyContent;
|
||||
- (IBAction)incrementPasswordCounter;
|
||||
- (IBAction)resetPasswordCounter:(UILongPressGestureRecognizer *)sender;
|
||||
- (IBAction)editUserName:(UILongPressGestureRecognizer *)sender;
|
||||
- (IBAction)editLoginName:(UILongPressGestureRecognizer *)sender;
|
||||
- (IBAction)editPassword;
|
||||
- (IBAction)closeAlert;
|
||||
- (IBAction)upgradePassword;
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
|
||||
@implementation MPMainViewController
|
||||
@synthesize userNameHidden = _userNameHidden;
|
||||
@synthesize siteInfoHidden = _siteInfoHidden;
|
||||
@synthesize activeElement = _activeElement;
|
||||
@synthesize searchDelegate = _searchDelegate;
|
||||
@synthesize typeButton = _typeButton;
|
||||
@ -28,20 +28,20 @@
|
||||
@synthesize displayContainer = _displayContainer;
|
||||
@synthesize helpContainer = _helpContainer;
|
||||
@synthesize contentTipContainer = _copiedContainer;
|
||||
@synthesize userNameTipContainer = _userNameTipContainer;
|
||||
@synthesize loginNameTipContainer = _loginNameTipContainer;
|
||||
@synthesize alertContainer = _alertContainer;
|
||||
@synthesize alertTitle = _alertTitle;
|
||||
@synthesize alertBody = _alertBody;
|
||||
@synthesize contentTipBody = _contentTipBody;
|
||||
@synthesize userNameTipBody = _userNameTipBody;
|
||||
@synthesize loginNameTipBody = _loginNameTipBody;
|
||||
@synthesize toolTipEditIcon = _contentTipEditIcon;
|
||||
@synthesize searchTipContainer = _searchTipContainer;
|
||||
@synthesize actionsTipContainer = _actionsTipContainer;
|
||||
@synthesize typeTipContainer = _typeTipContainer;
|
||||
@synthesize toolTipContainer = _toolTipContainer;
|
||||
@synthesize toolTipBody = _toolTipBody;
|
||||
@synthesize userNameContainer = _userNameContainer;
|
||||
@synthesize userNameField = _userNameField;
|
||||
@synthesize loginNameContainer = _loginNameContainer;
|
||||
@synthesize loginNameField = _loginNameField;
|
||||
@synthesize passwordUser = _passwordUser;
|
||||
@synthesize outdatedAlertContainer = _outdatedAlertContainer;
|
||||
@synthesize outdatedAlertBack = _outdatedAlertBack;
|
||||
@ -82,9 +82,9 @@
|
||||
|
||||
[self.passwordIncrementer addGestureRecognizer:[[UILongPressGestureRecognizer alloc] initWithTarget:self
|
||||
action:@selector(resetPasswordCounter:)]];
|
||||
[self.userNameContainer addGestureRecognizer:[[UILongPressGestureRecognizer alloc] initWithTarget:self
|
||||
action:@selector(editUserName:)]];
|
||||
[self.userNameContainer addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(copyUserName:)]];
|
||||
[self.loginNameContainer addGestureRecognizer:[[UILongPressGestureRecognizer alloc] initWithTarget:self
|
||||
action:@selector(editLoginName:)]];
|
||||
[self.loginNameContainer addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(copyLoginName:)]];
|
||||
[self.outdatedAlertBack addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self
|
||||
action:@selector(infoOutdatedAlert)]];
|
||||
|
||||
@ -225,10 +225,10 @@
|
||||
[self setToolTipContainer:nil];
|
||||
[self setToolTipBody:nil];
|
||||
[self setDisplayContainer:nil];
|
||||
[self setUserNameField:nil];
|
||||
[self setUserNameTipContainer:nil];
|
||||
[self setUserNameTipBody:nil];
|
||||
[self setUserNameContainer:nil];
|
||||
[self setLoginNameField:nil];
|
||||
[self setLoginNameTipContainer:nil];
|
||||
[self setLoginNameTipBody:nil];
|
||||
[self setLoginNameContainer:nil];
|
||||
[self setPasswordUser:nil];
|
||||
[self setOutdatedAlertContainer:nil];
|
||||
[self setOutdatedAlertCloseButton:nil];
|
||||
@ -289,9 +289,9 @@
|
||||
});
|
||||
});
|
||||
|
||||
self.userNameField.enabled = NO;
|
||||
self.userNameField.text = self.activeElement.userName;
|
||||
self.userNameHidden = !self.activeElement || ([[MPiOSConfig get].userNameHidden boolValue] && (self.activeElement.userName
|
||||
self.loginNameField.enabled = NO;
|
||||
self.loginNameField.text = self.activeElement.loginName;
|
||||
self.siteInfoHidden = !self.activeElement || ([[MPiOSConfig get].siteInfoHidden boolValue] && (self.activeElement.loginName
|
||||
== nil));
|
||||
[self updateUserHiddenAnimated:NO];
|
||||
}
|
||||
@ -333,8 +333,8 @@
|
||||
|
||||
- (void)toggleUserAnimated:(BOOL)animated {
|
||||
|
||||
[MPiOSConfig get].userNameHidden = PearlBool(!self.userNameHidden);
|
||||
self.userNameHidden = [[MPiOSConfig get].userNameHidden boolValue];
|
||||
[MPiOSConfig get].siteInfoHidden = PearlBool(!self.siteInfoHidden);
|
||||
self.siteInfoHidden = [[MPiOSConfig get].siteInfoHidden boolValue];
|
||||
[self updateUserHiddenAnimated:animated];
|
||||
}
|
||||
|
||||
@ -347,7 +347,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.userNameHidden) {
|
||||
if (self.siteInfoHidden) {
|
||||
self.displayContainer.frame = CGRectSetHeight(self.displayContainer.frame, 87);
|
||||
} else {
|
||||
self.displayContainer.frame = CGRectSetHeight(self.displayContainer.frame, 137);
|
||||
@ -402,19 +402,19 @@
|
||||
});
|
||||
}
|
||||
|
||||
- (void)showUserNameTip:(NSString *)message {
|
||||
- (void)showLoginNameTip:(NSString *)message {
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self.userNameTipBody.text = message;
|
||||
self.loginNameTipBody.text = message;
|
||||
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
self.userNameTipContainer.alpha = 1;
|
||||
self.loginNameTipContainer.alpha = 1;
|
||||
} completion:^(BOOL finished) {
|
||||
if (finished) {
|
||||
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC);
|
||||
dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {
|
||||
[UIView animateWithDuration:0.2f animations:^{
|
||||
self.userNameTipContainer.alpha = 0;
|
||||
self.loginNameTipContainer.alpha = 0;
|
||||
}];
|
||||
});
|
||||
}
|
||||
@ -488,18 +488,18 @@
|
||||
nil]];
|
||||
}
|
||||
|
||||
- (IBAction)copyUserName:(UITapGestureRecognizer *)sender {
|
||||
- (IBAction)copyLoginName:(UITapGestureRecognizer *)sender {
|
||||
|
||||
if (!self.activeElement.userName)
|
||||
if (!self.activeElement.loginName)
|
||||
return;
|
||||
|
||||
inf(@"Copying user name for: %@", self.activeElement.name);
|
||||
[UIPasteboard generalPasteboard].string = [self.activeElement.content description];
|
||||
|
||||
[self showUserNameTip:@"Copied!"];
|
||||
[self showLoginNameTip:@"Copied!"];
|
||||
|
||||
[TestFlight passCheckpoint:MPCheckpointCopyUserNameToPasteboard];
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointCopyUserNameToPasteboard
|
||||
[TestFlight passCheckpoint:MPCheckpointCopyLoginNameToPasteboard];
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointCopyLoginNameToPasteboard
|
||||
attributes:[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
self.activeElement.typeName, @"type",
|
||||
PearlUnsignedInteger(self.activeElement.version),
|
||||
@ -564,7 +564,7 @@
|
||||
}];
|
||||
}
|
||||
|
||||
- (IBAction)editUserName:(UILongPressGestureRecognizer *)sender {
|
||||
- (IBAction)editLoginName:(UILongPressGestureRecognizer *)sender {
|
||||
|
||||
if (sender.state != UIGestureRecognizerStateBegan)
|
||||
// Only fire when the gesture was first detected.
|
||||
@ -573,11 +573,11 @@
|
||||
if (!self.activeElement)
|
||||
return;
|
||||
|
||||
self.userNameField.enabled = YES;
|
||||
[self.userNameField becomeFirstResponder];
|
||||
self.loginNameField.enabled = YES;
|
||||
[self.loginNameField becomeFirstResponder];
|
||||
|
||||
[TestFlight passCheckpoint:MPCheckpointEditUserName];
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointEditUserName attributes:[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[TestFlight passCheckpoint:MPCheckpointEditLoginName];
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointEditLoginName attributes:[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
self.activeElement.typeName,
|
||||
@"type",
|
||||
PearlUnsignedInteger(self.activeElement.version),
|
||||
@ -903,8 +903,8 @@
|
||||
|
||||
if (textField == self.contentField)
|
||||
[self.contentField resignFirstResponder];
|
||||
if (textField == self.userNameField)
|
||||
[self.userNameField resignFirstResponder];
|
||||
if (textField == self.loginNameField)
|
||||
[self.loginNameField resignFirstResponder];
|
||||
|
||||
return YES;
|
||||
}
|
||||
@ -926,17 +926,17 @@
|
||||
}];
|
||||
}
|
||||
|
||||
if (textField == self.userNameField) {
|
||||
self.userNameField.enabled = NO;
|
||||
if (![[MPiOSConfig get].userNameTipShown boolValue]) {
|
||||
[self showUserNameTip:@"Tap to copy or hold to edit."];
|
||||
[MPiOSConfig get].userNameTipShown = PearlBool(YES);
|
||||
if (textField == self.loginNameField) {
|
||||
self.loginNameField.enabled = NO;
|
||||
if (![[MPiOSConfig get].loginNameTipShown boolValue]) {
|
||||
[self showLoginNameTip:@"Tap to copy or hold to edit."];
|
||||
[MPiOSConfig get].loginNameTipShown = PearlBool(YES);
|
||||
}
|
||||
|
||||
if ([self.userNameField.text length])
|
||||
self.activeElement.userName = self.userNameField.text;
|
||||
if ([self.loginNameField.text length])
|
||||
self.activeElement.loginName = self.loginNameField.text;
|
||||
else
|
||||
self.activeElement.userName = nil;
|
||||
self.activeElement.loginName = nil;
|
||||
|
||||
[[MPAppDelegate get] saveContext];
|
||||
}
|
||||
|
@ -218,9 +218,15 @@
|
||||
self.tip.text = @"Tap and hold to delete or reset.";
|
||||
[self.loadingUsersIndicator stopAnimating];
|
||||
|
||||
__block NSArray *users = nil;
|
||||
[moc performBlockAndWait:^{
|
||||
NSError *error = nil;
|
||||
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([MPUserEntity class])];
|
||||
fetchRequest.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO]];
|
||||
NSArray *users = [moc executeFetchRequest:fetchRequest error:nil];
|
||||
users = [moc executeFetchRequest:fetchRequest error:&error];
|
||||
if (!users)
|
||||
err(@"Failed to load users: %@", error);
|
||||
}];
|
||||
|
||||
// Clean up avatars.
|
||||
for (UIView *subview in [self.avatarsView subviews])
|
||||
@ -275,8 +281,10 @@
|
||||
|
||||
[self.avatarToUser setObject:NilToNSNull(user) forKey:[NSValue valueWithNonretainedObject:avatar]];
|
||||
|
||||
if (self.selectedUser && user == self.selectedUser)
|
||||
if ([self.selectedUser.objectID isEqual:user.objectID]) {
|
||||
self.selectedUser = user;
|
||||
avatar.selected = YES;
|
||||
}
|
||||
|
||||
return avatar;
|
||||
}
|
||||
|
@ -12,11 +12,11 @@
|
||||
|
||||
@property (nonatomic, retain) NSNumber *sendInfo;
|
||||
@property (nonatomic, retain) NSNumber *helpHidden;
|
||||
@property (nonatomic, retain) NSNumber *userNameHidden;
|
||||
@property (nonatomic, retain) NSNumber *siteInfoHidden;
|
||||
@property (nonatomic, retain) NSNumber *showQuickStart;
|
||||
@property (nonatomic, retain) NSNumber *actionsTipShown;
|
||||
@property (nonatomic, retain) NSNumber *typeTipShown;
|
||||
@property (nonatomic, retain) NSNumber *userNameTipShown;
|
||||
@property (nonatomic, retain) NSNumber *loginNameTipShown;
|
||||
|
||||
+ (MPiOSConfig *)get;
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
//
|
||||
|
||||
@implementation MPiOSConfig
|
||||
@dynamic sendInfo, helpHidden, userNameHidden, showQuickStart, actionsTipShown, typeTipShown, userNameTipShown;
|
||||
@dynamic sendInfo, helpHidden, siteInfoHidden, showQuickStart, actionsTipShown, typeTipShown, loginNameTipShown;
|
||||
|
||||
- (id)init {
|
||||
|
||||
@ -17,12 +17,12 @@
|
||||
[self.defaults registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSNumber numberWithBool:NO], NSStringFromSelector(@selector(sendInfo)),
|
||||
[NSNumber numberWithBool:NO], NSStringFromSelector(@selector(helpHidden)),
|
||||
[NSNumber numberWithBool:YES], NSStringFromSelector(@selector(userNameHidden)),
|
||||
[NSNumber numberWithBool:YES], NSStringFromSelector(@selector(siteInfoHidden)),
|
||||
[NSNumber numberWithBool:YES], NSStringFromSelector(@selector(showQuickStart)),
|
||||
@"510296984", NSStringFromSelector(@selector(iTunesID)),
|
||||
PearlBoolNot(self.firstRun), NSStringFromSelector(@selector(actionsTipShown)),
|
||||
PearlBoolNot(self.firstRun), NSStringFromSelector(@selector(typeTipShown)),
|
||||
PearlBool(NO), NSStringFromSelector(@selector(userNameTipShown)),
|
||||
PearlBool(NO), NSStringFromSelector(@selector(loginNameTipShown)),
|
||||
nil]];
|
||||
|
||||
return self;
|
||||
|
@ -811,12 +811,12 @@ L4m3P4sSw0rD</string>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" userInteractionEnabled="NO" contentMode="scaleToFill" editable="NO" id="HPZ-qz-fpL">
|
||||
<rect key="frame" x="20" y="60" width="260" height="130"/>
|
||||
<rect key="frame" x="20" y="52" width="260" height="108"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<string key="text">Some of your sites have outdated passwords. Tap this alert for more info or to find them.
|
||||
<string key="text">Some of your sites use 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 is convenient.</string>
|
||||
These sites should be upgraded and their account's passwords updated as soon as convenient.</string>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
@ -854,7 +854,7 @@ You should upgrade these sites and update their account's passwords as soon as i
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="Sfy-hx-kVU">
|
||||
<rect key="frame" x="184" y="81" width="22" height="22"/>
|
||||
<rect key="frame" x="183" y="73" width="22" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" hint="" label="Close"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
|
||||
@ -911,10 +911,10 @@ You should upgrade these sites and update their account's passwords as soon as i
|
||||
<outlet property="toolTipEditIcon" destination="KEn-n3-qhX" id="zEV-tR-Egq"/>
|
||||
<outlet property="typeButton" destination="xYM-e3-BMg" id="xvn-nR-MRV"/>
|
||||
<outlet property="typeTipContainer" destination="g55-0m-WjS" id="KZ9-KV-NMh"/>
|
||||
<outlet property="userNameContainer" destination="lrZ-PV-rgu" id="tTh-fp-qaN"/>
|
||||
<outlet property="userNameField" destination="tj6-Ot-Fuh" id="oFa-xd-UEm"/>
|
||||
<outlet property="userNameTipBody" destination="coX-1P-dqm" id="MRt-A5-ZMH"/>
|
||||
<outlet property="userNameTipContainer" destination="b9W-aA-93r" id="heZ-Gu-obO"/>
|
||||
<outlet property="loginNameContainer" destination="lrZ-PV-rgu" id="tTh-fp-qaN"/>
|
||||
<outlet property="loginNameField" destination="tj6-Ot-Fuh" id="oFa-xd-UEm"/>
|
||||
<outlet property="loginNameTipBody" destination="coX-1P-dqm" id="MRt-A5-ZMH"/>
|
||||
<outlet property="loginNameTipContainer" destination="b9W-aA-93r" id="heZ-Gu-obO"/>
|
||||
<segue destination="oLN-6u-GLb" kind="push" identifier="UserProfile" id="tKN-Sw-S5J"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
@ -1890,10 +1890,10 @@ You could use the word wall for inspiration in finding a memorable master passw
|
||||
<relationship kind="outlet" name="toolTipEditIcon" candidateClass="UIImageView"/>
|
||||
<relationship kind="outlet" name="typeButton" candidateClass="UIButton"/>
|
||||
<relationship kind="outlet" name="typeTipContainer" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="userNameContainer" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="userNameField" candidateClass="UITextField"/>
|
||||
<relationship kind="outlet" name="userNameTipBody" candidateClass="UILabel"/>
|
||||
<relationship kind="outlet" name="userNameTipContainer" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="loginNameContainer" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="loginNameField" candidateClass="UITextField"/>
|
||||
<relationship kind="outlet" name="loginNameTipBody" candidateClass="UILabel"/>
|
||||
<relationship kind="outlet" name="loginNameTipContainer" candidateClass="UIView"/>
|
||||
</relationships>
|
||||
</class>
|
||||
<class className="MPPreferencesViewController" superclassName="UITableViewController">
|
||||
@ -1951,6 +1951,6 @@ You could use the word wall for inspiration in finding a memorable master passw
|
||||
<simulatedScreenMetrics key="destination"/>
|
||||
</simulatedMetricsContainer>
|
||||
<inferredMetricsTieBreakers>
|
||||
<segue reference="KIl-ZW-M7G"/>
|
||||
<segue reference="9Bs-cD-ddF"/>
|
||||
</inferredMetricsTieBreakers>
|
||||
</document>
|
Loading…
Reference in New Issue
Block a user