2
0

Compare commits

..

33 Commits

Author SHA1 Message Date
Maarten Billemont
b4b9ee3cb9 Import sites from pasteboard. 2014-09-27 20:29:58 -04:00
Maarten Billemont
da4bad7977 Fix a few build warnings, two-way site question relationship. 2014-09-27 16:30:17 -04:00
Maarten Billemont
984434cca4 Remove header hack to inset password cells, use collection layout instead. 2014-09-27 14:53:30 -04:00
Maarten Billemont
064122f36d Build fixes. 2014-09-27 12:52:17 -04:00
Maarten Billemont
5db083bf7c Improve notification registration and cleanup + fix removal of site questions. 2014-09-27 01:27:05 -04:00
Maarten Billemont
44f91e0618 Merge branch 'master' of github.com:Lyndir/MasterPassword 2014-09-26 00:32:19 -04:00
Maarten Billemont
6050b5d6fd Development fuel, store improvements and navigation fixes. 2014-09-26 00:32:07 -04:00
Maarten Billemont
8e3e77c2c1 Remove UbiquityStoreManager 2014-09-24 16:04:35 -04:00
Maarten Billemont
a2e71aa94d Merge branch 'master' of github.com:Lyndir/MasterPassword
Conflicts:
	MasterPassword/ObjC/iOS/MPPasswordsViewController.m
2014-09-24 16:02:29 -04:00
Maarten Billemont
a5bc2eb584 Store product thumbnails. 2014-09-24 08:00:10 -04:00
Maarten Billemont
9bb613a3b6 MPAlgorithm V2: handle multi-byte UTF-8 correctly by counting bytes, not characters. 2014-09-24 07:58:23 -04:00
Maarten Billemont
466863f8fd Improved overlay navigation, store refactoring and automatic sizing of store cells. 2014-09-24 01:07:02 -04:00
Maarten Billemont
fe5828c724 Fix removal of questions. Blast you Core Data. 2014-09-22 22:32:31 -04:00
Maarten Billemont
b3ec7a848d Make answers VC a pop-over. 2014-09-22 08:48:51 -04:00
Maarten Billemont
17734652b4 Completed answers generation. 2014-09-21 23:48:49 -04:00
Maarten Billemont
9e742fa40f Use fullDescription for all error logging. 2014-09-21 23:28:50 -04:00
Maarten Billemont
d03b1746e0 Handle failure to load store. 2014-09-21 23:11:05 -04:00
Maarten Billemont
58156be793 Generating security question answers for sites. 2014-09-21 22:45:21 -04:00
Maarten Billemont
d5a5cd7de4 Fix a few issues after element->site rename. 2014-09-21 14:09:43 -04:00
Maarten Billemont
2100662fb3 Add a model version for MPSiteQuestionEntity and element->site renames. 2014-09-21 13:56:37 -04:00
Maarten Billemont
248627aa92 Project cleanup. 2014-09-21 13:45:33 -04:00
Maarten Billemont
449ccaa3d4 Storyboard fixups. 2014-09-21 13:39:47 -04:00
Maarten Billemont
0a7465282b Prepare generate answers product. 2014-09-21 12:54:48 -04:00
Maarten Billemont
5b85ba3a4b Element -> Site 2014-09-21 11:47:53 -04:00
Maarten Billemont
b3a0b6a7c0 Element -> Site, site security question answers 2014-09-21 10:49:57 -04:00
Maarten Billemont
4396ce436e Element -> Site WIP 2014-09-21 10:39:09 -04:00
Maarten Billemont
68e6106ee7 Extract In-App logic into app delegate category & improvements to import file handling and advanced export + store fixes. 2014-09-21 10:29:18 -04:00
Maarten Billemont
4c12f368f5 Ability to generate pass phrases as well as names. 2014-09-21 01:57:45 -04:00
Maarten Billemont
0156f8c3c8 More store work.
[FIXED]     A strange issue with reloading password cells.
[FIXED]     Product identifiers and showing the first product in the store.
[ADDED]     Restoring purchases made from other devices.
[REMOVED]   iCloud entitlements.
2014-09-17 20:59:03 -04:00
Maarten Billemont
2e5cbac761 Added in-app purchase store and made generated logins a product. 2014-09-17 02:00:33 -04:00
Maarten Billemont
a043b7c049 Fixes to new store loading if not migrated. 2014-09-16 07:53:31 -04:00
Maarten Billemont
06c62f70ed Fixes to V0 algorithm debug log output. 2014-09-16 00:45:39 -04:00
Maarten Billemont
bc88daf08d Don't use old section info from an old NSFetchedResultsSectionInfo object. 2014-09-02 14:51:04 -04:00
103 changed files with 4083 additions and 2185 deletions

6
.gitmodules vendored
View File

@@ -4,9 +4,9 @@
[submodule "External/InAppSettingsKit"] [submodule "External/InAppSettingsKit"]
path = External/InAppSettingsKit path = External/InAppSettingsKit
url = git://github.com/lhunath/InAppSettingsKit.git url = git://github.com/lhunath/InAppSettingsKit.git
[submodule "External/UbiquityStoreManager"]
path = External/UbiquityStoreManager
url = git://github.com/lhunath/UbiquityStoreManager.git
[submodule "External/RHStatusItemView"] [submodule "External/RHStatusItemView"]
path = External/RHStatusItemView path = External/RHStatusItemView
url = git://github.com/lhunath/RHStatusItemView.git url = git://github.com/lhunath/RHStatusItemView.git
[submodule "External/KCOrderedAccessorFix"]
path = External/KCOrderedAccessorFix
url = https://github.com/CFKevinRef/KCOrderedAccessorFix.git

2
External/Pearl vendored

View File

@@ -18,6 +18,8 @@
<string>github.com:Lyndir/Lyndir.git</string> <string>github.com:Lyndir/Lyndir.git</string>
<key>2FE140B36B7D26140DC8D5E5C639DC5900EFCF35</key> <key>2FE140B36B7D26140DC8D5E5C639DC5900EFCF35</key>
<string>git://github.com/lhunath/uicolor-utilities.git</string> <string>git://github.com/lhunath/uicolor-utilities.git</string>
<key>304AD0F97EA7B4893D91DFB8C3413D4E627B9472</key>
<string>https://github.com/CFKevinRef/KCOrderedAccessorFix.git</string>
<key>3E67FB08419C920516AAC3B00DAAF23073B8CF77</key> <key>3E67FB08419C920516AAC3B00DAAF23073B8CF77</key>
<string>git://github.com/lhunath/RHStatusItemView.git</string> <string>git://github.com/lhunath/RHStatusItemView.git</string>
<key>4DDCFFD91B41F00326AD14553BD66CFD366ABD91</key> <key>4DDCFFD91B41F00326AD14553BD66CFD366ABD91</key>
@@ -41,6 +43,8 @@
<string>../..</string> <string>../..</string>
<key>2FE140B36B7D26140DC8D5E5C639DC5900EFCF35</key> <key>2FE140B36B7D26140DC8D5E5C639DC5900EFCF35</key>
<string>../External/Pearl/External/uicolor-utilities</string> <string>../External/Pearl/External/uicolor-utilities</string>
<key>304AD0F97EA7B4893D91DFB8C3413D4E627B9472</key>
<string>../External/KCOrderedAccessorFix/</string>
<key>3E67FB08419C920516AAC3B00DAAF23073B8CF77</key> <key>3E67FB08419C920516AAC3B00DAAF23073B8CF77</key>
<string>../External/RHStatusItemView</string> <string>../External/RHStatusItemView</string>
<key>4DDCFFD91B41F00326AD14553BD66CFD366ABD91</key> <key>4DDCFFD91B41F00326AD14553BD66CFD366ABD91</key>
@@ -84,6 +88,14 @@
<key>IDESourceControlWCCName</key> <key>IDESourceControlWCCName</key>
<string>jrswizzle</string> <string>jrswizzle</string>
</dict> </dict>
<dict>
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
<string>public.vcs.git</string>
<key>IDESourceControlWCCIdentifierKey</key>
<string>304AD0F97EA7B4893D91DFB8C3413D4E627B9472</string>
<key>IDESourceControlWCCName</key>
<string>KCOrderedAccessorFix</string>
</dict>
<dict> <dict>
<key>IDESourceControlRepositoryExtensionIdentifierKey</key> <key>IDESourceControlRepositoryExtensionIdentifierKey</key>
<string>public.vcs.git</string> <string>public.vcs.git</string>

View File

@@ -44,8 +44,9 @@ void usage() {
" m, med, medium | Copy-friendly, 8 characters, contains symbols.\n" " m, med, medium | Copy-friendly, 8 characters, contains symbols.\n"
" b, basic | 8 characters, no symbols.\n" " b, basic | 8 characters, no symbols.\n"
" s, short | Copy-friendly, 4 characters, no symbols.\n" " s, short | Copy-friendly, 4 characters, no symbols.\n"
" p, pin | 4 numbers.\n" " i, pin | 4 numbers.\n"
" n, name | 9 letter name.\n\n", MP_env_sitetype); " n, name | 9 letter name.\n"
" p, phrase | 20 character sentence.\n\n", MP_env_sitetype);
fprintf(stderr, " -c counter The value of the counter.\n" fprintf(stderr, " -c counter The value of the counter.\n"
" Defaults to %s in env or '1'.\n\n", MP_env_sitecounter); " Defaults to %s in env or '1'.\n\n", MP_env_sitecounter);
fprintf(stderr, " -v variant The kind of content to generate.\n" fprintf(stderr, " -v variant The kind of content to generate.\n"

View File

@@ -31,10 +31,12 @@ const MPElementType TypeWithName(const char *typeName) {
return MPElementTypeGeneratedBasic; return MPElementTypeGeneratedBasic;
if (0 == strcmp(lowerTypeName, "s") || 0 == strcmp(lowerTypeName, "short")) if (0 == strcmp(lowerTypeName, "s") || 0 == strcmp(lowerTypeName, "short"))
return MPElementTypeGeneratedShort; return MPElementTypeGeneratedShort;
if (0 == strcmp(lowerTypeName, "p") || 0 == strcmp(lowerTypeName, "pin")) if (0 == strcmp(lowerTypeName, "i") || 0 == strcmp(lowerTypeName, "pin"))
return MPElementTypeGeneratedPIN; return MPElementTypeGeneratedPIN;
if (0 == strcmp(lowerTypeName, "n") || 0 == strcmp(lowerTypeName, "name")) if (0 == strcmp(lowerTypeName, "n") || 0 == strcmp(lowerTypeName, "name"))
return MPElementTypeGeneratedName; return MPElementTypeGeneratedName;
if (0 == strcmp(lowerTypeName, "p") || 0 == strcmp(lowerTypeName, "phrase"))
return MPElementTypeGeneratedPhrase;
fprintf(stderr, "Not a generated type name: %s", lowerTypeName); fprintf(stderr, "Not a generated type name: %s", lowerTypeName);
abort(); abort();
@@ -72,6 +74,10 @@ const char *CipherForType(MPElementType type, uint8_t seedByte) {
case MPElementTypeGeneratedName: { case MPElementTypeGeneratedName: {
return "cvccvcvcv"; return "cvccvcvcv";
} }
case MPElementTypeGeneratedPhrase: {
char *ciphers[] = { "cvcc cvc cvccvcv cvc", "cvc cvccvcvcv cvcv", "cv cvccv cvc cvcvccv" };
return ciphers[seedByte % 3];
}
default: { default: {
fprintf(stderr, "Unknown generated type: %d", type); fprintf(stderr, "Unknown generated type: %d", type);
abort(); abort();
@@ -148,6 +154,10 @@ const char CharacterFromClass(char characterClass, uint8_t seedByte) {
classCharacters = "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()"; classCharacters = "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()";
break; break;
} }
case ' ': {
classCharacters = " ";
break;
}
default: { default: {
fprintf(stderr, "Unknown character class: %c", characterClass); fprintf(stderr, "Unknown character class: %c", characterClass);
abort(); abort();

View File

@@ -34,7 +34,8 @@ typedef enum {
MPElementTypeGeneratedBasic = 0x4 | MPElementTypeClassGenerated | 0x0, MPElementTypeGeneratedBasic = 0x4 | MPElementTypeClassGenerated | 0x0,
MPElementTypeGeneratedShort = 0x3 | MPElementTypeClassGenerated | 0x0, MPElementTypeGeneratedShort = 0x3 | MPElementTypeClassGenerated | 0x0,
MPElementTypeGeneratedPIN = 0x5 | MPElementTypeClassGenerated | 0x0, MPElementTypeGeneratedPIN = 0x5 | MPElementTypeClassGenerated | 0x0,
MPElementTypeGeneratedName = 0xF | MPElementTypeClassGenerated | 0x0, MPElementTypeGeneratedName = 0xE | MPElementTypeClassGenerated | 0x0,
MPElementTypeGeneratedPhrase = 0xF | MPElementTypeClassGenerated | 0x0,
MPElementTypeStoredPersonal = 0x0 | MPElementTypeClassStored | MPElementFeatureExportContent, MPElementTypeStoredPersonal = 0x0 | MPElementTypeClassStored | MPElementFeatureExportContent,
MPElementTypeStoredDevicePrivate = 0x1 | MPElementTypeClassStored | MPElementFeatureDevicePrivate, MPElementTypeStoredDevicePrivate = 0x1 | MPElementTypeClassStored | MPElementFeatureDevicePrivate,

View File

@@ -16,10 +16,11 @@
// //
#import "MPKey.h" #import "MPKey.h"
#import "MPElementStoredEntity.h" #import "MPStoredSiteEntity.h"
#import "MPElementGeneratedEntity.h" #import "MPGeneratedSiteEntity.h"
#import "MPSiteQuestionEntity.h"
#define MPAlgorithmDefaultVersion 1 #define MPAlgorithmDefaultVersion 2
#define MPAlgorithmDefault MPAlgorithmForVersion(MPAlgorithmDefaultVersion) #define MPAlgorithmDefault MPAlgorithmForVersion(MPAlgorithmDefaultVersion)
id<MPAlgorithm> MPAlgorithmForVersion(NSUInteger version); id<MPAlgorithm> MPAlgorithmForVersion(NSUInteger version);
@@ -43,49 +44,56 @@ NSString *NSStringFromTimeToCrack(TimeToCrack timeToCrack);
@required @required
- (NSUInteger)version; - (NSUInteger)version;
- (BOOL)migrateUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc; - (BOOL)tryMigrateUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc;
- (BOOL)migrateElement:(MPElementEntity *)element explicit:(BOOL)explicit; - (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit;
- (MPKey *)keyForPassword:(NSString *)password ofUserNamed:(NSString *)userName; - (MPKey *)keyForPassword:(NSString *)password ofUserNamed:(NSString *)userName;
- (MPKey *)keyFromKeyData:(NSData *)keyData; - (MPKey *)keyFromKeyData:(NSData *)keyData;
- (NSData *)keyIDForKeyData:(NSData *)keyData; - (NSData *)keyIDForKeyData:(NSData *)keyData;
- (NSString *)scopeForVariant:(MPElementVariant)variant; - (NSString *)scopeForVariant:(MPSiteVariant)variant;
- (NSString *)nameOfType:(MPElementType)type; - (NSString *)nameOfType:(MPSiteType)type;
- (NSString *)shortNameOfType:(MPElementType)type; - (NSString *)shortNameOfType:(MPSiteType)type;
- (NSString *)classNameOfType:(MPElementType)type; - (NSString *)classNameOfType:(MPSiteType)type;
- (Class)classOfType:(MPElementType)type; - (Class)classOfType:(MPSiteType)type;
- (NSArray *)allTypes; - (NSArray *)allTypes;
- (NSArray *)allTypesStartingWith:(MPElementType)startingType; - (NSArray *)allTypesStartingWith:(MPSiteType)startingType;
- (MPElementType)nextType:(MPElementType)type; - (MPSiteType)nextType:(MPSiteType)type;
- (MPElementType)previousType:(MPElementType)type; - (MPSiteType)previousType:(MPSiteType)type;
- (NSString *)generateLoginForSiteNamed:(NSString *)name usingKey:(MPKey *)key; - (NSString *)generateLoginForSiteNamed:(NSString *)name usingKey:(MPKey *)key;
- (NSString *)generatePasswordForSiteNamed:(NSString *)name ofType:(MPElementType)type withCounter:(NSUInteger)counter - (NSString *)generatePasswordForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter
usingKey:(MPKey *)key; usingKey:(MPKey *)key;
- (NSString *)generateContentForSiteNamed:(NSString *)name ofType:(MPElementType)type withCounter:(NSUInteger)counter - (NSString *)generateAnswerForSiteNamed:(NSString *)name onQuestion:(NSString *)question usingKey:(MPKey *)key;
variant:(MPElementVariant)variant usingKey:(MPKey *)key; - (NSString *)generateContentForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter
variant:(MPSiteVariant)variant context:(NSString *)context usingKey:(MPKey *)key;
- (NSString *)storedLoginForElement:(MPElementStoredEntity *)element usingKey:(MPKey *)key; - (NSString *)storedLoginForSite:(MPStoredSiteEntity *)site usingKey:(MPKey *)key;
- (NSString *)storedPasswordForElement:(MPElementStoredEntity *)element usingKey:(MPKey *)key; - (NSString *)storedPasswordForSite:(MPStoredSiteEntity *)site usingKey:(MPKey *)key;
- (BOOL)savePassword:(NSString *)clearPassword toElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey; - (BOOL)savePassword:(NSString *)clearPassword toSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
- (NSString *)resolveLoginForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey; - (NSString *)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
- (NSString *)resolvePasswordForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey; - (NSString *)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
- (NSString *)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
- (NSString *)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)siteKey;
- (void)resolveLoginForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey - (void)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey
result:(void ( ^ )(NSString *result))resultBlock;
- (void)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey
result:(void ( ^ )(NSString *result))resultBlock; result:(void ( ^ )(NSString *result))resultBlock;
- (void)resolvePasswordForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey - (void)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey
result:(void ( ^ )(NSString *result))resultBlock; result:(void ( ^ )(NSString *result))resultBlock;
- (void)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)siteKey
result:(void ( ^ )(NSString *result))resultBlock;
- (void)importProtectedPassword:(NSString *)protectedPassword protectedByKey:(MPKey *)importKey - (void)importProtectedPassword:(NSString *)protectedPassword protectedByKey:(MPKey *)importKey
intoElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey; intoSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
- (void)importClearTextPassword:(NSString *)clearPassword intoElement:(MPElementEntity *)element - (void)importClearTextPassword:(NSString *)clearPassword intoSite:(MPSiteEntity *)site
usingKey:(MPKey *)elementKey; usingKey:(MPKey *)siteKey;
- (NSString *)exportPasswordForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey; - (NSString *)exportPasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordOfType:(MPElementType)type byAttacker:(MPAttacker)attacker; - (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordOfType:(MPSiteType)type byAttacker:(MPAttacker)attacker;
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordString:(NSString *)password byAttacker:(MPAttacker)attacker; - (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordString:(NSString *)password byAttacker:(MPAttacker)attacker;
@end @end

View File

@@ -1,12 +1,12 @@
/** /**
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com) * Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
* *
* See the enclosed file LICENSE for license information (LGPLv3). If you did * 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 * not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
* *
* @author Maarten Billemont <lhunath@lyndir.com> * @author Maarten Billemont <lhunath@lyndir.com>
* @license http://www.gnu.org/licenses/lgpl-3.0.txt * @license http://www.gnu.org/licenses/lgpl-3.0.txt
*/ */
// //
// MPAlgorithm // MPAlgorithm
@@ -24,7 +24,7 @@ id<MPAlgorithm> MPAlgorithmForVersion(NSUInteger version) {
versionToAlgorithm = [NSMutableDictionary dictionary]; versionToAlgorithm = [NSMutableDictionary dictionary];
id<MPAlgorithm> algorithm = versionToAlgorithm[@(version)]; id<MPAlgorithm> algorithm = versionToAlgorithm[@(version)];
if (!algorithm) if ((algorithm = [NSClassFromString( strf( @"MPAlgorithmV%lu", (unsigned long)version ) ) new])) if (!algorithm && (algorithm = (id<MPAlgorithm>)[NSClassFromString( strf( @"MPAlgorithmV%lu", (unsigned long)version ) ) new]))
versionToAlgorithm[@(version)] = algorithm; versionToAlgorithm[@(version)] = algorithm;
return algorithm; return algorithm;
@@ -33,8 +33,11 @@ id<MPAlgorithm> MPAlgorithmForVersion(NSUInteger version) {
id<MPAlgorithm> MPAlgorithmDefaultForBundleVersion(NSString *bundleVersion) { id<MPAlgorithm> MPAlgorithmDefaultForBundleVersion(NSString *bundleVersion) {
if (PearlCFBundleVersionCompare( bundleVersion, @"1.3" ) == NSOrderedAscending) if (PearlCFBundleVersionCompare( bundleVersion, @"1.3" ) == NSOrderedAscending)
// Pre-1.3 // Pre-1.3
return MPAlgorithmForVersion( 0 ); return MPAlgorithmForVersion( 0 );
if (PearlCFBundleVersionCompare( bundleVersion, @"2.1" ) == NSOrderedAscending)
// Pre-2.1
return MPAlgorithmForVersion( 1 );
return MPAlgorithmDefault; return MPAlgorithmDefault;
} }

View File

@@ -20,7 +20,7 @@
@interface MPAlgorithmV0 : NSObject<MPAlgorithm> @interface MPAlgorithmV0 : NSObject<MPAlgorithm>
- (NSDictionary *)allCiphers; - (NSDictionary *)allCiphers;
- (NSArray *)ciphersForType:(MPElementType)type; - (NSArray *)ciphersForType:(MPSiteType)type;
- (NSArray *)cipherClasses; - (NSArray *)cipherClasses;
- (NSArray *)cipherClassCharacters; - (NSArray *)cipherClassCharacters;
- (NSString *)charactersForCipherClass:(NSString *)cipherClass; - (NSString *)charactersForCipherClass:(NSString *)cipherClass;

View File

@@ -17,6 +17,9 @@
#import "MPAlgorithmV0.h" #import "MPAlgorithmV0.h"
#import "MPEntities.h" #import "MPEntities.h"
#import "MPAppDelegate_Shared.h"
#import "MPAppDelegate_InApp.h"
#import "MPSiteQuestionEntity.h"
#include <openssl/bn.h> #include <openssl/bn.h>
#include <openssl/err.h> #include <openssl/err.h>
@@ -70,40 +73,40 @@
return [(id<MPAlgorithm>)other version] == [self version]; return [(id<MPAlgorithm>)other version] == [self version];
} }
- (BOOL)migrateUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc { - (BOOL)tryMigrateUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc {
NSError *error = nil; NSError *error = nil;
NSFetchRequest *migrationRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )]; NSFetchRequest *migrationRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
migrationRequest.predicate = [NSPredicate predicateWithFormat:@"version_ < %d AND user == %@", self.version, user]; migrationRequest.predicate = [NSPredicate predicateWithFormat:@"version_ < %d AND user == %@", self.version, user];
NSArray *migrationElements = [moc executeFetchRequest:migrationRequest error:&error]; NSArray *migrationSites = [moc executeFetchRequest:migrationRequest error:&error];
if (!migrationElements) { if (!migrationSites) {
err( @"While looking for elements to migrate: %@", error ); err( @"While looking for sites to migrate: %@", [error fullDescription] );
return NO; return NO;
} }
BOOL requiresExplicitMigration = NO; BOOL success = YES;
for (MPElementEntity *migrationElement in migrationElements) for (MPSiteEntity *migrationSite in migrationSites)
if (![migrationElement migrateExplicitly:NO]) if (![migrationSite tryMigrateExplicitly:NO])
requiresExplicitMigration = YES; success = NO;
return requiresExplicitMigration; return success;
} }
- (BOOL)migrateElement:(MPElementEntity *)element explicit:(BOOL)explicit { - (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit {
if (element.version != [self version] - 1) if (site.version != [self version] - 1)
// Only migrate from previous version. // Only migrate from previous version.
return NO; return NO;
if (!explicit) { if (!explicit) {
// This migration requires explicit permission. // This migration requires explicit permission.
element.requiresExplicitMigration = YES; site.requiresExplicitMigration = YES;
return NO; return NO;
} }
// Apply migration. // Apply migration.
element.requiresExplicitMigration = NO; site.requiresExplicitMigration = NO;
element.version = [self version]; site.version = [self version];
return YES; return YES;
} }
@@ -136,129 +139,140 @@
return [keyData hashWith:MP_hash]; return [keyData hashWith:MP_hash];
} }
- (NSString *)scopeForVariant:(MPElementVariant)variant { - (NSString *)scopeForVariant:(MPSiteVariant)variant {
switch (variant) { switch (variant) {
case MPElementVariantPassword: case MPSiteVariantPassword:
return @"com.lyndir.masterpassword"; return @"com.lyndir.masterpassword";
case MPElementVariantLogin: case MPSiteVariantLogin:
return @"com.lyndir.masterpassword.login"; return @"com.lyndir.masterpassword.login";
case MPSiteVariantAnswer:
return @"com.lyndir.masterpassword.answer";
} }
Throw( @"Unsupported variant: %ld", (long)variant ); Throw( @"Unsupported variant: %ld", (long)variant );
} }
- (NSString *)nameOfType:(MPElementType)type { - (NSString *)nameOfType:(MPSiteType)type {
if (!type) if (!type)
return nil; return nil;
switch (type) { switch (type) {
case MPElementTypeGeneratedMaximum: case MPSiteTypeGeneratedMaximum:
return @"Maximum Security Password"; return @"Maximum Security Password";
case MPElementTypeGeneratedLong: case MPSiteTypeGeneratedLong:
return @"Long Password"; return @"Long Password";
case MPElementTypeGeneratedMedium: case MPSiteTypeGeneratedMedium:
return @"Medium Password"; return @"Medium Password";
case MPElementTypeGeneratedBasic: case MPSiteTypeGeneratedBasic:
return @"Basic Password"; return @"Basic Password";
case MPElementTypeGeneratedShort: case MPSiteTypeGeneratedShort:
return @"Short Password"; return @"Short Password";
case MPElementTypeGeneratedPIN: case MPSiteTypeGeneratedPIN:
return @"PIN"; return @"PIN";
case MPElementTypeGeneratedName: case MPSiteTypeGeneratedName:
return @"Login Name"; return @"Login Name";
case MPElementTypeStoredPersonal: case MPSiteTypeGeneratedPhrase:
return @"Phrase";
case MPSiteTypeStoredPersonal:
return @"Personal Password"; return @"Personal Password";
case MPElementTypeStoredDevicePrivate: case MPSiteTypeStoredDevicePrivate:
return @"Device Private Password"; return @"Device Private Password";
} }
Throw( @"Type not supported: %lu", (long)type ); Throw( @"Type not supported: %lu", (long)type );
} }
- (NSString *)shortNameOfType:(MPElementType)type { - (NSString *)shortNameOfType:(MPSiteType)type {
if (!type) if (!type)
return nil; return nil;
switch (type) { switch (type) {
case MPElementTypeGeneratedMaximum: case MPSiteTypeGeneratedMaximum:
return @"Maximum"; return @"Maximum";
case MPElementTypeGeneratedLong: case MPSiteTypeGeneratedLong:
return @"Long"; return @"Long";
case MPElementTypeGeneratedMedium: case MPSiteTypeGeneratedMedium:
return @"Medium"; return @"Medium";
case MPElementTypeGeneratedBasic: case MPSiteTypeGeneratedBasic:
return @"Basic"; return @"Basic";
case MPElementTypeGeneratedShort: case MPSiteTypeGeneratedShort:
return @"Short"; return @"Short";
case MPElementTypeGeneratedPIN: case MPSiteTypeGeneratedPIN:
return @"PIN"; return @"PIN";
case MPElementTypeGeneratedName: case MPSiteTypeGeneratedName:
return @"Name"; return @"Name";
case MPElementTypeStoredPersonal: case MPSiteTypeGeneratedPhrase:
return @"Phrase";
case MPSiteTypeStoredPersonal:
return @"Personal"; return @"Personal";
case MPElementTypeStoredDevicePrivate: case MPSiteTypeStoredDevicePrivate:
return @"Device"; return @"Device";
} }
Throw( @"Type not supported: %lu", (long)type ); Throw( @"Type not supported: %lu", (long)type );
} }
- (NSString *)classNameOfType:(MPElementType)type { - (NSString *)classNameOfType:(MPSiteType)type {
return NSStringFromClass( [self classOfType:type] ); return NSStringFromClass( [self classOfType:type] );
} }
- (Class)classOfType:(MPElementType)type { - (Class)classOfType:(MPSiteType)type {
if (!type) if (!type)
Throw( @"No type given." ); Throw( @"No type given." );
switch (type) { switch (type) {
case MPElementTypeGeneratedMaximum: case MPSiteTypeGeneratedMaximum:
return [MPElementGeneratedEntity class]; return [MPGeneratedSiteEntity class];
case MPElementTypeGeneratedLong: case MPSiteTypeGeneratedLong:
return [MPElementGeneratedEntity class]; return [MPGeneratedSiteEntity class];
case MPElementTypeGeneratedMedium: case MPSiteTypeGeneratedMedium:
return [MPElementGeneratedEntity class]; return [MPGeneratedSiteEntity class];
case MPElementTypeGeneratedBasic: case MPSiteTypeGeneratedBasic:
return [MPElementGeneratedEntity class]; return [MPGeneratedSiteEntity class];
case MPElementTypeGeneratedShort: case MPSiteTypeGeneratedShort:
return [MPElementGeneratedEntity class]; return [MPGeneratedSiteEntity class];
case MPElementTypeGeneratedPIN: case MPSiteTypeGeneratedPIN:
return [MPElementGeneratedEntity class]; return [MPGeneratedSiteEntity class];
case MPElementTypeGeneratedName: case MPSiteTypeGeneratedName:
return [MPElementGeneratedEntity class]; return [MPGeneratedSiteEntity class];
case MPElementTypeStoredPersonal: case MPSiteTypeGeneratedPhrase:
return [MPElementStoredEntity class]; return [MPGeneratedSiteEntity class];
case MPElementTypeStoredDevicePrivate: case MPSiteTypeStoredPersonal:
return [MPElementStoredEntity class]; return [MPStoredSiteEntity class];
case MPSiteTypeStoredDevicePrivate:
return [MPStoredSiteEntity class];
} }
Throw( @"Type not supported: %lu", (long)type ); Throw( @"Type not supported: %lu", (long)type );
@@ -266,13 +280,13 @@
- (NSArray *)allTypes { - (NSArray *)allTypes {
return [self allTypesStartingWith:MPElementTypeGeneratedMaximum]; return [self allTypesStartingWith:MPSiteTypeGeneratedMaximum];
} }
- (NSArray *)allTypesStartingWith:(MPElementType)startingType { - (NSArray *)allTypesStartingWith:(MPSiteType)startingType {
NSMutableArray *allTypes = [[NSMutableArray alloc] initWithCapacity:8]; NSMutableArray *allTypes = [[NSMutableArray alloc] initWithCapacity:8];
MPElementType currentType = startingType; MPSiteType currentType = startingType;
do { do {
[allTypes addObject:@(currentType)]; [allTypes addObject:@(currentType)];
} while ((currentType = [self nextType:currentType]) != startingType); } while ((currentType = [self nextType:currentType]) != startingType);
@@ -280,33 +294,33 @@
return allTypes; return allTypes;
} }
- (MPElementType)nextType:(MPElementType)type { - (MPSiteType)nextType:(MPSiteType)type {
switch (type) { switch (type) {
case MPElementTypeGeneratedMaximum: case MPSiteTypeGeneratedMaximum:
return MPElementTypeGeneratedLong; return MPSiteTypeGeneratedLong;
case MPElementTypeGeneratedLong: case MPSiteTypeGeneratedLong:
return MPElementTypeGeneratedMedium; return MPSiteTypeGeneratedMedium;
case MPElementTypeGeneratedMedium: case MPSiteTypeGeneratedMedium:
return MPElementTypeGeneratedBasic; return MPSiteTypeGeneratedBasic;
case MPElementTypeGeneratedBasic: case MPSiteTypeGeneratedBasic:
return MPElementTypeGeneratedShort; return MPSiteTypeGeneratedShort;
case MPElementTypeGeneratedShort: case MPSiteTypeGeneratedShort:
return MPElementTypeGeneratedPIN; return MPSiteTypeGeneratedPIN;
case MPElementTypeGeneratedPIN: case MPSiteTypeGeneratedPIN:
return MPElementTypeStoredPersonal; return MPSiteTypeStoredPersonal;
case MPElementTypeStoredPersonal: case MPSiteTypeStoredPersonal:
return MPElementTypeStoredDevicePrivate; return MPSiteTypeStoredDevicePrivate;
case MPElementTypeStoredDevicePrivate: case MPSiteTypeStoredDevicePrivate:
return MPElementTypeGeneratedMaximum; return MPSiteTypeGeneratedMaximum;
default: default:
return MPElementTypeGeneratedLong; return MPSiteTypeGeneratedLong;
} }
} }
- (MPElementType)previousType:(MPElementType)type { - (MPSiteType)previousType:(MPSiteType)type {
MPElementType previousType = type, nextType = type; MPSiteType previousType = type, nextType = type;
while ((nextType = [self nextType:nextType]) != type) while ((nextType = [self nextType:nextType]) != type)
previousType = nextType; previousType = nextType;
@@ -325,7 +339,7 @@
return ciphers; return ciphers;
} }
- (NSArray *)ciphersForType:(MPElementType)type { - (NSArray *)ciphersForType:(MPSiteType)type {
NSString *typeClass = [self classNameOfType:type]; NSString *typeClass = [self classNameOfType:type];
NSString *typeName = [self nameOfType:type]; NSString *typeName = [self nameOfType:type];
@@ -349,42 +363,51 @@
- (NSString *)generateLoginForSiteNamed:(NSString *)name usingKey:(MPKey *)key { - (NSString *)generateLoginForSiteNamed:(NSString *)name usingKey:(MPKey *)key {
return [self generateContentForSiteNamed:name ofType:MPElementTypeGeneratedName withCounter:1 return [self generateContentForSiteNamed:name ofType:MPSiteTypeGeneratedName withCounter:1
variant:MPElementVariantLogin usingKey:key]; variant:MPSiteVariantLogin context:nil usingKey:key];
} }
- (NSString *)generatePasswordForSiteNamed:(NSString *)name ofType:(MPElementType)type withCounter:(NSUInteger)counter - (NSString *)generatePasswordForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter
usingKey:(MPKey *)key { usingKey:(MPKey *)key {
return [self generateContentForSiteNamed:name ofType:type withCounter:counter return [self generateContentForSiteNamed:name ofType:type withCounter:counter
variant:MPElementVariantPassword usingKey:key]; variant:MPSiteVariantPassword context:nil usingKey:key];
} }
- (NSString *)generateContentForSiteNamed:(NSString *)name ofType:(MPElementType)type withCounter:(NSUInteger)counter - (NSString *)generateAnswerForSiteNamed:(NSString *)name onQuestion:(NSString *)question usingKey:(MPKey *)key {
variant:(MPElementVariant)variant usingKey:(MPKey *)key {
return [self generateContentForSiteNamed:name ofType:MPSiteTypeGeneratedPhrase withCounter:1
variant:MPSiteVariantAnswer context:question usingKey:key];
}
- (NSString *)generateContentForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter
variant:(MPSiteVariant)variant context:(NSString *)context usingKey:(MPKey *)key {
// Determine the seed whose bytes will be used for calculating a password // Determine the seed whose bytes will be used for calculating a password
uint32_t ncounter = htonl( counter ), nnameLength = htonl( name.length ); uint32_t ncounter = htonl( counter ), nnameLength = htonl( name.length ), ncontextLength = htonl( context.length );
NSData *counterBytes = [NSData dataWithBytes:&ncounter length:sizeof( ncounter )]; NSData *counterBytes = [NSData dataWithBytes:&ncounter length:sizeof( ncounter )];
NSData *nameLengthBytes = [NSData dataWithBytes:&nnameLength length:sizeof( nnameLength )]; NSData *nameLengthBytes = [NSData dataWithBytes:&nnameLength length:sizeof( nnameLength )];
NSString *scope = [self scopeForVariant:variant];; NSData *contextLengthBytes = [NSData dataWithBytes:&ncontextLength length:sizeof( ncontextLength )];
trc( @"seed from: hmac-sha256(%@, %@ | %@ | %@ | %@)", NSString *scope = [self scopeForVariant:variant];
[key.keyData encodeBase64], scope, [nameLengthBytes encodeHex], name, [counterBytes encodeHex] ); trc( @"seed from: hmac-sha256(%@, %@ | %@ | %@ | %@ | %@)",
[[key keyID] encodeHex], scope, [nameLengthBytes encodeHex], name, [counterBytes encodeHex], context );
NSData *seed = [[NSData dataByConcatenatingDatas: NSData *seed = [[NSData dataByConcatenatingDatas:
[scope dataUsingEncoding:NSUTF8StringEncoding], [scope dataUsingEncoding:NSUTF8StringEncoding],
nameLengthBytes, nameLengthBytes,
[name dataUsingEncoding:NSUTF8StringEncoding], [name dataUsingEncoding:NSUTF8StringEncoding],
counterBytes, counterBytes,
context? contextLengthBytes: nil,
[context dataUsingEncoding:NSUTF8StringEncoding],
nil] nil]
hmacWith:PearlHashSHA256 key:key.keyData]; hmacWith:PearlHashSHA256 key:key.keyData];
trc( @"seed is: %@", [seed encodeBase64] ); trc( @"seed is: %@", [seed encodeHex] );
const char *seedBytes = seed.bytes; const char *seedBytes = seed.bytes;
// Determine the cipher from the first seed byte. // Determine the cipher from the first seed byte.
NSAssert( [seed length], @"Missing seed." ); NSAssert( [seed length], @"Missing seed." );
NSArray *typeCiphers = [self ciphersForType:type]; NSArray *typeCiphers = [self ciphersForType:type];
NSString *cipher = typeCiphers[htons( seedBytes[0] ) % [typeCiphers count]]; NSString *cipher = typeCiphers[htons( seedBytes[0] ) % [typeCiphers count]];
trc( @"type %@, ciphers: %@, selected: %@", [self nameOfType:type], typeCiphers, cipher ); trc( @"type %@ (%lu), ciphers: %@, selected: %@", [self nameOfType:type], (unsigned long)type, typeCiphers, cipher );
// Encode the content, character by character, using subsequent seed bytes and the 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." ); NSAssert( [seed length] >= [cipher length] + 1, @"Insufficient seed bytes to encode cipher." );
@@ -402,79 +425,80 @@
return content; return content;
} }
- (NSString *)storedLoginForElement:(MPElementStoredEntity *)element usingKey:(MPKey *)key { - (NSString *)storedLoginForSite:(MPStoredSiteEntity *)site usingKey:(MPKey *)key {
return nil; return nil;
} }
- (NSString *)storedPasswordForElement:(MPElementStoredEntity *)element usingKey:(MPKey *)key { - (NSString *)storedPasswordForSite:(MPStoredSiteEntity *)site usingKey:(MPKey *)key {
return [self decryptContent:element.contentObject usingKey:key]; return [self decryptContent:site.contentObject usingKey:key];
} }
- (BOOL)savePassword:(NSString *)clearContent toElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey { - (BOOL)savePassword:(NSString *)clearContent toSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
NSAssert( [elementKey.keyID isEqualToData:element.user.keyID], @"Element does not belong to current user." ); NSAssert( [siteKey.keyID isEqualToData:site.user.keyID], @"Site does not belong to current user." );
switch (element.type) { switch (site.type) {
case MPElementTypeGeneratedMaximum: case MPSiteTypeGeneratedMaximum:
case MPElementTypeGeneratedLong: case MPSiteTypeGeneratedLong:
case MPElementTypeGeneratedMedium: case MPSiteTypeGeneratedMedium:
case MPElementTypeGeneratedBasic: case MPSiteTypeGeneratedBasic:
case MPElementTypeGeneratedShort: case MPSiteTypeGeneratedShort:
case MPElementTypeGeneratedPIN: case MPSiteTypeGeneratedPIN:
case MPElementTypeGeneratedName: { case MPSiteTypeGeneratedName:
NSAssert( NO, @"Cannot save content to element with generated type %lu.", (long)element.type ); case MPSiteTypeGeneratedPhrase: {
wrn( @"Cannot save content to site with generated type %lu.", (long)site.type );
return NO; return NO;
} }
case MPElementTypeStoredPersonal: { case MPSiteTypeStoredPersonal: {
if (![element isKindOfClass:[MPElementStoredEntity class]]) { if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
wrn( @"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
(long)element.type, [element class] ); (long)site.type, [site class] );
return NO; return NO;
} }
NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding] NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding]
encryptWithSymmetricKey:[elementKey subKeyOfLength:PearlCryptKeySize].keyData padding:YES]; encryptWithSymmetricKey:[siteKey subKeyOfLength:PearlCryptKeySize].keyData padding:YES];
if ([((MPElementStoredEntity *)element).contentObject isEqualToData:encryptedContent]) if ([((MPStoredSiteEntity *)site).contentObject isEqualToData:encryptedContent])
return NO; return NO;
((MPElementStoredEntity *)element).contentObject = encryptedContent; ((MPStoredSiteEntity *)site).contentObject = encryptedContent;
return YES; return YES;
} }
case MPElementTypeStoredDevicePrivate: { case MPSiteTypeStoredDevicePrivate: {
if (![element isKindOfClass:[MPElementStoredEntity class]]) { if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
wrn( @"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
(long)element.type, [element class] ); (long)site.type, [site class] );
return NO; return NO;
} }
NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding] NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding]
encryptWithSymmetricKey:[elementKey subKeyOfLength:PearlCryptKeySize].keyData padding:YES]; encryptWithSymmetricKey:[siteKey subKeyOfLength:PearlCryptKeySize].keyData padding:YES];
NSDictionary *elementQuery = [self queryForDevicePrivateElementNamed:element.name]; NSDictionary *siteQuery = [self queryForDevicePrivateSiteNamed:site.name];
if (!encryptedContent) if (!encryptedContent)
[PearlKeyChain deleteItemForQuery:elementQuery]; [PearlKeyChain deleteItemForQuery:siteQuery];
else else
[PearlKeyChain addOrUpdateItemForQuery:elementQuery withAttributes:@{ [PearlKeyChain addOrUpdateItemForQuery:siteQuery withAttributes:@{
(__bridge id)kSecValueData : encryptedContent, (__bridge id)kSecValueData : encryptedContent,
#if TARGET_OS_IPHONE #if TARGET_OS_IPHONE
(__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleWhenUnlockedThisDeviceOnly, (__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
#endif #endif
}]; }];
((MPElementStoredEntity *)element).contentObject = nil; ((MPStoredSiteEntity *)site).contentObject = nil;
return YES; return YES;
} }
} }
Throw( @"Unsupported type: %ld", (long)element.type ); Throw( @"Unsupported type: %ld", (long)site.type );
} }
- (NSString *)resolveLoginForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey { - (NSString *)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
dispatch_group_t group = dispatch_group_create(); dispatch_group_t group = dispatch_group_create();
dispatch_group_enter( group ); dispatch_group_enter( group );
__block NSString *result = nil; __block NSString *result = nil;
[self resolveLoginForElement:element usingKey:elementKey result:^(NSString *result_) { [self resolveLoginForSite:site usingKey:siteKey result:^(NSString *result_) {
result = result_; result = result_;
dispatch_group_leave( group ); dispatch_group_leave( group );
}]; }];
@@ -483,12 +507,12 @@
return result; return result;
} }
- (NSString *)resolvePasswordForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey { - (NSString *)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
dispatch_group_t group = dispatch_group_create(); dispatch_group_t group = dispatch_group_create();
dispatch_group_enter( group ); dispatch_group_enter( group );
__block NSString *result = nil; __block NSString *result = nil;
[self resolvePasswordForElement:element usingKey:elementKey result:^(NSString *result_) { [self resolvePasswordForSite:site usingKey:siteKey result:^(NSString *result_) {
result = result_; result = result_;
dispatch_group_leave( group ); dispatch_group_leave( group );
}]; }];
@@ -497,88 +521,117 @@
return result; return result;
} }
- (void)resolveLoginForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey result:(void ( ^ )(NSString *result))resultBlock { - (NSString *)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
NSAssert( [elementKey.keyID isEqualToData:element.user.keyID], @"Element does not belong to current user." ); dispatch_group_t group = dispatch_group_create();
NSString *name = element.name; dispatch_group_enter( group );
BOOL loginGenerated = element.loginGenerated; __block NSString *result = nil;
NSString *loginName = loginGenerated? nil: element.loginName; [self resolveAnswerForSite:site usingKey:siteKey result:^(NSString *result_) {
result = result_;
dispatch_group_leave( group );
}];
dispatch_group_wait( group, DISPATCH_TIME_FOREVER );
return result;
}
- (NSString *)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)siteKey {
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter( group );
__block NSString *result = nil;
[self resolveAnswerForQuestion:question usingKey:siteKey result:^(NSString *result_) {
result = result_;
dispatch_group_leave( group );
}];
dispatch_group_wait( group, DISPATCH_TIME_FOREVER );
return result;
}
- (void)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock {
NSAssert( [siteKey.keyID isEqualToData:site.user.keyID], @"Site does not belong to current user." );
NSString *name = site.name;
BOOL loginGenerated = site.loginGenerated && [[MPAppDelegate_Shared get] isFeatureUnlocked:MPProductGenerateLogins];
NSString *loginName = loginGenerated? nil: site.loginName;
id<MPAlgorithm> algorithm = nil; id<MPAlgorithm> algorithm = nil;
if (!name.length) if (!name.length)
err( @"Missing name." ); err( @"Missing name." );
else if (!elementKey.keyData.length) else if (!siteKey.keyData.length)
err( @"Missing key." ); err( @"Missing key." );
else else
algorithm = element.algorithm; algorithm = site.algorithm;
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{ dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
if (loginGenerated) if (loginGenerated)
resultBlock( [algorithm generateLoginForSiteNamed:name usingKey:elementKey] ); resultBlock( [algorithm generateLoginForSiteNamed:name usingKey:siteKey] );
else else
resultBlock( loginName ); resultBlock( loginName );
} ); } );
} }
- (void)resolvePasswordForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey result:(void ( ^ )(NSString *result))resultBlock { - (void)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock {
NSAssert( [elementKey.keyID isEqualToData:element.user.keyID], @"Element does not belong to current user." ); NSAssert( [siteKey.keyID isEqualToData:site.user.keyID], @"Site does not belong to current user." );
switch (element.type) { switch (site.type) {
case MPElementTypeGeneratedMaximum: case MPSiteTypeGeneratedMaximum:
case MPElementTypeGeneratedLong: case MPSiteTypeGeneratedLong:
case MPElementTypeGeneratedMedium: case MPSiteTypeGeneratedMedium:
case MPElementTypeGeneratedBasic: case MPSiteTypeGeneratedBasic:
case MPElementTypeGeneratedShort: case MPSiteTypeGeneratedShort:
case MPElementTypeGeneratedPIN: case MPSiteTypeGeneratedPIN:
case MPElementTypeGeneratedName: { case MPSiteTypeGeneratedName:
if (![element isKindOfClass:[MPElementGeneratedEntity class]]) { case MPSiteTypeGeneratedPhrase: {
wrn( @"Element with generated type %lu is not an MPElementGeneratedEntity, but a %@.", if (![site isKindOfClass:[MPGeneratedSiteEntity class]]) {
(long)element.type, [element class] ); wrn( @"Site with generated type %lu is not an MPGeneratedSiteEntity, but a %@.",
(long)site.type, [site class] );
break; break;
} }
NSString *name = element.name; NSString *name = site.name;
MPElementType type = element.type; MPSiteType type = site.type;
NSUInteger counter = ((MPElementGeneratedEntity *)element).counter; NSUInteger counter = ((MPGeneratedSiteEntity *)site).counter;
id<MPAlgorithm> algorithm = nil; id<MPAlgorithm> algorithm = nil;
if (!element.name.length) if (!site.name.length)
err( @"Missing name." ); err( @"Missing name." );
else if (!elementKey.keyData.length) else if (!siteKey.keyData.length)
err( @"Missing key." ); err( @"Missing key." );
else else
algorithm = element.algorithm; algorithm = site.algorithm;
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{ dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
NSString *result = [algorithm generatePasswordForSiteNamed:name ofType:type withCounter:counter usingKey:elementKey]; NSString *result = [algorithm generatePasswordForSiteNamed:name ofType:type withCounter:counter usingKey:siteKey];
resultBlock( result ); resultBlock( result );
} ); } );
break; break;
} }
case MPElementTypeStoredPersonal: { case MPSiteTypeStoredPersonal: {
if (![element isKindOfClass:[MPElementStoredEntity class]]) { if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
wrn( @"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
(long)element.type, [element class] ); (long)site.type, [site class] );
break; break;
} }
NSData *encryptedContent = ((MPElementStoredEntity *)element).contentObject; NSData *encryptedContent = ((MPStoredSiteEntity *)site).contentObject;
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{ dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
NSString *result = [self decryptContent:encryptedContent usingKey:elementKey]; NSString *result = [self decryptContent:encryptedContent usingKey:siteKey];
resultBlock( result ); resultBlock( result );
} ); } );
break; break;
} }
case MPElementTypeStoredDevicePrivate: { case MPSiteTypeStoredDevicePrivate: {
NSAssert( [element isKindOfClass:[MPElementStoredEntity class]], NSAssert( [site isKindOfClass:[MPStoredSiteEntity class]],
@"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", (long)element.type, @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.", (long)site.type,
[element class] ); [site class] );
NSDictionary *elementQuery = [self queryForDevicePrivateElementNamed:element.name]; NSDictionary *siteQuery = [self queryForDevicePrivateSiteNamed:site.name];
NSData *encryptedContent = [PearlKeyChain dataOfItemForQuery:elementQuery]; NSData *encryptedContent = [PearlKeyChain dataOfItemForQuery:siteQuery];
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{ dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
NSString *result = [self decryptContent:encryptedContent usingKey:elementKey]; NSString *result = [self decryptContent:encryptedContent usingKey:siteKey];
resultBlock( result ); resultBlock( result );
} ); } );
break; break;
@@ -586,94 +639,135 @@
} }
} }
- (void)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock {
NSAssert( [siteKey.keyID isEqualToData:site.user.keyID], @"Site does not belong to current user." );
NSString *name = site.name;
id<MPAlgorithm> algorithm = nil;
if (!site.name.length)
err( @"Missing name." );
else if (!siteKey.keyData.length)
err( @"Missing key." );
else
algorithm = site.algorithm;
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
NSString *result = [algorithm generateAnswerForSiteNamed:name onQuestion:nil usingKey:siteKey];
resultBlock( result );
} );
}
- (void)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)siteKey
result:(void ( ^ )(NSString *result))resultBlock {
NSAssert( [siteKey.keyID isEqualToData:question.site.user.keyID], @"Site does not belong to current user." );
NSString *name = question.site.name;
NSString *keyword = question.keyword;
id<MPAlgorithm> algorithm = nil;
if (!name.length)
err( @"Missing name." );
else if (!siteKey.keyData.length)
err( @"Missing key." );
else
algorithm = question.site.algorithm;
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
NSString *result = [algorithm generateAnswerForSiteNamed:name onQuestion:keyword usingKey:siteKey];
resultBlock( result );
} );
}
- (void)importProtectedPassword:(NSString *)protectedContent protectedByKey:(MPKey *)importKey - (void)importProtectedPassword:(NSString *)protectedContent protectedByKey:(MPKey *)importKey
intoElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey { intoSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
NSAssert( [elementKey.keyID isEqualToData:element.user.keyID], @"Element does not belong to current user." ); NSAssert( [siteKey.keyID isEqualToData:site.user.keyID], @"Site does not belong to current user." );
switch (element.type) { switch (site.type) {
case MPElementTypeGeneratedMaximum: case MPSiteTypeGeneratedMaximum:
case MPElementTypeGeneratedLong: case MPSiteTypeGeneratedLong:
case MPElementTypeGeneratedMedium: case MPSiteTypeGeneratedMedium:
case MPElementTypeGeneratedBasic: case MPSiteTypeGeneratedBasic:
case MPElementTypeGeneratedShort: case MPSiteTypeGeneratedShort:
case MPElementTypeGeneratedPIN: case MPSiteTypeGeneratedPIN:
case MPElementTypeGeneratedName: case MPSiteTypeGeneratedName:
case MPSiteTypeGeneratedPhrase:
break; break;
case MPElementTypeStoredPersonal: { case MPSiteTypeStoredPersonal: {
if (![element isKindOfClass:[MPElementStoredEntity class]]) { if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
wrn( @"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
(long)element.type, [element class] ); (long)site.type, [site class] );
break; break;
} }
if ([importKey.keyID isEqualToData:elementKey.keyID]) if ([importKey.keyID isEqualToData:siteKey.keyID])
((MPElementStoredEntity *)element).contentObject = [protectedContent decodeBase64]; ((MPStoredSiteEntity *)site).contentObject = [protectedContent decodeBase64];
else { else {
NSString *clearContent = [self decryptContent:[protectedContent decodeBase64] usingKey:importKey]; NSString *clearContent = [self decryptContent:[protectedContent decodeBase64] usingKey:importKey];
[self importClearTextPassword:clearContent intoElement:element usingKey:elementKey]; [self importClearTextPassword:clearContent intoSite:site usingKey:siteKey];
} }
break; break;
} }
case MPElementTypeStoredDevicePrivate: case MPSiteTypeStoredDevicePrivate:
break; break;
} }
} }
- (void)importClearTextPassword:(NSString *)clearContent intoElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey { - (void)importClearTextPassword:(NSString *)clearContent intoSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
NSAssert( [elementKey.keyID isEqualToData:element.user.keyID], @"Element does not belong to current user." ); NSAssert( [siteKey.keyID isEqualToData:site.user.keyID], @"Site does not belong to current user." );
switch (element.type) { switch (site.type) {
case MPElementTypeGeneratedMaximum: case MPSiteTypeGeneratedMaximum:
case MPElementTypeGeneratedLong: case MPSiteTypeGeneratedLong:
case MPElementTypeGeneratedMedium: case MPSiteTypeGeneratedMedium:
case MPElementTypeGeneratedBasic: case MPSiteTypeGeneratedBasic:
case MPElementTypeGeneratedShort: case MPSiteTypeGeneratedShort:
case MPElementTypeGeneratedPIN: case MPSiteTypeGeneratedPIN:
case MPElementTypeGeneratedName: case MPSiteTypeGeneratedName:
case MPSiteTypeGeneratedPhrase:
break; break;
case MPElementTypeStoredPersonal: { case MPSiteTypeStoredPersonal: {
[self savePassword:clearContent toElement:element usingKey:elementKey]; [self savePassword:clearContent toSite:site usingKey:siteKey];
break; break;
} }
case MPElementTypeStoredDevicePrivate: case MPSiteTypeStoredDevicePrivate:
break; break;
} }
} }
- (NSString *)exportPasswordForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey { - (NSString *)exportPasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
NSAssert( [elementKey.keyID isEqualToData:element.user.keyID], @"Element does not belong to current user." ); NSAssert( [siteKey.keyID isEqualToData:site.user.keyID], @"Site does not belong to current user." );
if (!(element.type & MPElementFeatureExportContent)) if (!(site.type & MPSiteFeatureExportContent))
return nil; return nil;
NSString *result = nil; NSString *result = nil;
switch (element.type) { switch (site.type) {
case MPElementTypeGeneratedMaximum: case MPSiteTypeGeneratedMaximum:
case MPElementTypeGeneratedLong: case MPSiteTypeGeneratedLong:
case MPElementTypeGeneratedMedium: case MPSiteTypeGeneratedMedium:
case MPElementTypeGeneratedBasic: case MPSiteTypeGeneratedBasic:
case MPElementTypeGeneratedShort: case MPSiteTypeGeneratedShort:
case MPElementTypeGeneratedPIN: case MPSiteTypeGeneratedPIN:
case MPElementTypeGeneratedName: { case MPSiteTypeGeneratedName:
case MPSiteTypeGeneratedPhrase: {
result = nil; result = nil;
break; break;
} }
case MPElementTypeStoredPersonal: { case MPSiteTypeStoredPersonal: {
if (![element isKindOfClass:[MPElementStoredEntity class]]) { if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
wrn( @"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
(long)element.type, [element class] ); (long)site.type, [site class] );
break; break;
} }
result = [((MPElementStoredEntity *)element).contentObject encodeBase64]; result = [((MPStoredSiteEntity *)site).contentObject encodeBase64];
break; break;
} }
case MPElementTypeStoredDevicePrivate: { case MPSiteTypeStoredDevicePrivate: {
result = nil; result = nil;
break; break;
} }
@@ -687,7 +781,7 @@
return NO; return NO;
} }
- (NSDictionary *)queryForDevicePrivateElementNamed:(NSString *)name { - (NSDictionary *)queryForDevicePrivateSiteNamed:(NSString *)name {
return [PearlKeyChain createQueryForClass:kSecClassGenericPassword return [PearlKeyChain createQueryForClass:kSecClassGenericPassword
attributes:@{ attributes:@{
@@ -708,7 +802,7 @@
return [[NSString alloc] initWithBytes:decryptedContent.bytes length:decryptedContent.length encoding:NSUTF8StringEncoding]; return [[NSString alloc] initWithBytes:decryptedContent.bytes length:decryptedContent.length encoding:NSUTF8StringEncoding];
} }
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordOfType:(MPElementType)type byAttacker:(MPAttacker)attacker { - (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordOfType:(MPSiteType)type byAttacker:(MPAttacker)attacker {
if (!type) if (!type)
return NO; return NO;

View File

@@ -25,33 +25,34 @@
return 1; return 1;
} }
- (BOOL)migrateElement:(MPElementEntity *)element explicit:(BOOL)explicit { - (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit {
if (element.version != [self version] - 1) if (site.version != [self version] - 1)
// Only migrate from previous version. // Only migrate from previous version.
return NO; return NO;
if (!explicit) { if (!explicit) {
if (element.type & MPElementTypeClassGenerated) { if (site.type & MPSiteTypeClassGenerated) {
// This migration requires explicit permission for types of the generated class. // This migration requires explicit permission for types of the generated class.
element.requiresExplicitMigration = YES; site.requiresExplicitMigration = YES;
return NO; return NO;
} }
} }
// Apply migration. // Apply migration.
element.requiresExplicitMigration = NO; site.requiresExplicitMigration = NO;
element.version = [self version]; site.version = [self version];
return YES; return YES;
} }
- (NSString *)generateContentForSiteNamed:(NSString *)name ofType:(MPElementType)type withCounter:(NSUInteger)counter - (NSString *)generateContentForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter
variant:(MPElementVariant)variant usingKey:(MPKey *)key { variant:(MPSiteVariant)variant context:(NSString *)context usingKey:(MPKey *)key {
// Determine the seed whose bytes will be used for calculating a password // Determine the seed whose bytes will be used for calculating a password
uint32_t ncounter = htonl( counter ), nnameLength = htonl( name.length ); uint32_t ncounter = htonl( counter ), nnameLength = htonl( name.length ), ncontextLength = htonl( context.length );
NSData *counterBytes = [NSData dataWithBytes:&ncounter length:sizeof( ncounter )]; NSData *counterBytes = [NSData dataWithBytes:&ncounter length:sizeof( ncounter )];
NSData *nameLengthBytes = [NSData dataWithBytes:&nnameLength length:sizeof( nnameLength )]; NSData *nameLengthBytes = [NSData dataWithBytes:&nnameLength length:sizeof( nnameLength )];
NSData *contextLengthBytes = [NSData dataWithBytes:&ncontextLength length:sizeof( ncontextLength )];
NSString *scope = [self scopeForVariant:variant]; NSString *scope = [self scopeForVariant:variant];
trc( @"seed from: hmac-sha256(%@, %@ | %@ | %@ | %@)", trc( @"seed from: hmac-sha256(%@, %@ | %@ | %@ | %@)",
[[key keyID] encodeHex], scope, [nameLengthBytes encodeHex], name, [counterBytes encodeHex] ); [[key keyID] encodeHex], scope, [nameLengthBytes encodeHex], name, [counterBytes encodeHex] );
@@ -60,6 +61,8 @@
nameLengthBytes, nameLengthBytes,
[name dataUsingEncoding:NSUTF8StringEncoding], [name dataUsingEncoding:NSUTF8StringEncoding],
counterBytes, counterBytes,
context? contextLengthBytes: nil,
[context dataUsingEncoding:NSUTF8StringEncoding],
nil] nil]
hmacWith:PearlHashSHA256 key:key.keyData]; hmacWith:PearlHashSHA256 key:key.keyData];
trc( @"seed is: %@", [seed encodeHex] ); trc( @"seed is: %@", [seed encodeHex] );
@@ -69,7 +72,7 @@
NSAssert( [seed length], @"Missing seed." ); NSAssert( [seed length], @"Missing seed." );
NSArray *typeCiphers = [self ciphersForType:type]; NSArray *typeCiphers = [self ciphersForType:type];
NSString *cipher = typeCiphers[seedBytes[0] % [typeCiphers count]]; NSString *cipher = typeCiphers[seedBytes[0] % [typeCiphers count]];
trc( @"type %@ (%d), ciphers: %@, selected: %@", [self nameOfType:type], type, typeCiphers, cipher ); trc( @"type %@ (%lu), ciphers: %@, selected: %@", [self nameOfType:type], (unsigned long)type, typeCiphers, cipher );
// Encode the content, character by character, using subsequent seed bytes and the 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." ); NSAssert( [seed length] >= [cipher length] + 1, @"Insufficient seed bytes to encode cipher." );

View File

@@ -0,0 +1,21 @@
/**
* 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
*/
//
// MPAlgorithmV1
//
// Created by Maarten Billemont on 17/07/12.
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
//
#import "MPAlgorithmV1.h"
@interface MPAlgorithmV2 : MPAlgorithmV1
@end

View File

@@ -0,0 +1,97 @@
/**
* 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
*/
//
// MPAlgorithmV1
//
// Created by Maarten Billemont on 17/07/12.
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
//
#import <objc/runtime.h>
#import "MPAlgorithmV2.h"
#import "MPEntities.h"
@implementation MPAlgorithmV2
- (NSUInteger)version {
return 2;
}
- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit {
if (site.version != [self version] - 1)
// Only migrate from previous version.
return NO;
if (!explicit) {
if (site.type & MPSiteTypeClassGenerated && site.name.length != [site.name dataUsingEncoding:NSUTF8StringEncoding].length) {
// This migration requires explicit permission for types of the generated class.
site.requiresExplicitMigration = YES;
return NO;
}
}
// Apply migration.
site.requiresExplicitMigration = NO;
site.version = [self version];
return YES;
}
- (NSString *)generateContentForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter
variant:(MPSiteVariant)variant context:(NSString *)context usingKey:(MPKey *)key {
// Determine the seed whose bytes will be used for calculating a password
NSData *nameBytes = [name dataUsingEncoding:NSUTF8StringEncoding];
NSData *contextBytes = [context dataUsingEncoding:NSUTF8StringEncoding];
uint32_t ncounter = htonl( counter ), nnameLength = htonl( nameBytes.length ), ncontextLength = htonl( contextBytes.length );
NSData *counterBytes = [NSData dataWithBytes:&ncounter length:sizeof( ncounter )];
NSData *nameLengthBytes = [NSData dataWithBytes:&nnameLength length:sizeof( nnameLength )];
NSData *contextLengthBytes = [NSData dataWithBytes:&ncontextLength length:sizeof( ncontextLength )];
NSString *scope = [self scopeForVariant:variant];
NSData *scopeBytes = [scope dataUsingEncoding:NSUTF8StringEncoding];
trc( @"seed from: hmac-sha256(%@, %@ | %@ | %@ | %@)",
[[key keyID] encodeHex], scope, [nameLengthBytes encodeHex], name, [counterBytes encodeHex] );
NSData *seed = [[NSData dataByConcatenatingDatas:
scopeBytes,
nameLengthBytes,
nameBytes,
counterBytes,
context? contextLengthBytes: nil,
contextBytes,
nil]
hmacWith:PearlHashSHA256 key:key.keyData];
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 %@ (%lu), ciphers: %@, selected: %@", [self nameOfType:type], (unsigned long)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." );
NSMutableString *content = [NSMutableString stringWithCapacity:[cipher length]];
for (NSUInteger c = 0; c < [cipher length]; ++c) {
uint16_t keyByte = seedBytes[c + 1];
NSString *cipherClass = [cipher substringWithRange:NSMakeRange( c, 1 )];
NSString *cipherClassCharacters = [self charactersForCipherClass:cipherClass];
NSString *character = [cipherClassCharacters substringWithRange:NSMakeRange( keyByte % [cipherClassCharacters length], 1 )];
trc( @"class %@ has characters: %@, index: %u, selected: %@", cipherClass, cipherClassCharacters, keyByte, character );
[content appendString:character];
}
return content;
}
@end

View File

@@ -0,0 +1,37 @@
//
// MPAppDelegate_Key.h
// MasterPassword
//
// Created by Maarten Billemont on 24/11/11.
// Copyright (c) 2011 Lyndir. All rights reserved.
//
#import <StoreKit/StoreKit.h>
#import "MPAppDelegate_Shared.h"
#define MPProductGenerateLogins @"com.lyndir.masterpassword.products.generatelogins"
#define MPProductGenerateAnswers @"com.lyndir.masterpassword.products.generateanswers"
#define MPProductFuel @"com.lyndir.masterpassword.products.fuel"
#define MP_FUEL_HOURLY_RATE 30.f /* Tier 1 purchases/h ~> USD/h */
@protocol MPInAppDelegate
- (void)updateWithProducts:(NSArray /* SKProduct */ *)products;
- (void)updateWithTransaction:(SKPaymentTransaction *)transaction;
@end
@interface MPAppDelegate_Shared(InApp)
- (void)registerProductsObserver:(id<MPInAppDelegate>)delegate;
- (void)removeProductsObserver:(id<MPInAppDelegate>)delegate;
- (void)reloadProducts;
- (BOOL)canMakePayments;
- (BOOL)isFeatureUnlocked:(NSString *)productIdentifier;
- (void)restoreCompletedTransactions;
- (void)purchaseProductWithIdentifier:(NSString *)productIdentifier quantity:(NSInteger)quantity;
@end

View File

@@ -0,0 +1,167 @@
//
// MPAppDelegate.m
// MasterPassword
//
// Created by Maarten Billemont on 24/11/11.
// Copyright (c) 2011 Lyndir. All rights reserved.
//
#import "MPAppDelegate_InApp.h"
@interface MPAppDelegate_Shared(InApp_Private)<SKProductsRequestDelegate, SKPaymentTransactionObserver>
@end
@implementation MPAppDelegate_Shared(InApp)
PearlAssociatedObjectProperty( NSArray*, Products, products );
PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObservers );
- (void)registerProductsObserver:(id<MPInAppDelegate>)delegate {
if (!self.productObservers)
self.productObservers = [NSMutableArray array];
[self.productObservers addObject:delegate];
if (self.products)
[delegate updateWithProducts:self.products];
else
[self reloadProducts];
}
- (void)removeProductsObserver:(id<MPInAppDelegate>)delegate {
[self.productObservers removeObject:delegate];
}
- (void)reloadProducts {
SKProductsRequest *productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:
[[NSSet alloc] initWithObjects:MPProductGenerateLogins, MPProductGenerateAnswers, MPProductFuel, nil]];
productsRequest.delegate = self;
[productsRequest start];
}
- (SKPaymentQueue *)paymentQueue {
static dispatch_once_t once = 0;
dispatch_once( &once, ^{
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
} );
return [SKPaymentQueue defaultQueue];
}
- (BOOL)canMakePayments {
return [SKPaymentQueue canMakePayments];
}
- (BOOL)isFeatureUnlocked:(NSString *)productIdentifier {
if (![productIdentifier length])
// Missing a product.
return NO;
if ([productIdentifier isEqualToString:MPProductFuel])
// Consumable product.
return NO;
#if ADHOC || DEBUG
// All features are unlocked for beta / debug versions.
return YES;
#else
// Check if product is purchased.
return [[NSUserDefaults standardUserDefaults] objectForKey:productIdentifier] != nil;
#endif
}
- (void)restoreCompletedTransactions {
[[self paymentQueue] restoreCompletedTransactions];
}
- (void)purchaseProductWithIdentifier:(NSString *)productIdentifier quantity:(NSInteger)quantity {
for (SKProduct *product in self.products)
if ([product.productIdentifier isEqualToString:productIdentifier]) {
SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product];
payment.quantity = quantity;
[[self paymentQueue] addPayment:payment];
return;
}
}
#pragma mark - SKProductsRequestDelegate
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
inf( @"products: %@, invalid: %@", response.products, response.invalidProductIdentifiers );
self.products = response.products;
for (id<MPInAppDelegate> productObserver in self.productObservers)
[productObserver updateWithProducts:self.products];
}
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
#if TARGET_OS_IPHONE
[PearlAlert showAlertWithTitle:@"Purchase Failed" message:
strf( @"%@\n\n%@", error.localizedDescription,
@"Ensure you are online and try logging out and back into iTunes from your device's Settings." )
viewStyle:UIAlertViewStyleDefault initAlert:nil tappedButtonBlock:nil
cancelTitle:@"OK" otherTitles:nil];
#else
#endif
err( @"StoreKit request (%@) failed: %@", request, [error fullDescription] );
}
- (void)requestDidFinish:(SKRequest *)request {
dbg( @"StoreKit request (%@) finished.", request );
}
#pragma mark - SKPaymentTransactionObserver
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
for (SKPaymentTransaction *transaction in transactions) {
dbg( @"transaction updated: %@ -> %d", transaction.payment.productIdentifier, (int)(transaction.transactionState) );
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchased: {
inf( @"purchased: %@", transaction.payment.productIdentifier );
if ([transaction.payment.productIdentifier isEqualToString:MPProductFuel]) {
float currentFuel = [[MPiOSConfig get].developmentFuel floatValue];
float purchasedFuel = transaction.payment.quantity / MP_FUEL_HOURLY_RATE;
[MPiOSConfig get].developmentFuel = @(currentFuel + purchasedFuel);
}
[[NSUserDefaults standardUserDefaults] setObject:transaction.transactionIdentifier
forKey:transaction.payment.productIdentifier];
[queue finishTransaction:transaction];
break;
}
case SKPaymentTransactionStateRestored: {
inf( @"restored: %@", transaction.payment.productIdentifier );
[[NSUserDefaults standardUserDefaults] setObject:transaction.transactionIdentifier
forKey:transaction.payment.productIdentifier];
[queue finishTransaction:transaction];
break;
}
case SKPaymentTransactionStatePurchasing:
case SKPaymentTransactionStateDeferred:
break;
case SKPaymentTransactionStateFailed:
err( @"Transaction failed: %@, reason: %@", transaction.payment.productIdentifier, [transaction.error fullDescription] );
[queue finishTransaction:transaction];
break;
}
for (id<MPInAppDelegate> productObserver in self.productObservers)
[productObserver updateWithTransaction:transaction];
}
}
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error {
err( @"StoreKit restore failed: %@", [error fullDescription] );
}
@end

View File

@@ -9,6 +9,12 @@
#import "MPAppDelegate_Key.h" #import "MPAppDelegate_Key.h"
#import "MPAppDelegate_Store.h" #import "MPAppDelegate_Store.h"
@interface MPAppDelegate_Shared()
@property(strong, nonatomic) MPKey *key;
@end
@implementation MPAppDelegate_Shared(Key) @implementation MPAppDelegate_Shared(Key)
static NSDictionary *keyQuery(MPUserEntity *user) { static NSDictionary *keyQuery(MPUserEntity *user) {
@@ -85,8 +91,8 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
if ([password length] && (tryKey = [MPAlgorithmDefault keyForPassword:password ofUserNamed:user.name])) { if ([password length] && (tryKey = [MPAlgorithmDefault keyForPassword:password ofUserNamed:user.name])) {
user.keyID = tryKey.keyID; user.keyID = tryKey.keyID;
// Migrate existing elements. // Migrate existing sites.
[self migrateElementsForUser:user saveInContext:moc toKey:tryKey]; [self migrateSitesForUser:user saveInContext:moc toKey:tryKey];
} }
} }
@@ -158,23 +164,23 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
return YES; return YES;
} }
- (void)migrateElementsForUser:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc toKey:(MPKey *)newKey { - (void)migrateSitesForUser:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc toKey:(MPKey *)newKey {
if (![user.elements count]) if (![user.sites count])
// Nothing to migrate. // Nothing to migrate.
return; return;
MPKey *recoverKey = newKey; MPKey *recoverKey = newKey;
#ifdef PEARL_UIKIT #ifdef PEARL_UIKIT
PearlOverlay *activityOverlay = [PearlOverlay showProgressOverlayWithTitle:PearlString( @"Migrating %ld sites...", PearlOverlay *activityOverlay = [PearlOverlay showProgressOverlayWithTitle:PearlString( @"Migrating %ld sites...",
(long)[user.elements count] )]; (long)[user.sites count] )];
#endif #endif
for (MPElementEntity *element in user.elements) { for (MPSiteEntity *site in user.sites) {
if (element.type & MPElementTypeClassStored) { if (site.type & MPSiteTypeClassStored) {
NSString *content; NSString *content;
while (!(content = [element.algorithm storedPasswordForElement:(MPElementStoredEntity *)element usingKey:recoverKey])) { while (!(content = [site.algorithm storedPasswordForSite:(MPStoredSiteEntity *)site usingKey:recoverKey])) {
// Failed to decrypt element with the current recoveryKey. Ask user for a new one to use. // Failed to decrypt site with the current recoveryKey. Ask user for a new one to use.
__block NSString *masterPassword = nil; __block NSString *masterPassword = nil;
#ifdef PEARL_UIKIT #ifdef PEARL_UIKIT
@@ -182,7 +188,7 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
dispatch_group_enter( recoverPasswordGroup ); dispatch_group_enter( recoverPasswordGroup );
[PearlAlert showAlertWithTitle:@"Enter Old Master Password" [PearlAlert showAlertWithTitle:@"Enter Old Master Password"
message:PearlString( @"Your old master password is required to migrate the stored password for %@", message:PearlString( @"Your old master password is required to migrate the stored password for %@",
element.name ) site.name )
viewStyle:UIAlertViewStyleSecureTextInput viewStyle:UIAlertViewStyleSecureTextInput
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) { initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
@try { @try {
@@ -202,7 +208,7 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
// Don't Migrate // Don't Migrate
break; break;
recoverKey = [element.algorithm keyForPassword:masterPassword ofUserNamed:user.name]; recoverKey = [site.algorithm keyForPassword:masterPassword ofUserNamed:user.name];
} }
if (!content) if (!content)
@@ -210,7 +216,7 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
break; break;
if (![recoverKey isEqualToKey:newKey]) if (![recoverKey isEqualToKey:newKey])
[element.algorithm savePassword:content toElement:element usingKey:newKey]; [site.algorithm savePassword:content toSite:site usingKey:newKey];
} }
} }

View File

@@ -9,19 +9,19 @@
#import "MPEntities.h" #import "MPEntities.h"
#if TARGET_OS_IPHONE #if TARGET_OS_IPHONE
@interface MPAppDelegate_Shared : PearlAppDelegate @interface MPAppDelegate_Shared : PearlAppDelegate
#else #else
@interface MPAppDelegate_Shared : NSObject <PearlConfigDelegate> @interface MPAppDelegate_Shared : NSObject <PearlConfigDelegate>
#endif #endif
@property(strong, nonatomic) MPKey *key; @property(strong, nonatomic, readonly) MPKey *key;
@property(strong, nonatomic) NSManagedObjectID *activeUserOID; @property(strong, nonatomic, readonly) NSManagedObjectID *activeUserOID;
+ (instancetype)get; + (instancetype)get;
- (MPUserEntity *)activeUserForMainThread; - (MPUserEntity *)activeUserForMainThread;
- (MPUserEntity *)activeUserInContext:(NSManagedObjectContext *)context; - (MPUserEntity *)activeUserInContext:(NSManagedObjectContext *)context;
- (void)setActiveUser:(MPUserEntity *)activeUser; - (void)setActiveUser:(MPUserEntity *)activeUser;
- (void)handleCoordinatorError:(NSError *)error;
@end @end

View File

@@ -6,10 +6,18 @@
// Copyright (c) 2011 Lyndir. All rights reserved. // Copyright (c) 2011 Lyndir. All rights reserved.
// //
#import <StoreKit/StoreKit.h>
#import "MPAppDelegate_Shared.h" #import "MPAppDelegate_Shared.h"
#import "MPAppDelegate_Store.h" #import "MPAppDelegate_Store.h"
#import "MPAppDelegate_Key.h" #import "MPAppDelegate_Key.h"
@interface MPAppDelegate_Shared ()
@property(strong, nonatomic) MPKey *key;
@property(strong, nonatomic) NSManagedObjectID *activeUserOID;
@end
@implementation MPAppDelegate_Shared @implementation MPAppDelegate_Shared
+ (MPAppDelegate_Shared *)get { + (MPAppDelegate_Shared *)get {
@@ -45,9 +53,13 @@
NSError *error; NSError *error;
if (activeUser.objectID.isTemporaryID && ![activeUser.managedObjectContext obtainPermanentIDsForObjects:@[ activeUser ] error:&error]) if (activeUser.objectID.isTemporaryID && ![activeUser.managedObjectContext obtainPermanentIDsForObjects:@[ activeUser ] error:&error])
err(@"Failed to obtain a permanent object ID after setting active user: %@", error); err(@"Failed to obtain a permanent object ID after setting active user: %@", [error fullDescription]);
self.activeUserOID = activeUser.objectID; self.activeUserOID = activeUser.objectID;
} }
- (void)handleCoordinatorError:(NSError *)error {
}
@end @end

View File

@@ -27,10 +27,11 @@ typedef NS_ENUM( NSUInteger, MPImportResult ) {
+ (BOOL)managedObjectContextPerformBlockAndWait:(void (^)(NSManagedObjectContext *context))mocBlock; + (BOOL)managedObjectContextPerformBlockAndWait:(void (^)(NSManagedObjectContext *context))mocBlock;
- (MPFixableResult)findAndFixInconsistenciesSaveInContext:(NSManagedObjectContext *)context; - (MPFixableResult)findAndFixInconsistenciesSaveInContext:(NSManagedObjectContext *)context;
- (void)deleteAndResetStore;
/** @param completion The block to execute after adding the element, executed from the main thread with the new element in the main MOC. */ /** @param completion The block to execute after adding the site, executed from the main thread with the new site in the main MOC. */
- (void)addElementNamed:(NSString *)siteName completion:(void ( ^ )(MPElementEntity *element, NSManagedObjectContext *context))completion; - (void)addSiteNamed:(NSString *)siteName completion:(void ( ^ )(MPSiteEntity *site, NSManagedObjectContext *context))completion;
- (MPElementEntity *)changeElement:(MPElementEntity *)element saveInContext:(NSManagedObjectContext *)context toType:(MPElementType)type; - (MPSiteEntity *)changeSite:(MPSiteEntity *)site saveInContext:(NSManagedObjectContext *)context toType:(MPSiteType)type;
- (MPImportResult)importSites:(NSString *)importedSitesString - (MPImportResult)importSites:(NSString *)importedSitesString
askImportPassword:(NSString *(^)(NSString *userName))importPassword askImportPassword:(NSString *(^)(NSString *userName))importPassword
askUserPassword:(NSString *(^)(NSString *userName, NSUInteger importCount, NSUInteger deleteCount))userPassword; askUserPassword:(NSString *(^)(NSString *userName, NSUInteger importCount, NSUInteger deleteCount))userPassword;

View File

@@ -7,6 +7,8 @@
// //
#import "MPAppDelegate_Store.h" #import "MPAppDelegate_Store.h"
#import "MPGeneratedSiteEntity.h"
#import "NSManagedObjectModel+KCOrderedAccessorFix.h"
#if TARGET_OS_IPHONE #if TARGET_OS_IPHONE
#define STORE_OPTIONS NSPersistentStoreFileProtectionKey : NSFileProtectionComplete, #define STORE_OPTIONS NSPersistentStoreFileProtectionKey : NSFileProtectionComplete,
@@ -33,6 +35,8 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, PrivateManagedObjectCont
PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext, mainManagedObjectContext ); PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext, mainManagedObjectContext );
PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
#pragma mark - Core Data setup #pragma mark - Core Data setup
+ (NSManagedObjectContext *)managedObjectContextForMainThreadIfReady { + (NSManagedObjectContext *)managedObjectContextForMainThreadIfReady {
@@ -142,19 +146,39 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
self.privateManagedObjectContext = nil; self.privateManagedObjectContext = nil;
}]; }];
// Don't load when the store is corrupted.
if ([self.storeCorrupted boolValue])
return;
// Check if migration is necessary. // Check if migration is necessary.
[self migrateStore]; [self migrateStore];
// Create a new store coordinator. // Create a new store coordinator.
self.persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: if (!self.persistentStoreCoordinator) {
[NSManagedObjectModel mergedModelFromBundles:nil]]; NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];
[model kc_generateOrderedSetAccessors];
self.persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
}
NSError *error = nil; NSError *error = nil;
[self.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[self localStoreURL] NSURL *localStoreURL = [self localStoreURL];
options:@{ if (![[NSFileManager defaultManager] createDirectoryAtURL:[localStoreURL URLByDeletingLastPathComponent]
NSMigratePersistentStoresAutomaticallyOption : @YES, withIntermediateDirectories:YES attributes:nil error:&error]) {
NSInferMappingModelAutomaticallyOption : @YES, err( @"Couldn't create our application support directory: %@", [error fullDescription] );
STORE_OPTIONS return;
} error:&error]; }
if (![self.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[self localStoreURL]
options:@{
NSMigratePersistentStoresAutomaticallyOption : @YES,
NSInferMappingModelAutomaticallyOption : @YES,
STORE_OPTIONS
} error:&error]) {
err( @"Failed to open store: %@", [error fullDescription] );
self.storeCorrupted = @YES;
[self handleCoordinatorError:error];
return;
}
self.storeCorrupted = @NO;
// Create our contexts and observer. // Create our contexts and observer.
self.privateManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; self.privateManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
@@ -176,22 +200,19 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
}]; }];
#if TARGET_OS_IPHONE #if TARGET_OS_IPHONE
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillTerminateNotification object:UIApp PearlAddNotificationObserver( UIApplicationWillTerminateNotification, UIApp, [NSOperationQueue mainQueue],
queue:[NSOperationQueue mainQueue] usingBlock: ^(MPAppDelegate_Shared *self, NSNotification *note) {
^(NSNotification *note) { [self.mainManagedObjectContext saveToStore];
[self.mainManagedObjectContext saveToStore]; } );
}]; PearlAddNotificationObserver( UIApplicationDidEnterBackgroundNotification, UIApp, [NSOperationQueue mainQueue],
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillResignActiveNotification object:UIApp ^(MPAppDelegate_Shared *self, NSNotification *note) {
queue:[NSOperationQueue mainQueue] usingBlock: [self.mainManagedObjectContext saveToStore];
^(NSNotification *note) { } );
[self.mainManagedObjectContext saveToStore];
}];
#else #else
[[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationWillTerminateNotification object:NSApp PearlAddNotificationObserver( NSApplicationWillTerminateNotification, NSApp, [NSOperationQueue mainQueue],
queue:[NSOperationQueue mainQueue] usingBlock: ^(MPAppDelegate_Shared *self, NSNotification *note) {
^(NSNotification *note) { [self.mainManagedObjectContext saveToStore];
[self.mainManagedObjectContext saveToStore]; } );
}];
#endif #endif
// Perform a data sanity check on the newly loaded store to find and fix any issues. // Perform a data sanity check on the newly loaded store to find and fix any issues.
@@ -202,6 +223,33 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
} }
} }
- (void)deleteAndResetStore {
@synchronized (self) {
// 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;
}];
NSError *error = nil;
for (NSPersistentStore *store in self.persistentStoreCoordinator.persistentStores) {
if (![self.persistentStoreCoordinator removePersistentStore:store error:&error])
err( @"Couldn't remove persistence store from coordinator: %@", [error fullDescription] );
}
self.persistentStoreCoordinator = nil;
if (![[NSFileManager defaultManager] removeItemAtURL:self.localStoreURL error:&error])
err( @"Couldn't remove persistence store at URL %@: %@", self.localStoreURL, [error fullDescription] );
[self loadStore];
}
}
- (MPFixableResult)findAndFixInconsistenciesSaveInContext:(NSManagedObjectContext *)context { - (MPFixableResult)findAndFixInconsistenciesSaveInContext:(NSManagedObjectContext *)context {
NSError *error = nil; NSError *error = nil;
@@ -214,7 +262,7 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
fetchRequest.entity = entity; fetchRequest.entity = entity;
NSArray *objects = [context executeFetchRequest:fetchRequest error:&error]; NSArray *objects = [context executeFetchRequest:fetchRequest error:&error];
if (!objects) { if (!objects) {
err( @"Failed to fetch %@ objects: %@", entity, error ); err( @"Failed to fetch %@ objects: %@", entity, [error fullDescription] );
continue; continue;
} }
@@ -326,7 +374,7 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
#pragma mark - Utilities #pragma mark - Utilities
- (void)addElementNamed:(NSString *)siteName completion:(void ( ^ )(MPElementEntity *element, NSManagedObjectContext *context))completion { - (void)addSiteNamed:(NSString *)siteName completion:(void ( ^ )(MPSiteEntity *site, NSManagedObjectContext *context))completion {
if (![siteName length]) { if (![siteName length]) {
completion( nil, nil ); completion( nil, nil );
@@ -341,61 +389,61 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
return; return;
} }
MPElementType type = activeUser.defaultType; MPSiteType type = activeUser.defaultType;
NSString *typeEntityName = [MPAlgorithmDefault classNameOfType:type]; NSString *typeEntityName = [MPAlgorithmDefault classNameOfType:type];
MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:typeEntityName inManagedObjectContext:context]; MPSiteEntity *site = [NSEntityDescription insertNewObjectForEntityForName:typeEntityName inManagedObjectContext:context];
element.name = siteName; site.name = siteName;
element.user = activeUser; site.user = activeUser;
element.type = type; site.type = type;
element.lastUsed = [NSDate date]; site.lastUsed = [NSDate date];
element.version = MPAlgorithmDefaultVersion; site.version = MPAlgorithmDefaultVersion;
NSError *error = nil; NSError *error = nil;
if (element.objectID.isTemporaryID && ![context obtainPermanentIDsForObjects:@[ element ] error:&error]) if (site.objectID.isTemporaryID && ![context obtainPermanentIDsForObjects:@[ site ] error:&error])
err( @"Failed to obtain a permanent object ID after creating new element: %@", error ); err( @"Failed to obtain a permanent object ID after creating new site: %@", [error fullDescription] );
[context saveToStore]; [context saveToStore];
completion( element, context ); completion( site, context );
}]; }];
} }
- (MPElementEntity *)changeElement:(MPElementEntity *)element saveInContext:(NSManagedObjectContext *)context toType:(MPElementType)type { - (MPSiteEntity *)changeSite:(MPSiteEntity *)site saveInContext:(NSManagedObjectContext *)context toType:(MPSiteType)type {
if (element.type == type) if (site.type == type)
return element; return site;
if ([element.algorithm classOfType:type] == element.typeClass) { if ([site.algorithm classOfType:type] == site.typeClass) {
element.type = type; site.type = type;
[context saveToStore]; [context saveToStore];
} }
else { else {
// Type requires a different class of element. Recreate the element. // Type requires a different class of site. Recreate the site.
NSString *typeEntityName = [element.algorithm classNameOfType:type]; NSString *typeEntityName = [site.algorithm classNameOfType:type];
MPElementEntity *newElement = [NSEntityDescription insertNewObjectForEntityForName:typeEntityName inManagedObjectContext:context]; MPSiteEntity *newSite = [NSEntityDescription insertNewObjectForEntityForName:typeEntityName inManagedObjectContext:context];
newElement.type = type; newSite.type = type;
newElement.name = element.name; newSite.name = site.name;
newElement.user = element.user; newSite.user = site.user;
newElement.uses = element.uses; newSite.uses = site.uses;
newElement.lastUsed = element.lastUsed; newSite.lastUsed = site.lastUsed;
newElement.version = element.version; newSite.version = site.version;
newElement.loginName = element.loginName; newSite.loginName = site.loginName;
NSError *error = nil; NSError *error = nil;
if (![context obtainPermanentIDsForObjects:@[ newElement ] error:&error]) if (![context obtainPermanentIDsForObjects:@[ newSite ] error:&error])
err( @"Failed to obtain a permanent object ID after changing object type: %@", error ); err( @"Failed to obtain a permanent object ID after changing object type: %@", [error fullDescription] );
[context deleteObject:element]; [context deleteObject:site];
[context saveToStore]; [context saveToStore];
[[NSNotificationCenter defaultCenter] postNotificationName:MPElementUpdatedNotification object:element.objectID]; [[NSNotificationCenter defaultCenter] postNotificationName:MPSiteUpdatedNotification object:site.objectID];
element = newElement; site = newSite;
} }
[[NSNotificationCenter defaultCenter] postNotificationName:MPElementUpdatedNotification object:element.objectID]; [[NSNotificationCenter defaultCenter] postNotificationName:MPSiteUpdatedNotification object:site.objectID];
return element; return site;
} }
- (MPImportResult)importSites:(NSString *)importedSitesString - (MPImportResult)importSites:(NSString *)importedSitesString
@@ -431,7 +479,7 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
initWithPattern:@"^#[[:space:]]*([^:]+): (.*)" initWithPattern:@"^#[[:space:]]*([^:]+): (.*)"
options:(NSRegularExpressionOptions)0 error:&error]; options:(NSRegularExpressionOptions)0 error:&error];
if (error) { if (error) {
err( @"Error loading the header pattern: %@", error ); err( @"Error loading the header pattern: %@", [error fullDescription] );
return MPImportResultInternalError; return MPImportResultInternalError;
} }
} }
@@ -445,7 +493,7 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
options:(NSRegularExpressionOptions)0 error:&error] options:(NSRegularExpressionOptions)0 error:&error]
]; ];
if (error) { if (error) {
err( @"Error loading the site patterns: %@", error ); err( @"Error loading the site patterns: %@", [error fullDescription] );
return MPImportResultInternalError; return MPImportResultInternalError;
} }
} }
@@ -460,9 +508,9 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
NSData *importKeyID = nil; NSData *importKeyID = nil;
BOOL headerStarted = NO, headerEnded = NO, clearText = NO; BOOL headerStarted = NO, headerEnded = NO, clearText = NO;
NSArray *importedSiteLines = [importedSitesString componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]; NSArray *importedSiteLines = [importedSitesString componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
NSMutableSet *elementsToDelete = [NSMutableSet set]; NSMutableSet *sitesToDelete = [NSMutableSet set];
NSMutableArray *importedSiteElements = [NSMutableArray arrayWithCapacity:[importedSiteLines count]]; NSMutableArray *importedSiteSites = [NSMutableArray arrayWithCapacity:[importedSiteLines count]];
NSFetchRequest *elementFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )]; NSFetchRequest *siteFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
for (NSString *importedSiteLine in importedSiteLines) { for (NSString *importedSiteLine in importedSiteLines) {
if ([importedSiteLine hasPrefix:@"#"]) { if ([importedSiteLine hasPrefix:@"#"]) {
// Comment or header // Comment or header
@@ -484,10 +532,10 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
err( @"Invalid header format in line: %@", importedSiteLine ); err( @"Invalid header format in line: %@", importedSiteLine );
return MPImportResultMalformedInput; return MPImportResultMalformedInput;
} }
NSTextCheckingResult *headerElements = [[headerPattern matchesInString:importedSiteLine options:(NSMatchingOptions)0 NSTextCheckingResult *headerSites = [[headerPattern matchesInString:importedSiteLine options:(NSMatchingOptions)0
range:NSMakeRange( 0, [importedSiteLine length] )] lastObject]; range:NSMakeRange( 0, [importedSiteLine length] )] lastObject];
NSString *headerName = [importedSiteLine substringWithRange:[headerElements rangeAtIndex:1]]; NSString *headerName = [importedSiteLine substringWithRange:[headerSites rangeAtIndex:1]];
NSString *headerValue = [importedSiteLine substringWithRange:[headerElements rangeAtIndex:2]]; NSString *headerValue = [importedSiteLine substringWithRange:[headerSites rangeAtIndex:2]];
if ([headerName isEqualToString:@"User Name"]) { if ([headerName isEqualToString:@"User Name"]) {
importUserName = headerValue; importUserName = headerValue;
@@ -495,7 +543,7 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
userFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", importUserName]; userFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", importUserName];
NSArray *users = [context executeFetchRequest:userFetchRequest error:&error]; NSArray *users = [context executeFetchRequest:userFetchRequest error:&error];
if (!users) { if (!users) {
err( @"While looking for user: %@, error: %@", importUserName, error ); err( @"While looking for user: %@, error: %@", importUserName, [error fullDescription] );
return MPImportResultInternalError; return MPImportResultInternalError;
} }
if ([users count] > 1) { if ([users count] > 1) {
@@ -579,27 +627,27 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
// Find existing site. // Find existing site.
if (user) { if (user) {
elementFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND user == %@", siteName, user]; siteFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND user == %@", siteName, user];
NSArray *existingSites = [context executeFetchRequest:elementFetchRequest error:&error]; NSArray *existingSites = [context executeFetchRequest:siteFetchRequest error:&error];
if (!existingSites) { if (!existingSites) {
err( @"Lookup of existing sites failed for site: %@, user: %@, error: %@", siteName, user.userID, error ); err( @"Lookup of existing sites failed for site: %@, user: %@, error: %@", siteName, user.userID, [error fullDescription] );
return MPImportResultInternalError; return MPImportResultInternalError;
} }
if ([existingSites count]) { if ([existingSites count]) {
dbg( @"Existing sites: %@", existingSites ); dbg( @"Existing sites: %@", existingSites );
[elementsToDelete addObjectsFromArray:existingSites]; [sitesToDelete addObjectsFromArray:existingSites];
} }
} }
[importedSiteElements addObject:@[ lastUsed, uses, type, version, counter, loginName, siteName, exportContent ]]; [importedSiteSites addObject:@[ lastUsed, uses, type, version, counter, loginName, siteName, exportContent ]];
dbg( @"Will import site: lastUsed=%@, uses=%@, type=%@, version=%@, counter=%@, loginName=%@, siteName=%@, exportContent=%@", dbg( @"Will import site: lastUsed=%@, uses=%@, type=%@, version=%@, counter=%@, loginName=%@, siteName=%@, exportContent=%@",
lastUsed, uses, type, version, counter, loginName, siteName, exportContent ); lastUsed, uses, type, version, counter, loginName, siteName, exportContent );
} }
// Ask for confirmation to import these sites and the master password of the user. // Ask for confirmation to import these sites and the master password of the user.
inf( @"Importing %lu sites, deleting %lu sites, for user: %@", (unsigned long)[importedSiteElements count], inf( @"Importing %lu sites, deleting %lu sites, for user: %@", (unsigned long)[importedSiteSites count],
(unsigned long)[elementsToDelete count], [MPUserEntity idFor:importUserName] ); (unsigned long)[sitesToDelete count], [MPUserEntity idFor:importUserName] );
NSString *userMasterPassword = askUserPassword( user? user.name: importUserName, [importedSiteElements count], NSString *userMasterPassword = askUserPassword( user? user.name: importUserName, [importedSiteSites count],
[elementsToDelete count] ); [sitesToDelete count] );
if (!userMasterPassword) { if (!userMasterPassword) {
inf( @"Import cancelled." ); inf( @"Import cancelled." );
return MPImportResultCancelled; return MPImportResultCancelled;
@@ -615,8 +663,8 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
// Delete existing sites. // Delete existing sites.
if (elementsToDelete.count) if (sitesToDelete.count)
[elementsToDelete enumerateObjectsUsingBlock:^(id obj, BOOL *stop) { [sitesToDelete enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
inf( @"Deleting site: %@, it will be replaced by an imported site.", [obj name] ); inf( @"Deleting site: %@, it will be replaced by an imported site.", [obj name] );
[context deleteObject:obj]; [context deleteObject:obj];
}]; }];
@@ -637,10 +685,10 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
} }
// Import new sites. // Import new sites.
for (NSArray *siteElements in importedSiteElements) { for (NSArray *siteElements in importedSiteSites) {
NSDate *lastUsed = [[NSDateFormatter rfc3339DateFormatter] dateFromString:siteElements[0]]; NSDate *lastUsed = [[NSDateFormatter rfc3339DateFormatter] dateFromString:siteElements[0]];
NSUInteger uses = (unsigned)[siteElements[1] integerValue]; NSUInteger uses = (unsigned)[siteElements[1] integerValue];
MPElementType type = (MPElementType)[siteElements[2] integerValue]; MPSiteType type = (MPSiteType)[siteElements[2] integerValue];
NSUInteger version = (unsigned)[siteElements[3] integerValue]; NSUInteger version = (unsigned)[siteElements[3] integerValue];
NSUInteger counter = [siteElements[4] length]? (unsigned)[siteElements[4] integerValue]: NSNotFound; NSUInteger counter = [siteElements[4] length]? (unsigned)[siteElements[4] integerValue]: NSNotFound;
NSString *loginName = [siteElements[5] length]? siteElements[5]: nil; NSString *loginName = [siteElements[5] length]? siteElements[5]: nil;
@@ -649,24 +697,24 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
// Create new site. // Create new site.
NSString *typeEntityName = [MPAlgorithmForVersion( version ) classNameOfType:type]; NSString *typeEntityName = [MPAlgorithmForVersion( version ) classNameOfType:type];
MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:typeEntityName inManagedObjectContext:context]; MPSiteEntity *site = [NSEntityDescription insertNewObjectForEntityForName:typeEntityName inManagedObjectContext:context];
element.name = siteName; site.name = siteName;
element.loginName = loginName; site.loginName = loginName;
element.user = user; site.user = user;
element.type = type; site.type = type;
element.uses = uses; site.uses = uses;
element.lastUsed = lastUsed; site.lastUsed = lastUsed;
element.version = version; site.version = version;
if ([exportContent length]) { if ([exportContent length]) {
if (clearText) if (clearText)
[element.algorithm importClearTextPassword:exportContent intoElement:element usingKey:userKey]; [site.algorithm importClearTextPassword:exportContent intoSite:site usingKey:userKey];
else else
[element.algorithm importProtectedPassword:exportContent protectedByKey:importKey intoElement:element usingKey:userKey]; [site.algorithm importProtectedPassword:exportContent protectedByKey:importKey intoSite:site usingKey:userKey];
} }
if ([element isKindOfClass:[MPElementGeneratedEntity class]] && counter != NSNotFound) if ([site isKindOfClass:[MPGeneratedSiteEntity class]] && counter != NSNotFound)
((MPElementGeneratedEntity *)element).counter = counter; ((MPGeneratedSiteEntity *)site).counter = counter;
dbg( @"Created Element: %@", [element debugDescription] ); dbg( @"Created Site: %@", [site debugDescription] );
} }
if (![context saveToStore]) if (![context saveToStore])
@@ -712,27 +760,27 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
[export appendFormat:@"# used used type name\t name\tpassword\n"]; [export appendFormat:@"# used used type name\t name\tpassword\n"];
// Sites. // Sites.
for (MPElementEntity *element in activeUser.elements) { for (MPSiteEntity *site in activeUser.sites) {
NSDate *lastUsed = element.lastUsed; NSDate *lastUsed = site.lastUsed;
NSUInteger uses = element.uses; NSUInteger uses = site.uses;
MPElementType type = element.type; MPSiteType type = site.type;
NSUInteger version = element.version; NSUInteger version = site.version;
NSUInteger counter = 0; NSUInteger counter = 0;
NSString *loginName = element.loginName; NSString *loginName = site.loginName;
NSString *siteName = element.name; NSString *siteName = site.name;
NSString *content = nil; NSString *content = nil;
// Generated-specific // Generated-specific
if ([element isKindOfClass:[MPElementGeneratedEntity class]]) if ([site isKindOfClass:[MPGeneratedSiteEntity class]])
counter = ((MPElementGeneratedEntity *)element).counter; counter = ((MPGeneratedSiteEntity *)site).counter;
// Determine the content to export. // Determine the content to export.
if (!(type & MPElementFeatureDevicePrivate)) { if (!(type & MPSiteFeatureDevicePrivate)) {
if (revealPasswords) if (revealPasswords)
content = [element.algorithm resolvePasswordForElement:element usingKey:self.key]; content = [site.algorithm resolvePasswordForSite:site usingKey:self.key];
else if (type & MPElementFeatureExportContent) else if (type & MPSiteFeatureExportContent)
content = [element.algorithm exportPasswordForElement:element usingKey:self.key]; content = [site.algorithm exportPasswordForSite:site usingKey:self.key];
} }
[export appendFormat:@"%@ %8ld %8s %25s\t%25s\t%@\n", [export appendFormat:@"%@ %8ld %8s %25s\t%25s\t%@\n",

View File

@@ -1,27 +0,0 @@
//
// MPElementEntity.h
// MasterPassword-iOS
//
// Created by Maarten Billemont on 2014-09-14.
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
@class MPUserEntity;
@interface MPElementEntity : NSManagedObject
//@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

View File

@@ -1,16 +0,0 @@
//
// MPElementGeneratedEntity.m
// MasterPassword-iOS
//
// Created by Maarten Billemont on 2014-09-14.
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#import "MPElementGeneratedEntity.h"
@implementation MPElementGeneratedEntity
@dynamic counter_;
@end

View File

@@ -1,18 +0,0 @@
//
// MPElementStoredEntity.h
// MasterPassword-iOS
//
// 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;
@end

View File

@@ -1,16 +0,0 @@
//
// MPElementStoredEntity.m
// MasterPassword-iOS
//
// Created by Maarten Billemont on 2014-09-14.
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#import "MPElementStoredEntity.h"
@implementation MPElementStoredEntity
@dynamic contentObject;
@end

View File

@@ -1,5 +1,5 @@
// //
// MPElementEntities.h // MPEntities.h
// MasterPassword-iOS // MasterPassword-iOS
// //
// Created by Maarten Billemont on 31/05/12. // Created by Maarten Billemont on 31/05/12.
@@ -7,9 +7,9 @@
// //
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import "MPElementEntity.h" #import "MPSiteEntity.h"
#import "MPElementStoredEntity.h" #import "MPStoredSiteEntity.h"
#import "MPElementGeneratedEntity.h" #import "MPGeneratedSiteEntity.h"
#import "MPUserEntity.h" #import "MPUserEntity.h"
#import "MPAlgorithm.h" #import "MPAlgorithm.h"
#import "MPFixable.h" #import "MPFixable.h"
@@ -22,10 +22,10 @@
@end @end
@interface MPElementEntity(MP)<MPFixable> @interface MPSiteEntity(MP)<MPFixable>
@property(assign) BOOL loginGenerated; @property(assign) BOOL loginGenerated;
@property(assign) MPElementType type; @property(assign) MPSiteType type;
@property(readonly) NSString *typeName; @property(readonly) NSString *typeName;
@property(readonly) NSString *typeShortName; @property(readonly) NSString *typeShortName;
@property(readonly) NSString *typeClassName; @property(readonly) NSString *typeClassName;
@@ -36,7 +36,7 @@
@property(readonly) id<MPAlgorithm> algorithm; @property(readonly) id<MPAlgorithm> algorithm;
- (NSUInteger)use; - (NSUInteger)use;
- (BOOL)migrateExplicitly:(BOOL)explicit; - (BOOL)tryMigrateExplicitly:(BOOL)explicit;
- (NSString *)resolveLoginUsingKey:(MPKey *)key; - (NSString *)resolveLoginUsingKey:(MPKey *)key;
- (NSString *)resolvePasswordUsingKey:(MPKey *)key; - (NSString *)resolvePasswordUsingKey:(MPKey *)key;
- (void)resolveLoginUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result; - (void)resolveLoginUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result;
@@ -44,7 +44,7 @@
@end @end
@interface MPElementGeneratedEntity(MP) @interface MPGeneratedSiteEntity(MP)
@property(assign) NSUInteger counter; @property(assign) NSUInteger counter;
@@ -54,7 +54,7 @@
@property(assign) NSUInteger avatar; @property(assign) NSUInteger avatar;
@property(assign) BOOL saveKey; @property(assign) BOOL saveKey;
@property(assign) MPElementType defaultType; @property(assign) MPSiteType defaultType;
@property(readonly) NSString *userID; @property(readonly) NSString *userID;
+ (NSString *)idFor:(NSString *)userName; + (NSString *)idFor:(NSString *)userName;

View File

@@ -1,5 +1,5 @@
// //
// MPElementEntities.m // MPEntities.m
// MasterPassword-iOS // MasterPassword-iOS
// //
// Created by Maarten Billemont on 31/05/12. // Created by Maarten Billemont on 31/05/12.
@@ -8,7 +8,6 @@
#import "MPEntities.h" #import "MPEntities.h"
#import "MPAppDelegate_Shared.h" #import "MPAppDelegate_Shared.h"
#import "MPAppDelegate_Store.h"
@implementation NSManagedObjectContext(MP) @implementation NSManagedObjectContext(MP)
@@ -34,16 +33,16 @@
@end @end
@implementation MPElementEntity(MP) @implementation MPSiteEntity(MP)
- (MPFixableResult)findAndFixInconsistenciesInContext:(NSManagedObjectContext *)context { - (MPFixableResult)findAndFixInconsistenciesInContext:(NSManagedObjectContext *)context {
return MPFixableResultNoProblems; return MPFixableResultNoProblems;
} }
- (MPElementType)type { - (MPSiteType)type {
return (MPElementType)[self.type_ unsignedIntegerValue]; return (MPSiteType)[self.type_ unsignedIntegerValue];
} }
- (void)setLoginGenerated:(BOOL)aLoginGenerated { - (void)setLoginGenerated:(BOOL)aLoginGenerated {
@@ -56,7 +55,7 @@
return [self.loginGenerated_ boolValue]; return [self.loginGenerated_ boolValue];
} }
- (void)setType:(MPElementType)aType { - (void)setType:(MPSiteType)aType {
self.type_ = @(aType); self.type_ = @(aType);
} }
@@ -135,50 +134,52 @@
self.loginName, self.requiresExplicitMigration ); self.loginName, self.requiresExplicitMigration );
} }
- (BOOL)migrateExplicitly:(BOOL)explicit { - (BOOL)tryMigrateExplicitly:(BOOL)explicit {
while (self.version < MPAlgorithmDefaultVersion) while (self.version < MPAlgorithmDefaultVersion) {
if ([MPAlgorithmForVersion( self.version + 1 ) migrateElement:self explicit:explicit]) NSUInteger toVersion = self.version + 1;
inf( @"%@ migration to version: %ld succeeded for element: %@", if (![MPAlgorithmForVersion( toVersion ) tryMigrateSite:self explicit:explicit]) {
explicit? @"Explicit": @"Automatic", (long)self.version + 1, self ); wrn( @"%@ migration to version: %ld failed for site: %@",
else { explicit? @"Explicit": @"Automatic", (long)toVersion, self );
wrn( @"%@ migration to version: %ld failed for element: %@",
explicit? @"Explicit": @"Automatic", (long)self.version + 1, self );
return NO; return NO;
} }
inf( @"%@ migration to version: %ld succeeded for site: %@",
explicit? @"Explicit": @"Automatic", (long)toVersion, self );
}
return YES; return YES;
} }
- (NSString *)resolveLoginUsingKey:(MPKey *)key { - (NSString *)resolveLoginUsingKey:(MPKey *)key {
return [self.algorithm resolveLoginForElement:self usingKey:key]; return [self.algorithm resolveLoginForSite:self usingKey:key];
} }
- (NSString *)resolvePasswordUsingKey:(MPKey *)key { - (NSString *)resolvePasswordUsingKey:(MPKey *)key {
return [self.algorithm resolvePasswordForElement:self usingKey:key]; return [self.algorithm resolvePasswordForSite:self usingKey:key];
} }
- (void)resolveLoginUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result { - (void)resolveLoginUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result {
[self.algorithm resolveLoginForElement:self usingKey:key result:result]; [self.algorithm resolveLoginForSite:self usingKey:key result:result];
} }
- (void)resolvePasswordUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result { - (void)resolvePasswordUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result {
[self.algorithm resolvePasswordForElement:self usingKey:key result:result]; [self.algorithm resolvePasswordForSite:self usingKey:key result:result];
} }
@end @end
@implementation MPElementGeneratedEntity(MP) @implementation MPGeneratedSiteEntity(MP)
- (MPFixableResult)findAndFixInconsistenciesInContext:(NSManagedObjectContext *)context { - (MPFixableResult)findAndFixInconsistenciesInContext:(NSManagedObjectContext *)context {
MPFixableResult result = [super findAndFixInconsistenciesInContext:context]; MPFixableResult result = [super findAndFixInconsistenciesInContext:context];
if (!self.type || self.type == (MPElementType)NSNotFound || ![[self.algorithm allTypes] containsObject:self.type_]) if (!self.type || self.type == (MPSiteType)NSNotFound || ![[self.algorithm allTypes] containsObject:self.type_])
// Invalid self.type // Invalid self.type
result = MPApplyFix( result, ^MPFixableResult { result = MPApplyFix( result, ^MPFixableResult {
wrn( @"Invalid type for: %@ of %@, type: %ld. Will use %ld instead.", wrn( @"Invalid type for: %@ of %@, type: %ld. Will use %ld instead.",
@@ -186,18 +187,18 @@
self.type = self.user.defaultType; self.type = self.user.defaultType;
return MPFixableResultProblemsFixed; return MPFixableResultProblemsFixed;
} ); } );
if (!self.type || self.type == (MPElementType)NSNotFound || ![[self.algorithm allTypes] containsObject:self.type_]) if (!self.type || self.type == (MPSiteType)NSNotFound || ![[self.algorithm allTypes] containsObject:self.type_])
// Invalid self.user.defaultType // Invalid self.user.defaultType
result = MPApplyFix( result, ^MPFixableResult { result = MPApplyFix( result, ^MPFixableResult {
wrn( @"Invalid type for: %@ of %@, type: %ld. Will use %ld instead.", wrn( @"Invalid type for: %@ of %@, type: %ld. Will use %ld instead.",
self.name, self.user.name, (long)self.type, (long)MPElementTypeGeneratedLong ); self.name, self.user.name, (long)self.type, (long)MPSiteTypeGeneratedLong );
self.type = MPElementTypeGeneratedLong; self.type = MPSiteTypeGeneratedLong;
return MPFixableResultProblemsFixed; return MPFixableResultProblemsFixed;
} ); } );
if (![self isKindOfClass:[self.algorithm classOfType:self.type]]) if (![self isKindOfClass:[self.algorithm classOfType:self.type]])
// Mismatch between self.type and self.class // Mismatch between self.type and self.class
result = MPApplyFix( result, ^MPFixableResult { result = MPApplyFix( result, ^MPFixableResult {
for (MPElementType newType = self.type; self.type != (newType = [self.algorithm nextType:newType]);) for (MPSiteType newType = self.type; self.type != (newType = [self.algorithm nextType:newType]);)
if ([self isKindOfClass:[self.algorithm classOfType:newType]]) { if ([self isKindOfClass:[self.algorithm classOfType:newType]]) {
wrn( @"Mismatching type for: %@ of %@, type: %lu, class: %@. Will use %ld instead.", 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.name, self.user.name, (long)self.type, self.class, (long)newType );
@@ -225,7 +226,7 @@
@end @end
@implementation MPElementStoredEntity(MP) @implementation MPStoredSiteEntity(MP)
- (MPFixableResult)findAndFixInconsistenciesInContext:(NSManagedObjectContext *)context { - (MPFixableResult)findAndFixInconsistenciesInContext:(NSManagedObjectContext *)context {
@@ -236,7 +237,7 @@
MPKey *key = [MPAppDelegate_Shared get].key; MPKey *key = [MPAppDelegate_Shared get].key;
if (key && [[MPAppDelegate_Shared get] activeUserInContext:context] == self.user) { if (key && [[MPAppDelegate_Shared get] activeUserInContext:context] == self.user) {
wrn( @"Content object not encrypted for: %@ of %@. Will re-encrypt.", self.name, self.user.name ); 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]; [self.algorithm savePassword:[self.contentObject description] toSite:self usingKey:key];
return MPFixableResultProblemsFixed; return MPFixableResultProblemsFixed;
} }
@@ -271,12 +272,12 @@
self.saveKey_ = @(aSaveKey); self.saveKey_ = @(aSaveKey);
} }
- (MPElementType)defaultType { - (MPSiteType)defaultType {
return (MPElementType)[self.defaultType_ unsignedIntegerValue]?: MPElementTypeGeneratedLong; return (MPSiteType)[self.defaultType_ unsignedIntegerValue]?: MPSiteTypeGeneratedLong;
} }
- (void)setDefaultType:(MPElementType)aDefaultType { - (void)setDefaultType:(MPSiteType)aDefaultType {
self.defaultType_ = @(aDefaultType); self.defaultType_ = @(aDefaultType);
} }

View File

@@ -1,17 +1,17 @@
// //
// MPElementGeneratedEntity.h // MPGeneratedSiteEntity.h
// MasterPassword-iOS // MasterPassword-Mac
// //
// Created by Maarten Billemont on 2014-09-14. // Created by Maarten Billemont on 2014-09-21.
// Copyright (c) 2014 Lyndir. All rights reserved. // Copyright (c) 2014 Lyndir. All rights reserved.
// //
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import <CoreData/CoreData.h> #import <CoreData/CoreData.h>
#import "MPElementEntity.h" #import "MPSiteEntity.h"
@interface MPElementGeneratedEntity : MPElementEntity @interface MPGeneratedSiteEntity : MPSiteEntity
@property (nonatomic, retain) NSNumber * counter_; @property (nonatomic, retain) NSNumber * counter_;

View File

@@ -0,0 +1,16 @@
//
// MPGeneratedSiteEntity.m
// MasterPassword-Mac
//
// Created by Maarten Billemont on 2014-09-21.
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#import "MPGeneratedSiteEntity.h"
@implementation MPGeneratedSiteEntity
@dynamic counter_;
@end

View File

@@ -0,0 +1,41 @@
//
// MPSiteEntity.h
// MasterPassword-Mac
//
// Created by Maarten Billemont on 2014-09-21.
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
@class MPSiteQuestionEntity, MPUserEntity;
@interface MPSiteEntity : NSManagedObject
//@property (nonatomic, retain) id content; // Hide here, reveal in MPStoredSiteEntity
@property (nonatomic, retain) NSDate * lastUsed;
@property (nonatomic, retain) NSNumber * loginGenerated_;
@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) NSOrderedSet *questions;
@property (nonatomic, retain) MPUserEntity *user;
@end
@interface MPSiteEntity (CoreDataGeneratedAccessors)
- (void)insertObject:(MPSiteQuestionEntity *)value inQuestionsAtIndex:(NSUInteger)idx;
- (void)removeObjectFromQuestionsAtIndex:(NSUInteger)idx;
- (void)insertQuestions:(NSArray *)value atIndexes:(NSIndexSet *)indexes;
- (void)removeQuestionsAtIndexes:(NSIndexSet *)indexes;
- (void)replaceObjectInQuestionsAtIndex:(NSUInteger)idx withObject:(MPSiteQuestionEntity *)value;
- (void)replaceQuestionsAtIndexes:(NSIndexSet *)indexes withQuestions:(NSArray *)values;
- (void)addQuestionsObject:(MPSiteQuestionEntity *)value;
- (void)removeQuestionsObject:(MPSiteQuestionEntity *)value;
- (void)addQuestions:(NSOrderedSet *)values;
- (void)removeQuestions:(NSOrderedSet *)values;
@end

View File

@@ -1,26 +1,28 @@
// //
// MPElementEntity.m // MPSiteEntity.m
// MasterPassword-iOS // MasterPassword-Mac
// //
// Created by Maarten Billemont on 2014-09-14. // Created by Maarten Billemont on 2014-09-21.
// Copyright (c) 2014 Lyndir. All rights reserved. // Copyright (c) 2014 Lyndir. All rights reserved.
// //
#import "MPElementEntity.h" #import "MPSiteEntity.h"
#import "MPSiteQuestionEntity.h"
#import "MPUserEntity.h" #import "MPUserEntity.h"
@implementation MPElementEntity @implementation MPSiteEntity
//@dynamic content; //@dynamic content;
@dynamic lastUsed; @dynamic lastUsed;
@dynamic loginGenerated_;
@dynamic loginName; @dynamic loginName;
@dynamic name; @dynamic name;
@dynamic requiresExplicitMigration_; @dynamic requiresExplicitMigration_;
@dynamic type_; @dynamic type_;
@dynamic uses_; @dynamic uses_;
@dynamic version_; @dynamic version_;
@dynamic loginGenerated_; @dynamic questions;
@dynamic user; @dynamic user;
@end @end

View File

@@ -0,0 +1,19 @@
//
// MPSiteQuestionEntity.h
// MasterPassword-iOS
//
// Created by Maarten Billemont on 2014-09-27.
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
@class MPSiteEntity;
@interface MPSiteQuestionEntity : NSManagedObject
@property (nonatomic, retain) NSString * keyword;
@property (nonatomic, retain) MPSiteEntity *site;
@end

View File

@@ -0,0 +1,18 @@
//
// MPSiteQuestionEntity.m
// MasterPassword-iOS
//
// Created by Maarten Billemont on 2014-09-27.
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#import "MPSiteQuestionEntity.h"
#import "MPSiteEntity.h"
@implementation MPSiteQuestionEntity
@dynamic keyword;
@dynamic site;
@end

View File

@@ -0,0 +1,18 @@
//
// MPStoredSiteEntity.h
// MasterPassword-Mac
//
// Created by Maarten Billemont on 2014-09-21.
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#import "MPSiteEntity.h"
@interface MPStoredSiteEntity : MPSiteEntity
@property (nonatomic, retain) NSData *contentObject;
@end

View File

@@ -0,0 +1,16 @@
//
// MPStoredSiteEntity.m
// MasterPassword-Mac
//
// Created by Maarten Billemont on 2014-09-21.
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#import "MPStoredSiteEntity.h"
@implementation MPStoredSiteEntity
@dynamic contentObject;
@end

View File

@@ -8,38 +8,41 @@
#import "MPKey.h" #import "MPKey.h"
typedef NS_ENUM(NSUInteger, MPElementTypeClass) { typedef NS_ENUM( NSUInteger, MPSiteTypeClass ) {
/** Generate the password. */ /** Generate the password. */
MPElementTypeClassGenerated = 1 << 4, MPSiteTypeClassGenerated = 1 << 4,
/** Store the password. */ /** Store the password. */
MPElementTypeClassStored = 1 << 5, MPSiteTypeClassStored = 1 << 5,
}; };
typedef NS_ENUM(NSUInteger, MPElementVariant) { typedef NS_ENUM( NSUInteger, MPSiteVariant ) {
/** Generate the password. */ /** Generate the password. */
MPElementVariantPassword, MPSiteVariantPassword,
/** Generate the login name. */ /** Generate the login name. */
MPElementVariantLogin, MPSiteVariantLogin,
/** Generate a security answer. */
MPSiteVariantAnswer,
}; };
typedef NS_ENUM(NSUInteger, MPElementFeature) { typedef NS_ENUM( NSUInteger, MPSiteFeature ) {
/** Export the key-protected content data. */ /** Export the key-protected content data. */
MPElementFeatureExportContent = 1 << 10, MPSiteFeatureExportContent = 1 << 10,
/** Never export content. */ /** Never export content. */
MPElementFeatureDevicePrivate = 1 << 11, MPSiteFeatureDevicePrivate = 1 << 11,
}; };
typedef NS_ENUM(NSUInteger, MPElementType) { typedef NS_ENUM(NSUInteger, MPSiteType) {
MPElementTypeGeneratedMaximum = 0x0 | MPElementTypeClassGenerated | 0x0, MPSiteTypeGeneratedMaximum = 0x0 | MPSiteTypeClassGenerated | 0x0,
MPElementTypeGeneratedLong = 0x1 | MPElementTypeClassGenerated | 0x0, MPSiteTypeGeneratedLong = 0x1 | MPSiteTypeClassGenerated | 0x0,
MPElementTypeGeneratedMedium = 0x2 | MPElementTypeClassGenerated | 0x0, MPSiteTypeGeneratedMedium = 0x2 | MPSiteTypeClassGenerated | 0x0,
MPElementTypeGeneratedBasic = 0x4 | MPElementTypeClassGenerated | 0x0, MPSiteTypeGeneratedBasic = 0x4 | MPSiteTypeClassGenerated | 0x0,
MPElementTypeGeneratedShort = 0x3 | MPElementTypeClassGenerated | 0x0, MPSiteTypeGeneratedShort = 0x3 | MPSiteTypeClassGenerated | 0x0,
MPElementTypeGeneratedPIN = 0x5 | MPElementTypeClassGenerated | 0x0, MPSiteTypeGeneratedPIN = 0x5 | MPSiteTypeClassGenerated | 0x0,
MPElementTypeGeneratedName = 0xF | MPElementTypeClassGenerated | 0x0, MPSiteTypeGeneratedName = 0xE | MPSiteTypeClassGenerated | 0x0,
MPSiteTypeGeneratedPhrase = 0xF | MPSiteTypeClassGenerated | 0x0,
MPElementTypeStoredPersonal = 0x0 | MPElementTypeClassStored | MPElementFeatureExportContent, MPSiteTypeStoredPersonal = 0x0 | MPSiteTypeClassStored | MPSiteFeatureExportContent,
MPElementTypeStoredDevicePrivate = 0x1 | MPElementTypeClassStored | MPElementFeatureDevicePrivate, MPSiteTypeStoredDevicePrivate = 0x1 | MPSiteTypeClassStored | MPSiteFeatureDevicePrivate,
}; };
#define MPErrorDomain @"MPErrorDomain" #define MPErrorDomain @"MPErrorDomain"
@@ -52,7 +55,7 @@ typedef NS_ENUM(NSUInteger, MPElementType) {
#define MPCheckpointEditPassword @"MPCheckpointEditPassword" #define MPCheckpointEditPassword @"MPCheckpointEditPassword"
#define MPCheckpointEditLoginName @"MPCheckpointEditLoginName" #define MPCheckpointEditLoginName @"MPCheckpointEditLoginName"
#define MPCheckpointUseType @"MPCheckpointUseType" #define MPCheckpointUseType @"MPCheckpointUseType"
#define MPCheckpointDeleteElement @"MPCheckpointDeleteElement" #define MPCheckpointDeleteSite @"MPCheckpointDeleteSite"
#define MPCheckpointShowGuide @"MPCheckpointShowGuide" #define MPCheckpointShowGuide @"MPCheckpointShowGuide"
#define MPCheckpointShowSetup @"MPCheckpointShowSetup" #define MPCheckpointShowSetup @"MPCheckpointShowSetup"
#define MPCheckpointChangeMP @"MPCheckpointChangeMP" #define MPCheckpointChangeMP @"MPCheckpointChangeMP"
@@ -76,7 +79,7 @@ typedef NS_ENUM(NSUInteger, MPElementType) {
#define MPSignedInNotification @"MPSignedInNotification" #define MPSignedInNotification @"MPSignedInNotification"
#define MPSignedOutNotification @"MPSignedOutNotification" #define MPSignedOutNotification @"MPSignedOutNotification"
#define MPKeyForgottenNotification @"MPKeyForgottenNotification" #define MPKeyForgottenNotification @"MPKeyForgottenNotification"
#define MPElementUpdatedNotification @"MPElementUpdatedNotification" #define MPSiteUpdatedNotification @"MPSiteUpdatedNotification"
#define MPCheckConfigNotification @"MPCheckConfigNotification" #define MPCheckConfigNotification @"MPCheckConfigNotification"
#define MPSitesImportedNotification @"MPSitesImportedNotification" #define MPSitesImportedNotification @"MPSitesImportedNotification"
#define MPFoundInconsistenciesNotification @"MPFoundInconsistenciesNotification" #define MPFoundInconsistenciesNotification @"MPFoundInconsistenciesNotification"

View File

@@ -1,15 +1,15 @@
// //
// MPUserEntity.h // MPUserEntity.h
// MasterPassword-iOS // MasterPassword-Mac
// //
// Created by Maarten Billemont on 2014-09-14. // Created by Maarten Billemont on 2014-09-21.
// Copyright (c) 2014 Lyndir. All rights reserved. // Copyright (c) 2014 Lyndir. All rights reserved.
// //
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import <CoreData/CoreData.h> #import <CoreData/CoreData.h>
@class MPElementEntity; @class MPSiteEntity;
@interface MPUserEntity : NSManagedObject @interface MPUserEntity : NSManagedObject
@@ -19,14 +19,14 @@
@property (nonatomic, retain) NSDate * lastUsed; @property (nonatomic, retain) NSDate * lastUsed;
@property (nonatomic, retain) NSString * name; @property (nonatomic, retain) NSString * name;
@property (nonatomic, retain) NSNumber * saveKey_; @property (nonatomic, retain) NSNumber * saveKey_;
@property (nonatomic, retain) NSSet *elements; @property (nonatomic, retain) NSSet *sites;
@end @end
@interface MPUserEntity (CoreDataGeneratedAccessors) @interface MPUserEntity (CoreDataGeneratedAccessors)
- (void)addElementsObject:(MPElementEntity *)value; - (void)addSitesObject:(MPSiteEntity *)value;
- (void)removeElementsObject:(MPElementEntity *)value; - (void)removeSitesObject:(MPSiteEntity *)value;
- (void)addElements:(NSSet *)values; - (void)addSites:(NSSet *)values;
- (void)removeElements:(NSSet *)values; - (void)removeSites:(NSSet *)values;
@end @end

View File

@@ -1,13 +1,13 @@
// //
// MPUserEntity.m // MPUserEntity.m
// MasterPassword-iOS // MasterPassword-Mac
// //
// Created by Maarten Billemont on 2014-09-14. // Created by Maarten Billemont on 2014-09-21.
// Copyright (c) 2014 Lyndir. All rights reserved. // Copyright (c) 2014 Lyndir. All rights reserved.
// //
#import "MPUserEntity.h" #import "MPUserEntity.h"
#import "MPElementEntity.h" #import "MPSiteEntity.h"
@implementation MPUserEntity @implementation MPUserEntity
@@ -18,6 +18,6 @@
@dynamic lastUsed; @dynamic lastUsed;
@dynamic name; @dynamic name;
@dynamic saveKey_; @dynamic saveKey_;
@dynamic elements; @dynamic sites;
@end @end

View File

@@ -229,7 +229,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
NSData *importedSitesData = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url] NSData *importedSitesData = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url]
returningResponse:&response error:&error]; returningResponse:&response error:&error];
if (error) if (error)
err( @"While reading imported sites from %@: %@", url, error ); err( @"While reading imported sites from %@: %@", url, [error fullDescription] );
if (!importedSitesData) if (!importedSitesData)
return; return;
@@ -346,7 +346,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
[moc saveToStore]; [moc saveToStore];
NSError *error = nil; NSError *error = nil;
if (![moc obtainPermanentIDsForObjects:@[ newUser ] error:&error]) if (![moc obtainPermanentIDsForObjects:@[ newUser ] error:&error])
err( @"Failed to obtain permanent object ID for new user: %@", error ); err( @"Failed to obtain permanent object ID for new user: %@", [error fullDescription] );
[[NSOperationQueue mainQueue] addOperationWithBlock:^{ [[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self updateUsers]; [self updateUsers];
@@ -510,7 +510,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
fetchRequest.sortDescriptors = @[ [NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO] ]; fetchRequest.sortDescriptors = @[ [NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO] ];
NSArray *users = [mainContext executeFetchRequest:fetchRequest error:&error]; NSArray *users = [mainContext executeFetchRequest:fetchRequest error:&error];
if (!users) if (!users)
err( @"Failed to load users: %@", error ); err( @"Failed to load users: %@", [error fullDescription] );
if (![users count]) { if (![users count]) {
NSMenuItem *noUsersItem = [self.usersItem.submenu addItemWithTitle:@"No users" action:NULL keyEquivalent:@""]; NSMenuItem *noUsersItem = [self.usersItem.submenu addItemWithTitle:@"No users" action:NULL keyEquivalent:@""];

View File

@@ -17,26 +17,26 @@
// //
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import "MPElementModel.h" #import "MPSiteModel.h"
#import "MPElementsTableView.h" #import "MPSitesTableView.h"
@class MPMacAppDelegate; @class MPMacAppDelegate;
@interface MPPasswordWindowController : NSWindowController<NSTextViewDelegate, NSTextFieldDelegate, NSTableViewDataSource, NSTableViewDelegate> @interface MPPasswordWindowController : NSWindowController<NSTextViewDelegate, NSTextFieldDelegate, NSTableViewDataSource, NSTableViewDelegate>
@property(nonatomic) NSMutableArray *elements; @property(nonatomic) NSMutableArray *sites;
@property(nonatomic) NSString *masterPassword; @property(nonatomic) NSString *masterPassword;
@property(nonatomic) BOOL alternatePressed; @property(nonatomic) BOOL alternatePressed;
@property(nonatomic) BOOL locked; @property(nonatomic) BOOL locked;
@property(nonatomic) BOOL newUser; @property(nonatomic) BOOL newUser;
@property(nonatomic, weak) IBOutlet NSArrayController *elementsController; @property(nonatomic, weak) IBOutlet NSArrayController *sitesController;
@property(nonatomic, weak) IBOutlet NSImageView *blurView; @property(nonatomic, weak) IBOutlet NSImageView *blurView;
@property(nonatomic, weak) IBOutlet NSTextField *inputLabel; @property(nonatomic, weak) IBOutlet NSTextField *inputLabel;
@property(nonatomic, weak) IBOutlet NSTextField *securePasswordField; @property(nonatomic, weak) IBOutlet NSTextField *securePasswordField;
@property(nonatomic, weak) IBOutlet NSTextField *revealPasswordField; @property(nonatomic, weak) IBOutlet NSTextField *revealPasswordField;
@property(nonatomic, weak) IBOutlet NSSearchField *siteField; @property(nonatomic, weak) IBOutlet NSSearchField *siteField;
@property(nonatomic, weak) IBOutlet MPElementsTableView *siteTable; @property(nonatomic, weak) IBOutlet MPSitesTableView *siteTable;
@property(nonatomic, weak) IBOutlet NSProgressIndicator *progressView; @property(nonatomic, weak) IBOutlet NSProgressIndicator *progressView;
@property(nonatomic, strong) IBOutlet NSBox *passwordTypesBox; @property(nonatomic, strong) IBOutlet NSBox *passwordTypesBox;

View File

@@ -20,7 +20,7 @@
#import "MPPasswordWindowController.h" #import "MPPasswordWindowController.h"
#import "MPMacAppDelegate.h" #import "MPMacAppDelegate.h"
#import "MPAppDelegate_Store.h" #import "MPAppDelegate_Store.h"
#import "MPElementModel.h" #import "MPSiteModel.h"
#import "MPAppDelegate_Key.h" #import "MPAppDelegate_Key.h"
#import "PearlProfiler.h" #import "PearlProfiler.h"
@@ -77,7 +77,7 @@
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
[self updateUser]; [self updateUser];
}]; }];
[self observeKeyPath:@"elementsController.selection" [self observeKeyPath:@"sitesController.selection"
withBlock:^(id from, id to, NSKeyValueChange cause, id _self) { withBlock:^(id from, id to, NSKeyValueChange cause, id _self) {
[_self updateSelection]; [_self updateSelection];
}]; }];
@@ -100,7 +100,7 @@
BOOL alternatePressed = (theEvent.modifierFlags & NSAlternateKeyMask) != 0; BOOL alternatePressed = (theEvent.modifierFlags & NSAlternateKeyMask) != 0;
if (alternatePressed != self.alternatePressed) { if (alternatePressed != self.alternatePressed) {
self.alternatePressed = alternatePressed; self.alternatePressed = alternatePressed;
[self.selectedElement updateContent]; [self.selectedSite updateContent];
if (self.locked) { if (self.locked) {
NSTextField *passwordField = self.securePasswordField; NSTextField *passwordField = self.securePasswordField;
@@ -169,9 +169,9 @@
}]; }];
} }
- (IBAction)doSearchElements:(id)sender { - (IBAction)doSearchSites:(id)sender {
[self updateElements]; [self updateSites];
} }
#pragma mark - NSTextViewDelegate #pragma mark - NSTextViewDelegate
@@ -186,7 +186,7 @@
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView { - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return (NSInteger)[self.elements count]; return (NSInteger)[self.sites count];
} }
#pragma mark - NSTableViewDelegate #pragma mark - NSTableViewDelegate
@@ -229,9 +229,10 @@
switch (returnCode) { switch (returnCode) {
case NSAlertFirstButtonReturn: { case NSAlertFirstButtonReturn: {
// "Create" button. // "Create" button.
[[MPMacAppDelegate get] addElementNamed:[self.siteField stringValue] completion:^(MPElementEntity *element, NSManagedObjectContext *context) { [[MPMacAppDelegate get] addSiteNamed:[self.siteField stringValue] completion:
if (element) ^(MPSiteEntity *site, NSManagedObjectContext *context) {
PearlMainQueue( ^{ [self updateElements]; } ); if (site)
PearlMainQueue( ^{ [self updateSites]; } );
}]; }];
break; break;
} }
@@ -243,11 +244,11 @@
switch (returnCode) { switch (returnCode) {
case NSAlertFirstButtonReturn: { case NSAlertFirstButtonReturn: {
// "Save" button. // "Save" button.
MPElementType type = (MPElementType)[self.passwordTypesMatrix.selectedCell tag]; MPSiteType type = (MPSiteType)[self.passwordTypesMatrix.selectedCell tag];
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPElementEntity *entity = [[MPMacAppDelegate get] changeElement:[self.selectedElement entityInContext:context] MPSiteEntity *entity = [[MPMacAppDelegate get] changeSite:[self.selectedSite entityInContext:context]
saveInContext:context toType:type]; saveInContext:context toType:type];
if ([entity isKindOfClass:[MPElementStoredEntity class]] && ![(MPElementStoredEntity *)entity contentObject].length) if ([entity isKindOfClass:[MPStoredSiteEntity class]] && ![(MPStoredSiteEntity *)entity contentObject].length)
PearlMainQueue( ^{ PearlMainQueue( ^{
[self changePassword:nil]; [self changePassword:nil];
} ); } );
@@ -264,7 +265,7 @@
// "Save" button. // "Save" button.
NSString *loginName = [(NSSecureTextField *)alert.accessoryView stringValue]; NSString *loginName = [(NSSecureTextField *)alert.accessoryView stringValue];
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPElementEntity *entity = [self.selectedElement entityInContext:context]; MPSiteEntity *entity = [self.selectedSite entityInContext:context];
entity.loginName = loginName; entity.loginName = loginName;
[context saveToStore]; [context saveToStore];
}]; }];
@@ -280,8 +281,8 @@
// "Save" button. // "Save" button.
NSString *password = [(NSSecureTextField *)alert.accessoryView stringValue]; NSString *password = [(NSSecureTextField *)alert.accessoryView stringValue];
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPElementEntity *entity = [self.selectedElement entityInContext:context]; MPSiteEntity *entity = [self.selectedSite entityInContext:context];
[entity.algorithm savePassword:password toElement:entity usingKey:[MPMacAppDelegate get].key]; [entity.algorithm savePassword:password toSite:entity usingKey:[MPMacAppDelegate get].key];
[context saveToStore]; [context saveToStore];
}]; }];
break; break;
@@ -295,7 +296,7 @@
case NSAlertFirstButtonReturn: { case NSAlertFirstButtonReturn: {
// "Delete" button. // "Delete" button.
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
[context deleteObject:[self.selectedElement entityInContext:context]]; [context deleteObject:[self.selectedSite entityInContext:context]];
[context saveToStore]; [context saveToStore];
}]; }];
break; break;
@@ -313,19 +314,19 @@
return [self.siteField.stringValue stringByReplacingCharactersInRange:self.siteField.currentEditor.selectedRange withString:@""]?: @""; return [self.siteField.stringValue stringByReplacingCharactersInRange:self.siteField.currentEditor.selectedRange withString:@""]?: @"";
} }
- (void)insertObject:(MPElementModel *)model inElementsAtIndex:(NSUInteger)index { - (void)insertObject:(MPSiteModel *)model inSitesAtIndex:(NSUInteger)index {
[self.elements insertObject:model atIndex:index]; [self.sites insertObject:model atIndex:index];
} }
- (void)removeObjectFromElementsAtIndex:(NSUInteger)index { - (void)removeObjectFromSitesAtIndex:(NSUInteger)index {
[self.elements removeObjectAtIndex:index]; [self.sites removeObjectAtIndex:index];
} }
- (MPElementModel *)selectedElement { - (MPSiteModel *)selectedSite {
return [self.elementsController.selectedObjects firstObject]; return [self.sitesController.selectedObjects firstObject];
} }
#pragma mark - Actions #pragma mark - Actions
@@ -336,13 +337,13 @@
[[MPMacAppDelegate get] showPopup:sender]; [[MPMacAppDelegate get] showPopup:sender];
} }
- (IBAction)deleteElement:(id)sender { - (IBAction)deleteSite:(id)sender {
NSAlert *alert = [NSAlert new]; NSAlert *alert = [NSAlert new];
[alert addButtonWithTitle:@"Delete"]; [alert addButtonWithTitle:@"Delete"];
[alert addButtonWithTitle:@"Cancel"]; [alert addButtonWithTitle:@"Cancel"];
[alert setMessageText:@"Delete Site?"]; [alert setMessageText:@"Delete Site?"];
[alert setInformativeText:strf( @"Do you want to delete the site named:\n\n%@", self.selectedElement.siteName )]; [alert setInformativeText:strf( @"Do you want to delete the site named:\n\n%@", self.selectedSite.siteName )];
[alert beginSheetModalForWindow:self.window modalDelegate:self [alert beginSheetModalForWindow:self.window modalDelegate:self
didEndSelector:@selector( alertDidEnd:returnCode:contextInfo: ) contextInfo:MPAlertDeleteSite]; didEndSelector:@selector( alertDidEnd:returnCode:contextInfo: ) contextInfo:MPAlertDeleteSite];
} }
@@ -353,9 +354,9 @@
[alert addButtonWithTitle:@"Save"]; [alert addButtonWithTitle:@"Save"];
[alert addButtonWithTitle:@"Cancel"]; [alert addButtonWithTitle:@"Cancel"];
[alert setMessageText:@"Change Login Name"]; [alert setMessageText:@"Change Login Name"];
[alert setInformativeText:strf( @"Enter the login name for: %@", self.selectedElement.siteName )]; [alert setInformativeText:strf( @"Enter the login name for: %@", self.selectedSite.siteName )];
NSTextField *loginField = [[NSTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )]; NSTextField *loginField = [[NSTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )];
loginField.stringValue = self.selectedElement.loginName?: @""; loginField.stringValue = self.selectedSite.loginName?: @"";
[loginField selectText:self]; [loginField selectText:self];
[alert setAccessoryView:loginField]; [alert setAccessoryView:loginField];
[alert layout]; [alert layout];
@@ -380,14 +381,14 @@
- (IBAction)changePassword:(id)sender { - (IBAction)changePassword:(id)sender {
if (!self.selectedElement.stored) if (!self.selectedSite.stored)
return; return;
NSAlert *alert = [NSAlert new]; NSAlert *alert = [NSAlert new];
[alert addButtonWithTitle:@"Save"]; [alert addButtonWithTitle:@"Save"];
[alert addButtonWithTitle:@"Cancel"]; [alert addButtonWithTitle:@"Cancel"];
[alert setMessageText:@"Change Password"]; [alert setMessageText:@"Change Password"];
[alert setInformativeText:strf( @"Enter the new password for: %@", self.selectedElement.siteName )]; [alert setInformativeText:strf( @"Enter the new password for: %@", self.selectedSite.siteName )];
[alert setAccessoryView:[[NSSecureTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )]]; [alert setAccessoryView:[[NSSecureTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )]];
[alert layout]; [alert layout];
[alert beginSheetModalForWindow:self.window modalDelegate:self [alert beginSheetModalForWindow:self.window modalDelegate:self
@@ -396,19 +397,19 @@
- (IBAction)changeType:(id)sender { - (IBAction)changeType:(id)sender {
MPElementModel *element = self.selectedElement; MPSiteModel *site = self.selectedSite;
NSArray *types = [element.algorithm allTypesStartingWith:MPElementTypeGeneratedPIN]; NSArray *types = [site.algorithm allTypesStartingWith:MPSiteTypeGeneratedPIN];
[self.passwordTypesMatrix renewRows:(NSInteger)[types count] columns:1]; [self.passwordTypesMatrix renewRows:(NSInteger)[types count] columns:1];
for (NSUInteger t = 0; t < [types count]; ++t) { for (NSUInteger t = 0; t < [types count]; ++t) {
MPElementType type = [types[t] unsignedIntegerValue]; MPSiteType type = [types[t] unsignedIntegerValue];
NSString *title = [element.algorithm nameOfType:type]; NSString *title = [site.algorithm nameOfType:type];
if (type & MPElementTypeClassGenerated) if (type & MPSiteTypeClassGenerated)
title = [element.algorithm generatePasswordForSiteNamed:element.siteName ofType:type title = [site.algorithm generatePasswordForSiteNamed:site.siteName ofType:type
withCounter:element.counter usingKey:[MPMacAppDelegate get].key]; withCounter:site.counter usingKey:[MPMacAppDelegate get].key];
NSButtonCell *cell = [self.passwordTypesMatrix cellAtRow:(NSInteger)t column:0]; NSButtonCell *cell = [self.passwordTypesMatrix cellAtRow:(NSInteger)t column:0];
cell.tag = type; cell.tag = type;
cell.state = type == element.type? NSOnState: NSOffState; cell.state = type == site.type? NSOnState: NSOffState;
cell.title = title; cell.title = title;
} }
@@ -416,7 +417,7 @@
[alert addButtonWithTitle:@"Save"]; [alert addButtonWithTitle:@"Save"];
[alert addButtonWithTitle:@"Cancel"]; [alert addButtonWithTitle:@"Cancel"];
[alert setMessageText:@"Change Password Type"]; [alert setMessageText:@"Change Password Type"];
[alert setInformativeText:strf( @"Choose a new password type for: %@", element.siteName )]; [alert setInformativeText:strf( @"Choose a new password type for: %@", site.siteName )];
[alert setAccessoryView:self.passwordTypesBox]; [alert setAccessoryView:self.passwordTypesBox];
[alert layout]; [alert layout];
[alert beginSheetModalForWindow:self.window modalDelegate:self [alert beginSheetModalForWindow:self.window modalDelegate:self
@@ -428,11 +429,11 @@
- (BOOL)handleCommand:(SEL)commandSelector { - (BOOL)handleCommand:(SEL)commandSelector {
if (commandSelector == @selector( moveUp: )) { if (commandSelector == @selector( moveUp: )) {
[self.elementsController selectPrevious:self]; [self.sitesController selectPrevious:self];
return YES; return YES;
} }
if (commandSelector == @selector( moveDown: )) { if (commandSelector == @selector( moveDown: )) {
[self.elementsController selectNext:self]; [self.sitesController selectNext:self];
return YES; return YES;
} }
if (commandSelector == @selector( insertNewline: )) { if (commandSelector == @selector( insertNewline: )) {
@@ -449,19 +450,19 @@
- (void)useSite { - (void)useSite {
MPElementModel *selectedElement = [self selectedElement]; MPSiteModel *selectedSite = [self selectedSite];
if (selectedElement) { if (selectedSite) {
// Performing action while content is available. Copy it. // Performing action while content is available. Copy it.
[self copyContent:selectedElement.content]; [self copyContent:selectedSite.content];
[self fadeOut]; [self fadeOut];
NSUserNotification *notification = [NSUserNotification new]; NSUserNotification *notification = [NSUserNotification new];
notification.title = @"Password Copied"; notification.title = @"Password Copied";
if (selectedElement.loginName.length) if (selectedSite.loginName.length)
notification.subtitle = strf( @"%@ at %@", selectedElement.loginName, selectedElement.siteName ); notification.subtitle = strf( @"%@ at %@", selectedSite.loginName, selectedSite.siteName );
else else
notification.subtitle = selectedElement.siteName; notification.subtitle = selectedSite.siteName;
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification]; [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
} }
else { else {
@@ -499,22 +500,22 @@
} }
} }
[self updateElements]; [self updateSites];
}]; }];
} }
- (void)updateElements { - (void)updateSites {
if (![MPMacAppDelegate get].key) { if (![MPMacAppDelegate get].key) {
self.elements = nil; self.sites = nil;
return; return;
} }
PearlProfiler *profiler = [PearlProfiler profilerForTask:@"updateElements"]; PearlProfiler *profiler = [PearlProfiler profilerForTask:@"updateSites"];
NSString *query = [self query]; NSString *query = [self query];
[profiler finishJob:@"query"]; [profiler finishJob:@"query"];
[MPMacAppDelegate managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) { [MPMacAppDelegate managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )]; NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
fetchRequest.sortDescriptors = [NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:@"lastUsed" ascending:NO]]; fetchRequest.sortDescriptors = [NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:@"lastUsed" ascending:NO]];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(%@ == '' OR name BEGINSWITH[cd] %@) AND user == %@", fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(%@ == '' OR name BEGINSWITH[cd] %@) AND user == %@",
query, query, [[MPMacAppDelegate get] activeUserInContext:context]]; query, query, [[MPMacAppDelegate get] activeUserInContext:context]];
@@ -523,17 +524,17 @@
NSError *error = nil; NSError *error = nil;
NSArray *siteResults = [context executeFetchRequest:fetchRequest error:&error]; NSArray *siteResults = [context executeFetchRequest:fetchRequest error:&error];
if (!siteResults) { if (!siteResults) {
err( @"While fetching elements for completion: %@", error ); err( @"While fetching sites for completion: %@", [error fullDescription] );
return; return;
} }
[profiler finishJob:@"do fetch"]; [profiler finishJob:@"do fetch"];
NSMutableArray *newElements = [NSMutableArray arrayWithCapacity:[siteResults count]]; NSMutableArray *newSites = [NSMutableArray arrayWithCapacity:[siteResults count]];
for (MPElementEntity *element in siteResults) for (MPSiteEntity *site in siteResults)
[newElements addObject:[[MPElementModel alloc] initWithEntity:element]]; [newSites addObject:[[MPSiteModel alloc] initWithEntity:site]];
[profiler finishJob:@"make models"]; [profiler finishJob:@"make models"];
self.elements = newElements; self.sites = newSites;
[profiler finishJob:@"update elements"]; [profiler finishJob:@"update sites"];
}]; }];
[profiler finishJob:@"done"]; [profiler finishJob:@"done"];
} }
@@ -545,7 +546,7 @@
return; return;
} }
NSString *siteName = self.selectedElement.siteName; NSString *siteName = self.selectedSite.siteName;
if (!siteName) if (!siteName)
return; return;
@@ -558,14 +559,14 @@
NSMakeRange( siteNameQueryRange.length, siteName.length - siteNameQueryRange.length ); NSMakeRange( siteNameQueryRange.length, siteName.length - siteNameQueryRange.length );
} }
[self.siteTable scrollRowToVisible:(NSInteger)self.elementsController.selectionIndex]; [self.siteTable scrollRowToVisible:(NSInteger)self.sitesController.selectionIndex];
[self updateGradient]; [self updateGradient];
} }
- (void)updateGradient { - (void)updateGradient {
NSView *siteScrollView = self.siteTable.superview.superview; NSView *siteScrollView = self.siteTable.superview.superview;
NSRect selectedCellFrame = [self.siteTable frameOfCellAtColumn:0 row:((NSInteger)self.elementsController.selectionIndex)]; NSRect selectedCellFrame = [self.siteTable frameOfCellAtColumn:0 row:((NSInteger)self.sitesController.selectionIndex)];
CGFloat selectedOffset = [siteScrollView convertPoint:selectedCellFrame.origin fromView:self.siteTable].y; CGFloat selectedOffset = [siteScrollView convertPoint:selectedCellFrame.origin fromView:self.siteTable].y;
CGFloat gradientOpacity = selectedOffset / siteScrollView.bounds.size.height; CGFloat gradientOpacity = selectedOffset / siteScrollView.bounds.size.height;
self.siteGradient.colors = @[ self.siteGradient.colors = @[
@@ -596,7 +597,7 @@
} }
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) { [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
[[self.selectedElement entityInContext:moc] use]; [[self.selectedSite entityInContext:moc] use];
[moc saveToStore]; [moc saveToStore];
}]; }];
} }

View File

@@ -8,7 +8,7 @@
<customObject id="-2" userLabel="File's Owner" customClass="MPPasswordWindowController"> <customObject id="-2" userLabel="File's Owner" customClass="MPPasswordWindowController">
<connections> <connections>
<outlet property="blurView" destination="Bwc-sd-6gm" id="wNV-0x-LJn"/> <outlet property="blurView" destination="Bwc-sd-6gm" id="wNV-0x-LJn"/>
<outlet property="elementsController" destination="mcS-ik-b0n" id="cdF-BL-lfg"/> <outlet property="sitesController" destination="mcS-ik-b0n" id="cdF-BL-lfg"/>
<outlet property="inputLabel" destination="OnR-s6-d4P" id="p6G-Ut-cdu"/> <outlet property="inputLabel" destination="OnR-s6-d4P" id="p6G-Ut-cdu"/>
<outlet property="passwordTypesBox" destination="bZe-7q-i6q" id="Ai3-pt-i6K"/> <outlet property="passwordTypesBox" destination="bZe-7q-i6q" id="Ai3-pt-i6K"/>
<outlet property="passwordTypesMatrix" destination="3fr-Fd-pxx" id="T8g-mS-lxP"/> <outlet property="passwordTypesMatrix" destination="3fr-Fd-pxx" id="T8g-mS-lxP"/>
@@ -144,7 +144,7 @@
<rect key="frame" x="0.0" y="0.0" width="512" height="147"/> <rect key="frame" x="0.0" y="0.0" width="512" height="147"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" selectionHighlightStyle="sourceList" columnReordering="NO" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" rowHeight="33" rowSizeStyle="automatic" viewBased="YES" floatsGroupRows="NO" id="xvJ-5c-vDp" customClass="MPElementsTableView"> <tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" selectionHighlightStyle="sourceList" columnReordering="NO" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" rowHeight="33" rowSizeStyle="automatic" viewBased="YES" floatsGroupRows="NO" id="xvJ-5c-vDp" customClass="MPSitesTableView">
<rect key="frame" x="0.0" y="0.0" width="515" height="147"/> <rect key="frame" x="0.0" y="0.0" width="515" height="147"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="2"/> <size key="intercellSpacing" width="3" height="2"/>
@@ -262,7 +262,7 @@
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.0" colorSpace="calibratedRGB"/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="0.0" colorSpace="calibratedRGB"/>
</searchFieldCell> </searchFieldCell>
<connections> <connections>
<action selector="doSearchElements:" target="-2" id="NJO-iR-OXt"/> <action selector="doSearchSites:" target="-2" id="NJO-iR-OXt"/>
<binding destination="-2" name="hidden" keyPath="locked" id="fAX-uK-cgn"/> <binding destination="-2" name="hidden" keyPath="locked" id="fAX-uK-cgn"/>
<outlet property="delegate" destination="-2" id="2WA-uI-asx"/> <outlet property="delegate" destination="-2" id="2WA-uI-asx"/>
</connections> </connections>
@@ -371,7 +371,7 @@
<modifierMask key="keyEquivalentModifierMask" command="YES"/> <modifierMask key="keyEquivalentModifierMask" command="YES"/>
</buttonCell> </buttonCell>
<connections> <connections>
<action selector="deleteElement:" target="-2" id="mVT-O6-KfN"/> <action selector="deleteSite:" target="-2" id="mVT-O6-KfN"/>
<binding destination="mcS-ik-b0n" name="hidden" keyPath="canRemove" id="eqU-d2-XhQ"> <binding destination="mcS-ik-b0n" name="hidden" keyPath="canRemove" id="eqU-d2-XhQ">
<dictionary key="options"> <dictionary key="options">
<string key="NSValueTransformerName">NSNegateBoolean</string> <string key="NSValueTransformerName">NSNegateBoolean</string>
@@ -869,9 +869,9 @@
</view> </view>
</window> </window>
<userDefaultsController representsSharedInstance="YES" id="yy2-3W-Ocj"/> <userDefaultsController representsSharedInstance="YES" id="yy2-3W-Ocj"/>
<arrayController objectClassName="MPElementModel" id="mcS-ik-b0n"> <arrayController objectClassName="MPSiteModel" id="mcS-ik-b0n">
<connections> <connections>
<binding destination="-2" name="contentArray" keyPath="elements" id="c96-Dv-HK1"/> <binding destination="-2" name="contentArray" keyPath="sites" id="c96-Dv-HK1"/>
</connections> </connections>
</arrayController> </arrayController>
<box autoresizesSubviews="NO" title="Password Types" borderType="line" titlePosition="noTitle" id="bZe-7q-i6q"> <box autoresizesSubviews="NO" title="Password Types" borderType="line" titlePosition="noTitle" id="bZe-7q-i6q">

View File

@@ -9,20 +9,20 @@
*/ */
// //
// MPElementModel.h // MPSiteModel.h
// MPElementModel // MPSiteModel
// //
// Created by lhunath on 2/11/2014. // Created by lhunath on 2/11/2014.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved. // Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
// //
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
@class MPElementEntity; @class MPSiteEntity;
@interface MPElementModel : NSObject @interface MPSiteModel : NSObject
@property (nonatomic) NSString *siteName; @property (nonatomic) NSString *siteName;
@property (nonatomic) MPElementType type; @property (nonatomic) MPSiteType type;
@property (nonatomic) NSString *typeName; @property (nonatomic) NSString *typeName;
@property (nonatomic) NSString *content; @property (nonatomic) NSString *content;
@property (nonatomic) NSString *contentDisplay; @property (nonatomic) NSString *contentDisplay;
@@ -34,8 +34,8 @@
@property (nonatomic) BOOL generated; @property (nonatomic) BOOL generated;
@property (nonatomic) BOOL stored; @property (nonatomic) BOOL stored;
- (id)initWithEntity:(MPElementEntity *)entity; - (id)initWithEntity:(MPSiteEntity *)entity;
- (MPElementEntity *)entityInContext:(NSManagedObjectContext *)moc; - (MPSiteEntity *)entityInContext:(NSManagedObjectContext *)moc;
- (void)updateContent; - (void)updateContent;
@end @end

View File

@@ -9,26 +9,26 @@
*/ */
// //
// MPElementModel.h // MPSiteModel.h
// MPElementModel // MPSiteModel
// //
// Created by lhunath on 2/11/2014. // Created by lhunath on 2/11/2014.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved. // Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
// //
#import "MPElementModel.h" #import "MPSiteModel.h"
#import "MPElementEntity.h" #import "MPSiteEntity.h"
#import "MPEntities.h" #import "MPEntities.h"
#import "MPAppDelegate_Shared.h" #import "MPAppDelegate_Shared.h"
#import "MPAppDelegate_Store.h" #import "MPAppDelegate_Store.h"
#import "MPMacAppDelegate.h" #import "MPMacAppDelegate.h"
@implementation MPElementModel { @implementation MPSiteModel {
NSManagedObjectID *_entityOID; NSManagedObjectID *_entityOID;
BOOL _initialized; BOOL _initialized;
} }
- (id)initWithEntity:(MPElementEntity *)entity { - (id)initWithEntity:(MPSiteEntity *)entity {
if (!(self = [super init])) if (!(self = [super init]))
return nil; return nil;
@@ -39,7 +39,7 @@
return self; return self;
} }
- (void)setEntity:(MPElementEntity *)entity { - (void)setEntity:(MPSiteEntity *)entity {
if ([_entityOID isEqual:entity.objectID]) if ([_entityOID isEqual:entity.objectID])
return; return;
@@ -52,21 +52,21 @@
self.type = entity.type; self.type = entity.type;
self.typeName = entity.typeName; self.typeName = entity.typeName;
self.uses = entity.uses_; self.uses = entity.uses_;
self.counter = [entity isKindOfClass:[MPElementGeneratedEntity class]]? [(MPElementGeneratedEntity *)entity counter]: 0; self.counter = [entity isKindOfClass:[MPGeneratedSiteEntity class]]? [(MPGeneratedSiteEntity *)entity counter]: 0;
// Find all password types and the index of the current type amongst them. // Find all password types and the index of the current type amongst them.
[self updateContent:entity]; [self updateContent:entity];
} }
- (MPElementEntity *)entityInContext:(NSManagedObjectContext *)moc { - (MPSiteEntity *)entityInContext:(NSManagedObjectContext *)moc {
if (!_entityOID) if (!_entityOID)
return nil; return nil;
NSError *error; NSError *error;
MPElementEntity *entity = (MPElementEntity *)[moc existingObjectWithID:_entityOID error:&error]; MPSiteEntity *entity = (MPSiteEntity *)[moc existingObjectWithID:_entityOID error:&error];
if (!entity) if (!entity)
err( @"Couldn't retrieve active element: %@", error ); err( @"Couldn't retrieve active site: %@", [error fullDescription] );
return entity; return entity;
} }
@@ -82,9 +82,9 @@
return; return;
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPElementEntity *entity = [self entityInContext:context]; MPSiteEntity *entity = [self entityInContext:context];
if ([entity isKindOfClass:[MPElementGeneratedEntity class]]) { if ([entity isKindOfClass:[MPGeneratedSiteEntity class]]) {
((MPElementGeneratedEntity *)entity).counter = counter; ((MPGeneratedSiteEntity *)entity).counter = counter;
[context saveToStore]; [context saveToStore];
[self updateContent:entity]; [self updateContent:entity];
@@ -94,22 +94,22 @@
- (BOOL)generated { - (BOOL)generated {
return self.type & MPElementTypeClassGenerated; return self.type & MPSiteTypeClassGenerated;
} }
- (BOOL)stored { - (BOOL)stored {
return self.type & MPElementTypeClassStored; return self.type & MPSiteTypeClassStored;
} }
- (void)updateContent { - (void)updateContent {
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
[self updateContent:[MPElementEntity existingObjectWithID:_entityOID inContext:context]]; [self updateContent:[MPSiteEntity existingObjectWithID:_entityOID inContext:context]];
}]; }];
} }
- (void)updateContent:(MPElementEntity *)entity { - (void)updateContent:(MPSiteEntity *)entity {
static NSRegularExpression *re_anyChar; static NSRegularExpression *re_anyChar;
static dispatch_once_t once = 0; static dispatch_once_t once = 0;

View File

@@ -9,8 +9,8 @@
*/ */
// //
// MPElementsTableView.h // MPSitesTableView.h
// MPElementsTableView // MPSitesTableView
// //
// Created by lhunath on 2014-06-30. // Created by lhunath on 2014-06-30.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved. // Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
@@ -20,7 +20,7 @@
@class MPPasswordWindowController; @class MPPasswordWindowController;
@interface MPElementsTableView : NSTableView @interface MPSitesTableView : NSTableView
@property(nonatomic, weak) MPPasswordWindowController *controller; @property(nonatomic, weak) MPPasswordWindowController *controller;

View File

@@ -9,17 +9,17 @@
*/ */
// //
// MPElementsTableView.h // MPSitesTableView.h
// MPElementsTableView // MPSitesTableView
// //
// Created by lhunath on 2014-06-30. // Created by lhunath on 2014-06-30.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved. // Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
// //
#import "MPElementsTableView.h" #import "MPSitesTableView.h"
#import "MPPasswordWindowController.h" #import "MPPasswordWindowController.h"
@implementation MPElementsTableView @implementation MPSitesTableView
- (void)doCommandBySelector:(SEL)aSelector { - (void)doCommandBySelector:(SEL)aSelector {

View File

@@ -19,6 +19,10 @@
<string></string> <string></string>
<key>CFBundleTypeIconFiles</key> <key>CFBundleTypeIconFiles</key>
<array/> <array/>
<key>CFBundleTypeMIMETypes</key>
<array>
<string>text/plain</string>
</array>
<key>CFBundleTypeName</key> <key>CFBundleTypeName</key>
<string>Master Password sites</string> <string>Master Password sites</string>
<key>CFBundleTypeRole</key> <key>CFBundleTypeRole</key>
@@ -27,8 +31,10 @@
<string>Owner</string> <string>Owner</string>
<key>LSItemContentTypes</key> <key>LSItemContentTypes</key>
<array> <array>
<string>com.lyndir.lhunath.MasterPassword.sites</string> <string>com.lyndir.masterpassword.sites</string>
</array> </array>
<key>LSTypeIsPackage</key>
<integer>0</integer>
</dict> </dict>
</array> </array>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
@@ -47,19 +53,6 @@
<string>[auto]</string> <string>[auto]</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>com.lyndir.lhunath.MasterPassword</string>
<key>CFBundleURLSchemes</key>
<array>
<string>com.lyndir.lhunath.MasterPassword</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>[auto]</string> <string>[auto]</string>
<key>LSApplicationCategoryType</key> <key>LSApplicationCategoryType</key>
@@ -77,12 +70,16 @@
<key>UTExportedTypeDeclarations</key> <key>UTExportedTypeDeclarations</key>
<array> <array>
<dict> <dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.utf8-plain-text</string>
</array>
<key>UTTypeDescription</key> <key>UTTypeDescription</key>
<string>Master Password sites</string> <string>Master Password sites</string>
<key>UTTypeIconFile</key> <key>UTTypeIconFile</key>
<string></string> <string></string>
<key>UTTypeIdentifier</key> <key>UTTypeIdentifier</key>
<string>com.lyndir.lhunath.MasterPassword.sites</string> <string>com.lyndir.masterpassword.sites</string>
<key>UTTypeSize320IconFile</key> <key>UTTypeSize320IconFile</key>
<string></string> <string></string>
<key>UTTypeSize64IconFile</key> <key>UTTypeSize64IconFile</key>

View File

@@ -9,12 +9,12 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
93D390C676DF52DA7E459F19 /* MPPasswordWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D9D0061FF1159998F06 /* MPPasswordWindow.m */; }; 93D390C676DF52DA7E459F19 /* MPPasswordWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D9D0061FF1159998F06 /* MPPasswordWindow.m */; };
93D392EC39DA43C46C692C12 /* NSDictionary+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */; }; 93D392EC39DA43C46C692C12 /* NSDictionary+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */; };
93D394C4254EEB45FB335AFB /* MPElementsTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39423D7BF4FD31FE6D27C /* MPElementsTableView.m */; }; 93D394C4254EEB45FB335AFB /* MPSitesTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39423D7BF4FD31FE6D27C /* MPSitesTableView.m */; };
93D395F08A087F8A24689347 /* NSArray+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */; }; 93D395F08A087F8A24689347 /* NSArray+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */; };
93D3970BCF85F7902E611168 /* PearlProfiler.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39DB3A8ADED08C39A6228 /* PearlProfiler.m */; }; 93D3970BCF85F7902E611168 /* PearlProfiler.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39DB3A8ADED08C39A6228 /* PearlProfiler.m */; };
93D39784E725A34D1EE3FB3B /* MPInitialWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D3CB30874147D9A9E1B /* MPInitialWindowController.m */; }; 93D39784E725A34D1EE3FB3B /* MPInitialWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D3CB30874147D9A9E1B /* MPInitialWindowController.m */; };
93D39C34FE35830EF5BE1D2A /* NSArray+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D396D04E57792A54D437AC /* NSArray+Indexing.h */; }; 93D39C34FE35830EF5BE1D2A /* NSArray+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D396D04E57792A54D437AC /* NSArray+Indexing.h */; };
93D39C5789EFA607CF788082 /* MPElementModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39E73BF5CBF8E5B005CD3 /* MPElementModel.m */; }; 93D39C5789EFA607CF788082 /* MPSiteModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39E73BF5CBF8E5B005CD3 /* MPSiteModel.m */; };
93D39D304F73B3BBA031522A /* PearlProfiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D394EEFF5BF555A55AF361 /* PearlProfiler.h */; }; 93D39D304F73B3BBA031522A /* PearlProfiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D394EEFF5BF555A55AF361 /* PearlProfiler.h */; };
93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */; }; 93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */; };
93D39F833DEC1C89B2F795AC /* MPPasswordWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39A57A7823DE98A0FF83C /* MPPasswordWindowController.m */; }; 93D39F833DEC1C89B2F795AC /* MPPasswordWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39A57A7823DE98A0FF83C /* MPPasswordWindowController.m */; };
@@ -43,6 +43,11 @@
DA30E9D215722EE500A68B4C /* Pearl-Crypto.m in Sources */ = {isa = PBXBuildFile; fileRef = DA30E9D115722EE500A68B4C /* Pearl-Crypto.m */; }; DA30E9D215722EE500A68B4C /* Pearl-Crypto.m in Sources */ = {isa = PBXBuildFile; fileRef = DA30E9D115722EE500A68B4C /* Pearl-Crypto.m */; };
DA30E9D715723E6900A68B4C /* PearlLazy.h in Headers */ = {isa = PBXBuildFile; fileRef = DA30E9D515723E6900A68B4C /* PearlLazy.h */; }; DA30E9D715723E6900A68B4C /* PearlLazy.h in Headers */ = {isa = PBXBuildFile; fileRef = DA30E9D515723E6900A68B4C /* PearlLazy.h */; };
DA30E9D815723E6900A68B4C /* PearlLazy.m in Sources */ = {isa = PBXBuildFile; fileRef = DA30E9D615723E6900A68B4C /* PearlLazy.m */; }; DA30E9D815723E6900A68B4C /* PearlLazy.m in Sources */ = {isa = PBXBuildFile; fileRef = DA30E9D615723E6900A68B4C /* PearlLazy.m */; };
DA32CFD919CF1C70004F3F0E /* MPGeneratedSiteEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA32CFD819CF1C70004F3F0E /* MPGeneratedSiteEntity.m */; };
DA32CFDC19CF1C70004F3F0E /* MPStoredSiteEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA32CFDB19CF1C70004F3F0E /* MPStoredSiteEntity.m */; };
DA32CFDF19CF1C70004F3F0E /* MPSiteEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA32CFDE19CF1C70004F3F0E /* MPSiteEntity.m */; };
DA32CFE219CF1C71004F3F0E /* MPSiteQuestionEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA32CFE119CF1C71004F3F0E /* MPSiteQuestionEntity.m */; };
DA32CFE519CF1C71004F3F0E /* MPUserEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA32CFE419CF1C71004F3F0E /* MPUserEntity.m */; };
DA3509FE15F101A500C14A8E /* PearlQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = DA3509FC15F101A500C14A8E /* PearlQueue.h */; }; DA3509FE15F101A500C14A8E /* PearlQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = DA3509FC15F101A500C14A8E /* PearlQueue.h */; };
DA3509FF15F101A500C14A8E /* PearlQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = DA3509FD15F101A500C14A8E /* PearlQueue.m */; }; DA3509FF15F101A500C14A8E /* PearlQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = DA3509FD15F101A500C14A8E /* PearlQueue.m */; };
DA3B844F190FC60900246EEA /* Crashlytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA3B844A190FC5A900246EEA /* Crashlytics.framework */; }; DA3B844F190FC60900246EEA /* Crashlytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA3B844A190FC5A900246EEA /* Crashlytics.framework */; };
@@ -60,12 +65,8 @@
DA5E5CFA1724A667003798D8 /* MPAppDelegate_Shared.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CA01724A667003798D8 /* MPAppDelegate_Shared.m */; }; DA5E5CFA1724A667003798D8 /* MPAppDelegate_Shared.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CA01724A667003798D8 /* MPAppDelegate_Shared.m */; };
DA5E5CFB1724A667003798D8 /* MPAppDelegate_Store.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CA21724A667003798D8 /* MPAppDelegate_Store.m */; }; DA5E5CFB1724A667003798D8 /* MPAppDelegate_Store.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CA21724A667003798D8 /* MPAppDelegate_Store.m */; };
DA5E5CFC1724A667003798D8 /* MPConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CA41724A667003798D8 /* MPConfig.m */; }; DA5E5CFC1724A667003798D8 /* MPConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CA41724A667003798D8 /* MPConfig.m */; };
DA5E5CFD1724A667003798D8 /* MPElementEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CA61724A667003798D8 /* MPElementEntity.m */; };
DA5E5CFE1724A667003798D8 /* MPElementGeneratedEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CA81724A667003798D8 /* MPElementGeneratedEntity.m */; };
DA5E5CFF1724A667003798D8 /* MPElementStoredEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CAA1724A667003798D8 /* MPElementStoredEntity.m */; };
DA5E5D001724A667003798D8 /* MPEntities.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CAC1724A667003798D8 /* MPEntities.m */; }; DA5E5D001724A667003798D8 /* MPEntities.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CAC1724A667003798D8 /* MPEntities.m */; };
DA5E5D011724A667003798D8 /* MPKey.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CAE1724A667003798D8 /* MPKey.m */; }; DA5E5D011724A667003798D8 /* MPKey.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CAE1724A667003798D8 /* MPKey.m */; };
DA5E5D021724A667003798D8 /* MPUserEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CB11724A667003798D8 /* MPUserEntity.m */; };
DA5E5D031724A667003798D8 /* MPMacAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CB41724A667003798D8 /* MPMacAppDelegate.m */; }; DA5E5D031724A667003798D8 /* MPMacAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CB41724A667003798D8 /* MPMacAppDelegate.m */; };
DA5E5D041724A667003798D8 /* MPMacConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CB61724A667003798D8 /* MPMacConfig.m */; }; DA5E5D041724A667003798D8 /* MPMacConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CB61724A667003798D8 /* MPMacConfig.m */; };
DA5E5D081724A667003798D8 /* MasterPassword.entitlements in Resources */ = {isa = PBXBuildFile; fileRef = DA5E5CBF1724A667003798D8 /* MasterPassword.entitlements */; }; DA5E5D081724A667003798D8 /* MasterPassword.entitlements in Resources */ = {isa = PBXBuildFile; fileRef = DA5E5CBF1724A667003798D8 /* MasterPassword.entitlements */; };
@@ -229,21 +230,21 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Indexing.m"; sourceTree = "<group>"; }; 93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Indexing.m"; sourceTree = "<group>"; };
93D39240B5143E01F0B75E96 /* MPElementModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementModel.h; sourceTree = "<group>"; }; 93D39240B5143E01F0B75E96 /* MPSiteModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSiteModel.h; sourceTree = "<group>"; };
93D392C3918763B3B72CF366 /* MPPasswordWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordWindowController.h; sourceTree = "<group>"; }; 93D392C3918763B3B72CF366 /* MPPasswordWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordWindowController.h; sourceTree = "<group>"; };
93D39368EF3CBFEF2AFCA15A /* MPInitialWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPInitialWindowController.h; sourceTree = "<group>"; }; 93D39368EF3CBFEF2AFCA15A /* MPInitialWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPInitialWindowController.h; sourceTree = "<group>"; };
93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Indexing.h"; sourceTree = "<group>"; }; 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Indexing.h"; sourceTree = "<group>"; };
93D39423D7BF4FD31FE6D27C /* MPElementsTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementsTableView.m; sourceTree = "<group>"; }; 93D39423D7BF4FD31FE6D27C /* MPSitesTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSitesTableView.m; sourceTree = "<group>"; };
93D394EEFF5BF555A55AF361 /* PearlProfiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PearlProfiler.h; path = ../../../External/Pearl/Pearl/PearlProfiler.h; sourceTree = "<group>"; }; 93D394EEFF5BF555A55AF361 /* PearlProfiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PearlProfiler.h; path = ../../../External/Pearl/Pearl/PearlProfiler.h; sourceTree = "<group>"; };
93D396D04E57792A54D437AC /* NSArray+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Indexing.h"; sourceTree = "<group>"; }; 93D396D04E57792A54D437AC /* NSArray+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Indexing.h"; sourceTree = "<group>"; };
93D3977484534E99F9BA579D /* MPPasswordWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordWindow.h; sourceTree = "<group>"; }; 93D3977484534E99F9BA579D /* MPPasswordWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordWindow.h; sourceTree = "<group>"; };
93D39A57A7823DE98A0FF83C /* MPPasswordWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordWindowController.m; sourceTree = "<group>"; }; 93D39A57A7823DE98A0FF83C /* MPPasswordWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordWindowController.m; sourceTree = "<group>"; };
93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Indexing.m"; sourceTree = "<group>"; }; 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Indexing.m"; sourceTree = "<group>"; };
93D39AC6360DDC16AEAA4119 /* MPElementsTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementsTableView.h; sourceTree = "<group>"; }; 93D39AC6360DDC16AEAA4119 /* MPSitesTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSitesTableView.h; sourceTree = "<group>"; };
93D39D3CB30874147D9A9E1B /* MPInitialWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPInitialWindowController.m; sourceTree = "<group>"; }; 93D39D3CB30874147D9A9E1B /* MPInitialWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPInitialWindowController.m; sourceTree = "<group>"; };
93D39D9D0061FF1159998F06 /* MPPasswordWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordWindow.m; sourceTree = "<group>"; }; 93D39D9D0061FF1159998F06 /* MPPasswordWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordWindow.m; sourceTree = "<group>"; };
93D39DB3A8ADED08C39A6228 /* PearlProfiler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PearlProfiler.m; path = ../../../External/Pearl/Pearl/PearlProfiler.m; sourceTree = "<group>"; }; 93D39DB3A8ADED08C39A6228 /* PearlProfiler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PearlProfiler.m; path = ../../../External/Pearl/Pearl/PearlProfiler.m; sourceTree = "<group>"; };
93D39E73BF5CBF8E5B005CD3 /* MPElementModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementModel.m; sourceTree = "<group>"; }; 93D39E73BF5CBF8E5B005CD3 /* MPSiteModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSiteModel.m; sourceTree = "<group>"; };
DA0933C91747A56A00DE1CEF /* MPInitialWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MPInitialWindow.xib; sourceTree = "<group>"; }; DA0933C91747A56A00DE1CEF /* MPInitialWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MPInitialWindow.xib; sourceTree = "<group>"; };
DA0933CB1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "shot-laptop-leaning-iphone.png"; sourceTree = "<group>"; }; DA0933CB1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "shot-laptop-leaning-iphone.png"; sourceTree = "<group>"; };
DA0933CF1747B91B00DE1CEF /* appstore.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = appstore.png; sourceTree = "<group>"; }; DA0933CF1747B91B00DE1CEF /* appstore.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = appstore.png; sourceTree = "<group>"; };
@@ -274,6 +275,17 @@
DA30E9D115722EE500A68B4C /* Pearl-Crypto.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "Pearl-Crypto.m"; sourceTree = "<group>"; }; DA30E9D115722EE500A68B4C /* Pearl-Crypto.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "Pearl-Crypto.m"; sourceTree = "<group>"; };
DA30E9D515723E6900A68B4C /* PearlLazy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlLazy.h; sourceTree = "<group>"; }; DA30E9D515723E6900A68B4C /* PearlLazy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlLazy.h; sourceTree = "<group>"; };
DA30E9D615723E6900A68B4C /* PearlLazy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlLazy.m; sourceTree = "<group>"; }; DA30E9D615723E6900A68B4C /* PearlLazy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlLazy.m; sourceTree = "<group>"; };
DA32CFD719CF1C70004F3F0E /* MPGeneratedSiteEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPGeneratedSiteEntity.h; sourceTree = "<group>"; };
DA32CFD819CF1C70004F3F0E /* MPGeneratedSiteEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPGeneratedSiteEntity.m; sourceTree = "<group>"; };
DA32CFDA19CF1C70004F3F0E /* MPStoredSiteEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPStoredSiteEntity.h; sourceTree = "<group>"; };
DA32CFDB19CF1C70004F3F0E /* MPStoredSiteEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPStoredSiteEntity.m; sourceTree = "<group>"; };
DA32CFDD19CF1C70004F3F0E /* MPSiteEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSiteEntity.h; sourceTree = "<group>"; };
DA32CFDE19CF1C70004F3F0E /* MPSiteEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSiteEntity.m; sourceTree = "<group>"; };
DA32CFE019CF1C71004F3F0E /* MPSiteQuestionEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSiteQuestionEntity.h; sourceTree = "<group>"; };
DA32CFE119CF1C71004F3F0E /* MPSiteQuestionEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSiteQuestionEntity.m; sourceTree = "<group>"; };
DA32CFE319CF1C71004F3F0E /* MPUserEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUserEntity.h; sourceTree = "<group>"; };
DA32CFE419CF1C71004F3F0E /* MPUserEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUserEntity.m; sourceTree = "<group>"; };
DA32D00019CF470E004F3F0E /* MasterPassword 6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 6.xcdatamodel"; sourceTree = "<group>"; };
DA3509FC15F101A500C14A8E /* PearlQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlQueue.h; sourceTree = "<group>"; }; DA3509FC15F101A500C14A8E /* PearlQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlQueue.h; sourceTree = "<group>"; };
DA3509FD15F101A500C14A8E /* PearlQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlQueue.m; sourceTree = "<group>"; }; DA3509FD15F101A500C14A8E /* PearlQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlQueue.m; sourceTree = "<group>"; };
DA3B844A190FC5A900246EEA /* Crashlytics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Crashlytics.framework; sourceTree = "<group>"; }; DA3B844A190FC5A900246EEA /* Crashlytics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Crashlytics.framework; sourceTree = "<group>"; };
@@ -302,19 +314,11 @@
DA5E5CA21724A667003798D8 /* MPAppDelegate_Store.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAppDelegate_Store.m; sourceTree = "<group>"; }; DA5E5CA21724A667003798D8 /* MPAppDelegate_Store.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAppDelegate_Store.m; sourceTree = "<group>"; };
DA5E5CA31724A667003798D8 /* MPConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPConfig.h; sourceTree = "<group>"; }; DA5E5CA31724A667003798D8 /* MPConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPConfig.h; sourceTree = "<group>"; };
DA5E5CA41724A667003798D8 /* MPConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPConfig.m; sourceTree = "<group>"; }; DA5E5CA41724A667003798D8 /* MPConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPConfig.m; sourceTree = "<group>"; };
DA5E5CA51724A667003798D8 /* MPElementEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementEntity.h; sourceTree = "<group>"; };
DA5E5CA61724A667003798D8 /* MPElementEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementEntity.m; sourceTree = "<group>"; };
DA5E5CA71724A667003798D8 /* MPElementGeneratedEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementGeneratedEntity.h; sourceTree = "<group>"; };
DA5E5CA81724A667003798D8 /* MPElementGeneratedEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementGeneratedEntity.m; sourceTree = "<group>"; };
DA5E5CA91724A667003798D8 /* MPElementStoredEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementStoredEntity.h; sourceTree = "<group>"; };
DA5E5CAA1724A667003798D8 /* MPElementStoredEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementStoredEntity.m; sourceTree = "<group>"; };
DA5E5CAB1724A667003798D8 /* MPEntities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPEntities.h; sourceTree = "<group>"; }; DA5E5CAB1724A667003798D8 /* MPEntities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPEntities.h; sourceTree = "<group>"; };
DA5E5CAC1724A667003798D8 /* MPEntities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPEntities.m; sourceTree = "<group>"; }; DA5E5CAC1724A667003798D8 /* MPEntities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPEntities.m; sourceTree = "<group>"; };
DA5E5CAD1724A667003798D8 /* MPKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPKey.h; sourceTree = "<group>"; }; DA5E5CAD1724A667003798D8 /* MPKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPKey.h; sourceTree = "<group>"; };
DA5E5CAE1724A667003798D8 /* MPKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPKey.m; sourceTree = "<group>"; }; DA5E5CAE1724A667003798D8 /* MPKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPKey.m; sourceTree = "<group>"; };
DA5E5CAF1724A667003798D8 /* MPTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPTypes.h; sourceTree = "<group>"; }; DA5E5CAF1724A667003798D8 /* MPTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPTypes.h; sourceTree = "<group>"; };
DA5E5CB01724A667003798D8 /* MPUserEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUserEntity.h; sourceTree = "<group>"; };
DA5E5CB11724A667003798D8 /* MPUserEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUserEntity.m; sourceTree = "<group>"; };
DA5E5CB31724A667003798D8 /* MPMacAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPMacAppDelegate.h; sourceTree = "<group>"; }; DA5E5CB31724A667003798D8 /* MPMacAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPMacAppDelegate.h; sourceTree = "<group>"; };
DA5E5CB41724A667003798D8 /* MPMacAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPMacAppDelegate.m; sourceTree = "<group>"; }; DA5E5CB41724A667003798D8 /* MPMacAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPMacAppDelegate.m; sourceTree = "<group>"; };
DA5E5CB51724A667003798D8 /* MPMacConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPMacConfig.h; sourceTree = "<group>"; }; DA5E5CB51724A667003798D8 /* MPMacConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPMacConfig.h; sourceTree = "<group>"; };
@@ -995,6 +999,16 @@
DA5E5C961724A667003798D8 /* ObjC */ = { DA5E5C961724A667003798D8 /* ObjC */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DA32CFE319CF1C71004F3F0E /* MPUserEntity.h */,
DA32CFE419CF1C71004F3F0E /* MPUserEntity.m */,
DA32CFE019CF1C71004F3F0E /* MPSiteQuestionEntity.h */,
DA32CFE119CF1C71004F3F0E /* MPSiteQuestionEntity.m */,
DA32CFDD19CF1C70004F3F0E /* MPSiteEntity.h */,
DA32CFDE19CF1C70004F3F0E /* MPSiteEntity.m */,
DA32CFDA19CF1C70004F3F0E /* MPStoredSiteEntity.h */,
DA32CFDB19CF1C70004F3F0E /* MPStoredSiteEntity.m */,
DA32CFD719CF1C70004F3F0E /* MPGeneratedSiteEntity.h */,
DA32CFD819CF1C70004F3F0E /* MPGeneratedSiteEntity.m */,
DA3B8454190FC89700246EEA /* MPFixable.m */, DA3B8454190FC89700246EEA /* MPFixable.m */,
DA3B8455190FC89700246EEA /* MPFixable.h */, DA3B8455190FC89700246EEA /* MPFixable.h */,
DA5E5CB21724A667003798D8 /* Mac */, DA5E5CB21724A667003798D8 /* Mac */,
@@ -1012,19 +1026,11 @@
DA5E5CA21724A667003798D8 /* MPAppDelegate_Store.m */, DA5E5CA21724A667003798D8 /* MPAppDelegate_Store.m */,
DA5E5CA31724A667003798D8 /* MPConfig.h */, DA5E5CA31724A667003798D8 /* MPConfig.h */,
DA5E5CA41724A667003798D8 /* MPConfig.m */, DA5E5CA41724A667003798D8 /* MPConfig.m */,
DA5E5CA51724A667003798D8 /* MPElementEntity.h */,
DA5E5CA61724A667003798D8 /* MPElementEntity.m */,
DA5E5CA71724A667003798D8 /* MPElementGeneratedEntity.h */,
DA5E5CA81724A667003798D8 /* MPElementGeneratedEntity.m */,
DA5E5CA91724A667003798D8 /* MPElementStoredEntity.h */,
DA5E5CAA1724A667003798D8 /* MPElementStoredEntity.m */,
DA5E5CAB1724A667003798D8 /* MPEntities.h */, DA5E5CAB1724A667003798D8 /* MPEntities.h */,
DA5E5CAC1724A667003798D8 /* MPEntities.m */, DA5E5CAC1724A667003798D8 /* MPEntities.m */,
DA5E5CAD1724A667003798D8 /* MPKey.h */, DA5E5CAD1724A667003798D8 /* MPKey.h */,
DA5E5CAE1724A667003798D8 /* MPKey.m */, DA5E5CAE1724A667003798D8 /* MPKey.m */,
DA5E5CAF1724A667003798D8 /* MPTypes.h */, DA5E5CAF1724A667003798D8 /* MPTypes.h */,
DA5E5CB01724A667003798D8 /* MPUserEntity.h */,
DA5E5CB11724A667003798D8 /* MPUserEntity.m */,
DA29992619C6A89900AF7DF1 /* MasterPassword.xcdatamodeld */, DA29992619C6A89900AF7DF1 /* MasterPassword.xcdatamodeld */,
); );
name = ObjC; name = ObjC;
@@ -1046,8 +1052,8 @@
DA5E5CC41724A667003798D8 /* MainMenu.xib */, DA5E5CC41724A667003798D8 /* MainMenu.xib */,
DA5E5CC61724A667003798D8 /* main.m */, DA5E5CC61724A667003798D8 /* main.m */,
DA0933C91747A56A00DE1CEF /* MPInitialWindow.xib */, DA0933C91747A56A00DE1CEF /* MPInitialWindow.xib */,
93D39E73BF5CBF8E5B005CD3 /* MPElementModel.m */, 93D39E73BF5CBF8E5B005CD3 /* MPSiteModel.m */,
93D39240B5143E01F0B75E96 /* MPElementModel.h */, 93D39240B5143E01F0B75E96 /* MPSiteModel.h */,
DA2508F019511D3600AC23F1 /* MPPasswordWindowController.xib */, DA2508F019511D3600AC23F1 /* MPPasswordWindowController.xib */,
93D39A57A7823DE98A0FF83C /* MPPasswordWindowController.m */, 93D39A57A7823DE98A0FF83C /* MPPasswordWindowController.m */,
93D392C3918763B3B72CF366 /* MPPasswordWindowController.h */, 93D392C3918763B3B72CF366 /* MPPasswordWindowController.h */,
@@ -1055,8 +1061,8 @@
93D3977484534E99F9BA579D /* MPPasswordWindow.h */, 93D3977484534E99F9BA579D /* MPPasswordWindow.h */,
93D39D3CB30874147D9A9E1B /* MPInitialWindowController.m */, 93D39D3CB30874147D9A9E1B /* MPInitialWindowController.m */,
93D39368EF3CBFEF2AFCA15A /* MPInitialWindowController.h */, 93D39368EF3CBFEF2AFCA15A /* MPInitialWindowController.h */,
93D39423D7BF4FD31FE6D27C /* MPElementsTableView.m */, 93D39423D7BF4FD31FE6D27C /* MPSitesTableView.m */,
93D39AC6360DDC16AEAA4119 /* MPElementsTableView.h */, 93D39AC6360DDC16AEAA4119 /* MPSitesTableView.h */,
); );
path = Mac; path = Mac;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -1943,7 +1949,7 @@
attributes = { attributes = {
CLASSPREFIX = MP; CLASSPREFIX = MP;
LastTestingUpgradeCheck = 0510; LastTestingUpgradeCheck = 0510;
LastUpgradeCheck = 0510; LastUpgradeCheck = 0600;
ORGANIZATIONNAME = Lyndir; ORGANIZATIONNAME = Lyndir;
TargetAttributes = { TargetAttributes = {
DA5BFA43147E415C00F98B1E = { DA5BFA43147E415C00F98B1E = {
@@ -1958,12 +1964,10 @@
}; };
buildConfigurationList = DA5BFA3E147E415C00F98B1E /* Build configuration list for PBXProject "MasterPassword-Mac" */; buildConfigurationList = DA5BFA3E147E415C00F98B1E /* Build configuration list for PBXProject "MasterPassword-Mac" */;
compatibilityVersion = "Xcode 3.2"; compatibilityVersion = "Xcode 3.2";
developmentRegion = English; developmentRegion = en;
hasScannedForEncodings = 0; hasScannedForEncodings = 0;
knownRegions = ( knownRegions = (
en, en,
English,
es,
); );
mainGroup = DA5BFA39147E415C00F98B1E; mainGroup = DA5BFA39147E415C00F98B1E;
productRefGroup = DA5BFA45147E415C00F98B1E /* Products */; productRefGroup = DA5BFA45147E415C00F98B1E /* Products */;
@@ -2138,28 +2142,29 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
DA5E5CF61724A667003798D8 /* MPAlgorithm.m in Sources */, DA5E5CF61724A667003798D8 /* MPAlgorithm.m in Sources */,
DA32CFE219CF1C71004F3F0E /* MPSiteQuestionEntity.m in Sources */,
DA32CFE519CF1C71004F3F0E /* MPUserEntity.m in Sources */,
DA5E5CF71724A667003798D8 /* MPAlgorithmV0.m in Sources */, DA5E5CF71724A667003798D8 /* MPAlgorithmV0.m in Sources */,
DA5E5CF81724A667003798D8 /* MPAlgorithmV1.m in Sources */, DA5E5CF81724A667003798D8 /* MPAlgorithmV1.m in Sources */,
DA5E5CF91724A667003798D8 /* MPAppDelegate_Key.m in Sources */, DA5E5CF91724A667003798D8 /* MPAppDelegate_Key.m in Sources */,
DA5E5CFA1724A667003798D8 /* MPAppDelegate_Shared.m in Sources */, DA5E5CFA1724A667003798D8 /* MPAppDelegate_Shared.m in Sources */,
DA5E5CFB1724A667003798D8 /* MPAppDelegate_Store.m in Sources */, DA5E5CFB1724A667003798D8 /* MPAppDelegate_Store.m in Sources */,
DA5E5CFC1724A667003798D8 /* MPConfig.m in Sources */, DA5E5CFC1724A667003798D8 /* MPConfig.m in Sources */,
DA5E5CFD1724A667003798D8 /* MPElementEntity.m in Sources */,
DA5E5CFE1724A667003798D8 /* MPElementGeneratedEntity.m in Sources */,
DA5E5CFF1724A667003798D8 /* MPElementStoredEntity.m in Sources */,
DA29992C19C6A89900AF7DF1 /* MasterPassword.xcdatamodeld in Sources */, DA29992C19C6A89900AF7DF1 /* MasterPassword.xcdatamodeld in Sources */,
DA3B8456190FC89700246EEA /* MPFixable.m in Sources */, DA3B8456190FC89700246EEA /* MPFixable.m in Sources */,
DA5E5D001724A667003798D8 /* MPEntities.m in Sources */, DA5E5D001724A667003798D8 /* MPEntities.m in Sources */,
DA5E5D011724A667003798D8 /* MPKey.m in Sources */, DA5E5D011724A667003798D8 /* MPKey.m in Sources */,
DA5E5D021724A667003798D8 /* MPUserEntity.m in Sources */, DA32CFDC19CF1C70004F3F0E /* MPStoredSiteEntity.m in Sources */,
DA5E5D031724A667003798D8 /* MPMacAppDelegate.m in Sources */, DA5E5D031724A667003798D8 /* MPMacAppDelegate.m in Sources */,
DA5E5D041724A667003798D8 /* MPMacConfig.m in Sources */, DA5E5D041724A667003798D8 /* MPMacConfig.m in Sources */,
DA5E5D0C1724A667003798D8 /* main.m in Sources */, DA5E5D0C1724A667003798D8 /* main.m in Sources */,
93D39C5789EFA607CF788082 /* MPElementModel.m in Sources */, 93D39C5789EFA607CF788082 /* MPSiteModel.m in Sources */,
93D39F833DEC1C89B2F795AC /* MPPasswordWindowController.m in Sources */, 93D39F833DEC1C89B2F795AC /* MPPasswordWindowController.m in Sources */,
DA32CFD919CF1C70004F3F0E /* MPGeneratedSiteEntity.m in Sources */,
93D390C676DF52DA7E459F19 /* MPPasswordWindow.m in Sources */, 93D390C676DF52DA7E459F19 /* MPPasswordWindow.m in Sources */,
93D39784E725A34D1EE3FB3B /* MPInitialWindowController.m in Sources */, 93D39784E725A34D1EE3FB3B /* MPInitialWindowController.m in Sources */,
93D394C4254EEB45FB335AFB /* MPElementsTableView.m in Sources */, DA32CFDF19CF1C70004F3F0E /* MPSiteEntity.m in Sources */,
93D394C4254EEB45FB335AFB /* MPSitesTableView.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -2265,6 +2270,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
COMBINE_HIDPI_IMAGES = YES;
}; };
name = "Debug-Mac"; name = "Debug-Mac";
}; };
@@ -2272,6 +2278,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
COMBINE_HIDPI_IMAGES = YES;
}; };
name = "AdHoc-Mac"; name = "AdHoc-Mac";
}; };
@@ -2279,6 +2286,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
COMBINE_HIDPI_IMAGES = YES;
}; };
name = "AppStore-Mac"; name = "AppStore-Mac";
}; };
@@ -2286,6 +2294,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
COMBINE_HIDPI_IMAGES = YES;
}; };
name = "Debug-Mac"; name = "Debug-Mac";
}; };
@@ -2293,6 +2302,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
COMBINE_HIDPI_IMAGES = YES;
}; };
name = "AdHoc-Mac"; name = "AdHoc-Mac";
}; };
@@ -2300,6 +2310,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
COMBINE_HIDPI_IMAGES = YES;
}; };
name = "AppStore-Mac"; name = "AppStore-Mac";
}; };
@@ -2320,11 +2331,13 @@
CLANG_WARN_OBJC_RECEIVER_WEAK = NO; CLANG_WARN_OBJC_RECEIVER_WEAK = NO;
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO; CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES; CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES; CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99; GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_OPTIMIZATION_LEVEL = 0; GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES;
@@ -2393,11 +2406,13 @@
CLANG_WARN_OBJC_RECEIVER_WEAK = NO; CLANG_WARN_OBJC_RECEIVER_WEAK = NO;
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO; CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES; CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES; CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99; GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREPROCESSOR_DEFINITIONS = ( GCC_PREPROCESSOR_DEFINITIONS = (
@@ -2515,11 +2530,13 @@
CLANG_WARN_OBJC_RECEIVER_WEAK = NO; CLANG_WARN_OBJC_RECEIVER_WEAK = NO;
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO; CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES; CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES; CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99; GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREPROCESSOR_DEFINITIONS = ( GCC_PREPROCESSOR_DEFINITIONS = (
@@ -2600,6 +2617,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
COMBINE_HIDPI_IMAGES = YES;
DSTROOT = /tmp/Pearl.dst; DSTROOT = /tmp/Pearl.dst;
GCC_PREFIX_HEADER = "../Pearl/Pearl-Prefix.pch"; GCC_PREFIX_HEADER = "../Pearl/Pearl-Prefix.pch";
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
@@ -2619,6 +2637,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CLANG_ENABLE_OBJC_ARC = NO; CLANG_ENABLE_OBJC_ARC = NO;
COMBINE_HIDPI_IMAGES = YES;
DSTROOT = /tmp/jrswizzle.dst; DSTROOT = /tmp/jrswizzle.dst;
GCC_WARN_INHIBIT_ALL_WARNINGS = YES; GCC_WARN_INHIBIT_ALL_WARNINGS = YES;
OTHER_LDFLAGS = "-ObjC"; OTHER_LDFLAGS = "-ObjC";
@@ -2630,18 +2649,21 @@
DABC6C0B175D8C85000C15D4 /* Debug-Mac */ = { DABC6C0B175D8C85000C15D4 /* Debug-Mac */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
}; };
name = "Debug-Mac"; name = "Debug-Mac";
}; };
DABC6C0C175D8C85000C15D4 /* AdHoc-Mac */ = { DABC6C0C175D8C85000C15D4 /* AdHoc-Mac */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
}; };
name = "AdHoc-Mac"; name = "AdHoc-Mac";
}; };
DABC6C0D175D8C85000C15D4 /* AppStore-Mac */ = { DABC6C0D175D8C85000C15D4 /* AppStore-Mac */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
}; };
name = "AppStore-Mac"; name = "AppStore-Mac";
}; };
@@ -2649,6 +2671,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CLANG_ENABLE_OBJC_ARC = NO; CLANG_ENABLE_OBJC_ARC = NO;
COMBINE_HIDPI_IMAGES = YES;
DSTROOT = /tmp/jrswizzle.dst; DSTROOT = /tmp/jrswizzle.dst;
GCC_WARN_INHIBIT_ALL_WARNINGS = YES; GCC_WARN_INHIBIT_ALL_WARNINGS = YES;
OTHER_LDFLAGS = "-ObjC"; OTHER_LDFLAGS = "-ObjC";
@@ -2661,6 +2684,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CLANG_ENABLE_OBJC_ARC = NO; CLANG_ENABLE_OBJC_ARC = NO;
COMBINE_HIDPI_IMAGES = YES;
DSTROOT = /tmp/jrswizzle.dst; DSTROOT = /tmp/jrswizzle.dst;
GCC_WARN_INHIBIT_ALL_WARNINGS = YES; GCC_WARN_INHIBIT_ALL_WARNINGS = YES;
OTHER_LDFLAGS = "-ObjC"; OTHER_LDFLAGS = "-ObjC";
@@ -2673,6 +2697,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
COMBINE_HIDPI_IMAGES = YES;
DSTROOT = /tmp/Pearl.dst; DSTROOT = /tmp/Pearl.dst;
GCC_PREFIX_HEADER = "../Pearl/Pearl-Prefix.pch"; GCC_PREFIX_HEADER = "../Pearl/Pearl-Prefix.pch";
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
@@ -2692,6 +2717,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
COMBINE_HIDPI_IMAGES = YES;
DSTROOT = /tmp/Pearl.dst; DSTROOT = /tmp/Pearl.dst;
GCC_PREFIX_HEADER = "../Pearl/Pearl-Prefix.pch"; GCC_PREFIX_HEADER = "../Pearl/Pearl-Prefix.pch";
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
@@ -2791,8 +2817,9 @@
DA29992919C6A89900AF7DF1 /* MasterPassword 3.xcdatamodel */, DA29992919C6A89900AF7DF1 /* MasterPassword 3.xcdatamodel */,
DA29992A19C6A89900AF7DF1 /* MasterPassword 4.xcdatamodel */, DA29992A19C6A89900AF7DF1 /* MasterPassword 4.xcdatamodel */,
DA29992B19C6A89900AF7DF1 /* MasterPassword 5.xcdatamodel */, DA29992B19C6A89900AF7DF1 /* MasterPassword 5.xcdatamodel */,
DA32D00019CF470E004F3F0E /* MasterPassword 6.xcdatamodel */,
); );
currentVersion = DA29992B19C6A89900AF7DF1 /* MasterPassword 5.xcdatamodel */; currentVersion = DA32D00019CF470E004F3F0E /* MasterPassword 6.xcdatamodel */;
path = MasterPassword.xcdatamodeld; path = MasterPassword.xcdatamodeld;
sourceTree = "<group>"; sourceTree = "<group>";
versionGroupType = wrapper.xcdatamodel; versionGroupType = wrapper.xcdatamodel;

View File

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

View File

@@ -1,30 +1,29 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" <model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="6244" systemVersion="13E28" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
lastSavedToolsVersion="1171" systemVersion="11E53" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic"> <entity name="MPElementEntity" representedClassName="MPSiteEntity" isAbstract="YES" elementID="58EE245C-6827-4C11-BB7E-5722F2426EC2" syncable="YES">
<entity name="MPElementEntity" representedClassName="MPElementEntity" isAbstract="YES" syncable="YES">
<attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/> <attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/>
<attribute name="lastUsed" attributeType="Date" syncable="YES"/> <attribute name="lastUsed" attributeType="Date" syncable="YES"/>
<attribute name="name" attributeType="String" minValueString="1" indexed="YES" syncable="YES"/> <attribute name="name" attributeType="String" minValueString="1" indexed="YES" syncable="YES"/>
<attribute name="type_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/> <attribute name="type_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/>
<attribute name="uses_" attributeType="Integer 16" defaultValueString="0" syncable="YES"/> <attribute name="uses_" attributeType="Integer 16" defaultValueString="0" syncable="YES"/>
<relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="elements" <relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="elements" inverseEntity="MPUserEntity" elementID="FC8AE32E-7BE3-4FA6-8611-B7DC0DB063EF" syncable="YES"/>
inverseEntity="MPUserEntity" syncable="YES"/>
</entity> </entity>
<entity name="MPElementGeneratedEntity" representedClassName="MPElementGeneratedEntity" parentEntity="MPElementEntity" syncable="YES"> <entity name="MPElementGeneratedEntity" representedClassName="MPGeneratedSiteEntity" parentEntity="MPElementEntity" elementID="789304EA-070D-4982-8C20-54EECFC20CB6" syncable="YES">
<attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/> <attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/>
</entity> </entity>
<entity name="MPElementStoredEntity" representedClassName="MPElementStoredEntity" parentEntity="MPElementEntity" syncable="YES"> <entity name="MPElementStoredEntity" representedClassName="MPStoredSiteEntity" parentEntity="MPElementEntity" elementID="BEEF1688-0CCD-4699-A86A-4D860FE2CEB8" syncable="YES">
<attribute name="contentObject" optional="YES" attributeType="Transformable" storedInTruthFile="YES" syncable="YES"/> <attribute name="contentObject" optional="YES" attributeType="Transformable" storedInTruthFile="YES" syncable="YES"/>
</entity> </entity>
<entity name="MPUserEntity" representedClassName="MPUserEntity" syncable="YES"> <entity name="MPUserEntity" representedClassName="MPUserEntity" elementID="D18D6772-040E-4CFE-8F32-A34B08E9E9BC" syncable="YES">
<attribute name="avatar_" attributeType="Integer 16" defaultValueString="0" syncable="YES"/> <attribute name="avatar_" attributeType="Integer 16" defaultValueString="0" syncable="YES"/>
<attribute name="defaultType_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/> <attribute name="defaultType_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/>
<attribute name="keyID" optional="YES" attributeType="Binary" syncable="YES"/> <attribute name="keyID" optional="YES" attributeType="Binary" syncable="YES"/>
<attribute name="lastUsed" optional="YES" attributeType="Date" syncable="YES"/> <attribute name="lastUsed" optional="YES" attributeType="Date" syncable="YES"/>
<attribute name="name" attributeType="String" syncable="YES"/> <attribute name="name" attributeType="String" syncable="YES"/>
<attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO"/> <attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO">
<relationship name="elements" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPElementEntity" <userInfo/>
inverseName="user" inverseEntity="MPElementEntity" syncable="YES"/> </attribute>
<relationship name="elements" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPElementEntity" inverseName="user" inverseEntity="MPElementEntity" syncable="YES"/>
</entity> </entity>
<elements> <elements>
<element name="MPElementEntity" positionX="160" positionY="192" width="128" height="135"/> <element name="MPElementEntity" positionX="160" positionY="192" width="128" height="135"/>
@@ -32,4 +31,4 @@
<element name="MPElementStoredEntity" positionX="160" positionY="192" width="128" height="60"/> <element name="MPElementStoredEntity" positionX="160" positionY="192" width="128" height="60"/>
<element name="MPUserEntity" positionX="160" positionY="192" width="128" height="150"/> <element name="MPUserEntity" positionX="160" positionY="192" width="128" height="150"/>
</elements> </elements>
</model> </model>

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" <model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="6244" systemVersion="13E28" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
lastSavedToolsVersion="2057" systemVersion="12C60" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic"> <entity name="MPElementEntity" representedClassName="MPSiteEntity" isAbstract="YES" elementID="58EE245C-6827-4C11-BB7E-5722F2426EC2" syncable="YES">
<entity name="MPElementEntity" representedClassName="MPElementEntity" isAbstract="YES" syncable="YES">
<attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/> <attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/>
<attribute name="lastUsed" attributeType="Date" indexed="YES" syncable="YES"/> <attribute name="lastUsed" attributeType="Date" indexed="YES" syncable="YES"/>
<attribute name="name" attributeType="String" minValueString="1" indexed="YES" syncable="YES"/> <attribute name="name" attributeType="String" minValueString="1" indexed="YES" syncable="YES"/>
@@ -12,16 +11,15 @@
<attribute name="userName" optional="YES" attributeType="String" syncable="YES"/> <attribute name="userName" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="uses_" attributeType="Integer 16" defaultValueString="0" indexed="YES" 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"/> <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" <relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="elements" inverseEntity="MPUserEntity" elementID="FC8AE32E-7BE3-4FA6-8611-B7DC0DB063EF" syncable="YES"/>
inverseEntity="MPUserEntity" syncable="YES"/>
</entity> </entity>
<entity name="MPElementGeneratedEntity" representedClassName="MPElementGeneratedEntity" parentEntity="MPElementEntity" syncable="YES"> <entity name="MPElementGeneratedEntity" representedClassName="MPGeneratedSiteEntity" parentEntity="MPElementEntity" elementID="789304EA-070D-4982-8C20-54EECFC20CB6" syncable="YES">
<attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/> <attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/>
</entity> </entity>
<entity name="MPElementStoredEntity" representedClassName="MPElementStoredEntity" parentEntity="MPElementEntity" syncable="YES"> <entity name="MPElementStoredEntity" representedClassName="MPStoredSiteEntity" parentEntity="MPElementEntity" elementID="BEEF1688-0CCD-4699-A86A-4D860FE2CEB8" syncable="YES">
<attribute name="contentObject" optional="YES" attributeType="Transformable" storedInTruthFile="YES" syncable="YES"/> <attribute name="contentObject" optional="YES" attributeType="Transformable" storedInTruthFile="YES" syncable="YES"/>
</entity> </entity>
<entity name="MPUserEntity" representedClassName="MPUserEntity" syncable="YES"> <entity name="MPUserEntity" representedClassName="MPUserEntity" elementID="D18D6772-040E-4CFE-8F32-A34B08E9E9BC" syncable="YES">
<attribute name="avatar_" attributeType="Integer 16" defaultValueString="0" syncable="YES"/> <attribute name="avatar_" attributeType="Integer 16" defaultValueString="0" syncable="YES"/>
<attribute name="defaultType_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/> <attribute name="defaultType_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/>
<attribute name="keyID" optional="YES" attributeType="Binary" syncable="YES"/> <attribute name="keyID" optional="YES" attributeType="Binary" syncable="YES"/>
@@ -31,8 +29,7 @@
<attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO"> <attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO">
<userInfo/> <userInfo/>
</attribute> </attribute>
<relationship name="elements" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPElementEntity" <relationship name="elements" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPElementEntity" inverseName="user" inverseEntity="MPElementEntity" syncable="YES"/>
inverseName="user" inverseEntity="MPElementEntity" syncable="YES"/>
</entity> </entity>
<elements> <elements>
<element name="MPElementEntity" positionX="0" positionY="0" width="128" height="180"/> <element name="MPElementEntity" positionX="0" positionY="0" width="128" height="180"/>
@@ -40,4 +37,4 @@
<element name="MPElementStoredEntity" positionX="216" positionY="144" width="128" height="60"/> <element name="MPElementStoredEntity" positionX="216" positionY="144" width="128" height="60"/>
<element name="MPUserEntity" positionX="-216" positionY="0" width="128" height="165"/> <element name="MPUserEntity" positionX="-216" positionY="0" width="128" height="165"/>
</elements> </elements>
</model> </model>

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" <model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="6244" systemVersion="13E28" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
lastSavedToolsVersion="1810" systemVersion="12B19" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic"> <entity name="MPElementEntity" representedClassName="MPSiteEntity" isAbstract="YES" elementID="58EE245C-6827-4C11-BB7E-5722F2426EC2" syncable="YES">
<entity name="MPElementEntity" representedClassName="MPElementEntity" isAbstract="YES" syncable="YES">
<attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/> <attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/>
<attribute name="lastUsed" attributeType="Date" indexed="YES" syncable="YES"/> <attribute name="lastUsed" attributeType="Date" indexed="YES" syncable="YES"/>
<attribute name="loginName" optional="YES" attributeType="String" elementID="A1B9F981-D33C-4BFE-9F94-C9D3E1F78E51" syncable="YES"/> <attribute name="loginName" optional="YES" attributeType="String" elementID="A1B9F981-D33C-4BFE-9F94-C9D3E1F78E51" syncable="YES"/>
@@ -12,13 +11,12 @@
<attribute name="type_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/> <attribute name="type_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/>
<attribute name="uses_" attributeType="Integer 16" defaultValueString="0" indexed="YES" 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"/> <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" <relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="elements" inverseEntity="MPUserEntity" elementID="FC8AE32E-7BE3-4FA6-8611-B7DC0DB063EF" syncable="YES"/>
inverseEntity="MPUserEntity" syncable="YES"/>
</entity> </entity>
<entity name="MPElementGeneratedEntity" representedClassName="MPElementGeneratedEntity" parentEntity="MPElementEntity" syncable="YES"> <entity name="MPElementGeneratedEntity" representedClassName="MPGeneratedSiteEntity" parentEntity="MPElementEntity" elementID="789304EA-070D-4982-8C20-54EECFC20CB6" syncable="YES">
<attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/> <attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/>
</entity> </entity>
<entity name="MPElementStoredEntity" representedClassName="MPElementStoredEntity" parentEntity="MPElementEntity" syncable="YES"> <entity name="MPElementStoredEntity" representedClassName="MPStoredSiteEntity" parentEntity="MPElementEntity" elementID="BEEF1688-0CCD-4699-A86A-4D860FE2CEB8" syncable="YES">
<attribute name="contentObject" optional="YES" attributeType="Transformable" storedInTruthFile="YES" syncable="YES"/> <attribute name="contentObject" optional="YES" attributeType="Transformable" storedInTruthFile="YES" syncable="YES"/>
</entity> </entity>
<entity name="MPUserEntity" representedClassName="MPUserEntity" syncable="YES"> <entity name="MPUserEntity" representedClassName="MPUserEntity" syncable="YES">
@@ -31,8 +29,7 @@
<attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO"> <attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO">
<userInfo/> <userInfo/>
</attribute> </attribute>
<relationship name="elements" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPElementEntity" <relationship name="elements" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPElementEntity" inverseName="user" inverseEntity="MPElementEntity" elementID="D18D6772-040E-4CFE-8F32-A34B08E9E9BC" syncable="YES"/>
inverseName="user" inverseEntity="MPElementEntity" syncable="YES"/>
</entity> </entity>
<elements> <elements>
<element name="MPElementEntity" positionX="-0" positionY="-286" width="128" height="178"/> <element name="MPElementEntity" positionX="-0" positionY="-286" width="128" height="178"/>
@@ -40,4 +37,4 @@
<element name="MPElementStoredEntity" positionX="214" positionY="-171" 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="163"/> <element name="MPUserEntity" positionX="-218" positionY="-288" width="128" height="163"/>
</elements> </elements>
</model> </model>

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" <model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="6244" systemVersion="13E28" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
lastSavedToolsVersion="2057" systemVersion="12C60" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic"> <entity name="MPElementEntity" representedClassName="MPSiteEntity" isAbstract="YES" elementID="58EE245C-6827-4C11-BB7E-5722F2426EC2" syncable="YES">
<entity name="MPElementEntity" representedClassName="MPElementEntity" isAbstract="YES" syncable="YES">
<attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/> <attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/>
<attribute name="lastUsed" attributeType="Date" indexed="YES" syncable="YES"/> <attribute name="lastUsed" attributeType="Date" indexed="YES" syncable="YES"/>
<attribute name="loginName" optional="YES" attributeType="String" elementID="A1B9F981-D33C-4BFE-9F94-C9D3E1F78E51" syncable="YES"/> <attribute name="loginName" optional="YES" attributeType="String" elementID="A1B9F981-D33C-4BFE-9F94-C9D3E1F78E51" syncable="YES"/>
@@ -12,13 +11,12 @@
<attribute name="type_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/> <attribute name="type_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/>
<attribute name="uses_" attributeType="Integer 16" defaultValueString="0" indexed="YES" 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"/> <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" <relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="elements" inverseEntity="MPUserEntity" elementID="FC8AE32E-7BE3-4FA6-8611-B7DC0DB063EF" syncable="YES"/>
inverseEntity="MPUserEntity" syncable="YES"/>
</entity> </entity>
<entity name="MPElementGeneratedEntity" representedClassName="MPElementGeneratedEntity" parentEntity="MPElementEntity" syncable="YES"> <entity name="MPElementGeneratedEntity" representedClassName="MPGeneratedSiteEntity" parentEntity="MPElementEntity" elementID="789304EA-070D-4982-8C20-54EECFC20CB6" syncable="YES">
<attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/> <attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/>
</entity> </entity>
<entity name="MPElementStoredEntity" representedClassName="MPElementStoredEntity" parentEntity="MPElementEntity" syncable="YES"> <entity name="MPElementStoredEntity" representedClassName="MPStoredSiteEntity" parentEntity="MPElementEntity" elementID="BEEF1688-0CCD-4699-A86A-4D860FE2CEB8" syncable="YES">
<attribute name="contentObject" optional="YES" attributeType="Transformable" storedInTruthFile="YES" syncable="YES"/> <attribute name="contentObject" optional="YES" attributeType="Transformable" storedInTruthFile="YES" syncable="YES"/>
</entity> </entity>
<entity name="MPUserEntity" representedClassName="MPUserEntity" syncable="YES"> <entity name="MPUserEntity" representedClassName="MPUserEntity" syncable="YES">
@@ -30,8 +28,7 @@
<attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO"> <attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO">
<userInfo/> <userInfo/>
</attribute> </attribute>
<relationship name="elements" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPElementEntity" <relationship name="elements" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPElementEntity" inverseName="user" inverseEntity="MPElementEntity" elementID="D18D6772-040E-4CFE-8F32-A34B08E9E9BC" syncable="YES"/>
inverseName="user" inverseEntity="MPElementEntity" syncable="YES"/>
</entity> </entity>
<elements> <elements>
<element name="MPElementEntity" positionX="-0" positionY="-286" width="128" height="178"/> <element name="MPElementEntity" positionX="-0" positionY="-286" width="128" height="178"/>
@@ -39,4 +36,4 @@
<element name="MPElementStoredEntity" positionX="214" positionY="-171" 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"/> <element name="MPUserEntity" positionX="-218" positionY="-288" width="128" height="148"/>
</elements> </elements>
</model> </model>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?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"> <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"> <entity name="MPElementEntity" representedClassName="MPElementEntity" isAbstract="YES" elementID="58EE245C-6827-4C11-BB7E-5722F2426EC2" syncable="YES">
<attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/> <attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/>
<attribute name="lastUsed" attributeType="Date" indexed="YES" syncable="YES"/> <attribute name="lastUsed" attributeType="Date" indexed="YES" syncable="YES"/>
<attribute name="loginGenerated_" attributeType="Boolean" defaultValueString="NO" syncable="YES"/> <attribute name="loginGenerated_" attributeType="Boolean" defaultValueString="NO" syncable="YES"/>
@@ -12,12 +12,12 @@
<attribute name="type_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/> <attribute name="type_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/>
<attribute name="uses_" attributeType="Integer 16" defaultValueString="0" indexed="YES" 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"/> <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"/> <relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="elements" inverseEntity="MPUserEntity" elementID="FC8AE32E-7BE3-4FA6-8611-B7DC0DB063EF" syncable="YES"/>
</entity> </entity>
<entity name="MPElementGeneratedEntity" representedClassName="MPElementGeneratedEntity" parentEntity="MPElementEntity" syncable="YES"> <entity name="MPElementGeneratedEntity" representedClassName="MPElementGeneratedEntity" parentEntity="MPElementEntity" elementID="789304EA-070D-4982-8C20-54EECFC20CB6" syncable="YES">
<attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/> <attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/>
</entity> </entity>
<entity name="MPElementStoredEntity" representedClassName="MPElementStoredEntity" parentEntity="MPElementEntity" syncable="YES"> <entity name="MPElementStoredEntity" representedClassName="MPElementStoredEntity" parentEntity="MPElementEntity" elementID="BEEF1688-0CCD-4699-A86A-4D860FE2CEB8" syncable="YES">
<attribute name="contentObject" optional="YES" attributeType="Transformable" storedInTruthFile="YES" syncable="YES"/> <attribute name="contentObject" optional="YES" attributeType="Transformable" storedInTruthFile="YES" syncable="YES"/>
</entity> </entity>
<entity name="MPUserEntity" representedClassName="MPUserEntity" syncable="YES"> <entity name="MPUserEntity" representedClassName="MPUserEntity" syncable="YES">
@@ -29,7 +29,7 @@
<attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO"> <attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO">
<userInfo/> <userInfo/>
</attribute> </attribute>
<relationship name="elements" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPElementEntity" inverseName="user" inverseEntity="MPElementEntity" syncable="YES"/> <relationship name="elements" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPElementEntity" inverseName="user" inverseEntity="MPElementEntity" elementID="D18D6772-040E-4CFE-8F32-A34B08E9E9BC" syncable="YES"/>
</entity> </entity>
<elements> <elements>
<element name="MPElementEntity" positionX="-0" positionY="-286" width="128" height="193"/> <element name="MPElementEntity" positionX="-0" positionY="-286" width="128" height="193"/>

View File

@@ -0,0 +1,46 @@
<?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="MPGeneratedSiteEntity" representedClassName="MPGeneratedSiteEntity" parentEntity="MPSiteEntity" elementID="789304EA-070D-4982-8C20-54EECFC20CB6" syncable="YES">
<attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/>
</entity>
<entity name="MPSiteEntity" representedClassName="MPSiteEntity" isAbstract="YES" elementID="58EE245C-6827-4C11-BB7E-5722F2426EC2" 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="questions" optional="YES" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="MPSiteQuestionEntity" inverseName="site" inverseEntity="MPSiteQuestionEntity" syncable="YES"/>
<relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="sites" inverseEntity="MPUserEntity" elementID="FC8AE32E-7BE3-4FA6-8611-B7DC0DB063EF" syncable="YES"/>
</entity>
<entity name="MPSiteQuestionEntity" representedClassName="MPSiteQuestionEntity" syncable="YES">
<attribute name="keyword" attributeType="String" syncable="YES"/>
<relationship name="site" maxCount="1" deletionRule="Nullify" destinationEntity="MPSiteEntity" inverseName="questions" inverseEntity="MPSiteEntity" syncable="YES"/>
</entity>
<entity name="MPStoredSiteEntity" representedClassName="MPStoredSiteEntity" parentEntity="MPSiteEntity" elementID="BEEF1688-0CCD-4699-A86A-4D860FE2CEB8" 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="sites" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPSiteEntity" inverseName="user" inverseEntity="MPSiteEntity" elementID="D18D6772-040E-4CFE-8F32-A34B08E9E9BC" syncable="YES"/>
</entity>
<elements>
<element name="MPGeneratedSiteEntity" positionX="216" positionY="-288" width="128" height="58"/>
<element name="MPSiteEntity" positionX="-0" positionY="-286" width="128" height="208"/>
<element name="MPSiteQuestionEntity" positionX="-2" positionY="-9" width="128" height="73"/>
<element name="MPStoredSiteEntity" positionX="214" positionY="-171" width="128" height="58"/>
<element name="MPUserEntity" positionX="-218" positionY="-288" width="128" height="148"/>
</elements>
</model>

View File

@@ -0,0 +1,46 @@
//
// MPPreferencesViewController.h
// MasterPassword-iOS
//
// Created by Maarten Billemont on 04/06/12.
// Copyright (c) 2012 Lyndir. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "MPTypeViewController.h"
#import "MPSiteQuestionEntity.h"
@interface MPAnswersViewController : UIViewController
@property (nonatomic) IBOutlet UITableView *tableView;
- (void)setSite:(MPSiteEntity *)site;
- (MPSiteEntity *)siteInContext:(NSManagedObjectContext *)context;
@end
@interface MPGlobalAnswersCell : UITableViewCell
@property (nonatomic) IBOutlet UILabel *titleLabel;
@property (nonatomic) IBOutlet UITextField *answerField;
- (void)setSite:(MPSiteEntity *)site;
@end
@interface MPSendAnswersCell : UITableViewCell
@end
@interface MPMultipleAnswersCell : UITableViewCell <UITextFieldDelegate>
@end
@interface MPAnswersQuestionCell : UITableViewCell
@property(nonatomic) IBOutlet UITextField *questionField;
@property(nonatomic) IBOutlet UITextField *answerField;
- (void)setQuestion:(MPSiteQuestionEntity *)question forSite:(MPSiteEntity *)site;
@end

View File

@@ -0,0 +1,321 @@
//
// MPPreferencesViewController.m
// MasterPassword-iOS
//
// Created by Maarten Billemont on 04/06/12.
// Copyright (c) 2012 Lyndir. All rights reserved.
//
#import "MPAnswersViewController.h"
#import "MPiOSAppDelegate.h"
#import "MPAppDelegate_Store.h"
#import "MPOverlayViewController.h"
@interface MPAnswersViewController()
@end
@implementation MPAnswersViewController {
NSManagedObjectID *_siteOID;
BOOL _multiple;
}
#pragma mark - Life
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.tableHeaderView = [UIView new];
self.tableView.tableFooterView = [UIView new];
self.view.backgroundColor = [UIColor clearColor];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
PearlAddNotificationObserver( MPSignedOutNotification, nil, [NSOperationQueue mainQueue],
^(MPAnswersViewController *self, NSNotification *note) {
if (![note.userInfo[@"animated"] boolValue])
[UIView setAnimationsEnabled:NO];
[[MPOverlaySegue dismissViewController:self] perform];
[UIView setAnimationsEnabled:YES];
} );
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
PearlRemoveNotificationObservers();
}
- (UIStatusBarStyle)preferredStatusBarStyle {
return UIStatusBarStyleLightContent;
}
#pragma mark - State
- (void)setSite:(MPSiteEntity *)site {
_siteOID = [site objectID];
_multiple = [site.questions count] > 0;
[self.tableView reloadData];
[self updateAnimated:NO];
}
- (void)setMultiple:(BOOL)multiple animated:(BOOL)animated {
_multiple = multiple;
[self updateAnimated:animated];
}
- (MPSiteEntity *)siteInContext:(NSManagedObjectContext *)context {
return [MPSiteEntity existingObjectWithID:_siteOID inContext:context];
}
#pragma mark - UITableViewDelegate
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (section == 0)
return 3;
if (!_multiple)
return 0;
return MAX( 2, [[self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]].questions count] );
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MPSiteEntity *site = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
if (indexPath.section == 0) {
if (indexPath.item == 0) {
MPGlobalAnswersCell *cell = [MPGlobalAnswersCell dequeueCellFromTableView:tableView indexPath:indexPath];
[cell setSite:site];
return cell;
}
if (indexPath.item == 1)
return [MPSendAnswersCell dequeueCellFromTableView:tableView indexPath:indexPath];
if (indexPath.item == 2) {
MPMultipleAnswersCell *cell = [MPMultipleAnswersCell dequeueCellFromTableView:tableView indexPath:indexPath];
cell.accessoryType = _multiple? UITableViewCellAccessoryCheckmark: UITableViewCellAccessoryNone;
return cell;
}
Throw( @"Unsupported row index: %@", indexPath );
}
MPAnswersQuestionCell *cell = [MPAnswersQuestionCell dequeueCellFromTableView:tableView indexPath:indexPath];
MPSiteQuestionEntity *question = nil;
if ([site.questions count] > indexPath.item)
question = site.questions[indexPath.item];
[cell setQuestion:question forSite:site];
return cell;
}
#pragma mark - UITableViewDelegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 0) {
if (indexPath.item == 0)
return 133;
return 44;
}
return 130;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
MPSiteEntity *site = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if ([cell isKindOfClass:[MPGlobalAnswersCell class]]) {
[PearlOverlay showTemporaryOverlayWithTitle:strl( @"Answer Copied" ) dismissAfter:2];
[UIPasteboard generalPasteboard].string = ((MPGlobalAnswersCell *)cell).answerField.text;
}
else if ([cell isKindOfClass:[MPMultipleAnswersCell class]]) {
if (!_multiple)
[self setMultiple:YES animated:YES];
else if (_multiple) {
if (![site.questions count])
[self setMultiple:NO animated:YES];
else
[PearlAlert showAlertWithTitle:@"Remove Site Questions?" message:
@"Do you want to remove the questions you have configured for this site?"
viewStyle:UIAlertViewStyleDefault initAlert:nil
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
if (buttonIndex == [alert cancelButtonIndex])
return;
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPSiteEntity *site_ = [self siteInContext:context];
NSOrderedSet *questions = [site_.questions copy];
for (MPSiteQuestionEntity *question in questions)
[context deleteObject:question];
[context saveToStore];
[self setMultiple:NO animated:YES];
}];
} cancelTitle:@"Cancel" otherTitles:@"Remove Questions", nil];
}
}
else if ([cell isKindOfClass:[MPSendAnswersCell class]]) {
NSString *body;
if (!_multiple) {
NSObject *answer = [site.algorithm resolveAnswerForSite:site usingKey:[MPiOSAppDelegate get].key];
body = strf( @"Master Password generated the following security answer for your site: %@\n\n"
@"%@\n"
@"\n\nYou should use this as the answer to each security question the site asks you.\n"
@"Do not share this answer with others!", site.name, answer );
}
else {
NSMutableString *bodyBuilder = [NSMutableString string];
[bodyBuilder appendFormat:@"Master Password generated the following security answers for your site: %@\n\n", site.name];
for (MPSiteQuestionEntity *question in site.questions) {
NSObject *answer = [site.algorithm resolveAnswerForQuestion:question usingKey:[MPiOSAppDelegate get].key];
[bodyBuilder appendFormat:@"For question: '%@', use answer: %@\n", question.keyword, answer];
}
[bodyBuilder appendFormat:@"\n\nUse the answer for the matching security question.\n"
@"Do not share this answer with others!"];
body = bodyBuilder;
}
[PearlEMail sendEMailTo:nil fromVC:self subject:strf( @"Master Password security answers for %@", site.name ) body:body];
}
else if ([cell isKindOfClass:[MPAnswersQuestionCell class]]) {
[PearlOverlay showTemporaryOverlayWithTitle:strl( @"Answer Copied" ) dismissAfter:2];
[UIPasteboard generalPasteboard].string = ((MPAnswersQuestionCell *)cell).answerField.text;
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
#pragma mark - Private
- (void)updateAnimated:(BOOL)animated {
PearlMainQueue( ^{
UITableViewCell *multipleAnswersCell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForItem:2 inSection:0]];
multipleAnswersCell.accessoryType = _multiple? UITableViewCellAccessoryCheckmark: UITableViewCellAccessoryNone;
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationAutomatic];
}];
} );
}
@end
@implementation MPGlobalAnswersCell
#pragma mark - State
- (void)setSite:(MPSiteEntity *)site {
self.titleLabel.text = strl( @"Answer for %@:", site.name );
self.answerField.text = @"...";
[site.algorithm resolveAnswerForSite:site usingKey:[MPiOSAppDelegate get].key result:^(NSString *result) {
PearlMainQueue( ^{
self.answerField.text = result;
} );
}];
}
@end
@implementation MPSendAnswersCell
@end
@implementation MPMultipleAnswersCell
@end
@implementation MPAnswersQuestionCell {
NSManagedObjectID *_siteOID;
NSManagedObjectID *_questionOID;
}
#pragma mark - State
- (void)setQuestion:(MPSiteQuestionEntity *)question forSite:(MPSiteEntity *)site {
_siteOID = site.objectID;
_questionOID = question.objectID;
[self updateAnswerForQuestion:question ofSite:site];
}
#pragma mark - UITextFieldDelegate
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
return NO;
}
- (IBAction)textFieldDidChange:(UITextField *)textField {
NSString *keyword = textField.text;
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPSiteEntity *site = [MPSiteEntity existingObjectWithID:_siteOID inContext:context];
MPSiteQuestionEntity *question = [MPSiteQuestionEntity existingObjectWithID:_questionOID inContext:context];
if (!question) {
[site addQuestionsObject:question = [MPSiteQuestionEntity insertNewObjectInContext:context]];
question.site = site;
}
question.keyword = keyword;
if ([context saveToStore]) {
if ([question.objectID isTemporaryID]) {
NSError *error = nil;
[context obtainPermanentIDsForObjects:@[ question ] error:&error];
if (error)
err( @"Failed to obtain permanent object ID: %@", [error fullDescription] );
}
_questionOID = question.objectID;
[self updateAnswerForQuestion:question ofSite:site];
}
}];
}
#pragma mark - Private
- (void)updateAnswerForQuestion:(MPSiteQuestionEntity *)question ofSite:(MPSiteEntity *)site {
if (!question)
PearlMainQueue( ^{
self.questionField.text = self.answerField.text = nil;
} );
else {
NSString *keyword = question.keyword;
PearlMainQueue( ^{
self.answerField.text = @"...";
} );
[site.algorithm resolveAnswerForQuestion:question usingKey:[MPiOSAppDelegate get].key result:^(NSString *result) {
PearlMainQueue( ^{
self.questionField.text = keyword;
self.answerField.text = result;
} );
}];
}
}
@end

View File

@@ -19,23 +19,6 @@
#import "MPAppSettingsViewController.h" #import "MPAppSettingsViewController.h"
#import "UIColor+Expanded.h" #import "UIColor+Expanded.h"
@interface MPTableView:UITableView
@end
@implementation MPTableView
- (void)layoutSubviews {
[super layoutSubviews];
}
- (void)setContentInset:(UIEdgeInsets)contentInset {
[super setContentInset:contentInset];
}
@end
@implementation MPAppSettingsViewController { @implementation MPAppSettingsViewController {
} }

View File

@@ -16,6 +16,10 @@
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved. // Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
// //
#import "MPUsersViewController.h"
#import "MPPasswordsViewController.h"
#import "MPEmergencyViewController.h"
typedef NS_ENUM(NSUInteger, MPCombinedMode) { typedef NS_ENUM(NSUInteger, MPCombinedMode) {
MPCombinedModeUserSelection, MPCombinedModeUserSelection,
MPCombinedModePasswordSelection, MPCombinedModePasswordSelection,
@@ -23,7 +27,9 @@ typedef NS_ENUM(NSUInteger, MPCombinedMode) {
@interface MPCombinedViewController : UIViewController @interface MPCombinedViewController : UIViewController
@property(assign, nonatomic) MPCombinedMode mode; @property(nonatomic) MPCombinedMode mode;
@property(strong, nonatomic) IBOutlet UIView *usersView; @property(nonatomic, weak) MPUsersViewController *usersVC;
@property(nonatomic, weak) MPPasswordsViewController *passwordsVC;
@property(nonatomic, weak) MPEmergencyViewController *emergencyVC;
@end @end

View File

@@ -1,12 +1,12 @@
/** /**
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com) * Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
* *
* See the enclosed file LICENSE for license information (LGPLv3). If you did * 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 * not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
* *
* @author Maarten Billemont <lhunath@lyndir.com> * @author Maarten Billemont <lhunath@lyndir.com>
* @license http://www.gnu.org/licenses/lgpl-3.0.txt * @license http://www.gnu.org/licenses/lgpl-3.0.txt
*/ */
// //
// MPCombinedViewController.h // MPCombinedViewController.h
@@ -19,26 +19,19 @@
#import "MPCombinedViewController.h" #import "MPCombinedViewController.h"
#import "MPUsersViewController.h" #import "MPUsersViewController.h"
#import "MPPasswordsViewController.h" #import "MPPasswordsViewController.h"
#import "MPEmergencySegue.h"
#import "MPEmergencyViewController.h" #import "MPEmergencyViewController.h"
#import "MPPasswordsSegue.h" #import "MPPasswordsSegue.h"
@interface MPCombinedViewController() @implementation MPCombinedViewController
@property(nonatomic, weak) MPUsersViewController *usersVC; #pragma mark - Life
@property(nonatomic, weak) MPEmergencyViewController *emergencyVC;
@end
@implementation MPCombinedViewController {
NSArray *_notificationObservers;
MPPasswordsViewController *_passwordsVC;
}
- (void)viewDidLoad { - (void)viewDidLoad {
[super viewDidLoad]; [super viewDidLoad];
_mode = MPCombinedModeUserSelection; _mode = MPCombinedModeUserSelection;
[self performSegueWithIdentifier:@"users" sender:self];
} }
- (void)viewWillAppear:(BOOL)animated { - (void)viewWillAppear:(BOOL)animated {
@@ -52,14 +45,21 @@
[super viewDidAppear:animated]; [super viewDidAppear:animated];
[self registerObservers]; PearlAddNotificationObserver( MPSignedInNotification, nil, [NSOperationQueue mainQueue],
^(MPCombinedViewController *self, NSNotification *note) {
[self setMode:MPCombinedModePasswordSelection];
} );
PearlAddNotificationObserver( MPSignedOutNotification, nil, [NSOperationQueue mainQueue],
^(MPCombinedViewController *self, NSNotification *note) {
[self setMode:MPCombinedModeUserSelection animated:[note.userInfo[@"animated"] boolValue]];
} );
} }
- (void)viewWillDisappear:(BOOL)animated { - (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated]; [super viewWillDisappear:animated];
[self removeObservers]; PearlRemoveNotificationObservers();
} }
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
@@ -67,9 +67,9 @@
if ([segue.identifier isEqualToString:@"users"]) if ([segue.identifier isEqualToString:@"users"])
self.usersVC = segue.destinationViewController; self.usersVC = segue.destinationViewController;
if ([segue.identifier isEqualToString:@"passwords"]) { if ([segue.identifier isEqualToString:@"passwords"]) {
NSAssert([segue isKindOfClass:[MPPasswordsSegue class]], @"passwords segue should be MPPasswordsSegue: %@", segue); NSAssert( [segue isKindOfClass:[MPPasswordsSegue class]], @"passwords segue should be MPPasswordsSegue: %@", segue );
NSAssert([sender isKindOfClass:[NSDictionary class]], @"sender should be dictionary: %@", sender); NSAssert( [sender isKindOfClass:[NSDictionary class]], @"sender should be dictionary: %@", sender );
NSAssert([[sender objectForKey:@"animated"] isKindOfClass:[NSNumber class]], @"sender should contain 'animated': %@", sender); NSAssert( [[sender objectForKey:@"animated"] isKindOfClass:[NSNumber class]], @"sender should contain 'animated': %@", sender );
[(MPPasswordsSegue *)segue setAnimated:[sender[@"animated"] boolValue]]; [(MPPasswordsSegue *)segue setAnimated:[sender[@"animated"] boolValue]];
UIViewController *destinationVC = segue.destinationViewController; UIViewController *destinationVC = segue.destinationViewController;
_passwordsVC = [destinationVC isKindOfClass:[MPPasswordsViewController class]]? (MPPasswordsViewController *)destinationVC: nil; _passwordsVC = [destinationVC isKindOfClass:[MPPasswordsViewController class]]? (MPPasswordsViewController *)destinationVC: nil;
@@ -99,20 +99,14 @@
[self performSegueWithIdentifier:@"emergency" sender:self]; [self performSegueWithIdentifier:@"emergency" sender:self];
} }
- (UIStoryboardSegue *)segueForUnwindingToViewController:(UIViewController *)toViewController #pragma mark - Actions
fromViewController:(UIViewController *)fromViewController identifier:(NSString *)identifier {
if ([identifier isEqualToString:@"unwind-emergency"]) { - (IBAction)unwindToCombined:(UIStoryboardSegue *)sender {
MPEmergencySegue *segue = [[MPEmergencySegue alloc] initWithIdentifier:identifier
source:fromViewController destination:toViewController];
segue.unwind = YES;
dbg_return(segue);
}
dbg_return((id)nil); dbg( @"unwindToCombined:%@", sender );
} }
#pragma mark - Properties #pragma mark - State
- (void)setMode:(MPCombinedMode)mode { - (void)setMode:(MPCombinedMode)mode {
@@ -133,7 +127,7 @@
switch (self.mode) { switch (self.mode) {
case MPCombinedModeUserSelection: { case MPCombinedModeUserSelection: {
self.usersView.userInteractionEnabled = YES; self.usersVC.view.userInteractionEnabled = YES;
[self.usersVC setActive:YES animated:animated]; [self.usersVC setActive:YES animated:animated];
if (_passwordsVC) { if (_passwordsVC) {
MPPasswordsSegue *segue = [[MPPasswordsSegue alloc] initWithIdentifier:@"passwords" source:_passwordsVC destination:self]; MPPasswordsSegue *segue = [[MPPasswordsSegue alloc] initWithIdentifier:@"passwords" source:_passwordsVC destination:self];
@@ -143,7 +137,7 @@
break; break;
} }
case MPCombinedModePasswordSelection: { case MPCombinedModePasswordSelection: {
self.usersView.userInteractionEnabled = NO; self.usersVC.view.userInteractionEnabled = NO;
[self.usersVC setActive:NO animated:animated]; [self.usersVC setActive:NO animated:animated];
[self performSegueWithIdentifier:@"passwords" sender:@{ @"animated" : @(animated) }]; [self performSegueWithIdentifier:@"passwords" sender:@{ @"animated" : @(animated) }];
break; break;
@@ -153,35 +147,4 @@
#pragma mark - Private #pragma mark - Private
- (void)registerObservers {
if ([_notificationObservers count])
return;
Weakify(self);
_notificationObservers = @[
[[NSNotificationCenter defaultCenter]
addObserverForName:MPSignedInNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
Strongify(self);
[self setMode:MPCombinedModePasswordSelection];
}],
[[NSNotificationCenter defaultCenter]
addObserverForName:MPSignedOutNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
Strongify(self);
[self setMode:MPCombinedModeUserSelection animated:[note.userInfo[@"animated"] boolValue]];
}],
];
}
- (void)removeObservers {
for (id observer in _notificationObservers)
[[NSNotificationCenter defaultCenter] removeObserver:observer];
_notificationObservers = nil;
}
@end @end

View File

@@ -1,57 +0,0 @@
/**
* 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
*/
//
// MPEmergencySegue.h
// MPEmergencySegue
//
// Created by lhunath on 2014-04-09.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
#import "MPEmergencySegue.h"
@implementation MPEmergencySegue {
}
- (void)perform {
UIViewController *sourceViewController = self.sourceViewController;
UIViewController *destinationViewController = self.destinationViewController;
if (!self.unwind) {
// Winding
[sourceViewController addChildViewController:destinationViewController];
[sourceViewController.view addSubview:destinationViewController.view];
CGRectSetY(destinationViewController.view.bounds, sourceViewController.view.frame.size.height);
[UIView transitionWithView:sourceViewController.view duration:0.3f options:UIViewAnimationOptionAllowAnimatedContent
animations:^{
CGRectSetY(destinationViewController.view.bounds, 0);
} completion:^(BOOL finished) {
if (finished)
[destinationViewController didMoveToParentViewController:sourceViewController];
}];
}
else {
// Unwinding
[sourceViewController willMoveToParentViewController:nil];
[UIView transitionWithView:sourceViewController.parentViewController.view duration:0.3f options:UIViewAnimationOptionAllowAnimatedContent
animations:^{
CGRectSetY(sourceViewController.view.bounds, sourceViewController.parentViewController.view.frame.size.height);
} completion:^(BOOL finished) {
if (finished) {
[sourceViewController.view removeFromSuperview];
[sourceViewController removeFromParentViewController];
}
}];
}
}
@end

View File

@@ -1,12 +1,12 @@
/** /**
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com) * Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
* *
* See the enclosed file LICENSE for license information (LGPLv3). If you did * 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 * not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
* *
* @author Maarten Billemont <lhunath@lyndir.com> * @author Maarten Billemont <lhunath@lyndir.com>
* @license http://www.gnu.org/licenses/lgpl-3.0.txt * @license http://www.gnu.org/licenses/lgpl-3.0.txt
*/ */
// //
// MPCombinedViewController.h // MPCombinedViewController.h
@@ -23,7 +23,6 @@
MPKey *_key; MPKey *_key;
NSOperationQueue *_emergencyKeyQueue; NSOperationQueue *_emergencyKeyQueue;
NSOperationQueue *_emergencyPasswordQueue; NSOperationQueue *_emergencyPasswordQueue;
NSArray *_notificationObservers;
} }
- (void)viewDidLoad { - (void)viewDidLoad {
@@ -42,27 +41,23 @@
[super viewWillAppear:animated]; [super viewWillAppear:animated];
[self reset]; [self reset];
[self registerObservers]; PearlAddNotificationObserver( UIApplicationWillResignActiveNotification, nil, [NSOperationQueue mainQueue],
^(MPEmergencyViewController *self, NSNotification *note) {
[self performSegueWithIdentifier:@"unwind-popover" sender:self];
} );
} }
- (void)viewDidDisappear:(BOOL)animated { - (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated]; [super viewDidDisappear:animated];
[self removeObservers]; PearlRemoveNotificationObservers();
[self reset]; [self reset];
} }
- (BOOL)canPerformUnwindSegueAction:(SEL)action fromViewController:(UIViewController *)fromViewController withSender:(id)sender { - (UIStatusBarStyle)preferredStatusBarStyle {
return [self respondsToSelector:action]; return UIStatusBarStyleLightContent;
}
#pragma mark - Actions
- (IBAction)unwindToCombined:(UIStoryboardSegue *)sender {
dbg(@"unwindToCombined:%@", sender);
} }
#pragma mark - UITextFieldDelegate #pragma mark - UITextFieldDelegate
@@ -126,7 +121,7 @@
- (void)updatePassword { - (void)updatePassword {
NSString *siteName = self.siteField.text; NSString *siteName = self.siteField.text;
MPElementType siteType = [self siteType]; MPSiteType siteType = [self siteType];
NSUInteger siteCounter = (NSUInteger)self.counterStepper.value; NSUInteger siteCounter = (NSUInteger)self.counterStepper.value;
self.counterLabel.text = strf( @"%lu", (unsigned long)siteCounter ); self.counterLabel.text = strf( @"%lu", (unsigned long)siteCounter );
@@ -145,23 +140,23 @@
}]; }];
} }
- (enum MPElementType)siteType { - (enum MPSiteType)siteType {
switch (self.typeControl.selectedSegmentIndex) { switch (self.typeControl.selectedSegmentIndex) {
case 0: case 0:
return MPElementTypeGeneratedMaximum; return MPSiteTypeGeneratedMaximum;
case 1: case 1:
return MPElementTypeGeneratedLong; return MPSiteTypeGeneratedLong;
case 2: case 2:
return MPElementTypeGeneratedMedium; return MPSiteTypeGeneratedMedium;
case 3: case 3:
return MPElementTypeGeneratedBasic; return MPSiteTypeGeneratedBasic;
case 4: case 4:
return MPElementTypeGeneratedShort; return MPSiteTypeGeneratedShort;
case 5: case 5:
return MPElementTypeGeneratedPIN; return MPSiteTypeGeneratedPIN;
default: default:
Throw(@"Unsupported type index: %ld", (long)self.typeControl.selectedSegmentIndex); Throw( @"Unsupported type index: %ld", (long)self.typeControl.selectedSegmentIndex );
} }
} }
@@ -175,28 +170,4 @@
[self updateKey]; [self updateKey];
} }
- (void)registerObservers {
if ([_notificationObservers count])
return;
Weakify(self);
_notificationObservers = @[
[[NSNotificationCenter defaultCenter]
addObserverForName:UIApplicationWillResignActiveNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
Strongify(self);
[self performSegueWithIdentifier:@"unwind-emergency" sender:self];
}],
];
}
- (void)removeObservers {
for (id observer in _notificationObservers)
[[NSNotificationCenter defaultCenter] removeObserver:observer];
_notificationObservers = nil;
}
@end @end

View File

@@ -1,12 +1,12 @@
/** /**
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com) * Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
* *
* See the enclosed file LICENSE for license information (LGPLv3). If you did * 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 * not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
* *
* @author Maarten Billemont <lhunath@lyndir.com> * @author Maarten Billemont <lhunath@lyndir.com>
* @license http://www.gnu.org/licenses/lgpl-3.0.txt * @license http://www.gnu.org/licenses/lgpl-3.0.txt
*/ */
// //
// MPLogsViewController.h // MPLogsViewController.h
@@ -27,18 +27,17 @@
[super viewDidLoad]; [super viewDidLoad];
self.view.backgroundColor = [UIColor clearColor]; self.view.backgroundColor = [UIColor clearColor];
[[NSNotificationCenter defaultCenter] addObserverForName:NSUserDefaultsDidChangeNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:
^(NSNotification *note) {
self.levelControl.selectedSegmentIndex = [[MPiOSConfig get].traceMode boolValue]? 1: 0;
}];
} }
- (void)viewWillAppear:(BOOL)animated { - (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated]; [super viewWillAppear:animated];
PearlAddNotificationObserver( NSUserDefaultsDidChangeNotification, nil, [NSOperationQueue mainQueue],
^(MPLogsViewController *self, NSNotification *note) {
self.levelControl.selectedSegmentIndex = [[MPiOSConfig get].traceMode boolValue]? 1: 0;
} );
self.logView.contentInset = UIEdgeInsetsMake( 64, 0, 93, 0 ); self.logView.contentInset = UIEdgeInsetsMake( 64, 0, 93, 0 );
[self refresh:nil]; [self refresh:nil];
@@ -46,13 +45,20 @@
self.levelControl.selectedSegmentIndex = [[MPiOSConfig get].traceMode boolValue]? 1: 0; self.levelControl.selectedSegmentIndex = [[MPiOSConfig get].traceMode boolValue]? 1: 0;
} }
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
PearlRemoveNotificationObservers();
}
- (IBAction)toggleLevelControl:(UISegmentedControl *)sender { - (IBAction)toggleLevelControl:(UISegmentedControl *)sender {
BOOL traceEnabled = (BOOL)self.levelControl.selectedSegmentIndex; BOOL traceEnabled = (BOOL)self.levelControl.selectedSegmentIndex;
if (traceEnabled) { if (traceEnabled) {
[PearlAlert showAlertWithTitle:@"Enable Trace Mode?" message: [PearlAlert showAlertWithTitle:@"Enable Trace Mode?" message:
@"Trace mode will log the internal operation of the application.\n" @"Trace mode will log the internal operation of the application.\n"
@"Unless you're looking for the cause of a problem, you should leave this off to save memory." @"Unless you're looking for the cause of a problem, you should leave this off to save memory."
viewStyle:UIAlertViewStyleDefault initAlert:nil viewStyle:UIAlertViewStyleDefault initAlert:nil
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) { tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
if (buttonIndex == [alert cancelButtonIndex]) if (buttonIndex == [alert cancelButtonIndex])
@@ -74,8 +80,8 @@
if ([[MPiOSConfig get].traceMode boolValue]) { if ([[MPiOSConfig get].traceMode boolValue]) {
[PearlAlert showAlertWithTitle:@"Hiding Trace Messages" message: [PearlAlert showAlertWithTitle:@"Hiding Trace Messages" message:
@"Trace-level log messages will not be mailed. " @"Trace-level log messages will not be mailed. "
@"These messages contain sensitive and personal information." @"These messages contain sensitive and personal information."
viewStyle:UIAlertViewStyleDefault initAlert:nil viewStyle:UIAlertViewStyleDefault initAlert:nil
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) { tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
[[MPiOSAppDelegate get] openFeedbackWithLogs:YES forVC:self]; [[MPiOSAppDelegate get] openFeedbackWithLogs:YES forVC:self];

View File

@@ -0,0 +1,29 @@
/**
* 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
*/
//
// MPOverlayViewController.h
// MPOverlayViewController
//
// Created by lhunath on 2014-09-22.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface MPOverlayViewController : UIViewController
@end
@interface MPOverlaySegue : UIStoryboardSegue
+ (instancetype)dismissViewController:(UIViewController *)viewController;
@end

View File

@@ -0,0 +1,162 @@
/**
* 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
*/
//
// MPOverlayViewController.h
// MPOverlayViewController
//
// Created by lhunath on 2014-09-22.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
#import "MPOverlayViewController.h"
@implementation MPOverlayViewController {
NSMutableDictionary *_dismissSegueByButton;
}
- (void)awakeFromNib {
[super awakeFromNib];
_dismissSegueByButton = [NSMutableDictionary dictionary];
}
- (void)viewDidLoad {
[super viewDidLoad];
[self performSegueWithIdentifier:@"root" sender:self];
}
- (UIViewController *)childViewControllerForStatusBarStyle {
return [self.childViewControllers lastObject];
}
- (UIViewController *)childViewControllerForStatusBarHidden {
return self.childViewControllerForStatusBarStyle;
}
- (UIStoryboardSegue *)segueForUnwindingToViewController:(UIViewController *)toViewController
fromViewController:(UIViewController *)fromViewController identifier:(NSString *)identifier {
return [[MPOverlaySegue alloc] initWithIdentifier:identifier source:fromViewController destination:toViewController];
}
- (UIView *)addDismissButtonForSegue:(MPOverlaySegue *)segue {
UIButton *dismissButton = [UIButton buttonWithType:UIButtonTypeCustom];
[dismissButton addTarget:self action:@selector( dismissOverlay: ) forControlEvents:UIControlEventTouchUpInside];
dismissButton.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5f];
dismissButton.alpha = 0;
dismissButton.frame = self.view.bounds;
dismissButton.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_dismissSegueByButton[[NSValue valueWithNonretainedObject:dismissButton]] =
[[MPOverlaySegue alloc] initWithIdentifier:@"dismiss-overlay"
source:segue.destinationViewController destination:segue.sourceViewController];
[self.view addSubview:dismissButton];
return dismissButton;
}
- (void)dismissOverlay:(UIButton *)dismissButton {
NSValue *dismissSegueKey = [NSValue valueWithNonretainedObject:dismissButton];
[((UIStoryboardSegue *)_dismissSegueByButton[dismissSegueKey]) perform];
}
- (void)removeDismissButtonForViewController:(UIViewController *)viewController {
UIButton *dismissButton = nil;
for (NSValue *dismissButtonValue in [_dismissSegueByButton allKeys])
if (((UIStoryboardSegue *)_dismissSegueByButton[dismissButtonValue]).sourceViewController == viewController) {
dismissButton = [dismissButtonValue nonretainedObjectValue];
NSAssert([self.view.subviews containsObject:dismissButton], @"Missing dismiss button in dictionary.");
}
if (!dismissButton)
return;
NSValue *dismissSegueKey = [NSValue valueWithNonretainedObject:dismissButton];
[_dismissSegueByButton removeObjectForKey:dismissSegueKey];
[UIView animateWithDuration:0.1f animations:^{
dismissButton.alpha = 0;
} completion:^(BOOL finished) {
[dismissButton removeFromSuperview];
}];
}
@end
@implementation MPOverlaySegue
+ (instancetype)dismissViewController:(UIViewController *)viewController {
return [[self alloc] initWithIdentifier:nil source:viewController destination:viewController];
}
- (void)perform {
UIViewController *sourceViewController = self.sourceViewController;
UIViewController *destinationViewController = self.destinationViewController;
MPOverlayViewController *containerViewController = self.sourceViewController;
while (containerViewController && ![(id)containerViewController isKindOfClass:[MPOverlayViewController class]])
containerViewController = (id)containerViewController.parentViewController;
NSAssert( [containerViewController isKindOfClass:[MPOverlayViewController class]],
@"Not an overlay container: %@", containerViewController );
if (!destinationViewController.parentViewController) {
// Winding
[containerViewController addChildViewController:destinationViewController];
UIView *dismissButton = [containerViewController addDismissButtonForSegue:self];
destinationViewController.view.frame = containerViewController.view.bounds;
destinationViewController.view.translatesAutoresizingMaskIntoConstraints = YES;
destinationViewController.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[containerViewController.view addSubview:destinationViewController.view];
[containerViewController setNeedsStatusBarAppearanceUpdate];
CGRectSetY( destinationViewController.view.frame, 100 );
destinationViewController.view.transform = CGAffineTransformMakeScale( 1.2f, 1.2f );
destinationViewController.view.alpha = 0;
[UIView transitionWithView:containerViewController.view duration:0.3f
options:UIViewAnimationOptionAllowAnimatedContent animations:^{
destinationViewController.view.transform = CGAffineTransformIdentity;
CGRectSetY( destinationViewController.view.frame, 0 );
destinationViewController.view.alpha = 1;
dismissButton.alpha = 1;
} completion:^(BOOL finished) {
[destinationViewController didMoveToParentViewController:containerViewController];
[containerViewController setNeedsStatusBarAppearanceUpdate];
}];
}
else {
// Unwinding
[sourceViewController willMoveToParentViewController:nil];
[UIView transitionWithView:containerViewController.view duration:0.2f
options:UIViewAnimationOptionAllowAnimatedContent animations:^{
CGRectSetY( sourceViewController.view.frame, 100 );
sourceViewController.view.transform = CGAffineTransformMakeScale( 0.8f, 0.8f );
sourceViewController.view.alpha = 0;
[containerViewController removeDismissButtonForViewController:sourceViewController];
} completion:^(BOOL finished) {
if (finished) {
[sourceViewController.view removeFromSuperview];
[sourceViewController removeFromParentViewController];
[containerViewController setNeedsStatusBarAppearanceUpdate];
}
}];
}
}
@end

View File

@@ -27,8 +27,9 @@ typedef NS_ENUM ( NSUInteger, MPPasswordCellMode ) {
@interface MPPasswordCell : MPCell <UIScrollViewDelegate, UITextFieldDelegate> @interface MPPasswordCell : MPCell <UIScrollViewDelegate, UITextFieldDelegate>
- (void)setElement:(MPElementEntity *)element animated:(BOOL)animated; - (void)setSite:(MPSiteEntity *)site animated:(BOOL)animated;
- (void)setTransientSite:(NSString *)siteName animated:(BOOL)animated; - (void)setTransientSite:(NSString *)siteName animated:(BOOL)animated;
- (void)setMode:(MPPasswordCellMode)mode animated:(BOOL)animated; - (void)setMode:(MPPasswordCellMode)mode animated:(BOOL)animated;
- (MPSiteEntity *)siteInContext:(NSManagedObjectContext *)context;
@end @end

View File

@@ -20,6 +20,7 @@
#import "MPiOSAppDelegate.h" #import "MPiOSAppDelegate.h"
#import "MPAppDelegate_Store.h" #import "MPAppDelegate_Store.h"
#import "UIColor+Expanded.h" #import "UIColor+Expanded.h"
#import "MPAppDelegate_InApp.h"
@interface MPPasswordCell() @interface MPPasswordCell()
@@ -31,6 +32,7 @@
@property(nonatomic, strong) IBOutlet UILabel *counterLabel; @property(nonatomic, strong) IBOutlet UILabel *counterLabel;
@property(nonatomic, strong) IBOutlet UIButton *counterButton; @property(nonatomic, strong) IBOutlet UIButton *counterButton;
@property(nonatomic, strong) IBOutlet UIButton *upgradeButton; @property(nonatomic, strong) IBOutlet UIButton *upgradeButton;
@property(nonatomic, strong) IBOutlet UIButton *answersButton;
@property(nonatomic, strong) IBOutlet UIButton *modeButton; @property(nonatomic, strong) IBOutlet UIButton *modeButton;
@property(nonatomic, strong) IBOutlet UIButton *editButton; @property(nonatomic, strong) IBOutlet UIButton *editButton;
@property(nonatomic, strong) IBOutlet UIScrollView *modeScrollView; @property(nonatomic, strong) IBOutlet UIScrollView *modeScrollView;
@@ -44,7 +46,7 @@
@end @end
@implementation MPPasswordCell { @implementation MPPasswordCell {
NSManagedObjectID *_elementOID; NSManagedObjectID *_siteOID;
} }
#pragma mark - Life cycle #pragma mark - Life cycle
@@ -60,11 +62,10 @@
[self setupLayer]; [self setupLayer];
[self observeKeyPath:@"bounds" withBlock:^(id from, id to, NSKeyValueChange cause, id _self) { [self observeKeyPath:@"bounds" withBlock:^(id from, id to, NSKeyValueChange cause, MPPasswordCell *_self) {
if (from && !CGSizeEqualToSize( [from CGRectValue].size, [to CGRectValue].size )) if (from && !CGSizeEqualToSize( [from CGRectValue].size, [to CGRectValue].size ))
[_self setupLayer]; [_self setupLayer];
}]; }];
[self.contentButton observeKeyPath:@"highlighted" [self.contentButton observeKeyPath:@"highlighted"
withBlock:^(id from, id to, NSKeyValueChange cause, UIButton *button) { withBlock:^(id from, id to, NSKeyValueChange cause, UIButton *button) {
[UIView animateWithDuration:.2f delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{ [UIView animateWithDuration:.2f delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
@@ -131,7 +132,7 @@
[super prepareForReuse]; [super prepareForReuse];
_elementOID = nil; _siteOID = nil;
self.transientSite = nil; self.transientSite = nil;
self.mode = MPPasswordCellModePassword; self.mode = MPPasswordCellModePassword;
[self updateAnimated:NO]; [self updateAnimated:NO];
@@ -139,6 +140,7 @@
- (void)dealloc { - (void)dealloc {
[self removeKeyPathObservers];
[self.contentButton removeKeyPathObservers]; [self.contentButton removeKeyPathObservers];
[self.loginNameButton removeKeyPathObservers]; [self.loginNameButton removeKeyPathObservers];
} }
@@ -154,9 +156,9 @@
[self updateAnimated:animated]; [self updateAnimated:animated];
} }
- (void)setElement:(MPElementEntity *)element animated:(BOOL)animated { - (void)setSite:(MPSiteEntity *)site animated:(BOOL)animated {
_elementOID = [element objectID]; _siteOID = [site objectID];
[self updateAnimated:animated]; [self updateAnimated:animated];
} }
@@ -194,10 +196,10 @@
NSString *password = self.passwordField.text; NSString *password = self.passwordField.text;
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
TimeToCrack timeToCrack; TimeToCrack timeToCrack;
MPElementEntity *element = [self elementInContext:context]; MPSiteEntity *site = [self siteInContext:context];
id<MPAlgorithm> algorithm = element.algorithm?: MPAlgorithmDefault; id<MPAlgorithm> algorithm = site.algorithm?: MPAlgorithmDefault;
MPAttacker attackHardware = [[MPConfig get].siteAttacker unsignedIntegerValue]; MPAttacker attackHardware = [[MPConfig get].siteAttacker unsignedIntegerValue];
if ([algorithm timeToCrack:&timeToCrack passwordOfType:[self elementInContext:context].type byAttacker:attackHardware] || if ([algorithm timeToCrack:&timeToCrack passwordOfType:[self siteInContext:context].type byAttacker:attackHardware] ||
[algorithm timeToCrack:&timeToCrack passwordString:password byAttacker:attackHardware]) [algorithm timeToCrack:&timeToCrack passwordString:password byAttacker:attackHardware])
PearlMainQueue( ^{ PearlMainQueue( ^{
self.strengthLabel.text = NSStringFromTimeToCrack( timeToCrack ); self.strengthLabel.text = NSStringFromTimeToCrack( timeToCrack );
@@ -213,19 +215,19 @@
NSString *text = textField.text; NSString *text = textField.text;
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPElementEntity *element = [self elementInContext:context]; MPSiteEntity *site = [self siteInContext:context];
if (!element) if (!site)
return; return;
if (textField == self.passwordField) { if (textField == self.passwordField) {
if ([element.algorithm savePassword:text toElement:element usingKey:[MPiOSAppDelegate get].key]) if ([site.algorithm savePassword:text toSite:site usingKey:[MPiOSAppDelegate get].key])
[PearlOverlay showTemporaryOverlayWithTitle:@"Password Updated" dismissAfter:2]; [PearlOverlay showTemporaryOverlayWithTitle:@"Password Updated" dismissAfter:2];
} }
else if (textField == self.loginNameField && else if (textField == self.loginNameField &&
((element.loginGenerated && ![text length]) || ((site.loginGenerated && ![text length]) ||
(!element.loginGenerated && ![text isEqualToString:element.loginName]))) { (!site.loginGenerated && ![text isEqualToString:site.loginName]))) {
element.loginName = text; site.loginName = text;
element.loginGenerated = NO; site.loginGenerated = NO;
if ([text length]) if ([text length])
[PearlOverlay showTemporaryOverlayWithTitle:@"Login Name Saved" dismissAfter:2]; [PearlOverlay showTemporaryOverlayWithTitle:@"Login Name Saved" dismissAfter:2];
else else
@@ -242,17 +244,17 @@
- (IBAction)doDelete:(UIButton *)sender { - (IBAction)doDelete:(UIButton *)sender {
MPElementEntity *element = [self elementInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]]; MPSiteEntity *site = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
if (!element) if (!site)
return; return;
[PearlSheet showSheetWithTitle:strf( @"Delete %@?", element.name ) viewStyle:UIActionSheetStyleAutomatic [PearlSheet showSheetWithTitle:strf( @"Delete %@?", site.name ) viewStyle:UIActionSheetStyleAutomatic
initSheet:nil tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) { initSheet:nil tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) {
if (buttonIndex == [sheet cancelButtonIndex]) if (buttonIndex == [sheet cancelButtonIndex])
return; return;
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
[context deleteObject:[self elementInContext:context]]; [context deleteObject:[self siteInContext:context]];
[context saveToStore]; [context saveToStore];
}]; }];
} cancelTitle:@"Cancel" destructiveTitle:@"Delete Site" otherTitles:nil]; } cancelTitle:@"Cancel" destructiveTitle:@"Delete Site" otherTitles:nil];
@@ -264,12 +266,11 @@
[PearlSheet showSheetWithTitle:@"Change Password Type" viewStyle:UIActionSheetStyleAutomatic [PearlSheet showSheetWithTitle:@"Change Password Type" viewStyle:UIActionSheetStyleAutomatic
initSheet:^(UIActionSheet *sheet) { initSheet:^(UIActionSheet *sheet) {
MPElementEntity MPSiteEntity *mainSite = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
*mainElement = [self elementInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
for (NSNumber *typeNumber in [MPAlgorithmDefault allTypes]) { for (NSNumber *typeNumber in [MPAlgorithmDefault allTypes]) {
MPElementType type = [typeNumber unsignedIntegerValue]; MPSiteType type = [typeNumber unsignedIntegerValue];
NSString *typeName = [MPAlgorithmDefault nameOfType:type]; NSString *typeName = [MPAlgorithmDefault nameOfType:type];
if (type == mainElement.type) if (type == mainSite.type)
[sheet addButtonWithTitle:strf( @"● %@", typeName )]; [sheet addButtonWithTitle:strf( @"● %@", typeName )];
else else
[sheet addButtonWithTitle:typeName]; [sheet addButtonWithTitle:typeName];
@@ -278,12 +279,12 @@
if (buttonIndex == [sheet cancelButtonIndex]) if (buttonIndex == [sheet cancelButtonIndex])
return; return;
MPElementType type = [[MPAlgorithmDefault allTypes][buttonIndex] unsignedIntegerValue]?: MPElementTypeGeneratedLong; MPSiteType type = [[MPAlgorithmDefault allTypes][buttonIndex] unsignedIntegerValue]?: MPSiteTypeGeneratedLong;
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPElementEntity *element = [self elementInContext:context]; MPSiteEntity *site = [self siteInContext:context];
element = [[MPiOSAppDelegate get] changeElement:element saveInContext:context toType:type]; site = [[MPiOSAppDelegate get] changeSite:site saveInContext:context toType:type];
[self setElement:element animated:YES]; [self setSite:site animated:YES];
}]; }];
} cancelTitle:@"Cancel" destructiveTitle:nil otherTitles:nil]; } cancelTitle:@"Cancel" destructiveTitle:nil otherTitles:nil];
} }
@@ -293,7 +294,7 @@
self.loginNameField.enabled = YES; self.loginNameField.enabled = YES;
self.passwordField.enabled = YES; self.passwordField.enabled = YES;
if ([self elementInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]].type & MPElementTypeClassStored) if ([self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]].type & MPSiteTypeClassStored)
[self.passwordField becomeFirstResponder]; [self.passwordField becomeFirstResponder];
else else
[self.loginNameField becomeFirstResponder]; [self.loginNameField becomeFirstResponder];
@@ -316,7 +317,7 @@
- (IBAction)doUpgrade:(UIButton *)sender { - (IBAction)doUpgrade:(UIButton *)sender {
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
if (![[self elementInContext:context] migrateExplicitly:YES]) { if (![[self siteInContext:context] tryMigrateExplicitly:YES]) {
[PearlOverlay showTemporaryOverlayWithTitle:@"Couldn't Upgrade Site" dismissAfter:2]; [PearlOverlay showTemporaryOverlayWithTitle:@"Couldn't Upgrade Site" dismissAfter:2];
return; return;
} }
@@ -330,11 +331,11 @@
- (IBAction)doIncrementCounter:(UIButton *)sender { - (IBAction)doIncrementCounter:(UIButton *)sender {
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPElementEntity *element = [self elementInContext:context]; MPSiteEntity *site = [self siteInContext:context];
if (!element || ![element isKindOfClass:[MPElementGeneratedEntity class]]) if (!site || ![site isKindOfClass:[MPGeneratedSiteEntity class]])
return; return;
++((MPElementGeneratedEntity *)element).counter; ++((MPGeneratedSiteEntity *)site).counter;
[context saveToStore]; [context saveToStore];
[PearlOverlay showTemporaryOverlayWithTitle:@"Generating New Password" dismissAfter:2]; [PearlOverlay showTemporaryOverlayWithTitle:@"Generating New Password" dismissAfter:2];
@@ -362,11 +363,11 @@
return; return;
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPElementEntity *element = [self elementInContext:context]; MPSiteEntity *site = [self siteInContext:context];
if (!element || ![element isKindOfClass:[MPElementGeneratedEntity class]]) if (!site || ![site isKindOfClass:[MPGeneratedSiteEntity class]])
return; return;
((MPElementGeneratedEntity *)element).counter = 1; ((MPGeneratedSiteEntity *)site).counter = 1;
[context saveToStore]; [context saveToStore];
[PearlOverlay showTemporaryOverlayWithTitle:@"Counter Reset" dismissAfter:2]; [PearlOverlay showTemporaryOverlayWithTitle:@"Counter Reset" dismissAfter:2];
@@ -392,8 +393,8 @@
} }
[[MPiOSAppDelegate get] [[MPiOSAppDelegate get]
addElementNamed:self.transientSite completion:^(MPElementEntity *element, NSManagedObjectContext *context) { addSiteNamed:self.transientSite completion:^(MPSiteEntity *site, NSManagedObjectContext *context) {
[self copyContentOfElement:element saveInContext:context]; [self copyContentOfSite:site saveInContext:context];
PearlMainQueueAfter( .3f, ^{ PearlMainQueueAfter( .3f, ^{
[UIView animateWithDuration:.2f animations:^{ [UIView animateWithDuration:.2f animations:^{
@@ -406,7 +407,7 @@
} }
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
[self copyContentOfElement:[self elementInContext:context] saveInContext:context]; [self copyContentOfSite:[self siteInContext:context] saveInContext:context];
PearlMainQueueAfter( .3f, ^{ PearlMainQueueAfter( .3f, ^{
[UIView animateWithDuration:.2f animations:^{ [UIView animateWithDuration:.2f animations:^{
@@ -423,9 +424,9 @@
}]; }];
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPElementEntity *element = [self elementInContext:context]; MPSiteEntity *site = [self siteInContext:context];
if (![self copyLoginOfElement:element saveInContext:context]) { if (![self copyLoginOfSite:site saveInContext:context]) {
element.loginGenerated = YES; site.loginGenerated = YES;
[context saveToStore]; [context saveToStore];
[PearlOverlay showTemporaryOverlayWithTitle:@"Login Name Generated" dismissAfter:2]; [PearlOverlay showTemporaryOverlayWithTitle:@"Login Name Generated" dismissAfter:2];
[self updateAnimated:YES]; [self updateAnimated:YES];
@@ -461,15 +462,16 @@
} }
[UIView animateWithDuration:animated? .3f: 0 animations:^{ [UIView animateWithDuration:animated? .3f: 0 animations:^{
MPElementEntity *mainElement = [self elementInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]]; MPSiteEntity *mainSite = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
// UI // UI
self.upgradeButton.alpha = mainElement.requiresExplicitMigration? 1: 0; self.upgradeButton.gone = !mainSite.requiresExplicitMigration;
self.answersButton.gone = ![[MPiOSAppDelegate get] isFeatureUnlocked:MPProductGenerateAnswers];
BOOL settingsMode = self.mode == MPPasswordCellModeSettings; BOOL settingsMode = self.mode == MPPasswordCellModeSettings;
self.loginNameContainer.alpha = settingsMode || mainElement.loginGenerated || [mainElement.loginName length]? 0.7f: 0; self.loginNameContainer.alpha = settingsMode || mainSite.loginGenerated || [mainSite.loginName length]? 0.7f: 0;
self.loginNameField.textColor = [UIColor colorWithHexString:mainElement.loginGenerated? @"5E636D": @"6D5E63"]; self.loginNameField.textColor = [UIColor colorWithHexString:mainSite.loginGenerated? @"5E636D": @"6D5E63"];
self.modeButton.alpha = self.transientSite? 0: settingsMode? 0.5f: 0.1f; self.modeButton.alpha = self.transientSite? 0: settingsMode? 0.5f: 0.1f;
self.counterLabel.alpha = self.counterButton.alpha = mainElement.type & MPElementTypeClassGenerated? 0.5f: 0; self.counterLabel.alpha = self.counterButton.alpha = mainSite.type & MPSiteTypeClassGenerated? 0.5f: 0;
self.modeButton.selected = settingsMode; self.modeButton.selected = settingsMode;
self.strengthLabel.gone = !settingsMode; self.strengthLabel.gone = !settingsMode;
self.modeScrollView.scrollEnabled = !self.transientSite; self.modeScrollView.scrollEnabled = !self.transientSite;
@@ -478,36 +480,40 @@
[self.loginNameField resignFirstResponder]; [self.loginNameField resignFirstResponder];
[self.passwordField resignFirstResponder]; [self.passwordField resignFirstResponder];
} }
if ([[MPiOSAppDelegate get] isFeatureUnlocked:MPProductGenerateLogins])
[self.loginNameButton setTitle:@"Tap to generate username or use pencil to save one" forState:UIControlStateNormal];
else
[self.loginNameButton setTitle:@"Tap the pencil to save a username" forState:UIControlStateNormal];
// Site Name // Site Name
self.siteNameLabel.text = strl( @"%@ - %@", self.transientSite?: mainElement.name, self.siteNameLabel.text = strl( @"%@ - %@", self.transientSite?: mainSite.name,
self.transientSite? @"Tap to create": [mainElement.algorithm shortNameOfType:mainElement.type] ); self.transientSite? @"Tap to create": [mainSite.algorithm shortNameOfType:mainSite.type] );
// Site Password // Site Password
self.passwordField.secureTextEntry = [[MPiOSConfig get].hidePasswords boolValue]; self.passwordField.secureTextEntry = [[MPiOSConfig get].hidePasswords boolValue];
self.passwordField.attributedPlaceholder = stra( self.passwordField.attributedPlaceholder = stra(
mainElement.type & MPElementTypeClassStored? strl( @"No password" ): mainSite.type & MPSiteTypeClassStored? strl( @"No password" ):
mainElement.type & MPElementTypeClassGenerated? strl( @"..." ): @"", @{ mainSite.type & MPSiteTypeClassGenerated? strl( @"..." ): @"", @{
NSForegroundColorAttributeName : [UIColor whiteColor] NSForegroundColorAttributeName : [UIColor whiteColor]
} ); } );
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPElementEntity *element = [self elementInContext:context]; MPSiteEntity *site = [self siteInContext:context];
MPKey *key = [MPiOSAppDelegate get].key; MPKey *key = [MPiOSAppDelegate get].key;
NSString *password, *loginName = [element resolveLoginUsingKey:key]; NSString *password, *loginName = [site resolveLoginUsingKey:key];
if (self.transientSite) if (self.transientSite)
password = [MPAlgorithmDefault generatePasswordForSiteNamed:self.transientSite ofType: password = [MPAlgorithmDefault generatePasswordForSiteNamed:self.transientSite ofType:
[[MPiOSAppDelegate get] activeUserInContext:context].defaultType?: MPElementTypeGeneratedLong [[MPiOSAppDelegate get] activeUserInContext:context].defaultType?: MPSiteTypeGeneratedLong
withCounter:1 usingKey:key]; withCounter:1 usingKey:key];
else if (element) else if (site)
password = [element resolvePasswordUsingKey:key]; password = [site resolvePasswordUsingKey:key];
else else
return; return;
TimeToCrack timeToCrack; TimeToCrack timeToCrack;
NSString *timeToCrackString = nil; NSString *timeToCrackString = nil;
id<MPAlgorithm> algorithm = element.algorithm?: MPAlgorithmDefault; id<MPAlgorithm> algorithm = site.algorithm?: MPAlgorithmDefault;
MPAttacker attackHardware = [[MPConfig get].siteAttacker unsignedIntegerValue]; MPAttacker attackHardware = [[MPConfig get].siteAttacker unsignedIntegerValue];
if ([algorithm timeToCrack:&timeToCrack passwordOfType:element.type byAttacker:attackHardware] || if ([algorithm timeToCrack:&timeToCrack passwordOfType:site.type byAttacker:attackHardware] ||
[algorithm timeToCrack:&timeToCrack passwordString:password byAttacker:attackHardware]) [algorithm timeToCrack:&timeToCrack passwordString:password byAttacker:attackHardware])
timeToCrackString = NSStringFromTimeToCrack( timeToCrack ); timeToCrackString = NSStringFromTimeToCrack( timeToCrack );
@@ -533,8 +539,8 @@
}]; }];
// Site Counter // Site Counter
if ([mainElement isKindOfClass:[MPElementGeneratedEntity class]]) if ([mainSite isKindOfClass:[MPGeneratedSiteEntity class]])
self.counterLabel.text = strf( @"%lu", (unsigned long)((MPElementGeneratedEntity *)mainElement).counter ); self.counterLabel.text = strf( @"%lu", (unsigned long)((MPGeneratedSiteEntity *)mainSite).counter );
// Site Login Name // Site Login Name
self.loginNameField.enabled = self.passwordField.enabled = // self.loginNameField.enabled = self.passwordField.enabled = //
@@ -544,10 +550,10 @@
}]; }];
} }
- (BOOL)copyContentOfElement:(MPElementEntity *)element saveInContext:(NSManagedObjectContext *)context { - (BOOL)copyContentOfSite:(MPSiteEntity *)site saveInContext:(NSManagedObjectContext *)context {
inf( @"Copying password for: %@", element.name ); inf( @"Copying password for: %@", site.name );
NSString *password = [element resolvePasswordUsingKey:[MPAppDelegate_Shared get].key]; NSString *password = [site resolvePasswordUsingKey:[MPAppDelegate_Shared get].key];
if (![password length]) if (![password length])
return NO; return NO;
@@ -556,15 +562,15 @@
[UIPasteboard generalPasteboard].string = password; [UIPasteboard generalPasteboard].string = password;
} ); } );
[element use]; [site use];
[context saveToStore]; [context saveToStore];
return YES; return YES;
} }
- (BOOL)copyLoginOfElement:(MPElementEntity *)element saveInContext:(NSManagedObjectContext *)context { - (BOOL)copyLoginOfSite:(MPSiteEntity *)site saveInContext:(NSManagedObjectContext *)context {
inf( @"Copying login for: %@", element.name ); inf( @"Copying login for: %@", site.name );
NSString *loginName = [element.algorithm resolveLoginForElement:element usingKey:[MPiOSAppDelegate get].key]; NSString *loginName = [site.algorithm resolveLoginForSite:site usingKey:[MPiOSAppDelegate get].key];
if (![loginName length]) if (![loginName length])
return NO; return NO;
@@ -573,14 +579,14 @@
[UIPasteboard generalPasteboard].string = loginName; [UIPasteboard generalPasteboard].string = loginName;
} ); } );
[element use]; [site use];
[context saveToStore]; [context saveToStore];
return YES; return YES;
} }
- (MPElementEntity *)elementInContext:(NSManagedObjectContext *)context { - (MPSiteEntity *)siteInContext:(NSManagedObjectContext *)context {
return [MPElementEntity existingObjectWithID:_elementOID inContext:context]; return [MPSiteEntity existingObjectWithID:_siteOID inContext:context];
} }
@end @end

View File

@@ -42,11 +42,9 @@
passwordsVC.active = NO; passwordsVC.active = NO;
UIView *passwordsView = passwordsVC.view; UIView *passwordsView = passwordsVC.view;
passwordsView.translatesAutoresizingMaskIntoConstraints = NO; passwordsView.frame = combinedVC.view.bounds;
[combinedVC.view insertSubview:passwordsView belowSubview:combinedVC.usersView]; passwordsView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[combinedVC.view addConstraintsWithVisualFormats:@[ @"H:|[passwordsView]|", @"V:|[passwordsView]|" ] [combinedVC.view insertSubview:passwordsView belowSubview:combinedVC.usersVC.view];
options:0 metrics:nil
views:NSDictionaryOfVariableBindings( passwordsView )];
[passwordsVC setActive:YES animated:self.animated completion:^(BOOL finished) { [passwordsVC setActive:YES animated:self.animated completion:^(BOOL finished) {
if (!finished) if (!finished)

View File

@@ -16,7 +16,7 @@
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved. // Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
// //
@class MPElementEntity; @class MPSiteEntity;
@class MPCoachmark; @class MPCoachmark;
@interface MPPasswordsViewController : UIViewController<UISearchBarDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout> @interface MPPasswordsViewController : UIViewController<UISearchBarDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>

View File

@@ -22,7 +22,7 @@
#import "MPPopdownSegue.h" #import "MPPopdownSegue.h"
#import "MPAppDelegate_Key.h" #import "MPAppDelegate_Key.h"
#import "MPPasswordCell.h" #import "MPPasswordCell.h"
#import "UICollectionView+PearlReloadFromArray.h" #import "MPAnswersViewController.h"
@interface MPPasswordsViewController()<NSFetchedResultsControllerDelegate> @interface MPPasswordsViewController()<NSFetchedResultsControllerDelegate>
@@ -32,10 +32,6 @@
@end @end
@implementation MPPasswordsViewController { @implementation MPPasswordsViewController {
__weak id _storeChangingObserver;
__weak id _storeChangedObserver;
__weak id _mocObserver;
NSArray *_notificationObservers;
__weak UITapGestureRecognizer *_passwordsDismissRecognizer; __weak UITapGestureRecognizer *_passwordsDismissRecognizer;
NSFetchedResultsController *_fetchedResultsController; NSFetchedResultsController *_fetchedResultsController;
UIColor *_backgroundColor; UIColor *_backgroundColor;
@@ -68,23 +64,36 @@
[super viewWillAppear:animated]; [super viewWillAppear:animated];
[self registerObservers]; [self registerObservers];
[self observeStore];
[self updateConfigKey:nil]; [self updateConfigKey:nil];
[self updatePasswords]; [self updatePasswords];
} }
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserInContext:context];
if (![MPAlgorithmDefault tryMigrateUser:activeUser inContext:context])
[PearlOverlay showTemporaryOverlayWithTitle:@"Some Sites Need Upgrade" dismissAfter:2];
[context saveToStore];
}];
}
- (void)viewWillDisappear:(BOOL)animated { - (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated]; [super viewWillDisappear:animated];
[self removeObservers]; PearlRemoveNotificationObservers();
[self stopObservingStore];
} }
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@"popdown"]) if ([segue.identifier isEqualToString:@"popdown"])
_popdownVC = segue.destinationViewController; _popdownVC = segue.destinationViewController;
if ([segue.identifier isEqualToString:@"answers"])
((MPAnswersViewController *)segue.destinationViewController).site =
[[MPPasswordCell findAsSuperviewOf:sender] siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
} }
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
@@ -96,12 +105,6 @@
#pragma mark - UICollectionViewDelegateFlowLayout #pragma mark - UICollectionViewDelegateFlowLayout
- (CGSize) collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout
referenceSizeForHeaderInSection:(NSInteger)section {
return CGSizeMake( collectionView.bounds.size.width, CGRectGetBottom( self.passwordsSearchBar.frame ).y );
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath { sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
@@ -131,19 +134,13 @@ referenceSizeForHeaderInSection:(NSInteger)section {
MPPasswordCell *cell = [MPPasswordCell dequeueCellFromCollectionView:collectionView indexPath:indexPath]; MPPasswordCell *cell = [MPPasswordCell dequeueCellFromCollectionView:collectionView indexPath:indexPath];
if (indexPath.item < ((id<NSFetchedResultsSectionInfo>)self.fetchedResultsController.sections[indexPath.section]).numberOfObjects) if (indexPath.item < ((id<NSFetchedResultsSectionInfo>)self.fetchedResultsController.sections[indexPath.section]).numberOfObjects)
[cell setElement:[self.fetchedResultsController objectAtIndexPath:indexPath] animated:NO]; [cell setSite:[self.fetchedResultsController objectAtIndexPath:indexPath] animated:NO];
else else
[cell setTransientSite:self.query animated:NO]; [cell setTransientSite:self.query animated:NO];
return cell; return cell;
} }
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind
atIndexPath:(NSIndexPath *)indexPath {
return [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:@"MPPasswordHeader" forIndexPath:indexPath];
}
#pragma mark - UIScrollDelegate #pragma mark - UIScrollDelegate
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
@@ -264,91 +261,49 @@ referenceSizeForHeaderInSection:(NSInteger)section {
- (void)registerObservers { - (void)registerObservers {
if ([_notificationObservers count]) PearlRemoveNotificationObservers();
return; PearlAddNotificationObserver( UIApplicationDidEnterBackgroundNotification, nil, [NSOperationQueue mainQueue],
^(MPPasswordsViewController *self, NSNotification *note) {
Weakify( self ); self.passwordSelectionContainer.alpha = 0;
_notificationObservers = @[ } );
[[NSNotificationCenter defaultCenter] PearlAddNotificationObserver( UIApplicationWillEnterForegroundNotification, nil, [NSOperationQueue mainQueue],
addObserverForName:UIApplicationWillResignActiveNotification object:nil ^(MPPasswordsViewController *self, NSNotification *note) {
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { [self updatePasswords];
Strongify( self ); } );
PearlAddNotificationObserver( UIApplicationDidBecomeActiveNotification, nil, [NSOperationQueue mainQueue],
self.passwordSelectionContainer.alpha = 0; ^(MPPasswordsViewController *self, NSNotification *note) {
}], [UIView animateWithDuration:0.7f animations:^{
[[NSNotificationCenter defaultCenter] self.passwordSelectionContainer.alpha = 1;
addObserverForName:MPSignedOutNotification object:nil }];
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { } );
Strongify( self ); PearlAddNotificationObserver( MPSignedOutNotification, nil, [NSOperationQueue mainQueue],
^(MPPasswordsViewController *self, NSNotification *note) {
_fetchedResultsController = nil; _fetchedResultsController = nil;
self.passwordsSearchBar.text = nil; self.passwordsSearchBar.text = nil;
[self.passwordCollectionView reloadData]; [self.passwordCollectionView reloadData];
}], } );
[[NSNotificationCenter defaultCenter] PearlAddNotificationObserver( MPCheckConfigNotification, nil, [NSOperationQueue mainQueue],
addObserverForName:UIApplicationDidBecomeActiveNotification object:nil ^(MPPasswordsViewController *self, NSNotification *note) {
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { [self updateConfigKey:note.object];
Strongify( self ); } );
PearlAddNotificationObserver( NSPersistentStoreCoordinatorStoresWillChangeNotification, nil, nil,
[self updatePasswords]; ^(MPPasswordsViewController *self, NSNotification *note) {
[UIView animateWithDuration:1 animations:^{ self->_fetchedResultsController = nil;
self.passwordSelectionContainer.alpha = 1; [self.passwordCollectionView reloadData];
}]; } );
}], PearlAddNotificationObserver( NSPersistentStoreCoordinatorStoresDidChangeNotification, nil, nil,
[[NSNotificationCenter defaultCenter] ^(MPPasswordsViewController *self, NSNotification *note) {
addObserverForName:MPCheckConfigNotification object:nil [self updatePasswords];
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { [self registerObservers];
[self updateConfigKey:note.object]; } );
}],
];
}
- (void)removeObservers {
for (id observer in _notificationObservers)
[[NSNotificationCenter defaultCenter] removeObserver:observer];
_notificationObservers = nil;
}
- (void)observeStore {
Weakify( self );
NSManagedObjectContext *mainContext = [MPiOSAppDelegate managedObjectContextForMainThreadIfReady]; NSManagedObjectContext *mainContext = [MPiOSAppDelegate managedObjectContextForMainThreadIfReady];
if (!_mocObserver && mainContext) if (mainContext)
_mocObserver = [[NSNotificationCenter defaultCenter] PearlAddNotificationObserver( NSManagedObjectContextDidSaveNotification, mainContext, nil,
addObserverForName:NSManagedObjectContextDidSaveNotification object:mainContext ^(MPPasswordsViewController *self, NSNotification *note) {
queue:nil usingBlock:^(NSNotification *note) { if (![[MPiOSAppDelegate get] activeUserInContext:note.object])
if (![[MPiOSAppDelegate get] activeUserInContext:mainContext])
[[MPiOSAppDelegate get] signOutAnimated:YES]; [[MPiOSAppDelegate get] signOutAnimated:YES];
}]; } );
if (!_storeChangingObserver)
_storeChangingObserver = [[NSNotificationCenter defaultCenter]
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];
}];
}
- (void)stopObservingStore {
if (_mocObserver)
[[NSNotificationCenter defaultCenter] removeObserver:_mocObserver];
if (_storeChangingObserver)
[[NSNotificationCenter defaultCenter] removeObserver:_storeChangingObserver];
if (_storeChangedObserver)
[[NSNotificationCenter defaultCenter] removeObserver:_storeChangedObserver];
} }
- (void)updateConfigKey:(NSString *)key { - (void)updateConfigKey:(NSString *)key {
@@ -384,7 +339,7 @@ referenceSizeForHeaderInSection:(NSInteger)section {
[NSPredicate predicateWithFormat:@"user == %@ AND name BEGINSWITH[cd] %@", activeUserOID, query]: [NSPredicate predicateWithFormat:@"user == %@ AND name BEGINSWITH[cd] %@", activeUserOID, query]:
[NSPredicate predicateWithFormat:@"user == %@", activeUserOID]; [NSPredicate predicateWithFormat:@"user == %@", activeUserOID];
if (![self.fetchedResultsController performFetch:&error]) if (![self.fetchedResultsController performFetch:&error])
err( @"Couldn't fetch elements: %@", error ); err( @"Couldn't fetch sites: %@", [error fullDescription] );
[self.passwordCollectionView performBatchUpdates:^{ [self.passwordCollectionView performBatchUpdates:^{
[self fetchedItemsDidUpdate]; [self fetchedItemsDidUpdate];
@@ -396,10 +351,12 @@ referenceSizeForHeaderInSection:(NSInteger)section {
[self.passwordCollectionView insertSections:[NSIndexSet indexSetWithIndex:section]]; [self.passwordCollectionView insertSections:[NSIndexSet indexSetWithIndex:section]];
else if (section >= toSections) else if (section >= toSections)
[self.passwordCollectionView deleteSections:[NSIndexSet indexSetWithIndex:section]]; [self.passwordCollectionView deleteSections:[NSIndexSet indexSetWithIndex:section]];
else else if (section < [oldSections count])
[self.passwordCollectionView reloadItemsFromArray:oldSections[section] [self.passwordCollectionView reloadItemsFromArray:oldSections[section]
toArray:[[self.fetchedResultsController sections][section] objects] toArray:[[self.fetchedResultsController sections][section] objects]
inSection:section]; inSection:section];
else
[self.passwordCollectionView reloadSections:[NSIndexSet indexSetWithIndex:section]];
} }
} completion:^(BOOL finished) { } completion:^(BOOL finished) {
if (finished) if (finished)
@@ -421,7 +378,7 @@ referenceSizeForHeaderInSection:(NSInteger)section {
if (!_fetchedResultsController) { if (!_fetchedResultsController) {
_showTransientItem = NO; _showTransientItem = NO;
[MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) { [MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )]; NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
fetchRequest.sortDescriptors = @[ fetchRequest.sortDescriptors = @[
[[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector( lastUsed ) ) ascending:NO] [[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector( lastUsed ) ) ascending:NO]
]; ];
@@ -430,7 +387,7 @@ referenceSizeForHeaderInSection:(NSInteger)section {
initWithFetchRequest:fetchRequest managedObjectContext:mainContext sectionNameKeyPath:nil cacheName:nil]; initWithFetchRequest:fetchRequest managedObjectContext:mainContext sectionNameKeyPath:nil cacheName:nil];
_fetchedResultsController.delegate = self; _fetchedResultsController.delegate = self;
}]; }];
[self observeStore]; [self registerObservers];
} }
return _fetchedResultsController; return _fetchedResultsController;

View File

@@ -102,7 +102,7 @@
self.generatedTypeControl.selectedSegmentIndex = -1; self.generatedTypeControl.selectedSegmentIndex = -1;
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPElementType defaultType = [[MPiOSAppDelegate get] activeUserInContext:context].defaultType = [self typeForSelectedSegment]; MPSiteType defaultType = [[MPiOSAppDelegate get] activeUserInContext:context].defaultType = [self typeForSelectedSegment];
[context saveToStore]; [context saveToStore];
PearlMainQueue( ^{ PearlMainQueue( ^{
@@ -179,31 +179,31 @@
return nil; return nil;
} }
- (enum MPElementType)typeForSelectedSegment { - (enum MPSiteType)typeForSelectedSegment {
NSInteger selectedGeneratedIndex = self.generatedTypeControl.selectedSegmentIndex; NSInteger selectedGeneratedIndex = self.generatedTypeControl.selectedSegmentIndex;
NSInteger selectedStoredIndex = self.storedTypeControl.selectedSegmentIndex; NSInteger selectedStoredIndex = self.storedTypeControl.selectedSegmentIndex;
switch (selectedGeneratedIndex) { switch (selectedGeneratedIndex) {
case 0: case 0:
return MPElementTypeGeneratedMaximum; return MPSiteTypeGeneratedMaximum;
case 1: case 1:
return MPElementTypeGeneratedLong; return MPSiteTypeGeneratedLong;
case 2: case 2:
return MPElementTypeGeneratedMedium; return MPSiteTypeGeneratedMedium;
case 3: case 3:
return MPElementTypeGeneratedBasic; return MPSiteTypeGeneratedBasic;
case 4: case 4:
return MPElementTypeGeneratedShort; return MPSiteTypeGeneratedShort;
case 5: case 5:
return MPElementTypeGeneratedPIN; return MPSiteTypeGeneratedPIN;
default: default:
switch (selectedStoredIndex) { switch (selectedStoredIndex) {
case 0: case 0:
return MPElementTypeStoredPersonal; return MPSiteTypeStoredPersonal;
case 1: case 1:
return MPElementTypeStoredDevicePrivate; return MPSiteTypeStoredDevicePrivate;
default: default:
Throw( @"unsupported selected type index: generated=%ld, stored=%ld", (long)selectedGeneratedIndex, Throw( @"unsupported selected type index: generated=%ld, stored=%ld", (long)selectedGeneratedIndex,
(long)selectedStoredIndex ); (long)selectedStoredIndex );
@@ -211,32 +211,32 @@
} }
} }
- (NSInteger)generatedSegmentIndexForType:(MPElementType)type { - (NSInteger)generatedSegmentIndexForType:(MPSiteType)type {
switch (type) { switch (type) {
case MPElementTypeGeneratedMaximum: case MPSiteTypeGeneratedMaximum:
return 0; return 0;
case MPElementTypeGeneratedLong: case MPSiteTypeGeneratedLong:
return 1; return 1;
case MPElementTypeGeneratedMedium: case MPSiteTypeGeneratedMedium:
return 2; return 2;
case MPElementTypeGeneratedBasic: case MPSiteTypeGeneratedBasic:
return 3; return 3;
case MPElementTypeGeneratedShort: case MPSiteTypeGeneratedShort:
return 4; return 4;
case MPElementTypeGeneratedPIN: case MPSiteTypeGeneratedPIN:
return 5; return 5;
default: default:
return -1; return -1;
} }
} }
- (NSInteger)storedSegmentIndexForType:(MPElementType)type { - (NSInteger)storedSegmentIndexForType:(MPSiteType)type {
switch (type) { switch (type) {
case MPElementTypeStoredPersonal: case MPSiteTypeStoredPersonal:
return 0; return 0;
case MPElementTypeStoredDevicePrivate: case MPSiteTypeStoredDevicePrivate:
return 1; return 1;
default: default:
return -1; return -1;

View File

@@ -9,16 +9,17 @@
*/ */
// //
// MPEmergencySegue.h // MPRootSegue.h
// MPEmergencySegue // MPRootSegue
// //
// Created by lhunath on 2014-04-09. // Created by lhunath on 2014-09-26.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved. // Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
// //
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
@interface MPEmergencySegue : UIStoryboardSegue
@property(nonatomic) BOOL unwind; @interface MPRootSegue : UIStoryboardSegue
@end @end

View File

@@ -0,0 +1,38 @@
/**
* 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
*/
//
// MPRootSegue.h
// MPRootSegue
//
// Created by lhunath on 2014-09-26.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
#import "MPRootSegue.h"
@implementation MPRootSegue {
}
- (void)perform {
UIViewController *sourceViewController = self.sourceViewController;
UIViewController *destinationViewController = self.destinationViewController;
[sourceViewController addChildViewController:destinationViewController];
destinationViewController.view.frame = sourceViewController.view.bounds;
destinationViewController.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[sourceViewController.view addSubview:destinationViewController.view];
[destinationViewController didMoveToParentViewController:sourceViewController];
[sourceViewController setNeedsStatusBarAppearanceUpdate];
}
@end

View File

@@ -0,0 +1,31 @@
//
// MPPreferencesViewController.h
// MasterPassword-iOS
//
// Created by Maarten Billemont on 04/06/12.
// Copyright (c) 2012 Lyndir. All rights reserved.
//
#import <UIKit/UIKit.h>
@class MPStoreProductCell;
@interface MPStoreViewController : PearlMutableStaticTableViewController
@property(weak, nonatomic) IBOutlet MPStoreProductCell *generateLoginCell;
@property(weak, nonatomic) IBOutlet MPStoreProductCell *generateAnswersCell;
@property(weak, nonatomic) IBOutlet MPStoreProductCell *iOSIntegrationCell;
@property(weak, nonatomic) IBOutlet MPStoreProductCell *touchIDCell;
@property(weak, nonatomic) IBOutlet MPStoreProductCell *fuelCell;
@property(weak, nonatomic) IBOutlet NSLayoutConstraint *fuelMeterConstraint;
@property(weak, nonatomic) IBOutlet UIButton *fuelSpeedButton;
@end
@interface MPStoreProductCell : UITableViewCell
@property(nonatomic) IBOutlet UILabel *priceLabel;
@property(nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;
@property(nonatomic) IBOutlet UIView *purchasedIndicator;
@end

View File

@@ -0,0 +1,278 @@
//
// MPPreferencesViewController.m
// MasterPassword-iOS
//
// Created by Maarten Billemont on 04/06/12.
// Copyright (c) 2012 Lyndir. All rights reserved.
//
#import "MPStoreViewController.h"
#import "MPiOSAppDelegate.h"
#import "UIColor+Expanded.h"
#import "MPAppDelegate_InApp.h"
PearlEnum( MPDevelopmentFuelConsumption,
MPDevelopmentFuelConsumptionQuarterly, MPDevelopmentFuelConsumptionMonthly, MPDevelopmentFuelWeekly );
@interface MPStoreViewController()<MPInAppDelegate>
@property(nonatomic, strong) NSNumberFormatter *currencyFormatter;
@property(nonatomic, strong) NSArray *products;
@end
@implementation MPStoreViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.currencyFormatter = [NSNumberFormatter new];
self.currencyFormatter.numberStyle = NSNumberFormatterCurrencyStyle;
self.tableView.tableHeaderView = [UIView new];
self.tableView.tableFooterView = [UIView new];
self.tableView.estimatedRowHeight = 400;
self.view.backgroundColor = [UIColor clearColor];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.tableView.contentInset = UIEdgeInsetsMake( 64, 0, 49, 0 );
[self reloadCellsHiding:self.allCellsBySection[0] showing:nil];
[self.allCellsBySection[0] enumerateObjectsUsingBlock:^(MPStoreProductCell *cell, NSUInteger idx, BOOL *stop) {
if ([cell isKindOfClass:[MPStoreProductCell class]]) {
cell.purchasedIndicator.alpha = 0;
[cell.activityIndicator stopAnimating];
}
}];
PearlAddNotificationObserver( NSUserDefaultsDidChangeNotification, nil, [NSOperationQueue mainQueue],
^(MPStoreViewController *self, NSNotification *note) {
[self updateProducts];
[self updateFuel];
} );
[[MPiOSAppDelegate get] registerProductsObserver:self];
[self updateFuel];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
PearlRemoveNotificationObservers();
}
#pragma mark - UITableViewDelegate
- (MPStoreProductCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MPStoreProductCell *cell = (MPStoreProductCell *)[super tableView:tableView cellForRowAtIndexPath:indexPath];
if (cell.contentView.translatesAutoresizingMaskIntoConstraints) {
cell.contentView.translatesAutoresizingMaskIntoConstraints = NO;
[cell addConstraint:
[NSLayoutConstraint constraintWithItem:cell attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual
toItem:cell.contentView attribute:NSLayoutAttributeWidth multiplier:1 constant:0]];
}
if (indexPath.section == 0)
cell.selectionStyle = [[MPiOSAppDelegate get] isFeatureUnlocked:[self productForCell:cell].productIdentifier]?
UITableViewCellSelectionStyleDefault: UITableViewCellSelectionStyleNone;
if (cell.selectionStyle != UITableViewCellSelectionStyleNone) {
cell.selectedBackgroundView = [[UIView alloc] initWithFrame:cell.bounds];
cell.selectedBackgroundView.backgroundColor = [UIColor colorWithRGBAHex:0x78DDFB33];
}
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
[cell layoutIfNeeded];
return cell.contentView.bounds.size.height;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (![[MPAppDelegate_Shared get] canMakePayments]) {
[PearlAlert showAlertWithTitle:@"Store Not Set Up" message:
@"Try logging using the App Store or from Settings."
viewStyle:UIAlertViewStyleDefault initAlert:nil
tappedButtonBlock:nil cancelTitle:@"Thanks" otherTitles:nil];
return;
}
MPStoreProductCell *cell = (MPStoreProductCell *)[self tableView:tableView cellForRowAtIndexPath:indexPath];
SKProduct *product = [self productForCell:cell];
if (product)
[[MPAppDelegate_Shared get] purchaseProductWithIdentifier:product.productIdentifier
quantity:[self quantityForProductIdentifier:product.productIdentifier]];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
#pragma mark - Actions
- (IBAction)toggleFuelConsumption:(id)sender {
NSUInteger fuelConsumption = [[MPiOSConfig get].developmentFuelConsumption unsignedIntegerValue];
[MPiOSConfig get].developmentFuelConsumption = @((fuelConsumption + 1) % MPDevelopmentFuelConsumptionCount);
[self updateProducts];
}
- (IBAction)restorePurchases:(id)sender {
[PearlAlert showAlertWithTitle:@"Restore Previous Purchases" message:
@"This will check with Apple to find and activate any purchases you made from other devices."
viewStyle:UIAlertViewStyleDefault initAlert:nil
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
if (buttonIndex == [alert cancelButtonIndex])
return;
[[MPAppDelegate_Shared get] restoreCompletedTransactions];
} cancelTitle:@"Cancel" otherTitles:@"Find Purchases", nil];
}
#pragma mark - MPInAppDelegate
- (void)updateWithProducts:(NSArray *)products {
self.products = products;
[self updateProducts];
}
- (void)updateWithTransaction:(SKPaymentTransaction *)transaction {
MPStoreProductCell *cell = [self cellForProductIdentifier:transaction.payment.productIdentifier];
if (!cell)
return;
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchasing:
[cell.activityIndicator startAnimating];
break;
case SKPaymentTransactionStatePurchased:
[cell.activityIndicator stopAnimating];
break;
case SKPaymentTransactionStateFailed:
[cell.activityIndicator stopAnimating];
break;
case SKPaymentTransactionStateRestored:
[cell.activityIndicator stopAnimating];
break;
case SKPaymentTransactionStateDeferred:
[cell.activityIndicator startAnimating];
break;
}
}
#pragma mark - Private
- (SKProduct *)productForCell:(MPStoreProductCell *)cell {
for (SKProduct *product in self.products)
if ([self cellForProductIdentifier:product.productIdentifier] == cell)
return product;
return nil;
}
- (MPStoreProductCell *)cellForProductIdentifier:(NSString *)productIdentifier {
if ([productIdentifier isEqualToString:MPProductGenerateLogins])
return self.generateLoginCell;
if ([productIdentifier isEqualToString:MPProductGenerateAnswers])
return self.generateAnswersCell;
if ([productIdentifier isEqualToString:MPProductFuel])
return self.fuelCell;
return nil;
}
- (void)updateProducts {
NSMutableArray *showCells = [NSMutableArray array];
NSMutableArray *hideCells = [NSMutableArray array];
[hideCells addObjectsFromArray:self.allCellsBySection[0]];
for (SKProduct *product in self.products) {
[self showCellForProductWithIdentifier:MPProductGenerateLogins ifProduct:product showingCells:showCells];
[self showCellForProductWithIdentifier:MPProductGenerateAnswers ifProduct:product showingCells:showCells];
[self showCellForProductWithIdentifier:MPProductFuel ifProduct:product showingCells:showCells];
}
[hideCells removeObjectsInArray:showCells];
if ([self.tableView numberOfRowsInSection:0])
[self updateCellsHiding:hideCells showing:showCells animation:UITableViewRowAnimationAutomatic];
else
[self updateCellsHiding:hideCells showing:showCells animation:UITableViewRowAnimationNone];
}
- (void)updateFuel {
CGFloat weeklyFuelConsumption = [self weeklyFuelConsumption]; /* consume x fuel / week */
CGFloat fuel = [[MPiOSConfig get].developmentFuel floatValue]; /* x fuel left */
NSTimeInterval fuelSecondsElapsed = [[MPiOSConfig get].developmentFuelChecked timeIntervalSinceNow];
if (fuelSecondsElapsed > 3600) {
NSTimeInterval weeksElapsed = fuelSecondsElapsed / (3600 * 24 * 7 /* 1 week */); /* x weeks elapsed */
fuel -= weeklyFuelConsumption * weeksElapsed;
[MPiOSConfig get].developmentFuel = @(fuel);
}
CGFloat fuelRatio = weeklyFuelConsumption == 0? 0: fuel / weeklyFuelConsumption; /* x weeks worth of fuel left */
[self.fuelMeterConstraint updateConstant:MIN( 0.5f, fuelRatio - 0.5f ) * 160]; /* -80pt = 0 weeks left, 80pt = >=1 week left */
}
- (CGFloat)weeklyFuelConsumption {
switch ((MPDevelopmentFuelConsumption)[[MPiOSConfig get].developmentFuelConsumption unsignedIntegerValue]) {
case MPDevelopmentFuelConsumptionQuarterly:
[self.fuelSpeedButton setTitle:@"1h / quarter" forState:UIControlStateNormal];
return 1.f / 12 /* 12 weeks */;
case MPDevelopmentFuelConsumptionMonthly:
[self.fuelSpeedButton setTitle:@"1h / month" forState:UIControlStateNormal];
return 1.f / 4 /* 4 weeks */;
case MPDevelopmentFuelWeekly:
[self.fuelSpeedButton setTitle:@"1h / week" forState:UIControlStateNormal];
return 1.f;
}
return 0;
}
- (void)showCellForProductWithIdentifier:(NSString *)productIdentifier ifProduct:(SKProduct *)product
showingCells:(NSMutableArray *)showCells {
if (![product.productIdentifier isEqualToString:productIdentifier])
return;
MPStoreProductCell *cell = [self cellForProductIdentifier:productIdentifier];
[showCells addObject:cell];
self.currencyFormatter.locale = product.priceLocale;
BOOL purchased = [[MPiOSAppDelegate get] isFeatureUnlocked:productIdentifier];
NSInteger quantity = [self quantityForProductIdentifier:productIdentifier];
cell.priceLabel.text = purchased? @"": [self.currencyFormatter stringFromNumber:@([product.price floatValue] * quantity)];
cell.purchasedIndicator.alpha = purchased? 1: 0;
}
- (NSInteger)quantityForProductIdentifier:(NSString *)productIdentifier {
if ([productIdentifier isEqualToString:MPProductFuel])
return (NSInteger)(MP_FUEL_HOURLY_RATE * [self weeklyFuelConsumption]);
return 1;
}
@end
@implementation MPStoreProductCell
@end

View File

@@ -13,11 +13,11 @@
@protocol MPTypeDelegate<NSObject> @protocol MPTypeDelegate<NSObject>
@required @required
- (void)didSelectType:(MPElementType)type; - (void)didSelectType:(MPSiteType)type;
- (MPElementType)selectedType; - (MPSiteType)selectedType;
@optional @optional
- (MPElementEntity *)selectedElement; - (MPSiteEntity *)selectedSite;
@end @end

View File

@@ -12,7 +12,7 @@
@interface MPTypeViewController() @interface MPTypeViewController()
- (MPElementType)typeAtIndexPath:(NSIndexPath *)indexPath; - (MPSiteType)typeAtIndexPath:(NSIndexPath *)indexPath;
@end @end
@@ -63,21 +63,21 @@
UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath]; UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
MPElementEntity *selectedElement = nil; MPSiteEntity *selectedSite = nil;
if ([self.delegate respondsToSelector:@selector(selectedElement)]) if ([self.delegate respondsToSelector:@selector( selectedSite )])
selectedElement = [self.delegate selectedElement]; selectedSite = [self.delegate selectedSite];
MPElementType cellType = [self typeAtIndexPath:indexPath]; MPSiteType cellType = [self typeAtIndexPath:indexPath];
MPElementType selectedType = selectedElement? selectedElement.type: [self.delegate selectedType]; MPSiteType selectedType = selectedSite? selectedSite.type: [self.delegate selectedType];
cell.selected = (selectedType == cellType); cell.selected = (selectedType == cellType);
if (cellType != (MPElementType)NSNotFound && cellType & MPElementTypeClassGenerated) { if (cellType != (MPSiteType)NSNotFound && cellType & MPSiteTypeClassGenerated) {
[(UITextField *)[cell viewWithTag:2] setText:@"..."]; [(UITextField *)[cell viewWithTag:2] setText:@"..."];
NSString *name = selectedElement.name; NSString *name = selectedSite.name;
NSUInteger counter = 0; NSUInteger counter = 0;
if ([selectedElement isKindOfClass:[MPElementGeneratedEntity class]]) if ([selectedSite isKindOfClass:[MPGeneratedSiteEntity class]])
counter = ((MPElementGeneratedEntity *)selectedElement).counter; counter = ((MPGeneratedSiteEntity *)selectedSite).counter;
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0 ), ^{ dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0 ), ^{
NSString *typeContent = [MPAlgorithmDefault generatePasswordForSiteNamed:name ofType:cellType NSString *typeContent = [MPAlgorithmDefault generatePasswordForSiteNamed:name ofType:cellType
@@ -96,8 +96,8 @@
NSAssert(self.navigationController.topViewController == self, @"Not the currently active navigation item."); NSAssert(self.navigationController.topViewController == self, @"Not the currently active navigation item.");
MPElementType type = [self typeAtIndexPath:indexPath]; MPSiteType type = [self typeAtIndexPath:indexPath];
if (type == (MPElementType)NSNotFound) if (type == (MPSiteType)NSNotFound)
// Selected a non-type row. // Selected a non-type row.
return; return;
@@ -105,31 +105,31 @@
[self.navigationController popViewControllerAnimated:YES]; [self.navigationController popViewControllerAnimated:YES];
} }
- (MPElementType)typeAtIndexPath:(NSIndexPath *)indexPath { - (MPSiteType)typeAtIndexPath:(NSIndexPath *)indexPath {
switch (indexPath.section) { switch (indexPath.section) {
case 0: { case 0: {
// Generated // Generated
switch (indexPath.row) { switch (indexPath.row) {
case 0: case 0:
return (MPElementType)NSNotFound; return (MPSiteType)NSNotFound;
case 1: case 1:
return MPElementTypeGeneratedMaximum; return MPSiteTypeGeneratedMaximum;
case 2: case 2:
return MPElementTypeGeneratedLong; return MPSiteTypeGeneratedLong;
case 3: case 3:
return MPElementTypeGeneratedMedium; return MPSiteTypeGeneratedMedium;
case 4: case 4:
return MPElementTypeGeneratedBasic; return MPSiteTypeGeneratedBasic;
case 5: case 5:
return MPElementTypeGeneratedShort; return MPSiteTypeGeneratedShort;
case 6: case 6:
return MPElementTypeGeneratedPIN; return MPSiteTypeGeneratedPIN;
case 7: case 7:
return (MPElementType)NSNotFound; return (MPSiteType)NSNotFound;
default: { default: {
Throw(@"Unsupported row: %ld, when selecting generated element type.", (long)indexPath.row); Throw(@"Unsupported row: %ld, when selecting generated site type.", (long)indexPath.row);
} }
} }
} }
@@ -138,22 +138,22 @@
// Stored // Stored
switch (indexPath.row) { switch (indexPath.row) {
case 0: case 0:
return (MPElementType)NSNotFound; return (MPSiteType)NSNotFound;
case 1: case 1:
return MPElementTypeStoredPersonal; return MPSiteTypeStoredPersonal;
case 2: case 2:
return MPElementTypeStoredDevicePrivate; return MPSiteTypeStoredDevicePrivate;
case 3: case 3:
return (MPElementType)NSNotFound; return (MPSiteType)NSNotFound;
default: { default: {
Throw(@"Unsupported row: %ld, when selecting stored element type.", (long)indexPath.row); Throw(@"Unsupported row: %ld, when selecting stored site type.", (long)indexPath.row);
} }
} }
} }
default: default:
Throw(@"Unsupported section: %ld, when selecting element type.", (long)indexPath.section); Throw(@"Unsupported section: %ld, when selecting site type.", (long)indexPath.section);
} }
} }

View File

@@ -22,9 +22,7 @@
#import "MPiOSAppDelegate.h" #import "MPiOSAppDelegate.h"
#import "MPAppDelegate_Store.h" #import "MPAppDelegate_Store.h"
#import "MPAppDelegate_Key.h" #import "MPAppDelegate_Key.h"
#import "PearlSizedTextView.h"
#import "MPWebViewController.h" #import "MPWebViewController.h"
#import "UIView+FontScale.h"
typedef NS_ENUM( NSUInteger, MPActiveUserState ) { typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
/** The users are all inactive */ /** The users are all inactive */
@@ -51,10 +49,6 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
@end @end
@implementation MPUsersViewController { @implementation MPUsersViewController {
__weak id _storeChangingObserver;
__weak id _storeChangedObserver;
__weak id _mocObserver;
NSArray *_notificationObservers;
NSString *_masterPasswordChoice; NSString *_masterPasswordChoice;
NSOperationQueue *_afterUpdates; NSOperationQueue *_afterUpdates;
} }
@@ -74,6 +68,8 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
self.view.backgroundColor = [UIColor clearColor]; self.view.backgroundColor = [UIColor clearColor];
self.avatarCollectionView.allowsMultipleSelection = YES; self.avatarCollectionView.allowsMultipleSelection = YES;
[self.entryField addTarget:self action:@selector( textFieldEditingChanged: ) forControlEvents:UIControlEventEditingChanged]; [self.entryField addTarget:self action:@selector( textFieldEditingChanged: ) forControlEvents:UIControlEventEditingChanged];
[self setActive:YES animated:NO];
} }
- (void)viewWillAppear:(BOOL)animated { - (void)viewWillAppear:(BOOL)animated {
@@ -81,15 +77,13 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
[super viewWillAppear:animated]; [super viewWillAppear:animated];
self.userSelectionContainer.alpha = 0; self.userSelectionContainer.alpha = 0;
[self setActive:YES animated:NO];
} }
- (void)viewWillDisappear:(BOOL)animated { - (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated]; [super viewWillDisappear:animated];
[self removeObservers]; PearlRemoveNotificationObservers();
[self stopObservingStore];
[self.marqueeTipTimer invalidate]; [self.marqueeTipTimer invalidate];
} }
@@ -98,7 +92,6 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
[super viewDidAppear:animated]; [super viewDidAppear:animated];
[self observeStore];
[self registerObservers]; [self registerObservers];
[self reloadUsers]; [self reloadUsers];
@@ -474,15 +467,15 @@ referenceSizeForFooterInSection:(NSInteger)section {
if ([nextMarqueeString isEqualToString:[self.marqueeButton titleForState:UIControlStateNormal]]) if ([nextMarqueeString isEqualToString:[self.marqueeButton titleForState:UIControlStateNormal]])
return; return;
[UIView animateWithDuration:timer? 0.5: 0 animations:^{ [UIView animateWithDuration:timer? 0.5f: 0 animations:^{
self.marqueeButton.alpha = 0; self.marqueeButton.alpha = 0;
} completion:^(BOOL finished) { } completion:^(BOOL finished) {
if (!finished) if (!finished)
return; return;
[self.marqueeButton setTitle:nextMarqueeString forState:UIControlStateNormal]; [self.marqueeButton setTitle:nextMarqueeString forState:UIControlStateNormal];
[UIView animateWithDuration:timer? 0.5: 0 animations:^{ [UIView animateWithDuration:timer? 0.5f: 0 animations:^{
self.marqueeButton.alpha = 0.5; self.marqueeButton.alpha = 0.5f;
}]; }];
}]; }];
} }
@@ -585,50 +578,27 @@ referenceSizeForFooterInSection:(NSInteger)section {
- (void)registerObservers { - (void)registerObservers {
if ([_notificationObservers count]) [self removeKeyPathObservers];
return;
Weakify( self );
_notificationObservers = @[
[[NSNotificationCenter defaultCenter]
addObserverForName:UIApplicationWillResignActiveNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
Strongify( self );
// [self emergencyCloseAnimated:NO];
self.userSelectionContainer.alpha = 0;
}],
[[NSNotificationCenter defaultCenter]
addObserverForName:UIApplicationDidBecomeActiveNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
Strongify( self );
[self reloadUsers];
[UIView animateWithDuration:1 animations:^{
self.userSelectionContainer.alpha = 1;
}];
}],
];
[self observeKeyPath:@"avatarCollectionView.contentOffset" withBlock: [self observeKeyPath:@"avatarCollectionView.contentOffset" withBlock:
^(id from, id to, NSKeyValueChange cause, MPUsersViewController *_self) { ^(id from, id to, NSKeyValueChange cause, MPUsersViewController *_self) {
[_self updateAvatarVisibility]; [_self updateAvatarVisibility];
}]; }];
}
- (void)removeObservers { PearlRemoveNotificationObservers();
PearlAddNotificationObserver( UIApplicationDidEnterBackgroundNotification, nil, [NSOperationQueue mainQueue],
for (id observer in _notificationObservers) ^(MPUsersViewController *self, NSNotification *note) {
[[NSNotificationCenter defaultCenter] removeObserver:observer]; self.userSelectionContainer.alpha = 0;
_notificationObservers = nil; } );
PearlAddNotificationObserver( UIApplicationWillEnterForegroundNotification, nil, [NSOperationQueue mainQueue],
[self removeKeyPathObservers]; ^(MPUsersViewController *self, NSNotification *note) {
} [self reloadUsers];
} );
- (void)observeStore { PearlAddNotificationObserver( UIApplicationDidBecomeActiveNotification, nil, [NSOperationQueue mainQueue],
^(MPUsersViewController *self, NSNotification *note) {
Weakify( self ); [UIView animateWithDuration:0.5f animations:^{
self.userSelectionContainer.alpha = 1;
}];
} );
NSManagedObjectContext *mainContext = [MPiOSAppDelegate managedObjectContextForMainThreadIfReady]; NSManagedObjectContext *mainContext = [MPiOSAppDelegate managedObjectContextForMainThreadIfReady];
[UIView animateWithDuration:0.3f animations:^{ [UIView animateWithDuration:0.3f animations:^{
@@ -639,51 +609,31 @@ referenceSizeForFooterInSection:(NSInteger)section {
if (!mainContext && !self.storeLoadingActivity.isAnimating) if (!mainContext && !self.storeLoadingActivity.isAnimating)
[self.storeLoadingActivity startAnimating]; [self.storeLoadingActivity startAnimating];
if (!_mocObserver && mainContext) if (mainContext)
_mocObserver = [[NSNotificationCenter defaultCenter] PearlAddNotificationObserver( NSManagedObjectContextObjectsDidChangeNotification, mainContext, [NSOperationQueue mainQueue],
addObserverForName:NSManagedObjectContextObjectsDidChangeNotification object:mainContext ^(MPUsersViewController *self, NSNotification *note) {
queue:nil usingBlock:^(NSNotification *note) { NSSet *insertedObjects = note.userInfo[NSInsertedObjectsKey];
Strongify( self ); NSSet *deletedObjects = note.userInfo[NSDeletedObjectsKey];
NSSet *insertedObjects = note.userInfo[NSInsertedObjectsKey]; if ([[NSSetUnion( insertedObjects, deletedObjects )
NSSet *deletedObjects = note.userInfo[NSDeletedObjectsKey]; filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
if ([[NSSetUnion( insertedObjects, deletedObjects ) return [evaluatedObject isKindOfClass:[MPUserEntity class]];
filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) { }]] count])
return [evaluatedObject isKindOfClass:[MPUserEntity class]];
}]] count])
[self reloadUsers];
}];
if (!_storeChangingObserver)
_storeChangingObserver = [[NSNotificationCenter defaultCenter]
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:NSPersistentStoreCoordinatorStoresDidChangeNotification object:nil
queue:nil usingBlock:^(NSNotification *note) {
Strongify( self );
[self reloadUsers]; [self reloadUsers];
}]; } );
} PearlAddNotificationObserver( NSPersistentStoreCoordinatorStoresWillChangeNotification, nil, [NSOperationQueue mainQueue],
^(MPUsersViewController *self, NSNotification *note) {
- (void)stopObservingStore { self.userIDs = nil;
} );
if (_mocObserver) PearlAddNotificationObserver( NSPersistentStoreCoordinatorStoresDidChangeNotification, nil, [NSOperationQueue mainQueue],
[[NSNotificationCenter defaultCenter] removeObserver:_mocObserver]; ^(MPUsersViewController *self, NSNotification *note) {
if (_storeChangingObserver) [self registerObservers];
[[NSNotificationCenter defaultCenter] removeObserver:_storeChangingObserver]; [self reloadUsers];
if (_storeChangedObserver) } );
[[NSNotificationCenter defaultCenter] removeObserver:_storeChangedObserver];
} }
- (void)reloadUsers { - (void)reloadUsers {
[self afterUpdatesMainQueue:^{ [self afterUpdatesMainQueue:^{
[self observeStore];
if (![MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) { if (![MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) {
NSError *error = nil; NSError *error = nil;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )]; NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
@@ -692,7 +642,7 @@ referenceSizeForFooterInSection:(NSInteger)section {
]; ];
NSArray *users = [mainContext executeFetchRequest:fetchRequest error:&error]; NSArray *users = [mainContext executeFetchRequest:fetchRequest error:&error];
if (!users) { if (!users) {
err( @"Failed to load users: %@", error ); err( @"Failed to load users: %@", [error fullDescription] );
self.userIDs = nil; self.userIDs = nil;
} }

View File

@@ -7,7 +7,6 @@
// //
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import <MessageUI/MessageUI.h>
#import "MPAppDelegate_Shared.h" #import "MPAppDelegate_Shared.h"

View File

@@ -11,26 +11,25 @@
#import "MPAppDelegate_Store.h" #import "MPAppDelegate_Store.h"
#import "IASKSettingsReader.h" #import "IASKSettingsReader.h"
@interface MPiOSAppDelegate() @interface MPiOSAppDelegate()<UIDocumentInteractionControllerDelegate>
@property(nonatomic, strong) UIDocumentInteractionController *interactionController;
@property(nonatomic, weak) PearlAlert *handleCloudDisabledAlert;
@property(nonatomic, weak) PearlAlert *handleCloudContentAlert;
@property(nonatomic, weak) PearlAlert *fixCloudContentAlert;
@property(nonatomic, weak) PearlOverlay *storeLoadingOverlay;
@end @end
@implementation MPiOSAppDelegate @implementation MPiOSAppDelegate
+ (void)initialize { + (void)initialize {
if ([self class] == [MPiOSAppDelegate class]) { static dispatch_once_t once = 0;
dispatch_once( &once, ^{
[PearlLogger get].historyLevel = [[MPiOSConfig get].traceMode boolValue]? PearlLogLevelTrace: PearlLogLevelInfo; [PearlLogger get].historyLevel = [[MPiOSConfig get].traceMode boolValue]? PearlLogLevelTrace: PearlLogLevelInfo;
#ifdef DEBUG #ifdef DEBUG
[PearlLogger get].printLevel = PearlLogLevelDebug; //Trace; [PearlLogger get].printLevel = PearlLogLevelDebug; //Trace;
#else #else
[PearlLogger get].printLevel = [[MPiOSConfig get].traceMode boolValue]? PearlLogLevelDebug: PearlLogLevelInfo; [PearlLogger get].printLevel = [[MPiOSConfig get].traceMode boolValue]? PearlLogLevelDebug: PearlLogLevelInfo;
#endif #endif
} } );
} }
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
@@ -70,18 +69,15 @@
err( @"During Analytics Setup: %@", exception ); err( @"During Analytics Setup: %@", exception );
} }
@try { @try {
[[NSNotificationCenter defaultCenter] addObserverForName:MPCheckConfigNotification object:nil queue:[NSOperationQueue mainQueue] PearlAddNotificationObserver( MPCheckConfigNotification, nil, [NSOperationQueue mainQueue], ^(id self, NSNotification *note) {
usingBlock:^(NSNotification *note) { [self updateConfigKey:note.object];
[self updateConfigKey:note.object]; } );
}]; PearlAddNotificationObserver( kIASKAppSettingChanged, nil, nil, ^(id self, NSNotification *note) {
[[NSNotificationCenter defaultCenter] addObserverForName:kIASKAppSettingChanged object:nil queue:nil usingBlock: [[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:note.object];
^(NSNotification *note) { } );
[[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:note.object]; PearlAddNotificationObserver( NSUserDefaultsDidChangeNotification, nil, nil, ^(id self, NSNotification *note) {
}]; [[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserverForName:NSUserDefaultsDidChangeNotification object:nil queue:nil usingBlock: } );
^(NSNotification *note) {
[[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:nil];
}];
#ifdef ADHOC #ifdef ADHOC
[PearlAlert showAlertWithTitle:@"Welcome, tester!" message: [PearlAlert showAlertWithTitle:@"Welcome, tester!" message:
@@ -107,28 +103,27 @@
@try { @try {
inf( @"Started up with device identifier: %@", [PearlKeyChain deviceIdentifier] ); inf( @"Started up with device identifier: %@", [PearlKeyChain deviceIdentifier] );
[[NSNotificationCenter defaultCenter] addObserverForName:MPFoundInconsistenciesNotification object:nil queue:nil usingBlock: PearlAddNotificationObserver( MPFoundInconsistenciesNotification, nil, nil, ^(id self, NSNotification *note) {
^(NSNotification *note) { switch ((MPFixableResult)[note.userInfo[MPInconsistenciesFixResultUserKey] unsignedIntegerValue]) {
switch ((MPFixableResult)[note.userInfo[MPInconsistenciesFixResultUserKey] unsignedIntegerValue]) {
case MPFixableResultNoProblems: case MPFixableResultNoProblems:
break; break;
case MPFixableResultProblemsFixed: case MPFixableResultProblemsFixed:
[PearlAlert showAlertWithTitle:@"Inconsistencies Fixed" message: [PearlAlert showAlertWithTitle:@"Inconsistencies Fixed" message:
@"Some inconsistencies were detected in your sites.\n" @"Some inconsistencies were detected in your sites.\n"
@"All issues were fixed." @"All issues were fixed."
viewStyle:UIAlertViewStyleDefault initAlert:nil viewStyle:UIAlertViewStyleDefault initAlert:nil
tappedButtonBlock:nil cancelTitle:[PearlStrings get].commonButtonOkay otherTitles:nil]; tappedButtonBlock:nil cancelTitle:[PearlStrings get].commonButtonOkay otherTitles:nil];
break; break;
case MPFixableResultProblemsNotFixed: case MPFixableResultProblemsNotFixed:
[PearlAlert showAlertWithTitle:@"Inconsistencies Found" message: [PearlAlert showAlertWithTitle:@"Inconsistencies Found" message:
@"Some inconsistencies were detected in your sites.\n" @"Some inconsistencies were detected in your sites.\n"
@"Not all issues could be fixed. Try signing in to each user or checking the logs." @"Not all issues could be fixed. Try signing in to each user or checking the logs."
viewStyle:UIAlertViewStyleDefault initAlert:nil viewStyle:UIAlertViewStyleDefault initAlert:nil
tappedButtonBlock:nil cancelTitle:[PearlStrings get].commonButtonOkay otherTitles:nil]; tappedButtonBlock:nil cancelTitle:[PearlStrings get].commonButtonOkay otherTitles:nil];
break; break;
} }
}]; } );
PearlMainQueue( ^{ PearlMainQueue( ^{
if ([[MPiOSConfig get].showSetup boolValue]) if ([[MPiOSConfig get].showSetup boolValue])
@@ -141,7 +136,7 @@
@"jailbroken" : PearlStringB( [PearlDeviceUtils isJailbroken] ), @"jailbroken" : PearlStringB( [PearlDeviceUtils isJailbroken] ),
@"platform" : [PearlDeviceUtils platform], @"platform" : [PearlDeviceUtils platform],
#ifdef APPSTORE #ifdef APPSTORE
@"legal" : PearlStringB([PearlDeviceUtils isAppEncrypted]), @"legal" : PearlStringB([PearlDeviceUtils isAppEncrypted]),
#else #else
@"legal" : @"YES", @"legal" : @"YES",
#endif #endif
@@ -168,88 +163,106 @@
NSData *importedSitesData = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url] NSData *importedSitesData = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url]
returningResponse:&response error:&error]; returningResponse:&response error:&error];
if (error) if (error)
err( @"While reading imported sites from %@: %@", url, error ); err( @"While reading imported sites from %@: %@", url, [error fullDescription] );
if (!importedSitesData) if (!importedSitesData) {
[PearlAlert showError:strf( @"Master Password couldn't read the import sites.\n\n%@", [error localizedDescription]?: error )];
return; return;
PearlOverlay *activityOverlay = [PearlOverlay showProgressOverlayWithTitle:@"Importing"];
NSString *importedSitesString = [[NSString alloc] initWithData:importedSitesData encoding:NSUTF8StringEncoding];
MPImportResult result = [self importSites:importedSitesString askImportPassword:^NSString *(NSString *userName) {
__block NSString *masterPassword = nil;
dispatch_group_t importPasswordGroup = dispatch_group_create();
dispatch_group_enter( importPasswordGroup );
dispatch_async( dispatch_get_main_queue(), ^{
[PearlAlert showAlertWithTitle:@"Import File's Master Password"
message:strf( @"%@'s export was done using a different master password.\n"
@"Enter that master password to unlock the exported data.", userName )
viewStyle:UIAlertViewStyleSecureTextInput
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
@try {
if (buttonIndex_ == [alert_ cancelButtonIndex])
return;
masterPassword = [alert_ textFieldAtIndex:0].text;
}
@finally {
dispatch_group_leave( importPasswordGroup );
}
}
cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Unlock Import", nil];
} );
dispatch_group_wait( importPasswordGroup, DISPATCH_TIME_FOREVER );
return masterPassword;
} askUserPassword:^NSString *(NSString *userName, NSUInteger importCount, NSUInteger deleteCount) {
__block NSString *masterPassword = nil;
dispatch_group_t userPasswordGroup = dispatch_group_create();
dispatch_group_enter( userPasswordGroup );
dispatch_async( dispatch_get_main_queue(), ^{
[PearlAlert showAlertWithTitle:strf( @"Master Password for\n%@", userName )
message:strf( @"Imports %lu sites, overwriting %lu.",
(unsigned long)importCount, (unsigned long)deleteCount )
viewStyle:UIAlertViewStyleSecureTextInput
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
@try {
if (buttonIndex_ == [alert_ cancelButtonIndex])
return;
masterPassword = [alert_ textFieldAtIndex:0].text;
}
@finally {
dispatch_group_leave( userPasswordGroup );
}
}
cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Import", nil];
} );
dispatch_group_wait( userPasswordGroup, DISPATCH_TIME_FOREVER );
return masterPassword;
}];
switch (result) {
case MPImportResultSuccess:
case MPImportResultCancelled:
break;
case MPImportResultInternalError:
[PearlAlert showError:@"Import failed because of an internal error."];
break;
case MPImportResultMalformedInput:
[PearlAlert showError:@"The import doesn't look like a Master Password export."];
break;
case MPImportResultInvalidPassword:
[PearlAlert showError:@"Incorrect master password for the import sites."];
break;
} }
[activityOverlay cancelOverlayAnimated:YES]; NSString *importedSitesString = [[NSString alloc] initWithData:importedSitesData encoding:NSUTF8StringEncoding];
if (!importedSitesString) {
[PearlAlert showError:@"Master Password couldn't understand the import file."];
return;
}
[self importSites:importedSitesString];
} ); } );
return YES; return YES;
} }
- (void)importSites:(NSString *)importedSitesString {
if ([NSThread isMainThread]) {
PearlNotMainQueue( ^{
[self importSites:importedSitesString];
} );
return;
}
PearlOverlay *activityOverlay = [PearlOverlay showProgressOverlayWithTitle:@"Importing"];
MPImportResult result = [self importSites:importedSitesString askImportPassword:^NSString *(NSString *userName) {
__block NSString *masterPassword = nil;
dispatch_group_t importPasswordGroup = dispatch_group_create();
dispatch_group_enter( importPasswordGroup );
dispatch_async( dispatch_get_main_queue(), ^{
[PearlAlert showAlertWithTitle:@"Import File's Master Password"
message:strf( @"%@'s export was done using a different master password.\n"
@"Enter that master password to unlock the exported data.", userName )
viewStyle:UIAlertViewStyleSecureTextInput
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
@try {
if (buttonIndex_ == [alert_ cancelButtonIndex])
return;
masterPassword = [alert_ textFieldAtIndex:0].text;
}
@finally {
dispatch_group_leave( importPasswordGroup );
}
}
cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Unlock Import", nil];
} );
dispatch_group_wait( importPasswordGroup, DISPATCH_TIME_FOREVER );
return masterPassword;
} askUserPassword:^NSString *(NSString *userName, NSUInteger importCount, NSUInteger deleteCount) {
__block NSString *masterPassword = nil;
dispatch_group_t userPasswordGroup = dispatch_group_create();
dispatch_group_enter( userPasswordGroup );
dispatch_async( dispatch_get_main_queue(), ^{
[PearlAlert showAlertWithTitle:strf( @"Master Password for\n%@", userName )
message:strf( @"Imports %lu sites, overwriting %lu.",
(unsigned long)importCount, (unsigned long)deleteCount )
viewStyle:UIAlertViewStyleSecureTextInput
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
@try {
if (buttonIndex_ == [alert_ cancelButtonIndex])
return;
masterPassword = [alert_ textFieldAtIndex:0].text;
}
@finally {
dispatch_group_leave( userPasswordGroup );
}
}
cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Import", nil];
} );
dispatch_group_wait( userPasswordGroup, DISPATCH_TIME_FOREVER );
return masterPassword;
}];
switch (result) {
case MPImportResultSuccess:
case MPImportResultCancelled:
break;
case MPImportResultInternalError:
[PearlAlert showError:@"Import failed because of an internal error."];
break;
case MPImportResultMalformedInput:
[PearlAlert showError:@"The import doesn't look like a Master Password export."];
break;
case MPImportResultInvalidPassword:
[PearlAlert showError:@"Incorrect master password for the import sites."];
break;
}
[activityOverlay cancelOverlayAnimated:YES];
}
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application { - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
inf( @"Received memory warning." ); inf( @"Received memory warning." );
@@ -257,13 +270,13 @@
[super applicationDidReceiveMemoryWarning:application]; [super applicationDidReceiveMemoryWarning:application];
} }
- (void)applicationWillResignActive:(UIApplication *)application { - (void)applicationDidEnterBackground:(UIApplication *)application {
inf( @"Will deactivate" ); inf( @"Will background" );
if (![[MPiOSConfig get].rememberLogin boolValue]) if (![[MPiOSConfig get].rememberLogin boolValue])
[self signOutAnimated:NO]; [self signOutAnimated:NO];
[super applicationWillResignActive:application]; [super applicationDidEnterBackground:application];
} }
- (void)applicationDidBecomeActive:(UIApplication *)application { - (void)applicationDidBecomeActive:(UIApplication *)application {
@@ -271,6 +284,20 @@
inf( @"Re-activated" ); inf( @"Re-activated" );
[[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:nil]; [[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:nil];
NSString *importHeader = @"# Master Password site export";
NSString *importedSitesString = [UIPasteboard generalPasteboard].string;
if ([[importedSitesString substringToIndex:[importHeader length]] isEqualToString:importHeader])
[PearlAlert showAlertWithTitle:@"Import Sites?" message:
@"We've detected Master Password import sites on your pasteboard, would you like to import them?"
viewStyle:UIAlertViewStyleDefault initAlert:nil
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
if (buttonIndex == [alert cancelButtonIndex])
return;
[self importSites:importedSitesString];
[UIPasteboard generalPasteboard].string = nil;
} cancelTitle:@"No" otherTitles:@"Import Sites", nil];
[super applicationDidBecomeActive:application]; [super applicationDidBecomeActive:application];
} }
@@ -339,6 +366,26 @@
showComposerForVC:viewController]; showComposerForVC:viewController];
} }
- (void)handleCoordinatorError:(NSError *)error {
static dispatch_once_t once = 0;
dispatch_once( &once, ^{
[PearlAlert showAlertWithTitle:@"Failed To Load Sites" message:
@"Master Password was unable to open your sites history.\n"
@"This may be due to corruption. You can either reset Master Password and "
@"recreate your user, or E-Mail us your logs and leave your corrupt store as-is for now."
viewStyle:UIAlertViewStyleDefault initAlert:nil
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
if (buttonIndex == [alert cancelButtonIndex])
return;
if (buttonIndex == [alert firstOtherButtonIndex])
[self openFeedbackWithLogs:YES forVC:nil];
if (buttonIndex == [alert firstOtherButtonIndex] + 1)
[self deleteAndResetStore];
} cancelTitle:@"Ignore" otherTitles:@"E-Mail Logs", @"Reset", nil];
} );
}
- (void)showExportForVC:(UIViewController *)viewController { - (void)showExportForVC:(UIViewController *)viewController {
[PearlAlert showAlertWithTitle:@"Exporting Your Sites" [PearlAlert showAlertWithTitle:@"Exporting Your Sites"
@@ -409,12 +456,38 @@
NSDateFormatter *exportDateFormatter = [NSDateFormatter new]; NSDateFormatter *exportDateFormatter = [NSDateFormatter new];
[exportDateFormatter setDateFormat:@"yyyy'-'MM'-'dd"]; [exportDateFormatter setDateFormat:@"yyyy'-'MM'-'dd"];
[PearlEMail sendEMailTo:nil fromVC:viewController subject:@"Master Password Export" body:message NSString *exportFileName = strf( @"%@ (%@).mpsites",
attachments:[[PearlEMailAttachment alloc] initWithContent:[exportedSites dataUsingEncoding:NSUTF8StringEncoding] [self activeUserForMainThread].name, [exportDateFormatter stringFromDate:[NSDate date]] );
mimeType:@"text/plain" fileName: [PearlSheet showSheetWithTitle:@"Export Destination" viewStyle:UIActionSheetStyleBlackTranslucent initSheet:nil
strf( @"%@ (%@).mpsites", [self activeUserForMainThread].name, tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) {
[exportDateFormatter stringFromDate:[NSDate date]] )], if (buttonIndex == [sheet cancelButtonIndex])
nil]; return;
if (buttonIndex == [sheet firstOtherButtonIndex]) {
[PearlEMail sendEMailTo:nil fromVC:viewController subject:@"Master Password Export" body:message
attachments:[[PearlEMailAttachment alloc]
initWithContent:[exportedSites dataUsingEncoding:NSUTF8StringEncoding]
mimeType:@"text/plain" fileName:exportFileName],
nil];
return;
}
NSURL *applicationSupportURL = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory
inDomains:NSUserDomainMask] lastObject];
NSURL *exportURL = [[applicationSupportURL
URLByAppendingPathComponent:[NSBundle mainBundle].bundleIdentifier isDirectory:YES]
URLByAppendingPathComponent:exportFileName isDirectory:NO];
NSError *error = nil;
if (![[exportedSites dataUsingEncoding:NSUTF8StringEncoding]
writeToURL:exportURL options:NSDataWritingFileProtectionComplete error:&error])
err( @"Failed to write export data to URL %@: %@", exportURL, [error fullDescription] );
else {
self.interactionController = [UIDocumentInteractionController interactionControllerWithURL:exportURL];
self.interactionController.UTI = @"com.lyndir.masterpassword.sites";
self.interactionController.delegate = self;
[self.interactionController presentOpenInMenuFromRect:CGRectZero inView:viewController.view animated:YES];
}
} cancelTitle:@"Cancel" destructiveTitle:nil otherTitles:@"Send As E-Mail", @"Share / Airdrop", nil];
} }
- (void)changeMasterPasswordFor:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc didResetBlock:(void ( ^ )(void))didReset { - (void)changeMasterPasswordFor:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc didResetBlock:(void ( ^ )(void))didReset {
@@ -446,6 +519,13 @@
otherTitles:[PearlStrings get].commonButtonContinue, nil]; otherTitles:[PearlStrings get].commonButtonContinue, nil];
} }
#pragma mark - UIDocumentInteractionControllerDelegate
- (void)documentInteractionController:(UIDocumentInteractionController *)controller didEndSendingToApplication:(NSString *)application {
// self.interactionController = nil;
}
#pragma mark - PearlConfigDelegate #pragma mark - PearlConfigDelegate
- (void)didUpdateConfigForKey:(SEL)configKey fromValue:(id)value { - (void)didUpdateConfigForKey:(SEL)configKey fromValue:(id)value {

View File

@@ -18,5 +18,8 @@
@property(nonatomic, retain) NSNumber *loginNameTipShown; @property(nonatomic, retain) NSNumber *loginNameTipShown;
@property(nonatomic, retain) NSNumber *traceMode; @property(nonatomic, retain) NSNumber *traceMode;
@property(nonatomic, retain) NSNumber *dictationSearch; @property(nonatomic, retain) NSNumber *dictationSearch;
@property(nonatomic, retain) NSNumber *developmentFuel;
@property(nonatomic, retain) NSNumber *developmentFuelConsumption;
@property(nonatomic, retain) NSDate *developmentFuelChecked;
@end @end

View File

@@ -9,6 +9,7 @@
@implementation MPiOSConfig @implementation MPiOSConfig
@dynamic helpHidden, siteInfoHidden, showSetup, actionsTipShown, typeTipShown, loginNameTipShown, traceMode, dictationSearch; @dynamic helpHidden, siteInfoHidden, showSetup, actionsTipShown, typeTipShown, loginNameTipShown, traceMode, dictationSearch;
@dynamic developmentFuel, developmentFuelConsumption, developmentFuelChecked;
- (id)init { - (id)init {
@@ -16,15 +17,15 @@
return self; return self;
[self.defaults registerDefaults:@{ [self.defaults registerDefaults:@{
NSStringFromSelector( @selector(helpHidden) ) : @NO, NSStringFromSelector( @selector( helpHidden ) ) : @NO,
NSStringFromSelector( @selector(siteInfoHidden) ) : @YES, NSStringFromSelector( @selector( siteInfoHidden ) ) : @YES,
NSStringFromSelector( @selector(showSetup) ) : @YES, NSStringFromSelector( @selector( showSetup ) ) : @YES,
NSStringFromSelector( @selector(iTunesID) ) : @"510296984", NSStringFromSelector( @selector( iTunesID ) ) : @"510296984",
NSStringFromSelector( @selector(actionsTipShown) ) : @(!self.firstRun), NSStringFromSelector( @selector( actionsTipShown ) ) : @(!self.firstRun),
NSStringFromSelector( @selector(typeTipShown) ) : @(!self.firstRun), NSStringFromSelector( @selector( typeTipShown ) ) : @(!self.firstRun),
NSStringFromSelector( @selector(loginNameTipShown) ) : @NO, NSStringFromSelector( @selector( loginNameTipShown ) ) : @NO,
NSStringFromSelector( @selector(traceMode) ) : @NO, NSStringFromSelector( @selector( traceMode ) ) : @NO,
NSStringFromSelector( @selector(dictationSearch) ) : @NO NSStringFromSelector( @selector( dictationSearch ) ) : @NO,
}]; }];
return self; return self;

View File

@@ -21,7 +21,7 @@
<string>Owner</string> <string>Owner</string>
<key>LSItemContentTypes</key> <key>LSItemContentTypes</key>
<array> <array>
<string>com.lyndir.lhunath.MasterPassword.sites</string> <string>com.lyndir.masterpassword.sites</string>
</array> </array>
</dict> </dict>
</array> </array>
@@ -39,19 +39,6 @@
<string>[auto]</string> <string>[auto]</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>com.lyndir.lhunath.MasterPassword</string>
<key>CFBundleURLSchemes</key>
<array>
<string>com.lyndir.lhunath.MasterPassword</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>[auto]</string> <string>[auto]</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
@@ -109,10 +96,14 @@
<key>UTExportedTypeDeclarations</key> <key>UTExportedTypeDeclarations</key>
<array> <array>
<dict> <dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.utf8-plain-text</string>
</array>
<key>UTTypeDescription</key> <key>UTTypeDescription</key>
<string>Master Password sites</string> <string>Master Password sites</string>
<key>UTTypeIdentifier</key> <key>UTTypeIdentifier</key>
<string>com.lyndir.lhunath.MasterPassword.sites</string> <string>com.lyndir.masterpassword.sites</string>
<key>UTTypeSize320IconFile</key> <key>UTTypeSize320IconFile</key>
<string></string> <string></string>
<key>UTTypeSize64IconFile</key> <key>UTTypeSize64IconFile</key>

View File

@@ -7,14 +7,15 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
93D390C1B93F9D3AE37DD0A5 /* MPAnswersViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39C426E03358384018E85 /* MPAnswersViewController.m */; };
93D391ECBD9BD2C64115B5DD /* PearlSizedTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39156E806BB78E04F78B9 /* PearlSizedTextView.m */; }; 93D391ECBD9BD2C64115B5DD /* PearlSizedTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39156E806BB78E04F78B9 /* PearlSizedTextView.m */; };
93D391ED37C9F687FA51EAA1 /* MPEmergencySegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3937712BF1B67623E5764 /* MPEmergencySegue.m */; };
93D3922A53E41A54832E90D9 /* PearlOverlay.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D390FADEB325D8D54A957D /* PearlOverlay.m */; }; 93D3922A53E41A54832E90D9 /* PearlOverlay.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D390FADEB325D8D54A957D /* PearlOverlay.m */; };
93D39262A8A97DB748213309 /* PearlEMail.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D393BB973253D4BAAC84AA /* PearlEMail.m */; }; 93D39262A8A97DB748213309 /* PearlEMail.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D393BB973253D4BAAC84AA /* PearlEMail.m */; };
93D392A8777DC30C11361647 /* UITextView+PearlAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39AA10CD00D05937671B1 /* UITextView+PearlAttributes.h */; }; 93D392A8777DC30C11361647 /* UITextView+PearlAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39AA10CD00D05937671B1 /* UITextView+PearlAttributes.h */; };
93D392EC39DA43C46C692C12 /* NSDictionary+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */; }; 93D392EC39DA43C46C692C12 /* NSDictionary+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */; };
93D3932889B6B4206E66A6D6 /* PearlEMail.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39F7C9F47BF6387FBC5C3 /* PearlEMail.h */; }; 93D3932889B6B4206E66A6D6 /* PearlEMail.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39F7C9F47BF6387FBC5C3 /* PearlEMail.h */; };
93D39392DEDA376F93C6C718 /* MPCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39BAA71DE51B4D8A1286C /* MPCell.m */; }; 93D39392DEDA376F93C6C718 /* MPCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39BAA71DE51B4D8A1286C /* MPCell.m */; };
93D3939661CE37180AF7CD6A /* MPStoreViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3957D76F71A652716EECC /* MPStoreViewController.m */; };
93D393DB5325820241BA90A7 /* PearlSizedTextView.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39A4759186F6D2D34AA6B /* PearlSizedTextView.h */; }; 93D393DB5325820241BA90A7 /* PearlSizedTextView.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39A4759186F6D2D34AA6B /* PearlSizedTextView.h */; };
93D394982CBD25D46692DD7C /* MPWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3990E0CD1B5CF9FBB2C07 /* MPWebViewController.m */; }; 93D394982CBD25D46692DD7C /* MPWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3990E0CD1B5CF9FBB2C07 /* MPWebViewController.m */; };
93D394B5036C882B33C71872 /* MPPasswordsSegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39E7A12CC352B2825AA66 /* MPPasswordsSegue.m */; }; 93D394B5036C882B33C71872 /* MPPasswordsSegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39E7A12CC352B2825AA66 /* MPPasswordsSegue.m */; };
@@ -33,14 +34,18 @@
93D399246DC90F50913A1287 /* UIResponder+PearlFirstResponder.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39A1DDFA09AE2E14D26DC /* UIResponder+PearlFirstResponder.m */; }; 93D399246DC90F50913A1287 /* UIResponder+PearlFirstResponder.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39A1DDFA09AE2E14D26DC /* UIResponder+PearlFirstResponder.m */; };
93D3992FA1546E01F498F665 /* PearlNavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D398567FD02DB2647B8CF3 /* PearlNavigationController.h */; }; 93D3992FA1546E01F498F665 /* PearlNavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D398567FD02DB2647B8CF3 /* PearlNavigationController.h */; };
93D399433EA75E50656040CB /* Twitter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93D394077F8FAB8167647187 /* Twitter.framework */; }; 93D399433EA75E50656040CB /* Twitter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93D394077F8FAB8167647187 /* Twitter.framework */; };
93D399D7E08A142776A74CB8 /* MPOverlayViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D395105935859D71679931 /* MPOverlayViewController.m */; };
93D39A27F2506C6FEEF9C588 /* MPAlgorithmV2.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399A8E3181B442D347CD7 /* MPAlgorithmV2.m */; };
93D39A53D76CA70786423458 /* UICollectionView+PearlReloadFromArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadFromArray.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 */; }; 93D39A5FF670957C0AF8298D /* MPPasswordCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39DEA995041A13DC9CAF7 /* MPPasswordCell.m */; };
93D39A8EA1C49CE43B63F47B /* PearlUICollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D8A953779B35403AF6E /* PearlUICollectionView.m */; }; 93D39A8EA1C49CE43B63F47B /* PearlUICollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D8A953779B35403AF6E /* PearlUICollectionView.m */; };
93D39B429C67A62E29DC02DA /* MPRootSegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399493FEDDE74DD1A0C15 /* MPRootSegue.m */; };
93D39B76DD5AB108BA8928E8 /* UIScrollView+PearlAdjustInsets.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39DE2CB351D4E3789462B /* UIScrollView+PearlAdjustInsets.h */; }; 93D39B76DD5AB108BA8928E8 /* UIScrollView+PearlAdjustInsets.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39DE2CB351D4E3789462B /* UIScrollView+PearlAdjustInsets.h */; };
93D39B842AB9A5D072810D76 /* NSError+PearlFullDescription.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D398C95847261903D781D3 /* NSError+PearlFullDescription.h */; }; 93D39B842AB9A5D072810D76 /* NSError+PearlFullDescription.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D398C95847261903D781D3 /* NSError+PearlFullDescription.h */; };
93D39B8F90F58A5D158DDBA3 /* MPPasswordsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3924EE15017F8A12CB436 /* MPPasswordsViewController.m */; }; 93D39B8F90F58A5D158DDBA3 /* MPPasswordsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3924EE15017F8A12CB436 /* MPPasswordsViewController.m */; };
93D39BA1EA3CAAC8A220B4A6 /* MPAppSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3916C1D8F1427DFBDEBCA /* MPAppSettingsViewController.m */; }; 93D39BA1EA3CAAC8A220B4A6 /* MPAppSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3916C1D8F1427DFBDEBCA /* MPAppSettingsViewController.m */; };
93D39C34FE35830EF5BE1D2A /* NSArray+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D396D04E57792A54D437AC /* NSArray+Indexing.h */; }; 93D39C34FE35830EF5BE1D2A /* NSArray+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D396D04E57792A54D437AC /* NSArray+Indexing.h */; };
93D39D38356F59DBEF934D70 /* MPAppDelegate_InApp.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D394C78C7B879C9AD9152C /* MPAppDelegate_InApp.m */; };
93D39D47FC623E91FC39D20C /* UICollectionView+PearlReloadFromArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3908DF8EABBD952065DC0 /* UICollectionView+PearlReloadFromArray.m */; }; 93D39D47FC623E91FC39D20C /* UICollectionView+PearlReloadFromArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3908DF8EABBD952065DC0 /* UICollectionView+PearlReloadFromArray.m */; };
93D39D596A2E376D6F6F5DA1 /* MPCombinedViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D393310223DDB35218467A /* MPCombinedViewController.m */; }; 93D39D596A2E376D6F6F5DA1 /* MPCombinedViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D393310223DDB35218467A /* MPCombinedViewController.m */; };
93D39D8F78978196D6ABDEDE /* MPNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39CC01630D0421205C4C4 /* MPNavigationController.m */; }; 93D39D8F78978196D6ABDEDE /* MPNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39CC01630D0421205C4C4 /* MPNavigationController.m */; };
@@ -90,6 +95,11 @@
DA25C601197DBF260046CDCF /* icon_trash@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38511711E29700CF925C /* icon_trash@2x.png */; }; DA25C601197DBF260046CDCF /* icon_trash@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38511711E29700CF925C /* icon_trash@2x.png */; };
DA25C6B41980D3C50046CDCF /* openssl in Headers */ = {isa = PBXBuildFile; fileRef = DA25C6B31980D3C10046CDCF /* openssl */; settings = {ATTRIBUTES = (Public, ); }; }; DA25C6B41980D3C50046CDCF /* openssl in Headers */ = {isa = PBXBuildFile; fileRef = DA25C6B31980D3C10046CDCF /* openssl */; settings = {ATTRIBUTES = (Public, ); }; };
DA25C6B61980D3DF0046CDCF /* scrypt in Headers */ = {isa = PBXBuildFile; fileRef = DA25C6B51980D3DD0046CDCF /* scrypt */; settings = {ATTRIBUTES = (Public, ); }; }; DA25C6B61980D3DF0046CDCF /* scrypt in Headers */ = {isa = PBXBuildFile; fileRef = DA25C6B51980D3DD0046CDCF /* scrypt */; settings = {ATTRIBUTES = (Public, ); }; };
DA29992F19C86F5700AF7DF1 /* thumb_generated_login@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA29992D19C86F5700AF7DF1 /* thumb_generated_login@2x.png */; };
DA29993019C86F5700AF7DF1 /* thumb_generated_login.png in Resources */ = {isa = PBXBuildFile; fileRef = DA29992E19C86F5700AF7DF1 /* thumb_generated_login.png */; };
DA29993219C9132F00AF7DF1 /* thumb_generated_login@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA29993119C9132F00AF7DF1 /* thumb_generated_login@3x.png */; };
DA29993319C9214600AF7DF1 /* icon_star-hollow.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD382A1711E29600CF925C /* icon_star-hollow.png */; };
DA29993419C9214600AF7DF1 /* icon_star-hollow@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD382B1711E29600CF925C /* icon_star-hollow@2x.png */; };
DA2CA4DD18D28859007798F8 /* NSArray+Pearl.m in Sources */ = {isa = PBXBuildFile; fileRef = DA2CA4D918D28859007798F8 /* NSArray+Pearl.m */; }; DA2CA4DD18D28859007798F8 /* NSArray+Pearl.m in Sources */ = {isa = PBXBuildFile; fileRef = DA2CA4D918D28859007798F8 /* NSArray+Pearl.m */; };
DA2CA4DE18D28859007798F8 /* NSArray+Pearl.h in Headers */ = {isa = PBXBuildFile; fileRef = DA2CA4DA18D28859007798F8 /* NSArray+Pearl.h */; }; DA2CA4DE18D28859007798F8 /* NSArray+Pearl.h in Headers */ = {isa = PBXBuildFile; fileRef = DA2CA4DA18D28859007798F8 /* NSArray+Pearl.h */; };
DA2CA4DF18D28859007798F8 /* NSTimer+PearlBlock.m in Sources */ = {isa = PBXBuildFile; fileRef = DA2CA4DB18D28859007798F8 /* NSTimer+PearlBlock.m */; }; DA2CA4DF18D28859007798F8 /* NSTimer+PearlBlock.m in Sources */ = {isa = PBXBuildFile; fileRef = DA2CA4DB18D28859007798F8 /* NSTimer+PearlBlock.m */; };
@@ -103,6 +113,29 @@
DA30E9D415722EF400A68B4C /* Pearl-UIKit.m in Sources */ = {isa = PBXBuildFile; fileRef = DA30E9D315722EF400A68B4C /* Pearl-UIKit.m */; }; DA30E9D415722EF400A68B4C /* Pearl-UIKit.m in Sources */ = {isa = PBXBuildFile; fileRef = DA30E9D315722EF400A68B4C /* Pearl-UIKit.m */; };
DA30E9D715723E6900A68B4C /* PearlLazy.h in Headers */ = {isa = PBXBuildFile; fileRef = DA30E9D515723E6900A68B4C /* PearlLazy.h */; }; DA30E9D715723E6900A68B4C /* PearlLazy.h in Headers */ = {isa = PBXBuildFile; fileRef = DA30E9D515723E6900A68B4C /* PearlLazy.h */; };
DA30E9D815723E6900A68B4C /* PearlLazy.m in Sources */ = {isa = PBXBuildFile; fileRef = DA30E9D615723E6900A68B4C /* PearlLazy.m */; }; DA30E9D815723E6900A68B4C /* PearlLazy.m in Sources */ = {isa = PBXBuildFile; fileRef = DA30E9D615723E6900A68B4C /* PearlLazy.m */; };
DA32CFF019CF1C8F004F3F0E /* MPUserEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA32CFE619CF1C8F004F3F0E /* MPUserEntity.m */; };
DA32CFF119CF1C8F004F3F0E /* MPStoredSiteEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA32CFE819CF1C8F004F3F0E /* MPStoredSiteEntity.m */; };
DA32CFF319CF1C8F004F3F0E /* MPSiteEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA32CFEC19CF1C8F004F3F0E /* MPSiteEntity.m */; };
DA32CFF419CF1C8F004F3F0E /* MPGeneratedSiteEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA32CFEE19CF1C8F004F3F0E /* MPGeneratedSiteEntity.m */; };
DA32D00819CF4735004F3F0E /* MasterPassword.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DA32D00119CF4735004F3F0E /* MasterPassword.xcdatamodeld */; };
DA32D00919CF5C55004F3F0E /* icon_question.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37FE1711E29600CF925C /* icon_question.png */; };
DA32D00A19CF5C55004F3F0E /* icon_question@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37FF1711E29600CF925C /* icon_question@2x.png */; };
DA32D01A19D046E1004F3F0E /* PearlFixedTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = DA32D01819D046E1004F3F0E /* PearlFixedTableView.m */; };
DA32D01B19D046E1004F3F0E /* PearlFixedTableView.h in Headers */ = {isa = PBXBuildFile; fileRef = DA32D01919D046E1004F3F0E /* PearlFixedTableView.h */; };
DA32D03C19D111EB004F3F0E /* NSManagedObjectModel+KCOrderedAccessorFix.m in Sources */ = {isa = PBXBuildFile; fileRef = DA32D03A19D111EB004F3F0E /* NSManagedObjectModel+KCOrderedAccessorFix.m */; };
DA32D03E19D11293004F3F0E /* libKCOrderedAccessorFix.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA32D02019D111C6004F3F0E /* libKCOrderedAccessorFix.a */; };
DA32D04219D27093004F3F0E /* thumb_generated_answers@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA32D03F19D27093004F3F0E /* thumb_generated_answers@3x.png */; };
DA32D04319D27093004F3F0E /* thumb_generated_answers@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA32D04019D27093004F3F0E /* thumb_generated_answers@2x.png */; };
DA32D04419D27093004F3F0E /* thumb_generated_answers.png in Resources */ = {isa = PBXBuildFile; fileRef = DA32D04119D27093004F3F0E /* thumb_generated_answers.png */; };
DA32D04819D2F417004F3F0E /* thumb_fuel@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA32D04519D2F417004F3F0E /* thumb_fuel@3x.png */; };
DA32D04919D2F417004F3F0E /* thumb_fuel@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA32D04619D2F417004F3F0E /* thumb_fuel@2x.png */; };
DA32D04A19D2F417004F3F0E /* thumb_fuel.png in Resources */ = {isa = PBXBuildFile; fileRef = DA32D04719D2F417004F3F0E /* thumb_fuel.png */; };
DA32D04E19D2F59B004F3F0E /* meter_fuel@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA32D04B19D2F59B004F3F0E /* meter_fuel@3x.png */; };
DA32D04F19D2F59B004F3F0E /* meter_fuel@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA32D04C19D2F59B004F3F0E /* meter_fuel@2x.png */; };
DA32D05019D2F59B004F3F0E /* meter_fuel.png in Resources */ = {isa = PBXBuildFile; fileRef = DA32D04D19D2F59B004F3F0E /* meter_fuel.png */; };
DA32D05119D3D107004F3F0E /* icon_meter.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37BA1711E29600CF925C /* icon_meter.png */; };
DA32D05219D3D107004F3F0E /* icon_meter@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37BB1711E29600CF925C /* icon_meter@2x.png */; };
DA32D05519D741DC004F3F0E /* MPSiteQuestionEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA32D05419D741DC004F3F0E /* MPSiteQuestionEntity.m */; };
DA3509FE15F101A500C14A8E /* PearlQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = DA3509FC15F101A500C14A8E /* PearlQueue.h */; }; DA3509FE15F101A500C14A8E /* PearlQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = DA3509FC15F101A500C14A8E /* PearlQueue.h */; };
DA3509FF15F101A500C14A8E /* PearlQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = DA3509FD15F101A500C14A8E /* PearlQueue.m */; }; DA3509FF15F101A500C14A8E /* PearlQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = DA3509FD15F101A500C14A8E /* PearlQueue.m */; };
DA38D6A318CCB5BF009AEB3E /* Storyboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DA38D6A218CCB5BF009AEB3E /* Storyboard.storyboard */; }; DA38D6A318CCB5BF009AEB3E /* Storyboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DA38D6A218CCB5BF009AEB3E /* Storyboard.storyboard */; };
@@ -215,7 +248,6 @@
DABD3ABF1711E29800CF925C /* icon_plus@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37F91711E29600CF925C /* icon_plus@2x.png */; }; DABD3ABF1711E29800CF925C /* icon_plus@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37F91711E29600CF925C /* icon_plus@2x.png */; };
DABD3B1C1711E29800CF925C /* icon_up.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38561711E29700CF925C /* icon_up.png */; }; DABD3B1C1711E29800CF925C /* icon_up.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38561711E29700CF925C /* icon_up.png */; };
DABD3B1D1711E29800CF925C /* icon_up@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38571711E29700CF925C /* icon_up@2x.png */; }; DABD3B1D1711E29800CF925C /* icon_up@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38571711E29700CF925C /* icon_up@2x.png */; };
DABD3B8A1711E29800CF925C /* help.html in Resources */ = {isa = PBXBuildFile; fileRef = DABD38C61711E29700CF925C /* help.html */; };
DABD3B8D1711E29800CF925C /* keypad.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38C91711E29700CF925C /* keypad.png */; }; DABD3B8D1711E29800CF925C /* keypad.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38C91711E29700CF925C /* keypad.png */; };
DABD3B8E1711E29800CF925C /* logo-bare.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38CA1711E29700CF925C /* logo-bare.png */; }; DABD3B8E1711E29800CF925C /* logo-bare.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38CA1711E29700CF925C /* logo-bare.png */; };
DABD3B8F1711E29800CF925C /* menu-icon.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38CB1711E29700CF925C /* menu-icon.png */; }; DABD3B8F1711E29800CF925C /* menu-icon.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38CB1711E29700CF925C /* menu-icon.png */; };
@@ -233,7 +265,6 @@
DABD3C031711E2DC00CF925C /* MPConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BAD1711E2DC00CF925C /* MPConfig.m */; }; DABD3C031711E2DC00CF925C /* MPConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BAD1711E2DC00CF925C /* MPConfig.m */; };
DABD3C071711E2DC00CF925C /* MPEntities.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BB51711E2DC00CF925C /* MPEntities.m */; }; DABD3C071711E2DC00CF925C /* MPEntities.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BB51711E2DC00CF925C /* MPEntities.m */; };
DABD3C081711E2DC00CF925C /* MPKey.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BB71711E2DC00CF925C /* MPKey.m */; }; DABD3C081711E2DC00CF925C /* MPKey.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BB71711E2DC00CF925C /* MPKey.m */; };
DABD3C141711E2DC00CF925C /* MasterPassword.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BD01711E2DC00CF925C /* MasterPassword.xcdatamodeld */; };
DABD3C151711E2DC00CF925C /* MPiOSAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BD91711E2DC00CF925C /* MPiOSAppDelegate.m */; }; DABD3C151711E2DC00CF925C /* MPiOSAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BD91711E2DC00CF925C /* MPiOSAppDelegate.m */; };
DABD3C1C1711E2DC00CF925C /* MPGuideViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BE71711E2DC00CF925C /* MPGuideViewController.m */; }; DABD3C1C1711E2DC00CF925C /* MPGuideViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BE71711E2DC00CF925C /* MPGuideViewController.m */; };
DABD3C1E1711E2DC00CF925C /* MPPreferencesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BEB1711E2DC00CF925C /* MPPreferencesViewController.m */; }; DABD3C1E1711E2DC00CF925C /* MPPreferencesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BEB1711E2DC00CF925C /* MPPreferencesViewController.m */; };
@@ -268,11 +299,11 @@
DACE2F6D19BA6A2A0010F92E /* PearlMutableStaticTableViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = DACE2F6919BA6A2A0010F92E /* PearlMutableStaticTableViewController.h */; }; DACE2F6D19BA6A2A0010F92E /* PearlMutableStaticTableViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = DACE2F6919BA6A2A0010F92E /* PearlMutableStaticTableViewController.h */; };
DACE2F6E19BA6A2A0010F92E /* UIView+FontScale.h in Headers */ = {isa = PBXBuildFile; fileRef = DACE2F6A19BA6A2A0010F92E /* UIView+FontScale.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 */; }; 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 */; }; DAE1EF2217E942DE00BC0086 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DAE1EF2417E942DE00BC0086 /* Localizable.strings */; };
DAE2725919C93B80007C5262 /* libInAppSettingsKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAFC5655172C573B00CB5CC5 /* libInAppSettingsKit.a */; };
DAE2725A19C93B8E007C5262 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA70EC7F1811B13C00F65DB2 /* StoreKit.framework */; };
DAE2726319CE9CB3007C5262 /* UITableViewCell+PearlDeque.m in Sources */ = {isa = PBXBuildFile; fileRef = DAE2726119CE9CB3007C5262 /* UITableViewCell+PearlDeque.m */; };
DAE2726419CE9CB3007C5262 /* UITableViewCell+PearlDeque.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE2726219CE9CB3007C5262 /* UITableViewCell+PearlDeque.h */; };
DAEBC45314F6364500987BF6 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAEBC45214F6364500987BF6 /* QuartzCore.framework */; }; DAEBC45314F6364500987BF6 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAEBC45214F6364500987BF6 /* QuartzCore.framework */; };
DAEC85B518E3DD9A007FC0DF /* UIView+Touches.m in Sources */ = {isa = PBXBuildFile; fileRef = DAEC85B118E3DD9A007FC0DF /* UIView+Touches.m */; }; DAEC85B518E3DD9A007FC0DF /* UIView+Touches.m in Sources */ = {isa = PBXBuildFile; fileRef = DAEC85B118E3DD9A007FC0DF /* UIView+Touches.m */; };
DAEC85B618E3DD9A007FC0DF /* PearlUINavigationBar.m in Sources */ = {isa = PBXBuildFile; fileRef = DAEC85B218E3DD9A007FC0DF /* PearlUINavigationBar.m */; }; DAEC85B618E3DD9A007FC0DF /* PearlUINavigationBar.m in Sources */ = {isa = PBXBuildFile; fileRef = DAEC85B218E3DD9A007FC0DF /* PearlUINavigationBar.m */; };
@@ -297,7 +328,6 @@
DAFC568E172C57EC00CB5CC5 /* IASKSlider.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC567E172C57EC00CB5CC5 /* IASKSlider.m */; }; DAFC568E172C57EC00CB5CC5 /* IASKSlider.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC567E172C57EC00CB5CC5 /* IASKSlider.m */; };
DAFC568F172C57EC00CB5CC5 /* IASKSwitch.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC5680172C57EC00CB5CC5 /* IASKSwitch.m */; }; DAFC568F172C57EC00CB5CC5 /* IASKSwitch.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC5680172C57EC00CB5CC5 /* IASKSwitch.m */; };
DAFC5690172C57EC00CB5CC5 /* IASKTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC5682172C57EC00CB5CC5 /* IASKTextField.m */; }; DAFC5690172C57EC00CB5CC5 /* IASKTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC5682172C57EC00CB5CC5 /* IASKTextField.m */; };
DAFC5691172C582A00CB5CC5 /* libInAppSettingsKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAFC5655172C573B00CB5CC5 /* libInAppSettingsKit.a */; };
DAFE4A1315039824003ABA7C /* NSObject+PearlExport.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE45D815039823003ABA7C /* NSObject+PearlExport.h */; }; DAFE4A1315039824003ABA7C /* NSObject+PearlExport.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE45D815039823003ABA7C /* NSObject+PearlExport.h */; };
DAFE4A1415039824003ABA7C /* NSObject+PearlExport.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFE45D915039823003ABA7C /* NSObject+PearlExport.m */; }; DAFE4A1415039824003ABA7C /* NSObject+PearlExport.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFE45D915039823003ABA7C /* NSObject+PearlExport.m */; };
DAFE4A1515039824003ABA7C /* NSString+PearlNSArrayFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE45DA15039823003ABA7C /* NSString+PearlNSArrayFormat.h */; }; DAFE4A1515039824003ABA7C /* NSString+PearlNSArrayFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE45DA15039823003ABA7C /* NSString+PearlNSArrayFormat.h */; };
@@ -380,27 +410,44 @@
DAFE4A63150399FF003ABA94 /* NSDateFormatter+RFC3339.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE4A63150399FF003ABA93 /* NSDateFormatter+RFC3339.h */; }; DAFE4A63150399FF003ABA94 /* NSDateFormatter+RFC3339.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE4A63150399FF003ABA93 /* NSDateFormatter+RFC3339.h */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
DA32D01E19D111C6004F3F0E /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
93D390519405B76CC6A57C4F /* MPCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCell.h; sourceTree = "<group>"; }; 93D390519405B76CC6A57C4F /* MPCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCell.h; sourceTree = "<group>"; };
93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Indexing.m"; sourceTree = "<group>"; }; 93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Indexing.m"; sourceTree = "<group>"; };
93D3908DF8EABBD952065DC0 /* UICollectionView+PearlReloadFromArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionView+PearlReloadFromArray.m"; sourceTree = "<group>"; }; 93D3908DF8EABBD952065DC0 /* UICollectionView+PearlReloadFromArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionView+PearlReloadFromArray.m"; sourceTree = "<group>"; };
93D390FADEB325D8D54A957D /* PearlOverlay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlOverlay.m; sourceTree = "<group>"; }; 93D390FADEB325D8D54A957D /* PearlOverlay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlOverlay.m; sourceTree = "<group>"; };
93D390FB3110DCCE68E600DC /* UIScrollView+PearlAdjustInsets.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIScrollView+PearlAdjustInsets.m"; sourceTree = "<group>"; }; 93D390FB3110DCCE68E600DC /* UIScrollView+PearlAdjustInsets.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIScrollView+PearlAdjustInsets.m"; sourceTree = "<group>"; };
93D39149A5F1F9B174D6D061 /* MPStoreViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPStoreViewController.h; sourceTree = "<group>"; };
93D3914D7597F9A28DB9D85E /* MPPasswordsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordsViewController.h; sourceTree = "<group>"; }; 93D3914D7597F9A28DB9D85E /* MPPasswordsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordsViewController.h; sourceTree = "<group>"; };
93D39156E806BB78E04F78B9 /* PearlSizedTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlSizedTextView.m; sourceTree = "<group>"; }; 93D39156E806BB78E04F78B9 /* PearlSizedTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlSizedTextView.m; sourceTree = "<group>"; };
93D3916C1D8F1427DFBDEBCA /* MPAppSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAppSettingsViewController.m; sourceTree = "<group>"; }; 93D3916C1D8F1427DFBDEBCA /* MPAppSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAppSettingsViewController.m; sourceTree = "<group>"; };
93D391943675426839501BB8 /* MPLogsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPLogsViewController.h; sourceTree = "<group>"; }; 93D391943675426839501BB8 /* MPLogsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPLogsViewController.h; sourceTree = "<group>"; };
93D391AA32F24290C424438E /* NSNotificationCenter+PearlEasyCleanup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNotificationCenter+PearlEasyCleanup.h"; sourceTree = "<group>"; };
93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadFromArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionView+PearlReloadFromArray.h"; sourceTree = "<group>"; }; 93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadFromArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionView+PearlReloadFromArray.h"; sourceTree = "<group>"; };
93D3924D6F77E6BF41AC32D3 /* MPRootSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPRootSegue.h; sourceTree = "<group>"; };
93D3924EE15017F8A12CB436 /* MPPasswordsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordsViewController.m; sourceTree = "<group>"; }; 93D3924EE15017F8A12CB436 /* MPPasswordsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordsViewController.m; sourceTree = "<group>"; };
93D392876BE5C011DE73B43F /* MPPopdownSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPopdownSegue.h; sourceTree = "<group>"; }; 93D392876BE5C011DE73B43F /* MPPopdownSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPopdownSegue.h; sourceTree = "<group>"; };
93D393310223DDB35218467A /* MPCombinedViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCombinedViewController.m; sourceTree = "<group>"; }; 93D393310223DDB35218467A /* MPCombinedViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCombinedViewController.m; sourceTree = "<group>"; };
93D3937712BF1B67623E5764 /* MPEmergencySegue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPEmergencySegue.m; sourceTree = "<group>"; };
93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Indexing.h"; sourceTree = "<group>"; }; 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Indexing.h"; sourceTree = "<group>"; };
93D393BB973253D4BAAC84AA /* PearlEMail.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlEMail.m; sourceTree = "<group>"; }; 93D393BB973253D4BAAC84AA /* PearlEMail.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlEMail.m; sourceTree = "<group>"; };
93D394077F8FAB8167647187 /* Twitter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Twitter.framework; path = System/Library/Frameworks/Twitter.framework; sourceTree = SDKROOT; }; 93D394077F8FAB8167647187 /* Twitter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Twitter.framework; path = System/Library/Frameworks/Twitter.framework; sourceTree = SDKROOT; };
93D3942A356B639724157982 /* PearlOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlOverlay.h; sourceTree = "<group>"; }; 93D3942A356B639724157982 /* PearlOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlOverlay.h; sourceTree = "<group>"; };
93D394482BB07F90E8FD1314 /* UIResponder+PearlFirstResponder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIResponder+PearlFirstResponder.h"; sourceTree = "<group>"; }; 93D394482BB07F90E8FD1314 /* UIResponder+PearlFirstResponder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIResponder+PearlFirstResponder.h"; sourceTree = "<group>"; };
93D394C78C7B879C9AD9152C /* MPAppDelegate_InApp.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAppDelegate_InApp.m; sourceTree = "<group>"; };
93D395105935859D71679931 /* MPOverlayViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPOverlayViewController.m; sourceTree = "<group>"; };
93D3956915634581E737B38C /* PearlNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlNavigationController.m; sourceTree = "<group>"; }; 93D3956915634581E737B38C /* PearlNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlNavigationController.m; sourceTree = "<group>"; };
93D3957D76F71A652716EECC /* MPStoreViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPStoreViewController.m; sourceTree = "<group>"; };
93D396D04E57792A54D437AC /* NSArray+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Indexing.h"; sourceTree = "<group>"; }; 93D396D04E57792A54D437AC /* NSArray+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Indexing.h"; sourceTree = "<group>"; };
93D3970502644794E8A027BE /* MPNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPNavigationController.h; sourceTree = "<group>"; }; 93D3970502644794E8A027BE /* MPNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPNavigationController.h; sourceTree = "<group>"; };
93D3971FE104BB4052484151 /* MPUsersViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUsersViewController.h; sourceTree = "<group>"; }; 93D3971FE104BB4052484151 /* MPUsersViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUsersViewController.h; sourceTree = "<group>"; };
@@ -410,28 +457,34 @@
93D398567FD02DB2647B8CF3 /* PearlNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlNavigationController.h; sourceTree = "<group>"; }; 93D398567FD02DB2647B8CF3 /* PearlNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlNavigationController.h; sourceTree = "<group>"; };
93D398C95847261903D781D3 /* NSError+PearlFullDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+PearlFullDescription.h"; sourceTree = "<group>"; }; 93D398C95847261903D781D3 /* NSError+PearlFullDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+PearlFullDescription.h"; sourceTree = "<group>"; };
93D3990E0CD1B5CF9FBB2C07 /* MPWebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPWebViewController.m; sourceTree = "<group>"; }; 93D3990E0CD1B5CF9FBB2C07 /* MPWebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPWebViewController.m; sourceTree = "<group>"; };
93D399493FEDDE74DD1A0C15 /* MPRootSegue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPRootSegue.m; sourceTree = "<group>"; };
93D3995B1D4DCE5A30D882BA /* MPCoachmarkViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCoachmarkViewController.m; sourceTree = "<group>"; }; 93D3995B1D4DCE5A30D882BA /* MPCoachmarkViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCoachmarkViewController.m; sourceTree = "<group>"; };
93D39975CE5AEC99E3F086C7 /* MPPasswordCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordCell.h; sourceTree = "<group>"; }; 93D39975CE5AEC99E3F086C7 /* MPPasswordCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordCell.h; sourceTree = "<group>"; };
93D3999693660C89A7465F4E /* MPCoachmarkViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCoachmarkViewController.h; sourceTree = "<group>"; }; 93D3999693660C89A7465F4E /* MPCoachmarkViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCoachmarkViewController.h; sourceTree = "<group>"; };
93D399A8E3181B442D347CD7 /* MPAlgorithmV2.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAlgorithmV2.m; sourceTree = "<group>"; };
93D399E571F61E50A9BF8FAF /* MPUsersViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUsersViewController.m; sourceTree = "<group>"; }; 93D399E571F61E50A9BF8FAF /* MPUsersViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUsersViewController.m; sourceTree = "<group>"; };
93D399F244BB522A317811BB /* MPFixable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPFixable.h; sourceTree = "<group>"; }; 93D399F244BB522A317811BB /* MPFixable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPFixable.h; sourceTree = "<group>"; };
93D39A1DDFA09AE2E14D26DC /* UIResponder+PearlFirstResponder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIResponder+PearlFirstResponder.m"; sourceTree = "<group>"; }; 93D39A1DDFA09AE2E14D26DC /* UIResponder+PearlFirstResponder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIResponder+PearlFirstResponder.m"; sourceTree = "<group>"; };
93D39A28369954D147E239BA /* MPSetupViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSetupViewController.m; sourceTree = "<group>"; }; 93D39A28369954D147E239BA /* MPSetupViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSetupViewController.m; sourceTree = "<group>"; };
93D39A41340CF778E00D0E6D /* MPEmergencySegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPEmergencySegue.h; sourceTree = "<group>"; };
93D39A4759186F6D2D34AA6B /* PearlSizedTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlSizedTextView.h; sourceTree = "<group>"; }; 93D39A4759186F6D2D34AA6B /* PearlSizedTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlSizedTextView.h; sourceTree = "<group>"; };
93D39A813CA9D7E192261ED2 /* MPFixable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPFixable.m; sourceTree = "<group>"; }; 93D39A813CA9D7E192261ED2 /* MPFixable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPFixable.m; sourceTree = "<group>"; };
93D39A97A7D48CB3B784194D /* MPAlgorithmV2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAlgorithmV2.h; sourceTree = "<group>"; };
93D39AA10CD00D05937671B1 /* UITextView+PearlAttributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITextView+PearlAttributes.h"; sourceTree = "<group>"; }; 93D39AA10CD00D05937671B1 /* UITextView+PearlAttributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITextView+PearlAttributes.h"; sourceTree = "<group>"; };
93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Indexing.m"; sourceTree = "<group>"; }; 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Indexing.m"; sourceTree = "<group>"; };
93D39ACBA9F4878B6A1CC33B /* MPEmergencyViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPEmergencyViewController.m; sourceTree = "<group>"; }; 93D39ACBA9F4878B6A1CC33B /* MPEmergencyViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPEmergencyViewController.m; sourceTree = "<group>"; };
93D39B050DD5F55E9794EFD4 /* MPPopdownSegue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPopdownSegue.m; sourceTree = "<group>"; }; 93D39B050DD5F55E9794EFD4 /* MPPopdownSegue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPopdownSegue.m; sourceTree = "<group>"; };
93D39B1D8177A86C5B9EDDE3 /* PearlUICollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlUICollectionView.h; sourceTree = "<group>"; }; 93D39B1D8177A86C5B9EDDE3 /* PearlUICollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlUICollectionView.h; sourceTree = "<group>"; };
93D39B381350802A194BF332 /* MPAvatarCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAvatarCell.m; sourceTree = "<group>"; }; 93D39B381350802A194BF332 /* MPAvatarCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAvatarCell.m; sourceTree = "<group>"; };
93D39B455A71EC98C749E623 /* MPOverlayViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPOverlayViewController.h; sourceTree = "<group>"; };
93D39BAA71DE51B4D8A1286C /* MPCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCell.m; sourceTree = "<group>"; }; 93D39BAA71DE51B4D8A1286C /* MPCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCell.m; sourceTree = "<group>"; };
93D39C426E03358384018E85 /* MPAnswersViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAnswersViewController.m; sourceTree = "<group>"; };
93D39C44361BE57AF0B3071F /* MPPasswordsSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordsSegue.h; sourceTree = "<group>"; }; 93D39C44361BE57AF0B3071F /* MPPasswordsSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordsSegue.h; sourceTree = "<group>"; };
93D39C86E984EC65DA5ACB1D /* MPAppSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAppSettingsViewController.h; sourceTree = "<group>"; }; 93D39C86E984EC65DA5ACB1D /* MPAppSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAppSettingsViewController.h; sourceTree = "<group>"; };
93D39CC01630D0421205C4C4 /* MPNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPNavigationController.m; sourceTree = "<group>"; }; 93D39CC01630D0421205C4C4 /* MPNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPNavigationController.m; sourceTree = "<group>"; };
93D39CDD434AFD6E1B0DA359 /* MPEmergencyViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPEmergencyViewController.h; sourceTree = "<group>"; }; 93D39CDD434AFD6E1B0DA359 /* MPEmergencyViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPEmergencyViewController.h; sourceTree = "<group>"; };
93D39CECA10BCCB0BA581BF1 /* MPAppDelegate_InApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAppDelegate_InApp.h; sourceTree = "<group>"; };
93D39CF8ADF4542CDC4CD385 /* MPCombinedViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCombinedViewController.h; sourceTree = "<group>"; }; 93D39CF8ADF4542CDC4CD385 /* MPCombinedViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCombinedViewController.h; sourceTree = "<group>"; };
93D39D6604447D7708039155 /* MPAnswersViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAnswersViewController.h; sourceTree = "<group>"; };
93D39D8A953779B35403AF6E /* PearlUICollectionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlUICollectionView.m; sourceTree = "<group>"; }; 93D39D8A953779B35403AF6E /* PearlUICollectionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlUICollectionView.m; sourceTree = "<group>"; };
93D39DA27D768B53C8B1330C /* MPAvatarCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAvatarCell.h; sourceTree = "<group>"; }; 93D39DA27D768B53C8B1330C /* MPAvatarCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAvatarCell.h; sourceTree = "<group>"; };
93D39DE2CB351D4E3789462B /* UIScrollView+PearlAdjustInsets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIScrollView+PearlAdjustInsets.h"; sourceTree = "<group>"; }; 93D39DE2CB351D4E3789462B /* UIScrollView+PearlAdjustInsets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIScrollView+PearlAdjustInsets.h"; sourceTree = "<group>"; };
@@ -471,6 +524,9 @@
DA250A16195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionReusableView+PearlDequeue.h"; sourceTree = "<group>"; }; DA250A16195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionReusableView+PearlDequeue.h"; sourceTree = "<group>"; };
DA25C6B31980D3C10046CDCF /* openssl */ = {isa = PBXFileReference; lastKnownFileType = folder; path = openssl; sourceTree = "<group>"; }; DA25C6B31980D3C10046CDCF /* openssl */ = {isa = PBXFileReference; lastKnownFileType = folder; path = openssl; sourceTree = "<group>"; };
DA25C6B51980D3DD0046CDCF /* scrypt */ = {isa = PBXFileReference; lastKnownFileType = folder; path = scrypt; sourceTree = "<group>"; }; DA25C6B51980D3DD0046CDCF /* scrypt */ = {isa = PBXFileReference; lastKnownFileType = folder; path = scrypt; sourceTree = "<group>"; };
DA29992D19C86F5700AF7DF1 /* thumb_generated_login@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "thumb_generated_login@2x.png"; sourceTree = "<group>"; };
DA29992E19C86F5700AF7DF1 /* thumb_generated_login.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = thumb_generated_login.png; sourceTree = "<group>"; };
DA29993119C9132F00AF7DF1 /* thumb_generated_login@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "thumb_generated_login@3x.png"; sourceTree = "<group>"; };
DA2CA4D918D28859007798F8 /* NSArray+Pearl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Pearl.m"; sourceTree = "<group>"; }; DA2CA4D918D28859007798F8 /* NSArray+Pearl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Pearl.m"; sourceTree = "<group>"; };
DA2CA4DA18D28859007798F8 /* NSArray+Pearl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Pearl.h"; sourceTree = "<group>"; }; DA2CA4DA18D28859007798F8 /* NSArray+Pearl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Pearl.h"; sourceTree = "<group>"; };
DA2CA4DB18D28859007798F8 /* NSTimer+PearlBlock.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSTimer+PearlBlock.m"; sourceTree = "<group>"; }; DA2CA4DB18D28859007798F8 /* NSTimer+PearlBlock.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSTimer+PearlBlock.m"; sourceTree = "<group>"; };
@@ -484,6 +540,36 @@
DA30E9D315722EF400A68B4C /* Pearl-UIKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "Pearl-UIKit.m"; sourceTree = "<group>"; }; DA30E9D315722EF400A68B4C /* Pearl-UIKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "Pearl-UIKit.m"; sourceTree = "<group>"; };
DA30E9D515723E6900A68B4C /* PearlLazy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlLazy.h; sourceTree = "<group>"; }; DA30E9D515723E6900A68B4C /* PearlLazy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlLazy.h; sourceTree = "<group>"; };
DA30E9D615723E6900A68B4C /* PearlLazy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlLazy.m; sourceTree = "<group>"; }; DA30E9D615723E6900A68B4C /* PearlLazy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlLazy.m; sourceTree = "<group>"; };
DA32CFE619CF1C8F004F3F0E /* MPUserEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUserEntity.m; sourceTree = "<group>"; };
DA32CFE719CF1C8F004F3F0E /* MPUserEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUserEntity.h; sourceTree = "<group>"; };
DA32CFE819CF1C8F004F3F0E /* MPStoredSiteEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPStoredSiteEntity.m; sourceTree = "<group>"; };
DA32CFE919CF1C8F004F3F0E /* MPStoredSiteEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPStoredSiteEntity.h; sourceTree = "<group>"; };
DA32CFEC19CF1C8F004F3F0E /* MPSiteEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSiteEntity.m; sourceTree = "<group>"; };
DA32CFED19CF1C8F004F3F0E /* MPSiteEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSiteEntity.h; sourceTree = "<group>"; };
DA32CFEE19CF1C8F004F3F0E /* MPGeneratedSiteEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPGeneratedSiteEntity.m; sourceTree = "<group>"; };
DA32CFEF19CF1C8F004F3F0E /* MPGeneratedSiteEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPGeneratedSiteEntity.h; sourceTree = "<group>"; };
DA32D00219CF4735004F3F0E /* MasterPassword 1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 1.xcdatamodel"; sourceTree = "<group>"; };
DA32D00319CF4735004F3F0E /* MasterPassword 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 2.xcdatamodel"; sourceTree = "<group>"; };
DA32D00419CF4735004F3F0E /* MasterPassword 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 3.xcdatamodel"; sourceTree = "<group>"; };
DA32D00519CF4735004F3F0E /* MasterPassword 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 4.xcdatamodel"; sourceTree = "<group>"; };
DA32D00619CF4735004F3F0E /* MasterPassword 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 5.xcdatamodel"; sourceTree = "<group>"; };
DA32D00719CF4735004F3F0E /* MasterPassword 6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 6.xcdatamodel"; sourceTree = "<group>"; };
DA32D01819D046E1004F3F0E /* PearlFixedTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlFixedTableView.m; sourceTree = "<group>"; };
DA32D01919D046E1004F3F0E /* PearlFixedTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlFixedTableView.h; sourceTree = "<group>"; };
DA32D02019D111C6004F3F0E /* libKCOrderedAccessorFix.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libKCOrderedAccessorFix.a; sourceTree = BUILT_PRODUCTS_DIR; };
DA32D03919D111EB004F3F0E /* NSManagedObjectModel+KCOrderedAccessorFix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSManagedObjectModel+KCOrderedAccessorFix.h"; sourceTree = "<group>"; };
DA32D03A19D111EB004F3F0E /* NSManagedObjectModel+KCOrderedAccessorFix.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSManagedObjectModel+KCOrderedAccessorFix.m"; sourceTree = "<group>"; };
DA32D03F19D27093004F3F0E /* thumb_generated_answers@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "thumb_generated_answers@3x.png"; sourceTree = "<group>"; };
DA32D04019D27093004F3F0E /* thumb_generated_answers@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "thumb_generated_answers@2x.png"; sourceTree = "<group>"; };
DA32D04119D27093004F3F0E /* thumb_generated_answers.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = thumb_generated_answers.png; sourceTree = "<group>"; };
DA32D04519D2F417004F3F0E /* thumb_fuel@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "thumb_fuel@3x.png"; sourceTree = "<group>"; };
DA32D04619D2F417004F3F0E /* thumb_fuel@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "thumb_fuel@2x.png"; sourceTree = "<group>"; };
DA32D04719D2F417004F3F0E /* thumb_fuel.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = thumb_fuel.png; sourceTree = "<group>"; };
DA32D04B19D2F59B004F3F0E /* meter_fuel@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "meter_fuel@3x.png"; sourceTree = "<group>"; };
DA32D04C19D2F59B004F3F0E /* meter_fuel@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "meter_fuel@2x.png"; sourceTree = "<group>"; };
DA32D04D19D2F59B004F3F0E /* meter_fuel.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = meter_fuel.png; sourceTree = "<group>"; };
DA32D05319D741DC004F3F0E /* MPSiteQuestionEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSiteQuestionEntity.h; sourceTree = "<group>"; };
DA32D05419D741DC004F3F0E /* MPSiteQuestionEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSiteQuestionEntity.m; sourceTree = "<group>"; };
DA3509FC15F101A500C14A8E /* PearlQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlQueue.h; sourceTree = "<group>"; }; DA3509FC15F101A500C14A8E /* PearlQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlQueue.h; sourceTree = "<group>"; };
DA3509FD15F101A500C14A8E /* PearlQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlQueue.m; sourceTree = "<group>"; }; DA3509FD15F101A500C14A8E /* PearlQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlQueue.m; sourceTree = "<group>"; };
DA38D6A218CCB5BF009AEB3E /* Storyboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Storyboard.storyboard; sourceTree = "<group>"; }; DA38D6A218CCB5BF009AEB3E /* Storyboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Storyboard.storyboard; sourceTree = "<group>"; };
@@ -499,7 +585,6 @@
DA5BFA4C147E415C00F98B1E /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 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; }; 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>"; }; 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; }; 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; }; 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; }; DA6701DF16406BB400B61001 /* AdSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AdSupport.framework; path = System/Library/Frameworks/AdSupport.framework; sourceTree = SDKROOT; };
@@ -1135,7 +1220,6 @@
DABD38C11711E29700CF925C /* tip_location_teal@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tip_location_teal@2x.png"; sourceTree = "<group>"; }; DABD38C11711E29700CF925C /* tip_location_teal@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tip_location_teal@2x.png"; sourceTree = "<group>"; };
DABD38C21711E29700CF925C /* tip_location_wood.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = tip_location_wood.png; sourceTree = "<group>"; }; DABD38C21711E29700CF925C /* tip_location_wood.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = tip_location_wood.png; sourceTree = "<group>"; };
DABD38C31711E29700CF925C /* tip_location_wood@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tip_location_wood@2x.png"; sourceTree = "<group>"; }; DABD38C31711E29700CF925C /* tip_location_wood@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tip_location_wood@2x.png"; sourceTree = "<group>"; };
DABD38C61711E29700CF925C /* help.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = help.html; sourceTree = "<group>"; };
DABD38C81711E29700CF925C /* jquery-1.6.1.min.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "jquery-1.6.1.min.js"; sourceTree = "<group>"; }; DABD38C81711E29700CF925C /* jquery-1.6.1.min.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "jquery-1.6.1.min.js"; sourceTree = "<group>"; };
DABD38C91711E29700CF925C /* keypad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = keypad.png; sourceTree = "<group>"; }; DABD38C91711E29700CF925C /* keypad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = keypad.png; sourceTree = "<group>"; };
DABD38CA1711E29700CF925C /* logo-bare.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "logo-bare.png"; sourceTree = "<group>"; }; DABD38CA1711E29700CF925C /* logo-bare.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "logo-bare.png"; sourceTree = "<group>"; };
@@ -1159,23 +1243,11 @@
DABD3BAB1711E2DC00CF925C /* MPAppDelegate_Store.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAppDelegate_Store.m; sourceTree = "<group>"; }; DABD3BAB1711E2DC00CF925C /* MPAppDelegate_Store.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAppDelegate_Store.m; sourceTree = "<group>"; };
DABD3BAC1711E2DC00CF925C /* MPConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPConfig.h; sourceTree = "<group>"; }; DABD3BAC1711E2DC00CF925C /* MPConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPConfig.h; sourceTree = "<group>"; };
DABD3BAD1711E2DC00CF925C /* MPConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPConfig.m; sourceTree = "<group>"; }; DABD3BAD1711E2DC00CF925C /* MPConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPConfig.m; sourceTree = "<group>"; };
DABD3BAE1711E2DC00CF925C /* MPElementEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementEntity.h; sourceTree = "<group>"; };
DABD3BAF1711E2DC00CF925C /* MPElementEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementEntity.m; sourceTree = "<group>"; };
DABD3BB01711E2DC00CF925C /* MPElementGeneratedEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementGeneratedEntity.h; sourceTree = "<group>"; };
DABD3BB11711E2DC00CF925C /* MPElementGeneratedEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementGeneratedEntity.m; sourceTree = "<group>"; };
DABD3BB21711E2DC00CF925C /* MPElementStoredEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementStoredEntity.h; sourceTree = "<group>"; };
DABD3BB31711E2DC00CF925C /* MPElementStoredEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementStoredEntity.m; sourceTree = "<group>"; };
DABD3BB41711E2DC00CF925C /* MPEntities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPEntities.h; sourceTree = "<group>"; }; DABD3BB41711E2DC00CF925C /* MPEntities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPEntities.h; sourceTree = "<group>"; };
DABD3BB51711E2DC00CF925C /* MPEntities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPEntities.m; sourceTree = "<group>"; }; DABD3BB51711E2DC00CF925C /* MPEntities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPEntities.m; sourceTree = "<group>"; };
DABD3BB61711E2DC00CF925C /* MPKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPKey.h; sourceTree = "<group>"; }; DABD3BB61711E2DC00CF925C /* MPKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPKey.h; sourceTree = "<group>"; };
DABD3BB71711E2DC00CF925C /* MPKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPKey.m; sourceTree = "<group>"; }; DABD3BB71711E2DC00CF925C /* MPKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPKey.m; sourceTree = "<group>"; };
DABD3BB81711E2DC00CF925C /* MPTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPTypes.h; sourceTree = "<group>"; }; DABD3BB81711E2DC00CF925C /* MPTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPTypes.h; sourceTree = "<group>"; };
DABD3BB91711E2DC00CF925C /* MPUserEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUserEntity.h; sourceTree = "<group>"; };
DABD3BBA1711E2DC00CF925C /* MPUserEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUserEntity.m; sourceTree = "<group>"; };
DABD3BD11711E2DC00CF925C /* MasterPassword 1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 1.xcdatamodel"; sourceTree = "<group>"; };
DABD3BD21711E2DC00CF925C /* MasterPassword 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 2.xcdatamodel"; sourceTree = "<group>"; };
DABD3BD31711E2DC00CF925C /* MasterPassword 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 3.xcdatamodel"; sourceTree = "<group>"; };
DABD3BD41711E2DC00CF925C /* MasterPassword 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 4.xcdatamodel"; sourceTree = "<group>"; };
DABD3BD81711E2DC00CF925C /* MPiOSAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPiOSAppDelegate.h; sourceTree = "<group>"; }; DABD3BD81711E2DC00CF925C /* MPiOSAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPiOSAppDelegate.h; sourceTree = "<group>"; };
DABD3BD91711E2DC00CF925C /* MPiOSAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPiOSAppDelegate.m; sourceTree = "<group>"; }; DABD3BD91711E2DC00CF925C /* MPiOSAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPiOSAppDelegate.m; sourceTree = "<group>"; };
DABD3BE61711E2DC00CF925C /* MPGuideViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPGuideViewController.h; sourceTree = "<group>"; }; DABD3BE61711E2DC00CF925C /* MPGuideViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPGuideViewController.h; sourceTree = "<group>"; };
@@ -1217,16 +1289,10 @@
DACE2F6919BA6A2A0010F92E /* PearlMutableStaticTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlMutableStaticTableViewController.h; sourceTree = "<group>"; }; 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>"; }; 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; }; 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>"; }; 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>"; }; DAE1EF2317E942DE00BC0086 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
DAE2726119CE9CB3007C5262 /* UITableViewCell+PearlDeque.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITableViewCell+PearlDeque.m"; sourceTree = "<group>"; };
DAE2726219CE9CB3007C5262 /* UITableViewCell+PearlDeque.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITableViewCell+PearlDeque.h"; sourceTree = "<group>"; };
DAE8E65119867AB500416A0F /* libopensslcrypto-ios.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libopensslcrypto-ios.a"; sourceTree = "<group>"; }; DAE8E65119867AB500416A0F /* libopensslcrypto-ios.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libopensslcrypto-ios.a"; sourceTree = "<group>"; };
DAEBC45214F6364500987BF6 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; DAEBC45214F6364500987BF6 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
DAEC85B118E3DD9A007FC0DF /* UIView+Touches.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+Touches.m"; sourceTree = "<group>"; }; DAEC85B118E3DD9A007FC0DF /* UIView+Touches.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+Touches.m"; sourceTree = "<group>"; };
@@ -1354,11 +1420,21 @@
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
DA32D01D19D111C6004F3F0E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
DA5BFA41147E415C00F98B1E /* Frameworks */ = { DA5BFA41147E415C00F98B1E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
DAFC5691172C582A00CB5CC5 /* libInAppSettingsKit.a in Frameworks */, DA32D03E19D11293004F3F0E /* libKCOrderedAccessorFix.a in Frameworks */,
DA04E33E14B1E70400ECA4F3 /* MobileCoreServices.framework in Frameworks */,
DAE2725A19C93B8E007C5262 /* StoreKit.framework in Frameworks */,
DAE2725919C93B80007C5262 /* libInAppSettingsKit.a in Frameworks */,
DA6701E016406BB400B61001 /* AdSupport.framework in Frameworks */, DA6701E016406BB400B61001 /* AdSupport.framework in Frameworks */,
DA6701DE16406B7300B61001 /* Social.framework in Frameworks */, DA6701DE16406B7300B61001 /* Social.framework in Frameworks */,
DA6701B816406A4100B61001 /* Accounts.framework in Frameworks */, DA6701B816406A4100B61001 /* Accounts.framework in Frameworks */,
@@ -1368,7 +1444,6 @@
DA672D2F14F92C6B004A189C /* libz.dylib in Frameworks */, DA672D2F14F92C6B004A189C /* libz.dylib in Frameworks */,
DAEBC45314F6364500987BF6 /* QuartzCore.framework in Frameworks */, DAEBC45314F6364500987BF6 /* QuartzCore.framework in Frameworks */,
DA95D5F214DF0B2C008D1B94 /* MessageUI.framework in Frameworks */, DA95D5F214DF0B2C008D1B94 /* MessageUI.framework in Frameworks */,
DA04E33E14B1E70400ECA4F3 /* MobileCoreServices.framework in Frameworks */,
DA48856019A5A82E000C2D79 /* Crashlytics.framework in Frameworks */, DA48856019A5A82E000C2D79 /* Crashlytics.framework in Frameworks */,
DAC632891486D9690075AEA5 /* Security.framework in Frameworks */, DAC632891486D9690075AEA5 /* Security.framework in Frameworks */,
DA72BD7919C137DE00E6ACFE /* libopensslcrypto-ios-dev.a in Frameworks */, DA72BD7919C137DE00E6ACFE /* libopensslcrypto-ios-dev.a in Frameworks */,
@@ -1447,6 +1522,15 @@
path = Guide; path = Guide;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
DA32D03719D111EB004F3F0E /* KCOrderedAccessorFix */ = {
isa = PBXGroup;
children = (
DA32D03919D111EB004F3F0E /* NSManagedObjectModel+KCOrderedAccessorFix.h */,
DA32D03A19D111EB004F3F0E /* NSManagedObjectModel+KCOrderedAccessorFix.m */,
);
path = KCOrderedAccessorFix;
sourceTree = "<group>";
};
DA5BFA39147E415C00F98B1E = { DA5BFA39147E415C00F98B1E = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -1455,6 +1539,12 @@
DACA22121705DDC5002C6C22 /* External */, DACA22121705DDC5002C6C22 /* External */,
DA5BFA47147E415C00F98B1E /* Frameworks */, DA5BFA47147E415C00F98B1E /* Frameworks */,
DA5BFA45147E415C00F98B1E /* Products */, DA5BFA45147E415C00F98B1E /* Products */,
93D39149A5F1F9B174D6D061 /* MPStoreViewController.h */,
93D3957D76F71A652716EECC /* MPStoreViewController.m */,
93D39C426E03358384018E85 /* MPAnswersViewController.m */,
93D39D6604447D7708039155 /* MPAnswersViewController.h */,
93D399493FEDDE74DD1A0C15 /* MPRootSegue.m */,
93D3924D6F77E6BF41AC32D3 /* MPRootSegue.h */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
}; };
@@ -1466,6 +1556,7 @@
DAC6325D1486805C0075AEA5 /* libuicolor-utilities.a */, DAC6325D1486805C0075AEA5 /* libuicolor-utilities.a */,
DAC6326C148680650075AEA5 /* libjrswizzle.a */, DAC6326C148680650075AEA5 /* libjrswizzle.a */,
DAFC5655172C573B00CB5CC5 /* libInAppSettingsKit.a */, DAFC5655172C573B00CB5CC5 /* libInAppSettingsKit.a */,
DA32D02019D111C6004F3F0E /* libKCOrderedAccessorFix.a */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -1535,6 +1626,18 @@
DABD360D1711E29400CF925C /* Media */ = { DABD360D1711E29400CF925C /* Media */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DA32D04B19D2F59B004F3F0E /* meter_fuel@3x.png */,
DA32D04C19D2F59B004F3F0E /* meter_fuel@2x.png */,
DA32D04D19D2F59B004F3F0E /* meter_fuel.png */,
DA32D04519D2F417004F3F0E /* thumb_fuel@3x.png */,
DA32D04619D2F417004F3F0E /* thumb_fuel@2x.png */,
DA32D04719D2F417004F3F0E /* thumb_fuel.png */,
DA32D03F19D27093004F3F0E /* thumb_generated_answers@3x.png */,
DA32D04019D27093004F3F0E /* thumb_generated_answers@2x.png */,
DA32D04119D27093004F3F0E /* thumb_generated_answers.png */,
DA29993119C9132F00AF7DF1 /* thumb_generated_login@3x.png */,
DA29992D19C86F5700AF7DF1 /* thumb_generated_login@2x.png */,
DA29992E19C86F5700AF7DF1 /* thumb_generated_login.png */,
DA2509B619563E1E00AC23F1 /* Guide */, DA2509B619563E1E00AC23F1 /* Guide */,
DA071BF1190187FE00179766 /* empty@2x.png */, DA071BF1190187FE00179766 /* empty@2x.png */,
DA071BF2190187FE00179766 /* empty.png */, DA071BF2190187FE00179766 /* empty.png */,
@@ -1553,7 +1656,6 @@
DABD38751711E29700CF925C /* Tooltips */, DABD38751711E29700CF925C /* Tooltips */,
DABD3FC81712446200CF925C /* cloud.png */, DABD3FC81712446200CF925C /* cloud.png */,
DABD3FC91712446200CF925C /* cloud@2x.png */, DABD3FC91712446200CF925C /* cloud@2x.png */,
DABD38C61711E29700CF925C /* help.html */,
DABD3FCC1714F45B00CF925C /* identity.png */, DABD3FCC1714F45B00CF925C /* identity.png */,
DABD3FCD1714F45B00CF925C /* identity@2x.png */, DABD3FCD1714F45B00CF925C /* identity@2x.png */,
DABD38C81711E29700CF925C /* jquery-1.6.1.min.js */, DABD38C81711E29700CF925C /* jquery-1.6.1.min.js */,
@@ -2235,15 +2337,17 @@
DABD3B9F1711E2DB00CF925C /* ObjC */ = { DABD3B9F1711E2DB00CF925C /* ObjC */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DA32D05319D741DC004F3F0E /* MPSiteQuestionEntity.h */,
DA32D05419D741DC004F3F0E /* MPSiteQuestionEntity.m */,
DA32CFE619CF1C8F004F3F0E /* MPUserEntity.m */,
DA32CFE719CF1C8F004F3F0E /* MPUserEntity.h */,
DA32CFE819CF1C8F004F3F0E /* MPStoredSiteEntity.m */,
DA32CFE919CF1C8F004F3F0E /* MPStoredSiteEntity.h */,
DA32CFEC19CF1C8F004F3F0E /* MPSiteEntity.m */,
DA32CFED19CF1C8F004F3F0E /* MPSiteEntity.h */,
DA32CFEE19CF1C8F004F3F0E /* MPGeneratedSiteEntity.m */,
DA32CFEF19CF1C8F004F3F0E /* MPGeneratedSiteEntity.h */,
DABD3BD71711E2DC00CF925C /* iOS */, 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 */, DABD3BA01711E2DC00CF925C /* MPAlgorithm.h */,
DABD3BA11711E2DC00CF925C /* MPAlgorithm.m */, DABD3BA11711E2DC00CF925C /* MPAlgorithm.m */,
DABD3BA21711E2DC00CF925C /* MPAlgorithmV0.h */, DABD3BA21711E2DC00CF925C /* MPAlgorithmV0.h */,
@@ -2258,22 +2362,18 @@
DABD3BAB1711E2DC00CF925C /* MPAppDelegate_Store.m */, DABD3BAB1711E2DC00CF925C /* MPAppDelegate_Store.m */,
DABD3BAC1711E2DC00CF925C /* MPConfig.h */, DABD3BAC1711E2DC00CF925C /* MPConfig.h */,
DABD3BAD1711E2DC00CF925C /* MPConfig.m */, DABD3BAD1711E2DC00CF925C /* MPConfig.m */,
DABD3BAE1711E2DC00CF925C /* MPElementEntity.h */,
DABD3BAF1711E2DC00CF925C /* MPElementEntity.m */,
DABD3BB01711E2DC00CF925C /* MPElementGeneratedEntity.h */,
DABD3BB11711E2DC00CF925C /* MPElementGeneratedEntity.m */,
DABD3BB21711E2DC00CF925C /* MPElementStoredEntity.h */,
DABD3BB31711E2DC00CF925C /* MPElementStoredEntity.m */,
DABD3BB41711E2DC00CF925C /* MPEntities.h */, DABD3BB41711E2DC00CF925C /* MPEntities.h */,
DABD3BB51711E2DC00CF925C /* MPEntities.m */, DABD3BB51711E2DC00CF925C /* MPEntities.m */,
DABD3BB61711E2DC00CF925C /* MPKey.h */, DABD3BB61711E2DC00CF925C /* MPKey.h */,
DABD3BB71711E2DC00CF925C /* MPKey.m */, DABD3BB71711E2DC00CF925C /* MPKey.m */,
DABD3BB81711E2DC00CF925C /* MPTypes.h */, DABD3BB81711E2DC00CF925C /* MPTypes.h */,
DABD3BB91711E2DC00CF925C /* MPUserEntity.h */, DA32D00119CF4735004F3F0E /* MasterPassword.xcdatamodeld */,
DABD3BBA1711E2DC00CF925C /* MPUserEntity.m */,
DABD3BD01711E2DC00CF925C /* MasterPassword.xcdatamodeld */,
93D399F244BB522A317811BB /* MPFixable.h */, 93D399F244BB522A317811BB /* MPFixable.h */,
93D39A813CA9D7E192261ED2 /* MPFixable.m */, 93D39A813CA9D7E192261ED2 /* MPFixable.m */,
93D394C78C7B879C9AD9152C /* MPAppDelegate_InApp.m */,
93D39CECA10BCCB0BA581BF1 /* MPAppDelegate_InApp.h */,
93D399A8E3181B442D347CD7 /* MPAlgorithmV2.m */,
93D39A97A7D48CB3B784194D /* MPAlgorithmV2.h */,
); );
name = ObjC; name = ObjC;
path = ..; path = ..;
@@ -2317,8 +2417,6 @@
93D3971FE104BB4052484151 /* MPUsersViewController.h */, 93D3971FE104BB4052484151 /* MPUsersViewController.h */,
93D39BAA71DE51B4D8A1286C /* MPCell.m */, 93D39BAA71DE51B4D8A1286C /* MPCell.m */,
93D390519405B76CC6A57C4F /* MPCell.h */, 93D390519405B76CC6A57C4F /* MPCell.h */,
93D3937712BF1B67623E5764 /* MPEmergencySegue.m */,
93D39A41340CF778E00D0E6D /* MPEmergencySegue.h */,
93D39E7A12CC352B2825AA66 /* MPPasswordsSegue.m */, 93D39E7A12CC352B2825AA66 /* MPPasswordsSegue.m */,
93D39C44361BE57AF0B3071F /* MPPasswordsSegue.h */, 93D39C44361BE57AF0B3071F /* MPPasswordsSegue.h */,
93D39B050DD5F55E9794EFD4 /* MPPopdownSegue.m */, 93D39B050DD5F55E9794EFD4 /* MPPopdownSegue.m */,
@@ -2331,6 +2429,8 @@
93D39F556F2F142740A65E59 /* MPWebViewController.h */, 93D39F556F2F142740A65E59 /* MPWebViewController.h */,
93D39CC01630D0421205C4C4 /* MPNavigationController.m */, 93D39CC01630D0421205C4C4 /* MPNavigationController.m */,
93D3970502644794E8A027BE /* MPNavigationController.h */, 93D3970502644794E8A027BE /* MPNavigationController.h */,
93D395105935859D71679931 /* MPOverlayViewController.m */,
93D39B455A71EC98C749E623 /* MPOverlayViewController.h */,
); );
path = iOS; path = iOS;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -2351,6 +2451,7 @@
DACA22121705DDC5002C6C22 /* External */ = { DACA22121705DDC5002C6C22 /* External */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DA32D03719D111EB004F3F0E /* KCOrderedAccessorFix */,
DAA141181922FED80032B392 /* iOS */, DAA141181922FED80032B392 /* iOS */,
DAFC5662172C57EC00CB5CC5 /* InAppSettingsKit */, DAFC5662172C57EC00CB5CC5 /* InAppSettingsKit */,
DAC77CAF148291A600BCF976 /* Pearl */, DAC77CAF148291A600BCF976 /* Pearl */,
@@ -2532,6 +2633,7 @@
DAFE45F915039823003ABA7C /* Resources */, DAFE45F915039823003ABA7C /* Resources */,
93D39F9106F2CCFB94283188 /* NSError+PearlFullDescription.m */, 93D39F9106F2CCFB94283188 /* NSError+PearlFullDescription.m */,
93D398C95847261903D781D3 /* NSError+PearlFullDescription.h */, 93D398C95847261903D781D3 /* NSError+PearlFullDescription.h */,
93D391AA32F24290C424438E /* NSNotificationCenter+PearlEasyCleanup.h */,
); );
path = Pearl; path = Pearl;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -2566,6 +2668,10 @@
DAFE460715039823003ABA7C /* Pearl-UIKit */ = { DAFE460715039823003ABA7C /* Pearl-UIKit */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DA32D01819D046E1004F3F0E /* PearlFixedTableView.m */,
DA32D01919D046E1004F3F0E /* PearlFixedTableView.h */,
DAE2726119CE9CB3007C5262 /* UITableViewCell+PearlDeque.m */,
DAE2726219CE9CB3007C5262 /* UITableViewCell+PearlDeque.h */,
DAEFB01C19BCBD9E00525079 /* UIView+LayoutGone.m */, DAEFB01C19BCBD9E00525079 /* UIView+LayoutGone.m */,
DAEFB01D19BCBD9E00525079 /* UIView+LayoutGone.h */, DAEFB01D19BCBD9E00525079 /* UIView+LayoutGone.h */,
DACE2F6719BA6A2A0010F92E /* UIView+FontScale.m */, DACE2F6719BA6A2A0010F92E /* UIView+FontScale.m */,
@@ -2702,8 +2808,10 @@
DAFE4A3415039824003ABA7C /* PearlCryptUtils.h in Headers */, DAFE4A3415039824003ABA7C /* PearlCryptUtils.h in Headers */,
DAFE4A3615039824003ABA7C /* PearlKeyChain.h in Headers */, DAFE4A3615039824003ABA7C /* PearlKeyChain.h in Headers */,
DAFE4A3815039824003ABA7C /* PearlRSAKey.h in Headers */, DAFE4A3815039824003ABA7C /* PearlRSAKey.h in Headers */,
DAE2726419CE9CB3007C5262 /* UITableViewCell+PearlDeque.h in Headers */,
DAFE4A3A15039824003ABA7C /* PearlSCrypt.h in Headers */, DAFE4A3A15039824003ABA7C /* PearlSCrypt.h in Headers */,
DACE2F6E19BA6A2A0010F92E /* UIView+FontScale.h in Headers */, DACE2F6E19BA6A2A0010F92E /* UIView+FontScale.h in Headers */,
DA32D01B19D046E1004F3F0E /* PearlFixedTableView.h in Headers */,
DAFE4A3C15039824003ABA7C /* Pearl-UIKit-Dependencies.h in Headers */, DAFE4A3C15039824003ABA7C /* Pearl-UIKit-Dependencies.h in Headers */,
DAEC85B818E3DD9A007FC0DF /* UIView+Touches.h in Headers */, DAEC85B818E3DD9A007FC0DF /* UIView+Touches.h in Headers */,
DAFE4A3D15039824003ABA7C /* Pearl-UIKit.h in Headers */, DAFE4A3D15039824003ABA7C /* Pearl-UIKit.h in Headers */,
@@ -2755,6 +2863,23 @@
/* End PBXHeadersBuildPhase section */ /* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
DA32D01F19D111C6004F3F0E /* KCOrderedAccessorFix */ = {
isa = PBXNativeTarget;
buildConfigurationList = DA32D03419D111C7004F3F0E /* Build configuration list for PBXNativeTarget "KCOrderedAccessorFix" */;
buildPhases = (
DA32D01C19D111C6004F3F0E /* Sources */,
DA32D01D19D111C6004F3F0E /* Frameworks */,
DA32D01E19D111C6004F3F0E /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = KCOrderedAccessorFix;
productName = KCOrderedAccessorFix;
productReference = DA32D02019D111C6004F3F0E /* libKCOrderedAccessorFix.a */;
productType = "com.apple.product-type.library.static";
};
DA5BFA43147E415C00F98B1E /* MasterPassword */ = { DA5BFA43147E415C00F98B1E /* MasterPassword */ = {
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = DA5BFA6D147E415C00F98B1E /* Build configuration list for PBXNativeTarget "MasterPassword" */; buildConfigurationList = DA5BFA6D147E415C00F98B1E /* Build configuration list for PBXNativeTarget "MasterPassword" */;
@@ -2851,6 +2976,9 @@
LastUpgradeCheck = 0600; LastUpgradeCheck = 0600;
ORGANIZATIONNAME = Lyndir; ORGANIZATIONNAME = Lyndir;
TargetAttributes = { TargetAttributes = {
DA32D01F19D111C6004F3F0E = {
CreatedOnToolsVersion = 6.0;
};
DA5BFA43147E415C00F98B1E = { DA5BFA43147E415C00F98B1E = {
DevelopmentTeam = HL3Q45LX9N; DevelopmentTeam = HL3Q45LX9N;
SystemCapabilities = { SystemCapabilities = {
@@ -2860,6 +2988,12 @@
com.apple.DataProtection = { com.apple.DataProtection = {
enabled = 1; enabled = 1;
}; };
com.apple.Keychain = {
enabled = 0;
};
com.apple.iCloud = {
enabled = 0;
};
}; };
}; };
}; };
@@ -2966,6 +3100,7 @@
DAC6325C1486805C0075AEA5 /* uicolor-utilities */, DAC6325C1486805C0075AEA5 /* uicolor-utilities */,
DAC6326B148680650075AEA5 /* jrswizzle */, DAC6326B148680650075AEA5 /* jrswizzle */,
DAFC5654172C573B00CB5CC5 /* InAppSettingsKit */, DAFC5654172C573B00CB5CC5 /* InAppSettingsKit */,
DA32D01F19D111C6004F3F0E /* KCOrderedAccessorFix */,
); );
}; };
/* End PBXProject section */ /* End PBXProject section */
@@ -2978,15 +3113,18 @@
DAFE4A5A1503982E003ABA7C /* Pearl.strings in Resources */, DAFE4A5A1503982E003ABA7C /* Pearl.strings in Resources */,
DACA296F1705DF81002C6C22 /* Crashlytics.plist in Resources */, DACA296F1705DF81002C6C22 /* Crashlytics.plist in Resources */,
DACA29731705E1A8002C6C22 /* ciphers.plist in Resources */, DACA29731705E1A8002C6C22 /* ciphers.plist in Resources */,
DA32D04F19D2F59B004F3F0E /* meter_fuel@2x.png in Resources */,
DA250A0F1956484D00AC23F1 /* image-1@2x.png in Resources */, DA250A0F1956484D00AC23F1 /* image-1@2x.png in Resources */,
DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */, DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */,
DA45224C190628B2008F650A /* icon_gear@2x.png in Resources */, DA45224C190628B2008F650A /* icon_gear@2x.png in Resources */,
DA854C8318D4CFBF00106317 /* avatar-add@2x.png in Resources */, DA854C8318D4CFBF00106317 /* avatar-add@2x.png in Resources */,
DA45224A190628A1008F650A /* icon_wrench@2x.png in Resources */, DA45224A190628A1008F650A /* icon_wrench@2x.png in Resources */,
DA29993419C9214600AF7DF1 /* icon_star-hollow@2x.png in Resources */,
DA071BF4190187FE00179766 /* empty.png in Resources */, DA071BF4190187FE00179766 /* empty.png in Resources */,
DA69540617D975D900BF294E /* icon_gears.png in Resources */, DA69540617D975D900BF294E /* icon_gears.png in Resources */,
DA67460D18DE7F0C00DFE240 /* Exo2.0-Thin.otf in Resources */, DA67460D18DE7F0C00DFE240 /* Exo2.0-Thin.otf in Resources */,
DA4522451902355C008F650A /* icon_book@2x.png in Resources */, DA4522451902355C008F650A /* icon_book@2x.png in Resources */,
DA32D04919D2F417004F3F0E /* thumb_fuel@2x.png in Resources */,
DABD39371711E29700CF925C /* avatar-0.png in Resources */, DABD39371711E29700CF925C /* avatar-0.png in Resources */,
DABD39381711E29700CF925C /* avatar-0@2x.png in Resources */, DABD39381711E29700CF925C /* avatar-0@2x.png in Resources */,
DA250A041956484D00AC23F1 /* image-7.png in Resources */, DA250A041956484D00AC23F1 /* image-7.png in Resources */,
@@ -3021,30 +3159,39 @@
DABD394A1711E29700CF925C /* avatar-18.png in Resources */, DABD394A1711E29700CF925C /* avatar-18.png in Resources */,
DABD394B1711E29700CF925C /* avatar-18@2x.png in Resources */, DABD394B1711E29700CF925C /* avatar-18@2x.png in Resources */,
DABD394C1711E29700CF925C /* avatar-1@2x.png in Resources */, DABD394C1711E29700CF925C /* avatar-1@2x.png in Resources */,
DA32D04319D27093004F3F0E /* thumb_generated_answers@2x.png in Resources */,
DA29993319C9214600AF7DF1 /* icon_star-hollow.png in Resources */,
DA29993019C86F5700AF7DF1 /* thumb_generated_login.png in Resources */,
DA071BF3190187FE00179766 /* empty@2x.png in Resources */, DA071BF3190187FE00179766 /* empty@2x.png in Resources */,
DA32D04219D27093004F3F0E /* thumb_generated_answers@3x.png in Resources */,
DA67460E18DE7F0C00DFE240 /* Exo2.0-Regular.otf in Resources */, DA67460E18DE7F0C00DFE240 /* Exo2.0-Regular.otf in Resources */,
DABD394D1711E29700CF925C /* avatar-2.png in Resources */, DABD394D1711E29700CF925C /* avatar-2.png in Resources */,
DABD394E1711E29700CF925C /* avatar-2@2x.png in Resources */, DABD394E1711E29700CF925C /* avatar-2@2x.png in Resources */,
DA250A061956484D00AC23F1 /* image-6.png in Resources */, DA250A061956484D00AC23F1 /* image-6.png in Resources */,
DA32D04A19D2F417004F3F0E /* thumb_fuel.png in Resources */,
DABD394F1711E29700CF925C /* avatar-3.png in Resources */, DABD394F1711E29700CF925C /* avatar-3.png in Resources */,
DA67460F18DE7F0C00DFE240 /* Exo2.0-ExtraBold.otf in Resources */, DA67460F18DE7F0C00DFE240 /* Exo2.0-ExtraBold.otf in Resources */,
DABD39501711E29700CF925C /* avatar-3@2x.png in Resources */, DABD39501711E29700CF925C /* avatar-3@2x.png in Resources */,
DA25C600197DBF260046CDCF /* icon_trash.png in Resources */, DA25C600197DBF260046CDCF /* icon_trash.png in Resources */,
DA32D05219D3D107004F3F0E /* icon_meter@2x.png in Resources */,
DABD39511711E29700CF925C /* avatar-4.png in Resources */, DABD39511711E29700CF925C /* avatar-4.png in Resources */,
DA2509FD1956484D00AC23F1 /* image-10@2x.png in Resources */, DA2509FD1956484D00AC23F1 /* image-10@2x.png in Resources */,
DABD39521711E29700CF925C /* avatar-4@2x.png in Resources */, DABD39521711E29700CF925C /* avatar-4@2x.png in Resources */,
DABD39531711E29700CF925C /* avatar-5.png in Resources */, DABD39531711E29700CF925C /* avatar-5.png in Resources */,
DA73049E194E022700E72520 /* ui_spinner@2x.png in Resources */, DA73049E194E022700E72520 /* ui_spinner@2x.png in Resources */,
DABD39541711E29700CF925C /* avatar-5@2x.png in Resources */, DABD39541711E29700CF925C /* avatar-5@2x.png in Resources */,
DA32D05019D2F59B004F3F0E /* meter_fuel.png in Resources */,
DA250A031956484D00AC23F1 /* image-7@2x.png in Resources */, DA250A031956484D00AC23F1 /* image-7@2x.png in Resources */,
DA25C5FA197CCAE00046CDCF /* icon_delete.png in Resources */, DA25C5FA197CCAE00046CDCF /* icon_delete.png in Resources */,
DA25C601197DBF260046CDCF /* icon_trash@2x.png in Resources */, DA25C601197DBF260046CDCF /* icon_trash@2x.png in Resources */,
DABD39551711E29700CF925C /* avatar-6.png in Resources */, DABD39551711E29700CF925C /* avatar-6.png in Resources */,
DA32D00919CF5C55004F3F0E /* icon_question.png in Resources */,
DABD39561711E29700CF925C /* avatar-6@2x.png in Resources */, DABD39561711E29700CF925C /* avatar-6@2x.png in Resources */,
DABD39571711E29700CF925C /* avatar-7.png in Resources */, DABD39571711E29700CF925C /* avatar-7.png in Resources */,
DABD39581711E29700CF925C /* avatar-7@2x.png in Resources */, DABD39581711E29700CF925C /* avatar-7@2x.png in Resources */,
DABD39591711E29700CF925C /* avatar-8.png in Resources */, DABD39591711E29700CF925C /* avatar-8.png in Resources */,
DA250A0D1956484D00AC23F1 /* image-2@2x.png in Resources */, DA250A0D1956484D00AC23F1 /* image-2@2x.png in Resources */,
DA32D00A19CF5C55004F3F0E /* icon_question@2x.png in Resources */,
DA250A051956484D00AC23F1 /* image-6@2x.png in Resources */, DA250A051956484D00AC23F1 /* image-6@2x.png in Resources */,
DABD395A1711E29700CF925C /* avatar-8@2x.png in Resources */, DABD395A1711E29700CF925C /* avatar-8@2x.png in Resources */,
DABD395B1711E29700CF925C /* avatar-9.png in Resources */, DABD395B1711E29700CF925C /* avatar-9.png in Resources */,
@@ -3053,6 +3200,7 @@
DA45224719062899008F650A /* icon_settings.png in Resources */, DA45224719062899008F650A /* icon_settings.png in Resources */,
DABD395E1711E29700CF925C /* background@2x.png in Resources */, DABD395E1711E29700CF925C /* background@2x.png in Resources */,
DA945C8717E3F3FD0053236B /* Images.xcassets in Resources */, DA945C8717E3F3FD0053236B /* Images.xcassets in Resources */,
DA32D04E19D2F59B004F3F0E /* meter_fuel@3x.png in Resources */,
DA250A101956484D00AC23F1 /* image-1.png in Resources */, DA250A101956484D00AC23F1 /* image-1.png in Resources */,
DA25C5FE197DBF200046CDCF /* icon_thumbs-up.png in Resources */, DA25C5FE197DBF200046CDCF /* icon_thumbs-up.png in Resources */,
DABD39871711E29700CF925C /* SourceCodePro-Black.otf in Resources */, DABD39871711E29700CF925C /* SourceCodePro-Black.otf in Resources */,
@@ -3062,6 +3210,8 @@
DABD39A11711E29700CF925C /* icon_action@2x.png in Resources */, DABD39A11711E29700CF925C /* icon_action@2x.png in Resources */,
DABD39F21711E29700CF925C /* icon_cancel.png in Resources */, DABD39F21711E29700CF925C /* icon_cancel.png in Resources */,
DA25C5FB197CCAE00046CDCF /* icon_delete@2x.png in Resources */, DA25C5FB197CCAE00046CDCF /* icon_delete@2x.png in Resources */,
DA29993219C9132F00AF7DF1 /* thumb_generated_login@3x.png in Resources */,
DA29992F19C86F5700AF7DF1 /* thumb_generated_login@2x.png in Resources */,
DA73049F194E022B00E72520 /* ui_textfield.png in Resources */, DA73049F194E022B00E72520 /* ui_textfield.png in Resources */,
DABD39F31711E29700CF925C /* icon_cancel@2x.png in Resources */, DABD39F31711E29700CF925C /* icon_cancel@2x.png in Resources */,
DA250A0C1956484D00AC23F1 /* image-3.png in Resources */, DA250A0C1956484D00AC23F1 /* image-3.png in Resources */,
@@ -3080,11 +3230,11 @@
DABD3ABF1711E29800CF925C /* icon_plus@2x.png in Resources */, DABD3ABF1711E29800CF925C /* icon_plus@2x.png in Resources */,
DA69540717D975D900BF294E /* icon_gears@2x.png in Resources */, DA69540717D975D900BF294E /* icon_gears@2x.png in Resources */,
DABD3B1C1711E29800CF925C /* icon_up.png in Resources */, DABD3B1C1711E29800CF925C /* icon_up.png in Resources */,
DA32D04419D27093004F3F0E /* thumb_generated_answers.png in Resources */,
DABD3B1D1711E29800CF925C /* icon_up@2x.png in Resources */, DABD3B1D1711E29800CF925C /* icon_up@2x.png in Resources */,
DA3BCFCB19BD09D5006B2681 /* SourceCodePro-Regular.otf in Resources */, DA3BCFCB19BD09D5006B2681 /* SourceCodePro-Regular.otf in Resources */,
DA250A121956484D00AC23F1 /* image-0.png in Resources */, DA250A121956484D00AC23F1 /* image-0.png in Resources */,
DA4522441902355C008F650A /* icon_book.png in Resources */, DA4522441902355C008F650A /* icon_book.png in Resources */,
DABD3B8A1711E29800CF925C /* help.html in Resources */,
DA2509FF1956484D00AC23F1 /* image-9@2x.png in Resources */, DA2509FF1956484D00AC23F1 /* image-9@2x.png in Resources */,
DABD3B8D1711E29800CF925C /* keypad.png in Resources */, DABD3B8D1711E29800CF925C /* keypad.png in Resources */,
DABD3B8E1711E29800CF925C /* logo-bare.png in Resources */, DABD3B8E1711E29800CF925C /* logo-bare.png in Resources */,
@@ -3097,6 +3247,7 @@
DABD3B971711E29800CF925C /* pull-up.png in Resources */, DABD3B971711E29800CF925C /* pull-up.png in Resources */,
DABD3B981711E29800CF925C /* pull-up@2x.png in Resources */, DABD3B981711E29800CF925C /* pull-up@2x.png in Resources */,
DA7304A0194E022B00E72520 /* ui_textfield@2x.png in Resources */, DA7304A0194E022B00E72520 /* ui_textfield@2x.png in Resources */,
DA32D04819D2F417004F3F0E /* thumb_fuel@3x.png in Resources */,
DA452249190628A1008F650A /* icon_wrench.png in Resources */, DA452249190628A1008F650A /* icon_wrench.png in Resources */,
DA45224819062899008F650A /* icon_settings@2x.png in Resources */, DA45224819062899008F650A /* icon_settings@2x.png in Resources */,
DA250A001956484D00AC23F1 /* image-9.png in Resources */, DA250A001956484D00AC23F1 /* image-9.png in Resources */,
@@ -3104,6 +3255,7 @@
DABD3C241711E2DC00CF925C /* MasterPassword.entitlements in Resources */, DABD3C241711E2DC00CF925C /* MasterPassword.entitlements in Resources */,
DABD3C251711E2DC00CF925C /* Settings.bundle in Resources */, DABD3C251711E2DC00CF925C /* Settings.bundle in Resources */,
DABD3C261711E2DC00CF925C /* InfoPlist.strings in Resources */, DABD3C261711E2DC00CF925C /* InfoPlist.strings in Resources */,
DA32D05119D3D107004F3F0E /* icon_meter.png in Resources */,
DA25C5F8197AFFB40046CDCF /* icon_tools.png in Resources */, DA25C5F8197AFFB40046CDCF /* icon_tools.png in Resources */,
DA250A0B1956484D00AC23F1 /* image-3@2x.png in Resources */, DA250A0B1956484D00AC23F1 /* image-3@2x.png in Resources */,
DABD3FCA1712446200CF925C /* cloud.png in Resources */, DABD3FCA1712446200CF925C /* cloud.png in Resources */,
@@ -3160,6 +3312,14 @@
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */
DA32D01C19D111C6004F3F0E /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
DA32D03C19D111EB004F3F0E /* NSManagedObjectModel+KCOrderedAccessorFix.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
DA5BFA40147E415C00F98B1E /* Sources */ = { DA5BFA40147E415C00F98B1E /* Sources */ = {
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@@ -3172,35 +3332,41 @@
DABD3C021711E2DC00CF925C /* MPAppDelegate_Store.m in Sources */, DABD3C021711E2DC00CF925C /* MPAppDelegate_Store.m in Sources */,
DABD3C031711E2DC00CF925C /* MPConfig.m in Sources */, DABD3C031711E2DC00CF925C /* MPConfig.m in Sources */,
DABD3C071711E2DC00CF925C /* MPEntities.m in Sources */, DABD3C071711E2DC00CF925C /* MPEntities.m in Sources */,
DA32CFF319CF1C8F004F3F0E /* MPSiteEntity.m in Sources */,
DABD3C081711E2DC00CF925C /* MPKey.m in Sources */, DABD3C081711E2DC00CF925C /* MPKey.m in Sources */,
DABD3C141711E2DC00CF925C /* MasterPassword.xcdatamodeld in Sources */,
DABD3C151711E2DC00CF925C /* MPiOSAppDelegate.m in Sources */, DABD3C151711E2DC00CF925C /* MPiOSAppDelegate.m in Sources */,
DABD3C1C1711E2DC00CF925C /* MPGuideViewController.m in Sources */, DABD3C1C1711E2DC00CF925C /* MPGuideViewController.m in Sources */,
DABD3C1E1711E2DC00CF925C /* MPPreferencesViewController.m in Sources */, DABD3C1E1711E2DC00CF925C /* MPPreferencesViewController.m in Sources */,
DABD3C1F1711E2DC00CF925C /* MPTypeViewController.m in Sources */, DABD3C1F1711E2DC00CF925C /* MPTypeViewController.m in Sources */,
DABD3C211711E2DC00CF925C /* MPiOSConfig.m in Sources */, DABD3C211711E2DC00CF925C /* MPiOSConfig.m in Sources */,
DABD3C271711E2DC00CF925C /* main.m in Sources */, DABD3C271711E2DC00CF925C /* main.m in Sources */,
DADB4EC719C66FB60065A78D /* MPUserEntity.m in Sources */,
93D39F8A9254177891F38705 /* MPSetupViewController.m in Sources */, 93D39F8A9254177891F38705 /* MPSetupViewController.m in Sources */,
DA095E75172F4CD8001C948B /* MPLogsViewController.m in Sources */, DA095E75172F4CD8001C948B /* MPLogsViewController.m in Sources */,
93D39D596A2E376D6F6F5DA1 /* MPCombinedViewController.m in Sources */, 93D39D596A2E376D6F6F5DA1 /* MPCombinedViewController.m in Sources */,
DADB4ECD19C66FB60065A78D /* MPElementGeneratedEntity.m in Sources */, DA32CFF419CF1C8F004F3F0E /* MPGeneratedSiteEntity.m in Sources */,
93D3957237D303DE2D38C267 /* MPAvatarCell.m in Sources */, 93D3957237D303DE2D38C267 /* MPAvatarCell.m in Sources */,
93D39B8F90F58A5D158DDBA3 /* MPPasswordsViewController.m in Sources */, 93D39B8F90F58A5D158DDBA3 /* MPPasswordsViewController.m in Sources */,
DADB4ED019C66FB70065A78D /* MPElementStoredEntity.m in Sources */, DA32D05519D741DC004F3F0E /* MPSiteQuestionEntity.m in Sources */,
93D3954FCE045A3CC7E804B7 /* MPUsersViewController.m in Sources */, 93D3954FCE045A3CC7E804B7 /* MPUsersViewController.m in Sources */,
93D39392DEDA376F93C6C718 /* MPCell.m in Sources */, 93D39392DEDA376F93C6C718 /* MPCell.m in Sources */,
93D39A5FF670957C0AF8298D /* MPPasswordCell.m in Sources */, 93D39A5FF670957C0AF8298D /* MPPasswordCell.m in Sources */,
93D398ECD7D1A0DEDDADF516 /* MPEmergencyViewController.m in Sources */, 93D398ECD7D1A0DEDDADF516 /* MPEmergencyViewController.m in Sources */,
93D391ED37C9F687FA51EAA1 /* MPEmergencySegue.m in Sources */, DA32D00819CF4735004F3F0E /* MasterPassword.xcdatamodeld in Sources */,
93D394B5036C882B33C71872 /* MPPasswordsSegue.m in Sources */, 93D394B5036C882B33C71872 /* MPPasswordsSegue.m in Sources */,
93D39673DDC085BE72C34D7C /* MPPopdownSegue.m in Sources */, 93D39673DDC085BE72C34D7C /* MPPopdownSegue.m in Sources */,
93D39BA1EA3CAAC8A220B4A6 /* MPAppSettingsViewController.m in Sources */, 93D39BA1EA3CAAC8A220B4A6 /* MPAppSettingsViewController.m in Sources */,
93D396D8B67DA6522CDBA142 /* MPCoachmarkViewController.m in Sources */, 93D396D8B67DA6522CDBA142 /* MPCoachmarkViewController.m in Sources */,
DADB4ECA19C66FB60065A78D /* MPElementEntity.m in Sources */,
93D39EAA4D064193074D3021 /* MPFixable.m in Sources */, 93D39EAA4D064193074D3021 /* MPFixable.m in Sources */,
DA32CFF119CF1C8F004F3F0E /* MPStoredSiteEntity.m in Sources */,
93D394982CBD25D46692DD7C /* MPWebViewController.m in Sources */, 93D394982CBD25D46692DD7C /* MPWebViewController.m in Sources */,
93D39D8F78978196D6ABDEDE /* MPNavigationController.m in Sources */, 93D39D8F78978196D6ABDEDE /* MPNavigationController.m in Sources */,
93D3939661CE37180AF7CD6A /* MPStoreViewController.m in Sources */,
DA32CFF019CF1C8F004F3F0E /* MPUserEntity.m in Sources */,
93D39D38356F59DBEF934D70 /* MPAppDelegate_InApp.m in Sources */,
93D390C1B93F9D3AE37DD0A5 /* MPAnswersViewController.m in Sources */,
93D399D7E08A142776A74CB8 /* MPOverlayViewController.m in Sources */,
93D39A27F2506C6FEEF9C588 /* MPAlgorithmV2.m in Sources */,
93D39B429C67A62E29DC02DA /* MPRootSegue.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -3252,6 +3418,7 @@
DAFE4A4715039824003ABA7C /* PearlLayout.m in Sources */, DAFE4A4715039824003ABA7C /* PearlLayout.m in Sources */,
DA250A19195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m in Sources */, DA250A19195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m in Sources */,
DAFE4A4915039824003ABA7C /* PearlLayoutView.m in Sources */, DAFE4A4915039824003ABA7C /* PearlLayoutView.m in Sources */,
DAE2726319CE9CB3007C5262 /* UITableViewCell+PearlDeque.m in Sources */,
DAFE4A4B15039824003ABA7C /* PearlMessageView.m in Sources */, DAFE4A4B15039824003ABA7C /* PearlMessageView.m in Sources */,
DACE2F6519BA6A0A0010F92E /* PearlProfiler.m in Sources */, DACE2F6519BA6A0A0010F92E /* PearlProfiler.m in Sources */,
DAFE4A4D15039824003ABA7C /* PearlRootViewController.m in Sources */, DAFE4A4D15039824003ABA7C /* PearlRootViewController.m in Sources */,
@@ -3283,6 +3450,7 @@
93D39262A8A97DB748213309 /* PearlEMail.m in Sources */, 93D39262A8A97DB748213309 /* PearlEMail.m in Sources */,
DA3509FF15F101A500C14A8E /* PearlQueue.m in Sources */, DA3509FF15F101A500C14A8E /* PearlQueue.m in Sources */,
93D3922A53E41A54832E90D9 /* PearlOverlay.m in Sources */, 93D3922A53E41A54832E90D9 /* PearlOverlay.m in Sources */,
DA32D01A19D046E1004F3F0E /* PearlFixedTableView.m in Sources */,
93D396AA30690B256F30378A /* PearlNavigationController.m in Sources */, 93D396AA30690B256F30378A /* PearlNavigationController.m in Sources */,
93D397952F5635C793C24DF1 /* NSError+PearlFullDescription.m in Sources */, 93D397952F5635C793C24DF1 /* NSError+PearlFullDescription.m in Sources */,
DA2CA4DF18D28859007798F8 /* NSTimer+PearlBlock.m in Sources */, DA2CA4DF18D28859007798F8 /* NSTimer+PearlBlock.m in Sources */,
@@ -3347,6 +3515,24 @@
/* End PBXVariantGroup section */ /* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
DA32D02E19D111C7004F3F0E /* Debug-iOS */ = {
isa = XCBuildConfiguration;
buildSettings = {
};
name = "Debug-iOS";
};
DA32D02F19D111C7004F3F0E /* AdHoc-iOS */ = {
isa = XCBuildConfiguration;
buildSettings = {
};
name = "AdHoc-iOS";
};
DA32D03019D111C7004F3F0E /* AppStore-iOS */ = {
isa = XCBuildConfiguration;
buildSettings = {
};
name = "AppStore-iOS";
};
DA5BFA6B147E415C00F98B1E /* Debug-iOS */ = { DA5BFA6B147E415C00F98B1E /* Debug-iOS */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
@@ -3509,7 +3695,6 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CODE_SIGN_ENTITLEMENTS = MasterPassword.entitlements;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"\"$(SRCROOT)/../../../External/iOS\"", "\"$(SRCROOT)/../../../External/iOS\"",
@@ -3539,7 +3724,6 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CODE_SIGN_ENTITLEMENTS = MasterPassword.entitlements;
COPY_PHASE_STRIP = YES; COPY_PHASE_STRIP = YES;
EXCLUDED_SOURCE_FILE_NAMES = libDCIntrospect.a; EXCLUDED_SOURCE_FILE_NAMES = libDCIntrospect.a;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
@@ -3647,7 +3831,6 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CODE_SIGN_ENTITLEMENTS = MasterPassword.entitlements;
COPY_PHASE_STRIP = YES; COPY_PHASE_STRIP = YES;
EXCLUDED_SOURCE_FILE_NAMES = libDCIntrospect.a; EXCLUDED_SOURCE_FILE_NAMES = libDCIntrospect.a;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
@@ -3701,11 +3884,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CLANG_ENABLE_OBJC_ARC = NO; CLANG_ENABLE_OBJC_ARC = NO;
DSTROOT = /tmp/jrswizzle.dst;
GCC_WARN_INHIBIT_ALL_WARNINGS = YES; GCC_WARN_INHIBIT_ALL_WARNINGS = YES;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
}; };
name = "AppStore-iOS"; name = "AppStore-iOS";
}; };
@@ -3737,11 +3916,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CLANG_ENABLE_OBJC_ARC = NO; CLANG_ENABLE_OBJC_ARC = NO;
DSTROOT = /tmp/jrswizzle.dst;
GCC_WARN_INHIBIT_ALL_WARNINGS = YES; GCC_WARN_INHIBIT_ALL_WARNINGS = YES;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
}; };
name = "Debug-iOS"; name = "Debug-iOS";
}; };
@@ -3749,11 +3924,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CLANG_ENABLE_OBJC_ARC = NO; CLANG_ENABLE_OBJC_ARC = NO;
DSTROOT = /tmp/jrswizzle.dst;
GCC_WARN_INHIBIT_ALL_WARNINGS = YES; GCC_WARN_INHIBIT_ALL_WARNINGS = YES;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
}; };
name = "AdHoc-iOS"; name = "AdHoc-iOS";
}; };
@@ -3816,6 +3987,16 @@
/* End XCBuildConfiguration section */ /* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */ /* Begin XCConfigurationList section */
DA32D03419D111C7004F3F0E /* Build configuration list for PBXNativeTarget "KCOrderedAccessorFix" */ = {
isa = XCConfigurationList;
buildConfigurations = (
DA32D02E19D111C7004F3F0E /* Debug-iOS */,
DA32D02F19D111C7004F3F0E /* AdHoc-iOS */,
DA32D03019D111C7004F3F0E /* AppStore-iOS */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = "AdHoc-iOS";
};
DA5BFA3E147E415C00F98B1E /* Build configuration list for PBXProject "MasterPassword-iOS" */ = { DA5BFA3E147E415C00F98B1E /* Build configuration list for PBXProject "MasterPassword-iOS" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (
@@ -3879,16 +4060,17 @@
/* End XCConfigurationList section */ /* End XCConfigurationList section */
/* Begin XCVersionGroup section */ /* Begin XCVersionGroup section */
DABD3BD01711E2DC00CF925C /* MasterPassword.xcdatamodeld */ = { DA32D00119CF4735004F3F0E /* MasterPassword.xcdatamodeld */ = {
isa = XCVersionGroup; isa = XCVersionGroup;
children = ( children = (
DABD3BD11711E2DC00CF925C /* MasterPassword 1.xcdatamodel */, DA32D00219CF4735004F3F0E /* MasterPassword 1.xcdatamodel */,
DABD3BD21711E2DC00CF925C /* MasterPassword 2.xcdatamodel */, DA32D00319CF4735004F3F0E /* MasterPassword 2.xcdatamodel */,
DABD3BD31711E2DC00CF925C /* MasterPassword 3.xcdatamodel */, DA32D00419CF4735004F3F0E /* MasterPassword 3.xcdatamodel */,
DABD3BD41711E2DC00CF925C /* MasterPassword 4.xcdatamodel */, DA32D00519CF4735004F3F0E /* MasterPassword 4.xcdatamodel */,
DA62140919C66A9700375240 /* MasterPassword 5.xcdatamodel */, DA32D00619CF4735004F3F0E /* MasterPassword 5.xcdatamodel */,
DA32D00719CF4735004F3F0E /* MasterPassword 6.xcdatamodel */,
); );
currentVersion = DA62140919C66A9700375240 /* MasterPassword 5.xcdatamodel */; currentVersion = DA32D00719CF4735004F3F0E /* MasterPassword 6.xcdatamodel */;
path = MasterPassword.xcdatamodeld; path = MasterPassword.xcdatamodeld;
sourceTree = "<group>"; sourceTree = "<group>";
versionGroupType = wrapper.xcdatamodel; versionGroupType = wrapper.xcdatamodel;

View File

@@ -1,17 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict/>
<key>com.apple.developer.ubiquity-container-identifiers</key>
<array>
<string>$(TeamIdentifierPrefix)com.lyndir.lhunath.MasterPassword</string>
<string>$(TeamIdentifierPrefix)com.lyndir.lhunath.MasterPassword.shared</string>
</array>
<key>com.apple.developer.ubiquity-kvstore-identifier</key>
<string>$(TeamIdentifierPrefix)com.lyndir.lhunath.MasterPassword.shared</string>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)com.lyndir.lhunath.MasterPassword</string>
</array>
</dict>
</plist> </plist>

File diff suppressed because it is too large Load Diff

View File

@@ -2,12 +2,18 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>MPElementGeneratedEntity</key> <key>MPGeneratedSiteEntity</key>
<dict> <dict>
<key>Login Name</key> <key>Login Name</key>
<array> <array>
<string>cvccvcvcv</string> <string>cvccvcvcv</string>
</array> </array>
<key>Phrase</key>
<array>
<string>cvcc cvc cvccvcv cvc</string>
<string>cvc cvccvcvcv cvcv</string>
<string>cv cvccv cvc cvcvccv</string>
</array>
<key>Maximum Security Password</key> <key>Maximum Security Password</key>
<array> <array>
<string>anoxxxxxxxxxxxxxxxxx</string> <string>anoxxxxxxxxxxxxxxxxx</string>
@@ -77,6 +83,8 @@
<string>@&amp;%?,=[]_:-+*$#!'^~;()/.</string> <string>@&amp;%?,=[]_:-+*$#!'^~;()/.</string>
<key>x</key> <key>x</key>
<string>AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&amp;*()</string> <string>AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&amp;*()</string>
<key> </key>
<string> </string>
</dict> </dict>
</dict> </dict>
</plist> </plist>

View File

@@ -1,342 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
color: white;
text-align: center;
text-shadow: 0 1px black;
font: 16px "Baskerville";
}
h1, h2 {
margin-top: 1.5em;
padding-top: 1em;
font-family: inherit;
font-weight: bold;
}
h2 {
font-size: inherit;
}
h3 {
padding-top: 1.5em;
font-size: 12px;
}
i {
font-weight: bold;
}
q {
font-style: italic;
}
img {
display: inline-block;
height: 1.4em;
margin: -0.2em 0;
vertical-align: middle;
}
a, a:link {
color: inherit;
font-weight: bold;
}
header {
height: 8em;
padding: 3em 0 0;
}
header h1, header h2 {
margin: 0;
padding: 0.5ex;
}
header h3 {
padding-top: 2em;
}
</style>
<script src="jquery-1.6.1.min.js" type="text/javascript"></script>
<script type="text/javascript">
function setClass(activeClass) {
$(".Class").css("display", "none");
if (!$(".Class." + activeClass).length)
return "Not found: " + activeClass;
$(".Class." + activeClass).css("display", "block");
}
</script>
</head>
<body>
<header>
<h1>Master Password</h1>
<h2>by <a href="http://www.lyndir.com">Lyndir</a></h2>
<h3>&copy; 2011</h3>
</header>
<h2 id="1">&mdash; 1 &mdash;</h2>
<p>
<b>Find the site</b> that you need a password for by entering its name into the <i>search field</i>.
</p>
<p>
<b>While searching</b>, the names of previously used sites will be listed.<br />
Tap one of these results to go straight to its password.
</p>
<h2 id="2">&mdash; 2 &mdash;</h2>
<p>
<b>The site</b>'s password is now displayed.<br />
Tap it to <i>copy the password</i>. Once copied, you can switch to another application and paste it into a password field.
</p>
<p class="Class MPElementStoredEntity">
<b>To change</b> the password for this site, tap the <i>edit icon</i> <img src="icon_edit.png" />.
</p>
<p>
<b>Below the password</b> you can set the <i>password type</i>. Some types <i>create a password for you</i>,
others let you <i>choose your own</i>.
</p>
<p class="Class MPElementGeneratedEntity">
<b>If the site complains</b> when you try to set or update the password, try changing the password type.
</p>
<p class="Class MPElementGeneratedEntity">
<b>To create a new</b> password for this site, you can increment the <i>password counter</i> <img src="icon_plus.png" />.
This is useful, for example, after you've had to share the password with somebody else.
</p>
<h2 id="faq">&mdash; F.A.Q. &mdash;</h2>
<ol>
<li><a href="#what" >What is it and how do I use it?</a></li>
<li><a href="#why" >Why do I need Master Password?</a></li>
<li><a href="#custom" >A password was given to me.</a></li>
<li><a href="#loss" >What if I loose my device?</a></li>
<li><a href="#device" >Am I dependant on my device?</a></li>
<li><a href="#paranoid" >How do I maximize my security?</a></li>
<li><a href="#hacked" >A website I use got hacked!</a></li>
<li><a href="#forgot" >I forgot my master password!</a></li>
<li><a href="#algorithm">How does Master Password work?</a></li>
<li><a href="#branded" >Do you offer enterprise solutions?</a></li>
</ol>
<h3 id="what">What is Master Password and how do I use it?</h3>
<p>
Master Password <b>creates secure and unique passwords for you</b>, so you don't have to.<br />
The human brain is not well suited for creating secure and random passwords, and it's also terrible at remembering lots of unique passwords.
Master Password does the work for you: all you need to do is remember a single long and secure master password to log into the app.
</p>
<p>
<b>Begin by entering the name</b> of the thing you want a password for. Naming is entirely up to you, but remember to be consistent.<br />
<i>Good names</i> could be:<br />
<code>apple.com</code>, <code>john@doe.com</code>, <code>office safe</code>, <code>bike lock</code>, etc.
</p>
<p>
Every name has a different password, so the following names may be <i>difficult to recall</i>:<br />
<code>pw for amazon</code>, <code>pin for my cell</code>, etc.
</p>
<p>
<b>Tap the resulting password</b> to copy it for pasting in a different application or read it to type it in or use it manually elsewhere.
</p>
<p id="why">
The thought behind this application is to secure your online (and offline) life by <b>changing all of your passwords</b>
to passwords generated by this app.
</p>
<h3>That's crazy talk.<br />
Why would I do that?</h3>
<p>
The theory of password authentication is simple: To log in to a site, you share a secret word with the site
that <b>only you and the site know</b>. Since nobody else knows your secret password, nobody else can log
into your account.
</p>
<p>
It sounds good in theory. In practice, it's an <b>absolute hell</b>. These days, people have hundreds of
accounts on sites all over the Internet. Does that mean we're all remembering hundreds of secret passwords?
No, of course not. That would be impossible. If you're like most people, you remember one or two
passwords, and use those for all your sites everywhere.
</p>
<p>
<q>So, what?</q>, you might say.<br />
Here's the problem: When you share a secret password with a site, and then share the same secret password
with another site, both sites can now use the password you gave them to log into your account on the
<i>other</i> site. Nothing is stopping them from trying to log into <i>your</i> GMail, Hotmail or Twitter
accounts using the same password that you used to register an account on their site. Even if you only give
your password to sites you trust, all it takes is for one of those sites to get hacked and lose their
passwords database. Those hackers now have all it takes to impersonate you.
</p>
<p>
Some of you already try to remember unique-ish passwords for different sites. This causes problems too:
with so many passwords to remember, you easily forget passwords for sites you haven't used in a while. Or
you make up a simplification algorithm such as tacking your birth year onto the site name. This is really
not any more secure than using the same password for every site. And then there's those sites with
<q>password policies</q>: suddenly your long password isn't good enough, because it begins with a number,
or because (god forbid) it's <q>too long</q>. You now find yourself forced to create a strange variant
of your password that you'll have forgotten before the day is out.
</p>
<p>
This app <b>solves the problem</b> by letting you remember only a single password without requiring you to
share the password with anyone else. Instead, the app creates secure passwords for use with whatever site
or purpose you might need a password for.
</p>
<h3 id="custom">I can't change all my passwords.<br />
Some of them were assigned to me.</h3>
<p>
That's why this application allows you to change the password type to <code>Personal</code> or <code>Device
Private</code>. These types let you enter a password for a site, and the app will encrypt and save it so
you it's there for future reference.
</p>
<p>
These types of <q>stored</q> passwords don't have all the advantages that their generated counterparts have
(they can be lost if you lose your device and don't back it up), but when you can't change a site's
password to one generated by the app, this is as good as it gets.
</p>
<h3 id="loss">So, what if I lose my device?<br />
I'm locked out of everything?</h3>
<p>
<b>Absolutely not!</b> In fact, generated passwords aren't even stored on your device. No, not in the
cloud either. They're not stored anywhere! What that basically means is, if you grab the iPhone of a
colleague or friend and open this app on it, re-create your user and log in, <i>it'll give you all your
generated passwords</i>. So, if you lose your iPhone or forget it, just open the app on your iPad,
or borrow a friend's device, and you're back in business. No backups or restores needed.
</p>
<p>
This also means that, unlike all those apps that store your passwords or send them off to be stored on the
Internet, this app makes your passwords much safer from theft. If your device is stolen, the thieves can't
get at your passwords. There's also no cloud service that can be mis-managed or hacked.
</p>
<h3 id="device">Great, but that still means I need my device to get my passwords.</h3>
<p>
Correct. However, remember that usually you'll only need to use this app once for each site. After you log
into a site once using the password generated by this app, your browser will probably ask you to remember
the password for the future. Agree to that, and you won't need to bring up your device again the next time
you log in to the account.
</p>
<p>
There is also a <b>Mac version</b> of Master Password that will be released on the Mac App Store.
It allows you to generate any of your passwords without the need to bring out your device.
</p>
<h3 id="paranoid">I'm paranoid.<br />
How do I maximize my security?</h3>
<p>
The <b>most important</b> aspect to the security of your passwords is your <b>master password</b>. Make sure
you've chosen a <em>long and unique master password</em>. Master Password's algorithm makes it exceedingly
difficult for an attacker to try and guess your master password, but that doesn't make you invulnerable when
your master password is short or easy to guess. Ideally, your master password should be <em>longer than 10 characters</em>.
<b>An absurd sentence is a great idea</b>, especially if you add non-english or gibberish words to it.
Absurd sentences are long and high in entropy, but also particularly easy for the human brain to remember.
</p>
<p>
Armed with a good master password, your next step is to assign generated passwords to all of your sites.
By default, Master Password creates passwords that are secure and still easy to copy from your device to a
computer by keyboard. If you prefer, you can go into Master Password's preferences (using the top-right icon)
and change the default password type to <code>Maximum Security</code>. Any new sites will now generate
passwords that are even higher in entropy. These types of passwords are nigh impossible for an attacker to
brute-force (though a <code>Long Password</code> really is secure enough for most any purpose, see
<a href="#hacked">What if a site I use gets hacked?</a>).
</p>
<p>
Also check out the application's preferences (using the action icon on the top right, select <code>Preferences</code>).
Make sure that <code>Save Password</code> is disabled. Saving your password is a convenience feature that lets your
device save your master password so you don't need to enter it anymore. It also means that if somebody finds your device
somewhere or steals it, the only obstacle between them and your passwords are your device's PIN code (assuming you even
have one set).<br />
If you go into <code>Settings</code> from the <code>Preferences</code> page, you'll see some global application settings.
Make sure that <code>Stay logged in</code> is disabled here. If enabled, Master Password will not log you out when you
close the app. Your master password isn't saved on your device, but kept in memory for as long as your device remains
powered on. Again, a malignent person can easily get to your passwords if they find your device powered on and logged
into Master Password.
</p>
<h3 id="hacked">What if a site I use gets hacked?</h3>
<p>
There have been some high-profile password database leaks lately. LinkedIn, eHarmony, Last.fm, to name a few,
have lost millions of people's password hashes. In these cases, attackers have obtained a <q>hash</q> of
the passwords of all of these people, which makes it much easier for them to guess their real password.
A single sophisticated computer can be used to try about 200 million password combinations per second in an
attempt to find the real password behind a hash. That means these millions of people should be really worried
about their account's security.<br />
However, if your account is protected by a <code>Long Password</code> generated by Master Password, it would
take an attacker with ten sophisticated machines multiple lifetimes to find your actual password from a hash.
If the attacker knew beforehand that you had used Master Password to generate your password, he could make
his approach smarter and ten sophisticated machines would still take more than a year of constantly trying
millions of password combinations to find out your actual password.<br />
If instead you used a <code>Maximum Security</code> password to protect your account, the time it would take
for an attacker to brute-force your password goes completely off the scale: 10,000 sophisticated machines
would take up to 312409704477000000 years to try and find your password, even if the attacker knew you're
using Master Password.
</p>
<p>
If you're worried anyway or you need a new password for your site for some other reason, tap the password
counter button (the plus icon) to instantly create a new password for that site.
</p>
<p>
Long story short: When a website you use gets hacked and your password hashes are revealed to hackers, this
is a big problem for the security of your account, but only if you're <b>not</b> using Master Password.
</p>
<h3 id="forgot">I forgot my master password. What are my options?</h3>
<p>
Due to the nature of this app's algorithms and the decisions that were made to protect against brute-force
attacks, it is simply infeasible to recover your master password. If you really can't remember it, your
passwords are <b>gone</b>.
</p>
<p>
Where you go from here is: on the unlock screen, tap and hold your user. A dialog will pop-up that will allow
you to reset your master password. Assign a new master password, log in, and for each of your accounts, go
through the password recovery procedure (which will usually involve the site sending a mail to your email account)
and reset the passwords of these accounts to passwords generated by your newly chosen master password.<br />
Now don't forget it again! :-)
</p>
<h3 id="algorithm">So how does this thing work internally?</h3>
<p>
The way Master Password works internally is <a href="http://masterpassword.lyndir.com/algorithm.html">fully disclosed</a>.
The source code for this application is also available from <a href="https://github.com/Lyndir/MasterPassword">GitHub</a>.
I invite anyone with a technical background to go through these resources to make certain of the trustworthiness of Master Password.
</p>
<h3 id="outdated">Is the algorithm stable?<br />
Will my passwords ever change?</h3>
<p>
While we're very confident of the strength of the Master Password algorithm, we're also constantly keeping an eye out
for what the evolutions are of hackers' tools and capabilities. To give you the best possible protection, there is
always the possibility that we'll have to make tweaks to the Master Password algorithm in order to fend off any
attempts at breaking in.
</p>
<p>
Usually, these tweaks will be automatically applied when you install the latest version. In this case, you will notice
nothing and all you need to take away from this is that it's best to always be running the latest version of Master Password.
</p>
<p>
It is possible, however, that to apply an upgrade to your passwords, a new password will need to be set for your site's
account. In this case, Master Password will leave your passwords the way they are but give you the <em>option</em> of
upgrading your passwords when it's convenient to you. Whenever you're ready, just tap the upgrade password icon and
Master Password will show you the old password and the new one so that you can easily update your site's account.
</p>
<p>
<em>Please note</em>: if Master Password warns you that you have outdated passwords, it's best to upgrade them all
as soon as convenient. If you lose your device or data and recreate your Master Password user on another device,
Master Password can only regenerate the passwords for you that you've upgraded. iCloud/iTunes sync or exports are not
affected, so these are good ways to safely back up your passwords.
</p>
<p>
<a href="?outdated">Tap here</a> to check if you have any outdated passwords.
</p>
<h3 id="branded">This stuff is gold.<br />
I want one branded for our company.</h3>
<p>
<a href="mailto:masterpassword@lyndir.com">Contact me</a> directly for enterprise inquiries.
I can provide branded clients and enterprise distribution if your company is interested in deploying this solution internally.
</p>
<p>
Master Password can also be used as a One-Time Password token generator to secure your infrastructure and client access.
</p>
<footer>
<a href="http://masterpassword.lyndir.com">Homepage</a> | <a href="http://www.lyndir.com">Lyndir</a> |
<a href="http://www.lyndir.com/contact">Contact</a>
</footer>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 906 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Some files were not shown because too many files have changed in this diff Show More