Removed iCloud + added generated login names.
[REMOVED] UbiquityStoreManager and iCloud support. Now using simplified local Core Data store logic. [ADDED] Generating site login names. [IMPROVED] Some refactoring and interface improvements for optionally generated user names.
This commit is contained in:
parent
9109a59410
commit
88fdc89f27
@ -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];
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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 <lhunath@lyndir.com>
|
||||
* @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 <lhunath@lyndir.com>
|
||||
* @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
|
||||
- (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)importClearTextContent:(NSString *)clearContent intoElement:(MPElementEntity *)element
|
||||
- (void)importClearTextPassword:(NSString *)clearPassword intoElement:(MPElementEntity *)element
|
||||
usingKey:(MPKey *)elementKey;
|
||||
- (NSString *)exportContentForElement:(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;
|
||||
|
@ -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;
|
||||
}
|
||||
@ -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<MPAlgorithm> 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,7 +528,8 @@
|
||||
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] );
|
||||
@ -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;
|
||||
@ -505,7 +586,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)importProtectedContent:(NSString *)protectedContent protectedByKey:(MPKey *)importKey
|
||||
- (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." );
|
||||
@ -516,6 +597,7 @@
|
||||
case MPElementTypeGeneratedBasic:
|
||||
case MPElementTypeGeneratedShort:
|
||||
case MPElementTypeGeneratedPIN:
|
||||
case MPElementTypeGeneratedName:
|
||||
break;
|
||||
|
||||
case MPElementTypeStoredPersonal: {
|
||||
@ -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;
|
||||
}
|
||||
|
@ -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]
|
||||
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." );
|
||||
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)<UbiquityStoreManagerDelegate>
|
||||
@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. */
|
||||
|
@ -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];
|
||||
[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,103 +237,27 @@ 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;
|
||||
}
|
||||
if (migrationLevel == MPStoreMigrationLevelV2 && ![self migrateV2LocalStore]) {
|
||||
inf( @"Failed to migrate old V2 to new local store." );
|
||||
return;
|
||||
}
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] setInteger:MPMigrationLevelLocalStoreCurrent forKey:MPMigrationLevelLocalStoreKey];
|
||||
[[NSUserDefaults standardUserDefaults] setInteger:MPStoreMigrationLevelCurrent forKey:MPStoreMigrationLevelKey];
|
||||
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.
|
||||
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];
|
||||
}
|
||||
|
||||
- (BOOL)migrateV1LocalStore {
|
||||
|
||||
NSURL *applicationFilesDirectory = [[[NSFileManager defaultManager]
|
||||
@ -296,141 +270,58 @@ 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] ),
|
||||
} );
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark - Utilities
|
||||
@ -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",
|
||||
|
@ -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 <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
#import "MPFixable.h"
|
||||
|
||||
@class MPUserEntity;
|
||||
|
||||
@interface MPElementEntity : NSManagedObject <MPFixable>
|
||||
@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
|
||||
|
@ -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
|
||||
|
@ -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 <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
#import "MPElementEntity.h"
|
||||
|
||||
|
||||
@interface MPElementGeneratedEntity : MPElementEntity
|
||||
|
||||
@property(nonatomic, retain) NSNumber *counter_;
|
||||
@property (nonatomic, retain) NSNumber * counter_;
|
||||
|
||||
@end
|
||||
|
@ -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
|
||||
|
@ -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 <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
#import "MPElementEntity.h"
|
||||
|
||||
|
||||
@interface MPElementStoredEntity : MPElementEntity
|
||||
|
||||
@property(nonatomic, retain) NSData *contentObject;
|
||||
@property (nonatomic, retain) NSData * contentObject;
|
||||
|
||||
@end
|
||||
|
@ -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
|
||||
|
@ -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)<MPFixable>
|
||||
|
||||
@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
|
||||
|
||||
|
@ -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);
|
||||
@ -135,20 +150,69 @@
|
||||
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)
|
||||
|
@ -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,
|
||||
|
@ -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 <Foundation/Foundation.h>
|
||||
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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:@"●"];
|
||||
|
@ -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,7 +403,7 @@
|
||||
MPElementType type = [types[t] unsignedIntegerValue];
|
||||
NSString *title = [element.algorithm nameOfType:type];
|
||||
if (type & MPElementTypeClassGenerated)
|
||||
title = [element.algorithm generateContentNamed:element.siteName ofType:type
|
||||
title = [element.algorithm generatePasswordForSiteNamed:element.siteName ofType:type
|
||||
withCounter:element.counter usingKey:[MPMacAppDelegate get].key];
|
||||
|
||||
NSButtonCell *cell = [self.passwordTypesMatrix cellAtRow:(NSInteger)t column:0];
|
||||
|
@ -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 = "<group>"; };
|
||||
DA2509271951B86C00AC23F1 /* screen.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = screen.png; sourceTree = "<group>"; };
|
||||
DA29992719C6A89900AF7DF1 /* MasterPassword 1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 1.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA29992819C6A89900AF7DF1 /* MasterPassword 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 2.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA29992919C6A89900AF7DF1 /* MasterPassword 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 3.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA29992A19C6A89900AF7DF1 /* MasterPassword 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 4.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA29992B19C6A89900AF7DF1 /* MasterPassword 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 5.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA2CA4E718D323D3007798F8 /* NSError+PearlFullDescription.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+PearlFullDescription.m"; sourceTree = "<group>"; };
|
||||
DA2CA4E818D323D3007798F8 /* NSError+PearlFullDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+PearlFullDescription.h"; sourceTree = "<group>"; };
|
||||
DA2CA4E918D323D3007798F8 /* NSArray+Pearl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Pearl.m"; sourceTree = "<group>"; };
|
||||
@ -320,10 +325,6 @@
|
||||
DA5E5CC31724A667003798D8 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
DA5E5CC51724A667003798D8 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
||||
DA5E5CC61724A667003798D8 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
DA5E5CC81724A667003798D8 /* MasterPassword 1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 1.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA5E5CC91724A667003798D8 /* MasterPassword 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 2.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA5E5CCA1724A667003798D8 /* MasterPassword 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 3.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA5E5CCB1724A667003798D8 /* MasterPassword 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 4.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA606FEA195D03E200CA98B5 /* icon_action.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon_action.png; sourceTree = "<group>"; };
|
||||
DA606FEB195D03E200CA98B5 /* icon_action@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon_action@2x.png"; sourceTree = "<group>"; };
|
||||
DA606FEC195D03E200CA98B5 /* icon_addressbook-person.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon_addressbook-person.png"; sourceTree = "<group>"; };
|
||||
@ -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 = "<group>";
|
||||
versionGroupType = wrapper.xcdatamodel;
|
||||
|
@ -3,6 +3,6 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>_XCCurrentVersionName</key>
|
||||
<string>MasterPassword 4.xcdatamodel</string>
|
||||
<string>MasterPassword 5.xcdatamodel</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="6244" systemVersion="13E28" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
|
||||
<entity name="MPElementEntity" representedClassName="MPElementEntity" isAbstract="YES" syncable="YES">
|
||||
<attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/>
|
||||
<attribute name="lastUsed" attributeType="Date" indexed="YES" syncable="YES"/>
|
||||
<attribute name="loginGenerated_" attributeType="Boolean" defaultValueString="NO" syncable="YES"/>
|
||||
<attribute name="loginName" optional="YES" attributeType="String" elementID="A1B9F981-D33C-4BFE-9F94-C9D3E1F78E51" syncable="YES"/>
|
||||
<attribute name="name" attributeType="String" minValueString="1" indexed="YES" syncable="YES"/>
|
||||
<attribute name="requiresExplicitMigration_" attributeType="Boolean" defaultValueString="NO">
|
||||
<userInfo/>
|
||||
</attribute>
|
||||
<attribute name="type_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/>
|
||||
<attribute name="uses_" attributeType="Integer 16" defaultValueString="0" indexed="YES" syncable="YES"/>
|
||||
<attribute name="version_" attributeType="Integer 16" minValueString="0" defaultValueString="0" syncable="YES"/>
|
||||
<relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="elements" inverseEntity="MPUserEntity" syncable="YES"/>
|
||||
</entity>
|
||||
<entity name="MPElementGeneratedEntity" representedClassName="MPElementGeneratedEntity" parentEntity="MPElementEntity" syncable="YES">
|
||||
<attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/>
|
||||
</entity>
|
||||
<entity name="MPElementStoredEntity" representedClassName="MPElementStoredEntity" parentEntity="MPElementEntity" syncable="YES">
|
||||
<attribute name="contentObject" optional="YES" attributeType="Transformable" storedInTruthFile="YES" syncable="YES"/>
|
||||
</entity>
|
||||
<entity name="MPUserEntity" representedClassName="MPUserEntity" syncable="YES">
|
||||
<attribute name="avatar_" attributeType="Integer 16" defaultValueString="0" syncable="YES"/>
|
||||
<attribute name="defaultType_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/>
|
||||
<attribute name="keyID" optional="YES" attributeType="Binary" syncable="YES"/>
|
||||
<attribute name="lastUsed" optional="YES" attributeType="Date" syncable="YES"/>
|
||||
<attribute name="name" attributeType="String" syncable="YES"/>
|
||||
<attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO">
|
||||
<userInfo/>
|
||||
</attribute>
|
||||
<relationship name="elements" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPElementEntity" inverseName="user" inverseEntity="MPElementEntity" syncable="YES"/>
|
||||
</entity>
|
||||
<elements>
|
||||
<element name="MPElementEntity" positionX="-0" positionY="-286" width="128" height="193"/>
|
||||
<element name="MPElementGeneratedEntity" positionX="216" positionY="-288" width="128" height="58"/>
|
||||
<element name="MPElementStoredEntity" positionX="214" positionY="-171" width="128" height="58"/>
|
||||
<element name="MPUserEntity" positionX="-218" positionY="-288" width="128" height="148"/>
|
||||
</elements>
|
||||
</model>
|
@ -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];
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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<MPAlgorithm> 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 {
|
||||
|
@ -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<NSFetchedResultsSectionInfo>)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 = @[
|
||||
|
@ -80,7 +80,7 @@
|
||||
counter = ((MPElementGeneratedEntity *)selectedElement).counter;
|
||||
|
||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0 ), ^{
|
||||
NSString *typeContent = [MPAlgorithmDefault generateContentNamed:name ofType:cellType
|
||||
NSString *typeContent = [MPAlgorithmDefault generatePasswordForSiteNamed:name ofType:cellType
|
||||
withCounter:counter usingKey:[MPiOSAppDelegate get].key];
|
||||
|
||||
dispatch_async( dispatch_get_main_queue(), ^{
|
||||
|
@ -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;
|
||||
}];
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}];
|
||||
|
||||
|
@ -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 = "<group>"; };
|
||||
DA3B844D190FC5DF00246EEA /* Crashlytics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Crashlytics.framework; path = ../../../External/iOS/Crashlytics.framework; sourceTree = "<group>"; };
|
||||
DA3BCFCA19BD09D5006B2681 /* SourceCodePro-Regular.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SourceCodePro-Regular.otf"; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
DA5A09DE171A70E4005284AB /* play@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "play@2x.png"; sourceTree = "<group>"; };
|
||||
DA5A09E8171BB0F7005284AB /* unlocked.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = unlocked.png; sourceTree = "<group>"; };
|
||||
@ -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 = "<group>"; };
|
||||
DA62140919C66A9700375240 /* MasterPassword 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 5.xcdatamodel"; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
DACA22B71705DE7D002C6C22 /* UbiquityStoreManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UbiquityStoreManager.m; sourceTree = "<group>"; };
|
||||
DACA22B81705DE7D002C6C22 /* NSError+UbiquityStoreManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+UbiquityStoreManager.h"; sourceTree = "<group>"; };
|
||||
DACA22B91705DE7D002C6C22 /* NSError+UbiquityStoreManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+UbiquityStoreManager.m"; sourceTree = "<group>"; };
|
||||
DACA22BA1705DE7D002C6C22 /* UbiquityStoreManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UbiquityStoreManager.h; sourceTree = "<group>"; };
|
||||
DACA269A1705DF81002C6C22 /* Crashlytics.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Crashlytics.plist; sourceTree = "<group>"; };
|
||||
DACA29711705E1A8002C6C22 /* ciphers.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = ciphers.plist; sourceTree = "<group>"; };
|
||||
DACA29721705E1A8002C6C22 /* dictionary.lst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dictionary.lst; sourceTree = "<group>"; };
|
||||
@ -1228,6 +1217,14 @@
|
||||
DACE2F6919BA6A2A0010F92E /* PearlMutableStaticTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlMutableStaticTableViewController.h; sourceTree = "<group>"; };
|
||||
DACE2F6A19BA6A2A0010F92E /* UIView+FontScale.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+FontScale.h"; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
DADB4EC619C66FB60065A78D /* MPUserEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUserEntity.m; sourceTree = "<group>"; };
|
||||
DADB4EC819C66FB60065A78D /* MPElementEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementEntity.h; sourceTree = "<group>"; };
|
||||
DADB4EC919C66FB60065A78D /* MPElementEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementEntity.m; sourceTree = "<group>"; };
|
||||
DADB4ECB19C66FB60065A78D /* MPElementGeneratedEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementGeneratedEntity.h; sourceTree = "<group>"; };
|
||||
DADB4ECC19C66FB60065A78D /* MPElementGeneratedEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementGeneratedEntity.m; sourceTree = "<group>"; };
|
||||
DADB4ECE19C66FB60065A78D /* MPElementStoredEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementStoredEntity.h; sourceTree = "<group>"; };
|
||||
DADB4ECF19C66FB70065A78D /* MPElementStoredEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementStoredEntity.m; sourceTree = "<group>"; };
|
||||
DADBB55918DB0CFC00D099FE /* keyboard-dark@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "keyboard-dark@2x.png"; sourceTree = "<group>"; };
|
||||
DAE1EF2317E942DE00BC0086 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
DAE8E65119867AB500416A0F /* libopensslcrypto-ios.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libopensslcrypto-ios.a"; sourceTree = "<group>"; };
|
||||
@ -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 = "<group>";
|
||||
};
|
||||
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 = "<group>";
|
||||
};
|
||||
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 = "<group>";
|
||||
versionGroupType = wrapper.xcdatamodel;
|
||||
|
@ -146,24 +146,6 @@ To see a site's password anyway, tap and hold your finger down for a while
|
||||
<integer>2</integer>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Type</key>
|
||||
<string>PSGroupSpecifier</string>
|
||||
<key>Title</key>
|
||||
<string></string>
|
||||
<key>FooterText</key>
|
||||
<string>Synchronizes your sites with your other Apple devices. It's also a good way of keeping automatic backups.</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Type</key>
|
||||
<string>PSToggleSwitchSpecifier</string>
|
||||
<key>Title</key>
|
||||
<string>iCloud</string>
|
||||
<key>Key</key>
|
||||
<string>iCloudEnabled</string>
|
||||
<key>DefaultValue</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Type</key>
|
||||
<string>PSGroupSpecifier</string>
|
||||
|
@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6245" systemVersion="13E28" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" initialViewController="Q1S-vU-GGO">
|
||||
<dependencies>
|
||||
<deployment defaultVersion="1792" identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6238"/>
|
||||
<capability name="Alignment constraints with different attributes" minToolsVersion="5.1"/>
|
||||
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
|
||||
@ -193,11 +192,11 @@
|
||||
</collectionViewCell>
|
||||
</cells>
|
||||
<collectionReusableView key="sectionHeaderView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="MPAvatarIndent" id="LD1-mt-htC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="80" height="548"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="80" height="647"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</collectionReusableView>
|
||||
<collectionReusableView key="sectionFooterView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="MPAvatarOutdent" id="G8v-Sb-ilL">
|
||||
<rect key="frame" x="240" y="0.0" width="80" height="548"/>
|
||||
<rect key="frame" x="295" y="0.0" width="80" height="647"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</collectionReusableView>
|
||||
<connections>
|
||||
@ -1061,9 +1060,16 @@
|
||||
<action selector="doContent:" destination="W2g-yv-V3V" eventType="touchUpInside" id="ukg-D8-8O3"/>
|
||||
</connections>
|
||||
</button>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="w2g-zN-1wZ" userLabel="Login Container">
|
||||
<view alpha="0.69999999999999996" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="w2g-zN-1wZ" userLabel="Login Container">
|
||||
<rect key="frame" x="0.0" y="0.0" width="300" height="21"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="q3g-CJ-LbN" userLabel="Separator">
|
||||
<rect key="frame" x="-1" y="20" width="300" height="1"/>
|
||||
<color key="backgroundColor" red="0.14901960784313725" green="0.14901960784313725" blue="0.14901960784313725" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="1" id="jyk-dC-QLb"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Iwe-rQ-ma0" userLabel="Copy Login">
|
||||
<rect key="frame" x="0.0" y="0.0" width="300" height="29"/>
|
||||
<color key="backgroundColor" red="0.18431372549019609" green="0.15686274509803921" blue="0.15686274509803921" alpha="0.01" colorSpace="calibratedRGB"/>
|
||||
@ -1071,26 +1077,19 @@
|
||||
<action selector="doLoginName:" destination="W2g-yv-V3V" eventType="touchUpInside" id="Ex0-re-QrI"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="lhunath" textAlignment="center" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="3I9-vf-IZK" userLabel="Login">
|
||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Tap to generate username" textAlignment="center" minimumFontSize="9" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="3I9-vf-IZK" userLabel="Login">
|
||||
<rect key="frame" x="8" y="0.0" width="284" height="20"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="20" id="9gA-Ti-g1e"/>
|
||||
</constraints>
|
||||
<color key="textColor" red="0.37254901959999998" green="0.3921568627" blue="0.42745098040000001" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="textColor" red="0.42745098039215684" green="0.36862745098039218" blue="0.38823529411764707" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<fontDescription key="fontDescription" name="SourceCodePro-Regular" family="Source Code Pro" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocorrectionType="no" keyboardType="alphabet" keyboardAppearance="alert" returnKeyType="done"/>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="W2g-yv-V3V" id="z8b-Vd-dWe"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="q3g-CJ-LbN" userLabel="Separator">
|
||||
<rect key="frame" x="0.0" y="20" width="300" height="1"/>
|
||||
<color key="backgroundColor" red="0.14901960784313725" green="0.14901960784313725" blue="0.14901960784313725" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="1" id="jyk-dC-QLb"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
@ -1110,7 +1109,7 @@
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="2tX-WK-ASq" userLabel="Password Container">
|
||||
<rect key="frame" x="0.0" y="20" width="300" height="43"/>
|
||||
<subviews>
|
||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="CuzaSasy3*Rimo" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="blw-Ou-8I8" userLabel="Password">
|
||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="CuzaSasy3*Rimo" textAlignment="center" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="blw-Ou-8I8" userLabel="Password">
|
||||
<rect key="frame" x="0.0" y="0.0" width="300" height="31"/>
|
||||
<color key="textColor" red="0.40000000600000002" green="0.80000001190000003" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<fontDescription key="fontDescription" name="SourceCodePro-Black" family="Source Code Pro" pointSize="24"/>
|
||||
@ -1419,7 +1418,7 @@
|
||||
</collectionViewCell>
|
||||
</cells>
|
||||
<collectionReusableView key="sectionHeaderView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="MPPasswordHeader" id="yzh-hh-YjZ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="108"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="108"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</collectionReusableView>
|
||||
<connections>
|
||||
@ -1463,7 +1462,7 @@
|
||||
</toolbar>
|
||||
<searchBar contentMode="redraw" barStyle="black" searchBarStyle="minimal" placeholder="eg. apple.com" translatesAutoresizingMaskIntoConstraints="NO" id="aGs-1S-aC3">
|
||||
<rect key="frame" x="0.0" y="64" width="375" height="44"/>
|
||||
<textInputTraits key="textInputTraits" autocorrectionType="no" keyboardType="URL"/>
|
||||
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" keyboardType="URL"/>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="nkY-z6-8jd" id="ENG-q5-XwX"/>
|
||||
</connections>
|
||||
@ -2027,26 +2026,9 @@ CgoKCgoKCgoKCgoKCg
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<inset key="scrollIndicatorInsets" minX="0.0" minY="64" maxX="0.0" maxY="93"/>
|
||||
<string key="text">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.
|
||||
</string>
|
||||
<string key="text">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.</string>
|
||||
<color key="textColor" cocoaTouchSystemColor="lightTextColor"/>
|
||||
<fontDescription key="fontDescription" name="SourceCodePro-Black" family="Source Code Pro" pointSize="9"/>
|
||||
<textInputTraits key="textInputTraits" autocorrectionType="no"/>
|
||||
@ -2055,11 +2037,6 @@ CgoKCgoKCgoKCgoKCg
|
||||
<toolbar contentMode="scaleToFill" barStyle="black" translatesAutoresizingMaskIntoConstraints="NO" id="WmH-JB-jp2">
|
||||
<rect key="frame" x="0.0" y="574" width="375" height="44"/>
|
||||
<items>
|
||||
<barButtonItem systemItem="action" id="JjQ-rY-m2q">
|
||||
<connections>
|
||||
<action selector="action:" destination="C0Q-RC-szS" id="Vi3-CE-YmV"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<barButtonItem systemItem="compose" id="BSV-3i-01h">
|
||||
<connections>
|
||||
<action selector="mail:" destination="C0Q-RC-szS" id="i37-jS-UfO"/>
|
||||
|
@ -4,6 +4,10 @@
|
||||
<dict>
|
||||
<key>MPElementGeneratedEntity</key>
|
||||
<dict>
|
||||
<key>Login Name</key>
|
||||
<array>
|
||||
<string>cvccvcvcv</string>
|
||||
</array>
|
||||
<key>Maximum Security Password</key>
|
||||
<array>
|
||||
<string>anoxxxxxxxxxxxxxxxxx</string>
|
||||
|
Loading…
Reference in New Issue
Block a user