2
0

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:
Maarten Billemont 2012-08-19 09:34:49 +02:00
parent 479d357bd1
commit 75ef15bb4d
29 changed files with 343 additions and 221 deletions

2
External/Pearl vendored

@ -1 +1 @@
Subproject commit cc97c1be4c0f2ee0080af417940244ac8f3e5f47 Subproject commit 5a35cdb73c57c65f22959accab2f6ddbeeee0b33

View File

@ -17,10 +17,6 @@
DA0A1D1515690AF40092735D /* Icon-72@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA0A1D1315690AF30092735D /* Icon-72@2x.png */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; DA30E9CF15722ECA00A68B4C /* NSBundle+PearlMutableInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = DA30E9CC15722ECA00A68B4C /* NSBundle+PearlMutableInfo.m */; };
DA30E9D015722ECA00A68B4C /* Pearl.m in Sources */ = {isa = PBXBuildFile; fileRef = DA30E9CD15722ECA00A68B4C /* Pearl.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 */; }; DA95D5F614DF0B9F008D1B94 /* IASKPSTextFieldSpecifierViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA95D5CC14DF0691008D1B94 /* IASKPSTextFieldSpecifierViewCell.xib */; };
DA95D5F714DF0B9F008D1B94 /* IASKPSToggleSwitchSpecifierViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA95D5CD14DF0691008D1B94 /* IASKPSToggleSwitchSpecifierViewCell.xib */; }; DA95D5F714DF0B9F008D1B94 /* IASKPSToggleSwitchSpecifierViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA95D5CD14DF0691008D1B94 /* IASKPSToggleSwitchSpecifierViewCell.xib */; };
DA95D5F814DF0B9F008D1B94 /* IASKSpecifierValuesView.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA95D5CE14DF0691008D1B94 /* IASKSpecifierValuesView.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 */; }; DAB8D45D15036BCF00CED3BC /* MasterPassword.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DAB8D43C15036BCF00CED3BC /* MasterPassword.xcdatamodeld */; };
DAB8D45E15036BCF00CED3BC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DAB8D43F15036BCF00CED3BC /* InfoPlist.strings */; }; DAB8D45E15036BCF00CED3BC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DAB8D43F15036BCF00CED3BC /* InfoPlist.strings */; };
DAB8D45F15036BCF00CED3BC /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB8D44115036BCF00CED3BC /* main.m */; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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; }; 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; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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; }; 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>"; }; 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>"; }; 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>"; }; 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 */, DA0E07941577FE490008A67E /* MPEntities.h */,
DA0E07951577FE490008A67E /* MPEntities.m */, DA0E07951577FE490008A67E /* MPEntities.m */,
DAB8D43C15036BCF00CED3BC /* MasterPassword.xcdatamodeld */, DAB8D43C15036BCF00CED3BC /* MasterPassword.xcdatamodeld */,
DA0F9F3115B55397007ED9BC /* MPUserEntity.h */, DAA096FA15E0C59B00912D63 /* MPElementEntity.h */,
DA0F9F3215B55397007ED9BC /* MPUserEntity.m */, DAA096FB15E0C59B00912D63 /* MPElementEntity.m */,
DA0F9F3415B55397007ED9BC /* MPElementGeneratedEntity.h */, DAA096FD15E0C59B00912D63 /* MPElementGeneratedEntity.h */,
DA0F9F3515B55397007ED9BC /* MPElementGeneratedEntity.m */, DAA096FE15E0C59B00912D63 /* MPElementGeneratedEntity.m */,
DA0F9F3715B55397007ED9BC /* MPElementStoredEntity.h */, DAA0970015E0C59B00912D63 /* MPElementStoredEntity.h */,
DA0F9F3A15B55397007ED9BC /* MPElementEntity.h */, DAA0970115E0C59B00912D63 /* MPElementStoredEntity.m */,
DA0F9F3B15B55397007ED9BC /* MPElementEntity.m */, DAA0970315E0C59B00912D63 /* MPUserEntity.h */,
DA0F9F3815B55397007ED9BC /* MPElementStoredEntity.m */, DAA0970415E0C59B00912D63 /* MPUserEntity.m */,
DA600C2415054F3A008E9AB6 /* MPAppDelegate_Key.h */, DA600C2415054F3A008E9AB6 /* MPAppDelegate_Key.h */,
DA600C2315054F3A008E9AB6 /* MPAppDelegate_Key.m */, DA600C2315054F3A008E9AB6 /* MPAppDelegate_Key.m */,
DA4426041557C1990052177D /* MPAppDelegate_Shared.h */, DA4426041557C1990052177D /* MPAppDelegate_Shared.h */,
@ -4269,10 +4270,10 @@
93D390BC6AE7A1C9B91A3668 /* MPKey.m in Sources */, 93D390BC6AE7A1C9B91A3668 /* MPKey.m in Sources */,
93D394744B5485303B326ECB /* MPAlgorithm.m in Sources */, 93D394744B5485303B326ECB /* MPAlgorithm.m in Sources */,
93D39DC7A7282137B08C8D82 /* MPAlgorithmV1.m in Sources */, 93D39DC7A7282137B08C8D82 /* MPAlgorithmV1.m in Sources */,
DA0F9F3315B55397007ED9BC /* MPUserEntity.m in Sources */, DAA096FC15E0C59B00912D63 /* MPElementEntity.m in Sources */,
DA0F9F3615B55397007ED9BC /* MPElementGeneratedEntity.m in Sources */, DAA096FF15E0C59B00912D63 /* MPElementGeneratedEntity.m in Sources */,
DA0F9F3915B55397007ED9BC /* MPElementStoredEntity.m in Sources */, DAA0970215E0C59B00912D63 /* MPElementStoredEntity.m in Sources */,
DA0F9F3C15B55397007ED9BC /* MPElementEntity.m in Sources */, DAA0970515E0C59B00912D63 /* MPUserEntity.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -5092,10 +5093,11 @@
DAB8D43C15036BCF00CED3BC /* MasterPassword.xcdatamodeld */ = { DAB8D43C15036BCF00CED3BC /* MasterPassword.xcdatamodeld */ = {
isa = XCVersionGroup; isa = XCVersionGroup;
children = ( children = (
DAF83D6B15E02E04009C8D49 /* MasterPassword 3.xcdatamodel */,
DA46826C15AB48F100FB09E7 /* MasterPassword 2.xcdatamodel */, DA46826C15AB48F100FB09E7 /* MasterPassword 2.xcdatamodel */,
DAB8D43D15036BCF00CED3BC /* MasterPassword 1.xcdatamodel */, DAB8D43D15036BCF00CED3BC /* MasterPassword 1.xcdatamodel */,
); );
currentVersion = DA46826C15AB48F100FB09E7 /* MasterPassword 2.xcdatamodel */; currentVersion = DAF83D6B15E02E04009C8D49 /* MasterPassword 3.xcdatamodel */;
path = MasterPassword.xcdatamodeld; path = MasterPassword.xcdatamodeld;
sourceTree = "<group>"; sourceTree = "<group>";
versionGroupType = wrapper.xcdatamodel; versionGroupType = wrapper.xcdatamodel;

View File

@ -50,13 +50,14 @@
</MacroExpansion> </MacroExpansion>
</TestAction> </TestAction>
<LaunchAction <LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
launchStyle = "0" launchStyle = "0"
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug" buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO" ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES" debugDocumentVersioning = "YES"
enableOpenGLFrameCaptureMode = "0"
allowLocationSimulation = "YES"> allowLocationSimulation = "YES">
<BuildableProductRunnable> <BuildableProductRunnable>
<BuildableReference <BuildableReference

View File

@ -47,6 +47,9 @@
- (MPUserEntity *)activeUser { - (MPUserEntity *)activeUser {
if (!self.activeUserID)
return nil;
return (MPUserEntity *)[self.managedObjectContextIfReady objectWithID:self.activeUserID]; return (MPUserEntity *)[self.managedObjectContextIfReady objectWithID:self.activeUserID];
} }

View File

@ -28,8 +28,9 @@ typedef enum {
- (UbiquityStoreManager *)storeManager; - (UbiquityStoreManager *)storeManager;
- (void)saveContext; - (void)saveContext;
- (MPImportResult)importSites:(NSString *)importedSitesString withPassword:(NSString *)password - (MPImportResult)importSites:(NSString *)importedSitesString
askConfirmation:(BOOL(^)(NSUInteger importCount, NSUInteger deleteCount))confirmation; askImportPassword:(NSString *(^)(NSString *userName))importPassword
askUserPassword:(NSString *(^)(NSString *userName, NSUInteger importCount, NSUInteger deleteCount))userPassword;
- (NSString *)exportSitesShowingPasswords:(BOOL)showPasswords; - (NSString *)exportSitesShowingPasswords:(BOOL)showPasswords;
@end @end

View File

@ -183,8 +183,12 @@
#pragma mark - Import / Export #pragma mark - Import / Export
- (MPImportResult)importSites:(NSString *)importedSitesString withPassword:(NSString *)password - (MPImportResult)importSites:(NSString *)importedSitesString
askConfirmation:(BOOL(^)(NSUInteger importCount, NSUInteger deleteCount))confirmation { 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."); inf(@"Importing sites.");
@ -205,14 +209,15 @@
if (!headerPattern || !sitePattern) if (!headerPattern || !sitePattern)
return MPImportResultInternalError; return MPImportResultInternalError;
MPKey *key = nil;
__block MPUserEntity *user = 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; BOOL headerStarted = NO, headerEnded = NO, clearText = NO;
NSArray *importedSiteLines = [importedSitesString componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]; NSArray *importedSiteLines = [importedSitesString componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
NSMutableSet *elementsToDelete = [NSMutableSet set]; NSMutableSet *elementsToDelete = [NSMutableSet set];
NSMutableArray *importedSiteElements = [NSMutableArray arrayWithCapacity:[importedSiteLines count]]; 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) { for (NSString *importedSiteLine in importedSiteLines) {
if ([importedSiteLine hasPrefix:@"#"]) { if ([importedSiteLine hasPrefix:@"#"]) {
// Comment or header // Comment or header
@ -238,20 +243,20 @@
NSString *headerName = [importedSiteLine substringWithRange:[headerElements rangeAtIndex:1]]; NSString *headerName = [importedSiteLine substringWithRange:[headerElements rangeAtIndex:1]];
NSString *headerValue = [importedSiteLine substringWithRange:[headerElements rangeAtIndex:2]]; NSString *headerValue = [importedSiteLine substringWithRange:[headerElements rangeAtIndex:2]];
if ([headerName isEqualToString:@"User Name"]) { if ([headerName isEqualToString:@"User Name"]) {
userName = headerValue; importUserName = headerValue;
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 == %@", importUserName];
__block NSArray *users = nil; __block NSArray *users = nil;
[self.managedObjectContextIfReady performBlockAndWait:^{ [self.managedObjectContextIfReady performBlockAndWait:^{
users = [self.managedObjectContextIfReady executeFetchRequest:userFetchRequest error:&error]; users = [self.managedObjectContextIfReady executeFetchRequest:userFetchRequest error:&error];
}]; }];
if (!users) { if (!users) {
err(@"While looking for user: %@, error: %@", userName, error); err(@"While looking for user: %@, error: %@", importUserName, error);
return MPImportResultInternalError; return MPImportResultInternalError;
} }
if ([users count] > 1) { 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; return MPImportResultInternalError;
} }
@ -259,9 +264,11 @@
dbg(@"Found user: %@", [user debugDescription]); dbg(@"Found user: %@", [user debugDescription]);
} }
if ([headerName isEqualToString:@"Key ID"]) if ([headerName isEqualToString:@"Key ID"])
keyIDHex = headerValue; importKeyID = [headerValue decodeHex];
if ([headerName isEqualToString:@"Version"]) if ([headerName isEqualToString:@"Version"]) {
bundleVersion = headerValue; importBundleVersion = headerValue;
importAlgorithm = MPAlgorithmDefaultForBundleVersion(importBundleVersion);
}
if ([headerName isEqualToString:@"Passwords"]) { if ([headerName isEqualToString:@"Passwords"]) {
if ([headerValue isEqualToString:@"VISIBLE"]) if ([headerValue isEqualToString:@"VISIBLE"])
clearText = YES; clearText = YES;
@ -271,13 +278,8 @@
} }
if (!headerEnded) if (!headerEnded)
continue; continue;
if (!keyIDHex || ![userName length]) if (!importKeyID || ![importUserName length])
return MPImportResultMalformedInput; return MPImportResultMalformedInput;
if (!key) {
key = [MPAlgorithmDefaultForBundleVersion(bundleVersion) keyForPassword:password ofUserNamed:userName];
if (![keyIDHex isEqualToString:[key.keyID encodeHex]])
return MPImportResultInvalidPassword;
}
if (![importedSiteLine length]) if (![importedSiteLine length])
continue; continue;
@ -297,10 +299,10 @@
// Find existing site. // Find existing site.
if (user) { if (user) {
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND user == %@", name, user]; elementFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND user == %@", name, user];
__block NSArray *existingSites = nil; __block NSArray *existingSites = nil;
[self.managedObjectContextIfReady performBlockAndWait:^{ [self.managedObjectContextIfReady performBlockAndWait:^{
existingSites = [self.managedObjectContextIfReady executeFetchRequest:fetchRequest error:&error]; existingSites = [self.managedObjectContextIfReady executeFetchRequest:elementFetchRequest error:&error];
}]; }];
if (!existingSites) { if (!existingSites) {
err(@"Lookup of existing sites failed for site: %@, user: %@, error: %@", name, user.userID, error); 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: %@", // Ask for confirmation to import these sites and the master password of the user.
[importedSiteElements count], [elementsToDelete count], [MPUserEntity idFor:userName]); 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]);
// Ask for confirmation to import these sites. if (!userMasterPassword) {
if (!confirmation([importedSiteElements count], [elementsToDelete count])) {
inf(@"Import cancelled."); inf(@"Import cancelled.");
return MPImportResultCancelled; 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; BOOL success = NO;
[self.managedObjectContextIfReady.undoManager beginUndoGrouping]; [self.managedObjectContextIfReady.undoManager beginUndoGrouping];
@ -337,16 +344,19 @@
}]; }];
}]; }];
// Import new sites. // Make sure there is a user.
if (!user) { if (!user) {
[self.managedObjectContextIfReady performBlockAndWait:^{ [self.managedObjectContextIfReady performBlockAndWait:^{
user = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([MPUserEntity class]) user = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([MPUserEntity class])
inManagedObjectContext:self.managedObjectContextIfReady]; inManagedObjectContext:self.managedObjectContextIfReady];
user.name = userName; user.name = importUserName;
user.keyID = [keyIDHex decodeHex]; user.keyID = importKeyID;
}]; }];
dbg(@"Created User: %@", [user debugDescription]); dbg(@"Created User: %@", [user debugDescription]);
} }
[self saveContext];
// Import new sites.
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]];
NSUInteger uses = (unsigned)[[siteElements objectAtIndex:1] integerValue]; NSUInteger uses = (unsigned)[[siteElements objectAtIndex:1] integerValue];
@ -356,8 +366,9 @@
NSString *exportContent = [siteElements objectAtIndex:5]; NSString *exportContent = [siteElements objectAtIndex:5];
// Create new site. // Create new site.
__block MPImportResult result = MPImportResultSuccess;
[self.managedObjectContextIfReady performBlockAndWait:^{ [self.managedObjectContextIfReady performBlockAndWait:^{
MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:[key.algorithm classNameOfType:type] MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:[MPAlgorithmForVersion(version) classNameOfType:type]
inManagedObjectContext:self.managedObjectContextIfReady]; inManagedObjectContext:self.managedObjectContextIfReady];
element.name = name; element.name = name;
element.user = user; element.user = user;
@ -367,12 +378,23 @@
element.version = version; element.version = version;
if ([exportContent length]) { if ([exportContent length]) {
if (clearText) if (clearText)
[element importClearTextContent:exportContent usingKey:key]; [element importClearTextContent:exportContent usingKey:userKey];
else else {
[element importProtectedContent:exportContent]; 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]); dbg(@"Created Element: %@", [element debugDescription]);
}]; }];
if (result != MPImportResultSuccess)
return result;
} }
[self saveContext]; [self saveContext];

View File

@ -2,7 +2,7 @@
// MPElementEntity.h // MPElementEntity.h
// MasterPassword-iOS // 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. // Copyright (c) 2012 Lyndir. All rights reserved.
// //
@ -15,10 +15,10 @@
@property (nonatomic, retain) id content; @property (nonatomic, retain) id content;
@property (nonatomic, retain) NSDate * lastUsed; @property (nonatomic, retain) NSDate * lastUsed;
@property (nonatomic, retain) NSString * loginName;
@property (nonatomic, retain) NSString * name; @property (nonatomic, retain) NSString * name;
@property (nonatomic, retain) NSNumber * requiresExplicitMigration_; @property (nonatomic, retain) NSNumber * requiresExplicitMigration_;
@property (nonatomic, retain) NSNumber * type_; @property (nonatomic, retain) NSNumber * type_;
@property (nonatomic, retain) NSString * userName;
@property (nonatomic, retain) NSNumber * uses_; @property (nonatomic, retain) NSNumber * uses_;
@property (nonatomic, retain) NSNumber * version_; @property (nonatomic, retain) NSNumber * version_;
@property (nonatomic, retain) MPUserEntity *user; @property (nonatomic, retain) MPUserEntity *user;

View File

@ -2,7 +2,7 @@
// MPElementEntity.m // MPElementEntity.m
// MasterPassword-iOS // 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. // Copyright (c) 2012 Lyndir. All rights reserved.
// //
@ -14,10 +14,10 @@
@dynamic content; @dynamic content;
@dynamic lastUsed; @dynamic lastUsed;
@dynamic loginName;
@dynamic name; @dynamic name;
@dynamic requiresExplicitMigration_; @dynamic requiresExplicitMigration_;
@dynamic type_; @dynamic type_;
@dynamic userName;
@dynamic uses_; @dynamic uses_;
@dynamic version_; @dynamic version_;
@dynamic user; @dynamic user;

View File

@ -2,7 +2,7 @@
// MPElementGeneratedEntity.h // MPElementGeneratedEntity.h
// MasterPassword-iOS // 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. // Copyright (c) 2012 Lyndir. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// MPElementGeneratedEntity.m // MPElementGeneratedEntity.m
// MasterPassword-iOS // 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. // Copyright (c) 2012 Lyndir. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// MPElementStoredEntity.h // MPElementStoredEntity.h
// MasterPassword-iOS // 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. // Copyright (c) 2012 Lyndir. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// MPElementStoredEntity.m // MPElementStoredEntity.m
// MasterPassword-iOS // 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. // Copyright (c) 2012 Lyndir. All rights reserved.
// //

View File

@ -29,7 +29,7 @@
- (NSUInteger)use; - (NSUInteger)use;
- (NSString *)exportContent; - (NSString *)exportContent;
- (void)importProtectedContent:(NSString *)protectedContent; - (void)importProtectedContent:(NSString *)protectedContent protectedByKey:(MPKey *)contentProtectionKey usingKey:(MPKey *)key2;
- (void)importClearTextContent:(NSString *)clearContent usingKey:(MPKey *)key; - (void)importClearTextContent:(NSString *)clearContent usingKey:(MPKey *)key;
- (BOOL)migrateExplicitly:(BOOL)explicit; - (BOOL)migrateExplicitly:(BOOL)explicit;

View File

@ -92,7 +92,7 @@
return nil; return nil;
} }
- (void)importProtectedContent:(NSString *)content { - (void)importProtectedContent:(NSString *)protectedContent protectedByKey:(MPKey *)contentProtectionKey usingKey:(MPKey *)key {
} }
@ -107,9 +107,9 @@
- (NSString *)debugDescription { - (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, 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 { - (BOOL)migrateExplicitly:(BOOL)explicit {
@ -185,7 +185,6 @@
if (!key) if (!key)
return; return;
assert([key.keyID isEqualToData:self.user.keyID]);
[self setContent:content usingKey:key]; [self setContent:content usingKey:key];
} }
@ -202,17 +201,23 @@
NSData *decryptedContent = nil; NSData *decryptedContent = nil;
if ([encryptedContent length]) 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]; 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 { - (void)setContent:(id)content usingKey:(MPKey *)key {
assert(self.type & MPElementTypeClassStored); 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) { if (self.type & MPElementFeatureDevicePrivate) {
[PearlKeyChain addOrUpdateItemForQuery:[MPElementStoredEntity queryForDevicePrivateElementNamed:self.name] [PearlKeyChain addOrUpdateItemForQuery:[MPElementStoredEntity queryForDevicePrivateElementNamed:self.name]
@ -233,9 +238,18 @@
return [self.contentObject encodeBase64]; 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]; 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 { - (void)importClearTextContent:(NSString *)clearContent usingKey:(MPKey *)key {

View File

@ -43,11 +43,11 @@ typedef enum {
#define MPCheckpointAction @"MPCheckpointAction" #define MPCheckpointAction @"MPCheckpointAction"
#define MPCheckpointHelpChapter @"MPCheckpointHelpChapter" #define MPCheckpointHelpChapter @"MPCheckpointHelpChapter"
#define MPCheckpointCopyToPasteboard @"MPCheckpointCopyToPasteboard" #define MPCheckpointCopyToPasteboard @"MPCheckpointCopyToPasteboard"
#define MPCheckpointCopyUserNameToPasteboard @"MPCheckpointCopyUserNameToPasteboard" #define MPCheckpointCopyLoginNameToPasteboard @"MPCheckpointCopyLoginNameToPasteboard"
#define MPCheckpointResetPasswordCounter @"MPCheckpointResetPasswordCounter" #define MPCheckpointResetPasswordCounter @"MPCheckpointResetPasswordCounter"
#define MPCheckpointIncrementPasswordCounter @"MPCheckpointIncrementPasswordCounter" #define MPCheckpointIncrementPasswordCounter @"MPCheckpointIncrementPasswordCounter"
#define MPCheckpointEditPassword @"MPCheckpointEditPassword" #define MPCheckpointEditPassword @"MPCheckpointEditPassword"
#define MPCheckpointEditUserName @"MPCheckpointEditUserName" #define MPCheckpointEditLoginName @"MPCheckpointEditLoginName"
#define MPCheckpointCloseAlert @"MPCheckpointCloseAlert" #define MPCheckpointCloseAlert @"MPCheckpointCloseAlert"
#define MPCheckpointCloseOutdatedAlert @"MPCheckpointCloseOutdatedAlert" #define MPCheckpointCloseOutdatedAlert @"MPCheckpointCloseOutdatedAlert"
#define MPCheckpointUseType @"MPCheckpointUseType" #define MPCheckpointUseType @"MPCheckpointUseType"

View File

@ -2,7 +2,7 @@
// MPUserEntity.h // MPUserEntity.h
// MasterPassword-iOS // 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. // Copyright (c) 2012 Lyndir. All rights reserved.
// //
@ -18,8 +18,8 @@
@property (nonatomic, retain) NSData * keyID; @property (nonatomic, retain) NSData * keyID;
@property (nonatomic, retain) NSDate * lastUsed; @property (nonatomic, retain) NSDate * lastUsed;
@property (nonatomic, retain) NSString * name; @property (nonatomic, retain) NSString * name;
@property (nonatomic, retain) NSNumber * saveKey_;
@property (nonatomic, retain) NSNumber * requiresExplicitMigration_; @property (nonatomic, retain) NSNumber * requiresExplicitMigration_;
@property (nonatomic, retain) NSNumber * saveKey_;
@property (nonatomic, retain) NSSet *elements; @property (nonatomic, retain) NSSet *elements;
@end @end

View File

@ -2,7 +2,7 @@
// MPUserEntity.m // MPUserEntity.m
// MasterPassword-iOS // 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. // Copyright (c) 2012 Lyndir. All rights reserved.
// //
@ -17,8 +17,8 @@
@dynamic keyID; @dynamic keyID;
@dynamic lastUsed; @dynamic lastUsed;
@dynamic name; @dynamic name;
@dynamic saveKey_;
@dynamic requiresExplicitMigration_; @dynamic requiresExplicitMigration_;
@dynamic saveKey_;
@dynamic elements; @dynamic elements;
@end @end

View File

@ -3,6 +3,6 @@
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>_XCCurrentVersionName</key> <key>_XCCurrentVersionName</key>
<string>MasterPassword 2.xcdatamodel</string> <string>MasterPassword 3.xcdatamodel</string>
</dict> </dict>
</plist> </plist>

View File

@ -32,9 +32,9 @@
<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>
<element name="MPElementEntity" positionX="160" positionY="192" width="128" height="180"/> <element name="MPElementEntity" positionX="0" positionY="0" width="0" height="0"/>
<element name="MPElementGeneratedEntity" positionX="160" positionY="192" width="128" height="60"/> <element name="MPElementGeneratedEntity" positionX="0" positionY="0" width="0" height="0"/>
<element name="MPElementStoredEntity" positionX="160" positionY="192" width="128" height="60"/> <element name="MPElementStoredEntity" positionX="0" positionY="0" width="0" height="0"/>
<element name="MPUserEntity" positionX="160" positionY="192" width="128" height="150"/> <element name="MPUserEntity" positionX="0" positionY="0" width="0" height="0"/>
</elements> </elements>
</model> </model>

View File

@ -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>

View File

@ -220,48 +220,79 @@
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
__autoreleasing NSError *error; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
__autoreleasing NSURLResponse *response; 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] NSData *importedSitesData = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url]
returningResponse:&response error:&error]; returningResponse:&response error:&error];
if (error) if (error)
err(@"While reading imported sites from %@: %@", url, error); err(@"While reading imported sites from %@: %@", url, error);
if (!importedSitesData) if (!importedSitesData)
return NO; return;
NSString *importedSitesString = [[NSString alloc] initWithData:importedSitesData encoding:NSUTF8StringEncoding]; NSString *importedSitesString = [[NSString alloc] initWithData:importedSitesData encoding:NSUTF8StringEncoding];
[PearlAlert showAlertWithTitle:@"Import Password" message: MPImportResult result = [self importSites:importedSitesString askImportPassword:^NSString *(NSString *userName) {
@"Enter the master password for this export:" __block NSString *masterPassword = nil;
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;
dispatch_group_t confirmationGroup = dispatch_group_create(); dispatch_group_t importPasswordGroup = dispatch_group_create();
dispatch_group_enter(confirmationGroup); dispatch_group_enter(importPasswordGroup);
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[PearlAlert showAlertWithTitle:@"Import Sites?" [PearlAlert showAlertWithTitle:@"Import File's Master Password"
message:PearlString( message:PearlString(@"%@'s export was done using a different master password.\n"
@"Import %d sites, overwriting %d existing sites?", @"Enter that master password to unlock the exported data.", userName)
importCount, deleteCount) viewStyle:UIAlertViewStyleSecureTextInput
viewStyle:UIAlertViewStyleDefault initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
initAlert:nil @try {
tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) { if (buttonIndex_ == [alert_ cancelButtonIndex])
if (buttonIndex_ return;
!= [alert_ cancelButtonIndex])
confirmation = YES;
dispatch_group_leave(confirmationGroup); masterPassword = [alert_ textFieldAtIndex:0].text;
} }
cancelTitle:[PearlStrings get].commonButtonCancel @finally {
otherTitles:@"Import", nil]; dispatch_group_leave(importPasswordGroup);
}
}
cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Unlock Import", nil];
}); });
dispatch_group_wait( dispatch_group_wait(importPasswordGroup, DISPATCH_TIME_FOREVER);
confirmationGroup, 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) { switch (result) {
@ -278,9 +309,9 @@
[PearlAlert showError:@"Incorrect master password for the import sites."]; [PearlAlert showError:@"Incorrect master password for the import sites."];
break; break;
} }
[activityAlert dismissAlert];
}); });
}
cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Unlock File", nil];
return YES; return YES;
} }

View File

@ -13,7 +13,7 @@
@interface MPMainViewController : UIViewController<MPTypeDelegate, UITextFieldDelegate, MPSearchResultsDelegate, UIWebViewDelegate, MFMailComposeViewControllerDelegate, UIGestureRecognizerDelegate> @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) MPElementEntity *activeElement;
@property (strong, nonatomic) IBOutlet MPSearchDelegate *searchDelegate; @property (strong, nonatomic) IBOutlet MPSearchDelegate *searchDelegate;
@property (weak, nonatomic) IBOutlet UITextField *contentField; @property (weak, nonatomic) IBOutlet UITextField *contentField;
@ -28,20 +28,20 @@
@property (weak, nonatomic) IBOutlet UIView *displayContainer; @property (weak, nonatomic) IBOutlet UIView *displayContainer;
@property (weak, nonatomic) IBOutlet UIView *helpContainer; @property (weak, nonatomic) IBOutlet UIView *helpContainer;
@property (weak, nonatomic) IBOutlet UIView *contentTipContainer; @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 UIView *alertContainer;
@property (weak, nonatomic) IBOutlet UILabel *alertTitle; @property (weak, nonatomic) IBOutlet UILabel *alertTitle;
@property (weak, nonatomic) IBOutlet UITextView *alertBody; @property (weak, nonatomic) IBOutlet UITextView *alertBody;
@property (weak, nonatomic) IBOutlet UILabel *contentTipBody; @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 UIImageView *toolTipEditIcon;
@property (weak, nonatomic) IBOutlet UIView *searchTipContainer; @property (weak, nonatomic) IBOutlet UIView *searchTipContainer;
@property (weak, nonatomic) IBOutlet UIView *actionsTipContainer; @property (weak, nonatomic) IBOutlet UIView *actionsTipContainer;
@property (weak, nonatomic) IBOutlet UIView *typeTipContainer; @property (weak, nonatomic) IBOutlet UIView *typeTipContainer;
@property (weak, nonatomic) IBOutlet UIView *toolTipContainer; @property (weak, nonatomic) IBOutlet UIView *toolTipContainer;
@property (weak, nonatomic) IBOutlet UILabel *toolTipBody; @property (weak, nonatomic) IBOutlet UILabel *toolTipBody;
@property (weak, nonatomic) IBOutlet UIView *userNameContainer; @property (weak, nonatomic) IBOutlet UIView *loginNameContainer;
@property (weak, nonatomic) IBOutlet UITextField *userNameField; @property (weak, nonatomic) IBOutlet UITextField *loginNameField;
@property (weak, nonatomic) IBOutlet UIButton *passwordUser; @property (weak, nonatomic) IBOutlet UIButton *passwordUser;
@property (weak, nonatomic) IBOutlet UIView *outdatedAlertContainer; @property (weak, nonatomic) IBOutlet UIView *outdatedAlertContainer;
@property (weak, nonatomic) IBOutlet UIImageView *outdatedAlertBack; @property (weak, nonatomic) IBOutlet UIImageView *outdatedAlertBack;
@ -54,7 +54,7 @@
- (IBAction)copyContent; - (IBAction)copyContent;
- (IBAction)incrementPasswordCounter; - (IBAction)incrementPasswordCounter;
- (IBAction)resetPasswordCounter:(UILongPressGestureRecognizer *)sender; - (IBAction)resetPasswordCounter:(UILongPressGestureRecognizer *)sender;
- (IBAction)editUserName:(UILongPressGestureRecognizer *)sender; - (IBAction)editLoginName:(UILongPressGestureRecognizer *)sender;
- (IBAction)editPassword; - (IBAction)editPassword;
- (IBAction)closeAlert; - (IBAction)closeAlert;
- (IBAction)upgradePassword; - (IBAction)upgradePassword;

View File

@ -14,7 +14,7 @@
@implementation MPMainViewController @implementation MPMainViewController
@synthesize userNameHidden = _userNameHidden; @synthesize siteInfoHidden = _siteInfoHidden;
@synthesize activeElement = _activeElement; @synthesize activeElement = _activeElement;
@synthesize searchDelegate = _searchDelegate; @synthesize searchDelegate = _searchDelegate;
@synthesize typeButton = _typeButton; @synthesize typeButton = _typeButton;
@ -28,20 +28,20 @@
@synthesize displayContainer = _displayContainer; @synthesize displayContainer = _displayContainer;
@synthesize helpContainer = _helpContainer; @synthesize helpContainer = _helpContainer;
@synthesize contentTipContainer = _copiedContainer; @synthesize contentTipContainer = _copiedContainer;
@synthesize userNameTipContainer = _userNameTipContainer; @synthesize loginNameTipContainer = _loginNameTipContainer;
@synthesize alertContainer = _alertContainer; @synthesize alertContainer = _alertContainer;
@synthesize alertTitle = _alertTitle; @synthesize alertTitle = _alertTitle;
@synthesize alertBody = _alertBody; @synthesize alertBody = _alertBody;
@synthesize contentTipBody = _contentTipBody; @synthesize contentTipBody = _contentTipBody;
@synthesize userNameTipBody = _userNameTipBody; @synthesize loginNameTipBody = _loginNameTipBody;
@synthesize toolTipEditIcon = _contentTipEditIcon; @synthesize toolTipEditIcon = _contentTipEditIcon;
@synthesize searchTipContainer = _searchTipContainer; @synthesize searchTipContainer = _searchTipContainer;
@synthesize actionsTipContainer = _actionsTipContainer; @synthesize actionsTipContainer = _actionsTipContainer;
@synthesize typeTipContainer = _typeTipContainer; @synthesize typeTipContainer = _typeTipContainer;
@synthesize toolTipContainer = _toolTipContainer; @synthesize toolTipContainer = _toolTipContainer;
@synthesize toolTipBody = _toolTipBody; @synthesize toolTipBody = _toolTipBody;
@synthesize userNameContainer = _userNameContainer; @synthesize loginNameContainer = _loginNameContainer;
@synthesize userNameField = _userNameField; @synthesize loginNameField = _loginNameField;
@synthesize passwordUser = _passwordUser; @synthesize passwordUser = _passwordUser;
@synthesize outdatedAlertContainer = _outdatedAlertContainer; @synthesize outdatedAlertContainer = _outdatedAlertContainer;
@synthesize outdatedAlertBack = _outdatedAlertBack; @synthesize outdatedAlertBack = _outdatedAlertBack;
@ -82,9 +82,9 @@
[self.passwordIncrementer addGestureRecognizer:[[UILongPressGestureRecognizer alloc] initWithTarget:self [self.passwordIncrementer addGestureRecognizer:[[UILongPressGestureRecognizer alloc] initWithTarget:self
action:@selector(resetPasswordCounter:)]]; action:@selector(resetPasswordCounter:)]];
[self.userNameContainer addGestureRecognizer:[[UILongPressGestureRecognizer alloc] initWithTarget:self [self.loginNameContainer addGestureRecognizer:[[UILongPressGestureRecognizer alloc] initWithTarget:self
action:@selector(editUserName:)]]; action:@selector(editLoginName:)]];
[self.userNameContainer addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(copyUserName:)]]; [self.loginNameContainer addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(copyLoginName:)]];
[self.outdatedAlertBack addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self [self.outdatedAlertBack addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(infoOutdatedAlert)]]; action:@selector(infoOutdatedAlert)]];
@ -225,10 +225,10 @@
[self setToolTipContainer:nil]; [self setToolTipContainer:nil];
[self setToolTipBody:nil]; [self setToolTipBody:nil];
[self setDisplayContainer:nil]; [self setDisplayContainer:nil];
[self setUserNameField:nil]; [self setLoginNameField:nil];
[self setUserNameTipContainer:nil]; [self setLoginNameTipContainer:nil];
[self setUserNameTipBody:nil]; [self setLoginNameTipBody:nil];
[self setUserNameContainer:nil]; [self setLoginNameContainer:nil];
[self setPasswordUser:nil]; [self setPasswordUser:nil];
[self setOutdatedAlertContainer:nil]; [self setOutdatedAlertContainer:nil];
[self setOutdatedAlertCloseButton:nil]; [self setOutdatedAlertCloseButton:nil];
@ -289,9 +289,9 @@
}); });
}); });
self.userNameField.enabled = NO; self.loginNameField.enabled = NO;
self.userNameField.text = self.activeElement.userName; self.loginNameField.text = self.activeElement.loginName;
self.userNameHidden = !self.activeElement || ([[MPiOSConfig get].userNameHidden boolValue] && (self.activeElement.userName self.siteInfoHidden = !self.activeElement || ([[MPiOSConfig get].siteInfoHidden boolValue] && (self.activeElement.loginName
== nil)); == nil));
[self updateUserHiddenAnimated:NO]; [self updateUserHiddenAnimated:NO];
} }
@ -333,8 +333,8 @@
- (void)toggleUserAnimated:(BOOL)animated { - (void)toggleUserAnimated:(BOOL)animated {
[MPiOSConfig get].userNameHidden = PearlBool(!self.userNameHidden); [MPiOSConfig get].siteInfoHidden = PearlBool(!self.siteInfoHidden);
self.userNameHidden = [[MPiOSConfig get].userNameHidden boolValue]; self.siteInfoHidden = [[MPiOSConfig get].siteInfoHidden boolValue];
[self updateUserHiddenAnimated:animated]; [self updateUserHiddenAnimated:animated];
} }
@ -347,7 +347,7 @@
return; return;
} }
if (self.userNameHidden) { if (self.siteInfoHidden) {
self.displayContainer.frame = CGRectSetHeight(self.displayContainer.frame, 87); self.displayContainer.frame = CGRectSetHeight(self.displayContainer.frame, 87);
} else { } else {
self.displayContainer.frame = CGRectSetHeight(self.displayContainer.frame, 137); 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(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
self.userNameTipBody.text = message; self.loginNameTipBody.text = message;
[UIView animateWithDuration:0.3f animations:^{ [UIView animateWithDuration:0.3f animations:^{
self.userNameTipContainer.alpha = 1; self.loginNameTipContainer.alpha = 1;
} completion:^(BOOL finished) { } completion:^(BOOL finished) {
if (finished) { if (finished) {
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC); dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void) { dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {
[UIView animateWithDuration:0.2f animations:^{ [UIView animateWithDuration:0.2f animations:^{
self.userNameTipContainer.alpha = 0; self.loginNameTipContainer.alpha = 0;
}]; }];
}); });
} }
@ -488,18 +488,18 @@
nil]]; nil]];
} }
- (IBAction)copyUserName:(UITapGestureRecognizer *)sender { - (IBAction)copyLoginName:(UITapGestureRecognizer *)sender {
if (!self.activeElement.userName) if (!self.activeElement.loginName)
return; return;
inf(@"Copying user name for: %@", self.activeElement.name); inf(@"Copying user name for: %@", self.activeElement.name);
[UIPasteboard generalPasteboard].string = [self.activeElement.content description]; [UIPasteboard generalPasteboard].string = [self.activeElement.content description];
[self showUserNameTip:@"Copied!"]; [self showLoginNameTip:@"Copied!"];
[TestFlight passCheckpoint:MPCheckpointCopyUserNameToPasteboard]; [TestFlight passCheckpoint:MPCheckpointCopyLoginNameToPasteboard];
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointCopyUserNameToPasteboard [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointCopyLoginNameToPasteboard
attributes:[NSDictionary dictionaryWithObjectsAndKeys: attributes:[NSDictionary dictionaryWithObjectsAndKeys:
self.activeElement.typeName, @"type", self.activeElement.typeName, @"type",
PearlUnsignedInteger(self.activeElement.version), PearlUnsignedInteger(self.activeElement.version),
@ -564,7 +564,7 @@
}]; }];
} }
- (IBAction)editUserName:(UILongPressGestureRecognizer *)sender { - (IBAction)editLoginName:(UILongPressGestureRecognizer *)sender {
if (sender.state != UIGestureRecognizerStateBegan) if (sender.state != UIGestureRecognizerStateBegan)
// Only fire when the gesture was first detected. // Only fire when the gesture was first detected.
@ -573,11 +573,11 @@
if (!self.activeElement) if (!self.activeElement)
return; return;
self.userNameField.enabled = YES; self.loginNameField.enabled = YES;
[self.userNameField becomeFirstResponder]; [self.loginNameField becomeFirstResponder];
[TestFlight passCheckpoint:MPCheckpointEditUserName]; [TestFlight passCheckpoint:MPCheckpointEditLoginName];
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointEditUserName attributes:[NSDictionary dictionaryWithObjectsAndKeys: [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointEditLoginName attributes:[NSDictionary dictionaryWithObjectsAndKeys:
self.activeElement.typeName, self.activeElement.typeName,
@"type", @"type",
PearlUnsignedInteger(self.activeElement.version), PearlUnsignedInteger(self.activeElement.version),
@ -903,8 +903,8 @@
if (textField == self.contentField) if (textField == self.contentField)
[self.contentField resignFirstResponder]; [self.contentField resignFirstResponder];
if (textField == self.userNameField) if (textField == self.loginNameField)
[self.userNameField resignFirstResponder]; [self.loginNameField resignFirstResponder];
return YES; return YES;
} }
@ -926,17 +926,17 @@
}]; }];
} }
if (textField == self.userNameField) { if (textField == self.loginNameField) {
self.userNameField.enabled = NO; self.loginNameField.enabled = NO;
if (![[MPiOSConfig get].userNameTipShown boolValue]) { if (![[MPiOSConfig get].loginNameTipShown boolValue]) {
[self showUserNameTip:@"Tap to copy or hold to edit."]; [self showLoginNameTip:@"Tap to copy or hold to edit."];
[MPiOSConfig get].userNameTipShown = PearlBool(YES); [MPiOSConfig get].loginNameTipShown = PearlBool(YES);
} }
if ([self.userNameField.text length]) if ([self.loginNameField.text length])
self.activeElement.userName = self.userNameField.text; self.activeElement.loginName = self.loginNameField.text;
else else
self.activeElement.userName = nil; self.activeElement.loginName = nil;
[[MPAppDelegate get] saveContext]; [[MPAppDelegate get] saveContext];
} }

View File

@ -218,9 +218,15 @@
self.tip.text = @"Tap and hold to delete or reset."; self.tip.text = @"Tap and hold to delete or reset.";
[self.loadingUsersIndicator stopAnimating]; [self.loadingUsersIndicator stopAnimating];
__block NSArray *users = nil;
[moc performBlockAndWait:^{
NSError *error = nil;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([MPUserEntity class])]; NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([MPUserEntity class])];
fetchRequest.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO]]; 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. // Clean up avatars.
for (UIView *subview in [self.avatarsView subviews]) for (UIView *subview in [self.avatarsView subviews])
@ -275,8 +281,10 @@
[self.avatarToUser setObject:NilToNSNull(user) forKey:[NSValue valueWithNonretainedObject:avatar]]; [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; avatar.selected = YES;
}
return avatar; return avatar;
} }

View File

@ -12,11 +12,11 @@
@property (nonatomic, retain) NSNumber *sendInfo; @property (nonatomic, retain) NSNumber *sendInfo;
@property (nonatomic, retain) NSNumber *helpHidden; @property (nonatomic, retain) NSNumber *helpHidden;
@property (nonatomic, retain) NSNumber *userNameHidden; @property (nonatomic, retain) NSNumber *siteInfoHidden;
@property (nonatomic, retain) NSNumber *showQuickStart; @property (nonatomic, retain) NSNumber *showQuickStart;
@property (nonatomic, retain) NSNumber *actionsTipShown; @property (nonatomic, retain) NSNumber *actionsTipShown;
@property (nonatomic, retain) NSNumber *typeTipShown; @property (nonatomic, retain) NSNumber *typeTipShown;
@property (nonatomic, retain) NSNumber *userNameTipShown; @property (nonatomic, retain) NSNumber *loginNameTipShown;
+ (MPiOSConfig *)get; + (MPiOSConfig *)get;

View File

@ -7,7 +7,7 @@
// //
@implementation MPiOSConfig @implementation MPiOSConfig
@dynamic sendInfo, helpHidden, userNameHidden, showQuickStart, actionsTipShown, typeTipShown, userNameTipShown; @dynamic sendInfo, helpHidden, siteInfoHidden, showQuickStart, actionsTipShown, typeTipShown, loginNameTipShown;
- (id)init { - (id)init {
@ -17,12 +17,12 @@
[self.defaults registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys: [self.defaults registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO], NSStringFromSelector(@selector(sendInfo)), [NSNumber numberWithBool:NO], NSStringFromSelector(@selector(sendInfo)),
[NSNumber numberWithBool:NO], NSStringFromSelector(@selector(helpHidden)), [NSNumber numberWithBool:NO], NSStringFromSelector(@selector(helpHidden)),
[NSNumber numberWithBool:YES], NSStringFromSelector(@selector(userNameHidden)), [NSNumber numberWithBool:YES], NSStringFromSelector(@selector(siteInfoHidden)),
[NSNumber numberWithBool:YES], NSStringFromSelector(@selector(showQuickStart)), [NSNumber numberWithBool:YES], NSStringFromSelector(@selector(showQuickStart)),
@"510296984", NSStringFromSelector(@selector(iTunesID)), @"510296984", NSStringFromSelector(@selector(iTunesID)),
PearlBoolNot(self.firstRun), NSStringFromSelector(@selector(actionsTipShown)), PearlBoolNot(self.firstRun), NSStringFromSelector(@selector(actionsTipShown)),
PearlBoolNot(self.firstRun), NSStringFromSelector(@selector(typeTipShown)), PearlBoolNot(self.firstRun), NSStringFromSelector(@selector(typeTipShown)),
PearlBool(NO), NSStringFromSelector(@selector(userNameTipShown)), PearlBool(NO), NSStringFromSelector(@selector(loginNameTipShown)),
nil]]; nil]];
return self; return self;

View File

@ -811,12 +811,12 @@ L4m3P4sSw0rD</string>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" userInteractionEnabled="NO" contentMode="scaleToFill" editable="NO" id="HPZ-qz-fpL"> <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"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/> <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"/> <color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<fontDescription key="fontDescription" type="system" pointSize="12"/> <fontDescription key="fontDescription" type="system" pointSize="12"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/> <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> </connections>
</button> </button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="Sfy-hx-kVU"> <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"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" hint="" label="Close"/> <accessibility key="accessibilityConfiguration" hint="" label="Close"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/> <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="toolTipEditIcon" destination="KEn-n3-qhX" id="zEV-tR-Egq"/>
<outlet property="typeButton" destination="xYM-e3-BMg" id="xvn-nR-MRV"/> <outlet property="typeButton" destination="xYM-e3-BMg" id="xvn-nR-MRV"/>
<outlet property="typeTipContainer" destination="g55-0m-WjS" id="KZ9-KV-NMh"/> <outlet property="typeTipContainer" destination="g55-0m-WjS" id="KZ9-KV-NMh"/>
<outlet property="userNameContainer" destination="lrZ-PV-rgu" id="tTh-fp-qaN"/> <outlet property="loginNameContainer" destination="lrZ-PV-rgu" id="tTh-fp-qaN"/>
<outlet property="userNameField" destination="tj6-Ot-Fuh" id="oFa-xd-UEm"/> <outlet property="loginNameField" destination="tj6-Ot-Fuh" id="oFa-xd-UEm"/>
<outlet property="userNameTipBody" destination="coX-1P-dqm" id="MRt-A5-ZMH"/> <outlet property="loginNameTipBody" destination="coX-1P-dqm" id="MRt-A5-ZMH"/>
<outlet property="userNameTipContainer" destination="b9W-aA-93r" id="heZ-Gu-obO"/> <outlet property="loginNameTipContainer" destination="b9W-aA-93r" id="heZ-Gu-obO"/>
<segue destination="oLN-6u-GLb" kind="push" identifier="UserProfile" id="tKN-Sw-S5J"/> <segue destination="oLN-6u-GLb" kind="push" identifier="UserProfile" id="tKN-Sw-S5J"/>
</connections> </connections>
</viewController> </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="toolTipEditIcon" candidateClass="UIImageView"/>
<relationship kind="outlet" name="typeButton" candidateClass="UIButton"/> <relationship kind="outlet" name="typeButton" candidateClass="UIButton"/>
<relationship kind="outlet" name="typeTipContainer" candidateClass="UIView"/> <relationship kind="outlet" name="typeTipContainer" candidateClass="UIView"/>
<relationship kind="outlet" name="userNameContainer" candidateClass="UIView"/> <relationship kind="outlet" name="loginNameContainer" candidateClass="UIView"/>
<relationship kind="outlet" name="userNameField" candidateClass="UITextField"/> <relationship kind="outlet" name="loginNameField" candidateClass="UITextField"/>
<relationship kind="outlet" name="userNameTipBody" candidateClass="UILabel"/> <relationship kind="outlet" name="loginNameTipBody" candidateClass="UILabel"/>
<relationship kind="outlet" name="userNameTipContainer" candidateClass="UIView"/> <relationship kind="outlet" name="loginNameTipContainer" candidateClass="UIView"/>
</relationships> </relationships>
</class> </class>
<class className="MPPreferencesViewController" superclassName="UITableViewController"> <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"/> <simulatedScreenMetrics key="destination"/>
</simulatedMetricsContainer> </simulatedMetricsContainer>
<inferredMetricsTieBreakers> <inferredMetricsTieBreakers>
<segue reference="KIl-ZW-M7G"/> <segue reference="9Bs-cD-ddF"/>
</inferredMetricsTieBreakers> </inferredMetricsTieBreakers>
</document> </document>