diff --git a/.gitmodules b/.gitmodules
index 8b03e0d0..d50d17c4 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -10,3 +10,6 @@
[submodule "External/RHStatusItemView"]
path = External/RHStatusItemView
url = git://github.com/lhunath/RHStatusItemView.git
+[submodule "External/KCOrderedAccessorFix"]
+ path = External/KCOrderedAccessorFix
+ url = https://github.com/CFKevinRef/KCOrderedAccessorFix.git
diff --git a/External/KCOrderedAccessorFix b/External/KCOrderedAccessorFix
new file mode 160000
index 00000000..e1955221
--- /dev/null
+++ b/External/KCOrderedAccessorFix
@@ -0,0 +1 @@
+Subproject commit e1955221bf52d53736e7d3e7d38465c509e02562
diff --git a/External/iOS/Crashlytics.framework/Modules/module.modulemap b/External/iOS/Crashlytics.framework/Modules/module.modulemap
new file mode 100644
index 00000000..e552e9ca
--- /dev/null
+++ b/External/iOS/Crashlytics.framework/Modules/module.modulemap
@@ -0,0 +1,6 @@
+framework module Crashlytics {
+ umbrella header "Crashlytics.h"
+
+ export *
+ module * { export * }
+}
diff --git a/External/iOS/Crashlytics.framework/Versions/A/Crashlytics b/External/iOS/Crashlytics.framework/Versions/A/Crashlytics
index 552e04bb..301497d3 100644
Binary files a/External/iOS/Crashlytics.framework/Versions/A/Crashlytics and b/External/iOS/Crashlytics.framework/Versions/A/Crashlytics differ
diff --git a/External/iOS/Crashlytics.framework/Versions/A/Resources/Info.plist b/External/iOS/Crashlytics.framework/Versions/A/Resources/Info.plist
index 48c7bd50..14505b8a 100644
--- a/External/iOS/Crashlytics.framework/Versions/A/Resources/Info.plist
+++ b/External/iOS/Crashlytics.framework/Versions/A/Resources/Info.plist
@@ -15,13 +15,13 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.2.2
+ 2.2.4
CFBundleSupportedPlatforms
iPhoneOS
CFBundleVersion
- 36
+ 38
DTPlatformName
iphoneos
MinimumOSVersion
diff --git a/External/iOS/Crashlytics.framework/run b/External/iOS/Crashlytics.framework/run
index 42c401d3..9ab42065 100755
Binary files a/External/iOS/Crashlytics.framework/run and b/External/iOS/Crashlytics.framework/run differ
diff --git a/External/iOS/Crashlytics.framework/submit b/External/iOS/Crashlytics.framework/submit
index 3febb780..a74ed3bb 100755
Binary files a/External/iOS/Crashlytics.framework/submit and b/External/iOS/Crashlytics.framework/submit differ
diff --git a/External/iOS/Reveal.framework/Versions/A/Reveal b/External/iOS/Reveal.framework/Versions/A/Reveal
index 4ffdd56e..91b23296 100644
Binary files a/External/iOS/Reveal.framework/Versions/A/Reveal and b/External/iOS/Reveal.framework/Versions/A/Reveal differ
diff --git a/MasterPassword.xcworkspace/xcshareddata/MasterPassword.xccheckout b/MasterPassword.xcworkspace/xcshareddata/MasterPassword.xccheckout
index 27ca984b..f11d1ee6 100644
--- a/MasterPassword.xcworkspace/xcshareddata/MasterPassword.xccheckout
+++ b/MasterPassword.xcworkspace/xcshareddata/MasterPassword.xccheckout
@@ -10,57 +10,73 @@
MasterPassword
IDESourceControlProjectOriginsDictionary
+ 1712FC0BC3C9AABD8B7B5376E310E93FBDB3BCFA
+ git://github.com/lhunath/InAppSettingsKit.git
1AA8C0BE-EEC3-4FBC-A801-8939A1AC093A
git://github.com/Lyndir/love-lyndir.client.git
- 42C94803-87A2-403E-896C-D9AC3A807E1B
- git://github.com/lhunath/UbiquityStoreManager.git
- 6A449EC2-A2A3-4635-9C5F-A811E011EAC3
- ssh://github.com/Lyndir/MasterPassword.git
- ADA0D7F9-4871-4128-8FEE-FD1021EEF3AC
- ssh://github.com/Lyndir/Pearl.git
- AE3786C7-912B-4651-A73F-2E1DACBFB604
+ 2A70319CE0F91B35406CA7D970AE7CB4957B0A75
+ github.com:Lyndir/Lyndir.git
+ 2FE140B36B7D26140DC8D5E5C639DC5900EFCF35
git://github.com/lhunath/uicolor-utilities.git
- B0F634DD-AEE1-4F0D-AE35-4FAF51AD1B5A
+ 304AD0F97EA7B4893D91DFB8C3413D4E627B9472
+ https://github.com/CFKevinRef/KCOrderedAccessorFix.git
+ 3E67FB08419C920516AAC3B00DAAF23073B8CF77
git://github.com/lhunath/RHStatusItemView.git
- CDDE92CF-0136-4DE0-8318-80EDB5C8CAF9
- git://github.com/lhunath/InAppSettingsKit.git
- E4C8E206-229C-4DA8-A130-0C544DEC7E07
+ 4DDCFFD91B41F00326AD14553BD66CFD366ABD91
+ ssh://github.com/Lyndir/Pearl.git
+ 8A15A8EA0B3D0B497C4883425BC74DF995224BB3
git://github.com/jonmarimba/jrswizzle.git
+ E47DEC29CB0D0FDE3560EF46E1808FA1C723D657
+ git://github.com/lhunath/UbiquityStoreManager.git
+ F788B28042EDBEF29EFE34687DA79A778C2CC260
+ ssh://github.com/Lyndir/MasterPassword.git
IDESourceControlProjectPath
MasterPassword.xcworkspace
IDESourceControlProjectRelativeInstallPathDictionary
+ 1712FC0BC3C9AABD8B7B5376E310E93FBDB3BCFA
+ ../External/InAppSettingsKit
1AA8C0BE-EEC3-4FBC-A801-8939A1AC093A
../External/LoveLyndir
- 42C94803-87A2-403E-896C-D9AC3A807E1B
- ../External/UbiquityStoreManager
- 6A449EC2-A2A3-4635-9C5F-A811E011EAC3
- ..
- ADA0D7F9-4871-4128-8FEE-FD1021EEF3AC
- ../External/Pearl
- AE3786C7-912B-4651-A73F-2E1DACBFB604
+ 2A70319CE0F91B35406CA7D970AE7CB4957B0A75
+ ../..
+ 2FE140B36B7D26140DC8D5E5C639DC5900EFCF35
../External/Pearl/External/uicolor-utilities
- B0F634DD-AEE1-4F0D-AE35-4FAF51AD1B5A
+ 304AD0F97EA7B4893D91DFB8C3413D4E627B9472
+ ../External/KCOrderedAccessorFix/
+ 3E67FB08419C920516AAC3B00DAAF23073B8CF77
../External/RHStatusItemView
- CDDE92CF-0136-4DE0-8318-80EDB5C8CAF9
- ../External/InAppSettingsKit
- E4C8E206-229C-4DA8-A130-0C544DEC7E07
+ 4DDCFFD91B41F00326AD14553BD66CFD366ABD91
+ ../External/Pearl
+ 8A15A8EA0B3D0B497C4883425BC74DF995224BB3
../External/Pearl/External/jrswizzle
+ E47DEC29CB0D0FDE3560EF46E1808FA1C723D657
+ ../External/UbiquityStoreManager
+ F788B28042EDBEF29EFE34687DA79A778C2CC260
+ ..
IDESourceControlProjectURL
ssh://github.com/Lyndir/MasterPassword.git
IDESourceControlProjectVersion
- 110
+ 111
IDESourceControlProjectWCCIdentifier
- 6A449EC2-A2A3-4635-9C5F-A811E011EAC3
+ F788B28042EDBEF29EFE34687DA79A778C2CC260
IDESourceControlProjectWCConfigurations
IDESourceControlRepositoryExtensionIdentifierKey
public.vcs.git
IDESourceControlWCCIdentifierKey
- CDDE92CF-0136-4DE0-8318-80EDB5C8CAF9
+ 2A70319CE0F91B35406CA7D970AE7CB4957B0A75
+ IDESourceControlWCCName
+
+
+
+ IDESourceControlRepositoryExtensionIdentifierKey
+ public.vcs.git
+ IDESourceControlWCCIdentifierKey
+ 1712FC0BC3C9AABD8B7B5376E310E93FBDB3BCFA
IDESourceControlWCCName
InAppSettingsKit
@@ -68,10 +84,18 @@
IDESourceControlRepositoryExtensionIdentifierKey
public.vcs.git
IDESourceControlWCCIdentifierKey
- E4C8E206-229C-4DA8-A130-0C544DEC7E07
+ 8A15A8EA0B3D0B497C4883425BC74DF995224BB3
IDESourceControlWCCName
jrswizzle
+
+ IDESourceControlRepositoryExtensionIdentifierKey
+ public.vcs.git
+ IDESourceControlWCCIdentifierKey
+ 304AD0F97EA7B4893D91DFB8C3413D4E627B9472
+ IDESourceControlWCCName
+ KCOrderedAccessorFix
+
IDESourceControlRepositoryExtensionIdentifierKey
public.vcs.git
@@ -84,7 +108,7 @@
IDESourceControlRepositoryExtensionIdentifierKey
public.vcs.git
IDESourceControlWCCIdentifierKey
- 6A449EC2-A2A3-4635-9C5F-A811E011EAC3
+ F788B28042EDBEF29EFE34687DA79A778C2CC260
IDESourceControlWCCName
MasterPassword
@@ -92,7 +116,7 @@
IDESourceControlRepositoryExtensionIdentifierKey
public.vcs.git
IDESourceControlWCCIdentifierKey
- ADA0D7F9-4871-4128-8FEE-FD1021EEF3AC
+ 4DDCFFD91B41F00326AD14553BD66CFD366ABD91
IDESourceControlWCCName
Pearl
@@ -100,7 +124,7 @@
IDESourceControlRepositoryExtensionIdentifierKey
public.vcs.git
IDESourceControlWCCIdentifierKey
- B0F634DD-AEE1-4F0D-AE35-4FAF51AD1B5A
+ 3E67FB08419C920516AAC3B00DAAF23073B8CF77
IDESourceControlWCCName
RHStatusItemView
@@ -108,7 +132,7 @@
IDESourceControlRepositoryExtensionIdentifierKey
public.vcs.git
IDESourceControlWCCIdentifierKey
- 42C94803-87A2-403E-896C-D9AC3A807E1B
+ E47DEC29CB0D0FDE3560EF46E1808FA1C723D657
IDESourceControlWCCName
UbiquityStoreManager
@@ -116,7 +140,7 @@
IDESourceControlRepositoryExtensionIdentifierKey
public.vcs.git
IDESourceControlWCCIdentifierKey
- AE3786C7-912B-4651-A73F-2E1DACBFB604
+ 2FE140B36B7D26140DC8D5E5C639DC5900EFCF35
IDESourceControlWCCName
uicolor-utilities
diff --git a/MasterPassword/C/mpw.c b/MasterPassword/C/mpw.c
index d0ebbeee..a435887d 100644
--- a/MasterPassword/C/mpw.c
+++ b/MasterPassword/C/mpw.c
@@ -38,15 +38,21 @@ void usage() {
fprintf(stderr, " -u name Specify the full name of the user.\n"
" Defaults to %s in env.\n\n", MP_env_username);
fprintf(stderr, " -t type Specify the password's template.\n"
- " Defaults to %s in env or 'long'.\n"
+ " Defaults to %s in env or 'long' for password, 'name' for login.\n"
" x, max, maximum | 20 characters, contains symbols.\n"
" l, long | Copy-friendly, 14 characters, contains symbols.\n"
" m, med, medium | Copy-friendly, 8 characters, contains symbols.\n"
" b, basic | 8 characters, no symbols.\n"
" s, short | Copy-friendly, 4 characters, no symbols.\n"
- " p, pin | 4 numbers.\n\n", MP_env_sitetype);
+ " i, pin | 4 numbers.\n"
+ " 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"
" Defaults to %s in env or '1'.\n\n", MP_env_sitecounter);
+ fprintf(stderr, " -v variant The kind of content to generate.\n"
+ " Defaults to 'password'.\n"
+ " p, password | The password to log in with.\n"
+ " l, login | The username to log in as.\n\n");
exit(0);
}
@@ -86,12 +92,14 @@ int main(int argc, char *const argv[]) {
const char *siteName = NULL;
MPElementType siteType = MPElementTypeGeneratedLong;
const char *siteTypeString = getenv( MP_env_sitetype );
+ MPElementVariant siteVariant = MPElementVariantPassword;
+ const char *siteVariantString = NULL;
uint32_t siteCounter = 1;
const char *siteCounterString = getenv( MP_env_sitecounter );
// Read the options.
char opt;
- while ((opt = getopt(argc, argv, "u:t:c:h")) != -1)
+ while ((opt = getopt(argc, argv, "u:t:c:v:h")) != -1)
switch (opt) {
case 'h':
usage();
@@ -102,6 +110,9 @@ int main(int argc, char *const argv[]) {
case 't':
siteTypeString = optarg;
break;
+ case 'v':
+ siteVariantString = optarg;
+ break;
case 'c':
siteCounterString = optarg;
break;
@@ -144,6 +155,11 @@ int main(int argc, char *const argv[]) {
return 1;
}
trc("siteCounter: %d\n", siteCounter);
+ if (siteVariantString)
+ siteVariant = VariantWithName( siteVariantString );
+ trc("siteVariant: %d (%s)\n", siteVariant, siteVariantString);
+ if (siteVariant == MPElementVariantLogin)
+ siteType = MPElementTypeGeneratedName;
if (siteTypeString)
siteType = TypeWithName( siteTypeString );
trc("siteType: %d (%s)\n", siteType, siteTypeString);
@@ -176,9 +192,10 @@ int main(int argc, char *const argv[]) {
trc("masterPassword: %s\n", masterPassword);
// Calculate the master key salt.
- char *mpNameSpace = "com.lyndir.masterpassword";
+ const char *mpKeyScope = ScopeForVariant(MPElementVariantPassword);
+ trc("key scope: %s\n", mpKeyScope);
const uint32_t n_userNameLength = htonl(strlen(userName));
- size_t masterKeySaltLength = strlen(mpNameSpace) + sizeof(n_userNameLength) + strlen(userName);
+ size_t masterKeySaltLength = strlen(mpKeyScope) + sizeof(n_userNameLength) + strlen(userName);
char *masterKeySalt = malloc( masterKeySaltLength );
if (!masterKeySalt) {
fprintf(stderr, "Could not allocate master key salt: %d\n", errno);
@@ -186,7 +203,7 @@ int main(int argc, char *const argv[]) {
}
char *mKS = masterKeySalt;
- memcpy(mKS, mpNameSpace, strlen(mpNameSpace)); mKS += strlen(mpNameSpace);
+ memcpy(mKS, mpKeyScope, strlen(mpKeyScope)); mKS += strlen(mpKeyScope);
memcpy(mKS, &n_userNameLength, sizeof(n_userNameLength)); mKS += sizeof(n_userNameLength);
memcpy(mKS, userName, strlen(userName)); mKS += strlen(userName);
if (mKS - masterKeySalt != masterKeySaltLength)
@@ -210,9 +227,11 @@ int main(int argc, char *const argv[]) {
trc("masterKey ID: %s\n", IDForBuf(masterKey, MP_dkLen));
// Calculate the site seed.
+ const char *mpSiteScope = ScopeForVariant(siteVariant);
+ trc("site scope: %s\n", mpSiteScope);
const uint32_t n_siteNameLength = htonl(strlen(siteName));
const uint32_t n_siteCounter = htonl(siteCounter);
- size_t sitePasswordInfoLength = strlen(mpNameSpace) + sizeof(n_siteNameLength) + strlen(siteName) + sizeof(n_siteCounter);
+ size_t sitePasswordInfoLength = strlen(mpSiteScope) + sizeof(n_siteNameLength) + strlen(siteName) + sizeof(n_siteCounter);
char *sitePasswordInfo = malloc( sitePasswordInfoLength );
if (!sitePasswordInfo) {
fprintf(stderr, "Could not allocate site seed: %d\n", errno);
@@ -220,13 +239,13 @@ int main(int argc, char *const argv[]) {
}
char *sPI = sitePasswordInfo;
- memcpy(sPI, mpNameSpace, strlen(mpNameSpace)); sPI += strlen(mpNameSpace);
+ memcpy(sPI, mpSiteScope, strlen(mpSiteScope)); sPI += strlen(mpSiteScope);
memcpy(sPI, &n_siteNameLength, sizeof(n_siteNameLength)); sPI += sizeof(n_siteNameLength);
memcpy(sPI, siteName, strlen(siteName)); sPI += strlen(siteName);
memcpy(sPI, &n_siteCounter, sizeof(n_siteCounter)); sPI += sizeof(n_siteCounter);
if (sPI - sitePasswordInfo != sitePasswordInfoLength)
abort();
- trc("seed from: hmac-sha256(masterKey, 'com.lyndir.masterpassword' | %s | %s | %s)\n", Hex(&n_siteNameLength, sizeof(n_siteNameLength)), siteName, Hex(&n_siteCounter, sizeof(n_siteCounter)));
+ trc("seed from: hmac-sha256(masterKey, %s | %s | %s | %s)\n", mpSiteScope, Hex(&n_siteNameLength, sizeof(n_siteNameLength)), siteName, Hex(&n_siteCounter, sizeof(n_siteCounter)));
trc("sitePasswordInfo ID: %s\n", IDForBuf(sitePasswordInfo, sitePasswordInfoLength));
uint8_t sitePasswordSeed[32];
diff --git a/MasterPassword/C/types.c b/MasterPassword/C/types.c
index 2ec38089..fc387441 100644
--- a/MasterPassword/C/types.c
+++ b/MasterPassword/C/types.c
@@ -31,8 +31,12 @@ const MPElementType TypeWithName(const char *typeName) {
return MPElementTypeGeneratedBasic;
if (0 == strcmp(lowerTypeName, "s") || 0 == strcmp(lowerTypeName, "short"))
return MPElementTypeGeneratedShort;
- if (0 == strcmp(lowerTypeName, "p") || 0 == strcmp(lowerTypeName, "pin"))
+ if (0 == strcmp(lowerTypeName, "i") || 0 == strcmp(lowerTypeName, "pin"))
return MPElementTypeGeneratedPIN;
+ if (0 == strcmp(lowerTypeName, "n") || 0 == strcmp(lowerTypeName, "name"))
+ return MPElementTypeGeneratedName;
+ if (0 == strcmp(lowerTypeName, "p") || 0 == strcmp(lowerTypeName, "phrase"))
+ return MPElementTypeGeneratedPhrase;
fprintf(stderr, "Not a generated type name: %s", lowerTypeName);
abort();
@@ -67,6 +71,13 @@ const char *CipherForType(MPElementType type, uint8_t seedByte) {
case MPElementTypeGeneratedPIN: {
return "nnnn";
}
+ case MPElementTypeGeneratedName: {
+ return "cvccvcvcv";
+ }
+ case MPElementTypeGeneratedPhrase: {
+ char *ciphers[] = { "cvcc cvc cvccvcv cvc", "cvc cvccvcvcv cvcv", "cv cvccv cvc cvcvccv" };
+ return ciphers[seedByte % 3];
+ }
default: {
fprintf(stderr, "Unknown generated type: %d", type);
abort();
@@ -74,6 +85,36 @@ const char *CipherForType(MPElementType type, uint8_t seedByte) {
}
}
+const MPElementVariant VariantWithName(const char *variantName) {
+ char lowerVariantName[strlen(variantName)];
+ strcpy(lowerVariantName, variantName);
+ for (char *vN = lowerVariantName; *vN; ++vN)
+ *vN = tolower(*vN);
+
+ if (0 == strcmp(lowerVariantName, "p") || 0 == strcmp(lowerVariantName, "password"))
+ return MPElementVariantPassword;
+ if (0 == strcmp(lowerVariantName, "l") || 0 == strcmp(lowerVariantName, "login"))
+ return MPElementVariantLogin;
+
+ fprintf(stderr, "Not a variant name: %s", lowerVariantName);
+ abort();
+}
+
+const char *ScopeForVariant(MPElementVariant variant) {
+ switch (variant) {
+ case MPElementVariantPassword: {
+ return "com.lyndir.masterpassword";
+ }
+ case MPElementVariantLogin: {
+ return "com.lyndir.masterpassword.login";
+ }
+ default: {
+ fprintf(stderr, "Unknown variant: %d", variant);
+ abort();
+ }
+ }
+}
+
const char CharacterFromClass(char characterClass, uint8_t seedByte) {
const char *classCharacters;
switch (characterClass) {
@@ -113,6 +154,10 @@ const char CharacterFromClass(char characterClass, uint8_t seedByte) {
classCharacters = "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()";
break;
}
+ case ' ': {
+ classCharacters = " ";
+ break;
+ }
default: {
fprintf(stderr, "Unknown character class: %c", characterClass);
abort();
diff --git a/MasterPassword/C/types.h b/MasterPassword/C/types.h
index d275e457..1dbdfcdb 100644
--- a/MasterPassword/C/types.h
+++ b/MasterPassword/C/types.h
@@ -7,10 +7,11 @@
//
typedef enum {
- MPElementContentTypePassword,
- MPElementContentTypeNote,
- MPElementContentTypePicture,
-} MPElementContentType;
+ /** Generate the password to log in with. */
+ MPElementVariantPassword,
+ /** Generate the login name to log in as. */
+ MPElementVariantLogin,
+} MPElementVariant;
typedef enum {
/** Generate the password. */
@@ -33,6 +34,8 @@ typedef enum {
MPElementTypeGeneratedBasic = 0x4 | MPElementTypeClassGenerated | 0x0,
MPElementTypeGeneratedShort = 0x3 | MPElementTypeClassGenerated | 0x0,
MPElementTypeGeneratedPIN = 0x5 | MPElementTypeClassGenerated | 0x0,
+ MPElementTypeGeneratedName = 0xE | MPElementTypeClassGenerated | 0x0,
+ MPElementTypeGeneratedPhrase = 0xF | MPElementTypeClassGenerated | 0x0,
MPElementTypeStoredPersonal = 0x0 | MPElementTypeClassStored | MPElementFeatureExportContent,
MPElementTypeStoredDevicePrivate = 0x1 | MPElementTypeClassStored | MPElementFeatureDevicePrivate,
@@ -44,6 +47,8 @@ typedef enum {
#define trc(...) do {} while (0)
#endif
+const MPElementVariant VariantWithName(const char *variantName);
+const char *ScopeForVariant(MPElementVariant variant);
const MPElementType TypeWithName(const char *typeName);
const char *CipherForType(MPElementType type, uint8_t seedByte);
const char CharacterFromClass(char characterClass, uint8_t seedByte);
diff --git a/MasterPassword/ObjC/MPAlgorithm.h b/MasterPassword/ObjC/MPAlgorithm.h
index b8f356fb..2c8d3b11 100644
--- a/MasterPassword/ObjC/MPAlgorithm.h
+++ b/MasterPassword/ObjC/MPAlgorithm.h
@@ -1,12 +1,12 @@
/**
- * Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
- *
- * See the enclosed file LICENSE for license information (LGPLv3). If you did
- * not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
- *
- * @author Maarten Billemont
- * @license http://www.gnu.org/licenses/lgpl-3.0.txt
- */
+* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
+*
+* See the enclosed file LICENSE for license information (LGPLv3). If you did
+* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
+*
+* @author Maarten Billemont
+* @license http://www.gnu.org/licenses/lgpl-3.0.txt
+*/
//
// MPAlgorithm
@@ -16,10 +16,10 @@
//
#import "MPKey.h"
-#import "MPElementStoredEntity.h"
-#import "MPElementGeneratedEntity.h"
+#import "MPStoredSiteEntity.h"
+#import "MPGeneratedSiteEntity.h"
-#define MPAlgorithmDefaultVersion 1
+#define MPAlgorithmDefaultVersion 2
#define MPAlgorithmDefault MPAlgorithmForVersion(MPAlgorithmDefaultVersion)
id MPAlgorithmForVersion(NSUInteger version);
@@ -43,37 +43,56 @@ NSString *NSStringFromTimeToCrack(TimeToCrack timeToCrack);
@required
- (NSUInteger)version;
-- (BOOL)migrateUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc;
-- (BOOL)migrateElement:(MPElementEntity *)element explicit:(BOOL)explicit;
+- (BOOL)tryMigrateUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc;
+- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit;
- (MPKey *)keyForPassword:(NSString *)password ofUserNamed:(NSString *)userName;
- (MPKey *)keyFromKeyData:(NSData *)keyData;
- (NSData *)keyIDForKeyData:(NSData *)keyData;
-- (NSString *)nameOfType:(MPElementType)type;
-- (NSString *)shortNameOfType:(MPElementType)type;
-- (NSString *)classNameOfType:(MPElementType)type;
-- (Class)classOfType:(MPElementType)type;
+- (NSString *)scopeForVariant:(MPSiteVariant)variant;
+- (NSString *)nameOfType:(MPSiteType)type;
+- (NSString *)shortNameOfType:(MPSiteType)type;
+- (NSString *)classNameOfType:(MPSiteType)type;
+- (Class)classOfType:(MPSiteType)type;
- (NSArray *)allTypes;
-- (NSArray *)allTypesStartingWith:(MPElementType)startingType;
-- (MPElementType)nextType:(MPElementType)type;
-- (MPElementType)previousType:(MPElementType)type;
+- (NSArray *)allTypesStartingWith:(MPSiteType)startingType;
+- (MPSiteType)nextType:(MPSiteType)type;
+- (MPSiteType)previousType:(MPSiteType)type;
-- (NSString *)generateContentNamed:(NSString *)name ofType:(MPElementType)type withCounter:(NSUInteger)counter usingKey:(MPKey *)key;
-- (NSString *)storedContentForElement:(MPElementStoredEntity *)element usingKey:(MPKey *)key;
+- (NSString *)generateLoginForSiteNamed:(NSString *)name usingKey:(MPKey *)key;
+- (NSString *)generatePasswordForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter
+ usingKey:(MPKey *)key;
+- (NSString *)generateAnswerForSiteNamed:(NSString *)name onQuestion:(NSString *)question usingKey:(MPKey *)key;
+- (NSString *)generateContentForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter
+ variant:(MPSiteVariant)variant context:(NSString *)context usingKey:(MPKey *)key;
-- (void)saveContent:(NSString *)clearContent toElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey;
-- (NSString *)resolveContentForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey;
-- (void)resolveContentForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey
- result:(void (^)(NSString *result))resultBlock;
+- (NSString *)storedLoginForSite:(MPStoredSiteEntity *)site usingKey:(MPKey *)key;
+- (NSString *)storedPasswordForSite:(MPStoredSiteEntity *)site usingKey:(MPKey *)key;
-- (void)importProtectedContent:(NSString *)protectedContent protectedByKey:(MPKey *)importKey
- intoElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey;
-- (void)importClearTextContent:(NSString *)clearContent intoElement:(MPElementEntity *)element
- usingKey:(MPKey *)elementKey;
-- (NSString *)exportContentForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey;
+- (BOOL)savePassword:(NSString *)clearPassword toSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
-- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordOfType:(MPElementType)type byAttacker:(MPAttacker)attacker;
+- (NSString *)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
+- (NSString *)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
+- (NSString *)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
+- (NSString *)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question ofSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
+
+- (void)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey
+ result:(void ( ^ )(NSString *result))resultBlock;
+- (void)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey
+ result:(void ( ^ )(NSString *result))resultBlock;
+- (void)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey
+ result:(void ( ^ )(NSString *result))resultBlock;
+- (void)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question ofSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey
+ result:(void ( ^ )(NSString *result))resultBlock;
+
+- (void)importProtectedPassword:(NSString *)protectedPassword protectedByKey:(MPKey *)importKey
+ intoSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
+- (void)importClearTextPassword:(NSString *)clearPassword intoSite:(MPSiteEntity *)site
+ usingKey:(MPKey *)siteKey;
+- (NSString *)exportPasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
+
+- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordOfType:(MPSiteType)type byAttacker:(MPAttacker)attacker;
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordString:(NSString *)password byAttacker:(MPAttacker)attacker;
@end
diff --git a/MasterPassword/ObjC/MPAlgorithm.m b/MasterPassword/ObjC/MPAlgorithm.m
index 7b92652d..788210f2 100644
--- a/MasterPassword/ObjC/MPAlgorithm.m
+++ b/MasterPassword/ObjC/MPAlgorithm.m
@@ -1,12 +1,12 @@
/**
- * Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
- *
- * See the enclosed file LICENSE for license information (LGPLv3). If you did
- * not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
- *
- * @author Maarten Billemont
- * @license http://www.gnu.org/licenses/lgpl-3.0.txt
- */
+* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
+*
+* See the enclosed file LICENSE for license information (LGPLv3). If you did
+* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
+*
+* @author Maarten Billemont
+* @license http://www.gnu.org/licenses/lgpl-3.0.txt
+*/
//
// MPAlgorithm
@@ -24,7 +24,7 @@ id MPAlgorithmForVersion(NSUInteger version) {
versionToAlgorithm = [NSMutableDictionary dictionary];
id algorithm = versionToAlgorithm[@(version)];
- if (!algorithm) if ((algorithm = [NSClassFromString( strf( @"MPAlgorithmV%lu", (unsigned long)version ) ) new]))
+ if (!algorithm && (algorithm = (id)[NSClassFromString( strf( @"MPAlgorithmV%lu", (unsigned long)version ) ) new]))
versionToAlgorithm[@(version)] = algorithm;
return algorithm;
@@ -33,8 +33,11 @@ id MPAlgorithmForVersion(NSUInteger version) {
id MPAlgorithmDefaultForBundleVersion(NSString *bundleVersion) {
if (PearlCFBundleVersionCompare( bundleVersion, @"1.3" ) == NSOrderedAscending)
- // Pre-1.3
+ // Pre-1.3
return MPAlgorithmForVersion( 0 );
+ if (PearlCFBundleVersionCompare( bundleVersion, @"2.1" ) == NSOrderedAscending)
+ // Pre-2.1
+ return MPAlgorithmForVersion( 1 );
return MPAlgorithmDefault;
}
diff --git a/MasterPassword/ObjC/MPAlgorithmV0.h b/MasterPassword/ObjC/MPAlgorithmV0.h
index ff081d8b..3e7c902a 100644
--- a/MasterPassword/ObjC/MPAlgorithmV0.h
+++ b/MasterPassword/ObjC/MPAlgorithmV0.h
@@ -20,7 +20,7 @@
@interface MPAlgorithmV0 : NSObject
- (NSDictionary *)allCiphers;
-- (NSArray *)ciphersForType:(MPElementType)type;
+- (NSArray *)ciphersForType:(MPSiteType)type;
- (NSArray *)cipherClasses;
- (NSArray *)cipherClassCharacters;
- (NSString *)charactersForCipherClass:(NSString *)cipherClass;
diff --git a/MasterPassword/ObjC/MPAlgorithmV0.m b/MasterPassword/ObjC/MPAlgorithmV0.m
index 815f4b51..f847791f 100644
--- a/MasterPassword/ObjC/MPAlgorithmV0.m
+++ b/MasterPassword/ObjC/MPAlgorithmV0.m
@@ -17,6 +17,9 @@
#import "MPAlgorithmV0.h"
#import "MPEntities.h"
+#import "MPAppDelegate_Shared.h"
+#import "MPAppDelegate_InApp.h"
+#import "MPSiteQuestionEntity.h"
#include
#include
@@ -70,40 +73,40 @@
return [(id)other version] == [self version];
}
-- (BOOL)migrateUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc {
+- (BOOL)tryMigrateUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc {
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];
- NSArray *migrationElements = [moc executeFetchRequest:migrationRequest error:&error];
- if (!migrationElements) {
- err( @"While looking for elements to migrate: %@", error );
+ NSArray *migrationSites = [moc executeFetchRequest:migrationRequest error:&error];
+ if (!migrationSites) {
+ err( @"While looking for sites to migrate: %@", [error fullDescription] );
return NO;
}
- BOOL requiresExplicitMigration = NO;
- for (MPElementEntity *migrationElement in migrationElements)
- if (![migrationElement migrateExplicitly:NO])
- requiresExplicitMigration = YES;
+ BOOL success = YES;
+ for (MPSiteEntity *migrationSite in migrationSites)
+ if (![migrationSite tryMigrateExplicitly:NO])
+ 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.
return NO;
if (!explicit) {
// This migration requires explicit permission.
- element.requiresExplicitMigration = YES;
+ site.requiresExplicitMigration = YES;
return NO;
}
// Apply migration.
- element.requiresExplicitMigration = NO;
- element.version = [self version];
+ site.requiresExplicitMigration = NO;
+ site.version = [self version];
return YES;
}
@@ -117,11 +120,11 @@
[NSData dataWithBytes:&nuserNameLength
length:sizeof( nuserNameLength )],
[userName dataUsingEncoding:NSUTF8StringEncoding],
- nil] N:MP_N r:MP_r p:MP_p];
+ nil] N:MP_N r:MP_r p:MP_p];
MPKey *key = [self keyFromKeyData:keyData];
trc( @"User: %@, password: %@ derives to key ID: %@ (took %0.2fs)", userName, password, [key.keyID encodeHex],
- -[start timeIntervalSinceNow] );
+ -[start timeIntervalSinceNow] );
return key;
}
@@ -136,108 +139,140 @@
return [keyData hashWith:MP_hash];
}
-- (NSString *)nameOfType:(MPElementType)type {
+- (NSString *)scopeForVariant:(MPSiteVariant)variant {
+
+ switch (variant) {
+ case MPSiteVariantPassword:
+ return @"com.lyndir.masterpassword";
+ case MPSiteVariantLogin:
+ return @"com.lyndir.masterpassword.login";
+ case MPSiteVariantAnswer:
+ return @"com.lyndir.masterpassword.answer";
+ }
+
+ Throw( @"Unsupported variant: %ld", (long)variant );
+}
+
+- (NSString *)nameOfType:(MPSiteType)type {
if (!type)
return nil;
switch (type) {
- case MPElementTypeGeneratedMaximum:
+ case MPSiteTypeGeneratedMaximum:
return @"Maximum Security Password";
- case MPElementTypeGeneratedLong:
+ case MPSiteTypeGeneratedLong:
return @"Long Password";
- case MPElementTypeGeneratedMedium:
+ case MPSiteTypeGeneratedMedium:
return @"Medium Password";
- case MPElementTypeGeneratedBasic:
+ case MPSiteTypeGeneratedBasic:
return @"Basic Password";
- case MPElementTypeGeneratedShort:
+ case MPSiteTypeGeneratedShort:
return @"Short Password";
- case MPElementTypeGeneratedPIN:
+ case MPSiteTypeGeneratedPIN:
return @"PIN";
- case MPElementTypeStoredPersonal:
+ case MPSiteTypeGeneratedName:
+ return @"Login Name";
+
+ case MPSiteTypeGeneratedPhrase:
+ return @"Phrase";
+
+ case MPSiteTypeStoredPersonal:
return @"Personal Password";
- case MPElementTypeStoredDevicePrivate:
+ case MPSiteTypeStoredDevicePrivate:
return @"Device Private Password";
}
Throw( @"Type not supported: %lu", (long)type );
}
-- (NSString *)shortNameOfType:(MPElementType)type {
+- (NSString *)shortNameOfType:(MPSiteType)type {
if (!type)
return nil;
switch (type) {
- case MPElementTypeGeneratedMaximum:
+ case MPSiteTypeGeneratedMaximum:
return @"Maximum";
- case MPElementTypeGeneratedLong:
+ case MPSiteTypeGeneratedLong:
return @"Long";
- case MPElementTypeGeneratedMedium:
+ case MPSiteTypeGeneratedMedium:
return @"Medium";
- case MPElementTypeGeneratedBasic:
+ case MPSiteTypeGeneratedBasic:
return @"Basic";
- case MPElementTypeGeneratedShort:
+ case MPSiteTypeGeneratedShort:
return @"Short";
- case MPElementTypeGeneratedPIN:
+ case MPSiteTypeGeneratedPIN:
return @"PIN";
- case MPElementTypeStoredPersonal:
+ case MPSiteTypeGeneratedName:
+ return @"Name";
+
+ case MPSiteTypeGeneratedPhrase:
+ return @"Phrase";
+
+ case MPSiteTypeStoredPersonal:
return @"Personal";
- case MPElementTypeStoredDevicePrivate:
+ case MPSiteTypeStoredDevicePrivate:
return @"Device";
}
Throw( @"Type not supported: %lu", (long)type );
}
-- (NSString *)classNameOfType:(MPElementType)type {
+- (NSString *)classNameOfType:(MPSiteType)type {
return NSStringFromClass( [self classOfType:type] );
}
-- (Class)classOfType:(MPElementType)type {
+- (Class)classOfType:(MPSiteType)type {
if (!type)
Throw( @"No type given." );
switch (type) {
- case MPElementTypeGeneratedMaximum:
- return [MPElementGeneratedEntity class];
+ case MPSiteTypeGeneratedMaximum:
+ return [MPGeneratedSiteEntity class];
- case MPElementTypeGeneratedLong:
- return [MPElementGeneratedEntity class];
+ case MPSiteTypeGeneratedLong:
+ return [MPGeneratedSiteEntity class];
- case MPElementTypeGeneratedMedium:
- return [MPElementGeneratedEntity class];
+ case MPSiteTypeGeneratedMedium:
+ return [MPGeneratedSiteEntity class];
- case MPElementTypeGeneratedBasic:
- return [MPElementGeneratedEntity class];
+ case MPSiteTypeGeneratedBasic:
+ return [MPGeneratedSiteEntity class];
- case MPElementTypeGeneratedShort:
- return [MPElementGeneratedEntity class];
+ case MPSiteTypeGeneratedShort:
+ return [MPGeneratedSiteEntity class];
- case MPElementTypeGeneratedPIN:
- return [MPElementGeneratedEntity class];
+ case MPSiteTypeGeneratedPIN:
+ return [MPGeneratedSiteEntity class];
- case MPElementTypeStoredPersonal:
- return [MPElementStoredEntity class];
+ case MPSiteTypeGeneratedName:
+ return [MPGeneratedSiteEntity class];
- case MPElementTypeStoredDevicePrivate:
- return [MPElementStoredEntity class];
+ case MPSiteTypeGeneratedPhrase:
+ return [MPGeneratedSiteEntity class];
+
+ case MPSiteTypeStoredPersonal:
+ return [MPStoredSiteEntity class];
+
+ case MPSiteTypeStoredDevicePrivate:
+ return [MPStoredSiteEntity class];
}
Throw( @"Type not supported: %lu", (long)type );
@@ -245,13 +280,13 @@
- (NSArray *)allTypes {
- return [self allTypesStartingWith:MPElementTypeGeneratedMaximum];
+ return [self allTypesStartingWith:MPSiteTypeGeneratedMaximum];
}
-- (NSArray *)allTypesStartingWith:(MPElementType)startingType {
+- (NSArray *)allTypesStartingWith:(MPSiteType)startingType {
NSMutableArray *allTypes = [[NSMutableArray alloc] initWithCapacity:8];
- MPElementType currentType = startingType;
+ MPSiteType currentType = startingType;
do {
[allTypes addObject:@(currentType)];
} while ((currentType = [self nextType:currentType]) != startingType);
@@ -259,33 +294,33 @@
return allTypes;
}
-- (MPElementType)nextType:(MPElementType)type {
+- (MPSiteType)nextType:(MPSiteType)type {
switch (type) {
- case MPElementTypeGeneratedMaximum:
- return MPElementTypeGeneratedLong;
- case MPElementTypeGeneratedLong:
- return MPElementTypeGeneratedMedium;
- case MPElementTypeGeneratedMedium:
- return MPElementTypeGeneratedBasic;
- case MPElementTypeGeneratedBasic:
- return MPElementTypeGeneratedShort;
- case MPElementTypeGeneratedShort:
- return MPElementTypeGeneratedPIN;
- case MPElementTypeGeneratedPIN:
- return MPElementTypeStoredPersonal;
- case MPElementTypeStoredPersonal:
- return MPElementTypeStoredDevicePrivate;
- case MPElementTypeStoredDevicePrivate:
- return MPElementTypeGeneratedMaximum;
+ case MPSiteTypeGeneratedMaximum:
+ return MPSiteTypeGeneratedLong;
+ case MPSiteTypeGeneratedLong:
+ return MPSiteTypeGeneratedMedium;
+ case MPSiteTypeGeneratedMedium:
+ return MPSiteTypeGeneratedBasic;
+ case MPSiteTypeGeneratedBasic:
+ return MPSiteTypeGeneratedShort;
+ case MPSiteTypeGeneratedShort:
+ return MPSiteTypeGeneratedPIN;
+ case MPSiteTypeGeneratedPIN:
+ return MPSiteTypeStoredPersonal;
+ case MPSiteTypeStoredPersonal:
+ return MPSiteTypeStoredDevicePrivate;
+ case MPSiteTypeStoredDevicePrivate:
+ return MPSiteTypeGeneratedMaximum;
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)
previousType = nextType;
@@ -304,7 +339,7 @@
return ciphers;
}
-- (NSArray *)ciphersForType:(MPElementType)type {
+- (NSArray *)ciphersForType:(MPSiteType)type {
NSString *typeClass = [self classNameOfType:type];
NSString *typeName = [self nameOfType:type];
@@ -326,27 +361,53 @@
return [NSNullToNil( [NSNullToNil( [[self allCiphers] valueForKey:@"MPCharacterClasses"] ) valueForKey:cipherClass] ) copy];
}
-- (NSString *)generateContentNamed:(NSString *)name ofType:(MPElementType)type withCounter:(NSUInteger)counter usingKey:(MPKey *)key {
+- (NSString *)generateLoginForSiteNamed:(NSString *)name usingKey:(MPKey *)key {
+
+ return [self generateContentForSiteNamed:name ofType:MPSiteTypeGeneratedName withCounter:1
+ variant:MPSiteVariantLogin context:nil usingKey:key];
+}
+
+- (NSString *)generatePasswordForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter
+ usingKey:(MPKey *)key {
+
+ return [self generateContentForSiteNamed:name ofType:type withCounter:counter
+ variant:MPSiteVariantPassword context:nil usingKey:key];
+}
+
+- (NSString *)generateAnswerForSiteNamed:(NSString *)name onQuestion:(NSString *)question 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
- 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 *nameLengthBytes = [NSData dataWithBytes:&nnameLength length:sizeof( nnameLength )];
- trc( @"seed from: hmac-sha256(%@, 'com.lyndir.masterpassword' | %@ | %@ | %@)", [key.keyData encodeBase64],
- [nameLengthBytes encodeHex], name, [counterBytes encodeHex] );
+ NSData *contextLengthBytes = [NSData dataWithBytes:&ncontextLength length:sizeof( ncontextLength )];
+ NSString *scope = [self scopeForVariant:variant];
+ trc( @"seed from: hmac-sha256(%@, %@ | %@ | %@ | %@ | %@)",
+ [[key keyID] encodeHex], scope, [nameLengthBytes encodeHex], name, [counterBytes encodeHex], context );
NSData *seed = [[NSData dataByConcatenatingDatas:
- [@"com.lyndir.masterpassword" dataUsingEncoding:NSUTF8StringEncoding],
- nameLengthBytes, [name dataUsingEncoding:NSUTF8StringEncoding],
- counterBytes, nil]
+ [scope dataUsingEncoding:NSUTF8StringEncoding],
+ nameLengthBytes,
+ [name dataUsingEncoding:NSUTF8StringEncoding],
+ counterBytes,
+ context? contextLengthBytes: nil,
+ [context dataUsingEncoding:NSUTF8StringEncoding],
+ nil]
hmacWith:PearlHashSHA256 key:key.keyData];
- trc( @"seed is: %@", [seed encodeBase64] );
+ trc( @"seed is: %@", [seed encodeHex] );
const 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[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.
NSAssert( [seed length] >= [cipher length] + 1, @"Insufficient seed bytes to encode cipher." );
@@ -364,68 +425,80 @@
return content;
}
-- (NSString *)storedContentForElement:(MPElementStoredEntity *)element usingKey:(MPKey *)key {
+- (NSString *)storedLoginForSite:(MPStoredSiteEntity *)site usingKey:(MPKey *)key {
- return [self decryptContent:element.contentObject usingKey:key];
+ return nil;
}
-- (void)saveContent:(NSString *)clearContent toElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey {
+- (NSString *)storedPasswordForSite:(MPStoredSiteEntity *)site usingKey:(MPKey *)key {
- NSAssert( [elementKey.keyID isEqualToData:element.user.keyID], @"Element does not belong to current user." );
- switch (element.type) {
- case MPElementTypeGeneratedMaximum:
- case MPElementTypeGeneratedLong:
- case MPElementTypeGeneratedMedium:
- case MPElementTypeGeneratedBasic:
- case MPElementTypeGeneratedShort:
- case MPElementTypeGeneratedPIN: {
- NSAssert( NO, @"Cannot save content to element with generated type %lu.", (long)element.type );
- break;
+ return [self decryptContent:site.contentObject usingKey:key];
+}
+
+- (BOOL)savePassword:(NSString *)clearContent toSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
+
+ NSAssert( [siteKey.keyID isEqualToData:site.user.keyID], @"Site does not belong to current user." );
+ switch (site.type) {
+ case MPSiteTypeGeneratedMaximum:
+ case MPSiteTypeGeneratedLong:
+ case MPSiteTypeGeneratedMedium:
+ case MPSiteTypeGeneratedBasic:
+ case MPSiteTypeGeneratedShort:
+ case MPSiteTypeGeneratedPIN:
+ case MPSiteTypeGeneratedName:
+ case MPSiteTypeGeneratedPhrase: {
+ wrn( @"Cannot save content to site with generated type %lu.", (long)site.type );
+ return NO;
}
- case MPElementTypeStoredPersonal: {
- if (![element isKindOfClass:[MPElementStoredEntity class]]) {
- wrn( @"Element with stored type %lu is not an MPElementStoredEntity, but a %@.",
- (long)element.type, [element class] );
- break;
+ case MPSiteTypeStoredPersonal: {
+ if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
+ wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
+ (long)site.type, [site class] );
+ return NO;
}
NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding]
- encryptWithSymmetricKey:[elementKey subKeyOfLength:PearlCryptKeySize].keyData padding:YES];
- ((MPElementStoredEntity *)element).contentObject = encryptedContent;
- break;
+ encryptWithSymmetricKey:[siteKey subKeyOfLength:PearlCryptKeySize].keyData padding:YES];
+ if ([((MPStoredSiteEntity *)site).contentObject isEqualToData:encryptedContent])
+ return NO;
+
+ ((MPStoredSiteEntity *)site).contentObject = encryptedContent;
+ return YES;
}
- case MPElementTypeStoredDevicePrivate: {
- if (![element isKindOfClass:[MPElementStoredEntity class]]) {
- wrn( @"Element with stored type %lu is not an MPElementStoredEntity, but a %@.",
- (long)element.type, [element class] );
- break;
+ case MPSiteTypeStoredDevicePrivate: {
+ if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
+ wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
+ (long)site.type, [site class] );
+ return NO;
}
NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding]
- encryptWithSymmetricKey:[elementKey subKeyOfLength:PearlCryptKeySize].keyData padding:YES];
- NSDictionary *elementQuery = [self queryForDevicePrivateElementNamed:element.name];
+ encryptWithSymmetricKey:[siteKey subKeyOfLength:PearlCryptKeySize].keyData padding:YES];
+ NSDictionary *siteQuery = [self queryForDevicePrivateSiteNamed:site.name];
if (!encryptedContent)
- [PearlKeyChain deleteItemForQuery:elementQuery];
+ [PearlKeyChain deleteItemForQuery:siteQuery];
else
- [PearlKeyChain addOrUpdateItemForQuery:elementQuery withAttributes:@{
- (__bridge id)kSecValueData : encryptedContent,
+ [PearlKeyChain addOrUpdateItemForQuery:siteQuery withAttributes:@{
+ (__bridge id)kSecValueData : encryptedContent,
#if TARGET_OS_IPHONE
(__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
#endif
}];
- ((MPElementStoredEntity *)element).contentObject = nil;
- break;
+ ((MPStoredSiteEntity *)site).contentObject = nil;
+ return YES;
}
}
+
+ Throw( @"Unsupported type: %ld", (long)site.type );
}
-- (NSString *)resolveContentForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey {
+- (NSString *)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter( group );
__block NSString *result = nil;
- [self resolveContentForElement:element usingKey:elementKey result:^(NSString *result_) {
+ [self resolveLoginForSite:site usingKey:siteKey result:^(NSString *result_) {
result = result_;
dispatch_group_leave( group );
}];
@@ -434,65 +507,131 @@
return result;
}
-- (void)resolveContentForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey result:(void ( ^ )(NSString *result))resultBlock {
+- (NSString *)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
- NSAssert( [elementKey.keyID isEqualToData:element.user.keyID], @"Element does not belong to current user." );
- switch (element.type) {
- case MPElementTypeGeneratedMaximum:
- case MPElementTypeGeneratedLong:
- case MPElementTypeGeneratedMedium:
- case MPElementTypeGeneratedBasic:
- case MPElementTypeGeneratedShort:
- case MPElementTypeGeneratedPIN: {
- if (![element isKindOfClass:[MPElementGeneratedEntity class]]) {
- wrn( @"Element with generated type %lu is not an MPElementGeneratedEntity, but a %@.",
- (long)element.type, [element class] );
+ dispatch_group_t group = dispatch_group_create();
+ dispatch_group_enter( group );
+ __block NSString *result = nil;
+ [self resolvePasswordForSite:site usingKey:siteKey result:^(NSString *result_) {
+ result = result_;
+ dispatch_group_leave( group );
+ }];
+ dispatch_group_wait( group, DISPATCH_TIME_FOREVER );
+
+ return result;
+}
+
+- (NSString *)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
+
+ dispatch_group_t group = dispatch_group_create();
+ dispatch_group_enter( group );
+ __block NSString *result = nil;
+ [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 ofSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
+
+ dispatch_group_t group = dispatch_group_create();
+ dispatch_group_enter( group );
+ __block NSString *result = nil;
+ [self resolveAnswerForQuestion:question ofSite:site 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] isPurchased:MPProductGenerateLogins];
+ NSString *loginName = loginGenerated? nil: site.loginName;
+ id algorithm = nil;
+ if (!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 ), ^{
+ if (loginGenerated)
+ resultBlock( [algorithm generateLoginForSiteNamed:name usingKey:siteKey] );
+ else
+ resultBlock( loginName );
+ } );
+}
+
+- (void)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock {
+
+ NSAssert( [siteKey.keyID isEqualToData:site.user.keyID], @"Site does not belong to current user." );
+ switch (site.type) {
+ case MPSiteTypeGeneratedMaximum:
+ case MPSiteTypeGeneratedLong:
+ case MPSiteTypeGeneratedMedium:
+ case MPSiteTypeGeneratedBasic:
+ case MPSiteTypeGeneratedShort:
+ case MPSiteTypeGeneratedPIN:
+ case MPSiteTypeGeneratedName:
+ case MPSiteTypeGeneratedPhrase: {
+ if (![site isKindOfClass:[MPGeneratedSiteEntity class]]) {
+ wrn( @"Site with generated type %lu is not an MPGeneratedSiteEntity, but a %@.",
+ (long)site.type, [site class] );
break;
}
- NSString *name = element.name;
- MPElementType type = element.type;
- NSUInteger counter = ((MPElementGeneratedEntity *)element).counter;
+ NSString *name = site.name;
+ MPSiteType type = site.type;
+ NSUInteger counter = ((MPGeneratedSiteEntity *)site).counter;
id algorithm = nil;
- if (!element.name.length)
+ if (!site.name.length)
err( @"Missing name." );
- else if (!elementKey.keyData.length)
+ else if (!siteKey.keyData.length)
err( @"Missing key." );
else
- algorithm = element.algorithm;
+ algorithm = site.algorithm;
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
- NSString *result = [algorithm generateContentNamed:name ofType:type withCounter:counter usingKey:elementKey];
+ NSString *result = [algorithm generatePasswordForSiteNamed:name ofType:type withCounter:counter usingKey:siteKey];
resultBlock( result );
} );
break;
}
- case MPElementTypeStoredPersonal: {
- if (![element isKindOfClass:[MPElementStoredEntity class]]) {
- wrn( @"Element with stored type %lu is not an MPElementStoredEntity, but a %@.",
- (long)element.type, [element class] );
+ case MPSiteTypeStoredPersonal: {
+ if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
+ wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
+ (long)site.type, [site class] );
break;
}
- NSData *encryptedContent = ((MPElementStoredEntity *)element).contentObject;
+ NSData *encryptedContent = ((MPStoredSiteEntity *)site).contentObject;
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 );
} );
break;
}
- case MPElementTypeStoredDevicePrivate: {
- NSAssert( [element isKindOfClass:[MPElementStoredEntity class]],
- @"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", (long)element.type,
- [element class] );
+ case MPSiteTypeStoredDevicePrivate: {
+ NSAssert( [site isKindOfClass:[MPStoredSiteEntity class]],
+ @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.", (long)site.type,
+ [site class] );
- NSDictionary *elementQuery = [self queryForDevicePrivateElementNamed:element.name];
- NSData *encryptedContent = [PearlKeyChain dataOfItemForQuery:elementQuery];
+ NSDictionary *siteQuery = [self queryForDevicePrivateSiteNamed:site.name];
+ NSData *encryptedContent = [PearlKeyChain dataOfItemForQuery:siteQuery];
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 );
} );
break;
@@ -500,91 +639,135 @@
}
}
-- (void)importProtectedContent:(NSString *)protectedContent protectedByKey:(MPKey *)importKey
- intoElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey {
+- (void)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock {
- NSAssert( [elementKey.keyID isEqualToData:element.user.keyID], @"Element does not belong to current user." );
- switch (element.type) {
- case MPElementTypeGeneratedMaximum:
- case MPElementTypeGeneratedLong:
- case MPElementTypeGeneratedMedium:
- case MPElementTypeGeneratedBasic:
- case MPElementTypeGeneratedShort:
- case MPElementTypeGeneratedPIN:
+ NSAssert( [siteKey.keyID isEqualToData:site.user.keyID], @"Site does not belong to current user." );
+ NSString *name = site.name;
+ id 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 ofSite:(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;
+ NSString *keyword = question.keyword;
+ id 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:keyword usingKey:siteKey];
+ resultBlock( result );
+ } );
+}
+
+- (void)importProtectedPassword:(NSString *)protectedContent protectedByKey:(MPKey *)importKey
+ intoSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
+
+ NSAssert( [siteKey.keyID isEqualToData:site.user.keyID], @"Site does not belong to current user." );
+ switch (site.type) {
+ case MPSiteTypeGeneratedMaximum:
+ case MPSiteTypeGeneratedLong:
+ case MPSiteTypeGeneratedMedium:
+ case MPSiteTypeGeneratedBasic:
+ case MPSiteTypeGeneratedShort:
+ case MPSiteTypeGeneratedPIN:
+ case MPSiteTypeGeneratedName:
+ case MPSiteTypeGeneratedPhrase:
break;
- case MPElementTypeStoredPersonal: {
- if (![element isKindOfClass:[MPElementStoredEntity class]]) {
- wrn( @"Element with stored type %lu is not an MPElementStoredEntity, but a %@.",
- (long)element.type, [element class] );
+ case MPSiteTypeStoredPersonal: {
+ if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
+ wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
+ (long)site.type, [site class] );
break;
}
- if ([importKey.keyID isEqualToData:elementKey.keyID])
- ((MPElementStoredEntity *)element).contentObject = [protectedContent decodeBase64];
+ if ([importKey.keyID isEqualToData:siteKey.keyID])
+ ((MPStoredSiteEntity *)site).contentObject = [protectedContent decodeBase64];
else {
NSString *clearContent = [self decryptContent:[protectedContent decodeBase64] usingKey:importKey];
- [self importClearTextContent:clearContent intoElement:element usingKey:elementKey];
+ [self importClearTextPassword:clearContent intoSite:site usingKey:siteKey];
}
break;
}
- case MPElementTypeStoredDevicePrivate:
+ case MPSiteTypeStoredDevicePrivate:
break;
}
}
-- (void)importClearTextContent:(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." );
- switch (element.type) {
- case MPElementTypeGeneratedMaximum:
- case MPElementTypeGeneratedLong:
- case MPElementTypeGeneratedMedium:
- case MPElementTypeGeneratedBasic:
- case MPElementTypeGeneratedShort:
- case MPElementTypeGeneratedPIN:
+ NSAssert( [siteKey.keyID isEqualToData:site.user.keyID], @"Site does not belong to current user." );
+ switch (site.type) {
+ case MPSiteTypeGeneratedMaximum:
+ case MPSiteTypeGeneratedLong:
+ case MPSiteTypeGeneratedMedium:
+ case MPSiteTypeGeneratedBasic:
+ case MPSiteTypeGeneratedShort:
+ case MPSiteTypeGeneratedPIN:
+ case MPSiteTypeGeneratedName:
+ case MPSiteTypeGeneratedPhrase:
break;
- case MPElementTypeStoredPersonal: {
- [self saveContent:clearContent toElement:element usingKey:elementKey];
+ case MPSiteTypeStoredPersonal: {
+ [self savePassword:clearContent toSite:site usingKey:siteKey];
break;
}
- case MPElementTypeStoredDevicePrivate:
+ case MPSiteTypeStoredDevicePrivate:
break;
}
}
-- (NSString *)exportContentForElement:(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." );
- if (!(element.type & MPElementFeatureExportContent))
+ NSAssert( [siteKey.keyID isEqualToData:site.user.keyID], @"Site does not belong to current user." );
+ if (!(site.type & MPSiteFeatureExportContent))
return nil;
NSString *result = nil;
- switch (element.type) {
- case MPElementTypeGeneratedMaximum:
- case MPElementTypeGeneratedLong:
- case MPElementTypeGeneratedMedium:
- case MPElementTypeGeneratedBasic:
- case MPElementTypeGeneratedShort:
- case MPElementTypeGeneratedPIN: {
+ switch (site.type) {
+ case MPSiteTypeGeneratedMaximum:
+ case MPSiteTypeGeneratedLong:
+ case MPSiteTypeGeneratedMedium:
+ case MPSiteTypeGeneratedBasic:
+ case MPSiteTypeGeneratedShort:
+ case MPSiteTypeGeneratedPIN:
+ case MPSiteTypeGeneratedName:
+ case MPSiteTypeGeneratedPhrase: {
result = nil;
break;
}
- case MPElementTypeStoredPersonal: {
- if (![element isKindOfClass:[MPElementStoredEntity class]]) {
- wrn( @"Element with stored type %lu is not an MPElementStoredEntity, but a %@.",
- (long)element.type, [element class] );
+ case MPSiteTypeStoredPersonal: {
+ if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
+ wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
+ (long)site.type, [site class] );
break;
}
- result = [((MPElementStoredEntity *)element).contentObject encodeBase64];
+ result = [((MPStoredSiteEntity *)site).contentObject encodeBase64];
break;
}
- case MPElementTypeStoredDevicePrivate: {
+ case MPSiteTypeStoredDevicePrivate: {
result = nil;
break;
}
@@ -598,7 +781,7 @@
return NO;
}
-- (NSDictionary *)queryForDevicePrivateElementNamed:(NSString *)name {
+- (NSDictionary *)queryForDevicePrivateSiteNamed:(NSString *)name {
return [PearlKeyChain createQueryForClass:kSecClassGenericPassword
attributes:@{
@@ -619,7 +802,7 @@
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)
return NO;
diff --git a/MasterPassword/ObjC/MPAlgorithmV1.m b/MasterPassword/ObjC/MPAlgorithmV1.m
index 6ef54659..22e2ef0f 100644
--- a/MasterPassword/ObjC/MPAlgorithmV1.m
+++ b/MasterPassword/ObjC/MPAlgorithmV1.m
@@ -25,49 +25,54 @@
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.
return NO;
if (!explicit) {
- if (element.type & MPElementTypeClassGenerated) {
+ if (site.type & MPSiteTypeClassGenerated) {
// This migration requires explicit permission for types of the generated class.
- element.requiresExplicitMigration = YES;
+ site.requiresExplicitMigration = YES;
return NO;
}
}
// Apply migration.
- element.requiresExplicitMigration = NO;
- element.version = [self version];
+ site.requiresExplicitMigration = NO;
+ site.version = [self version];
return YES;
}
-- (NSString *)generateContentNamed:(NSString *)name ofType:(MPElementType)type withCounter:(NSUInteger)counter usingKey:(MPKey *)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
- 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 *nameLengthBytes = [NSData dataWithBytes:&nnameLength length:sizeof( nnameLength )];
- trc( @"seed from: hmac-sha256(%@, 'com.lyndir.masterpassword' | %@ | %@ | %@)", [key.keyData encodeBase64], [nameLengthBytes encodeHex],
- name, [counterBytes encodeHex] );
+ NSData *contextLengthBytes = [NSData dataWithBytes:&ncontextLength length:sizeof( ncontextLength )];
+ NSString *scope = [self scopeForVariant:variant];
+ trc( @"seed from: hmac-sha256(%@, %@ | %@ | %@ | %@)",
+ [[key keyID] encodeHex], scope, [nameLengthBytes encodeHex], name, [counterBytes encodeHex] );
NSData *seed = [[NSData dataByConcatenatingDatas:
- [@"com.lyndir.masterpassword" dataUsingEncoding:NSUTF8StringEncoding],
+ [scope dataUsingEncoding:NSUTF8StringEncoding],
nameLengthBytes,
[name dataUsingEncoding:NSUTF8StringEncoding],
counterBytes,
- nil]
+ context? contextLengthBytes: nil,
+ [context dataUsingEncoding:NSUTF8StringEncoding],
+ nil]
hmacWith:PearlHashSHA256 key:key.keyData];
- trc( @"seed is: %@", [seed encodeBase64] );
+ trc( @"seed is: %@", [seed encodeHex] );
const unsigned char *seedBytes = seed.bytes;
// Determine the cipher from the first seed byte.
NSAssert( [seed length], @"Missing seed." );
NSArray *typeCiphers = [self ciphersForType:type];
NSString *cipher = typeCiphers[seedBytes[0] % [typeCiphers count]];
- trc( @"type %@, ciphers: %@, selected: %@", [self nameOfType:type], typeCiphers, cipher );
+ trc( @"type %@ (%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." );
diff --git a/MasterPassword/ObjC/MPAlgorithmV2.h b/MasterPassword/ObjC/MPAlgorithmV2.h
new file mode 100644
index 00000000..ec76634a
--- /dev/null
+++ b/MasterPassword/ObjC/MPAlgorithmV2.h
@@ -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
+ * @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
diff --git a/MasterPassword/ObjC/MPAlgorithmV2.m b/MasterPassword/ObjC/MPAlgorithmV2.m
new file mode 100644
index 00000000..b5db03a2
--- /dev/null
+++ b/MasterPassword/ObjC/MPAlgorithmV2.m
@@ -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
+* @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
+#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
diff --git a/MasterPassword/ObjC/MPAppDelegate_InApp.h b/MasterPassword/ObjC/MPAppDelegate_InApp.h
new file mode 100644
index 00000000..e702484c
--- /dev/null
+++ b/MasterPassword/ObjC/MPAppDelegate_InApp.h
@@ -0,0 +1,26 @@
+//
+// MPAppDelegate_Key.h
+// MasterPassword
+//
+// Created by Maarten Billemont on 24/11/11.
+// Copyright (c) 2011 Lyndir. All rights reserved.
+//
+
+#import "MPAppDelegate_Shared.h"
+
+#define MPProductGenerateLogins @"com.lyndir.masterpassword.products.generatelogins"
+#define MPProductGenerateAnswers @"com.lyndir.masterpassword.products.generateanswers"
+
+@interface MPAppDelegate_Shared(InApp)
+
+@property(nonatomic, strong) NSArray /* SKProduct */ *products;
+@property(nonatomic, strong) NSArray /* SKPaymentTransaction */ *paymentTransactions;
+
+- (void)updateProducts;
+- (BOOL)canMakePayments;
+- (BOOL)isPurchased:(NSString *)productIdentifier;
+
+- (void)restoreCompletedTransactions;
+- (void)purchaseProductWithIdentifier:(NSString *)productIdentifier;
+
+@end
diff --git a/MasterPassword/ObjC/MPAppDelegate_InApp.m b/MasterPassword/ObjC/MPAppDelegate_InApp.m
new file mode 100644
index 00000000..43d34198
--- /dev/null
+++ b/MasterPassword/ObjC/MPAppDelegate_InApp.m
@@ -0,0 +1,109 @@
+//
+// MPAppDelegate.m
+// MasterPassword
+//
+// Created by Maarten Billemont on 24/11/11.
+// Copyright (c) 2011 Lyndir. All rights reserved.
+//
+
+#import "MPAppDelegate_InApp.h"
+#import
+
+@interface MPAppDelegate_Shared(InApp_Private)
+@end
+
+@implementation MPAppDelegate_Shared(InApp)
+
+PearlAssociatedObjectProperty( NSArray*, Products, products );
+PearlAssociatedObjectProperty( NSArray*, PaymentTransactions, paymentTransactions );
+
+- (void)updateProducts {
+
+ SKProductsRequest *productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:
+ [[NSSet alloc] initWithObjects:MPProductGenerateLogins, MPProductGenerateAnswers, 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)isPurchased:(NSString *)productIdentifier {
+
+ return YES; //[[NSUserDefaults standardUserDefaults] objectForKey:productIdentifier] != nil;
+}
+
+- (void)restoreCompletedTransactions {
+
+ [[self paymentQueue] restoreCompletedTransactions];
+}
+
+- (void)purchaseProductWithIdentifier:(NSString *)productIdentifier {
+
+ for (SKProduct *product in self.products)
+ if ([product.productIdentifier isEqualToString:productIdentifier]) {
+ [[self paymentQueue] addPayment:[SKPayment paymentWithProduct:product]];
+ return;
+ }
+}
+
+#pragma mark - SKProductsRequestDelegate
+
+- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
+
+ inf( @"products: %@, invalid: %@", response.products, response.invalidProductIdentifiers );
+ self.products = response.products;
+}
+
+- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
+
+ 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: %@", transaction );
+ switch (transaction.transactionState) {
+ case SKPaymentTransactionStatePurchased:
+ case SKPaymentTransactionStateRestored: {
+ inf( @"purchased: %@", transaction.payment.productIdentifier );
+ [[NSUserDefaults standardUserDefaults] setObject:transaction.transactionIdentifier
+ forKey:transaction.payment.productIdentifier];
+ break;
+ }
+ case SKPaymentTransactionStatePurchasing:
+ case SKPaymentTransactionStateFailed:
+ case SKPaymentTransactionStateDeferred:
+ break;
+ }
+ }
+
+ self.paymentTransactions = transactions;
+}
+
+- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error {
+
+ err( @"StoreKit restore failed: %@", [error fullDescription] );
+}
+
+@end
diff --git a/MasterPassword/ObjC/MPAppDelegate_Key.m b/MasterPassword/ObjC/MPAppDelegate_Key.m
index fcc99508..ed6d5d78 100644
--- a/MasterPassword/ObjC/MPAppDelegate_Key.m
+++ b/MasterPassword/ObjC/MPAppDelegate_Key.m
@@ -9,6 +9,12 @@
#import "MPAppDelegate_Key.h"
#import "MPAppDelegate_Store.h"
+@interface MPAppDelegate_Shared()
+
+@property(strong, nonatomic) MPKey *key;
+
+@end
+
@implementation MPAppDelegate_Shared(Key)
static NSDictionary *keyQuery(MPUserEntity *user) {
@@ -85,8 +91,8 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
if ([password length] && (tryKey = [MPAlgorithmDefault keyForPassword:password ofUserNamed:user.name])) {
user.keyID = tryKey.keyID;
- // Migrate existing elements.
- [self migrateElementsForUser:user saveInContext:moc toKey:tryKey];
+ // Migrate existing sites.
+ [self migrateSitesForUser:user saveInContext:moc toKey:tryKey];
}
}
@@ -143,8 +149,8 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
}
user.lastUsed = [NSDate date];
- [moc saveToStore];
self.activeUser = user;
+ [moc saveToStore];
// Perform a data sanity check now that we're logged in as the user to allow fixes that require the user's key.
if ([[MPConfig get].checkInconsistency boolValue])
@@ -158,23 +164,23 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
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.
return;
MPKey *recoverKey = newKey;
#ifdef PEARL_UIKIT
PearlOverlay *activityOverlay = [PearlOverlay showProgressOverlayWithTitle:PearlString( @"Migrating %ld sites...",
- (long)[user.elements count] )];
+ (long)[user.sites count] )];
#endif
- for (MPElementEntity *element in user.elements) {
- if (element.type & MPElementTypeClassStored) {
+ for (MPSiteEntity *site in user.sites) {
+ if (site.type & MPSiteTypeClassStored) {
NSString *content;
- while (!(content = [element.algorithm storedContentForElement:(MPElementStoredEntity *)element usingKey:recoverKey])) {
- // Failed to decrypt element with the current recoveryKey. Ask user for a new one to use.
+ while (!(content = [site.algorithm storedPasswordForSite:(MPStoredSiteEntity *)site usingKey:recoverKey])) {
+ // Failed to decrypt site with the current recoveryKey. Ask user for a new one to use.
__block NSString *masterPassword = nil;
#ifdef PEARL_UIKIT
@@ -182,7 +188,7 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
dispatch_group_enter( recoverPasswordGroup );
[PearlAlert showAlertWithTitle:@"Enter Old Master Password"
message:PearlString( @"Your old master password is required to migrate the stored password for %@",
- element.name )
+ site.name )
viewStyle:UIAlertViewStyleSecureTextInput
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
@try {
@@ -202,7 +208,7 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
// Don't Migrate
break;
- recoverKey = [element.algorithm keyForPassword:masterPassword ofUserNamed:user.name];
+ recoverKey = [site.algorithm keyForPassword:masterPassword ofUserNamed:user.name];
}
if (!content)
@@ -210,7 +216,7 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
break;
if (![recoverKey isEqualToKey:newKey])
- [element.algorithm saveContent:content toElement:element usingKey:newKey];
+ [site.algorithm savePassword:content toSite:site usingKey:newKey];
}
}
diff --git a/MasterPassword/ObjC/MPAppDelegate_Shared.h b/MasterPassword/ObjC/MPAppDelegate_Shared.h
index 96e2f0ff..24213eba 100644
--- a/MasterPassword/ObjC/MPAppDelegate_Shared.h
+++ b/MasterPassword/ObjC/MPAppDelegate_Shared.h
@@ -9,19 +9,19 @@
#import "MPEntities.h"
#if TARGET_OS_IPHONE
-
@interface MPAppDelegate_Shared : PearlAppDelegate
#else
@interface MPAppDelegate_Shared : NSObject
#endif
-@property(strong, nonatomic) MPKey *key;
-@property(strong, nonatomic) NSManagedObjectID *activeUserOID;
+@property(strong, nonatomic, readonly) MPKey *key;
+@property(strong, nonatomic, readonly) NSManagedObjectID *activeUserOID;
+ (instancetype)get;
- (MPUserEntity *)activeUserForMainThread;
- (MPUserEntity *)activeUserInContext:(NSManagedObjectContext *)context;
- (void)setActiveUser:(MPUserEntity *)activeUser;
+- (void)handleCoordinatorError:(NSError *)error;
@end
diff --git a/MasterPassword/ObjC/MPAppDelegate_Shared.m b/MasterPassword/ObjC/MPAppDelegate_Shared.m
index cdfcfe02..1d72f074 100644
--- a/MasterPassword/ObjC/MPAppDelegate_Shared.m
+++ b/MasterPassword/ObjC/MPAppDelegate_Shared.m
@@ -6,10 +6,18 @@
// Copyright (c) 2011 Lyndir. All rights reserved.
//
+#import
#import "MPAppDelegate_Shared.h"
#import "MPAppDelegate_Store.h"
#import "MPAppDelegate_Key.h"
+@interface MPAppDelegate_Shared ()
+
+@property(strong, nonatomic) MPKey *key;
+@property(strong, nonatomic) NSManagedObjectID *activeUserOID;
+
+@end
+
@implementation MPAppDelegate_Shared
+ (MPAppDelegate_Shared *)get {
@@ -45,9 +53,13 @@
NSError *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;
}
+- (void)handleCoordinatorError:(NSError *)error {
+
+}
+
@end
diff --git a/MasterPassword/ObjC/MPAppDelegate_Store.h b/MasterPassword/ObjC/MPAppDelegate_Store.h
index 96891b53..9f5a7b04 100644
--- a/MasterPassword/ObjC/MPAppDelegate_Store.h
+++ b/MasterPassword/ObjC/MPAppDelegate_Store.h
@@ -8,7 +8,6 @@
#import "MPAppDelegate_Shared.h"
-#import "UbiquityStoreManager.h"
#import "MPFixable.h"
typedef NS_ENUM( NSUInteger, MPImportResult ) {
@@ -19,7 +18,7 @@ typedef NS_ENUM( NSUInteger, MPImportResult ) {
MPImportResultInternalError,
};
-@interface MPAppDelegate_Shared(Store)
+@interface MPAppDelegate_Shared(Store)
+ (NSManagedObjectContext *)managedObjectContextForMainThreadIfReady;
+ (BOOL)managedObjectContextForMainThreadPerformBlock:(void (^)(NSManagedObjectContext *mainContext))mocBlock;
@@ -27,12 +26,12 @@ typedef NS_ENUM( NSUInteger, MPImportResult ) {
+ (BOOL)managedObjectContextPerformBlock:(void (^)(NSManagedObjectContext *context))mocBlock;
+ (BOOL)managedObjectContextPerformBlockAndWait:(void (^)(NSManagedObjectContext *context))mocBlock;
-- (UbiquityStoreManager *)storeManager;
- (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. */
-- (void)addElementNamed:(NSString *)siteName completion:(void ( ^ )(MPElementEntity *element, NSManagedObjectContext *context))completion;
-- (MPElementEntity *)changeElement:(MPElementEntity *)element saveInContext:(NSManagedObjectContext *)context toType:(MPElementType)type;
+/** @param completion The block to execute after adding the site, executed from the main thread with the new site in the main MOC. */
+- (void)addSiteNamed:(NSString *)siteName completion:(void ( ^ )(MPSiteEntity *site, NSManagedObjectContext *context))completion;
+- (MPSiteEntity *)changeSite:(MPSiteEntity *)site saveInContext:(NSManagedObjectContext *)context toType:(MPSiteType)type;
- (MPImportResult)importSites:(NSString *)importedSitesString
askImportPassword:(NSString *(^)(NSString *userName))importPassword
askUserPassword:(NSString *(^)(NSString *userName, NSUInteger importCount, NSUInteger deleteCount))userPassword;
diff --git a/MasterPassword/ObjC/MPAppDelegate_Store.m b/MasterPassword/ObjC/MPAppDelegate_Store.m
index 42a50f97..5cdc7dbe 100644
--- a/MasterPassword/ObjC/MPAppDelegate_Store.m
+++ b/MasterPassword/ObjC/MPAppDelegate_Store.m
@@ -7,6 +7,8 @@
//
#import "MPAppDelegate_Store.h"
+#import "MPGeneratedSiteEntity.h"
+#import "NSManagedObjectModel+KCOrderedAccessorFix.h"
#if TARGET_OS_IPHONE
#define STORE_OPTIONS NSPersistentStoreFileProtectionKey : NSFileProtectionComplete,
@@ -14,27 +16,21 @@
#define STORE_OPTIONS
#endif
-#define MPCloudContainerIdentifier @"HL3Q45LX9N.com.lyndir.lhunath.MasterPassword.shared"
-#define MPMigrationLevelLocalStoreKey @"MPMigrationLevelLocalStoreKey"
-#define MPMigrationLevelCloudStoreKey @"MPMigrationLevelCloudStoreKey"
+#define MPStoreMigrationLevelKey @"MPMigrationLevelLocalStoreKey"
-typedef NS_ENUM( NSInteger, MPMigrationLevelLocalStore ) {
- MPMigrationLevelLocalStoreV1,
- MPMigrationLevelLocalStoreV2,
- MPMigrationLevelLocalStoreCurrent = MPMigrationLevelLocalStoreV2,
-};
-
-typedef NS_ENUM( NSInteger, MPMigrationLevelCloudStore ) {
- MPMigrationLevelCloudStoreV1,
- MPMigrationLevelCloudStoreV2,
- MPMigrationLevelCloudStoreV3,
- MPMigrationLevelCloudStoreCurrent = MPMigrationLevelCloudStoreV3,
+typedef NS_ENUM( NSInteger, MPStoreMigrationLevel ) {
+ MPStoreMigrationLevelV1,
+ MPStoreMigrationLevelV2,
+ MPStoreMigrationLevelV3,
+ MPStoreMigrationLevelCurrent = MPStoreMigrationLevelV3,
};
@implementation MPAppDelegate_Shared(Store)
PearlAssociatedObjectProperty( id, SaveObserver, saveObserver );
+PearlAssociatedObjectProperty( NSPersistentStoreCoordinator*, PersistentStoreCoordinator, persistentStoreCoordinator );
+
PearlAssociatedObjectProperty( NSManagedObjectContext*, PrivateManagedObjectContext, privateManagedObjectContext );
PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext, mainManagedObjectContext );
@@ -109,47 +105,144 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
- (NSManagedObjectContext *)mainManagedObjectContextIfReady {
- [self storeManager];
+ [self loadStore];
return self.mainManagedObjectContext;
}
- (NSManagedObjectContext *)privateManagedObjectContextIfReady {
- [self storeManager];
+ [self loadStore];
return self.privateManagedObjectContext;
}
-- (UbiquityStoreManager *)storeManager {
+- (NSURL *)localStoreURL {
- static UbiquityStoreManager *storeManager = nil;
- if (storeManager)
- return storeManager;
+ NSURL *applicationSupportURL = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory
+ inDomains:NSUserDomainMask] lastObject];
+ return [[[applicationSupportURL
+ URLByAppendingPathComponent:[NSBundle mainBundle].bundleIdentifier isDirectory:YES]
+ URLByAppendingPathComponent:@"UbiquityStore" isDirectory:NO]
+ URLByAppendingPathExtension:@"sqlite"];
+}
- storeManager = [[UbiquityStoreManager alloc] initStoreNamed:nil withManagedObjectModel:nil localStoreURL:nil
- containerIdentifier:MPCloudContainerIdentifier
- storeConfiguration:nil storeOptions:@{ STORE_OPTIONS }
- delegate:self];
+- (void)loadStore {
+
+ @synchronized (self) {
+ // Do nothing if already fully set up, otherwise (re-)load the store.
+ if (self.persistentStoreCoordinator && self.saveObserver && self.mainManagedObjectContext && self.privateManagedObjectContext)
+ return;
+
+ // Unregister any existing observers and contexts.
+ if (self.saveObserver)
+ [[NSNotificationCenter defaultCenter] removeObserver:self.saveObserver];
+ [self.mainManagedObjectContext performBlockAndWait:^{
+ [self.mainManagedObjectContext reset];
+ self.mainManagedObjectContext = nil;
+ }];
+ [self.privateManagedObjectContext performBlockAndWait:^{
+ [self.privateManagedObjectContext reset];
+ self.privateManagedObjectContext = nil;
+ }];
+
+ // Check if migration is necessary.
+ [self migrateStore];
+
+ // Create a new store coordinator.
+ if (!self.persistentStoreCoordinator) {
+ NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];
+ [model kc_generateOrderedSetAccessors];
+ self.persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
+ }
+
+ NSError *error = nil;
+ NSURL *localStoreURL = [self localStoreURL];
+ if (![[NSFileManager defaultManager] createDirectoryAtURL:[localStoreURL URLByDeletingLastPathComponent]
+ withIntermediateDirectories:YES attributes:nil error:&error]) {
+ err( @"Couldn't create our application support directory: %@", [error fullDescription] );
+ return;
+ }
+ 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 handleCoordinatorError:error];
+ return;
+ }
+
+ // Create our contexts and observer.
+ self.privateManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
+ [self.privateManagedObjectContext performBlockAndWait:^{
+ self.privateManagedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
+ self.privateManagedObjectContext.persistentStoreCoordinator = self.persistentStoreCoordinator;
+ }];
+
+ self.mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
+ self.mainManagedObjectContext.parentContext = self.privateManagedObjectContext;
+
+ self.saveObserver = [[NSNotificationCenter defaultCenter] addObserverForName:NSManagedObjectContextDidSaveNotification
+ object:self.privateManagedObjectContext queue:nil usingBlock:
+ ^(NSNotification *note) {
+ // When privateManagedObjectContext is saved, import the changes into mainManagedObjectContext.
+ [self.mainManagedObjectContext performBlock:^{
+ [self.mainManagedObjectContext mergeChangesFromContextDidSaveNotification:note];
+ }];
+ }];
#if TARGET_OS_IPHONE
- [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillTerminateNotification object:UIApp
- queue:[NSOperationQueue mainQueue] usingBlock:
- ^(NSNotification *note) {
- [[self mainManagedObjectContext] saveToStore];
- }];
- [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillResignActiveNotification object:UIApp
- queue:[NSOperationQueue mainQueue] usingBlock:
- ^(NSNotification *note) {
- [[self mainManagedObjectContext] saveToStore];
- }];
+ [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillTerminateNotification object:UIApp
+ queue:[NSOperationQueue mainQueue] usingBlock:
+ ^(NSNotification *note) {
+ [self.mainManagedObjectContext saveToStore];
+ }];
+ [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillResignActiveNotification object:UIApp
+ queue:[NSOperationQueue mainQueue] usingBlock:
+ ^(NSNotification *note) {
+ [self.mainManagedObjectContext saveToStore];
+ }];
#else
[[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationWillTerminateNotification object:NSApp
queue:[NSOperationQueue mainQueue] usingBlock:
- ^(NSNotification *note) {
- [self.mainManagedObjectContextIfReady saveToStore];
- }];
+ ^(NSNotification *note) {
+ [self.mainManagedObjectContext saveToStore];
+ }];
#endif
- return storeManager;
+ // Perform a data sanity check on the newly loaded store to find and fix any issues.
+ if ([[MPConfig get].checkInconsistency boolValue])
+ [MPAppDelegate_Shared managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) {
+ [self findAndFixInconsistenciesSaveInContext:context];
+ }];
+ }
+}
+
+- (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 {
@@ -164,7 +257,7 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
fetchRequest.entity = entity;
NSArray *objects = [context executeFetchRequest:fetchRequest error:&error];
if (!objects) {
- err( @"Failed to fetch %@ objects: %@", entity, error );
+ err( @"Failed to fetch %@ objects: %@", entity, [error fullDescription] );
continue;
}
@@ -187,101 +280,25 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
return result;
}
-- (void)migrateStoreForManager:(UbiquityStoreManager *)manager isCloud:(BOOL)isCloudStore {
+- (void)migrateStore {
- [self migrateLocalStore];
-
- if (isCloudStore)
- [self migrateCloudStore];
-}
-
-- (void)migrateLocalStore {
-
- MPMigrationLevelLocalStore migrationLevel = (signed)[[NSUserDefaults standardUserDefaults] integerForKey:MPMigrationLevelLocalStoreKey];
- if (migrationLevel >= MPMigrationLevelLocalStoreCurrent)
+ MPStoreMigrationLevel migrationLevel = (signed)[[NSUserDefaults standardUserDefaults] integerForKey:MPStoreMigrationLevelKey];
+ if (migrationLevel >= MPStoreMigrationLevelCurrent)
// Local store up-to-date.
return;
- inf( @"Local store migration level: %d (current %d)", (signed)migrationLevel, (signed)MPMigrationLevelLocalStoreCurrent );
- if (migrationLevel <= MPMigrationLevelLocalStoreV1) if (![self migrateV1LocalStore]) {
+ inf( @"Local store migration level: %d (current %d)", (signed)migrationLevel, (signed)MPStoreMigrationLevelCurrent );
+ if (migrationLevel == MPStoreMigrationLevelV1 && ![self migrateV1LocalStore]) {
inf( @"Failed to migrate old V1 to new local store." );
return;
}
-
- [[NSUserDefaults standardUserDefaults] setInteger:MPMigrationLevelLocalStoreCurrent forKey:MPMigrationLevelLocalStoreKey];
- inf( @"Successfully migrated old to new local store." );
-}
-
-- (void)migrateCloudStore {
-
- MPMigrationLevelCloudStore migrationLevel = (signed)[[NSUserDefaults standardUserDefaults] integerForKey:MPMigrationLevelCloudStoreKey];
- if (migrationLevel >= MPMigrationLevelCloudStoreCurrent)
- // Cloud store up-to-date.
+ if (migrationLevel == MPStoreMigrationLevelV2 && ![self migrateV2LocalStore]) {
+ inf( @"Failed to migrate old V2 to new local store." );
return;
-
- inf( @"Cloud store migration level: %d (current %d)", (signed)migrationLevel, (signed)MPMigrationLevelCloudStoreCurrent );
- if (migrationLevel <= MPMigrationLevelCloudStoreV1) {
- if (![self migrateV1CloudStore]) {
- inf( @"Failed to migrate old V1 to new cloud store." );
- return;
- }
- }
- else if (migrationLevel <= MPMigrationLevelCloudStoreV2) {
- if (![self migrateV2CloudStore]) {
- inf( @"Failed to migrate old V2 to new cloud store." );
- return;
- }
}
- [[NSUserDefaults standardUserDefaults] setInteger:MPMigrationLevelCloudStoreCurrent forKey:MPMigrationLevelCloudStoreKey];
- inf( @"Successfully migrated old to new cloud store." );
-}
-
-- (BOOL)migrateV1CloudStore {
-
- // Migrate cloud enabled preference.
- NSNumber *oldCloudEnabled = [[NSUserDefaults standardUserDefaults] objectForKey:@"iCloudEnabledKey"];
- if ([oldCloudEnabled boolValue])
- [[NSUserDefaults standardUserDefaults] setBool:YES forKey:USMCloudEnabledKey];
-
- // Migrate cloud store.
- NSString *uuid = [[NSUserDefaults standardUserDefaults] stringForKey:@"LocalUUIDKey"];
- if (!uuid) {
- inf( @"No V1 cloud store to migrate." );
- return YES;
- }
-
- inf( @"Migrating V1 cloud store: %@ -> %@", uuid, [self.storeManager valueForKey:@"storeUUID"] );
- NSURL *cloudContainerURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:MPCloudContainerIdentifier];
- NSURL *oldCloudContentURL = [[cloudContainerURL
- URLByAppendingPathComponent:@"Data" isDirectory:YES]
- URLByAppendingPathComponent:uuid isDirectory:YES];
- NSURL *oldCloudStoreURL = [[[cloudContainerURL
- URLByAppendingPathComponent:@"Database.nosync" isDirectory:YES]
- URLByAppendingPathComponent:uuid isDirectory:NO] URLByAppendingPathExtension:@"sqlite"];
-
- return [self migrateFromCloudStore:oldCloudStoreURL cloudContent:oldCloudContentURL];
-}
-
-- (BOOL)migrateV2CloudStore {
-
- // Migrate cloud store.
- NSString *uuid = [[NSUbiquitousKeyValueStore defaultStore] stringForKey:@"USMStoreUUIDKey"];
- if (!uuid) {
- inf( @"No V2 cloud store to migrate." );
- return YES;
- }
-
- inf( @"Migrating V2 cloud store: %@ -> %@", uuid, [self.storeManager valueForKey:@"storeUUID"] );
- NSURL *cloudContainerURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:MPCloudContainerIdentifier];
- NSURL *oldCloudContentURL = [[cloudContainerURL
- URLByAppendingPathComponent:@"CloudLogs" isDirectory:YES]
- URLByAppendingPathComponent:uuid isDirectory:YES];
- NSURL *oldCloudStoreURL = [[[cloudContainerURL
- URLByAppendingPathComponent:@"CloudStore.nosync" isDirectory:YES]
- URLByAppendingPathComponent:uuid isDirectory:NO] URLByAppendingPathExtension:@"sqlite"];
-
- return [self migrateFromCloudStore:oldCloudStoreURL cloudContent:oldCloudContentURL];
+ [[NSUserDefaults standardUserDefaults] setInteger:MPStoreMigrationLevelCurrent forKey:MPStoreMigrationLevelKey];
+ inf( @"Successfully migrated old to new local store." );
}
- (BOOL)migrateV1LocalStore {
@@ -296,146 +313,63 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
}
inf( @"Migrating V1 local store" );
- return [self migrateFromLocalStore:oldLocalStoreURL];
-}
-
-- (BOOL)migrateFromLocalStore:(NSURL *)oldLocalStoreURL {
-
- NSURL *newLocalStoreURL = [self.storeManager URLForLocalStore];
- if ([[NSFileManager defaultManager] fileExistsAtPath:newLocalStoreURL.path isDirectory:NULL]) {
- wrn( @"Can't migrate local store: A new local store already exists." );
- return YES;
+ NSURL *newLocalStoreURL = [self localStoreURL];
+ NSError *error = nil;
+ if (![[NSFileManager defaultManager] createDirectoryAtURL:[newLocalStoreURL URLByDeletingLastPathComponent]
+ withIntermediateDirectories:YES attributes:nil error:&error]) {
+ err( @"Couldn't create our application support directory: %@", [error fullDescription] );
+ return NO;
}
-
- if (![self.storeManager migrateStore:oldLocalStoreURL withOptions:nil
- toStore:newLocalStoreURL withOptions:nil
- strategy:0 error:nil cause:nil context:nil]) {
- self.storeManager.localStoreURL = oldLocalStoreURL;
+ if (![[NSFileManager defaultManager] moveItemAtURL:oldLocalStoreURL toURL:newLocalStoreURL error:&error]) {
+ err( @"Couldn't move the old store to the new location: %@", [error fullDescription] );
return NO;
}
- inf( @"Successfully migrated to new local store." );
return YES;
}
-- (BOOL)migrateFromCloudStore:(NSURL *)oldCloudStoreURL cloudContent:(NSURL *)oldCloudContentURL {
+- (BOOL)migrateV2LocalStore {
- if (![self.storeManager cloudSafeForSeeding]) {
- inf( @"Can't migrate cloud store: A new cloud store already exists." );
+ NSURL *applicationSupportURL = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory
+ inDomains:NSUserDomainMask] lastObject];
+ NSURL *oldLocalStoreURL;
+ // On iOS, each app is in a sandbox so we don't need to app-scope this directory.
+#if TARGET_OS_IPHONE
+ oldLocalStoreURL = [[applicationSupportURL
+ URLByAppendingPathComponent:@"UbiquityStore" isDirectory:NO]
+ URLByAppendingPathExtension:@"sqlite"];
+#else
+ // The directory is shared between all apps on the system so we need to scope it for the running app.
+ oldLocalStoreURL = [[[applicationSupportURL
+ URLByAppendingPathComponent:[NSRunningApplication currentApplication].bundleIdentifier isDirectory:YES]
+ URLByAppendingPathComponent:@"UbiquityStore" isDirectory:NO]
+ URLByAppendingPathExtension:@"sqlite"];
+#endif
+
+ if (![[NSFileManager defaultManager] fileExistsAtPath:oldLocalStoreURL.path isDirectory:NULL]) {
+ inf( @"No V2 local store to migrate." );
return YES;
}
- NSURL *newCloudStoreURL = [self.storeManager URLForCloudStore];
- if (![self.storeManager migrateStore:oldCloudStoreURL withOptions:nil
- toStore:newCloudStoreURL withOptions:nil
- strategy:0 error:nil cause:nil context:nil])
+ inf( @"Migrating V2 local store" );
+ NSURL *newLocalStoreURL = [self localStoreURL];
+ NSError *error = nil;
+ if (![[NSFileManager defaultManager] createDirectoryAtURL:[newLocalStoreURL URLByDeletingLastPathComponent]
+ withIntermediateDirectories:YES attributes:nil error:&error]) {
+ err( @"Couldn't create our application support directory: %@", [error fullDescription] );
return NO;
+ }
+ if (![[NSFileManager defaultManager] moveItemAtURL:oldLocalStoreURL toURL:newLocalStoreURL error:&error]) {
+ err( @"Couldn't move the old store to the new location: %@", [error fullDescription] );
+ return NO;
+ }
- inf( @"Successfully migrated to new cloud store." );
return YES;
}
-#pragma mark - UbiquityStoreManagerDelegate
-
-- (NSManagedObjectContext *)ubiquityStoreManager:(UbiquityStoreManager *)manager
- managedObjectContextForUbiquityChanges:(NSNotification *)note {
-
- return [self mainManagedObjectContextIfReady];
-}
-
-- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager log:(NSString *)message {
-
- inf( @"[StoreManager] %@", message );
-}
-
-- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager willLoadStoreIsCloud:(BOOL)isCloudStore {
-
- NSManagedObjectContext *moc = [self mainManagedObjectContextIfReady];
- [moc performBlockAndWait:^{
- [moc saveToStore];
- [moc reset];
-
- if (self.saveObserver) {
- [[NSNotificationCenter defaultCenter] removeObserver:self.saveObserver];
- self.saveObserver = nil;
- }
-
- self.privateManagedObjectContext = nil;
- self.mainManagedObjectContext = nil;
- }];
-
- [self migrateStoreForManager:manager isCloud:isCloudStore];
-}
-
-- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didLoadStoreForCoordinator:(NSPersistentStoreCoordinator *)coordinator
- isCloud:(BOOL)isCloudStore {
-
- inf( @"Using iCloud? %@", @(isCloudStore) );
- MPCheckpoint( MPCheckpointCloud, @{
- @"enabled" : @(isCloudStore)
- } );
-
- // Create our contexts.
- NSManagedObjectContext *privateManagedObjectContext =
- [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
- [privateManagedObjectContext performBlockAndWait:^{
- privateManagedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
- privateManagedObjectContext.persistentStoreCoordinator = coordinator;
-
-// dbg(@"===");
-// NSError *error;
-// for (NSEntityDescription *entityDescription in [coordinator.managedObjectModel entities]) {
-// dbg(@"Entities: %@", entityDescription.name);
-// NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:entityDescription.name];
-// NSArray *entities = [privateManagedObjectContext executeFetchRequest:request error:&error];
-// if (!entities)
-// err(@" - Error: %@", error);
-// else
-// for (id entity in entities)
-// dbg(@" - %@", [entity debugDescription]);
-// }
-// dbg(@"===");
- }];
-
- NSManagedObjectContext *mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
- mainManagedObjectContext.parentContext = privateManagedObjectContext;
-
- if (self.saveObserver)
- [[NSNotificationCenter defaultCenter] removeObserver:self.saveObserver];
- self.saveObserver = [[NSNotificationCenter defaultCenter] addObserverForName:NSManagedObjectContextDidSaveNotification
- object:privateManagedObjectContext queue:nil usingBlock:
- ^(NSNotification *note) {
- // When privateManagedObjectContext is saved, import the changes into mainManagedObjectContext.
- [mainManagedObjectContext performBlock:^{
- [mainManagedObjectContext mergeChangesFromContextDidSaveNotification:note];
- }];
- }];
-
- self.privateManagedObjectContext = privateManagedObjectContext;
- self.mainManagedObjectContext = mainManagedObjectContext;
-
- // Perform a data sanity check on the newly loaded store to find and fix any issues.
- if ([[MPConfig get].checkInconsistency boolValue])
- [MPAppDelegate_Shared managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) {
- [self findAndFixInconsistenciesSaveInContext:context];
- }];
-}
-
-- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didEncounterError:(NSError *)error cause:(UbiquityStoreErrorCause)cause
- context:(id)context {
-
- err( @"[StoreManager] ERROR: cause=%@, context=%@, error=%@", NSStringFromUSMCause( cause ), context, error );
- MPCheckpoint( MPCheckpointMPErrorUbiquity, @{
- @"cause" : @(cause),
- @"error.code" : @(error.code),
- @"error.domain" : NilToNSNull( error.domain ),
- @"error.reason" : NilToNSNull( [error localizedFailureReason]?: [error localizedDescription] ),
- } );
-}
-
#pragma mark - Utilities
-- (void)addElementNamed:(NSString *)siteName completion:(void ( ^ )(MPElementEntity *element, NSManagedObjectContext *context))completion {
+- (void)addSiteNamed:(NSString *)siteName completion:(void ( ^ )(MPSiteEntity *site, NSManagedObjectContext *context))completion {
if (![siteName length]) {
completion( nil, nil );
@@ -450,61 +384,61 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
return;
}
- MPElementType type = activeUser.defaultType;
+ MPSiteType type = activeUser.defaultType;
NSString *typeEntityName = [MPAlgorithmDefault classNameOfType:type];
- MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:typeEntityName inManagedObjectContext:context];
- element.name = siteName;
- element.user = activeUser;
- element.type = type;
- element.lastUsed = [NSDate date];
- element.version = MPAlgorithmDefaultVersion;
+ MPSiteEntity *site = [NSEntityDescription insertNewObjectForEntityForName:typeEntityName inManagedObjectContext:context];
+ site.name = siteName;
+ site.user = activeUser;
+ site.type = type;
+ site.lastUsed = [NSDate date];
+ site.version = MPAlgorithmDefaultVersion;
NSError *error = nil;
- if (element.objectID.isTemporaryID && ![context obtainPermanentIDsForObjects:@[ element ] error:&error])
- err( @"Failed to obtain a permanent object ID after creating new element: %@", error );
+ if (site.objectID.isTemporaryID && ![context obtainPermanentIDsForObjects:@[ site ] error:&error])
+ err( @"Failed to obtain a permanent object ID after creating new site: %@", [error fullDescription] );
[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)
- return element;
+ if (site.type == type)
+ return site;
- if ([element.algorithm classOfType:type] == element.typeClass) {
- element.type = type;
+ if ([site.algorithm classOfType:type] == site.typeClass) {
+ site.type = type;
[context saveToStore];
}
else {
- // Type requires a different class of element. Recreate the element.
- NSString *typeEntityName = [element.algorithm classNameOfType:type];
- MPElementEntity *newElement = [NSEntityDescription insertNewObjectForEntityForName:typeEntityName inManagedObjectContext:context];
- newElement.type = type;
- newElement.name = element.name;
- newElement.user = element.user;
- newElement.uses = element.uses;
- newElement.lastUsed = element.lastUsed;
- newElement.version = element.version;
- newElement.loginName = element.loginName;
+ // Type requires a different class of site. Recreate the site.
+ NSString *typeEntityName = [site.algorithm classNameOfType:type];
+ MPSiteEntity *newSite = [NSEntityDescription insertNewObjectForEntityForName:typeEntityName inManagedObjectContext:context];
+ newSite.type = type;
+ newSite.name = site.name;
+ newSite.user = site.user;
+ newSite.uses = site.uses;
+ newSite.lastUsed = site.lastUsed;
+ newSite.version = site.version;
+ newSite.loginName = site.loginName;
NSError *error = nil;
- if (![context obtainPermanentIDsForObjects:@[ newElement ] error:&error])
- err( @"Failed to obtain a permanent object ID after changing object type: %@", error );
+ if (![context obtainPermanentIDsForObjects:@[ newSite ] error:&error])
+ err( @"Failed to obtain a permanent object ID after changing object type: %@", [error fullDescription] );
- [context deleteObject:element];
+ [context deleteObject:site];
[context saveToStore];
- [[NSNotificationCenter defaultCenter] postNotificationName:MPElementUpdatedNotification object:element.objectID];
- element = newElement;
+ [[NSNotificationCenter defaultCenter] postNotificationName:MPSiteUpdatedNotification object:site.objectID];
+ site = newSite;
}
- [[NSNotificationCenter defaultCenter] postNotificationName:MPElementUpdatedNotification object:element.objectID];
- return element;
+ [[NSNotificationCenter defaultCenter] postNotificationName:MPSiteUpdatedNotification object:site.objectID];
+ return site;
}
- (MPImportResult)importSites:(NSString *)importedSitesString
@@ -540,7 +474,7 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
initWithPattern:@"^#[[:space:]]*([^:]+): (.*)"
options:(NSRegularExpressionOptions)0 error:&error];
if (error) {
- err( @"Error loading the header pattern: %@", error );
+ err( @"Error loading the header pattern: %@", [error fullDescription] );
return MPImportResultInternalError;
}
}
@@ -554,7 +488,7 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
options:(NSRegularExpressionOptions)0 error:&error]
];
if (error) {
- err( @"Error loading the site patterns: %@", error );
+ err( @"Error loading the site patterns: %@", [error fullDescription] );
return MPImportResultInternalError;
}
}
@@ -569,9 +503,9 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
NSData *importKeyID = nil;
BOOL headerStarted = NO, headerEnded = NO, clearText = NO;
NSArray *importedSiteLines = [importedSitesString componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
- NSMutableSet *elementsToDelete = [NSMutableSet set];
- NSMutableArray *importedSiteElements = [NSMutableArray arrayWithCapacity:[importedSiteLines count]];
- NSFetchRequest *elementFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )];
+ NSMutableSet *sitesToDelete = [NSMutableSet set];
+ NSMutableArray *importedSiteSites = [NSMutableArray arrayWithCapacity:[importedSiteLines count]];
+ NSFetchRequest *siteFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
for (NSString *importedSiteLine in importedSiteLines) {
if ([importedSiteLine hasPrefix:@"#"]) {
// Comment or header
@@ -593,10 +527,10 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
err( @"Invalid header format in line: %@", importedSiteLine );
return MPImportResultMalformedInput;
}
- NSTextCheckingResult *headerElements = [[headerPattern matchesInString:importedSiteLine options:(NSMatchingOptions)0
+ NSTextCheckingResult *headerSites = [[headerPattern matchesInString:importedSiteLine options:(NSMatchingOptions)0
range:NSMakeRange( 0, [importedSiteLine length] )] lastObject];
- NSString *headerName = [importedSiteLine substringWithRange:[headerElements rangeAtIndex:1]];
- NSString *headerValue = [importedSiteLine substringWithRange:[headerElements rangeAtIndex:2]];
+ NSString *headerName = [importedSiteLine substringWithRange:[headerSites rangeAtIndex:1]];
+ NSString *headerValue = [importedSiteLine substringWithRange:[headerSites rangeAtIndex:2]];
if ([headerName isEqualToString:@"User Name"]) {
importUserName = headerValue;
@@ -604,7 +538,7 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
userFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", importUserName];
NSArray *users = [context executeFetchRequest:userFetchRequest error:&error];
if (!users) {
- err( @"While looking for user: %@, error: %@", importUserName, error );
+ err( @"While looking for user: %@, error: %@", importUserName, [error fullDescription] );
return MPImportResultInternalError;
}
if ([users count] > 1) {
@@ -688,27 +622,27 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
// Find existing site.
if (user) {
- elementFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND user == %@", siteName, user];
- NSArray *existingSites = [context executeFetchRequest:elementFetchRequest error:&error];
+ siteFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND user == %@", siteName, user];
+ NSArray *existingSites = [context executeFetchRequest:siteFetchRequest error:&error];
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;
}
if ([existingSites count]) {
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=%@",
lastUsed, uses, type, version, counter, loginName, siteName, exportContent );
}
// 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],
- (unsigned long)[elementsToDelete count], [MPUserEntity idFor:importUserName] );
- NSString *userMasterPassword = askUserPassword( user? user.name: importUserName, [importedSiteElements count],
- [elementsToDelete count] );
+ inf( @"Importing %lu sites, deleting %lu sites, for user: %@", (unsigned long)[importedSiteSites count],
+ (unsigned long)[sitesToDelete count], [MPUserEntity idFor:importUserName] );
+ NSString *userMasterPassword = askUserPassword( user? user.name: importUserName, [importedSiteSites count],
+ [sitesToDelete count] );
if (!userMasterPassword) {
inf( @"Import cancelled." );
return MPImportResultCancelled;
@@ -724,8 +658,8 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
// Delete existing sites.
- if (elementsToDelete.count)
- [elementsToDelete enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
+ if (sitesToDelete.count)
+ [sitesToDelete enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
inf( @"Deleting site: %@, it will be replaced by an imported site.", [obj name] );
[context deleteObject:obj];
}];
@@ -735,7 +669,8 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
if (importAvatar != NSNotFound)
user.avatar = importAvatar;
dbg( @"Updating User: %@", [user debugDescription] );
- } else {
+ }
+ else {
user = [MPUserEntity insertNewObjectInContext:context];
user.name = importUserName;
user.keyID = importKeyID;
@@ -745,10 +680,10 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
}
// Import new sites.
- for (NSArray *siteElements in importedSiteElements) {
+ for (NSArray *siteElements in importedSiteSites) {
NSDate *lastUsed = [[NSDateFormatter rfc3339DateFormatter] dateFromString:siteElements[0]];
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 counter = [siteElements[4] length]? (unsigned)[siteElements[4] integerValue]: NSNotFound;
NSString *loginName = [siteElements[5] length]? siteElements[5]: nil;
@@ -757,24 +692,24 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
// Create new site.
NSString *typeEntityName = [MPAlgorithmForVersion( version ) classNameOfType:type];
- MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:typeEntityName inManagedObjectContext:context];
- element.name = siteName;
- element.loginName = loginName;
- element.user = user;
- element.type = type;
- element.uses = uses;
- element.lastUsed = lastUsed;
- element.version = version;
+ MPSiteEntity *site = [NSEntityDescription insertNewObjectForEntityForName:typeEntityName inManagedObjectContext:context];
+ site.name = siteName;
+ site.loginName = loginName;
+ site.user = user;
+ site.type = type;
+ site.uses = uses;
+ site.lastUsed = lastUsed;
+ site.version = version;
if ([exportContent length]) {
if (clearText)
- [element.algorithm importClearTextContent:exportContent intoElement:element usingKey:userKey];
+ [site.algorithm importClearTextPassword:exportContent intoSite:site usingKey:userKey];
else
- [element.algorithm importProtectedContent:exportContent protectedByKey:importKey intoElement:element usingKey:userKey];
+ [site.algorithm importProtectedPassword:exportContent protectedByKey:importKey intoSite:site usingKey:userKey];
}
- if ([element isKindOfClass:[MPElementGeneratedEntity class]] && counter != NSNotFound)
- ((MPElementGeneratedEntity *)element).counter = counter;
+ if ([site isKindOfClass:[MPGeneratedSiteEntity class]] && counter != NSNotFound)
+ ((MPGeneratedSiteEntity *)site).counter = counter;
- dbg( @"Created Element: %@", [element debugDescription] );
+ dbg( @"Created Site: %@", [site debugDescription] );
}
if (![context saveToStore])
@@ -820,27 +755,27 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
[export appendFormat:@"# used used type name\t name\tpassword\n"];
// Sites.
- for (MPElementEntity *element in activeUser.elements) {
- NSDate *lastUsed = element.lastUsed;
- NSUInteger uses = element.uses;
- MPElementType type = element.type;
- NSUInteger version = element.version;
+ for (MPSiteEntity *site in activeUser.sites) {
+ NSDate *lastUsed = site.lastUsed;
+ NSUInteger uses = site.uses;
+ MPSiteType type = site.type;
+ NSUInteger version = site.version;
NSUInteger counter = 0;
- NSString *loginName = element.loginName;
- NSString *siteName = element.name;
+ NSString *loginName = site.loginName;
+ NSString *siteName = site.name;
NSString *content = nil;
// Generated-specific
- if ([element isKindOfClass:[MPElementGeneratedEntity class]])
- counter = ((MPElementGeneratedEntity *)element).counter;
+ if ([site isKindOfClass:[MPGeneratedSiteEntity class]])
+ counter = ((MPGeneratedSiteEntity *)site).counter;
// Determine the content to export.
- if (!(type & MPElementFeatureDevicePrivate)) {
+ if (!(type & MPSiteFeatureDevicePrivate)) {
if (revealPasswords)
- content = [element.algorithm resolveContentForElement:element usingKey:self.key];
- else if (type & MPElementFeatureExportContent)
- content = [element.algorithm exportContentForElement:element usingKey:self.key];
+ content = [site.algorithm resolvePasswordForSite:site usingKey:self.key];
+ else if (type & MPSiteFeatureExportContent)
+ content = [site.algorithm exportPasswordForSite:site usingKey:self.key];
}
[export appendFormat:@"%@ %8ld %8s %25s\t%25s\t%@\n",
diff --git a/MasterPassword/ObjC/MPElementEntity.h b/MasterPassword/ObjC/MPElementEntity.h
deleted file mode 100644
index 510d14f6..00000000
--- a/MasterPassword/ObjC/MPElementEntity.h
+++ /dev/null
@@ -1,26 +0,0 @@
-//
-// MPElementEntity.h
-// MasterPassword-iOS
-//
-// Created by Maarten Billemont on 2013-01-29.
-// Copyright (c) 2013 Lyndir. All rights reserved.
-//
-
-#import
-#import
-#import "MPFixable.h"
-
-@class MPUserEntity;
-
-@interface MPElementEntity : NSManagedObject
-
-@property(nonatomic, retain) NSDate *lastUsed;
-@property(nonatomic, retain) NSString *loginName;
-@property(nonatomic, retain) NSString *name;
-@property(nonatomic, retain) NSNumber *requiresExplicitMigration_;
-@property(nonatomic, retain) NSNumber *type_;
-@property(nonatomic, retain) NSNumber *uses_;
-@property(nonatomic, retain) NSNumber *version_;
-@property(nonatomic, retain) MPUserEntity *user;
-
-@end
diff --git a/MasterPassword/ObjC/MPElementEntity.m b/MasterPassword/ObjC/MPElementEntity.m
deleted file mode 100644
index d90110a4..00000000
--- a/MasterPassword/ObjC/MPElementEntity.m
+++ /dev/null
@@ -1,27 +0,0 @@
-//
-// MPElementEntity.m
-// MasterPassword-iOS
-//
-// Created by Maarten Billemont on 2013-01-29.
-// Copyright (c) 2013 Lyndir. All rights reserved.
-//
-
-#import "MPElementEntity.h"
-
-@implementation MPElementEntity
-
-@dynamic lastUsed;
-@dynamic loginName;
-@dynamic name;
-@dynamic requiresExplicitMigration_;
-@dynamic type_;
-@dynamic uses_;
-@dynamic version_;
-@dynamic user;
-
-- (MPFixableResult)findAndFixInconsistenciesInContext:(NSManagedObjectContext *)context {
-
- return MPFixableResultNoProblems;
-}
-
-@end
diff --git a/MasterPassword/ObjC/MPElementGeneratedEntity.h b/MasterPassword/ObjC/MPElementGeneratedEntity.h
deleted file mode 100644
index c6e5b05d..00000000
--- a/MasterPassword/ObjC/MPElementGeneratedEntity.h
+++ /dev/null
@@ -1,17 +0,0 @@
-//
-// MPElementGeneratedEntity.h
-// MasterPassword-iOS
-//
-// Created by Maarten Billemont on 2013-01-29.
-// Copyright (c) 2013 Lyndir. All rights reserved.
-//
-
-#import
-#import
-#import "MPElementEntity.h"
-
-@interface MPElementGeneratedEntity : MPElementEntity
-
-@property(nonatomic, retain) NSNumber *counter_;
-
-@end
diff --git a/MasterPassword/ObjC/MPElementGeneratedEntity.m b/MasterPassword/ObjC/MPElementGeneratedEntity.m
deleted file mode 100644
index 6914a180..00000000
--- a/MasterPassword/ObjC/MPElementGeneratedEntity.m
+++ /dev/null
@@ -1,55 +0,0 @@
-//
-// MPElementGeneratedEntity.m
-// MasterPassword-iOS
-//
-// Created by Maarten Billemont on 2013-01-29.
-// Copyright (c) 2013 Lyndir. All rights reserved.
-//
-
-#import "MPElementGeneratedEntity.h"
-#import "MPAppDelegate_Shared.h"
-
-@implementation MPElementGeneratedEntity
-
-@dynamic counter_;
-
-- (MPFixableResult)findAndFixInconsistenciesInContext:(NSManagedObjectContext *)context {
-
- MPFixableResult result = [super findAndFixInconsistenciesInContext:context];
-
- if (!self.type || self.type == (MPElementType)NSNotFound || ![[self.algorithm allTypes] containsObject:self.type_])
- // Invalid self.type
- result = MPApplyFix( result, ^MPFixableResult {
- wrn( @"Invalid type for: %@ of %@, type: %ld. Will use %ld instead.",
- self.name, self.user.name, (long)self.type, (long)self.user.defaultType );
- self.type = self.user.defaultType;
- return MPFixableResultProblemsFixed;
- } );
- if (!self.type || self.type == (MPElementType)NSNotFound || ![[self.algorithm allTypes] containsObject:self.type_])
- // Invalid self.user.defaultType
- result = MPApplyFix( result, ^MPFixableResult {
- wrn( @"Invalid type for: %@ of %@, type: %ld. Will use %ld instead.",
- self.name, self.user.name, (long)self.type, (long)MPElementTypeGeneratedLong );
- self.type = MPElementTypeGeneratedLong;
- return MPFixableResultProblemsFixed;
- } );
- if (![self isKindOfClass:[self.algorithm classOfType:self.type]])
- // Mismatch between self.type and self.class
- result = MPApplyFix( result, ^MPFixableResult {
- for (MPElementType newType = self.type; self.type != (newType = [self.algorithm nextType:newType]);)
- if ([self isKindOfClass:[self.algorithm classOfType:newType]]) {
- wrn( @"Mismatching type for: %@ of %@, type: %lu, class: %@. Will use %ld instead.",
- self.name, self.user.name, (long)self.type, self.class, (long)newType );
- self.type = newType;
- return MPFixableResultProblemsFixed;
- }
-
- err( @"Mismatching type for: %@ of %@, type: %lu, class: %@. Couldn't find a type to fix problem with.",
- self.name, self.user.name, (long)self.type, self.class );
- return MPFixableResultProblemsNotFixed;
- } );
-
- return result;
-}
-
-@end
diff --git a/MasterPassword/ObjC/MPElementStoredEntity.h b/MasterPassword/ObjC/MPElementStoredEntity.h
deleted file mode 100644
index 0d0a4600..00000000
--- a/MasterPassword/ObjC/MPElementStoredEntity.h
+++ /dev/null
@@ -1,17 +0,0 @@
-//
-// MPElementStoredEntity.h
-// MasterPassword-iOS
-//
-// Created by Maarten Billemont on 2013-01-29.
-// Copyright (c) 2013 Lyndir. All rights reserved.
-//
-
-#import
-#import
-#import "MPElementEntity.h"
-
-@interface MPElementStoredEntity : MPElementEntity
-
-@property(nonatomic, retain) NSData *contentObject;
-
-@end
diff --git a/MasterPassword/ObjC/MPElementStoredEntity.m b/MasterPassword/ObjC/MPElementStoredEntity.m
deleted file mode 100644
index af6ecd6c..00000000
--- a/MasterPassword/ObjC/MPElementStoredEntity.m
+++ /dev/null
@@ -1,37 +0,0 @@
-//
-// MPElementStoredEntity.m
-// MasterPassword-iOS
-//
-// Created by Maarten Billemont on 2013-01-29.
-// Copyright (c) 2013 Lyndir. All rights reserved.
-//
-
-#import "MPElementStoredEntity.h"
-#import "MPEntities.h"
-#import "MPAppDelegate_Shared.h"
-
-@implementation MPElementStoredEntity
-
-@dynamic contentObject;
-
-- (MPFixableResult)findAndFixInconsistenciesInContext:(NSManagedObjectContext *)context {
-
- MPFixableResult result = [super findAndFixInconsistenciesInContext:context];
-
- if (self.contentObject && ![self.contentObject isKindOfClass:[NSData class]])
- result = MPApplyFix( result, ^MPFixableResult {
- MPKey *key = [MPAppDelegate_Shared get].key;
- if (key && [[MPAppDelegate_Shared get] activeUserInContext:context] == self.user) {
- wrn( @"Content object not encrypted for: %@ of %@. Will re-encrypt.", self.name, self.user.name );
- [self.algorithm saveContent:[self.contentObject description] toElement:self usingKey:key];
- return MPFixableResultProblemsFixed;
- }
-
- err( @"Content object not encrypted for: %@ of %@. Couldn't fix, please sign in.", self.name, self.user.name );
- return MPFixableResultProblemsNotFixed;
- } );
-
- return result;
-}
-
-@end
diff --git a/MasterPassword/ObjC/MPEntities.h b/MasterPassword/ObjC/MPEntities.h
index dc6d1351..0d1930f3 100644
--- a/MasterPassword/ObjC/MPEntities.h
+++ b/MasterPassword/ObjC/MPEntities.h
@@ -1,5 +1,5 @@
//
-// MPElementEntities.h
+// MPEntities.h
// MasterPassword-iOS
//
// Created by Maarten Billemont on 31/05/12.
@@ -7,11 +7,12 @@
//
#import
-#import "MPElementEntity.h"
-#import "MPElementStoredEntity.h"
-#import "MPElementGeneratedEntity.h"
+#import "MPSiteEntity.h"
+#import "MPStoredSiteEntity.h"
+#import "MPGeneratedSiteEntity.h"
#import "MPUserEntity.h"
#import "MPAlgorithm.h"
+#import "MPFixable.h"
#define MPAvatarCount 19
@@ -21,9 +22,10 @@
@end
-@interface MPElementEntity(MP)
+@interface MPSiteEntity(MP)
-@property(assign) MPElementType type;
+@property(assign) BOOL loginGenerated;
+@property(assign) MPSiteType type;
@property(readonly) NSString *typeName;
@property(readonly) NSString *typeShortName;
@property(readonly) NSString *typeClassName;
@@ -34,13 +36,15 @@
@property(readonly) id algorithm;
- (NSUInteger)use;
-- (BOOL)migrateExplicitly:(BOOL)explicit;
-- (NSString *)resolveContentUsingKey:(MPKey *)key;
-- (void)resolveContentUsingKey:(MPKey *)key result:(void (^)(NSString *))result;
+- (BOOL)tryMigrateExplicitly:(BOOL)explicit;
+- (NSString *)resolveLoginUsingKey:(MPKey *)key;
+- (NSString *)resolvePasswordUsingKey:(MPKey *)key;
+- (void)resolveLoginUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result;
+- (void)resolvePasswordUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result;
@end
-@interface MPElementGeneratedEntity(MP)
+@interface MPGeneratedSiteEntity(MP)
@property(assign) NSUInteger counter;
@@ -50,7 +54,7 @@
@property(assign) NSUInteger avatar;
@property(assign) BOOL saveKey;
-@property(assign) MPElementType defaultType;
+@property(assign) MPSiteType defaultType;
@property(readonly) NSString *userID;
+ (NSString *)idFor:(NSString *)userName;
diff --git a/MasterPassword/ObjC/MPEntities.m b/MasterPassword/ObjC/MPEntities.m
index 33ec96e4..1c6a6c37 100644
--- a/MasterPassword/ObjC/MPEntities.m
+++ b/MasterPassword/ObjC/MPEntities.m
@@ -1,5 +1,5 @@
//
-// MPElementEntities.m
+// MPEntities.m
// MasterPassword-iOS
//
// Created by Maarten Billemont on 31/05/12.
@@ -8,7 +8,6 @@
#import "MPEntities.h"
#import "MPAppDelegate_Shared.h"
-#import "MPAppDelegate_Store.h"
@implementation NSManagedObjectContext(MP)
@@ -34,14 +33,29 @@
@end
-@implementation MPElementEntity(MP)
+@implementation MPSiteEntity(MP)
-- (MPElementType)type {
+- (MPFixableResult)findAndFixInconsistenciesInContext:(NSManagedObjectContext *)context {
- return (MPElementType)[self.type_ unsignedIntegerValue];
+ return MPFixableResultNoProblems;
}
-- (void)setType:(MPElementType)aType {
+- (MPSiteType)type {
+
+ return (MPSiteType)[self.type_ unsignedIntegerValue];
+}
+
+- (void)setLoginGenerated:(BOOL)aLoginGenerated {
+
+ self.loginGenerated_ = @(aLoginGenerated);
+}
+
+- (BOOL)loginGenerated {
+
+ return [self.loginGenerated_ boolValue];
+}
+
+- (void)setType:(MPSiteType)aType {
self.type_ = @(aType);
}
@@ -120,34 +134,85 @@
self.loginName, self.requiresExplicitMigration );
}
-- (BOOL)migrateExplicitly:(BOOL)explicit {
+- (BOOL)tryMigrateExplicitly:(BOOL)explicit {
- while (self.version < MPAlgorithmDefaultVersion)
- if ([MPAlgorithmForVersion( self.version + 1 ) migrateElement:self explicit:explicit])
- inf( @"%@ migration to version: %ld succeeded for element: %@",
- explicit? @"Explicit": @"Automatic", (long)self.version + 1, self );
- else {
- wrn( @"%@ migration to version: %ld failed for element: %@",
- explicit? @"Explicit": @"Automatic", (long)self.version + 1, self );
+ while (self.version < MPAlgorithmDefaultVersion) {
+ NSUInteger toVersion = self.version + 1;
+ if (![MPAlgorithmForVersion( toVersion ) tryMigrateSite:self explicit:explicit]) {
+ wrn( @"%@ migration to version: %ld failed for site: %@",
+ explicit? @"Explicit": @"Automatic", (long)toVersion, self );
return NO;
}
+ inf( @"%@ migration to version: %ld succeeded for site: %@",
+ explicit? @"Explicit": @"Automatic", (long)toVersion, self );
+ }
+
return YES;
}
-- (NSString *)resolveContentUsingKey:(MPKey *)key {
+- (NSString *)resolveLoginUsingKey:(MPKey *)key {
- return [self.algorithm resolveContentForElement:self usingKey:key];
+ return [self.algorithm resolveLoginForSite:self usingKey:key];
}
-- (void)resolveContentUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result {
+- (NSString *)resolvePasswordUsingKey:(MPKey *)key {
- [self.algorithm resolveContentForElement:self usingKey:key result:result];
+ return [self.algorithm resolvePasswordForSite:self usingKey:key];
+}
+
+- (void)resolveLoginUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result {
+
+ [self.algorithm resolveLoginForSite:self usingKey:key result:result];
+}
+
+- (void)resolvePasswordUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result {
+
+ [self.algorithm resolvePasswordForSite:self usingKey:key result:result];
}
@end
-@implementation MPElementGeneratedEntity(MP)
+@implementation MPGeneratedSiteEntity(MP)
+
+- (MPFixableResult)findAndFixInconsistenciesInContext:(NSManagedObjectContext *)context {
+
+ MPFixableResult result = [super findAndFixInconsistenciesInContext:context];
+
+ if (!self.type || self.type == (MPSiteType)NSNotFound || ![[self.algorithm allTypes] containsObject:self.type_])
+ // Invalid self.type
+ result = MPApplyFix( result, ^MPFixableResult {
+ wrn( @"Invalid type for: %@ of %@, type: %ld. Will use %ld instead.",
+ self.name, self.user.name, (long)self.type, (long)self.user.defaultType );
+ self.type = self.user.defaultType;
+ return MPFixableResultProblemsFixed;
+ } );
+ if (!self.type || self.type == (MPSiteType)NSNotFound || ![[self.algorithm allTypes] containsObject:self.type_])
+ // Invalid self.user.defaultType
+ result = MPApplyFix( result, ^MPFixableResult {
+ wrn( @"Invalid type for: %@ of %@, type: %ld. Will use %ld instead.",
+ self.name, self.user.name, (long)self.type, (long)MPSiteTypeGeneratedLong );
+ self.type = MPSiteTypeGeneratedLong;
+ return MPFixableResultProblemsFixed;
+ } );
+ if (![self isKindOfClass:[self.algorithm classOfType:self.type]])
+ // Mismatch between self.type and self.class
+ result = MPApplyFix( result, ^MPFixableResult {
+ for (MPSiteType newType = self.type; self.type != (newType = [self.algorithm nextType:newType]);)
+ if ([self isKindOfClass:[self.algorithm classOfType:newType]]) {
+ wrn( @"Mismatching type for: %@ of %@, type: %lu, class: %@. Will use %ld instead.",
+ self.name, self.user.name, (long)self.type, self.class, (long)newType );
+ self.type = newType;
+ return MPFixableResultProblemsFixed;
+ }
+
+ err( @"Mismatching type for: %@ of %@, type: %lu, class: %@. Couldn't find a type to fix problem with.",
+ self.name, self.user.name, (long)self.type, self.class );
+ return MPFixableResultProblemsNotFixed;
+ } );
+
+ return result;
+}
- (NSUInteger)counter {
@@ -161,7 +226,27 @@
@end
-@implementation MPElementStoredEntity(MP)
+@implementation MPStoredSiteEntity(MP)
+
+- (MPFixableResult)findAndFixInconsistenciesInContext:(NSManagedObjectContext *)context {
+
+ MPFixableResult result = [super findAndFixInconsistenciesInContext:context];
+
+ if (self.contentObject && ![self.contentObject isKindOfClass:[NSData class]])
+ result = MPApplyFix( result, ^MPFixableResult {
+ MPKey *key = [MPAppDelegate_Shared get].key;
+ if (key && [[MPAppDelegate_Shared get] activeUserInContext:context] == self.user) {
+ wrn( @"Content object not encrypted for: %@ of %@. Will re-encrypt.", self.name, self.user.name );
+ [self.algorithm savePassword:[self.contentObject description] toSite:self usingKey:key];
+ return MPFixableResultProblemsFixed;
+ }
+
+ err( @"Content object not encrypted for: %@ of %@. Couldn't fix, please sign in.", self.name, self.user.name );
+ return MPFixableResultProblemsNotFixed;
+ } );
+
+ return result;
+}
@end
@@ -187,12 +272,12 @@
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);
}
diff --git a/MasterPassword/ObjC/MPGeneratedSiteEntity.h b/MasterPassword/ObjC/MPGeneratedSiteEntity.h
new file mode 100644
index 00000000..af7760be
--- /dev/null
+++ b/MasterPassword/ObjC/MPGeneratedSiteEntity.h
@@ -0,0 +1,18 @@
+//
+// MPGeneratedSiteEntity.h
+// MasterPassword-Mac
+//
+// Created by Maarten Billemont on 2014-09-21.
+// Copyright (c) 2014 Lyndir. All rights reserved.
+//
+
+#import
+#import
+#import "MPSiteEntity.h"
+
+
+@interface MPGeneratedSiteEntity : MPSiteEntity
+
+@property (nonatomic, retain) NSNumber * counter_;
+
+@end
diff --git a/MasterPassword/ObjC/MPGeneratedSiteEntity.m b/MasterPassword/ObjC/MPGeneratedSiteEntity.m
new file mode 100644
index 00000000..0d96650a
--- /dev/null
+++ b/MasterPassword/ObjC/MPGeneratedSiteEntity.m
@@ -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
diff --git a/MasterPassword/ObjC/MPSiteEntity.h b/MasterPassword/ObjC/MPSiteEntity.h
new file mode 100644
index 00000000..f938e93b
--- /dev/null
+++ b/MasterPassword/ObjC/MPSiteEntity.h
@@ -0,0 +1,41 @@
+//
+// MPSiteEntity.h
+// MasterPassword-Mac
+//
+// Created by Maarten Billemont on 2014-09-21.
+// Copyright (c) 2014 Lyndir. All rights reserved.
+//
+
+#import
+#import
+
+@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
diff --git a/MasterPassword/ObjC/MPSiteEntity.m b/MasterPassword/ObjC/MPSiteEntity.m
new file mode 100644
index 00000000..3c7b8dba
--- /dev/null
+++ b/MasterPassword/ObjC/MPSiteEntity.m
@@ -0,0 +1,28 @@
+//
+// MPSiteEntity.m
+// MasterPassword-Mac
+//
+// Created by Maarten Billemont on 2014-09-21.
+// Copyright (c) 2014 Lyndir. All rights reserved.
+//
+
+#import "MPSiteEntity.h"
+#import "MPSiteQuestionEntity.h"
+#import "MPUserEntity.h"
+
+
+@implementation MPSiteEntity
+
+//@dynamic content;
+@dynamic lastUsed;
+@dynamic loginGenerated_;
+@dynamic loginName;
+@dynamic name;
+@dynamic requiresExplicitMigration_;
+@dynamic type_;
+@dynamic uses_;
+@dynamic version_;
+@dynamic questions;
+@dynamic user;
+
+@end
diff --git a/MasterPassword/ObjC/MPSiteQuestionEntity.h b/MasterPassword/ObjC/MPSiteQuestionEntity.h
new file mode 100644
index 00000000..0a0c132f
--- /dev/null
+++ b/MasterPassword/ObjC/MPSiteQuestionEntity.h
@@ -0,0 +1,18 @@
+//
+// MPSiteQuestionEntity.h
+// MasterPassword-Mac
+//
+// Created by Maarten Billemont on 2014-09-21.
+// Copyright (c) 2014 Lyndir. All rights reserved.
+//
+
+#import
+#import
+
+@class MPSiteEntity;
+
+@interface MPSiteQuestionEntity : NSManagedObject
+
+@property (nonatomic, retain) NSString * keyword;
+
+@end
diff --git a/MasterPassword/ObjC/MPSiteQuestionEntity.m b/MasterPassword/ObjC/MPSiteQuestionEntity.m
new file mode 100644
index 00000000..891cdf3b
--- /dev/null
+++ b/MasterPassword/ObjC/MPSiteQuestionEntity.m
@@ -0,0 +1,17 @@
+//
+// MPSiteQuestionEntity.m
+// MasterPassword-Mac
+//
+// Created by Maarten Billemont on 2014-09-21.
+// Copyright (c) 2014 Lyndir. All rights reserved.
+//
+
+#import "MPSiteQuestionEntity.h"
+#import "MPSiteEntity.h"
+
+
+@implementation MPSiteQuestionEntity
+
+@dynamic keyword;
+
+@end
diff --git a/MasterPassword/ObjC/MPStoredSiteEntity.h b/MasterPassword/ObjC/MPStoredSiteEntity.h
new file mode 100644
index 00000000..20348ff6
--- /dev/null
+++ b/MasterPassword/ObjC/MPStoredSiteEntity.h
@@ -0,0 +1,18 @@
+//
+// MPStoredSiteEntity.h
+// MasterPassword-Mac
+//
+// Created by Maarten Billemont on 2014-09-21.
+// Copyright (c) 2014 Lyndir. All rights reserved.
+//
+
+#import
+#import
+#import "MPSiteEntity.h"
+
+
+@interface MPStoredSiteEntity : MPSiteEntity
+
+@property (nonatomic, retain) id contentObject;
+
+@end
diff --git a/MasterPassword/ObjC/MPStoredSiteEntity.m b/MasterPassword/ObjC/MPStoredSiteEntity.m
new file mode 100644
index 00000000..4ff1497e
--- /dev/null
+++ b/MasterPassword/ObjC/MPStoredSiteEntity.m
@@ -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
diff --git a/MasterPassword/ObjC/MPTypes.h b/MasterPassword/ObjC/MPTypes.h
index 13bbf2e6..2b28a159 100644
--- a/MasterPassword/ObjC/MPTypes.h
+++ b/MasterPassword/ObjC/MPTypes.h
@@ -8,36 +8,41 @@
#import "MPKey.h"
-typedef NS_ENUM(NSUInteger, MPElementContentType) {
- MPElementContentTypePassword,
- MPElementContentTypeNote,
- MPElementContentTypePicture,
-};
-
-typedef NS_ENUM(NSUInteger, MPElementTypeClass) {
+typedef NS_ENUM( NSUInteger, MPSiteTypeClass ) {
/** Generate the password. */
- MPElementTypeClassGenerated = 1 << 4,
+ MPSiteTypeClassGenerated = 1 << 4,
/** Store the password. */
- MPElementTypeClassStored = 1 << 5,
+ MPSiteTypeClassStored = 1 << 5,
};
-typedef NS_ENUM(NSUInteger, MPElementFeature) {
+typedef NS_ENUM( NSUInteger, MPSiteVariant ) {
+ /** Generate the password. */
+ MPSiteVariantPassword,
+ /** Generate the login name. */
+ MPSiteVariantLogin,
+ /** Generate a security answer. */
+ MPSiteVariantAnswer,
+};
+
+typedef NS_ENUM( NSUInteger, MPSiteFeature ) {
/** Export the key-protected content data. */
- MPElementFeatureExportContent = 1 << 10,
+ MPSiteFeatureExportContent = 1 << 10,
/** Never export content. */
- MPElementFeatureDevicePrivate = 1 << 11,
+ MPSiteFeatureDevicePrivate = 1 << 11,
};
-typedef NS_ENUM(NSUInteger, MPElementType) {
- MPElementTypeGeneratedMaximum = 0x0 | MPElementTypeClassGenerated | 0x0,
- MPElementTypeGeneratedLong = 0x1 | MPElementTypeClassGenerated | 0x0,
- MPElementTypeGeneratedMedium = 0x2 | MPElementTypeClassGenerated | 0x0,
- MPElementTypeGeneratedBasic = 0x4 | MPElementTypeClassGenerated | 0x0,
- MPElementTypeGeneratedShort = 0x3 | MPElementTypeClassGenerated | 0x0,
- MPElementTypeGeneratedPIN = 0x5 | MPElementTypeClassGenerated | 0x0,
+typedef NS_ENUM(NSUInteger, MPSiteType) {
+ MPSiteTypeGeneratedMaximum = 0x0 | MPSiteTypeClassGenerated | 0x0,
+ MPSiteTypeGeneratedLong = 0x1 | MPSiteTypeClassGenerated | 0x0,
+ MPSiteTypeGeneratedMedium = 0x2 | MPSiteTypeClassGenerated | 0x0,
+ MPSiteTypeGeneratedBasic = 0x4 | MPSiteTypeClassGenerated | 0x0,
+ MPSiteTypeGeneratedShort = 0x3 | MPSiteTypeClassGenerated | 0x0,
+ MPSiteTypeGeneratedPIN = 0x5 | MPSiteTypeClassGenerated | 0x0,
+ MPSiteTypeGeneratedName = 0xE | MPSiteTypeClassGenerated | 0x0,
+ MPSiteTypeGeneratedPhrase = 0xF | MPSiteTypeClassGenerated | 0x0,
- MPElementTypeStoredPersonal = 0x0 | MPElementTypeClassStored | MPElementFeatureExportContent,
- MPElementTypeStoredDevicePrivate = 0x1 | MPElementTypeClassStored | MPElementFeatureDevicePrivate,
+ MPSiteTypeStoredPersonal = 0x0 | MPSiteTypeClassStored | MPSiteFeatureExportContent,
+ MPSiteTypeStoredDevicePrivate = 0x1 | MPSiteTypeClassStored | MPSiteFeatureDevicePrivate,
};
#define MPErrorDomain @"MPErrorDomain"
@@ -50,7 +55,7 @@ typedef NS_ENUM(NSUInteger, MPElementType) {
#define MPCheckpointEditPassword @"MPCheckpointEditPassword"
#define MPCheckpointEditLoginName @"MPCheckpointEditLoginName"
#define MPCheckpointUseType @"MPCheckpointUseType"
-#define MPCheckpointDeleteElement @"MPCheckpointDeleteElement"
+#define MPCheckpointDeleteSite @"MPCheckpointDeleteSite"
#define MPCheckpointShowGuide @"MPCheckpointShowGuide"
#define MPCheckpointShowSetup @"MPCheckpointShowSetup"
#define MPCheckpointChangeMP @"MPCheckpointChangeMP"
@@ -74,7 +79,7 @@ typedef NS_ENUM(NSUInteger, MPElementType) {
#define MPSignedInNotification @"MPSignedInNotification"
#define MPSignedOutNotification @"MPSignedOutNotification"
#define MPKeyForgottenNotification @"MPKeyForgottenNotification"
-#define MPElementUpdatedNotification @"MPElementUpdatedNotification"
+#define MPSiteUpdatedNotification @"MPSiteUpdatedNotification"
#define MPCheckConfigNotification @"MPCheckConfigNotification"
#define MPSitesImportedNotification @"MPSitesImportedNotification"
#define MPFoundInconsistenciesNotification @"MPFoundInconsistenciesNotification"
diff --git a/MasterPassword/ObjC/MPUserEntity.h b/MasterPassword/ObjC/MPUserEntity.h
index 5b29fa9b..eb615712 100644
--- a/MasterPassword/ObjC/MPUserEntity.h
+++ b/MasterPassword/ObjC/MPUserEntity.h
@@ -1,32 +1,32 @@
//
// MPUserEntity.h
-// MasterPassword-iOS
+// MasterPassword-Mac
//
-// Created by Maarten Billemont on 2013-01-29.
-// Copyright (c) 2013 Lyndir. All rights reserved.
+// Created by Maarten Billemont on 2014-09-21.
+// Copyright (c) 2014 Lyndir. All rights reserved.
//
#import
#import
-@class MPElementEntity;
+@class MPSiteEntity;
@interface MPUserEntity : NSManagedObject
-@property(nonatomic, retain) NSNumber *avatar_;
-@property(nonatomic, retain) NSNumber *defaultType_;
-@property(nonatomic, retain) NSData *keyID;
-@property(nonatomic, retain) NSDate *lastUsed;
-@property(nonatomic, retain) NSString *name;
-@property(nonatomic, retain) NSNumber *saveKey_;
-@property(nonatomic, retain) NSSet *elements;
+@property (nonatomic, retain) NSNumber * avatar_;
+@property (nonatomic, retain) NSNumber * defaultType_;
+@property (nonatomic, retain) NSData * keyID;
+@property (nonatomic, retain) NSDate * lastUsed;
+@property (nonatomic, retain) NSString * name;
+@property (nonatomic, retain) NSNumber * saveKey_;
+@property (nonatomic, retain) NSSet *sites;
@end
-@interface MPUserEntity(CoreDataGeneratedAccessors)
+@interface MPUserEntity (CoreDataGeneratedAccessors)
-- (void)addElementsObject:(MPElementEntity *)value;
-- (void)removeElementsObject:(MPElementEntity *)value;
-- (void)addElements:(NSSet *)values;
-- (void)removeElements:(NSSet *)values;
+- (void)addSitesObject:(MPSiteEntity *)value;
+- (void)removeSitesObject:(MPSiteEntity *)value;
+- (void)addSites:(NSSet *)values;
+- (void)removeSites:(NSSet *)values;
@end
diff --git a/MasterPassword/ObjC/MPUserEntity.m b/MasterPassword/ObjC/MPUserEntity.m
index 3d1f1aa5..da97fdcc 100644
--- a/MasterPassword/ObjC/MPUserEntity.m
+++ b/MasterPassword/ObjC/MPUserEntity.m
@@ -1,12 +1,14 @@
//
// MPUserEntity.m
-// MasterPassword-iOS
+// MasterPassword-Mac
//
-// Created by Maarten Billemont on 2013-01-29.
-// Copyright (c) 2013 Lyndir. All rights reserved.
+// Created by Maarten Billemont on 2014-09-21.
+// Copyright (c) 2014 Lyndir. All rights reserved.
//
#import "MPUserEntity.h"
+#import "MPSiteEntity.h"
+
@implementation MPUserEntity
@@ -16,6 +18,6 @@
@dynamic lastUsed;
@dynamic name;
@dynamic saveKey_;
-@dynamic elements;
+@dynamic sites;
@end
diff --git a/MasterPassword/ObjC/Mac/MPMacAppDelegate.m b/MasterPassword/ObjC/Mac/MPMacAppDelegate.m
index b00a4ca1..5efa2da2 100644
--- a/MasterPassword/ObjC/Mac/MPMacAppDelegate.m
+++ b/MasterPassword/ObjC/Mac/MPMacAppDelegate.m
@@ -229,7 +229,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
NSData *importedSitesData = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url]
returningResponse:&response error:&error];
if (error)
- err( @"While reading imported sites from %@: %@", url, error );
+ err( @"While reading imported sites from %@: %@", url, [error fullDescription] );
if (!importedSitesData)
return;
@@ -346,7 +346,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
[moc saveToStore];
NSError *error = nil;
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:^{
[self updateUsers];
@@ -510,7 +510,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
fetchRequest.sortDescriptors = @[ [NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO] ];
NSArray *users = [mainContext executeFetchRequest:fetchRequest error:&error];
if (!users)
- err( @"Failed to load users: %@", error );
+ err( @"Failed to load users: %@", [error fullDescription] );
if (![users count]) {
NSMenuItem *noUsersItem = [self.usersItem.submenu addItemWithTitle:@"No users" action:NULL keyEquivalent:@""];
diff --git a/MasterPassword/ObjC/Mac/MPPasswordWindowController.h b/MasterPassword/ObjC/Mac/MPPasswordWindowController.h
index 11928b4c..2bc790f6 100644
--- a/MasterPassword/ObjC/Mac/MPPasswordWindowController.h
+++ b/MasterPassword/ObjC/Mac/MPPasswordWindowController.h
@@ -17,26 +17,26 @@
//
#import
-#import "MPElementModel.h"
-#import "MPElementsTableView.h"
+#import "MPSiteModel.h"
+#import "MPSitesTableView.h"
@class MPMacAppDelegate;
@interface MPPasswordWindowController : NSWindowController
-@property(nonatomic) NSMutableArray *elements;
+@property(nonatomic) NSMutableArray *sites;
@property(nonatomic) NSString *masterPassword;
@property(nonatomic) BOOL alternatePressed;
@property(nonatomic) BOOL locked;
@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 NSTextField *inputLabel;
@property(nonatomic, weak) IBOutlet NSTextField *securePasswordField;
@property(nonatomic, weak) IBOutlet NSTextField *revealPasswordField;
@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, strong) IBOutlet NSBox *passwordTypesBox;
diff --git a/MasterPassword/ObjC/Mac/MPPasswordWindowController.m b/MasterPassword/ObjC/Mac/MPPasswordWindowController.m
index 4a41a485..ee0e5453 100644
--- a/MasterPassword/ObjC/Mac/MPPasswordWindowController.m
+++ b/MasterPassword/ObjC/Mac/MPPasswordWindowController.m
@@ -20,7 +20,7 @@
#import "MPPasswordWindowController.h"
#import "MPMacAppDelegate.h"
#import "MPAppDelegate_Store.h"
-#import "MPElementModel.h"
+#import "MPSiteModel.h"
#import "MPAppDelegate_Key.h"
#import "PearlProfiler.h"
@@ -77,7 +77,7 @@
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
[self updateUser];
}];
- [self observeKeyPath:@"elementsController.selection"
+ [self observeKeyPath:@"sitesController.selection"
withBlock:^(id from, id to, NSKeyValueChange cause, id _self) {
[_self updateSelection];
}];
@@ -100,7 +100,7 @@
BOOL alternatePressed = (theEvent.modifierFlags & NSAlternateKeyMask) != 0;
if (alternatePressed != self.alternatePressed) {
self.alternatePressed = alternatePressed;
- [self.selectedElement updateContent];
+ [self.selectedSite updateContent];
if (self.locked) {
NSTextField *passwordField = self.securePasswordField;
@@ -169,9 +169,9 @@
}];
}
-- (IBAction)doSearchElements:(id)sender {
+- (IBAction)doSearchSites:(id)sender {
- [self updateElements];
+ [self updateSites];
}
#pragma mark - NSTextViewDelegate
@@ -186,7 +186,7 @@
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
- return (NSInteger)[self.elements count];
+ return (NSInteger)[self.sites count];
}
#pragma mark - NSTableViewDelegate
@@ -229,9 +229,10 @@
switch (returnCode) {
case NSAlertFirstButtonReturn: {
// "Create" button.
- [[MPMacAppDelegate get] addElementNamed:[self.siteField stringValue] completion:^(MPElementEntity *element, NSManagedObjectContext *context) {
- if (element)
- PearlMainQueue( ^{ [self updateElements]; } );
+ [[MPMacAppDelegate get] addSiteNamed:[self.siteField stringValue] completion:
+ ^(MPSiteEntity *site, NSManagedObjectContext *context) {
+ if (site)
+ PearlMainQueue( ^{ [self updateSites]; } );
}];
break;
}
@@ -243,11 +244,11 @@
switch (returnCode) {
case NSAlertFirstButtonReturn: {
// "Save" button.
- MPElementType type = (MPElementType)[self.passwordTypesMatrix.selectedCell tag];
+ MPSiteType type = (MPSiteType)[self.passwordTypesMatrix.selectedCell tag];
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
- MPElementEntity *entity = [[MPMacAppDelegate get] changeElement:[self.selectedElement entityInContext:context]
- saveInContext:context toType:type];
- if ([entity isKindOfClass:[MPElementStoredEntity class]] && ![(MPElementStoredEntity *)entity contentObject].length)
+ MPSiteEntity *entity = [[MPMacAppDelegate get] changeSite:[self.selectedSite entityInContext:context]
+ saveInContext:context toType:type];
+ if ([entity isKindOfClass:[MPStoredSiteEntity class]] && ![(MPStoredSiteEntity *)entity contentObject].length)
PearlMainQueue( ^{
[self changePassword:nil];
} );
@@ -264,7 +265,7 @@
// "Save" button.
NSString *loginName = [(NSSecureTextField *)alert.accessoryView stringValue];
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
- MPElementEntity *entity = [self.selectedElement entityInContext:context];
+ MPSiteEntity *entity = [self.selectedSite entityInContext:context];
entity.loginName = loginName;
[context saveToStore];
}];
@@ -280,8 +281,8 @@
// "Save" button.
NSString *password = [(NSSecureTextField *)alert.accessoryView stringValue];
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
- MPElementEntity *entity = [self.selectedElement entityInContext:context];
- [entity.algorithm saveContent:password toElement:entity usingKey:[MPMacAppDelegate get].key];
+ MPSiteEntity *entity = [self.selectedSite entityInContext:context];
+ [entity.algorithm savePassword:password toSite:entity usingKey:[MPMacAppDelegate get].key];
[context saveToStore];
}];
break;
@@ -295,7 +296,7 @@
case NSAlertFirstButtonReturn: {
// "Delete" button.
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
- [context deleteObject:[self.selectedElement entityInContext:context]];
+ [context deleteObject:[self.selectedSite entityInContext:context]];
[context saveToStore];
}];
break;
@@ -313,19 +314,19 @@
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
@@ -336,13 +337,13 @@
[[MPMacAppDelegate get] showPopup:sender];
}
-- (IBAction)deleteElement:(id)sender {
+- (IBAction)deleteSite:(id)sender {
NSAlert *alert = [NSAlert new];
[alert addButtonWithTitle:@"Delete"];
[alert addButtonWithTitle:@"Cancel"];
[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
didEndSelector:@selector( alertDidEnd:returnCode:contextInfo: ) contextInfo:MPAlertDeleteSite];
}
@@ -353,9 +354,9 @@
[alert addButtonWithTitle:@"Save"];
[alert addButtonWithTitle:@"Cancel"];
[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 )];
- loginField.stringValue = self.selectedElement.loginName?: @"";
+ loginField.stringValue = self.selectedSite.loginName?: @"";
[loginField selectText:self];
[alert setAccessoryView:loginField];
[alert layout];
@@ -380,14 +381,14 @@
- (IBAction)changePassword:(id)sender {
- if (!self.selectedElement.stored)
+ if (!self.selectedSite.stored)
return;
NSAlert *alert = [NSAlert new];
[alert addButtonWithTitle:@"Save"];
[alert addButtonWithTitle:@"Cancel"];
[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 layout];
[alert beginSheetModalForWindow:self.window modalDelegate:self
@@ -396,19 +397,19 @@
- (IBAction)changeType:(id)sender {
- MPElementModel *element = self.selectedElement;
- NSArray *types = [element.algorithm allTypesStartingWith:MPElementTypeGeneratedPIN];
+ MPSiteModel *site = self.selectedSite;
+ NSArray *types = [site.algorithm allTypesStartingWith:MPSiteTypeGeneratedPIN];
[self.passwordTypesMatrix renewRows:(NSInteger)[types count] columns:1];
for (NSUInteger t = 0; t < [types count]; ++t) {
- MPElementType type = [types[t] unsignedIntegerValue];
- NSString *title = [element.algorithm nameOfType:type];
- if (type & MPElementTypeClassGenerated)
- title = [element.algorithm generateContentNamed:element.siteName ofType:type
- withCounter:element.counter usingKey:[MPMacAppDelegate get].key];
+ MPSiteType type = [types[t] unsignedIntegerValue];
+ NSString *title = [site.algorithm nameOfType:type];
+ if (type & MPSiteTypeClassGenerated)
+ title = [site.algorithm generatePasswordForSiteNamed:site.siteName ofType:type
+ withCounter:site.counter usingKey:[MPMacAppDelegate get].key];
NSButtonCell *cell = [self.passwordTypesMatrix cellAtRow:(NSInteger)t column:0];
cell.tag = type;
- cell.state = type == element.type? NSOnState: NSOffState;
+ cell.state = type == site.type? NSOnState: NSOffState;
cell.title = title;
}
@@ -416,7 +417,7 @@
[alert addButtonWithTitle:@"Save"];
[alert addButtonWithTitle:@"Cancel"];
[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 layout];
[alert beginSheetModalForWindow:self.window modalDelegate:self
@@ -428,11 +429,11 @@
- (BOOL)handleCommand:(SEL)commandSelector {
if (commandSelector == @selector( moveUp: )) {
- [self.elementsController selectPrevious:self];
+ [self.sitesController selectPrevious:self];
return YES;
}
if (commandSelector == @selector( moveDown: )) {
- [self.elementsController selectNext:self];
+ [self.sitesController selectNext:self];
return YES;
}
if (commandSelector == @selector( insertNewline: )) {
@@ -449,19 +450,19 @@
- (void)useSite {
- MPElementModel *selectedElement = [self selectedElement];
- if (selectedElement) {
+ MPSiteModel *selectedSite = [self selectedSite];
+ if (selectedSite) {
// Performing action while content is available. Copy it.
- [self copyContent:selectedElement.content];
+ [self copyContent:selectedSite.content];
[self fadeOut];
NSUserNotification *notification = [NSUserNotification new];
notification.title = @"Password Copied";
- if (selectedElement.loginName.length)
- notification.subtitle = strf( @"%@ at %@", selectedElement.loginName, selectedElement.siteName );
+ if (selectedSite.loginName.length)
+ notification.subtitle = strf( @"%@ at %@", selectedSite.loginName, selectedSite.siteName );
else
- notification.subtitle = selectedElement.siteName;
+ notification.subtitle = selectedSite.siteName;
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
}
else {
@@ -499,22 +500,22 @@
}
}
- [self updateElements];
+ [self updateSites];
}];
}
-- (void)updateElements {
+- (void)updateSites {
if (![MPMacAppDelegate get].key) {
- self.elements = nil;
+ self.sites = nil;
return;
}
- PearlProfiler *profiler = [PearlProfiler profilerForTask:@"updateElements"];
+ PearlProfiler *profiler = [PearlProfiler profilerForTask:@"updateSites"];
NSString *query = [self query];
[profiler finishJob:@"query"];
[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.predicate = [NSPredicate predicateWithFormat:@"(%@ == '' OR name BEGINSWITH[cd] %@) AND user == %@",
query, query, [[MPMacAppDelegate get] activeUserInContext:context]];
@@ -523,17 +524,17 @@
NSError *error = nil;
NSArray *siteResults = [context executeFetchRequest:fetchRequest error:&error];
if (!siteResults) {
- err( @"While fetching elements for completion: %@", error );
+ err( @"While fetching sites for completion: %@", [error fullDescription] );
return;
}
[profiler finishJob:@"do fetch"];
- NSMutableArray *newElements = [NSMutableArray arrayWithCapacity:[siteResults count]];
- for (MPElementEntity *element in siteResults)
- [newElements addObject:[[MPElementModel alloc] initWithEntity:element]];
+ NSMutableArray *newSites = [NSMutableArray arrayWithCapacity:[siteResults count]];
+ for (MPSiteEntity *site in siteResults)
+ [newSites addObject:[[MPSiteModel alloc] initWithEntity:site]];
[profiler finishJob:@"make models"];
- self.elements = newElements;
- [profiler finishJob:@"update elements"];
+ self.sites = newSites;
+ [profiler finishJob:@"update sites"];
}];
[profiler finishJob:@"done"];
}
@@ -545,7 +546,7 @@
return;
}
- NSString *siteName = self.selectedElement.siteName;
+ NSString *siteName = self.selectedSite.siteName;
if (!siteName)
return;
@@ -558,14 +559,14 @@
NSMakeRange( siteNameQueryRange.length, siteName.length - siteNameQueryRange.length );
}
- [self.siteTable scrollRowToVisible:(NSInteger)self.elementsController.selectionIndex];
+ [self.siteTable scrollRowToVisible:(NSInteger)self.sitesController.selectionIndex];
[self updateGradient];
}
- (void)updateGradient {
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 gradientOpacity = selectedOffset / siteScrollView.bounds.size.height;
self.siteGradient.colors = @[
@@ -596,7 +597,7 @@
}
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
- [[self.selectedElement entityInContext:moc] use];
+ [[self.selectedSite entityInContext:moc] use];
[moc saveToStore];
}];
}
diff --git a/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib b/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib
index c4448d3e..47793877 100644
--- a/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib
+++ b/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib
@@ -8,7 +8,7 @@
-
+
@@ -144,7 +144,7 @@
-
+
@@ -262,7 +262,7 @@
-
+
@@ -371,7 +371,7 @@
-
+
NSNegateBoolean
@@ -869,9 +869,9 @@
-
+
-
+
diff --git a/MasterPassword/ObjC/Mac/MPElementModel.h b/MasterPassword/ObjC/Mac/MPSiteModel.h
similarity index 79%
rename from MasterPassword/ObjC/Mac/MPElementModel.h
rename to MasterPassword/ObjC/Mac/MPSiteModel.h
index 6aaaccde..2a83576e 100644
--- a/MasterPassword/ObjC/Mac/MPElementModel.h
+++ b/MasterPassword/ObjC/Mac/MPSiteModel.h
@@ -9,20 +9,20 @@
*/
//
-// MPElementModel.h
-// MPElementModel
+// MPSiteModel.h
+// MPSiteModel
//
// Created by lhunath on 2/11/2014.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
#import
-@class MPElementEntity;
+@class MPSiteEntity;
-@interface MPElementModel : NSObject
+@interface MPSiteModel : NSObject
@property (nonatomic) NSString *siteName;
-@property (nonatomic) MPElementType type;
+@property (nonatomic) MPSiteType type;
@property (nonatomic) NSString *typeName;
@property (nonatomic) NSString *content;
@property (nonatomic) NSString *contentDisplay;
@@ -34,8 +34,8 @@
@property (nonatomic) BOOL generated;
@property (nonatomic) BOOL stored;
-- (id)initWithEntity:(MPElementEntity *)entity;
-- (MPElementEntity *)entityInContext:(NSManagedObjectContext *)moc;
+- (id)initWithEntity:(MPSiteEntity *)entity;
+- (MPSiteEntity *)entityInContext:(NSManagedObjectContext *)moc;
- (void)updateContent;
@end
diff --git a/MasterPassword/ObjC/Mac/MPElementModel.m b/MasterPassword/ObjC/Mac/MPSiteModel.m
similarity index 68%
rename from MasterPassword/ObjC/Mac/MPElementModel.m
rename to MasterPassword/ObjC/Mac/MPSiteModel.m
index bfb2864a..a6299611 100644
--- a/MasterPassword/ObjC/Mac/MPElementModel.m
+++ b/MasterPassword/ObjC/Mac/MPSiteModel.m
@@ -9,26 +9,26 @@
*/
//
-// MPElementModel.h
-// MPElementModel
+// MPSiteModel.h
+// MPSiteModel
//
// Created by lhunath on 2/11/2014.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
-#import "MPElementModel.h"
-#import "MPElementEntity.h"
+#import "MPSiteModel.h"
+#import "MPSiteEntity.h"
#import "MPEntities.h"
#import "MPAppDelegate_Shared.h"
#import "MPAppDelegate_Store.h"
#import "MPMacAppDelegate.h"
-@implementation MPElementModel {
+@implementation MPSiteModel {
NSManagedObjectID *_entityOID;
BOOL _initialized;
}
-- (id)initWithEntity:(MPElementEntity *)entity {
+- (id)initWithEntity:(MPSiteEntity *)entity {
if (!(self = [super init]))
return nil;
@@ -39,7 +39,7 @@
return self;
}
-- (void)setEntity:(MPElementEntity *)entity {
+- (void)setEntity:(MPSiteEntity *)entity {
if ([_entityOID isEqual:entity.objectID])
return;
@@ -52,21 +52,21 @@
self.type = entity.type;
self.typeName = entity.typeName;
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.
[self updateContent:entity];
}
-- (MPElementEntity *)entityInContext:(NSManagedObjectContext *)moc {
+- (MPSiteEntity *)entityInContext:(NSManagedObjectContext *)moc {
if (!_entityOID)
return nil;
NSError *error;
- MPElementEntity *entity = (MPElementEntity *)[moc existingObjectWithID:_entityOID error:&error];
+ MPSiteEntity *entity = (MPSiteEntity *)[moc existingObjectWithID:_entityOID error:&error];
if (!entity)
- err( @"Couldn't retrieve active element: %@", error );
+ err( @"Couldn't retrieve active site: %@", [error fullDescription] );
return entity;
}
@@ -82,9 +82,9 @@
return;
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
- MPElementEntity *entity = [self entityInContext:context];
- if ([entity isKindOfClass:[MPElementGeneratedEntity class]]) {
- ((MPElementGeneratedEntity *)entity).counter = counter;
+ MPSiteEntity *entity = [self entityInContext:context];
+ if ([entity isKindOfClass:[MPGeneratedSiteEntity class]]) {
+ ((MPGeneratedSiteEntity *)entity).counter = counter;
[context saveToStore];
[self updateContent:entity];
@@ -94,22 +94,22 @@
- (BOOL)generated {
- return self.type & MPElementTypeClassGenerated;
+ return self.type & MPSiteTypeClassGenerated;
}
- (BOOL)stored {
- return self.type & MPElementTypeClassStored;
+ return self.type & MPSiteTypeClassStored;
}
- (void)updateContent {
[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 dispatch_once_t once = 0;
@@ -117,7 +117,7 @@
re_anyChar = [NSRegularExpression regularExpressionWithPattern:@"." options:0 error:nil];
} );
- [entity resolveContentUsingKey:[MPAppDelegate_Shared get].key result:^(NSString *result) {
+ [entity resolvePasswordUsingKey:[MPAppDelegate_Shared get].key result:^(NSString *result) {
NSString *displayResult = result;
if ([[MPConfig get].hidePasswords boolValue] && !([NSEvent modifierFlags] & NSAlternateKeyMask))
displayResult = [displayResult stringByReplacingMatchesOfExpression:re_anyChar withTemplate:@"●"];
diff --git a/MasterPassword/ObjC/Mac/MPElementsTableView.h b/MasterPassword/ObjC/Mac/MPSitesTableView.h
similarity index 86%
rename from MasterPassword/ObjC/Mac/MPElementsTableView.h
rename to MasterPassword/ObjC/Mac/MPSitesTableView.h
index aaa864f0..c5c8df43 100644
--- a/MasterPassword/ObjC/Mac/MPElementsTableView.h
+++ b/MasterPassword/ObjC/Mac/MPSitesTableView.h
@@ -9,8 +9,8 @@
*/
//
-// MPElementsTableView.h
-// MPElementsTableView
+// MPSitesTableView.h
+// MPSitesTableView
//
// Created by lhunath on 2014-06-30.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
@@ -20,7 +20,7 @@
@class MPPasswordWindowController;
-@interface MPElementsTableView : NSTableView
+@interface MPSitesTableView : NSTableView
@property(nonatomic, weak) MPPasswordWindowController *controller;
diff --git a/MasterPassword/ObjC/Mac/MPElementsTableView.m b/MasterPassword/ObjC/Mac/MPSitesTableView.m
similarity index 85%
rename from MasterPassword/ObjC/Mac/MPElementsTableView.m
rename to MasterPassword/ObjC/Mac/MPSitesTableView.m
index 9809435a..ef3215ab 100644
--- a/MasterPassword/ObjC/Mac/MPElementsTableView.m
+++ b/MasterPassword/ObjC/Mac/MPSitesTableView.m
@@ -9,17 +9,17 @@
*/
//
-// MPElementsTableView.h
-// MPElementsTableView
+// MPSitesTableView.h
+// MPSitesTableView
//
// Created by lhunath on 2014-06-30.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
-#import "MPElementsTableView.h"
+#import "MPSitesTableView.h"
#import "MPPasswordWindowController.h"
-@implementation MPElementsTableView
+@implementation MPSitesTableView
- (void)doCommandBySelector:(SEL)aSelector {
diff --git a/MasterPassword/ObjC/Mac/MasterPassword-Info.plist b/MasterPassword/ObjC/Mac/MasterPassword-Info.plist
index e6df7def..0acabfa0 100644
--- a/MasterPassword/ObjC/Mac/MasterPassword-Info.plist
+++ b/MasterPassword/ObjC/Mac/MasterPassword-Info.plist
@@ -19,6 +19,10 @@
CFBundleTypeIconFiles
+ CFBundleTypeMIMETypes
+
+ text/plain
+
CFBundleTypeName
Master Password sites
CFBundleTypeRole
@@ -27,8 +31,10 @@
Owner
LSItemContentTypes
- com.lyndir.lhunath.MasterPassword.sites
+ com.lyndir.masterpassword.sites
+ LSTypeIsPackage
+ 0
CFBundleExecutable
@@ -47,19 +53,6 @@
[auto]
CFBundleSignature
????
- CFBundleURLTypes
-
-
- CFBundleTypeRole
- Editor
- CFBundleURLName
- com.lyndir.lhunath.MasterPassword
- CFBundleURLSchemes
-
- com.lyndir.lhunath.MasterPassword
-
-
-
CFBundleVersion
[auto]
LSApplicationCategoryType
@@ -77,12 +70,16 @@
UTExportedTypeDeclarations
+ UTTypeConformsTo
+
+ public.utf8-plain-text
+
UTTypeDescription
Master Password sites
UTTypeIconFile
UTTypeIdentifier
- com.lyndir.lhunath.MasterPassword.sites
+ com.lyndir.masterpassword.sites
UTTypeSize320IconFile
UTTypeSize64IconFile
diff --git a/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.pbxproj b/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.pbxproj
index 934bdbdc..cfb74216 100644
--- a/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.pbxproj
+++ b/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.pbxproj
@@ -9,12 +9,12 @@
/* Begin PBXBuildFile section */
93D390C676DF52DA7E459F19 /* MPPasswordWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D9D0061FF1159998F06 /* MPPasswordWindow.m */; };
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 */; };
93D3970BCF85F7902E611168 /* PearlProfiler.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39DB3A8ADED08C39A6228 /* PearlProfiler.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 */; };
- 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 */; };
93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */; };
93D39F833DEC1C89B2F795AC /* MPPasswordWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39A57A7823DE98A0FF83C /* MPPasswordWindowController.m */; };
@@ -30,6 +30,7 @@
DA2508F119511D3600AC23F1 /* MPPasswordWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA2508F019511D3600AC23F1 /* MPPasswordWindowController.xib */; };
DA2508F719513C1400AC23F1 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA16B343170661EE000A0EAB /* Cocoa.framework */; };
DA250925195148E200AC23F1 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAEBC45214F6364500987BF6 /* QuartzCore.framework */; };
+ DA29992C19C6A89900AF7DF1 /* MasterPassword.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DA29992619C6A89900AF7DF1 /* MasterPassword.xcdatamodeld */; };
DA2CA4ED18D323D3007798F8 /* NSError+PearlFullDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = DA2CA4E718D323D3007798F8 /* NSError+PearlFullDescription.m */; };
DA2CA4EE18D323D3007798F8 /* NSError+PearlFullDescription.h in Headers */ = {isa = PBXBuildFile; fileRef = DA2CA4E818D323D3007798F8 /* NSError+PearlFullDescription.h */; };
DA2CA4EF18D323D3007798F8 /* NSArray+Pearl.m in Sources */ = {isa = PBXBuildFile; fileRef = DA2CA4E918D323D3007798F8 /* NSArray+Pearl.m */; };
@@ -42,12 +43,18 @@
DA30E9D215722EE500A68B4C /* Pearl-Crypto.m in Sources */ = {isa = PBXBuildFile; fileRef = DA30E9D115722EE500A68B4C /* Pearl-Crypto.m */; };
DA30E9D715723E6900A68B4C /* PearlLazy.h in Headers */ = {isa = PBXBuildFile; fileRef = DA30E9D515723E6900A68B4C /* PearlLazy.h */; };
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 */; };
DA3509FF15F101A500C14A8E /* PearlQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = DA3509FD15F101A500C14A8E /* PearlQueue.m */; };
DA3B844F190FC60900246EEA /* Crashlytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA3B844A190FC5A900246EEA /* Crashlytics.framework */; };
DA3B8452190FC86F00246EEA /* NSManagedObject+Pearl.m in Sources */ = {isa = PBXBuildFile; fileRef = DA3B8450190FC86F00246EEA /* NSManagedObject+Pearl.m */; };
DA3B8453190FC86F00246EEA /* NSManagedObject+Pearl.h in Headers */ = {isa = PBXBuildFile; fileRef = DA3B8451190FC86F00246EEA /* NSManagedObject+Pearl.h */; };
DA3B8456190FC89700246EEA /* MPFixable.m in Sources */ = {isa = PBXBuildFile; fileRef = DA3B8454190FC89700246EEA /* MPFixable.m */; };
+ DA3BCFCD19BD09E0006B2681 /* SourceCodePro-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = DA3BCFCC19BD09E0006B2681 /* SourceCodePro-Regular.otf */; };
DA4425CC1557BED40052177D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
DA4DA1D91564471A00F6F596 /* libjrswizzle.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC6326C148680650075AEA5 /* libjrswizzle.a */; };
DA5E5C9417248AA1003798D8 /* libscryptenc-osx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5E5C8717248AA1003798D8 /* libscryptenc-osx.a */; };
@@ -58,19 +65,14 @@
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 */; };
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 */; };
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 */; };
DA5E5D041724A667003798D8 /* MPMacConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CB61724A667003798D8 /* MPMacConfig.m */; };
DA5E5D081724A667003798D8 /* MasterPassword.entitlements in Resources */ = {isa = PBXBuildFile; fileRef = DA5E5CBF1724A667003798D8 /* MasterPassword.entitlements */; };
DA5E5D0A1724A667003798D8 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DA5E5CC21724A667003798D8 /* InfoPlist.strings */; };
DA5E5D0B1724A667003798D8 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA5E5CC41724A667003798D8 /* MainMenu.xib */; };
DA5E5D0C1724A667003798D8 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CC61724A667003798D8 /* main.m */; };
- DA5E5D0D1724A667003798D8 /* MasterPassword.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CC71724A667003798D8 /* MasterPassword.xcdatamodeld */; };
DA60717C195D040500CA98B5 /* icon_gear.png in Resources */ = {isa = PBXBuildFile; fileRef = DA607092195D03E200CA98B5 /* icon_gear.png */; };
DA60717D195D040500CA98B5 /* icon_gear@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA607093195D03E200CA98B5 /* icon_gear@2x.png */; };
DA6558A419A99609009A0BEB /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DA6558A319A99609009A0BEB /* Images.xcassets */; };
@@ -228,21 +230,21 @@
/* Begin PBXFileReference section */
93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Indexing.m"; sourceTree = ""; };
- 93D39240B5143E01F0B75E96 /* MPElementModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementModel.h; sourceTree = ""; };
+ 93D39240B5143E01F0B75E96 /* MPSiteModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSiteModel.h; sourceTree = ""; };
93D392C3918763B3B72CF366 /* MPPasswordWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordWindowController.h; sourceTree = ""; };
93D39368EF3CBFEF2AFCA15A /* MPInitialWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPInitialWindowController.h; sourceTree = ""; };
93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Indexing.h"; sourceTree = ""; };
- 93D39423D7BF4FD31FE6D27C /* MPElementsTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementsTableView.m; sourceTree = ""; };
+ 93D39423D7BF4FD31FE6D27C /* MPSitesTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSitesTableView.m; sourceTree = ""; };
93D394EEFF5BF555A55AF361 /* PearlProfiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PearlProfiler.h; path = ../../../External/Pearl/Pearl/PearlProfiler.h; sourceTree = ""; };
93D396D04E57792A54D437AC /* NSArray+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Indexing.h"; sourceTree = ""; };
93D3977484534E99F9BA579D /* MPPasswordWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordWindow.h; sourceTree = ""; };
93D39A57A7823DE98A0FF83C /* MPPasswordWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordWindowController.m; sourceTree = ""; };
93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Indexing.m"; sourceTree = ""; };
- 93D39AC6360DDC16AEAA4119 /* MPElementsTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementsTableView.h; sourceTree = ""; };
+ 93D39AC6360DDC16AEAA4119 /* MPSitesTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSitesTableView.h; sourceTree = ""; };
93D39D3CB30874147D9A9E1B /* MPInitialWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPInitialWindowController.m; sourceTree = ""; };
93D39D9D0061FF1159998F06 /* MPPasswordWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordWindow.m; sourceTree = ""; };
93D39DB3A8ADED08C39A6228 /* PearlProfiler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PearlProfiler.m; path = ../../../External/Pearl/Pearl/PearlProfiler.m; sourceTree = ""; };
- 93D39E73BF5CBF8E5B005CD3 /* MPElementModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementModel.m; sourceTree = ""; };
+ 93D39E73BF5CBF8E5B005CD3 /* MPSiteModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSiteModel.m; sourceTree = ""; };
DA0933C91747A56A00DE1CEF /* MPInitialWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MPInitialWindow.xib; sourceTree = ""; };
DA0933CB1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "shot-laptop-leaning-iphone.png"; sourceTree = ""; };
DA0933CF1747B91B00DE1CEF /* appstore.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = appstore.png; sourceTree = ""; };
@@ -256,6 +258,11 @@
DA25090719513C1400AC23F1 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
DA2509261951B86C00AC23F1 /* small-screen.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "small-screen.png"; sourceTree = ""; };
DA2509271951B86C00AC23F1 /* screen.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = screen.png; sourceTree = ""; };
+ DA29992719C6A89900AF7DF1 /* MasterPassword 1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 1.xcdatamodel"; sourceTree = ""; };
+ DA29992819C6A89900AF7DF1 /* MasterPassword 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 2.xcdatamodel"; sourceTree = ""; };
+ DA29992919C6A89900AF7DF1 /* MasterPassword 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 3.xcdatamodel"; sourceTree = ""; };
+ DA29992A19C6A89900AF7DF1 /* MasterPassword 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 4.xcdatamodel"; sourceTree = ""; };
+ DA29992B19C6A89900AF7DF1 /* MasterPassword 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 5.xcdatamodel"; sourceTree = ""; };
DA2CA4E718D323D3007798F8 /* NSError+PearlFullDescription.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+PearlFullDescription.m"; sourceTree = ""; };
DA2CA4E818D323D3007798F8 /* NSError+PearlFullDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+PearlFullDescription.h"; sourceTree = ""; };
DA2CA4E918D323D3007798F8 /* NSArray+Pearl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Pearl.m"; sourceTree = ""; };
@@ -268,6 +275,17 @@
DA30E9D115722EE500A68B4C /* Pearl-Crypto.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "Pearl-Crypto.m"; sourceTree = ""; };
DA30E9D515723E6900A68B4C /* PearlLazy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlLazy.h; sourceTree = ""; };
DA30E9D615723E6900A68B4C /* PearlLazy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlLazy.m; sourceTree = ""; };
+ DA32CFD719CF1C70004F3F0E /* MPGeneratedSiteEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPGeneratedSiteEntity.h; sourceTree = ""; };
+ DA32CFD819CF1C70004F3F0E /* MPGeneratedSiteEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPGeneratedSiteEntity.m; sourceTree = ""; };
+ DA32CFDA19CF1C70004F3F0E /* MPStoredSiteEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPStoredSiteEntity.h; sourceTree = ""; };
+ DA32CFDB19CF1C70004F3F0E /* MPStoredSiteEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPStoredSiteEntity.m; sourceTree = ""; };
+ DA32CFDD19CF1C70004F3F0E /* MPSiteEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSiteEntity.h; sourceTree = ""; };
+ DA32CFDE19CF1C70004F3F0E /* MPSiteEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSiteEntity.m; sourceTree = ""; };
+ DA32CFE019CF1C71004F3F0E /* MPSiteQuestionEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSiteQuestionEntity.h; sourceTree = ""; };
+ DA32CFE119CF1C71004F3F0E /* MPSiteQuestionEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSiteQuestionEntity.m; sourceTree = ""; };
+ DA32CFE319CF1C71004F3F0E /* MPUserEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUserEntity.h; sourceTree = ""; };
+ DA32CFE419CF1C71004F3F0E /* MPUserEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUserEntity.m; sourceTree = ""; };
+ DA32D00019CF470E004F3F0E /* MasterPassword 6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 6.xcdatamodel"; sourceTree = ""; };
DA3509FC15F101A500C14A8E /* PearlQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlQueue.h; sourceTree = ""; };
DA3509FD15F101A500C14A8E /* PearlQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlQueue.m; sourceTree = ""; };
DA3B844A190FC5A900246EEA /* Crashlytics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Crashlytics.framework; sourceTree = ""; };
@@ -275,6 +293,7 @@
DA3B8451190FC86F00246EEA /* NSManagedObject+Pearl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSManagedObject+Pearl.h"; sourceTree = ""; };
DA3B8454190FC89700246EEA /* MPFixable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPFixable.m; sourceTree = ""; };
DA3B8455190FC89700246EEA /* MPFixable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPFixable.h; sourceTree = ""; };
+ DA3BCFCC19BD09E0006B2681 /* SourceCodePro-Regular.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SourceCodePro-Regular.otf"; sourceTree = ""; };
DA4425CB1557BED40052177D /* libUbiquityStoreManager.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libUbiquityStoreManager.a; sourceTree = BUILT_PRODUCTS_DIR; };
DA5BFA44147E415C00F98B1E /* Master Password.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Master Password.app"; sourceTree = BUILT_PRODUCTS_DIR; };
DA5BFA4A147E415C00F98B1E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
@@ -295,19 +314,11 @@
DA5E5CA21724A667003798D8 /* MPAppDelegate_Store.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAppDelegate_Store.m; sourceTree = ""; };
DA5E5CA31724A667003798D8 /* MPConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPConfig.h; sourceTree = ""; };
DA5E5CA41724A667003798D8 /* MPConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPConfig.m; sourceTree = ""; };
- DA5E5CA51724A667003798D8 /* MPElementEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementEntity.h; sourceTree = ""; };
- DA5E5CA61724A667003798D8 /* MPElementEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementEntity.m; sourceTree = ""; };
- DA5E5CA71724A667003798D8 /* MPElementGeneratedEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementGeneratedEntity.h; sourceTree = ""; };
- DA5E5CA81724A667003798D8 /* MPElementGeneratedEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementGeneratedEntity.m; sourceTree = ""; };
- DA5E5CA91724A667003798D8 /* MPElementStoredEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementStoredEntity.h; sourceTree = ""; };
- DA5E5CAA1724A667003798D8 /* MPElementStoredEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementStoredEntity.m; sourceTree = ""; };
DA5E5CAB1724A667003798D8 /* MPEntities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPEntities.h; sourceTree = ""; };
DA5E5CAC1724A667003798D8 /* MPEntities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPEntities.m; sourceTree = ""; };
DA5E5CAD1724A667003798D8 /* MPKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPKey.h; sourceTree = ""; };
DA5E5CAE1724A667003798D8 /* MPKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPKey.m; sourceTree = ""; };
DA5E5CAF1724A667003798D8 /* MPTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPTypes.h; sourceTree = ""; };
- DA5E5CB01724A667003798D8 /* MPUserEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUserEntity.h; sourceTree = ""; };
- DA5E5CB11724A667003798D8 /* MPUserEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUserEntity.m; sourceTree = ""; };
DA5E5CB31724A667003798D8 /* MPMacAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPMacAppDelegate.h; sourceTree = ""; };
DA5E5CB41724A667003798D8 /* MPMacAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPMacAppDelegate.m; sourceTree = ""; };
DA5E5CB51724A667003798D8 /* MPMacConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPMacConfig.h; sourceTree = ""; };
@@ -318,10 +329,6 @@
DA5E5CC31724A667003798D8 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; };
DA5E5CC51724A667003798D8 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = ""; };
DA5E5CC61724A667003798D8 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
- DA5E5CC81724A667003798D8 /* MasterPassword 1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 1.xcdatamodel"; sourceTree = ""; };
- DA5E5CC91724A667003798D8 /* MasterPassword 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 2.xcdatamodel"; sourceTree = ""; };
- DA5E5CCA1724A667003798D8 /* MasterPassword 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 3.xcdatamodel"; sourceTree = ""; };
- DA5E5CCB1724A667003798D8 /* MasterPassword 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 4.xcdatamodel"; sourceTree = ""; };
DA606FEA195D03E200CA98B5 /* icon_action.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon_action.png; sourceTree = ""; };
DA606FEB195D03E200CA98B5 /* icon_action@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon_action@2x.png"; sourceTree = ""; };
DA606FEC195D03E200CA98B5 /* icon_addressbook-person.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon_addressbook-person.png"; sourceTree = ""; };
@@ -992,6 +999,16 @@
DA5E5C961724A667003798D8 /* ObjC */ = {
isa = PBXGroup;
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 */,
DA3B8455190FC89700246EEA /* MPFixable.h */,
DA5E5CB21724A667003798D8 /* Mac */,
@@ -1009,20 +1026,12 @@
DA5E5CA21724A667003798D8 /* MPAppDelegate_Store.m */,
DA5E5CA31724A667003798D8 /* MPConfig.h */,
DA5E5CA41724A667003798D8 /* MPConfig.m */,
- DA5E5CA51724A667003798D8 /* MPElementEntity.h */,
- DA5E5CA61724A667003798D8 /* MPElementEntity.m */,
- DA5E5CA71724A667003798D8 /* MPElementGeneratedEntity.h */,
- DA5E5CA81724A667003798D8 /* MPElementGeneratedEntity.m */,
- DA5E5CA91724A667003798D8 /* MPElementStoredEntity.h */,
- DA5E5CAA1724A667003798D8 /* MPElementStoredEntity.m */,
DA5E5CAB1724A667003798D8 /* MPEntities.h */,
DA5E5CAC1724A667003798D8 /* MPEntities.m */,
DA5E5CAD1724A667003798D8 /* MPKey.h */,
DA5E5CAE1724A667003798D8 /* MPKey.m */,
DA5E5CAF1724A667003798D8 /* MPTypes.h */,
- DA5E5CB01724A667003798D8 /* MPUserEntity.h */,
- DA5E5CB11724A667003798D8 /* MPUserEntity.m */,
- DA5E5CC71724A667003798D8 /* MasterPassword.xcdatamodeld */,
+ DA29992619C6A89900AF7DF1 /* MasterPassword.xcdatamodeld */,
);
name = ObjC;
path = ..;
@@ -1043,8 +1052,8 @@
DA5E5CC41724A667003798D8 /* MainMenu.xib */,
DA5E5CC61724A667003798D8 /* main.m */,
DA0933C91747A56A00DE1CEF /* MPInitialWindow.xib */,
- 93D39E73BF5CBF8E5B005CD3 /* MPElementModel.m */,
- 93D39240B5143E01F0B75E96 /* MPElementModel.h */,
+ 93D39E73BF5CBF8E5B005CD3 /* MPSiteModel.m */,
+ 93D39240B5143E01F0B75E96 /* MPSiteModel.h */,
DA2508F019511D3600AC23F1 /* MPPasswordWindowController.xib */,
93D39A57A7823DE98A0FF83C /* MPPasswordWindowController.m */,
93D392C3918763B3B72CF366 /* MPPasswordWindowController.h */,
@@ -1052,8 +1061,8 @@
93D3977484534E99F9BA579D /* MPPasswordWindow.h */,
93D39D3CB30874147D9A9E1B /* MPInitialWindowController.m */,
93D39368EF3CBFEF2AFCA15A /* MPInitialWindowController.h */,
- 93D39423D7BF4FD31FE6D27C /* MPElementsTableView.m */,
- 93D39AC6360DDC16AEAA4119 /* MPElementsTableView.h */,
+ 93D39423D7BF4FD31FE6D27C /* MPSitesTableView.m */,
+ 93D39AC6360DDC16AEAA4119 /* MPSitesTableView.h */,
);
path = Mac;
sourceTree = "";
@@ -1607,6 +1616,7 @@
DACA268A1705DF81002C6C22 /* Fonts */ = {
isa = PBXGroup;
children = (
+ DA3BCFCC19BD09E0006B2681 /* SourceCodePro-Regular.otf */,
DAF4EF52190A828100023C90 /* Exo2.0-Thin.otf */,
DAF4EF53190A828100023C90 /* Exo2.0-Regular.otf */,
DAF4EF54190A828100023C90 /* Exo2.0-ExtraBold.otf */,
@@ -1939,7 +1949,7 @@
attributes = {
CLASSPREFIX = MP;
LastTestingUpgradeCheck = 0510;
- LastUpgradeCheck = 0510;
+ LastUpgradeCheck = 0600;
ORGANIZATIONNAME = Lyndir;
TargetAttributes = {
DA5BFA43147E415C00F98B1E = {
@@ -1954,12 +1964,10 @@
};
buildConfigurationList = DA5BFA3E147E415C00F98B1E /* Build configuration list for PBXProject "MasterPassword-Mac" */;
compatibilityVersion = "Xcode 3.2";
- developmentRegion = English;
+ developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
- English,
- es,
);
mainGroup = DA5BFA39147E415C00F98B1E;
productRefGroup = DA5BFA45147E415C00F98B1E /* Products */;
@@ -2041,6 +2049,7 @@
DACA27301705DF81002C6C22 /* avatar-18.png in Resources */,
DACA27311705DF81002C6C22 /* avatar-4.png in Resources */,
DAF4EF58190A828100023C90 /* Exo2.0-ExtraBold.otf in Resources */,
+ DA3BCFCD19BD09E0006B2681 /* SourceCodePro-Regular.otf in Resources */,
DAF4EF56190A828100023C90 /* Exo2.0-Thin.otf in Resources */,
DACA27321705DF81002C6C22 /* avatar-16.png in Resources */,
DACA27331705DF81002C6C22 /* avatar-12@2x.png in Resources */,
@@ -2133,28 +2142,29 @@
buildActionMask = 2147483647;
files = (
DA5E5CF61724A667003798D8 /* MPAlgorithm.m in Sources */,
+ DA32CFE219CF1C71004F3F0E /* MPSiteQuestionEntity.m in Sources */,
+ DA32CFE519CF1C71004F3F0E /* MPUserEntity.m in Sources */,
DA5E5CF71724A667003798D8 /* MPAlgorithmV0.m in Sources */,
DA5E5CF81724A667003798D8 /* MPAlgorithmV1.m in Sources */,
DA5E5CF91724A667003798D8 /* MPAppDelegate_Key.m in Sources */,
DA5E5CFA1724A667003798D8 /* MPAppDelegate_Shared.m in Sources */,
DA5E5CFB1724A667003798D8 /* MPAppDelegate_Store.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 */,
DA3B8456190FC89700246EEA /* MPFixable.m in Sources */,
DA5E5D001724A667003798D8 /* MPEntities.m in Sources */,
DA5E5D011724A667003798D8 /* MPKey.m in Sources */,
- DA5E5D021724A667003798D8 /* MPUserEntity.m in Sources */,
+ DA32CFDC19CF1C70004F3F0E /* MPStoredSiteEntity.m in Sources */,
DA5E5D031724A667003798D8 /* MPMacAppDelegate.m in Sources */,
DA5E5D041724A667003798D8 /* MPMacConfig.m in Sources */,
DA5E5D0C1724A667003798D8 /* main.m in Sources */,
- DA5E5D0D1724A667003798D8 /* MasterPassword.xcdatamodeld in Sources */,
- 93D39C5789EFA607CF788082 /* MPElementModel.m in Sources */,
+ 93D39C5789EFA607CF788082 /* MPSiteModel.m in Sources */,
93D39F833DEC1C89B2F795AC /* MPPasswordWindowController.m in Sources */,
+ DA32CFD919CF1C70004F3F0E /* MPGeneratedSiteEntity.m in Sources */,
93D390C676DF52DA7E459F19 /* MPPasswordWindow.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;
};
@@ -2260,6 +2270,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES;
+ COMBINE_HIDPI_IMAGES = YES;
};
name = "Debug-Mac";
};
@@ -2267,6 +2278,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES;
+ COMBINE_HIDPI_IMAGES = YES;
};
name = "AdHoc-Mac";
};
@@ -2274,6 +2286,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES;
+ COMBINE_HIDPI_IMAGES = YES;
};
name = "AppStore-Mac";
};
@@ -2281,6 +2294,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES;
+ COMBINE_HIDPI_IMAGES = YES;
};
name = "Debug-Mac";
};
@@ -2288,6 +2302,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES;
+ COMBINE_HIDPI_IMAGES = YES;
};
name = "AdHoc-Mac";
};
@@ -2295,6 +2310,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES;
+ COMBINE_HIDPI_IMAGES = YES;
};
name = "AppStore-Mac";
};
@@ -2315,11 +2331,13 @@
CLANG_WARN_OBJC_RECEIVER_WEAK = NO;
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
@@ -2388,11 +2406,13 @@
CLANG_WARN_OBJC_RECEIVER_WEAK = NO;
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -2510,11 +2530,13 @@
CLANG_WARN_OBJC_RECEIVER_WEAK = NO;
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -2595,6 +2617,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES;
+ COMBINE_HIDPI_IMAGES = YES;
DSTROOT = /tmp/Pearl.dst;
GCC_PREFIX_HEADER = "../Pearl/Pearl-Prefix.pch";
LIBRARY_SEARCH_PATHS = (
@@ -2614,6 +2637,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_ARC = NO;
+ COMBINE_HIDPI_IMAGES = YES;
DSTROOT = /tmp/jrswizzle.dst;
GCC_WARN_INHIBIT_ALL_WARNINGS = YES;
OTHER_LDFLAGS = "-ObjC";
@@ -2625,18 +2649,21 @@
DABC6C0B175D8C85000C15D4 /* Debug-Mac */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
};
name = "Debug-Mac";
};
DABC6C0C175D8C85000C15D4 /* AdHoc-Mac */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
};
name = "AdHoc-Mac";
};
DABC6C0D175D8C85000C15D4 /* AppStore-Mac */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
};
name = "AppStore-Mac";
};
@@ -2644,6 +2671,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_ARC = NO;
+ COMBINE_HIDPI_IMAGES = YES;
DSTROOT = /tmp/jrswizzle.dst;
GCC_WARN_INHIBIT_ALL_WARNINGS = YES;
OTHER_LDFLAGS = "-ObjC";
@@ -2656,6 +2684,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_ARC = NO;
+ COMBINE_HIDPI_IMAGES = YES;
DSTROOT = /tmp/jrswizzle.dst;
GCC_WARN_INHIBIT_ALL_WARNINGS = YES;
OTHER_LDFLAGS = "-ObjC";
@@ -2668,6 +2697,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES;
+ COMBINE_HIDPI_IMAGES = YES;
DSTROOT = /tmp/Pearl.dst;
GCC_PREFIX_HEADER = "../Pearl/Pearl-Prefix.pch";
LIBRARY_SEARCH_PATHS = (
@@ -2687,6 +2717,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES;
+ COMBINE_HIDPI_IMAGES = YES;
DSTROOT = /tmp/Pearl.dst;
GCC_PREFIX_HEADER = "../Pearl/Pearl-Prefix.pch";
LIBRARY_SEARCH_PATHS = (
@@ -2778,15 +2809,17 @@
/* End XCConfigurationList section */
/* Begin XCVersionGroup section */
- DA5E5CC71724A667003798D8 /* MasterPassword.xcdatamodeld */ = {
+ DA29992619C6A89900AF7DF1 /* MasterPassword.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
- DA5E5CC81724A667003798D8 /* MasterPassword 1.xcdatamodel */,
- DA5E5CC91724A667003798D8 /* MasterPassword 2.xcdatamodel */,
- DA5E5CCA1724A667003798D8 /* MasterPassword 3.xcdatamodel */,
- DA5E5CCB1724A667003798D8 /* MasterPassword 4.xcdatamodel */,
+ DA29992719C6A89900AF7DF1 /* MasterPassword 1.xcdatamodel */,
+ DA29992819C6A89900AF7DF1 /* MasterPassword 2.xcdatamodel */,
+ DA29992919C6A89900AF7DF1 /* MasterPassword 3.xcdatamodel */,
+ DA29992A19C6A89900AF7DF1 /* MasterPassword 4.xcdatamodel */,
+ DA29992B19C6A89900AF7DF1 /* MasterPassword 5.xcdatamodel */,
+ DA32D00019CF470E004F3F0E /* MasterPassword 6.xcdatamodel */,
);
- currentVersion = DA5E5CCB1724A667003798D8 /* MasterPassword 4.xcdatamodel */;
+ currentVersion = DA32D00019CF470E004F3F0E /* MasterPassword 6.xcdatamodel */;
path = MasterPassword.xcdatamodeld;
sourceTree = "";
versionGroupType = wrapper.xcdatamodel;
diff --git a/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/xcshareddata/xcschemes/MasterPassword Mac (App Store).xcscheme b/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/xcshareddata/xcschemes/MasterPassword Mac (App Store).xcscheme
index 0c3d2652..d446f090 100644
--- a/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/xcshareddata/xcschemes/MasterPassword Mac (App Store).xcscheme
+++ b/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/xcshareddata/xcschemes/MasterPassword Mac (App Store).xcscheme
@@ -1,6 +1,6 @@
_XCCurrentVersionName
- MasterPassword 4.xcdatamodel
+ MasterPassword 6.xcdatamodel
diff --git a/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 1.xcdatamodel/contents b/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 1.xcdatamodel/contents
index b89e2ea1..68a117df 100644
--- a/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 1.xcdatamodel/contents
+++ b/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 1.xcdatamodel/contents
@@ -1,30 +1,29 @@
-
-
+
+
-
+
-
+
-
+
-
+
-
-
+
+
+
+
@@ -32,4 +31,4 @@
-
+
\ No newline at end of file
diff --git a/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 2.xcdatamodel/contents b/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 2.xcdatamodel/contents
index 0a6d4e19..f13a64fc 100644
--- a/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 2.xcdatamodel/contents
+++ b/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 2.xcdatamodel/contents
@@ -1,7 +1,6 @@
-
-
+
+
@@ -12,16 +11,15 @@
-
+
-
+
-
+
-
+
@@ -31,8 +29,7 @@
-
+
@@ -40,4 +37,4 @@
-
+
\ No newline at end of file
diff --git a/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 3.xcdatamodel/contents b/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 3.xcdatamodel/contents
index 47b66152..b6bdbd0f 100644
--- a/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 3.xcdatamodel/contents
+++ b/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 3.xcdatamodel/contents
@@ -1,7 +1,6 @@
-
-
+
+
@@ -12,13 +11,12 @@
-
+
-
+
-
+
@@ -31,8 +29,7 @@
-
+
@@ -40,4 +37,4 @@
-
+
\ No newline at end of file
diff --git a/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 4.xcdatamodel/contents b/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 4.xcdatamodel/contents
index cef7ad8d..ce674890 100644
--- a/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 4.xcdatamodel/contents
+++ b/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 4.xcdatamodel/contents
@@ -1,7 +1,6 @@
-
-
+
+
@@ -12,13 +11,12 @@
-
+
-
+
-
+
@@ -30,8 +28,7 @@
-
+
@@ -39,4 +36,4 @@
-
+
\ No newline at end of file
diff --git a/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 5.xcdatamodel/contents b/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 5.xcdatamodel/contents
new file mode 100644
index 00000000..0ccbdbd8
--- /dev/null
+++ b/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 5.xcdatamodel/contents
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 6.xcdatamodel/contents b/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 6.xcdatamodel/contents
new file mode 100644
index 00000000..96ba87f6
--- /dev/null
+++ b/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 6.xcdatamodel/contents
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MasterPassword/ObjC/iOS/MPAnswersViewController.h b/MasterPassword/ObjC/iOS/MPAnswersViewController.h
new file mode 100644
index 00000000..367039c4
--- /dev/null
+++ b/MasterPassword/ObjC/iOS/MPAnswersViewController.h
@@ -0,0 +1,46 @@
+//
+// MPPreferencesViewController.h
+// MasterPassword-iOS
+//
+// Created by Maarten Billemont on 04/06/12.
+// Copyright (c) 2012 Lyndir. All rights reserved.
+//
+
+#import
+#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
+
+@end
+
+@interface MPAnswersQuestionCell : UITableViewCell
+
+@property(nonatomic) IBOutlet UITextField *questionField;
+@property(nonatomic) IBOutlet UITextField *answerField;
+
+- (void)setQuestion:(MPSiteQuestionEntity *)question forSite:(MPSiteEntity *)site;
+
+@end
diff --git a/MasterPassword/ObjC/iOS/MPAnswersViewController.m b/MasterPassword/ObjC/iOS/MPAnswersViewController.m
new file mode 100644
index 00000000..a161f917
--- /dev/null
+++ b/MasterPassword/ObjC/iOS/MPAnswersViewController.m
@@ -0,0 +1,301 @@
+//
+// 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_Key.h"
+#import "MPAppDelegate_Store.h"
+#import "UIColor+Expanded.h"
+#import "MPPasswordsViewController.h"
+#import "MPCoachmarkViewController.h"
+#import "MPSiteQuestionEntity.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];
+}
+
+- (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];
+ [site_ removeQuestions:site_.questions];
+ [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 ofSite:site 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.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 ofSite:site usingKey:[MPiOSAppDelegate get].key result:^(NSString *result) {
+ PearlMainQueue( ^{
+ self.questionField.text = keyword;
+ self.answerField.text = result;
+ } );
+ }];
+ }
+}
+
+@end
diff --git a/MasterPassword/ObjC/iOS/MPAppSettingsViewController.m b/MasterPassword/ObjC/iOS/MPAppSettingsViewController.m
index 37e48c40..fa107c1f 100644
--- a/MasterPassword/ObjC/iOS/MPAppSettingsViewController.m
+++ b/MasterPassword/ObjC/iOS/MPAppSettingsViewController.m
@@ -19,23 +19,6 @@
#import "MPAppSettingsViewController.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 {
}
diff --git a/MasterPassword/ObjC/iOS/MPAvatarCell.m b/MasterPassword/ObjC/iOS/MPAvatarCell.m
index eb827809..111b7bb8 100644
--- a/MasterPassword/ObjC/iOS/MPAvatarCell.m
+++ b/MasterPassword/ObjC/iOS/MPAvatarCell.m
@@ -1,12 +1,12 @@
/**
- * Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
- *
- * See the enclosed file LICENSE for license information (LGPLv3). If you did
- * not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
- *
- * @author Maarten Billemont
- * @license http://www.gnu.org/licenses/lgpl-3.0.txt
- */
+* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
+*
+* See the enclosed file LICENSE for license information (LGPLv3). If you did
+* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
+*
+* @author Maarten Billemont
+* @license http://www.gnu.org/licenses/lgpl-3.0.txt
+*/
//
// MPAvatarCell.h
@@ -57,11 +57,14 @@ const long MPAvatarAdd = 10000;
self.avatarImageView.layer.masksToBounds = NO;
self.avatarImageView.backgroundColor = [UIColor clearColor];
- [self observeKeyPath:@"selected" withBlock:^(id from, id to, NSKeyValueChange cause, id _self) {
- [_self updateAnimated:YES];
+ [self observeKeyPath:@"bounds" withBlock:^(id from, id to, NSKeyValueChange cause, MPAvatarCell *_self) {
+ _self.contentView.frame = _self.bounds;
}];
- [self observeKeyPath:@"highlighted" withBlock:^(id from, id to, NSKeyValueChange cause, id _self) {
- [_self updateAnimated:YES];
+ [self observeKeyPath:@"selected" withBlock:^(id from, id to, NSKeyValueChange cause, MPAvatarCell *_self) {
+ [_self updateAnimated:_self.superview != nil];
+ }];
+ [self observeKeyPath:@"highlighted" withBlock:^(id from, id to, NSKeyValueChange cause, MPAvatarCell *_self) {
+ [_self updateAnimated:_self.superview != nil];
}];
CABasicAnimation *toShadowOpacityAnimation = [CABasicAnimation animationWithKeyPath:@"shadowOpacity"];
@@ -88,9 +91,9 @@ const long MPAvatarAdd = 10000;
[super prepareForReuse];
_newUser = NO;
- [self setVisibility:0 animated:NO];
- [self setMode:MPAvatarModeLowered animated:NO];
- [self setSpinnerActive:NO animated:NO];
+ _visibility = 0;
+ _mode = MPAvatarModeLowered;
+ _spinnerActive = NO;
}
- (void)dealloc {
@@ -130,6 +133,8 @@ const long MPAvatarAdd = 10000;
- (void)setVisibility:(CGFloat)visibility animated:(BOOL)animated {
+ if (visibility == _visibility)
+ return;
_visibility = visibility;
[self updateAnimated:animated];
@@ -149,6 +154,8 @@ const long MPAvatarAdd = 10000;
- (void)setMode:(MPAvatarMode)mode animated:(BOOL)animated {
+ if (mode == _mode)
+ return;
_mode = mode;
[self updateAnimated:animated];
@@ -189,92 +196,95 @@ const long MPAvatarAdd = 10000;
- (void)updateAnimated:(BOOL)animated {
+ [self.contentView layoutIfNeeded];
[UIView animateWithDuration:animated? 0.2f: 0 animations:^{
self.avatarImageView.transform = CGAffineTransformIdentity;
}];
- [UIView animateWithDuration:animated? 0.3f: 0 delay:0 options:UIViewAnimationOptionOverrideInheritedDuration animations:^{
- self.alpha = 1;
+ [UIView animateWithDuration:animated? 0.5f: 0 delay:0
+ options:UIViewAnimationOptionOverrideInheritedDuration | UIViewAnimationOptionBeginFromCurrentState
+ animations:^{
+ self.alpha = 1;
- if (self.newUser) {
- if (self.mode == MPAvatarModeLowered)
- self.avatar = MPAvatarAdd;
- else if (self.avatar == MPAvatarAdd)
- self.avatar = arc4random() % MPAvatarCount;
- }
+ if (self.newUser) {
+ if (self.mode == MPAvatarModeLowered)
+ self.avatar = MPAvatarAdd;
+ else if (self.avatar == MPAvatarAdd)
+ self.avatar = arc4random() % MPAvatarCount;
+ }
- switch (self.mode) {
- case MPAvatarModeLowered: {
- [self.avatarSizeConstraint updateConstant:self.avatarImageView.image.size.height];
- [self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultLow];
- [self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow];
- [self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultLow];
- self.nameContainer.alpha = self.visibility;
- self.nameContainer.backgroundColor = [UIColor clearColor];
- self.avatarImageView.alpha = self.visibility / 0.7f + 0.3f;
- self.avatarImageView.layer.shadowRadius = 15 * self.visibility * self.visibility;
- break;
- }
- case MPAvatarModeRaisedButInactive: {
- [self.avatarSizeConstraint updateConstant:self.avatarImageView.image.size.height];
- [self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultHigh];
- [self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow];
- [self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultLow];
- self.nameContainer.alpha = self.visibility;
- self.nameContainer.backgroundColor = [UIColor clearColor];
- self.avatarImageView.alpha = 0;
- self.avatarImageView.layer.shadowRadius = 15 * self.visibility * self.visibility;
- break;
- }
- case MPAvatarModeRaisedAndActive: {
- [self.avatarSizeConstraint updateConstant:self.avatarImageView.image.size.height];
- [self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultHigh];
- [self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow];
- [self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultHigh];
- self.nameContainer.alpha = self.visibility;
- self.nameContainer.backgroundColor = [UIColor blackColor];
- self.avatarImageView.alpha = 1;
- self.avatarImageView.layer.shadowRadius = 15 * self.visibility * self.visibility;
- break;
- }
- case MPAvatarModeRaisedAndHidden: {
- [self.avatarSizeConstraint updateConstant:self.avatarImageView.image.size.height];
- [self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultHigh];
- [self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow];
- [self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultHigh];
- self.nameContainer.alpha = 0;
- self.nameContainer.backgroundColor = [UIColor blackColor];
- self.avatarImageView.alpha = 0;
- self.avatarImageView.layer.shadowRadius = 15 * self.visibility * self.visibility;
- break;
- }
- case MPAvatarModeRaisedAndMinimized: {
- [self.avatarSizeConstraint updateConstant:36];
- [self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultLow];
- [self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultHigh];
- [self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultHigh];
- self.nameContainer.alpha = 0;
- self.nameContainer.backgroundColor = [UIColor blackColor];
- self.avatarImageView.alpha = 1;
- break;
- }
- }
+ switch (self.mode) {
+ case MPAvatarModeLowered: {
+ [self.avatarSizeConstraint updateConstant:self.avatarImageView.image.size.height];
+ [self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultLow];
+ [self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow];
+ [self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultLow];
+ self.nameContainer.alpha = self.visibility;
+ self.nameContainer.backgroundColor = [UIColor clearColor];
+ self.avatarImageView.alpha = self.visibility / 0.7f + 0.3f;
+ self.avatarImageView.layer.shadowRadius = 15 * self.visibility * self.visibility;
+ break;
+ }
+ case MPAvatarModeRaisedButInactive: {
+ [self.avatarSizeConstraint updateConstant:self.avatarImageView.image.size.height];
+ [self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultHigh];
+ [self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow];
+ [self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultLow];
+ self.nameContainer.alpha = self.visibility;
+ self.nameContainer.backgroundColor = [UIColor clearColor];
+ self.avatarImageView.alpha = 0;
+ self.avatarImageView.layer.shadowRadius = 15 * self.visibility * self.visibility;
+ break;
+ }
+ case MPAvatarModeRaisedAndActive: {
+ [self.avatarSizeConstraint updateConstant:self.avatarImageView.image.size.height];
+ [self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultHigh];
+ [self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow];
+ [self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultHigh];
+ self.nameContainer.alpha = self.visibility;
+ self.nameContainer.backgroundColor = [UIColor blackColor];
+ self.avatarImageView.alpha = 1;
+ self.avatarImageView.layer.shadowRadius = 15 * self.visibility * self.visibility;
+ break;
+ }
+ case MPAvatarModeRaisedAndHidden: {
+ [self.avatarSizeConstraint updateConstant:self.avatarImageView.image.size.height];
+ [self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultHigh];
+ [self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow];
+ [self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultHigh];
+ self.nameContainer.alpha = 0;
+ self.nameContainer.backgroundColor = [UIColor blackColor];
+ self.avatarImageView.alpha = 0;
+ self.avatarImageView.layer.shadowRadius = 15 * self.visibility * self.visibility;
+ break;
+ }
+ case MPAvatarModeRaisedAndMinimized: {
+ [self.avatarSizeConstraint updateConstant:36];
+ [self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultLow];
+ [self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultHigh];
+ [self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultHigh];
+ self.nameContainer.alpha = 0;
+ self.nameContainer.backgroundColor = [UIColor blackColor];
+ self.avatarImageView.alpha = 1;
+ break;
+ }
+ }
- // Avatar minimized.
- if (self.mode == MPAvatarModeRaisedAndMinimized)
- [self.avatarImageView.layer removeAllAnimations];
- else if (![self.avatarImageView.layer animationForKey:@"targetedShadow"])
- [self.avatarImageView.layer addAnimation:_targetedShadowAnimation forKey:@"targetedShadow"];
+ // Avatar minimized.
+ if (self.mode == MPAvatarModeRaisedAndMinimized)
+ [self.avatarImageView.layer removeAnimationForKey:@"targetedShadow"];
+ else if (![self.avatarImageView.layer animationForKey:@"targetedShadow"])
+ [self.avatarImageView.layer addAnimation:_targetedShadowAnimation forKey:@"targetedShadow"];
- // Avatar selection and spinner.
- if (self.mode != MPAvatarModeRaisedAndMinimized && (self.selected || self.highlighted) && !self.spinnerActive)
- self.avatarImageView.backgroundColor = self.avatarImageView.tintColor;
- else
- self.avatarImageView.backgroundColor = [UIColor clearColor];
- self.avatarImageView.layer.cornerRadius = self.avatarImageView.bounds.size.height / 2;
- self.spinner.alpha = self.spinnerActive? 1: 0;
+ // Avatar selection and spinner.
+ if (self.mode != MPAvatarModeRaisedAndMinimized && (self.selected || self.highlighted) && !self.spinnerActive)
+ self.avatarImageView.backgroundColor = self.avatarImageView.tintColor;
+ else
+ self.avatarImageView.backgroundColor = [UIColor clearColor];
+ self.avatarImageView.layer.cornerRadius = self.avatarImageView.bounds.size.height / 2;
+ self.spinner.alpha = self.spinnerActive? 1: 0;
- [self layoutSubviews];
- } completion:nil];
+ [self.contentView layoutIfNeeded];
+ } completion:nil];
}
@end
diff --git a/MasterPassword/ObjC/iOS/MPCombinedViewController.m b/MasterPassword/ObjC/iOS/MPCombinedViewController.m
index abaeb6c9..dbb245e8 100644
--- a/MasterPassword/ObjC/iOS/MPCombinedViewController.m
+++ b/MasterPassword/ObjC/iOS/MPCombinedViewController.m
@@ -1,12 +1,12 @@
/**
- * Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
- *
- * See the enclosed file LICENSE for license information (LGPLv3). If you did
- * not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
- *
- * @author Maarten Billemont
- * @license http://www.gnu.org/licenses/lgpl-3.0.txt
- */
+* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
+*
+* See the enclosed file LICENSE for license information (LGPLv3). If you did
+* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
+*
+* @author Maarten Billemont
+* @license http://www.gnu.org/licenses/lgpl-3.0.txt
+*/
//
// MPCombinedViewController.h
@@ -19,7 +19,6 @@
#import "MPCombinedViewController.h"
#import "MPUsersViewController.h"
#import "MPPasswordsViewController.h"
-#import "MPEmergencySegue.h"
#import "MPEmergencyViewController.h"
#import "MPPasswordsSegue.h"
@@ -34,11 +33,13 @@
MPPasswordsViewController *_passwordsVC;
}
+#pragma mark - Life
+
- (void)viewDidLoad {
[super viewDidLoad];
- [self setMode:MPCombinedModeUserSelection animated:NO];
+ _mode = MPCombinedModeUserSelection;
}
- (void)viewWillAppear:(BOOL)animated {
@@ -67,9 +68,9 @@
if ([segue.identifier isEqualToString:@"users"])
self.usersVC = segue.destinationViewController;
if ([segue.identifier isEqualToString:@"passwords"]) {
- NSAssert([segue isKindOfClass:[MPPasswordsSegue class]], @"passwords segue should be MPPasswordsSegue: %@", segue);
- NSAssert([sender isKindOfClass:[NSDictionary class]], @"sender should be dictionary: %@", sender);
- NSAssert([[sender objectForKey:@"animated"] isKindOfClass:[NSNumber class]], @"sender should contain 'animated': %@", sender);
+ NSAssert( [segue isKindOfClass:[MPPasswordsSegue class]], @"passwords segue should be MPPasswordsSegue: %@", segue );
+ NSAssert( [sender isKindOfClass:[NSDictionary class]], @"sender should be dictionary: %@", sender );
+ NSAssert( [[sender objectForKey:@"animated"] isKindOfClass:[NSNumber class]], @"sender should contain 'animated': %@", sender );
[(MPPasswordsSegue *)segue setAnimated:[sender[@"animated"] boolValue]];
UIViewController *destinationVC = segue.destinationViewController;
_passwordsVC = [destinationVC isKindOfClass:[MPPasswordsViewController class]]? (MPPasswordsViewController *)destinationVC: nil;
@@ -99,20 +100,14 @@
[self performSegueWithIdentifier:@"emergency" sender:self];
}
-- (UIStoryboardSegue *)segueForUnwindingToViewController:(UIViewController *)toViewController
- fromViewController:(UIViewController *)fromViewController identifier:(NSString *)identifier {
+#pragma mark - Actions
- if ([identifier isEqualToString:@"unwind-emergency"]) {
- MPEmergencySegue *segue = [[MPEmergencySegue alloc] initWithIdentifier:identifier
- source:fromViewController destination:toViewController];
- segue.unwind = YES;
- dbg_return(segue);
- }
+- (IBAction)unwindToCombined:(UIStoryboardSegue *)sender {
- dbg_return((id)nil);
+ dbg( @"unwindToCombined:%@", sender );
}
-#pragma mark - Properties
+#pragma mark - State
- (void)setMode:(MPCombinedMode)mode {
@@ -158,22 +153,22 @@
if ([_notificationObservers count])
return;
- Weakify(self);
+ Weakify( self );
_notificationObservers = @[
[[NSNotificationCenter defaultCenter]
addObserverForName:MPSignedInNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
- Strongify(self);
+ Strongify( self );
- [self setMode:MPCombinedModePasswordSelection];
- }],
+ [self setMode:MPCombinedModePasswordSelection];
+ }],
[[NSNotificationCenter defaultCenter]
addObserverForName:MPSignedOutNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
- Strongify(self);
+ Strongify( self );
- [self setMode:MPCombinedModeUserSelection animated:[note.userInfo[@"animated"] boolValue]];
- }],
+ [self setMode:MPCombinedModeUserSelection animated:[note.userInfo[@"animated"] boolValue]];
+ }],
];
}
diff --git a/MasterPassword/ObjC/iOS/MPEmergencySegue.m b/MasterPassword/ObjC/iOS/MPEmergencySegue.m
deleted file mode 100644
index 1bef02c3..00000000
--- a/MasterPassword/ObjC/iOS/MPEmergencySegue.m
+++ /dev/null
@@ -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
- * @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
diff --git a/MasterPassword/ObjC/iOS/MPEmergencyViewController.m b/MasterPassword/ObjC/iOS/MPEmergencyViewController.m
index 1f86f655..6384789c 100644
--- a/MasterPassword/ObjC/iOS/MPEmergencyViewController.m
+++ b/MasterPassword/ObjC/iOS/MPEmergencyViewController.m
@@ -53,16 +53,9 @@
[self reset];
}
-- (BOOL)canPerformUnwindSegueAction:(SEL)action fromViewController:(UIViewController *)fromViewController withSender:(id)sender {
+- (UIStatusBarStyle)preferredStatusBarStyle {
- return [self respondsToSelector:action];
-}
-
-#pragma mark - Actions
-
-- (IBAction)unwindToCombined:(UIStoryboardSegue *)sender {
-
- dbg(@"unwindToCombined:%@", sender);
+ return UIStatusBarStyleLightContent;
}
#pragma mark - UITextFieldDelegate
@@ -126,7 +119,7 @@
- (void)updatePassword {
NSString *siteName = self.siteField.text;
- MPElementType siteType = [self siteType];
+ MPSiteType siteType = [self siteType];
NSUInteger siteCounter = (NSUInteger)self.counterStepper.value;
self.counterLabel.text = strf( @"%lu", (unsigned long)siteCounter );
@@ -136,7 +129,7 @@
[_emergencyPasswordQueue addOperationWithBlock:^{
NSString *sitePassword = nil;
if (_key && [siteName length])
- sitePassword = [MPAlgorithmDefault generateContentNamed:siteName ofType:siteType withCounter:siteCounter usingKey:_key];
+ sitePassword = [MPAlgorithmDefault generatePasswordForSiteNamed:siteName ofType:siteType withCounter:siteCounter usingKey:_key];
PearlMainQueue( ^{
[self.activity stopAnimating];
@@ -145,21 +138,21 @@
}];
}
-- (enum MPElementType)siteType {
+- (enum MPSiteType)siteType {
switch (self.typeControl.selectedSegmentIndex) {
case 0:
- return MPElementTypeGeneratedMaximum;
+ return MPSiteTypeGeneratedMaximum;
case 1:
- return MPElementTypeGeneratedLong;
+ return MPSiteTypeGeneratedLong;
case 2:
- return MPElementTypeGeneratedMedium;
+ return MPSiteTypeGeneratedMedium;
case 3:
- return MPElementTypeGeneratedBasic;
+ return MPSiteTypeGeneratedBasic;
case 4:
- return MPElementTypeGeneratedShort;
+ return MPSiteTypeGeneratedShort;
case 5:
- return MPElementTypeGeneratedPIN;
+ return MPSiteTypeGeneratedPIN;
default:
Throw(@"Unsupported type index: %ld", (long)self.typeControl.selectedSegmentIndex);
}
@@ -187,7 +180,7 @@
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
Strongify(self);
- [self performSegueWithIdentifier:@"unwind-emergency" sender:self];
+ [self performSegueWithIdentifier:@"unwind-popover" sender:self];
}],
];
}
diff --git a/MasterPassword/ObjC/iOS/MPLogsViewController.h b/MasterPassword/ObjC/iOS/MPLogsViewController.h
index 823c41e5..946de9ed 100644
--- a/MasterPassword/ObjC/iOS/MPLogsViewController.h
+++ b/MasterPassword/ObjC/iOS/MPLogsViewController.h
@@ -23,7 +23,6 @@
@property (weak, nonatomic) IBOutlet UITextView *logView;
@property (weak, nonatomic) IBOutlet UISegmentedControl *levelControl;
-- (IBAction)action:(id)sender;
- (IBAction)toggleLevelControl:(UISegmentedControl *)sender;
- (IBAction)refresh:(UIBarButtonItem *)sender;
- (IBAction)mail:(UIBarButtonItem *)sender;
diff --git a/MasterPassword/ObjC/iOS/MPLogsViewController.m b/MasterPassword/ObjC/iOS/MPLogsViewController.m
index 8011a2b7..58473015 100644
--- a/MasterPassword/ObjC/iOS/MPLogsViewController.m
+++ b/MasterPassword/ObjC/iOS/MPLogsViewController.m
@@ -20,9 +20,7 @@
#import "MPiOSAppDelegate.h"
#import "MPAppDelegate_Store.h"
-@implementation MPLogsViewController {
- PearlOverlay *_switchCloudStoreProgress;
-}
+@implementation MPLogsViewController
- (void)viewDidLoad {
@@ -48,139 +46,6 @@
self.levelControl.selectedSegmentIndex = [[MPiOSConfig get].traceMode boolValue]? 1: 0;
}
-- (IBAction)action:(id)sender {
-
- [PearlSheet showSheetWithTitle:@"Advanced Actions" viewStyle:UIActionSheetStyleAutomatic
- initSheet:nil tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) {
- if (buttonIndex == sheet.cancelButtonIndex)
- return;
-
- if (buttonIndex == sheet.firstOtherButtonIndex) {
- // Switch
- [PearlAlert showAlertWithTitle:@"Switching iCloud Store" message:
- @"WARNING: This is an advanced operation and should only be done if you're having trouble with iCloud."
- viewStyle:UIAlertViewStyleDefault initAlert:nil
- tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex_) {
- if (buttonIndex_ == alert.cancelButtonIndex)
- return;
-
- _switchCloudStoreProgress = [PearlOverlay showProgressOverlayWithTitle:@"Enumerating Stores"];
- dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0 ), ^{
- [self switchCloudStore];
- } );
- } cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonContinue, nil];
- }
-
- if (buttonIndex == sheet.firstOtherButtonIndex + 1) {
- // Rebuild
- [PearlAlert showAlertWithTitle:@"Rebuilding iCloud Store" message:
- @"WARNING: This is an advanced operation and should only be done if you're having trouble with iCloud.\n"
- @"Your local iCloud data will be removed and redownloaded from iCloud."
- viewStyle:UIAlertViewStyleDefault initAlert:nil
- tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex_) {
- if (buttonIndex_ == alert.cancelButtonIndex)
- return;
-
- [[MPiOSAppDelegate get].storeManager deleteCloudContainerLocalOnly:YES];
- }
- cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonContinue, nil];
- }
-
- if (buttonIndex == sheet.firstOtherButtonIndex + 2) {
- // Wipe
- [PearlAlert showAlertWithTitle:@"Wiping iCloud Clean" message:
- @"WARNING: This is an advanced operation and should only be done if you're having trouble with iCloud.\n"
- @"All your iCloud data will be permanently lost. This is a clean slate!"
- viewStyle:UIAlertViewStyleDefault initAlert:nil
- tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex_) {
- if (buttonIndex_ == alert.cancelButtonIndex)
- return;
-
- [[MPiOSAppDelegate get].storeManager deleteCloudContainerLocalOnly:NO];
- }
- cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonContinue, nil];
- }
- } cancelTitle:[PearlStrings get].commonButtonCancel
- destructiveTitle:nil otherTitles:@"Switch iCloud Store", @"Rebuild iCloud Container", @"Wipe iCloud Clean", nil];
-}
-
-- (void)switchCloudStore {
-
- NSDictionary *cloudStores = [[MPiOSAppDelegate get].storeManager enumerateCloudStores];
- if (!cloudStores) {
- wrn( @"Failed enumerating cloud stores." );
- return;
- }
-
- NSString *currentStoreUUID = nil;
- NSMutableDictionary *stores = [NSMutableDictionary dictionary];
- NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];
- NSPersistentStoreCoordinator *storePSC = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
- NSFetchRequest *usersFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
- NSFetchRequest *sitesFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )];
- for (NSURL *cloudStoreURL in cloudStores) {
- NSString *storeUUID = [[cloudStoreURL URLByDeletingPathExtension] lastPathComponent];
- for (NSDictionary *cloudStoreOptions in cloudStores[cloudStoreURL]) {
- NSError *error = nil;
- NSPersistentStore *store = nil;
- NSUInteger firstDash = [storeUUID rangeOfString:@"-" options:0].location;
- NSString *storeDescription = strf( @"%@ v%@",
- firstDash == NSNotFound? storeUUID: [storeUUID substringToIndex:firstDash],
- cloudStoreOptions[USMCloudVersionKey] );
- if ([cloudStoreOptions[USMCloudCurrentKey] boolValue])
- currentStoreUUID = storeUUID;
- @try {
- if (!(store = [storePSC addPersistentStoreWithType:NSSQLiteStoreType configuration:nil
- URL:cloudStoreURL options:cloudStoreOptions error:&error])) {
- wrn(@"Couldn't describe store %@. While opening: %@", storeDescription, error);
- continue;
- }
-
- NSUInteger userCount, siteCount;
- NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
- moc.persistentStoreCoordinator = storePSC;
- if ((userCount = [moc countForFetchRequest:usersFetchRequest error:&error]) == NSNotFound) {
- wrn(@"Couldn't describe store %@. While determining userCount: %@", storeDescription, error);
- continue;
- }
- if ((siteCount = [moc countForFetchRequest:sitesFetchRequest error:&error]) == NSNotFound) {
- wrn(@"Couldn't describe store %@. While determining siteCount: %@", storeDescription, error);
- continue;
- }
-
- storeDescription = strf( @"%@: %luU, %luS", storeDescription, (unsigned long)userCount, (unsigned long)siteCount );
- }
- @catch (NSException *exception) {
- wrn(@"Couldn't describe store %@: %@", storeDescription, exception);
- }
- @finally {
- if (store && ![storePSC removePersistentStore:store error:&error]) {
- wrn(@"Couldn't remove store %@: %@", storeDescription, error);
- storePSC = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
- }
-
- stores[storeDescription] = cloudStoreOptions;
- }
- }
- }
-
- PearlArrayTVC *vc = [[PearlArrayTVC alloc] initWithStyle:UITableViewStylePlain];
- NSUInteger firstDash = [currentStoreUUID rangeOfString:@"-" options:0].location;
- vc.title = strf( @"Active: %@", firstDash == NSNotFound? currentStoreUUID: [currentStoreUUID substringToIndex:firstDash] );
- [stores enumerateKeysAndObjectsUsingBlock:^(id storeDescription, id cloudStoreOptions, BOOL *stop) {
- [vc addRowWithName:storeDescription style:PearlArrayTVCRowStyleLink toggled:[cloudStoreOptions[USMCloudCurrentKey] boolValue]
- toSection:@"Cloud Stores" activationBlock:^BOOL(BOOL wasToggled) {
- [[MPiOSAppDelegate get].storeManager switchToCloudStoreWithOptions:cloudStoreOptions];
- [self.navigationController popToRootViewControllerAnimated:YES];
- return YES;
- }];
- }];
- dispatch_async( dispatch_get_main_queue(), ^{
- [_switchCloudStoreProgress cancelOverlayAnimated:YES];
- [self.navigationController pushViewController:vc animated:YES];
- } );
-}
-
- (IBAction)toggleLevelControl:(UISegmentedControl *)sender {
BOOL traceEnabled = (BOOL)self.levelControl.selectedSegmentIndex;
diff --git a/MasterPassword/ObjC/iOS/MPEmergencySegue.h b/MasterPassword/ObjC/iOS/MPOverlayViewController.h
similarity index 69%
rename from MasterPassword/ObjC/iOS/MPEmergencySegue.h
rename to MasterPassword/ObjC/iOS/MPOverlayViewController.h
index b527d467..5f2090d7 100644
--- a/MasterPassword/ObjC/iOS/MPEmergencySegue.h
+++ b/MasterPassword/ObjC/iOS/MPOverlayViewController.h
@@ -9,16 +9,18 @@
*/
//
-// MPEmergencySegue.h
-// MPEmergencySegue
+// MPOverlayViewController.h
+// MPOverlayViewController
//
-// Created by lhunath on 2014-04-09.
+// Created by lhunath on 2014-09-22.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
#import
-@interface MPEmergencySegue : UIStoryboardSegue
-@property(nonatomic) BOOL unwind;
+@interface MPOverlayViewController : UIViewController
+@end
+
+@interface MPOverlaySegue : UIStoryboardSegue
@end
diff --git a/MasterPassword/ObjC/iOS/MPOverlayViewController.m b/MasterPassword/ObjC/iOS/MPOverlayViewController.m
new file mode 100644
index 00000000..891c4dd2
--- /dev/null
+++ b/MasterPassword/ObjC/iOS/MPOverlayViewController.m
@@ -0,0 +1,154 @@
+/**
+* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
+*
+* See the enclosed file LICENSE for license information (LGPLv3). If you did
+* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
+*
+* @author Maarten Billemont
+* @license http://www.gnu.org/licenses/lgpl-3.0.txt
+*/
+
+//
+// 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)viewWillAppear:(BOOL)animated {
+
+ [super viewWillAppear:animated];
+
+ if (![self.childViewControllers count])
+ [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];
+}
+
+- (void)addDismissButtonForSegue:(MPOverlaySegue *)segue {
+
+ UIButton *dismissButton = [UIButton buttonWithType:UIButtonTypeCustom];
+ [dismissButton addTarget:self action:@selector( dismissOverlay: ) forControlEvents:UIControlEventTouchUpInside];
+ dismissButton.backgroundColor = [UIColor colorWithWhite:0 alpha:0.3f];
+ 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];
+}
+
+- (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
+
+- (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];
+ [containerViewController setNeedsStatusBarAppearanceUpdate];
+
+ [containerViewController addDismissButtonForSegue:self];
+ destinationViewController.view.frame = containerViewController.view.bounds;
+ destinationViewController.view.translatesAutoresizingMaskIntoConstraints = YES;
+ destinationViewController.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+ [containerViewController.view addSubview:destinationViewController.view];
+
+ CGRectSetY( destinationViewController.view.frame, 100 );
+ destinationViewController.view.transform = CGAffineTransformMakeScale( 1.2f, 1.2f );
+ destinationViewController.view.alpha = 0;
+
+ [UIView transitionWithView:containerViewController.view duration:[self.identifier isEqualToString:@"root"]? 0: 0.3f
+ options:UIViewAnimationOptionAllowAnimatedContent animations:^{
+ destinationViewController.view.transform = CGAffineTransformIdentity;
+ CGRectSetY( destinationViewController.view.frame, 0 );
+ destinationViewController.view.alpha = 1;
+ } completion:^(BOOL finished) {
+ if (finished)
+ [destinationViewController didMoveToParentViewController:containerViewController];
+ }];
+ }
+ 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
diff --git a/MasterPassword/ObjC/iOS/MPPasswordCell.h b/MasterPassword/ObjC/iOS/MPPasswordCell.h
index 891c1957..4bb9a67a 100644
--- a/MasterPassword/ObjC/iOS/MPPasswordCell.h
+++ b/MasterPassword/ObjC/iOS/MPPasswordCell.h
@@ -27,8 +27,9 @@ typedef NS_ENUM ( NSUInteger, MPPasswordCellMode ) {
@interface MPPasswordCell : MPCell
-- (void)setElement:(MPElementEntity *)element animated:(BOOL)animated;
+- (void)setSite:(MPSiteEntity *)site animated:(BOOL)animated;
- (void)setTransientSite:(NSString *)siteName animated:(BOOL)animated;
- (void)setMode:(MPPasswordCellMode)mode animated:(BOOL)animated;
+- (MPSiteEntity *)siteInContext:(NSManagedObjectContext *)context;
@end
diff --git a/MasterPassword/ObjC/iOS/MPPasswordCell.m b/MasterPassword/ObjC/iOS/MPPasswordCell.m
index 78403784..4540fe13 100644
--- a/MasterPassword/ObjC/iOS/MPPasswordCell.m
+++ b/MasterPassword/ObjC/iOS/MPPasswordCell.m
@@ -19,22 +19,25 @@
#import "MPPasswordCell.h"
#import "MPiOSAppDelegate.h"
#import "MPAppDelegate_Store.h"
+#import "UIColor+Expanded.h"
+#import "MPAppDelegate_InApp.h"
@interface MPPasswordCell()
@property(nonatomic, strong) IBOutlet UILabel *siteNameLabel;
@property(nonatomic, strong) IBOutlet UITextField *passwordField;
+@property(nonatomic, strong) IBOutlet UIView *loginNameContainer;
@property(nonatomic, strong) IBOutlet UITextField *loginNameField;
-@property(nonatomic, strong) IBOutlet UIPageControl *pageControl;
@property(nonatomic, strong) IBOutlet UILabel *strengthLabel;
@property(nonatomic, strong) IBOutlet UILabel *counterLabel;
@property(nonatomic, strong) IBOutlet UIButton *counterButton;
@property(nonatomic, strong) IBOutlet UIButton *upgradeButton;
+@property(nonatomic, strong) IBOutlet UIButton *answersButton;
@property(nonatomic, strong) IBOutlet UIButton *modeButton;
-@property(nonatomic, strong) IBOutlet UIButton *loginModeButton;
@property(nonatomic, strong) IBOutlet UIButton *editButton;
@property(nonatomic, strong) IBOutlet UIScrollView *modeScrollView;
-@property(nonatomic, strong) IBOutlet UIButton *selectionButton;
+@property(nonatomic, strong) IBOutlet UIButton *contentButton;
+@property(nonatomic, strong) IBOutlet UIButton *loginNameButton;
@property(nonatomic, strong) IBOutlet UIView *indicatorView;
@property(nonatomic) MPPasswordCellMode mode;
@@ -43,7 +46,7 @@
@end
@implementation MPPasswordCell {
- NSManagedObjectID *_elementOID;
+ NSManagedObjectID *_siteOID;
}
#pragma mark - Life cycle
@@ -57,21 +60,39 @@
[self.counterButton addGestureRecognizer:
[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector( doResetCounter: )]];
- self.selectionButton.layer.cornerRadius = 5;
- self.selectionButton.layer.shadowOffset = CGSizeZero;
- self.selectionButton.layer.shadowRadius = 5;
- self.selectionButton.layer.shadowOpacity = 0;
- self.selectionButton.layer.shadowColor = [UIColor whiteColor].CGColor;
+ [self setupLayer];
- self.pageControl.transform = CGAffineTransformMakeScale( 0.4f, 0.4f );
-
- [self.selectionButton observeKeyPath:@"highlighted"
+ [self observeKeyPath:@"bounds" withBlock:^(id from, id to, NSKeyValueChange cause, MPPasswordCell *_self) {
+ if (from && !CGSizeEqualToSize( [from CGRectValue].size, [to CGRectValue].size ))
+ [_self setupLayer];
+ }];
+ [self.contentButton observeKeyPath:@"highlighted"
+ withBlock:^(id from, id to, NSKeyValueChange cause, UIButton *button) {
+ [UIView animateWithDuration:.2f delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
+ button.layer.shadowOpacity = button.selected? 0.7f: button.highlighted? 0.3f: 0;
+ } completion:nil];
+ }];
+ [self.contentButton observeKeyPath:@"selected"
+ withBlock:^(id from, id to, NSKeyValueChange cause, UIButton *button) {
+ [UIView animateWithDuration:.2f delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
+ button.layer.shadowOpacity = button.selected? 0.7f: button.highlighted? 0.3f: 0;
+ } completion:nil];
+ }];
+ [self.loginNameButton observeKeyPath:@"highlighted"
withBlock:^(id from, id to, NSKeyValueChange cause, UIButton *button) {
- button.layer.shadowOpacity = button.selected? 1: button.highlighted? 0.3f: 0;
+ [UIView animateWithDuration:.2f delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
+ button.backgroundColor = [button.backgroundColor colorWithAlphaComponent:
+ button.selected || button.highlighted? 0.1f: 0];
+ button.layer.shadowOpacity = button.selected? 0.7f: button.highlighted? 0.3f: 0;
+ } completion:nil];
}];
- [self.selectionButton observeKeyPath:@"selected"
+ [self.loginNameButton observeKeyPath:@"selected"
withBlock:^(id from, id to, NSKeyValueChange cause, UIButton *button) {
- button.layer.shadowOpacity = button.selected? 1: button.highlighted? 0.3f: 0;
+ [UIView animateWithDuration:.2f delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
+ button.backgroundColor = [button.backgroundColor colorWithAlphaComponent:
+ button.selected || button.highlighted? 0.1f: 0];
+ button.layer.shadowOpacity = button.selected? 0.7f: button.highlighted? 0.3f: 0;
+ } completion:nil];
}];
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position.y"];
@@ -82,17 +103,48 @@
[self.indicatorView.layer addAnimation:animation forKey:@"bounce"];
}
+- (void)setupLayer {
+
+ self.contentView.frame = self.bounds;
+ self.contentButton.layer.cornerRadius = 4;
+ self.contentButton.layer.shadowOffset = CGSizeZero;
+ self.contentButton.layer.shadowRadius = 5;
+ self.contentButton.layer.shadowOpacity = 0;
+ self.contentButton.layer.shadowColor = [UIColor whiteColor].CGColor;
+ self.contentButton.layer.borderWidth = 1;
+ self.contentButton.layer.borderColor = [UIColor colorWithWhite:0.15f alpha:0.6f].CGColor;
+ self.loginNameButton.layer.cornerRadius = 4;
+ self.loginNameButton.layer.shadowOffset = CGSizeZero;
+ self.loginNameButton.layer.shadowRadius = 5;
+ self.loginNameButton.layer.shadowOpacity = 0;
+ self.loginNameButton.layer.shadowColor = [UIColor whiteColor].CGColor;
+ self.contentView.layer.shadowRadius = 5;
+ self.contentView.layer.shadowOpacity = 1;
+ self.contentView.layer.shadowColor = [UIColor colorWithWhite:0 alpha:0.6f].CGColor;
+ self.contentView.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:self.contentView.bounds cornerRadius:4].CGPath;
+ self.contentView.layer.masksToBounds = NO;
+ self.contentView.clipsToBounds = NO;
+ self.layer.masksToBounds = NO;
+ self.clipsToBounds = NO;
+}
+
- (void)prepareForReuse {
[super prepareForReuse];
- _elementOID = nil;
+ _siteOID = nil;
self.transientSite = nil;
- self.loginModeButton.selected = NO;
self.mode = MPPasswordCellModePassword;
[self updateAnimated:NO];
}
+- (void)dealloc {
+
+ [self removeKeyPathObservers];
+ [self.contentButton removeKeyPathObservers];
+ [self.loginNameButton removeKeyPathObservers];
+}
+
#pragma mark - State
- (void)setMode:(MPPasswordCellMode)mode animated:(BOOL)animated {
@@ -104,9 +156,9 @@
[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];
}
@@ -120,7 +172,11 @@
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
- [textField resignFirstResponder];
+ if (textField == self.passwordField)
+ [self.loginNameField becomeFirstResponder];
+ else
+ [textField resignFirstResponder];
+
return YES;
}
@@ -129,6 +185,9 @@
UICollectionView *collectionView = [UICollectionView findAsSuperviewOf:self];
[collectionView scrollToItemAtIndexPath:[collectionView indexPathForCell:self]
atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:YES];
+
+ if (textField == self.loginNameField)
+ self.loginNameButton.titleLabel.alpha = [self.loginNameField.text length] || self.loginNameField.enabled? 0: 1;
}
- (IBAction)textFieldDidChange:(UITextField *)textField {
@@ -137,10 +196,10 @@
NSString *password = self.passwordField.text;
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
TimeToCrack timeToCrack;
- MPElementEntity *element = [self elementInContext:context];
- id algorithm = element.algorithm?: MPAlgorithmDefault;
+ MPSiteEntity *site = [self siteInContext:context];
+ id algorithm = site.algorithm?: MPAlgorithmDefault;
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])
PearlMainQueue( ^{
self.strengthLabel.text = NSStringFromTimeToCrack( timeToCrack );
@@ -152,21 +211,27 @@
- (void)textFieldDidEndEditing:(UITextField *)textField {
if (textField == self.passwordField || textField == self.loginNameField) {
- NSString *text = textField.text;
textField.enabled = NO;
+ NSString *text = textField.text;
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
- MPElementEntity *element = [self elementInContext:context];
- if (!element)
+ MPSiteEntity *site = [self siteInContext:context];
+ if (!site)
return;
if (textField == self.passwordField) {
- [element.algorithm saveContent:text toElement:element usingKey:[MPiOSAppDelegate get].key];
- [PearlOverlay showTemporaryOverlayWithTitle:@"Password Updated" dismissAfter:2];
+ if ([site.algorithm savePassword:text toSite:site usingKey:[MPiOSAppDelegate get].key])
+ [PearlOverlay showTemporaryOverlayWithTitle:@"Password Updated" dismissAfter:2];
}
- else if (textField == self.loginNameField) {
- element.loginName = text;
- [PearlOverlay showTemporaryOverlayWithTitle:@"Login Updated" dismissAfter:2];
+ else if (textField == self.loginNameField &&
+ ((site.loginGenerated && ![text length]) ||
+ (!site.loginGenerated && ![text isEqualToString:site.loginName]))) {
+ site.loginName = text;
+ site.loginGenerated = NO;
+ if ([text length])
+ [PearlOverlay showTemporaryOverlayWithTitle:@"Login Name Saved" dismissAfter:2];
+ else
+ [PearlOverlay showTemporaryOverlayWithTitle:@"Login Name Cleared" dismissAfter:2];
}
[context saveToStore];
@@ -177,25 +242,19 @@
#pragma mark - Actions
-- (IBAction)doLoginMode:(UIButton *)sender {
-
- self.loginModeButton.selected = !self.loginModeButton.selected;
- [self updateAnimated:YES];
-}
-
- (IBAction)doDelete:(UIButton *)sender {
- MPElementEntity *element = [self elementInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
- if (!element)
+ MPSiteEntity *site = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
+ if (!site)
return;
- [PearlSheet showSheetWithTitle:strf( @"Delete %@?", element.name ) viewStyle:UIActionSheetStyleAutomatic
+ [PearlSheet showSheetWithTitle:strf( @"Delete %@?", site.name ) viewStyle:UIActionSheetStyleAutomatic
initSheet:nil tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) {
if (buttonIndex == [sheet cancelButtonIndex])
return;
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
- [context deleteObject:[self elementInContext:context]];
+ [context deleteObject:[self siteInContext:context]];
[context saveToStore];
}];
} cancelTitle:@"Cancel" destructiveTitle:@"Delete Site" otherTitles:nil];
@@ -207,12 +266,11 @@
[PearlSheet showSheetWithTitle:@"Change Password Type" viewStyle:UIActionSheetStyleAutomatic
initSheet:^(UIActionSheet *sheet) {
- MPElementEntity
- *mainElement = [self elementInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
+ MPSiteEntity *mainSite = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
for (NSNumber *typeNumber in [MPAlgorithmDefault allTypes]) {
- MPElementType type = [typeNumber unsignedIntegerValue];
+ MPSiteType type = [typeNumber unsignedIntegerValue];
NSString *typeName = [MPAlgorithmDefault nameOfType:type];
- if (type == mainElement.type)
+ if (type == mainSite.type)
[sheet addButtonWithTitle:strf( @"● %@", typeName )];
else
[sheet addButtonWithTitle:typeName];
@@ -221,26 +279,25 @@
if (buttonIndex == [sheet cancelButtonIndex])
return;
- MPElementType type = [[MPAlgorithmDefault allTypes][buttonIndex] unsignedIntegerValue]?: MPElementTypeGeneratedLong;
+ MPSiteType type = [[MPAlgorithmDefault allTypes][buttonIndex] unsignedIntegerValue]?: MPSiteTypeGeneratedLong;
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
- MPElementEntity *element = [self elementInContext:context];
- element = [[MPiOSAppDelegate get] changeElement:element saveInContext:context toType:type];
- [self setElement:element animated:YES];
+ MPSiteEntity *site = [self siteInContext:context];
+ site = [[MPiOSAppDelegate get] changeSite:site saveInContext:context toType:type];
+ [self setSite:site animated:YES];
}];
} cancelTitle:@"Cancel" destructiveTitle:nil otherTitles:nil];
}
- (IBAction)doEdit:(UIButton *)sender {
- if (self.loginModeButton.selected) {
- self.loginNameField.enabled = YES;
- [self.loginNameField becomeFirstResponder];
- }
- else if ([self elementInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]].type & MPElementTypeClassStored) {
- self.passwordField.enabled = YES;
+ self.loginNameField.enabled = YES;
+ self.passwordField.enabled = YES;
+
+ if ([self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]].type & MPSiteTypeClassStored)
[self.passwordField becomeFirstResponder];
- }
+ else
+ [self.loginNameField becomeFirstResponder];
}
- (IBAction)doMode:(UIButton *)sender {
@@ -260,7 +317,7 @@
- (IBAction)doUpgrade:(UIButton *)sender {
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
- if (![[self elementInContext:context] migrateExplicitly:YES]) {
+ if (![[self siteInContext:context] tryMigrateExplicitly:YES]) {
[PearlOverlay showTemporaryOverlayWithTitle:@"Couldn't Upgrade Site" dismissAfter:2];
return;
}
@@ -274,11 +331,11 @@
- (IBAction)doIncrementCounter:(UIButton *)sender {
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
- MPElementEntity *element = [self elementInContext:context];
- if (!element || ![element isKindOfClass:[MPElementGeneratedEntity class]])
+ MPSiteEntity *site = [self siteInContext:context];
+ if (!site || ![site isKindOfClass:[MPGeneratedSiteEntity class]])
return;
- ++((MPElementGeneratedEntity *)element).counter;
+ ++((MPGeneratedSiteEntity *)site).counter;
[context saveToStore];
[PearlOverlay showTemporaryOverlayWithTitle:@"Generating New Password" dismissAfter:2];
@@ -306,11 +363,11 @@
return;
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
- MPElementEntity *element = [self elementInContext:context];
- if (!element || ![element isKindOfClass:[MPElementGeneratedEntity class]])
+ MPSiteEntity *site = [self siteInContext:context];
+ if (!site || ![site isKindOfClass:[MPGeneratedSiteEntity class]])
return;
- ((MPElementGeneratedEntity *)element).counter = 1;
+ ((MPGeneratedSiteEntity *)site).counter = 1;
[context saveToStore];
[PearlOverlay showTemporaryOverlayWithTitle:@"Counter Reset" dismissAfter:2];
@@ -318,9 +375,11 @@
}];
}
-- (IBAction)doUse:(id)sender {
+- (IBAction)doContent:(id)sender {
- self.selectionButton.selected = YES;
+ [UIView animateWithDuration:.2f animations:^{
+ self.contentButton.selected = YES;
+ }];
if (self.transientSite) {
[[UIResponder findFirstResponder] resignFirstResponder];
@@ -329,15 +388,18 @@
viewStyle:UIAlertViewStyleDefault
initAlert:nil tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
if (buttonIndex == [alert cancelButtonIndex]) {
- self.selectionButton.selected = NO;
+ self.contentButton.selected = NO;
return;
}
[[MPiOSAppDelegate get]
- addElementNamed:self.transientSite completion:^(MPElementEntity *element, NSManagedObjectContext *context) {
- [self copyContentOfElement:element saveInContext:context];
- PearlMainQueue( ^{
- self.selectionButton.selected = NO;
+ addSiteNamed:self.transientSite completion:^(MPSiteEntity *site, NSManagedObjectContext *context) {
+ [self copyContentOfSite:site saveInContext:context];
+
+ PearlMainQueueAfter( .3f, ^{
+ [UIView animateWithDuration:.2f animations:^{
+ self.contentButton.selected = NO;
+ }];
} );
}];
} cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonYes, nil];
@@ -345,9 +407,35 @@
}
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
- [self copyContentOfElement:[self elementInContext:context] saveInContext:context];
- PearlMainQueue( ^{
- self.selectionButton.selected = NO;
+ [self copyContentOfSite:[self siteInContext:context] saveInContext:context];
+
+ PearlMainQueueAfter( .3f, ^{
+ [UIView animateWithDuration:.2f animations:^{
+ self.contentButton.selected = NO;
+ }];
+ } );
+ }];
+}
+
+- (IBAction)doLoginName:(id)sender {
+
+ [UIView animateWithDuration:.2f animations:^{
+ self.loginNameButton.selected = YES;
+ }];
+
+ [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
+ MPSiteEntity *site = [self siteInContext:context];
+ if (![self copyLoginOfSite:site saveInContext:context]) {
+ site.loginGenerated = YES;
+ [context saveToStore];
+ [PearlOverlay showTemporaryOverlayWithTitle:@"Login Name Generated" dismissAfter:2];
+ [self updateAnimated:YES];
+ }
+
+ PearlMainQueueAfter( .3f, ^{
+ [UIView animateWithDuration:.2f animations:^{
+ self.loginNameButton.selected = NO;
+ }];
} );
}];
}
@@ -373,149 +461,132 @@
return;
}
- [UIView animateWithDuration:animated? 0.3f: 0 animations:^{
- MPElementEntity *mainElement = [self elementInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
+ [UIView animateWithDuration:animated? .3f: 0 animations:^{
+ MPSiteEntity *mainSite = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
// UI
- self.selectionButton.layer.shadowOpacity = self.selectionButton.selected? 1: self.selectionButton.highlighted? 0.3f: 0;
- self.upgradeButton.alpha = mainElement.requiresExplicitMigration? 1: 0;
- self.passwordField.alpha = self.loginModeButton.selected? 0: 1;
- self.loginNameField.alpha = self.loginModeButton.selected? 1: 0;
- self.modeButton.alpha = self.transientSite? 0: 1;
- self.loginModeButton.alpha = self.transientSite? 0: 1;
- self.counterLabel.alpha = self.counterButton.alpha = mainElement.type & MPElementTypeClassGenerated? 1: 0;
- self.modeButton.selected = self.mode == MPPasswordCellModeSettings;
- self.pageControl.currentPage = self.mode == MPPasswordCellModePassword? 0: 1;
- self.strengthLabel.alpha = self.mode == MPPasswordCellModePassword? 0: 1;
- self.editButton.enabled = self.loginModeButton.selected || mainElement.type & MPElementTypeClassStored;
+ self.upgradeButton.gone = !mainSite.requiresExplicitMigration;
+ self.answersButton.gone = ![[MPiOSAppDelegate get] isPurchased:MPProductGenerateAnswers];
+ BOOL settingsMode = self.mode == MPPasswordCellModeSettings;
+ self.loginNameContainer.alpha = settingsMode || mainSite.loginGenerated || [mainSite.loginName length]? 0.7f: 0;
+ self.loginNameField.textColor = [UIColor colorWithHexString:mainSite.loginGenerated? @"5E636D": @"6D5E63"];
+ self.modeButton.alpha = self.transientSite? 0: settingsMode? 0.5f: 0.1f;
+ self.counterLabel.alpha = self.counterButton.alpha = mainSite.type & MPSiteTypeClassGenerated? 0.5f: 0;
+ self.modeButton.selected = settingsMode;
+ self.strengthLabel.gone = !settingsMode;
self.modeScrollView.scrollEnabled = !self.transientSite;
- self.pageControl.alpha = self.transientSite? 0: 1;
[self.modeScrollView setContentOffset:CGPointMake( self.mode * self.modeScrollView.frame.size.width, 0 ) animated:animated];
-
- // Indicator
- if (self.loginModeButton.selected) {
- if ([mainElement.loginName length])
- self.indicatorView.alpha = 0;
- else {
- self.indicatorView.alpha = 1;
- [self.indicatorView removeFromSuperview];
- [self.modeScrollView addSubview:self.indicatorView];
- [self.contentView addConstraintsWithVisualFormat:@"V:[indicator][view]" options:NSLayoutFormatAlignAllCenterX
- metrics:nil views:@{
- @"indicator" : self.indicatorView,
- @"view" : self.mode == MPPasswordCellModeSettings? self.editButton: self.modeButton
- }];
- }
- }
- switch (self.mode) {
- case MPPasswordCellModePassword:
- if (mainElement.type & MPElementTypeClassStored)
- break;
- case MPPasswordCellModeSettings:
- break;
+ if (!settingsMode) {
+ [self.loginNameField resignFirstResponder];
+ [self.passwordField resignFirstResponder];
}
+ if ([[MPiOSAppDelegate get] isPurchased: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
- self.siteNameLabel.text = strl( @"%@ - %@", self.transientSite?: mainElement.name,
- self.transientSite? @"Tap to create": [mainElement.algorithm shortNameOfType:mainElement.type] );
+ self.siteNameLabel.text = strl( @"%@ - %@", self.transientSite?: mainSite.name,
+ self.transientSite? @"Tap to create": [mainSite.algorithm shortNameOfType:mainSite.type] );
// Site Password
- self.passwordField.enabled = NO;
self.passwordField.secureTextEntry = [[MPiOSConfig get].hidePasswords boolValue];
self.passwordField.attributedPlaceholder = stra(
- mainElement.type & MPElementTypeClassStored? strl( @"No password" ):
- mainElement.type & MPElementTypeClassGenerated? strl( @"..." ): @"", @{
+ mainSite.type & MPSiteTypeClassStored? strl( @"No password" ):
+ mainSite.type & MPSiteTypeClassGenerated? strl( @"..." ): @"", @{
NSForegroundColorAttributeName : [UIColor whiteColor]
} );
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
- NSString *password;
+ MPSiteEntity *site = [self siteInContext:context];
+ MPKey *key = [MPiOSAppDelegate get].key;
+ NSString *password, *loginName = [site resolveLoginUsingKey:key];
if (self.transientSite)
- password = [MPAlgorithmDefault generateContentNamed:self.transientSite ofType:
- [[MPiOSAppDelegate get] activeUserInContext:context].defaultType?: MPElementTypeGeneratedLong
- withCounter:1 usingKey:[MPiOSAppDelegate get].key];
+ password = [MPAlgorithmDefault generatePasswordForSiteNamed:self.transientSite ofType:
+ [[MPiOSAppDelegate get] activeUserInContext:context].defaultType?: MPSiteTypeGeneratedLong
+ withCounter:1 usingKey:key];
+ else if (site)
+ password = [site resolvePasswordUsingKey:key];
else
- password = [[self elementInContext:context] resolveContentUsingKey:[MPiOSAppDelegate get].key];
+ return;
TimeToCrack timeToCrack;
NSString *timeToCrackString = nil;
- id algorithm = mainElement.algorithm?: MPAlgorithmDefault;
+ id algorithm = site.algorithm?: MPAlgorithmDefault;
MPAttacker attackHardware = [[MPConfig get].siteAttacker unsignedIntegerValue];
- if ([algorithm timeToCrack:&timeToCrack passwordOfType:[self elementInContext:context].type byAttacker:attackHardware] ||
+ if ([algorithm timeToCrack:&timeToCrack passwordOfType:site.type byAttacker:attackHardware] ||
[algorithm timeToCrack:&timeToCrack passwordString:password byAttacker:attackHardware])
timeToCrackString = NSStringFromTimeToCrack( timeToCrack );
PearlMainQueue( ^{
+ self.loginNameField.text = loginName;
self.passwordField.text = password;
self.strengthLabel.text = timeToCrackString;
+ self.loginNameButton.titleLabel.alpha = [loginName length] || self.loginNameField.enabled? 0: 1;
- if (!self.loginModeButton.selected) {
- if ([password length])
- self.indicatorView.alpha = 0;
- else {
- self.indicatorView.alpha = 1;
- [self.indicatorView removeFromSuperview];
- [self.modeScrollView addSubview:self.indicatorView];
- [self.contentView addConstraintsWithVisualFormat:@"V:[indicator][view]" options:NSLayoutFormatAlignAllCenterX
- metrics:nil views:@{
- @"indicator" : self.indicatorView,
- @"view" : self.mode == MPPasswordCellModeSettings? self.editButton: self.modeButton
- }];
- }
+ if ([password length])
+ self.indicatorView.alpha = 0;
+ else {
+ self.indicatorView.alpha = 1;
+ [self.indicatorView removeFromSuperview];
+ [self.modeScrollView addSubview:self.indicatorView];
+ [self.contentView addConstraintsWithVisualFormat:@"V:[indicator][target]" options:NSLayoutFormatAlignAllCenterX
+ metrics:nil views:@{
+ @"indicator" : self.indicatorView,
+ @"target" : settingsMode? self.editButton: self.modeButton
+ }];
}
} );
}];
// Site Counter
- if ([mainElement isKindOfClass:[MPElementGeneratedEntity class]])
- self.counterLabel.text = strf( @"%lu", (unsigned long)((MPElementGeneratedEntity *)mainElement).counter );
+ if ([mainSite isKindOfClass:[MPGeneratedSiteEntity class]])
+ self.counterLabel.text = strf( @"%lu", (unsigned long)((MPGeneratedSiteEntity *)mainSite).counter );
// Site Login Name
- self.loginNameField.enabled = NO;
- self.loginNameField.text = mainElement.loginName;
- self.loginNameField.attributedPlaceholder = stra( strl( @"Set login name" ), @{
- NSForegroundColorAttributeName : [UIColor whiteColor]
- } );
+ self.loginNameField.enabled = self.passwordField.enabled = //
+ [self.loginNameField isFirstResponder] || [self.passwordField isFirstResponder];
+
+ [self.contentView layoutIfNeeded];
}];
}
-- (void)copyContentOfElement:(MPElementEntity *)element saveInContext:(NSManagedObjectContext *)context {
+- (BOOL)copyContentOfSite:(MPSiteEntity *)site saveInContext:(NSManagedObjectContext *)context {
- // Copy content.
- if (self.loginModeButton.selected) {
- // Login Mode
- inf( @"Copying login for: %@", element.name );
- NSString *loginName = element.loginName;
- if (![loginName length])
- return;
+ inf( @"Copying password for: %@", site.name );
+ NSString *password = [site resolvePasswordUsingKey:[MPAppDelegate_Shared get].key];
+ if (![password length])
+ return NO;
- PearlMainQueue( ^{
- [PearlOverlay showTemporaryOverlayWithTitle:strl( @"Login Name Copied" ) dismissAfter:2];
- [UIPasteboard generalPasteboard].string = loginName;
- } );
+ PearlMainQueue( ^{
+ [PearlOverlay showTemporaryOverlayWithTitle:strl( @"Password Copied" ) dismissAfter:2];
+ [UIPasteboard generalPasteboard].string = password;
+ } );
- [element use];
- [context saveToStore];
- }
- else {
- // Password Mode
- inf( @"Copying password for: %@", element.name );
- NSString *password = [element resolveContentUsingKey:[MPAppDelegate_Shared get].key];
- if (![password length])
- return;
-
- PearlMainQueue( ^{
- [PearlOverlay showTemporaryOverlayWithTitle:strl( @"Password Copied" ) dismissAfter:2];
- [UIPasteboard generalPasteboard].string = password;
- } );
-
- [element use];
- [context saveToStore];
- }
+ [site use];
+ [context saveToStore];
+ return YES;
}
-- (MPElementEntity *)elementInContext:(NSManagedObjectContext *)context {
+- (BOOL)copyLoginOfSite:(MPSiteEntity *)site saveInContext:(NSManagedObjectContext *)context {
- return [MPElementEntity existingObjectWithID:_elementOID inContext:context];
+ inf( @"Copying login for: %@", site.name );
+ NSString *loginName = [site.algorithm resolveLoginForSite:site usingKey:[MPiOSAppDelegate get].key];
+ if (![loginName length])
+ return NO;
+
+ PearlMainQueue( ^{
+ [PearlOverlay showTemporaryOverlayWithTitle:strl( @"Login Name Copied" ) dismissAfter:2];
+ [UIPasteboard generalPasteboard].string = loginName;
+ } );
+
+ [site use];
+ [context saveToStore];
+ return YES;
+}
+
+- (MPSiteEntity *)siteInContext:(NSManagedObjectContext *)context {
+
+ return [MPSiteEntity existingObjectWithID:_siteOID inContext:context];
}
@end
diff --git a/MasterPassword/ObjC/iOS/MPPasswordsViewController.h b/MasterPassword/ObjC/iOS/MPPasswordsViewController.h
index 46eddb1d..e3f51ac7 100644
--- a/MasterPassword/ObjC/iOS/MPPasswordsViewController.h
+++ b/MasterPassword/ObjC/iOS/MPPasswordsViewController.h
@@ -16,7 +16,7 @@
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
-@class MPElementEntity;
+@class MPSiteEntity;
@class MPCoachmark;
@interface MPPasswordsViewController : UIViewController
diff --git a/MasterPassword/ObjC/iOS/MPPasswordsViewController.m b/MasterPassword/ObjC/iOS/MPPasswordsViewController.m
index 344f9691..d3760fd6 100644
--- a/MasterPassword/ObjC/iOS/MPPasswordsViewController.m
+++ b/MasterPassword/ObjC/iOS/MPPasswordsViewController.m
@@ -23,6 +23,7 @@
#import "MPAppDelegate_Key.h"
#import "MPPasswordCell.h"
#import "UICollectionView+PearlReloadFromArray.h"
+#import "MPAnswersViewController.h"
@interface MPPasswordsViewController()
@@ -73,6 +74,18 @@
[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 {
[super viewWillDisappear:animated];
@@ -85,6 +98,9 @@
if ([segue.identifier isEqualToString:@"popdown"])
_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 {
@@ -106,7 +122,7 @@ referenceSizeForHeaderInSection:(NSInteger)section {
sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)collectionViewLayout;
- CGFloat itemWidth = UIEdgeInsetsInsetRect( self.passwordCollectionView.bounds, layout.sectionInset ).size.width;
+ CGFloat itemWidth = UIEdgeInsetsInsetRect( collectionView.bounds, layout.sectionInset ).size.width;
return CGSizeMake( itemWidth, 100 );
}
@@ -119,7 +135,7 @@ referenceSizeForHeaderInSection:(NSInteger)section {
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
- if (![MPiOSAppDelegate get].activeUserOID)
+ if (![MPiOSAppDelegate get].activeUserOID || !_fetchedResultsController)
return 0;
NSUInteger objects = ((id)self.fetchedResultsController.sections[section]).numberOfObjects;
@@ -131,7 +147,7 @@ referenceSizeForHeaderInSection:(NSInteger)section {
MPPasswordCell *cell = [MPPasswordCell dequeueCellFromCollectionView:collectionView indexPath:indexPath];
if (indexPath.item < ((id)self.fetchedResultsController.sections[indexPath.section]).numberOfObjects)
- [cell setElement:[self.fetchedResultsController objectAtIndexPath:indexPath] animated:NO];
+ [cell setSite:[self.fetchedResultsController objectAtIndexPath:indexPath] animated:NO];
else
[cell setTransientSite:self.query animated:NO];
@@ -215,6 +231,7 @@ referenceSizeForHeaderInSection:(NSInteger)section {
if (_passwordsDismissRecognizer)
[self.view removeGestureRecognizer:_passwordsDismissRecognizer];
+ [self updatePasswords];
[UIView animateWithDuration:0.3f animations:^{
self.passwordCollectionView.backgroundColor = _backgroundColor;
}];
@@ -223,6 +240,7 @@ referenceSizeForHeaderInSection:(NSInteger)section {
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
+ searchBar.text = nil;
[searchBar resignFirstResponder];
}
@@ -281,7 +299,7 @@ referenceSizeForHeaderInSection:(NSInteger)section {
_fetchedResultsController = nil;
self.passwordsSearchBar.text = nil;
- [self updatePasswords];
+ [self.passwordCollectionView reloadData];
}],
[[NSNotificationCenter defaultCenter]
addObserverForName:UIApplicationDidBecomeActiveNotification object:nil
@@ -296,8 +314,8 @@ referenceSizeForHeaderInSection:(NSInteger)section {
[[NSNotificationCenter defaultCenter]
addObserverForName:MPCheckConfigNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
- [self updateConfigKey:note.object];
- }],
+ [self updateConfigKey:note.object];
+ }],
];
}
@@ -322,18 +340,19 @@ referenceSizeForHeaderInSection:(NSInteger)section {
}];
if (!_storeChangingObserver)
_storeChangingObserver = [[NSNotificationCenter defaultCenter]
- addObserverForName:USMStoreWillChangeNotification object:nil
- queue:nil usingBlock:^(NSNotification *note) {
- Strongify( self );
- if (self->_mocObserver)
- [[NSNotificationCenter defaultCenter] removeObserver:self->_mocObserver];
- }];
- if (!_storeChangedObserver)
- _storeChangedObserver = [[NSNotificationCenter defaultCenter]
- addObserverForName:USMStoreDidChangeNotification object:nil
+ addObserverForName:NSPersistentStoreCoordinatorStoresWillChangeNotification object:nil
queue:nil usingBlock:^(NSNotification *note) {
Strongify( self );
self->_fetchedResultsController = nil;
+ if (self->_mocObserver)
+ [[NSNotificationCenter defaultCenter] removeObserver:self->_mocObserver];
+ [self.passwordCollectionView reloadData];
+ }];
+ if (!_storeChangedObserver)
+ _storeChangedObserver = [[NSNotificationCenter defaultCenter]
+ addObserverForName:NSPersistentStoreCoordinatorStoresDidChangeNotification object:nil
+ queue:nil usingBlock:^(NSNotification *note) {
+ Strongify( self );
[self updatePasswords];
}];
}
@@ -353,7 +372,7 @@ referenceSizeForHeaderInSection:(NSInteger)section {
if (!key || [key isEqualToString:NSStringFromSelector( @selector( dictationSearch ) )])
self.passwordsSearchBar.keyboardType = [[MPiOSConfig get].dictationSearch boolValue]? UIKeyboardTypeDefault: UIKeyboardTypeURL;
if (!key || [key isEqualToString:NSStringFromSelector( @selector( hidePasswords ) )])
- [self updatePasswords];
+ [self.passwordCollectionView reloadData];
}
- (void)updatePasswords {
@@ -370,9 +389,10 @@ referenceSizeForHeaderInSection:(NSInteger)section {
}
[self.fetchedResultsController.managedObjectContext performBlock:^{
- NSMutableArray *oldSections = [NSMutableArray arrayWithCapacity:[[self.fetchedResultsController sections] count]];
- for (id section in [self.fetchedResultsController sections])
- [oldSections addObject:[section.objects copy]];
+ NSArray *oldSectionInfos = [self.fetchedResultsController sections];
+ NSMutableArray *oldSections = [[NSMutableArray alloc] initWithCapacity:[oldSectionInfos count]];
+ for (id sectionInfo in oldSectionInfos)
+ [oldSections addObject:[sectionInfo.objects copy]];
NSError *error = nil;
self.fetchedResultsController.fetchRequest.predicate =
@@ -380,7 +400,7 @@ referenceSizeForHeaderInSection:(NSInteger)section {
[NSPredicate predicateWithFormat:@"user == %@ AND name BEGINSWITH[cd] %@", activeUserOID, query]:
[NSPredicate predicateWithFormat:@"user == %@", activeUserOID];
if (![self.fetchedResultsController performFetch:&error])
- err( @"Couldn't fetch elements: %@", error );
+ err( @"Couldn't fetch sites: %@", [error fullDescription] );
[self.passwordCollectionView performBatchUpdates:^{
[self fetchedItemsDidUpdate];
@@ -392,10 +412,12 @@ referenceSizeForHeaderInSection:(NSInteger)section {
[self.passwordCollectionView insertSections:[NSIndexSet indexSetWithIndex:section]];
else if (section >= toSections)
[self.passwordCollectionView deleteSections:[NSIndexSet indexSetWithIndex:section]];
- else
+ else if (section < [oldSections count])
[self.passwordCollectionView reloadItemsFromArray:oldSections[section]
toArray:[[self.fetchedResultsController sections][section] objects]
inSection:section];
+ else
+ [self.passwordCollectionView reloadSections:[NSIndexSet indexSetWithIndex:section]];
}
} completion:^(BOOL finished) {
if (finished)
@@ -415,8 +437,9 @@ referenceSizeForHeaderInSection:(NSInteger)section {
- (NSFetchedResultsController *)fetchedResultsController {
if (!_fetchedResultsController) {
+ _showTransientItem = NO;
[MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) {
- NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )];
+ NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
fetchRequest.sortDescriptors = @[
[[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector( lastUsed ) ) ascending:NO]
];
diff --git a/MasterPassword/ObjC/iOS/MPPreferencesViewController.m b/MasterPassword/ObjC/iOS/MPPreferencesViewController.m
index 55dc5354..78214615 100644
--- a/MasterPassword/ObjC/iOS/MPPreferencesViewController.m
+++ b/MasterPassword/ObjC/iOS/MPPreferencesViewController.m
@@ -102,7 +102,7 @@
self.generatedTypeControl.selectedSegmentIndex = -1;
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
- MPElementType defaultType = [[MPiOSAppDelegate get] activeUserInContext:context].defaultType = [self typeForSelectedSegment];
+ MPSiteType defaultType = [[MPiOSAppDelegate get] activeUserInContext:context].defaultType = [self typeForSelectedSegment];
[context saveToStore];
PearlMainQueue( ^{
@@ -179,31 +179,31 @@
return nil;
}
-- (enum MPElementType)typeForSelectedSegment {
+- (enum MPSiteType)typeForSelectedSegment {
NSInteger selectedGeneratedIndex = self.generatedTypeControl.selectedSegmentIndex;
NSInteger selectedStoredIndex = self.storedTypeControl.selectedSegmentIndex;
switch (selectedGeneratedIndex) {
case 0:
- return MPElementTypeGeneratedMaximum;
+ return MPSiteTypeGeneratedMaximum;
case 1:
- return MPElementTypeGeneratedLong;
+ return MPSiteTypeGeneratedLong;
case 2:
- return MPElementTypeGeneratedMedium;
+ return MPSiteTypeGeneratedMedium;
case 3:
- return MPElementTypeGeneratedBasic;
+ return MPSiteTypeGeneratedBasic;
case 4:
- return MPElementTypeGeneratedShort;
+ return MPSiteTypeGeneratedShort;
case 5:
- return MPElementTypeGeneratedPIN;
+ return MPSiteTypeGeneratedPIN;
default:
switch (selectedStoredIndex) {
case 0:
- return MPElementTypeStoredPersonal;
+ return MPSiteTypeStoredPersonal;
case 1:
- return MPElementTypeStoredDevicePrivate;
+ return MPSiteTypeStoredDevicePrivate;
default:
Throw( @"unsupported selected type index: generated=%ld, stored=%ld", (long)selectedGeneratedIndex,
(long)selectedStoredIndex );
@@ -211,32 +211,32 @@
}
}
-- (NSInteger)generatedSegmentIndexForType:(MPElementType)type {
+- (NSInteger)generatedSegmentIndexForType:(MPSiteType)type {
switch (type) {
- case MPElementTypeGeneratedMaximum:
+ case MPSiteTypeGeneratedMaximum:
return 0;
- case MPElementTypeGeneratedLong:
+ case MPSiteTypeGeneratedLong:
return 1;
- case MPElementTypeGeneratedMedium:
+ case MPSiteTypeGeneratedMedium:
return 2;
- case MPElementTypeGeneratedBasic:
+ case MPSiteTypeGeneratedBasic:
return 3;
- case MPElementTypeGeneratedShort:
+ case MPSiteTypeGeneratedShort:
return 4;
- case MPElementTypeGeneratedPIN:
+ case MPSiteTypeGeneratedPIN:
return 5;
default:
return -1;
}
}
-- (NSInteger)storedSegmentIndexForType:(MPElementType)type {
+- (NSInteger)storedSegmentIndexForType:(MPSiteType)type {
switch (type) {
- case MPElementTypeStoredPersonal:
+ case MPSiteTypeStoredPersonal:
return 0;
- case MPElementTypeStoredDevicePrivate:
+ case MPSiteTypeStoredDevicePrivate:
return 1;
default:
return -1;
diff --git a/MasterPassword/ObjC/iOS/MPStoreViewController.h b/MasterPassword/ObjC/iOS/MPStoreViewController.h
new file mode 100644
index 00000000..285a1718
--- /dev/null
+++ b/MasterPassword/ObjC/iOS/MPStoreViewController.h
@@ -0,0 +1,28 @@
+//
+// MPPreferencesViewController.h
+// MasterPassword-iOS
+//
+// Created by Maarten Billemont on 04/06/12.
+// Copyright (c) 2012 Lyndir. All rights reserved.
+//
+
+#import
+
+@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;
+
+@end
+
+@interface MPStoreProductCell : UITableViewCell
+
+@property(nonatomic) IBOutlet UILabel *priceLabel;
+@property(nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;
+@property(nonatomic) IBOutlet UIView *purchasedIndicator;
+
+@end
diff --git a/MasterPassword/ObjC/iOS/MPStoreViewController.m b/MasterPassword/ObjC/iOS/MPStoreViewController.m
new file mode 100644
index 00000000..7381acf5
--- /dev/null
+++ b/MasterPassword/ObjC/iOS/MPStoreViewController.m
@@ -0,0 +1,220 @@
+//
+// 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"
+#import
+
+@interface MPStoreViewController()
+
+@property(nonatomic, strong) NSNumberFormatter *currencyFormatter;
+
+@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];
+ }
+ }];
+
+ [[MPiOSAppDelegate get] observeKeyPath:@"products" withBlock:^(id from, id to, NSKeyValueChange cause, id _self) {
+ if (NSNullToNil( to ))
+ PearlMainQueue( ^{
+ [self updateWithProducts:to];
+ } );
+ }];
+ [[MPiOSAppDelegate get] observeKeyPath:@"paymentTransactions" withBlock:^(id from, id to, NSKeyValueChange cause, id _self) {
+ if (NSNullToNil( to ))
+ PearlMainQueue( ^{
+ [self updateWithTransactions:to];
+ } );
+ }];
+ [[NSNotificationCenter defaultCenter] addObserverForName:NSUserDefaultsDidChangeNotification object:nil
+ queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
+ [self updateWithProducts:[MPiOSAppDelegate get].products];
+ }];
+
+ [[MPiOSAppDelegate get] updateProducts];
+}
+
+#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] isPurchased:[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];
+
+ [tableView deselectRowAtIndexPath:indexPath animated:YES];
+}
+
+#pragma mark - Actions
+
+- (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 - Private
+
+- (SKProduct *)productForCell:(MPStoreProductCell *)cell {
+
+ for (SKProduct *product in [MPiOSAppDelegate get].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;
+
+ return nil;
+}
+
+- (void)updateWithProducts:(NSArray *)products {
+
+ NSMutableArray *showCells = [NSMutableArray array];
+ NSMutableArray *hideCells = [NSMutableArray array];
+ [hideCells addObjectsFromArray:self.allCellsBySection[0]];
+
+ for (SKProduct *product in products) {
+ [self showCellForProductWithIdentifier:MPProductGenerateLogins ifProduct:product showingCells:showCells];
+ [self showCellForProductWithIdentifier:MPProductGenerateAnswers 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)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] isPurchased:productIdentifier];
+ cell.priceLabel.text = purchased? @"": [self.currencyFormatter stringFromNumber:product.price];
+ cell.purchasedIndicator.alpha = purchased? 1: 0;
+}
+
+- (void)updateWithTransactions:(NSArray *)transactions {
+
+ for (SKPaymentTransaction *transaction in transactions) {
+ MPStoreProductCell *cell = [self cellForProductIdentifier:transaction.payment.productIdentifier];
+ if (!cell)
+ continue;
+
+ 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;
+ }
+ }
+}
+
+@end
+
+@implementation MPStoreProductCell
+@end
diff --git a/MasterPassword/ObjC/iOS/MPTypeViewController.h b/MasterPassword/ObjC/iOS/MPTypeViewController.h
index a41d4801..9de956d8 100644
--- a/MasterPassword/ObjC/iOS/MPTypeViewController.h
+++ b/MasterPassword/ObjC/iOS/MPTypeViewController.h
@@ -13,11 +13,11 @@
@protocol MPTypeDelegate
@required
-- (void)didSelectType:(MPElementType)type;
-- (MPElementType)selectedType;
+- (void)didSelectType:(MPSiteType)type;
+- (MPSiteType)selectedType;
@optional
-- (MPElementEntity *)selectedElement;
+- (MPSiteEntity *)selectedSite;
@end
diff --git a/MasterPassword/ObjC/iOS/MPTypeViewController.m b/MasterPassword/ObjC/iOS/MPTypeViewController.m
index 5f5a7fe2..9a48129f 100644
--- a/MasterPassword/ObjC/iOS/MPTypeViewController.m
+++ b/MasterPassword/ObjC/iOS/MPTypeViewController.m
@@ -12,7 +12,7 @@
@interface MPTypeViewController()
-- (MPElementType)typeAtIndexPath:(NSIndexPath *)indexPath;
+- (MPSiteType)typeAtIndexPath:(NSIndexPath *)indexPath;
@end
@@ -63,25 +63,25 @@
UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
- MPElementEntity *selectedElement = nil;
- if ([self.delegate respondsToSelector:@selector(selectedElement)])
- selectedElement = [self.delegate selectedElement];
+ MPSiteEntity *selectedSite = nil;
+ if ([self.delegate respondsToSelector:@selector( selectedSite )])
+ selectedSite = [self.delegate selectedSite];
- MPElementType cellType = [self typeAtIndexPath:indexPath];
- MPElementType selectedType = selectedElement? selectedElement.type: [self.delegate selectedType];
+ MPSiteType cellType = [self typeAtIndexPath:indexPath];
+ MPSiteType selectedType = selectedSite? selectedSite.type: [self.delegate selectedType];
cell.selected = (selectedType == cellType);
- if (cellType != (MPElementType)NSNotFound && cellType & MPElementTypeClassGenerated) {
+ if (cellType != (MPSiteType)NSNotFound && cellType & MPSiteTypeClassGenerated) {
[(UITextField *)[cell viewWithTag:2] setText:@"..."];
- NSString *name = selectedElement.name;
+ NSString *name = selectedSite.name;
NSUInteger counter = 0;
- if ([selectedElement isKindOfClass:[MPElementGeneratedEntity class]])
- counter = ((MPElementGeneratedEntity *)selectedElement).counter;
+ if ([selectedSite isKindOfClass:[MPGeneratedSiteEntity class]])
+ counter = ((MPGeneratedSiteEntity *)selectedSite).counter;
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0 ), ^{
- NSString *typeContent = [MPAlgorithmDefault generateContentNamed:name ofType:cellType
- withCounter:counter usingKey:[MPiOSAppDelegate get].key];
+ NSString *typeContent = [MPAlgorithmDefault generatePasswordForSiteNamed:name ofType:cellType
+ withCounter:counter usingKey:[MPiOSAppDelegate get].key];
dispatch_async( dispatch_get_main_queue(), ^{
[(UITextField *)[[tableView cellForRowAtIndexPath:indexPath] viewWithTag:2] setText:typeContent];
@@ -96,8 +96,8 @@
NSAssert(self.navigationController.topViewController == self, @"Not the currently active navigation item.");
- MPElementType type = [self typeAtIndexPath:indexPath];
- if (type == (MPElementType)NSNotFound)
+ MPSiteType type = [self typeAtIndexPath:indexPath];
+ if (type == (MPSiteType)NSNotFound)
// Selected a non-type row.
return;
@@ -105,31 +105,31 @@
[self.navigationController popViewControllerAnimated:YES];
}
-- (MPElementType)typeAtIndexPath:(NSIndexPath *)indexPath {
+- (MPSiteType)typeAtIndexPath:(NSIndexPath *)indexPath {
switch (indexPath.section) {
case 0: {
// Generated
switch (indexPath.row) {
case 0:
- return (MPElementType)NSNotFound;
+ return (MPSiteType)NSNotFound;
case 1:
- return MPElementTypeGeneratedMaximum;
+ return MPSiteTypeGeneratedMaximum;
case 2:
- return MPElementTypeGeneratedLong;
+ return MPSiteTypeGeneratedLong;
case 3:
- return MPElementTypeGeneratedMedium;
+ return MPSiteTypeGeneratedMedium;
case 4:
- return MPElementTypeGeneratedBasic;
+ return MPSiteTypeGeneratedBasic;
case 5:
- return MPElementTypeGeneratedShort;
+ return MPSiteTypeGeneratedShort;
case 6:
- return MPElementTypeGeneratedPIN;
+ return MPSiteTypeGeneratedPIN;
case 7:
- return (MPElementType)NSNotFound;
+ return (MPSiteType)NSNotFound;
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
switch (indexPath.row) {
case 0:
- return (MPElementType)NSNotFound;
+ return (MPSiteType)NSNotFound;
case 1:
- return MPElementTypeStoredPersonal;
+ return MPSiteTypeStoredPersonal;
case 2:
- return MPElementTypeStoredDevicePrivate;
+ return MPSiteTypeStoredDevicePrivate;
case 3:
- return (MPElementType)NSNotFound;
+ return (MPSiteType)NSNotFound;
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:
- Throw(@"Unsupported section: %ld, when selecting element type.", (long)indexPath.section);
+ Throw(@"Unsupported section: %ld, when selecting site type.", (long)indexPath.section);
}
}
diff --git a/MasterPassword/ObjC/iOS/MPUsersViewController.h b/MasterPassword/ObjC/iOS/MPUsersViewController.h
index 5e2118b1..dac5b01a 100644
--- a/MasterPassword/ObjC/iOS/MPUsersViewController.h
+++ b/MasterPassword/ObjC/iOS/MPUsersViewController.h
@@ -19,7 +19,6 @@
@interface MPUsersViewController : UIViewController
-@property (strong, nonatomic) IBOutlet UINavigationBar *navigationBar;
@property(weak, nonatomic) IBOutlet UIView *userSelectionContainer;
@property(weak, nonatomic) IBOutlet UIButton *marqueeButton;
@property(weak, nonatomic) IBOutlet UIView *gitTipTip;
@@ -32,7 +31,6 @@
@property(weak, nonatomic) IBOutlet UIView *footerContainer;
@property(weak, nonatomic) IBOutlet UIActivityIndicatorView *storeLoadingActivity;
@property(weak, nonatomic) IBOutlet UICollectionView *avatarCollectionView;
-@property (strong, nonatomic) IBOutlet NSLayoutConstraint *navigationBarToTopConstraint;
@property (strong, nonatomic) IBOutlet UIButton *nextAvatarButton;
@property (strong, nonatomic) IBOutlet UIButton *previousAvatarButton;
diff --git a/MasterPassword/ObjC/iOS/MPUsersViewController.m b/MasterPassword/ObjC/iOS/MPUsersViewController.m
index 94e86ef4..bd5a06fe 100644
--- a/MasterPassword/ObjC/iOS/MPUsersViewController.m
+++ b/MasterPassword/ObjC/iOS/MPUsersViewController.m
@@ -1,12 +1,12 @@
/**
- * Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
- *
- * See the enclosed file LICENSE for license information (LGPLv3). If you did
- * not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
- *
- * @author Maarten Billemont
- * @license http://www.gnu.org/licenses/lgpl-3.0.txt
- */
+* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
+*
+* See the enclosed file LICENSE for license information (LGPLv3). If you did
+* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
+*
+* @author Maarten Billemont
+* @license http://www.gnu.org/licenses/lgpl-3.0.txt
+*/
//
// MPCombinedViewController.h
@@ -24,8 +24,9 @@
#import "MPAppDelegate_Key.h"
#import "PearlSizedTextView.h"
#import "MPWebViewController.h"
+#import "UIView+FontScale.h"
-typedef NS_ENUM(NSUInteger, MPActiveUserState) {
+typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
/** The users are all inactive */
MPActiveUserStateNone,
/** The selected user is activated and being logged in with */
@@ -72,7 +73,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
self.view.backgroundColor = [UIColor clearColor];
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];
}
@@ -82,15 +83,6 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
[super viewWillAppear:animated];
self.userSelectionContainer.alpha = 0;
-
- [self observeStore];
- [self registerObservers];
- [self reloadUsers];
-
- [self.marqueeTipTimer invalidate];
- self.marqueeTipTimer = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector(firedMarqueeTimer:)
- userInfo:nil repeats:YES];
- [self firedMarqueeTimer:nil];
}
- (void)viewWillDisappear:(BOOL)animated {
@@ -103,9 +95,29 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
[self.marqueeTipTimer invalidate];
}
-- (void)viewDidLayoutSubviews {
+- (void)viewDidAppear:(BOOL)animated {
- [super viewDidLayoutSubviews];
+ [super viewDidAppear:animated];
+
+ [self observeStore];
+ [self registerObservers];
+ [self reloadUsers];
+
+ [self.marqueeTipTimer invalidate];
+ self.marqueeTipTimer = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector( firedMarqueeTimer: )
+ userInfo:nil repeats:YES];
+ [self firedMarqueeTimer:nil];
+}
+
+- (void)viewWillLayoutSubviews {
+
+ [self.avatarCollectionView.collectionViewLayout invalidateLayout];
+ [super viewWillLayoutSubviews];
+}
+
+- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
+
+ [super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
[self.avatarCollectionView.collectionViewLayout invalidateLayout];
}
@@ -257,15 +269,25 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
#pragma mark - UICollectionViewDelegateFlowLayout
+- (CGSize) collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout
+referenceSizeForHeaderInSection:(NSInteger)section {
+
+ CGSize parentSize = self.avatarCollectionView.bounds.size;
+ return CGSizeMake( parentSize.width / 4, parentSize.height );
+}
+
+- (CGSize) collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout
+referenceSizeForFooterInSection:(NSInteger)section {
+
+ CGSize parentSize = self.avatarCollectionView.bounds.size;
+ return CGSizeMake( parentSize.width / 4, parentSize.height );
+}
+
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
- if (collectionView == self.avatarCollectionView) {
- CGSize parentSize = self.avatarCollectionView.bounds.size;
- return CGSizeMake( parentSize.width / 2, parentSize.height );
- }
-
- Throw(@"unexpected collection view: %@", collectionView);
+ CGSize parentSize = self.avatarCollectionView.bounds.size;
+ return CGSizeMake( parentSize.width / 2, parentSize.height );
}
#pragma mark - UICollectionViewDataSource
@@ -275,14 +297,15 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
if (collectionView == self.avatarCollectionView)
return [self.userIDs count] + 1;
- Throw(@"unexpected collection view: %@", collectionView);
+ Throw( @"unexpected collection view: %@", collectionView );
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
if (collectionView == self.avatarCollectionView) {
MPAvatarCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:[MPAvatarCell reuseIdentifier] forIndexPath:indexPath];
- [cell addGestureRecognizer:[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(didLongPress:)]];
+ cell.contentView.frame = cell.bounds;
+ [cell addGestureRecognizer:[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector( didLongPress: )]];
[self updateModeForAvatar:cell atIndexPath:indexPath animated:NO];
[self updateVisibilityForAvatar:cell atIndexPath:indexPath animated:NO];
@@ -290,7 +313,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
MPUserEntity *user = [self userForIndexPath:indexPath inContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]
isNew:&isNew];
if (isNew)
- // New User
+ // New User
cell.avatar = MPAvatarAdd;
else {
// Existing User
@@ -301,7 +324,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
return cell;
}
- Throw(@"unexpected collection view: %@", collectionView);
+ Throw( @"unexpected collection view: %@", collectionView );
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
@@ -356,7 +379,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
if ([recognizer.view isKindOfClass:[MPAvatarCell class]]) {
if (recognizer.state != UIGestureRecognizerStateBegan)
- // Don't show the action menu unless the state is Began.
+ // Don't show the action menu unless the state is Began.
return;
MPAvatarCell *avatarCell = (MPAvatarCell *)recognizer.view;
@@ -371,40 +394,40 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
[PearlSheet showSheetWithTitle:user.name
viewStyle:UIActionSheetStyleBlackTranslucent
initSheet:nil tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) {
- if (buttonIndex == [sheet cancelButtonIndex])
- return;
-
- if (buttonIndex == [sheet destructiveButtonIndex]) {
- // Delete User
- [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
- MPUserEntity *user_ = [MPUserEntity existingObjectWithID:userID inContext:context];
- if (!user_)
+ if (buttonIndex == [sheet cancelButtonIndex])
return;
- [context deleteObject:user_];
- [context saveToStore];
- [self reloadUsers]; // I do NOT understand why our ObjectsDidChangeNotification isn't firing on saveToStore.
- }];
- return;
- }
+ if (buttonIndex == [sheet destructiveButtonIndex]) {
+ // Delete User
+ [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
+ MPUserEntity *user_ = [MPUserEntity existingObjectWithID:userID inContext:context];
+ if (!user_)
+ return;
- if (buttonIndex == [sheet firstOtherButtonIndex])
- // Reset Password
- [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
- MPUserEntity *user_ = [MPUserEntity existingObjectWithID:userID inContext:context];
- if (!user_)
+ [context deleteObject:user_];
+ [context saveToStore];
+ [self reloadUsers]; // I do NOT understand why our ObjectsDidChangeNotification isn't firing on saveToStore.
+ }];
return;
+ }
- [[MPiOSAppDelegate get] changeMasterPasswordFor:user_ saveInContext:context didResetBlock:^{
- PearlMainQueue( ^{
- NSIndexPath *avatarIndexPath = [self.avatarCollectionView indexPathForCell:avatarCell];
- [self.avatarCollectionView selectItemAtIndexPath:avatarIndexPath animated:NO
- scrollPosition:UICollectionViewScrollPositionNone];
- [self collectionView:self.avatarCollectionView didSelectItemAtIndexPath:avatarIndexPath];
- } );
- }];
- }];
- } cancelTitle:[PearlStrings get].commonButtonCancel
+ if (buttonIndex == [sheet firstOtherButtonIndex])
+ // Reset Password
+ [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
+ MPUserEntity *user_ = [MPUserEntity existingObjectWithID:userID inContext:context];
+ if (!user_)
+ return;
+
+ [[MPiOSAppDelegate get] changeMasterPasswordFor:user_ saveInContext:context didResetBlock:^{
+ PearlMainQueue( ^{
+ NSIndexPath *avatarIndexPath = [self.avatarCollectionView indexPathForCell:avatarCell];
+ [self.avatarCollectionView selectItemAtIndexPath:avatarIndexPath animated:NO
+ scrollPosition:UICollectionViewScrollPositionNone];
+ [self collectionView:self.avatarCollectionView didSelectItemAtIndexPath:avatarIndexPath];
+ } );
+ }];
+ }];
+ } cancelTitle:[PearlStrings get].commonButtonCancel
destructiveTitle:@"Delete User" otherTitles:@"Reset Password", nil];
}
}
@@ -420,7 +443,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
CGPointPlusCGPoint( *targetContentOffset, offsetToCenter )];
CGPoint targetCenter = [self.avatarCollectionView layoutAttributesForItemAtIndexPath:avatarIndexPath].center;
*targetContentOffset = CGPointMinusCGPoint( targetCenter, offsetToCenter );
- NSAssert([self.avatarCollectionView indexPathForItemAtPoint:targetCenter].item == avatarIndexPath.item, @"should be same item");
+ NSAssert( [self.avatarCollectionView indexPathForItemAtPoint:targetCenter].item == avatarIndexPath.item, @"should be same item" );
}
}
@@ -501,19 +524,14 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
return [MPUserEntity existingObjectWithID:self.userIDs[indexPath.item] inContext:context];
}
-- (void)updateAvatars {
+- (void)updateAvatarVisibility {
self.previousAvatarButton.alpha = 0;
self.nextAvatarButton.alpha = 0;
- for (NSIndexPath *indexPath in self.avatarCollectionView.indexPathsForVisibleItems)
- [self updateAvatarAtIndexPath:indexPath];
-}
-
-- (void)updateAvatarAtIndexPath:(NSIndexPath *)indexPath {
-
- MPAvatarCell *cell = (MPAvatarCell *)[self.avatarCollectionView cellForItemAtIndexPath:indexPath];
- [self updateModeForAvatar:cell atIndexPath:indexPath animated:NO];
- [self updateVisibilityForAvatar:cell atIndexPath:indexPath animated:NO];
+ for (NSIndexPath *indexPath in self.avatarCollectionView.indexPathsForVisibleItems) {
+ MPAvatarCell *cell = (MPAvatarCell *)[self.avatarCollectionView cellForItemAtIndexPath:indexPath];
+ [self updateVisibilityForAvatar:cell atIndexPath:indexPath animated:NO];
+ }
}
- (void)updateModeForAvatar:(MPAvatarCell *)avatarCell atIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated {
@@ -550,7 +568,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
self.avatarCollectionView.contentOffset.x;
CGFloat max = self.avatarCollectionView.bounds.size.width;
- CGFloat visibility = MAX(0, MIN( 1, 1 - ABS( current / (max / 2) - 1 ) ));
+ CGFloat visibility = MAX( 0, MIN( 1, 1 - ABS( current / (max / 2) - 1 ) ) );
[cell setVisibility:visibility animated:animated];
if (cell.newUser) {
@@ -559,7 +577,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
}
}
-- (void)afterUpdatesMainQueue:(void (^)(void))block {
+- (void)afterUpdatesMainQueue:(void ( ^ )(void))block {
[_afterUpdates addOperationWithBlock:^{
PearlMainQueue( block );
@@ -571,32 +589,32 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
if ([_notificationObservers count])
return;
- Weakify(self);
+ Weakify( self );
_notificationObservers = @[
[[NSNotificationCenter defaultCenter]
addObserverForName:UIApplicationWillResignActiveNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
- Strongify(self);
+ Strongify( self );
// [self emergencyCloseAnimated:NO];
- self.userSelectionContainer.alpha = 0;
- }],
+ self.userSelectionContainer.alpha = 0;
+ }],
[[NSNotificationCenter defaultCenter]
addObserverForName:UIApplicationDidBecomeActiveNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
- Strongify(self);
+ Strongify( self );
- [self reloadUsers];
+ [self reloadUsers];
- [UIView animateWithDuration:1 animations:^{
- self.userSelectionContainer.alpha = 1;
- }];
- }],
+ [UIView animateWithDuration:1 animations:^{
+ self.userSelectionContainer.alpha = 1;
+ }];
+ }],
];
[self observeKeyPath:@"avatarCollectionView.contentOffset" withBlock:
^(id from, id to, NSKeyValueChange cause, MPUsersViewController *_self) {
- [_self updateAvatars];
+ [_self updateAvatarVisibility];
}];
}
@@ -611,7 +629,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
- (void)observeStore {
- Weakify(self);
+ Weakify( self );
NSManagedObjectContext *mainContext = [MPiOSAppDelegate managedObjectContextForMainThreadIfReady];
[UIView animateWithDuration:0.3f animations:^{
@@ -626,10 +644,10 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
_mocObserver = [[NSNotificationCenter defaultCenter]
addObserverForName:NSManagedObjectContextObjectsDidChangeNotification object:mainContext
queue:nil usingBlock:^(NSNotification *note) {
- Strongify(self);
+ Strongify( self );
NSSet *insertedObjects = note.userInfo[NSInsertedObjectsKey];
NSSet *deletedObjects = note.userInfo[NSDeletedObjectsKey];
- if ([[NSSetUnion(insertedObjects, deletedObjects)
+ if ([[NSSetUnion( insertedObjects, deletedObjects )
filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
return [evaluatedObject isKindOfClass:[MPUserEntity class]];
}]] count])
@@ -637,17 +655,18 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
}];
if (!_storeChangingObserver)
_storeChangingObserver = [[NSNotificationCenter defaultCenter]
- addObserverForName:USMStoreWillChangeNotification object:nil
+ addObserverForName:NSPersistentStoreCoordinatorStoresWillChangeNotification object:nil
queue:nil usingBlock:^(NSNotification *note) {
- Strongify(self);
+ Strongify( self );
if (self->_mocObserver)
[[NSNotificationCenter defaultCenter] removeObserver:self->_mocObserver];
+ self.userIDs = nil;
}];
if (!_storeChangedObserver)
_storeChangedObserver = [[NSNotificationCenter defaultCenter]
- addObserverForName:USMStoreDidChangeNotification object:nil
+ addObserverForName:NSPersistentStoreCoordinatorStoresDidChangeNotification object:nil
queue:nil usingBlock:^(NSNotification *note) {
- Strongify(self);
+ Strongify( self );
[self reloadUsers];
}];
}
@@ -666,15 +685,15 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
[self afterUpdatesMainQueue:^{
[self observeStore];
- [MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) {
+ if (![MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) {
NSError *error = nil;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
fetchRequest.sortDescriptors = @[
- [NSSortDescriptor sortDescriptorWithKey:NSStringFromSelector( @selector(lastUsed) ) ascending:NO]
+ [NSSortDescriptor sortDescriptorWithKey:NSStringFromSelector( @selector( lastUsed ) ) ascending:NO]
];
NSArray *users = [mainContext executeFetchRequest:fetchRequest error:&error];
if (!users) {
- err(@"Failed to load users: %@", error);
+ err( @"Failed to load users: %@", [error fullDescription] );
self.userIDs = nil;
}
@@ -682,7 +701,8 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
for (MPUserEntity *user in users)
[userIDs addObject:user.objectID];
self.userIDs = userIDs;
- }];
+ }])
+ self.userIDs = nil;
}];
}
@@ -741,24 +761,11 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
return;
}
+ // Set the entry container's contents.
[_afterUpdates setSuspended:YES];
__block BOOL requestFirstResponder = NO;
+ [self.view layoutIfNeeded];
[UIView animateWithDuration:animated? 0.4f: 0 animations:^{
- MPAvatarCell *selectedAvatar = [self selectedAvatar];
-
- // Set avatar modes.
- for (NSUInteger item = 0; item < [self.avatarCollectionView numberOfItemsInSection:0]; ++item) {
- NSIndexPath *indexPath = [NSIndexPath indexPathForItem:item inSection:0];
- MPAvatarCell *avatarCell = (MPAvatarCell *)[self.avatarCollectionView cellForItemAtIndexPath:indexPath];
- [self updateModeForAvatar:avatarCell atIndexPath:indexPath animated:animated];
- [self updateVisibilityForAvatar:avatarCell atIndexPath:indexPath animated:animated];
-
- if (selectedAvatar && avatarCell == selectedAvatar)
- [self.avatarCollectionView scrollToItemAtIndexPath:indexPath
- atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
- }
-
- // Set the entry container's contents.
switch (activeUserState) {
case MPActiveUserStateNone:
break;
@@ -798,7 +805,6 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
// Manage the entry container depending on whether a user is activate or not.
switch (activeUserState) {
case MPActiveUserStateNone: {
- [self.navigationBarToTopConstraint updatePriority:UILayoutPriorityDefaultHigh];
self.avatarCollectionView.scrollEnabled = YES;
self.entryContainer.alpha = 0;
self.footerContainer.alpha = 1;
@@ -808,7 +814,6 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
case MPActiveUserStateUserName:
case MPActiveUserStateMasterPasswordChoice:
case MPActiveUserStateMasterPasswordConfirmation: {
- [self.navigationBarToTopConstraint updatePriority:UILayoutPriorityDefaultHigh];
self.avatarCollectionView.scrollEnabled = NO;
self.entryContainer.alpha = 1;
self.footerContainer.alpha = 1;
@@ -816,7 +821,6 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
break;
}
case MPActiveUserStateMinimized: {
- [self.navigationBarToTopConstraint updatePriority:1];
self.avatarCollectionView.scrollEnabled = NO;
self.entryContainer.alpha = 0;
self.footerContainer.alpha = 0;
@@ -832,6 +836,18 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
[self.entryField resignFirstResponder];
if (requestFirstResponder)
[self.entryField becomeFirstResponder];
+
+ // Set avatar modes.
+ MPAvatarCell *selectedAvatar = [self selectedAvatar];
+ for (NSIndexPath *indexPath in [self.avatarCollectionView indexPathsForVisibleItems]) {
+ MPAvatarCell *avatarCell = (MPAvatarCell *)[self.avatarCollectionView cellForItemAtIndexPath:indexPath];
+ [self updateModeForAvatar:avatarCell atIndexPath:indexPath animated:animated];
+ [self updateVisibilityForAvatar:avatarCell atIndexPath:indexPath animated:animated];
+
+ if (selectedAvatar && avatarCell == selectedAvatar)
+ [self.avatarCollectionView scrollToItemAtIndexPath:indexPath
+ atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
+ }
}
#pragma mark - Actions
diff --git a/MasterPassword/ObjC/iOS/MPiOSAppDelegate.h b/MasterPassword/ObjC/iOS/MPiOSAppDelegate.h
index abeb98b5..617fe7e9 100644
--- a/MasterPassword/ObjC/iOS/MPiOSAppDelegate.h
+++ b/MasterPassword/ObjC/iOS/MPiOSAppDelegate.h
@@ -7,7 +7,6 @@
//
#import
-#import
#import "MPAppDelegate_Shared.h"
diff --git a/MasterPassword/ObjC/iOS/MPiOSAppDelegate.m b/MasterPassword/ObjC/iOS/MPiOSAppDelegate.m
index c609b0c8..dcfb8ec6 100644
--- a/MasterPassword/ObjC/iOS/MPiOSAppDelegate.m
+++ b/MasterPassword/ObjC/iOS/MPiOSAppDelegate.m
@@ -11,26 +11,25 @@
#import "MPAppDelegate_Store.h"
#import "IASKSettingsReader.h"
-@interface MPiOSAppDelegate()
+@interface MPiOSAppDelegate()
+
+@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
@implementation MPiOSAppDelegate
+ (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;
#ifdef DEBUG
[PearlLogger get].printLevel = PearlLogLevelDebug; //Trace;
#else
[PearlLogger get].printLevel = [[MPiOSConfig get].traceMode boolValue]? PearlLogLevelDebug: PearlLogLevelInfo;
#endif
- }
+ } );
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
@@ -168,7 +167,7 @@
NSData *importedSitesData = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url]
returningResponse:&response error:&error];
if (error)
- err( @"While reading imported sites from %@: %@", url, error );
+ err( @"While reading imported sites from %@: %@", url, [error fullDescription] );
if (!importedSitesData)
return;
@@ -339,6 +338,26 @@
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 {
[PearlAlert showAlertWithTitle:@"Exporting Your Sites"
@@ -409,12 +428,38 @@
NSDateFormatter *exportDateFormatter = [NSDateFormatter new];
[exportDateFormatter setDateFormat:@"yyyy'-'MM'-'dd"];
- [PearlEMail sendEMailTo:nil fromVC:viewController subject:@"Master Password Export" body:message
- attachments:[[PearlEMailAttachment alloc] initWithContent:[exportedSites dataUsingEncoding:NSUTF8StringEncoding]
- mimeType:@"text/plain" fileName:
- strf( @"%@ (%@).mpsites", [self activeUserForMainThread].name,
- [exportDateFormatter stringFromDate:[NSDate date]] )],
- nil];
+ NSString *exportFileName = strf( @"%@ (%@).mpsites",
+ [self activeUserForMainThread].name, [exportDateFormatter stringFromDate:[NSDate date]] );
+ [PearlSheet showSheetWithTitle:@"Export Destination" viewStyle:UIActionSheetStyleBlackTranslucent initSheet:nil
+ tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) {
+ if (buttonIndex == [sheet cancelButtonIndex])
+ 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 {
@@ -446,6 +491,13 @@
otherTitles:[PearlStrings get].commonButtonContinue, nil];
}
+#pragma mark - UIDocumentInteractionControllerDelegate
+
+- (void)documentInteractionController:(UIDocumentInteractionController *)controller didEndSendingToApplication:(NSString *)application {
+
+// self.interactionController = nil;
+}
+
#pragma mark - PearlConfigDelegate
- (void)didUpdateConfigForKey:(SEL)configKey fromValue:(id)value {
@@ -455,73 +507,6 @@
- (void)updateConfigKey:(NSString *)key {
- // iCloud enabled / disabled
- BOOL iCloudEnabled = [[MPiOSConfig get].iCloudEnabled boolValue];
- BOOL cloudEnabled = self.storeManager.cloudEnabled;
- if (iCloudEnabled != cloudEnabled) {
- if ([[MPiOSConfig get].iCloudEnabled boolValue])
- [self.storeManager setCloudEnabledAndOverwriteCloudWithLocalIfConfirmed:^(void (^setConfirmationAnswer)(BOOL answer)) {
- __block NSUInteger siteCount = NSNotFound;
- [MPAppDelegate_Shared managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) {
- NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )];
- NSError *error = nil;
- if ((siteCount = [context countForFetchRequest:fetchRequest error:&error]) == NSNotFound) {
- wrn( @"Couldn't count current sites: %@", error );
- return;
- }
- }];
-
- // If we currently have no sites, don't bother asking to copy them.
- if (siteCount == 0) {
- setConfirmationAnswer( NO );
- return;
- }
-
- // The current store has sites, ask the user if he wants to copy them to the cloud
- [PearlAlert showAlertWithTitle:@"Copy Sites To iCloud?"
- message:@"You can either switch to your old iCloud sites "
- @"or overwrite them with your current sites."
- viewStyle:UIAlertViewStyleDefault initAlert:nil
- tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
- if (buttonIndex == [alert cancelButtonIndex])
- setConfirmationAnswer( NO );
- if (buttonIndex == [alert firstOtherButtonIndex])
- setConfirmationAnswer( YES );
- }
- cancelTitle:@"Use Old" otherTitles:@"Overwrite", nil];
- }];
- else
- [self.storeManager setCloudDisabledAndOverwriteLocalWithCloudIfConfirmed:^(void (^setConfirmationAnswer)(BOOL answer)) {
- __block NSUInteger siteCount = NSNotFound;
- [MPAppDelegate_Shared managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) {
- NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )];
- NSError *error = nil;
- if ((siteCount = [context countForFetchRequest:fetchRequest error:&error]) == NSNotFound) {
- wrn( @"Couldn't count current sites: %@", error );
- return;
- }
- }];
-
- // If we currently have no sites, don't bother asking to copy them.
- if (siteCount == 0) {
- setConfirmationAnswer( NO );
- return;
- }
-
- [PearlAlert showAlertWithTitle:@"Copy iCloud Sites?"
- message:@"You can either switch to the old sites on your device "
- @"or overwrite them with your current iCloud sites."
- viewStyle:UIAlertViewStyleDefault initAlert:nil
- tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
- if (buttonIndex == [alert cancelButtonIndex])
- setConfirmationAnswer( NO );
- if (buttonIndex == [alert firstOtherButtonIndex])
- setConfirmationAnswer( YES );
- }
- cancelTitle:@"Use Old" otherTitles:@"Overwrite", nil];
- }];
- }
-
// Trace mode
[PearlLogger get].historyLevel = [[MPiOSConfig get].traceMode boolValue]? PearlLogLevelTrace: PearlLogLevelInfo;
@@ -532,21 +517,18 @@
#ifdef CRASHLYTICS
[[Crashlytics sharedInstance] setBoolValue:[[MPConfig get].rememberLogin boolValue] forKey:@"rememberLogin"];
- [[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].iCloudEnabled boolValue] forKey:@"iCloudEnabled"];
[[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].sendInfo boolValue] forKey:@"sendInfo"];
[[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].helpHidden boolValue] forKey:@"helpHidden"];
[[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].showSetup boolValue] forKey:@"showQuickStart"];
[[Crashlytics sharedInstance] setBoolValue:[[PearlConfig get].firstRun boolValue] forKey:@"firstRun"];
[[Crashlytics sharedInstance] setIntValue:[[PearlConfig get].launchCount intValue] forKey:@"launchCount"];
[[Crashlytics sharedInstance] setBoolValue:[[PearlConfig get].askForReviews boolValue] forKey:@"askForReviews"];
- [[Crashlytics sharedInstance]
- setIntValue:[[PearlConfig get].reviewAfterLaunches intValue] forKey:@"reviewAfterLaunches"];
+ [[Crashlytics sharedInstance] setIntValue:[[PearlConfig get].reviewAfterLaunches intValue] forKey:@"reviewAfterLaunches"];
[[Crashlytics sharedInstance] setObjectValue:[PearlConfig get].reviewedVersion forKey:@"reviewedVersion"];
#endif
MPCheckpoint( MPCheckpointConfig, @{
@"rememberLogin" : @([[MPConfig get].rememberLogin boolValue]),
- @"iCloudEnabled" : @([[MPiOSConfig get].iCloudEnabled boolValue]),
@"sendInfo" : @([[MPiOSConfig get].sendInfo boolValue]),
@"helpHidden" : @([[MPiOSConfig get].helpHidden boolValue]),
@"showQuickStart" : @([[MPiOSConfig get].showSetup boolValue]),
@@ -559,123 +541,6 @@
}
}
-#pragma mark - UbiquityStoreManager
-
-- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager willLoadStoreIsCloud:(BOOL)isCloudStore {
-
- dispatch_async( dispatch_get_main_queue(), ^{
- [self signOutAnimated:YES];
- [self.handleCloudContentAlert cancelAlertAnimated:YES];
- if (!self.storeLoadingOverlay)
- self.storeLoadingOverlay = [PearlOverlay showProgressOverlayWithTitle:@"Loading Sites"];
- } );
-
- [super ubiquityStoreManager:manager willLoadStoreIsCloud:isCloudStore];
-}
-
-- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didLoadStoreForCoordinator:(NSPersistentStoreCoordinator *)coordinator
- isCloud:(BOOL)isCloudStore {
-
- [MPiOSConfig get].iCloudEnabled = @(isCloudStore);
- [super ubiquityStoreManager:manager didLoadStoreForCoordinator:coordinator isCloud:isCloudStore];
-
- [self.handleCloudContentAlert cancelAlertAnimated:YES];
- [self.fixCloudContentAlert cancelAlertAnimated:YES];
- [self.storeLoadingOverlay cancelOverlayAnimated:YES];
- [self.handleCloudDisabledAlert cancelAlertAnimated:YES];
-
- if (isCloudStore)
- [PearlAlert showAlertWithTitle:@"iCloud Support Deprecated" message:
- @"Master Password is moving away from iCloud due to limited platform support and reliability issues. "
- @"\n\nMaster Password's generated passwords do not require syncing. "
- @"Your sites will always have the same passwords on all your devices, even without iCloud. "
- @"\n\niCloud continues to work for now but will be deactivated in a future update. "
- @"Disable iCloud now to copy your iCloud sites to your device and avoid having to recreate them "
- @"when iCloud becomes discontinued."
- viewStyle:UIAlertViewStyleDefault initAlert:nil
- tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
- if (buttonIndex == [alert cancelButtonIndex])
- return;
-
- if (buttonIndex == [alert firstOtherButtonIndex])
- [UIApp openURL:[NSURL URLWithString:
- @"http://support.lyndir.com/topic/486731-why-doesnt-the-mac-version-have-icloud-support/#comment-755394"]];
-
- if (buttonIndex == [alert firstOtherButtonIndex] + 1)
- [MPiOSConfig get].iCloudEnabled = @NO;
- }
- cancelTitle:@"Ignore For Now" otherTitles:@"Why?", @"Disable iCloud", nil];
-}
-
-- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager failedLoadingStoreWithCause:(UbiquityStoreErrorCause)cause context:(id)context
- wasCloud:(BOOL)wasCloudStore {
-
- [self.storeLoadingOverlay cancelOverlayAnimated:YES];
- [self.handleCloudDisabledAlert cancelAlertAnimated:YES];
-}
-
-- (BOOL)ubiquityStoreManager:(UbiquityStoreManager *)manager handleCloudContentCorruptionWithHealthyStore:(BOOL)storeHealthy {
-
- if (manager.cloudEnabled && !storeHealthy && !(self.handleCloudContentAlert || self.fixCloudContentAlert)) {
- [self.storeLoadingOverlay cancelOverlayAnimated:YES];
- [self.handleCloudDisabledAlert cancelAlertAnimated:YES];
- [self showCloudContentAlert];
- };
-
- return NO;
-}
-
-- (BOOL)ubiquityStoreManagerHandleCloudDisabled:(UbiquityStoreManager *)manager {
-
- if (!self.handleCloudDisabledAlert)
- self.handleCloudDisabledAlert = [PearlAlert showAlertWithTitle:@"iCloud Login" message:
- @"You haven't added an iCloud account to your device yet.\n"
- @"To add one, go into Apple's Settings -> iCloud."
- viewStyle:UIAlertViewStyleDefault initAlert:nil
- tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
- if (buttonIndex == alert.firstOtherButtonIndex) {
- [MPiOSConfig get].iCloudEnabled = @NO;
- return;
- }
-
- [self.storeManager reloadStore];
- } cancelTitle:@"Try Again" otherTitles:@"Disable iCloud", nil];
-
- return YES;
-}
-
-- (void)showCloudContentAlert {
-
- __weak MPiOSAppDelegate *wSelf = self;
- [self.handleCloudContentAlert cancelAlertAnimated:NO];
- // TODO: Add the activity indicator back.
- self.handleCloudContentAlert = [PearlAlert showAlertWithTitle:@"iCloud Sync Problem"
- message:@"Waiting for your other device to auto‑correct the problem..."
- viewStyle:UIAlertViewStyleDefault initAlert:nil tappedButtonBlock:
- ^(UIAlertView *alert, NSInteger buttonIndex) {
- if (buttonIndex == [alert firstOtherButtonIndex])
- wSelf.fixCloudContentAlert = [PearlAlert showAlertWithTitle:@"Fix iCloud Now" message:
- @"This problem can be auto‑corrected by opening the app on another device where you recently made changes.\n"
- @"You can fix the problem from this device anyway, but recent changes from another device might get lost.\n\n"
- @"You can also turn iCloud off for now."
- viewStyle:UIAlertViewStyleDefault
- initAlert:nil tappedButtonBlock:
- ^(UIAlertView *alert_, NSInteger buttonIndex_) {
- if (buttonIndex_ == alert_.cancelButtonIndex)
- [wSelf showCloudContentAlert];
- if (buttonIndex_ == [alert_ firstOtherButtonIndex])
- [wSelf.storeManager rebuildCloudContentFromCloudStoreOrLocalStore:YES];
- if (buttonIndex_ == [alert_ firstOtherButtonIndex] + 1)
- [MPiOSConfig get].iCloudEnabled = @NO;
- }
- cancelTitle:[PearlStrings get].commonButtonBack
- otherTitles:@"Fix Anyway",
- @"Turn Off", nil];
- if (buttonIndex == [alert firstOtherButtonIndex] + 1)
- [MPiOSConfig get].iCloudEnabled = @NO;
- } cancelTitle:nil otherTitles:@"Fix Now", @"Turn Off", nil];
-}
-
#pragma mark - Crashlytics
- (NSDictionary *)crashlyticsInfo {
diff --git a/MasterPassword/ObjC/iOS/MPiOSConfig.h b/MasterPassword/ObjC/iOS/MPiOSConfig.h
index 434d30b7..f1063b08 100644
--- a/MasterPassword/ObjC/iOS/MPiOSConfig.h
+++ b/MasterPassword/ObjC/iOS/MPiOSConfig.h
@@ -17,7 +17,6 @@
@property(nonatomic, retain) NSNumber *typeTipShown;
@property(nonatomic, retain) NSNumber *loginNameTipShown;
@property(nonatomic, retain) NSNumber *traceMode;
-@property(nonatomic, retain) NSNumber *iCloudEnabled;
@property(nonatomic, retain) NSNumber *dictationSearch;
@end
diff --git a/MasterPassword/ObjC/iOS/MPiOSConfig.m b/MasterPassword/ObjC/iOS/MPiOSConfig.m
index b31e3be9..05ae35fc 100644
--- a/MasterPassword/ObjC/iOS/MPiOSConfig.m
+++ b/MasterPassword/ObjC/iOS/MPiOSConfig.m
@@ -8,7 +8,7 @@
@implementation MPiOSConfig
-@dynamic helpHidden, siteInfoHidden, showSetup, actionsTipShown, typeTipShown, loginNameTipShown, traceMode, iCloudEnabled, dictationSearch;
+@dynamic helpHidden, siteInfoHidden, showSetup, actionsTipShown, typeTipShown, loginNameTipShown, traceMode, dictationSearch;
- (id)init {
@@ -24,7 +24,6 @@
NSStringFromSelector( @selector(typeTipShown) ) : @(!self.firstRun),
NSStringFromSelector( @selector(loginNameTipShown) ) : @NO,
NSStringFromSelector( @selector(traceMode) ) : @NO,
- NSStringFromSelector( @selector(iCloudEnabled) ) : @NO,
NSStringFromSelector( @selector(dictationSearch) ) : @NO
}];
diff --git a/MasterPassword/ObjC/iOS/MasterPassword-Info.plist b/MasterPassword/ObjC/iOS/MasterPassword-Info.plist
index 20ed0e9f..5ae970b9 100644
--- a/MasterPassword/ObjC/iOS/MasterPassword-Info.plist
+++ b/MasterPassword/ObjC/iOS/MasterPassword-Info.plist
@@ -21,7 +21,7 @@
Owner
LSItemContentTypes
- com.lyndir.lhunath.MasterPassword.sites
+ com.lyndir.masterpassword.sites
@@ -39,36 +39,12 @@
[auto]
CFBundleSignature
????
- CFBundleURLTypes
-
-
- CFBundleTypeRole
- Editor
- CFBundleURLName
- com.lyndir.lhunath.MasterPassword
- CFBundleURLSchemes
-
- com.lyndir.lhunath.MasterPassword
-
-
-
CFBundleVersion
[auto]
LSRequiresIPhoneOS
NSHumanReadableCopyright
© 2011-2013, Lyndir
- ReplacementFonts
-
- AmericanTypewriter-Bold
- SourceCodePro-Black
- AmericanTypewriter-Light
- SourceCodePro-ExtraLight
- Futura-CondensedExtraBold
- Exo-ExtraBold
- Futura-Medium
- Exo
-
UIAppFonts
Exo2.0-Bold.otf
@@ -76,6 +52,7 @@
Exo2.0-Regular.otf
Exo2.0-Thin.otf
SourceCodePro-Black.otf
+ SourceCodePro-Regular.otf
SourceCodePro-ExtraLight.otf
UIMainStoryboardFile
@@ -119,10 +96,14 @@
UTExportedTypeDeclarations
+ UTTypeConformsTo
+
+ public.utf8-plain-text
+
UTTypeDescription
Master Password sites
UTTypeIdentifier
- com.lyndir.lhunath.MasterPassword.sites
+ com.lyndir.masterpassword.sites
UTTypeSize320IconFile
UTTypeSize64IconFile
diff --git a/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj b/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj
index febc1803..9d1850bf 100644
--- a/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj
+++ b/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj
@@ -7,14 +7,15 @@
objects = {
/* 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 */; };
- 93D391ED37C9F687FA51EAA1 /* MPEmergencySegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3937712BF1B67623E5764 /* MPEmergencySegue.m */; };
93D3922A53E41A54832E90D9 /* PearlOverlay.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D390FADEB325D8D54A957D /* PearlOverlay.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 */; };
93D392EC39DA43C46C692C12 /* NSDictionary+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */; };
93D3932889B6B4206E66A6D6 /* PearlEMail.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39F7C9F47BF6387FBC5C3 /* PearlEMail.h */; };
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 */; };
93D394982CBD25D46692DD7C /* MPWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3990E0CD1B5CF9FBB2C07 /* MPWebViewController.m */; };
93D394B5036C882B33C71872 /* MPPasswordsSegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39E7A12CC352B2825AA66 /* MPPasswordsSegue.m */; };
@@ -33,7 +34,8 @@
93D399246DC90F50913A1287 /* UIResponder+PearlFirstResponder.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39A1DDFA09AE2E14D26DC /* UIResponder+PearlFirstResponder.m */; };
93D3992FA1546E01F498F665 /* PearlNavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D398567FD02DB2647B8CF3 /* PearlNavigationController.h */; };
93D399433EA75E50656040CB /* Twitter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93D394077F8FAB8167647187 /* Twitter.framework */; };
- 93D399BBC0A7EC746CB1B19B /* MPLogsViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D391943675426839501BB8 /* MPLogsViewController.h */; };
+ 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 */; };
93D39A5FF670957C0AF8298D /* MPPasswordCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39DEA995041A13DC9CAF7 /* MPPasswordCell.m */; };
93D39A8EA1C49CE43B63F47B /* PearlUICollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D8A953779B35403AF6E /* PearlUICollectionView.m */; };
@@ -42,6 +44,7 @@
93D39B8F90F58A5D158DDBA3 /* MPPasswordsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3924EE15017F8A12CB436 /* MPPasswordsViewController.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 */; };
+ 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 */; };
93D39D596A2E376D6F6F5DA1 /* MPCombinedViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D393310223DDB35218467A /* MPCombinedViewController.m */; };
93D39D8F78978196D6ABDEDE /* MPNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39CC01630D0421205C4C4 /* MPNavigationController.m */; };
@@ -91,6 +94,11 @@
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, ); }; };
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 */; };
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 */; };
@@ -104,11 +112,25 @@
DA30E9D415722EF400A68B4C /* Pearl-UIKit.m in Sources */ = {isa = PBXBuildFile; fileRef = DA30E9D315722EF400A68B4C /* Pearl-UIKit.m */; };
DA30E9D715723E6900A68B4C /* PearlLazy.h in Headers */ = {isa = PBXBuildFile; fileRef = DA30E9D515723E6900A68B4C /* PearlLazy.h */; };
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 */; };
+ DA32CFF219CF1C8F004F3F0E /* MPSiteQuestionEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA32CFEA19CF1C8F004F3F0E /* MPSiteQuestionEntity.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 */; };
DA3509FE15F101A500C14A8E /* PearlQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = DA3509FC15F101A500C14A8E /* PearlQueue.h */; };
DA3509FF15F101A500C14A8E /* PearlQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = DA3509FD15F101A500C14A8E /* PearlQueue.m */; };
DA38D6A318CCB5BF009AEB3E /* Storyboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DA38D6A218CCB5BF009AEB3E /* Storyboard.storyboard */; };
- DA4425CC1557BED40052177D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
- DA44260A1557D9E40052177D /* libUbiquityStoreManager.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA4425CB1557BED40052177D /* libUbiquityStoreManager.a */; };
+ DA3BCFCB19BD09D5006B2681 /* SourceCodePro-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = DA3BCFCA19BD09D5006B2681 /* SourceCodePro-Regular.otf */; };
DA4522441902355C008F650A /* icon_book.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD370C1711E29500CF925C /* icon_book.png */; };
DA4522451902355C008F650A /* icon_book@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD370D1711E29500CF925C /* icon_book@2x.png */; };
DA45224719062899008F650A /* icon_settings.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38141711E29600CF925C /* icon_settings.png */; };
@@ -118,7 +140,6 @@
DA45224B190628B2008F650A /* icon_gear.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37821711E29500CF925C /* icon_gear.png */; };
DA45224C190628B2008F650A /* icon_gear@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37831711E29500CF925C /* icon_gear@2x.png */; };
DA48856019A5A82E000C2D79 /* Crashlytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAA141191922FED80032B392 /* Crashlytics.framework */; };
- DA48856119A5AC1E000C2D79 /* libopensslcrypto-ios.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAE8E65119867AB500416A0F /* libopensslcrypto-ios.a */; };
DA4DA1D91564471A00F6F596 /* libjrswizzle.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC6326C148680650075AEA5 /* libjrswizzle.a */; };
DA4DA1DA1564471F00F6F596 /* libuicolor-utilities.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC6325D1486805C0075AEA5 /* libuicolor-utilities.a */; };
DA5A09DF171A70E4005284AB /* play.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5A09DD171A70E4005284AB /* play.png */; };
@@ -140,6 +161,9 @@
DA67461018DE7F0C00DFE240 /* Exo2.0-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = DA67460C18DE7F0C00DFE240 /* Exo2.0-Bold.otf */; };
DA69540617D975D900BF294E /* icon_gears.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37841711E29500CF925C /* icon_gears.png */; };
DA69540717D975D900BF294E /* icon_gears@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37851711E29500CF925C /* icon_gears@2x.png */; };
+ DA72BD7519C133BF00E6ACFE /* libscryptenc-ios-dev.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA72BD7419C133BF00E6ACFE /* libscryptenc-ios-dev.a */; };
+ DA72BD7919C137DE00E6ACFE /* libopensslcrypto-ios-dev.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA72BD7719C137D500E6ACFE /* libopensslcrypto-ios-dev.a */; };
+ DA72BD7B19C1510C00E6ACFE /* UIView+FontScale.m in Sources */ = {isa = PBXBuildFile; fileRef = DACE2F6719BA6A2A0010F92E /* UIView+FontScale.m */; };
DA73049D194E022700E72520 /* ui_spinner.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD36511711E29400CF925C /* ui_spinner.png */; };
DA73049E194E022700E72520 /* ui_spinner@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD36521711E29400CF925C /* ui_spinner@2x.png */; };
DA73049F194E022B00E72520 /* ui_textfield.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD365B1711E29400CF925C /* ui_textfield.png */; };
@@ -215,7 +239,6 @@
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 */; };
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 */; };
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 */; };
@@ -231,13 +254,8 @@
DABD3C011711E2DC00CF925C /* MPAppDelegate_Shared.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BA91711E2DC00CF925C /* MPAppDelegate_Shared.m */; };
DABD3C021711E2DC00CF925C /* MPAppDelegate_Store.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BAB1711E2DC00CF925C /* MPAppDelegate_Store.m */; };
DABD3C031711E2DC00CF925C /* MPConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BAD1711E2DC00CF925C /* MPConfig.m */; };
- DABD3C041711E2DC00CF925C /* MPElementEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BAF1711E2DC00CF925C /* MPElementEntity.m */; };
- DABD3C051711E2DC00CF925C /* MPElementGeneratedEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BB11711E2DC00CF925C /* MPElementGeneratedEntity.m */; };
- DABD3C061711E2DC00CF925C /* MPElementStoredEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BB31711E2DC00CF925C /* MPElementStoredEntity.m */; };
DABD3C071711E2DC00CF925C /* MPEntities.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BB51711E2DC00CF925C /* MPEntities.m */; };
DABD3C081711E2DC00CF925C /* MPKey.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BB71711E2DC00CF925C /* MPKey.m */; };
- DABD3C091711E2DC00CF925C /* MPUserEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BBA1711E2DC00CF925C /* MPUserEntity.m */; };
- DABD3C141711E2DC00CF925C /* MasterPassword.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BD01711E2DC00CF925C /* MasterPassword.xcdatamodeld */; };
DABD3C151711E2DC00CF925C /* MPiOSAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BD91711E2DC00CF925C /* MPiOSAppDelegate.m */; };
DABD3C1C1711E2DC00CF925C /* MPGuideViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BE71711E2DC00CF925C /* MPGuideViewController.m */; };
DABD3C1E1711E2DC00CF925C /* MPPreferencesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BEB1711E2DC00CF925C /* MPPreferencesViewController.m */; };
@@ -257,10 +275,6 @@
DAC77CAE148291A600BCF976 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
DAC8DF47192831E100BA7D71 /* icon_key.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD379A1711E29600CF925C /* icon_key.png */; };
DAC8DF48192831E100BA7D71 /* icon_key@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD379B1711E29600CF925C /* icon_key@2x.png */; };
- DACA22BB1705DE7D002C6C22 /* UbiquityStoreManager.m in Sources */ = {isa = PBXBuildFile; fileRef = DACA22B71705DE7D002C6C22 /* UbiquityStoreManager.m */; };
- DACA22BC1705DE7D002C6C22 /* NSError+UbiquityStoreManager.h in Headers */ = {isa = PBXBuildFile; fileRef = DACA22B81705DE7D002C6C22 /* NSError+UbiquityStoreManager.h */; };
- DACA22BD1705DE7D002C6C22 /* NSError+UbiquityStoreManager.m in Sources */ = {isa = PBXBuildFile; fileRef = DACA22B91705DE7D002C6C22 /* NSError+UbiquityStoreManager.m */; };
- DACA22BE1705DE7D002C6C22 /* UbiquityStoreManager.h in Headers */ = {isa = PBXBuildFile; fileRef = DACA22BA1705DE7D002C6C22 /* UbiquityStoreManager.h */; };
DACA296F1705DF81002C6C22 /* Crashlytics.plist in Resources */ = {isa = PBXBuildFile; fileRef = DACA269A1705DF81002C6C22 /* Crashlytics.plist */; };
DACA29731705E1A8002C6C22 /* ciphers.plist in Resources */ = {isa = PBXBuildFile; fileRef = DACA29711705E1A8002C6C22 /* ciphers.plist */; };
DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */ = {isa = PBXBuildFile; fileRef = DACA29721705E1A8002C6C22 /* dictionary.lst */; };
@@ -270,13 +284,24 @@
DACA29BD1705E2DE002C6C22 /* UIColor+HSV.h in Headers */ = {isa = PBXBuildFile; fileRef = DACA29B61705E2DE002C6C22 /* UIColor+HSV.h */; };
DACA29BE1705E2DE002C6C22 /* UIColor+Expanded.m in Sources */ = {isa = PBXBuildFile; fileRef = DACA29BA1705E2DE002C6C22 /* UIColor+Expanded.m */; };
DACA29BF1705E2DE002C6C22 /* UIColor+HSV.m in Sources */ = {isa = PBXBuildFile; fileRef = DACA29BB1705E2DE002C6C22 /* UIColor+HSV.m */; };
+ DACE2F6519BA6A0A0010F92E /* PearlProfiler.m in Sources */ = {isa = PBXBuildFile; fileRef = DACE2F6319BA6A0A0010F92E /* PearlProfiler.m */; };
+ DACE2F6619BA6A0A0010F92E /* PearlProfiler.h in Headers */ = {isa = PBXBuildFile; fileRef = DACE2F6419BA6A0A0010F92E /* PearlProfiler.h */; };
+ DACE2F6C19BA6A2A0010F92E /* PearlMutableStaticTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DACE2F6819BA6A2A0010F92E /* PearlMutableStaticTableViewController.m */; };
+ DACE2F6D19BA6A2A0010F92E /* PearlMutableStaticTableViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = DACE2F6919BA6A2A0010F92E /* PearlMutableStaticTableViewController.h */; };
+ DACE2F6E19BA6A2A0010F92E /* UIView+FontScale.h in Headers */ = {isa = PBXBuildFile; fileRef = DACE2F6A19BA6A2A0010F92E /* UIView+FontScale.h */; };
DAD312C21552A22700A3F9ED /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = DAD312C01552A20800A3F9ED /* libsqlite3.dylib */; };
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 */; };
DAEC85B518E3DD9A007FC0DF /* UIView+Touches.m in Sources */ = {isa = PBXBuildFile; fileRef = DAEC85B118E3DD9A007FC0DF /* UIView+Touches.m */; };
DAEC85B618E3DD9A007FC0DF /* PearlUINavigationBar.m in Sources */ = {isa = PBXBuildFile; fileRef = DAEC85B218E3DD9A007FC0DF /* PearlUINavigationBar.m */; };
DAEC85B718E3DD9A007FC0DF /* PearlUINavigationBar.h in Headers */ = {isa = PBXBuildFile; fileRef = DAEC85B318E3DD9A007FC0DF /* PearlUINavigationBar.h */; };
DAEC85B818E3DD9A007FC0DF /* UIView+Touches.h in Headers */ = {isa = PBXBuildFile; fileRef = DAEC85B418E3DD9A007FC0DF /* UIView+Touches.h */; };
+ DAEFB01E19BCBD9E00525079 /* UIView+LayoutGone.m in Sources */ = {isa = PBXBuildFile; fileRef = DAEFB01C19BCBD9E00525079 /* UIView+LayoutGone.m */; };
+ DAEFB01F19BCBD9E00525079 /* UIView+LayoutGone.h in Headers */ = {isa = PBXBuildFile; fileRef = DAEFB01D19BCBD9E00525079 /* UIView+LayoutGone.h */; };
DAF4EF50190A81E400023C90 /* NSManagedObject+Pearl.m in Sources */ = {isa = PBXBuildFile; fileRef = DAF4EF4E190A81E400023C90 /* NSManagedObject+Pearl.m */; };
DAF4EF51190A81E400023C90 /* NSManagedObject+Pearl.h in Headers */ = {isa = PBXBuildFile; fileRef = DAF4EF4F190A81E400023C90 /* NSManagedObject+Pearl.h */; };
DAFC5656172C573B00CB5CC5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
@@ -294,7 +319,6 @@
DAFC568E172C57EC00CB5CC5 /* IASKSlider.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC567E172C57EC00CB5CC5 /* IASKSlider.m */; };
DAFC568F172C57EC00CB5CC5 /* IASKSwitch.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC5680172C57EC00CB5CC5 /* IASKSwitch.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 */; };
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 */; };
@@ -375,15 +399,27 @@
DAFE4A63150399FF003ABA90 /* UIScrollView+PearlFlashingIndicators.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE4A63150399FF003ABA8F /* UIScrollView+PearlFlashingIndicators.h */; };
DAFE4A63150399FF003ABA92 /* NSDateFormatter+RFC3339.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFE4A63150399FF003ABA91 /* NSDateFormatter+RFC3339.m */; };
DAFE4A63150399FF003ABA94 /* NSDateFormatter+RFC3339.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE4A63150399FF003ABA93 /* NSDateFormatter+RFC3339.h */; };
- DAFFC63F17EDDA7C007BB020 /* libscryptenc-ios.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAFFC63E17EDDA7C007BB020 /* libscryptenc-ios.a */; };
/* 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 */
93D390519405B76CC6A57C4F /* MPCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCell.h; sourceTree = ""; };
93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Indexing.m"; sourceTree = ""; };
93D3908DF8EABBD952065DC0 /* UICollectionView+PearlReloadFromArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionView+PearlReloadFromArray.m"; sourceTree = ""; };
93D390FADEB325D8D54A957D /* PearlOverlay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlOverlay.m; sourceTree = ""; };
93D390FB3110DCCE68E600DC /* UIScrollView+PearlAdjustInsets.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIScrollView+PearlAdjustInsets.m"; sourceTree = ""; };
+ 93D39149A5F1F9B174D6D061 /* MPStoreViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPStoreViewController.h; sourceTree = ""; };
93D3914D7597F9A28DB9D85E /* MPPasswordsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordsViewController.h; sourceTree = ""; };
93D39156E806BB78E04F78B9 /* PearlSizedTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlSizedTextView.m; sourceTree = ""; };
93D3916C1D8F1427DFBDEBCA /* MPAppSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAppSettingsViewController.m; sourceTree = ""; };
@@ -392,13 +428,15 @@
93D3924EE15017F8A12CB436 /* MPPasswordsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordsViewController.m; sourceTree = ""; };
93D392876BE5C011DE73B43F /* MPPopdownSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPopdownSegue.h; sourceTree = ""; };
93D393310223DDB35218467A /* MPCombinedViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCombinedViewController.m; sourceTree = ""; };
- 93D3937712BF1B67623E5764 /* MPEmergencySegue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPEmergencySegue.m; sourceTree = ""; };
93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Indexing.h"; sourceTree = ""; };
93D393BB973253D4BAAC84AA /* PearlEMail.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlEMail.m; sourceTree = ""; };
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 = ""; };
93D394482BB07F90E8FD1314 /* UIResponder+PearlFirstResponder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIResponder+PearlFirstResponder.h"; sourceTree = ""; };
+ 93D394C78C7B879C9AD9152C /* MPAppDelegate_InApp.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAppDelegate_InApp.m; sourceTree = ""; };
+ 93D395105935859D71679931 /* MPOverlayViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPOverlayViewController.m; sourceTree = ""; };
93D3956915634581E737B38C /* PearlNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlNavigationController.m; sourceTree = ""; };
+ 93D3957D76F71A652716EECC /* MPStoreViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPStoreViewController.m; sourceTree = ""; };
93D396D04E57792A54D437AC /* NSArray+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Indexing.h"; sourceTree = ""; };
93D3970502644794E8A027BE /* MPNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPNavigationController.h; sourceTree = ""; };
93D3971FE104BB4052484151 /* MPUsersViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUsersViewController.h; sourceTree = ""; };
@@ -411,25 +449,30 @@
93D3995B1D4DCE5A30D882BA /* MPCoachmarkViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCoachmarkViewController.m; sourceTree = ""; };
93D39975CE5AEC99E3F086C7 /* MPPasswordCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordCell.h; sourceTree = ""; };
93D3999693660C89A7465F4E /* MPCoachmarkViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCoachmarkViewController.h; sourceTree = ""; };
+ 93D399A8E3181B442D347CD7 /* MPAlgorithmV2.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAlgorithmV2.m; sourceTree = ""; };
93D399E571F61E50A9BF8FAF /* MPUsersViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUsersViewController.m; sourceTree = ""; };
93D399F244BB522A317811BB /* MPFixable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPFixable.h; sourceTree = ""; };
93D39A1DDFA09AE2E14D26DC /* UIResponder+PearlFirstResponder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIResponder+PearlFirstResponder.m"; sourceTree = ""; };
93D39A28369954D147E239BA /* MPSetupViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSetupViewController.m; sourceTree = ""; };
- 93D39A41340CF778E00D0E6D /* MPEmergencySegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPEmergencySegue.h; sourceTree = ""; };
93D39A4759186F6D2D34AA6B /* PearlSizedTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlSizedTextView.h; sourceTree = ""; };
93D39A813CA9D7E192261ED2 /* MPFixable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPFixable.m; sourceTree = ""; };
+ 93D39A97A7D48CB3B784194D /* MPAlgorithmV2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAlgorithmV2.h; sourceTree = ""; };
93D39AA10CD00D05937671B1 /* UITextView+PearlAttributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITextView+PearlAttributes.h"; sourceTree = ""; };
93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Indexing.m"; sourceTree = ""; };
93D39ACBA9F4878B6A1CC33B /* MPEmergencyViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPEmergencyViewController.m; sourceTree = ""; };
93D39B050DD5F55E9794EFD4 /* MPPopdownSegue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPopdownSegue.m; sourceTree = ""; };
93D39B1D8177A86C5B9EDDE3 /* PearlUICollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlUICollectionView.h; sourceTree = ""; };
93D39B381350802A194BF332 /* MPAvatarCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAvatarCell.m; sourceTree = ""; };
+ 93D39B455A71EC98C749E623 /* MPOverlayViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPOverlayViewController.h; sourceTree = ""; };
93D39BAA71DE51B4D8A1286C /* MPCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCell.m; sourceTree = ""; };
+ 93D39C426E03358384018E85 /* MPAnswersViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAnswersViewController.m; sourceTree = ""; };
93D39C44361BE57AF0B3071F /* MPPasswordsSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordsSegue.h; sourceTree = ""; };
93D39C86E984EC65DA5ACB1D /* MPAppSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAppSettingsViewController.h; sourceTree = ""; };
93D39CC01630D0421205C4C4 /* MPNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPNavigationController.m; sourceTree = ""; };
93D39CDD434AFD6E1B0DA359 /* MPEmergencyViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPEmergencyViewController.h; sourceTree = ""; };
+ 93D39CECA10BCCB0BA581BF1 /* MPAppDelegate_InApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAppDelegate_InApp.h; sourceTree = ""; };
93D39CF8ADF4542CDC4CD385 /* MPCombinedViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCombinedViewController.h; sourceTree = ""; };
+ 93D39D6604447D7708039155 /* MPAnswersViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAnswersViewController.h; sourceTree = ""; };
93D39D8A953779B35403AF6E /* PearlUICollectionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlUICollectionView.m; sourceTree = ""; };
93D39DA27D768B53C8B1330C /* MPAvatarCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAvatarCell.h; sourceTree = ""; };
93D39DE2CB351D4E3789462B /* UIScrollView+PearlAdjustInsets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIScrollView+PearlAdjustInsets.h"; sourceTree = ""; };
@@ -469,6 +512,9 @@
DA250A16195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionReusableView+PearlDequeue.h"; sourceTree = ""; };
DA25C6B31980D3C10046CDCF /* openssl */ = {isa = PBXFileReference; lastKnownFileType = folder; path = openssl; sourceTree = ""; };
DA25C6B51980D3DD0046CDCF /* scrypt */ = {isa = PBXFileReference; lastKnownFileType = folder; path = scrypt; sourceTree = ""; };
+ DA29992D19C86F5700AF7DF1 /* thumb_generated_login@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "thumb_generated_login@2x.png"; sourceTree = ""; };
+ DA29992E19C86F5700AF7DF1 /* thumb_generated_login.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = thumb_generated_login.png; sourceTree = ""; };
+ DA29993119C9132F00AF7DF1 /* thumb_generated_login@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "thumb_generated_login@3x.png"; sourceTree = ""; };
DA2CA4D918D28859007798F8 /* NSArray+Pearl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Pearl.m"; sourceTree = ""; };
DA2CA4DA18D28859007798F8 /* NSArray+Pearl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Pearl.h"; sourceTree = ""; };
DA2CA4DB18D28859007798F8 /* NSTimer+PearlBlock.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSTimer+PearlBlock.m"; sourceTree = ""; };
@@ -482,11 +528,35 @@
DA30E9D315722EF400A68B4C /* Pearl-UIKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "Pearl-UIKit.m"; sourceTree = ""; };
DA30E9D515723E6900A68B4C /* PearlLazy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlLazy.h; sourceTree = ""; };
DA30E9D615723E6900A68B4C /* PearlLazy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlLazy.m; sourceTree = ""; };
+ DA32CFE619CF1C8F004F3F0E /* MPUserEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUserEntity.m; sourceTree = ""; };
+ DA32CFE719CF1C8F004F3F0E /* MPUserEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUserEntity.h; sourceTree = ""; };
+ DA32CFE819CF1C8F004F3F0E /* MPStoredSiteEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPStoredSiteEntity.m; sourceTree = ""; };
+ DA32CFE919CF1C8F004F3F0E /* MPStoredSiteEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPStoredSiteEntity.h; sourceTree = ""; };
+ DA32CFEA19CF1C8F004F3F0E /* MPSiteQuestionEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSiteQuestionEntity.m; sourceTree = ""; };
+ DA32CFEB19CF1C8F004F3F0E /* MPSiteQuestionEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSiteQuestionEntity.h; sourceTree = ""; };
+ DA32CFEC19CF1C8F004F3F0E /* MPSiteEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSiteEntity.m; sourceTree = ""; };
+ DA32CFED19CF1C8F004F3F0E /* MPSiteEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSiteEntity.h; sourceTree = ""; };
+ DA32CFEE19CF1C8F004F3F0E /* MPGeneratedSiteEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPGeneratedSiteEntity.m; sourceTree = ""; };
+ DA32CFEF19CF1C8F004F3F0E /* MPGeneratedSiteEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPGeneratedSiteEntity.h; sourceTree = ""; };
+ DA32D00219CF4735004F3F0E /* MasterPassword 1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 1.xcdatamodel"; sourceTree = ""; };
+ DA32D00319CF4735004F3F0E /* MasterPassword 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 2.xcdatamodel"; sourceTree = ""; };
+ DA32D00419CF4735004F3F0E /* MasterPassword 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 3.xcdatamodel"; sourceTree = ""; };
+ DA32D00519CF4735004F3F0E /* MasterPassword 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 4.xcdatamodel"; sourceTree = ""; };
+ DA32D00619CF4735004F3F0E /* MasterPassword 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 5.xcdatamodel"; sourceTree = ""; };
+ DA32D00719CF4735004F3F0E /* MasterPassword 6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 6.xcdatamodel"; sourceTree = ""; };
+ DA32D01819D046E1004F3F0E /* PearlFixedTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlFixedTableView.m; sourceTree = ""; };
+ DA32D01919D046E1004F3F0E /* PearlFixedTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlFixedTableView.h; sourceTree = ""; };
+ 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 = ""; };
+ DA32D03A19D111EB004F3F0E /* NSManagedObjectModel+KCOrderedAccessorFix.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSManagedObjectModel+KCOrderedAccessorFix.m"; sourceTree = ""; };
+ DA32D03F19D27093004F3F0E /* thumb_generated_answers@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "thumb_generated_answers@3x.png"; sourceTree = ""; };
+ DA32D04019D27093004F3F0E /* thumb_generated_answers@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "thumb_generated_answers@2x.png"; sourceTree = ""; };
+ DA32D04119D27093004F3F0E /* thumb_generated_answers.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = thumb_generated_answers.png; sourceTree = ""; };
DA3509FC15F101A500C14A8E /* PearlQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlQueue.h; sourceTree = ""; };
DA3509FD15F101A500C14A8E /* PearlQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlQueue.m; sourceTree = ""; };
DA38D6A218CCB5BF009AEB3E /* Storyboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Storyboard.storyboard; sourceTree = ""; };
DA3B844D190FC5DF00246EEA /* Crashlytics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Crashlytics.framework; path = ../../../External/iOS/Crashlytics.framework; sourceTree = ""; };
- DA4425CB1557BED40052177D /* libUbiquityStoreManager.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libUbiquityStoreManager.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ DA3BCFCA19BD09D5006B2681 /* SourceCodePro-Regular.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SourceCodePro-Regular.otf"; sourceTree = ""; };
DA5A09DD171A70E4005284AB /* play.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = play.png; sourceTree = ""; };
DA5A09DE171A70E4005284AB /* play@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "play@2x.png"; sourceTree = ""; };
DA5A09E8171BB0F7005284AB /* unlocked.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = unlocked.png; sourceTree = ""; };
@@ -506,6 +576,8 @@
DA67460B18DE7F0C00DFE240 /* Exo2.0-ExtraBold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Exo2.0-ExtraBold.otf"; sourceTree = ""; };
DA67460C18DE7F0C00DFE240 /* Exo2.0-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Exo2.0-Bold.otf"; sourceTree = ""; };
DA70EC7F1811B13C00F65DB2 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; };
+ DA72BD7419C133BF00E6ACFE /* libscryptenc-ios-dev.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libscryptenc-ios-dev.a"; sourceTree = ""; };
+ DA72BD7719C137D500E6ACFE /* libopensslcrypto-ios-dev.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libopensslcrypto-ios-dev.a"; sourceTree = ""; };
DA854C8118D4CFBF00106317 /* avatar-add@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "avatar-add@2x.png"; sourceTree = ""; };
DA854C8218D4CFBF00106317 /* avatar-add.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "avatar-add.png"; sourceTree = ""; };
DA945C8617E3F3FD0053236B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; };
@@ -1130,7 +1202,6 @@
DABD38C11711E29700CF925C /* tip_location_teal@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tip_location_teal@2x.png"; sourceTree = ""; };
DABD38C21711E29700CF925C /* tip_location_wood.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = tip_location_wood.png; sourceTree = ""; };
DABD38C31711E29700CF925C /* tip_location_wood@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tip_location_wood@2x.png"; sourceTree = ""; };
- DABD38C61711E29700CF925C /* help.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = help.html; sourceTree = ""; };
DABD38C81711E29700CF925C /* jquery-1.6.1.min.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "jquery-1.6.1.min.js"; sourceTree = ""; };
DABD38C91711E29700CF925C /* keypad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = keypad.png; sourceTree = ""; };
DABD38CA1711E29700CF925C /* logo-bare.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "logo-bare.png"; sourceTree = ""; };
@@ -1154,23 +1225,11 @@
DABD3BAB1711E2DC00CF925C /* MPAppDelegate_Store.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAppDelegate_Store.m; sourceTree = ""; };
DABD3BAC1711E2DC00CF925C /* MPConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPConfig.h; sourceTree = ""; };
DABD3BAD1711E2DC00CF925C /* MPConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPConfig.m; sourceTree = ""; };
- DABD3BAE1711E2DC00CF925C /* MPElementEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementEntity.h; sourceTree = ""; };
- DABD3BAF1711E2DC00CF925C /* MPElementEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementEntity.m; sourceTree = ""; };
- DABD3BB01711E2DC00CF925C /* MPElementGeneratedEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementGeneratedEntity.h; sourceTree = ""; };
- DABD3BB11711E2DC00CF925C /* MPElementGeneratedEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementGeneratedEntity.m; sourceTree = ""; };
- DABD3BB21711E2DC00CF925C /* MPElementStoredEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementStoredEntity.h; sourceTree = ""; };
- DABD3BB31711E2DC00CF925C /* MPElementStoredEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementStoredEntity.m; sourceTree = ""; };
DABD3BB41711E2DC00CF925C /* MPEntities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPEntities.h; sourceTree = ""; };
DABD3BB51711E2DC00CF925C /* MPEntities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPEntities.m; sourceTree = ""; };
DABD3BB61711E2DC00CF925C /* MPKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPKey.h; sourceTree = ""; };
DABD3BB71711E2DC00CF925C /* MPKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPKey.m; sourceTree = ""; };
DABD3BB81711E2DC00CF925C /* MPTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPTypes.h; sourceTree = ""; };
- DABD3BB91711E2DC00CF925C /* MPUserEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUserEntity.h; sourceTree = ""; };
- DABD3BBA1711E2DC00CF925C /* MPUserEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUserEntity.m; sourceTree = ""; };
- DABD3BD11711E2DC00CF925C /* MasterPassword 1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 1.xcdatamodel"; sourceTree = ""; };
- DABD3BD21711E2DC00CF925C /* MasterPassword 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 2.xcdatamodel"; sourceTree = ""; };
- DABD3BD31711E2DC00CF925C /* MasterPassword 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 3.xcdatamodel"; sourceTree = ""; };
- DABD3BD41711E2DC00CF925C /* MasterPassword 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 4.xcdatamodel"; sourceTree = ""; };
DABD3BD81711E2DC00CF925C /* MPiOSAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPiOSAppDelegate.h; sourceTree = ""; };
DABD3BD91711E2DC00CF925C /* MPiOSAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPiOSAppDelegate.m; sourceTree = ""; };
DABD3BE61711E2DC00CF925C /* MPGuideViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPGuideViewController.h; sourceTree = ""; };
@@ -1196,10 +1255,6 @@
DAC632871486D95D0075AEA5 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
DAC77CAD148291A600BCF976 /* libPearl.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPearl.a; sourceTree = BUILT_PRODUCTS_DIR; };
DAC77CB1148291A600BCF976 /* Pearl-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "Pearl-Prefix.pch"; path = "../../MasterPassword/ObjC/Pearl/Pearl-Prefix.pch"; sourceTree = ""; };
- DACA22B71705DE7D002C6C22 /* UbiquityStoreManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UbiquityStoreManager.m; sourceTree = ""; };
- DACA22B81705DE7D002C6C22 /* NSError+UbiquityStoreManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+UbiquityStoreManager.h"; sourceTree = ""; };
- DACA22B91705DE7D002C6C22 /* NSError+UbiquityStoreManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+UbiquityStoreManager.m"; sourceTree = ""; };
- DACA22BA1705DE7D002C6C22 /* UbiquityStoreManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UbiquityStoreManager.h; sourceTree = ""; };
DACA269A1705DF81002C6C22 /* Crashlytics.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Crashlytics.plist; sourceTree = ""; };
DACA29711705E1A8002C6C22 /* ciphers.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = ciphers.plist; sourceTree = ""; };
DACA29721705E1A8002C6C22 /* dictionary.lst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dictionary.lst; sourceTree = ""; };
@@ -1209,15 +1264,25 @@
DACA29B61705E2DE002C6C22 /* UIColor+HSV.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIColor+HSV.h"; sourceTree = ""; };
DACA29BA1705E2DE002C6C22 /* UIColor+Expanded.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIColor+Expanded.m"; sourceTree = ""; };
DACA29BB1705E2DE002C6C22 /* UIColor+HSV.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIColor+HSV.m"; sourceTree = ""; };
+ DACE2F6319BA6A0A0010F92E /* PearlProfiler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlProfiler.m; sourceTree = ""; };
+ DACE2F6419BA6A0A0010F92E /* PearlProfiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlProfiler.h; sourceTree = ""; };
+ DACE2F6719BA6A2A0010F92E /* UIView+FontScale.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+FontScale.m"; sourceTree = ""; };
+ DACE2F6819BA6A2A0010F92E /* PearlMutableStaticTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlMutableStaticTableViewController.m; sourceTree = ""; };
+ DACE2F6919BA6A2A0010F92E /* PearlMutableStaticTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlMutableStaticTableViewController.h; sourceTree = ""; };
+ DACE2F6A19BA6A2A0010F92E /* UIView+FontScale.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+FontScale.h"; sourceTree = ""; };
DAD312C01552A20800A3F9ED /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; };
DADBB55918DB0CFC00D099FE /* keyboard-dark@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "keyboard-dark@2x.png"; sourceTree = ""; };
DAE1EF2317E942DE00BC0086 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; };
+ DAE2726119CE9CB3007C5262 /* UITableViewCell+PearlDeque.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITableViewCell+PearlDeque.m"; sourceTree = ""; };
+ DAE2726219CE9CB3007C5262 /* UITableViewCell+PearlDeque.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITableViewCell+PearlDeque.h"; sourceTree = ""; };
DAE8E65119867AB500416A0F /* libopensslcrypto-ios.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libopensslcrypto-ios.a"; sourceTree = ""; };
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 = ""; };
DAEC85B218E3DD9A007FC0DF /* PearlUINavigationBar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlUINavigationBar.m; sourceTree = ""; };
DAEC85B318E3DD9A007FC0DF /* PearlUINavigationBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlUINavigationBar.h; sourceTree = ""; };
DAEC85B418E3DD9A007FC0DF /* UIView+Touches.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+Touches.h"; sourceTree = ""; };
+ DAEFB01C19BCBD9E00525079 /* UIView+LayoutGone.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+LayoutGone.m"; sourceTree = ""; };
+ DAEFB01D19BCBD9E00525079 /* UIView+LayoutGone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+LayoutGone.h"; sourceTree = ""; };
DAF4EF4E190A81E400023C90 /* NSManagedObject+Pearl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSManagedObject+Pearl.m"; sourceTree = ""; };
DAF4EF4F190A81E400023C90 /* NSManagedObject+Pearl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSManagedObject+Pearl.h"; sourceTree = ""; };
DAFC5655172C573B00CB5CC5 /* libInAppSettingsKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libInAppSettingsKit.a; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -1337,11 +1402,10 @@
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
- DA4425C81557BED40052177D /* Frameworks */ = {
+ DA32D01D19D111C6004F3F0E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- DA4425CC1557BED40052177D /* Foundation.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1349,21 +1413,22 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- DA48856119A5AC1E000C2D79 /* libopensslcrypto-ios.a in Frameworks */,
- 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 */,
DA6701DE16406B7300B61001 /* Social.framework in Frameworks */,
DA6701B816406A4100B61001 /* Accounts.framework in Frameworks */,
- DA44260A1557D9E40052177D /* libUbiquityStoreManager.a in Frameworks */,
DAD312C21552A22700A3F9ED /* libsqlite3.dylib in Frameworks */,
DABB981615100B4000B05417 /* SystemConfiguration.framework in Frameworks */,
DA672D3014F9413D004A189C /* libPearl.a in Frameworks */,
DA672D2F14F92C6B004A189C /* libz.dylib in Frameworks */,
DAEBC45314F6364500987BF6 /* QuartzCore.framework in Frameworks */,
DA95D5F214DF0B2C008D1B94 /* MessageUI.framework in Frameworks */,
- DA04E33E14B1E70400ECA4F3 /* MobileCoreServices.framework in Frameworks */,
DA48856019A5A82E000C2D79 /* Crashlytics.framework in Frameworks */,
DAC632891486D9690075AEA5 /* Security.framework in Frameworks */,
+ DA72BD7919C137DE00E6ACFE /* libopensslcrypto-ios-dev.a in Frameworks */,
DA5BFA49147E415C00F98B1E /* UIKit.framework in Frameworks */,
DA5BFA4B147E415C00F98B1E /* Foundation.framework in Frameworks */,
DA5BFA4D147E415C00F98B1E /* CoreGraphics.framework in Frameworks */,
@@ -1393,8 +1458,8 @@
buildActionMask = 2147483647;
files = (
DA4DA1DA1564471F00F6F596 /* libuicolor-utilities.a in Frameworks */,
- DAFFC63F17EDDA7C007BB020 /* libscryptenc-ios.a in Frameworks */,
DA4DA1D91564471A00F6F596 /* libjrswizzle.a in Frameworks */,
+ DA72BD7519C133BF00E6ACFE /* libscryptenc-ios-dev.a in Frameworks */,
DAC77CAE148291A600BCF976 /* Foundation.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -1439,6 +1504,15 @@
path = Guide;
sourceTree = "";
};
+ DA32D03719D111EB004F3F0E /* KCOrderedAccessorFix */ = {
+ isa = PBXGroup;
+ children = (
+ DA32D03919D111EB004F3F0E /* NSManagedObjectModel+KCOrderedAccessorFix.h */,
+ DA32D03A19D111EB004F3F0E /* NSManagedObjectModel+KCOrderedAccessorFix.m */,
+ );
+ path = KCOrderedAccessorFix;
+ sourceTree = "";
+ };
DA5BFA39147E415C00F98B1E = {
isa = PBXGroup;
children = (
@@ -1447,6 +1521,10 @@
DACA22121705DDC5002C6C22 /* External */,
DA5BFA47147E415C00F98B1E /* Frameworks */,
DA5BFA45147E415C00F98B1E /* Products */,
+ 93D39149A5F1F9B174D6D061 /* MPStoreViewController.h */,
+ 93D3957D76F71A652716EECC /* MPStoreViewController.m */,
+ 93D39C426E03358384018E85 /* MPAnswersViewController.m */,
+ 93D39D6604447D7708039155 /* MPAnswersViewController.h */,
);
sourceTree = "";
};
@@ -1457,8 +1535,8 @@
DAC77CAD148291A600BCF976 /* libPearl.a */,
DAC6325D1486805C0075AEA5 /* libuicolor-utilities.a */,
DAC6326C148680650075AEA5 /* libjrswizzle.a */,
- DA4425CB1557BED40052177D /* libUbiquityStoreManager.a */,
DAFC5655172C573B00CB5CC5 /* libInAppSettingsKit.a */,
+ DA32D02019D111C6004F3F0E /* libKCOrderedAccessorFix.a */,
);
name = Products;
sourceTree = "";
@@ -1491,6 +1569,8 @@
DA5E5C6317248959003798D8 /* lib */ = {
isa = PBXGroup;
children = (
+ DA72BD7719C137D500E6ACFE /* libopensslcrypto-ios-dev.a */,
+ DA72BD7419C133BF00E6ACFE /* libscryptenc-ios-dev.a */,
DAE8E65119867AB500416A0F /* libopensslcrypto-ios.a */,
DAFFC63E17EDDA7C007BB020 /* libscryptenc-ios.a */,
DA5E5C6417248959003798D8 /* include */,
@@ -1526,6 +1606,12 @@
DABD360D1711E29400CF925C /* Media */ = {
isa = PBXGroup;
children = (
+ 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 */,
DA071BF1190187FE00179766 /* empty@2x.png */,
DA071BF2190187FE00179766 /* empty.png */,
@@ -1544,7 +1630,6 @@
DABD38751711E29700CF925C /* Tooltips */,
DABD3FC81712446200CF925C /* cloud.png */,
DABD3FC91712446200CF925C /* cloud@2x.png */,
- DABD38C61711E29700CF925C /* help.html */,
DABD3FCC1714F45B00CF925C /* identity.png */,
DABD3FCD1714F45B00CF925C /* identity@2x.png */,
DABD38C81711E29700CF925C /* jquery-1.6.1.min.js */,
@@ -1718,6 +1803,7 @@
DABD36BB1711E29500CF925C /* Fonts */ = {
isa = PBXGroup;
children = (
+ DA3BCFCA19BD09D5006B2681 /* SourceCodePro-Regular.otf */,
DA67460918DE7F0C00DFE240 /* Exo2.0-Thin.otf */,
DA67460A18DE7F0C00DFE240 /* Exo2.0-Regular.otf */,
DA67460B18DE7F0C00DFE240 /* Exo2.0-ExtraBold.otf */,
@@ -2225,6 +2311,16 @@
DABD3B9F1711E2DB00CF925C /* ObjC */ = {
isa = PBXGroup;
children = (
+ DA32CFE619CF1C8F004F3F0E /* MPUserEntity.m */,
+ DA32CFE719CF1C8F004F3F0E /* MPUserEntity.h */,
+ DA32CFE819CF1C8F004F3F0E /* MPStoredSiteEntity.m */,
+ DA32CFE919CF1C8F004F3F0E /* MPStoredSiteEntity.h */,
+ DA32CFEA19CF1C8F004F3F0E /* MPSiteQuestionEntity.m */,
+ DA32CFEB19CF1C8F004F3F0E /* MPSiteQuestionEntity.h */,
+ DA32CFEC19CF1C8F004F3F0E /* MPSiteEntity.m */,
+ DA32CFED19CF1C8F004F3F0E /* MPSiteEntity.h */,
+ DA32CFEE19CF1C8F004F3F0E /* MPGeneratedSiteEntity.m */,
+ DA32CFEF19CF1C8F004F3F0E /* MPGeneratedSiteEntity.h */,
DABD3BD71711E2DC00CF925C /* iOS */,
DABD3BA01711E2DC00CF925C /* MPAlgorithm.h */,
DABD3BA11711E2DC00CF925C /* MPAlgorithm.m */,
@@ -2240,22 +2336,18 @@
DABD3BAB1711E2DC00CF925C /* MPAppDelegate_Store.m */,
DABD3BAC1711E2DC00CF925C /* MPConfig.h */,
DABD3BAD1711E2DC00CF925C /* MPConfig.m */,
- DABD3BAE1711E2DC00CF925C /* MPElementEntity.h */,
- DABD3BAF1711E2DC00CF925C /* MPElementEntity.m */,
- DABD3BB01711E2DC00CF925C /* MPElementGeneratedEntity.h */,
- DABD3BB11711E2DC00CF925C /* MPElementGeneratedEntity.m */,
- DABD3BB21711E2DC00CF925C /* MPElementStoredEntity.h */,
- DABD3BB31711E2DC00CF925C /* MPElementStoredEntity.m */,
DABD3BB41711E2DC00CF925C /* MPEntities.h */,
DABD3BB51711E2DC00CF925C /* MPEntities.m */,
DABD3BB61711E2DC00CF925C /* MPKey.h */,
DABD3BB71711E2DC00CF925C /* MPKey.m */,
DABD3BB81711E2DC00CF925C /* MPTypes.h */,
- DABD3BB91711E2DC00CF925C /* MPUserEntity.h */,
- DABD3BBA1711E2DC00CF925C /* MPUserEntity.m */,
- DABD3BD01711E2DC00CF925C /* MasterPassword.xcdatamodeld */,
+ DA32D00119CF4735004F3F0E /* MasterPassword.xcdatamodeld */,
93D399F244BB522A317811BB /* MPFixable.h */,
93D39A813CA9D7E192261ED2 /* MPFixable.m */,
+ 93D394C78C7B879C9AD9152C /* MPAppDelegate_InApp.m */,
+ 93D39CECA10BCCB0BA581BF1 /* MPAppDelegate_InApp.h */,
+ 93D399A8E3181B442D347CD7 /* MPAlgorithmV2.m */,
+ 93D39A97A7D48CB3B784194D /* MPAlgorithmV2.h */,
);
name = ObjC;
path = ..;
@@ -2299,8 +2391,6 @@
93D3971FE104BB4052484151 /* MPUsersViewController.h */,
93D39BAA71DE51B4D8A1286C /* MPCell.m */,
93D390519405B76CC6A57C4F /* MPCell.h */,
- 93D3937712BF1B67623E5764 /* MPEmergencySegue.m */,
- 93D39A41340CF778E00D0E6D /* MPEmergencySegue.h */,
93D39E7A12CC352B2825AA66 /* MPPasswordsSegue.m */,
93D39C44361BE57AF0B3071F /* MPPasswordsSegue.h */,
93D39B050DD5F55E9794EFD4 /* MPPopdownSegue.m */,
@@ -2313,6 +2403,8 @@
93D39F556F2F142740A65E59 /* MPWebViewController.h */,
93D39CC01630D0421205C4C4 /* MPNavigationController.m */,
93D3970502644794E8A027BE /* MPNavigationController.h */,
+ 93D395105935859D71679931 /* MPOverlayViewController.m */,
+ 93D39B455A71EC98C749E623 /* MPOverlayViewController.h */,
);
path = iOS;
sourceTree = "";
@@ -2333,27 +2425,15 @@
DACA22121705DDC5002C6C22 /* External */ = {
isa = PBXGroup;
children = (
+ DA32D03719D111EB004F3F0E /* KCOrderedAccessorFix */,
DAA141181922FED80032B392 /* iOS */,
DAFC5662172C57EC00CB5CC5 /* InAppSettingsKit */,
DAC77CAF148291A600BCF976 /* Pearl */,
- DACA22B61705DE7D002C6C22 /* UbiquityStoreManager */,
);
name = External;
path = ../../../External;
sourceTree = "";
};
- DACA22B61705DE7D002C6C22 /* UbiquityStoreManager */ = {
- isa = PBXGroup;
- children = (
- DACA22BA1705DE7D002C6C22 /* UbiquityStoreManager.h */,
- DACA22B71705DE7D002C6C22 /* UbiquityStoreManager.m */,
- DACA22B81705DE7D002C6C22 /* NSError+UbiquityStoreManager.h */,
- DACA22B91705DE7D002C6C22 /* NSError+UbiquityStoreManager.m */,
- );
- name = UbiquityStoreManager;
- path = UbiquityStoreManager/UbiquityStoreManager;
- sourceTree = "";
- };
DACA23B41705DF7D002C6C22 /* Resources */ = {
isa = PBXGroup;
children = (
@@ -2469,6 +2549,8 @@
DAFE45D715039823003ABA7C /* Pearl */ = {
isa = PBXGroup;
children = (
+ DACE2F6319BA6A0A0010F92E /* PearlProfiler.m */,
+ DACE2F6419BA6A0A0010F92E /* PearlProfiler.h */,
DAA1411C1922FF020032B392 /* PearlTween.m */,
DAA1411D1922FF020032B392 /* PearlTween.h */,
DAA1411E1922FF020032B392 /* include */,
@@ -2559,6 +2641,16 @@
DAFE460715039823003ABA7C /* Pearl-UIKit */ = {
isa = PBXGroup;
children = (
+ DA32D01819D046E1004F3F0E /* PearlFixedTableView.m */,
+ DA32D01919D046E1004F3F0E /* PearlFixedTableView.h */,
+ DAE2726119CE9CB3007C5262 /* UITableViewCell+PearlDeque.m */,
+ DAE2726219CE9CB3007C5262 /* UITableViewCell+PearlDeque.h */,
+ DAEFB01C19BCBD9E00525079 /* UIView+LayoutGone.m */,
+ DAEFB01D19BCBD9E00525079 /* UIView+LayoutGone.h */,
+ DACE2F6719BA6A2A0010F92E /* UIView+FontScale.m */,
+ DACE2F6819BA6A2A0010F92E /* PearlMutableStaticTableViewController.m */,
+ DACE2F6919BA6A2A0010F92E /* PearlMutableStaticTableViewController.h */,
+ DACE2F6A19BA6A2A0010F92E /* UIView+FontScale.h */,
DA250A13195665A100AC23F1 /* UITableView+PearlReloadFromArray.m */,
DA250A14195665A100AC23F1 /* UITableView+PearlReloadFromArray.h */,
DA250A15195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m */,
@@ -2642,16 +2734,6 @@
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
- DA4425C91557BED40052177D /* Headers */ = {
- isa = PBXHeadersBuildPhase;
- buildActionMask = 2147483647;
- files = (
- DACA22BC1705DE7D002C6C22 /* NSError+UbiquityStoreManager.h in Headers */,
- DACA22BE1705DE7D002C6C22 /* UbiquityStoreManager.h in Headers */,
- 93D399BBC0A7EC746CB1B19B /* MPLogsViewController.h in Headers */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
DAC6325B1486805C0075AEA5 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
@@ -2686,6 +2768,7 @@
DAFE4A2215039824003ABA7C /* PearlDeviceUtils.h in Headers */,
DAFE4A2415039824003ABA7C /* PearlInfoPlist.h in Headers */,
DA2CA4E418D28866007798F8 /* NSLayoutConstraint+PearlUIKit.h in Headers */,
+ DACE2F6D19BA6A2A0010F92E /* PearlMutableStaticTableViewController.h in Headers */,
DAFE4A2615039824003ABA7C /* PearlLogger.h in Headers */,
DAFE4A2815039824003ABA7C /* PearlMathUtils.h in Headers */,
DAFE4A2A15039824003ABA7C /* PearlObjectUtils.h in Headers */,
@@ -2698,17 +2781,22 @@
DAFE4A3415039824003ABA7C /* PearlCryptUtils.h in Headers */,
DAFE4A3615039824003ABA7C /* PearlKeyChain.h in Headers */,
DAFE4A3815039824003ABA7C /* PearlRSAKey.h in Headers */,
+ DAE2726419CE9CB3007C5262 /* UITableViewCell+PearlDeque.h in Headers */,
DAFE4A3A15039824003ABA7C /* PearlSCrypt.h in Headers */,
+ DACE2F6E19BA6A2A0010F92E /* UIView+FontScale.h in Headers */,
+ DA32D01B19D046E1004F3F0E /* PearlFixedTableView.h in Headers */,
DAFE4A3C15039824003ABA7C /* Pearl-UIKit-Dependencies.h in Headers */,
DAEC85B818E3DD9A007FC0DF /* UIView+Touches.h in Headers */,
DAFE4A3D15039824003ABA7C /* Pearl-UIKit.h in Headers */,
DAFE4A3E15039824003ABA7C /* PearlAlert.h in Headers */,
DAFE4A4015039824003ABA7C /* PearlArrayTVC.h in Headers */,
DAFE4A4215039824003ABA7C /* PearlBoxView.h in Headers */,
+ DAEFB01F19BCBD9E00525079 /* UIView+LayoutGone.h in Headers */,
DAFE4A4415039824003ABA7C /* PearlGradientView.h in Headers */,
DAFE4A4615039824003ABA7C /* PearlLayout.h in Headers */,
DAFE4A4815039824003ABA7C /* PearlLayoutView.h in Headers */,
DAFE4A4A15039824003ABA7C /* PearlMessageView.h in Headers */,
+ DACE2F6619BA6A0A0010F92E /* PearlProfiler.h in Headers */,
DAFE4A4C15039824003ABA7C /* PearlRootViewController.h in Headers */,
DAFE4A4E15039824003ABA7C /* PearlSheet.h in Headers */,
DAFE4A5015039824003ABA7C /* PearlUIDebug.h in Headers */,
@@ -2748,21 +2836,21 @@
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
- DA4425CA1557BED40052177D /* UbiquityStoreManager */ = {
+ DA32D01F19D111C6004F3F0E /* KCOrderedAccessorFix */ = {
isa = PBXNativeTarget;
- buildConfigurationList = DA4425D31557BED40052177D /* Build configuration list for PBXNativeTarget "UbiquityStoreManager" */;
+ buildConfigurationList = DA32D03419D111C7004F3F0E /* Build configuration list for PBXNativeTarget "KCOrderedAccessorFix" */;
buildPhases = (
- DA4425C71557BED40052177D /* Sources */,
- DA4425C81557BED40052177D /* Frameworks */,
- DA4425C91557BED40052177D /* Headers */,
+ DA32D01C19D111C6004F3F0E /* Sources */,
+ DA32D01D19D111C6004F3F0E /* Frameworks */,
+ DA32D01E19D111C6004F3F0E /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
- name = UbiquityStoreManager;
- productName = iCloudStoreManager;
- productReference = DA4425CB1557BED40052177D /* libUbiquityStoreManager.a */;
+ name = KCOrderedAccessorFix;
+ productName = KCOrderedAccessorFix;
+ productReference = DA32D02019D111C6004F3F0E /* libKCOrderedAccessorFix.a */;
productType = "com.apple.product-type.library.static";
};
DA5BFA43147E415C00F98B1E /* MasterPassword */ = {
@@ -2858,9 +2946,12 @@
isa = PBXProject;
attributes = {
CLASSPREFIX = MP;
- LastUpgradeCheck = 0510;
+ LastUpgradeCheck = 0600;
ORGANIZATIONNAME = Lyndir;
TargetAttributes = {
+ DA32D01F19D111C6004F3F0E = {
+ CreatedOnToolsVersion = 6.0;
+ };
DA5BFA43147E415C00F98B1E = {
DevelopmentTeam = HL3Q45LX9N;
SystemCapabilities = {
@@ -2870,6 +2961,12 @@
com.apple.DataProtection = {
enabled = 1;
};
+ com.apple.Keychain = {
+ enabled = 0;
+ };
+ com.apple.iCloud = {
+ enabled = 0;
+ };
};
};
};
@@ -2975,8 +3072,8 @@
DAC77CAC148291A600BCF976 /* Pearl */,
DAC6325C1486805C0075AEA5 /* uicolor-utilities */,
DAC6326B148680650075AEA5 /* jrswizzle */,
- DA4425CA1557BED40052177D /* UbiquityStoreManager */,
DAFC5654172C573B00CB5CC5 /* InAppSettingsKit */,
+ DA32D01F19D111C6004F3F0E /* KCOrderedAccessorFix */,
);
};
/* End PBXProject section */
@@ -2994,6 +3091,7 @@
DA45224C190628B2008F650A /* icon_gear@2x.png in Resources */,
DA854C8318D4CFBF00106317 /* avatar-add@2x.png in Resources */,
DA45224A190628A1008F650A /* icon_wrench@2x.png in Resources */,
+ DA29993419C9214600AF7DF1 /* icon_star-hollow@2x.png in Resources */,
DA071BF4190187FE00179766 /* empty.png in Resources */,
DA69540617D975D900BF294E /* icon_gears.png in Resources */,
DA67460D18DE7F0C00DFE240 /* Exo2.0-Thin.otf in Resources */,
@@ -3032,7 +3130,11 @@
DABD394A1711E29700CF925C /* avatar-18.png in Resources */,
DABD394B1711E29700CF925C /* avatar-18@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 */,
+ DA32D04219D27093004F3F0E /* thumb_generated_answers@3x.png in Resources */,
DA67460E18DE7F0C00DFE240 /* Exo2.0-Regular.otf in Resources */,
DABD394D1711E29700CF925C /* avatar-2.png in Resources */,
DABD394E1711E29700CF925C /* avatar-2@2x.png in Resources */,
@@ -3051,11 +3153,13 @@
DA25C5FA197CCAE00046CDCF /* icon_delete.png in Resources */,
DA25C601197DBF260046CDCF /* icon_trash@2x.png in Resources */,
DABD39551711E29700CF925C /* avatar-6.png in Resources */,
+ DA32D00919CF5C55004F3F0E /* icon_question.png in Resources */,
DABD39561711E29700CF925C /* avatar-6@2x.png in Resources */,
DABD39571711E29700CF925C /* avatar-7.png in Resources */,
DABD39581711E29700CF925C /* avatar-7@2x.png in Resources */,
DABD39591711E29700CF925C /* avatar-8.png in Resources */,
DA250A0D1956484D00AC23F1 /* image-2@2x.png in Resources */,
+ DA32D00A19CF5C55004F3F0E /* icon_question@2x.png in Resources */,
DA250A051956484D00AC23F1 /* image-6@2x.png in Resources */,
DABD395A1711E29700CF925C /* avatar-8@2x.png in Resources */,
DABD395B1711E29700CF925C /* avatar-9.png in Resources */,
@@ -3073,6 +3177,8 @@
DABD39A11711E29700CF925C /* icon_action@2x.png in Resources */,
DABD39F21711E29700CF925C /* icon_cancel.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 */,
DABD39F31711E29700CF925C /* icon_cancel@2x.png in Resources */,
DA250A0C1956484D00AC23F1 /* image-3.png in Resources */,
@@ -3091,10 +3197,11 @@
DABD3ABF1711E29800CF925C /* icon_plus@2x.png in Resources */,
DA69540717D975D900BF294E /* icon_gears@2x.png in Resources */,
DABD3B1C1711E29800CF925C /* icon_up.png in Resources */,
+ DA32D04419D27093004F3F0E /* thumb_generated_answers.png in Resources */,
DABD3B1D1711E29800CF925C /* icon_up@2x.png in Resources */,
+ DA3BCFCB19BD09D5006B2681 /* SourceCodePro-Regular.otf in Resources */,
DA250A121956484D00AC23F1 /* image-0.png in Resources */,
DA4522441902355C008F650A /* icon_book.png in Resources */,
- DABD3B8A1711E29800CF925C /* help.html in Resources */,
DA2509FF1956484D00AC23F1 /* image-9@2x.png in Resources */,
DABD3B8D1711E29800CF925C /* keypad.png in Resources */,
DABD3B8E1711E29800CF925C /* logo-bare.png in Resources */,
@@ -3170,12 +3277,11 @@
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
- DA4425C71557BED40052177D /* Sources */ = {
+ DA32D01C19D111C6004F3F0E /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- DACA22BB1705DE7D002C6C22 /* UbiquityStoreManager.m in Sources */,
- DACA22BD1705DE7D002C6C22 /* NSError+UbiquityStoreManager.m in Sources */,
+ DA32D03C19D111EB004F3F0E /* NSManagedObjectModel+KCOrderedAccessorFix.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -3190,13 +3296,9 @@
DABD3C011711E2DC00CF925C /* MPAppDelegate_Shared.m in Sources */,
DABD3C021711E2DC00CF925C /* MPAppDelegate_Store.m in Sources */,
DABD3C031711E2DC00CF925C /* MPConfig.m in Sources */,
- DABD3C041711E2DC00CF925C /* MPElementEntity.m in Sources */,
- DABD3C051711E2DC00CF925C /* MPElementGeneratedEntity.m in Sources */,
- DABD3C061711E2DC00CF925C /* MPElementStoredEntity.m in Sources */,
DABD3C071711E2DC00CF925C /* MPEntities.m in Sources */,
+ DA32CFF319CF1C8F004F3F0E /* MPSiteEntity.m in Sources */,
DABD3C081711E2DC00CF925C /* MPKey.m in Sources */,
- DABD3C091711E2DC00CF925C /* MPUserEntity.m in Sources */,
- DABD3C141711E2DC00CF925C /* MasterPassword.xcdatamodeld in Sources */,
DABD3C151711E2DC00CF925C /* MPiOSAppDelegate.m in Sources */,
DABD3C1C1711E2DC00CF925C /* MPGuideViewController.m in Sources */,
DABD3C1E1711E2DC00CF925C /* MPPreferencesViewController.m in Sources */,
@@ -3204,22 +3306,31 @@
DABD3C211711E2DC00CF925C /* MPiOSConfig.m in Sources */,
DABD3C271711E2DC00CF925C /* main.m in Sources */,
93D39F8A9254177891F38705 /* MPSetupViewController.m in Sources */,
+ DA32CFF219CF1C8F004F3F0E /* MPSiteQuestionEntity.m in Sources */,
DA095E75172F4CD8001C948B /* MPLogsViewController.m in Sources */,
93D39D596A2E376D6F6F5DA1 /* MPCombinedViewController.m in Sources */,
+ DA32CFF419CF1C8F004F3F0E /* MPGeneratedSiteEntity.m in Sources */,
93D3957237D303DE2D38C267 /* MPAvatarCell.m in Sources */,
93D39B8F90F58A5D158DDBA3 /* MPPasswordsViewController.m in Sources */,
93D3954FCE045A3CC7E804B7 /* MPUsersViewController.m in Sources */,
93D39392DEDA376F93C6C718 /* MPCell.m in Sources */,
93D39A5FF670957C0AF8298D /* MPPasswordCell.m in Sources */,
93D398ECD7D1A0DEDDADF516 /* MPEmergencyViewController.m in Sources */,
- 93D391ED37C9F687FA51EAA1 /* MPEmergencySegue.m in Sources */,
+ DA32D00819CF4735004F3F0E /* MasterPassword.xcdatamodeld in Sources */,
93D394B5036C882B33C71872 /* MPPasswordsSegue.m in Sources */,
93D39673DDC085BE72C34D7C /* MPPopdownSegue.m in Sources */,
93D39BA1EA3CAAC8A220B4A6 /* MPAppSettingsViewController.m in Sources */,
93D396D8B67DA6522CDBA142 /* MPCoachmarkViewController.m in Sources */,
93D39EAA4D064193074D3021 /* MPFixable.m in Sources */,
+ DA32CFF119CF1C8F004F3F0E /* MPStoredSiteEntity.m in Sources */,
93D394982CBD25D46692DD7C /* MPWebViewController.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 */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -3249,6 +3360,7 @@
DAFE4A1815039824003ABA7C /* NSString+PearlSEL.m in Sources */,
DAFE4A1B15039824003ABA7C /* PearlAbstractStrings.m in Sources */,
DAFE4A1F15039824003ABA7C /* PearlCodeUtils.m in Sources */,
+ DAEFB01E19BCBD9E00525079 /* UIView+LayoutGone.m in Sources */,
DAFE4A2115039824003ABA7C /* PearlConfig.m in Sources */,
DAFE4A2315039824003ABA7C /* PearlDeviceUtils.m in Sources */,
DAFE4A2515039824003ABA7C /* PearlInfoPlist.m in Sources */,
@@ -3259,6 +3371,7 @@
DAFE4A3015039824003ABA7C /* PearlStringUtils.m in Sources */,
DAFE4A3515039824003ABA7C /* PearlCryptUtils.m in Sources */,
DAFE4A3715039824003ABA7C /* PearlKeyChain.m in Sources */,
+ DA72BD7B19C1510C00E6ACFE /* UIView+FontScale.m in Sources */,
DA250A17195665A100AC23F1 /* UITableView+PearlReloadFromArray.m in Sources */,
DAFE4A3915039824003ABA7C /* PearlRSAKey.m in Sources */,
DAFE4A3B15039824003ABA7C /* PearlSCrypt.m in Sources */,
@@ -3269,7 +3382,9 @@
DAFE4A4715039824003ABA7C /* PearlLayout.m in Sources */,
DA250A19195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m in Sources */,
DAFE4A4915039824003ABA7C /* PearlLayoutView.m in Sources */,
+ DAE2726319CE9CB3007C5262 /* UITableViewCell+PearlDeque.m in Sources */,
DAFE4A4B15039824003ABA7C /* PearlMessageView.m in Sources */,
+ DACE2F6519BA6A0A0010F92E /* PearlProfiler.m in Sources */,
DAFE4A4D15039824003ABA7C /* PearlRootViewController.m in Sources */,
DAFE4A4F15039824003ABA7C /* PearlSheet.m in Sources */,
DAFE4A5115039824003ABA7C /* PearlUIDebug.m in Sources */,
@@ -3277,6 +3392,7 @@
DAFE4A5515039824003ABA7C /* PearlValidatingTextField.m in Sources */,
DAEC85B618E3DD9A007FC0DF /* PearlUINavigationBar.m in Sources */,
DAFE4A5715039824003ABA7C /* PearlWebViewController.m in Sources */,
+ DACE2F6C19BA6A2A0010F92E /* PearlMutableStaticTableViewController.m in Sources */,
DAFE4A5915039824003ABA7C /* UIImage+PearlScaling.m in Sources */,
DAFE4A62150399FF003ABA7C /* PearlAppDelegate.m in Sources */,
DA30E9CF15722ECA00A68B4C /* NSBundle+PearlMutableInfo.m in Sources */,
@@ -3298,6 +3414,7 @@
93D39262A8A97DB748213309 /* PearlEMail.m in Sources */,
DA3509FF15F101A500C14A8E /* PearlQueue.m in Sources */,
93D3922A53E41A54832E90D9 /* PearlOverlay.m in Sources */,
+ DA32D01A19D046E1004F3F0E /* PearlFixedTableView.m in Sources */,
93D396AA30690B256F30378A /* PearlNavigationController.m in Sources */,
93D397952F5635C793C24DF1 /* NSError+PearlFullDescription.m in Sources */,
DA2CA4DF18D28859007798F8 /* NSTimer+PearlBlock.m in Sources */,
@@ -3362,24 +3479,21 @@
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
- DA4425D41557BED40052177D /* Debug-iOS */ = {
+ DA32D02E19D111C7004F3F0E /* Debug-iOS */ = {
isa = XCBuildConfiguration;
buildSettings = {
- CLANG_ENABLE_OBJC_ARC = YES;
};
name = "Debug-iOS";
};
- DA4425D51557BED40052177D /* AdHoc-iOS */ = {
+ DA32D02F19D111C7004F3F0E /* AdHoc-iOS */ = {
isa = XCBuildConfiguration;
buildSettings = {
- CLANG_ENABLE_OBJC_ARC = YES;
};
name = "AdHoc-iOS";
};
- DA4425D61557BED40052177D /* AppStore-iOS */ = {
+ DA32D03019D111C7004F3F0E /* AppStore-iOS */ = {
isa = XCBuildConfiguration;
buildSettings = {
- CLANG_ENABLE_OBJC_ARC = YES;
};
name = "AppStore-iOS";
};
@@ -3444,7 +3558,7 @@
GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = NO;
- GCC_WARN_UNKNOWN_PRAGMAS = YES;
+ GCC_WARN_UNKNOWN_PRAGMAS = NO;
GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_VALUE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
@@ -3523,7 +3637,7 @@
GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
- GCC_WARN_UNKNOWN_PRAGMAS = YES;
+ GCC_WARN_UNKNOWN_PRAGMAS = NO;
GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_VALUE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
@@ -3545,7 +3659,6 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
CLANG_ENABLE_OBJC_ARC = YES;
- CODE_SIGN_ENTITLEMENTS = MasterPassword.entitlements;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/../../../External/iOS\"",
@@ -3556,7 +3669,6 @@
LIBRARY_SEARCH_PATHS = (
"\"$(SRCROOT)/../../../External/Pearl/Pearl-Crypto/lib\"",
"$(inherited)",
- "/Users/lhunath/Documents/workspace/lyndir/MasterPassword/External/Pearl/Pearl-Crypto/lib",
);
OTHER_LDFLAGS = (
"$(inherited)",
@@ -3564,7 +3676,7 @@
Reveal,
);
PROVISIONING_PROFILE = "";
- "PROVISIONING_PROFILE[sdk=iphoneos*]" = "94333D7F-68F7-473D-B3B1-86AA41F33449";
+ "PROVISIONING_PROFILE[sdk=iphoneos*]" = "78fbee53-abe7-4a47-b917-c223df3a6952";
SKIP_INSTALL = NO;
TARGETED_DEVICE_FAMILY = 1;
};
@@ -3576,7 +3688,6 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
CLANG_ENABLE_OBJC_ARC = YES;
- CODE_SIGN_ENTITLEMENTS = MasterPassword.entitlements;
COPY_PHASE_STRIP = YES;
EXCLUDED_SOURCE_FILE_NAMES = libDCIntrospect.a;
FRAMEWORK_SEARCH_PATHS = (
@@ -3589,10 +3700,9 @@
LIBRARY_SEARCH_PATHS = (
"\"$(SRCROOT)/../../../External/Pearl/Pearl-Crypto/lib\"",
"$(inherited)",
- "/Users/lhunath/Documents/workspace/lyndir/MasterPassword/External/Pearl/Pearl-Crypto/lib",
);
PROVISIONING_PROFILE = "";
- "PROVISIONING_PROFILE[sdk=iphoneos*]" = "923E2981-1F88-4F05-9408-9EC085C53225";
+ "PROVISIONING_PROFILE[sdk=iphoneos*]" = "7142c408-252a-43c0-94c6-1ae1f43173f4";
SKIP_INSTALL = NO;
STRIP_INSTALLED_PRODUCT = YES;
TARGETED_DEVICE_FAMILY = 1;
@@ -3663,7 +3773,7 @@
GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
- GCC_WARN_UNKNOWN_PRAGMAS = YES;
+ GCC_WARN_UNKNOWN_PRAGMAS = NO;
GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_VALUE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
@@ -3685,7 +3795,6 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
CLANG_ENABLE_OBJC_ARC = YES;
- CODE_SIGN_ENTITLEMENTS = MasterPassword.entitlements;
COPY_PHASE_STRIP = YES;
EXCLUDED_SOURCE_FILE_NAMES = libDCIntrospect.a;
FRAMEWORK_SEARCH_PATHS = (
@@ -3698,10 +3807,9 @@
LIBRARY_SEARCH_PATHS = (
"\"$(SRCROOT)/../../../External/Pearl/Pearl-Crypto/lib\"",
"$(inherited)",
- "/Users/lhunath/Documents/workspace/lyndir/MasterPassword/External/Pearl/Pearl-Crypto/lib",
);
PROVISIONING_PROFILE = "";
- "PROVISIONING_PROFILE[sdk=iphoneos*]" = "36641E9D-D5E0-4E80-94F4-B2CF898CFE10";
+ "PROVISIONING_PROFILE[sdk=iphoneos*]" = "48f9bae8-b80e-41c7-8792-663102bed54f";
SKIP_INSTALL = NO;
STRIP_INSTALLED_PRODUCT = YES;
TARGETED_DEVICE_FAMILY = 1;
@@ -3740,11 +3848,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_ARC = NO;
- DSTROOT = /tmp/jrswizzle.dst;
GCC_WARN_INHIBIT_ALL_WARNINGS = YES;
- OTHER_LDFLAGS = "-ObjC";
- PRODUCT_NAME = "$(TARGET_NAME)";
- SKIP_INSTALL = YES;
};
name = "AppStore-iOS";
};
@@ -3776,11 +3880,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_ARC = NO;
- DSTROOT = /tmp/jrswizzle.dst;
GCC_WARN_INHIBIT_ALL_WARNINGS = YES;
- OTHER_LDFLAGS = "-ObjC";
- PRODUCT_NAME = "$(TARGET_NAME)";
- SKIP_INSTALL = YES;
};
name = "Debug-iOS";
};
@@ -3788,11 +3888,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_ARC = NO;
- DSTROOT = /tmp/jrswizzle.dst;
GCC_WARN_INHIBIT_ALL_WARNINGS = YES;
- OTHER_LDFLAGS = "-ObjC";
- PRODUCT_NAME = "$(TARGET_NAME)";
- SKIP_INSTALL = YES;
};
name = "AdHoc-iOS";
};
@@ -3855,15 +3951,14 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
- DA4425D31557BED40052177D /* Build configuration list for PBXNativeTarget "UbiquityStoreManager" */ = {
+ DA32D03419D111C7004F3F0E /* Build configuration list for PBXNativeTarget "KCOrderedAccessorFix" */ = {
isa = XCConfigurationList;
buildConfigurations = (
- DA4425D41557BED40052177D /* Debug-iOS */,
- DA4425D51557BED40052177D /* AdHoc-iOS */,
- DA4425D61557BED40052177D /* AppStore-iOS */,
+ DA32D02E19D111C7004F3F0E /* Debug-iOS */,
+ DA32D02F19D111C7004F3F0E /* AdHoc-iOS */,
+ DA32D03019D111C7004F3F0E /* AppStore-iOS */,
);
defaultConfigurationIsVisible = 0;
- defaultConfigurationName = "AdHoc-iOS";
};
DA5BFA3E147E415C00F98B1E /* Build configuration list for PBXProject "MasterPassword-iOS" */ = {
isa = XCConfigurationList;
@@ -3928,15 +4023,17 @@
/* End XCConfigurationList section */
/* Begin XCVersionGroup section */
- DABD3BD01711E2DC00CF925C /* MasterPassword.xcdatamodeld */ = {
+ DA32D00119CF4735004F3F0E /* MasterPassword.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
- DABD3BD11711E2DC00CF925C /* MasterPassword 1.xcdatamodel */,
- DABD3BD21711E2DC00CF925C /* MasterPassword 2.xcdatamodel */,
- DABD3BD31711E2DC00CF925C /* MasterPassword 3.xcdatamodel */,
- DABD3BD41711E2DC00CF925C /* MasterPassword 4.xcdatamodel */,
+ DA32D00219CF4735004F3F0E /* MasterPassword 1.xcdatamodel */,
+ DA32D00319CF4735004F3F0E /* MasterPassword 2.xcdatamodel */,
+ DA32D00419CF4735004F3F0E /* MasterPassword 3.xcdatamodel */,
+ DA32D00519CF4735004F3F0E /* MasterPassword 4.xcdatamodel */,
+ DA32D00619CF4735004F3F0E /* MasterPassword 5.xcdatamodel */,
+ DA32D00719CF4735004F3F0E /* MasterPassword 6.xcdatamodel */,
);
- currentVersion = DABD3BD41711E2DC00CF925C /* MasterPassword 4.xcdatamodel */;
+ currentVersion = DA32D00719CF4735004F3F0E /* MasterPassword 6.xcdatamodel */;
path = MasterPassword.xcdatamodeld;
sourceTree = "";
versionGroupType = wrapper.xcdatamodel;
diff --git a/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/xcshareddata/xcschemes/MasterPassword iOS (App Store).xcscheme b/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/xcshareddata/xcschemes/MasterPassword iOS (App Store).xcscheme
index efa42669..45d51de9 100644
--- a/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/xcshareddata/xcschemes/MasterPassword iOS (App Store).xcscheme
+++ b/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/xcshareddata/xcschemes/MasterPassword iOS (App Store).xcscheme
@@ -1,6 +1,6 @@
-
- com.apple.developer.ubiquity-container-identifiers
-
- $(TeamIdentifierPrefix)com.lyndir.lhunath.MasterPassword
- $(TeamIdentifierPrefix)com.lyndir.lhunath.MasterPassword.shared
-
- com.apple.developer.ubiquity-kvstore-identifier
- $(TeamIdentifierPrefix)com.lyndir.lhunath.MasterPassword.shared
- keychain-access-groups
-
- $(AppIdentifierPrefix)com.lyndir.lhunath.MasterPassword
-
-
+
diff --git a/MasterPassword/ObjC/iOS/Settings.bundle/Root.plist b/MasterPassword/ObjC/iOS/Settings.bundle/Root.plist
index ca7a66a0..7ef64c7d 100644
--- a/MasterPassword/ObjC/iOS/Settings.bundle/Root.plist
+++ b/MasterPassword/ObjC/iOS/Settings.bundle/Root.plist
@@ -146,24 +146,6 @@ To see a site's password anyway, tap and hold your finger down for a while
2
-
- Type
- PSGroupSpecifier
- Title
-
- FooterText
- Synchronizes your sites with your other Apple devices. It's also a good way of keeping automatic backups.
-
-
- Type
- PSToggleSwitchSpecifier
- Title
- iCloud
- Key
- iCloudEnabled
- DefaultValue
-
-
Type
PSGroupSpecifier
diff --git a/MasterPassword/ObjC/iOS/Storyboard.storyboard b/MasterPassword/ObjC/iOS/Storyboard.storyboard
index 7f78b9b8..f1d5a2e6 100644
--- a/MasterPassword/ObjC/iOS/Storyboard.storyboard
+++ b/MasterPassword/ObjC/iOS/Storyboard.storyboard
@@ -1,11 +1,107 @@
-
+
-
+
+
+
+
+ Exo2.0-Bold
+ Exo2.0-Bold
+ Exo2.0-Bold
+ Exo2.0-Bold
+ Exo2.0-Bold
+ Exo2.0-Bold
+ Exo2.0-Bold
+ Exo2.0-Bold
+ Exo2.0-Bold
+ Exo2.0-Bold
+ Exo2.0-Bold
+ Exo2.0-Bold
+ Exo2.0-Bold
+ Exo2.0-Bold
+ Exo2.0-Bold
+ Exo2.0-Bold
+ Exo2.0-Bold
+ Exo2.0-Bold
+ Exo2.0-Bold
+
+
+ Exo2.0-ExtraBold
+
+
+ Exo2.0-Regular
+ Exo2.0-Regular
+ Exo2.0-Regular
+ Exo2.0-Regular
+ Exo2.0-Regular
+ Exo2.0-Regular
+ Exo2.0-Regular
+ Exo2.0-Regular
+ Exo2.0-Regular
+ Exo2.0-Regular
+ Exo2.0-Regular
+ Exo2.0-Regular
+ Exo2.0-Regular
+ Exo2.0-Regular
+ Exo2.0-Regular
+ Exo2.0-Regular
+ Exo2.0-Regular
+ Exo2.0-Regular
+ Exo2.0-Regular
+ Exo2.0-Regular
+ Exo2.0-Regular
+ Exo2.0-Regular
+ Exo2.0-Regular
+ Exo2.0-Regular
+ Exo2.0-Regular
+ Exo2.0-Regular
+ Exo2.0-Regular
+ Exo2.0-Regular
+ Exo2.0-Regular
+ Exo2.0-Regular
+ Exo2.0-Regular
+ Exo2.0-Regular
+
+
+ Exo2.0-Thin
+ Exo2.0-Thin
+ Exo2.0-Thin
+ Exo2.0-Thin
+ Exo2.0-Thin
+ Exo2.0-Thin
+ Exo2.0-Thin
+ Exo2.0-Thin
+ Exo2.0-Thin
+ Exo2.0-Thin
+ Exo2.0-Thin
+ Exo2.0-Thin
+ Exo2.0-Thin
+ Exo2.0-Thin
+ Exo2.0-Thin
+ Exo2.0-Thin
+ Exo2.0-Thin
+
+
+ SourceCodePro-Black
+ SourceCodePro-Black
+ SourceCodePro-Black
+ SourceCodePro-Black
+ SourceCodePro-Black
+
+
+ SourceCodePro-ExtraLight
+ SourceCodePro-ExtraLight
+ SourceCodePro-ExtraLight
+ SourceCodePro-ExtraLight
+
+
+ SourceCodePro-Regular
+
+
@@ -16,45 +112,55 @@
-
+
+
+
+
+
+
+
+
+
-
-
+
-
-
+
-
-
+
-
+
-
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
+
-
-
+
@@ -62,12 +168,10 @@
-
-
+
-
-
-
+
+
@@ -85,23 +189,29 @@
+
+
-
+
-
+
+
+
+
+
-
+
-
+
@@ -110,11 +220,11 @@
-
+
-
+
@@ -123,8 +233,7 @@
-
-
+
@@ -137,8 +246,7 @@
-
-
+
@@ -151,51 +259,43 @@
-
-
+
-
-
-
+
+
-
-
+
-
-
+
-
+
-
-
+
-
-
+
-
-
-
+
+
-
-
-
-
+
+
+
@@ -237,14 +337,13 @@
-
-
+
-
-
-
+
+
+
@@ -252,18 +351,15 @@
-
-
+
-
-
+
-
-
-
+
+
@@ -295,16 +391,14 @@
-
-
+
-
@@ -314,10 +408,15 @@
+
+
+
+
+
-
+
@@ -350,7 +449,7 @@
-
+
@@ -361,20 +460,18 @@
-
+
-
-
+
-
-
+
@@ -405,7 +502,7 @@
-
+
@@ -416,14 +513,36 @@
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -434,16 +553,14 @@
-
+
-
-
+
-
-
+
@@ -464,13 +581,13 @@
-
+
-
+
@@ -485,18 +602,19 @@
+
-
+
-
+
-
-
+
+
@@ -512,15 +630,13 @@
-
-
+
-
-
+
@@ -546,15 +662,13 @@
-
-
+
-
-
+
@@ -580,22 +694,19 @@
-
-
+
-
-
+
-
-
+
@@ -610,8 +721,7 @@
-
-
+
@@ -646,22 +756,19 @@
-
-
+
-
-
+
-
-
+
@@ -670,7 +777,6 @@
-
@@ -684,8 +790,7 @@
-
-
+
@@ -726,22 +831,19 @@
-
-
+
-
-
+
-
-
+
This will store your master password in your device's keychain. As a result, you'll be able to log in without entering your master password. This is somewhat less secure in the event of theft: anyone who figures out your device's PIN can get in. You can compensate for the reduced security by going into Settings and navigating: General->Passcode Lock, disabling "Simple Passcode", and setting a more secure passcode for your device.
@@ -770,15 +872,13 @@
-
-
+
-
-
+
@@ -804,15 +904,13 @@
-
-
+
-
-
+
@@ -838,15 +936,13 @@
-
-
+
-
-
+
An export saves all your sites and optionally their passwords to a file that you can store separately. It can function as a back-up or a way to move all your sites to a new device.
@@ -873,15 +969,13 @@
-
-
+
-
-
+
@@ -891,8 +985,7 @@
-
-
+
@@ -902,8 +995,7 @@
-
-
+
@@ -913,8 +1005,7 @@
-
-
+
@@ -955,7 +1046,7 @@
-
+
@@ -970,7 +1061,7 @@
-
+
@@ -981,20 +1072,17 @@
-
+
-
-
+
-
-
+
-
-
+
@@ -1013,109 +1101,137 @@
-
-
-
+
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
@@ -1127,14 +1243,33 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
-
+
@@ -1142,7 +1277,6 @@
-
@@ -1164,7 +1298,6 @@
-
@@ -1185,7 +1318,6 @@
-
@@ -1206,7 +1338,6 @@
-
@@ -1228,9 +1359,11 @@
+
+
@@ -1242,7 +1375,6 @@
-
@@ -1262,18 +1394,12 @@
-
-
-
-
+
+
+
-
-
-
-
-
@@ -1281,12 +1407,11 @@
-
-
+
@@ -1302,30 +1427,25 @@
-
-
-
+
-
+
-
-
-
+
+
-
-
-
+
+
-
@@ -1343,25 +1463,26 @@
+
+
-
+
+
-
-
-
+
-
+
@@ -1379,13 +1500,12 @@
-
-
+
-
+
@@ -1401,35 +1521,29 @@
-
-
+
-
-
-
+
+
-
-
+
-
-
+
-
-
+
-
-
+
@@ -1515,7 +1629,7 @@
-
+
@@ -1525,257 +1639,220 @@
-
-
-
+
+
+
-
-
-
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
-
-
+
+
+
+
+
@@ -1792,7 +1869,7 @@
-
+
@@ -1803,9 +1880,9 @@
-
+
-
+
@@ -1819,14 +1896,14 @@
-
+
-
+
-
+
@@ -1854,9 +1931,9 @@
-
+
-
+
@@ -1865,12 +1942,11 @@
-
+
-
-
+
@@ -1882,12 +1958,10 @@
-
-
+
-
-
+
@@ -1897,7 +1971,7 @@
-
+
@@ -1905,7 +1979,6 @@
-
@@ -1928,19 +2001,17 @@
-
-
-
+
+
-
-
+
CgoKCgoKCgoKCgoKCg
-
+
@@ -1973,6 +2044,7 @@ CgoKCgoKCgoKCgoKCg
+
@@ -1982,27 +2054,9 @@ CgoKCgoKCgoKCgoKCg
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -2011,48 +2065,24 @@ CgoKCgoKCgoKCgoKCg
-
+
-
-
+
- 119-20:51:52 MPiOSAppDelegate.m:36 | INFO : Initializing TestFlight
-119-20:51:52 MPiOSAppDelegate.m:80 | INFO : Initializing Crashlytics
-119-20:51:53 PearlAppDelegate.m:71 | INFO : Master Password (MasterPassword) 1.4 (1.4.0) (GIT: 1.4-0-g8a4eecd-dirty)
-119-20:51:53 MPiOSAppDelegate.m:257 | INFO : Started up with device identifier: A8C51CDA-6F60-4F0C-BFC9-68A08F2F2DD7
-119-20:51:59 MPAppDelegate_Store.m:278 | DEBUG : [StoreManager] (Re)loading store...
-119-20:51:59 MPAppDelegate_Store.m:278 | DEBUG : [StoreManager] Will load cloud store: 0B3CA2DF-5796-44DF-B5E0-121EC3846464 (definite).
-119-20:51:59 PearlConfig.m:193 | INFO : Lock screen will appear
-119-20:51:59 MPiOSAppDelegate.m:412 | INFO : Re-activated
-119-20:51:59 PearlConfig.m:180 | DEBUG : MPiOSConfig.launchCount = [70 ->] 71
-119-20:52:02 MPAppDelegate_Store.m:278 | DEBUG : [StoreManager] Clearing stores...
-119-20:52:03 MPAppDelegate_Store.m:278 | DEBUG : [StoreManager] Loading store without seeding.
-119-20:52:09 MPAppDelegate_Store.m:278 | DEBUG : [StoreManager] Cloud enabled and successfully loaded cloud store.
-119-20:52:09 MPAppDelegate_Store.m:299 | INFO : Using iCloud? 1
-119-20:52:12 MPAppDelegate_Key.m:28 | INFO : Found key in keychain for: b55911588b178466be1d6392597e899b8de46f9a
-119-20:52:12 MPAppDelegate_Key.m:132 | INFO : Logged in: b55911588b178466be1d6392597e899b8de46f9a
-119-20:52:13 MPUnlockViewController.m:229 | INFO : Lock screen will disappear
-119-20:52:13 MPMainViewController.m:142 | INFO : Main will appear
-119-20:52:16 MPMainViewController.m:734 | INFO : Action: Preferences
-119-20:52:17 MPMainViewController.m:187 | INFO : Main will disappear.
-
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris facilisis tortor leo, iaculis mollis elit dictum et. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In congue justo porta enim imperdiet, id luctus justo fringilla. Nunc nec sem id augue bibendum hendrerit eu ut eros. Ut fermentum augue quis nunc feugiat vehicula. Quisque in ultrices magna. Praesent quis mollis lectus. Sed fringilla massa vitae eros luctus, eget convallis justo pretium. Duis non tristique ante. Sed suscipit tortor ligula, sed fermentum eros sodales ut. Maecenas sed ante et orci posuere lobortis et sodales diam. Nunc non ullamcorper orci.
+
+Suspendisse potenti. Etiam ut nisi id augue tempor ultrices et sit amet sapien. Quisque fringilla ex dolor, at iaculis turpis facilisis et. Donec pellentesque quam vitae libero facilisis, quis scelerisque augue feugiat. Nam eros eros, posuere eget ultricies quis, maximus sodales velit. Aliquam euismod iaculis consectetur. Etiam dictum orci id dapibus fermentum. Vestibulum posuere tortor vitae viverra dictum. Morbi rutrum arcu mattis felis hendrerit pretium. Praesent rutrum euismod leo, in faucibus lectus rhoncus in. Vestibulum porttitor semper eros, eu dapibus risus tincidunt sed. Curabitur ex enim, ullamcorper nec laoreet id, consectetur et nisl. Etiam id pulvinar risus.
-
-
+
-
-
-
-
-
@@ -2101,7 +2131,7 @@ CgoKCgoKCgoKCgoKCg
-
+
@@ -2123,9 +2153,9 @@ CgoKCgoKCgoKCgoKCg
-
+
-
+
@@ -2134,42 +2164,36 @@ CgoKCgoKCgoKCgoKCg
-
+
-
-
+
-
-
+
-
-
+
-
-
-
+
+
-
-
+
-
-
+
The right balance between security and convenience is often very personal.
To make getting to your passwords faster, you can remain logged in after you close Master Password. This allows you to skip having to log in the next time.
However, it means that anyone who finds your device unlocked can do the same.
-
+
@@ -2217,9 +2241,9 @@ However, it means that anyone who finds your device unlocked can do the same.
-
+
-
+
@@ -2228,37 +2252,32 @@ However, it means that anyone who finds your device unlocked can do the same.
-
+
-
-
+
-
-
+
-
-
+
-
-
-
+
+
-
-
+
-
+
@@ -2266,7 +2285,7 @@ However, it means that anyone who finds your device unlocked can do the same.
-
+
@@ -2279,7 +2298,7 @@ You don't need to remember any password generated by this app, but
-
+
@@ -2287,7 +2306,7 @@ You don't need to remember any password generated by this app, but
-
+
@@ -2295,7 +2314,7 @@ You don't need to remember any password generated by this app, but
-
+
@@ -2310,7 +2329,7 @@ You don't need to remember any password generated by this app, but
-
+
@@ -2318,7 +2337,7 @@ You don't need to remember any password generated by this app, but
-
+
@@ -2331,7 +2350,7 @@ See
-
+
@@ -2339,7 +2358,7 @@ See
-
+
@@ -2348,7 +2367,7 @@ See
-
+
@@ -2398,25 +2417,535 @@ See
-
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Master Password is great for making your passwords immune to loss, and it even lets you save your login name. But saved login names can't be recovered when all is lost. Generated login names use the same algorithm to generate a good login name to use for each of your sites. In addition, using unique non-identifying login names for your sites helps protect against companies working together to build a bigger picture on your identity.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The infamous security questions are in fact a veritable assault on your security. For starters, they weaken the protection of your account by allowing an attacker in by simply knowing enough about you, allowing him to bypass guessing your strong password. Arguably worse is the amount of highly personal and private information you're "willingly" divulging to these websites, under the assumption that only you know the answers. Generating security answers allows you to stop sharing your highly personal details and weakening your acccounts by generating unguessable answers to these questions.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Up to now, the best way to use your passwords in other iOS apps was by copy/pasting them from Master Password. With iOS integration, you can access your Master Password passwords directly from other applications, without having to switch back and forth between it and Master Password.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Since the iPhone 5s, all iPhones are now equipped with an advanced Touch ID fingerprint sensor in the home button. This sensor allows you to quickly and easily identify yourself to the system. With Touch ID support, you will be able to skip the step of entering your Master Password manually and gain the ability to unlock your user using Touch ID instead.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -2426,29 +2955,32 @@ See
-
+
+
+
+
-
-
+
+
diff --git a/MasterPassword/Resources/Data/ciphers.plist b/MasterPassword/Resources/Data/ciphers.plist
index 0c5f6472..3a64450c 100644
--- a/MasterPassword/Resources/Data/ciphers.plist
+++ b/MasterPassword/Resources/Data/ciphers.plist
@@ -2,8 +2,18 @@
- MPElementGeneratedEntity
+ MPGeneratedSiteEntity
+ Login Name
+
+ cvccvcvcv
+
+ Phrase
+
+ cvcc cvc cvccvcv cvc
+ cvc cvccvcvcv cvcv
+ cv cvccv cvc cvcvccv
+
Maximum Security Password
anoxxxxxxxxxxxxxxxxx
@@ -73,6 +83,8 @@
@&%?,=[]_:-+*$#!'^~;()/.
x
AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()
+
+
diff --git a/MasterPassword/Resources/Media/Fonts/SourceCodePro-Regular.otf b/MasterPassword/Resources/Media/Fonts/SourceCodePro-Regular.otf
new file mode 100644
index 00000000..8f031da9
Binary files /dev/null and b/MasterPassword/Resources/Media/Fonts/SourceCodePro-Regular.otf differ
diff --git a/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Contents.json b/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Contents.json
index c4b43412..9bf232b9 100644
--- a/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Contents.json
+++ b/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Contents.json
@@ -18,6 +18,11 @@
"filename" : "Icon-60@2x.png",
"scale" : "2x"
},
+ {
+ "idiom" : "iphone",
+ "size" : "60x60",
+ "scale" : "3x"
+ },
{
"size" : "29x29",
"idiom" : "ipad",
diff --git a/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-40.png b/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-40.png
index de0c2876..1440a52c 100644
Binary files a/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-40.png and b/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-40.png differ
diff --git a/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png b/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png
index 68d9e639..49782f32 100644
Binary files a/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png and b/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png differ
diff --git a/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-40~ipad.png b/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-40~ipad.png
index de0c2876..f15120d0 100644
Binary files a/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-40~ipad.png and b/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-40~ipad.png differ
diff --git a/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-40~ipad@2x.png b/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-40~ipad@2x.png
index 68d9e639..49782f32 100644
Binary files a/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-40~ipad@2x.png and b/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-40~ipad@2x.png differ
diff --git a/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-60.png b/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-60.png
index 350dbb1b..7abcac4b 100644
Binary files a/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-60.png and b/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-60.png differ
diff --git a/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png b/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png
index 408fa270..14fda56b 100644
Binary files a/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png and b/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png differ
diff --git a/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-76~ipad.png b/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-76~ipad.png
index 1fa3cb12..c61b5405 100644
Binary files a/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-76~ipad.png and b/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-76~ipad.png differ
diff --git a/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-76~ipad@2x.png b/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-76~ipad@2x.png
index 637a56b9..02754d64 100644
Binary files a/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-76~ipad@2x.png and b/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-76~ipad@2x.png differ
diff --git a/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-Small.png b/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-Small.png
index f9deb443..e2c69304 100644
Binary files a/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-Small.png and b/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-Small.png differ
diff --git a/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png b/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png
index a840c1e0..891c2215 100644
Binary files a/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png and b/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png differ
diff --git a/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-Small~ipad.png b/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-Small~ipad.png
index 65da0751..80f33ab4 100644
Binary files a/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-Small~ipad.png and b/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-Small~ipad.png differ
diff --git a/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-Small~ipad@2x.png b/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-Small~ipad@2x.png
index a840c1e0..891c2215 100644
Binary files a/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-Small~ipad@2x.png and b/MasterPassword/Resources/Media/Images.xcassets/AppIcon.appiconset/Icon-Small~ipad@2x.png differ
diff --git a/MasterPassword/Resources/Media/help.html b/MasterPassword/Resources/Media/help.html
deleted file mode 100644
index 9eb5ac78..00000000
--- a/MasterPassword/Resources/Media/help.html
+++ /dev/null
@@ -1,342 +0,0 @@
-
-
-
-
-
-
-
-
-
- Master Password
-
- © 2011
-
-
- — 1 —
-
- Find the site that you need a password for by entering its name into the search field .
-
-
- While searching , the names of previously used sites will be listed.
- Tap one of these results to go straight to its password.
-
-
- — 2 —
-
- The site 's password is now displayed.
- Tap it to copy the password . Once copied, you can switch to another application and paste it into a password field.
-
-
-
- To change the password for this site, tap the edit icon .
-
-
-
- Below the password you can set the password type . Some types create a password for you ,
- others let you choose your own .
-
-
-
- If the site complains when you try to set or update the password, try changing the password type.
-
-
- To create a new password for this site, you can increment the password counter .
- This is useful, for example, after you've had to share the password with somebody else.
-
-
- — F.A.Q. —
-
-
- What is it and how do I use it?
- Why do I need Master Password?
- A password was given to me.
- What if I loose my device?
- Am I dependant on my device?
- How do I maximize my security?
- A website I use got hacked!
- I forgot my master password!
- How does Master Password work?
- Do you offer enterprise solutions?
-
-
- What is Master Password and how do I use it?
-
- Master Password creates secure and unique passwords for you , so you don't have to.
- 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.
-
-
- Begin by entering the name of the thing you want a password for. Naming is entirely up to you, but remember to be consistent.
- Good names could be:
- apple.com
, john@doe.com
, office safe
, bike lock
, etc.
-
-
- Every name has a different password, so the following names may be difficult to recall :
- pw for amazon
, pin for my cell
, etc.
-
-
- Tap the resulting password to copy it for pasting in a different application or read it to type it in or use it manually elsewhere.
-
-
- The thought behind this application is to secure your online (and offline) life by changing all of your passwords
- to passwords generated by this app.
-
-
- That's crazy talk.
- Why would I do that?
-
- The theory of password authentication is simple: To log in to a site, you share a secret word with the site
- that only you and the site know . Since nobody else knows your secret password, nobody else can log
- into your account.
-
-
- It sounds good in theory. In practice, it's an absolute hell . 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.
-
-
- So, what? , you might say.
- 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
- other site. Nothing is stopping them from trying to log into your 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.
-
-
- 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
- password policies : suddenly your long password isn't good enough, because it begins with a number,
- or because (god forbid) it's too long . You now find yourself forced to create a strange variant
- of your password that you'll have forgotten before the day is out.
-
-
- This app solves the problem 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.
-
-
- I can't change all my passwords.
- Some of them were assigned to me.
-
- That's why this application allows you to change the password type to Personal
or Device
- Private
. 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.
-
-
- These types of stored 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.
-
-
- So, what if I lose my device?
- I'm locked out of everything?
-
- Absolutely not! 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, it'll give you all your
- generated passwords . 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.
-
-
- 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.
-
-
- Great, but that still means I need my device to get my passwords.
-
- 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.
-
-
- There is also a Mac version 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.
-
-
- I'm paranoid.
- How do I maximize my security?
-
- The most important aspect to the security of your passwords is your master password . Make sure
- you've chosen a long and unique master password . 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 longer than 10 characters .
- An absurd sentence is a great idea , 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.
-
-
- 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 Maximum Security
. 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 Long Password
really is secure enough for most any purpose, see
- What if a site I use gets hacked? ).
-
-
- Also check out the application's preferences (using the action icon on the top right, select Preferences
).
- Make sure that Save Password
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).
- If you go into Settings
from the Preferences
page, you'll see some global application settings.
- Make sure that Stay logged in
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.
-
-
- What if a site I use gets hacked?
-
- 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 hash 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.
- However, if your account is protected by a Long Password
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.
- If instead you used a Maximum Security
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.
-
-
- 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.
-
-
- 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 not using Master Password.
-
-
-
- I forgot my master password. What are my options?
-
- 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 gone .
-
-
- 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.
- Now don't forget it again! :-)
-
-
- So how does this thing work internally?
-
- The way Master Password works internally is fully disclosed .
- The source code for this application is also available from GitHub .
- I invite anyone with a technical background to go through these resources to make certain of the trustworthiness of Master Password.
-
-
- Is the algorithm stable?
- Will my passwords ever change?
-
- 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.
-
-
- 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.
-
-
- 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 option 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.
-
-
- Please note : 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.
-
-
- Tap here to check if you have any outdated passwords.
-
-
- This stuff is gold.
- I want one branded for our company.
-
- Contact me directly for enterprise inquiries.
- I can provide branded clients and enterprise distribution if your company is interested in deploying this solution internally.
-
-
- Master Password can also be used as a One-Time Password token generator to secure your infrastructure and client access.
-
-
-
-
-
-
diff --git a/MasterPassword/Resources/Media/ios-icon-256.png b/MasterPassword/Resources/Media/ios-icon-256.png
new file mode 100644
index 00000000..937bcffd
Binary files /dev/null and b/MasterPassword/Resources/Media/ios-icon-256.png differ
diff --git a/MasterPassword/Resources/Media/ios-icon.png b/MasterPassword/Resources/Media/ios-icon.png
index 4aba9c1b..5b0398ba 100644
Binary files a/MasterPassword/Resources/Media/ios-icon.png and b/MasterPassword/Resources/Media/ios-icon.png differ
diff --git a/MasterPassword/Resources/Media/ios-icon@2x.png b/MasterPassword/Resources/Media/ios-icon@2x.png
index dbd707c9..98032dc3 100644
Binary files a/MasterPassword/Resources/Media/ios-icon@2x.png and b/MasterPassword/Resources/Media/ios-icon@2x.png differ
diff --git a/MasterPassword/Resources/Media/mac-icon-256.png b/MasterPassword/Resources/Media/mac-icon-256.png
new file mode 100644
index 00000000..d98bf5e9
Binary files /dev/null and b/MasterPassword/Resources/Media/mac-icon-256.png differ
diff --git a/MasterPassword/Resources/Media/mac-icon.png b/MasterPassword/Resources/Media/mac-icon.png
index 9c44abf6..537b8d7a 100644
Binary files a/MasterPassword/Resources/Media/mac-icon.png and b/MasterPassword/Resources/Media/mac-icon.png differ
diff --git a/MasterPassword/Resources/Media/mac-icon@2x.png b/MasterPassword/Resources/Media/mac-icon@2x.png
index b7d560e6..208c00ed 100644
Binary files a/MasterPassword/Resources/Media/mac-icon@2x.png and b/MasterPassword/Resources/Media/mac-icon@2x.png differ
diff --git a/MasterPassword/Resources/Media/thumb_generated_answers.png b/MasterPassword/Resources/Media/thumb_generated_answers.png
new file mode 100644
index 00000000..32db5649
Binary files /dev/null and b/MasterPassword/Resources/Media/thumb_generated_answers.png differ
diff --git a/MasterPassword/Resources/Media/thumb_generated_answers@2x.png b/MasterPassword/Resources/Media/thumb_generated_answers@2x.png
new file mode 100644
index 00000000..a644a320
Binary files /dev/null and b/MasterPassword/Resources/Media/thumb_generated_answers@2x.png differ
diff --git a/MasterPassword/Resources/Media/thumb_generated_answers@3x.png b/MasterPassword/Resources/Media/thumb_generated_answers@3x.png
new file mode 100644
index 00000000..64724312
Binary files /dev/null and b/MasterPassword/Resources/Media/thumb_generated_answers@3x.png differ
diff --git a/MasterPassword/Resources/Media/thumb_generated_login.png b/MasterPassword/Resources/Media/thumb_generated_login.png
new file mode 100644
index 00000000..3b0d4b3d
Binary files /dev/null and b/MasterPassword/Resources/Media/thumb_generated_login.png differ
diff --git a/MasterPassword/Resources/Media/thumb_generated_login@2x.png b/MasterPassword/Resources/Media/thumb_generated_login@2x.png
new file mode 100644
index 00000000..401a735f
Binary files /dev/null and b/MasterPassword/Resources/Media/thumb_generated_login@2x.png differ
diff --git a/MasterPassword/Resources/Media/thumb_generated_login@3x.png b/MasterPassword/Resources/Media/thumb_generated_login@3x.png
new file mode 100644
index 00000000..187054d1
Binary files /dev/null and b/MasterPassword/Resources/Media/thumb_generated_login@3x.png differ
diff --git a/MasterPassword/Resources/Raw/MasterPassword.sketch/Data b/MasterPassword/Resources/Raw/MasterPassword.sketch/Data
index d867b8eb..81e5dc85 100644
Binary files a/MasterPassword/Resources/Raw/MasterPassword.sketch/Data and b/MasterPassword/Resources/Raw/MasterPassword.sketch/Data differ
diff --git a/MasterPassword/Resources/Raw/MasterPassword.sketch/metadata b/MasterPassword/Resources/Raw/MasterPassword.sketch/metadata
index 2959dfa6..08076e51 100644
--- a/MasterPassword/Resources/Raw/MasterPassword.sketch/metadata
+++ b/MasterPassword/Resources/Raw/MasterPassword.sketch/metadata
@@ -17,7 +17,7 @@
Inconsolata
length
- 101442
+ 145599
version
37
diff --git a/MasterPassword/Resources/Raw/Store_Thumb.psd b/MasterPassword/Resources/Raw/Store_Thumb.psd
new file mode 100644
index 00000000..7ca264b4
Binary files /dev/null and b/MasterPassword/Resources/Raw/Store_Thumb.psd differ
diff --git a/Site/2013-05/algorithm.html b/Site/2013-05/algorithm.html
index 9cb3f83c..eadd1be2 100644
--- a/Site/2013-05/algorithm.html
+++ b/Site/2013-05/algorithm.html
@@ -356,44 +356,8 @@ passWord[i] = passChar
-
-
-
-
-
+
+
-
-
-
-
-
+
+
@@ -86,7 +86,7 @@
Get it for:
iPhone / iPad |
- Mac |
+ Mac |
Desktop (Java) |
Terminal (Java /
Native C ) |
@@ -175,44 +175,8 @@
-
-
-
-
-
+
+
-
-
-
-
-
+
+
-
-
-
-
-
+
+
-
-
-
-
-
+
+
-
-
-
-
-
+
+