diff --git a/External/Pearl b/External/Pearl index 6be4c8b6..5c0c968d 160000 --- a/External/Pearl +++ b/External/Pearl @@ -1 +1 @@ -Subproject commit 6be4c8b6646c89fe341c40dac40a9accb6b73bbf +Subproject commit 5c0c968d29f2133e517e4d9432794998e1ce0d4c diff --git a/MasterPassword-iOS.xcodeproj/project.pbxproj b/MasterPassword-iOS.xcodeproj/project.pbxproj index 4f6714fd..8c3e6ebb 100644 --- a/MasterPassword-iOS.xcodeproj/project.pbxproj +++ b/MasterPassword-iOS.xcodeproj/project.pbxproj @@ -25,9 +25,6 @@ DA3EF17D15A47744003ABF4E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; }; DA3EF18315A47744003ABF4E /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DA3EF18115A47744003ABF4E /* InfoPlist.strings */; }; DA3EF18615A47744003ABF4E /* Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA3EF18515A47744003ABF4E /* Tests.m */; }; - DA40C2611586099D0079CE6E /* MPUserEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA40C2601586099D0079CE6E /* MPUserEntity.m */; }; - DA40C2671586099E0079CE6E /* MPElementGeneratedEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA40C2661586099E0079CE6E /* MPElementGeneratedEntity.m */; }; - DA40C26A1586099E0079CE6E /* MPElementStoredEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA40C2691586099E0079CE6E /* MPElementStoredEntity.m */; }; DA4425CC1557BED40052177D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; }; DA4426001557BF260052177D /* UbiquityStoreManager.h in Headers */ = {isa = PBXBuildFile; fileRef = DA4425F11557BF260052177D /* UbiquityStoreManager.h */; }; DA4426011557BF260052177D /* UbiquityStoreManager.m in Sources */ = {isa = PBXBuildFile; fileRef = DA4425F21557BF260052177D /* UbiquityStoreManager.m */; }; @@ -36,7 +33,6 @@ DA44260A1557D9E40052177D /* libiCloudStoreManager.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA4425CB1557BED40052177D /* libiCloudStoreManager.a */; }; DA46826F15AB843200FB09E7 /* tip_basic_black_bottom_right.png in Resources */ = {isa = PBXBuildFile; fileRef = DA46826D15AB843200FB09E7 /* tip_basic_black_bottom_right.png */; }; DA46827015AB843200FB09E7 /* tip_basic_black_bottom_right@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA46826E15AB843200FB09E7 /* tip_basic_black_bottom_right@2x.png */; }; - DA46827315ABF00100FB09E7 /* MPElementEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA46827215ABF00100FB09E7 /* MPElementEntity.m */; }; DA4DA1D91564471A00F6F596 /* libjrswizzle.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC6326C148680650075AEA5 /* libjrswizzle.a */; }; DA4DA1DA1564471F00F6F596 /* libuicolor-utilities.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC6325D1486805C0075AEA5 /* libuicolor-utilities.a */; }; DA4DA1DB1564475E00F6F596 /* libscryptenc-ios.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA79A9BB1557DB6F00BAA07A /* libscryptenc-ios.a */; }; @@ -676,6 +672,10 @@ DAB8D93815036BF700CED3BC /* lock_red@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DAB8D6B515036BF600CED3BC /* lock_red@2x.png */; }; DAB8D93915036BF700CED3BC /* logo-bare.png in Resources */ = {isa = PBXBuildFile; fileRef = DAB8D6B615036BF600CED3BC /* logo-bare.png */; }; DAB8D97C1503718B00CED3BC /* jquery-1.6.1.min.js in Resources */ = {isa = PBXBuildFile; fileRef = DAB8D6AB15036BF600CED3BC /* jquery-1.6.1.min.js */; }; + DAB9FE1C15AC00C0007A7E5C /* MPElementGeneratedEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB9FE1B15AC00C0007A7E5C /* MPElementGeneratedEntity.m */; }; + DAB9FE1F15AC00C0007A7E5C /* MPUserEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB9FE1E15AC00C0007A7E5C /* MPUserEntity.m */; }; + DAB9FE2215AC00C0007A7E5C /* MPElementStoredEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB9FE2115AC00C0007A7E5C /* MPElementStoredEntity.m */; }; + DAB9FE2515AC00C0007A7E5C /* MPElementEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB9FE2415AC00C0007A7E5C /* MPElementEntity.m */; }; DABB981615100B4000B05417 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DABB981515100B4000B05417 /* SystemConfiguration.framework */; }; DAC6325E1486805C0075AEA5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; }; DAC6326D148680650075AEA5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; }; @@ -914,12 +914,6 @@ DA3EF18415A47744003ABF4E /* Tests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Tests.h; sourceTree = ""; }; DA3EF18515A47744003ABF4E /* Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Tests.m; sourceTree = ""; }; DA3EF18715A47744003ABF4E /* Tests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tests-Prefix.pch"; sourceTree = ""; }; - DA40C25F1586099D0079CE6E /* MPUserEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUserEntity.h; sourceTree = ""; }; - DA40C2601586099D0079CE6E /* MPUserEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUserEntity.m; sourceTree = ""; }; - DA40C2651586099E0079CE6E /* MPElementGeneratedEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementGeneratedEntity.h; sourceTree = ""; }; - DA40C2661586099E0079CE6E /* MPElementGeneratedEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementGeneratedEntity.m; sourceTree = ""; }; - DA40C2681586099E0079CE6E /* MPElementStoredEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementStoredEntity.h; sourceTree = ""; }; - DA40C2691586099E0079CE6E /* MPElementStoredEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementStoredEntity.m; sourceTree = ""; }; DA4425CB1557BED40052177D /* libiCloudStoreManager.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libiCloudStoreManager.a; sourceTree = BUILT_PRODUCTS_DIR; }; DA4425F11557BF260052177D /* UbiquityStoreManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UbiquityStoreManager.h; sourceTree = ""; }; DA4425F21557BF260052177D /* UbiquityStoreManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UbiquityStoreManager.m; sourceTree = ""; }; @@ -930,8 +924,6 @@ DA46826C15AB48F100FB09E7 /* MasterPassword 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 2.xcdatamodel"; sourceTree = ""; }; DA46826D15AB843200FB09E7 /* tip_basic_black_bottom_right.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = tip_basic_black_bottom_right.png; sourceTree = ""; }; DA46826E15AB843200FB09E7 /* tip_basic_black_bottom_right@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tip_basic_black_bottom_right@2x.png"; sourceTree = ""; }; - DA46827115ABF00100FB09E7 /* MPElementEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementEntity.h; sourceTree = ""; }; - DA46827215ABF00100FB09E7 /* MPElementEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementEntity.m; sourceTree = ""; }; DA5BFA44147E415C00F98B1E /* MasterPassword.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MasterPassword.app; sourceTree = BUILT_PRODUCTS_DIR; }; DA5BFA48147E415C00F98B1E /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; DA5BFA4A147E415C00F98B1E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; @@ -1648,6 +1640,14 @@ DAB8D6F715036BF600CED3BC /* tip_location_teal@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tip_location_teal@2x.png"; sourceTree = ""; }; DAB8D6F815036BF600CED3BC /* tip_location_wood.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = tip_location_wood.png; sourceTree = ""; }; DAB8D6F915036BF600CED3BC /* tip_location_wood@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tip_location_wood@2x.png"; sourceTree = ""; }; + DAB9FE1A15AC00C0007A7E5C /* MPElementGeneratedEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementGeneratedEntity.h; sourceTree = ""; }; + DAB9FE1B15AC00C0007A7E5C /* MPElementGeneratedEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementGeneratedEntity.m; sourceTree = ""; }; + DAB9FE1D15AC00C0007A7E5C /* MPUserEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUserEntity.h; sourceTree = ""; }; + DAB9FE1E15AC00C0007A7E5C /* MPUserEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUserEntity.m; sourceTree = ""; }; + DAB9FE2015AC00C0007A7E5C /* MPElementStoredEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementStoredEntity.h; sourceTree = ""; }; + DAB9FE2115AC00C0007A7E5C /* MPElementStoredEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementStoredEntity.m; sourceTree = ""; }; + DAB9FE2315AC00C0007A7E5C /* MPElementEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementEntity.h; sourceTree = ""; }; + DAB9FE2415AC00C0007A7E5C /* MPElementEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementEntity.m; sourceTree = ""; }; DABB980C150FF40100B05417 /* SendToMac-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SendToMac-Prefix.pch"; sourceTree = ""; }; DABB980D150FF40100B05417 /* SendToMac.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SendToMac.h; sourceTree = ""; }; DABB980E150FF40100B05417 /* SendToMac.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SendToMac.m; sourceTree = ""; }; @@ -1999,17 +1999,17 @@ DA5BFA50147E415C00F98B1E /* MasterPassword */ = { isa = PBXGroup; children = ( - DA40C2681586099E0079CE6E /* MPElementStoredEntity.h */, - DA40C2691586099E0079CE6E /* MPElementStoredEntity.m */, - DA40C2651586099E0079CE6E /* MPElementGeneratedEntity.h */, - DA40C2661586099E0079CE6E /* MPElementGeneratedEntity.m */, - DA40C25F1586099D0079CE6E /* MPUserEntity.h */, - DA40C2601586099D0079CE6E /* MPUserEntity.m */, + DAB9FE2315AC00C0007A7E5C /* MPElementEntity.h */, + DAB9FE2415AC00C0007A7E5C /* MPElementEntity.m */, + DAB9FE2015AC00C0007A7E5C /* MPElementStoredEntity.h */, + DAB9FE2115AC00C0007A7E5C /* MPElementStoredEntity.m */, + DAB9FE1D15AC00C0007A7E5C /* MPUserEntity.h */, + DAB9FE1E15AC00C0007A7E5C /* MPUserEntity.m */, + DAB9FE1A15AC00C0007A7E5C /* MPElementGeneratedEntity.h */, + DAB9FE1B15AC00C0007A7E5C /* MPElementGeneratedEntity.m */, DA0E07941577FE490008A67E /* MPEntities.h */, DA0E07951577FE490008A67E /* MPEntities.m */, DAB8D43C15036BCF00CED3BC /* MasterPassword.xcdatamodeld */, - DA46827115ABF00100FB09E7 /* MPElementEntity.h */, - DA46827215ABF00100FB09E7 /* MPElementEntity.m */, DA600C2415054F3A008E9AB6 /* MPAppDelegate_Key.h */, DA600C2315054F3A008E9AB6 /* MPAppDelegate_Key.m */, DA4426041557C1990052177D /* MPAppDelegate_Shared.h */, @@ -4245,10 +4245,10 @@ DA4426091557C1990052177D /* MPAppDelegate_Store.m in Sources */, DA0E07961577FE490008A67E /* MPEntities.m in Sources */, DAC728CA157C247B00889EF2 /* MPPreferencesViewController.m in Sources */, - DA40C2611586099D0079CE6E /* MPUserEntity.m in Sources */, - DA40C2671586099E0079CE6E /* MPElementGeneratedEntity.m in Sources */, - DA40C26A1586099E0079CE6E /* MPElementStoredEntity.m in Sources */, - DA46827315ABF00100FB09E7 /* MPElementEntity.m in Sources */, + DAB9FE1C15AC00C0007A7E5C /* MPElementGeneratedEntity.m in Sources */, + DAB9FE1F15AC00C0007A7E5C /* MPUserEntity.m in Sources */, + DAB9FE2215AC00C0007A7E5C /* MPElementStoredEntity.m in Sources */, + DAB9FE2515AC00C0007A7E5C /* MPElementEntity.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/MasterPassword/MPElementEntity.h b/MasterPassword/MPElementEntity.h index d57d50f9..ad0cce00 100644 --- a/MasterPassword/MPElementEntity.h +++ b/MasterPassword/MPElementEntity.h @@ -19,6 +19,7 @@ @property (nonatomic, retain) NSNumber * type_; @property (nonatomic, retain) NSNumber * uses_; @property (nonatomic, retain) NSNumber * version_; +@property (nonatomic, retain) NSNumber * requiresExplicitMigration_; @property (nonatomic, retain) MPUserEntity *user; @end diff --git a/MasterPassword/MPElementEntity.m b/MasterPassword/MPElementEntity.m index 5dd05dec..efbb68cb 100644 --- a/MasterPassword/MPElementEntity.m +++ b/MasterPassword/MPElementEntity.m @@ -18,6 +18,7 @@ @dynamic type_; @dynamic uses_; @dynamic version_; +@dynamic requiresExplicitMigration_; @dynamic user; @end diff --git a/MasterPassword/MPElementGeneratedEntity.h b/MasterPassword/MPElementGeneratedEntity.h index d31c2dea..95f0e029 100644 --- a/MasterPassword/MPElementGeneratedEntity.h +++ b/MasterPassword/MPElementGeneratedEntity.h @@ -2,7 +2,7 @@ // MPElementGeneratedEntity.h // MasterPassword-iOS // -// Created by Maarten Billemont on 11/06/12. +// Created by Maarten Billemont on 10/07/12. // Copyright (c) 2012 Lyndir. All rights reserved. // diff --git a/MasterPassword/MPElementGeneratedEntity.m b/MasterPassword/MPElementGeneratedEntity.m index ed781a4b..04259644 100644 --- a/MasterPassword/MPElementGeneratedEntity.m +++ b/MasterPassword/MPElementGeneratedEntity.m @@ -2,7 +2,7 @@ // MPElementGeneratedEntity.m // MasterPassword-iOS // -// Created by Maarten Billemont on 11/06/12. +// Created by Maarten Billemont on 10/07/12. // Copyright (c) 2012 Lyndir. All rights reserved. // diff --git a/MasterPassword/MPElementStoredEntity.h b/MasterPassword/MPElementStoredEntity.h index 9a5088c1..57980279 100644 --- a/MasterPassword/MPElementStoredEntity.h +++ b/MasterPassword/MPElementStoredEntity.h @@ -2,7 +2,7 @@ // MPElementStoredEntity.h // MasterPassword-iOS // -// Created by Maarten Billemont on 11/06/12. +// Created by Maarten Billemont on 10/07/12. // Copyright (c) 2012 Lyndir. All rights reserved. // diff --git a/MasterPassword/MPElementStoredEntity.m b/MasterPassword/MPElementStoredEntity.m index c1a94f1a..e19e433b 100644 --- a/MasterPassword/MPElementStoredEntity.m +++ b/MasterPassword/MPElementStoredEntity.m @@ -2,7 +2,7 @@ // MPElementStoredEntity.m // MasterPassword-iOS // -// Created by Maarten Billemont on 11/06/12. +// Created by Maarten Billemont on 10/07/12. // Copyright (c) 2012 Lyndir. All rights reserved. // diff --git a/MasterPassword/MPEntities.h b/MasterPassword/MPEntities.h index db5df3fa..5bd4636f 100644 --- a/MasterPassword/MPEntities.h +++ b/MasterPassword/MPEntities.h @@ -18,6 +18,8 @@ @property (assign) MPElementType type; @property (assign) NSUInteger uses; +@property (assign) NSUInteger version; +@property (assign) BOOL requiresExplicitMigration; - (NSUInteger)use; - (NSString *)exportContent; diff --git a/MasterPassword/MPEntities.m b/MasterPassword/MPEntities.m index 6d62e0a1..796bf704 100644 --- a/MasterPassword/MPEntities.m +++ b/MasterPassword/MPEntities.m @@ -33,6 +33,25 @@ self.uses_ = PearlUnsignedInteger(anUses); } +- (NSUInteger)version { + + return [self.version_ unsignedIntegerValue]; +} + +- (void)setVersion:(NSUInteger)version { + + self.version_ = PearlUnsignedInteger(version); +} + +- (BOOL)requiresExplicitMigration { + + return [self.requiresExplicitMigration_ boolValue]; +} + +- (void)setRequiresExplicitMigration:(BOOL)requiresExplicitMigration { + + self.requiresExplicitMigration_ = PearlBool(requiresExplicitMigration); +} - (NSUInteger)use { diff --git a/MasterPassword/MPTypes.h b/MasterPassword/MPTypes.h index 70cd6728..37ee86e3 100644 --- a/MasterPassword/MPTypes.h +++ b/MasterPassword/MPTypes.h @@ -8,7 +8,7 @@ #import -#define MPPersistentStoreDidChangeNotification @"MPPersistentStoreDidChange" +@class MPElementEntity; typedef enum { MPElementContentTypePassword, @@ -70,12 +70,12 @@ typedef enum { #define MPCheckpointCloudDisabled @"MPCheckpointCloudDisabled" #define MPCheckpointSitesImported @"MPCheckpointSitesImported" #define MPCheckpointSitesExported @"MPCheckpointSitesExported" +#define MPCheckpointExplicitMigration @"MPCheckpointExplicitMigration" -#define MPNotificationStoreUpdated @"MPNotificationStoreUpdated" #define MPNotificationSignedIn @"MPNotificationKeySet" #define MPNotificationSignedOut @"MPNotificationKeyUnset" #define MPNotificationKeyForgotten @"MPNotificationKeyForgotten" -#define MPNotificationElementUsed @"MPNotificationElementUsed" +#define MPNotificationElementUpdated @"MPNotificationElementUpdated" NSData *keyForPassword(NSString *password, NSString *username); NSData *subkeyForKey(NSData *key, NSUInteger subkeyLength); @@ -84,5 +84,6 @@ NSData *keyIDForKey(NSData *key); NSString *NSStringFromMPElementType(MPElementType type); NSString *NSStringShortFromMPElementType(MPElementType type); NSString *ClassNameFromMPElementType(MPElementType type); -Class ClassFromMPElementType(MPElementType type); +Class ClassFromMPElementType(MPElementType type); NSString *MPCalculateContent(MPElementType type, NSString *name, NSData *key, uint32_t counter); +void MPElementMigrate(MPElementEntity *element, BOOL explicit); diff --git a/MasterPassword/MPTypes.m b/MasterPassword/MPTypes.m index 0fb29e71..30df319a 100644 --- a/MasterPassword/MPTypes.m +++ b/MasterPassword/MPTypes.m @@ -221,3 +221,11 @@ NSString *MPCalculateContent(MPElementType type, NSString *name, NSData *key, ui return content; } + +void MPElementMigrate(MPElementEntity *element, BOOL explicit) { + + if (element.version == 0 && explicit) { + // 0 -> 1 + element.version = 1; + } +} diff --git a/MasterPassword/MPUserEntity.h b/MasterPassword/MPUserEntity.h index 54751768..ff766e6c 100644 --- a/MasterPassword/MPUserEntity.h +++ b/MasterPassword/MPUserEntity.h @@ -2,7 +2,7 @@ // MPUserEntity.h // MasterPassword-iOS // -// Created by Maarten Billemont on 11/06/12. +// Created by Maarten Billemont on 10/07/12. // Copyright (c) 2012 Lyndir. All rights reserved. // @@ -14,11 +14,11 @@ @interface MPUserEntity : NSManagedObject @property (nonatomic, retain) NSNumber * avatar_; +@property (nonatomic, retain) NSNumber * defaultType_; @property (nonatomic, retain) NSData * keyID; @property (nonatomic, retain) NSDate * lastUsed; @property (nonatomic, retain) NSString * name; @property (nonatomic, retain) NSNumber * saveKey_; -@property (nonatomic, retain) NSNumber * defaultType_; @property (nonatomic, retain) NSSet *elements; @end diff --git a/MasterPassword/MPUserEntity.m b/MasterPassword/MPUserEntity.m index 559525b9..a89b4949 100644 --- a/MasterPassword/MPUserEntity.m +++ b/MasterPassword/MPUserEntity.m @@ -2,7 +2,7 @@ // MPUserEntity.m // MasterPassword-iOS // -// Created by Maarten Billemont on 11/06/12. +// Created by Maarten Billemont on 10/07/12. // Copyright (c) 2012 Lyndir. All rights reserved. // @@ -13,11 +13,11 @@ @implementation MPUserEntity @dynamic avatar_; +@dynamic defaultType_; @dynamic keyID; @dynamic lastUsed; @dynamic name; @dynamic saveKey_; -@dynamic defaultType_; @dynamic elements; @end diff --git a/MasterPassword/MasterPassword.xcdatamodeld/MasterPassword 2.xcdatamodel/contents b/MasterPassword/MasterPassword.xcdatamodeld/MasterPassword 2.xcdatamodel/contents index 6ef02c4a..a87e63c2 100644 --- a/MasterPassword/MasterPassword.xcdatamodeld/MasterPassword 2.xcdatamodel/contents +++ b/MasterPassword/MasterPassword.xcdatamodeld/MasterPassword 2.xcdatamodel/contents @@ -4,9 +4,10 @@ + - + @@ -25,7 +26,7 @@ - + diff --git a/MasterPassword/iOS/MPAppDelegate.m b/MasterPassword/iOS/MPAppDelegate.m index 92cec731..4d058b76 100644 --- a/MasterPassword/iOS/MPAppDelegate.m +++ b/MasterPassword/iOS/MPAppDelegate.m @@ -74,6 +74,8 @@ return YES; }]; + TFLog(@"TestFlight (%@) initialized for: %@ v%@.", // + TESTFLIGHT_SDK_VERSION, [PearlInfoPlist get].CFBundleName, [PearlInfoPlist get].CFBundleVersion); } } @catch (id exception) { @@ -99,6 +101,8 @@ return YES; }]; + CLSLog(@"Crashlytics (%@) initialized for: %@ v%@.", // + [Crashlytics sharedInstance].version, [PearlInfoPlist get].CFBundleName, [PearlInfoPlist get].CFBundleVersion); } } @catch (id exception) { @@ -427,7 +431,7 @@ } - (void)exportShowPasswords:(BOOL)showPasswords { - + if (![MFMailComposeViewController canSendMail]) { [PearlAlert showAlertWithTitle:@"Cannot Send Mail" message: @@ -460,7 +464,7 @@ self.activeUser.name, [PearlInfoPlist get].CFBundleShortVersionString, [PearlInfoPlist get].CFBundleVersion); - + NSDateFormatter *exportDateFormatter = [NSDateFormatter new]; [exportDateFormatter setDateFormat:@"yyyy'-'MM'-'DD"]; diff --git a/MasterPassword/iOS/MPMainViewController.h b/MasterPassword/iOS/MPMainViewController.h index d4c60faf..4ac1550d 100644 --- a/MasterPassword/iOS/MPMainViewController.h +++ b/MasterPassword/iOS/MPMainViewController.h @@ -23,6 +23,7 @@ @property (weak, nonatomic) IBOutlet UILabel *passwordCounter; @property (weak, nonatomic) IBOutlet UIButton *passwordIncrementer; @property (weak, nonatomic) IBOutlet UIButton *passwordEdit; +@property (weak, nonatomic) IBOutlet UIButton *passwordUpgrade; @property (weak, nonatomic) IBOutlet UIView *contentContainer; @property (weak, nonatomic) IBOutlet UIView *helpContainer; @property (weak, nonatomic) IBOutlet UIView *contentTipContainer; @@ -34,8 +35,8 @@ @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 *toolTipContainer; +@property (weak, nonatomic) IBOutlet UILabel *toolTipBody; @property (copy) void (^contentTipCleanup)(BOOL finished); @property (copy) void (^toolTipCleanup)(BOOL finished); @@ -45,6 +46,7 @@ - (IBAction)resetPasswordCounter:(UILongPressGestureRecognizer *)sender; - (IBAction)editPassword; - (IBAction)closeAlert; +- (IBAction)upgradePassword; - (IBAction)action:(UIBarButtonItem *)sender; - (BOOL)isHelpVisible; diff --git a/MasterPassword/iOS/MPMainViewController.m b/MasterPassword/iOS/MPMainViewController.m index 370de71a..a202cfea 100644 --- a/MasterPassword/iOS/MPMainViewController.m +++ b/MasterPassword/iOS/MPMainViewController.m @@ -13,6 +13,8 @@ #import "LocalyticsSession.h" +void MPElementMigrate(MPElementEntity *entity, BOOL i); + @interface MPMainViewController (Private) - (void)updateAnimated:(BOOL)animated; @@ -33,6 +35,7 @@ @synthesize passwordCounter = _passwordCounter; @synthesize passwordIncrementer = _passwordIncrementer; @synthesize passwordEdit = _passwordEdit; +@synthesize passwordUpgrade = _passwordUpgrade; @synthesize contentContainer = _contentContainer; @synthesize helpContainer = _helpContainer; @synthesize contentTipContainer = _copiedContainer; @@ -70,26 +73,39 @@ } - (void)viewDidLoad { - - self.searchDelegate = [MPSearchDelegate new]; - self.searchDelegate.delegate = self; - self.searchDelegate.searchDisplayController = self.searchDisplayController; - self.searchDelegate.searchTipContainer = self.searchTipContainer; - self.searchDisplayController.searchBar.delegate = self.searchDelegate; - self.searchDisplayController.delegate = self.searchDelegate; - self.searchDisplayController.searchResultsDelegate = self.searchDelegate; + + self.searchDelegate = [MPSearchDelegate new]; + self.searchDelegate.delegate = self; + self.searchDelegate.searchDisplayController = self.searchDisplayController; + self.searchDelegate.searchTipContainer = self.searchTipContainer; + self.searchDisplayController.searchBar.delegate = self.searchDelegate; + self.searchDisplayController.delegate = self.searchDelegate; + self.searchDisplayController.searchResultsDelegate = self.searchDelegate; self.searchDisplayController.searchResultsDataSource = self.searchDelegate; self.resetPasswordCounterGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(resetPasswordCounter:)]; [self.passwordIncrementer addGestureRecognizer:self.resetPasswordCounterGesture]; - + self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"ui_background"]]; self.contentField.font = [UIFont fontWithName:@"Exo-Black" size:self.contentField.font.pointSize]; - self.alertBody.text = nil; + self.alertBody.text = nil; self.toolTipEditIcon.hidden = YES; + [[NSNotificationCenter defaultCenter] addObserverForName:MPNotificationElementUpdated object:nil queue:nil + usingBlock:^void(NSNotification *note) { + if (self.activeElement.type & MPElementTypeClassStored + && ![[self.activeElement.content description] length]) + [self showToolTip:@"Tap to set a password." withIcon:self.toolTipEditIcon]; + if (self.activeElement.requiresExplicitMigration) + [self showToolTip:@"Password is outdated. Tap to upgrade it." withIcon:nil]; + }]; + [[NSNotificationCenter defaultCenter] addObserverForName:MPNotificationSignedOut object:nil queue:nil + usingBlock:^void(NSNotification *note) { + self.activeElement = nil; + }]; + [super viewDidLoad]; } @@ -100,7 +116,7 @@ if (![MPAppDelegate get].activeUser) [self.navigationController presentViewController:[self.storyboard instantiateViewControllerWithIdentifier:@"MPUnlockViewController"] - animated:animated completion:nil]; + animated:animated completion:nil]; if (self.activeElement.user != [MPAppDelegate get].activeUser) self.activeElement = nil; self.searchDisplayController.searchBar.text = nil; @@ -108,6 +124,7 @@ self.searchTipContainer.alpha = 0; self.actionsTipContainer.alpha = 0; self.typeTipContainer.alpha = 0; + self.toolTipContainer.alpha = 0; [self setHelpHidden:[[MPiOSConfig get].helpHidden boolValue] animated:animated]; [self updateAnimated:animated]; @@ -117,11 +134,13 @@ - (void)viewDidAppear:(BOOL)animated { - if ([[MPiOSConfig get].firstRun boolValue]) + if (![[MPiOSConfig get].actionsTipShown boolValue]) [UIView animateWithDuration:animated? 0.3f: 0 animations:^{ self.actionsTipContainer.alpha = 1; } completion:^(BOOL finished) { if (finished) { + [MPiOSConfig get].actionsTipShown = PearlBool(YES); + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [UIView animateWithDuration:0.2f animations:^{ self.actionsTipContainer.alpha = 0; @@ -153,6 +172,7 @@ [self setPasswordCounter:nil]; [self setPasswordIncrementer:nil]; [self setPasswordEdit:nil]; + [self setPasswordUpgrade:nil]; [self setContentContainer:nil]; [self setHelpContainer:nil]; [self setContentTipContainer:nil]; @@ -183,9 +203,22 @@ [self setHelpChapter:self.activeElement? @"2": @"1"]; self.siteName.text = self.activeElement.name; - self.passwordCounter.alpha = self.activeElement.type & MPElementTypeClassGenerated? 0.5f: 0; - self.passwordIncrementer.alpha = self.activeElement.type & MPElementTypeClassGenerated? 0.5f: 0; - self.passwordEdit.alpha = self.activeElement.type & MPElementTypeClassStored? 0.5f: 0; + self.passwordCounter.alpha = 0; + self.passwordIncrementer.alpha = 0; + self.passwordEdit.alpha = 0; + self.passwordUpgrade.alpha = 0; + + if (self.activeElement.requiresExplicitMigration) + self.passwordUpgrade.alpha = 0.5f; + + else { + if (self.activeElement.type & MPElementTypeClassGenerated) { + self.passwordCounter.alpha = 0.5f; + self.passwordIncrementer.alpha = 0.5f; + } else + if (self.activeElement.type & MPElementTypeClassStored) + self.passwordEdit.alpha = 0.5f; + } [self.typeButton setTitle:NSStringFromMPElementType(self.activeElement.type) forState:UIControlStateNormal]; @@ -254,17 +287,17 @@ } - (void)showContentTip:(NSString *)message withIcon:(UIImageView *)icon { - + dispatch_async(dispatch_get_main_queue(), ^{ if (self.contentTipCleanup) self.contentTipCleanup(NO); - + self.contentTipBody.text = message; self.contentTipCleanup = ^(BOOL finished) { icon.hidden = YES; self.contentTipCleanup = nil; }; - + icon.hidden = NO; [UIView animateWithDuration:0.3f animations:^{ self.contentTipContainer.alpha = 1; @@ -282,17 +315,17 @@ } - (void)showToolTip:(NSString *)message withIcon:(UIImageView *)icon { - + dispatch_async(dispatch_get_main_queue(), ^{ if (self.toolTipCleanup) self.toolTipCleanup(NO); - + self.toolTipBody.text = message; self.toolTipCleanup = ^(BOOL finished) { - icon.hidden = YES; + icon.hidden = YES; self.toolTipCleanup = nil; }; - + icon.hidden = NO; [UIView animateWithDuration:0.3f animations:^{ self.toolTipContainer.alpha = 1; @@ -363,7 +396,8 @@ [TestFlight passCheckpoint:MPCheckpointIncrementPasswordCounter]; [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointIncrementPasswordCounter attributes:[NSDictionary dictionaryWithObjectsAndKeys: - NSStringFromMPElementType(self.activeElement.type), @"type", + NSStringFromMPElementType( + self.activeElement.type), @"type", nil]]; }]; } @@ -391,7 +425,8 @@ [TestFlight passCheckpoint:MPCheckpointResetPasswordCounter]; [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointResetPasswordCounter attributes:[NSDictionary dictionaryWithObjectsAndKeys: - NSStringFromMPElementType(self.activeElement.type), @"type", + NSStringFromMPElementType( + self.activeElement.type), @"type", nil]]; }]; } @@ -404,7 +439,7 @@ return; [self changeElementWithoutWarningDo:task]; - } cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonContinue, nil]; + } cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonContinue, nil]; } - (void)changeElementWithoutWarningDo:(void (^)(void))task; { @@ -442,6 +477,31 @@ } } +- (IBAction)upgradePassword { + + [self changeElementWithWarning: + self.activeElement.type & MPElementTypeClassGenerated? + @"You are upgrading the site.\n\n" + @"This upgrade improves the site's compatibility with the latest version of Master Password.\n\n" + @"Your password will change and you will need to update your site's account." + : + @"You are upgrading the site.\n\n" + @"This upgrade improves the site's compatibility with the latest version of Master Password." + do:^{ + inf(@"Explicitly migrating element: %@", self.activeElement); + MPElementMigrate(self.activeElement, YES); + + [TestFlight passCheckpoint:MPCheckpointExplicitMigration]; + [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointExplicitMigration + attributes:[NSDictionary dictionaryWithObjectsAndKeys: + NSStringFromMPElementType( + self.activeElement.type), @"type", + PearlUnsignedInteger(self.activeElement.version), + @"version", + nil]]; + }]; +} + - (IBAction)closeAlert { [UIView animateWithDuration:0.3f animations:^{ @@ -501,7 +561,7 @@ @"masterpassword@lyndir.com" viewStyle:UIAlertViewStyleDefault initAlert:nil tappedButtonBlock:nil cancelTitle:[PearlStrings get].commonButtonOkay - otherTitles:nil]; + otherTitles:nil]; else { [PearlAlert showAlertWithTitle:@"Sending Feedback" @@ -514,7 +574,8 @@ MFMailComposeViewController *composer = [MFMailComposeViewController new]; [composer setMailComposeDelegate:self]; [composer setToRecipients:[NSArray arrayWithObject:@"Master Password Development "]]; - [composer setSubject:PearlString(@"Feedback for Master Password [%@]", [[PearlKeyChain deviceIdentifier] stringByDeletingMatchesOf:@"-.*"])]; + [composer setSubject:PearlString(@"Feedback for Master Password [%@]", + [[PearlKeyChain deviceIdentifier] stringByDeletingMatchesOf:@"-.*"])]; [composer setMessageBody: PearlString( @"\n\n\n" @@ -524,20 +585,21 @@ [MPAppDelegate get].activeUser.name, [PearlInfoPlist get].CFBundleShortVersionString, [PearlInfoPlist get].CFBundleVersion) - isHTML:NO]; + isHTML:NO]; if (buttonIndex_ == [alert_ firstOtherButtonIndex]) { - PearlLogLevel logLevel = [[MPiOSConfig get].sendInfo boolValue]? PearlLogLevelDebug: PearlLogLevelInfo; + PearlLogLevel logLevel = [[MPiOSConfig get].sendInfo boolValue]? PearlLogLevelDebug + : PearlLogLevelInfo; [composer addAttachmentData:[[[PearlLogger get] formatMessagesWithLevel:logLevel] dataUsingEncoding:NSUTF8StringEncoding] - mimeType:@"text/plain" - fileName:PearlString(@"%@-%@.log", - [[NSDateFormatter rfc3339DateFormatter] stringFromDate:[NSDate date]], - [PearlKeyChain deviceIdentifier])]; + mimeType:@"text/plain" + fileName:PearlString(@"%@-%@.log", + [[NSDateFormatter rfc3339DateFormatter] stringFromDate:[NSDate date]], + [PearlKeyChain deviceIdentifier])]; } [self presentModalViewController:composer animated:YES]; } - cancelTitle:nil otherTitles:@"Include Logs", @"No Logs", nil]; + cancelTitle:nil otherTitles:@"Include Logs", @"No Logs", nil]; } break; } @@ -552,7 +614,7 @@ [TestFlight passCheckpoint:MPCheckpointAction]; } - cancelTitle:[PearlStrings get].commonButtonCancel destructiveTitle:nil otherTitles: + cancelTitle:[PearlStrings get].commonButtonCancel destructiveTitle:nil otherTitles: [self isHelpVisible]? @"Hide Help": @"Show Help", @"FAQ", @"Tutorial", @"Preferences", @"Feedback", @"Sign Out", nil]; } @@ -597,14 +659,14 @@ self.activeElement.type = type; - if (type & MPElementTypeClassStored && ![[self.activeElement.content description] length]) - [self showToolTip:@"Tap to set a password." withIcon:self.toolTipEditIcon]; + [[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationElementUpdated + object:self.activeElement]; }]; } - (void)didSelectElement:(MPElementEntity *)element { - inf(@"Selected: %@, version: %@", element.name, element.version_); + inf(@"Selected: %@", element.name); [self closeAlert]; @@ -619,27 +681,31 @@ self.activeElement.name, self.activeElement.name)]; [[MPAppDelegate get] saveContext]; - if ([[MPiOSConfig get].firstRun boolValue]) + if (![[MPiOSConfig get].typeTipShown boolValue]) [UIView animateWithDuration:0.5f animations:^{ self.typeTipContainer.alpha = 1; } completion:^(BOOL finished) { if (finished) { - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - [UIView animateWithDuration:0.2f animations:^{ - self.typeTipContainer.alpha = 0; - }]; - }); + [MPiOSConfig get].typeTipShown = PearlBool(YES); + + dispatch_after( + dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [UIView animateWithDuration:0.2f animations:^{ + self.typeTipContainer.alpha = 0; + }]; + }); } }]; [self.searchDisplayController setActive:NO animated:YES]; self.searchDisplayController.searchBar.text = self.activeElement.name; - [[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationElementUsed object:self.activeElement]; + [[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationElementUpdated object:self.activeElement]; [TestFlight passCheckpoint:PearlString(MPCheckpointUseType @"_%@", NSStringFromMPElementType(self.activeElement.type))]; [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointUseType attributes:[NSDictionary dictionaryWithObjectsAndKeys: NSStringFromMPElementType( - self.activeElement.type), @"type", + self.activeElement.type), + @"type", nil]]; } diff --git a/MasterPassword/iOS/MPUnlockViewController.m b/MasterPassword/iOS/MPUnlockViewController.m index d7eaf403..2e5f7c3a 100644 --- a/MasterPassword/iOS/MPUnlockViewController.m +++ b/MasterPassword/iOS/MPUnlockViewController.m @@ -155,6 +155,9 @@ [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:animated? UIStatusBarAnimationSlide: UIStatusBarAnimationNone]; + if (!animated) + [[self findTargetedAvatar] setSelected:YES]; + [super viewDidAppear:animated]; } @@ -617,10 +620,9 @@ MPUserEntity *targetedUser = [self userForAvatar:[self findTargetedAvatar]]; if (!targetedUser) return; - + [PearlSheet showSheetWithTitle:targetedUser.name - message:nil - viewStyle:UIActionSheetStyleBlackTranslucent + message:nil viewStyle:UIActionSheetStyleBlackTranslucent tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) { if (buttonIndex == [sheet cancelButtonIndex]) return; @@ -629,8 +631,11 @@ [[MPAppDelegate get].managedObjectContext deleteObject:targetedUser]; [[MPAppDelegate get] saveContext]; [self updateUsers]; - } else if (buttonIndex == [sheet firstOtherButtonIndex]) - [[MPAppDelegate get] changeMasterPasswordFor:targetedUser]; + } else + if (buttonIndex == [sheet firstOtherButtonIndex]) { + [[MPAppDelegate get] changeMasterPasswordFor:targetedUser]; + [[self avatarForUser:targetedUser] setSelected:YES]; + } } cancelTitle:[PearlStrings get].commonButtonCancel destructiveTitle:@"Delete User" otherTitles:@"Reset Password", nil]; } @end diff --git a/MasterPassword/iOS/MPiOSConfig.h b/MasterPassword/iOS/MPiOSConfig.h index 38cf8f87..4bd4acab 100644 --- a/MasterPassword/iOS/MPiOSConfig.h +++ b/MasterPassword/iOS/MPiOSConfig.h @@ -13,6 +13,8 @@ @property (nonatomic, retain) NSNumber *sendInfo; @property (nonatomic, retain) NSNumber *helpHidden; @property (nonatomic, retain) NSNumber *showQuickStart; +@property (nonatomic, retain) NSNumber *actionsTipShown; +@property (nonatomic, retain) NSNumber *typeTipShown; + (MPiOSConfig *)get; diff --git a/MasterPassword/iOS/MPiOSConfig.m b/MasterPassword/iOS/MPiOSConfig.m index 41093dbc..2f1231c2 100644 --- a/MasterPassword/iOS/MPiOSConfig.m +++ b/MasterPassword/iOS/MPiOSConfig.m @@ -7,7 +7,7 @@ // @implementation MPiOSConfig -@dynamic sendInfo, helpHidden, showQuickStart; +@dynamic sendInfo, helpHidden, showQuickStart, actionsTipShown, typeTipShown; - (id)init { @@ -19,6 +19,8 @@ [NSNumber numberWithBool:NO], NSStringFromSelector(@selector(helpHidden)), [NSNumber numberWithBool:YES], NSStringFromSelector(@selector(showQuickStart)), @"510296984", NSStringFromSelector(@selector(iTunesID)), + PearlBoolNot(self.firstRun), NSStringFromSelector(@selector(actionsTipShown)), + PearlBoolNot(self.firstRun), NSStringFromSelector(@selector(typeTipShown)), nil]]; return self; diff --git a/MasterPassword/iOS/MainStoryboard_iPhone.storyboard b/MasterPassword/iOS/MainStoryboard_iPhone.storyboard index 34b2bf58..5bd32c92 100644 --- a/MasterPassword/iOS/MainStoryboard_iPhone.storyboard +++ b/MasterPassword/iOS/MainStoryboard_iPhone.storyboard @@ -480,7 +480,7 @@ Your passwords will be AES-encrypted with your master password. -