diff --git a/MasterPassword/C/mpw.c b/MasterPassword/C/mpw.c index d0ebbeee..d9023023 100644 --- a/MasterPassword/C/mpw.c +++ b/MasterPassword/C/mpw.c @@ -38,15 +38,20 @@ void usage() { fprintf(stderr, " -u name Specify the full name of the user.\n" " Defaults to %s in env.\n\n", MP_env_username); fprintf(stderr, " -t type Specify the password's template.\n" - " Defaults to %s in env or 'long'.\n" + " Defaults to %s in env or 'long' for password, 'name' for login.\n" " x, max, maximum | 20 characters, contains symbols.\n" " l, long | Copy-friendly, 14 characters, contains symbols.\n" " m, med, medium | Copy-friendly, 8 characters, contains symbols.\n" " b, basic | 8 characters, no symbols.\n" " s, short | Copy-friendly, 4 characters, no symbols.\n" - " p, pin | 4 numbers.\n\n", MP_env_sitetype); + " p, pin | 4 numbers.\n" + " n, name | 9 letter name.\n\n", MP_env_sitetype); fprintf(stderr, " -c counter The value of the counter.\n" " Defaults to %s in env or '1'.\n\n", MP_env_sitecounter); + fprintf(stderr, " -v variant The kind of content to generate.\n" + " Defaults to 'password'.\n" + " p, password | The password to log in with.\n" + " l, login | The username to log in as.\n\n"); exit(0); } @@ -86,12 +91,14 @@ int main(int argc, char *const argv[]) { const char *siteName = NULL; MPElementType siteType = MPElementTypeGeneratedLong; const char *siteTypeString = getenv( MP_env_sitetype ); + MPElementVariant siteVariant = MPElementVariantPassword; + const char *siteVariantString = NULL; uint32_t siteCounter = 1; const char *siteCounterString = getenv( MP_env_sitecounter ); // Read the options. char opt; - while ((opt = getopt(argc, argv, "u:t:c:h")) != -1) + while ((opt = getopt(argc, argv, "u:t:c:v:h")) != -1) switch (opt) { case 'h': usage(); @@ -102,6 +109,9 @@ int main(int argc, char *const argv[]) { case 't': siteTypeString = optarg; break; + case 'v': + siteVariantString = optarg; + break; case 'c': siteCounterString = optarg; break; @@ -144,6 +154,11 @@ int main(int argc, char *const argv[]) { return 1; } trc("siteCounter: %d\n", siteCounter); + if (siteVariantString) + siteVariant = VariantWithName( siteVariantString ); + trc("siteVariant: %d (%s)\n", siteVariant, siteVariantString); + if (siteVariant == MPElementVariantLogin) + siteType = MPElementTypeGeneratedName; if (siteTypeString) siteType = TypeWithName( siteTypeString ); trc("siteType: %d (%s)\n", siteType, siteTypeString); @@ -176,9 +191,10 @@ int main(int argc, char *const argv[]) { trc("masterPassword: %s\n", masterPassword); // Calculate the master key salt. - char *mpNameSpace = "com.lyndir.masterpassword"; + const char *mpKeyScope = ScopeForVariant(MPElementVariantPassword); + trc("key scope: %s\n", mpKeyScope); const uint32_t n_userNameLength = htonl(strlen(userName)); - size_t masterKeySaltLength = strlen(mpNameSpace) + sizeof(n_userNameLength) + strlen(userName); + size_t masterKeySaltLength = strlen(mpKeyScope) + sizeof(n_userNameLength) + strlen(userName); char *masterKeySalt = malloc( masterKeySaltLength ); if (!masterKeySalt) { fprintf(stderr, "Could not allocate master key salt: %d\n", errno); @@ -186,7 +202,7 @@ int main(int argc, char *const argv[]) { } char *mKS = masterKeySalt; - memcpy(mKS, mpNameSpace, strlen(mpNameSpace)); mKS += strlen(mpNameSpace); + memcpy(mKS, mpKeyScope, strlen(mpKeyScope)); mKS += strlen(mpKeyScope); memcpy(mKS, &n_userNameLength, sizeof(n_userNameLength)); mKS += sizeof(n_userNameLength); memcpy(mKS, userName, strlen(userName)); mKS += strlen(userName); if (mKS - masterKeySalt != masterKeySaltLength) @@ -210,9 +226,11 @@ int main(int argc, char *const argv[]) { trc("masterKey ID: %s\n", IDForBuf(masterKey, MP_dkLen)); // Calculate the site seed. + const char *mpSiteScope = ScopeForVariant(siteVariant); + trc("site scope: %s\n", mpSiteScope); const uint32_t n_siteNameLength = htonl(strlen(siteName)); const uint32_t n_siteCounter = htonl(siteCounter); - size_t sitePasswordInfoLength = strlen(mpNameSpace) + sizeof(n_siteNameLength) + strlen(siteName) + sizeof(n_siteCounter); + size_t sitePasswordInfoLength = strlen(mpSiteScope) + sizeof(n_siteNameLength) + strlen(siteName) + sizeof(n_siteCounter); char *sitePasswordInfo = malloc( sitePasswordInfoLength ); if (!sitePasswordInfo) { fprintf(stderr, "Could not allocate site seed: %d\n", errno); @@ -220,13 +238,13 @@ int main(int argc, char *const argv[]) { } char *sPI = sitePasswordInfo; - memcpy(sPI, mpNameSpace, strlen(mpNameSpace)); sPI += strlen(mpNameSpace); + memcpy(sPI, mpSiteScope, strlen(mpSiteScope)); sPI += strlen(mpSiteScope); memcpy(sPI, &n_siteNameLength, sizeof(n_siteNameLength)); sPI += sizeof(n_siteNameLength); memcpy(sPI, siteName, strlen(siteName)); sPI += strlen(siteName); memcpy(sPI, &n_siteCounter, sizeof(n_siteCounter)); sPI += sizeof(n_siteCounter); if (sPI - sitePasswordInfo != sitePasswordInfoLength) abort(); - trc("seed from: hmac-sha256(masterKey, 'com.lyndir.masterpassword' | %s | %s | %s)\n", Hex(&n_siteNameLength, sizeof(n_siteNameLength)), siteName, Hex(&n_siteCounter, sizeof(n_siteCounter))); + trc("seed from: hmac-sha256(masterKey, %s | %s | %s | %s)\n", mpSiteScope, Hex(&n_siteNameLength, sizeof(n_siteNameLength)), siteName, Hex(&n_siteCounter, sizeof(n_siteCounter))); trc("sitePasswordInfo ID: %s\n", IDForBuf(sitePasswordInfo, sitePasswordInfoLength)); uint8_t sitePasswordSeed[32]; diff --git a/MasterPassword/C/types.c b/MasterPassword/C/types.c index 2ec38089..22e636e8 100644 --- a/MasterPassword/C/types.c +++ b/MasterPassword/C/types.c @@ -33,6 +33,8 @@ const MPElementType TypeWithName(const char *typeName) { return MPElementTypeGeneratedShort; if (0 == strcmp(lowerTypeName, "p") || 0 == strcmp(lowerTypeName, "pin")) return MPElementTypeGeneratedPIN; + if (0 == strcmp(lowerTypeName, "n") || 0 == strcmp(lowerTypeName, "name")) + return MPElementTypeGeneratedName; fprintf(stderr, "Not a generated type name: %s", lowerTypeName); abort(); @@ -67,6 +69,9 @@ const char *CipherForType(MPElementType type, uint8_t seedByte) { case MPElementTypeGeneratedPIN: { return "nnnn"; } + case MPElementTypeGeneratedName: { + return "cvccvcvcv"; + } default: { fprintf(stderr, "Unknown generated type: %d", type); abort(); @@ -74,6 +79,36 @@ const char *CipherForType(MPElementType type, uint8_t seedByte) { } } +const MPElementVariant VariantWithName(const char *variantName) { + char lowerVariantName[strlen(variantName)]; + strcpy(lowerVariantName, variantName); + for (char *vN = lowerVariantName; *vN; ++vN) + *vN = tolower(*vN); + + if (0 == strcmp(lowerVariantName, "p") || 0 == strcmp(lowerVariantName, "password")) + return MPElementVariantPassword; + if (0 == strcmp(lowerVariantName, "l") || 0 == strcmp(lowerVariantName, "login")) + return MPElementVariantLogin; + + fprintf(stderr, "Not a variant name: %s", lowerVariantName); + abort(); +} + +const char *ScopeForVariant(MPElementVariant variant) { + switch (variant) { + case MPElementVariantPassword: { + return "com.lyndir.masterpassword"; + } + case MPElementVariantLogin: { + return "com.lyndir.masterpassword.login"; + } + default: { + fprintf(stderr, "Unknown variant: %d", variant); + abort(); + } + } +} + const char CharacterFromClass(char characterClass, uint8_t seedByte) { const char *classCharacters; switch (characterClass) { diff --git a/MasterPassword/C/types.h b/MasterPassword/C/types.h index d275e457..708ffce6 100644 --- a/MasterPassword/C/types.h +++ b/MasterPassword/C/types.h @@ -7,10 +7,11 @@ // typedef enum { - MPElementContentTypePassword, - MPElementContentTypeNote, - MPElementContentTypePicture, -} MPElementContentType; + /** Generate the password to log in with. */ + MPElementVariantPassword, + /** Generate the login name to log in as. */ + MPElementVariantLogin, +} MPElementVariant; typedef enum { /** Generate the password. */ @@ -33,6 +34,7 @@ typedef enum { MPElementTypeGeneratedBasic = 0x4 | MPElementTypeClassGenerated | 0x0, MPElementTypeGeneratedShort = 0x3 | MPElementTypeClassGenerated | 0x0, MPElementTypeGeneratedPIN = 0x5 | MPElementTypeClassGenerated | 0x0, + MPElementTypeGeneratedName = 0xF | MPElementTypeClassGenerated | 0x0, MPElementTypeStoredPersonal = 0x0 | MPElementTypeClassStored | MPElementFeatureExportContent, MPElementTypeStoredDevicePrivate = 0x1 | MPElementTypeClassStored | MPElementFeatureDevicePrivate, @@ -44,6 +46,8 @@ typedef enum { #define trc(...) do {} while (0) #endif +const MPElementVariant VariantWithName(const char *variantName); +const char *ScopeForVariant(MPElementVariant variant); const MPElementType TypeWithName(const char *typeName); const char *CipherForType(MPElementType type, uint8_t seedByte); const char CharacterFromClass(char characterClass, uint8_t seedByte); diff --git a/MasterPassword/ObjC/MPAlgorithm.h b/MasterPassword/ObjC/MPAlgorithm.h index 2c49452d..cc6d185f 100644 --- a/MasterPassword/ObjC/MPAlgorithm.h +++ b/MasterPassword/ObjC/MPAlgorithm.h @@ -1,12 +1,12 @@ /** - * Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com) - * - * See the enclosed file LICENSE for license information (LGPLv3). If you did - * not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt - * - * @author Maarten Billemont - * @license http://www.gnu.org/licenses/lgpl-3.0.txt - */ +* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com) +* +* See the enclosed file LICENSE for license information (LGPLv3). If you did +* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt +* +* @author Maarten Billemont +* @license http://www.gnu.org/licenses/lgpl-3.0.txt +*/ // // MPAlgorithm @@ -50,6 +50,7 @@ NSString *NSStringFromTimeToCrack(TimeToCrack timeToCrack); - (MPKey *)keyFromKeyData:(NSData *)keyData; - (NSData *)keyIDForKeyData:(NSData *)keyData; +- (NSString *)scopeForVariant:(MPElementVariant)variant; - (NSString *)nameOfType:(MPElementType)type; - (NSString *)shortNameOfType:(MPElementType)type; - (NSString *)classNameOfType:(MPElementType)type; @@ -59,19 +60,30 @@ NSString *NSStringFromTimeToCrack(TimeToCrack timeToCrack); - (MPElementType)nextType:(MPElementType)type; - (MPElementType)previousType:(MPElementType)type; -- (NSString *)generateContentNamed:(NSString *)name ofType:(MPElementType)type withCounter:(NSUInteger)counter usingKey:(MPKey *)key; -- (NSString *)storedContentForElement:(MPElementStoredEntity *)element usingKey:(MPKey *)key; +- (NSString *)generateLoginForSiteNamed:(NSString *)name usingKey:(MPKey *)key; +- (NSString *)generatePasswordForSiteNamed:(NSString *)name ofType:(MPElementType)type withCounter:(NSUInteger)counter + usingKey:(MPKey *)key; +- (NSString *)generateContentForSiteNamed:(NSString *)name ofType:(MPElementType)type withCounter:(NSUInteger)counter + variant:(MPElementVariant)variant usingKey:(MPKey *)key; -- (BOOL)saveContent:(NSString *)clearContent toElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey; -- (NSString *)resolveContentForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey; -- (void)resolveContentForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey - result:(void (^)(NSString *result))resultBlock; +- (NSString *)storedLoginForElement:(MPElementStoredEntity *)element usingKey:(MPKey *)key; +- (NSString *)storedPasswordForElement:(MPElementStoredEntity *)element usingKey:(MPKey *)key; -- (void)importProtectedContent:(NSString *)protectedContent protectedByKey:(MPKey *)importKey - intoElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey; -- (void)importClearTextContent:(NSString *)clearContent intoElement:(MPElementEntity *)element - usingKey:(MPKey *)elementKey; -- (NSString *)exportContentForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey; +- (BOOL)savePassword:(NSString *)clearPassword toElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey; + +- (NSString *)resolveLoginForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey; +- (NSString *)resolvePasswordForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey; + +- (void)resolveLoginForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey + result:(void ( ^ )(NSString *result))resultBlock; +- (void)resolvePasswordForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey + result:(void ( ^ )(NSString *result))resultBlock; + +- (void)importProtectedPassword:(NSString *)protectedPassword protectedByKey:(MPKey *)importKey + intoElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey; +- (void)importClearTextPassword:(NSString *)clearPassword intoElement:(MPElementEntity *)element + usingKey:(MPKey *)elementKey; +- (NSString *)exportPasswordForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey; - (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordOfType:(MPElementType)type byAttacker:(MPAttacker)attacker; - (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordString:(NSString *)password byAttacker:(MPAttacker)attacker; diff --git a/MasterPassword/ObjC/MPAlgorithmV0.m b/MasterPassword/ObjC/MPAlgorithmV0.m index 7b5d7e74..d7a2baee 100644 --- a/MasterPassword/ObjC/MPAlgorithmV0.m +++ b/MasterPassword/ObjC/MPAlgorithmV0.m @@ -117,11 +117,11 @@ [NSData dataWithBytes:&nuserNameLength length:sizeof( nuserNameLength )], [userName dataUsingEncoding:NSUTF8StringEncoding], - nil] N:MP_N r:MP_r p:MP_p]; + nil] N:MP_N r:MP_r p:MP_p]; MPKey *key = [self keyFromKeyData:keyData]; trc( @"User: %@, password: %@ derives to key ID: %@ (took %0.2fs)", userName, password, [key.keyID encodeHex], - -[start timeIntervalSinceNow] ); + -[start timeIntervalSinceNow] ); return key; } @@ -136,6 +136,18 @@ return [keyData hashWith:MP_hash]; } +- (NSString *)scopeForVariant:(MPElementVariant)variant { + + switch (variant) { + case MPElementVariantPassword: + return @"com.lyndir.masterpassword"; + case MPElementVariantLogin: + return @"com.lyndir.masterpassword.login"; + } + + Throw( @"Unsupported variant: %ld", (long)variant ); +} + - (NSString *)nameOfType:(MPElementType)type { if (!type) @@ -160,6 +172,9 @@ case MPElementTypeGeneratedPIN: return @"PIN"; + case MPElementTypeGeneratedName: + return @"Login Name"; + case MPElementTypeStoredPersonal: return @"Personal Password"; @@ -194,6 +209,9 @@ case MPElementTypeGeneratedPIN: return @"PIN"; + case MPElementTypeGeneratedName: + return @"Name"; + case MPElementTypeStoredPersonal: return @"Personal"; @@ -233,6 +251,9 @@ case MPElementTypeGeneratedPIN: return [MPElementGeneratedEntity class]; + case MPElementTypeGeneratedName: + return [MPElementGeneratedEntity class]; + case MPElementTypeStoredPersonal: return [MPElementStoredEntity class]; @@ -326,18 +347,35 @@ return [NSNullToNil( [NSNullToNil( [[self allCiphers] valueForKey:@"MPCharacterClasses"] ) valueForKey:cipherClass] ) copy]; } -- (NSString *)generateContentNamed:(NSString *)name ofType:(MPElementType)type withCounter:(NSUInteger)counter usingKey:(MPKey *)key { +- (NSString *)generateLoginForSiteNamed:(NSString *)name usingKey:(MPKey *)key { + + return [self generateContentForSiteNamed:name ofType:MPElementTypeGeneratedName withCounter:1 + variant:MPElementVariantLogin usingKey:key]; +} + +- (NSString *)generatePasswordForSiteNamed:(NSString *)name ofType:(MPElementType)type withCounter:(NSUInteger)counter + usingKey:(MPKey *)key { + + return [self generateContentForSiteNamed:name ofType:type withCounter:counter + variant:MPElementVariantPassword usingKey:key]; +} + +- (NSString *)generateContentForSiteNamed:(NSString *)name ofType:(MPElementType)type withCounter:(NSUInteger)counter + variant:(MPElementVariant)variant usingKey:(MPKey *)key { // Determine the seed whose bytes will be used for calculating a password uint32_t ncounter = htonl( counter ), nnameLength = htonl( name.length ); NSData *counterBytes = [NSData dataWithBytes:&ncounter length:sizeof( ncounter )]; NSData *nameLengthBytes = [NSData dataWithBytes:&nnameLength length:sizeof( nnameLength )]; - trc( @"seed from: hmac-sha256(%@, 'com.lyndir.masterpassword' | %@ | %@ | %@)", [key.keyData encodeBase64], - [nameLengthBytes encodeHex], name, [counterBytes encodeHex] ); + NSString *scope = [self scopeForVariant:variant];; + trc( @"seed from: hmac-sha256(%@, %@ | %@ | %@ | %@)", + [key.keyData encodeBase64], scope, [nameLengthBytes encodeHex], name, [counterBytes encodeHex] ); NSData *seed = [[NSData dataByConcatenatingDatas: - [@"com.lyndir.masterpassword" dataUsingEncoding:NSUTF8StringEncoding], - nameLengthBytes, [name dataUsingEncoding:NSUTF8StringEncoding], - counterBytes, nil] + [scope dataUsingEncoding:NSUTF8StringEncoding], + nameLengthBytes, + [name dataUsingEncoding:NSUTF8StringEncoding], + counterBytes, + nil] hmacWith:PearlHashSHA256 key:key.keyData]; trc( @"seed is: %@", [seed encodeBase64] ); const char *seedBytes = seed.bytes; @@ -364,12 +402,17 @@ return content; } -- (NSString *)storedContentForElement:(MPElementStoredEntity *)element usingKey:(MPKey *)key { +- (NSString *)storedLoginForElement:(MPElementStoredEntity *)element usingKey:(MPKey *)key { + + return nil; +} + +- (NSString *)storedPasswordForElement:(MPElementStoredEntity *)element usingKey:(MPKey *)key { return [self decryptContent:element.contentObject usingKey:key]; } -- (BOOL)saveContent:(NSString *)clearContent toElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey { +- (BOOL)savePassword:(NSString *)clearContent toElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey { NSAssert( [elementKey.keyID isEqualToData:element.user.keyID], @"Element does not belong to current user." ); switch (element.type) { @@ -378,7 +421,8 @@ case MPElementTypeGeneratedMedium: case MPElementTypeGeneratedBasic: case MPElementTypeGeneratedShort: - case MPElementTypeGeneratedPIN: { + case MPElementTypeGeneratedPIN: + case MPElementTypeGeneratedName: { NSAssert( NO, @"Cannot save content to element with generated type %lu.", (long)element.type ); return NO; } @@ -386,7 +430,7 @@ case MPElementTypeStoredPersonal: { if (![element isKindOfClass:[MPElementStoredEntity class]]) { wrn( @"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", - (long)element.type, [element class] ); + (long)element.type, [element class] ); return NO; } @@ -401,7 +445,7 @@ case MPElementTypeStoredDevicePrivate: { if (![element isKindOfClass:[MPElementStoredEntity class]]) { wrn( @"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", - (long)element.type, [element class] ); + (long)element.type, [element class] ); return NO; } @@ -412,7 +456,7 @@ [PearlKeyChain deleteItemForQuery:elementQuery]; else [PearlKeyChain addOrUpdateItemForQuery:elementQuery withAttributes:@{ - (__bridge id)kSecValueData : encryptedContent, + (__bridge id)kSecValueData : encryptedContent, #if TARGET_OS_IPHONE (__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleWhenUnlockedThisDeviceOnly, #endif @@ -425,12 +469,12 @@ Throw( @"Unsupported type: %ld", (long)element.type ); } -- (NSString *)resolveContentForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey { +- (NSString *)resolveLoginForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey { dispatch_group_t group = dispatch_group_create(); dispatch_group_enter( group ); __block NSString *result = nil; - [self resolveContentForElement:element usingKey:elementKey result:^(NSString *result_) { + [self resolveLoginForElement:element usingKey:elementKey result:^(NSString *result_) { result = result_; dispatch_group_leave( group ); }]; @@ -439,7 +483,43 @@ return result; } -- (void)resolveContentForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey result:(void ( ^ )(NSString *result))resultBlock { +- (NSString *)resolvePasswordForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey { + + dispatch_group_t group = dispatch_group_create(); + dispatch_group_enter( group ); + __block NSString *result = nil; + [self resolvePasswordForElement:element usingKey:elementKey result:^(NSString *result_) { + result = result_; + dispatch_group_leave( group ); + }]; + dispatch_group_wait( group, DISPATCH_TIME_FOREVER ); + + return result; +} + +- (void)resolveLoginForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey result:(void ( ^ )(NSString *result))resultBlock { + + NSAssert( [elementKey.keyID isEqualToData:element.user.keyID], @"Element does not belong to current user." ); + NSString *name = element.name; + BOOL loginGenerated = element.loginGenerated; + NSString *loginName = loginGenerated? nil: element.loginName; + id algorithm = nil; + if (!name.length) + err( @"Missing name." ); + else if (!elementKey.keyData.length) + err( @"Missing key." ); + else + algorithm = element.algorithm; + + dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{ + if (loginGenerated) + resultBlock( [algorithm generateLoginForSiteNamed:name usingKey:elementKey] ); + else + resultBlock( loginName ); + } ); +} + +- (void)resolvePasswordForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey result:(void ( ^ )(NSString *result))resultBlock { NSAssert( [elementKey.keyID isEqualToData:element.user.keyID], @"Element does not belong to current user." ); switch (element.type) { @@ -448,10 +528,11 @@ case MPElementTypeGeneratedMedium: case MPElementTypeGeneratedBasic: case MPElementTypeGeneratedShort: - case MPElementTypeGeneratedPIN: { + case MPElementTypeGeneratedPIN: + case MPElementTypeGeneratedName: { if (![element isKindOfClass:[MPElementGeneratedEntity class]]) { wrn( @"Element with generated type %lu is not an MPElementGeneratedEntity, but a %@.", - (long)element.type, [element class] ); + (long)element.type, [element class] ); break; } @@ -467,7 +548,7 @@ algorithm = element.algorithm; dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{ - NSString *result = [algorithm generateContentNamed:name ofType:type withCounter:counter usingKey:elementKey]; + NSString *result = [algorithm generatePasswordForSiteNamed:name ofType:type withCounter:counter usingKey:elementKey]; resultBlock( result ); } ); break; @@ -476,7 +557,7 @@ case MPElementTypeStoredPersonal: { if (![element isKindOfClass:[MPElementStoredEntity class]]) { wrn( @"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", - (long)element.type, [element class] ); + (long)element.type, [element class] ); break; } @@ -490,8 +571,8 @@ } case MPElementTypeStoredDevicePrivate: { NSAssert( [element isKindOfClass:[MPElementStoredEntity class]], - @"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", (long)element.type, - [element class] ); + @"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", (long)element.type, + [element class] ); NSDictionary *elementQuery = [self queryForDevicePrivateElementNamed:element.name]; NSData *encryptedContent = [PearlKeyChain dataOfItemForQuery:elementQuery]; @@ -505,8 +586,8 @@ } } -- (void)importProtectedContent:(NSString *)protectedContent protectedByKey:(MPKey *)importKey - intoElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey { +- (void)importProtectedPassword:(NSString *)protectedContent protectedByKey:(MPKey *)importKey + intoElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey { NSAssert( [elementKey.keyID isEqualToData:element.user.keyID], @"Element does not belong to current user." ); switch (element.type) { @@ -516,12 +597,13 @@ case MPElementTypeGeneratedBasic: case MPElementTypeGeneratedShort: case MPElementTypeGeneratedPIN: + case MPElementTypeGeneratedName: break; case MPElementTypeStoredPersonal: { if (![element isKindOfClass:[MPElementStoredEntity class]]) { wrn( @"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", - (long)element.type, [element class] ); + (long)element.type, [element class] ); break; } if ([importKey.keyID isEqualToData:elementKey.keyID]) @@ -529,7 +611,7 @@ else { NSString *clearContent = [self decryptContent:[protectedContent decodeBase64] usingKey:importKey]; - [self importClearTextContent:clearContent intoElement:element usingKey:elementKey]; + [self importClearTextPassword:clearContent intoElement:element usingKey:elementKey]; } break; } @@ -539,7 +621,7 @@ } } -- (void)importClearTextContent:(NSString *)clearContent intoElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey { +- (void)importClearTextPassword:(NSString *)clearContent intoElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey { NSAssert( [elementKey.keyID isEqualToData:element.user.keyID], @"Element does not belong to current user." ); switch (element.type) { @@ -549,10 +631,11 @@ case MPElementTypeGeneratedBasic: case MPElementTypeGeneratedShort: case MPElementTypeGeneratedPIN: + case MPElementTypeGeneratedName: break; case MPElementTypeStoredPersonal: { - [self saveContent:clearContent toElement:element usingKey:elementKey]; + [self savePassword:clearContent toElement:element usingKey:elementKey]; break; } @@ -561,7 +644,7 @@ } } -- (NSString *)exportContentForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey { +- (NSString *)exportPasswordForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey { NSAssert( [elementKey.keyID isEqualToData:element.user.keyID], @"Element does not belong to current user." ); if (!(element.type & MPElementFeatureExportContent)) @@ -574,7 +657,8 @@ case MPElementTypeGeneratedMedium: case MPElementTypeGeneratedBasic: case MPElementTypeGeneratedShort: - case MPElementTypeGeneratedPIN: { + case MPElementTypeGeneratedPIN: + case MPElementTypeGeneratedName: { result = nil; break; } @@ -582,7 +666,7 @@ case MPElementTypeStoredPersonal: { if (![element isKindOfClass:[MPElementStoredEntity class]]) { wrn( @"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", - (long)element.type, [element class] ); + (long)element.type, [element class] ); break; } result = [((MPElementStoredEntity *)element).contentObject encodeBase64]; diff --git a/MasterPassword/ObjC/MPAlgorithmV1.m b/MasterPassword/ObjC/MPAlgorithmV1.m index 6ef54659..2ae95c74 100644 --- a/MasterPassword/ObjC/MPAlgorithmV1.m +++ b/MasterPassword/ObjC/MPAlgorithmV1.m @@ -45,29 +45,31 @@ return YES; } -- (NSString *)generateContentNamed:(NSString *)name ofType:(MPElementType)type withCounter:(NSUInteger)counter usingKey:(MPKey *)key { +- (NSString *)generateContentForSiteNamed:(NSString *)name ofType:(MPElementType)type withCounter:(NSUInteger)counter + variant:(MPElementVariant)variant usingKey:(MPKey *)key { // Determine the seed whose bytes will be used for calculating a password uint32_t ncounter = htonl( counter ), nnameLength = htonl( name.length ); NSData *counterBytes = [NSData dataWithBytes:&ncounter length:sizeof( ncounter )]; NSData *nameLengthBytes = [NSData dataWithBytes:&nnameLength length:sizeof( nnameLength )]; - trc( @"seed from: hmac-sha256(%@, 'com.lyndir.masterpassword' | %@ | %@ | %@)", [key.keyData encodeBase64], [nameLengthBytes encodeHex], - name, [counterBytes encodeHex] ); + NSString *scope = [self scopeForVariant:variant]; + trc( @"seed from: hmac-sha256(%@, %@ | %@ | %@ | %@)", + [[key keyID] encodeHex], scope, [nameLengthBytes encodeHex], name, [counterBytes encodeHex] ); NSData *seed = [[NSData dataByConcatenatingDatas: - [@"com.lyndir.masterpassword" dataUsingEncoding:NSUTF8StringEncoding], + [scope dataUsingEncoding:NSUTF8StringEncoding], nameLengthBytes, [name dataUsingEncoding:NSUTF8StringEncoding], counterBytes, - nil] + nil] hmacWith:PearlHashSHA256 key:key.keyData]; - trc( @"seed is: %@", [seed encodeBase64] ); + trc( @"seed is: %@", [seed encodeHex] ); const unsigned char *seedBytes = seed.bytes; // Determine the cipher from the first seed byte. NSAssert( [seed length], @"Missing seed." ); NSArray *typeCiphers = [self ciphersForType:type]; NSString *cipher = typeCiphers[seedBytes[0] % [typeCiphers count]]; - trc( @"type %@, ciphers: %@, selected: %@", [self nameOfType:type], typeCiphers, cipher ); + trc( @"type %@ (%d), ciphers: %@, selected: %@", [self nameOfType:type], type, typeCiphers, cipher ); // Encode the content, character by character, using subsequent seed bytes and the cipher. NSAssert( [seed length] >= [cipher length] + 1, @"Insufficient seed bytes to encode cipher." ); diff --git a/MasterPassword/ObjC/MPAppDelegate_Key.m b/MasterPassword/ObjC/MPAppDelegate_Key.m index 5d86146c..4ca48c0c 100644 --- a/MasterPassword/ObjC/MPAppDelegate_Key.m +++ b/MasterPassword/ObjC/MPAppDelegate_Key.m @@ -173,7 +173,7 @@ static NSDictionary *keyQuery(MPUserEntity *user) { for (MPElementEntity *element in user.elements) { if (element.type & MPElementTypeClassStored) { NSString *content; - while (!(content = [element.algorithm storedContentForElement:(MPElementStoredEntity *)element usingKey:recoverKey])) { + while (!(content = [element.algorithm storedPasswordForElement:(MPElementStoredEntity *)element usingKey:recoverKey])) { // Failed to decrypt element with the current recoveryKey. Ask user for a new one to use. __block NSString *masterPassword = nil; @@ -210,7 +210,7 @@ static NSDictionary *keyQuery(MPUserEntity *user) { break; if (![recoverKey isEqualToKey:newKey]) - [element.algorithm saveContent:content toElement:element usingKey:newKey]; + [element.algorithm savePassword:content toElement:element usingKey:newKey]; } } diff --git a/MasterPassword/ObjC/MPAppDelegate_Store.h b/MasterPassword/ObjC/MPAppDelegate_Store.h index 96891b53..671a694a 100644 --- a/MasterPassword/ObjC/MPAppDelegate_Store.h +++ b/MasterPassword/ObjC/MPAppDelegate_Store.h @@ -8,7 +8,6 @@ #import "MPAppDelegate_Shared.h" -#import "UbiquityStoreManager.h" #import "MPFixable.h" typedef NS_ENUM( NSUInteger, MPImportResult ) { @@ -19,7 +18,7 @@ typedef NS_ENUM( NSUInteger, MPImportResult ) { MPImportResultInternalError, }; -@interface MPAppDelegate_Shared(Store) +@interface MPAppDelegate_Shared(Store) + (NSManagedObjectContext *)managedObjectContextForMainThreadIfReady; + (BOOL)managedObjectContextForMainThreadPerformBlock:(void (^)(NSManagedObjectContext *mainContext))mocBlock; @@ -27,7 +26,6 @@ typedef NS_ENUM( NSUInteger, MPImportResult ) { + (BOOL)managedObjectContextPerformBlock:(void (^)(NSManagedObjectContext *context))mocBlock; + (BOOL)managedObjectContextPerformBlockAndWait:(void (^)(NSManagedObjectContext *context))mocBlock; -- (UbiquityStoreManager *)storeManager; - (MPFixableResult)findAndFixInconsistenciesSaveInContext:(NSManagedObjectContext *)context; /** @param completion The block to execute after adding the element, executed from the main thread with the new element in the main MOC. */ diff --git a/MasterPassword/ObjC/MPAppDelegate_Store.m b/MasterPassword/ObjC/MPAppDelegate_Store.m index 42a50f97..24b0d96c 100644 --- a/MasterPassword/ObjC/MPAppDelegate_Store.m +++ b/MasterPassword/ObjC/MPAppDelegate_Store.m @@ -14,27 +14,21 @@ #define STORE_OPTIONS #endif -#define MPCloudContainerIdentifier @"HL3Q45LX9N.com.lyndir.lhunath.MasterPassword.shared" -#define MPMigrationLevelLocalStoreKey @"MPMigrationLevelLocalStoreKey" -#define MPMigrationLevelCloudStoreKey @"MPMigrationLevelCloudStoreKey" +#define MPStoreMigrationLevelKey @"MPMigrationLevelLocalStoreKey" -typedef NS_ENUM( NSInteger, MPMigrationLevelLocalStore ) { - MPMigrationLevelLocalStoreV1, - MPMigrationLevelLocalStoreV2, - MPMigrationLevelLocalStoreCurrent = MPMigrationLevelLocalStoreV2, -}; - -typedef NS_ENUM( NSInteger, MPMigrationLevelCloudStore ) { - MPMigrationLevelCloudStoreV1, - MPMigrationLevelCloudStoreV2, - MPMigrationLevelCloudStoreV3, - MPMigrationLevelCloudStoreCurrent = MPMigrationLevelCloudStoreV3, +typedef NS_ENUM( NSInteger, MPStoreMigrationLevel ) { + MPStoreMigrationLevelV1, + MPStoreMigrationLevelV2, + MPStoreMigrationLevelV3, + MPStoreMigrationLevelCurrent = MPStoreMigrationLevelV3, }; @implementation MPAppDelegate_Shared(Store) PearlAssociatedObjectProperty( id, SaveObserver, saveObserver ); +PearlAssociatedObjectProperty( NSPersistentStoreCoordinator*, PersistentStoreCoordinator, persistentStoreCoordinator ); + PearlAssociatedObjectProperty( NSManagedObjectContext*, PrivateManagedObjectContext, privateManagedObjectContext ); PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext, mainManagedObjectContext ); @@ -109,47 +103,103 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext - (NSManagedObjectContext *)mainManagedObjectContextIfReady { - [self storeManager]; + [self loadStore]; return self.mainManagedObjectContext; } - (NSManagedObjectContext *)privateManagedObjectContextIfReady { - [self storeManager]; + [self loadStore]; return self.privateManagedObjectContext; } -- (UbiquityStoreManager *)storeManager { +- (NSURL *)localStoreURL { - static UbiquityStoreManager *storeManager = nil; - if (storeManager) - return storeManager; + NSURL *applicationSupportURL = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory + inDomains:NSUserDomainMask] lastObject]; + return [[[applicationSupportURL + URLByAppendingPathComponent:[NSBundle mainBundle].bundleIdentifier isDirectory:YES] + URLByAppendingPathComponent:@"UbiquityStore" isDirectory:NO] + URLByAppendingPathExtension:@"sqlite"]; +} - storeManager = [[UbiquityStoreManager alloc] initStoreNamed:nil withManagedObjectModel:nil localStoreURL:nil - containerIdentifier:MPCloudContainerIdentifier - storeConfiguration:nil storeOptions:@{ STORE_OPTIONS } - delegate:self]; +- (void)loadStore { + + @synchronized (self) { + // Do nothing if already fully set up, otherwise (re-)load the store. + if (self.persistentStoreCoordinator && self.saveObserver && self.mainManagedObjectContext && self.privateManagedObjectContext) + return; + + // Unregister any existing observers and contexts. + if (self.saveObserver) + [[NSNotificationCenter defaultCenter] removeObserver:self.saveObserver]; + [self.mainManagedObjectContext performBlockAndWait:^{ + [self.mainManagedObjectContext reset]; + self.mainManagedObjectContext = nil; + }]; + [self.privateManagedObjectContext performBlockAndWait:^{ + [self.privateManagedObjectContext reset]; + self.privateManagedObjectContext = nil; + }]; + + // Check if migration is necessary. + [self migrateStore]; + + // Create a new store coordinator. + self.persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: + [NSManagedObjectModel mergedModelFromBundles:nil]]; + NSError *error = nil; + [self.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[self localStoreURL] + options:@{ + NSMigratePersistentStoresAutomaticallyOption : @YES, + NSInferMappingModelAutomaticallyOption : @YES, + STORE_OPTIONS + } error:&error]; + + // Create our contexts and observer. + self.privateManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; + [self.privateManagedObjectContext performBlockAndWait:^{ + self.privateManagedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy; + self.privateManagedObjectContext.persistentStoreCoordinator = self.persistentStoreCoordinator; + }]; + + self.mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; + self.mainManagedObjectContext.parentContext = self.privateManagedObjectContext; + + self.saveObserver = [[NSNotificationCenter defaultCenter] addObserverForName:NSManagedObjectContextDidSaveNotification + object:self.privateManagedObjectContext queue:nil usingBlock: + ^(NSNotification *note) { + // When privateManagedObjectContext is saved, import the changes into mainManagedObjectContext. + [self.mainManagedObjectContext performBlock:^{ + [self.mainManagedObjectContext mergeChangesFromContextDidSaveNotification:note]; + }]; + }]; #if TARGET_OS_IPHONE [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillTerminateNotification object:UIApp queue:[NSOperationQueue mainQueue] usingBlock: ^(NSNotification *note) { - [[self mainManagedObjectContext] saveToStore]; + [self.mainManagedObjectContext saveToStore]; }]; [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillResignActiveNotification object:UIApp queue:[NSOperationQueue mainQueue] usingBlock: ^(NSNotification *note) { - [[self mainManagedObjectContext] saveToStore]; + [self.mainManagedObjectContext saveToStore]; }]; #else [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationWillTerminateNotification object:NSApp queue:[NSOperationQueue mainQueue] usingBlock: - ^(NSNotification *note) { - [self.mainManagedObjectContextIfReady saveToStore]; - }]; + ^(NSNotification *note) { + [self.mainManagedObjectContext saveToStore]; + }]; #endif - return storeManager; + // Perform a data sanity check on the newly loaded store to find and fix any issues. + if ([[MPConfig get].checkInconsistency boolValue]) + [MPAppDelegate_Shared managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) { + [self findAndFixInconsistenciesSaveInContext:context]; + }]; + } } - (MPFixableResult)findAndFixInconsistenciesSaveInContext:(NSManagedObjectContext *)context { @@ -187,101 +237,25 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext return result; } -- (void)migrateStoreForManager:(UbiquityStoreManager *)manager isCloud:(BOOL)isCloudStore { +- (void)migrateStore { - [self migrateLocalStore]; - - if (isCloudStore) - [self migrateCloudStore]; -} - -- (void)migrateLocalStore { - - MPMigrationLevelLocalStore migrationLevel = (signed)[[NSUserDefaults standardUserDefaults] integerForKey:MPMigrationLevelLocalStoreKey]; - if (migrationLevel >= MPMigrationLevelLocalStoreCurrent) + MPStoreMigrationLevel migrationLevel = (signed)[[NSUserDefaults standardUserDefaults] integerForKey:MPStoreMigrationLevelKey]; + if (migrationLevel >= MPStoreMigrationLevelCurrent) // Local store up-to-date. return; - inf( @"Local store migration level: %d (current %d)", (signed)migrationLevel, (signed)MPMigrationLevelLocalStoreCurrent ); - if (migrationLevel <= MPMigrationLevelLocalStoreV1) if (![self migrateV1LocalStore]) { + inf( @"Local store migration level: %d (current %d)", (signed)migrationLevel, (signed)MPStoreMigrationLevelCurrent ); + if (migrationLevel == MPStoreMigrationLevelV1 && ![self migrateV1LocalStore]) { inf( @"Failed to migrate old V1 to new local store." ); return; } - - [[NSUserDefaults standardUserDefaults] setInteger:MPMigrationLevelLocalStoreCurrent forKey:MPMigrationLevelLocalStoreKey]; - inf( @"Successfully migrated old to new local store." ); -} - -- (void)migrateCloudStore { - - MPMigrationLevelCloudStore migrationLevel = (signed)[[NSUserDefaults standardUserDefaults] integerForKey:MPMigrationLevelCloudStoreKey]; - if (migrationLevel >= MPMigrationLevelCloudStoreCurrent) - // Cloud store up-to-date. + if (migrationLevel == MPStoreMigrationLevelV2 && ![self migrateV2LocalStore]) { + inf( @"Failed to migrate old V2 to new local store." ); return; - - inf( @"Cloud store migration level: %d (current %d)", (signed)migrationLevel, (signed)MPMigrationLevelCloudStoreCurrent ); - if (migrationLevel <= MPMigrationLevelCloudStoreV1) { - if (![self migrateV1CloudStore]) { - inf( @"Failed to migrate old V1 to new cloud store." ); - return; - } - } - else if (migrationLevel <= MPMigrationLevelCloudStoreV2) { - if (![self migrateV2CloudStore]) { - inf( @"Failed to migrate old V2 to new cloud store." ); - return; - } } - [[NSUserDefaults standardUserDefaults] setInteger:MPMigrationLevelCloudStoreCurrent forKey:MPMigrationLevelCloudStoreKey]; - inf( @"Successfully migrated old to new cloud store." ); -} - -- (BOOL)migrateV1CloudStore { - - // Migrate cloud enabled preference. - NSNumber *oldCloudEnabled = [[NSUserDefaults standardUserDefaults] objectForKey:@"iCloudEnabledKey"]; - if ([oldCloudEnabled boolValue]) - [[NSUserDefaults standardUserDefaults] setBool:YES forKey:USMCloudEnabledKey]; - - // Migrate cloud store. - NSString *uuid = [[NSUserDefaults standardUserDefaults] stringForKey:@"LocalUUIDKey"]; - if (!uuid) { - inf( @"No V1 cloud store to migrate." ); - return YES; - } - - inf( @"Migrating V1 cloud store: %@ -> %@", uuid, [self.storeManager valueForKey:@"storeUUID"] ); - NSURL *cloudContainerURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:MPCloudContainerIdentifier]; - NSURL *oldCloudContentURL = [[cloudContainerURL - URLByAppendingPathComponent:@"Data" isDirectory:YES] - URLByAppendingPathComponent:uuid isDirectory:YES]; - NSURL *oldCloudStoreURL = [[[cloudContainerURL - URLByAppendingPathComponent:@"Database.nosync" isDirectory:YES] - URLByAppendingPathComponent:uuid isDirectory:NO] URLByAppendingPathExtension:@"sqlite"]; - - return [self migrateFromCloudStore:oldCloudStoreURL cloudContent:oldCloudContentURL]; -} - -- (BOOL)migrateV2CloudStore { - - // Migrate cloud store. - NSString *uuid = [[NSUbiquitousKeyValueStore defaultStore] stringForKey:@"USMStoreUUIDKey"]; - if (!uuid) { - inf( @"No V2 cloud store to migrate." ); - return YES; - } - - inf( @"Migrating V2 cloud store: %@ -> %@", uuid, [self.storeManager valueForKey:@"storeUUID"] ); - NSURL *cloudContainerURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:MPCloudContainerIdentifier]; - NSURL *oldCloudContentURL = [[cloudContainerURL - URLByAppendingPathComponent:@"CloudLogs" isDirectory:YES] - URLByAppendingPathComponent:uuid isDirectory:YES]; - NSURL *oldCloudStoreURL = [[[cloudContainerURL - URLByAppendingPathComponent:@"CloudStore.nosync" isDirectory:YES] - URLByAppendingPathComponent:uuid isDirectory:NO] URLByAppendingPathExtension:@"sqlite"]; - - return [self migrateFromCloudStore:oldCloudStoreURL cloudContent:oldCloudContentURL]; + [[NSUserDefaults standardUserDefaults] setInteger:MPStoreMigrationLevelCurrent forKey:MPStoreMigrationLevelKey]; + inf( @"Successfully migrated old to new local store." ); } - (BOOL)migrateV1LocalStore { @@ -296,143 +270,60 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext } inf( @"Migrating V1 local store" ); - return [self migrateFromLocalStore:oldLocalStoreURL]; -} - -- (BOOL)migrateFromLocalStore:(NSURL *)oldLocalStoreURL { - - NSURL *newLocalStoreURL = [self.storeManager URLForLocalStore]; - if ([[NSFileManager defaultManager] fileExistsAtPath:newLocalStoreURL.path isDirectory:NULL]) { - wrn( @"Can't migrate local store: A new local store already exists." ); - return YES; + NSURL *newLocalStoreURL = [self localStoreURL]; + NSError *error = nil; + if (![[NSFileManager defaultManager] createDirectoryAtURL:[newLocalStoreURL URLByDeletingLastPathComponent] + withIntermediateDirectories:YES attributes:nil error:&error]) { + err( @"Couldn't create our application support directory: %@", [error fullDescription] ); + return NO; } - - if (![self.storeManager migrateStore:oldLocalStoreURL withOptions:nil - toStore:newLocalStoreURL withOptions:nil - strategy:0 error:nil cause:nil context:nil]) { - self.storeManager.localStoreURL = oldLocalStoreURL; + if (![[NSFileManager defaultManager] moveItemAtURL:oldLocalStoreURL toURL:newLocalStoreURL error:&error]) { + err( @"Couldn't move the old store to the new location: %@", [error fullDescription] ); return NO; } - inf( @"Successfully migrated to new local store." ); return YES; } -- (BOOL)migrateFromCloudStore:(NSURL *)oldCloudStoreURL cloudContent:(NSURL *)oldCloudContentURL { +- (BOOL)migrateV2LocalStore { - if (![self.storeManager cloudSafeForSeeding]) { - inf( @"Can't migrate cloud store: A new cloud store already exists." ); + NSURL *applicationSupportURL = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory + inDomains:NSUserDomainMask] lastObject]; + NSURL *oldLocalStoreURL; + // On iOS, each app is in a sandbox so we don't need to app-scope this directory. +#if TARGET_OS_IPHONE + oldLocalStoreURL = [[applicationSupportURL + URLByAppendingPathComponent:@"UbiquityStore" isDirectory:NO] + URLByAppendingPathExtension:@"sqlite"]; +#else + // The directory is shared between all apps on the system so we need to scope it for the running app. + oldLocalStoreURL = [[[applicationSupportURL + URLByAppendingPathComponent:[NSRunningApplication currentApplication].bundleIdentifier isDirectory:YES] + URLByAppendingPathComponent:@"UbiquityStore" isDirectory:NO] + URLByAppendingPathExtension:@"sqlite"]; +#endif + + if (![[NSFileManager defaultManager] fileExistsAtPath:oldLocalStoreURL.path isDirectory:NULL]) { + inf( @"No V2 local store to migrate." ); return YES; } - NSURL *newCloudStoreURL = [self.storeManager URLForCloudStore]; - if (![self.storeManager migrateStore:oldCloudStoreURL withOptions:nil - toStore:newCloudStoreURL withOptions:nil - strategy:0 error:nil cause:nil context:nil]) + inf( @"Migrating V2 local store" ); + NSURL *newLocalStoreURL = [self localStoreURL]; + NSError *error = nil; + if (![[NSFileManager defaultManager] createDirectoryAtURL:[newLocalStoreURL URLByDeletingLastPathComponent] + withIntermediateDirectories:YES attributes:nil error:&error]) { + err( @"Couldn't create our application support directory: %@", [error fullDescription] ); return NO; + } + if (![[NSFileManager defaultManager] moveItemAtURL:oldLocalStoreURL toURL:newLocalStoreURL error:&error]) { + err( @"Couldn't move the old store to the new location: %@", [error fullDescription] ); + return NO; + } - inf( @"Successfully migrated to new cloud store." ); return YES; } -#pragma mark - UbiquityStoreManagerDelegate - -- (NSManagedObjectContext *)ubiquityStoreManager:(UbiquityStoreManager *)manager - managedObjectContextForUbiquityChanges:(NSNotification *)note { - - return [self mainManagedObjectContextIfReady]; -} - -- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager log:(NSString *)message { - - inf( @"[StoreManager] %@", message ); -} - -- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager willLoadStoreIsCloud:(BOOL)isCloudStore { - - NSManagedObjectContext *moc = [self mainManagedObjectContextIfReady]; - [moc performBlockAndWait:^{ - [moc saveToStore]; - [moc reset]; - - if (self.saveObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:self.saveObserver]; - self.saveObserver = nil; - } - - self.privateManagedObjectContext = nil; - self.mainManagedObjectContext = nil; - }]; - - [self migrateStoreForManager:manager isCloud:isCloudStore]; -} - -- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didLoadStoreForCoordinator:(NSPersistentStoreCoordinator *)coordinator - isCloud:(BOOL)isCloudStore { - - inf( @"Using iCloud? %@", @(isCloudStore) ); - MPCheckpoint( MPCheckpointCloud, @{ - @"enabled" : @(isCloudStore) - } ); - - // Create our contexts. - NSManagedObjectContext *privateManagedObjectContext = - [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; - [privateManagedObjectContext performBlockAndWait:^{ - privateManagedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy; - privateManagedObjectContext.persistentStoreCoordinator = coordinator; - -// dbg(@"==="); -// NSError *error; -// for (NSEntityDescription *entityDescription in [coordinator.managedObjectModel entities]) { -// dbg(@"Entities: %@", entityDescription.name); -// NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:entityDescription.name]; -// NSArray *entities = [privateManagedObjectContext executeFetchRequest:request error:&error]; -// if (!entities) -// err(@" - Error: %@", error); -// else -// for (id entity in entities) -// dbg(@" - %@", [entity debugDescription]); -// } -// dbg(@"==="); - }]; - - NSManagedObjectContext *mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; - mainManagedObjectContext.parentContext = privateManagedObjectContext; - - if (self.saveObserver) - [[NSNotificationCenter defaultCenter] removeObserver:self.saveObserver]; - self.saveObserver = [[NSNotificationCenter defaultCenter] addObserverForName:NSManagedObjectContextDidSaveNotification - object:privateManagedObjectContext queue:nil usingBlock: - ^(NSNotification *note) { - // When privateManagedObjectContext is saved, import the changes into mainManagedObjectContext. - [mainManagedObjectContext performBlock:^{ - [mainManagedObjectContext mergeChangesFromContextDidSaveNotification:note]; - }]; - }]; - - self.privateManagedObjectContext = privateManagedObjectContext; - self.mainManagedObjectContext = mainManagedObjectContext; - - // Perform a data sanity check on the newly loaded store to find and fix any issues. - if ([[MPConfig get].checkInconsistency boolValue]) - [MPAppDelegate_Shared managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) { - [self findAndFixInconsistenciesSaveInContext:context]; - }]; -} - -- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didEncounterError:(NSError *)error cause:(UbiquityStoreErrorCause)cause - context:(id)context { - - err( @"[StoreManager] ERROR: cause=%@, context=%@, error=%@", NSStringFromUSMCause( cause ), context, error ); - MPCheckpoint( MPCheckpointMPErrorUbiquity, @{ - @"cause" : @(cause), - @"error.code" : @(error.code), - @"error.domain" : NilToNSNull( error.domain ), - @"error.reason" : NilToNSNull( [error localizedFailureReason]?: [error localizedDescription] ), - } ); -} - #pragma mark - Utilities - (void)addElementNamed:(NSString *)siteName completion:(void ( ^ )(MPElementEntity *element, NSManagedObjectContext *context))completion { @@ -735,7 +626,8 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext if (importAvatar != NSNotFound) user.avatar = importAvatar; dbg( @"Updating User: %@", [user debugDescription] ); - } else { + } + else { user = [MPUserEntity insertNewObjectInContext:context]; user.name = importUserName; user.keyID = importKeyID; @@ -767,9 +659,9 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext element.version = version; if ([exportContent length]) { if (clearText) - [element.algorithm importClearTextContent:exportContent intoElement:element usingKey:userKey]; + [element.algorithm importClearTextPassword:exportContent intoElement:element usingKey:userKey]; else - [element.algorithm importProtectedContent:exportContent protectedByKey:importKey intoElement:element usingKey:userKey]; + [element.algorithm importProtectedPassword:exportContent protectedByKey:importKey intoElement:element usingKey:userKey]; } if ([element isKindOfClass:[MPElementGeneratedEntity class]] && counter != NSNotFound) ((MPElementGeneratedEntity *)element).counter = counter; @@ -838,9 +730,9 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext // Determine the content to export. if (!(type & MPElementFeatureDevicePrivate)) { if (revealPasswords) - content = [element.algorithm resolveContentForElement:element usingKey:self.key]; + content = [element.algorithm resolvePasswordForElement:element usingKey:self.key]; else if (type & MPElementFeatureExportContent) - content = [element.algorithm exportContentForElement:element usingKey:self.key]; + content = [element.algorithm exportPasswordForElement:element usingKey:self.key]; } [export appendFormat:@"%@ %8ld %8s %25s\t%25s\t%@\n", diff --git a/MasterPassword/ObjC/MPElementEntity.h b/MasterPassword/ObjC/MPElementEntity.h index 510d14f6..bfc2b7c5 100644 --- a/MasterPassword/ObjC/MPElementEntity.h +++ b/MasterPassword/ObjC/MPElementEntity.h @@ -2,25 +2,26 @@ // MPElementEntity.h // MasterPassword-iOS // -// Created by Maarten Billemont on 2013-01-29. -// Copyright (c) 2013 Lyndir. All rights reserved. +// Created by Maarten Billemont on 2014-09-14. +// Copyright (c) 2014 Lyndir. All rights reserved. // #import #import -#import "MPFixable.h" @class MPUserEntity; -@interface MPElementEntity : NSManagedObject +@interface MPElementEntity : NSManagedObject -@property(nonatomic, retain) NSDate *lastUsed; -@property(nonatomic, retain) NSString *loginName; -@property(nonatomic, retain) NSString *name; -@property(nonatomic, retain) NSNumber *requiresExplicitMigration_; -@property(nonatomic, retain) NSNumber *type_; -@property(nonatomic, retain) NSNumber *uses_; -@property(nonatomic, retain) NSNumber *version_; -@property(nonatomic, retain) MPUserEntity *user; +//@property (nonatomic, retain) id content; // Hide here, reveal in MPElementStoredEntity +@property (nonatomic, retain) NSDate * lastUsed; +@property (nonatomic, retain) NSString * loginName; +@property (nonatomic, retain) NSString * name; +@property (nonatomic, retain) NSNumber * requiresExplicitMigration_; +@property (nonatomic, retain) NSNumber * type_; +@property (nonatomic, retain) NSNumber * uses_; +@property (nonatomic, retain) NSNumber * version_; +@property (nonatomic, retain) NSNumber * loginGenerated_; +@property (nonatomic, retain) MPUserEntity *user; @end diff --git a/MasterPassword/ObjC/MPElementEntity.m b/MasterPassword/ObjC/MPElementEntity.m index d90110a4..676a0c11 100644 --- a/MasterPassword/ObjC/MPElementEntity.m +++ b/MasterPassword/ObjC/MPElementEntity.m @@ -2,14 +2,17 @@ // MPElementEntity.m // MasterPassword-iOS // -// Created by Maarten Billemont on 2013-01-29. -// Copyright (c) 2013 Lyndir. All rights reserved. +// Created by Maarten Billemont on 2014-09-14. +// Copyright (c) 2014 Lyndir. All rights reserved. // #import "MPElementEntity.h" +#import "MPUserEntity.h" + @implementation MPElementEntity +//@dynamic content; @dynamic lastUsed; @dynamic loginName; @dynamic name; @@ -17,11 +20,7 @@ @dynamic type_; @dynamic uses_; @dynamic version_; +@dynamic loginGenerated_; @dynamic user; -- (MPFixableResult)findAndFixInconsistenciesInContext:(NSManagedObjectContext *)context { - - return MPFixableResultNoProblems; -} - @end diff --git a/MasterPassword/ObjC/MPElementGeneratedEntity.h b/MasterPassword/ObjC/MPElementGeneratedEntity.h index c6e5b05d..cb59d988 100644 --- a/MasterPassword/ObjC/MPElementGeneratedEntity.h +++ b/MasterPassword/ObjC/MPElementGeneratedEntity.h @@ -2,16 +2,17 @@ // MPElementGeneratedEntity.h // MasterPassword-iOS // -// Created by Maarten Billemont on 2013-01-29. -// Copyright (c) 2013 Lyndir. All rights reserved. +// Created by Maarten Billemont on 2014-09-14. +// Copyright (c) 2014 Lyndir. All rights reserved. // #import #import #import "MPElementEntity.h" + @interface MPElementGeneratedEntity : MPElementEntity -@property(nonatomic, retain) NSNumber *counter_; +@property (nonatomic, retain) NSNumber * counter_; @end diff --git a/MasterPassword/ObjC/MPElementGeneratedEntity.m b/MasterPassword/ObjC/MPElementGeneratedEntity.m index 6914a180..96fee719 100644 --- a/MasterPassword/ObjC/MPElementGeneratedEntity.m +++ b/MasterPassword/ObjC/MPElementGeneratedEntity.m @@ -2,54 +2,15 @@ // MPElementGeneratedEntity.m // MasterPassword-iOS // -// Created by Maarten Billemont on 2013-01-29. -// Copyright (c) 2013 Lyndir. All rights reserved. +// Created by Maarten Billemont on 2014-09-14. +// Copyright (c) 2014 Lyndir. All rights reserved. // #import "MPElementGeneratedEntity.h" -#import "MPAppDelegate_Shared.h" + @implementation MPElementGeneratedEntity @dynamic counter_; -- (MPFixableResult)findAndFixInconsistenciesInContext:(NSManagedObjectContext *)context { - - MPFixableResult result = [super findAndFixInconsistenciesInContext:context]; - - if (!self.type || self.type == (MPElementType)NSNotFound || ![[self.algorithm allTypes] containsObject:self.type_]) - // Invalid self.type - result = MPApplyFix( result, ^MPFixableResult { - wrn( @"Invalid type for: %@ of %@, type: %ld. Will use %ld instead.", - self.name, self.user.name, (long)self.type, (long)self.user.defaultType ); - self.type = self.user.defaultType; - return MPFixableResultProblemsFixed; - } ); - if (!self.type || self.type == (MPElementType)NSNotFound || ![[self.algorithm allTypes] containsObject:self.type_]) - // Invalid self.user.defaultType - result = MPApplyFix( result, ^MPFixableResult { - wrn( @"Invalid type for: %@ of %@, type: %ld. Will use %ld instead.", - self.name, self.user.name, (long)self.type, (long)MPElementTypeGeneratedLong ); - self.type = MPElementTypeGeneratedLong; - return MPFixableResultProblemsFixed; - } ); - if (![self isKindOfClass:[self.algorithm classOfType:self.type]]) - // Mismatch between self.type and self.class - result = MPApplyFix( result, ^MPFixableResult { - for (MPElementType newType = self.type; self.type != (newType = [self.algorithm nextType:newType]);) - if ([self isKindOfClass:[self.algorithm classOfType:newType]]) { - wrn( @"Mismatching type for: %@ of %@, type: %lu, class: %@. Will use %ld instead.", - self.name, self.user.name, (long)self.type, self.class, (long)newType ); - self.type = newType; - return MPFixableResultProblemsFixed; - } - - err( @"Mismatching type for: %@ of %@, type: %lu, class: %@. Couldn't find a type to fix problem with.", - self.name, self.user.name, (long)self.type, self.class ); - return MPFixableResultProblemsNotFixed; - } ); - - return result; -} - @end diff --git a/MasterPassword/ObjC/MPElementStoredEntity.h b/MasterPassword/ObjC/MPElementStoredEntity.h index 0d0a4600..9acb764e 100644 --- a/MasterPassword/ObjC/MPElementStoredEntity.h +++ b/MasterPassword/ObjC/MPElementStoredEntity.h @@ -2,16 +2,17 @@ // MPElementStoredEntity.h // MasterPassword-iOS // -// Created by Maarten Billemont on 2013-01-29. -// Copyright (c) 2013 Lyndir. All rights reserved. +// Created by Maarten Billemont on 2014-09-14. +// Copyright (c) 2014 Lyndir. All rights reserved. // #import #import #import "MPElementEntity.h" + @interface MPElementStoredEntity : MPElementEntity -@property(nonatomic, retain) NSData *contentObject; +@property (nonatomic, retain) NSData * contentObject; @end diff --git a/MasterPassword/ObjC/MPElementStoredEntity.m b/MasterPassword/ObjC/MPElementStoredEntity.m index af6ecd6c..a8ef2f62 100644 --- a/MasterPassword/ObjC/MPElementStoredEntity.m +++ b/MasterPassword/ObjC/MPElementStoredEntity.m @@ -2,36 +2,15 @@ // MPElementStoredEntity.m // MasterPassword-iOS // -// Created by Maarten Billemont on 2013-01-29. -// Copyright (c) 2013 Lyndir. All rights reserved. +// Created by Maarten Billemont on 2014-09-14. +// Copyright (c) 2014 Lyndir. All rights reserved. // #import "MPElementStoredEntity.h" -#import "MPEntities.h" -#import "MPAppDelegate_Shared.h" + @implementation MPElementStoredEntity @dynamic contentObject; -- (MPFixableResult)findAndFixInconsistenciesInContext:(NSManagedObjectContext *)context { - - MPFixableResult result = [super findAndFixInconsistenciesInContext:context]; - - if (self.contentObject && ![self.contentObject isKindOfClass:[NSData class]]) - result = MPApplyFix( result, ^MPFixableResult { - MPKey *key = [MPAppDelegate_Shared get].key; - if (key && [[MPAppDelegate_Shared get] activeUserInContext:context] == self.user) { - wrn( @"Content object not encrypted for: %@ of %@. Will re-encrypt.", self.name, self.user.name ); - [self.algorithm saveContent:[self.contentObject description] toElement:self usingKey:key]; - return MPFixableResultProblemsFixed; - } - - err( @"Content object not encrypted for: %@ of %@. Couldn't fix, please sign in.", self.name, self.user.name ); - return MPFixableResultProblemsNotFixed; - } ); - - return result; -} - @end diff --git a/MasterPassword/ObjC/MPEntities.h b/MasterPassword/ObjC/MPEntities.h index dc6d1351..a2c29449 100644 --- a/MasterPassword/ObjC/MPEntities.h +++ b/MasterPassword/ObjC/MPEntities.h @@ -12,6 +12,7 @@ #import "MPElementGeneratedEntity.h" #import "MPUserEntity.h" #import "MPAlgorithm.h" +#import "MPFixable.h" #define MPAvatarCount 19 @@ -21,8 +22,9 @@ @end -@interface MPElementEntity(MP) +@interface MPElementEntity(MP) +@property(assign) BOOL loginGenerated; @property(assign) MPElementType type; @property(readonly) NSString *typeName; @property(readonly) NSString *typeShortName; @@ -35,8 +37,10 @@ - (NSUInteger)use; - (BOOL)migrateExplicitly:(BOOL)explicit; -- (NSString *)resolveContentUsingKey:(MPKey *)key; -- (void)resolveContentUsingKey:(MPKey *)key result:(void (^)(NSString *))result; +- (NSString *)resolveLoginUsingKey:(MPKey *)key; +- (NSString *)resolvePasswordUsingKey:(MPKey *)key; +- (void)resolveLoginUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result; +- (void)resolvePasswordUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result; @end diff --git a/MasterPassword/ObjC/MPEntities.m b/MasterPassword/ObjC/MPEntities.m index 33ec96e4..be4b5379 100644 --- a/MasterPassword/ObjC/MPEntities.m +++ b/MasterPassword/ObjC/MPEntities.m @@ -36,11 +36,26 @@ @implementation MPElementEntity(MP) +- (MPFixableResult)findAndFixInconsistenciesInContext:(NSManagedObjectContext *)context { + + return MPFixableResultNoProblems; +} + - (MPElementType)type { return (MPElementType)[self.type_ unsignedIntegerValue]; } +- (void)setLoginGenerated:(BOOL)aLoginGenerated { + + self.loginGenerated_ = @(aLoginGenerated); +} + +- (BOOL)loginGenerated { + + return [self.loginGenerated_ boolValue]; +} + - (void)setType:(MPElementType)aType { self.type_ = @(aType); @@ -125,30 +140,79 @@ while (self.version < MPAlgorithmDefaultVersion) if ([MPAlgorithmForVersion( self.version + 1 ) migrateElement:self explicit:explicit]) inf( @"%@ migration to version: %ld succeeded for element: %@", - explicit? @"Explicit": @"Automatic", (long)self.version + 1, self ); + explicit? @"Explicit": @"Automatic", (long)self.version + 1, self ); else { wrn( @"%@ migration to version: %ld failed for element: %@", - explicit? @"Explicit": @"Automatic", (long)self.version + 1, self ); + explicit? @"Explicit": @"Automatic", (long)self.version + 1, self ); return NO; } return YES; } -- (NSString *)resolveContentUsingKey:(MPKey *)key { +- (NSString *)resolveLoginUsingKey:(MPKey *)key { - return [self.algorithm resolveContentForElement:self usingKey:key]; + return [self.algorithm resolveLoginForElement:self usingKey:key]; } -- (void)resolveContentUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result { +- (NSString *)resolvePasswordUsingKey:(MPKey *)key { - [self.algorithm resolveContentForElement:self usingKey:key result:result]; + return [self.algorithm resolvePasswordForElement:self usingKey:key]; +} + +- (void)resolveLoginUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result { + + [self.algorithm resolveLoginForElement:self usingKey:key result:result]; +} + +- (void)resolvePasswordUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result { + + [self.algorithm resolvePasswordForElement:self usingKey:key result:result]; } @end @implementation MPElementGeneratedEntity(MP) +- (MPFixableResult)findAndFixInconsistenciesInContext:(NSManagedObjectContext *)context { + + MPFixableResult result = [super findAndFixInconsistenciesInContext:context]; + + if (!self.type || self.type == (MPElementType)NSNotFound || ![[self.algorithm allTypes] containsObject:self.type_]) + // Invalid self.type + result = MPApplyFix( result, ^MPFixableResult { + wrn( @"Invalid type for: %@ of %@, type: %ld. Will use %ld instead.", + self.name, self.user.name, (long)self.type, (long)self.user.defaultType ); + self.type = self.user.defaultType; + return MPFixableResultProblemsFixed; + } ); + if (!self.type || self.type == (MPElementType)NSNotFound || ![[self.algorithm allTypes] containsObject:self.type_]) + // Invalid self.user.defaultType + result = MPApplyFix( result, ^MPFixableResult { + wrn( @"Invalid type for: %@ of %@, type: %ld. Will use %ld instead.", + self.name, self.user.name, (long)self.type, (long)MPElementTypeGeneratedLong ); + self.type = MPElementTypeGeneratedLong; + return MPFixableResultProblemsFixed; + } ); + if (![self isKindOfClass:[self.algorithm classOfType:self.type]]) + // Mismatch between self.type and self.class + result = MPApplyFix( result, ^MPFixableResult { + for (MPElementType newType = self.type; self.type != (newType = [self.algorithm nextType:newType]);) + if ([self isKindOfClass:[self.algorithm classOfType:newType]]) { + wrn( @"Mismatching type for: %@ of %@, type: %lu, class: %@. Will use %ld instead.", + self.name, self.user.name, (long)self.type, self.class, (long)newType ); + self.type = newType; + return MPFixableResultProblemsFixed; + } + + err( @"Mismatching type for: %@ of %@, type: %lu, class: %@. Couldn't find a type to fix problem with.", + self.name, self.user.name, (long)self.type, self.class ); + return MPFixableResultProblemsNotFixed; + } ); + + return result; +} + - (NSUInteger)counter { return [self.counter_ unsignedIntegerValue]; @@ -163,6 +227,26 @@ @implementation MPElementStoredEntity(MP) +- (MPFixableResult)findAndFixInconsistenciesInContext:(NSManagedObjectContext *)context { + + MPFixableResult result = [super findAndFixInconsistenciesInContext:context]; + + if (self.contentObject && ![self.contentObject isKindOfClass:[NSData class]]) + result = MPApplyFix( result, ^MPFixableResult { + MPKey *key = [MPAppDelegate_Shared get].key; + if (key && [[MPAppDelegate_Shared get] activeUserInContext:context] == self.user) { + wrn( @"Content object not encrypted for: %@ of %@. Will re-encrypt.", self.name, self.user.name ); + [self.algorithm savePassword:[self.contentObject description] toElement:self usingKey:key]; + return MPFixableResultProblemsFixed; + } + + err( @"Content object not encrypted for: %@ of %@. Couldn't fix, please sign in.", self.name, self.user.name ); + return MPFixableResultProblemsNotFixed; + } ); + + return result; +} + @end @implementation MPUserEntity(MP) diff --git a/MasterPassword/ObjC/MPTypes.h b/MasterPassword/ObjC/MPTypes.h index 13bbf2e6..b1d127fd 100644 --- a/MasterPassword/ObjC/MPTypes.h +++ b/MasterPassword/ObjC/MPTypes.h @@ -8,12 +8,6 @@ #import "MPKey.h" -typedef NS_ENUM(NSUInteger, MPElementContentType) { - MPElementContentTypePassword, - MPElementContentTypeNote, - MPElementContentTypePicture, -}; - typedef NS_ENUM(NSUInteger, MPElementTypeClass) { /** Generate the password. */ MPElementTypeClassGenerated = 1 << 4, @@ -21,6 +15,13 @@ typedef NS_ENUM(NSUInteger, MPElementTypeClass) { MPElementTypeClassStored = 1 << 5, }; +typedef NS_ENUM(NSUInteger, MPElementVariant) { + /** Generate the password. */ + MPElementVariantPassword, + /** Generate the login name. */ + MPElementVariantLogin, +}; + typedef NS_ENUM(NSUInteger, MPElementFeature) { /** Export the key-protected content data. */ MPElementFeatureExportContent = 1 << 10, @@ -35,6 +36,7 @@ typedef NS_ENUM(NSUInteger, MPElementType) { MPElementTypeGeneratedBasic = 0x4 | MPElementTypeClassGenerated | 0x0, MPElementTypeGeneratedShort = 0x3 | MPElementTypeClassGenerated | 0x0, MPElementTypeGeneratedPIN = 0x5 | MPElementTypeClassGenerated | 0x0, + MPElementTypeGeneratedName = 0xF | MPElementTypeClassGenerated | 0x0, MPElementTypeStoredPersonal = 0x0 | MPElementTypeClassStored | MPElementFeatureExportContent, MPElementTypeStoredDevicePrivate = 0x1 | MPElementTypeClassStored | MPElementFeatureDevicePrivate, diff --git a/MasterPassword/ObjC/MPUserEntity.h b/MasterPassword/ObjC/MPUserEntity.h index 5b29fa9b..3fbf4fc6 100644 --- a/MasterPassword/ObjC/MPUserEntity.h +++ b/MasterPassword/ObjC/MPUserEntity.h @@ -2,8 +2,8 @@ // MPUserEntity.h // MasterPassword-iOS // -// Created by Maarten Billemont on 2013-01-29. -// Copyright (c) 2013 Lyndir. All rights reserved. +// Created by Maarten Billemont on 2014-09-14. +// Copyright (c) 2014 Lyndir. All rights reserved. // #import @@ -13,16 +13,16 @@ @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) NSSet *elements; +@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) NSSet *elements; @end -@interface MPUserEntity(CoreDataGeneratedAccessors) +@interface MPUserEntity (CoreDataGeneratedAccessors) - (void)addElementsObject:(MPElementEntity *)value; - (void)removeElementsObject:(MPElementEntity *)value; diff --git a/MasterPassword/ObjC/MPUserEntity.m b/MasterPassword/ObjC/MPUserEntity.m index 3d1f1aa5..74d7d31b 100644 --- a/MasterPassword/ObjC/MPUserEntity.m +++ b/MasterPassword/ObjC/MPUserEntity.m @@ -2,11 +2,13 @@ // MPUserEntity.m // MasterPassword-iOS // -// Created by Maarten Billemont on 2013-01-29. -// Copyright (c) 2013 Lyndir. All rights reserved. +// Created by Maarten Billemont on 2014-09-14. +// Copyright (c) 2014 Lyndir. All rights reserved. // #import "MPUserEntity.h" +#import "MPElementEntity.h" + @implementation MPUserEntity diff --git a/MasterPassword/ObjC/Mac/MPElementModel.m b/MasterPassword/ObjC/Mac/MPElementModel.m index bfb2864a..41a5ddd1 100644 --- a/MasterPassword/ObjC/Mac/MPElementModel.m +++ b/MasterPassword/ObjC/Mac/MPElementModel.m @@ -117,7 +117,7 @@ re_anyChar = [NSRegularExpression regularExpressionWithPattern:@"." options:0 error:nil]; } ); - [entity resolveContentUsingKey:[MPAppDelegate_Shared get].key result:^(NSString *result) { + [entity resolvePasswordUsingKey:[MPAppDelegate_Shared get].key result:^(NSString *result) { NSString *displayResult = result; if ([[MPConfig get].hidePasswords boolValue] && !([NSEvent modifierFlags] & NSAlternateKeyMask)) displayResult = [displayResult stringByReplacingMatchesOfExpression:re_anyChar withTemplate:@"●"]; diff --git a/MasterPassword/ObjC/Mac/MPPasswordWindowController.m b/MasterPassword/ObjC/Mac/MPPasswordWindowController.m index 4a41a485..a344adcc 100644 --- a/MasterPassword/ObjC/Mac/MPPasswordWindowController.m +++ b/MasterPassword/ObjC/Mac/MPPasswordWindowController.m @@ -281,7 +281,7 @@ NSString *password = [(NSSecureTextField *)alert.accessoryView stringValue]; [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { MPElementEntity *entity = [self.selectedElement entityInContext:context]; - [entity.algorithm saveContent:password toElement:entity usingKey:[MPMacAppDelegate get].key]; + [entity.algorithm savePassword:password toElement:entity usingKey:[MPMacAppDelegate get].key]; [context saveToStore]; }]; break; @@ -403,8 +403,8 @@ MPElementType type = [types[t] unsignedIntegerValue]; NSString *title = [element.algorithm nameOfType:type]; if (type & MPElementTypeClassGenerated) - title = [element.algorithm generateContentNamed:element.siteName ofType:type - withCounter:element.counter usingKey:[MPMacAppDelegate get].key]; + title = [element.algorithm generatePasswordForSiteNamed:element.siteName ofType:type + withCounter:element.counter usingKey:[MPMacAppDelegate get].key]; NSButtonCell *cell = [self.passwordTypesMatrix cellAtRow:(NSInteger)t column:0]; cell.tag = type; diff --git a/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.pbxproj b/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.pbxproj index 34d4a1ce..1d218abc 100644 --- a/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.pbxproj +++ b/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.pbxproj @@ -30,6 +30,7 @@ DA2508F119511D3600AC23F1 /* MPPasswordWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA2508F019511D3600AC23F1 /* MPPasswordWindowController.xib */; }; DA2508F719513C1400AC23F1 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA16B343170661EE000A0EAB /* Cocoa.framework */; }; DA250925195148E200AC23F1 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAEBC45214F6364500987BF6 /* QuartzCore.framework */; }; + DA29992C19C6A89900AF7DF1 /* MasterPassword.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DA29992619C6A89900AF7DF1 /* MasterPassword.xcdatamodeld */; }; DA2CA4ED18D323D3007798F8 /* NSError+PearlFullDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = DA2CA4E718D323D3007798F8 /* NSError+PearlFullDescription.m */; }; DA2CA4EE18D323D3007798F8 /* NSError+PearlFullDescription.h in Headers */ = {isa = PBXBuildFile; fileRef = DA2CA4E818D323D3007798F8 /* NSError+PearlFullDescription.h */; }; DA2CA4EF18D323D3007798F8 /* NSArray+Pearl.m in Sources */ = {isa = PBXBuildFile; fileRef = DA2CA4E918D323D3007798F8 /* NSArray+Pearl.m */; }; @@ -71,7 +72,6 @@ DA5E5D0A1724A667003798D8 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DA5E5CC21724A667003798D8 /* InfoPlist.strings */; }; DA5E5D0B1724A667003798D8 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA5E5CC41724A667003798D8 /* MainMenu.xib */; }; DA5E5D0C1724A667003798D8 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CC61724A667003798D8 /* main.m */; }; - DA5E5D0D1724A667003798D8 /* MasterPassword.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CC71724A667003798D8 /* MasterPassword.xcdatamodeld */; }; DA60717C195D040500CA98B5 /* icon_gear.png in Resources */ = {isa = PBXBuildFile; fileRef = DA607092195D03E200CA98B5 /* icon_gear.png */; }; DA60717D195D040500CA98B5 /* icon_gear@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA607093195D03E200CA98B5 /* icon_gear@2x.png */; }; DA6558A419A99609009A0BEB /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DA6558A319A99609009A0BEB /* Images.xcassets */; }; @@ -257,6 +257,11 @@ DA25090719513C1400AC23F1 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; DA2509261951B86C00AC23F1 /* small-screen.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "small-screen.png"; sourceTree = ""; }; DA2509271951B86C00AC23F1 /* screen.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = screen.png; sourceTree = ""; }; + DA29992719C6A89900AF7DF1 /* MasterPassword 1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 1.xcdatamodel"; sourceTree = ""; }; + DA29992819C6A89900AF7DF1 /* MasterPassword 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 2.xcdatamodel"; sourceTree = ""; }; + DA29992919C6A89900AF7DF1 /* MasterPassword 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 3.xcdatamodel"; sourceTree = ""; }; + DA29992A19C6A89900AF7DF1 /* MasterPassword 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 4.xcdatamodel"; sourceTree = ""; }; + DA29992B19C6A89900AF7DF1 /* MasterPassword 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 5.xcdatamodel"; sourceTree = ""; }; DA2CA4E718D323D3007798F8 /* NSError+PearlFullDescription.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+PearlFullDescription.m"; sourceTree = ""; }; DA2CA4E818D323D3007798F8 /* NSError+PearlFullDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+PearlFullDescription.h"; sourceTree = ""; }; DA2CA4E918D323D3007798F8 /* NSArray+Pearl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Pearl.m"; sourceTree = ""; }; @@ -320,10 +325,6 @@ DA5E5CC31724A667003798D8 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; DA5E5CC51724A667003798D8 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = ""; }; DA5E5CC61724A667003798D8 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - DA5E5CC81724A667003798D8 /* MasterPassword 1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 1.xcdatamodel"; sourceTree = ""; }; - DA5E5CC91724A667003798D8 /* MasterPassword 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 2.xcdatamodel"; sourceTree = ""; }; - DA5E5CCA1724A667003798D8 /* MasterPassword 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 3.xcdatamodel"; sourceTree = ""; }; - DA5E5CCB1724A667003798D8 /* MasterPassword 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 4.xcdatamodel"; sourceTree = ""; }; DA606FEA195D03E200CA98B5 /* icon_action.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon_action.png; sourceTree = ""; }; DA606FEB195D03E200CA98B5 /* icon_action@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon_action@2x.png"; sourceTree = ""; }; DA606FEC195D03E200CA98B5 /* icon_addressbook-person.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon_addressbook-person.png"; sourceTree = ""; }; @@ -1024,7 +1025,7 @@ DA5E5CAF1724A667003798D8 /* MPTypes.h */, DA5E5CB01724A667003798D8 /* MPUserEntity.h */, DA5E5CB11724A667003798D8 /* MPUserEntity.m */, - DA5E5CC71724A667003798D8 /* MasterPassword.xcdatamodeld */, + DA29992619C6A89900AF7DF1 /* MasterPassword.xcdatamodeld */, ); name = ObjC; path = ..; @@ -2146,6 +2147,7 @@ DA5E5CFD1724A667003798D8 /* MPElementEntity.m in Sources */, DA5E5CFE1724A667003798D8 /* MPElementGeneratedEntity.m in Sources */, DA5E5CFF1724A667003798D8 /* MPElementStoredEntity.m in Sources */, + DA29992C19C6A89900AF7DF1 /* MasterPassword.xcdatamodeld in Sources */, DA3B8456190FC89700246EEA /* MPFixable.m in Sources */, DA5E5D001724A667003798D8 /* MPEntities.m in Sources */, DA5E5D011724A667003798D8 /* MPKey.m in Sources */, @@ -2153,7 +2155,6 @@ DA5E5D031724A667003798D8 /* MPMacAppDelegate.m in Sources */, DA5E5D041724A667003798D8 /* MPMacConfig.m in Sources */, DA5E5D0C1724A667003798D8 /* main.m in Sources */, - DA5E5D0D1724A667003798D8 /* MasterPassword.xcdatamodeld in Sources */, 93D39C5789EFA607CF788082 /* MPElementModel.m in Sources */, 93D39F833DEC1C89B2F795AC /* MPPasswordWindowController.m in Sources */, 93D390C676DF52DA7E459F19 /* MPPasswordWindow.m in Sources */, @@ -2782,15 +2783,16 @@ /* End XCConfigurationList section */ /* Begin XCVersionGroup section */ - DA5E5CC71724A667003798D8 /* MasterPassword.xcdatamodeld */ = { + DA29992619C6A89900AF7DF1 /* MasterPassword.xcdatamodeld */ = { isa = XCVersionGroup; children = ( - DA5E5CC81724A667003798D8 /* MasterPassword 1.xcdatamodel */, - DA5E5CC91724A667003798D8 /* MasterPassword 2.xcdatamodel */, - DA5E5CCA1724A667003798D8 /* MasterPassword 3.xcdatamodel */, - DA5E5CCB1724A667003798D8 /* MasterPassword 4.xcdatamodel */, + DA29992719C6A89900AF7DF1 /* MasterPassword 1.xcdatamodel */, + DA29992819C6A89900AF7DF1 /* MasterPassword 2.xcdatamodel */, + DA29992919C6A89900AF7DF1 /* MasterPassword 3.xcdatamodel */, + DA29992A19C6A89900AF7DF1 /* MasterPassword 4.xcdatamodel */, + DA29992B19C6A89900AF7DF1 /* MasterPassword 5.xcdatamodel */, ); - currentVersion = DA5E5CCB1724A667003798D8 /* MasterPassword 4.xcdatamodel */; + currentVersion = DA29992B19C6A89900AF7DF1 /* MasterPassword 5.xcdatamodel */; path = MasterPassword.xcdatamodeld; sourceTree = ""; versionGroupType = wrapper.xcdatamodel; diff --git a/MasterPassword/ObjC/MasterPassword.xcdatamodeld/.xccurrentversion b/MasterPassword/ObjC/MasterPassword.xcdatamodeld/.xccurrentversion index 000aa55d..016c71c5 100644 --- a/MasterPassword/ObjC/MasterPassword.xcdatamodeld/.xccurrentversion +++ b/MasterPassword/ObjC/MasterPassword.xcdatamodeld/.xccurrentversion @@ -3,6 +3,6 @@ _XCCurrentVersionName - MasterPassword 4.xcdatamodel + MasterPassword 5.xcdatamodel diff --git a/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 5.xcdatamodel/contents b/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 5.xcdatamodel/contents new file mode 100644 index 00000000..65e2add3 --- /dev/null +++ b/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 5.xcdatamodel/contents @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MasterPassword/ObjC/iOS/MPEmergencyViewController.m b/MasterPassword/ObjC/iOS/MPEmergencyViewController.m index 1f86f655..32c59ccc 100644 --- a/MasterPassword/ObjC/iOS/MPEmergencyViewController.m +++ b/MasterPassword/ObjC/iOS/MPEmergencyViewController.m @@ -136,7 +136,7 @@ [_emergencyPasswordQueue addOperationWithBlock:^{ NSString *sitePassword = nil; if (_key && [siteName length]) - sitePassword = [MPAlgorithmDefault generateContentNamed:siteName ofType:siteType withCounter:siteCounter usingKey:_key]; + sitePassword = [MPAlgorithmDefault generatePasswordForSiteNamed:siteName ofType:siteType withCounter:siteCounter usingKey:_key]; PearlMainQueue( ^{ [self.activity stopAnimating]; diff --git a/MasterPassword/ObjC/iOS/MPLogsViewController.h b/MasterPassword/ObjC/iOS/MPLogsViewController.h index 823c41e5..946de9ed 100644 --- a/MasterPassword/ObjC/iOS/MPLogsViewController.h +++ b/MasterPassword/ObjC/iOS/MPLogsViewController.h @@ -23,7 +23,6 @@ @property (weak, nonatomic) IBOutlet UITextView *logView; @property (weak, nonatomic) IBOutlet UISegmentedControl *levelControl; -- (IBAction)action:(id)sender; - (IBAction)toggleLevelControl:(UISegmentedControl *)sender; - (IBAction)refresh:(UIBarButtonItem *)sender; - (IBAction)mail:(UIBarButtonItem *)sender; diff --git a/MasterPassword/ObjC/iOS/MPLogsViewController.m b/MasterPassword/ObjC/iOS/MPLogsViewController.m index 8011a2b7..58473015 100644 --- a/MasterPassword/ObjC/iOS/MPLogsViewController.m +++ b/MasterPassword/ObjC/iOS/MPLogsViewController.m @@ -20,9 +20,7 @@ #import "MPiOSAppDelegate.h" #import "MPAppDelegate_Store.h" -@implementation MPLogsViewController { - PearlOverlay *_switchCloudStoreProgress; -} +@implementation MPLogsViewController - (void)viewDidLoad { @@ -48,139 +46,6 @@ self.levelControl.selectedSegmentIndex = [[MPiOSConfig get].traceMode boolValue]? 1: 0; } -- (IBAction)action:(id)sender { - - [PearlSheet showSheetWithTitle:@"Advanced Actions" viewStyle:UIActionSheetStyleAutomatic - initSheet:nil tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) { - if (buttonIndex == sheet.cancelButtonIndex) - return; - - if (buttonIndex == sheet.firstOtherButtonIndex) { - // Switch - [PearlAlert showAlertWithTitle:@"Switching iCloud Store" message: - @"WARNING: This is an advanced operation and should only be done if you're having trouble with iCloud." - viewStyle:UIAlertViewStyleDefault initAlert:nil - tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex_) { - if (buttonIndex_ == alert.cancelButtonIndex) - return; - - _switchCloudStoreProgress = [PearlOverlay showProgressOverlayWithTitle:@"Enumerating Stores"]; - dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0 ), ^{ - [self switchCloudStore]; - } ); - } cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonContinue, nil]; - } - - if (buttonIndex == sheet.firstOtherButtonIndex + 1) { - // Rebuild - [PearlAlert showAlertWithTitle:@"Rebuilding iCloud Store" message: - @"WARNING: This is an advanced operation and should only be done if you're having trouble with iCloud.\n" - @"Your local iCloud data will be removed and redownloaded from iCloud." - viewStyle:UIAlertViewStyleDefault initAlert:nil - tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex_) { - if (buttonIndex_ == alert.cancelButtonIndex) - return; - - [[MPiOSAppDelegate get].storeManager deleteCloudContainerLocalOnly:YES]; - } - cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonContinue, nil]; - } - - if (buttonIndex == sheet.firstOtherButtonIndex + 2) { - // Wipe - [PearlAlert showAlertWithTitle:@"Wiping iCloud Clean" message: - @"WARNING: This is an advanced operation and should only be done if you're having trouble with iCloud.\n" - @"All your iCloud data will be permanently lost. This is a clean slate!" - viewStyle:UIAlertViewStyleDefault initAlert:nil - tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex_) { - if (buttonIndex_ == alert.cancelButtonIndex) - return; - - [[MPiOSAppDelegate get].storeManager deleteCloudContainerLocalOnly:NO]; - } - cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonContinue, nil]; - } - } cancelTitle:[PearlStrings get].commonButtonCancel - destructiveTitle:nil otherTitles:@"Switch iCloud Store", @"Rebuild iCloud Container", @"Wipe iCloud Clean", nil]; -} - -- (void)switchCloudStore { - - NSDictionary *cloudStores = [[MPiOSAppDelegate get].storeManager enumerateCloudStores]; - if (!cloudStores) { - wrn( @"Failed enumerating cloud stores." ); - return; - } - - NSString *currentStoreUUID = nil; - NSMutableDictionary *stores = [NSMutableDictionary dictionary]; - NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil]; - NSPersistentStoreCoordinator *storePSC = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; - NSFetchRequest *usersFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )]; - NSFetchRequest *sitesFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )]; - for (NSURL *cloudStoreURL in cloudStores) { - NSString *storeUUID = [[cloudStoreURL URLByDeletingPathExtension] lastPathComponent]; - for (NSDictionary *cloudStoreOptions in cloudStores[cloudStoreURL]) { - NSError *error = nil; - NSPersistentStore *store = nil; - NSUInteger firstDash = [storeUUID rangeOfString:@"-" options:0].location; - NSString *storeDescription = strf( @"%@ v%@", - firstDash == NSNotFound? storeUUID: [storeUUID substringToIndex:firstDash], - cloudStoreOptions[USMCloudVersionKey] ); - if ([cloudStoreOptions[USMCloudCurrentKey] boolValue]) - currentStoreUUID = storeUUID; - @try { - if (!(store = [storePSC addPersistentStoreWithType:NSSQLiteStoreType configuration:nil - URL:cloudStoreURL options:cloudStoreOptions error:&error])) { - wrn(@"Couldn't describe store %@. While opening: %@", storeDescription, error); - continue; - } - - NSUInteger userCount, siteCount; - NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; - moc.persistentStoreCoordinator = storePSC; - if ((userCount = [moc countForFetchRequest:usersFetchRequest error:&error]) == NSNotFound) { - wrn(@"Couldn't describe store %@. While determining userCount: %@", storeDescription, error); - continue; - } - if ((siteCount = [moc countForFetchRequest:sitesFetchRequest error:&error]) == NSNotFound) { - wrn(@"Couldn't describe store %@. While determining siteCount: %@", storeDescription, error); - continue; - } - - storeDescription = strf( @"%@: %luU, %luS", storeDescription, (unsigned long)userCount, (unsigned long)siteCount ); - } - @catch (NSException *exception) { - wrn(@"Couldn't describe store %@: %@", storeDescription, exception); - } - @finally { - if (store && ![storePSC removePersistentStore:store error:&error]) { - wrn(@"Couldn't remove store %@: %@", storeDescription, error); - storePSC = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; - } - - stores[storeDescription] = cloudStoreOptions; - } - } - } - - PearlArrayTVC *vc = [[PearlArrayTVC alloc] initWithStyle:UITableViewStylePlain]; - NSUInteger firstDash = [currentStoreUUID rangeOfString:@"-" options:0].location; - vc.title = strf( @"Active: %@", firstDash == NSNotFound? currentStoreUUID: [currentStoreUUID substringToIndex:firstDash] ); - [stores enumerateKeysAndObjectsUsingBlock:^(id storeDescription, id cloudStoreOptions, BOOL *stop) { - [vc addRowWithName:storeDescription style:PearlArrayTVCRowStyleLink toggled:[cloudStoreOptions[USMCloudCurrentKey] boolValue] - toSection:@"Cloud Stores" activationBlock:^BOOL(BOOL wasToggled) { - [[MPiOSAppDelegate get].storeManager switchToCloudStoreWithOptions:cloudStoreOptions]; - [self.navigationController popToRootViewControllerAnimated:YES]; - return YES; - }]; - }]; - dispatch_async( dispatch_get_main_queue(), ^{ - [_switchCloudStoreProgress cancelOverlayAnimated:YES]; - [self.navigationController pushViewController:vc animated:YES]; - } ); -} - - (IBAction)toggleLevelControl:(UISegmentedControl *)sender { BOOL traceEnabled = (BOOL)self.levelControl.selectedSegmentIndex; diff --git a/MasterPassword/ObjC/iOS/MPPasswordCell.m b/MasterPassword/ObjC/iOS/MPPasswordCell.m index e8ea4ce2..cd6f4ac1 100644 --- a/MasterPassword/ObjC/iOS/MPPasswordCell.m +++ b/MasterPassword/ObjC/iOS/MPPasswordCell.m @@ -19,6 +19,7 @@ #import "MPPasswordCell.h" #import "MPiOSAppDelegate.h" #import "MPAppDelegate_Store.h" +#import "UIColor+Expanded.h" @interface MPPasswordCell() @@ -167,6 +168,11 @@ UICollectionView *collectionView = [UICollectionView findAsSuperviewOf:self]; [collectionView scrollToItemAtIndexPath:[collectionView indexPathForCell:self] atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:YES]; + if (textField == self.loginNameField) + [MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) { + if (![[self elementInContext:mainContext].loginName length]) + self.loginNameField.text = nil; + }]; } - (IBAction)textFieldDidChange:(UITextField *)textField { @@ -199,7 +205,7 @@ return; if (textField == self.passwordField) { - if ([element.algorithm saveContent:text toElement:element usingKey:[MPiOSAppDelegate get].key]) + if ([element.algorithm savePassword:text toElement:element usingKey:[MPiOSAppDelegate get].key]) [PearlOverlay showTemporaryOverlayWithTitle:@"Password Updated" dismissAfter:2]; } else if (textField == self.loginNameField && ![text isEqualToString:element.loginName]) { @@ -398,7 +404,12 @@ }]; [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { - [self copyLoginOfElement:[self elementInContext:context] saveInContext:context]; + MPElementEntity *element = [self elementInContext:context]; + if (![self copyLoginOfElement:element saveInContext:context]) { + element.loginGenerated = YES; + [context saveToStore]; + [self updateAnimated:YES]; + } PearlMainQueueAfter( .3f, ^{ [UIView animateWithDuration:.2f animations:^{ @@ -434,7 +445,9 @@ // UI self.upgradeButton.alpha = mainElement.requiresExplicitMigration? 1: 0; - self.loginNameContainer.alpha = [mainElement.loginName length] || self.mode == MPPasswordCellModeSettings? 1: 0; + self.loginNameContainer.alpha = self.mode == MPPasswordCellModeSettings || + mainElement.loginGenerated || [mainElement.loginName length]? 0.7f: 0; + self.loginNameField.textColor = [UIColor colorWithHexString:[mainElement.loginName length]? @"6D5E63": @"5E636D"]; self.modeButton.alpha = self.transientSite? 0: self.mode == MPPasswordCellModePassword? 0.1f: 0.5f; self.counterLabel.alpha = self.counterButton.alpha = mainElement.type & MPElementTypeClassGenerated? 0.5f: 0; self.modeButton.selected = self.mode == MPPasswordCellModeSettings; @@ -464,23 +477,27 @@ NSForegroundColorAttributeName : [UIColor whiteColor] } ); [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { - NSString *password; + MPElementEntity *element = [self elementInContext:context]; + MPKey *key = [MPiOSAppDelegate get].key; + NSString *password, *loginName = [element resolveLoginUsingKey:key]; if (self.transientSite) - password = [MPAlgorithmDefault generateContentNamed:self.transientSite ofType: + password = [MPAlgorithmDefault generatePasswordForSiteNamed:self.transientSite ofType: [[MPiOSAppDelegate get] activeUserInContext:context].defaultType?: MPElementTypeGeneratedLong - withCounter:1 usingKey:[MPiOSAppDelegate get].key]; - else - password = [[self elementInContext:context] resolveContentUsingKey:[MPiOSAppDelegate get].key]; + withCounter:1 usingKey:key]; + else { + password = [element resolvePasswordUsingKey:key]; + } TimeToCrack timeToCrack; NSString *timeToCrackString = nil; id algorithm = mainElement.algorithm?: MPAlgorithmDefault; MPAttacker attackHardware = [[MPConfig get].siteAttacker unsignedIntegerValue]; - if ([algorithm timeToCrack:&timeToCrack passwordOfType:[self elementInContext:context].type byAttacker:attackHardware] || + if ([algorithm timeToCrack:&timeToCrack passwordOfType:element.type byAttacker:attackHardware] || [algorithm timeToCrack:&timeToCrack passwordString:password byAttacker:attackHardware]) timeToCrackString = NSStringFromTimeToCrack( timeToCrack ); PearlMainQueue( ^{ + self.loginNameField.text = loginName; self.passwordField.text = password; self.strengthLabel.text = timeToCrackString; @@ -504,8 +521,7 @@ self.counterLabel.text = strf( @"%lu", (unsigned long)((MPElementGeneratedEntity *)mainElement).counter ); // Site Login Name - self.loginNameField.text = mainElement.loginName; - self.loginNameField.attributedPlaceholder = stra( strl( @"Set login name" ), @{ + self.loginNameField.attributedPlaceholder = stra( self.loginNameField.placeholder, @{ NSForegroundColorAttributeName : [UIColor whiteColor] } ); self.loginNameField.enabled = self.passwordField.enabled = // @@ -515,12 +531,12 @@ }]; } -- (void)copyContentOfElement:(MPElementEntity *)element saveInContext:(NSManagedObjectContext *)context { +- (BOOL)copyContentOfElement:(MPElementEntity *)element saveInContext:(NSManagedObjectContext *)context { inf( @"Copying password for: %@", element.name ); - NSString *password = [element resolveContentUsingKey:[MPAppDelegate_Shared get].key]; + NSString *password = [element resolvePasswordUsingKey:[MPAppDelegate_Shared get].key]; if (![password length]) - return; + return NO; PearlMainQueue( ^{ [PearlOverlay showTemporaryOverlayWithTitle:strl( @"Password Copied" ) dismissAfter:2]; @@ -529,14 +545,15 @@ [element use]; [context saveToStore]; + return YES; } -- (void)copyLoginOfElement:(MPElementEntity *)element saveInContext:(NSManagedObjectContext *)context { +- (BOOL)copyLoginOfElement:(MPElementEntity *)element saveInContext:(NSManagedObjectContext *)context { inf( @"Copying login for: %@", element.name ); - NSString *loginName = element.loginName; + NSString *loginName = [element.algorithm resolveLoginForElement:element usingKey:[MPiOSAppDelegate get].key]; if (![loginName length]) - return; + return NO; PearlMainQueue( ^{ [PearlOverlay showTemporaryOverlayWithTitle:strl( @"Login Name Copied" ) dismissAfter:2]; @@ -545,6 +562,7 @@ [element use]; [context saveToStore]; + return YES; } - (MPElementEntity *)elementInContext:(NSManagedObjectContext *)context { diff --git a/MasterPassword/ObjC/iOS/MPPasswordsViewController.m b/MasterPassword/ObjC/iOS/MPPasswordsViewController.m index 56653bbc..0473d8de 100644 --- a/MasterPassword/ObjC/iOS/MPPasswordsViewController.m +++ b/MasterPassword/ObjC/iOS/MPPasswordsViewController.m @@ -119,7 +119,7 @@ referenceSizeForHeaderInSection:(NSInteger)section { - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { - if (![MPiOSAppDelegate get].activeUserOID) + if (![MPiOSAppDelegate get].activeUserOID || !_fetchedResultsController) return 0; NSUInteger objects = ((id)self.fetchedResultsController.sections[section]).numberOfObjects; @@ -283,7 +283,7 @@ referenceSizeForHeaderInSection:(NSInteger)section { _fetchedResultsController = nil; self.passwordsSearchBar.text = nil; - [self updatePasswords]; + [self.passwordCollectionView reloadData]; }], [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidBecomeActiveNotification object:nil @@ -324,18 +324,19 @@ referenceSizeForHeaderInSection:(NSInteger)section { }]; if (!_storeChangingObserver) _storeChangingObserver = [[NSNotificationCenter defaultCenter] - addObserverForName:USMStoreWillChangeNotification object:nil - queue:nil usingBlock:^(NSNotification *note) { - Strongify( self ); - if (self->_mocObserver) - [[NSNotificationCenter defaultCenter] removeObserver:self->_mocObserver]; - }]; - if (!_storeChangedObserver) - _storeChangedObserver = [[NSNotificationCenter defaultCenter] - addObserverForName:USMStoreDidChangeNotification object:nil + addObserverForName:NSPersistentStoreCoordinatorStoresWillChangeNotification object:nil queue:nil usingBlock:^(NSNotification *note) { Strongify( self ); self->_fetchedResultsController = nil; + if (self->_mocObserver) + [[NSNotificationCenter defaultCenter] removeObserver:self->_mocObserver]; + [self.passwordCollectionView reloadData]; + }]; + if (!_storeChangedObserver) + _storeChangedObserver = [[NSNotificationCenter defaultCenter] + addObserverForName:NSPersistentStoreCoordinatorStoresDidChangeNotification object:nil + queue:nil usingBlock:^(NSNotification *note) { + Strongify( self ); [self updatePasswords]; }]; } @@ -418,6 +419,7 @@ referenceSizeForHeaderInSection:(NSInteger)section { - (NSFetchedResultsController *)fetchedResultsController { if (!_fetchedResultsController) { + _showTransientItem = NO; [MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) { NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )]; fetchRequest.sortDescriptors = @[ diff --git a/MasterPassword/ObjC/iOS/MPTypeViewController.m b/MasterPassword/ObjC/iOS/MPTypeViewController.m index 5f5a7fe2..c0bc3a20 100644 --- a/MasterPassword/ObjC/iOS/MPTypeViewController.m +++ b/MasterPassword/ObjC/iOS/MPTypeViewController.m @@ -80,8 +80,8 @@ counter = ((MPElementGeneratedEntity *)selectedElement).counter; dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0 ), ^{ - NSString *typeContent = [MPAlgorithmDefault generateContentNamed:name ofType:cellType - withCounter:counter usingKey:[MPiOSAppDelegate get].key]; + NSString *typeContent = [MPAlgorithmDefault generatePasswordForSiteNamed:name ofType:cellType + withCounter:counter usingKey:[MPiOSAppDelegate get].key]; dispatch_async( dispatch_get_main_queue(), ^{ [(UITextField *)[[tableView cellForRowAtIndexPath:indexPath] viewWithTag:2] setText:typeContent]; diff --git a/MasterPassword/ObjC/iOS/MPUsersViewController.m b/MasterPassword/ObjC/iOS/MPUsersViewController.m index 48ad9016..c81855ac 100644 --- a/MasterPassword/ObjC/iOS/MPUsersViewController.m +++ b/MasterPassword/ObjC/iOS/MPUsersViewController.m @@ -654,15 +654,16 @@ referenceSizeForFooterInSection:(NSInteger)section { }]; if (!_storeChangingObserver) _storeChangingObserver = [[NSNotificationCenter defaultCenter] - addObserverForName:USMStoreWillChangeNotification object:nil + addObserverForName:NSPersistentStoreCoordinatorStoresWillChangeNotification object:nil queue:nil usingBlock:^(NSNotification *note) { Strongify( self ); if (self->_mocObserver) [[NSNotificationCenter defaultCenter] removeObserver:self->_mocObserver]; + self.userIDs = nil; }]; if (!_storeChangedObserver) _storeChangedObserver = [[NSNotificationCenter defaultCenter] - addObserverForName:USMStoreDidChangeNotification object:nil + addObserverForName:NSPersistentStoreCoordinatorStoresDidChangeNotification object:nil queue:nil usingBlock:^(NSNotification *note) { Strongify( self ); [self reloadUsers]; @@ -683,7 +684,7 @@ referenceSizeForFooterInSection:(NSInteger)section { [self afterUpdatesMainQueue:^{ [self observeStore]; - [MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) { + if (![MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) { NSError *error = nil; NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )]; fetchRequest.sortDescriptors = @[ @@ -699,7 +700,8 @@ referenceSizeForFooterInSection:(NSInteger)section { for (MPUserEntity *user in users) [userIDs addObject:user.objectID]; self.userIDs = userIDs; - }]; + }]) + self.userIDs = nil; }]; } diff --git a/MasterPassword/ObjC/iOS/MPiOSAppDelegate.m b/MasterPassword/ObjC/iOS/MPiOSAppDelegate.m index c609b0c8..f5bfff0b 100644 --- a/MasterPassword/ObjC/iOS/MPiOSAppDelegate.m +++ b/MasterPassword/ObjC/iOS/MPiOSAppDelegate.m @@ -26,7 +26,7 @@ if ([self class] == [MPiOSAppDelegate class]) { [PearlLogger get].historyLevel = [[MPiOSConfig get].traceMode boolValue]? PearlLogLevelTrace: PearlLogLevelInfo; #ifdef DEBUG - [PearlLogger get].printLevel = PearlLogLevelDebug; //Trace; + [PearlLogger get].printLevel = PearlLogLevelTrace; #else [PearlLogger get].printLevel = [[MPiOSConfig get].traceMode boolValue]? PearlLogLevelDebug: PearlLogLevelInfo; #endif @@ -455,73 +455,6 @@ - (void)updateConfigKey:(NSString *)key { - // iCloud enabled / disabled - BOOL iCloudEnabled = [[MPiOSConfig get].iCloudEnabled boolValue]; - BOOL cloudEnabled = self.storeManager.cloudEnabled; - if (iCloudEnabled != cloudEnabled) { - if ([[MPiOSConfig get].iCloudEnabled boolValue]) - [self.storeManager setCloudEnabledAndOverwriteCloudWithLocalIfConfirmed:^(void (^setConfirmationAnswer)(BOOL answer)) { - __block NSUInteger siteCount = NSNotFound; - [MPAppDelegate_Shared managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) { - NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )]; - NSError *error = nil; - if ((siteCount = [context countForFetchRequest:fetchRequest error:&error]) == NSNotFound) { - wrn( @"Couldn't count current sites: %@", error ); - return; - } - }]; - - // If we currently have no sites, don't bother asking to copy them. - if (siteCount == 0) { - setConfirmationAnswer( NO ); - return; - } - - // The current store has sites, ask the user if he wants to copy them to the cloud - [PearlAlert showAlertWithTitle:@"Copy Sites To iCloud?" - message:@"You can either switch to your old iCloud sites " - @"or overwrite them with your current sites." - viewStyle:UIAlertViewStyleDefault initAlert:nil - tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) { - if (buttonIndex == [alert cancelButtonIndex]) - setConfirmationAnswer( NO ); - if (buttonIndex == [alert firstOtherButtonIndex]) - setConfirmationAnswer( YES ); - } - cancelTitle:@"Use Old" otherTitles:@"Overwrite", nil]; - }]; - else - [self.storeManager setCloudDisabledAndOverwriteLocalWithCloudIfConfirmed:^(void (^setConfirmationAnswer)(BOOL answer)) { - __block NSUInteger siteCount = NSNotFound; - [MPAppDelegate_Shared managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) { - NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )]; - NSError *error = nil; - if ((siteCount = [context countForFetchRequest:fetchRequest error:&error]) == NSNotFound) { - wrn( @"Couldn't count current sites: %@", error ); - return; - } - }]; - - // If we currently have no sites, don't bother asking to copy them. - if (siteCount == 0) { - setConfirmationAnswer( NO ); - return; - } - - [PearlAlert showAlertWithTitle:@"Copy iCloud Sites?" - message:@"You can either switch to the old sites on your device " - @"or overwrite them with your current iCloud sites." - viewStyle:UIAlertViewStyleDefault initAlert:nil - tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) { - if (buttonIndex == [alert cancelButtonIndex]) - setConfirmationAnswer( NO ); - if (buttonIndex == [alert firstOtherButtonIndex]) - setConfirmationAnswer( YES ); - } - cancelTitle:@"Use Old" otherTitles:@"Overwrite", nil]; - }]; - } - // Trace mode [PearlLogger get].historyLevel = [[MPiOSConfig get].traceMode boolValue]? PearlLogLevelTrace: PearlLogLevelInfo; @@ -532,21 +465,18 @@ #ifdef CRASHLYTICS [[Crashlytics sharedInstance] setBoolValue:[[MPConfig get].rememberLogin boolValue] forKey:@"rememberLogin"]; - [[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].iCloudEnabled boolValue] forKey:@"iCloudEnabled"]; [[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].sendInfo boolValue] forKey:@"sendInfo"]; [[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].helpHidden boolValue] forKey:@"helpHidden"]; [[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].showSetup boolValue] forKey:@"showQuickStart"]; [[Crashlytics sharedInstance] setBoolValue:[[PearlConfig get].firstRun boolValue] forKey:@"firstRun"]; [[Crashlytics sharedInstance] setIntValue:[[PearlConfig get].launchCount intValue] forKey:@"launchCount"]; [[Crashlytics sharedInstance] setBoolValue:[[PearlConfig get].askForReviews boolValue] forKey:@"askForReviews"]; - [[Crashlytics sharedInstance] - setIntValue:[[PearlConfig get].reviewAfterLaunches intValue] forKey:@"reviewAfterLaunches"]; + [[Crashlytics sharedInstance] setIntValue:[[PearlConfig get].reviewAfterLaunches intValue] forKey:@"reviewAfterLaunches"]; [[Crashlytics sharedInstance] setObjectValue:[PearlConfig get].reviewedVersion forKey:@"reviewedVersion"]; #endif MPCheckpoint( MPCheckpointConfig, @{ @"rememberLogin" : @([[MPConfig get].rememberLogin boolValue]), - @"iCloudEnabled" : @([[MPiOSConfig get].iCloudEnabled boolValue]), @"sendInfo" : @([[MPiOSConfig get].sendInfo boolValue]), @"helpHidden" : @([[MPiOSConfig get].helpHidden boolValue]), @"showQuickStart" : @([[MPiOSConfig get].showSetup boolValue]), @@ -559,123 +489,6 @@ } } -#pragma mark - UbiquityStoreManager - -- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager willLoadStoreIsCloud:(BOOL)isCloudStore { - - dispatch_async( dispatch_get_main_queue(), ^{ - [self signOutAnimated:YES]; - [self.handleCloudContentAlert cancelAlertAnimated:YES]; - if (!self.storeLoadingOverlay) - self.storeLoadingOverlay = [PearlOverlay showProgressOverlayWithTitle:@"Loading Sites"]; - } ); - - [super ubiquityStoreManager:manager willLoadStoreIsCloud:isCloudStore]; -} - -- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didLoadStoreForCoordinator:(NSPersistentStoreCoordinator *)coordinator - isCloud:(BOOL)isCloudStore { - - [MPiOSConfig get].iCloudEnabled = @(isCloudStore); - [super ubiquityStoreManager:manager didLoadStoreForCoordinator:coordinator isCloud:isCloudStore]; - - [self.handleCloudContentAlert cancelAlertAnimated:YES]; - [self.fixCloudContentAlert cancelAlertAnimated:YES]; - [self.storeLoadingOverlay cancelOverlayAnimated:YES]; - [self.handleCloudDisabledAlert cancelAlertAnimated:YES]; - - if (isCloudStore) - [PearlAlert showAlertWithTitle:@"iCloud Support Deprecated" message: - @"Master Password is moving away from iCloud due to limited platform support and reliability issues. " - @"\n\nMaster Password's generated passwords do not require syncing. " - @"Your sites will always have the same passwords on all your devices, even without iCloud. " - @"\n\niCloud continues to work for now but will be deactivated in a future update. " - @"Disable iCloud now to copy your iCloud sites to your device and avoid having to recreate them " - @"when iCloud becomes discontinued." - viewStyle:UIAlertViewStyleDefault initAlert:nil - tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) { - if (buttonIndex == [alert cancelButtonIndex]) - return; - - if (buttonIndex == [alert firstOtherButtonIndex]) - [UIApp openURL:[NSURL URLWithString: - @"http://support.lyndir.com/topic/486731-why-doesnt-the-mac-version-have-icloud-support/#comment-755394"]]; - - if (buttonIndex == [alert firstOtherButtonIndex] + 1) - [MPiOSConfig get].iCloudEnabled = @NO; - } - cancelTitle:@"Ignore For Now" otherTitles:@"Why?", @"Disable iCloud", nil]; -} - -- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager failedLoadingStoreWithCause:(UbiquityStoreErrorCause)cause context:(id)context - wasCloud:(BOOL)wasCloudStore { - - [self.storeLoadingOverlay cancelOverlayAnimated:YES]; - [self.handleCloudDisabledAlert cancelAlertAnimated:YES]; -} - -- (BOOL)ubiquityStoreManager:(UbiquityStoreManager *)manager handleCloudContentCorruptionWithHealthyStore:(BOOL)storeHealthy { - - if (manager.cloudEnabled && !storeHealthy && !(self.handleCloudContentAlert || self.fixCloudContentAlert)) { - [self.storeLoadingOverlay cancelOverlayAnimated:YES]; - [self.handleCloudDisabledAlert cancelAlertAnimated:YES]; - [self showCloudContentAlert]; - }; - - return NO; -} - -- (BOOL)ubiquityStoreManagerHandleCloudDisabled:(UbiquityStoreManager *)manager { - - if (!self.handleCloudDisabledAlert) - self.handleCloudDisabledAlert = [PearlAlert showAlertWithTitle:@"iCloud Login" message: - @"You haven't added an iCloud account to your device yet.\n" - @"To add one, go into Apple's Settings -> iCloud." - viewStyle:UIAlertViewStyleDefault initAlert:nil - tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) { - if (buttonIndex == alert.firstOtherButtonIndex) { - [MPiOSConfig get].iCloudEnabled = @NO; - return; - } - - [self.storeManager reloadStore]; - } cancelTitle:@"Try Again" otherTitles:@"Disable iCloud", nil]; - - return YES; -} - -- (void)showCloudContentAlert { - - __weak MPiOSAppDelegate *wSelf = self; - [self.handleCloudContentAlert cancelAlertAnimated:NO]; - // TODO: Add the activity indicator back. - self.handleCloudContentAlert = [PearlAlert showAlertWithTitle:@"iCloud Sync Problem" - message:@"Waiting for your other device to auto‑correct the problem..." - viewStyle:UIAlertViewStyleDefault initAlert:nil tappedButtonBlock: - ^(UIAlertView *alert, NSInteger buttonIndex) { - if (buttonIndex == [alert firstOtherButtonIndex]) - wSelf.fixCloudContentAlert = [PearlAlert showAlertWithTitle:@"Fix iCloud Now" message: - @"This problem can be auto‑corrected by opening the app on another device where you recently made changes.\n" - @"You can fix the problem from this device anyway, but recent changes from another device might get lost.\n\n" - @"You can also turn iCloud off for now." - viewStyle:UIAlertViewStyleDefault - initAlert:nil tappedButtonBlock: - ^(UIAlertView *alert_, NSInteger buttonIndex_) { - if (buttonIndex_ == alert_.cancelButtonIndex) - [wSelf showCloudContentAlert]; - if (buttonIndex_ == [alert_ firstOtherButtonIndex]) - [wSelf.storeManager rebuildCloudContentFromCloudStoreOrLocalStore:YES]; - if (buttonIndex_ == [alert_ firstOtherButtonIndex] + 1) - [MPiOSConfig get].iCloudEnabled = @NO; - } - cancelTitle:[PearlStrings get].commonButtonBack - otherTitles:@"Fix Anyway", - @"Turn Off", nil]; - if (buttonIndex == [alert firstOtherButtonIndex] + 1) - [MPiOSConfig get].iCloudEnabled = @NO; - } cancelTitle:nil otherTitles:@"Fix Now", @"Turn Off", nil]; -} - #pragma mark - Crashlytics - (NSDictionary *)crashlyticsInfo { diff --git a/MasterPassword/ObjC/iOS/MPiOSConfig.h b/MasterPassword/ObjC/iOS/MPiOSConfig.h index 434d30b7..f1063b08 100644 --- a/MasterPassword/ObjC/iOS/MPiOSConfig.h +++ b/MasterPassword/ObjC/iOS/MPiOSConfig.h @@ -17,7 +17,6 @@ @property(nonatomic, retain) NSNumber *typeTipShown; @property(nonatomic, retain) NSNumber *loginNameTipShown; @property(nonatomic, retain) NSNumber *traceMode; -@property(nonatomic, retain) NSNumber *iCloudEnabled; @property(nonatomic, retain) NSNumber *dictationSearch; @end diff --git a/MasterPassword/ObjC/iOS/MPiOSConfig.m b/MasterPassword/ObjC/iOS/MPiOSConfig.m index b31e3be9..05ae35fc 100644 --- a/MasterPassword/ObjC/iOS/MPiOSConfig.m +++ b/MasterPassword/ObjC/iOS/MPiOSConfig.m @@ -8,7 +8,7 @@ @implementation MPiOSConfig -@dynamic helpHidden, siteInfoHidden, showSetup, actionsTipShown, typeTipShown, loginNameTipShown, traceMode, iCloudEnabled, dictationSearch; +@dynamic helpHidden, siteInfoHidden, showSetup, actionsTipShown, typeTipShown, loginNameTipShown, traceMode, dictationSearch; - (id)init { @@ -24,7 +24,6 @@ NSStringFromSelector( @selector(typeTipShown) ) : @(!self.firstRun), NSStringFromSelector( @selector(loginNameTipShown) ) : @NO, NSStringFromSelector( @selector(traceMode) ) : @NO, - NSStringFromSelector( @selector(iCloudEnabled) ) : @NO, NSStringFromSelector( @selector(dictationSearch) ) : @NO }]; diff --git a/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj b/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj index 8e4444c6..5443fdb7 100644 --- a/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj +++ b/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj @@ -33,7 +33,6 @@ 93D399246DC90F50913A1287 /* UIResponder+PearlFirstResponder.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39A1DDFA09AE2E14D26DC /* UIResponder+PearlFirstResponder.m */; }; 93D3992FA1546E01F498F665 /* PearlNavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D398567FD02DB2647B8CF3 /* PearlNavigationController.h */; }; 93D399433EA75E50656040CB /* Twitter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93D394077F8FAB8167647187 /* Twitter.framework */; }; - 93D399BBC0A7EC746CB1B19B /* MPLogsViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D391943675426839501BB8 /* MPLogsViewController.h */; }; 93D39A53D76CA70786423458 /* UICollectionView+PearlReloadFromArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadFromArray.h */; }; 93D39A5FF670957C0AF8298D /* MPPasswordCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39DEA995041A13DC9CAF7 /* MPPasswordCell.m */; }; 93D39A8EA1C49CE43B63F47B /* PearlUICollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D8A953779B35403AF6E /* PearlUICollectionView.m */; }; @@ -108,8 +107,6 @@ DA3509FF15F101A500C14A8E /* PearlQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = DA3509FD15F101A500C14A8E /* PearlQueue.m */; }; DA38D6A318CCB5BF009AEB3E /* Storyboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DA38D6A218CCB5BF009AEB3E /* Storyboard.storyboard */; }; DA3BCFCB19BD09D5006B2681 /* SourceCodePro-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = DA3BCFCA19BD09D5006B2681 /* SourceCodePro-Regular.otf */; }; - DA4425CC1557BED40052177D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; }; - DA44260A1557D9E40052177D /* libUbiquityStoreManager.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA4425CB1557BED40052177D /* libUbiquityStoreManager.a */; }; DA4522441902355C008F650A /* icon_book.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD370C1711E29500CF925C /* icon_book.png */; }; DA4522451902355C008F650A /* icon_book@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD370D1711E29500CF925C /* icon_book@2x.png */; }; DA45224719062899008F650A /* icon_settings.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38141711E29600CF925C /* icon_settings.png */; }; @@ -234,12 +231,8 @@ DABD3C011711E2DC00CF925C /* MPAppDelegate_Shared.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BA91711E2DC00CF925C /* MPAppDelegate_Shared.m */; }; DABD3C021711E2DC00CF925C /* MPAppDelegate_Store.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BAB1711E2DC00CF925C /* MPAppDelegate_Store.m */; }; DABD3C031711E2DC00CF925C /* MPConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BAD1711E2DC00CF925C /* MPConfig.m */; }; - DABD3C041711E2DC00CF925C /* MPElementEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BAF1711E2DC00CF925C /* MPElementEntity.m */; }; - DABD3C051711E2DC00CF925C /* MPElementGeneratedEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BB11711E2DC00CF925C /* MPElementGeneratedEntity.m */; }; - DABD3C061711E2DC00CF925C /* MPElementStoredEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BB31711E2DC00CF925C /* MPElementStoredEntity.m */; }; DABD3C071711E2DC00CF925C /* MPEntities.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BB51711E2DC00CF925C /* MPEntities.m */; }; DABD3C081711E2DC00CF925C /* MPKey.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BB71711E2DC00CF925C /* MPKey.m */; }; - DABD3C091711E2DC00CF925C /* MPUserEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BBA1711E2DC00CF925C /* MPUserEntity.m */; }; DABD3C141711E2DC00CF925C /* MasterPassword.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BD01711E2DC00CF925C /* MasterPassword.xcdatamodeld */; }; DABD3C151711E2DC00CF925C /* MPiOSAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BD91711E2DC00CF925C /* MPiOSAppDelegate.m */; }; DABD3C1C1711E2DC00CF925C /* MPGuideViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BE71711E2DC00CF925C /* MPGuideViewController.m */; }; @@ -260,10 +253,6 @@ DAC77CAE148291A600BCF976 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; }; DAC8DF47192831E100BA7D71 /* icon_key.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD379A1711E29600CF925C /* icon_key.png */; }; DAC8DF48192831E100BA7D71 /* icon_key@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD379B1711E29600CF925C /* icon_key@2x.png */; }; - DACA22BB1705DE7D002C6C22 /* UbiquityStoreManager.m in Sources */ = {isa = PBXBuildFile; fileRef = DACA22B71705DE7D002C6C22 /* UbiquityStoreManager.m */; }; - DACA22BC1705DE7D002C6C22 /* NSError+UbiquityStoreManager.h in Headers */ = {isa = PBXBuildFile; fileRef = DACA22B81705DE7D002C6C22 /* NSError+UbiquityStoreManager.h */; }; - DACA22BD1705DE7D002C6C22 /* NSError+UbiquityStoreManager.m in Sources */ = {isa = PBXBuildFile; fileRef = DACA22B91705DE7D002C6C22 /* NSError+UbiquityStoreManager.m */; }; - DACA22BE1705DE7D002C6C22 /* UbiquityStoreManager.h in Headers */ = {isa = PBXBuildFile; fileRef = DACA22BA1705DE7D002C6C22 /* UbiquityStoreManager.h */; }; DACA296F1705DF81002C6C22 /* Crashlytics.plist in Resources */ = {isa = PBXBuildFile; fileRef = DACA269A1705DF81002C6C22 /* Crashlytics.plist */; }; DACA29731705E1A8002C6C22 /* ciphers.plist in Resources */ = {isa = PBXBuildFile; fileRef = DACA29711705E1A8002C6C22 /* ciphers.plist */; }; DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */ = {isa = PBXBuildFile; fileRef = DACA29721705E1A8002C6C22 /* dictionary.lst */; }; @@ -279,6 +268,10 @@ DACE2F6D19BA6A2A0010F92E /* PearlMutableStaticTableViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = DACE2F6919BA6A2A0010F92E /* PearlMutableStaticTableViewController.h */; }; DACE2F6E19BA6A2A0010F92E /* UIView+FontScale.h in Headers */ = {isa = PBXBuildFile; fileRef = DACE2F6A19BA6A2A0010F92E /* UIView+FontScale.h */; }; DAD312C21552A22700A3F9ED /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = DAD312C01552A20800A3F9ED /* libsqlite3.dylib */; }; + DADB4EC719C66FB60065A78D /* MPUserEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DADB4EC619C66FB60065A78D /* MPUserEntity.m */; }; + DADB4ECA19C66FB60065A78D /* MPElementEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DADB4EC919C66FB60065A78D /* MPElementEntity.m */; }; + DADB4ECD19C66FB60065A78D /* MPElementGeneratedEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DADB4ECC19C66FB60065A78D /* MPElementGeneratedEntity.m */; }; + DADB4ED019C66FB70065A78D /* MPElementStoredEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DADB4ECF19C66FB70065A78D /* MPElementStoredEntity.m */; }; DAE1EF2217E942DE00BC0086 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DAE1EF2417E942DE00BC0086 /* Localizable.strings */; }; DAEBC45314F6364500987BF6 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAEBC45214F6364500987BF6 /* QuartzCore.framework */; }; DAEC85B518E3DD9A007FC0DF /* UIView+Touches.m in Sources */ = {isa = PBXBuildFile; fileRef = DAEC85B118E3DD9A007FC0DF /* UIView+Touches.m */; }; @@ -496,7 +489,6 @@ DA38D6A218CCB5BF009AEB3E /* Storyboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Storyboard.storyboard; sourceTree = ""; }; DA3B844D190FC5DF00246EEA /* Crashlytics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Crashlytics.framework; path = ../../../External/iOS/Crashlytics.framework; sourceTree = ""; }; DA3BCFCA19BD09D5006B2681 /* SourceCodePro-Regular.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SourceCodePro-Regular.otf"; sourceTree = ""; }; - DA4425CB1557BED40052177D /* libUbiquityStoreManager.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libUbiquityStoreManager.a; sourceTree = BUILT_PRODUCTS_DIR; }; DA5A09DD171A70E4005284AB /* play.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = play.png; sourceTree = ""; }; DA5A09DE171A70E4005284AB /* play@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "play@2x.png"; sourceTree = ""; }; DA5A09E8171BB0F7005284AB /* unlocked.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = unlocked.png; sourceTree = ""; }; @@ -507,6 +499,7 @@ DA5BFA4C147E415C00F98B1E /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; DA5BFA4E147E415C00F98B1E /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; DA5E5C3C1723681B003798D8 /* Square-bottom.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Square-bottom.png"; path = "Dividers/Square-bottom.png"; sourceTree = ""; }; + DA62140919C66A9700375240 /* MasterPassword 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 5.xcdatamodel"; sourceTree = ""; }; DA6701B716406A4100B61001 /* Accounts.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accounts.framework; path = System/Library/Frameworks/Accounts.framework; sourceTree = SDKROOT; }; DA6701DD16406B7300B61001 /* Social.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Social.framework; path = System/Library/Frameworks/Social.framework; sourceTree = SDKROOT; }; DA6701DF16406BB400B61001 /* AdSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AdSupport.framework; path = System/Library/Frameworks/AdSupport.framework; sourceTree = SDKROOT; }; @@ -1208,10 +1201,6 @@ DAC632871486D95D0075AEA5 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; DAC77CAD148291A600BCF976 /* libPearl.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPearl.a; sourceTree = BUILT_PRODUCTS_DIR; }; DAC77CB1148291A600BCF976 /* Pearl-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "Pearl-Prefix.pch"; path = "../../MasterPassword/ObjC/Pearl/Pearl-Prefix.pch"; sourceTree = ""; }; - DACA22B71705DE7D002C6C22 /* UbiquityStoreManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UbiquityStoreManager.m; sourceTree = ""; }; - DACA22B81705DE7D002C6C22 /* NSError+UbiquityStoreManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+UbiquityStoreManager.h"; sourceTree = ""; }; - DACA22B91705DE7D002C6C22 /* NSError+UbiquityStoreManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+UbiquityStoreManager.m"; sourceTree = ""; }; - DACA22BA1705DE7D002C6C22 /* UbiquityStoreManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UbiquityStoreManager.h; sourceTree = ""; }; DACA269A1705DF81002C6C22 /* Crashlytics.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Crashlytics.plist; sourceTree = ""; }; DACA29711705E1A8002C6C22 /* ciphers.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = ciphers.plist; sourceTree = ""; }; DACA29721705E1A8002C6C22 /* dictionary.lst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dictionary.lst; sourceTree = ""; }; @@ -1228,6 +1217,14 @@ DACE2F6919BA6A2A0010F92E /* PearlMutableStaticTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlMutableStaticTableViewController.h; sourceTree = ""; }; DACE2F6A19BA6A2A0010F92E /* UIView+FontScale.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+FontScale.h"; sourceTree = ""; }; DAD312C01552A20800A3F9ED /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; }; + DADB4EC519C66FB50065A78D /* MPUserEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUserEntity.h; sourceTree = ""; }; + DADB4EC619C66FB60065A78D /* MPUserEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUserEntity.m; sourceTree = ""; }; + DADB4EC819C66FB60065A78D /* MPElementEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementEntity.h; sourceTree = ""; }; + DADB4EC919C66FB60065A78D /* MPElementEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementEntity.m; sourceTree = ""; }; + DADB4ECB19C66FB60065A78D /* MPElementGeneratedEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementGeneratedEntity.h; sourceTree = ""; }; + DADB4ECC19C66FB60065A78D /* MPElementGeneratedEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementGeneratedEntity.m; sourceTree = ""; }; + DADB4ECE19C66FB60065A78D /* MPElementStoredEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementStoredEntity.h; sourceTree = ""; }; + DADB4ECF19C66FB70065A78D /* MPElementStoredEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementStoredEntity.m; sourceTree = ""; }; DADBB55918DB0CFC00D099FE /* keyboard-dark@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "keyboard-dark@2x.png"; sourceTree = ""; }; DAE1EF2317E942DE00BC0086 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; DAE8E65119867AB500416A0F /* libopensslcrypto-ios.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libopensslcrypto-ios.a"; sourceTree = ""; }; @@ -1357,14 +1354,6 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - DA4425C81557BED40052177D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - DA4425CC1557BED40052177D /* Foundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; DA5BFA41147E415C00F98B1E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -1373,7 +1362,6 @@ DA6701E016406BB400B61001 /* AdSupport.framework in Frameworks */, DA6701DE16406B7300B61001 /* Social.framework in Frameworks */, DA6701B816406A4100B61001 /* Accounts.framework in Frameworks */, - DA44260A1557D9E40052177D /* libUbiquityStoreManager.a in Frameworks */, DAD312C21552A22700A3F9ED /* libsqlite3.dylib in Frameworks */, DABB981615100B4000B05417 /* SystemConfiguration.framework in Frameworks */, DA672D3014F9413D004A189C /* libPearl.a in Frameworks */, @@ -1477,7 +1465,6 @@ DAC77CAD148291A600BCF976 /* libPearl.a */, DAC6325D1486805C0075AEA5 /* libuicolor-utilities.a */, DAC6326C148680650075AEA5 /* libjrswizzle.a */, - DA4425CB1557BED40052177D /* libUbiquityStoreManager.a */, DAFC5655172C573B00CB5CC5 /* libInAppSettingsKit.a */, ); name = Products; @@ -2249,6 +2236,14 @@ isa = PBXGroup; children = ( DABD3BD71711E2DC00CF925C /* iOS */, + DADB4ECE19C66FB60065A78D /* MPElementStoredEntity.h */, + DADB4ECF19C66FB70065A78D /* MPElementStoredEntity.m */, + DADB4ECB19C66FB60065A78D /* MPElementGeneratedEntity.h */, + DADB4ECC19C66FB60065A78D /* MPElementGeneratedEntity.m */, + DADB4EC819C66FB60065A78D /* MPElementEntity.h */, + DADB4EC919C66FB60065A78D /* MPElementEntity.m */, + DADB4EC519C66FB50065A78D /* MPUserEntity.h */, + DADB4EC619C66FB60065A78D /* MPUserEntity.m */, DABD3BA01711E2DC00CF925C /* MPAlgorithm.h */, DABD3BA11711E2DC00CF925C /* MPAlgorithm.m */, DABD3BA21711E2DC00CF925C /* MPAlgorithmV0.h */, @@ -2359,24 +2354,11 @@ DAA141181922FED80032B392 /* iOS */, DAFC5662172C57EC00CB5CC5 /* InAppSettingsKit */, DAC77CAF148291A600BCF976 /* Pearl */, - DACA22B61705DE7D002C6C22 /* UbiquityStoreManager */, ); name = External; path = ../../../External; sourceTree = ""; }; - DACA22B61705DE7D002C6C22 /* UbiquityStoreManager */ = { - isa = PBXGroup; - children = ( - DACA22BA1705DE7D002C6C22 /* UbiquityStoreManager.h */, - DACA22B71705DE7D002C6C22 /* UbiquityStoreManager.m */, - DACA22B81705DE7D002C6C22 /* NSError+UbiquityStoreManager.h */, - DACA22B91705DE7D002C6C22 /* NSError+UbiquityStoreManager.m */, - ); - name = UbiquityStoreManager; - path = UbiquityStoreManager/UbiquityStoreManager; - sourceTree = ""; - }; DACA23B41705DF7D002C6C22 /* Resources */ = { isa = PBXGroup; children = ( @@ -2673,16 +2655,6 @@ /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ - DA4425C91557BED40052177D /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - DACA22BC1705DE7D002C6C22 /* NSError+UbiquityStoreManager.h in Headers */, - DACA22BE1705DE7D002C6C22 /* UbiquityStoreManager.h in Headers */, - 93D399BBC0A7EC746CB1B19B /* MPLogsViewController.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; DAC6325B1486805C0075AEA5 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -2783,23 +2755,6 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ - DA4425CA1557BED40052177D /* UbiquityStoreManager */ = { - isa = PBXNativeTarget; - buildConfigurationList = DA4425D31557BED40052177D /* Build configuration list for PBXNativeTarget "UbiquityStoreManager" */; - buildPhases = ( - DA4425C71557BED40052177D /* Sources */, - DA4425C81557BED40052177D /* Frameworks */, - DA4425C91557BED40052177D /* Headers */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = UbiquityStoreManager; - productName = iCloudStoreManager; - productReference = DA4425CB1557BED40052177D /* libUbiquityStoreManager.a */; - productType = "com.apple.product-type.library.static"; - }; DA5BFA43147E415C00F98B1E /* MasterPassword */ = { isa = PBXNativeTarget; buildConfigurationList = DA5BFA6D147E415C00F98B1E /* Build configuration list for PBXNativeTarget "MasterPassword" */; @@ -3010,7 +2965,6 @@ DAC77CAC148291A600BCF976 /* Pearl */, DAC6325C1486805C0075AEA5 /* uicolor-utilities */, DAC6326B148680650075AEA5 /* jrswizzle */, - DA4425CA1557BED40052177D /* UbiquityStoreManager */, DAFC5654172C573B00CB5CC5 /* InAppSettingsKit */, ); }; @@ -3206,15 +3160,6 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - DA4425C71557BED40052177D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - DACA22BB1705DE7D002C6C22 /* UbiquityStoreManager.m in Sources */, - DACA22BD1705DE7D002C6C22 /* NSError+UbiquityStoreManager.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; DA5BFA40147E415C00F98B1E /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -3226,12 +3171,8 @@ DABD3C011711E2DC00CF925C /* MPAppDelegate_Shared.m in Sources */, DABD3C021711E2DC00CF925C /* MPAppDelegate_Store.m in Sources */, DABD3C031711E2DC00CF925C /* MPConfig.m in Sources */, - DABD3C041711E2DC00CF925C /* MPElementEntity.m in Sources */, - DABD3C051711E2DC00CF925C /* MPElementGeneratedEntity.m in Sources */, - DABD3C061711E2DC00CF925C /* MPElementStoredEntity.m in Sources */, DABD3C071711E2DC00CF925C /* MPEntities.m in Sources */, DABD3C081711E2DC00CF925C /* MPKey.m in Sources */, - DABD3C091711E2DC00CF925C /* MPUserEntity.m in Sources */, DABD3C141711E2DC00CF925C /* MasterPassword.xcdatamodeld in Sources */, DABD3C151711E2DC00CF925C /* MPiOSAppDelegate.m in Sources */, DABD3C1C1711E2DC00CF925C /* MPGuideViewController.m in Sources */, @@ -3239,11 +3180,14 @@ DABD3C1F1711E2DC00CF925C /* MPTypeViewController.m in Sources */, DABD3C211711E2DC00CF925C /* MPiOSConfig.m in Sources */, DABD3C271711E2DC00CF925C /* main.m in Sources */, + DADB4EC719C66FB60065A78D /* MPUserEntity.m in Sources */, 93D39F8A9254177891F38705 /* MPSetupViewController.m in Sources */, DA095E75172F4CD8001C948B /* MPLogsViewController.m in Sources */, 93D39D596A2E376D6F6F5DA1 /* MPCombinedViewController.m in Sources */, + DADB4ECD19C66FB60065A78D /* MPElementGeneratedEntity.m in Sources */, 93D3957237D303DE2D38C267 /* MPAvatarCell.m in Sources */, 93D39B8F90F58A5D158DDBA3 /* MPPasswordsViewController.m in Sources */, + DADB4ED019C66FB70065A78D /* MPElementStoredEntity.m in Sources */, 93D3954FCE045A3CC7E804B7 /* MPUsersViewController.m in Sources */, 93D39392DEDA376F93C6C718 /* MPCell.m in Sources */, 93D39A5FF670957C0AF8298D /* MPPasswordCell.m in Sources */, @@ -3253,6 +3197,7 @@ 93D39673DDC085BE72C34D7C /* MPPopdownSegue.m in Sources */, 93D39BA1EA3CAAC8A220B4A6 /* MPAppSettingsViewController.m in Sources */, 93D396D8B67DA6522CDBA142 /* MPCoachmarkViewController.m in Sources */, + DADB4ECA19C66FB60065A78D /* MPElementEntity.m in Sources */, 93D39EAA4D064193074D3021 /* MPFixable.m in Sources */, 93D394982CBD25D46692DD7C /* MPWebViewController.m in Sources */, 93D39D8F78978196D6ABDEDE /* MPNavigationController.m in Sources */, @@ -3402,27 +3347,6 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ - DA4425D41557BED40052177D /* Debug-iOS */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_OBJC_ARC = YES; - }; - name = "Debug-iOS"; - }; - DA4425D51557BED40052177D /* AdHoc-iOS */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_OBJC_ARC = YES; - }; - name = "AdHoc-iOS"; - }; - DA4425D61557BED40052177D /* AppStore-iOS */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_OBJC_ARC = YES; - }; - name = "AppStore-iOS"; - }; DA5BFA6B147E415C00F98B1E /* Debug-iOS */ = { isa = XCBuildConfiguration; buildSettings = { @@ -3892,16 +3816,6 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - DA4425D31557BED40052177D /* Build configuration list for PBXNativeTarget "UbiquityStoreManager" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - DA4425D41557BED40052177D /* Debug-iOS */, - DA4425D51557BED40052177D /* AdHoc-iOS */, - DA4425D61557BED40052177D /* AppStore-iOS */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = "AdHoc-iOS"; - }; DA5BFA3E147E415C00F98B1E /* Build configuration list for PBXProject "MasterPassword-iOS" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -3972,8 +3886,9 @@ DABD3BD21711E2DC00CF925C /* MasterPassword 2.xcdatamodel */, DABD3BD31711E2DC00CF925C /* MasterPassword 3.xcdatamodel */, DABD3BD41711E2DC00CF925C /* MasterPassword 4.xcdatamodel */, + DA62140919C66A9700375240 /* MasterPassword 5.xcdatamodel */, ); - currentVersion = DABD3BD41711E2DC00CF925C /* MasterPassword 4.xcdatamodel */; + currentVersion = DA62140919C66A9700375240 /* MasterPassword 5.xcdatamodel */; path = MasterPassword.xcdatamodeld; sourceTree = ""; versionGroupType = wrapper.xcdatamodel; diff --git a/MasterPassword/ObjC/iOS/Settings.bundle/Root.plist b/MasterPassword/ObjC/iOS/Settings.bundle/Root.plist index ca7a66a0..7ef64c7d 100644 --- a/MasterPassword/ObjC/iOS/Settings.bundle/Root.plist +++ b/MasterPassword/ObjC/iOS/Settings.bundle/Root.plist @@ -146,24 +146,6 @@ To see a site's password anyway, tap and hold your finger down for a while 2 - - Type - PSGroupSpecifier - Title - - FooterText - Synchronizes your sites with your other Apple devices. It's also a good way of keeping automatic backups. - - - Type - PSToggleSwitchSpecifier - Title - iCloud - Key - iCloudEnabled - DefaultValue - - Type PSGroupSpecifier diff --git a/MasterPassword/ObjC/iOS/Storyboard.storyboard b/MasterPassword/ObjC/iOS/Storyboard.storyboard index 4ec52333..cdf6f31d 100644 --- a/MasterPassword/ObjC/iOS/Storyboard.storyboard +++ b/MasterPassword/ObjC/iOS/Storyboard.storyboard @@ -1,7 +1,6 @@ - @@ -193,11 +192,11 @@ - + - + @@ -1061,9 +1060,16 @@ - + + + + + + + + - + - + - - - - - - - @@ -1110,7 +1109,7 @@ - + @@ -1419,7 +1418,7 @@ - + @@ -1463,7 +1462,7 @@ - + @@ -2027,26 +2026,9 @@ CgoKCgoKCgoKCgoKCg - 119-20:51:52 MPiOSAppDelegate.m:36 | INFO : Initializing TestFlight -119-20:51:52 MPiOSAppDelegate.m:80 | INFO : Initializing Crashlytics -119-20:51:53 PearlAppDelegate.m:71 | INFO : Master Password (MasterPassword) 1.4 (1.4.0) (GIT: 1.4-0-g8a4eecd-dirty) -119-20:51:53 MPiOSAppDelegate.m:257 | INFO : Started up with device identifier: A8C51CDA-6F60-4F0C-BFC9-68A08F2F2DD7 -119-20:51:59 MPAppDelegate_Store.m:278 | DEBUG : [StoreManager] (Re)loading store... -119-20:51:59 MPAppDelegate_Store.m:278 | DEBUG : [StoreManager] Will load cloud store: 0B3CA2DF-5796-44DF-B5E0-121EC3846464 (definite). -119-20:51:59 PearlConfig.m:193 | INFO : Lock screen will appear -119-20:51:59 MPiOSAppDelegate.m:412 | INFO : Re-activated -119-20:51:59 PearlConfig.m:180 | DEBUG : MPiOSConfig.launchCount = [70 ->] 71 -119-20:52:02 MPAppDelegate_Store.m:278 | DEBUG : [StoreManager] Clearing stores... -119-20:52:03 MPAppDelegate_Store.m:278 | DEBUG : [StoreManager] Loading store without seeding. -119-20:52:09 MPAppDelegate_Store.m:278 | DEBUG : [StoreManager] Cloud enabled and successfully loaded cloud store. -119-20:52:09 MPAppDelegate_Store.m:299 | INFO : Using iCloud? 1 -119-20:52:12 MPAppDelegate_Key.m:28 | INFO : Found key in keychain for: b55911588b178466be1d6392597e899b8de46f9a -119-20:52:12 MPAppDelegate_Key.m:132 | INFO : Logged in: b55911588b178466be1d6392597e899b8de46f9a -119-20:52:13 MPUnlockViewController.m:229 | INFO : Lock screen will disappear -119-20:52:13 MPMainViewController.m:142 | INFO : Main will appear -119-20:52:16 MPMainViewController.m:734 | INFO : Action: Preferences -119-20:52:17 MPMainViewController.m:187 | INFO : Main will disappear. - + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris facilisis tortor leo, iaculis mollis elit dictum et. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In congue justo porta enim imperdiet, id luctus justo fringilla. Nunc nec sem id augue bibendum hendrerit eu ut eros. Ut fermentum augue quis nunc feugiat vehicula. Quisque in ultrices magna. Praesent quis mollis lectus. Sed fringilla massa vitae eros luctus, eget convallis justo pretium. Duis non tristique ante. Sed suscipit tortor ligula, sed fermentum eros sodales ut. Maecenas sed ante et orci posuere lobortis et sodales diam. Nunc non ullamcorper orci. + +Suspendisse potenti. Etiam ut nisi id augue tempor ultrices et sit amet sapien. Quisque fringilla ex dolor, at iaculis turpis facilisis et. Donec pellentesque quam vitae libero facilisis, quis scelerisque augue feugiat. Nam eros eros, posuere eget ultricies quis, maximus sodales velit. Aliquam euismod iaculis consectetur. Etiam dictum orci id dapibus fermentum. Vestibulum posuere tortor vitae viverra dictum. Morbi rutrum arcu mattis felis hendrerit pretium. Praesent rutrum euismod leo, in faucibus lectus rhoncus in. Vestibulum porttitor semper eros, eu dapibus risus tincidunt sed. Curabitur ex enim, ullamcorper nec laoreet id, consectetur et nisl. Etiam id pulvinar risus. @@ -2055,11 +2037,6 @@ CgoKCgoKCgoKCgoKCg - - - - - diff --git a/MasterPassword/Resources/Data/ciphers.plist b/MasterPassword/Resources/Data/ciphers.plist index 0c5f6472..c386b151 100644 --- a/MasterPassword/Resources/Data/ciphers.plist +++ b/MasterPassword/Resources/Data/ciphers.plist @@ -4,6 +4,10 @@ MPElementGeneratedEntity + Login Name + + cvccvcvcv + Maximum Security Password anoxxxxxxxxxxxxxxxxx