Merge branch 'master' of github.com:Lyndir/MasterPassword
Conflicts: MasterPassword/ObjC/iOS/MPPasswordsViewController.m
This commit is contained in:
commit
a2e71aa94d
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -10,3 +10,6 @@
|
|||||||
[submodule "External/RHStatusItemView"]
|
[submodule "External/RHStatusItemView"]
|
||||||
path = External/RHStatusItemView
|
path = External/RHStatusItemView
|
||||||
url = git://github.com/lhunath/RHStatusItemView.git
|
url = git://github.com/lhunath/RHStatusItemView.git
|
||||||
|
[submodule "External/KCOrderedAccessorFix"]
|
||||||
|
path = External/KCOrderedAccessorFix
|
||||||
|
url = https://github.com/CFKevinRef/KCOrderedAccessorFix.git
|
||||||
|
1
External/KCOrderedAccessorFix
vendored
Submodule
1
External/KCOrderedAccessorFix
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit e1955221bf52d53736e7d3e7d38465c509e02562
|
6
External/iOS/Crashlytics.framework/Modules/module.modulemap
vendored
Normal file
6
External/iOS/Crashlytics.framework/Modules/module.modulemap
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
framework module Crashlytics {
|
||||||
|
umbrella header "Crashlytics.h"
|
||||||
|
|
||||||
|
export *
|
||||||
|
module * { export * }
|
||||||
|
}
|
Binary file not shown.
@ -15,13 +15,13 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>FMWK</string>
|
<string>FMWK</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>2.2.2</string>
|
<string>2.2.4</string>
|
||||||
<key>CFBundleSupportedPlatforms</key>
|
<key>CFBundleSupportedPlatforms</key>
|
||||||
<array>
|
<array>
|
||||||
<string>iPhoneOS</string>
|
<string>iPhoneOS</string>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>36</string>
|
<string>38</string>
|
||||||
<key>DTPlatformName</key>
|
<key>DTPlatformName</key>
|
||||||
<string>iphoneos</string>
|
<string>iphoneos</string>
|
||||||
<key>MinimumOSVersion</key>
|
<key>MinimumOSVersion</key>
|
||||||
|
BIN
External/iOS/Crashlytics.framework/run
vendored
BIN
External/iOS/Crashlytics.framework/run
vendored
Binary file not shown.
BIN
External/iOS/Crashlytics.framework/submit
vendored
BIN
External/iOS/Crashlytics.framework/submit
vendored
Binary file not shown.
BIN
External/iOS/Reveal.framework/Versions/A/Reveal
vendored
BIN
External/iOS/Reveal.framework/Versions/A/Reveal
vendored
Binary file not shown.
@ -10,57 +10,73 @@
|
|||||||
<string>MasterPassword</string>
|
<string>MasterPassword</string>
|
||||||
<key>IDESourceControlProjectOriginsDictionary</key>
|
<key>IDESourceControlProjectOriginsDictionary</key>
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>1712FC0BC3C9AABD8B7B5376E310E93FBDB3BCFA</key>
|
||||||
|
<string>git://github.com/lhunath/InAppSettingsKit.git</string>
|
||||||
<key>1AA8C0BE-EEC3-4FBC-A801-8939A1AC093A</key>
|
<key>1AA8C0BE-EEC3-4FBC-A801-8939A1AC093A</key>
|
||||||
<string>git://github.com/Lyndir/love-lyndir.client.git</string>
|
<string>git://github.com/Lyndir/love-lyndir.client.git</string>
|
||||||
<key>42C94803-87A2-403E-896C-D9AC3A807E1B</key>
|
<key>2A70319CE0F91B35406CA7D970AE7CB4957B0A75</key>
|
||||||
<string>git://github.com/lhunath/UbiquityStoreManager.git</string>
|
<string>github.com:Lyndir/Lyndir.git</string>
|
||||||
<key>6A449EC2-A2A3-4635-9C5F-A811E011EAC3</key>
|
<key>2FE140B36B7D26140DC8D5E5C639DC5900EFCF35</key>
|
||||||
<string>ssh://github.com/Lyndir/MasterPassword.git</string>
|
|
||||||
<key>ADA0D7F9-4871-4128-8FEE-FD1021EEF3AC</key>
|
|
||||||
<string>ssh://github.com/Lyndir/Pearl.git</string>
|
|
||||||
<key>AE3786C7-912B-4651-A73F-2E1DACBFB604</key>
|
|
||||||
<string>git://github.com/lhunath/uicolor-utilities.git</string>
|
<string>git://github.com/lhunath/uicolor-utilities.git</string>
|
||||||
<key>B0F634DD-AEE1-4F0D-AE35-4FAF51AD1B5A</key>
|
<key>304AD0F97EA7B4893D91DFB8C3413D4E627B9472</key>
|
||||||
|
<string>https://github.com/CFKevinRef/KCOrderedAccessorFix.git</string>
|
||||||
|
<key>3E67FB08419C920516AAC3B00DAAF23073B8CF77</key>
|
||||||
<string>git://github.com/lhunath/RHStatusItemView.git</string>
|
<string>git://github.com/lhunath/RHStatusItemView.git</string>
|
||||||
<key>CDDE92CF-0136-4DE0-8318-80EDB5C8CAF9</key>
|
<key>4DDCFFD91B41F00326AD14553BD66CFD366ABD91</key>
|
||||||
<string>git://github.com/lhunath/InAppSettingsKit.git</string>
|
<string>ssh://github.com/Lyndir/Pearl.git</string>
|
||||||
<key>E4C8E206-229C-4DA8-A130-0C544DEC7E07</key>
|
<key>8A15A8EA0B3D0B497C4883425BC74DF995224BB3</key>
|
||||||
<string>git://github.com/jonmarimba/jrswizzle.git</string>
|
<string>git://github.com/jonmarimba/jrswizzle.git</string>
|
||||||
|
<key>E47DEC29CB0D0FDE3560EF46E1808FA1C723D657</key>
|
||||||
|
<string>git://github.com/lhunath/UbiquityStoreManager.git</string>
|
||||||
|
<key>F788B28042EDBEF29EFE34687DA79A778C2CC260</key>
|
||||||
|
<string>ssh://github.com/Lyndir/MasterPassword.git</string>
|
||||||
</dict>
|
</dict>
|
||||||
<key>IDESourceControlProjectPath</key>
|
<key>IDESourceControlProjectPath</key>
|
||||||
<string>MasterPassword.xcworkspace</string>
|
<string>MasterPassword.xcworkspace</string>
|
||||||
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
|
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>1712FC0BC3C9AABD8B7B5376E310E93FBDB3BCFA</key>
|
||||||
|
<string>../External/InAppSettingsKit</string>
|
||||||
<key>1AA8C0BE-EEC3-4FBC-A801-8939A1AC093A</key>
|
<key>1AA8C0BE-EEC3-4FBC-A801-8939A1AC093A</key>
|
||||||
<string>../External/LoveLyndir</string>
|
<string>../External/LoveLyndir</string>
|
||||||
<key>42C94803-87A2-403E-896C-D9AC3A807E1B</key>
|
<key>2A70319CE0F91B35406CA7D970AE7CB4957B0A75</key>
|
||||||
<string>../External/UbiquityStoreManager</string>
|
<string>../..</string>
|
||||||
<key>6A449EC2-A2A3-4635-9C5F-A811E011EAC3</key>
|
<key>2FE140B36B7D26140DC8D5E5C639DC5900EFCF35</key>
|
||||||
<string>..</string>
|
|
||||||
<key>ADA0D7F9-4871-4128-8FEE-FD1021EEF3AC</key>
|
|
||||||
<string>../External/Pearl</string>
|
|
||||||
<key>AE3786C7-912B-4651-A73F-2E1DACBFB604</key>
|
|
||||||
<string>../External/Pearl/External/uicolor-utilities</string>
|
<string>../External/Pearl/External/uicolor-utilities</string>
|
||||||
<key>B0F634DD-AEE1-4F0D-AE35-4FAF51AD1B5A</key>
|
<key>304AD0F97EA7B4893D91DFB8C3413D4E627B9472</key>
|
||||||
|
<string>../External/KCOrderedAccessorFix/</string>
|
||||||
|
<key>3E67FB08419C920516AAC3B00DAAF23073B8CF77</key>
|
||||||
<string>../External/RHStatusItemView</string>
|
<string>../External/RHStatusItemView</string>
|
||||||
<key>CDDE92CF-0136-4DE0-8318-80EDB5C8CAF9</key>
|
<key>4DDCFFD91B41F00326AD14553BD66CFD366ABD91</key>
|
||||||
<string>../External/InAppSettingsKit</string>
|
<string>../External/Pearl</string>
|
||||||
<key>E4C8E206-229C-4DA8-A130-0C544DEC7E07</key>
|
<key>8A15A8EA0B3D0B497C4883425BC74DF995224BB3</key>
|
||||||
<string>../External/Pearl/External/jrswizzle</string>
|
<string>../External/Pearl/External/jrswizzle</string>
|
||||||
|
<key>E47DEC29CB0D0FDE3560EF46E1808FA1C723D657</key>
|
||||||
|
<string>../External/UbiquityStoreManager</string>
|
||||||
|
<key>F788B28042EDBEF29EFE34687DA79A778C2CC260</key>
|
||||||
|
<string>..</string>
|
||||||
</dict>
|
</dict>
|
||||||
<key>IDESourceControlProjectURL</key>
|
<key>IDESourceControlProjectURL</key>
|
||||||
<string>ssh://github.com/Lyndir/MasterPassword.git</string>
|
<string>ssh://github.com/Lyndir/MasterPassword.git</string>
|
||||||
<key>IDESourceControlProjectVersion</key>
|
<key>IDESourceControlProjectVersion</key>
|
||||||
<integer>110</integer>
|
<integer>111</integer>
|
||||||
<key>IDESourceControlProjectWCCIdentifier</key>
|
<key>IDESourceControlProjectWCCIdentifier</key>
|
||||||
<string>6A449EC2-A2A3-4635-9C5F-A811E011EAC3</string>
|
<string>F788B28042EDBEF29EFE34687DA79A778C2CC260</string>
|
||||||
<key>IDESourceControlProjectWCConfigurations</key>
|
<key>IDESourceControlProjectWCConfigurations</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||||
<string>public.vcs.git</string>
|
<string>public.vcs.git</string>
|
||||||
<key>IDESourceControlWCCIdentifierKey</key>
|
<key>IDESourceControlWCCIdentifierKey</key>
|
||||||
<string>CDDE92CF-0136-4DE0-8318-80EDB5C8CAF9</string>
|
<string>2A70319CE0F91B35406CA7D970AE7CB4957B0A75</string>
|
||||||
|
<key>IDESourceControlWCCName</key>
|
||||||
|
<string></string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||||
|
<string>public.vcs.git</string>
|
||||||
|
<key>IDESourceControlWCCIdentifierKey</key>
|
||||||
|
<string>1712FC0BC3C9AABD8B7B5376E310E93FBDB3BCFA</string>
|
||||||
<key>IDESourceControlWCCName</key>
|
<key>IDESourceControlWCCName</key>
|
||||||
<string>InAppSettingsKit</string>
|
<string>InAppSettingsKit</string>
|
||||||
</dict>
|
</dict>
|
||||||
@ -68,10 +84,18 @@
|
|||||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||||
<string>public.vcs.git</string>
|
<string>public.vcs.git</string>
|
||||||
<key>IDESourceControlWCCIdentifierKey</key>
|
<key>IDESourceControlWCCIdentifierKey</key>
|
||||||
<string>E4C8E206-229C-4DA8-A130-0C544DEC7E07</string>
|
<string>8A15A8EA0B3D0B497C4883425BC74DF995224BB3</string>
|
||||||
<key>IDESourceControlWCCName</key>
|
<key>IDESourceControlWCCName</key>
|
||||||
<string>jrswizzle</string>
|
<string>jrswizzle</string>
|
||||||
</dict>
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||||
|
<string>public.vcs.git</string>
|
||||||
|
<key>IDESourceControlWCCIdentifierKey</key>
|
||||||
|
<string>304AD0F97EA7B4893D91DFB8C3413D4E627B9472</string>
|
||||||
|
<key>IDESourceControlWCCName</key>
|
||||||
|
<string>KCOrderedAccessorFix</string>
|
||||||
|
</dict>
|
||||||
<dict>
|
<dict>
|
||||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||||
<string>public.vcs.git</string>
|
<string>public.vcs.git</string>
|
||||||
@ -84,7 +108,7 @@
|
|||||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||||
<string>public.vcs.git</string>
|
<string>public.vcs.git</string>
|
||||||
<key>IDESourceControlWCCIdentifierKey</key>
|
<key>IDESourceControlWCCIdentifierKey</key>
|
||||||
<string>6A449EC2-A2A3-4635-9C5F-A811E011EAC3</string>
|
<string>F788B28042EDBEF29EFE34687DA79A778C2CC260</string>
|
||||||
<key>IDESourceControlWCCName</key>
|
<key>IDESourceControlWCCName</key>
|
||||||
<string>MasterPassword</string>
|
<string>MasterPassword</string>
|
||||||
</dict>
|
</dict>
|
||||||
@ -92,7 +116,7 @@
|
|||||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||||
<string>public.vcs.git</string>
|
<string>public.vcs.git</string>
|
||||||
<key>IDESourceControlWCCIdentifierKey</key>
|
<key>IDESourceControlWCCIdentifierKey</key>
|
||||||
<string>ADA0D7F9-4871-4128-8FEE-FD1021EEF3AC</string>
|
<string>4DDCFFD91B41F00326AD14553BD66CFD366ABD91</string>
|
||||||
<key>IDESourceControlWCCName</key>
|
<key>IDESourceControlWCCName</key>
|
||||||
<string>Pearl</string>
|
<string>Pearl</string>
|
||||||
</dict>
|
</dict>
|
||||||
@ -100,7 +124,7 @@
|
|||||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||||
<string>public.vcs.git</string>
|
<string>public.vcs.git</string>
|
||||||
<key>IDESourceControlWCCIdentifierKey</key>
|
<key>IDESourceControlWCCIdentifierKey</key>
|
||||||
<string>B0F634DD-AEE1-4F0D-AE35-4FAF51AD1B5A</string>
|
<string>3E67FB08419C920516AAC3B00DAAF23073B8CF77</string>
|
||||||
<key>IDESourceControlWCCName</key>
|
<key>IDESourceControlWCCName</key>
|
||||||
<string>RHStatusItemView</string>
|
<string>RHStatusItemView</string>
|
||||||
</dict>
|
</dict>
|
||||||
@ -108,7 +132,7 @@
|
|||||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||||
<string>public.vcs.git</string>
|
<string>public.vcs.git</string>
|
||||||
<key>IDESourceControlWCCIdentifierKey</key>
|
<key>IDESourceControlWCCIdentifierKey</key>
|
||||||
<string>42C94803-87A2-403E-896C-D9AC3A807E1B</string>
|
<string>E47DEC29CB0D0FDE3560EF46E1808FA1C723D657</string>
|
||||||
<key>IDESourceControlWCCName</key>
|
<key>IDESourceControlWCCName</key>
|
||||||
<string>UbiquityStoreManager</string>
|
<string>UbiquityStoreManager</string>
|
||||||
</dict>
|
</dict>
|
||||||
@ -116,7 +140,7 @@
|
|||||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||||
<string>public.vcs.git</string>
|
<string>public.vcs.git</string>
|
||||||
<key>IDESourceControlWCCIdentifierKey</key>
|
<key>IDESourceControlWCCIdentifierKey</key>
|
||||||
<string>AE3786C7-912B-4651-A73F-2E1DACBFB604</string>
|
<string>2FE140B36B7D26140DC8D5E5C639DC5900EFCF35</string>
|
||||||
<key>IDESourceControlWCCName</key>
|
<key>IDESourceControlWCCName</key>
|
||||||
<string>uicolor-utilities</string>
|
<string>uicolor-utilities</string>
|
||||||
</dict>
|
</dict>
|
||||||
|
@ -38,15 +38,21 @@ void usage() {
|
|||||||
fprintf(stderr, " -u name Specify the full name of the user.\n"
|
fprintf(stderr, " -u name Specify the full name of the user.\n"
|
||||||
" Defaults to %s in env.\n\n", MP_env_username);
|
" Defaults to %s in env.\n\n", MP_env_username);
|
||||||
fprintf(stderr, " -t type Specify the password's template.\n"
|
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"
|
" x, max, maximum | 20 characters, contains symbols.\n"
|
||||||
" l, long | Copy-friendly, 14 characters, contains symbols.\n"
|
" l, long | Copy-friendly, 14 characters, contains symbols.\n"
|
||||||
" m, med, medium | Copy-friendly, 8 characters, contains symbols.\n"
|
" m, med, medium | Copy-friendly, 8 characters, contains symbols.\n"
|
||||||
" b, basic | 8 characters, no symbols.\n"
|
" b, basic | 8 characters, no symbols.\n"
|
||||||
" s, short | Copy-friendly, 4 characters, no symbols.\n"
|
" s, short | Copy-friendly, 4 characters, no symbols.\n"
|
||||||
" p, pin | 4 numbers.\n\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"
|
fprintf(stderr, " -c counter The value of the counter.\n"
|
||||||
" Defaults to %s in env or '1'.\n\n", MP_env_sitecounter);
|
" Defaults to %s in env or '1'.\n\n", MP_env_sitecounter);
|
||||||
|
fprintf(stderr, " -v variant The kind of content to generate.\n"
|
||||||
|
" 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);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,12 +92,14 @@ int main(int argc, char *const argv[]) {
|
|||||||
const char *siteName = NULL;
|
const char *siteName = NULL;
|
||||||
MPElementType siteType = MPElementTypeGeneratedLong;
|
MPElementType siteType = MPElementTypeGeneratedLong;
|
||||||
const char *siteTypeString = getenv( MP_env_sitetype );
|
const char *siteTypeString = getenv( MP_env_sitetype );
|
||||||
|
MPElementVariant siteVariant = MPElementVariantPassword;
|
||||||
|
const char *siteVariantString = NULL;
|
||||||
uint32_t siteCounter = 1;
|
uint32_t siteCounter = 1;
|
||||||
const char *siteCounterString = getenv( MP_env_sitecounter );
|
const char *siteCounterString = getenv( MP_env_sitecounter );
|
||||||
|
|
||||||
// Read the options.
|
// Read the options.
|
||||||
char opt;
|
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) {
|
switch (opt) {
|
||||||
case 'h':
|
case 'h':
|
||||||
usage();
|
usage();
|
||||||
@ -102,6 +110,9 @@ int main(int argc, char *const argv[]) {
|
|||||||
case 't':
|
case 't':
|
||||||
siteTypeString = optarg;
|
siteTypeString = optarg;
|
||||||
break;
|
break;
|
||||||
|
case 'v':
|
||||||
|
siteVariantString = optarg;
|
||||||
|
break;
|
||||||
case 'c':
|
case 'c':
|
||||||
siteCounterString = optarg;
|
siteCounterString = optarg;
|
||||||
break;
|
break;
|
||||||
@ -144,6 +155,11 @@ int main(int argc, char *const argv[]) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
trc("siteCounter: %d\n", siteCounter);
|
trc("siteCounter: %d\n", siteCounter);
|
||||||
|
if (siteVariantString)
|
||||||
|
siteVariant = VariantWithName( siteVariantString );
|
||||||
|
trc("siteVariant: %d (%s)\n", siteVariant, siteVariantString);
|
||||||
|
if (siteVariant == MPElementVariantLogin)
|
||||||
|
siteType = MPElementTypeGeneratedName;
|
||||||
if (siteTypeString)
|
if (siteTypeString)
|
||||||
siteType = TypeWithName( siteTypeString );
|
siteType = TypeWithName( siteTypeString );
|
||||||
trc("siteType: %d (%s)\n", siteType, siteTypeString);
|
trc("siteType: %d (%s)\n", siteType, siteTypeString);
|
||||||
@ -176,9 +192,10 @@ int main(int argc, char *const argv[]) {
|
|||||||
trc("masterPassword: %s\n", masterPassword);
|
trc("masterPassword: %s\n", masterPassword);
|
||||||
|
|
||||||
// Calculate the master key salt.
|
// 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));
|
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 );
|
char *masterKeySalt = malloc( masterKeySaltLength );
|
||||||
if (!masterKeySalt) {
|
if (!masterKeySalt) {
|
||||||
fprintf(stderr, "Could not allocate master key salt: %d\n", errno);
|
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;
|
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, &n_userNameLength, sizeof(n_userNameLength)); mKS += sizeof(n_userNameLength);
|
||||||
memcpy(mKS, userName, strlen(userName)); mKS += strlen(userName);
|
memcpy(mKS, userName, strlen(userName)); mKS += strlen(userName);
|
||||||
if (mKS - masterKeySalt != masterKeySaltLength)
|
if (mKS - masterKeySalt != masterKeySaltLength)
|
||||||
@ -210,9 +227,11 @@ int main(int argc, char *const argv[]) {
|
|||||||
trc("masterKey ID: %s\n", IDForBuf(masterKey, MP_dkLen));
|
trc("masterKey ID: %s\n", IDForBuf(masterKey, MP_dkLen));
|
||||||
|
|
||||||
// Calculate the site seed.
|
// 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_siteNameLength = htonl(strlen(siteName));
|
||||||
const uint32_t n_siteCounter = htonl(siteCounter);
|
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 );
|
char *sitePasswordInfo = malloc( sitePasswordInfoLength );
|
||||||
if (!sitePasswordInfo) {
|
if (!sitePasswordInfo) {
|
||||||
fprintf(stderr, "Could not allocate site seed: %d\n", errno);
|
fprintf(stderr, "Could not allocate site seed: %d\n", errno);
|
||||||
@ -220,13 +239,13 @@ int main(int argc, char *const argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
char *sPI = sitePasswordInfo;
|
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, &n_siteNameLength, sizeof(n_siteNameLength)); sPI += sizeof(n_siteNameLength);
|
||||||
memcpy(sPI, siteName, strlen(siteName)); sPI += strlen(siteName);
|
memcpy(sPI, siteName, strlen(siteName)); sPI += strlen(siteName);
|
||||||
memcpy(sPI, &n_siteCounter, sizeof(n_siteCounter)); sPI += sizeof(n_siteCounter);
|
memcpy(sPI, &n_siteCounter, sizeof(n_siteCounter)); sPI += sizeof(n_siteCounter);
|
||||||
if (sPI - sitePasswordInfo != sitePasswordInfoLength)
|
if (sPI - sitePasswordInfo != sitePasswordInfoLength)
|
||||||
abort();
|
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));
|
trc("sitePasswordInfo ID: %s\n", IDForBuf(sitePasswordInfo, sitePasswordInfoLength));
|
||||||
|
|
||||||
uint8_t sitePasswordSeed[32];
|
uint8_t sitePasswordSeed[32];
|
||||||
|
@ -31,8 +31,12 @@ const MPElementType TypeWithName(const char *typeName) {
|
|||||||
return MPElementTypeGeneratedBasic;
|
return MPElementTypeGeneratedBasic;
|
||||||
if (0 == strcmp(lowerTypeName, "s") || 0 == strcmp(lowerTypeName, "short"))
|
if (0 == strcmp(lowerTypeName, "s") || 0 == strcmp(lowerTypeName, "short"))
|
||||||
return MPElementTypeGeneratedShort;
|
return MPElementTypeGeneratedShort;
|
||||||
if (0 == strcmp(lowerTypeName, "p") || 0 == strcmp(lowerTypeName, "pin"))
|
if (0 == strcmp(lowerTypeName, "i") || 0 == strcmp(lowerTypeName, "pin"))
|
||||||
return MPElementTypeGeneratedPIN;
|
return MPElementTypeGeneratedPIN;
|
||||||
|
if (0 == strcmp(lowerTypeName, "n") || 0 == strcmp(lowerTypeName, "name"))
|
||||||
|
return MPElementTypeGeneratedName;
|
||||||
|
if (0 == strcmp(lowerTypeName, "p") || 0 == strcmp(lowerTypeName, "phrase"))
|
||||||
|
return MPElementTypeGeneratedPhrase;
|
||||||
|
|
||||||
fprintf(stderr, "Not a generated type name: %s", lowerTypeName);
|
fprintf(stderr, "Not a generated type name: %s", lowerTypeName);
|
||||||
abort();
|
abort();
|
||||||
@ -67,6 +71,13 @@ const char *CipherForType(MPElementType type, uint8_t seedByte) {
|
|||||||
case MPElementTypeGeneratedPIN: {
|
case MPElementTypeGeneratedPIN: {
|
||||||
return "nnnn";
|
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: {
|
default: {
|
||||||
fprintf(stderr, "Unknown generated type: %d", type);
|
fprintf(stderr, "Unknown generated type: %d", type);
|
||||||
abort();
|
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 CharacterFromClass(char characterClass, uint8_t seedByte) {
|
||||||
const char *classCharacters;
|
const char *classCharacters;
|
||||||
switch (characterClass) {
|
switch (characterClass) {
|
||||||
@ -113,6 +154,10 @@ const char CharacterFromClass(char characterClass, uint8_t seedByte) {
|
|||||||
classCharacters = "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()";
|
classCharacters = "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case ' ': {
|
||||||
|
classCharacters = " ";
|
||||||
|
break;
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
fprintf(stderr, "Unknown character class: %c", characterClass);
|
fprintf(stderr, "Unknown character class: %c", characterClass);
|
||||||
abort();
|
abort();
|
||||||
|
@ -7,10 +7,11 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
MPElementContentTypePassword,
|
/** Generate the password to log in with. */
|
||||||
MPElementContentTypeNote,
|
MPElementVariantPassword,
|
||||||
MPElementContentTypePicture,
|
/** Generate the login name to log in as. */
|
||||||
} MPElementContentType;
|
MPElementVariantLogin,
|
||||||
|
} MPElementVariant;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
/** Generate the password. */
|
/** Generate the password. */
|
||||||
@ -33,6 +34,8 @@ typedef enum {
|
|||||||
MPElementTypeGeneratedBasic = 0x4 | MPElementTypeClassGenerated | 0x0,
|
MPElementTypeGeneratedBasic = 0x4 | MPElementTypeClassGenerated | 0x0,
|
||||||
MPElementTypeGeneratedShort = 0x3 | MPElementTypeClassGenerated | 0x0,
|
MPElementTypeGeneratedShort = 0x3 | MPElementTypeClassGenerated | 0x0,
|
||||||
MPElementTypeGeneratedPIN = 0x5 | MPElementTypeClassGenerated | 0x0,
|
MPElementTypeGeneratedPIN = 0x5 | MPElementTypeClassGenerated | 0x0,
|
||||||
|
MPElementTypeGeneratedName = 0xE | MPElementTypeClassGenerated | 0x0,
|
||||||
|
MPElementTypeGeneratedPhrase = 0xF | MPElementTypeClassGenerated | 0x0,
|
||||||
|
|
||||||
MPElementTypeStoredPersonal = 0x0 | MPElementTypeClassStored | MPElementFeatureExportContent,
|
MPElementTypeStoredPersonal = 0x0 | MPElementTypeClassStored | MPElementFeatureExportContent,
|
||||||
MPElementTypeStoredDevicePrivate = 0x1 | MPElementTypeClassStored | MPElementFeatureDevicePrivate,
|
MPElementTypeStoredDevicePrivate = 0x1 | MPElementTypeClassStored | MPElementFeatureDevicePrivate,
|
||||||
@ -44,6 +47,8 @@ typedef enum {
|
|||||||
#define trc(...) do {} while (0)
|
#define trc(...) do {} while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
const MPElementVariant VariantWithName(const char *variantName);
|
||||||
|
const char *ScopeForVariant(MPElementVariant variant);
|
||||||
const MPElementType TypeWithName(const char *typeName);
|
const MPElementType TypeWithName(const char *typeName);
|
||||||
const char *CipherForType(MPElementType type, uint8_t seedByte);
|
const char *CipherForType(MPElementType type, uint8_t seedByte);
|
||||||
const char CharacterFromClass(char characterClass, uint8_t seedByte);
|
const char CharacterFromClass(char characterClass, uint8_t seedByte);
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||||
*
|
*
|
||||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
*
|
*
|
||||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//
|
//
|
||||||
// MPAlgorithm
|
// MPAlgorithm
|
||||||
@ -16,10 +16,10 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#import "MPKey.h"
|
#import "MPKey.h"
|
||||||
#import "MPElementStoredEntity.h"
|
#import "MPStoredSiteEntity.h"
|
||||||
#import "MPElementGeneratedEntity.h"
|
#import "MPGeneratedSiteEntity.h"
|
||||||
|
|
||||||
#define MPAlgorithmDefaultVersion 1
|
#define MPAlgorithmDefaultVersion 2
|
||||||
#define MPAlgorithmDefault MPAlgorithmForVersion(MPAlgorithmDefaultVersion)
|
#define MPAlgorithmDefault MPAlgorithmForVersion(MPAlgorithmDefaultVersion)
|
||||||
|
|
||||||
id<MPAlgorithm> MPAlgorithmForVersion(NSUInteger version);
|
id<MPAlgorithm> MPAlgorithmForVersion(NSUInteger version);
|
||||||
@ -43,37 +43,56 @@ NSString *NSStringFromTimeToCrack(TimeToCrack timeToCrack);
|
|||||||
|
|
||||||
@required
|
@required
|
||||||
- (NSUInteger)version;
|
- (NSUInteger)version;
|
||||||
- (BOOL)migrateUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc;
|
- (BOOL)tryMigrateUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc;
|
||||||
- (BOOL)migrateElement:(MPElementEntity *)element explicit:(BOOL)explicit;
|
- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit;
|
||||||
|
|
||||||
- (MPKey *)keyForPassword:(NSString *)password ofUserNamed:(NSString *)userName;
|
- (MPKey *)keyForPassword:(NSString *)password ofUserNamed:(NSString *)userName;
|
||||||
- (MPKey *)keyFromKeyData:(NSData *)keyData;
|
- (MPKey *)keyFromKeyData:(NSData *)keyData;
|
||||||
- (NSData *)keyIDForKeyData:(NSData *)keyData;
|
- (NSData *)keyIDForKeyData:(NSData *)keyData;
|
||||||
|
|
||||||
- (NSString *)nameOfType:(MPElementType)type;
|
- (NSString *)scopeForVariant:(MPSiteVariant)variant;
|
||||||
- (NSString *)shortNameOfType:(MPElementType)type;
|
- (NSString *)nameOfType:(MPSiteType)type;
|
||||||
- (NSString *)classNameOfType:(MPElementType)type;
|
- (NSString *)shortNameOfType:(MPSiteType)type;
|
||||||
- (Class)classOfType:(MPElementType)type;
|
- (NSString *)classNameOfType:(MPSiteType)type;
|
||||||
|
- (Class)classOfType:(MPSiteType)type;
|
||||||
- (NSArray *)allTypes;
|
- (NSArray *)allTypes;
|
||||||
- (NSArray *)allTypesStartingWith:(MPElementType)startingType;
|
- (NSArray *)allTypesStartingWith:(MPSiteType)startingType;
|
||||||
- (MPElementType)nextType:(MPElementType)type;
|
- (MPSiteType)nextType:(MPSiteType)type;
|
||||||
- (MPElementType)previousType:(MPElementType)type;
|
- (MPSiteType)previousType:(MPSiteType)type;
|
||||||
|
|
||||||
- (NSString *)generateContentNamed:(NSString *)name ofType:(MPElementType)type withCounter:(NSUInteger)counter usingKey:(MPKey *)key;
|
- (NSString *)generateLoginForSiteNamed:(NSString *)name usingKey:(MPKey *)key;
|
||||||
- (NSString *)storedContentForElement:(MPElementStoredEntity *)element 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 *)storedLoginForSite:(MPStoredSiteEntity *)site usingKey:(MPKey *)key;
|
||||||
- (NSString *)resolveContentForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey;
|
- (NSString *)storedPasswordForSite:(MPStoredSiteEntity *)site usingKey:(MPKey *)key;
|
||||||
- (void)resolveContentForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey
|
|
||||||
result:(void (^)(NSString *result))resultBlock;
|
|
||||||
|
|
||||||
- (void)importProtectedContent:(NSString *)protectedContent protectedByKey:(MPKey *)importKey
|
- (BOOL)savePassword:(NSString *)clearPassword toSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
|
||||||
intoElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey;
|
|
||||||
- (void)importClearTextContent:(NSString *)clearContent intoElement:(MPElementEntity *)element
|
|
||||||
usingKey:(MPKey *)elementKey;
|
|
||||||
- (NSString *)exportContentForElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey;
|
|
||||||
|
|
||||||
- (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;
|
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordString:(NSString *)password byAttacker:(MPAttacker)attacker;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||||
*
|
*
|
||||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
*
|
*
|
||||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//
|
//
|
||||||
// MPAlgorithm
|
// MPAlgorithm
|
||||||
@ -24,7 +24,7 @@ id<MPAlgorithm> MPAlgorithmForVersion(NSUInteger version) {
|
|||||||
versionToAlgorithm = [NSMutableDictionary dictionary];
|
versionToAlgorithm = [NSMutableDictionary dictionary];
|
||||||
|
|
||||||
id<MPAlgorithm> algorithm = versionToAlgorithm[@(version)];
|
id<MPAlgorithm> algorithm = versionToAlgorithm[@(version)];
|
||||||
if (!algorithm) if ((algorithm = [NSClassFromString( strf( @"MPAlgorithmV%lu", (unsigned long)version ) ) new]))
|
if (!algorithm && (algorithm = (id<MPAlgorithm>)[NSClassFromString( strf( @"MPAlgorithmV%lu", (unsigned long)version ) ) new]))
|
||||||
versionToAlgorithm[@(version)] = algorithm;
|
versionToAlgorithm[@(version)] = algorithm;
|
||||||
|
|
||||||
return algorithm;
|
return algorithm;
|
||||||
@ -33,8 +33,11 @@ id<MPAlgorithm> MPAlgorithmForVersion(NSUInteger version) {
|
|||||||
id<MPAlgorithm> MPAlgorithmDefaultForBundleVersion(NSString *bundleVersion) {
|
id<MPAlgorithm> MPAlgorithmDefaultForBundleVersion(NSString *bundleVersion) {
|
||||||
|
|
||||||
if (PearlCFBundleVersionCompare( bundleVersion, @"1.3" ) == NSOrderedAscending)
|
if (PearlCFBundleVersionCompare( bundleVersion, @"1.3" ) == NSOrderedAscending)
|
||||||
// Pre-1.3
|
// Pre-1.3
|
||||||
return MPAlgorithmForVersion( 0 );
|
return MPAlgorithmForVersion( 0 );
|
||||||
|
if (PearlCFBundleVersionCompare( bundleVersion, @"2.1" ) == NSOrderedAscending)
|
||||||
|
// Pre-2.1
|
||||||
|
return MPAlgorithmForVersion( 1 );
|
||||||
|
|
||||||
return MPAlgorithmDefault;
|
return MPAlgorithmDefault;
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
@interface MPAlgorithmV0 : NSObject<MPAlgorithm>
|
@interface MPAlgorithmV0 : NSObject<MPAlgorithm>
|
||||||
|
|
||||||
- (NSDictionary *)allCiphers;
|
- (NSDictionary *)allCiphers;
|
||||||
- (NSArray *)ciphersForType:(MPElementType)type;
|
- (NSArray *)ciphersForType:(MPSiteType)type;
|
||||||
- (NSArray *)cipherClasses;
|
- (NSArray *)cipherClasses;
|
||||||
- (NSArray *)cipherClassCharacters;
|
- (NSArray *)cipherClassCharacters;
|
||||||
- (NSString *)charactersForCipherClass:(NSString *)cipherClass;
|
- (NSString *)charactersForCipherClass:(NSString *)cipherClass;
|
||||||
|
@ -17,6 +17,9 @@
|
|||||||
|
|
||||||
#import "MPAlgorithmV0.h"
|
#import "MPAlgorithmV0.h"
|
||||||
#import "MPEntities.h"
|
#import "MPEntities.h"
|
||||||
|
#import "MPAppDelegate_Shared.h"
|
||||||
|
#import "MPAppDelegate_InApp.h"
|
||||||
|
#import "MPSiteQuestionEntity.h"
|
||||||
#include <openssl/bn.h>
|
#include <openssl/bn.h>
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
|
|
||||||
@ -70,40 +73,40 @@
|
|||||||
return [(id<MPAlgorithm>)other version] == [self version];
|
return [(id<MPAlgorithm>)other version] == [self version];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)migrateUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc {
|
- (BOOL)tryMigrateUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc {
|
||||||
|
|
||||||
NSError *error = nil;
|
NSError *error = nil;
|
||||||
NSFetchRequest *migrationRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )];
|
NSFetchRequest *migrationRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
|
||||||
migrationRequest.predicate = [NSPredicate predicateWithFormat:@"version_ < %d AND user == %@", self.version, user];
|
migrationRequest.predicate = [NSPredicate predicateWithFormat:@"version_ < %d AND user == %@", self.version, user];
|
||||||
NSArray *migrationElements = [moc executeFetchRequest:migrationRequest error:&error];
|
NSArray *migrationSites = [moc executeFetchRequest:migrationRequest error:&error];
|
||||||
if (!migrationElements) {
|
if (!migrationSites) {
|
||||||
err( @"While looking for elements to migrate: %@", error );
|
err( @"While looking for sites to migrate: %@", [error fullDescription] );
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL requiresExplicitMigration = NO;
|
BOOL success = YES;
|
||||||
for (MPElementEntity *migrationElement in migrationElements)
|
for (MPSiteEntity *migrationSite in migrationSites)
|
||||||
if (![migrationElement migrateExplicitly:NO])
|
if (![migrationSite tryMigrateExplicitly:NO])
|
||||||
requiresExplicitMigration = YES;
|
success = NO;
|
||||||
|
|
||||||
return requiresExplicitMigration;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)migrateElement:(MPElementEntity *)element explicit:(BOOL)explicit {
|
- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit {
|
||||||
|
|
||||||
if (element.version != [self version] - 1)
|
if (site.version != [self version] - 1)
|
||||||
// Only migrate from previous version.
|
// Only migrate from previous version.
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
if (!explicit) {
|
if (!explicit) {
|
||||||
// This migration requires explicit permission.
|
// This migration requires explicit permission.
|
||||||
element.requiresExplicitMigration = YES;
|
site.requiresExplicitMigration = YES;
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply migration.
|
// Apply migration.
|
||||||
element.requiresExplicitMigration = NO;
|
site.requiresExplicitMigration = NO;
|
||||||
element.version = [self version];
|
site.version = [self version];
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,11 +120,11 @@
|
|||||||
[NSData dataWithBytes:&nuserNameLength
|
[NSData dataWithBytes:&nuserNameLength
|
||||||
length:sizeof( nuserNameLength )],
|
length:sizeof( nuserNameLength )],
|
||||||
[userName dataUsingEncoding:NSUTF8StringEncoding],
|
[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];
|
MPKey *key = [self keyFromKeyData:keyData];
|
||||||
trc( @"User: %@, password: %@ derives to key ID: %@ (took %0.2fs)", userName, password, [key.keyID encodeHex],
|
trc( @"User: %@, password: %@ derives to key ID: %@ (took %0.2fs)", userName, password, [key.keyID encodeHex],
|
||||||
-[start timeIntervalSinceNow] );
|
-[start timeIntervalSinceNow] );
|
||||||
|
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
@ -136,108 +139,140 @@
|
|||||||
return [keyData hashWith:MP_hash];
|
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)
|
if (!type)
|
||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case MPElementTypeGeneratedMaximum:
|
case MPSiteTypeGeneratedMaximum:
|
||||||
return @"Maximum Security Password";
|
return @"Maximum Security Password";
|
||||||
|
|
||||||
case MPElementTypeGeneratedLong:
|
case MPSiteTypeGeneratedLong:
|
||||||
return @"Long Password";
|
return @"Long Password";
|
||||||
|
|
||||||
case MPElementTypeGeneratedMedium:
|
case MPSiteTypeGeneratedMedium:
|
||||||
return @"Medium Password";
|
return @"Medium Password";
|
||||||
|
|
||||||
case MPElementTypeGeneratedBasic:
|
case MPSiteTypeGeneratedBasic:
|
||||||
return @"Basic Password";
|
return @"Basic Password";
|
||||||
|
|
||||||
case MPElementTypeGeneratedShort:
|
case MPSiteTypeGeneratedShort:
|
||||||
return @"Short Password";
|
return @"Short Password";
|
||||||
|
|
||||||
case MPElementTypeGeneratedPIN:
|
case MPSiteTypeGeneratedPIN:
|
||||||
return @"PIN";
|
return @"PIN";
|
||||||
|
|
||||||
case MPElementTypeStoredPersonal:
|
case MPSiteTypeGeneratedName:
|
||||||
|
return @"Login Name";
|
||||||
|
|
||||||
|
case MPSiteTypeGeneratedPhrase:
|
||||||
|
return @"Phrase";
|
||||||
|
|
||||||
|
case MPSiteTypeStoredPersonal:
|
||||||
return @"Personal Password";
|
return @"Personal Password";
|
||||||
|
|
||||||
case MPElementTypeStoredDevicePrivate:
|
case MPSiteTypeStoredDevicePrivate:
|
||||||
return @"Device Private Password";
|
return @"Device Private Password";
|
||||||
}
|
}
|
||||||
|
|
||||||
Throw( @"Type not supported: %lu", (long)type );
|
Throw( @"Type not supported: %lu", (long)type );
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)shortNameOfType:(MPElementType)type {
|
- (NSString *)shortNameOfType:(MPSiteType)type {
|
||||||
|
|
||||||
if (!type)
|
if (!type)
|
||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case MPElementTypeGeneratedMaximum:
|
case MPSiteTypeGeneratedMaximum:
|
||||||
return @"Maximum";
|
return @"Maximum";
|
||||||
|
|
||||||
case MPElementTypeGeneratedLong:
|
case MPSiteTypeGeneratedLong:
|
||||||
return @"Long";
|
return @"Long";
|
||||||
|
|
||||||
case MPElementTypeGeneratedMedium:
|
case MPSiteTypeGeneratedMedium:
|
||||||
return @"Medium";
|
return @"Medium";
|
||||||
|
|
||||||
case MPElementTypeGeneratedBasic:
|
case MPSiteTypeGeneratedBasic:
|
||||||
return @"Basic";
|
return @"Basic";
|
||||||
|
|
||||||
case MPElementTypeGeneratedShort:
|
case MPSiteTypeGeneratedShort:
|
||||||
return @"Short";
|
return @"Short";
|
||||||
|
|
||||||
case MPElementTypeGeneratedPIN:
|
case MPSiteTypeGeneratedPIN:
|
||||||
return @"PIN";
|
return @"PIN";
|
||||||
|
|
||||||
case MPElementTypeStoredPersonal:
|
case MPSiteTypeGeneratedName:
|
||||||
|
return @"Name";
|
||||||
|
|
||||||
|
case MPSiteTypeGeneratedPhrase:
|
||||||
|
return @"Phrase";
|
||||||
|
|
||||||
|
case MPSiteTypeStoredPersonal:
|
||||||
return @"Personal";
|
return @"Personal";
|
||||||
|
|
||||||
case MPElementTypeStoredDevicePrivate:
|
case MPSiteTypeStoredDevicePrivate:
|
||||||
return @"Device";
|
return @"Device";
|
||||||
}
|
}
|
||||||
|
|
||||||
Throw( @"Type not supported: %lu", (long)type );
|
Throw( @"Type not supported: %lu", (long)type );
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)classNameOfType:(MPElementType)type {
|
- (NSString *)classNameOfType:(MPSiteType)type {
|
||||||
|
|
||||||
return NSStringFromClass( [self classOfType:type] );
|
return NSStringFromClass( [self classOfType:type] );
|
||||||
}
|
}
|
||||||
|
|
||||||
- (Class)classOfType:(MPElementType)type {
|
- (Class)classOfType:(MPSiteType)type {
|
||||||
|
|
||||||
if (!type)
|
if (!type)
|
||||||
Throw( @"No type given." );
|
Throw( @"No type given." );
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case MPElementTypeGeneratedMaximum:
|
case MPSiteTypeGeneratedMaximum:
|
||||||
return [MPElementGeneratedEntity class];
|
return [MPGeneratedSiteEntity class];
|
||||||
|
|
||||||
case MPElementTypeGeneratedLong:
|
case MPSiteTypeGeneratedLong:
|
||||||
return [MPElementGeneratedEntity class];
|
return [MPGeneratedSiteEntity class];
|
||||||
|
|
||||||
case MPElementTypeGeneratedMedium:
|
case MPSiteTypeGeneratedMedium:
|
||||||
return [MPElementGeneratedEntity class];
|
return [MPGeneratedSiteEntity class];
|
||||||
|
|
||||||
case MPElementTypeGeneratedBasic:
|
case MPSiteTypeGeneratedBasic:
|
||||||
return [MPElementGeneratedEntity class];
|
return [MPGeneratedSiteEntity class];
|
||||||
|
|
||||||
case MPElementTypeGeneratedShort:
|
case MPSiteTypeGeneratedShort:
|
||||||
return [MPElementGeneratedEntity class];
|
return [MPGeneratedSiteEntity class];
|
||||||
|
|
||||||
case MPElementTypeGeneratedPIN:
|
case MPSiteTypeGeneratedPIN:
|
||||||
return [MPElementGeneratedEntity class];
|
return [MPGeneratedSiteEntity class];
|
||||||
|
|
||||||
case MPElementTypeStoredPersonal:
|
case MPSiteTypeGeneratedName:
|
||||||
return [MPElementStoredEntity class];
|
return [MPGeneratedSiteEntity class];
|
||||||
|
|
||||||
case MPElementTypeStoredDevicePrivate:
|
case MPSiteTypeGeneratedPhrase:
|
||||||
return [MPElementStoredEntity class];
|
return [MPGeneratedSiteEntity class];
|
||||||
|
|
||||||
|
case MPSiteTypeStoredPersonal:
|
||||||
|
return [MPStoredSiteEntity class];
|
||||||
|
|
||||||
|
case MPSiteTypeStoredDevicePrivate:
|
||||||
|
return [MPStoredSiteEntity class];
|
||||||
}
|
}
|
||||||
|
|
||||||
Throw( @"Type not supported: %lu", (long)type );
|
Throw( @"Type not supported: %lu", (long)type );
|
||||||
@ -245,13 +280,13 @@
|
|||||||
|
|
||||||
- (NSArray *)allTypes {
|
- (NSArray *)allTypes {
|
||||||
|
|
||||||
return [self allTypesStartingWith:MPElementTypeGeneratedMaximum];
|
return [self allTypesStartingWith:MPSiteTypeGeneratedMaximum];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSArray *)allTypesStartingWith:(MPElementType)startingType {
|
- (NSArray *)allTypesStartingWith:(MPSiteType)startingType {
|
||||||
|
|
||||||
NSMutableArray *allTypes = [[NSMutableArray alloc] initWithCapacity:8];
|
NSMutableArray *allTypes = [[NSMutableArray alloc] initWithCapacity:8];
|
||||||
MPElementType currentType = startingType;
|
MPSiteType currentType = startingType;
|
||||||
do {
|
do {
|
||||||
[allTypes addObject:@(currentType)];
|
[allTypes addObject:@(currentType)];
|
||||||
} while ((currentType = [self nextType:currentType]) != startingType);
|
} while ((currentType = [self nextType:currentType]) != startingType);
|
||||||
@ -259,33 +294,33 @@
|
|||||||
return allTypes;
|
return allTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MPElementType)nextType:(MPElementType)type {
|
- (MPSiteType)nextType:(MPSiteType)type {
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case MPElementTypeGeneratedMaximum:
|
case MPSiteTypeGeneratedMaximum:
|
||||||
return MPElementTypeGeneratedLong;
|
return MPSiteTypeGeneratedLong;
|
||||||
case MPElementTypeGeneratedLong:
|
case MPSiteTypeGeneratedLong:
|
||||||
return MPElementTypeGeneratedMedium;
|
return MPSiteTypeGeneratedMedium;
|
||||||
case MPElementTypeGeneratedMedium:
|
case MPSiteTypeGeneratedMedium:
|
||||||
return MPElementTypeGeneratedBasic;
|
return MPSiteTypeGeneratedBasic;
|
||||||
case MPElementTypeGeneratedBasic:
|
case MPSiteTypeGeneratedBasic:
|
||||||
return MPElementTypeGeneratedShort;
|
return MPSiteTypeGeneratedShort;
|
||||||
case MPElementTypeGeneratedShort:
|
case MPSiteTypeGeneratedShort:
|
||||||
return MPElementTypeGeneratedPIN;
|
return MPSiteTypeGeneratedPIN;
|
||||||
case MPElementTypeGeneratedPIN:
|
case MPSiteTypeGeneratedPIN:
|
||||||
return MPElementTypeStoredPersonal;
|
return MPSiteTypeStoredPersonal;
|
||||||
case MPElementTypeStoredPersonal:
|
case MPSiteTypeStoredPersonal:
|
||||||
return MPElementTypeStoredDevicePrivate;
|
return MPSiteTypeStoredDevicePrivate;
|
||||||
case MPElementTypeStoredDevicePrivate:
|
case MPSiteTypeStoredDevicePrivate:
|
||||||
return MPElementTypeGeneratedMaximum;
|
return MPSiteTypeGeneratedMaximum;
|
||||||
default:
|
default:
|
||||||
return MPElementTypeGeneratedLong;
|
return MPSiteTypeGeneratedLong;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MPElementType)previousType:(MPElementType)type {
|
- (MPSiteType)previousType:(MPSiteType)type {
|
||||||
|
|
||||||
MPElementType previousType = type, nextType = type;
|
MPSiteType previousType = type, nextType = type;
|
||||||
while ((nextType = [self nextType:nextType]) != type)
|
while ((nextType = [self nextType:nextType]) != type)
|
||||||
previousType = nextType;
|
previousType = nextType;
|
||||||
|
|
||||||
@ -304,7 +339,7 @@
|
|||||||
return ciphers;
|
return ciphers;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSArray *)ciphersForType:(MPElementType)type {
|
- (NSArray *)ciphersForType:(MPSiteType)type {
|
||||||
|
|
||||||
NSString *typeClass = [self classNameOfType:type];
|
NSString *typeClass = [self classNameOfType:type];
|
||||||
NSString *typeName = [self nameOfType:type];
|
NSString *typeName = [self nameOfType:type];
|
||||||
@ -326,27 +361,53 @@
|
|||||||
return [NSNullToNil( [NSNullToNil( [[self allCiphers] valueForKey:@"MPCharacterClasses"] ) valueForKey:cipherClass] ) copy];
|
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
|
// Determine the seed whose bytes will be used for calculating a password
|
||||||
uint32_t ncounter = htonl( counter ), nnameLength = htonl( name.length );
|
uint32_t ncounter = htonl( counter ), nnameLength = htonl( name.length ), ncontextLength = htonl( context.length );
|
||||||
NSData *counterBytes = [NSData dataWithBytes:&ncounter length:sizeof( ncounter )];
|
NSData *counterBytes = [NSData dataWithBytes:&ncounter length:sizeof( ncounter )];
|
||||||
NSData *nameLengthBytes = [NSData dataWithBytes:&nnameLength length:sizeof( nnameLength )];
|
NSData *nameLengthBytes = [NSData dataWithBytes:&nnameLength length:sizeof( nnameLength )];
|
||||||
trc( @"seed from: hmac-sha256(%@, 'com.lyndir.masterpassword' | %@ | %@ | %@)", [key.keyData encodeBase64],
|
NSData *contextLengthBytes = [NSData dataWithBytes:&ncontextLength length:sizeof( ncontextLength )];
|
||||||
[nameLengthBytes encodeHex], name, [counterBytes encodeHex] );
|
NSString *scope = [self scopeForVariant:variant];
|
||||||
|
trc( @"seed from: hmac-sha256(%@, %@ | %@ | %@ | %@ | %@)",
|
||||||
|
[[key keyID] encodeHex], scope, [nameLengthBytes encodeHex], name, [counterBytes encodeHex], context );
|
||||||
NSData *seed = [[NSData dataByConcatenatingDatas:
|
NSData *seed = [[NSData dataByConcatenatingDatas:
|
||||||
[@"com.lyndir.masterpassword" dataUsingEncoding:NSUTF8StringEncoding],
|
[scope dataUsingEncoding:NSUTF8StringEncoding],
|
||||||
nameLengthBytes, [name dataUsingEncoding:NSUTF8StringEncoding],
|
nameLengthBytes,
|
||||||
counterBytes, nil]
|
[name dataUsingEncoding:NSUTF8StringEncoding],
|
||||||
|
counterBytes,
|
||||||
|
context? contextLengthBytes: nil,
|
||||||
|
[context dataUsingEncoding:NSUTF8StringEncoding],
|
||||||
|
nil]
|
||||||
hmacWith:PearlHashSHA256 key:key.keyData];
|
hmacWith:PearlHashSHA256 key:key.keyData];
|
||||||
trc( @"seed is: %@", [seed encodeBase64] );
|
trc( @"seed is: %@", [seed encodeHex] );
|
||||||
const char *seedBytes = seed.bytes;
|
const char *seedBytes = seed.bytes;
|
||||||
|
|
||||||
// Determine the cipher from the first seed byte.
|
// Determine the cipher from the first seed byte.
|
||||||
NSAssert( [seed length], @"Missing seed." );
|
NSAssert( [seed length], @"Missing seed." );
|
||||||
NSArray *typeCiphers = [self ciphersForType:type];
|
NSArray *typeCiphers = [self ciphersForType:type];
|
||||||
NSString *cipher = typeCiphers[htons( seedBytes[0] ) % [typeCiphers count]];
|
NSString *cipher = typeCiphers[htons( seedBytes[0] ) % [typeCiphers count]];
|
||||||
trc( @"type %@, ciphers: %@, selected: %@", [self nameOfType:type], typeCiphers, cipher );
|
trc( @"type %@ (%lu), ciphers: %@, selected: %@", [self nameOfType:type], (unsigned long)type, typeCiphers, cipher );
|
||||||
|
|
||||||
// Encode the content, character by character, using subsequent seed bytes and the cipher.
|
// Encode the content, character by character, using subsequent seed bytes and the cipher.
|
||||||
NSAssert( [seed length] >= [cipher length] + 1, @"Insufficient seed bytes to encode cipher." );
|
NSAssert( [seed length] >= [cipher length] + 1, @"Insufficient seed bytes to encode cipher." );
|
||||||
@ -364,68 +425,80 @@
|
|||||||
return content;
|
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." );
|
return [self decryptContent:site.contentObject usingKey:key];
|
||||||
switch (element.type) {
|
}
|
||||||
case MPElementTypeGeneratedMaximum:
|
|
||||||
case MPElementTypeGeneratedLong:
|
- (BOOL)savePassword:(NSString *)clearContent toSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
||||||
case MPElementTypeGeneratedMedium:
|
|
||||||
case MPElementTypeGeneratedBasic:
|
NSAssert( [siteKey.keyID isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
||||||
case MPElementTypeGeneratedShort:
|
switch (site.type) {
|
||||||
case MPElementTypeGeneratedPIN: {
|
case MPSiteTypeGeneratedMaximum:
|
||||||
NSAssert( NO, @"Cannot save content to element with generated type %lu.", (long)element.type );
|
case MPSiteTypeGeneratedLong:
|
||||||
break;
|
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: {
|
case MPSiteTypeStoredPersonal: {
|
||||||
if (![element isKindOfClass:[MPElementStoredEntity class]]) {
|
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
|
||||||
wrn( @"Element with stored type %lu is not an MPElementStoredEntity, but a %@.",
|
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
|
||||||
(long)element.type, [element class] );
|
(long)site.type, [site class] );
|
||||||
break;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding]
|
NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding]
|
||||||
encryptWithSymmetricKey:[elementKey subKeyOfLength:PearlCryptKeySize].keyData padding:YES];
|
encryptWithSymmetricKey:[siteKey subKeyOfLength:PearlCryptKeySize].keyData padding:YES];
|
||||||
((MPElementStoredEntity *)element).contentObject = encryptedContent;
|
if ([((MPStoredSiteEntity *)site).contentObject isEqualToData:encryptedContent])
|
||||||
break;
|
return NO;
|
||||||
|
|
||||||
|
((MPStoredSiteEntity *)site).contentObject = encryptedContent;
|
||||||
|
return YES;
|
||||||
}
|
}
|
||||||
case MPElementTypeStoredDevicePrivate: {
|
case MPSiteTypeStoredDevicePrivate: {
|
||||||
if (![element isKindOfClass:[MPElementStoredEntity class]]) {
|
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
|
||||||
wrn( @"Element with stored type %lu is not an MPElementStoredEntity, but a %@.",
|
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
|
||||||
(long)element.type, [element class] );
|
(long)site.type, [site class] );
|
||||||
break;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding]
|
NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding]
|
||||||
encryptWithSymmetricKey:[elementKey subKeyOfLength:PearlCryptKeySize].keyData padding:YES];
|
encryptWithSymmetricKey:[siteKey subKeyOfLength:PearlCryptKeySize].keyData padding:YES];
|
||||||
NSDictionary *elementQuery = [self queryForDevicePrivateElementNamed:element.name];
|
NSDictionary *siteQuery = [self queryForDevicePrivateSiteNamed:site.name];
|
||||||
if (!encryptedContent)
|
if (!encryptedContent)
|
||||||
[PearlKeyChain deleteItemForQuery:elementQuery];
|
[PearlKeyChain deleteItemForQuery:siteQuery];
|
||||||
else
|
else
|
||||||
[PearlKeyChain addOrUpdateItemForQuery:elementQuery withAttributes:@{
|
[PearlKeyChain addOrUpdateItemForQuery:siteQuery withAttributes:@{
|
||||||
(__bridge id)kSecValueData : encryptedContent,
|
(__bridge id)kSecValueData : encryptedContent,
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
(__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
|
(__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
|
||||||
#endif
|
#endif
|
||||||
}];
|
}];
|
||||||
((MPElementStoredEntity *)element).contentObject = nil;
|
((MPStoredSiteEntity *)site).contentObject = nil;
|
||||||
break;
|
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_t group = dispatch_group_create();
|
||||||
dispatch_group_enter( group );
|
dispatch_group_enter( group );
|
||||||
__block NSString *result = nil;
|
__block NSString *result = nil;
|
||||||
[self resolveContentForElement:element usingKey:elementKey result:^(NSString *result_) {
|
[self resolveLoginForSite:site usingKey:siteKey result:^(NSString *result_) {
|
||||||
result = result_;
|
result = result_;
|
||||||
dispatch_group_leave( group );
|
dispatch_group_leave( group );
|
||||||
}];
|
}];
|
||||||
@ -434,65 +507,131 @@
|
|||||||
return result;
|
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." );
|
dispatch_group_t group = dispatch_group_create();
|
||||||
switch (element.type) {
|
dispatch_group_enter( group );
|
||||||
case MPElementTypeGeneratedMaximum:
|
__block NSString *result = nil;
|
||||||
case MPElementTypeGeneratedLong:
|
[self resolvePasswordForSite:site usingKey:siteKey result:^(NSString *result_) {
|
||||||
case MPElementTypeGeneratedMedium:
|
result = result_;
|
||||||
case MPElementTypeGeneratedBasic:
|
dispatch_group_leave( group );
|
||||||
case MPElementTypeGeneratedShort:
|
}];
|
||||||
case MPElementTypeGeneratedPIN: {
|
dispatch_group_wait( group, DISPATCH_TIME_FOREVER );
|
||||||
if (![element isKindOfClass:[MPElementGeneratedEntity class]]) {
|
|
||||||
wrn( @"Element with generated type %lu is not an MPElementGeneratedEntity, but a %@.",
|
return result;
|
||||||
(long)element.type, [element class] );
|
}
|
||||||
|
|
||||||
|
- (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<MPAlgorithm> 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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString *name = element.name;
|
NSString *name = site.name;
|
||||||
MPElementType type = element.type;
|
MPSiteType type = site.type;
|
||||||
NSUInteger counter = ((MPElementGeneratedEntity *)element).counter;
|
NSUInteger counter = ((MPGeneratedSiteEntity *)site).counter;
|
||||||
id<MPAlgorithm> algorithm = nil;
|
id<MPAlgorithm> algorithm = nil;
|
||||||
if (!element.name.length)
|
if (!site.name.length)
|
||||||
err( @"Missing name." );
|
err( @"Missing name." );
|
||||||
else if (!elementKey.keyData.length)
|
else if (!siteKey.keyData.length)
|
||||||
err( @"Missing key." );
|
err( @"Missing key." );
|
||||||
else
|
else
|
||||||
algorithm = element.algorithm;
|
algorithm = site.algorithm;
|
||||||
|
|
||||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
||||||
NSString *result = [algorithm generateContentNamed:name ofType:type withCounter:counter usingKey:elementKey];
|
NSString *result = [algorithm generatePasswordForSiteNamed:name ofType:type withCounter:counter usingKey:siteKey];
|
||||||
resultBlock( result );
|
resultBlock( result );
|
||||||
} );
|
} );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case MPElementTypeStoredPersonal: {
|
case MPSiteTypeStoredPersonal: {
|
||||||
if (![element isKindOfClass:[MPElementStoredEntity class]]) {
|
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
|
||||||
wrn( @"Element with stored type %lu is not an MPElementStoredEntity, but a %@.",
|
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
|
||||||
(long)element.type, [element class] );
|
(long)site.type, [site class] );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSData *encryptedContent = ((MPElementStoredEntity *)element).contentObject;
|
NSData *encryptedContent = ((MPStoredSiteEntity *)site).contentObject;
|
||||||
|
|
||||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
||||||
NSString *result = [self decryptContent:encryptedContent usingKey:elementKey];
|
NSString *result = [self decryptContent:encryptedContent usingKey:siteKey];
|
||||||
resultBlock( result );
|
resultBlock( result );
|
||||||
} );
|
} );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MPElementTypeStoredDevicePrivate: {
|
case MPSiteTypeStoredDevicePrivate: {
|
||||||
NSAssert( [element isKindOfClass:[MPElementStoredEntity class]],
|
NSAssert( [site isKindOfClass:[MPStoredSiteEntity class]],
|
||||||
@"Element with stored type %lu is not an MPElementStoredEntity, but a %@.", (long)element.type,
|
@"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.", (long)site.type,
|
||||||
[element class] );
|
[site class] );
|
||||||
|
|
||||||
NSDictionary *elementQuery = [self queryForDevicePrivateElementNamed:element.name];
|
NSDictionary *siteQuery = [self queryForDevicePrivateSiteNamed:site.name];
|
||||||
NSData *encryptedContent = [PearlKeyChain dataOfItemForQuery:elementQuery];
|
NSData *encryptedContent = [PearlKeyChain dataOfItemForQuery:siteQuery];
|
||||||
|
|
||||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
||||||
NSString *result = [self decryptContent:encryptedContent usingKey:elementKey];
|
NSString *result = [self decryptContent:encryptedContent usingKey:siteKey];
|
||||||
resultBlock( result );
|
resultBlock( result );
|
||||||
} );
|
} );
|
||||||
break;
|
break;
|
||||||
@ -500,91 +639,135 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)importProtectedContent:(NSString *)protectedContent protectedByKey:(MPKey *)importKey
|
- (void)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock {
|
||||||
intoElement:(MPElementEntity *)element usingKey:(MPKey *)elementKey {
|
|
||||||
|
|
||||||
NSAssert( [elementKey.keyID isEqualToData:element.user.keyID], @"Element does not belong to current user." );
|
NSAssert( [siteKey.keyID isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
||||||
switch (element.type) {
|
NSString *name = site.name;
|
||||||
case MPElementTypeGeneratedMaximum:
|
id<MPAlgorithm> algorithm = nil;
|
||||||
case MPElementTypeGeneratedLong:
|
if (!site.name.length)
|
||||||
case MPElementTypeGeneratedMedium:
|
err( @"Missing name." );
|
||||||
case MPElementTypeGeneratedBasic:
|
else if (!siteKey.keyData.length)
|
||||||
case MPElementTypeGeneratedShort:
|
err( @"Missing key." );
|
||||||
case MPElementTypeGeneratedPIN:
|
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<MPAlgorithm> algorithm = nil;
|
||||||
|
if (!site.name.length)
|
||||||
|
err( @"Missing name." );
|
||||||
|
else if (!siteKey.keyData.length)
|
||||||
|
err( @"Missing key." );
|
||||||
|
else
|
||||||
|
algorithm = site.algorithm;
|
||||||
|
|
||||||
|
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
||||||
|
NSString *result = [algorithm generateAnswerForSiteNamed:name onQuestion: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;
|
break;
|
||||||
|
|
||||||
case MPElementTypeStoredPersonal: {
|
case MPSiteTypeStoredPersonal: {
|
||||||
if (![element isKindOfClass:[MPElementStoredEntity class]]) {
|
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
|
||||||
wrn( @"Element with stored type %lu is not an MPElementStoredEntity, but a %@.",
|
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
|
||||||
(long)element.type, [element class] );
|
(long)site.type, [site class] );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if ([importKey.keyID isEqualToData:elementKey.keyID])
|
if ([importKey.keyID isEqualToData:siteKey.keyID])
|
||||||
((MPElementStoredEntity *)element).contentObject = [protectedContent decodeBase64];
|
((MPStoredSiteEntity *)site).contentObject = [protectedContent decodeBase64];
|
||||||
|
|
||||||
else {
|
else {
|
||||||
NSString *clearContent = [self decryptContent:[protectedContent decodeBase64] usingKey:importKey];
|
NSString *clearContent = [self decryptContent:[protectedContent decodeBase64] usingKey:importKey];
|
||||||
[self importClearTextContent:clearContent intoElement:element usingKey:elementKey];
|
[self importClearTextPassword:clearContent intoSite:site usingKey:siteKey];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case MPElementTypeStoredDevicePrivate:
|
case MPSiteTypeStoredDevicePrivate:
|
||||||
break;
|
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." );
|
NSAssert( [siteKey.keyID isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
||||||
switch (element.type) {
|
switch (site.type) {
|
||||||
case MPElementTypeGeneratedMaximum:
|
case MPSiteTypeGeneratedMaximum:
|
||||||
case MPElementTypeGeneratedLong:
|
case MPSiteTypeGeneratedLong:
|
||||||
case MPElementTypeGeneratedMedium:
|
case MPSiteTypeGeneratedMedium:
|
||||||
case MPElementTypeGeneratedBasic:
|
case MPSiteTypeGeneratedBasic:
|
||||||
case MPElementTypeGeneratedShort:
|
case MPSiteTypeGeneratedShort:
|
||||||
case MPElementTypeGeneratedPIN:
|
case MPSiteTypeGeneratedPIN:
|
||||||
|
case MPSiteTypeGeneratedName:
|
||||||
|
case MPSiteTypeGeneratedPhrase:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MPElementTypeStoredPersonal: {
|
case MPSiteTypeStoredPersonal: {
|
||||||
[self saveContent:clearContent toElement:element usingKey:elementKey];
|
[self savePassword:clearContent toSite:site usingKey:siteKey];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case MPElementTypeStoredDevicePrivate:
|
case MPSiteTypeStoredDevicePrivate:
|
||||||
break;
|
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." );
|
NSAssert( [siteKey.keyID isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
||||||
if (!(element.type & MPElementFeatureExportContent))
|
if (!(site.type & MPSiteFeatureExportContent))
|
||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
NSString *result = nil;
|
NSString *result = nil;
|
||||||
switch (element.type) {
|
switch (site.type) {
|
||||||
case MPElementTypeGeneratedMaximum:
|
case MPSiteTypeGeneratedMaximum:
|
||||||
case MPElementTypeGeneratedLong:
|
case MPSiteTypeGeneratedLong:
|
||||||
case MPElementTypeGeneratedMedium:
|
case MPSiteTypeGeneratedMedium:
|
||||||
case MPElementTypeGeneratedBasic:
|
case MPSiteTypeGeneratedBasic:
|
||||||
case MPElementTypeGeneratedShort:
|
case MPSiteTypeGeneratedShort:
|
||||||
case MPElementTypeGeneratedPIN: {
|
case MPSiteTypeGeneratedPIN:
|
||||||
|
case MPSiteTypeGeneratedName:
|
||||||
|
case MPSiteTypeGeneratedPhrase: {
|
||||||
result = nil;
|
result = nil;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case MPElementTypeStoredPersonal: {
|
case MPSiteTypeStoredPersonal: {
|
||||||
if (![element isKindOfClass:[MPElementStoredEntity class]]) {
|
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
|
||||||
wrn( @"Element with stored type %lu is not an MPElementStoredEntity, but a %@.",
|
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
|
||||||
(long)element.type, [element class] );
|
(long)site.type, [site class] );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
result = [((MPElementStoredEntity *)element).contentObject encodeBase64];
|
result = [((MPStoredSiteEntity *)site).contentObject encodeBase64];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case MPElementTypeStoredDevicePrivate: {
|
case MPSiteTypeStoredDevicePrivate: {
|
||||||
result = nil;
|
result = nil;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -598,7 +781,7 @@
|
|||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSDictionary *)queryForDevicePrivateElementNamed:(NSString *)name {
|
- (NSDictionary *)queryForDevicePrivateSiteNamed:(NSString *)name {
|
||||||
|
|
||||||
return [PearlKeyChain createQueryForClass:kSecClassGenericPassword
|
return [PearlKeyChain createQueryForClass:kSecClassGenericPassword
|
||||||
attributes:@{
|
attributes:@{
|
||||||
@ -619,7 +802,7 @@
|
|||||||
return [[NSString alloc] initWithBytes:decryptedContent.bytes length:decryptedContent.length encoding:NSUTF8StringEncoding];
|
return [[NSString alloc] initWithBytes:decryptedContent.bytes length:decryptedContent.length encoding:NSUTF8StringEncoding];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordOfType:(MPElementType)type byAttacker:(MPAttacker)attacker {
|
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordOfType:(MPSiteType)type byAttacker:(MPAttacker)attacker {
|
||||||
|
|
||||||
if (!type)
|
if (!type)
|
||||||
return NO;
|
return NO;
|
||||||
|
@ -25,49 +25,54 @@
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)migrateElement:(MPElementEntity *)element explicit:(BOOL)explicit {
|
- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit {
|
||||||
|
|
||||||
if (element.version != [self version] - 1)
|
if (site.version != [self version] - 1)
|
||||||
// Only migrate from previous version.
|
// Only migrate from previous version.
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
if (!explicit) {
|
if (!explicit) {
|
||||||
if (element.type & MPElementTypeClassGenerated) {
|
if (site.type & MPSiteTypeClassGenerated) {
|
||||||
// This migration requires explicit permission for types of the generated class.
|
// This migration requires explicit permission for types of the generated class.
|
||||||
element.requiresExplicitMigration = YES;
|
site.requiresExplicitMigration = YES;
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply migration.
|
// Apply migration.
|
||||||
element.requiresExplicitMigration = NO;
|
site.requiresExplicitMigration = NO;
|
||||||
element.version = [self version];
|
site.version = [self version];
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)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
|
// Determine the seed whose bytes will be used for calculating a password
|
||||||
uint32_t ncounter = htonl( counter ), nnameLength = htonl( name.length );
|
uint32_t ncounter = htonl( counter ), nnameLength = htonl( name.length ), ncontextLength = htonl( context.length );
|
||||||
NSData *counterBytes = [NSData dataWithBytes:&ncounter length:sizeof( ncounter )];
|
NSData *counterBytes = [NSData dataWithBytes:&ncounter length:sizeof( ncounter )];
|
||||||
NSData *nameLengthBytes = [NSData dataWithBytes:&nnameLength length:sizeof( nnameLength )];
|
NSData *nameLengthBytes = [NSData dataWithBytes:&nnameLength length:sizeof( nnameLength )];
|
||||||
trc( @"seed from: hmac-sha256(%@, 'com.lyndir.masterpassword' | %@ | %@ | %@)", [key.keyData encodeBase64], [nameLengthBytes encodeHex],
|
NSData *contextLengthBytes = [NSData dataWithBytes:&ncontextLength length:sizeof( ncontextLength )];
|
||||||
name, [counterBytes encodeHex] );
|
NSString *scope = [self scopeForVariant:variant];
|
||||||
|
trc( @"seed from: hmac-sha256(%@, %@ | %@ | %@ | %@)",
|
||||||
|
[[key keyID] encodeHex], scope, [nameLengthBytes encodeHex], name, [counterBytes encodeHex] );
|
||||||
NSData *seed = [[NSData dataByConcatenatingDatas:
|
NSData *seed = [[NSData dataByConcatenatingDatas:
|
||||||
[@"com.lyndir.masterpassword" dataUsingEncoding:NSUTF8StringEncoding],
|
[scope dataUsingEncoding:NSUTF8StringEncoding],
|
||||||
nameLengthBytes,
|
nameLengthBytes,
|
||||||
[name dataUsingEncoding:NSUTF8StringEncoding],
|
[name dataUsingEncoding:NSUTF8StringEncoding],
|
||||||
counterBytes,
|
counterBytes,
|
||||||
nil]
|
context? contextLengthBytes: nil,
|
||||||
|
[context dataUsingEncoding:NSUTF8StringEncoding],
|
||||||
|
nil]
|
||||||
hmacWith:PearlHashSHA256 key:key.keyData];
|
hmacWith:PearlHashSHA256 key:key.keyData];
|
||||||
trc( @"seed is: %@", [seed encodeBase64] );
|
trc( @"seed is: %@", [seed encodeHex] );
|
||||||
const unsigned char *seedBytes = seed.bytes;
|
const unsigned char *seedBytes = seed.bytes;
|
||||||
|
|
||||||
// Determine the cipher from the first seed byte.
|
// Determine the cipher from the first seed byte.
|
||||||
NSAssert( [seed length], @"Missing seed." );
|
NSAssert( [seed length], @"Missing seed." );
|
||||||
NSArray *typeCiphers = [self ciphersForType:type];
|
NSArray *typeCiphers = [self ciphersForType:type];
|
||||||
NSString *cipher = typeCiphers[seedBytes[0] % [typeCiphers count]];
|
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.
|
// Encode the content, character by character, using subsequent seed bytes and the cipher.
|
||||||
NSAssert( [seed length] >= [cipher length] + 1, @"Insufficient seed bytes to encode cipher." );
|
NSAssert( [seed length] >= [cipher length] + 1, @"Insufficient seed bytes to encode cipher." );
|
||||||
|
21
MasterPassword/ObjC/MPAlgorithmV2.h
Normal file
21
MasterPassword/ObjC/MPAlgorithmV2.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||||
|
*
|
||||||
|
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||||
|
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
|
*
|
||||||
|
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||||
|
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
//
|
||||||
|
// MPAlgorithmV1
|
||||||
|
//
|
||||||
|
// Created by Maarten Billemont on 17/07/12.
|
||||||
|
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MPAlgorithmV1.h"
|
||||||
|
|
||||||
|
@interface MPAlgorithmV2 : MPAlgorithmV1
|
||||||
|
@end
|
97
MasterPassword/ObjC/MPAlgorithmV2.m
Normal file
97
MasterPassword/ObjC/MPAlgorithmV2.m
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/**
|
||||||
|
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||||
|
*
|
||||||
|
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||||
|
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
|
*
|
||||||
|
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||||
|
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
//
|
||||||
|
// MPAlgorithmV1
|
||||||
|
//
|
||||||
|
// Created by Maarten Billemont on 17/07/12.
|
||||||
|
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <objc/runtime.h>
|
||||||
|
#import "MPAlgorithmV2.h"
|
||||||
|
#import "MPEntities.h"
|
||||||
|
|
||||||
|
@implementation MPAlgorithmV2
|
||||||
|
|
||||||
|
- (NSUInteger)version {
|
||||||
|
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit {
|
||||||
|
|
||||||
|
if (site.version != [self version] - 1)
|
||||||
|
// Only migrate from previous version.
|
||||||
|
return NO;
|
||||||
|
|
||||||
|
if (!explicit) {
|
||||||
|
if (site.type & MPSiteTypeClassGenerated && site.name.length != [site.name dataUsingEncoding:NSUTF8StringEncoding].length) {
|
||||||
|
// This migration requires explicit permission for types of the generated class.
|
||||||
|
site.requiresExplicitMigration = YES;
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply migration.
|
||||||
|
site.requiresExplicitMigration = NO;
|
||||||
|
site.version = [self version];
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)generateContentForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter
|
||||||
|
variant:(MPSiteVariant)variant context:(NSString *)context usingKey:(MPKey *)key {
|
||||||
|
|
||||||
|
// Determine the seed whose bytes will be used for calculating a password
|
||||||
|
NSData *nameBytes = [name dataUsingEncoding:NSUTF8StringEncoding];
|
||||||
|
NSData *contextBytes = [context dataUsingEncoding:NSUTF8StringEncoding];
|
||||||
|
uint32_t ncounter = htonl( counter ), nnameLength = htonl( nameBytes.length ), ncontextLength = htonl( contextBytes.length );
|
||||||
|
NSData *counterBytes = [NSData dataWithBytes:&ncounter length:sizeof( ncounter )];
|
||||||
|
NSData *nameLengthBytes = [NSData dataWithBytes:&nnameLength length:sizeof( nnameLength )];
|
||||||
|
NSData *contextLengthBytes = [NSData dataWithBytes:&ncontextLength length:sizeof( ncontextLength )];
|
||||||
|
NSString *scope = [self scopeForVariant:variant];
|
||||||
|
NSData *scopeBytes = [scope dataUsingEncoding:NSUTF8StringEncoding];
|
||||||
|
trc( @"seed from: hmac-sha256(%@, %@ | %@ | %@ | %@)",
|
||||||
|
[[key keyID] encodeHex], scope, [nameLengthBytes encodeHex], name, [counterBytes encodeHex] );
|
||||||
|
NSData *seed = [[NSData dataByConcatenatingDatas:
|
||||||
|
scopeBytes,
|
||||||
|
nameLengthBytes,
|
||||||
|
nameBytes,
|
||||||
|
counterBytes,
|
||||||
|
context? contextLengthBytes: nil,
|
||||||
|
contextBytes,
|
||||||
|
nil]
|
||||||
|
hmacWith:PearlHashSHA256 key:key.keyData];
|
||||||
|
trc( @"seed is: %@", [seed encodeHex] );
|
||||||
|
const unsigned char *seedBytes = seed.bytes;
|
||||||
|
|
||||||
|
// Determine the cipher from the first seed byte.
|
||||||
|
NSAssert( [seed length], @"Missing seed." );
|
||||||
|
NSArray *typeCiphers = [self ciphersForType:type];
|
||||||
|
NSString *cipher = typeCiphers[seedBytes[0] % [typeCiphers count]];
|
||||||
|
trc( @"type %@ (%lu), ciphers: %@, selected: %@", [self nameOfType:type], (unsigned long)type, typeCiphers, cipher );
|
||||||
|
|
||||||
|
// Encode the content, character by character, using subsequent seed bytes and the cipher.
|
||||||
|
NSAssert( [seed length] >= [cipher length] + 1, @"Insufficient seed bytes to encode cipher." );
|
||||||
|
NSMutableString *content = [NSMutableString stringWithCapacity:[cipher length]];
|
||||||
|
for (NSUInteger c = 0; c < [cipher length]; ++c) {
|
||||||
|
uint16_t keyByte = seedBytes[c + 1];
|
||||||
|
NSString *cipherClass = [cipher substringWithRange:NSMakeRange( c, 1 )];
|
||||||
|
NSString *cipherClassCharacters = [self charactersForCipherClass:cipherClass];
|
||||||
|
NSString *character = [cipherClassCharacters substringWithRange:NSMakeRange( keyByte % [cipherClassCharacters length], 1 )];
|
||||||
|
|
||||||
|
trc( @"class %@ has characters: %@, index: %u, selected: %@", cipherClass, cipherClassCharacters, keyByte, character );
|
||||||
|
[content appendString:character];
|
||||||
|
}
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
26
MasterPassword/ObjC/MPAppDelegate_InApp.h
Normal file
26
MasterPassword/ObjC/MPAppDelegate_InApp.h
Normal file
@ -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
|
109
MasterPassword/ObjC/MPAppDelegate_InApp.m
Normal file
109
MasterPassword/ObjC/MPAppDelegate_InApp.m
Normal file
@ -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 <StoreKit/StoreKit.h>
|
||||||
|
|
||||||
|
@interface MPAppDelegate_Shared(InApp_Private)<SKProductsRequestDelegate, SKPaymentTransactionObserver>
|
||||||
|
@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
|
@ -9,6 +9,12 @@
|
|||||||
#import "MPAppDelegate_Key.h"
|
#import "MPAppDelegate_Key.h"
|
||||||
#import "MPAppDelegate_Store.h"
|
#import "MPAppDelegate_Store.h"
|
||||||
|
|
||||||
|
@interface MPAppDelegate_Shared()
|
||||||
|
|
||||||
|
@property(strong, nonatomic) MPKey *key;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
@implementation MPAppDelegate_Shared(Key)
|
@implementation MPAppDelegate_Shared(Key)
|
||||||
|
|
||||||
static NSDictionary *keyQuery(MPUserEntity *user) {
|
static NSDictionary *keyQuery(MPUserEntity *user) {
|
||||||
@ -85,8 +91,8 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
|
|||||||
if ([password length] && (tryKey = [MPAlgorithmDefault keyForPassword:password ofUserNamed:user.name])) {
|
if ([password length] && (tryKey = [MPAlgorithmDefault keyForPassword:password ofUserNamed:user.name])) {
|
||||||
user.keyID = tryKey.keyID;
|
user.keyID = tryKey.keyID;
|
||||||
|
|
||||||
// Migrate existing elements.
|
// Migrate existing sites.
|
||||||
[self migrateElementsForUser:user saveInContext:moc toKey:tryKey];
|
[self migrateSitesForUser:user saveInContext:moc toKey:tryKey];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,8 +149,8 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
user.lastUsed = [NSDate date];
|
user.lastUsed = [NSDate date];
|
||||||
[moc saveToStore];
|
|
||||||
self.activeUser = user;
|
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.
|
// 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])
|
if ([[MPConfig get].checkInconsistency boolValue])
|
||||||
@ -158,23 +164,23 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
|
|||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)migrateElementsForUser:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc toKey:(MPKey *)newKey {
|
- (void)migrateSitesForUser:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc toKey:(MPKey *)newKey {
|
||||||
|
|
||||||
if (![user.elements count])
|
if (![user.sites count])
|
||||||
// Nothing to migrate.
|
// Nothing to migrate.
|
||||||
return;
|
return;
|
||||||
|
|
||||||
MPKey *recoverKey = newKey;
|
MPKey *recoverKey = newKey;
|
||||||
#ifdef PEARL_UIKIT
|
#ifdef PEARL_UIKIT
|
||||||
PearlOverlay *activityOverlay = [PearlOverlay showProgressOverlayWithTitle:PearlString( @"Migrating %ld sites...",
|
PearlOverlay *activityOverlay = [PearlOverlay showProgressOverlayWithTitle:PearlString( @"Migrating %ld sites...",
|
||||||
(long)[user.elements count] )];
|
(long)[user.sites count] )];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (MPElementEntity *element in user.elements) {
|
for (MPSiteEntity *site in user.sites) {
|
||||||
if (element.type & MPElementTypeClassStored) {
|
if (site.type & MPSiteTypeClassStored) {
|
||||||
NSString *content;
|
NSString *content;
|
||||||
while (!(content = [element.algorithm storedContentForElement:(MPElementStoredEntity *)element usingKey:recoverKey])) {
|
while (!(content = [site.algorithm storedPasswordForSite:(MPStoredSiteEntity *)site usingKey:recoverKey])) {
|
||||||
// Failed to decrypt element with the current recoveryKey. Ask user for a new one to use.
|
// Failed to decrypt site with the current recoveryKey. Ask user for a new one to use.
|
||||||
__block NSString *masterPassword = nil;
|
__block NSString *masterPassword = nil;
|
||||||
|
|
||||||
#ifdef PEARL_UIKIT
|
#ifdef PEARL_UIKIT
|
||||||
@ -182,7 +188,7 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
|
|||||||
dispatch_group_enter( recoverPasswordGroup );
|
dispatch_group_enter( recoverPasswordGroup );
|
||||||
[PearlAlert showAlertWithTitle:@"Enter Old Master Password"
|
[PearlAlert showAlertWithTitle:@"Enter Old Master Password"
|
||||||
message:PearlString( @"Your old master password is required to migrate the stored password for %@",
|
message:PearlString( @"Your old master password is required to migrate the stored password for %@",
|
||||||
element.name )
|
site.name )
|
||||||
viewStyle:UIAlertViewStyleSecureTextInput
|
viewStyle:UIAlertViewStyleSecureTextInput
|
||||||
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
|
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
|
||||||
@try {
|
@try {
|
||||||
@ -202,7 +208,7 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
|
|||||||
// Don't Migrate
|
// Don't Migrate
|
||||||
break;
|
break;
|
||||||
|
|
||||||
recoverKey = [element.algorithm keyForPassword:masterPassword ofUserNamed:user.name];
|
recoverKey = [site.algorithm keyForPassword:masterPassword ofUserNamed:user.name];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!content)
|
if (!content)
|
||||||
@ -210,7 +216,7 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
if (![recoverKey isEqualToKey:newKey])
|
if (![recoverKey isEqualToKey:newKey])
|
||||||
[element.algorithm saveContent:content toElement:element usingKey:newKey];
|
[site.algorithm savePassword:content toSite:site usingKey:newKey];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,19 +9,19 @@
|
|||||||
#import "MPEntities.h"
|
#import "MPEntities.h"
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
|
|
||||||
@interface MPAppDelegate_Shared : PearlAppDelegate
|
@interface MPAppDelegate_Shared : PearlAppDelegate
|
||||||
#else
|
#else
|
||||||
@interface MPAppDelegate_Shared : NSObject <PearlConfigDelegate>
|
@interface MPAppDelegate_Shared : NSObject <PearlConfigDelegate>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@property(strong, nonatomic) MPKey *key;
|
@property(strong, nonatomic, readonly) MPKey *key;
|
||||||
@property(strong, nonatomic) NSManagedObjectID *activeUserOID;
|
@property(strong, nonatomic, readonly) NSManagedObjectID *activeUserOID;
|
||||||
|
|
||||||
+ (instancetype)get;
|
+ (instancetype)get;
|
||||||
|
|
||||||
- (MPUserEntity *)activeUserForMainThread;
|
- (MPUserEntity *)activeUserForMainThread;
|
||||||
- (MPUserEntity *)activeUserInContext:(NSManagedObjectContext *)context;
|
- (MPUserEntity *)activeUserInContext:(NSManagedObjectContext *)context;
|
||||||
- (void)setActiveUser:(MPUserEntity *)activeUser;
|
- (void)setActiveUser:(MPUserEntity *)activeUser;
|
||||||
|
- (void)handleCoordinatorError:(NSError *)error;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -6,10 +6,18 @@
|
|||||||
// Copyright (c) 2011 Lyndir. All rights reserved.
|
// Copyright (c) 2011 Lyndir. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#import <StoreKit/StoreKit.h>
|
||||||
#import "MPAppDelegate_Shared.h"
|
#import "MPAppDelegate_Shared.h"
|
||||||
#import "MPAppDelegate_Store.h"
|
#import "MPAppDelegate_Store.h"
|
||||||
#import "MPAppDelegate_Key.h"
|
#import "MPAppDelegate_Key.h"
|
||||||
|
|
||||||
|
@interface MPAppDelegate_Shared ()
|
||||||
|
|
||||||
|
@property(strong, nonatomic) MPKey *key;
|
||||||
|
@property(strong, nonatomic) NSManagedObjectID *activeUserOID;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
@implementation MPAppDelegate_Shared
|
@implementation MPAppDelegate_Shared
|
||||||
|
|
||||||
+ (MPAppDelegate_Shared *)get {
|
+ (MPAppDelegate_Shared *)get {
|
||||||
@ -45,9 +53,13 @@
|
|||||||
|
|
||||||
NSError *error;
|
NSError *error;
|
||||||
if (activeUser.objectID.isTemporaryID && ![activeUser.managedObjectContext obtainPermanentIDsForObjects:@[ activeUser ] error:&error])
|
if (activeUser.objectID.isTemporaryID && ![activeUser.managedObjectContext obtainPermanentIDsForObjects:@[ activeUser ] error:&error])
|
||||||
err(@"Failed to obtain a permanent object ID after setting active user: %@", error);
|
err(@"Failed to obtain a permanent object ID after setting active user: %@", [error fullDescription]);
|
||||||
|
|
||||||
self.activeUserOID = activeUser.objectID;
|
self.activeUserOID = activeUser.objectID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)handleCoordinatorError:(NSError *)error {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
#import "MPAppDelegate_Shared.h"
|
#import "MPAppDelegate_Shared.h"
|
||||||
|
|
||||||
#import "UbiquityStoreManager.h"
|
|
||||||
#import "MPFixable.h"
|
#import "MPFixable.h"
|
||||||
|
|
||||||
typedef NS_ENUM( NSUInteger, MPImportResult ) {
|
typedef NS_ENUM( NSUInteger, MPImportResult ) {
|
||||||
@ -19,7 +18,7 @@ typedef NS_ENUM( NSUInteger, MPImportResult ) {
|
|||||||
MPImportResultInternalError,
|
MPImportResultInternalError,
|
||||||
};
|
};
|
||||||
|
|
||||||
@interface MPAppDelegate_Shared(Store)<UbiquityStoreManagerDelegate>
|
@interface MPAppDelegate_Shared(Store)
|
||||||
|
|
||||||
+ (NSManagedObjectContext *)managedObjectContextForMainThreadIfReady;
|
+ (NSManagedObjectContext *)managedObjectContextForMainThreadIfReady;
|
||||||
+ (BOOL)managedObjectContextForMainThreadPerformBlock:(void (^)(NSManagedObjectContext *mainContext))mocBlock;
|
+ (BOOL)managedObjectContextForMainThreadPerformBlock:(void (^)(NSManagedObjectContext *mainContext))mocBlock;
|
||||||
@ -27,12 +26,12 @@ typedef NS_ENUM( NSUInteger, MPImportResult ) {
|
|||||||
+ (BOOL)managedObjectContextPerformBlock:(void (^)(NSManagedObjectContext *context))mocBlock;
|
+ (BOOL)managedObjectContextPerformBlock:(void (^)(NSManagedObjectContext *context))mocBlock;
|
||||||
+ (BOOL)managedObjectContextPerformBlockAndWait:(void (^)(NSManagedObjectContext *context))mocBlock;
|
+ (BOOL)managedObjectContextPerformBlockAndWait:(void (^)(NSManagedObjectContext *context))mocBlock;
|
||||||
|
|
||||||
- (UbiquityStoreManager *)storeManager;
|
|
||||||
- (MPFixableResult)findAndFixInconsistenciesSaveInContext:(NSManagedObjectContext *)context;
|
- (MPFixableResult)findAndFixInconsistenciesSaveInContext:(NSManagedObjectContext *)context;
|
||||||
|
- (void)deleteAndResetStore;
|
||||||
|
|
||||||
/** @param completion The block to execute after adding the element, executed from the main thread with the new element in the main MOC. */
|
/** @param completion The block to execute after adding the site, executed from the main thread with the new site in the main MOC. */
|
||||||
- (void)addElementNamed:(NSString *)siteName completion:(void ( ^ )(MPElementEntity *element, NSManagedObjectContext *context))completion;
|
- (void)addSiteNamed:(NSString *)siteName completion:(void ( ^ )(MPSiteEntity *site, NSManagedObjectContext *context))completion;
|
||||||
- (MPElementEntity *)changeElement:(MPElementEntity *)element saveInContext:(NSManagedObjectContext *)context toType:(MPElementType)type;
|
- (MPSiteEntity *)changeSite:(MPSiteEntity *)site saveInContext:(NSManagedObjectContext *)context toType:(MPSiteType)type;
|
||||||
- (MPImportResult)importSites:(NSString *)importedSitesString
|
- (MPImportResult)importSites:(NSString *)importedSitesString
|
||||||
askImportPassword:(NSString *(^)(NSString *userName))importPassword
|
askImportPassword:(NSString *(^)(NSString *userName))importPassword
|
||||||
askUserPassword:(NSString *(^)(NSString *userName, NSUInteger importCount, NSUInteger deleteCount))userPassword;
|
askUserPassword:(NSString *(^)(NSString *userName, NSUInteger importCount, NSUInteger deleteCount))userPassword;
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#import "MPAppDelegate_Store.h"
|
#import "MPAppDelegate_Store.h"
|
||||||
|
#import "MPGeneratedSiteEntity.h"
|
||||||
|
#import "NSManagedObjectModel+KCOrderedAccessorFix.h"
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
#define STORE_OPTIONS NSPersistentStoreFileProtectionKey : NSFileProtectionComplete,
|
#define STORE_OPTIONS NSPersistentStoreFileProtectionKey : NSFileProtectionComplete,
|
||||||
@ -14,27 +16,21 @@
|
|||||||
#define STORE_OPTIONS
|
#define STORE_OPTIONS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define MPCloudContainerIdentifier @"HL3Q45LX9N.com.lyndir.lhunath.MasterPassword.shared"
|
#define MPStoreMigrationLevelKey @"MPMigrationLevelLocalStoreKey"
|
||||||
#define MPMigrationLevelLocalStoreKey @"MPMigrationLevelLocalStoreKey"
|
|
||||||
#define MPMigrationLevelCloudStoreKey @"MPMigrationLevelCloudStoreKey"
|
|
||||||
|
|
||||||
typedef NS_ENUM( NSInteger, MPMigrationLevelLocalStore ) {
|
typedef NS_ENUM( NSInteger, MPStoreMigrationLevel ) {
|
||||||
MPMigrationLevelLocalStoreV1,
|
MPStoreMigrationLevelV1,
|
||||||
MPMigrationLevelLocalStoreV2,
|
MPStoreMigrationLevelV2,
|
||||||
MPMigrationLevelLocalStoreCurrent = MPMigrationLevelLocalStoreV2,
|
MPStoreMigrationLevelV3,
|
||||||
};
|
MPStoreMigrationLevelCurrent = MPStoreMigrationLevelV3,
|
||||||
|
|
||||||
typedef NS_ENUM( NSInteger, MPMigrationLevelCloudStore ) {
|
|
||||||
MPMigrationLevelCloudStoreV1,
|
|
||||||
MPMigrationLevelCloudStoreV2,
|
|
||||||
MPMigrationLevelCloudStoreV3,
|
|
||||||
MPMigrationLevelCloudStoreCurrent = MPMigrationLevelCloudStoreV3,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@implementation MPAppDelegate_Shared(Store)
|
@implementation MPAppDelegate_Shared(Store)
|
||||||
|
|
||||||
PearlAssociatedObjectProperty( id, SaveObserver, saveObserver );
|
PearlAssociatedObjectProperty( id, SaveObserver, saveObserver );
|
||||||
|
|
||||||
|
PearlAssociatedObjectProperty( NSPersistentStoreCoordinator*, PersistentStoreCoordinator, persistentStoreCoordinator );
|
||||||
|
|
||||||
PearlAssociatedObjectProperty( NSManagedObjectContext*, PrivateManagedObjectContext, privateManagedObjectContext );
|
PearlAssociatedObjectProperty( NSManagedObjectContext*, PrivateManagedObjectContext, privateManagedObjectContext );
|
||||||
|
|
||||||
PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext, mainManagedObjectContext );
|
PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext, mainManagedObjectContext );
|
||||||
@ -109,47 +105,144 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
|
|||||||
|
|
||||||
- (NSManagedObjectContext *)mainManagedObjectContextIfReady {
|
- (NSManagedObjectContext *)mainManagedObjectContextIfReady {
|
||||||
|
|
||||||
[self storeManager];
|
[self loadStore];
|
||||||
return self.mainManagedObjectContext;
|
return self.mainManagedObjectContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSManagedObjectContext *)privateManagedObjectContextIfReady {
|
- (NSManagedObjectContext *)privateManagedObjectContextIfReady {
|
||||||
|
|
||||||
[self storeManager];
|
[self loadStore];
|
||||||
return self.privateManagedObjectContext;
|
return self.privateManagedObjectContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UbiquityStoreManager *)storeManager {
|
- (NSURL *)localStoreURL {
|
||||||
|
|
||||||
static UbiquityStoreManager *storeManager = nil;
|
NSURL *applicationSupportURL = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory
|
||||||
if (storeManager)
|
inDomains:NSUserDomainMask] lastObject];
|
||||||
return storeManager;
|
return [[[applicationSupportURL
|
||||||
|
URLByAppendingPathComponent:[NSBundle mainBundle].bundleIdentifier isDirectory:YES]
|
||||||
|
URLByAppendingPathComponent:@"UbiquityStore" isDirectory:NO]
|
||||||
|
URLByAppendingPathExtension:@"sqlite"];
|
||||||
|
}
|
||||||
|
|
||||||
storeManager = [[UbiquityStoreManager alloc] initStoreNamed:nil withManagedObjectModel:nil localStoreURL:nil
|
- (void)loadStore {
|
||||||
containerIdentifier:MPCloudContainerIdentifier
|
|
||||||
storeConfiguration:nil storeOptions:@{ STORE_OPTIONS }
|
@synchronized (self) {
|
||||||
delegate: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
|
#if TARGET_OS_IPHONE
|
||||||
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillTerminateNotification object:UIApp
|
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillTerminateNotification object:UIApp
|
||||||
queue:[NSOperationQueue mainQueue] usingBlock:
|
queue:[NSOperationQueue mainQueue] usingBlock:
|
||||||
^(NSNotification *note) {
|
^(NSNotification *note) {
|
||||||
[[self mainManagedObjectContext] saveToStore];
|
[self.mainManagedObjectContext saveToStore];
|
||||||
}];
|
}];
|
||||||
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillResignActiveNotification object:UIApp
|
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillResignActiveNotification object:UIApp
|
||||||
queue:[NSOperationQueue mainQueue] usingBlock:
|
queue:[NSOperationQueue mainQueue] usingBlock:
|
||||||
^(NSNotification *note) {
|
^(NSNotification *note) {
|
||||||
[[self mainManagedObjectContext] saveToStore];
|
[self.mainManagedObjectContext saveToStore];
|
||||||
}];
|
}];
|
||||||
#else
|
#else
|
||||||
[[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationWillTerminateNotification object:NSApp
|
[[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationWillTerminateNotification object:NSApp
|
||||||
queue:[NSOperationQueue mainQueue] usingBlock:
|
queue:[NSOperationQueue mainQueue] usingBlock:
|
||||||
^(NSNotification *note) {
|
^(NSNotification *note) {
|
||||||
[self.mainManagedObjectContextIfReady saveToStore];
|
[self.mainManagedObjectContext saveToStore];
|
||||||
}];
|
}];
|
||||||
#endif
|
#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 {
|
- (MPFixableResult)findAndFixInconsistenciesSaveInContext:(NSManagedObjectContext *)context {
|
||||||
@ -164,7 +257,7 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
|
|||||||
fetchRequest.entity = entity;
|
fetchRequest.entity = entity;
|
||||||
NSArray *objects = [context executeFetchRequest:fetchRequest error:&error];
|
NSArray *objects = [context executeFetchRequest:fetchRequest error:&error];
|
||||||
if (!objects) {
|
if (!objects) {
|
||||||
err( @"Failed to fetch %@ objects: %@", entity, error );
|
err( @"Failed to fetch %@ objects: %@", entity, [error fullDescription] );
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,101 +280,25 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)migrateStoreForManager:(UbiquityStoreManager *)manager isCloud:(BOOL)isCloudStore {
|
- (void)migrateStore {
|
||||||
|
|
||||||
[self migrateLocalStore];
|
MPStoreMigrationLevel migrationLevel = (signed)[[NSUserDefaults standardUserDefaults] integerForKey:MPStoreMigrationLevelKey];
|
||||||
|
if (migrationLevel >= MPStoreMigrationLevelCurrent)
|
||||||
if (isCloudStore)
|
|
||||||
[self migrateCloudStore];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)migrateLocalStore {
|
|
||||||
|
|
||||||
MPMigrationLevelLocalStore migrationLevel = (signed)[[NSUserDefaults standardUserDefaults] integerForKey:MPMigrationLevelLocalStoreKey];
|
|
||||||
if (migrationLevel >= MPMigrationLevelLocalStoreCurrent)
|
|
||||||
// Local store up-to-date.
|
// Local store up-to-date.
|
||||||
return;
|
return;
|
||||||
|
|
||||||
inf( @"Local store migration level: %d (current %d)", (signed)migrationLevel, (signed)MPMigrationLevelLocalStoreCurrent );
|
inf( @"Local store migration level: %d (current %d)", (signed)migrationLevel, (signed)MPStoreMigrationLevelCurrent );
|
||||||
if (migrationLevel <= MPMigrationLevelLocalStoreV1) if (![self migrateV1LocalStore]) {
|
if (migrationLevel == MPStoreMigrationLevelV1 && ![self migrateV1LocalStore]) {
|
||||||
inf( @"Failed to migrate old V1 to new local store." );
|
inf( @"Failed to migrate old V1 to new local store." );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (migrationLevel == MPStoreMigrationLevelV2 && ![self migrateV2LocalStore]) {
|
||||||
[[NSUserDefaults standardUserDefaults] setInteger:MPMigrationLevelLocalStoreCurrent forKey:MPMigrationLevelLocalStoreKey];
|
inf( @"Failed to migrate old V2 to new local store." );
|
||||||
inf( @"Successfully migrated old to new local store." );
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)migrateCloudStore {
|
|
||||||
|
|
||||||
MPMigrationLevelCloudStore migrationLevel = (signed)[[NSUserDefaults standardUserDefaults] integerForKey:MPMigrationLevelCloudStoreKey];
|
|
||||||
if (migrationLevel >= MPMigrationLevelCloudStoreCurrent)
|
|
||||||
// Cloud store up-to-date.
|
|
||||||
return;
|
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];
|
[[NSUserDefaults standardUserDefaults] setInteger:MPStoreMigrationLevelCurrent forKey:MPStoreMigrationLevelKey];
|
||||||
inf( @"Successfully migrated old to new cloud store." );
|
inf( @"Successfully migrated old to new local store." );
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)migrateV1CloudStore {
|
|
||||||
|
|
||||||
// Migrate cloud enabled preference.
|
|
||||||
NSNumber *oldCloudEnabled = [[NSUserDefaults standardUserDefaults] objectForKey:@"iCloudEnabledKey"];
|
|
||||||
if ([oldCloudEnabled boolValue])
|
|
||||||
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:USMCloudEnabledKey];
|
|
||||||
|
|
||||||
// Migrate cloud store.
|
|
||||||
NSString *uuid = [[NSUserDefaults standardUserDefaults] stringForKey:@"LocalUUIDKey"];
|
|
||||||
if (!uuid) {
|
|
||||||
inf( @"No V1 cloud store to migrate." );
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
inf( @"Migrating V1 cloud store: %@ -> %@", uuid, [self.storeManager valueForKey:@"storeUUID"] );
|
|
||||||
NSURL *cloudContainerURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:MPCloudContainerIdentifier];
|
|
||||||
NSURL *oldCloudContentURL = [[cloudContainerURL
|
|
||||||
URLByAppendingPathComponent:@"Data" isDirectory:YES]
|
|
||||||
URLByAppendingPathComponent:uuid isDirectory:YES];
|
|
||||||
NSURL *oldCloudStoreURL = [[[cloudContainerURL
|
|
||||||
URLByAppendingPathComponent:@"Database.nosync" isDirectory:YES]
|
|
||||||
URLByAppendingPathComponent:uuid isDirectory:NO] URLByAppendingPathExtension:@"sqlite"];
|
|
||||||
|
|
||||||
return [self migrateFromCloudStore:oldCloudStoreURL cloudContent:oldCloudContentURL];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)migrateV2CloudStore {
|
|
||||||
|
|
||||||
// Migrate cloud store.
|
|
||||||
NSString *uuid = [[NSUbiquitousKeyValueStore defaultStore] stringForKey:@"USMStoreUUIDKey"];
|
|
||||||
if (!uuid) {
|
|
||||||
inf( @"No V2 cloud store to migrate." );
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
inf( @"Migrating V2 cloud store: %@ -> %@", uuid, [self.storeManager valueForKey:@"storeUUID"] );
|
|
||||||
NSURL *cloudContainerURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:MPCloudContainerIdentifier];
|
|
||||||
NSURL *oldCloudContentURL = [[cloudContainerURL
|
|
||||||
URLByAppendingPathComponent:@"CloudLogs" isDirectory:YES]
|
|
||||||
URLByAppendingPathComponent:uuid isDirectory:YES];
|
|
||||||
NSURL *oldCloudStoreURL = [[[cloudContainerURL
|
|
||||||
URLByAppendingPathComponent:@"CloudStore.nosync" isDirectory:YES]
|
|
||||||
URLByAppendingPathComponent:uuid isDirectory:NO] URLByAppendingPathExtension:@"sqlite"];
|
|
||||||
|
|
||||||
return [self migrateFromCloudStore:oldCloudStoreURL cloudContent:oldCloudContentURL];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)migrateV1LocalStore {
|
- (BOOL)migrateV1LocalStore {
|
||||||
@ -296,146 +313,63 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
|
|||||||
}
|
}
|
||||||
|
|
||||||
inf( @"Migrating V1 local store" );
|
inf( @"Migrating V1 local store" );
|
||||||
return [self migrateFromLocalStore:oldLocalStoreURL];
|
NSURL *newLocalStoreURL = [self localStoreURL];
|
||||||
}
|
NSError *error = nil;
|
||||||
|
if (![[NSFileManager defaultManager] createDirectoryAtURL:[newLocalStoreURL URLByDeletingLastPathComponent]
|
||||||
- (BOOL)migrateFromLocalStore:(NSURL *)oldLocalStoreURL {
|
withIntermediateDirectories:YES attributes:nil error:&error]) {
|
||||||
|
err( @"Couldn't create our application support directory: %@", [error fullDescription] );
|
||||||
NSURL *newLocalStoreURL = [self.storeManager URLForLocalStore];
|
return NO;
|
||||||
if ([[NSFileManager defaultManager] fileExistsAtPath:newLocalStoreURL.path isDirectory:NULL]) {
|
|
||||||
wrn( @"Can't migrate local store: A new local store already exists." );
|
|
||||||
return YES;
|
|
||||||
}
|
}
|
||||||
|
if (![[NSFileManager defaultManager] moveItemAtURL:oldLocalStoreURL toURL:newLocalStoreURL error:&error]) {
|
||||||
if (![self.storeManager migrateStore:oldLocalStoreURL withOptions:nil
|
err( @"Couldn't move the old store to the new location: %@", [error fullDescription] );
|
||||||
toStore:newLocalStoreURL withOptions:nil
|
|
||||||
strategy:0 error:nil cause:nil context:nil]) {
|
|
||||||
self.storeManager.localStoreURL = oldLocalStoreURL;
|
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
inf( @"Successfully migrated to new local store." );
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)migrateFromCloudStore:(NSURL *)oldCloudStoreURL cloudContent:(NSURL *)oldCloudContentURL {
|
- (BOOL)migrateV2LocalStore {
|
||||||
|
|
||||||
if (![self.storeManager cloudSafeForSeeding]) {
|
NSURL *applicationSupportURL = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory
|
||||||
inf( @"Can't migrate cloud store: A new cloud store already exists." );
|
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;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSURL *newCloudStoreURL = [self.storeManager URLForCloudStore];
|
inf( @"Migrating V2 local store" );
|
||||||
if (![self.storeManager migrateStore:oldCloudStoreURL withOptions:nil
|
NSURL *newLocalStoreURL = [self localStoreURL];
|
||||||
toStore:newCloudStoreURL withOptions:nil
|
NSError *error = nil;
|
||||||
strategy:0 error:nil cause:nil context: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;
|
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;
|
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
|
#pragma mark - Utilities
|
||||||
|
|
||||||
- (void)addElementNamed:(NSString *)siteName completion:(void ( ^ )(MPElementEntity *element, NSManagedObjectContext *context))completion {
|
- (void)addSiteNamed:(NSString *)siteName completion:(void ( ^ )(MPSiteEntity *site, NSManagedObjectContext *context))completion {
|
||||||
|
|
||||||
if (![siteName length]) {
|
if (![siteName length]) {
|
||||||
completion( nil, nil );
|
completion( nil, nil );
|
||||||
@ -450,61 +384,61 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MPElementType type = activeUser.defaultType;
|
MPSiteType type = activeUser.defaultType;
|
||||||
NSString *typeEntityName = [MPAlgorithmDefault classNameOfType:type];
|
NSString *typeEntityName = [MPAlgorithmDefault classNameOfType:type];
|
||||||
|
|
||||||
MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:typeEntityName inManagedObjectContext:context];
|
MPSiteEntity *site = [NSEntityDescription insertNewObjectForEntityForName:typeEntityName inManagedObjectContext:context];
|
||||||
element.name = siteName;
|
site.name = siteName;
|
||||||
element.user = activeUser;
|
site.user = activeUser;
|
||||||
element.type = type;
|
site.type = type;
|
||||||
element.lastUsed = [NSDate date];
|
site.lastUsed = [NSDate date];
|
||||||
element.version = MPAlgorithmDefaultVersion;
|
site.version = MPAlgorithmDefaultVersion;
|
||||||
|
|
||||||
NSError *error = nil;
|
NSError *error = nil;
|
||||||
if (element.objectID.isTemporaryID && ![context obtainPermanentIDsForObjects:@[ element ] error:&error])
|
if (site.objectID.isTemporaryID && ![context obtainPermanentIDsForObjects:@[ site ] error:&error])
|
||||||
err( @"Failed to obtain a permanent object ID after creating new element: %@", error );
|
err( @"Failed to obtain a permanent object ID after creating new site: %@", [error fullDescription] );
|
||||||
|
|
||||||
[context saveToStore];
|
[context saveToStore];
|
||||||
|
|
||||||
completion( element, context );
|
completion( site, context );
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MPElementEntity *)changeElement:(MPElementEntity *)element saveInContext:(NSManagedObjectContext *)context toType:(MPElementType)type {
|
- (MPSiteEntity *)changeSite:(MPSiteEntity *)site saveInContext:(NSManagedObjectContext *)context toType:(MPSiteType)type {
|
||||||
|
|
||||||
if (element.type == type)
|
if (site.type == type)
|
||||||
return element;
|
return site;
|
||||||
|
|
||||||
if ([element.algorithm classOfType:type] == element.typeClass) {
|
if ([site.algorithm classOfType:type] == site.typeClass) {
|
||||||
element.type = type;
|
site.type = type;
|
||||||
[context saveToStore];
|
[context saveToStore];
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
// Type requires a different class of element. Recreate the element.
|
// Type requires a different class of site. Recreate the site.
|
||||||
NSString *typeEntityName = [element.algorithm classNameOfType:type];
|
NSString *typeEntityName = [site.algorithm classNameOfType:type];
|
||||||
MPElementEntity *newElement = [NSEntityDescription insertNewObjectForEntityForName:typeEntityName inManagedObjectContext:context];
|
MPSiteEntity *newSite = [NSEntityDescription insertNewObjectForEntityForName:typeEntityName inManagedObjectContext:context];
|
||||||
newElement.type = type;
|
newSite.type = type;
|
||||||
newElement.name = element.name;
|
newSite.name = site.name;
|
||||||
newElement.user = element.user;
|
newSite.user = site.user;
|
||||||
newElement.uses = element.uses;
|
newSite.uses = site.uses;
|
||||||
newElement.lastUsed = element.lastUsed;
|
newSite.lastUsed = site.lastUsed;
|
||||||
newElement.version = element.version;
|
newSite.version = site.version;
|
||||||
newElement.loginName = element.loginName;
|
newSite.loginName = site.loginName;
|
||||||
|
|
||||||
NSError *error = nil;
|
NSError *error = nil;
|
||||||
if (![context obtainPermanentIDsForObjects:@[ newElement ] error:&error])
|
if (![context obtainPermanentIDsForObjects:@[ newSite ] error:&error])
|
||||||
err( @"Failed to obtain a permanent object ID after changing object type: %@", error );
|
err( @"Failed to obtain a permanent object ID after changing object type: %@", [error fullDescription] );
|
||||||
|
|
||||||
[context deleteObject:element];
|
[context deleteObject:site];
|
||||||
[context saveToStore];
|
[context saveToStore];
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPElementUpdatedNotification object:element.objectID];
|
[[NSNotificationCenter defaultCenter] postNotificationName:MPSiteUpdatedNotification object:site.objectID];
|
||||||
element = newElement;
|
site = newSite;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPElementUpdatedNotification object:element.objectID];
|
[[NSNotificationCenter defaultCenter] postNotificationName:MPSiteUpdatedNotification object:site.objectID];
|
||||||
return element;
|
return site;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MPImportResult)importSites:(NSString *)importedSitesString
|
- (MPImportResult)importSites:(NSString *)importedSitesString
|
||||||
@ -540,7 +474,7 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
|
|||||||
initWithPattern:@"^#[[:space:]]*([^:]+): (.*)"
|
initWithPattern:@"^#[[:space:]]*([^:]+): (.*)"
|
||||||
options:(NSRegularExpressionOptions)0 error:&error];
|
options:(NSRegularExpressionOptions)0 error:&error];
|
||||||
if (error) {
|
if (error) {
|
||||||
err( @"Error loading the header pattern: %@", error );
|
err( @"Error loading the header pattern: %@", [error fullDescription] );
|
||||||
return MPImportResultInternalError;
|
return MPImportResultInternalError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -554,7 +488,7 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
|
|||||||
options:(NSRegularExpressionOptions)0 error:&error]
|
options:(NSRegularExpressionOptions)0 error:&error]
|
||||||
];
|
];
|
||||||
if (error) {
|
if (error) {
|
||||||
err( @"Error loading the site patterns: %@", error );
|
err( @"Error loading the site patterns: %@", [error fullDescription] );
|
||||||
return MPImportResultInternalError;
|
return MPImportResultInternalError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -569,9 +503,9 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
|
|||||||
NSData *importKeyID = nil;
|
NSData *importKeyID = nil;
|
||||||
BOOL headerStarted = NO, headerEnded = NO, clearText = NO;
|
BOOL headerStarted = NO, headerEnded = NO, clearText = NO;
|
||||||
NSArray *importedSiteLines = [importedSitesString componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
|
NSArray *importedSiteLines = [importedSitesString componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
|
||||||
NSMutableSet *elementsToDelete = [NSMutableSet set];
|
NSMutableSet *sitesToDelete = [NSMutableSet set];
|
||||||
NSMutableArray *importedSiteElements = [NSMutableArray arrayWithCapacity:[importedSiteLines count]];
|
NSMutableArray *importedSiteSites = [NSMutableArray arrayWithCapacity:[importedSiteLines count]];
|
||||||
NSFetchRequest *elementFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )];
|
NSFetchRequest *siteFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
|
||||||
for (NSString *importedSiteLine in importedSiteLines) {
|
for (NSString *importedSiteLine in importedSiteLines) {
|
||||||
if ([importedSiteLine hasPrefix:@"#"]) {
|
if ([importedSiteLine hasPrefix:@"#"]) {
|
||||||
// Comment or header
|
// Comment or header
|
||||||
@ -593,10 +527,10 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
|
|||||||
err( @"Invalid header format in line: %@", importedSiteLine );
|
err( @"Invalid header format in line: %@", importedSiteLine );
|
||||||
return MPImportResultMalformedInput;
|
return MPImportResultMalformedInput;
|
||||||
}
|
}
|
||||||
NSTextCheckingResult *headerElements = [[headerPattern matchesInString:importedSiteLine options:(NSMatchingOptions)0
|
NSTextCheckingResult *headerSites = [[headerPattern matchesInString:importedSiteLine options:(NSMatchingOptions)0
|
||||||
range:NSMakeRange( 0, [importedSiteLine length] )] lastObject];
|
range:NSMakeRange( 0, [importedSiteLine length] )] lastObject];
|
||||||
NSString *headerName = [importedSiteLine substringWithRange:[headerElements rangeAtIndex:1]];
|
NSString *headerName = [importedSiteLine substringWithRange:[headerSites rangeAtIndex:1]];
|
||||||
NSString *headerValue = [importedSiteLine substringWithRange:[headerElements rangeAtIndex:2]];
|
NSString *headerValue = [importedSiteLine substringWithRange:[headerSites rangeAtIndex:2]];
|
||||||
if ([headerName isEqualToString:@"User Name"]) {
|
if ([headerName isEqualToString:@"User Name"]) {
|
||||||
importUserName = headerValue;
|
importUserName = headerValue;
|
||||||
|
|
||||||
@ -604,7 +538,7 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
|
|||||||
userFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", importUserName];
|
userFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", importUserName];
|
||||||
NSArray *users = [context executeFetchRequest:userFetchRequest error:&error];
|
NSArray *users = [context executeFetchRequest:userFetchRequest error:&error];
|
||||||
if (!users) {
|
if (!users) {
|
||||||
err( @"While looking for user: %@, error: %@", importUserName, error );
|
err( @"While looking for user: %@, error: %@", importUserName, [error fullDescription] );
|
||||||
return MPImportResultInternalError;
|
return MPImportResultInternalError;
|
||||||
}
|
}
|
||||||
if ([users count] > 1) {
|
if ([users count] > 1) {
|
||||||
@ -688,27 +622,27 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
|
|||||||
|
|
||||||
// Find existing site.
|
// Find existing site.
|
||||||
if (user) {
|
if (user) {
|
||||||
elementFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND user == %@", siteName, user];
|
siteFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND user == %@", siteName, user];
|
||||||
NSArray *existingSites = [context executeFetchRequest:elementFetchRequest error:&error];
|
NSArray *existingSites = [context executeFetchRequest:siteFetchRequest error:&error];
|
||||||
if (!existingSites) {
|
if (!existingSites) {
|
||||||
err( @"Lookup of existing sites failed for site: %@, user: %@, error: %@", siteName, user.userID, error );
|
err( @"Lookup of existing sites failed for site: %@, user: %@, error: %@", siteName, user.userID, [error fullDescription] );
|
||||||
return MPImportResultInternalError;
|
return MPImportResultInternalError;
|
||||||
}
|
}
|
||||||
if ([existingSites count]) {
|
if ([existingSites count]) {
|
||||||
dbg( @"Existing sites: %@", existingSites );
|
dbg( @"Existing sites: %@", existingSites );
|
||||||
[elementsToDelete addObjectsFromArray:existingSites];
|
[sitesToDelete addObjectsFromArray:existingSites];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[importedSiteElements addObject:@[ lastUsed, uses, type, version, counter, loginName, siteName, exportContent ]];
|
[importedSiteSites addObject:@[ lastUsed, uses, type, version, counter, loginName, siteName, exportContent ]];
|
||||||
dbg( @"Will import site: lastUsed=%@, uses=%@, type=%@, version=%@, counter=%@, loginName=%@, siteName=%@, exportContent=%@",
|
dbg( @"Will import site: lastUsed=%@, uses=%@, type=%@, version=%@, counter=%@, loginName=%@, siteName=%@, exportContent=%@",
|
||||||
lastUsed, uses, type, version, counter, loginName, siteName, exportContent );
|
lastUsed, uses, type, version, counter, loginName, siteName, exportContent );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ask for confirmation to import these sites and the master password of the user.
|
// Ask for confirmation to import these sites and the master password of the user.
|
||||||
inf( @"Importing %lu sites, deleting %lu sites, for user: %@", (unsigned long)[importedSiteElements count],
|
inf( @"Importing %lu sites, deleting %lu sites, for user: %@", (unsigned long)[importedSiteSites count],
|
||||||
(unsigned long)[elementsToDelete count], [MPUserEntity idFor:importUserName] );
|
(unsigned long)[sitesToDelete count], [MPUserEntity idFor:importUserName] );
|
||||||
NSString *userMasterPassword = askUserPassword( user? user.name: importUserName, [importedSiteElements count],
|
NSString *userMasterPassword = askUserPassword( user? user.name: importUserName, [importedSiteSites count],
|
||||||
[elementsToDelete count] );
|
[sitesToDelete count] );
|
||||||
if (!userMasterPassword) {
|
if (!userMasterPassword) {
|
||||||
inf( @"Import cancelled." );
|
inf( @"Import cancelled." );
|
||||||
return MPImportResultCancelled;
|
return MPImportResultCancelled;
|
||||||
@ -724,8 +658,8 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
|
|||||||
|
|
||||||
|
|
||||||
// Delete existing sites.
|
// Delete existing sites.
|
||||||
if (elementsToDelete.count)
|
if (sitesToDelete.count)
|
||||||
[elementsToDelete enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
|
[sitesToDelete enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
|
||||||
inf( @"Deleting site: %@, it will be replaced by an imported site.", [obj name] );
|
inf( @"Deleting site: %@, it will be replaced by an imported site.", [obj name] );
|
||||||
[context deleteObject:obj];
|
[context deleteObject:obj];
|
||||||
}];
|
}];
|
||||||
@ -735,7 +669,8 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
|
|||||||
if (importAvatar != NSNotFound)
|
if (importAvatar != NSNotFound)
|
||||||
user.avatar = importAvatar;
|
user.avatar = importAvatar;
|
||||||
dbg( @"Updating User: %@", [user debugDescription] );
|
dbg( @"Updating User: %@", [user debugDescription] );
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
user = [MPUserEntity insertNewObjectInContext:context];
|
user = [MPUserEntity insertNewObjectInContext:context];
|
||||||
user.name = importUserName;
|
user.name = importUserName;
|
||||||
user.keyID = importKeyID;
|
user.keyID = importKeyID;
|
||||||
@ -745,10 +680,10 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Import new sites.
|
// Import new sites.
|
||||||
for (NSArray *siteElements in importedSiteElements) {
|
for (NSArray *siteElements in importedSiteSites) {
|
||||||
NSDate *lastUsed = [[NSDateFormatter rfc3339DateFormatter] dateFromString:siteElements[0]];
|
NSDate *lastUsed = [[NSDateFormatter rfc3339DateFormatter] dateFromString:siteElements[0]];
|
||||||
NSUInteger uses = (unsigned)[siteElements[1] integerValue];
|
NSUInteger uses = (unsigned)[siteElements[1] integerValue];
|
||||||
MPElementType type = (MPElementType)[siteElements[2] integerValue];
|
MPSiteType type = (MPSiteType)[siteElements[2] integerValue];
|
||||||
NSUInteger version = (unsigned)[siteElements[3] integerValue];
|
NSUInteger version = (unsigned)[siteElements[3] integerValue];
|
||||||
NSUInteger counter = [siteElements[4] length]? (unsigned)[siteElements[4] integerValue]: NSNotFound;
|
NSUInteger counter = [siteElements[4] length]? (unsigned)[siteElements[4] integerValue]: NSNotFound;
|
||||||
NSString *loginName = [siteElements[5] length]? siteElements[5]: nil;
|
NSString *loginName = [siteElements[5] length]? siteElements[5]: nil;
|
||||||
@ -757,24 +692,24 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
|
|||||||
|
|
||||||
// Create new site.
|
// Create new site.
|
||||||
NSString *typeEntityName = [MPAlgorithmForVersion( version ) classNameOfType:type];
|
NSString *typeEntityName = [MPAlgorithmForVersion( version ) classNameOfType:type];
|
||||||
MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:typeEntityName inManagedObjectContext:context];
|
MPSiteEntity *site = [NSEntityDescription insertNewObjectForEntityForName:typeEntityName inManagedObjectContext:context];
|
||||||
element.name = siteName;
|
site.name = siteName;
|
||||||
element.loginName = loginName;
|
site.loginName = loginName;
|
||||||
element.user = user;
|
site.user = user;
|
||||||
element.type = type;
|
site.type = type;
|
||||||
element.uses = uses;
|
site.uses = uses;
|
||||||
element.lastUsed = lastUsed;
|
site.lastUsed = lastUsed;
|
||||||
element.version = version;
|
site.version = version;
|
||||||
if ([exportContent length]) {
|
if ([exportContent length]) {
|
||||||
if (clearText)
|
if (clearText)
|
||||||
[element.algorithm importClearTextContent:exportContent intoElement:element usingKey:userKey];
|
[site.algorithm importClearTextPassword:exportContent intoSite:site usingKey:userKey];
|
||||||
else
|
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)
|
if ([site isKindOfClass:[MPGeneratedSiteEntity class]] && counter != NSNotFound)
|
||||||
((MPElementGeneratedEntity *)element).counter = counter;
|
((MPGeneratedSiteEntity *)site).counter = counter;
|
||||||
|
|
||||||
dbg( @"Created Element: %@", [element debugDescription] );
|
dbg( @"Created Site: %@", [site debugDescription] );
|
||||||
}
|
}
|
||||||
|
|
||||||
if (![context saveToStore])
|
if (![context saveToStore])
|
||||||
@ -820,27 +755,27 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext
|
|||||||
[export appendFormat:@"# used used type name\t name\tpassword\n"];
|
[export appendFormat:@"# used used type name\t name\tpassword\n"];
|
||||||
|
|
||||||
// Sites.
|
// Sites.
|
||||||
for (MPElementEntity *element in activeUser.elements) {
|
for (MPSiteEntity *site in activeUser.sites) {
|
||||||
NSDate *lastUsed = element.lastUsed;
|
NSDate *lastUsed = site.lastUsed;
|
||||||
NSUInteger uses = element.uses;
|
NSUInteger uses = site.uses;
|
||||||
MPElementType type = element.type;
|
MPSiteType type = site.type;
|
||||||
NSUInteger version = element.version;
|
NSUInteger version = site.version;
|
||||||
NSUInteger counter = 0;
|
NSUInteger counter = 0;
|
||||||
NSString *loginName = element.loginName;
|
NSString *loginName = site.loginName;
|
||||||
NSString *siteName = element.name;
|
NSString *siteName = site.name;
|
||||||
NSString *content = nil;
|
NSString *content = nil;
|
||||||
|
|
||||||
// Generated-specific
|
// Generated-specific
|
||||||
if ([element isKindOfClass:[MPElementGeneratedEntity class]])
|
if ([site isKindOfClass:[MPGeneratedSiteEntity class]])
|
||||||
counter = ((MPElementGeneratedEntity *)element).counter;
|
counter = ((MPGeneratedSiteEntity *)site).counter;
|
||||||
|
|
||||||
|
|
||||||
// Determine the content to export.
|
// Determine the content to export.
|
||||||
if (!(type & MPElementFeatureDevicePrivate)) {
|
if (!(type & MPSiteFeatureDevicePrivate)) {
|
||||||
if (revealPasswords)
|
if (revealPasswords)
|
||||||
content = [element.algorithm resolveContentForElement:element usingKey:self.key];
|
content = [site.algorithm resolvePasswordForSite:site usingKey:self.key];
|
||||||
else if (type & MPElementFeatureExportContent)
|
else if (type & MPSiteFeatureExportContent)
|
||||||
content = [element.algorithm exportContentForElement:element usingKey:self.key];
|
content = [site.algorithm exportPasswordForSite:site usingKey:self.key];
|
||||||
}
|
}
|
||||||
|
|
||||||
[export appendFormat:@"%@ %8ld %8s %25s\t%25s\t%@\n",
|
[export appendFormat:@"%@ %8ld %8s %25s\t%25s\t%@\n",
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
//
|
|
||||||
// MPElementEntity.h
|
|
||||||
// MasterPassword-iOS
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2013-01-29.
|
|
||||||
// Copyright (c) 2013 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
#import <CoreData/CoreData.h>
|
|
||||||
#import "MPFixable.h"
|
|
||||||
|
|
||||||
@class MPUserEntity;
|
|
||||||
|
|
||||||
@interface MPElementEntity : NSManagedObject <MPFixable>
|
|
||||||
|
|
||||||
@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
|
|
@ -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
|
|
@ -1,17 +0,0 @@
|
|||||||
//
|
|
||||||
// MPElementGeneratedEntity.h
|
|
||||||
// MasterPassword-iOS
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2013-01-29.
|
|
||||||
// Copyright (c) 2013 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
#import <CoreData/CoreData.h>
|
|
||||||
#import "MPElementEntity.h"
|
|
||||||
|
|
||||||
@interface MPElementGeneratedEntity : MPElementEntity
|
|
||||||
|
|
||||||
@property(nonatomic, retain) NSNumber *counter_;
|
|
||||||
|
|
||||||
@end
|
|
@ -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
|
|
@ -1,17 +0,0 @@
|
|||||||
//
|
|
||||||
// MPElementStoredEntity.h
|
|
||||||
// MasterPassword-iOS
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2013-01-29.
|
|
||||||
// Copyright (c) 2013 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
#import <CoreData/CoreData.h>
|
|
||||||
#import "MPElementEntity.h"
|
|
||||||
|
|
||||||
@interface MPElementStoredEntity : MPElementEntity
|
|
||||||
|
|
||||||
@property(nonatomic, retain) NSData *contentObject;
|
|
||||||
|
|
||||||
@end
|
|
@ -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
|
|
@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// MPElementEntities.h
|
// MPEntities.h
|
||||||
// MasterPassword-iOS
|
// MasterPassword-iOS
|
||||||
//
|
//
|
||||||
// Created by Maarten Billemont on 31/05/12.
|
// Created by Maarten Billemont on 31/05/12.
|
||||||
@ -7,11 +7,12 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import "MPElementEntity.h"
|
#import "MPSiteEntity.h"
|
||||||
#import "MPElementStoredEntity.h"
|
#import "MPStoredSiteEntity.h"
|
||||||
#import "MPElementGeneratedEntity.h"
|
#import "MPGeneratedSiteEntity.h"
|
||||||
#import "MPUserEntity.h"
|
#import "MPUserEntity.h"
|
||||||
#import "MPAlgorithm.h"
|
#import "MPAlgorithm.h"
|
||||||
|
#import "MPFixable.h"
|
||||||
|
|
||||||
#define MPAvatarCount 19
|
#define MPAvatarCount 19
|
||||||
|
|
||||||
@ -21,9 +22,10 @@
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface MPElementEntity(MP)
|
@interface MPSiteEntity(MP)<MPFixable>
|
||||||
|
|
||||||
@property(assign) MPElementType type;
|
@property(assign) BOOL loginGenerated;
|
||||||
|
@property(assign) MPSiteType type;
|
||||||
@property(readonly) NSString *typeName;
|
@property(readonly) NSString *typeName;
|
||||||
@property(readonly) NSString *typeShortName;
|
@property(readonly) NSString *typeShortName;
|
||||||
@property(readonly) NSString *typeClassName;
|
@property(readonly) NSString *typeClassName;
|
||||||
@ -34,13 +36,15 @@
|
|||||||
@property(readonly) id<MPAlgorithm> algorithm;
|
@property(readonly) id<MPAlgorithm> algorithm;
|
||||||
|
|
||||||
- (NSUInteger)use;
|
- (NSUInteger)use;
|
||||||
- (BOOL)migrateExplicitly:(BOOL)explicit;
|
- (BOOL)tryMigrateExplicitly:(BOOL)explicit;
|
||||||
- (NSString *)resolveContentUsingKey:(MPKey *)key;
|
- (NSString *)resolveLoginUsingKey:(MPKey *)key;
|
||||||
- (void)resolveContentUsingKey:(MPKey *)key result:(void (^)(NSString *))result;
|
- (NSString *)resolvePasswordUsingKey:(MPKey *)key;
|
||||||
|
- (void)resolveLoginUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result;
|
||||||
|
- (void)resolvePasswordUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface MPElementGeneratedEntity(MP)
|
@interface MPGeneratedSiteEntity(MP)
|
||||||
|
|
||||||
@property(assign) NSUInteger counter;
|
@property(assign) NSUInteger counter;
|
||||||
|
|
||||||
@ -50,7 +54,7 @@
|
|||||||
|
|
||||||
@property(assign) NSUInteger avatar;
|
@property(assign) NSUInteger avatar;
|
||||||
@property(assign) BOOL saveKey;
|
@property(assign) BOOL saveKey;
|
||||||
@property(assign) MPElementType defaultType;
|
@property(assign) MPSiteType defaultType;
|
||||||
@property(readonly) NSString *userID;
|
@property(readonly) NSString *userID;
|
||||||
|
|
||||||
+ (NSString *)idFor:(NSString *)userName;
|
+ (NSString *)idFor:(NSString *)userName;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// MPElementEntities.m
|
// MPEntities.m
|
||||||
// MasterPassword-iOS
|
// MasterPassword-iOS
|
||||||
//
|
//
|
||||||
// Created by Maarten Billemont on 31/05/12.
|
// Created by Maarten Billemont on 31/05/12.
|
||||||
@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
#import "MPEntities.h"
|
#import "MPEntities.h"
|
||||||
#import "MPAppDelegate_Shared.h"
|
#import "MPAppDelegate_Shared.h"
|
||||||
#import "MPAppDelegate_Store.h"
|
|
||||||
|
|
||||||
@implementation NSManagedObjectContext(MP)
|
@implementation NSManagedObjectContext(MP)
|
||||||
|
|
||||||
@ -34,14 +33,29 @@
|
|||||||
|
|
||||||
@end
|
@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);
|
self.type_ = @(aType);
|
||||||
}
|
}
|
||||||
@ -120,34 +134,85 @@
|
|||||||
self.loginName, self.requiresExplicitMigration );
|
self.loginName, self.requiresExplicitMigration );
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)migrateExplicitly:(BOOL)explicit {
|
- (BOOL)tryMigrateExplicitly:(BOOL)explicit {
|
||||||
|
|
||||||
while (self.version < MPAlgorithmDefaultVersion)
|
while (self.version < MPAlgorithmDefaultVersion) {
|
||||||
if ([MPAlgorithmForVersion( self.version + 1 ) migrateElement:self explicit:explicit])
|
NSUInteger toVersion = self.version + 1;
|
||||||
inf( @"%@ migration to version: %ld succeeded for element: %@",
|
if (![MPAlgorithmForVersion( toVersion ) tryMigrateSite:self explicit:explicit]) {
|
||||||
explicit? @"Explicit": @"Automatic", (long)self.version + 1, self );
|
wrn( @"%@ migration to version: %ld failed for site: %@",
|
||||||
else {
|
explicit? @"Explicit": @"Automatic", (long)toVersion, self );
|
||||||
wrn( @"%@ migration to version: %ld failed for element: %@",
|
|
||||||
explicit? @"Explicit": @"Automatic", (long)self.version + 1, self );
|
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inf( @"%@ migration to version: %ld succeeded for site: %@",
|
||||||
|
explicit? @"Explicit": @"Automatic", (long)toVersion, self );
|
||||||
|
}
|
||||||
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)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
|
@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 {
|
- (NSUInteger)counter {
|
||||||
|
|
||||||
@ -161,7 +226,27 @@
|
|||||||
|
|
||||||
@end
|
@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
|
@end
|
||||||
|
|
||||||
@ -187,12 +272,12 @@
|
|||||||
self.saveKey_ = @(aSaveKey);
|
self.saveKey_ = @(aSaveKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MPElementType)defaultType {
|
- (MPSiteType)defaultType {
|
||||||
|
|
||||||
return (MPElementType)[self.defaultType_ unsignedIntegerValue]?: MPElementTypeGeneratedLong;
|
return (MPSiteType)[self.defaultType_ unsignedIntegerValue]?: MPSiteTypeGeneratedLong;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setDefaultType:(MPElementType)aDefaultType {
|
- (void)setDefaultType:(MPSiteType)aDefaultType {
|
||||||
|
|
||||||
self.defaultType_ = @(aDefaultType);
|
self.defaultType_ = @(aDefaultType);
|
||||||
}
|
}
|
||||||
|
18
MasterPassword/ObjC/MPGeneratedSiteEntity.h
Normal file
18
MasterPassword/ObjC/MPGeneratedSiteEntity.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
//
|
||||||
|
// MPGeneratedSiteEntity.h
|
||||||
|
// MasterPassword-Mac
|
||||||
|
//
|
||||||
|
// Created by Maarten Billemont on 2014-09-21.
|
||||||
|
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <CoreData/CoreData.h>
|
||||||
|
#import "MPSiteEntity.h"
|
||||||
|
|
||||||
|
|
||||||
|
@interface MPGeneratedSiteEntity : MPSiteEntity
|
||||||
|
|
||||||
|
@property (nonatomic, retain) NSNumber * counter_;
|
||||||
|
|
||||||
|
@end
|
16
MasterPassword/ObjC/MPGeneratedSiteEntity.m
Normal file
16
MasterPassword/ObjC/MPGeneratedSiteEntity.m
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//
|
||||||
|
// MPGeneratedSiteEntity.m
|
||||||
|
// MasterPassword-Mac
|
||||||
|
//
|
||||||
|
// Created by Maarten Billemont on 2014-09-21.
|
||||||
|
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MPGeneratedSiteEntity.h"
|
||||||
|
|
||||||
|
|
||||||
|
@implementation MPGeneratedSiteEntity
|
||||||
|
|
||||||
|
@dynamic counter_;
|
||||||
|
|
||||||
|
@end
|
41
MasterPassword/ObjC/MPSiteEntity.h
Normal file
41
MasterPassword/ObjC/MPSiteEntity.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
//
|
||||||
|
// MPSiteEntity.h
|
||||||
|
// MasterPassword-Mac
|
||||||
|
//
|
||||||
|
// Created by Maarten Billemont on 2014-09-21.
|
||||||
|
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <CoreData/CoreData.h>
|
||||||
|
|
||||||
|
@class MPSiteQuestionEntity, MPUserEntity;
|
||||||
|
|
||||||
|
@interface MPSiteEntity : NSManagedObject
|
||||||
|
|
||||||
|
//@property (nonatomic, retain) id content; // Hide here, reveal in MPStoredSiteEntity
|
||||||
|
@property (nonatomic, retain) NSDate * lastUsed;
|
||||||
|
@property (nonatomic, retain) NSNumber * loginGenerated_;
|
||||||
|
@property (nonatomic, retain) NSString * loginName;
|
||||||
|
@property (nonatomic, retain) NSString * name;
|
||||||
|
@property (nonatomic, retain) NSNumber * requiresExplicitMigration_;
|
||||||
|
@property (nonatomic, retain) NSNumber * type_;
|
||||||
|
@property (nonatomic, retain) NSNumber * uses_;
|
||||||
|
@property (nonatomic, retain) NSNumber * version_;
|
||||||
|
@property (nonatomic, retain) NSOrderedSet *questions;
|
||||||
|
@property (nonatomic, retain) MPUserEntity *user;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface MPSiteEntity (CoreDataGeneratedAccessors)
|
||||||
|
|
||||||
|
- (void)insertObject:(MPSiteQuestionEntity *)value inQuestionsAtIndex:(NSUInteger)idx;
|
||||||
|
- (void)removeObjectFromQuestionsAtIndex:(NSUInteger)idx;
|
||||||
|
- (void)insertQuestions:(NSArray *)value atIndexes:(NSIndexSet *)indexes;
|
||||||
|
- (void)removeQuestionsAtIndexes:(NSIndexSet *)indexes;
|
||||||
|
- (void)replaceObjectInQuestionsAtIndex:(NSUInteger)idx withObject:(MPSiteQuestionEntity *)value;
|
||||||
|
- (void)replaceQuestionsAtIndexes:(NSIndexSet *)indexes withQuestions:(NSArray *)values;
|
||||||
|
- (void)addQuestionsObject:(MPSiteQuestionEntity *)value;
|
||||||
|
- (void)removeQuestionsObject:(MPSiteQuestionEntity *)value;
|
||||||
|
- (void)addQuestions:(NSOrderedSet *)values;
|
||||||
|
- (void)removeQuestions:(NSOrderedSet *)values;
|
||||||
|
@end
|
28
MasterPassword/ObjC/MPSiteEntity.m
Normal file
28
MasterPassword/ObjC/MPSiteEntity.m
Normal file
@ -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
|
18
MasterPassword/ObjC/MPSiteQuestionEntity.h
Normal file
18
MasterPassword/ObjC/MPSiteQuestionEntity.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
//
|
||||||
|
// MPSiteQuestionEntity.h
|
||||||
|
// MasterPassword-Mac
|
||||||
|
//
|
||||||
|
// Created by Maarten Billemont on 2014-09-21.
|
||||||
|
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <CoreData/CoreData.h>
|
||||||
|
|
||||||
|
@class MPSiteEntity;
|
||||||
|
|
||||||
|
@interface MPSiteQuestionEntity : NSManagedObject
|
||||||
|
|
||||||
|
@property (nonatomic, retain) NSString * keyword;
|
||||||
|
|
||||||
|
@end
|
17
MasterPassword/ObjC/MPSiteQuestionEntity.m
Normal file
17
MasterPassword/ObjC/MPSiteQuestionEntity.m
Normal file
@ -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
|
18
MasterPassword/ObjC/MPStoredSiteEntity.h
Normal file
18
MasterPassword/ObjC/MPStoredSiteEntity.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
//
|
||||||
|
// MPStoredSiteEntity.h
|
||||||
|
// MasterPassword-Mac
|
||||||
|
//
|
||||||
|
// Created by Maarten Billemont on 2014-09-21.
|
||||||
|
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <CoreData/CoreData.h>
|
||||||
|
#import "MPSiteEntity.h"
|
||||||
|
|
||||||
|
|
||||||
|
@interface MPStoredSiteEntity : MPSiteEntity
|
||||||
|
|
||||||
|
@property (nonatomic, retain) id contentObject;
|
||||||
|
|
||||||
|
@end
|
16
MasterPassword/ObjC/MPStoredSiteEntity.m
Normal file
16
MasterPassword/ObjC/MPStoredSiteEntity.m
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//
|
||||||
|
// MPStoredSiteEntity.m
|
||||||
|
// MasterPassword-Mac
|
||||||
|
//
|
||||||
|
// Created by Maarten Billemont on 2014-09-21.
|
||||||
|
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MPStoredSiteEntity.h"
|
||||||
|
|
||||||
|
|
||||||
|
@implementation MPStoredSiteEntity
|
||||||
|
|
||||||
|
@dynamic contentObject;
|
||||||
|
|
||||||
|
@end
|
@ -8,36 +8,41 @@
|
|||||||
|
|
||||||
#import "MPKey.h"
|
#import "MPKey.h"
|
||||||
|
|
||||||
typedef NS_ENUM(NSUInteger, MPElementContentType) {
|
typedef NS_ENUM( NSUInteger, MPSiteTypeClass ) {
|
||||||
MPElementContentTypePassword,
|
|
||||||
MPElementContentTypeNote,
|
|
||||||
MPElementContentTypePicture,
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef NS_ENUM(NSUInteger, MPElementTypeClass) {
|
|
||||||
/** Generate the password. */
|
/** Generate the password. */
|
||||||
MPElementTypeClassGenerated = 1 << 4,
|
MPSiteTypeClassGenerated = 1 << 4,
|
||||||
/** Store the password. */
|
/** Store the password. */
|
||||||
MPElementTypeClassStored = 1 << 5,
|
MPSiteTypeClassStored = 1 << 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef NS_ENUM(NSUInteger, 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. */
|
/** Export the key-protected content data. */
|
||||||
MPElementFeatureExportContent = 1 << 10,
|
MPSiteFeatureExportContent = 1 << 10,
|
||||||
/** Never export content. */
|
/** Never export content. */
|
||||||
MPElementFeatureDevicePrivate = 1 << 11,
|
MPSiteFeatureDevicePrivate = 1 << 11,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef NS_ENUM(NSUInteger, MPElementType) {
|
typedef NS_ENUM(NSUInteger, MPSiteType) {
|
||||||
MPElementTypeGeneratedMaximum = 0x0 | MPElementTypeClassGenerated | 0x0,
|
MPSiteTypeGeneratedMaximum = 0x0 | MPSiteTypeClassGenerated | 0x0,
|
||||||
MPElementTypeGeneratedLong = 0x1 | MPElementTypeClassGenerated | 0x0,
|
MPSiteTypeGeneratedLong = 0x1 | MPSiteTypeClassGenerated | 0x0,
|
||||||
MPElementTypeGeneratedMedium = 0x2 | MPElementTypeClassGenerated | 0x0,
|
MPSiteTypeGeneratedMedium = 0x2 | MPSiteTypeClassGenerated | 0x0,
|
||||||
MPElementTypeGeneratedBasic = 0x4 | MPElementTypeClassGenerated | 0x0,
|
MPSiteTypeGeneratedBasic = 0x4 | MPSiteTypeClassGenerated | 0x0,
|
||||||
MPElementTypeGeneratedShort = 0x3 | MPElementTypeClassGenerated | 0x0,
|
MPSiteTypeGeneratedShort = 0x3 | MPSiteTypeClassGenerated | 0x0,
|
||||||
MPElementTypeGeneratedPIN = 0x5 | MPElementTypeClassGenerated | 0x0,
|
MPSiteTypeGeneratedPIN = 0x5 | MPSiteTypeClassGenerated | 0x0,
|
||||||
|
MPSiteTypeGeneratedName = 0xE | MPSiteTypeClassGenerated | 0x0,
|
||||||
|
MPSiteTypeGeneratedPhrase = 0xF | MPSiteTypeClassGenerated | 0x0,
|
||||||
|
|
||||||
MPElementTypeStoredPersonal = 0x0 | MPElementTypeClassStored | MPElementFeatureExportContent,
|
MPSiteTypeStoredPersonal = 0x0 | MPSiteTypeClassStored | MPSiteFeatureExportContent,
|
||||||
MPElementTypeStoredDevicePrivate = 0x1 | MPElementTypeClassStored | MPElementFeatureDevicePrivate,
|
MPSiteTypeStoredDevicePrivate = 0x1 | MPSiteTypeClassStored | MPSiteFeatureDevicePrivate,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MPErrorDomain @"MPErrorDomain"
|
#define MPErrorDomain @"MPErrorDomain"
|
||||||
@ -50,7 +55,7 @@ typedef NS_ENUM(NSUInteger, MPElementType) {
|
|||||||
#define MPCheckpointEditPassword @"MPCheckpointEditPassword"
|
#define MPCheckpointEditPassword @"MPCheckpointEditPassword"
|
||||||
#define MPCheckpointEditLoginName @"MPCheckpointEditLoginName"
|
#define MPCheckpointEditLoginName @"MPCheckpointEditLoginName"
|
||||||
#define MPCheckpointUseType @"MPCheckpointUseType"
|
#define MPCheckpointUseType @"MPCheckpointUseType"
|
||||||
#define MPCheckpointDeleteElement @"MPCheckpointDeleteElement"
|
#define MPCheckpointDeleteSite @"MPCheckpointDeleteSite"
|
||||||
#define MPCheckpointShowGuide @"MPCheckpointShowGuide"
|
#define MPCheckpointShowGuide @"MPCheckpointShowGuide"
|
||||||
#define MPCheckpointShowSetup @"MPCheckpointShowSetup"
|
#define MPCheckpointShowSetup @"MPCheckpointShowSetup"
|
||||||
#define MPCheckpointChangeMP @"MPCheckpointChangeMP"
|
#define MPCheckpointChangeMP @"MPCheckpointChangeMP"
|
||||||
@ -74,7 +79,7 @@ typedef NS_ENUM(NSUInteger, MPElementType) {
|
|||||||
#define MPSignedInNotification @"MPSignedInNotification"
|
#define MPSignedInNotification @"MPSignedInNotification"
|
||||||
#define MPSignedOutNotification @"MPSignedOutNotification"
|
#define MPSignedOutNotification @"MPSignedOutNotification"
|
||||||
#define MPKeyForgottenNotification @"MPKeyForgottenNotification"
|
#define MPKeyForgottenNotification @"MPKeyForgottenNotification"
|
||||||
#define MPElementUpdatedNotification @"MPElementUpdatedNotification"
|
#define MPSiteUpdatedNotification @"MPSiteUpdatedNotification"
|
||||||
#define MPCheckConfigNotification @"MPCheckConfigNotification"
|
#define MPCheckConfigNotification @"MPCheckConfigNotification"
|
||||||
#define MPSitesImportedNotification @"MPSitesImportedNotification"
|
#define MPSitesImportedNotification @"MPSitesImportedNotification"
|
||||||
#define MPFoundInconsistenciesNotification @"MPFoundInconsistenciesNotification"
|
#define MPFoundInconsistenciesNotification @"MPFoundInconsistenciesNotification"
|
||||||
|
@ -1,32 +1,32 @@
|
|||||||
//
|
//
|
||||||
// MPUserEntity.h
|
// MPUserEntity.h
|
||||||
// MasterPassword-iOS
|
// MasterPassword-Mac
|
||||||
//
|
//
|
||||||
// Created by Maarten Billemont on 2013-01-29.
|
// Created by Maarten Billemont on 2014-09-21.
|
||||||
// Copyright (c) 2013 Lyndir. All rights reserved.
|
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import <CoreData/CoreData.h>
|
#import <CoreData/CoreData.h>
|
||||||
|
|
||||||
@class MPElementEntity;
|
@class MPSiteEntity;
|
||||||
|
|
||||||
@interface MPUserEntity : NSManagedObject
|
@interface MPUserEntity : NSManagedObject
|
||||||
|
|
||||||
@property(nonatomic, retain) NSNumber *avatar_;
|
@property (nonatomic, retain) NSNumber * avatar_;
|
||||||
@property(nonatomic, retain) NSNumber *defaultType_;
|
@property (nonatomic, retain) NSNumber * defaultType_;
|
||||||
@property(nonatomic, retain) NSData *keyID;
|
@property (nonatomic, retain) NSData * keyID;
|
||||||
@property(nonatomic, retain) NSDate *lastUsed;
|
@property (nonatomic, retain) NSDate * lastUsed;
|
||||||
@property(nonatomic, retain) NSString *name;
|
@property (nonatomic, retain) NSString * name;
|
||||||
@property(nonatomic, retain) NSNumber *saveKey_;
|
@property (nonatomic, retain) NSNumber * saveKey_;
|
||||||
@property(nonatomic, retain) NSSet *elements;
|
@property (nonatomic, retain) NSSet *sites;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface MPUserEntity(CoreDataGeneratedAccessors)
|
@interface MPUserEntity (CoreDataGeneratedAccessors)
|
||||||
|
|
||||||
- (void)addElementsObject:(MPElementEntity *)value;
|
- (void)addSitesObject:(MPSiteEntity *)value;
|
||||||
- (void)removeElementsObject:(MPElementEntity *)value;
|
- (void)removeSitesObject:(MPSiteEntity *)value;
|
||||||
- (void)addElements:(NSSet *)values;
|
- (void)addSites:(NSSet *)values;
|
||||||
- (void)removeElements:(NSSet *)values;
|
- (void)removeSites:(NSSet *)values;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
//
|
//
|
||||||
// MPUserEntity.m
|
// MPUserEntity.m
|
||||||
// MasterPassword-iOS
|
// MasterPassword-Mac
|
||||||
//
|
//
|
||||||
// Created by Maarten Billemont on 2013-01-29.
|
// Created by Maarten Billemont on 2014-09-21.
|
||||||
// Copyright (c) 2013 Lyndir. All rights reserved.
|
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#import "MPUserEntity.h"
|
#import "MPUserEntity.h"
|
||||||
|
#import "MPSiteEntity.h"
|
||||||
|
|
||||||
|
|
||||||
@implementation MPUserEntity
|
@implementation MPUserEntity
|
||||||
|
|
||||||
@ -16,6 +18,6 @@
|
|||||||
@dynamic lastUsed;
|
@dynamic lastUsed;
|
||||||
@dynamic name;
|
@dynamic name;
|
||||||
@dynamic saveKey_;
|
@dynamic saveKey_;
|
||||||
@dynamic elements;
|
@dynamic sites;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -229,7 +229,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
|||||||
NSData *importedSitesData = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url]
|
NSData *importedSitesData = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url]
|
||||||
returningResponse:&response error:&error];
|
returningResponse:&response error:&error];
|
||||||
if (error)
|
if (error)
|
||||||
err( @"While reading imported sites from %@: %@", url, error );
|
err( @"While reading imported sites from %@: %@", url, [error fullDescription] );
|
||||||
if (!importedSitesData)
|
if (!importedSitesData)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -346,7 +346,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
|||||||
[moc saveToStore];
|
[moc saveToStore];
|
||||||
NSError *error = nil;
|
NSError *error = nil;
|
||||||
if (![moc obtainPermanentIDsForObjects:@[ newUser ] error:&error])
|
if (![moc obtainPermanentIDsForObjects:@[ newUser ] error:&error])
|
||||||
err( @"Failed to obtain permanent object ID for new user: %@", error );
|
err( @"Failed to obtain permanent object ID for new user: %@", [error fullDescription] );
|
||||||
|
|
||||||
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
|
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
|
||||||
[self updateUsers];
|
[self updateUsers];
|
||||||
@ -510,7 +510,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
|||||||
fetchRequest.sortDescriptors = @[ [NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO] ];
|
fetchRequest.sortDescriptors = @[ [NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO] ];
|
||||||
NSArray *users = [mainContext executeFetchRequest:fetchRequest error:&error];
|
NSArray *users = [mainContext executeFetchRequest:fetchRequest error:&error];
|
||||||
if (!users)
|
if (!users)
|
||||||
err( @"Failed to load users: %@", error );
|
err( @"Failed to load users: %@", [error fullDescription] );
|
||||||
|
|
||||||
if (![users count]) {
|
if (![users count]) {
|
||||||
NSMenuItem *noUsersItem = [self.usersItem.submenu addItemWithTitle:@"No users" action:NULL keyEquivalent:@""];
|
NSMenuItem *noUsersItem = [self.usersItem.submenu addItemWithTitle:@"No users" action:NULL keyEquivalent:@""];
|
||||||
|
@ -17,26 +17,26 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#import "MPElementModel.h"
|
#import "MPSiteModel.h"
|
||||||
#import "MPElementsTableView.h"
|
#import "MPSitesTableView.h"
|
||||||
|
|
||||||
@class MPMacAppDelegate;
|
@class MPMacAppDelegate;
|
||||||
|
|
||||||
@interface MPPasswordWindowController : NSWindowController<NSTextViewDelegate, NSTextFieldDelegate, NSTableViewDataSource, NSTableViewDelegate>
|
@interface MPPasswordWindowController : NSWindowController<NSTextViewDelegate, NSTextFieldDelegate, NSTableViewDataSource, NSTableViewDelegate>
|
||||||
|
|
||||||
@property(nonatomic) NSMutableArray *elements;
|
@property(nonatomic) NSMutableArray *sites;
|
||||||
@property(nonatomic) NSString *masterPassword;
|
@property(nonatomic) NSString *masterPassword;
|
||||||
@property(nonatomic) BOOL alternatePressed;
|
@property(nonatomic) BOOL alternatePressed;
|
||||||
@property(nonatomic) BOOL locked;
|
@property(nonatomic) BOOL locked;
|
||||||
@property(nonatomic) BOOL newUser;
|
@property(nonatomic) BOOL newUser;
|
||||||
|
|
||||||
@property(nonatomic, weak) IBOutlet NSArrayController *elementsController;
|
@property(nonatomic, weak) IBOutlet NSArrayController *sitesController;
|
||||||
@property(nonatomic, weak) IBOutlet NSImageView *blurView;
|
@property(nonatomic, weak) IBOutlet NSImageView *blurView;
|
||||||
@property(nonatomic, weak) IBOutlet NSTextField *inputLabel;
|
@property(nonatomic, weak) IBOutlet NSTextField *inputLabel;
|
||||||
@property(nonatomic, weak) IBOutlet NSTextField *securePasswordField;
|
@property(nonatomic, weak) IBOutlet NSTextField *securePasswordField;
|
||||||
@property(nonatomic, weak) IBOutlet NSTextField *revealPasswordField;
|
@property(nonatomic, weak) IBOutlet NSTextField *revealPasswordField;
|
||||||
@property(nonatomic, weak) IBOutlet NSSearchField *siteField;
|
@property(nonatomic, weak) IBOutlet NSSearchField *siteField;
|
||||||
@property(nonatomic, weak) IBOutlet MPElementsTableView *siteTable;
|
@property(nonatomic, weak) IBOutlet MPSitesTableView *siteTable;
|
||||||
@property(nonatomic, weak) IBOutlet NSProgressIndicator *progressView;
|
@property(nonatomic, weak) IBOutlet NSProgressIndicator *progressView;
|
||||||
|
|
||||||
@property(nonatomic, strong) IBOutlet NSBox *passwordTypesBox;
|
@property(nonatomic, strong) IBOutlet NSBox *passwordTypesBox;
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
#import "MPPasswordWindowController.h"
|
#import "MPPasswordWindowController.h"
|
||||||
#import "MPMacAppDelegate.h"
|
#import "MPMacAppDelegate.h"
|
||||||
#import "MPAppDelegate_Store.h"
|
#import "MPAppDelegate_Store.h"
|
||||||
#import "MPElementModel.h"
|
#import "MPSiteModel.h"
|
||||||
#import "MPAppDelegate_Key.h"
|
#import "MPAppDelegate_Key.h"
|
||||||
#import "PearlProfiler.h"
|
#import "PearlProfiler.h"
|
||||||
|
|
||||||
@ -77,7 +77,7 @@
|
|||||||
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||||
[self updateUser];
|
[self updateUser];
|
||||||
}];
|
}];
|
||||||
[self observeKeyPath:@"elementsController.selection"
|
[self observeKeyPath:@"sitesController.selection"
|
||||||
withBlock:^(id from, id to, NSKeyValueChange cause, id _self) {
|
withBlock:^(id from, id to, NSKeyValueChange cause, id _self) {
|
||||||
[_self updateSelection];
|
[_self updateSelection];
|
||||||
}];
|
}];
|
||||||
@ -100,7 +100,7 @@
|
|||||||
BOOL alternatePressed = (theEvent.modifierFlags & NSAlternateKeyMask) != 0;
|
BOOL alternatePressed = (theEvent.modifierFlags & NSAlternateKeyMask) != 0;
|
||||||
if (alternatePressed != self.alternatePressed) {
|
if (alternatePressed != self.alternatePressed) {
|
||||||
self.alternatePressed = alternatePressed;
|
self.alternatePressed = alternatePressed;
|
||||||
[self.selectedElement updateContent];
|
[self.selectedSite updateContent];
|
||||||
|
|
||||||
if (self.locked) {
|
if (self.locked) {
|
||||||
NSTextField *passwordField = self.securePasswordField;
|
NSTextField *passwordField = self.securePasswordField;
|
||||||
@ -169,9 +169,9 @@
|
|||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)doSearchElements:(id)sender {
|
- (IBAction)doSearchSites:(id)sender {
|
||||||
|
|
||||||
[self updateElements];
|
[self updateSites];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - NSTextViewDelegate
|
#pragma mark - NSTextViewDelegate
|
||||||
@ -186,7 +186,7 @@
|
|||||||
|
|
||||||
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
|
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
|
||||||
|
|
||||||
return (NSInteger)[self.elements count];
|
return (NSInteger)[self.sites count];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - NSTableViewDelegate
|
#pragma mark - NSTableViewDelegate
|
||||||
@ -229,9 +229,10 @@
|
|||||||
switch (returnCode) {
|
switch (returnCode) {
|
||||||
case NSAlertFirstButtonReturn: {
|
case NSAlertFirstButtonReturn: {
|
||||||
// "Create" button.
|
// "Create" button.
|
||||||
[[MPMacAppDelegate get] addElementNamed:[self.siteField stringValue] completion:^(MPElementEntity *element, NSManagedObjectContext *context) {
|
[[MPMacAppDelegate get] addSiteNamed:[self.siteField stringValue] completion:
|
||||||
if (element)
|
^(MPSiteEntity *site, NSManagedObjectContext *context) {
|
||||||
PearlMainQueue( ^{ [self updateElements]; } );
|
if (site)
|
||||||
|
PearlMainQueue( ^{ [self updateSites]; } );
|
||||||
}];
|
}];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -243,11 +244,11 @@
|
|||||||
switch (returnCode) {
|
switch (returnCode) {
|
||||||
case NSAlertFirstButtonReturn: {
|
case NSAlertFirstButtonReturn: {
|
||||||
// "Save" button.
|
// "Save" button.
|
||||||
MPElementType type = (MPElementType)[self.passwordTypesMatrix.selectedCell tag];
|
MPSiteType type = (MPSiteType)[self.passwordTypesMatrix.selectedCell tag];
|
||||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
MPElementEntity *entity = [[MPMacAppDelegate get] changeElement:[self.selectedElement entityInContext:context]
|
MPSiteEntity *entity = [[MPMacAppDelegate get] changeSite:[self.selectedSite entityInContext:context]
|
||||||
saveInContext:context toType:type];
|
saveInContext:context toType:type];
|
||||||
if ([entity isKindOfClass:[MPElementStoredEntity class]] && ![(MPElementStoredEntity *)entity contentObject].length)
|
if ([entity isKindOfClass:[MPStoredSiteEntity class]] && ![(MPStoredSiteEntity *)entity contentObject].length)
|
||||||
PearlMainQueue( ^{
|
PearlMainQueue( ^{
|
||||||
[self changePassword:nil];
|
[self changePassword:nil];
|
||||||
} );
|
} );
|
||||||
@ -264,7 +265,7 @@
|
|||||||
// "Save" button.
|
// "Save" button.
|
||||||
NSString *loginName = [(NSSecureTextField *)alert.accessoryView stringValue];
|
NSString *loginName = [(NSSecureTextField *)alert.accessoryView stringValue];
|
||||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
MPElementEntity *entity = [self.selectedElement entityInContext:context];
|
MPSiteEntity *entity = [self.selectedSite entityInContext:context];
|
||||||
entity.loginName = loginName;
|
entity.loginName = loginName;
|
||||||
[context saveToStore];
|
[context saveToStore];
|
||||||
}];
|
}];
|
||||||
@ -280,8 +281,8 @@
|
|||||||
// "Save" button.
|
// "Save" button.
|
||||||
NSString *password = [(NSSecureTextField *)alert.accessoryView stringValue];
|
NSString *password = [(NSSecureTextField *)alert.accessoryView stringValue];
|
||||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
MPElementEntity *entity = [self.selectedElement entityInContext:context];
|
MPSiteEntity *entity = [self.selectedSite entityInContext:context];
|
||||||
[entity.algorithm saveContent:password toElement:entity usingKey:[MPMacAppDelegate get].key];
|
[entity.algorithm savePassword:password toSite:entity usingKey:[MPMacAppDelegate get].key];
|
||||||
[context saveToStore];
|
[context saveToStore];
|
||||||
}];
|
}];
|
||||||
break;
|
break;
|
||||||
@ -295,7 +296,7 @@
|
|||||||
case NSAlertFirstButtonReturn: {
|
case NSAlertFirstButtonReturn: {
|
||||||
// "Delete" button.
|
// "Delete" button.
|
||||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
[context deleteObject:[self.selectedElement entityInContext:context]];
|
[context deleteObject:[self.selectedSite entityInContext:context]];
|
||||||
[context saveToStore];
|
[context saveToStore];
|
||||||
}];
|
}];
|
||||||
break;
|
break;
|
||||||
@ -313,19 +314,19 @@
|
|||||||
return [self.siteField.stringValue stringByReplacingCharactersInRange:self.siteField.currentEditor.selectedRange withString:@""]?: @"";
|
return [self.siteField.stringValue stringByReplacingCharactersInRange:self.siteField.currentEditor.selectedRange withString:@""]?: @"";
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)insertObject:(MPElementModel *)model inElementsAtIndex:(NSUInteger)index {
|
- (void)insertObject:(MPSiteModel *)model inSitesAtIndex:(NSUInteger)index {
|
||||||
|
|
||||||
[self.elements insertObject:model atIndex:index];
|
[self.sites insertObject:model atIndex:index];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)removeObjectFromElementsAtIndex:(NSUInteger)index {
|
- (void)removeObjectFromSitesAtIndex:(NSUInteger)index {
|
||||||
|
|
||||||
[self.elements removeObjectAtIndex:index];
|
[self.sites removeObjectAtIndex:index];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MPElementModel *)selectedElement {
|
- (MPSiteModel *)selectedSite {
|
||||||
|
|
||||||
return [self.elementsController.selectedObjects firstObject];
|
return [self.sitesController.selectedObjects firstObject];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Actions
|
#pragma mark - Actions
|
||||||
@ -336,13 +337,13 @@
|
|||||||
[[MPMacAppDelegate get] showPopup:sender];
|
[[MPMacAppDelegate get] showPopup:sender];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)deleteElement:(id)sender {
|
- (IBAction)deleteSite:(id)sender {
|
||||||
|
|
||||||
NSAlert *alert = [NSAlert new];
|
NSAlert *alert = [NSAlert new];
|
||||||
[alert addButtonWithTitle:@"Delete"];
|
[alert addButtonWithTitle:@"Delete"];
|
||||||
[alert addButtonWithTitle:@"Cancel"];
|
[alert addButtonWithTitle:@"Cancel"];
|
||||||
[alert setMessageText:@"Delete Site?"];
|
[alert setMessageText:@"Delete Site?"];
|
||||||
[alert setInformativeText:strf( @"Do you want to delete the site named:\n\n%@", self.selectedElement.siteName )];
|
[alert setInformativeText:strf( @"Do you want to delete the site named:\n\n%@", self.selectedSite.siteName )];
|
||||||
[alert beginSheetModalForWindow:self.window modalDelegate:self
|
[alert beginSheetModalForWindow:self.window modalDelegate:self
|
||||||
didEndSelector:@selector( alertDidEnd:returnCode:contextInfo: ) contextInfo:MPAlertDeleteSite];
|
didEndSelector:@selector( alertDidEnd:returnCode:contextInfo: ) contextInfo:MPAlertDeleteSite];
|
||||||
}
|
}
|
||||||
@ -353,9 +354,9 @@
|
|||||||
[alert addButtonWithTitle:@"Save"];
|
[alert addButtonWithTitle:@"Save"];
|
||||||
[alert addButtonWithTitle:@"Cancel"];
|
[alert addButtonWithTitle:@"Cancel"];
|
||||||
[alert setMessageText:@"Change Login Name"];
|
[alert setMessageText:@"Change Login Name"];
|
||||||
[alert setInformativeText:strf( @"Enter the login name for: %@", self.selectedElement.siteName )];
|
[alert setInformativeText:strf( @"Enter the login name for: %@", self.selectedSite.siteName )];
|
||||||
NSTextField *loginField = [[NSTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )];
|
NSTextField *loginField = [[NSTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )];
|
||||||
loginField.stringValue = self.selectedElement.loginName?: @"";
|
loginField.stringValue = self.selectedSite.loginName?: @"";
|
||||||
[loginField selectText:self];
|
[loginField selectText:self];
|
||||||
[alert setAccessoryView:loginField];
|
[alert setAccessoryView:loginField];
|
||||||
[alert layout];
|
[alert layout];
|
||||||
@ -380,14 +381,14 @@
|
|||||||
|
|
||||||
- (IBAction)changePassword:(id)sender {
|
- (IBAction)changePassword:(id)sender {
|
||||||
|
|
||||||
if (!self.selectedElement.stored)
|
if (!self.selectedSite.stored)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
NSAlert *alert = [NSAlert new];
|
NSAlert *alert = [NSAlert new];
|
||||||
[alert addButtonWithTitle:@"Save"];
|
[alert addButtonWithTitle:@"Save"];
|
||||||
[alert addButtonWithTitle:@"Cancel"];
|
[alert addButtonWithTitle:@"Cancel"];
|
||||||
[alert setMessageText:@"Change Password"];
|
[alert setMessageText:@"Change Password"];
|
||||||
[alert setInformativeText:strf( @"Enter the new password for: %@", self.selectedElement.siteName )];
|
[alert setInformativeText:strf( @"Enter the new password for: %@", self.selectedSite.siteName )];
|
||||||
[alert setAccessoryView:[[NSSecureTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )]];
|
[alert setAccessoryView:[[NSSecureTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )]];
|
||||||
[alert layout];
|
[alert layout];
|
||||||
[alert beginSheetModalForWindow:self.window modalDelegate:self
|
[alert beginSheetModalForWindow:self.window modalDelegate:self
|
||||||
@ -396,19 +397,19 @@
|
|||||||
|
|
||||||
- (IBAction)changeType:(id)sender {
|
- (IBAction)changeType:(id)sender {
|
||||||
|
|
||||||
MPElementModel *element = self.selectedElement;
|
MPSiteModel *site = self.selectedSite;
|
||||||
NSArray *types = [element.algorithm allTypesStartingWith:MPElementTypeGeneratedPIN];
|
NSArray *types = [site.algorithm allTypesStartingWith:MPSiteTypeGeneratedPIN];
|
||||||
[self.passwordTypesMatrix renewRows:(NSInteger)[types count] columns:1];
|
[self.passwordTypesMatrix renewRows:(NSInteger)[types count] columns:1];
|
||||||
for (NSUInteger t = 0; t < [types count]; ++t) {
|
for (NSUInteger t = 0; t < [types count]; ++t) {
|
||||||
MPElementType type = [types[t] unsignedIntegerValue];
|
MPSiteType type = [types[t] unsignedIntegerValue];
|
||||||
NSString *title = [element.algorithm nameOfType:type];
|
NSString *title = [site.algorithm nameOfType:type];
|
||||||
if (type & MPElementTypeClassGenerated)
|
if (type & MPSiteTypeClassGenerated)
|
||||||
title = [element.algorithm generateContentNamed:element.siteName ofType:type
|
title = [site.algorithm generatePasswordForSiteNamed:site.siteName ofType:type
|
||||||
withCounter:element.counter usingKey:[MPMacAppDelegate get].key];
|
withCounter:site.counter usingKey:[MPMacAppDelegate get].key];
|
||||||
|
|
||||||
NSButtonCell *cell = [self.passwordTypesMatrix cellAtRow:(NSInteger)t column:0];
|
NSButtonCell *cell = [self.passwordTypesMatrix cellAtRow:(NSInteger)t column:0];
|
||||||
cell.tag = type;
|
cell.tag = type;
|
||||||
cell.state = type == element.type? NSOnState: NSOffState;
|
cell.state = type == site.type? NSOnState: NSOffState;
|
||||||
cell.title = title;
|
cell.title = title;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -416,7 +417,7 @@
|
|||||||
[alert addButtonWithTitle:@"Save"];
|
[alert addButtonWithTitle:@"Save"];
|
||||||
[alert addButtonWithTitle:@"Cancel"];
|
[alert addButtonWithTitle:@"Cancel"];
|
||||||
[alert setMessageText:@"Change Password Type"];
|
[alert setMessageText:@"Change Password Type"];
|
||||||
[alert setInformativeText:strf( @"Choose a new password type for: %@", element.siteName )];
|
[alert setInformativeText:strf( @"Choose a new password type for: %@", site.siteName )];
|
||||||
[alert setAccessoryView:self.passwordTypesBox];
|
[alert setAccessoryView:self.passwordTypesBox];
|
||||||
[alert layout];
|
[alert layout];
|
||||||
[alert beginSheetModalForWindow:self.window modalDelegate:self
|
[alert beginSheetModalForWindow:self.window modalDelegate:self
|
||||||
@ -428,11 +429,11 @@
|
|||||||
- (BOOL)handleCommand:(SEL)commandSelector {
|
- (BOOL)handleCommand:(SEL)commandSelector {
|
||||||
|
|
||||||
if (commandSelector == @selector( moveUp: )) {
|
if (commandSelector == @selector( moveUp: )) {
|
||||||
[self.elementsController selectPrevious:self];
|
[self.sitesController selectPrevious:self];
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
if (commandSelector == @selector( moveDown: )) {
|
if (commandSelector == @selector( moveDown: )) {
|
||||||
[self.elementsController selectNext:self];
|
[self.sitesController selectNext:self];
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
if (commandSelector == @selector( insertNewline: )) {
|
if (commandSelector == @selector( insertNewline: )) {
|
||||||
@ -449,19 +450,19 @@
|
|||||||
|
|
||||||
- (void)useSite {
|
- (void)useSite {
|
||||||
|
|
||||||
MPElementModel *selectedElement = [self selectedElement];
|
MPSiteModel *selectedSite = [self selectedSite];
|
||||||
if (selectedElement) {
|
if (selectedSite) {
|
||||||
// Performing action while content is available. Copy it.
|
// Performing action while content is available. Copy it.
|
||||||
[self copyContent:selectedElement.content];
|
[self copyContent:selectedSite.content];
|
||||||
|
|
||||||
[self fadeOut];
|
[self fadeOut];
|
||||||
|
|
||||||
NSUserNotification *notification = [NSUserNotification new];
|
NSUserNotification *notification = [NSUserNotification new];
|
||||||
notification.title = @"Password Copied";
|
notification.title = @"Password Copied";
|
||||||
if (selectedElement.loginName.length)
|
if (selectedSite.loginName.length)
|
||||||
notification.subtitle = strf( @"%@ at %@", selectedElement.loginName, selectedElement.siteName );
|
notification.subtitle = strf( @"%@ at %@", selectedSite.loginName, selectedSite.siteName );
|
||||||
else
|
else
|
||||||
notification.subtitle = selectedElement.siteName;
|
notification.subtitle = selectedSite.siteName;
|
||||||
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
|
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -499,22 +500,22 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[self updateElements];
|
[self updateSites];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)updateElements {
|
- (void)updateSites {
|
||||||
|
|
||||||
if (![MPMacAppDelegate get].key) {
|
if (![MPMacAppDelegate get].key) {
|
||||||
self.elements = nil;
|
self.sites = nil;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PearlProfiler *profiler = [PearlProfiler profilerForTask:@"updateElements"];
|
PearlProfiler *profiler = [PearlProfiler profilerForTask:@"updateSites"];
|
||||||
NSString *query = [self query];
|
NSString *query = [self query];
|
||||||
[profiler finishJob:@"query"];
|
[profiler finishJob:@"query"];
|
||||||
[MPMacAppDelegate managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) {
|
[MPMacAppDelegate managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) {
|
||||||
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )];
|
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
|
||||||
fetchRequest.sortDescriptors = [NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:@"lastUsed" ascending:NO]];
|
fetchRequest.sortDescriptors = [NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:@"lastUsed" ascending:NO]];
|
||||||
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(%@ == '' OR name BEGINSWITH[cd] %@) AND user == %@",
|
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(%@ == '' OR name BEGINSWITH[cd] %@) AND user == %@",
|
||||||
query, query, [[MPMacAppDelegate get] activeUserInContext:context]];
|
query, query, [[MPMacAppDelegate get] activeUserInContext:context]];
|
||||||
@ -523,17 +524,17 @@
|
|||||||
NSError *error = nil;
|
NSError *error = nil;
|
||||||
NSArray *siteResults = [context executeFetchRequest:fetchRequest error:&error];
|
NSArray *siteResults = [context executeFetchRequest:fetchRequest error:&error];
|
||||||
if (!siteResults) {
|
if (!siteResults) {
|
||||||
err( @"While fetching elements for completion: %@", error );
|
err( @"While fetching sites for completion: %@", [error fullDescription] );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
[profiler finishJob:@"do fetch"];
|
[profiler finishJob:@"do fetch"];
|
||||||
|
|
||||||
NSMutableArray *newElements = [NSMutableArray arrayWithCapacity:[siteResults count]];
|
NSMutableArray *newSites = [NSMutableArray arrayWithCapacity:[siteResults count]];
|
||||||
for (MPElementEntity *element in siteResults)
|
for (MPSiteEntity *site in siteResults)
|
||||||
[newElements addObject:[[MPElementModel alloc] initWithEntity:element]];
|
[newSites addObject:[[MPSiteModel alloc] initWithEntity:site]];
|
||||||
[profiler finishJob:@"make models"];
|
[profiler finishJob:@"make models"];
|
||||||
self.elements = newElements;
|
self.sites = newSites;
|
||||||
[profiler finishJob:@"update elements"];
|
[profiler finishJob:@"update sites"];
|
||||||
}];
|
}];
|
||||||
[profiler finishJob:@"done"];
|
[profiler finishJob:@"done"];
|
||||||
}
|
}
|
||||||
@ -545,7 +546,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString *siteName = self.selectedElement.siteName;
|
NSString *siteName = self.selectedSite.siteName;
|
||||||
if (!siteName)
|
if (!siteName)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -558,14 +559,14 @@
|
|||||||
NSMakeRange( siteNameQueryRange.length, siteName.length - siteNameQueryRange.length );
|
NSMakeRange( siteNameQueryRange.length, siteName.length - siteNameQueryRange.length );
|
||||||
}
|
}
|
||||||
|
|
||||||
[self.siteTable scrollRowToVisible:(NSInteger)self.elementsController.selectionIndex];
|
[self.siteTable scrollRowToVisible:(NSInteger)self.sitesController.selectionIndex];
|
||||||
[self updateGradient];
|
[self updateGradient];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)updateGradient {
|
- (void)updateGradient {
|
||||||
|
|
||||||
NSView *siteScrollView = self.siteTable.superview.superview;
|
NSView *siteScrollView = self.siteTable.superview.superview;
|
||||||
NSRect selectedCellFrame = [self.siteTable frameOfCellAtColumn:0 row:((NSInteger)self.elementsController.selectionIndex)];
|
NSRect selectedCellFrame = [self.siteTable frameOfCellAtColumn:0 row:((NSInteger)self.sitesController.selectionIndex)];
|
||||||
CGFloat selectedOffset = [siteScrollView convertPoint:selectedCellFrame.origin fromView:self.siteTable].y;
|
CGFloat selectedOffset = [siteScrollView convertPoint:selectedCellFrame.origin fromView:self.siteTable].y;
|
||||||
CGFloat gradientOpacity = selectedOffset / siteScrollView.bounds.size.height;
|
CGFloat gradientOpacity = selectedOffset / siteScrollView.bounds.size.height;
|
||||||
self.siteGradient.colors = @[
|
self.siteGradient.colors = @[
|
||||||
@ -596,7 +597,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
|
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
|
||||||
[[self.selectedElement entityInContext:moc] use];
|
[[self.selectedSite entityInContext:moc] use];
|
||||||
[moc saveToStore];
|
[moc saveToStore];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<customObject id="-2" userLabel="File's Owner" customClass="MPPasswordWindowController">
|
<customObject id="-2" userLabel="File's Owner" customClass="MPPasswordWindowController">
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="blurView" destination="Bwc-sd-6gm" id="wNV-0x-LJn"/>
|
<outlet property="blurView" destination="Bwc-sd-6gm" id="wNV-0x-LJn"/>
|
||||||
<outlet property="elementsController" destination="mcS-ik-b0n" id="cdF-BL-lfg"/>
|
<outlet property="sitesController" destination="mcS-ik-b0n" id="cdF-BL-lfg"/>
|
||||||
<outlet property="inputLabel" destination="OnR-s6-d4P" id="p6G-Ut-cdu"/>
|
<outlet property="inputLabel" destination="OnR-s6-d4P" id="p6G-Ut-cdu"/>
|
||||||
<outlet property="passwordTypesBox" destination="bZe-7q-i6q" id="Ai3-pt-i6K"/>
|
<outlet property="passwordTypesBox" destination="bZe-7q-i6q" id="Ai3-pt-i6K"/>
|
||||||
<outlet property="passwordTypesMatrix" destination="3fr-Fd-pxx" id="T8g-mS-lxP"/>
|
<outlet property="passwordTypesMatrix" destination="3fr-Fd-pxx" id="T8g-mS-lxP"/>
|
||||||
@ -144,7 +144,7 @@
|
|||||||
<rect key="frame" x="0.0" y="0.0" width="512" height="147"/>
|
<rect key="frame" x="0.0" y="0.0" width="512" height="147"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" selectionHighlightStyle="sourceList" columnReordering="NO" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" rowHeight="33" rowSizeStyle="automatic" viewBased="YES" floatsGroupRows="NO" id="xvJ-5c-vDp" customClass="MPElementsTableView">
|
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" selectionHighlightStyle="sourceList" columnReordering="NO" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" rowHeight="33" rowSizeStyle="automatic" viewBased="YES" floatsGroupRows="NO" id="xvJ-5c-vDp" customClass="MPSitesTableView">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="515" height="147"/>
|
<rect key="frame" x="0.0" y="0.0" width="515" height="147"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<size key="intercellSpacing" width="3" height="2"/>
|
<size key="intercellSpacing" width="3" height="2"/>
|
||||||
@ -262,7 +262,7 @@
|
|||||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.0" colorSpace="calibratedRGB"/>
|
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.0" colorSpace="calibratedRGB"/>
|
||||||
</searchFieldCell>
|
</searchFieldCell>
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="doSearchElements:" target="-2" id="NJO-iR-OXt"/>
|
<action selector="doSearchSites:" target="-2" id="NJO-iR-OXt"/>
|
||||||
<binding destination="-2" name="hidden" keyPath="locked" id="fAX-uK-cgn"/>
|
<binding destination="-2" name="hidden" keyPath="locked" id="fAX-uK-cgn"/>
|
||||||
<outlet property="delegate" destination="-2" id="2WA-uI-asx"/>
|
<outlet property="delegate" destination="-2" id="2WA-uI-asx"/>
|
||||||
</connections>
|
</connections>
|
||||||
@ -371,7 +371,7 @@
|
|||||||
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
|
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
|
||||||
</buttonCell>
|
</buttonCell>
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="deleteElement:" target="-2" id="mVT-O6-KfN"/>
|
<action selector="deleteSite:" target="-2" id="mVT-O6-KfN"/>
|
||||||
<binding destination="mcS-ik-b0n" name="hidden" keyPath="canRemove" id="eqU-d2-XhQ">
|
<binding destination="mcS-ik-b0n" name="hidden" keyPath="canRemove" id="eqU-d2-XhQ">
|
||||||
<dictionary key="options">
|
<dictionary key="options">
|
||||||
<string key="NSValueTransformerName">NSNegateBoolean</string>
|
<string key="NSValueTransformerName">NSNegateBoolean</string>
|
||||||
@ -869,9 +869,9 @@
|
|||||||
</view>
|
</view>
|
||||||
</window>
|
</window>
|
||||||
<userDefaultsController representsSharedInstance="YES" id="yy2-3W-Ocj"/>
|
<userDefaultsController representsSharedInstance="YES" id="yy2-3W-Ocj"/>
|
||||||
<arrayController objectClassName="MPElementModel" id="mcS-ik-b0n">
|
<arrayController objectClassName="MPSiteModel" id="mcS-ik-b0n">
|
||||||
<connections>
|
<connections>
|
||||||
<binding destination="-2" name="contentArray" keyPath="elements" id="c96-Dv-HK1"/>
|
<binding destination="-2" name="contentArray" keyPath="sites" id="c96-Dv-HK1"/>
|
||||||
</connections>
|
</connections>
|
||||||
</arrayController>
|
</arrayController>
|
||||||
<box autoresizesSubviews="NO" title="Password Types" borderType="line" titlePosition="noTitle" id="bZe-7q-i6q">
|
<box autoresizesSubviews="NO" title="Password Types" borderType="line" titlePosition="noTitle" id="bZe-7q-i6q">
|
||||||
|
@ -9,20 +9,20 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
//
|
//
|
||||||
// MPElementModel.h
|
// MPSiteModel.h
|
||||||
// MPElementModel
|
// MPSiteModel
|
||||||
//
|
//
|
||||||
// Created by lhunath on 2/11/2014.
|
// Created by lhunath on 2/11/2014.
|
||||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
@class MPElementEntity;
|
@class MPSiteEntity;
|
||||||
|
|
||||||
@interface MPElementModel : NSObject
|
@interface MPSiteModel : NSObject
|
||||||
|
|
||||||
@property (nonatomic) NSString *siteName;
|
@property (nonatomic) NSString *siteName;
|
||||||
@property (nonatomic) MPElementType type;
|
@property (nonatomic) MPSiteType type;
|
||||||
@property (nonatomic) NSString *typeName;
|
@property (nonatomic) NSString *typeName;
|
||||||
@property (nonatomic) NSString *content;
|
@property (nonatomic) NSString *content;
|
||||||
@property (nonatomic) NSString *contentDisplay;
|
@property (nonatomic) NSString *contentDisplay;
|
||||||
@ -34,8 +34,8 @@
|
|||||||
@property (nonatomic) BOOL generated;
|
@property (nonatomic) BOOL generated;
|
||||||
@property (nonatomic) BOOL stored;
|
@property (nonatomic) BOOL stored;
|
||||||
|
|
||||||
- (id)initWithEntity:(MPElementEntity *)entity;
|
- (id)initWithEntity:(MPSiteEntity *)entity;
|
||||||
- (MPElementEntity *)entityInContext:(NSManagedObjectContext *)moc;
|
- (MPSiteEntity *)entityInContext:(NSManagedObjectContext *)moc;
|
||||||
|
|
||||||
- (void)updateContent;
|
- (void)updateContent;
|
||||||
@end
|
@end
|
@ -9,26 +9,26 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
//
|
//
|
||||||
// MPElementModel.h
|
// MPSiteModel.h
|
||||||
// MPElementModel
|
// MPSiteModel
|
||||||
//
|
//
|
||||||
// Created by lhunath on 2/11/2014.
|
// Created by lhunath on 2/11/2014.
|
||||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#import "MPElementModel.h"
|
#import "MPSiteModel.h"
|
||||||
#import "MPElementEntity.h"
|
#import "MPSiteEntity.h"
|
||||||
#import "MPEntities.h"
|
#import "MPEntities.h"
|
||||||
#import "MPAppDelegate_Shared.h"
|
#import "MPAppDelegate_Shared.h"
|
||||||
#import "MPAppDelegate_Store.h"
|
#import "MPAppDelegate_Store.h"
|
||||||
#import "MPMacAppDelegate.h"
|
#import "MPMacAppDelegate.h"
|
||||||
|
|
||||||
@implementation MPElementModel {
|
@implementation MPSiteModel {
|
||||||
NSManagedObjectID *_entityOID;
|
NSManagedObjectID *_entityOID;
|
||||||
BOOL _initialized;
|
BOOL _initialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithEntity:(MPElementEntity *)entity {
|
- (id)initWithEntity:(MPSiteEntity *)entity {
|
||||||
|
|
||||||
if (!(self = [super init]))
|
if (!(self = [super init]))
|
||||||
return nil;
|
return nil;
|
||||||
@ -39,7 +39,7 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setEntity:(MPElementEntity *)entity {
|
- (void)setEntity:(MPSiteEntity *)entity {
|
||||||
|
|
||||||
if ([_entityOID isEqual:entity.objectID])
|
if ([_entityOID isEqual:entity.objectID])
|
||||||
return;
|
return;
|
||||||
@ -52,21 +52,21 @@
|
|||||||
self.type = entity.type;
|
self.type = entity.type;
|
||||||
self.typeName = entity.typeName;
|
self.typeName = entity.typeName;
|
||||||
self.uses = entity.uses_;
|
self.uses = entity.uses_;
|
||||||
self.counter = [entity isKindOfClass:[MPElementGeneratedEntity class]]? [(MPElementGeneratedEntity *)entity counter]: 0;
|
self.counter = [entity isKindOfClass:[MPGeneratedSiteEntity class]]? [(MPGeneratedSiteEntity *)entity counter]: 0;
|
||||||
|
|
||||||
// Find all password types and the index of the current type amongst them.
|
// Find all password types and the index of the current type amongst them.
|
||||||
[self updateContent:entity];
|
[self updateContent:entity];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MPElementEntity *)entityInContext:(NSManagedObjectContext *)moc {
|
- (MPSiteEntity *)entityInContext:(NSManagedObjectContext *)moc {
|
||||||
|
|
||||||
if (!_entityOID)
|
if (!_entityOID)
|
||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
NSError *error;
|
NSError *error;
|
||||||
MPElementEntity *entity = (MPElementEntity *)[moc existingObjectWithID:_entityOID error:&error];
|
MPSiteEntity *entity = (MPSiteEntity *)[moc existingObjectWithID:_entityOID error:&error];
|
||||||
if (!entity)
|
if (!entity)
|
||||||
err( @"Couldn't retrieve active element: %@", error );
|
err( @"Couldn't retrieve active site: %@", [error fullDescription] );
|
||||||
|
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
@ -82,9 +82,9 @@
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
MPElementEntity *entity = [self entityInContext:context];
|
MPSiteEntity *entity = [self entityInContext:context];
|
||||||
if ([entity isKindOfClass:[MPElementGeneratedEntity class]]) {
|
if ([entity isKindOfClass:[MPGeneratedSiteEntity class]]) {
|
||||||
((MPElementGeneratedEntity *)entity).counter = counter;
|
((MPGeneratedSiteEntity *)entity).counter = counter;
|
||||||
[context saveToStore];
|
[context saveToStore];
|
||||||
|
|
||||||
[self updateContent:entity];
|
[self updateContent:entity];
|
||||||
@ -94,22 +94,22 @@
|
|||||||
|
|
||||||
- (BOOL)generated {
|
- (BOOL)generated {
|
||||||
|
|
||||||
return self.type & MPElementTypeClassGenerated;
|
return self.type & MPSiteTypeClassGenerated;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)stored {
|
- (BOOL)stored {
|
||||||
|
|
||||||
return self.type & MPElementTypeClassStored;
|
return self.type & MPSiteTypeClassStored;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)updateContent {
|
- (void)updateContent {
|
||||||
|
|
||||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
[self updateContent:[MPElementEntity existingObjectWithID:_entityOID inContext:context]];
|
[self updateContent:[MPSiteEntity existingObjectWithID:_entityOID inContext:context]];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)updateContent:(MPElementEntity *)entity {
|
- (void)updateContent:(MPSiteEntity *)entity {
|
||||||
|
|
||||||
static NSRegularExpression *re_anyChar;
|
static NSRegularExpression *re_anyChar;
|
||||||
static dispatch_once_t once = 0;
|
static dispatch_once_t once = 0;
|
||||||
@ -117,7 +117,7 @@
|
|||||||
re_anyChar = [NSRegularExpression regularExpressionWithPattern:@"." options:0 error:nil];
|
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;
|
NSString *displayResult = result;
|
||||||
if ([[MPConfig get].hidePasswords boolValue] && !([NSEvent modifierFlags] & NSAlternateKeyMask))
|
if ([[MPConfig get].hidePasswords boolValue] && !([NSEvent modifierFlags] & NSAlternateKeyMask))
|
||||||
displayResult = [displayResult stringByReplacingMatchesOfExpression:re_anyChar withTemplate:@"●"];
|
displayResult = [displayResult stringByReplacingMatchesOfExpression:re_anyChar withTemplate:@"●"];
|
@ -9,8 +9,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
//
|
//
|
||||||
// MPElementsTableView.h
|
// MPSitesTableView.h
|
||||||
// MPElementsTableView
|
// MPSitesTableView
|
||||||
//
|
//
|
||||||
// Created by lhunath on 2014-06-30.
|
// Created by lhunath on 2014-06-30.
|
||||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
||||||
@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
@class MPPasswordWindowController;
|
@class MPPasswordWindowController;
|
||||||
|
|
||||||
@interface MPElementsTableView : NSTableView
|
@interface MPSitesTableView : NSTableView
|
||||||
|
|
||||||
@property(nonatomic, weak) MPPasswordWindowController *controller;
|
@property(nonatomic, weak) MPPasswordWindowController *controller;
|
||||||
|
|
@ -9,17 +9,17 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
//
|
//
|
||||||
// MPElementsTableView.h
|
// MPSitesTableView.h
|
||||||
// MPElementsTableView
|
// MPSitesTableView
|
||||||
//
|
//
|
||||||
// Created by lhunath on 2014-06-30.
|
// Created by lhunath on 2014-06-30.
|
||||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#import "MPElementsTableView.h"
|
#import "MPSitesTableView.h"
|
||||||
#import "MPPasswordWindowController.h"
|
#import "MPPasswordWindowController.h"
|
||||||
|
|
||||||
@implementation MPElementsTableView
|
@implementation MPSitesTableView
|
||||||
|
|
||||||
- (void)doCommandBySelector:(SEL)aSelector {
|
- (void)doCommandBySelector:(SEL)aSelector {
|
||||||
|
|
@ -19,6 +19,10 @@
|
|||||||
<string></string>
|
<string></string>
|
||||||
<key>CFBundleTypeIconFiles</key>
|
<key>CFBundleTypeIconFiles</key>
|
||||||
<array/>
|
<array/>
|
||||||
|
<key>CFBundleTypeMIMETypes</key>
|
||||||
|
<array>
|
||||||
|
<string>text/plain</string>
|
||||||
|
</array>
|
||||||
<key>CFBundleTypeName</key>
|
<key>CFBundleTypeName</key>
|
||||||
<string>Master Password sites</string>
|
<string>Master Password sites</string>
|
||||||
<key>CFBundleTypeRole</key>
|
<key>CFBundleTypeRole</key>
|
||||||
@ -27,8 +31,10 @@
|
|||||||
<string>Owner</string>
|
<string>Owner</string>
|
||||||
<key>LSItemContentTypes</key>
|
<key>LSItemContentTypes</key>
|
||||||
<array>
|
<array>
|
||||||
<string>com.lyndir.lhunath.MasterPassword.sites</string>
|
<string>com.lyndir.masterpassword.sites</string>
|
||||||
</array>
|
</array>
|
||||||
|
<key>LSTypeIsPackage</key>
|
||||||
|
<integer>0</integer>
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
@ -47,19 +53,6 @@
|
|||||||
<string>[auto]</string>
|
<string>[auto]</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleURLTypes</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Editor</string>
|
|
||||||
<key>CFBundleURLName</key>
|
|
||||||
<string>com.lyndir.lhunath.MasterPassword</string>
|
|
||||||
<key>CFBundleURLSchemes</key>
|
|
||||||
<array>
|
|
||||||
<string>com.lyndir.lhunath.MasterPassword</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>[auto]</string>
|
<string>[auto]</string>
|
||||||
<key>LSApplicationCategoryType</key>
|
<key>LSApplicationCategoryType</key>
|
||||||
@ -77,12 +70,16 @@
|
|||||||
<key>UTExportedTypeDeclarations</key>
|
<key>UTExportedTypeDeclarations</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>UTTypeConformsTo</key>
|
||||||
|
<array>
|
||||||
|
<string>public.utf8-plain-text</string>
|
||||||
|
</array>
|
||||||
<key>UTTypeDescription</key>
|
<key>UTTypeDescription</key>
|
||||||
<string>Master Password sites</string>
|
<string>Master Password sites</string>
|
||||||
<key>UTTypeIconFile</key>
|
<key>UTTypeIconFile</key>
|
||||||
<string></string>
|
<string></string>
|
||||||
<key>UTTypeIdentifier</key>
|
<key>UTTypeIdentifier</key>
|
||||||
<string>com.lyndir.lhunath.MasterPassword.sites</string>
|
<string>com.lyndir.masterpassword.sites</string>
|
||||||
<key>UTTypeSize320IconFile</key>
|
<key>UTTypeSize320IconFile</key>
|
||||||
<string></string>
|
<string></string>
|
||||||
<key>UTTypeSize64IconFile</key>
|
<key>UTTypeSize64IconFile</key>
|
||||||
|
@ -9,12 +9,12 @@
|
|||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
93D390C676DF52DA7E459F19 /* MPPasswordWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D9D0061FF1159998F06 /* MPPasswordWindow.m */; };
|
93D390C676DF52DA7E459F19 /* MPPasswordWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D9D0061FF1159998F06 /* MPPasswordWindow.m */; };
|
||||||
93D392EC39DA43C46C692C12 /* NSDictionary+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */; };
|
93D392EC39DA43C46C692C12 /* NSDictionary+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */; };
|
||||||
93D394C4254EEB45FB335AFB /* MPElementsTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39423D7BF4FD31FE6D27C /* MPElementsTableView.m */; };
|
93D394C4254EEB45FB335AFB /* MPSitesTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39423D7BF4FD31FE6D27C /* MPSitesTableView.m */; };
|
||||||
93D395F08A087F8A24689347 /* NSArray+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */; };
|
93D395F08A087F8A24689347 /* NSArray+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */; };
|
||||||
93D3970BCF85F7902E611168 /* PearlProfiler.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39DB3A8ADED08C39A6228 /* PearlProfiler.m */; };
|
93D3970BCF85F7902E611168 /* PearlProfiler.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39DB3A8ADED08C39A6228 /* PearlProfiler.m */; };
|
||||||
93D39784E725A34D1EE3FB3B /* MPInitialWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D3CB30874147D9A9E1B /* MPInitialWindowController.m */; };
|
93D39784E725A34D1EE3FB3B /* MPInitialWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D3CB30874147D9A9E1B /* MPInitialWindowController.m */; };
|
||||||
93D39C34FE35830EF5BE1D2A /* NSArray+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D396D04E57792A54D437AC /* NSArray+Indexing.h */; };
|
93D39C34FE35830EF5BE1D2A /* NSArray+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D396D04E57792A54D437AC /* NSArray+Indexing.h */; };
|
||||||
93D39C5789EFA607CF788082 /* MPElementModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39E73BF5CBF8E5B005CD3 /* MPElementModel.m */; };
|
93D39C5789EFA607CF788082 /* MPSiteModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39E73BF5CBF8E5B005CD3 /* MPSiteModel.m */; };
|
||||||
93D39D304F73B3BBA031522A /* PearlProfiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D394EEFF5BF555A55AF361 /* PearlProfiler.h */; };
|
93D39D304F73B3BBA031522A /* PearlProfiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D394EEFF5BF555A55AF361 /* PearlProfiler.h */; };
|
||||||
93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */; };
|
93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */; };
|
||||||
93D39F833DEC1C89B2F795AC /* MPPasswordWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39A57A7823DE98A0FF83C /* MPPasswordWindowController.m */; };
|
93D39F833DEC1C89B2F795AC /* MPPasswordWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39A57A7823DE98A0FF83C /* MPPasswordWindowController.m */; };
|
||||||
@ -30,6 +30,7 @@
|
|||||||
DA2508F119511D3600AC23F1 /* MPPasswordWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA2508F019511D3600AC23F1 /* MPPasswordWindowController.xib */; };
|
DA2508F119511D3600AC23F1 /* MPPasswordWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA2508F019511D3600AC23F1 /* MPPasswordWindowController.xib */; };
|
||||||
DA2508F719513C1400AC23F1 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA16B343170661EE000A0EAB /* Cocoa.framework */; };
|
DA2508F719513C1400AC23F1 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA16B343170661EE000A0EAB /* Cocoa.framework */; };
|
||||||
DA250925195148E200AC23F1 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAEBC45214F6364500987BF6 /* QuartzCore.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 */; };
|
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 */; };
|
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 */; };
|
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 */; };
|
DA30E9D215722EE500A68B4C /* Pearl-Crypto.m in Sources */ = {isa = PBXBuildFile; fileRef = DA30E9D115722EE500A68B4C /* Pearl-Crypto.m */; };
|
||||||
DA30E9D715723E6900A68B4C /* PearlLazy.h in Headers */ = {isa = PBXBuildFile; fileRef = DA30E9D515723E6900A68B4C /* PearlLazy.h */; };
|
DA30E9D715723E6900A68B4C /* PearlLazy.h in Headers */ = {isa = PBXBuildFile; fileRef = DA30E9D515723E6900A68B4C /* PearlLazy.h */; };
|
||||||
DA30E9D815723E6900A68B4C /* PearlLazy.m in Sources */ = {isa = PBXBuildFile; fileRef = DA30E9D615723E6900A68B4C /* PearlLazy.m */; };
|
DA30E9D815723E6900A68B4C /* PearlLazy.m in Sources */ = {isa = PBXBuildFile; fileRef = DA30E9D615723E6900A68B4C /* PearlLazy.m */; };
|
||||||
|
DA32CFD919CF1C70004F3F0E /* MPGeneratedSiteEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA32CFD819CF1C70004F3F0E /* MPGeneratedSiteEntity.m */; };
|
||||||
|
DA32CFDC19CF1C70004F3F0E /* MPStoredSiteEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA32CFDB19CF1C70004F3F0E /* MPStoredSiteEntity.m */; };
|
||||||
|
DA32CFDF19CF1C70004F3F0E /* MPSiteEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA32CFDE19CF1C70004F3F0E /* MPSiteEntity.m */; };
|
||||||
|
DA32CFE219CF1C71004F3F0E /* MPSiteQuestionEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA32CFE119CF1C71004F3F0E /* MPSiteQuestionEntity.m */; };
|
||||||
|
DA32CFE519CF1C71004F3F0E /* MPUserEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA32CFE419CF1C71004F3F0E /* MPUserEntity.m */; };
|
||||||
DA3509FE15F101A500C14A8E /* PearlQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = DA3509FC15F101A500C14A8E /* PearlQueue.h */; };
|
DA3509FE15F101A500C14A8E /* PearlQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = DA3509FC15F101A500C14A8E /* PearlQueue.h */; };
|
||||||
DA3509FF15F101A500C14A8E /* PearlQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = DA3509FD15F101A500C14A8E /* PearlQueue.m */; };
|
DA3509FF15F101A500C14A8E /* PearlQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = DA3509FD15F101A500C14A8E /* PearlQueue.m */; };
|
||||||
DA3B844F190FC60900246EEA /* Crashlytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA3B844A190FC5A900246EEA /* Crashlytics.framework */; };
|
DA3B844F190FC60900246EEA /* Crashlytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA3B844A190FC5A900246EEA /* Crashlytics.framework */; };
|
||||||
DA3B8452190FC86F00246EEA /* NSManagedObject+Pearl.m in Sources */ = {isa = PBXBuildFile; fileRef = DA3B8450190FC86F00246EEA /* NSManagedObject+Pearl.m */; };
|
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 */; };
|
DA3B8453190FC86F00246EEA /* NSManagedObject+Pearl.h in Headers */ = {isa = PBXBuildFile; fileRef = DA3B8451190FC86F00246EEA /* NSManagedObject+Pearl.h */; };
|
||||||
DA3B8456190FC89700246EEA /* MPFixable.m in Sources */ = {isa = PBXBuildFile; fileRef = DA3B8454190FC89700246EEA /* MPFixable.m */; };
|
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 */; };
|
DA4425CC1557BED40052177D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
|
||||||
DA4DA1D91564471A00F6F596 /* libjrswizzle.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC6326C148680650075AEA5 /* libjrswizzle.a */; };
|
DA4DA1D91564471A00F6F596 /* libjrswizzle.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC6326C148680650075AEA5 /* libjrswizzle.a */; };
|
||||||
DA5E5C9417248AA1003798D8 /* libscryptenc-osx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5E5C8717248AA1003798D8 /* libscryptenc-osx.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 */; };
|
DA5E5CFA1724A667003798D8 /* MPAppDelegate_Shared.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CA01724A667003798D8 /* MPAppDelegate_Shared.m */; };
|
||||||
DA5E5CFB1724A667003798D8 /* MPAppDelegate_Store.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CA21724A667003798D8 /* MPAppDelegate_Store.m */; };
|
DA5E5CFB1724A667003798D8 /* MPAppDelegate_Store.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CA21724A667003798D8 /* MPAppDelegate_Store.m */; };
|
||||||
DA5E5CFC1724A667003798D8 /* MPConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CA41724A667003798D8 /* MPConfig.m */; };
|
DA5E5CFC1724A667003798D8 /* MPConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CA41724A667003798D8 /* MPConfig.m */; };
|
||||||
DA5E5CFD1724A667003798D8 /* MPElementEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CA61724A667003798D8 /* MPElementEntity.m */; };
|
|
||||||
DA5E5CFE1724A667003798D8 /* MPElementGeneratedEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CA81724A667003798D8 /* MPElementGeneratedEntity.m */; };
|
|
||||||
DA5E5CFF1724A667003798D8 /* MPElementStoredEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CAA1724A667003798D8 /* MPElementStoredEntity.m */; };
|
|
||||||
DA5E5D001724A667003798D8 /* MPEntities.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CAC1724A667003798D8 /* MPEntities.m */; };
|
DA5E5D001724A667003798D8 /* MPEntities.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CAC1724A667003798D8 /* MPEntities.m */; };
|
||||||
DA5E5D011724A667003798D8 /* MPKey.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CAE1724A667003798D8 /* MPKey.m */; };
|
DA5E5D011724A667003798D8 /* MPKey.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CAE1724A667003798D8 /* MPKey.m */; };
|
||||||
DA5E5D021724A667003798D8 /* MPUserEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CB11724A667003798D8 /* MPUserEntity.m */; };
|
|
||||||
DA5E5D031724A667003798D8 /* MPMacAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CB41724A667003798D8 /* MPMacAppDelegate.m */; };
|
DA5E5D031724A667003798D8 /* MPMacAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CB41724A667003798D8 /* MPMacAppDelegate.m */; };
|
||||||
DA5E5D041724A667003798D8 /* MPMacConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CB61724A667003798D8 /* MPMacConfig.m */; };
|
DA5E5D041724A667003798D8 /* MPMacConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CB61724A667003798D8 /* MPMacConfig.m */; };
|
||||||
DA5E5D081724A667003798D8 /* MasterPassword.entitlements in Resources */ = {isa = PBXBuildFile; fileRef = DA5E5CBF1724A667003798D8 /* MasterPassword.entitlements */; };
|
DA5E5D081724A667003798D8 /* MasterPassword.entitlements in Resources */ = {isa = PBXBuildFile; fileRef = DA5E5CBF1724A667003798D8 /* MasterPassword.entitlements */; };
|
||||||
DA5E5D0A1724A667003798D8 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DA5E5CC21724A667003798D8 /* InfoPlist.strings */; };
|
DA5E5D0A1724A667003798D8 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DA5E5CC21724A667003798D8 /* InfoPlist.strings */; };
|
||||||
DA5E5D0B1724A667003798D8 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA5E5CC41724A667003798D8 /* MainMenu.xib */; };
|
DA5E5D0B1724A667003798D8 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA5E5CC41724A667003798D8 /* MainMenu.xib */; };
|
||||||
DA5E5D0C1724A667003798D8 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CC61724A667003798D8 /* main.m */; };
|
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 */; };
|
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 */; };
|
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 */; };
|
DA6558A419A99609009A0BEB /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DA6558A319A99609009A0BEB /* Images.xcassets */; };
|
||||||
@ -228,21 +230,21 @@
|
|||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Indexing.m"; sourceTree = "<group>"; };
|
93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Indexing.m"; sourceTree = "<group>"; };
|
||||||
93D39240B5143E01F0B75E96 /* MPElementModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementModel.h; sourceTree = "<group>"; };
|
93D39240B5143E01F0B75E96 /* MPSiteModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSiteModel.h; sourceTree = "<group>"; };
|
||||||
93D392C3918763B3B72CF366 /* MPPasswordWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordWindowController.h; sourceTree = "<group>"; };
|
93D392C3918763B3B72CF366 /* MPPasswordWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordWindowController.h; sourceTree = "<group>"; };
|
||||||
93D39368EF3CBFEF2AFCA15A /* MPInitialWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPInitialWindowController.h; sourceTree = "<group>"; };
|
93D39368EF3CBFEF2AFCA15A /* MPInitialWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPInitialWindowController.h; sourceTree = "<group>"; };
|
||||||
93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Indexing.h"; sourceTree = "<group>"; };
|
93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Indexing.h"; sourceTree = "<group>"; };
|
||||||
93D39423D7BF4FD31FE6D27C /* MPElementsTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementsTableView.m; sourceTree = "<group>"; };
|
93D39423D7BF4FD31FE6D27C /* MPSitesTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSitesTableView.m; sourceTree = "<group>"; };
|
||||||
93D394EEFF5BF555A55AF361 /* PearlProfiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PearlProfiler.h; path = ../../../External/Pearl/Pearl/PearlProfiler.h; sourceTree = "<group>"; };
|
93D394EEFF5BF555A55AF361 /* PearlProfiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PearlProfiler.h; path = ../../../External/Pearl/Pearl/PearlProfiler.h; sourceTree = "<group>"; };
|
||||||
93D396D04E57792A54D437AC /* NSArray+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Indexing.h"; sourceTree = "<group>"; };
|
93D396D04E57792A54D437AC /* NSArray+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Indexing.h"; sourceTree = "<group>"; };
|
||||||
93D3977484534E99F9BA579D /* MPPasswordWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordWindow.h; sourceTree = "<group>"; };
|
93D3977484534E99F9BA579D /* MPPasswordWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordWindow.h; sourceTree = "<group>"; };
|
||||||
93D39A57A7823DE98A0FF83C /* MPPasswordWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordWindowController.m; sourceTree = "<group>"; };
|
93D39A57A7823DE98A0FF83C /* MPPasswordWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordWindowController.m; sourceTree = "<group>"; };
|
||||||
93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Indexing.m"; sourceTree = "<group>"; };
|
93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Indexing.m"; sourceTree = "<group>"; };
|
||||||
93D39AC6360DDC16AEAA4119 /* MPElementsTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementsTableView.h; sourceTree = "<group>"; };
|
93D39AC6360DDC16AEAA4119 /* MPSitesTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSitesTableView.h; sourceTree = "<group>"; };
|
||||||
93D39D3CB30874147D9A9E1B /* MPInitialWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPInitialWindowController.m; sourceTree = "<group>"; };
|
93D39D3CB30874147D9A9E1B /* MPInitialWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPInitialWindowController.m; sourceTree = "<group>"; };
|
||||||
93D39D9D0061FF1159998F06 /* MPPasswordWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordWindow.m; sourceTree = "<group>"; };
|
93D39D9D0061FF1159998F06 /* MPPasswordWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordWindow.m; sourceTree = "<group>"; };
|
||||||
93D39DB3A8ADED08C39A6228 /* PearlProfiler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PearlProfiler.m; path = ../../../External/Pearl/Pearl/PearlProfiler.m; sourceTree = "<group>"; };
|
93D39DB3A8ADED08C39A6228 /* PearlProfiler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PearlProfiler.m; path = ../../../External/Pearl/Pearl/PearlProfiler.m; sourceTree = "<group>"; };
|
||||||
93D39E73BF5CBF8E5B005CD3 /* MPElementModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementModel.m; sourceTree = "<group>"; };
|
93D39E73BF5CBF8E5B005CD3 /* MPSiteModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSiteModel.m; sourceTree = "<group>"; };
|
||||||
DA0933C91747A56A00DE1CEF /* MPInitialWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MPInitialWindow.xib; sourceTree = "<group>"; };
|
DA0933C91747A56A00DE1CEF /* MPInitialWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MPInitialWindow.xib; sourceTree = "<group>"; };
|
||||||
DA0933CB1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "shot-laptop-leaning-iphone.png"; sourceTree = "<group>"; };
|
DA0933CB1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "shot-laptop-leaning-iphone.png"; sourceTree = "<group>"; };
|
||||||
DA0933CF1747B91B00DE1CEF /* appstore.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = appstore.png; sourceTree = "<group>"; };
|
DA0933CF1747B91B00DE1CEF /* appstore.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = appstore.png; sourceTree = "<group>"; };
|
||||||
@ -256,6 +258,11 @@
|
|||||||
DA25090719513C1400AC23F1 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
|
DA25090719513C1400AC23F1 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
|
||||||
DA2509261951B86C00AC23F1 /* small-screen.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "small-screen.png"; sourceTree = "<group>"; };
|
DA2509261951B86C00AC23F1 /* small-screen.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "small-screen.png"; sourceTree = "<group>"; };
|
||||||
DA2509271951B86C00AC23F1 /* screen.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = screen.png; sourceTree = "<group>"; };
|
DA2509271951B86C00AC23F1 /* screen.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = screen.png; sourceTree = "<group>"; };
|
||||||
|
DA29992719C6A89900AF7DF1 /* MasterPassword 1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 1.xcdatamodel"; sourceTree = "<group>"; };
|
||||||
|
DA29992819C6A89900AF7DF1 /* MasterPassword 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 2.xcdatamodel"; sourceTree = "<group>"; };
|
||||||
|
DA29992919C6A89900AF7DF1 /* MasterPassword 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 3.xcdatamodel"; sourceTree = "<group>"; };
|
||||||
|
DA29992A19C6A89900AF7DF1 /* MasterPassword 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 4.xcdatamodel"; sourceTree = "<group>"; };
|
||||||
|
DA29992B19C6A89900AF7DF1 /* MasterPassword 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 5.xcdatamodel"; sourceTree = "<group>"; };
|
||||||
DA2CA4E718D323D3007798F8 /* NSError+PearlFullDescription.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+PearlFullDescription.m"; sourceTree = "<group>"; };
|
DA2CA4E718D323D3007798F8 /* NSError+PearlFullDescription.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+PearlFullDescription.m"; sourceTree = "<group>"; };
|
||||||
DA2CA4E818D323D3007798F8 /* NSError+PearlFullDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+PearlFullDescription.h"; sourceTree = "<group>"; };
|
DA2CA4E818D323D3007798F8 /* NSError+PearlFullDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+PearlFullDescription.h"; sourceTree = "<group>"; };
|
||||||
DA2CA4E918D323D3007798F8 /* NSArray+Pearl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Pearl.m"; sourceTree = "<group>"; };
|
DA2CA4E918D323D3007798F8 /* NSArray+Pearl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Pearl.m"; sourceTree = "<group>"; };
|
||||||
@ -268,6 +275,17 @@
|
|||||||
DA30E9D115722EE500A68B4C /* Pearl-Crypto.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "Pearl-Crypto.m"; sourceTree = "<group>"; };
|
DA30E9D115722EE500A68B4C /* Pearl-Crypto.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "Pearl-Crypto.m"; sourceTree = "<group>"; };
|
||||||
DA30E9D515723E6900A68B4C /* PearlLazy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlLazy.h; sourceTree = "<group>"; };
|
DA30E9D515723E6900A68B4C /* PearlLazy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlLazy.h; sourceTree = "<group>"; };
|
||||||
DA30E9D615723E6900A68B4C /* PearlLazy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlLazy.m; sourceTree = "<group>"; };
|
DA30E9D615723E6900A68B4C /* PearlLazy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlLazy.m; sourceTree = "<group>"; };
|
||||||
|
DA32CFD719CF1C70004F3F0E /* MPGeneratedSiteEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPGeneratedSiteEntity.h; sourceTree = "<group>"; };
|
||||||
|
DA32CFD819CF1C70004F3F0E /* MPGeneratedSiteEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPGeneratedSiteEntity.m; sourceTree = "<group>"; };
|
||||||
|
DA32CFDA19CF1C70004F3F0E /* MPStoredSiteEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPStoredSiteEntity.h; sourceTree = "<group>"; };
|
||||||
|
DA32CFDB19CF1C70004F3F0E /* MPStoredSiteEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPStoredSiteEntity.m; sourceTree = "<group>"; };
|
||||||
|
DA32CFDD19CF1C70004F3F0E /* MPSiteEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSiteEntity.h; sourceTree = "<group>"; };
|
||||||
|
DA32CFDE19CF1C70004F3F0E /* MPSiteEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSiteEntity.m; sourceTree = "<group>"; };
|
||||||
|
DA32CFE019CF1C71004F3F0E /* MPSiteQuestionEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSiteQuestionEntity.h; sourceTree = "<group>"; };
|
||||||
|
DA32CFE119CF1C71004F3F0E /* MPSiteQuestionEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSiteQuestionEntity.m; sourceTree = "<group>"; };
|
||||||
|
DA32CFE319CF1C71004F3F0E /* MPUserEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUserEntity.h; sourceTree = "<group>"; };
|
||||||
|
DA32CFE419CF1C71004F3F0E /* MPUserEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUserEntity.m; sourceTree = "<group>"; };
|
||||||
|
DA32D00019CF470E004F3F0E /* MasterPassword 6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 6.xcdatamodel"; sourceTree = "<group>"; };
|
||||||
DA3509FC15F101A500C14A8E /* PearlQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlQueue.h; sourceTree = "<group>"; };
|
DA3509FC15F101A500C14A8E /* PearlQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlQueue.h; sourceTree = "<group>"; };
|
||||||
DA3509FD15F101A500C14A8E /* PearlQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlQueue.m; sourceTree = "<group>"; };
|
DA3509FD15F101A500C14A8E /* PearlQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlQueue.m; sourceTree = "<group>"; };
|
||||||
DA3B844A190FC5A900246EEA /* Crashlytics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Crashlytics.framework; sourceTree = "<group>"; };
|
DA3B844A190FC5A900246EEA /* Crashlytics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Crashlytics.framework; sourceTree = "<group>"; };
|
||||||
@ -275,6 +293,7 @@
|
|||||||
DA3B8451190FC86F00246EEA /* NSManagedObject+Pearl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSManagedObject+Pearl.h"; sourceTree = "<group>"; };
|
DA3B8451190FC86F00246EEA /* NSManagedObject+Pearl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSManagedObject+Pearl.h"; sourceTree = "<group>"; };
|
||||||
DA3B8454190FC89700246EEA /* MPFixable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPFixable.m; sourceTree = "<group>"; };
|
DA3B8454190FC89700246EEA /* MPFixable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPFixable.m; sourceTree = "<group>"; };
|
||||||
DA3B8455190FC89700246EEA /* MPFixable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPFixable.h; sourceTree = "<group>"; };
|
DA3B8455190FC89700246EEA /* MPFixable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPFixable.h; sourceTree = "<group>"; };
|
||||||
|
DA3BCFCC19BD09E0006B2681 /* SourceCodePro-Regular.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SourceCodePro-Regular.otf"; sourceTree = "<group>"; };
|
||||||
DA4425CB1557BED40052177D /* libUbiquityStoreManager.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libUbiquityStoreManager.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
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; };
|
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; };
|
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 = "<group>"; };
|
DA5E5CA21724A667003798D8 /* MPAppDelegate_Store.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAppDelegate_Store.m; sourceTree = "<group>"; };
|
||||||
DA5E5CA31724A667003798D8 /* MPConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPConfig.h; sourceTree = "<group>"; };
|
DA5E5CA31724A667003798D8 /* MPConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPConfig.h; sourceTree = "<group>"; };
|
||||||
DA5E5CA41724A667003798D8 /* MPConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPConfig.m; sourceTree = "<group>"; };
|
DA5E5CA41724A667003798D8 /* MPConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPConfig.m; sourceTree = "<group>"; };
|
||||||
DA5E5CA51724A667003798D8 /* MPElementEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementEntity.h; sourceTree = "<group>"; };
|
|
||||||
DA5E5CA61724A667003798D8 /* MPElementEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementEntity.m; sourceTree = "<group>"; };
|
|
||||||
DA5E5CA71724A667003798D8 /* MPElementGeneratedEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementGeneratedEntity.h; sourceTree = "<group>"; };
|
|
||||||
DA5E5CA81724A667003798D8 /* MPElementGeneratedEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementGeneratedEntity.m; sourceTree = "<group>"; };
|
|
||||||
DA5E5CA91724A667003798D8 /* MPElementStoredEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementStoredEntity.h; sourceTree = "<group>"; };
|
|
||||||
DA5E5CAA1724A667003798D8 /* MPElementStoredEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementStoredEntity.m; sourceTree = "<group>"; };
|
|
||||||
DA5E5CAB1724A667003798D8 /* MPEntities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPEntities.h; sourceTree = "<group>"; };
|
DA5E5CAB1724A667003798D8 /* MPEntities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPEntities.h; sourceTree = "<group>"; };
|
||||||
DA5E5CAC1724A667003798D8 /* MPEntities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPEntities.m; sourceTree = "<group>"; };
|
DA5E5CAC1724A667003798D8 /* MPEntities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPEntities.m; sourceTree = "<group>"; };
|
||||||
DA5E5CAD1724A667003798D8 /* MPKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPKey.h; sourceTree = "<group>"; };
|
DA5E5CAD1724A667003798D8 /* MPKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPKey.h; sourceTree = "<group>"; };
|
||||||
DA5E5CAE1724A667003798D8 /* MPKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPKey.m; sourceTree = "<group>"; };
|
DA5E5CAE1724A667003798D8 /* MPKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPKey.m; sourceTree = "<group>"; };
|
||||||
DA5E5CAF1724A667003798D8 /* MPTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPTypes.h; sourceTree = "<group>"; };
|
DA5E5CAF1724A667003798D8 /* MPTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPTypes.h; sourceTree = "<group>"; };
|
||||||
DA5E5CB01724A667003798D8 /* MPUserEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUserEntity.h; sourceTree = "<group>"; };
|
|
||||||
DA5E5CB11724A667003798D8 /* MPUserEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUserEntity.m; sourceTree = "<group>"; };
|
|
||||||
DA5E5CB31724A667003798D8 /* MPMacAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPMacAppDelegate.h; sourceTree = "<group>"; };
|
DA5E5CB31724A667003798D8 /* MPMacAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPMacAppDelegate.h; sourceTree = "<group>"; };
|
||||||
DA5E5CB41724A667003798D8 /* MPMacAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPMacAppDelegate.m; sourceTree = "<group>"; };
|
DA5E5CB41724A667003798D8 /* MPMacAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPMacAppDelegate.m; sourceTree = "<group>"; };
|
||||||
DA5E5CB51724A667003798D8 /* MPMacConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPMacConfig.h; sourceTree = "<group>"; };
|
DA5E5CB51724A667003798D8 /* MPMacConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPMacConfig.h; sourceTree = "<group>"; };
|
||||||
@ -318,10 +329,6 @@
|
|||||||
DA5E5CC31724A667003798D8 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
DA5E5CC31724A667003798D8 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
DA5E5CC51724A667003798D8 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
DA5E5CC51724A667003798D8 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
||||||
DA5E5CC61724A667003798D8 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
DA5E5CC61724A667003798D8 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||||
DA5E5CC81724A667003798D8 /* MasterPassword 1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 1.xcdatamodel"; sourceTree = "<group>"; };
|
|
||||||
DA5E5CC91724A667003798D8 /* MasterPassword 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 2.xcdatamodel"; sourceTree = "<group>"; };
|
|
||||||
DA5E5CCA1724A667003798D8 /* MasterPassword 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 3.xcdatamodel"; sourceTree = "<group>"; };
|
|
||||||
DA5E5CCB1724A667003798D8 /* MasterPassword 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 4.xcdatamodel"; sourceTree = "<group>"; };
|
|
||||||
DA606FEA195D03E200CA98B5 /* icon_action.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon_action.png; sourceTree = "<group>"; };
|
DA606FEA195D03E200CA98B5 /* icon_action.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon_action.png; sourceTree = "<group>"; };
|
||||||
DA606FEB195D03E200CA98B5 /* icon_action@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon_action@2x.png"; sourceTree = "<group>"; };
|
DA606FEB195D03E200CA98B5 /* icon_action@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon_action@2x.png"; sourceTree = "<group>"; };
|
||||||
DA606FEC195D03E200CA98B5 /* icon_addressbook-person.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon_addressbook-person.png"; sourceTree = "<group>"; };
|
DA606FEC195D03E200CA98B5 /* icon_addressbook-person.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon_addressbook-person.png"; sourceTree = "<group>"; };
|
||||||
@ -992,6 +999,16 @@
|
|||||||
DA5E5C961724A667003798D8 /* ObjC */ = {
|
DA5E5C961724A667003798D8 /* ObjC */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
DA32CFE319CF1C71004F3F0E /* MPUserEntity.h */,
|
||||||
|
DA32CFE419CF1C71004F3F0E /* MPUserEntity.m */,
|
||||||
|
DA32CFE019CF1C71004F3F0E /* MPSiteQuestionEntity.h */,
|
||||||
|
DA32CFE119CF1C71004F3F0E /* MPSiteQuestionEntity.m */,
|
||||||
|
DA32CFDD19CF1C70004F3F0E /* MPSiteEntity.h */,
|
||||||
|
DA32CFDE19CF1C70004F3F0E /* MPSiteEntity.m */,
|
||||||
|
DA32CFDA19CF1C70004F3F0E /* MPStoredSiteEntity.h */,
|
||||||
|
DA32CFDB19CF1C70004F3F0E /* MPStoredSiteEntity.m */,
|
||||||
|
DA32CFD719CF1C70004F3F0E /* MPGeneratedSiteEntity.h */,
|
||||||
|
DA32CFD819CF1C70004F3F0E /* MPGeneratedSiteEntity.m */,
|
||||||
DA3B8454190FC89700246EEA /* MPFixable.m */,
|
DA3B8454190FC89700246EEA /* MPFixable.m */,
|
||||||
DA3B8455190FC89700246EEA /* MPFixable.h */,
|
DA3B8455190FC89700246EEA /* MPFixable.h */,
|
||||||
DA5E5CB21724A667003798D8 /* Mac */,
|
DA5E5CB21724A667003798D8 /* Mac */,
|
||||||
@ -1009,20 +1026,12 @@
|
|||||||
DA5E5CA21724A667003798D8 /* MPAppDelegate_Store.m */,
|
DA5E5CA21724A667003798D8 /* MPAppDelegate_Store.m */,
|
||||||
DA5E5CA31724A667003798D8 /* MPConfig.h */,
|
DA5E5CA31724A667003798D8 /* MPConfig.h */,
|
||||||
DA5E5CA41724A667003798D8 /* MPConfig.m */,
|
DA5E5CA41724A667003798D8 /* MPConfig.m */,
|
||||||
DA5E5CA51724A667003798D8 /* MPElementEntity.h */,
|
|
||||||
DA5E5CA61724A667003798D8 /* MPElementEntity.m */,
|
|
||||||
DA5E5CA71724A667003798D8 /* MPElementGeneratedEntity.h */,
|
|
||||||
DA5E5CA81724A667003798D8 /* MPElementGeneratedEntity.m */,
|
|
||||||
DA5E5CA91724A667003798D8 /* MPElementStoredEntity.h */,
|
|
||||||
DA5E5CAA1724A667003798D8 /* MPElementStoredEntity.m */,
|
|
||||||
DA5E5CAB1724A667003798D8 /* MPEntities.h */,
|
DA5E5CAB1724A667003798D8 /* MPEntities.h */,
|
||||||
DA5E5CAC1724A667003798D8 /* MPEntities.m */,
|
DA5E5CAC1724A667003798D8 /* MPEntities.m */,
|
||||||
DA5E5CAD1724A667003798D8 /* MPKey.h */,
|
DA5E5CAD1724A667003798D8 /* MPKey.h */,
|
||||||
DA5E5CAE1724A667003798D8 /* MPKey.m */,
|
DA5E5CAE1724A667003798D8 /* MPKey.m */,
|
||||||
DA5E5CAF1724A667003798D8 /* MPTypes.h */,
|
DA5E5CAF1724A667003798D8 /* MPTypes.h */,
|
||||||
DA5E5CB01724A667003798D8 /* MPUserEntity.h */,
|
DA29992619C6A89900AF7DF1 /* MasterPassword.xcdatamodeld */,
|
||||||
DA5E5CB11724A667003798D8 /* MPUserEntity.m */,
|
|
||||||
DA5E5CC71724A667003798D8 /* MasterPassword.xcdatamodeld */,
|
|
||||||
);
|
);
|
||||||
name = ObjC;
|
name = ObjC;
|
||||||
path = ..;
|
path = ..;
|
||||||
@ -1043,8 +1052,8 @@
|
|||||||
DA5E5CC41724A667003798D8 /* MainMenu.xib */,
|
DA5E5CC41724A667003798D8 /* MainMenu.xib */,
|
||||||
DA5E5CC61724A667003798D8 /* main.m */,
|
DA5E5CC61724A667003798D8 /* main.m */,
|
||||||
DA0933C91747A56A00DE1CEF /* MPInitialWindow.xib */,
|
DA0933C91747A56A00DE1CEF /* MPInitialWindow.xib */,
|
||||||
93D39E73BF5CBF8E5B005CD3 /* MPElementModel.m */,
|
93D39E73BF5CBF8E5B005CD3 /* MPSiteModel.m */,
|
||||||
93D39240B5143E01F0B75E96 /* MPElementModel.h */,
|
93D39240B5143E01F0B75E96 /* MPSiteModel.h */,
|
||||||
DA2508F019511D3600AC23F1 /* MPPasswordWindowController.xib */,
|
DA2508F019511D3600AC23F1 /* MPPasswordWindowController.xib */,
|
||||||
93D39A57A7823DE98A0FF83C /* MPPasswordWindowController.m */,
|
93D39A57A7823DE98A0FF83C /* MPPasswordWindowController.m */,
|
||||||
93D392C3918763B3B72CF366 /* MPPasswordWindowController.h */,
|
93D392C3918763B3B72CF366 /* MPPasswordWindowController.h */,
|
||||||
@ -1052,8 +1061,8 @@
|
|||||||
93D3977484534E99F9BA579D /* MPPasswordWindow.h */,
|
93D3977484534E99F9BA579D /* MPPasswordWindow.h */,
|
||||||
93D39D3CB30874147D9A9E1B /* MPInitialWindowController.m */,
|
93D39D3CB30874147D9A9E1B /* MPInitialWindowController.m */,
|
||||||
93D39368EF3CBFEF2AFCA15A /* MPInitialWindowController.h */,
|
93D39368EF3CBFEF2AFCA15A /* MPInitialWindowController.h */,
|
||||||
93D39423D7BF4FD31FE6D27C /* MPElementsTableView.m */,
|
93D39423D7BF4FD31FE6D27C /* MPSitesTableView.m */,
|
||||||
93D39AC6360DDC16AEAA4119 /* MPElementsTableView.h */,
|
93D39AC6360DDC16AEAA4119 /* MPSitesTableView.h */,
|
||||||
);
|
);
|
||||||
path = Mac;
|
path = Mac;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -1607,6 +1616,7 @@
|
|||||||
DACA268A1705DF81002C6C22 /* Fonts */ = {
|
DACA268A1705DF81002C6C22 /* Fonts */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
DA3BCFCC19BD09E0006B2681 /* SourceCodePro-Regular.otf */,
|
||||||
DAF4EF52190A828100023C90 /* Exo2.0-Thin.otf */,
|
DAF4EF52190A828100023C90 /* Exo2.0-Thin.otf */,
|
||||||
DAF4EF53190A828100023C90 /* Exo2.0-Regular.otf */,
|
DAF4EF53190A828100023C90 /* Exo2.0-Regular.otf */,
|
||||||
DAF4EF54190A828100023C90 /* Exo2.0-ExtraBold.otf */,
|
DAF4EF54190A828100023C90 /* Exo2.0-ExtraBold.otf */,
|
||||||
@ -1939,7 +1949,7 @@
|
|||||||
attributes = {
|
attributes = {
|
||||||
CLASSPREFIX = MP;
|
CLASSPREFIX = MP;
|
||||||
LastTestingUpgradeCheck = 0510;
|
LastTestingUpgradeCheck = 0510;
|
||||||
LastUpgradeCheck = 0510;
|
LastUpgradeCheck = 0600;
|
||||||
ORGANIZATIONNAME = Lyndir;
|
ORGANIZATIONNAME = Lyndir;
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
DA5BFA43147E415C00F98B1E = {
|
DA5BFA43147E415C00F98B1E = {
|
||||||
@ -1954,12 +1964,10 @@
|
|||||||
};
|
};
|
||||||
buildConfigurationList = DA5BFA3E147E415C00F98B1E /* Build configuration list for PBXProject "MasterPassword-Mac" */;
|
buildConfigurationList = DA5BFA3E147E415C00F98B1E /* Build configuration list for PBXProject "MasterPassword-Mac" */;
|
||||||
compatibilityVersion = "Xcode 3.2";
|
compatibilityVersion = "Xcode 3.2";
|
||||||
developmentRegion = English;
|
developmentRegion = en;
|
||||||
hasScannedForEncodings = 0;
|
hasScannedForEncodings = 0;
|
||||||
knownRegions = (
|
knownRegions = (
|
||||||
en,
|
en,
|
||||||
English,
|
|
||||||
es,
|
|
||||||
);
|
);
|
||||||
mainGroup = DA5BFA39147E415C00F98B1E;
|
mainGroup = DA5BFA39147E415C00F98B1E;
|
||||||
productRefGroup = DA5BFA45147E415C00F98B1E /* Products */;
|
productRefGroup = DA5BFA45147E415C00F98B1E /* Products */;
|
||||||
@ -2041,6 +2049,7 @@
|
|||||||
DACA27301705DF81002C6C22 /* avatar-18.png in Resources */,
|
DACA27301705DF81002C6C22 /* avatar-18.png in Resources */,
|
||||||
DACA27311705DF81002C6C22 /* avatar-4.png in Resources */,
|
DACA27311705DF81002C6C22 /* avatar-4.png in Resources */,
|
||||||
DAF4EF58190A828100023C90 /* Exo2.0-ExtraBold.otf in Resources */,
|
DAF4EF58190A828100023C90 /* Exo2.0-ExtraBold.otf in Resources */,
|
||||||
|
DA3BCFCD19BD09E0006B2681 /* SourceCodePro-Regular.otf in Resources */,
|
||||||
DAF4EF56190A828100023C90 /* Exo2.0-Thin.otf in Resources */,
|
DAF4EF56190A828100023C90 /* Exo2.0-Thin.otf in Resources */,
|
||||||
DACA27321705DF81002C6C22 /* avatar-16.png in Resources */,
|
DACA27321705DF81002C6C22 /* avatar-16.png in Resources */,
|
||||||
DACA27331705DF81002C6C22 /* avatar-12@2x.png in Resources */,
|
DACA27331705DF81002C6C22 /* avatar-12@2x.png in Resources */,
|
||||||
@ -2133,28 +2142,29 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
DA5E5CF61724A667003798D8 /* MPAlgorithm.m in Sources */,
|
DA5E5CF61724A667003798D8 /* MPAlgorithm.m in Sources */,
|
||||||
|
DA32CFE219CF1C71004F3F0E /* MPSiteQuestionEntity.m in Sources */,
|
||||||
|
DA32CFE519CF1C71004F3F0E /* MPUserEntity.m in Sources */,
|
||||||
DA5E5CF71724A667003798D8 /* MPAlgorithmV0.m in Sources */,
|
DA5E5CF71724A667003798D8 /* MPAlgorithmV0.m in Sources */,
|
||||||
DA5E5CF81724A667003798D8 /* MPAlgorithmV1.m in Sources */,
|
DA5E5CF81724A667003798D8 /* MPAlgorithmV1.m in Sources */,
|
||||||
DA5E5CF91724A667003798D8 /* MPAppDelegate_Key.m in Sources */,
|
DA5E5CF91724A667003798D8 /* MPAppDelegate_Key.m in Sources */,
|
||||||
DA5E5CFA1724A667003798D8 /* MPAppDelegate_Shared.m in Sources */,
|
DA5E5CFA1724A667003798D8 /* MPAppDelegate_Shared.m in Sources */,
|
||||||
DA5E5CFB1724A667003798D8 /* MPAppDelegate_Store.m in Sources */,
|
DA5E5CFB1724A667003798D8 /* MPAppDelegate_Store.m in Sources */,
|
||||||
DA5E5CFC1724A667003798D8 /* MPConfig.m in Sources */,
|
DA5E5CFC1724A667003798D8 /* MPConfig.m in Sources */,
|
||||||
DA5E5CFD1724A667003798D8 /* MPElementEntity.m in Sources */,
|
DA29992C19C6A89900AF7DF1 /* MasterPassword.xcdatamodeld in Sources */,
|
||||||
DA5E5CFE1724A667003798D8 /* MPElementGeneratedEntity.m in Sources */,
|
|
||||||
DA5E5CFF1724A667003798D8 /* MPElementStoredEntity.m in Sources */,
|
|
||||||
DA3B8456190FC89700246EEA /* MPFixable.m in Sources */,
|
DA3B8456190FC89700246EEA /* MPFixable.m in Sources */,
|
||||||
DA5E5D001724A667003798D8 /* MPEntities.m in Sources */,
|
DA5E5D001724A667003798D8 /* MPEntities.m in Sources */,
|
||||||
DA5E5D011724A667003798D8 /* MPKey.m in Sources */,
|
DA5E5D011724A667003798D8 /* MPKey.m in Sources */,
|
||||||
DA5E5D021724A667003798D8 /* MPUserEntity.m in Sources */,
|
DA32CFDC19CF1C70004F3F0E /* MPStoredSiteEntity.m in Sources */,
|
||||||
DA5E5D031724A667003798D8 /* MPMacAppDelegate.m in Sources */,
|
DA5E5D031724A667003798D8 /* MPMacAppDelegate.m in Sources */,
|
||||||
DA5E5D041724A667003798D8 /* MPMacConfig.m in Sources */,
|
DA5E5D041724A667003798D8 /* MPMacConfig.m in Sources */,
|
||||||
DA5E5D0C1724A667003798D8 /* main.m in Sources */,
|
DA5E5D0C1724A667003798D8 /* main.m in Sources */,
|
||||||
DA5E5D0D1724A667003798D8 /* MasterPassword.xcdatamodeld in Sources */,
|
93D39C5789EFA607CF788082 /* MPSiteModel.m in Sources */,
|
||||||
93D39C5789EFA607CF788082 /* MPElementModel.m in Sources */,
|
|
||||||
93D39F833DEC1C89B2F795AC /* MPPasswordWindowController.m in Sources */,
|
93D39F833DEC1C89B2F795AC /* MPPasswordWindowController.m in Sources */,
|
||||||
|
DA32CFD919CF1C70004F3F0E /* MPGeneratedSiteEntity.m in Sources */,
|
||||||
93D390C676DF52DA7E459F19 /* MPPasswordWindow.m in Sources */,
|
93D390C676DF52DA7E459F19 /* MPPasswordWindow.m in Sources */,
|
||||||
93D39784E725A34D1EE3FB3B /* MPInitialWindowController.m in Sources */,
|
93D39784E725A34D1EE3FB3B /* MPInitialWindowController.m in Sources */,
|
||||||
93D394C4254EEB45FB335AFB /* MPElementsTableView.m in Sources */,
|
DA32CFDF19CF1C70004F3F0E /* MPSiteEntity.m in Sources */,
|
||||||
|
93D394C4254EEB45FB335AFB /* MPSitesTableView.m in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -2260,6 +2270,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
};
|
};
|
||||||
name = "Debug-Mac";
|
name = "Debug-Mac";
|
||||||
};
|
};
|
||||||
@ -2267,6 +2278,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
};
|
};
|
||||||
name = "AdHoc-Mac";
|
name = "AdHoc-Mac";
|
||||||
};
|
};
|
||||||
@ -2274,6 +2286,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
};
|
};
|
||||||
name = "AppStore-Mac";
|
name = "AppStore-Mac";
|
||||||
};
|
};
|
||||||
@ -2281,6 +2294,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
};
|
};
|
||||||
name = "Debug-Mac";
|
name = "Debug-Mac";
|
||||||
};
|
};
|
||||||
@ -2288,6 +2302,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
};
|
};
|
||||||
name = "AdHoc-Mac";
|
name = "AdHoc-Mac";
|
||||||
};
|
};
|
||||||
@ -2295,6 +2310,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
};
|
};
|
||||||
name = "AppStore-Mac";
|
name = "AppStore-Mac";
|
||||||
};
|
};
|
||||||
@ -2315,11 +2331,13 @@
|
|||||||
CLANG_WARN_OBJC_RECEIVER_WEAK = NO;
|
CLANG_WARN_OBJC_RECEIVER_WEAK = NO;
|
||||||
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO;
|
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO;
|
||||||
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES;
|
CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES;
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES;
|
CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES;
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
GCC_OPTIMIZATION_LEVEL = 0;
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||||
@ -2388,11 +2406,13 @@
|
|||||||
CLANG_WARN_OBJC_RECEIVER_WEAK = NO;
|
CLANG_WARN_OBJC_RECEIVER_WEAK = NO;
|
||||||
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO;
|
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO;
|
||||||
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES;
|
CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES;
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES;
|
CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES;
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
@ -2510,11 +2530,13 @@
|
|||||||
CLANG_WARN_OBJC_RECEIVER_WEAK = NO;
|
CLANG_WARN_OBJC_RECEIVER_WEAK = NO;
|
||||||
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO;
|
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO;
|
||||||
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES;
|
CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES;
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES;
|
CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES;
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
@ -2595,6 +2617,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
DSTROOT = /tmp/Pearl.dst;
|
DSTROOT = /tmp/Pearl.dst;
|
||||||
GCC_PREFIX_HEADER = "../Pearl/Pearl-Prefix.pch";
|
GCC_PREFIX_HEADER = "../Pearl/Pearl-Prefix.pch";
|
||||||
LIBRARY_SEARCH_PATHS = (
|
LIBRARY_SEARCH_PATHS = (
|
||||||
@ -2614,6 +2637,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CLANG_ENABLE_OBJC_ARC = NO;
|
CLANG_ENABLE_OBJC_ARC = NO;
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
DSTROOT = /tmp/jrswizzle.dst;
|
DSTROOT = /tmp/jrswizzle.dst;
|
||||||
GCC_WARN_INHIBIT_ALL_WARNINGS = YES;
|
GCC_WARN_INHIBIT_ALL_WARNINGS = YES;
|
||||||
OTHER_LDFLAGS = "-ObjC";
|
OTHER_LDFLAGS = "-ObjC";
|
||||||
@ -2625,18 +2649,21 @@
|
|||||||
DABC6C0B175D8C85000C15D4 /* Debug-Mac */ = {
|
DABC6C0B175D8C85000C15D4 /* Debug-Mac */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
};
|
};
|
||||||
name = "Debug-Mac";
|
name = "Debug-Mac";
|
||||||
};
|
};
|
||||||
DABC6C0C175D8C85000C15D4 /* AdHoc-Mac */ = {
|
DABC6C0C175D8C85000C15D4 /* AdHoc-Mac */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
};
|
};
|
||||||
name = "AdHoc-Mac";
|
name = "AdHoc-Mac";
|
||||||
};
|
};
|
||||||
DABC6C0D175D8C85000C15D4 /* AppStore-Mac */ = {
|
DABC6C0D175D8C85000C15D4 /* AppStore-Mac */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
};
|
};
|
||||||
name = "AppStore-Mac";
|
name = "AppStore-Mac";
|
||||||
};
|
};
|
||||||
@ -2644,6 +2671,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CLANG_ENABLE_OBJC_ARC = NO;
|
CLANG_ENABLE_OBJC_ARC = NO;
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
DSTROOT = /tmp/jrswizzle.dst;
|
DSTROOT = /tmp/jrswizzle.dst;
|
||||||
GCC_WARN_INHIBIT_ALL_WARNINGS = YES;
|
GCC_WARN_INHIBIT_ALL_WARNINGS = YES;
|
||||||
OTHER_LDFLAGS = "-ObjC";
|
OTHER_LDFLAGS = "-ObjC";
|
||||||
@ -2656,6 +2684,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CLANG_ENABLE_OBJC_ARC = NO;
|
CLANG_ENABLE_OBJC_ARC = NO;
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
DSTROOT = /tmp/jrswizzle.dst;
|
DSTROOT = /tmp/jrswizzle.dst;
|
||||||
GCC_WARN_INHIBIT_ALL_WARNINGS = YES;
|
GCC_WARN_INHIBIT_ALL_WARNINGS = YES;
|
||||||
OTHER_LDFLAGS = "-ObjC";
|
OTHER_LDFLAGS = "-ObjC";
|
||||||
@ -2668,6 +2697,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
DSTROOT = /tmp/Pearl.dst;
|
DSTROOT = /tmp/Pearl.dst;
|
||||||
GCC_PREFIX_HEADER = "../Pearl/Pearl-Prefix.pch";
|
GCC_PREFIX_HEADER = "../Pearl/Pearl-Prefix.pch";
|
||||||
LIBRARY_SEARCH_PATHS = (
|
LIBRARY_SEARCH_PATHS = (
|
||||||
@ -2687,6 +2717,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
DSTROOT = /tmp/Pearl.dst;
|
DSTROOT = /tmp/Pearl.dst;
|
||||||
GCC_PREFIX_HEADER = "../Pearl/Pearl-Prefix.pch";
|
GCC_PREFIX_HEADER = "../Pearl/Pearl-Prefix.pch";
|
||||||
LIBRARY_SEARCH_PATHS = (
|
LIBRARY_SEARCH_PATHS = (
|
||||||
@ -2778,15 +2809,17 @@
|
|||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
/* Begin XCVersionGroup section */
|
/* Begin XCVersionGroup section */
|
||||||
DA5E5CC71724A667003798D8 /* MasterPassword.xcdatamodeld */ = {
|
DA29992619C6A89900AF7DF1 /* MasterPassword.xcdatamodeld */ = {
|
||||||
isa = XCVersionGroup;
|
isa = XCVersionGroup;
|
||||||
children = (
|
children = (
|
||||||
DA5E5CC81724A667003798D8 /* MasterPassword 1.xcdatamodel */,
|
DA29992719C6A89900AF7DF1 /* MasterPassword 1.xcdatamodel */,
|
||||||
DA5E5CC91724A667003798D8 /* MasterPassword 2.xcdatamodel */,
|
DA29992819C6A89900AF7DF1 /* MasterPassword 2.xcdatamodel */,
|
||||||
DA5E5CCA1724A667003798D8 /* MasterPassword 3.xcdatamodel */,
|
DA29992919C6A89900AF7DF1 /* MasterPassword 3.xcdatamodel */,
|
||||||
DA5E5CCB1724A667003798D8 /* MasterPassword 4.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;
|
path = MasterPassword.xcdatamodeld;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
versionGroupType = wrapper.xcdatamodel;
|
versionGroupType = wrapper.xcdatamodel;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "0510"
|
LastUpgradeVersion = "0600"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "0510"
|
LastUpgradeVersion = "0600"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
@ -3,6 +3,6 @@
|
|||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>_XCCurrentVersionName</key>
|
<key>_XCCurrentVersionName</key>
|
||||||
<string>MasterPassword 4.xcdatamodel</string>
|
<string>MasterPassword 6.xcdatamodel</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
@ -1,30 +1,29 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0"
|
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="6244" systemVersion="13E28" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
|
||||||
lastSavedToolsVersion="1171" systemVersion="11E53" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
|
<entity name="MPElementEntity" representedClassName="MPSiteEntity" isAbstract="YES" elementID="58EE245C-6827-4C11-BB7E-5722F2426EC2" syncable="YES">
|
||||||
<entity name="MPElementEntity" representedClassName="MPElementEntity" isAbstract="YES" syncable="YES">
|
|
||||||
<attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/>
|
<attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/>
|
||||||
<attribute name="lastUsed" attributeType="Date" syncable="YES"/>
|
<attribute name="lastUsed" attributeType="Date" syncable="YES"/>
|
||||||
<attribute name="name" attributeType="String" minValueString="1" indexed="YES" syncable="YES"/>
|
<attribute name="name" attributeType="String" minValueString="1" indexed="YES" syncable="YES"/>
|
||||||
<attribute name="type_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/>
|
<attribute name="type_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/>
|
||||||
<attribute name="uses_" attributeType="Integer 16" defaultValueString="0" syncable="YES"/>
|
<attribute name="uses_" attributeType="Integer 16" defaultValueString="0" syncable="YES"/>
|
||||||
<relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="elements"
|
<relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="elements" inverseEntity="MPUserEntity" elementID="FC8AE32E-7BE3-4FA6-8611-B7DC0DB063EF" syncable="YES"/>
|
||||||
inverseEntity="MPUserEntity" syncable="YES"/>
|
|
||||||
</entity>
|
</entity>
|
||||||
<entity name="MPElementGeneratedEntity" representedClassName="MPElementGeneratedEntity" parentEntity="MPElementEntity" syncable="YES">
|
<entity name="MPElementGeneratedEntity" representedClassName="MPGeneratedSiteEntity" parentEntity="MPElementEntity" elementID="789304EA-070D-4982-8C20-54EECFC20CB6" syncable="YES">
|
||||||
<attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/>
|
<attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/>
|
||||||
</entity>
|
</entity>
|
||||||
<entity name="MPElementStoredEntity" representedClassName="MPElementStoredEntity" parentEntity="MPElementEntity" syncable="YES">
|
<entity name="MPElementStoredEntity" representedClassName="MPStoredSiteEntity" parentEntity="MPElementEntity" elementID="BEEF1688-0CCD-4699-A86A-4D860FE2CEB8" syncable="YES">
|
||||||
<attribute name="contentObject" optional="YES" attributeType="Transformable" storedInTruthFile="YES" syncable="YES"/>
|
<attribute name="contentObject" optional="YES" attributeType="Transformable" storedInTruthFile="YES" syncable="YES"/>
|
||||||
</entity>
|
</entity>
|
||||||
<entity name="MPUserEntity" representedClassName="MPUserEntity" syncable="YES">
|
<entity name="MPUserEntity" representedClassName="MPUserEntity" elementID="D18D6772-040E-4CFE-8F32-A34B08E9E9BC" syncable="YES">
|
||||||
<attribute name="avatar_" attributeType="Integer 16" defaultValueString="0" syncable="YES"/>
|
<attribute name="avatar_" attributeType="Integer 16" defaultValueString="0" syncable="YES"/>
|
||||||
<attribute name="defaultType_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/>
|
<attribute name="defaultType_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/>
|
||||||
<attribute name="keyID" optional="YES" attributeType="Binary" syncable="YES"/>
|
<attribute name="keyID" optional="YES" attributeType="Binary" syncable="YES"/>
|
||||||
<attribute name="lastUsed" optional="YES" attributeType="Date" syncable="YES"/>
|
<attribute name="lastUsed" optional="YES" attributeType="Date" syncable="YES"/>
|
||||||
<attribute name="name" attributeType="String" syncable="YES"/>
|
<attribute name="name" attributeType="String" syncable="YES"/>
|
||||||
<attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO"/>
|
<attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO">
|
||||||
<relationship name="elements" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPElementEntity"
|
<userInfo/>
|
||||||
inverseName="user" inverseEntity="MPElementEntity" syncable="YES"/>
|
</attribute>
|
||||||
|
<relationship name="elements" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPElementEntity" inverseName="user" inverseEntity="MPElementEntity" syncable="YES"/>
|
||||||
</entity>
|
</entity>
|
||||||
<elements>
|
<elements>
|
||||||
<element name="MPElementEntity" positionX="160" positionY="192" width="128" height="135"/>
|
<element name="MPElementEntity" positionX="160" positionY="192" width="128" height="135"/>
|
||||||
@ -32,4 +31,4 @@
|
|||||||
<element name="MPElementStoredEntity" positionX="160" positionY="192" width="128" height="60"/>
|
<element name="MPElementStoredEntity" positionX="160" positionY="192" width="128" height="60"/>
|
||||||
<element name="MPUserEntity" positionX="160" positionY="192" width="128" height="150"/>
|
<element name="MPUserEntity" positionX="160" positionY="192" width="128" height="150"/>
|
||||||
</elements>
|
</elements>
|
||||||
</model>
|
</model>
|
@ -1,7 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0"
|
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="6244" systemVersion="13E28" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
|
||||||
lastSavedToolsVersion="2057" systemVersion="12C60" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
|
<entity name="MPElementEntity" representedClassName="MPSiteEntity" isAbstract="YES" elementID="58EE245C-6827-4C11-BB7E-5722F2426EC2" syncable="YES">
|
||||||
<entity name="MPElementEntity" representedClassName="MPElementEntity" isAbstract="YES" syncable="YES">
|
|
||||||
<attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/>
|
<attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/>
|
||||||
<attribute name="lastUsed" attributeType="Date" indexed="YES" syncable="YES"/>
|
<attribute name="lastUsed" attributeType="Date" indexed="YES" syncable="YES"/>
|
||||||
<attribute name="name" attributeType="String" minValueString="1" indexed="YES" syncable="YES"/>
|
<attribute name="name" attributeType="String" minValueString="1" indexed="YES" syncable="YES"/>
|
||||||
@ -12,16 +11,15 @@
|
|||||||
<attribute name="userName" optional="YES" attributeType="String" syncable="YES"/>
|
<attribute name="userName" optional="YES" attributeType="String" syncable="YES"/>
|
||||||
<attribute name="uses_" attributeType="Integer 16" defaultValueString="0" indexed="YES" syncable="YES"/>
|
<attribute name="uses_" attributeType="Integer 16" defaultValueString="0" indexed="YES" syncable="YES"/>
|
||||||
<attribute name="version_" attributeType="Integer 16" minValueString="0" defaultValueString="0" syncable="YES"/>
|
<attribute name="version_" attributeType="Integer 16" minValueString="0" defaultValueString="0" syncable="YES"/>
|
||||||
<relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="elements"
|
<relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="elements" inverseEntity="MPUserEntity" elementID="FC8AE32E-7BE3-4FA6-8611-B7DC0DB063EF" syncable="YES"/>
|
||||||
inverseEntity="MPUserEntity" syncable="YES"/>
|
|
||||||
</entity>
|
</entity>
|
||||||
<entity name="MPElementGeneratedEntity" representedClassName="MPElementGeneratedEntity" parentEntity="MPElementEntity" syncable="YES">
|
<entity name="MPElementGeneratedEntity" representedClassName="MPGeneratedSiteEntity" parentEntity="MPElementEntity" elementID="789304EA-070D-4982-8C20-54EECFC20CB6" syncable="YES">
|
||||||
<attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/>
|
<attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/>
|
||||||
</entity>
|
</entity>
|
||||||
<entity name="MPElementStoredEntity" representedClassName="MPElementStoredEntity" parentEntity="MPElementEntity" syncable="YES">
|
<entity name="MPElementStoredEntity" representedClassName="MPStoredSiteEntity" parentEntity="MPElementEntity" elementID="BEEF1688-0CCD-4699-A86A-4D860FE2CEB8" syncable="YES">
|
||||||
<attribute name="contentObject" optional="YES" attributeType="Transformable" storedInTruthFile="YES" syncable="YES"/>
|
<attribute name="contentObject" optional="YES" attributeType="Transformable" storedInTruthFile="YES" syncable="YES"/>
|
||||||
</entity>
|
</entity>
|
||||||
<entity name="MPUserEntity" representedClassName="MPUserEntity" syncable="YES">
|
<entity name="MPUserEntity" representedClassName="MPUserEntity" elementID="D18D6772-040E-4CFE-8F32-A34B08E9E9BC" syncable="YES">
|
||||||
<attribute name="avatar_" attributeType="Integer 16" defaultValueString="0" syncable="YES"/>
|
<attribute name="avatar_" attributeType="Integer 16" defaultValueString="0" syncable="YES"/>
|
||||||
<attribute name="defaultType_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/>
|
<attribute name="defaultType_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/>
|
||||||
<attribute name="keyID" optional="YES" attributeType="Binary" syncable="YES"/>
|
<attribute name="keyID" optional="YES" attributeType="Binary" syncable="YES"/>
|
||||||
@ -31,8 +29,7 @@
|
|||||||
<attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO">
|
<attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO">
|
||||||
<userInfo/>
|
<userInfo/>
|
||||||
</attribute>
|
</attribute>
|
||||||
<relationship name="elements" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPElementEntity"
|
<relationship name="elements" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPElementEntity" inverseName="user" inverseEntity="MPElementEntity" syncable="YES"/>
|
||||||
inverseName="user" inverseEntity="MPElementEntity" syncable="YES"/>
|
|
||||||
</entity>
|
</entity>
|
||||||
<elements>
|
<elements>
|
||||||
<element name="MPElementEntity" positionX="0" positionY="0" width="128" height="180"/>
|
<element name="MPElementEntity" positionX="0" positionY="0" width="128" height="180"/>
|
||||||
@ -40,4 +37,4 @@
|
|||||||
<element name="MPElementStoredEntity" positionX="216" positionY="144" width="128" height="60"/>
|
<element name="MPElementStoredEntity" positionX="216" positionY="144" width="128" height="60"/>
|
||||||
<element name="MPUserEntity" positionX="-216" positionY="0" width="128" height="165"/>
|
<element name="MPUserEntity" positionX="-216" positionY="0" width="128" height="165"/>
|
||||||
</elements>
|
</elements>
|
||||||
</model>
|
</model>
|
@ -1,7 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0"
|
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="6244" systemVersion="13E28" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
|
||||||
lastSavedToolsVersion="1810" systemVersion="12B19" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
|
<entity name="MPElementEntity" representedClassName="MPSiteEntity" isAbstract="YES" elementID="58EE245C-6827-4C11-BB7E-5722F2426EC2" syncable="YES">
|
||||||
<entity name="MPElementEntity" representedClassName="MPElementEntity" isAbstract="YES" syncable="YES">
|
|
||||||
<attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/>
|
<attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/>
|
||||||
<attribute name="lastUsed" attributeType="Date" indexed="YES" syncable="YES"/>
|
<attribute name="lastUsed" attributeType="Date" indexed="YES" syncable="YES"/>
|
||||||
<attribute name="loginName" optional="YES" attributeType="String" elementID="A1B9F981-D33C-4BFE-9F94-C9D3E1F78E51" syncable="YES"/>
|
<attribute name="loginName" optional="YES" attributeType="String" elementID="A1B9F981-D33C-4BFE-9F94-C9D3E1F78E51" syncable="YES"/>
|
||||||
@ -12,13 +11,12 @@
|
|||||||
<attribute name="type_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/>
|
<attribute name="type_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/>
|
||||||
<attribute name="uses_" attributeType="Integer 16" defaultValueString="0" indexed="YES" syncable="YES"/>
|
<attribute name="uses_" attributeType="Integer 16" defaultValueString="0" indexed="YES" syncable="YES"/>
|
||||||
<attribute name="version_" attributeType="Integer 16" minValueString="0" defaultValueString="0" syncable="YES"/>
|
<attribute name="version_" attributeType="Integer 16" minValueString="0" defaultValueString="0" syncable="YES"/>
|
||||||
<relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="elements"
|
<relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="elements" inverseEntity="MPUserEntity" elementID="FC8AE32E-7BE3-4FA6-8611-B7DC0DB063EF" syncable="YES"/>
|
||||||
inverseEntity="MPUserEntity" syncable="YES"/>
|
|
||||||
</entity>
|
</entity>
|
||||||
<entity name="MPElementGeneratedEntity" representedClassName="MPElementGeneratedEntity" parentEntity="MPElementEntity" syncable="YES">
|
<entity name="MPElementGeneratedEntity" representedClassName="MPGeneratedSiteEntity" parentEntity="MPElementEntity" elementID="789304EA-070D-4982-8C20-54EECFC20CB6" syncable="YES">
|
||||||
<attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/>
|
<attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/>
|
||||||
</entity>
|
</entity>
|
||||||
<entity name="MPElementStoredEntity" representedClassName="MPElementStoredEntity" parentEntity="MPElementEntity" syncable="YES">
|
<entity name="MPElementStoredEntity" representedClassName="MPStoredSiteEntity" parentEntity="MPElementEntity" elementID="BEEF1688-0CCD-4699-A86A-4D860FE2CEB8" syncable="YES">
|
||||||
<attribute name="contentObject" optional="YES" attributeType="Transformable" storedInTruthFile="YES" syncable="YES"/>
|
<attribute name="contentObject" optional="YES" attributeType="Transformable" storedInTruthFile="YES" syncable="YES"/>
|
||||||
</entity>
|
</entity>
|
||||||
<entity name="MPUserEntity" representedClassName="MPUserEntity" syncable="YES">
|
<entity name="MPUserEntity" representedClassName="MPUserEntity" syncable="YES">
|
||||||
@ -31,8 +29,7 @@
|
|||||||
<attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO">
|
<attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO">
|
||||||
<userInfo/>
|
<userInfo/>
|
||||||
</attribute>
|
</attribute>
|
||||||
<relationship name="elements" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPElementEntity"
|
<relationship name="elements" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPElementEntity" inverseName="user" inverseEntity="MPElementEntity" elementID="D18D6772-040E-4CFE-8F32-A34B08E9E9BC" syncable="YES"/>
|
||||||
inverseName="user" inverseEntity="MPElementEntity" syncable="YES"/>
|
|
||||||
</entity>
|
</entity>
|
||||||
<elements>
|
<elements>
|
||||||
<element name="MPElementEntity" positionX="-0" positionY="-286" width="128" height="178"/>
|
<element name="MPElementEntity" positionX="-0" positionY="-286" width="128" height="178"/>
|
||||||
@ -40,4 +37,4 @@
|
|||||||
<element name="MPElementStoredEntity" positionX="214" positionY="-171" width="128" height="58"/>
|
<element name="MPElementStoredEntity" positionX="214" positionY="-171" width="128" height="58"/>
|
||||||
<element name="MPUserEntity" positionX="-218" positionY="-288" width="128" height="163"/>
|
<element name="MPUserEntity" positionX="-218" positionY="-288" width="128" height="163"/>
|
||||||
</elements>
|
</elements>
|
||||||
</model>
|
</model>
|
@ -1,7 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0"
|
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="6244" systemVersion="13E28" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
|
||||||
lastSavedToolsVersion="2057" systemVersion="12C60" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
|
<entity name="MPElementEntity" representedClassName="MPSiteEntity" isAbstract="YES" elementID="58EE245C-6827-4C11-BB7E-5722F2426EC2" syncable="YES">
|
||||||
<entity name="MPElementEntity" representedClassName="MPElementEntity" isAbstract="YES" syncable="YES">
|
|
||||||
<attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/>
|
<attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/>
|
||||||
<attribute name="lastUsed" attributeType="Date" indexed="YES" syncable="YES"/>
|
<attribute name="lastUsed" attributeType="Date" indexed="YES" syncable="YES"/>
|
||||||
<attribute name="loginName" optional="YES" attributeType="String" elementID="A1B9F981-D33C-4BFE-9F94-C9D3E1F78E51" syncable="YES"/>
|
<attribute name="loginName" optional="YES" attributeType="String" elementID="A1B9F981-D33C-4BFE-9F94-C9D3E1F78E51" syncable="YES"/>
|
||||||
@ -12,13 +11,12 @@
|
|||||||
<attribute name="type_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/>
|
<attribute name="type_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/>
|
||||||
<attribute name="uses_" attributeType="Integer 16" defaultValueString="0" indexed="YES" syncable="YES"/>
|
<attribute name="uses_" attributeType="Integer 16" defaultValueString="0" indexed="YES" syncable="YES"/>
|
||||||
<attribute name="version_" attributeType="Integer 16" minValueString="0" defaultValueString="0" syncable="YES"/>
|
<attribute name="version_" attributeType="Integer 16" minValueString="0" defaultValueString="0" syncable="YES"/>
|
||||||
<relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="elements"
|
<relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="elements" inverseEntity="MPUserEntity" elementID="FC8AE32E-7BE3-4FA6-8611-B7DC0DB063EF" syncable="YES"/>
|
||||||
inverseEntity="MPUserEntity" syncable="YES"/>
|
|
||||||
</entity>
|
</entity>
|
||||||
<entity name="MPElementGeneratedEntity" representedClassName="MPElementGeneratedEntity" parentEntity="MPElementEntity" syncable="YES">
|
<entity name="MPElementGeneratedEntity" representedClassName="MPGeneratedSiteEntity" parentEntity="MPElementEntity" elementID="789304EA-070D-4982-8C20-54EECFC20CB6" syncable="YES">
|
||||||
<attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/>
|
<attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/>
|
||||||
</entity>
|
</entity>
|
||||||
<entity name="MPElementStoredEntity" representedClassName="MPElementStoredEntity" parentEntity="MPElementEntity" syncable="YES">
|
<entity name="MPElementStoredEntity" representedClassName="MPStoredSiteEntity" parentEntity="MPElementEntity" elementID="BEEF1688-0CCD-4699-A86A-4D860FE2CEB8" syncable="YES">
|
||||||
<attribute name="contentObject" optional="YES" attributeType="Transformable" storedInTruthFile="YES" syncable="YES"/>
|
<attribute name="contentObject" optional="YES" attributeType="Transformable" storedInTruthFile="YES" syncable="YES"/>
|
||||||
</entity>
|
</entity>
|
||||||
<entity name="MPUserEntity" representedClassName="MPUserEntity" syncable="YES">
|
<entity name="MPUserEntity" representedClassName="MPUserEntity" syncable="YES">
|
||||||
@ -30,8 +28,7 @@
|
|||||||
<attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO">
|
<attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO">
|
||||||
<userInfo/>
|
<userInfo/>
|
||||||
</attribute>
|
</attribute>
|
||||||
<relationship name="elements" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPElementEntity"
|
<relationship name="elements" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPElementEntity" inverseName="user" inverseEntity="MPElementEntity" elementID="D18D6772-040E-4CFE-8F32-A34B08E9E9BC" syncable="YES"/>
|
||||||
inverseName="user" inverseEntity="MPElementEntity" syncable="YES"/>
|
|
||||||
</entity>
|
</entity>
|
||||||
<elements>
|
<elements>
|
||||||
<element name="MPElementEntity" positionX="-0" positionY="-286" width="128" height="178"/>
|
<element name="MPElementEntity" positionX="-0" positionY="-286" width="128" height="178"/>
|
||||||
@ -39,4 +36,4 @@
|
|||||||
<element name="MPElementStoredEntity" positionX="214" positionY="-171" width="128" height="58"/>
|
<element name="MPElementStoredEntity" positionX="214" positionY="-171" width="128" height="58"/>
|
||||||
<element name="MPUserEntity" positionX="-218" positionY="-288" width="128" height="148"/>
|
<element name="MPUserEntity" positionX="-218" positionY="-288" width="128" height="148"/>
|
||||||
</elements>
|
</elements>
|
||||||
</model>
|
</model>
|
@ -0,0 +1,40 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="6244" systemVersion="13E28" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
|
||||||
|
<entity name="MPElementEntity" representedClassName="MPElementEntity" isAbstract="YES" elementID="58EE245C-6827-4C11-BB7E-5722F2426EC2" syncable="YES">
|
||||||
|
<attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/>
|
||||||
|
<attribute name="lastUsed" attributeType="Date" indexed="YES" syncable="YES"/>
|
||||||
|
<attribute name="loginGenerated_" attributeType="Boolean" defaultValueString="NO" syncable="YES"/>
|
||||||
|
<attribute name="loginName" optional="YES" attributeType="String" elementID="A1B9F981-D33C-4BFE-9F94-C9D3E1F78E51" syncable="YES"/>
|
||||||
|
<attribute name="name" attributeType="String" minValueString="1" indexed="YES" syncable="YES"/>
|
||||||
|
<attribute name="requiresExplicitMigration_" attributeType="Boolean" defaultValueString="NO">
|
||||||
|
<userInfo/>
|
||||||
|
</attribute>
|
||||||
|
<attribute name="type_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/>
|
||||||
|
<attribute name="uses_" attributeType="Integer 16" defaultValueString="0" indexed="YES" syncable="YES"/>
|
||||||
|
<attribute name="version_" attributeType="Integer 16" minValueString="0" defaultValueString="0" syncable="YES"/>
|
||||||
|
<relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="elements" inverseEntity="MPUserEntity" elementID="FC8AE32E-7BE3-4FA6-8611-B7DC0DB063EF" syncable="YES"/>
|
||||||
|
</entity>
|
||||||
|
<entity name="MPElementGeneratedEntity" representedClassName="MPElementGeneratedEntity" parentEntity="MPElementEntity" elementID="789304EA-070D-4982-8C20-54EECFC20CB6" syncable="YES">
|
||||||
|
<attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/>
|
||||||
|
</entity>
|
||||||
|
<entity name="MPElementStoredEntity" representedClassName="MPElementStoredEntity" parentEntity="MPElementEntity" elementID="BEEF1688-0CCD-4699-A86A-4D860FE2CEB8" syncable="YES">
|
||||||
|
<attribute name="contentObject" optional="YES" attributeType="Transformable" storedInTruthFile="YES" syncable="YES"/>
|
||||||
|
</entity>
|
||||||
|
<entity name="MPUserEntity" representedClassName="MPUserEntity" syncable="YES">
|
||||||
|
<attribute name="avatar_" attributeType="Integer 16" defaultValueString="0" syncable="YES"/>
|
||||||
|
<attribute name="defaultType_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/>
|
||||||
|
<attribute name="keyID" optional="YES" attributeType="Binary" syncable="YES"/>
|
||||||
|
<attribute name="lastUsed" optional="YES" attributeType="Date" syncable="YES"/>
|
||||||
|
<attribute name="name" attributeType="String" syncable="YES"/>
|
||||||
|
<attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO">
|
||||||
|
<userInfo/>
|
||||||
|
</attribute>
|
||||||
|
<relationship name="elements" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPElementEntity" inverseName="user" inverseEntity="MPElementEntity" elementID="D18D6772-040E-4CFE-8F32-A34B08E9E9BC" syncable="YES"/>
|
||||||
|
</entity>
|
||||||
|
<elements>
|
||||||
|
<element name="MPElementEntity" positionX="-0" positionY="-286" width="128" height="193"/>
|
||||||
|
<element name="MPElementGeneratedEntity" positionX="216" positionY="-288" width="128" height="58"/>
|
||||||
|
<element name="MPElementStoredEntity" positionX="214" positionY="-171" width="128" height="58"/>
|
||||||
|
<element name="MPUserEntity" positionX="-218" positionY="-288" width="128" height="148"/>
|
||||||
|
</elements>
|
||||||
|
</model>
|
@ -0,0 +1,45 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="6244" systemVersion="13E28" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
|
||||||
|
<entity name="MPGeneratedSiteEntity" representedClassName="MPGeneratedSiteEntity" parentEntity="MPSiteEntity" elementID="789304EA-070D-4982-8C20-54EECFC20CB6" syncable="YES">
|
||||||
|
<attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/>
|
||||||
|
</entity>
|
||||||
|
<entity name="MPSiteEntity" representedClassName="MPSiteEntity" isAbstract="YES" elementID="58EE245C-6827-4C11-BB7E-5722F2426EC2" syncable="YES">
|
||||||
|
<attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/>
|
||||||
|
<attribute name="lastUsed" attributeType="Date" indexed="YES" syncable="YES"/>
|
||||||
|
<attribute name="loginGenerated_" attributeType="Boolean" defaultValueString="NO" syncable="YES"/>
|
||||||
|
<attribute name="loginName" optional="YES" attributeType="String" elementID="A1B9F981-D33C-4BFE-9F94-C9D3E1F78E51" syncable="YES"/>
|
||||||
|
<attribute name="name" attributeType="String" minValueString="1" indexed="YES" syncable="YES"/>
|
||||||
|
<attribute name="requiresExplicitMigration_" attributeType="Boolean" defaultValueString="NO">
|
||||||
|
<userInfo/>
|
||||||
|
</attribute>
|
||||||
|
<attribute name="type_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/>
|
||||||
|
<attribute name="uses_" attributeType="Integer 16" defaultValueString="0" indexed="YES" syncable="YES"/>
|
||||||
|
<attribute name="version_" attributeType="Integer 16" minValueString="0" defaultValueString="0" syncable="YES"/>
|
||||||
|
<relationship name="questions" optional="YES" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="MPSiteQuestionEntity" syncable="YES"/>
|
||||||
|
<relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="sites" inverseEntity="MPUserEntity" elementID="FC8AE32E-7BE3-4FA6-8611-B7DC0DB063EF" syncable="YES"/>
|
||||||
|
</entity>
|
||||||
|
<entity name="MPSiteQuestionEntity" representedClassName="MPSiteQuestionEntity" syncable="YES">
|
||||||
|
<attribute name="keyword" attributeType="String" syncable="YES"/>
|
||||||
|
</entity>
|
||||||
|
<entity name="MPStoredSiteEntity" representedClassName="MPStoredSiteEntity" parentEntity="MPSiteEntity" elementID="BEEF1688-0CCD-4699-A86A-4D860FE2CEB8" syncable="YES">
|
||||||
|
<attribute name="contentObject" optional="YES" attributeType="Transformable" storedInTruthFile="YES" syncable="YES"/>
|
||||||
|
</entity>
|
||||||
|
<entity name="MPUserEntity" representedClassName="MPUserEntity" syncable="YES">
|
||||||
|
<attribute name="avatar_" attributeType="Integer 16" defaultValueString="0" syncable="YES"/>
|
||||||
|
<attribute name="defaultType_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/>
|
||||||
|
<attribute name="keyID" optional="YES" attributeType="Binary" syncable="YES"/>
|
||||||
|
<attribute name="lastUsed" optional="YES" attributeType="Date" syncable="YES"/>
|
||||||
|
<attribute name="name" attributeType="String" syncable="YES"/>
|
||||||
|
<attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO">
|
||||||
|
<userInfo/>
|
||||||
|
</attribute>
|
||||||
|
<relationship name="sites" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPSiteEntity" inverseName="user" inverseEntity="MPSiteEntity" elementID="D18D6772-040E-4CFE-8F32-A34B08E9E9BC" syncable="YES"/>
|
||||||
|
</entity>
|
||||||
|
<elements>
|
||||||
|
<element name="MPGeneratedSiteEntity" positionX="216" positionY="-288" width="128" height="58"/>
|
||||||
|
<element name="MPSiteEntity" positionX="-0" positionY="-286" width="128" height="208"/>
|
||||||
|
<element name="MPSiteQuestionEntity" positionX="-2" positionY="-9" width="128" height="58"/>
|
||||||
|
<element name="MPStoredSiteEntity" positionX="214" positionY="-171" width="128" height="58"/>
|
||||||
|
<element name="MPUserEntity" positionX="-218" positionY="-288" width="128" height="148"/>
|
||||||
|
</elements>
|
||||||
|
</model>
|
46
MasterPassword/ObjC/iOS/MPAnswersViewController.h
Normal file
46
MasterPassword/ObjC/iOS/MPAnswersViewController.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
//
|
||||||
|
// MPPreferencesViewController.h
|
||||||
|
// MasterPassword-iOS
|
||||||
|
//
|
||||||
|
// Created by Maarten Billemont on 04/06/12.
|
||||||
|
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
#import "MPTypeViewController.h"
|
||||||
|
#import "MPSiteQuestionEntity.h"
|
||||||
|
|
||||||
|
@interface MPAnswersViewController : UIViewController
|
||||||
|
|
||||||
|
@property (nonatomic) IBOutlet UITableView *tableView;
|
||||||
|
|
||||||
|
- (void)setSite:(MPSiteEntity *)site;
|
||||||
|
- (MPSiteEntity *)siteInContext:(NSManagedObjectContext *)context;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface MPGlobalAnswersCell : UITableViewCell
|
||||||
|
|
||||||
|
@property (nonatomic) IBOutlet UILabel *titleLabel;
|
||||||
|
@property (nonatomic) IBOutlet UITextField *answerField;
|
||||||
|
|
||||||
|
- (void)setSite:(MPSiteEntity *)site;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface MPSendAnswersCell : UITableViewCell
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface MPMultipleAnswersCell : UITableViewCell <UITextFieldDelegate>
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface MPAnswersQuestionCell : UITableViewCell
|
||||||
|
|
||||||
|
@property(nonatomic) IBOutlet UITextField *questionField;
|
||||||
|
@property(nonatomic) IBOutlet UITextField *answerField;
|
||||||
|
|
||||||
|
- (void)setQuestion:(MPSiteQuestionEntity *)question forSite:(MPSiteEntity *)site;
|
||||||
|
|
||||||
|
@end
|
301
MasterPassword/ObjC/iOS/MPAnswersViewController.m
Normal file
301
MasterPassword/ObjC/iOS/MPAnswersViewController.m
Normal file
@ -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
|
@ -19,23 +19,6 @@
|
|||||||
#import "MPAppSettingsViewController.h"
|
#import "MPAppSettingsViewController.h"
|
||||||
#import "UIColor+Expanded.h"
|
#import "UIColor+Expanded.h"
|
||||||
|
|
||||||
@interface MPTableView:UITableView
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation MPTableView
|
|
||||||
|
|
||||||
- (void)layoutSubviews {
|
|
||||||
|
|
||||||
[super layoutSubviews];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setContentInset:(UIEdgeInsets)contentInset {
|
|
||||||
|
|
||||||
[super setContentInset:contentInset];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation MPAppSettingsViewController {
|
@implementation MPAppSettingsViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||||
*
|
*
|
||||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
*
|
*
|
||||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//
|
//
|
||||||
// MPAvatarCell.h
|
// MPAvatarCell.h
|
||||||
@ -57,11 +57,14 @@ const long MPAvatarAdd = 10000;
|
|||||||
self.avatarImageView.layer.masksToBounds = NO;
|
self.avatarImageView.layer.masksToBounds = NO;
|
||||||
self.avatarImageView.backgroundColor = [UIColor clearColor];
|
self.avatarImageView.backgroundColor = [UIColor clearColor];
|
||||||
|
|
||||||
[self observeKeyPath:@"selected" withBlock:^(id from, id to, NSKeyValueChange cause, id _self) {
|
[self observeKeyPath:@"bounds" withBlock:^(id from, id to, NSKeyValueChange cause, MPAvatarCell *_self) {
|
||||||
[_self updateAnimated:YES];
|
_self.contentView.frame = _self.bounds;
|
||||||
}];
|
}];
|
||||||
[self observeKeyPath:@"highlighted" withBlock:^(id from, id to, NSKeyValueChange cause, id _self) {
|
[self observeKeyPath:@"selected" withBlock:^(id from, id to, NSKeyValueChange cause, MPAvatarCell *_self) {
|
||||||
[_self updateAnimated:YES];
|
[_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"];
|
CABasicAnimation *toShadowOpacityAnimation = [CABasicAnimation animationWithKeyPath:@"shadowOpacity"];
|
||||||
@ -88,9 +91,9 @@ const long MPAvatarAdd = 10000;
|
|||||||
[super prepareForReuse];
|
[super prepareForReuse];
|
||||||
|
|
||||||
_newUser = NO;
|
_newUser = NO;
|
||||||
[self setVisibility:0 animated:NO];
|
_visibility = 0;
|
||||||
[self setMode:MPAvatarModeLowered animated:NO];
|
_mode = MPAvatarModeLowered;
|
||||||
[self setSpinnerActive:NO animated:NO];
|
_spinnerActive = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
@ -130,6 +133,8 @@ const long MPAvatarAdd = 10000;
|
|||||||
|
|
||||||
- (void)setVisibility:(CGFloat)visibility animated:(BOOL)animated {
|
- (void)setVisibility:(CGFloat)visibility animated:(BOOL)animated {
|
||||||
|
|
||||||
|
if (visibility == _visibility)
|
||||||
|
return;
|
||||||
_visibility = visibility;
|
_visibility = visibility;
|
||||||
|
|
||||||
[self updateAnimated:animated];
|
[self updateAnimated:animated];
|
||||||
@ -149,6 +154,8 @@ const long MPAvatarAdd = 10000;
|
|||||||
|
|
||||||
- (void)setMode:(MPAvatarMode)mode animated:(BOOL)animated {
|
- (void)setMode:(MPAvatarMode)mode animated:(BOOL)animated {
|
||||||
|
|
||||||
|
if (mode == _mode)
|
||||||
|
return;
|
||||||
_mode = mode;
|
_mode = mode;
|
||||||
|
|
||||||
[self updateAnimated:animated];
|
[self updateAnimated:animated];
|
||||||
@ -189,92 +196,95 @@ const long MPAvatarAdd = 10000;
|
|||||||
|
|
||||||
- (void)updateAnimated:(BOOL)animated {
|
- (void)updateAnimated:(BOOL)animated {
|
||||||
|
|
||||||
|
[self.contentView layoutIfNeeded];
|
||||||
[UIView animateWithDuration:animated? 0.2f: 0 animations:^{
|
[UIView animateWithDuration:animated? 0.2f: 0 animations:^{
|
||||||
self.avatarImageView.transform = CGAffineTransformIdentity;
|
self.avatarImageView.transform = CGAffineTransformIdentity;
|
||||||
}];
|
}];
|
||||||
[UIView animateWithDuration:animated? 0.3f: 0 delay:0 options:UIViewAnimationOptionOverrideInheritedDuration animations:^{
|
[UIView animateWithDuration:animated? 0.5f: 0 delay:0
|
||||||
self.alpha = 1;
|
options:UIViewAnimationOptionOverrideInheritedDuration | UIViewAnimationOptionBeginFromCurrentState
|
||||||
|
animations:^{
|
||||||
|
self.alpha = 1;
|
||||||
|
|
||||||
if (self.newUser) {
|
if (self.newUser) {
|
||||||
if (self.mode == MPAvatarModeLowered)
|
if (self.mode == MPAvatarModeLowered)
|
||||||
self.avatar = MPAvatarAdd;
|
self.avatar = MPAvatarAdd;
|
||||||
else if (self.avatar == MPAvatarAdd)
|
else if (self.avatar == MPAvatarAdd)
|
||||||
self.avatar = arc4random() % MPAvatarCount;
|
self.avatar = arc4random() % MPAvatarCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (self.mode) {
|
switch (self.mode) {
|
||||||
case MPAvatarModeLowered: {
|
case MPAvatarModeLowered: {
|
||||||
[self.avatarSizeConstraint updateConstant:self.avatarImageView.image.size.height];
|
[self.avatarSizeConstraint updateConstant:self.avatarImageView.image.size.height];
|
||||||
[self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultLow];
|
[self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultLow];
|
||||||
[self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow];
|
[self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow];
|
||||||
[self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultLow];
|
[self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultLow];
|
||||||
self.nameContainer.alpha = self.visibility;
|
self.nameContainer.alpha = self.visibility;
|
||||||
self.nameContainer.backgroundColor = [UIColor clearColor];
|
self.nameContainer.backgroundColor = [UIColor clearColor];
|
||||||
self.avatarImageView.alpha = self.visibility / 0.7f + 0.3f;
|
self.avatarImageView.alpha = self.visibility / 0.7f + 0.3f;
|
||||||
self.avatarImageView.layer.shadowRadius = 15 * self.visibility * self.visibility;
|
self.avatarImageView.layer.shadowRadius = 15 * self.visibility * self.visibility;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MPAvatarModeRaisedButInactive: {
|
case MPAvatarModeRaisedButInactive: {
|
||||||
[self.avatarSizeConstraint updateConstant:self.avatarImageView.image.size.height];
|
[self.avatarSizeConstraint updateConstant:self.avatarImageView.image.size.height];
|
||||||
[self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultHigh];
|
[self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultHigh];
|
||||||
[self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow];
|
[self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow];
|
||||||
[self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultLow];
|
[self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultLow];
|
||||||
self.nameContainer.alpha = self.visibility;
|
self.nameContainer.alpha = self.visibility;
|
||||||
self.nameContainer.backgroundColor = [UIColor clearColor];
|
self.nameContainer.backgroundColor = [UIColor clearColor];
|
||||||
self.avatarImageView.alpha = 0;
|
self.avatarImageView.alpha = 0;
|
||||||
self.avatarImageView.layer.shadowRadius = 15 * self.visibility * self.visibility;
|
self.avatarImageView.layer.shadowRadius = 15 * self.visibility * self.visibility;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MPAvatarModeRaisedAndActive: {
|
case MPAvatarModeRaisedAndActive: {
|
||||||
[self.avatarSizeConstraint updateConstant:self.avatarImageView.image.size.height];
|
[self.avatarSizeConstraint updateConstant:self.avatarImageView.image.size.height];
|
||||||
[self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultHigh];
|
[self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultHigh];
|
||||||
[self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow];
|
[self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow];
|
||||||
[self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultHigh];
|
[self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultHigh];
|
||||||
self.nameContainer.alpha = self.visibility;
|
self.nameContainer.alpha = self.visibility;
|
||||||
self.nameContainer.backgroundColor = [UIColor blackColor];
|
self.nameContainer.backgroundColor = [UIColor blackColor];
|
||||||
self.avatarImageView.alpha = 1;
|
self.avatarImageView.alpha = 1;
|
||||||
self.avatarImageView.layer.shadowRadius = 15 * self.visibility * self.visibility;
|
self.avatarImageView.layer.shadowRadius = 15 * self.visibility * self.visibility;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MPAvatarModeRaisedAndHidden: {
|
case MPAvatarModeRaisedAndHidden: {
|
||||||
[self.avatarSizeConstraint updateConstant:self.avatarImageView.image.size.height];
|
[self.avatarSizeConstraint updateConstant:self.avatarImageView.image.size.height];
|
||||||
[self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultHigh];
|
[self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultHigh];
|
||||||
[self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow];
|
[self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow];
|
||||||
[self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultHigh];
|
[self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultHigh];
|
||||||
self.nameContainer.alpha = 0;
|
self.nameContainer.alpha = 0;
|
||||||
self.nameContainer.backgroundColor = [UIColor blackColor];
|
self.nameContainer.backgroundColor = [UIColor blackColor];
|
||||||
self.avatarImageView.alpha = 0;
|
self.avatarImageView.alpha = 0;
|
||||||
self.avatarImageView.layer.shadowRadius = 15 * self.visibility * self.visibility;
|
self.avatarImageView.layer.shadowRadius = 15 * self.visibility * self.visibility;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MPAvatarModeRaisedAndMinimized: {
|
case MPAvatarModeRaisedAndMinimized: {
|
||||||
[self.avatarSizeConstraint updateConstant:36];
|
[self.avatarSizeConstraint updateConstant:36];
|
||||||
[self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultLow];
|
[self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultLow];
|
||||||
[self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultHigh];
|
[self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultHigh];
|
||||||
[self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultHigh];
|
[self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultHigh];
|
||||||
self.nameContainer.alpha = 0;
|
self.nameContainer.alpha = 0;
|
||||||
self.nameContainer.backgroundColor = [UIColor blackColor];
|
self.nameContainer.backgroundColor = [UIColor blackColor];
|
||||||
self.avatarImageView.alpha = 1;
|
self.avatarImageView.alpha = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Avatar minimized.
|
// Avatar minimized.
|
||||||
if (self.mode == MPAvatarModeRaisedAndMinimized)
|
if (self.mode == MPAvatarModeRaisedAndMinimized)
|
||||||
[self.avatarImageView.layer removeAllAnimations];
|
[self.avatarImageView.layer removeAnimationForKey:@"targetedShadow"];
|
||||||
else if (![self.avatarImageView.layer animationForKey:@"targetedShadow"])
|
else if (![self.avatarImageView.layer animationForKey:@"targetedShadow"])
|
||||||
[self.avatarImageView.layer addAnimation:_targetedShadowAnimation forKey:@"targetedShadow"];
|
[self.avatarImageView.layer addAnimation:_targetedShadowAnimation forKey:@"targetedShadow"];
|
||||||
|
|
||||||
// Avatar selection and spinner.
|
// Avatar selection and spinner.
|
||||||
if (self.mode != MPAvatarModeRaisedAndMinimized && (self.selected || self.highlighted) && !self.spinnerActive)
|
if (self.mode != MPAvatarModeRaisedAndMinimized && (self.selected || self.highlighted) && !self.spinnerActive)
|
||||||
self.avatarImageView.backgroundColor = self.avatarImageView.tintColor;
|
self.avatarImageView.backgroundColor = self.avatarImageView.tintColor;
|
||||||
else
|
else
|
||||||
self.avatarImageView.backgroundColor = [UIColor clearColor];
|
self.avatarImageView.backgroundColor = [UIColor clearColor];
|
||||||
self.avatarImageView.layer.cornerRadius = self.avatarImageView.bounds.size.height / 2;
|
self.avatarImageView.layer.cornerRadius = self.avatarImageView.bounds.size.height / 2;
|
||||||
self.spinner.alpha = self.spinnerActive? 1: 0;
|
self.spinner.alpha = self.spinnerActive? 1: 0;
|
||||||
|
|
||||||
[self layoutSubviews];
|
[self.contentView layoutIfNeeded];
|
||||||
} completion:nil];
|
} completion:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||||
*
|
*
|
||||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
*
|
*
|
||||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//
|
//
|
||||||
// MPCombinedViewController.h
|
// MPCombinedViewController.h
|
||||||
@ -19,7 +19,6 @@
|
|||||||
#import "MPCombinedViewController.h"
|
#import "MPCombinedViewController.h"
|
||||||
#import "MPUsersViewController.h"
|
#import "MPUsersViewController.h"
|
||||||
#import "MPPasswordsViewController.h"
|
#import "MPPasswordsViewController.h"
|
||||||
#import "MPEmergencySegue.h"
|
|
||||||
#import "MPEmergencyViewController.h"
|
#import "MPEmergencyViewController.h"
|
||||||
#import "MPPasswordsSegue.h"
|
#import "MPPasswordsSegue.h"
|
||||||
|
|
||||||
@ -34,11 +33,13 @@
|
|||||||
MPPasswordsViewController *_passwordsVC;
|
MPPasswordsViewController *_passwordsVC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark - Life
|
||||||
|
|
||||||
- (void)viewDidLoad {
|
- (void)viewDidLoad {
|
||||||
|
|
||||||
[super viewDidLoad];
|
[super viewDidLoad];
|
||||||
|
|
||||||
[self setMode:MPCombinedModeUserSelection animated:NO];
|
_mode = MPCombinedModeUserSelection;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)viewWillAppear:(BOOL)animated {
|
- (void)viewWillAppear:(BOOL)animated {
|
||||||
@ -67,9 +68,9 @@
|
|||||||
if ([segue.identifier isEqualToString:@"users"])
|
if ([segue.identifier isEqualToString:@"users"])
|
||||||
self.usersVC = segue.destinationViewController;
|
self.usersVC = segue.destinationViewController;
|
||||||
if ([segue.identifier isEqualToString:@"passwords"]) {
|
if ([segue.identifier isEqualToString:@"passwords"]) {
|
||||||
NSAssert([segue isKindOfClass:[MPPasswordsSegue class]], @"passwords segue should be MPPasswordsSegue: %@", segue);
|
NSAssert( [segue isKindOfClass:[MPPasswordsSegue class]], @"passwords segue should be MPPasswordsSegue: %@", segue );
|
||||||
NSAssert([sender isKindOfClass:[NSDictionary class]], @"sender should be dictionary: %@", sender);
|
NSAssert( [sender isKindOfClass:[NSDictionary class]], @"sender should be dictionary: %@", sender );
|
||||||
NSAssert([[sender objectForKey:@"animated"] isKindOfClass:[NSNumber class]], @"sender should contain 'animated': %@", sender);
|
NSAssert( [[sender objectForKey:@"animated"] isKindOfClass:[NSNumber class]], @"sender should contain 'animated': %@", sender );
|
||||||
[(MPPasswordsSegue *)segue setAnimated:[sender[@"animated"] boolValue]];
|
[(MPPasswordsSegue *)segue setAnimated:[sender[@"animated"] boolValue]];
|
||||||
UIViewController *destinationVC = segue.destinationViewController;
|
UIViewController *destinationVC = segue.destinationViewController;
|
||||||
_passwordsVC = [destinationVC isKindOfClass:[MPPasswordsViewController class]]? (MPPasswordsViewController *)destinationVC: nil;
|
_passwordsVC = [destinationVC isKindOfClass:[MPPasswordsViewController class]]? (MPPasswordsViewController *)destinationVC: nil;
|
||||||
@ -99,20 +100,14 @@
|
|||||||
[self performSegueWithIdentifier:@"emergency" sender:self];
|
[self performSegueWithIdentifier:@"emergency" sender:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UIStoryboardSegue *)segueForUnwindingToViewController:(UIViewController *)toViewController
|
#pragma mark - Actions
|
||||||
fromViewController:(UIViewController *)fromViewController identifier:(NSString *)identifier {
|
|
||||||
|
|
||||||
if ([identifier isEqualToString:@"unwind-emergency"]) {
|
- (IBAction)unwindToCombined:(UIStoryboardSegue *)sender {
|
||||||
MPEmergencySegue *segue = [[MPEmergencySegue alloc] initWithIdentifier:identifier
|
|
||||||
source:fromViewController destination:toViewController];
|
|
||||||
segue.unwind = YES;
|
|
||||||
dbg_return(segue);
|
|
||||||
}
|
|
||||||
|
|
||||||
dbg_return((id)nil);
|
dbg( @"unwindToCombined:%@", sender );
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Properties
|
#pragma mark - State
|
||||||
|
|
||||||
- (void)setMode:(MPCombinedMode)mode {
|
- (void)setMode:(MPCombinedMode)mode {
|
||||||
|
|
||||||
@ -158,22 +153,22 @@
|
|||||||
if ([_notificationObservers count])
|
if ([_notificationObservers count])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Weakify(self);
|
Weakify( self );
|
||||||
_notificationObservers = @[
|
_notificationObservers = @[
|
||||||
[[NSNotificationCenter defaultCenter]
|
[[NSNotificationCenter defaultCenter]
|
||||||
addObserverForName:MPSignedInNotification object:nil
|
addObserverForName:MPSignedInNotification object:nil
|
||||||
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||||
Strongify(self);
|
Strongify( self );
|
||||||
|
|
||||||
[self setMode:MPCombinedModePasswordSelection];
|
[self setMode:MPCombinedModePasswordSelection];
|
||||||
}],
|
}],
|
||||||
[[NSNotificationCenter defaultCenter]
|
[[NSNotificationCenter defaultCenter]
|
||||||
addObserverForName:MPSignedOutNotification object:nil
|
addObserverForName:MPSignedOutNotification object:nil
|
||||||
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
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]];
|
||||||
}],
|
}],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,57 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
|
||||||
*
|
|
||||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
|
||||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*
|
|
||||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
|
||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*/
|
|
||||||
|
|
||||||
//
|
|
||||||
// MPEmergencySegue.h
|
|
||||||
// MPEmergencySegue
|
|
||||||
//
|
|
||||||
// Created by lhunath on 2014-04-09.
|
|
||||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MPEmergencySegue.h"
|
|
||||||
|
|
||||||
@implementation MPEmergencySegue {
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)perform {
|
|
||||||
|
|
||||||
UIViewController *sourceViewController = self.sourceViewController;
|
|
||||||
UIViewController *destinationViewController = self.destinationViewController;
|
|
||||||
|
|
||||||
if (!self.unwind) {
|
|
||||||
// Winding
|
|
||||||
[sourceViewController addChildViewController:destinationViewController];
|
|
||||||
[sourceViewController.view addSubview:destinationViewController.view];
|
|
||||||
CGRectSetY(destinationViewController.view.bounds, sourceViewController.view.frame.size.height);
|
|
||||||
[UIView transitionWithView:sourceViewController.view duration:0.3f options:UIViewAnimationOptionAllowAnimatedContent
|
|
||||||
animations:^{
|
|
||||||
CGRectSetY(destinationViewController.view.bounds, 0);
|
|
||||||
} completion:^(BOOL finished) {
|
|
||||||
if (finished)
|
|
||||||
[destinationViewController didMoveToParentViewController:sourceViewController];
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Unwinding
|
|
||||||
[sourceViewController willMoveToParentViewController:nil];
|
|
||||||
[UIView transitionWithView:sourceViewController.parentViewController.view duration:0.3f options:UIViewAnimationOptionAllowAnimatedContent
|
|
||||||
animations:^{
|
|
||||||
CGRectSetY(sourceViewController.view.bounds, sourceViewController.parentViewController.view.frame.size.height);
|
|
||||||
} completion:^(BOOL finished) {
|
|
||||||
if (finished) {
|
|
||||||
[sourceViewController.view removeFromSuperview];
|
|
||||||
[sourceViewController removeFromParentViewController];
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
@ -53,16 +53,9 @@
|
|||||||
[self reset];
|
[self reset];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)canPerformUnwindSegueAction:(SEL)action fromViewController:(UIViewController *)fromViewController withSender:(id)sender {
|
- (UIStatusBarStyle)preferredStatusBarStyle {
|
||||||
|
|
||||||
return [self respondsToSelector:action];
|
return UIStatusBarStyleLightContent;
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Actions
|
|
||||||
|
|
||||||
- (IBAction)unwindToCombined:(UIStoryboardSegue *)sender {
|
|
||||||
|
|
||||||
dbg(@"unwindToCombined:%@", sender);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - UITextFieldDelegate
|
#pragma mark - UITextFieldDelegate
|
||||||
@ -126,7 +119,7 @@
|
|||||||
- (void)updatePassword {
|
- (void)updatePassword {
|
||||||
|
|
||||||
NSString *siteName = self.siteField.text;
|
NSString *siteName = self.siteField.text;
|
||||||
MPElementType siteType = [self siteType];
|
MPSiteType siteType = [self siteType];
|
||||||
NSUInteger siteCounter = (NSUInteger)self.counterStepper.value;
|
NSUInteger siteCounter = (NSUInteger)self.counterStepper.value;
|
||||||
self.counterLabel.text = strf( @"%lu", (unsigned long)siteCounter );
|
self.counterLabel.text = strf( @"%lu", (unsigned long)siteCounter );
|
||||||
|
|
||||||
@ -136,7 +129,7 @@
|
|||||||
[_emergencyPasswordQueue addOperationWithBlock:^{
|
[_emergencyPasswordQueue addOperationWithBlock:^{
|
||||||
NSString *sitePassword = nil;
|
NSString *sitePassword = nil;
|
||||||
if (_key && [siteName length])
|
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( ^{
|
PearlMainQueue( ^{
|
||||||
[self.activity stopAnimating];
|
[self.activity stopAnimating];
|
||||||
@ -145,21 +138,21 @@
|
|||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (enum MPElementType)siteType {
|
- (enum MPSiteType)siteType {
|
||||||
|
|
||||||
switch (self.typeControl.selectedSegmentIndex) {
|
switch (self.typeControl.selectedSegmentIndex) {
|
||||||
case 0:
|
case 0:
|
||||||
return MPElementTypeGeneratedMaximum;
|
return MPSiteTypeGeneratedMaximum;
|
||||||
case 1:
|
case 1:
|
||||||
return MPElementTypeGeneratedLong;
|
return MPSiteTypeGeneratedLong;
|
||||||
case 2:
|
case 2:
|
||||||
return MPElementTypeGeneratedMedium;
|
return MPSiteTypeGeneratedMedium;
|
||||||
case 3:
|
case 3:
|
||||||
return MPElementTypeGeneratedBasic;
|
return MPSiteTypeGeneratedBasic;
|
||||||
case 4:
|
case 4:
|
||||||
return MPElementTypeGeneratedShort;
|
return MPSiteTypeGeneratedShort;
|
||||||
case 5:
|
case 5:
|
||||||
return MPElementTypeGeneratedPIN;
|
return MPSiteTypeGeneratedPIN;
|
||||||
default:
|
default:
|
||||||
Throw(@"Unsupported type index: %ld", (long)self.typeControl.selectedSegmentIndex);
|
Throw(@"Unsupported type index: %ld", (long)self.typeControl.selectedSegmentIndex);
|
||||||
}
|
}
|
||||||
@ -187,7 +180,7 @@
|
|||||||
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||||
Strongify(self);
|
Strongify(self);
|
||||||
|
|
||||||
[self performSegueWithIdentifier:@"unwind-emergency" sender:self];
|
[self performSegueWithIdentifier:@"unwind-popover" sender:self];
|
||||||
}],
|
}],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,6 @@
|
|||||||
@property (weak, nonatomic) IBOutlet UITextView *logView;
|
@property (weak, nonatomic) IBOutlet UITextView *logView;
|
||||||
@property (weak, nonatomic) IBOutlet UISegmentedControl *levelControl;
|
@property (weak, nonatomic) IBOutlet UISegmentedControl *levelControl;
|
||||||
|
|
||||||
- (IBAction)action:(id)sender;
|
|
||||||
- (IBAction)toggleLevelControl:(UISegmentedControl *)sender;
|
- (IBAction)toggleLevelControl:(UISegmentedControl *)sender;
|
||||||
- (IBAction)refresh:(UIBarButtonItem *)sender;
|
- (IBAction)refresh:(UIBarButtonItem *)sender;
|
||||||
- (IBAction)mail:(UIBarButtonItem *)sender;
|
- (IBAction)mail:(UIBarButtonItem *)sender;
|
||||||
|
@ -20,9 +20,7 @@
|
|||||||
#import "MPiOSAppDelegate.h"
|
#import "MPiOSAppDelegate.h"
|
||||||
#import "MPAppDelegate_Store.h"
|
#import "MPAppDelegate_Store.h"
|
||||||
|
|
||||||
@implementation MPLogsViewController {
|
@implementation MPLogsViewController
|
||||||
PearlOverlay *_switchCloudStoreProgress;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)viewDidLoad {
|
- (void)viewDidLoad {
|
||||||
|
|
||||||
@ -48,139 +46,6 @@
|
|||||||
self.levelControl.selectedSegmentIndex = [[MPiOSConfig get].traceMode boolValue]? 1: 0;
|
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 {
|
- (IBAction)toggleLevelControl:(UISegmentedControl *)sender {
|
||||||
|
|
||||||
BOOL traceEnabled = (BOOL)self.levelControl.selectedSegmentIndex;
|
BOOL traceEnabled = (BOOL)self.levelControl.selectedSegmentIndex;
|
||||||
|
@ -9,16 +9,18 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
//
|
//
|
||||||
// MPEmergencySegue.h
|
// MPOverlayViewController.h
|
||||||
// MPEmergencySegue
|
// MPOverlayViewController
|
||||||
//
|
//
|
||||||
// Created by lhunath on 2014-04-09.
|
// Created by lhunath on 2014-09-22.
|
||||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
@interface MPEmergencySegue : UIStoryboardSegue
|
|
||||||
|
|
||||||
@property(nonatomic) BOOL unwind;
|
@interface MPOverlayViewController : UIViewController
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface MPOverlaySegue : UIStoryboardSegue
|
||||||
@end
|
@end
|
154
MasterPassword/ObjC/iOS/MPOverlayViewController.m
Normal file
154
MasterPassword/ObjC/iOS/MPOverlayViewController.m
Normal file
@ -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 <lhunath@lyndir.com>
|
||||||
|
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
//
|
||||||
|
// MPOverlayViewController.h
|
||||||
|
// MPOverlayViewController
|
||||||
|
//
|
||||||
|
// Created by lhunath on 2014-09-22.
|
||||||
|
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MPOverlayViewController.h"
|
||||||
|
|
||||||
|
@implementation MPOverlayViewController {
|
||||||
|
NSMutableDictionary *_dismissSegueByButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)awakeFromNib {
|
||||||
|
|
||||||
|
[super awakeFromNib];
|
||||||
|
|
||||||
|
_dismissSegueByButton = [NSMutableDictionary dictionary];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)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
|
@ -27,8 +27,9 @@ typedef NS_ENUM ( NSUInteger, MPPasswordCellMode ) {
|
|||||||
|
|
||||||
@interface MPPasswordCell : MPCell <UIScrollViewDelegate, UITextFieldDelegate>
|
@interface MPPasswordCell : MPCell <UIScrollViewDelegate, UITextFieldDelegate>
|
||||||
|
|
||||||
- (void)setElement:(MPElementEntity *)element animated:(BOOL)animated;
|
- (void)setSite:(MPSiteEntity *)site animated:(BOOL)animated;
|
||||||
- (void)setTransientSite:(NSString *)siteName animated:(BOOL)animated;
|
- (void)setTransientSite:(NSString *)siteName animated:(BOOL)animated;
|
||||||
- (void)setMode:(MPPasswordCellMode)mode animated:(BOOL)animated;
|
- (void)setMode:(MPPasswordCellMode)mode animated:(BOOL)animated;
|
||||||
|
- (MPSiteEntity *)siteInContext:(NSManagedObjectContext *)context;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -19,22 +19,25 @@
|
|||||||
#import "MPPasswordCell.h"
|
#import "MPPasswordCell.h"
|
||||||
#import "MPiOSAppDelegate.h"
|
#import "MPiOSAppDelegate.h"
|
||||||
#import "MPAppDelegate_Store.h"
|
#import "MPAppDelegate_Store.h"
|
||||||
|
#import "UIColor+Expanded.h"
|
||||||
|
#import "MPAppDelegate_InApp.h"
|
||||||
|
|
||||||
@interface MPPasswordCell()
|
@interface MPPasswordCell()
|
||||||
|
|
||||||
@property(nonatomic, strong) IBOutlet UILabel *siteNameLabel;
|
@property(nonatomic, strong) IBOutlet UILabel *siteNameLabel;
|
||||||
@property(nonatomic, strong) IBOutlet UITextField *passwordField;
|
@property(nonatomic, strong) IBOutlet UITextField *passwordField;
|
||||||
|
@property(nonatomic, strong) IBOutlet UIView *loginNameContainer;
|
||||||
@property(nonatomic, strong) IBOutlet UITextField *loginNameField;
|
@property(nonatomic, strong) IBOutlet UITextField *loginNameField;
|
||||||
@property(nonatomic, strong) IBOutlet UIPageControl *pageControl;
|
|
||||||
@property(nonatomic, strong) IBOutlet UILabel *strengthLabel;
|
@property(nonatomic, strong) IBOutlet UILabel *strengthLabel;
|
||||||
@property(nonatomic, strong) IBOutlet UILabel *counterLabel;
|
@property(nonatomic, strong) IBOutlet UILabel *counterLabel;
|
||||||
@property(nonatomic, strong) IBOutlet UIButton *counterButton;
|
@property(nonatomic, strong) IBOutlet UIButton *counterButton;
|
||||||
@property(nonatomic, strong) IBOutlet UIButton *upgradeButton;
|
@property(nonatomic, strong) IBOutlet UIButton *upgradeButton;
|
||||||
|
@property(nonatomic, strong) IBOutlet UIButton *answersButton;
|
||||||
@property(nonatomic, strong) IBOutlet UIButton *modeButton;
|
@property(nonatomic, strong) IBOutlet UIButton *modeButton;
|
||||||
@property(nonatomic, strong) IBOutlet UIButton *loginModeButton;
|
|
||||||
@property(nonatomic, strong) IBOutlet UIButton *editButton;
|
@property(nonatomic, strong) IBOutlet UIButton *editButton;
|
||||||
@property(nonatomic, strong) IBOutlet UIScrollView *modeScrollView;
|
@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, strong) IBOutlet UIView *indicatorView;
|
||||||
|
|
||||||
@property(nonatomic) MPPasswordCellMode mode;
|
@property(nonatomic) MPPasswordCellMode mode;
|
||||||
@ -43,7 +46,7 @@
|
|||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation MPPasswordCell {
|
@implementation MPPasswordCell {
|
||||||
NSManagedObjectID *_elementOID;
|
NSManagedObjectID *_siteOID;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Life cycle
|
#pragma mark - Life cycle
|
||||||
@ -57,21 +60,39 @@
|
|||||||
[self.counterButton addGestureRecognizer:
|
[self.counterButton addGestureRecognizer:
|
||||||
[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector( doResetCounter: )]];
|
[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector( doResetCounter: )]];
|
||||||
|
|
||||||
self.selectionButton.layer.cornerRadius = 5;
|
[self setupLayer];
|
||||||
self.selectionButton.layer.shadowOffset = CGSizeZero;
|
|
||||||
self.selectionButton.layer.shadowRadius = 5;
|
|
||||||
self.selectionButton.layer.shadowOpacity = 0;
|
|
||||||
self.selectionButton.layer.shadowColor = [UIColor whiteColor].CGColor;
|
|
||||||
|
|
||||||
self.pageControl.transform = CGAffineTransformMakeScale( 0.4f, 0.4f );
|
[self observeKeyPath:@"bounds" withBlock:^(id from, id to, NSKeyValueChange cause, MPPasswordCell *_self) {
|
||||||
|
if (from && !CGSizeEqualToSize( [from CGRectValue].size, [to CGRectValue].size ))
|
||||||
[self.selectionButton observeKeyPath:@"highlighted"
|
[_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) {
|
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) {
|
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"];
|
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position.y"];
|
||||||
@ -82,17 +103,48 @@
|
|||||||
[self.indicatorView.layer addAnimation:animation forKey:@"bounce"];
|
[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 {
|
- (void)prepareForReuse {
|
||||||
|
|
||||||
[super prepareForReuse];
|
[super prepareForReuse];
|
||||||
|
|
||||||
_elementOID = nil;
|
_siteOID = nil;
|
||||||
self.transientSite = nil;
|
self.transientSite = nil;
|
||||||
self.loginModeButton.selected = NO;
|
|
||||||
self.mode = MPPasswordCellModePassword;
|
self.mode = MPPasswordCellModePassword;
|
||||||
[self updateAnimated:NO];
|
[self updateAnimated:NO];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)dealloc {
|
||||||
|
|
||||||
|
[self removeKeyPathObservers];
|
||||||
|
[self.contentButton removeKeyPathObservers];
|
||||||
|
[self.loginNameButton removeKeyPathObservers];
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark - State
|
#pragma mark - State
|
||||||
|
|
||||||
- (void)setMode:(MPPasswordCellMode)mode animated:(BOOL)animated {
|
- (void)setMode:(MPPasswordCellMode)mode animated:(BOOL)animated {
|
||||||
@ -104,9 +156,9 @@
|
|||||||
[self updateAnimated:animated];
|
[self updateAnimated:animated];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setElement:(MPElementEntity *)element animated:(BOOL)animated {
|
- (void)setSite:(MPSiteEntity *)site animated:(BOOL)animated {
|
||||||
|
|
||||||
_elementOID = [element objectID];
|
_siteOID = [site objectID];
|
||||||
[self updateAnimated:animated];
|
[self updateAnimated:animated];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +172,11 @@
|
|||||||
|
|
||||||
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
|
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
|
||||||
|
|
||||||
[textField resignFirstResponder];
|
if (textField == self.passwordField)
|
||||||
|
[self.loginNameField becomeFirstResponder];
|
||||||
|
else
|
||||||
|
[textField resignFirstResponder];
|
||||||
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,6 +185,9 @@
|
|||||||
UICollectionView *collectionView = [UICollectionView findAsSuperviewOf:self];
|
UICollectionView *collectionView = [UICollectionView findAsSuperviewOf:self];
|
||||||
[collectionView scrollToItemAtIndexPath:[collectionView indexPathForCell:self]
|
[collectionView scrollToItemAtIndexPath:[collectionView indexPathForCell:self]
|
||||||
atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:YES];
|
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 {
|
- (IBAction)textFieldDidChange:(UITextField *)textField {
|
||||||
@ -137,10 +196,10 @@
|
|||||||
NSString *password = self.passwordField.text;
|
NSString *password = self.passwordField.text;
|
||||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
TimeToCrack timeToCrack;
|
TimeToCrack timeToCrack;
|
||||||
MPElementEntity *element = [self elementInContext:context];
|
MPSiteEntity *site = [self siteInContext:context];
|
||||||
id<MPAlgorithm> algorithm = element.algorithm?: MPAlgorithmDefault;
|
id<MPAlgorithm> algorithm = site.algorithm?: MPAlgorithmDefault;
|
||||||
MPAttacker attackHardware = [[MPConfig get].siteAttacker unsignedIntegerValue];
|
MPAttacker attackHardware = [[MPConfig get].siteAttacker unsignedIntegerValue];
|
||||||
if ([algorithm timeToCrack:&timeToCrack passwordOfType:[self elementInContext:context].type byAttacker:attackHardware] ||
|
if ([algorithm timeToCrack:&timeToCrack passwordOfType:[self siteInContext:context].type byAttacker:attackHardware] ||
|
||||||
[algorithm timeToCrack:&timeToCrack passwordString:password byAttacker:attackHardware])
|
[algorithm timeToCrack:&timeToCrack passwordString:password byAttacker:attackHardware])
|
||||||
PearlMainQueue( ^{
|
PearlMainQueue( ^{
|
||||||
self.strengthLabel.text = NSStringFromTimeToCrack( timeToCrack );
|
self.strengthLabel.text = NSStringFromTimeToCrack( timeToCrack );
|
||||||
@ -152,21 +211,27 @@
|
|||||||
- (void)textFieldDidEndEditing:(UITextField *)textField {
|
- (void)textFieldDidEndEditing:(UITextField *)textField {
|
||||||
|
|
||||||
if (textField == self.passwordField || textField == self.loginNameField) {
|
if (textField == self.passwordField || textField == self.loginNameField) {
|
||||||
NSString *text = textField.text;
|
|
||||||
textField.enabled = NO;
|
textField.enabled = NO;
|
||||||
|
NSString *text = textField.text;
|
||||||
|
|
||||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
MPElementEntity *element = [self elementInContext:context];
|
MPSiteEntity *site = [self siteInContext:context];
|
||||||
if (!element)
|
if (!site)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (textField == self.passwordField) {
|
if (textField == self.passwordField) {
|
||||||
[element.algorithm saveContent:text toElement:element usingKey:[MPiOSAppDelegate get].key];
|
if ([site.algorithm savePassword:text toSite:site usingKey:[MPiOSAppDelegate get].key])
|
||||||
[PearlOverlay showTemporaryOverlayWithTitle:@"Password Updated" dismissAfter:2];
|
[PearlOverlay showTemporaryOverlayWithTitle:@"Password Updated" dismissAfter:2];
|
||||||
}
|
}
|
||||||
else if (textField == self.loginNameField) {
|
else if (textField == self.loginNameField &&
|
||||||
element.loginName = text;
|
((site.loginGenerated && ![text length]) ||
|
||||||
[PearlOverlay showTemporaryOverlayWithTitle:@"Login Updated" dismissAfter:2];
|
(!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];
|
[context saveToStore];
|
||||||
@ -177,25 +242,19 @@
|
|||||||
|
|
||||||
#pragma mark - Actions
|
#pragma mark - Actions
|
||||||
|
|
||||||
- (IBAction)doLoginMode:(UIButton *)sender {
|
|
||||||
|
|
||||||
self.loginModeButton.selected = !self.loginModeButton.selected;
|
|
||||||
[self updateAnimated:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)doDelete:(UIButton *)sender {
|
- (IBAction)doDelete:(UIButton *)sender {
|
||||||
|
|
||||||
MPElementEntity *element = [self elementInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
|
MPSiteEntity *site = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
|
||||||
if (!element)
|
if (!site)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[PearlSheet showSheetWithTitle:strf( @"Delete %@?", element.name ) viewStyle:UIActionSheetStyleAutomatic
|
[PearlSheet showSheetWithTitle:strf( @"Delete %@?", site.name ) viewStyle:UIActionSheetStyleAutomatic
|
||||||
initSheet:nil tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) {
|
initSheet:nil tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) {
|
||||||
if (buttonIndex == [sheet cancelButtonIndex])
|
if (buttonIndex == [sheet cancelButtonIndex])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
[context deleteObject:[self elementInContext:context]];
|
[context deleteObject:[self siteInContext:context]];
|
||||||
[context saveToStore];
|
[context saveToStore];
|
||||||
}];
|
}];
|
||||||
} cancelTitle:@"Cancel" destructiveTitle:@"Delete Site" otherTitles:nil];
|
} cancelTitle:@"Cancel" destructiveTitle:@"Delete Site" otherTitles:nil];
|
||||||
@ -207,12 +266,11 @@
|
|||||||
|
|
||||||
[PearlSheet showSheetWithTitle:@"Change Password Type" viewStyle:UIActionSheetStyleAutomatic
|
[PearlSheet showSheetWithTitle:@"Change Password Type" viewStyle:UIActionSheetStyleAutomatic
|
||||||
initSheet:^(UIActionSheet *sheet) {
|
initSheet:^(UIActionSheet *sheet) {
|
||||||
MPElementEntity
|
MPSiteEntity *mainSite = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
|
||||||
*mainElement = [self elementInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
|
|
||||||
for (NSNumber *typeNumber in [MPAlgorithmDefault allTypes]) {
|
for (NSNumber *typeNumber in [MPAlgorithmDefault allTypes]) {
|
||||||
MPElementType type = [typeNumber unsignedIntegerValue];
|
MPSiteType type = [typeNumber unsignedIntegerValue];
|
||||||
NSString *typeName = [MPAlgorithmDefault nameOfType:type];
|
NSString *typeName = [MPAlgorithmDefault nameOfType:type];
|
||||||
if (type == mainElement.type)
|
if (type == mainSite.type)
|
||||||
[sheet addButtonWithTitle:strf( @"● %@", typeName )];
|
[sheet addButtonWithTitle:strf( @"● %@", typeName )];
|
||||||
else
|
else
|
||||||
[sheet addButtonWithTitle:typeName];
|
[sheet addButtonWithTitle:typeName];
|
||||||
@ -221,26 +279,25 @@
|
|||||||
if (buttonIndex == [sheet cancelButtonIndex])
|
if (buttonIndex == [sheet cancelButtonIndex])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
MPElementType type = [[MPAlgorithmDefault allTypes][buttonIndex] unsignedIntegerValue]?: MPElementTypeGeneratedLong;
|
MPSiteType type = [[MPAlgorithmDefault allTypes][buttonIndex] unsignedIntegerValue]?: MPSiteTypeGeneratedLong;
|
||||||
|
|
||||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
MPElementEntity *element = [self elementInContext:context];
|
MPSiteEntity *site = [self siteInContext:context];
|
||||||
element = [[MPiOSAppDelegate get] changeElement:element saveInContext:context toType:type];
|
site = [[MPiOSAppDelegate get] changeSite:site saveInContext:context toType:type];
|
||||||
[self setElement:element animated:YES];
|
[self setSite:site animated:YES];
|
||||||
}];
|
}];
|
||||||
} cancelTitle:@"Cancel" destructiveTitle:nil otherTitles:nil];
|
} cancelTitle:@"Cancel" destructiveTitle:nil otherTitles:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)doEdit:(UIButton *)sender {
|
- (IBAction)doEdit:(UIButton *)sender {
|
||||||
|
|
||||||
if (self.loginModeButton.selected) {
|
self.loginNameField.enabled = YES;
|
||||||
self.loginNameField.enabled = YES;
|
self.passwordField.enabled = YES;
|
||||||
[self.loginNameField becomeFirstResponder];
|
|
||||||
}
|
if ([self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]].type & MPSiteTypeClassStored)
|
||||||
else if ([self elementInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]].type & MPElementTypeClassStored) {
|
|
||||||
self.passwordField.enabled = YES;
|
|
||||||
[self.passwordField becomeFirstResponder];
|
[self.passwordField becomeFirstResponder];
|
||||||
}
|
else
|
||||||
|
[self.loginNameField becomeFirstResponder];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)doMode:(UIButton *)sender {
|
- (IBAction)doMode:(UIButton *)sender {
|
||||||
@ -260,7 +317,7 @@
|
|||||||
- (IBAction)doUpgrade:(UIButton *)sender {
|
- (IBAction)doUpgrade:(UIButton *)sender {
|
||||||
|
|
||||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
if (![[self elementInContext:context] migrateExplicitly:YES]) {
|
if (![[self siteInContext:context] tryMigrateExplicitly:YES]) {
|
||||||
[PearlOverlay showTemporaryOverlayWithTitle:@"Couldn't Upgrade Site" dismissAfter:2];
|
[PearlOverlay showTemporaryOverlayWithTitle:@"Couldn't Upgrade Site" dismissAfter:2];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -274,11 +331,11 @@
|
|||||||
- (IBAction)doIncrementCounter:(UIButton *)sender {
|
- (IBAction)doIncrementCounter:(UIButton *)sender {
|
||||||
|
|
||||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
MPElementEntity *element = [self elementInContext:context];
|
MPSiteEntity *site = [self siteInContext:context];
|
||||||
if (!element || ![element isKindOfClass:[MPElementGeneratedEntity class]])
|
if (!site || ![site isKindOfClass:[MPGeneratedSiteEntity class]])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
++((MPElementGeneratedEntity *)element).counter;
|
++((MPGeneratedSiteEntity *)site).counter;
|
||||||
[context saveToStore];
|
[context saveToStore];
|
||||||
|
|
||||||
[PearlOverlay showTemporaryOverlayWithTitle:@"Generating New Password" dismissAfter:2];
|
[PearlOverlay showTemporaryOverlayWithTitle:@"Generating New Password" dismissAfter:2];
|
||||||
@ -306,11 +363,11 @@
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
MPElementEntity *element = [self elementInContext:context];
|
MPSiteEntity *site = [self siteInContext:context];
|
||||||
if (!element || ![element isKindOfClass:[MPElementGeneratedEntity class]])
|
if (!site || ![site isKindOfClass:[MPGeneratedSiteEntity class]])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
((MPElementGeneratedEntity *)element).counter = 1;
|
((MPGeneratedSiteEntity *)site).counter = 1;
|
||||||
[context saveToStore];
|
[context saveToStore];
|
||||||
|
|
||||||
[PearlOverlay showTemporaryOverlayWithTitle:@"Counter Reset" dismissAfter:2];
|
[PearlOverlay showTemporaryOverlayWithTitle:@"Counter Reset" dismissAfter:2];
|
||||||
@ -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) {
|
if (self.transientSite) {
|
||||||
[[UIResponder findFirstResponder] resignFirstResponder];
|
[[UIResponder findFirstResponder] resignFirstResponder];
|
||||||
@ -329,15 +388,18 @@
|
|||||||
viewStyle:UIAlertViewStyleDefault
|
viewStyle:UIAlertViewStyleDefault
|
||||||
initAlert:nil tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
|
initAlert:nil tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
|
||||||
if (buttonIndex == [alert cancelButtonIndex]) {
|
if (buttonIndex == [alert cancelButtonIndex]) {
|
||||||
self.selectionButton.selected = NO;
|
self.contentButton.selected = NO;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[MPiOSAppDelegate get]
|
[[MPiOSAppDelegate get]
|
||||||
addElementNamed:self.transientSite completion:^(MPElementEntity *element, NSManagedObjectContext *context) {
|
addSiteNamed:self.transientSite completion:^(MPSiteEntity *site, NSManagedObjectContext *context) {
|
||||||
[self copyContentOfElement:element saveInContext:context];
|
[self copyContentOfSite:site saveInContext:context];
|
||||||
PearlMainQueue( ^{
|
|
||||||
self.selectionButton.selected = NO;
|
PearlMainQueueAfter( .3f, ^{
|
||||||
|
[UIView animateWithDuration:.2f animations:^{
|
||||||
|
self.contentButton.selected = NO;
|
||||||
|
}];
|
||||||
} );
|
} );
|
||||||
}];
|
}];
|
||||||
} cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonYes, nil];
|
} cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonYes, nil];
|
||||||
@ -345,9 +407,35 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
[self copyContentOfElement:[self elementInContext:context] saveInContext:context];
|
[self copyContentOfSite:[self siteInContext:context] saveInContext:context];
|
||||||
PearlMainQueue( ^{
|
|
||||||
self.selectionButton.selected = NO;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
[UIView animateWithDuration:animated? 0.3f: 0 animations:^{
|
[UIView animateWithDuration:animated? .3f: 0 animations:^{
|
||||||
MPElementEntity *mainElement = [self elementInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
|
MPSiteEntity *mainSite = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
|
||||||
|
|
||||||
// UI
|
// UI
|
||||||
self.selectionButton.layer.shadowOpacity = self.selectionButton.selected? 1: self.selectionButton.highlighted? 0.3f: 0;
|
self.upgradeButton.gone = !mainSite.requiresExplicitMigration;
|
||||||
self.upgradeButton.alpha = mainElement.requiresExplicitMigration? 1: 0;
|
self.answersButton.gone = ![[MPiOSAppDelegate get] isPurchased:MPProductGenerateAnswers];
|
||||||
self.passwordField.alpha = self.loginModeButton.selected? 0: 1;
|
BOOL settingsMode = self.mode == MPPasswordCellModeSettings;
|
||||||
self.loginNameField.alpha = self.loginModeButton.selected? 1: 0;
|
self.loginNameContainer.alpha = settingsMode || mainSite.loginGenerated || [mainSite.loginName length]? 0.7f: 0;
|
||||||
self.modeButton.alpha = self.transientSite? 0: 1;
|
self.loginNameField.textColor = [UIColor colorWithHexString:mainSite.loginGenerated? @"5E636D": @"6D5E63"];
|
||||||
self.loginModeButton.alpha = self.transientSite? 0: 1;
|
self.modeButton.alpha = self.transientSite? 0: settingsMode? 0.5f: 0.1f;
|
||||||
self.counterLabel.alpha = self.counterButton.alpha = mainElement.type & MPElementTypeClassGenerated? 1: 0;
|
self.counterLabel.alpha = self.counterButton.alpha = mainSite.type & MPSiteTypeClassGenerated? 0.5f: 0;
|
||||||
self.modeButton.selected = self.mode == MPPasswordCellModeSettings;
|
self.modeButton.selected = settingsMode;
|
||||||
self.pageControl.currentPage = self.mode == MPPasswordCellModePassword? 0: 1;
|
self.strengthLabel.gone = !settingsMode;
|
||||||
self.strengthLabel.alpha = self.mode == MPPasswordCellModePassword? 0: 1;
|
|
||||||
self.editButton.enabled = self.loginModeButton.selected || mainElement.type & MPElementTypeClassStored;
|
|
||||||
self.modeScrollView.scrollEnabled = !self.transientSite;
|
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];
|
[self.modeScrollView setContentOffset:CGPointMake( self.mode * self.modeScrollView.frame.size.width, 0 ) animated:animated];
|
||||||
|
if (!settingsMode) {
|
||||||
// Indicator
|
[self.loginNameField resignFirstResponder];
|
||||||
if (self.loginModeButton.selected) {
|
[self.passwordField resignFirstResponder];
|
||||||
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 ([[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
|
// Site Name
|
||||||
self.siteNameLabel.text = strl( @"%@ - %@", self.transientSite?: mainElement.name,
|
self.siteNameLabel.text = strl( @"%@ - %@", self.transientSite?: mainSite.name,
|
||||||
self.transientSite? @"Tap to create": [mainElement.algorithm shortNameOfType:mainElement.type] );
|
self.transientSite? @"Tap to create": [mainSite.algorithm shortNameOfType:mainSite.type] );
|
||||||
|
|
||||||
// Site Password
|
// Site Password
|
||||||
self.passwordField.enabled = NO;
|
|
||||||
self.passwordField.secureTextEntry = [[MPiOSConfig get].hidePasswords boolValue];
|
self.passwordField.secureTextEntry = [[MPiOSConfig get].hidePasswords boolValue];
|
||||||
self.passwordField.attributedPlaceholder = stra(
|
self.passwordField.attributedPlaceholder = stra(
|
||||||
mainElement.type & MPElementTypeClassStored? strl( @"No password" ):
|
mainSite.type & MPSiteTypeClassStored? strl( @"No password" ):
|
||||||
mainElement.type & MPElementTypeClassGenerated? strl( @"..." ): @"", @{
|
mainSite.type & MPSiteTypeClassGenerated? strl( @"..." ): @"", @{
|
||||||
NSForegroundColorAttributeName : [UIColor whiteColor]
|
NSForegroundColorAttributeName : [UIColor whiteColor]
|
||||||
} );
|
} );
|
||||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
NSString *password;
|
MPSiteEntity *site = [self siteInContext:context];
|
||||||
|
MPKey *key = [MPiOSAppDelegate get].key;
|
||||||
|
NSString *password, *loginName = [site resolveLoginUsingKey:key];
|
||||||
if (self.transientSite)
|
if (self.transientSite)
|
||||||
password = [MPAlgorithmDefault generateContentNamed:self.transientSite ofType:
|
password = [MPAlgorithmDefault generatePasswordForSiteNamed:self.transientSite ofType:
|
||||||
[[MPiOSAppDelegate get] activeUserInContext:context].defaultType?: MPElementTypeGeneratedLong
|
[[MPiOSAppDelegate get] activeUserInContext:context].defaultType?: MPSiteTypeGeneratedLong
|
||||||
withCounter:1 usingKey:[MPiOSAppDelegate get].key];
|
withCounter:1 usingKey:key];
|
||||||
|
else if (site)
|
||||||
|
password = [site resolvePasswordUsingKey:key];
|
||||||
else
|
else
|
||||||
password = [[self elementInContext:context] resolveContentUsingKey:[MPiOSAppDelegate get].key];
|
return;
|
||||||
|
|
||||||
TimeToCrack timeToCrack;
|
TimeToCrack timeToCrack;
|
||||||
NSString *timeToCrackString = nil;
|
NSString *timeToCrackString = nil;
|
||||||
id<MPAlgorithm> algorithm = mainElement.algorithm?: MPAlgorithmDefault;
|
id<MPAlgorithm> algorithm = site.algorithm?: MPAlgorithmDefault;
|
||||||
MPAttacker attackHardware = [[MPConfig get].siteAttacker unsignedIntegerValue];
|
MPAttacker attackHardware = [[MPConfig get].siteAttacker unsignedIntegerValue];
|
||||||
if ([algorithm timeToCrack:&timeToCrack passwordOfType:[self elementInContext:context].type byAttacker:attackHardware] ||
|
if ([algorithm timeToCrack:&timeToCrack passwordOfType:site.type byAttacker:attackHardware] ||
|
||||||
[algorithm timeToCrack:&timeToCrack passwordString:password byAttacker:attackHardware])
|
[algorithm timeToCrack:&timeToCrack passwordString:password byAttacker:attackHardware])
|
||||||
timeToCrackString = NSStringFromTimeToCrack( timeToCrack );
|
timeToCrackString = NSStringFromTimeToCrack( timeToCrack );
|
||||||
|
|
||||||
PearlMainQueue( ^{
|
PearlMainQueue( ^{
|
||||||
|
self.loginNameField.text = loginName;
|
||||||
self.passwordField.text = password;
|
self.passwordField.text = password;
|
||||||
self.strengthLabel.text = timeToCrackString;
|
self.strengthLabel.text = timeToCrackString;
|
||||||
|
self.loginNameButton.titleLabel.alpha = [loginName length] || self.loginNameField.enabled? 0: 1;
|
||||||
|
|
||||||
if (!self.loginModeButton.selected) {
|
if ([password length])
|
||||||
if ([password length])
|
self.indicatorView.alpha = 0;
|
||||||
self.indicatorView.alpha = 0;
|
else {
|
||||||
else {
|
self.indicatorView.alpha = 1;
|
||||||
self.indicatorView.alpha = 1;
|
[self.indicatorView removeFromSuperview];
|
||||||
[self.indicatorView removeFromSuperview];
|
[self.modeScrollView addSubview:self.indicatorView];
|
||||||
[self.modeScrollView addSubview:self.indicatorView];
|
[self.contentView addConstraintsWithVisualFormat:@"V:[indicator][target]" options:NSLayoutFormatAlignAllCenterX
|
||||||
[self.contentView addConstraintsWithVisualFormat:@"V:[indicator][view]" options:NSLayoutFormatAlignAllCenterX
|
metrics:nil views:@{
|
||||||
metrics:nil views:@{
|
@"indicator" : self.indicatorView,
|
||||||
@"indicator" : self.indicatorView,
|
@"target" : settingsMode? self.editButton: self.modeButton
|
||||||
@"view" : self.mode == MPPasswordCellModeSettings? self.editButton: self.modeButton
|
}];
|
||||||
}];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
}];
|
}];
|
||||||
|
|
||||||
// Site Counter
|
// Site Counter
|
||||||
if ([mainElement isKindOfClass:[MPElementGeneratedEntity class]])
|
if ([mainSite isKindOfClass:[MPGeneratedSiteEntity class]])
|
||||||
self.counterLabel.text = strf( @"%lu", (unsigned long)((MPElementGeneratedEntity *)mainElement).counter );
|
self.counterLabel.text = strf( @"%lu", (unsigned long)((MPGeneratedSiteEntity *)mainSite).counter );
|
||||||
|
|
||||||
// Site Login Name
|
// Site Login Name
|
||||||
self.loginNameField.enabled = NO;
|
self.loginNameField.enabled = self.passwordField.enabled = //
|
||||||
self.loginNameField.text = mainElement.loginName;
|
[self.loginNameField isFirstResponder] || [self.passwordField isFirstResponder];
|
||||||
self.loginNameField.attributedPlaceholder = stra( strl( @"Set login name" ), @{
|
|
||||||
NSForegroundColorAttributeName : [UIColor whiteColor]
|
[self.contentView layoutIfNeeded];
|
||||||
} );
|
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)copyContentOfElement:(MPElementEntity *)element saveInContext:(NSManagedObjectContext *)context {
|
- (BOOL)copyContentOfSite:(MPSiteEntity *)site saveInContext:(NSManagedObjectContext *)context {
|
||||||
|
|
||||||
// Copy content.
|
inf( @"Copying password for: %@", site.name );
|
||||||
if (self.loginModeButton.selected) {
|
NSString *password = [site resolvePasswordUsingKey:[MPAppDelegate_Shared get].key];
|
||||||
// Login Mode
|
if (![password length])
|
||||||
inf( @"Copying login for: %@", element.name );
|
return NO;
|
||||||
NSString *loginName = element.loginName;
|
|
||||||
if (![loginName length])
|
|
||||||
return;
|
|
||||||
|
|
||||||
PearlMainQueue( ^{
|
PearlMainQueue( ^{
|
||||||
[PearlOverlay showTemporaryOverlayWithTitle:strl( @"Login Name Copied" ) dismissAfter:2];
|
[PearlOverlay showTemporaryOverlayWithTitle:strl( @"Password Copied" ) dismissAfter:2];
|
||||||
[UIPasteboard generalPasteboard].string = loginName;
|
[UIPasteboard generalPasteboard].string = password;
|
||||||
} );
|
} );
|
||||||
|
|
||||||
[element use];
|
[site use];
|
||||||
[context saveToStore];
|
[context saveToStore];
|
||||||
}
|
return YES;
|
||||||
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];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (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
|
@end
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
@class MPElementEntity;
|
@class MPSiteEntity;
|
||||||
@class MPCoachmark;
|
@class MPCoachmark;
|
||||||
|
|
||||||
@interface MPPasswordsViewController : UIViewController<UISearchBarDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
|
@interface MPPasswordsViewController : UIViewController<UISearchBarDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#import "MPAppDelegate_Key.h"
|
#import "MPAppDelegate_Key.h"
|
||||||
#import "MPPasswordCell.h"
|
#import "MPPasswordCell.h"
|
||||||
#import "UICollectionView+PearlReloadFromArray.h"
|
#import "UICollectionView+PearlReloadFromArray.h"
|
||||||
|
#import "MPAnswersViewController.h"
|
||||||
|
|
||||||
@interface MPPasswordsViewController()<NSFetchedResultsControllerDelegate>
|
@interface MPPasswordsViewController()<NSFetchedResultsControllerDelegate>
|
||||||
|
|
||||||
@ -73,6 +74,18 @@
|
|||||||
[self updatePasswords];
|
[self updatePasswords];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)viewDidAppear:(BOOL)animated {
|
||||||
|
|
||||||
|
[super viewDidAppear:animated];
|
||||||
|
|
||||||
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
|
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserInContext:context];
|
||||||
|
if (![MPAlgorithmDefault tryMigrateUser:activeUser inContext:context])
|
||||||
|
[PearlOverlay showTemporaryOverlayWithTitle:@"Some Sites Need Upgrade" dismissAfter:2];
|
||||||
|
[context saveToStore];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)viewWillDisappear:(BOOL)animated {
|
- (void)viewWillDisappear:(BOOL)animated {
|
||||||
|
|
||||||
[super viewWillDisappear:animated];
|
[super viewWillDisappear:animated];
|
||||||
@ -85,6 +98,9 @@
|
|||||||
|
|
||||||
if ([segue.identifier isEqualToString:@"popdown"])
|
if ([segue.identifier isEqualToString:@"popdown"])
|
||||||
_popdownVC = segue.destinationViewController;
|
_popdownVC = segue.destinationViewController;
|
||||||
|
if ([segue.identifier isEqualToString:@"answers"])
|
||||||
|
((MPAnswersViewController *)segue.destinationViewController).site =
|
||||||
|
[[MPPasswordCell findAsSuperviewOf:sender] siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
|
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
|
||||||
@ -106,7 +122,7 @@ referenceSizeForHeaderInSection:(NSInteger)section {
|
|||||||
sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
|
sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||||
|
|
||||||
UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)collectionViewLayout;
|
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 );
|
return CGSizeMake( itemWidth, 100 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +135,7 @@ referenceSizeForHeaderInSection:(NSInteger)section {
|
|||||||
|
|
||||||
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
|
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
|
||||||
|
|
||||||
if (![MPiOSAppDelegate get].activeUserOID)
|
if (![MPiOSAppDelegate get].activeUserOID || !_fetchedResultsController)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
NSUInteger objects = ((id<NSFetchedResultsSectionInfo>)self.fetchedResultsController.sections[section]).numberOfObjects;
|
NSUInteger objects = ((id<NSFetchedResultsSectionInfo>)self.fetchedResultsController.sections[section]).numberOfObjects;
|
||||||
@ -131,7 +147,7 @@ referenceSizeForHeaderInSection:(NSInteger)section {
|
|||||||
|
|
||||||
MPPasswordCell *cell = [MPPasswordCell dequeueCellFromCollectionView:collectionView indexPath:indexPath];
|
MPPasswordCell *cell = [MPPasswordCell dequeueCellFromCollectionView:collectionView indexPath:indexPath];
|
||||||
if (indexPath.item < ((id<NSFetchedResultsSectionInfo>)self.fetchedResultsController.sections[indexPath.section]).numberOfObjects)
|
if (indexPath.item < ((id<NSFetchedResultsSectionInfo>)self.fetchedResultsController.sections[indexPath.section]).numberOfObjects)
|
||||||
[cell setElement:[self.fetchedResultsController objectAtIndexPath:indexPath] animated:NO];
|
[cell setSite:[self.fetchedResultsController objectAtIndexPath:indexPath] animated:NO];
|
||||||
else
|
else
|
||||||
[cell setTransientSite:self.query animated:NO];
|
[cell setTransientSite:self.query animated:NO];
|
||||||
|
|
||||||
@ -215,6 +231,7 @@ referenceSizeForHeaderInSection:(NSInteger)section {
|
|||||||
if (_passwordsDismissRecognizer)
|
if (_passwordsDismissRecognizer)
|
||||||
[self.view removeGestureRecognizer:_passwordsDismissRecognizer];
|
[self.view removeGestureRecognizer:_passwordsDismissRecognizer];
|
||||||
|
|
||||||
|
[self updatePasswords];
|
||||||
[UIView animateWithDuration:0.3f animations:^{
|
[UIView animateWithDuration:0.3f animations:^{
|
||||||
self.passwordCollectionView.backgroundColor = _backgroundColor;
|
self.passwordCollectionView.backgroundColor = _backgroundColor;
|
||||||
}];
|
}];
|
||||||
@ -223,6 +240,7 @@ referenceSizeForHeaderInSection:(NSInteger)section {
|
|||||||
|
|
||||||
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
|
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
|
||||||
|
|
||||||
|
searchBar.text = nil;
|
||||||
[searchBar resignFirstResponder];
|
[searchBar resignFirstResponder];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,7 +299,7 @@ referenceSizeForHeaderInSection:(NSInteger)section {
|
|||||||
|
|
||||||
_fetchedResultsController = nil;
|
_fetchedResultsController = nil;
|
||||||
self.passwordsSearchBar.text = nil;
|
self.passwordsSearchBar.text = nil;
|
||||||
[self updatePasswords];
|
[self.passwordCollectionView reloadData];
|
||||||
}],
|
}],
|
||||||
[[NSNotificationCenter defaultCenter]
|
[[NSNotificationCenter defaultCenter]
|
||||||
addObserverForName:UIApplicationDidBecomeActiveNotification object:nil
|
addObserverForName:UIApplicationDidBecomeActiveNotification object:nil
|
||||||
@ -296,8 +314,8 @@ referenceSizeForHeaderInSection:(NSInteger)section {
|
|||||||
[[NSNotificationCenter defaultCenter]
|
[[NSNotificationCenter defaultCenter]
|
||||||
addObserverForName:MPCheckConfigNotification object:nil
|
addObserverForName:MPCheckConfigNotification object:nil
|
||||||
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||||
[self updateConfigKey:note.object];
|
[self updateConfigKey:note.object];
|
||||||
}],
|
}],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -322,18 +340,19 @@ referenceSizeForHeaderInSection:(NSInteger)section {
|
|||||||
}];
|
}];
|
||||||
if (!_storeChangingObserver)
|
if (!_storeChangingObserver)
|
||||||
_storeChangingObserver = [[NSNotificationCenter defaultCenter]
|
_storeChangingObserver = [[NSNotificationCenter defaultCenter]
|
||||||
addObserverForName:USMStoreWillChangeNotification object:nil
|
addObserverForName:NSPersistentStoreCoordinatorStoresWillChangeNotification object:nil
|
||||||
queue:nil usingBlock:^(NSNotification *note) {
|
|
||||||
Strongify( self );
|
|
||||||
if (self->_mocObserver)
|
|
||||||
[[NSNotificationCenter defaultCenter] removeObserver:self->_mocObserver];
|
|
||||||
}];
|
|
||||||
if (!_storeChangedObserver)
|
|
||||||
_storeChangedObserver = [[NSNotificationCenter defaultCenter]
|
|
||||||
addObserverForName:USMStoreDidChangeNotification object:nil
|
|
||||||
queue:nil usingBlock:^(NSNotification *note) {
|
queue:nil usingBlock:^(NSNotification *note) {
|
||||||
Strongify( self );
|
Strongify( self );
|
||||||
self->_fetchedResultsController = nil;
|
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];
|
[self updatePasswords];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@ -353,7 +372,7 @@ referenceSizeForHeaderInSection:(NSInteger)section {
|
|||||||
if (!key || [key isEqualToString:NSStringFromSelector( @selector( dictationSearch ) )])
|
if (!key || [key isEqualToString:NSStringFromSelector( @selector( dictationSearch ) )])
|
||||||
self.passwordsSearchBar.keyboardType = [[MPiOSConfig get].dictationSearch boolValue]? UIKeyboardTypeDefault: UIKeyboardTypeURL;
|
self.passwordsSearchBar.keyboardType = [[MPiOSConfig get].dictationSearch boolValue]? UIKeyboardTypeDefault: UIKeyboardTypeURL;
|
||||||
if (!key || [key isEqualToString:NSStringFromSelector( @selector( hidePasswords ) )])
|
if (!key || [key isEqualToString:NSStringFromSelector( @selector( hidePasswords ) )])
|
||||||
[self updatePasswords];
|
[self.passwordCollectionView reloadData];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)updatePasswords {
|
- (void)updatePasswords {
|
||||||
@ -370,9 +389,10 @@ referenceSizeForHeaderInSection:(NSInteger)section {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[self.fetchedResultsController.managedObjectContext performBlock:^{
|
[self.fetchedResultsController.managedObjectContext performBlock:^{
|
||||||
NSMutableArray *oldSections = [NSMutableArray arrayWithCapacity:[[self.fetchedResultsController sections] count]];
|
NSArray *oldSectionInfos = [self.fetchedResultsController sections];
|
||||||
for (id<NSFetchedResultsSectionInfo> section in [self.fetchedResultsController sections])
|
NSMutableArray *oldSections = [[NSMutableArray alloc] initWithCapacity:[oldSectionInfos count]];
|
||||||
[oldSections addObject:[section.objects copy]];
|
for (id<NSFetchedResultsSectionInfo> sectionInfo in oldSectionInfos)
|
||||||
|
[oldSections addObject:[sectionInfo.objects copy]];
|
||||||
|
|
||||||
NSError *error = nil;
|
NSError *error = nil;
|
||||||
self.fetchedResultsController.fetchRequest.predicate =
|
self.fetchedResultsController.fetchRequest.predicate =
|
||||||
@ -380,7 +400,7 @@ referenceSizeForHeaderInSection:(NSInteger)section {
|
|||||||
[NSPredicate predicateWithFormat:@"user == %@ AND name BEGINSWITH[cd] %@", activeUserOID, query]:
|
[NSPredicate predicateWithFormat:@"user == %@ AND name BEGINSWITH[cd] %@", activeUserOID, query]:
|
||||||
[NSPredicate predicateWithFormat:@"user == %@", activeUserOID];
|
[NSPredicate predicateWithFormat:@"user == %@", activeUserOID];
|
||||||
if (![self.fetchedResultsController performFetch:&error])
|
if (![self.fetchedResultsController performFetch:&error])
|
||||||
err( @"Couldn't fetch elements: %@", error );
|
err( @"Couldn't fetch sites: %@", [error fullDescription] );
|
||||||
|
|
||||||
[self.passwordCollectionView performBatchUpdates:^{
|
[self.passwordCollectionView performBatchUpdates:^{
|
||||||
[self fetchedItemsDidUpdate];
|
[self fetchedItemsDidUpdate];
|
||||||
@ -392,10 +412,12 @@ referenceSizeForHeaderInSection:(NSInteger)section {
|
|||||||
[self.passwordCollectionView insertSections:[NSIndexSet indexSetWithIndex:section]];
|
[self.passwordCollectionView insertSections:[NSIndexSet indexSetWithIndex:section]];
|
||||||
else if (section >= toSections)
|
else if (section >= toSections)
|
||||||
[self.passwordCollectionView deleteSections:[NSIndexSet indexSetWithIndex:section]];
|
[self.passwordCollectionView deleteSections:[NSIndexSet indexSetWithIndex:section]];
|
||||||
else
|
else if (section < [oldSections count])
|
||||||
[self.passwordCollectionView reloadItemsFromArray:oldSections[section]
|
[self.passwordCollectionView reloadItemsFromArray:oldSections[section]
|
||||||
toArray:[[self.fetchedResultsController sections][section] objects]
|
toArray:[[self.fetchedResultsController sections][section] objects]
|
||||||
inSection:section];
|
inSection:section];
|
||||||
|
else
|
||||||
|
[self.passwordCollectionView reloadSections:[NSIndexSet indexSetWithIndex:section]];
|
||||||
}
|
}
|
||||||
} completion:^(BOOL finished) {
|
} completion:^(BOOL finished) {
|
||||||
if (finished)
|
if (finished)
|
||||||
@ -415,8 +437,9 @@ referenceSizeForHeaderInSection:(NSInteger)section {
|
|||||||
- (NSFetchedResultsController *)fetchedResultsController {
|
- (NSFetchedResultsController *)fetchedResultsController {
|
||||||
|
|
||||||
if (!_fetchedResultsController) {
|
if (!_fetchedResultsController) {
|
||||||
|
_showTransientItem = NO;
|
||||||
[MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) {
|
[MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) {
|
||||||
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )];
|
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
|
||||||
fetchRequest.sortDescriptors = @[
|
fetchRequest.sortDescriptors = @[
|
||||||
[[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector( lastUsed ) ) ascending:NO]
|
[[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector( lastUsed ) ) ascending:NO]
|
||||||
];
|
];
|
||||||
|
@ -102,7 +102,7 @@
|
|||||||
self.generatedTypeControl.selectedSegmentIndex = -1;
|
self.generatedTypeControl.selectedSegmentIndex = -1;
|
||||||
|
|
||||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
MPElementType defaultType = [[MPiOSAppDelegate get] activeUserInContext:context].defaultType = [self typeForSelectedSegment];
|
MPSiteType defaultType = [[MPiOSAppDelegate get] activeUserInContext:context].defaultType = [self typeForSelectedSegment];
|
||||||
[context saveToStore];
|
[context saveToStore];
|
||||||
|
|
||||||
PearlMainQueue( ^{
|
PearlMainQueue( ^{
|
||||||
@ -179,31 +179,31 @@
|
|||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (enum MPElementType)typeForSelectedSegment {
|
- (enum MPSiteType)typeForSelectedSegment {
|
||||||
|
|
||||||
NSInteger selectedGeneratedIndex = self.generatedTypeControl.selectedSegmentIndex;
|
NSInteger selectedGeneratedIndex = self.generatedTypeControl.selectedSegmentIndex;
|
||||||
NSInteger selectedStoredIndex = self.storedTypeControl.selectedSegmentIndex;
|
NSInteger selectedStoredIndex = self.storedTypeControl.selectedSegmentIndex;
|
||||||
|
|
||||||
switch (selectedGeneratedIndex) {
|
switch (selectedGeneratedIndex) {
|
||||||
case 0:
|
case 0:
|
||||||
return MPElementTypeGeneratedMaximum;
|
return MPSiteTypeGeneratedMaximum;
|
||||||
case 1:
|
case 1:
|
||||||
return MPElementTypeGeneratedLong;
|
return MPSiteTypeGeneratedLong;
|
||||||
case 2:
|
case 2:
|
||||||
return MPElementTypeGeneratedMedium;
|
return MPSiteTypeGeneratedMedium;
|
||||||
case 3:
|
case 3:
|
||||||
return MPElementTypeGeneratedBasic;
|
return MPSiteTypeGeneratedBasic;
|
||||||
case 4:
|
case 4:
|
||||||
return MPElementTypeGeneratedShort;
|
return MPSiteTypeGeneratedShort;
|
||||||
case 5:
|
case 5:
|
||||||
return MPElementTypeGeneratedPIN;
|
return MPSiteTypeGeneratedPIN;
|
||||||
default:
|
default:
|
||||||
|
|
||||||
switch (selectedStoredIndex) {
|
switch (selectedStoredIndex) {
|
||||||
case 0:
|
case 0:
|
||||||
return MPElementTypeStoredPersonal;
|
return MPSiteTypeStoredPersonal;
|
||||||
case 1:
|
case 1:
|
||||||
return MPElementTypeStoredDevicePrivate;
|
return MPSiteTypeStoredDevicePrivate;
|
||||||
default:
|
default:
|
||||||
Throw( @"unsupported selected type index: generated=%ld, stored=%ld", (long)selectedGeneratedIndex,
|
Throw( @"unsupported selected type index: generated=%ld, stored=%ld", (long)selectedGeneratedIndex,
|
||||||
(long)selectedStoredIndex );
|
(long)selectedStoredIndex );
|
||||||
@ -211,32 +211,32 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSInteger)generatedSegmentIndexForType:(MPElementType)type {
|
- (NSInteger)generatedSegmentIndexForType:(MPSiteType)type {
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case MPElementTypeGeneratedMaximum:
|
case MPSiteTypeGeneratedMaximum:
|
||||||
return 0;
|
return 0;
|
||||||
case MPElementTypeGeneratedLong:
|
case MPSiteTypeGeneratedLong:
|
||||||
return 1;
|
return 1;
|
||||||
case MPElementTypeGeneratedMedium:
|
case MPSiteTypeGeneratedMedium:
|
||||||
return 2;
|
return 2;
|
||||||
case MPElementTypeGeneratedBasic:
|
case MPSiteTypeGeneratedBasic:
|
||||||
return 3;
|
return 3;
|
||||||
case MPElementTypeGeneratedShort:
|
case MPSiteTypeGeneratedShort:
|
||||||
return 4;
|
return 4;
|
||||||
case MPElementTypeGeneratedPIN:
|
case MPSiteTypeGeneratedPIN:
|
||||||
return 5;
|
return 5;
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSInteger)storedSegmentIndexForType:(MPElementType)type {
|
- (NSInteger)storedSegmentIndexForType:(MPSiteType)type {
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case MPElementTypeStoredPersonal:
|
case MPSiteTypeStoredPersonal:
|
||||||
return 0;
|
return 0;
|
||||||
case MPElementTypeStoredDevicePrivate:
|
case MPSiteTypeStoredDevicePrivate:
|
||||||
return 1;
|
return 1;
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
|
28
MasterPassword/ObjC/iOS/MPStoreViewController.h
Normal file
28
MasterPassword/ObjC/iOS/MPStoreViewController.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
//
|
||||||
|
// MPPreferencesViewController.h
|
||||||
|
// MasterPassword-iOS
|
||||||
|
//
|
||||||
|
// Created by Maarten Billemont on 04/06/12.
|
||||||
|
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
@class MPStoreProductCell;
|
||||||
|
|
||||||
|
@interface MPStoreViewController : PearlMutableStaticTableViewController
|
||||||
|
|
||||||
|
@property(weak, nonatomic) IBOutlet MPStoreProductCell *generateLoginCell;
|
||||||
|
@property(weak, nonatomic) IBOutlet MPStoreProductCell *generateAnswersCell;
|
||||||
|
@property(weak, nonatomic) IBOutlet MPStoreProductCell *iOSIntegrationCell;
|
||||||
|
@property(weak, nonatomic) IBOutlet MPStoreProductCell *touchIDCell;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface MPStoreProductCell : UITableViewCell
|
||||||
|
|
||||||
|
@property(nonatomic) IBOutlet UILabel *priceLabel;
|
||||||
|
@property(nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;
|
||||||
|
@property(nonatomic) IBOutlet UIView *purchasedIndicator;
|
||||||
|
|
||||||
|
@end
|
220
MasterPassword/ObjC/iOS/MPStoreViewController.m
Normal file
220
MasterPassword/ObjC/iOS/MPStoreViewController.m
Normal file
@ -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 <StoreKit/StoreKit.h>
|
||||||
|
|
||||||
|
@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
|
@ -13,11 +13,11 @@
|
|||||||
@protocol MPTypeDelegate<NSObject>
|
@protocol MPTypeDelegate<NSObject>
|
||||||
|
|
||||||
@required
|
@required
|
||||||
- (void)didSelectType:(MPElementType)type;
|
- (void)didSelectType:(MPSiteType)type;
|
||||||
- (MPElementType)selectedType;
|
- (MPSiteType)selectedType;
|
||||||
|
|
||||||
@optional
|
@optional
|
||||||
- (MPElementEntity *)selectedElement;
|
- (MPSiteEntity *)selectedSite;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
@interface MPTypeViewController()
|
@interface MPTypeViewController()
|
||||||
|
|
||||||
- (MPElementType)typeAtIndexPath:(NSIndexPath *)indexPath;
|
- (MPSiteType)typeAtIndexPath:(NSIndexPath *)indexPath;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@ -63,25 +63,25 @@
|
|||||||
|
|
||||||
UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
|
UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
|
||||||
|
|
||||||
MPElementEntity *selectedElement = nil;
|
MPSiteEntity *selectedSite = nil;
|
||||||
if ([self.delegate respondsToSelector:@selector(selectedElement)])
|
if ([self.delegate respondsToSelector:@selector( selectedSite )])
|
||||||
selectedElement = [self.delegate selectedElement];
|
selectedSite = [self.delegate selectedSite];
|
||||||
|
|
||||||
MPElementType cellType = [self typeAtIndexPath:indexPath];
|
MPSiteType cellType = [self typeAtIndexPath:indexPath];
|
||||||
MPElementType selectedType = selectedElement? selectedElement.type: [self.delegate selectedType];
|
MPSiteType selectedType = selectedSite? selectedSite.type: [self.delegate selectedType];
|
||||||
cell.selected = (selectedType == cellType);
|
cell.selected = (selectedType == cellType);
|
||||||
|
|
||||||
if (cellType != (MPElementType)NSNotFound && cellType & MPElementTypeClassGenerated) {
|
if (cellType != (MPSiteType)NSNotFound && cellType & MPSiteTypeClassGenerated) {
|
||||||
[(UITextField *)[cell viewWithTag:2] setText:@"..."];
|
[(UITextField *)[cell viewWithTag:2] setText:@"..."];
|
||||||
|
|
||||||
NSString *name = selectedElement.name;
|
NSString *name = selectedSite.name;
|
||||||
NSUInteger counter = 0;
|
NSUInteger counter = 0;
|
||||||
if ([selectedElement isKindOfClass:[MPElementGeneratedEntity class]])
|
if ([selectedSite isKindOfClass:[MPGeneratedSiteEntity class]])
|
||||||
counter = ((MPElementGeneratedEntity *)selectedElement).counter;
|
counter = ((MPGeneratedSiteEntity *)selectedSite).counter;
|
||||||
|
|
||||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0 ), ^{
|
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0 ), ^{
|
||||||
NSString *typeContent = [MPAlgorithmDefault generateContentNamed:name ofType:cellType
|
NSString *typeContent = [MPAlgorithmDefault generatePasswordForSiteNamed:name ofType:cellType
|
||||||
withCounter:counter usingKey:[MPiOSAppDelegate get].key];
|
withCounter:counter usingKey:[MPiOSAppDelegate get].key];
|
||||||
|
|
||||||
dispatch_async( dispatch_get_main_queue(), ^{
|
dispatch_async( dispatch_get_main_queue(), ^{
|
||||||
[(UITextField *)[[tableView cellForRowAtIndexPath:indexPath] viewWithTag:2] setText:typeContent];
|
[(UITextField *)[[tableView cellForRowAtIndexPath:indexPath] viewWithTag:2] setText:typeContent];
|
||||||
@ -96,8 +96,8 @@
|
|||||||
|
|
||||||
NSAssert(self.navigationController.topViewController == self, @"Not the currently active navigation item.");
|
NSAssert(self.navigationController.topViewController == self, @"Not the currently active navigation item.");
|
||||||
|
|
||||||
MPElementType type = [self typeAtIndexPath:indexPath];
|
MPSiteType type = [self typeAtIndexPath:indexPath];
|
||||||
if (type == (MPElementType)NSNotFound)
|
if (type == (MPSiteType)NSNotFound)
|
||||||
// Selected a non-type row.
|
// Selected a non-type row.
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -105,31 +105,31 @@
|
|||||||
[self.navigationController popViewControllerAnimated:YES];
|
[self.navigationController popViewControllerAnimated:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MPElementType)typeAtIndexPath:(NSIndexPath *)indexPath {
|
- (MPSiteType)typeAtIndexPath:(NSIndexPath *)indexPath {
|
||||||
|
|
||||||
switch (indexPath.section) {
|
switch (indexPath.section) {
|
||||||
case 0: {
|
case 0: {
|
||||||
// Generated
|
// Generated
|
||||||
switch (indexPath.row) {
|
switch (indexPath.row) {
|
||||||
case 0:
|
case 0:
|
||||||
return (MPElementType)NSNotFound;
|
return (MPSiteType)NSNotFound;
|
||||||
case 1:
|
case 1:
|
||||||
return MPElementTypeGeneratedMaximum;
|
return MPSiteTypeGeneratedMaximum;
|
||||||
case 2:
|
case 2:
|
||||||
return MPElementTypeGeneratedLong;
|
return MPSiteTypeGeneratedLong;
|
||||||
case 3:
|
case 3:
|
||||||
return MPElementTypeGeneratedMedium;
|
return MPSiteTypeGeneratedMedium;
|
||||||
case 4:
|
case 4:
|
||||||
return MPElementTypeGeneratedBasic;
|
return MPSiteTypeGeneratedBasic;
|
||||||
case 5:
|
case 5:
|
||||||
return MPElementTypeGeneratedShort;
|
return MPSiteTypeGeneratedShort;
|
||||||
case 6:
|
case 6:
|
||||||
return MPElementTypeGeneratedPIN;
|
return MPSiteTypeGeneratedPIN;
|
||||||
case 7:
|
case 7:
|
||||||
return (MPElementType)NSNotFound;
|
return (MPSiteType)NSNotFound;
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
Throw(@"Unsupported row: %ld, when selecting generated element type.", (long)indexPath.row);
|
Throw(@"Unsupported row: %ld, when selecting generated site type.", (long)indexPath.row);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -138,22 +138,22 @@
|
|||||||
// Stored
|
// Stored
|
||||||
switch (indexPath.row) {
|
switch (indexPath.row) {
|
||||||
case 0:
|
case 0:
|
||||||
return (MPElementType)NSNotFound;
|
return (MPSiteType)NSNotFound;
|
||||||
case 1:
|
case 1:
|
||||||
return MPElementTypeStoredPersonal;
|
return MPSiteTypeStoredPersonal;
|
||||||
case 2:
|
case 2:
|
||||||
return MPElementTypeStoredDevicePrivate;
|
return MPSiteTypeStoredDevicePrivate;
|
||||||
case 3:
|
case 3:
|
||||||
return (MPElementType)NSNotFound;
|
return (MPSiteType)NSNotFound;
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
Throw(@"Unsupported row: %ld, when selecting stored element type.", (long)indexPath.row);
|
Throw(@"Unsupported row: %ld, when selecting stored site type.", (long)indexPath.row);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Throw(@"Unsupported section: %ld, when selecting element type.", (long)indexPath.section);
|
Throw(@"Unsupported section: %ld, when selecting site type.", (long)indexPath.section);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
@interface MPUsersViewController : UIViewController <UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UITextFieldDelegate>
|
@interface MPUsersViewController : UIViewController <UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UITextFieldDelegate>
|
||||||
|
|
||||||
@property (strong, nonatomic) IBOutlet UINavigationBar *navigationBar;
|
|
||||||
@property(weak, nonatomic) IBOutlet UIView *userSelectionContainer;
|
@property(weak, nonatomic) IBOutlet UIView *userSelectionContainer;
|
||||||
@property(weak, nonatomic) IBOutlet UIButton *marqueeButton;
|
@property(weak, nonatomic) IBOutlet UIButton *marqueeButton;
|
||||||
@property(weak, nonatomic) IBOutlet UIView *gitTipTip;
|
@property(weak, nonatomic) IBOutlet UIView *gitTipTip;
|
||||||
@ -32,7 +31,6 @@
|
|||||||
@property(weak, nonatomic) IBOutlet UIView *footerContainer;
|
@property(weak, nonatomic) IBOutlet UIView *footerContainer;
|
||||||
@property(weak, nonatomic) IBOutlet UIActivityIndicatorView *storeLoadingActivity;
|
@property(weak, nonatomic) IBOutlet UIActivityIndicatorView *storeLoadingActivity;
|
||||||
@property(weak, nonatomic) IBOutlet UICollectionView *avatarCollectionView;
|
@property(weak, nonatomic) IBOutlet UICollectionView *avatarCollectionView;
|
||||||
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *navigationBarToTopConstraint;
|
|
||||||
@property (strong, nonatomic) IBOutlet UIButton *nextAvatarButton;
|
@property (strong, nonatomic) IBOutlet UIButton *nextAvatarButton;
|
||||||
@property (strong, nonatomic) IBOutlet UIButton *previousAvatarButton;
|
@property (strong, nonatomic) IBOutlet UIButton *previousAvatarButton;
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||||
*
|
*
|
||||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
*
|
*
|
||||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//
|
//
|
||||||
// MPCombinedViewController.h
|
// MPCombinedViewController.h
|
||||||
@ -24,8 +24,9 @@
|
|||||||
#import "MPAppDelegate_Key.h"
|
#import "MPAppDelegate_Key.h"
|
||||||
#import "PearlSizedTextView.h"
|
#import "PearlSizedTextView.h"
|
||||||
#import "MPWebViewController.h"
|
#import "MPWebViewController.h"
|
||||||
|
#import "UIView+FontScale.h"
|
||||||
|
|
||||||
typedef NS_ENUM(NSUInteger, MPActiveUserState) {
|
typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
|
||||||
/** The users are all inactive */
|
/** The users are all inactive */
|
||||||
MPActiveUserStateNone,
|
MPActiveUserStateNone,
|
||||||
/** The selected user is activated and being logged in with */
|
/** 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.view.backgroundColor = [UIColor clearColor];
|
||||||
self.avatarCollectionView.allowsMultipleSelection = YES;
|
self.avatarCollectionView.allowsMultipleSelection = YES;
|
||||||
[self.entryField addTarget:self action:@selector(textFieldEditingChanged:) forControlEvents:UIControlEventEditingChanged];
|
[self.entryField addTarget:self action:@selector( textFieldEditingChanged: ) forControlEvents:UIControlEventEditingChanged];
|
||||||
|
|
||||||
[self setActive:YES animated:NO];
|
[self setActive:YES animated:NO];
|
||||||
}
|
}
|
||||||
@ -82,15 +83,6 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
|
|||||||
[super viewWillAppear:animated];
|
[super viewWillAppear:animated];
|
||||||
|
|
||||||
self.userSelectionContainer.alpha = 0;
|
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 {
|
- (void)viewWillDisappear:(BOOL)animated {
|
||||||
@ -103,9 +95,29 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
|
|||||||
[self.marqueeTipTimer invalidate];
|
[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];
|
[self.avatarCollectionView.collectionViewLayout invalidateLayout];
|
||||||
}
|
}
|
||||||
@ -257,15 +269,25 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
|
|||||||
|
|
||||||
#pragma mark - UICollectionViewDelegateFlowLayout
|
#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
|
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout
|
||||||
sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
|
sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||||
|
|
||||||
if (collectionView == self.avatarCollectionView) {
|
CGSize parentSize = self.avatarCollectionView.bounds.size;
|
||||||
CGSize parentSize = self.avatarCollectionView.bounds.size;
|
return CGSizeMake( parentSize.width / 2, parentSize.height );
|
||||||
return CGSizeMake( parentSize.width / 2, parentSize.height );
|
|
||||||
}
|
|
||||||
|
|
||||||
Throw(@"unexpected collection view: %@", collectionView);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - UICollectionViewDataSource
|
#pragma mark - UICollectionViewDataSource
|
||||||
@ -275,14 +297,15 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
|
|||||||
if (collectionView == self.avatarCollectionView)
|
if (collectionView == self.avatarCollectionView)
|
||||||
return [self.userIDs count] + 1;
|
return [self.userIDs count] + 1;
|
||||||
|
|
||||||
Throw(@"unexpected collection view: %@", collectionView);
|
Throw( @"unexpected collection view: %@", collectionView );
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||||
|
|
||||||
if (collectionView == self.avatarCollectionView) {
|
if (collectionView == self.avatarCollectionView) {
|
||||||
MPAvatarCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:[MPAvatarCell reuseIdentifier] forIndexPath:indexPath];
|
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 updateModeForAvatar:cell atIndexPath:indexPath animated:NO];
|
||||||
[self updateVisibilityForAvatar: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]
|
MPUserEntity *user = [self userForIndexPath:indexPath inContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]
|
||||||
isNew:&isNew];
|
isNew:&isNew];
|
||||||
if (isNew)
|
if (isNew)
|
||||||
// New User
|
// New User
|
||||||
cell.avatar = MPAvatarAdd;
|
cell.avatar = MPAvatarAdd;
|
||||||
else {
|
else {
|
||||||
// Existing User
|
// Existing User
|
||||||
@ -301,7 +324,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
|
|||||||
return cell;
|
return cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
Throw(@"unexpected collection view: %@", collectionView);
|
Throw( @"unexpected collection view: %@", collectionView );
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
|
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||||
@ -356,7 +379,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
|
|||||||
|
|
||||||
if ([recognizer.view isKindOfClass:[MPAvatarCell class]]) {
|
if ([recognizer.view isKindOfClass:[MPAvatarCell class]]) {
|
||||||
if (recognizer.state != UIGestureRecognizerStateBegan)
|
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;
|
return;
|
||||||
|
|
||||||
MPAvatarCell *avatarCell = (MPAvatarCell *)recognizer.view;
|
MPAvatarCell *avatarCell = (MPAvatarCell *)recognizer.view;
|
||||||
@ -371,40 +394,40 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
|
|||||||
[PearlSheet showSheetWithTitle:user.name
|
[PearlSheet showSheetWithTitle:user.name
|
||||||
viewStyle:UIActionSheetStyleBlackTranslucent
|
viewStyle:UIActionSheetStyleBlackTranslucent
|
||||||
initSheet:nil tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) {
|
initSheet:nil tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) {
|
||||||
if (buttonIndex == [sheet cancelButtonIndex])
|
if (buttonIndex == [sheet cancelButtonIndex])
|
||||||
return;
|
|
||||||
|
|
||||||
if (buttonIndex == [sheet destructiveButtonIndex]) {
|
|
||||||
// Delete User
|
|
||||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
|
||||||
MPUserEntity *user_ = [MPUserEntity existingObjectWithID:userID inContext:context];
|
|
||||||
if (!user_)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[context deleteObject:user_];
|
if (buttonIndex == [sheet destructiveButtonIndex]) {
|
||||||
[context saveToStore];
|
// Delete User
|
||||||
[self reloadUsers]; // I do NOT understand why our ObjectsDidChangeNotification isn't firing on saveToStore.
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
}];
|
MPUserEntity *user_ = [MPUserEntity existingObjectWithID:userID inContext:context];
|
||||||
return;
|
if (!user_)
|
||||||
}
|
return;
|
||||||
|
|
||||||
if (buttonIndex == [sheet firstOtherButtonIndex])
|
[context deleteObject:user_];
|
||||||
// Reset Password
|
[context saveToStore];
|
||||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
[self reloadUsers]; // I do NOT understand why our ObjectsDidChangeNotification isn't firing on saveToStore.
|
||||||
MPUserEntity *user_ = [MPUserEntity existingObjectWithID:userID inContext:context];
|
}];
|
||||||
if (!user_)
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
[[MPiOSAppDelegate get] changeMasterPasswordFor:user_ saveInContext:context didResetBlock:^{
|
if (buttonIndex == [sheet firstOtherButtonIndex])
|
||||||
PearlMainQueue( ^{
|
// Reset Password
|
||||||
NSIndexPath *avatarIndexPath = [self.avatarCollectionView indexPathForCell:avatarCell];
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
[self.avatarCollectionView selectItemAtIndexPath:avatarIndexPath animated:NO
|
MPUserEntity *user_ = [MPUserEntity existingObjectWithID:userID inContext:context];
|
||||||
scrollPosition:UICollectionViewScrollPositionNone];
|
if (!user_)
|
||||||
[self collectionView:self.avatarCollectionView didSelectItemAtIndexPath:avatarIndexPath];
|
return;
|
||||||
} );
|
|
||||||
}];
|
[[MPiOSAppDelegate get] changeMasterPasswordFor:user_ saveInContext:context didResetBlock:^{
|
||||||
}];
|
PearlMainQueue( ^{
|
||||||
} cancelTitle:[PearlStrings get].commonButtonCancel
|
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];
|
destructiveTitle:@"Delete User" otherTitles:@"Reset Password", nil];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -420,7 +443,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
|
|||||||
CGPointPlusCGPoint( *targetContentOffset, offsetToCenter )];
|
CGPointPlusCGPoint( *targetContentOffset, offsetToCenter )];
|
||||||
CGPoint targetCenter = [self.avatarCollectionView layoutAttributesForItemAtIndexPath:avatarIndexPath].center;
|
CGPoint targetCenter = [self.avatarCollectionView layoutAttributesForItemAtIndexPath:avatarIndexPath].center;
|
||||||
*targetContentOffset = CGPointMinusCGPoint( targetCenter, offsetToCenter );
|
*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];
|
return [MPUserEntity existingObjectWithID:self.userIDs[indexPath.item] inContext:context];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)updateAvatars {
|
- (void)updateAvatarVisibility {
|
||||||
|
|
||||||
self.previousAvatarButton.alpha = 0;
|
self.previousAvatarButton.alpha = 0;
|
||||||
self.nextAvatarButton.alpha = 0;
|
self.nextAvatarButton.alpha = 0;
|
||||||
for (NSIndexPath *indexPath in self.avatarCollectionView.indexPathsForVisibleItems)
|
for (NSIndexPath *indexPath in self.avatarCollectionView.indexPathsForVisibleItems) {
|
||||||
[self updateAvatarAtIndexPath:indexPath];
|
MPAvatarCell *cell = (MPAvatarCell *)[self.avatarCollectionView cellForItemAtIndexPath:indexPath];
|
||||||
}
|
[self updateVisibilityForAvatar:cell atIndexPath:indexPath animated:NO];
|
||||||
|
}
|
||||||
- (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];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)updateModeForAvatar:(MPAvatarCell *)avatarCell atIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated {
|
- (void)updateModeForAvatar:(MPAvatarCell *)avatarCell atIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated {
|
||||||
@ -550,7 +568,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
|
|||||||
self.avatarCollectionView.contentOffset.x;
|
self.avatarCollectionView.contentOffset.x;
|
||||||
CGFloat max = self.avatarCollectionView.bounds.size.width;
|
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];
|
[cell setVisibility:visibility animated:animated];
|
||||||
|
|
||||||
if (cell.newUser) {
|
if (cell.newUser) {
|
||||||
@ -559,7 +577,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)afterUpdatesMainQueue:(void (^)(void))block {
|
- (void)afterUpdatesMainQueue:(void ( ^ )(void))block {
|
||||||
|
|
||||||
[_afterUpdates addOperationWithBlock:^{
|
[_afterUpdates addOperationWithBlock:^{
|
||||||
PearlMainQueue( block );
|
PearlMainQueue( block );
|
||||||
@ -571,32 +589,32 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
|
|||||||
if ([_notificationObservers count])
|
if ([_notificationObservers count])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Weakify(self);
|
Weakify( self );
|
||||||
_notificationObservers = @[
|
_notificationObservers = @[
|
||||||
[[NSNotificationCenter defaultCenter]
|
[[NSNotificationCenter defaultCenter]
|
||||||
addObserverForName:UIApplicationWillResignActiveNotification object:nil
|
addObserverForName:UIApplicationWillResignActiveNotification object:nil
|
||||||
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||||
Strongify(self);
|
Strongify( self );
|
||||||
|
|
||||||
// [self emergencyCloseAnimated:NO];
|
// [self emergencyCloseAnimated:NO];
|
||||||
self.userSelectionContainer.alpha = 0;
|
self.userSelectionContainer.alpha = 0;
|
||||||
}],
|
}],
|
||||||
[[NSNotificationCenter defaultCenter]
|
[[NSNotificationCenter defaultCenter]
|
||||||
addObserverForName:UIApplicationDidBecomeActiveNotification object:nil
|
addObserverForName:UIApplicationDidBecomeActiveNotification object:nil
|
||||||
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||||
Strongify(self);
|
Strongify( self );
|
||||||
|
|
||||||
[self reloadUsers];
|
[self reloadUsers];
|
||||||
|
|
||||||
[UIView animateWithDuration:1 animations:^{
|
[UIView animateWithDuration:1 animations:^{
|
||||||
self.userSelectionContainer.alpha = 1;
|
self.userSelectionContainer.alpha = 1;
|
||||||
}];
|
}];
|
||||||
}],
|
}],
|
||||||
];
|
];
|
||||||
|
|
||||||
[self observeKeyPath:@"avatarCollectionView.contentOffset" withBlock:
|
[self observeKeyPath:@"avatarCollectionView.contentOffset" withBlock:
|
||||||
^(id from, id to, NSKeyValueChange cause, MPUsersViewController *_self) {
|
^(id from, id to, NSKeyValueChange cause, MPUsersViewController *_self) {
|
||||||
[_self updateAvatars];
|
[_self updateAvatarVisibility];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -611,7 +629,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
|
|||||||
|
|
||||||
- (void)observeStore {
|
- (void)observeStore {
|
||||||
|
|
||||||
Weakify(self);
|
Weakify( self );
|
||||||
|
|
||||||
NSManagedObjectContext *mainContext = [MPiOSAppDelegate managedObjectContextForMainThreadIfReady];
|
NSManagedObjectContext *mainContext = [MPiOSAppDelegate managedObjectContextForMainThreadIfReady];
|
||||||
[UIView animateWithDuration:0.3f animations:^{
|
[UIView animateWithDuration:0.3f animations:^{
|
||||||
@ -626,10 +644,10 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
|
|||||||
_mocObserver = [[NSNotificationCenter defaultCenter]
|
_mocObserver = [[NSNotificationCenter defaultCenter]
|
||||||
addObserverForName:NSManagedObjectContextObjectsDidChangeNotification object:mainContext
|
addObserverForName:NSManagedObjectContextObjectsDidChangeNotification object:mainContext
|
||||||
queue:nil usingBlock:^(NSNotification *note) {
|
queue:nil usingBlock:^(NSNotification *note) {
|
||||||
Strongify(self);
|
Strongify( self );
|
||||||
NSSet *insertedObjects = note.userInfo[NSInsertedObjectsKey];
|
NSSet *insertedObjects = note.userInfo[NSInsertedObjectsKey];
|
||||||
NSSet *deletedObjects = note.userInfo[NSDeletedObjectsKey];
|
NSSet *deletedObjects = note.userInfo[NSDeletedObjectsKey];
|
||||||
if ([[NSSetUnion(insertedObjects, deletedObjects)
|
if ([[NSSetUnion( insertedObjects, deletedObjects )
|
||||||
filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
|
filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
|
||||||
return [evaluatedObject isKindOfClass:[MPUserEntity class]];
|
return [evaluatedObject isKindOfClass:[MPUserEntity class]];
|
||||||
}]] count])
|
}]] count])
|
||||||
@ -637,17 +655,18 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
|
|||||||
}];
|
}];
|
||||||
if (!_storeChangingObserver)
|
if (!_storeChangingObserver)
|
||||||
_storeChangingObserver = [[NSNotificationCenter defaultCenter]
|
_storeChangingObserver = [[NSNotificationCenter defaultCenter]
|
||||||
addObserverForName:USMStoreWillChangeNotification object:nil
|
addObserverForName:NSPersistentStoreCoordinatorStoresWillChangeNotification object:nil
|
||||||
queue:nil usingBlock:^(NSNotification *note) {
|
queue:nil usingBlock:^(NSNotification *note) {
|
||||||
Strongify(self);
|
Strongify( self );
|
||||||
if (self->_mocObserver)
|
if (self->_mocObserver)
|
||||||
[[NSNotificationCenter defaultCenter] removeObserver:self->_mocObserver];
|
[[NSNotificationCenter defaultCenter] removeObserver:self->_mocObserver];
|
||||||
|
self.userIDs = nil;
|
||||||
}];
|
}];
|
||||||
if (!_storeChangedObserver)
|
if (!_storeChangedObserver)
|
||||||
_storeChangedObserver = [[NSNotificationCenter defaultCenter]
|
_storeChangedObserver = [[NSNotificationCenter defaultCenter]
|
||||||
addObserverForName:USMStoreDidChangeNotification object:nil
|
addObserverForName:NSPersistentStoreCoordinatorStoresDidChangeNotification object:nil
|
||||||
queue:nil usingBlock:^(NSNotification *note) {
|
queue:nil usingBlock:^(NSNotification *note) {
|
||||||
Strongify(self);
|
Strongify( self );
|
||||||
[self reloadUsers];
|
[self reloadUsers];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@ -666,15 +685,15 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
|
|||||||
|
|
||||||
[self afterUpdatesMainQueue:^{
|
[self afterUpdatesMainQueue:^{
|
||||||
[self observeStore];
|
[self observeStore];
|
||||||
[MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) {
|
if (![MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) {
|
||||||
NSError *error = nil;
|
NSError *error = nil;
|
||||||
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
|
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
|
||||||
fetchRequest.sortDescriptors = @[
|
fetchRequest.sortDescriptors = @[
|
||||||
[NSSortDescriptor sortDescriptorWithKey:NSStringFromSelector( @selector(lastUsed) ) ascending:NO]
|
[NSSortDescriptor sortDescriptorWithKey:NSStringFromSelector( @selector( lastUsed ) ) ascending:NO]
|
||||||
];
|
];
|
||||||
NSArray *users = [mainContext executeFetchRequest:fetchRequest error:&error];
|
NSArray *users = [mainContext executeFetchRequest:fetchRequest error:&error];
|
||||||
if (!users) {
|
if (!users) {
|
||||||
err(@"Failed to load users: %@", error);
|
err( @"Failed to load users: %@", [error fullDescription] );
|
||||||
self.userIDs = nil;
|
self.userIDs = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -682,7 +701,8 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
|
|||||||
for (MPUserEntity *user in users)
|
for (MPUserEntity *user in users)
|
||||||
[userIDs addObject:user.objectID];
|
[userIDs addObject:user.objectID];
|
||||||
self.userIDs = userIDs;
|
self.userIDs = userIDs;
|
||||||
}];
|
}])
|
||||||
|
self.userIDs = nil;
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -741,24 +761,11 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the entry container's contents.
|
||||||
[_afterUpdates setSuspended:YES];
|
[_afterUpdates setSuspended:YES];
|
||||||
__block BOOL requestFirstResponder = NO;
|
__block BOOL requestFirstResponder = NO;
|
||||||
|
[self.view layoutIfNeeded];
|
||||||
[UIView animateWithDuration:animated? 0.4f: 0 animations:^{
|
[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) {
|
switch (activeUserState) {
|
||||||
case MPActiveUserStateNone:
|
case MPActiveUserStateNone:
|
||||||
break;
|
break;
|
||||||
@ -798,7 +805,6 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
|
|||||||
// Manage the entry container depending on whether a user is activate or not.
|
// Manage the entry container depending on whether a user is activate or not.
|
||||||
switch (activeUserState) {
|
switch (activeUserState) {
|
||||||
case MPActiveUserStateNone: {
|
case MPActiveUserStateNone: {
|
||||||
[self.navigationBarToTopConstraint updatePriority:UILayoutPriorityDefaultHigh];
|
|
||||||
self.avatarCollectionView.scrollEnabled = YES;
|
self.avatarCollectionView.scrollEnabled = YES;
|
||||||
self.entryContainer.alpha = 0;
|
self.entryContainer.alpha = 0;
|
||||||
self.footerContainer.alpha = 1;
|
self.footerContainer.alpha = 1;
|
||||||
@ -808,7 +814,6 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
|
|||||||
case MPActiveUserStateUserName:
|
case MPActiveUserStateUserName:
|
||||||
case MPActiveUserStateMasterPasswordChoice:
|
case MPActiveUserStateMasterPasswordChoice:
|
||||||
case MPActiveUserStateMasterPasswordConfirmation: {
|
case MPActiveUserStateMasterPasswordConfirmation: {
|
||||||
[self.navigationBarToTopConstraint updatePriority:UILayoutPriorityDefaultHigh];
|
|
||||||
self.avatarCollectionView.scrollEnabled = NO;
|
self.avatarCollectionView.scrollEnabled = NO;
|
||||||
self.entryContainer.alpha = 1;
|
self.entryContainer.alpha = 1;
|
||||||
self.footerContainer.alpha = 1;
|
self.footerContainer.alpha = 1;
|
||||||
@ -816,7 +821,6 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MPActiveUserStateMinimized: {
|
case MPActiveUserStateMinimized: {
|
||||||
[self.navigationBarToTopConstraint updatePriority:1];
|
|
||||||
self.avatarCollectionView.scrollEnabled = NO;
|
self.avatarCollectionView.scrollEnabled = NO;
|
||||||
self.entryContainer.alpha = 0;
|
self.entryContainer.alpha = 0;
|
||||||
self.footerContainer.alpha = 0;
|
self.footerContainer.alpha = 0;
|
||||||
@ -832,6 +836,18 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
|
|||||||
[self.entryField resignFirstResponder];
|
[self.entryField resignFirstResponder];
|
||||||
if (requestFirstResponder)
|
if (requestFirstResponder)
|
||||||
[self.entryField becomeFirstResponder];
|
[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
|
#pragma mark - Actions
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
#import <MessageUI/MessageUI.h>
|
|
||||||
|
|
||||||
#import "MPAppDelegate_Shared.h"
|
#import "MPAppDelegate_Shared.h"
|
||||||
|
|
||||||
|
@ -11,26 +11,25 @@
|
|||||||
#import "MPAppDelegate_Store.h"
|
#import "MPAppDelegate_Store.h"
|
||||||
#import "IASKSettingsReader.h"
|
#import "IASKSettingsReader.h"
|
||||||
|
|
||||||
@interface MPiOSAppDelegate()
|
@interface MPiOSAppDelegate()<UIDocumentInteractionControllerDelegate>
|
||||||
|
|
||||||
|
@property(nonatomic, strong) UIDocumentInteractionController *interactionController;
|
||||||
|
|
||||||
@property(nonatomic, weak) PearlAlert *handleCloudDisabledAlert;
|
|
||||||
@property(nonatomic, weak) PearlAlert *handleCloudContentAlert;
|
|
||||||
@property(nonatomic, weak) PearlAlert *fixCloudContentAlert;
|
|
||||||
@property(nonatomic, weak) PearlOverlay *storeLoadingOverlay;
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation MPiOSAppDelegate
|
@implementation MPiOSAppDelegate
|
||||||
|
|
||||||
+ (void)initialize {
|
+ (void)initialize {
|
||||||
|
|
||||||
if ([self class] == [MPiOSAppDelegate class]) {
|
static dispatch_once_t once = 0;
|
||||||
|
dispatch_once( &once, ^{
|
||||||
[PearlLogger get].historyLevel = [[MPiOSConfig get].traceMode boolValue]? PearlLogLevelTrace: PearlLogLevelInfo;
|
[PearlLogger get].historyLevel = [[MPiOSConfig get].traceMode boolValue]? PearlLogLevelTrace: PearlLogLevelInfo;
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
[PearlLogger get].printLevel = PearlLogLevelDebug; //Trace;
|
[PearlLogger get].printLevel = PearlLogLevelDebug; //Trace;
|
||||||
#else
|
#else
|
||||||
[PearlLogger get].printLevel = [[MPiOSConfig get].traceMode boolValue]? PearlLogLevelDebug: PearlLogLevelInfo;
|
[PearlLogger get].printLevel = [[MPiOSConfig get].traceMode boolValue]? PearlLogLevelDebug: PearlLogLevelInfo;
|
||||||
#endif
|
#endif
|
||||||
}
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
||||||
@ -168,7 +167,7 @@
|
|||||||
NSData *importedSitesData = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url]
|
NSData *importedSitesData = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url]
|
||||||
returningResponse:&response error:&error];
|
returningResponse:&response error:&error];
|
||||||
if (error)
|
if (error)
|
||||||
err( @"While reading imported sites from %@: %@", url, error );
|
err( @"While reading imported sites from %@: %@", url, [error fullDescription] );
|
||||||
if (!importedSitesData)
|
if (!importedSitesData)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -339,6 +338,26 @@
|
|||||||
showComposerForVC:viewController];
|
showComposerForVC:viewController];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)handleCoordinatorError:(NSError *)error {
|
||||||
|
|
||||||
|
static dispatch_once_t once = 0;
|
||||||
|
dispatch_once( &once, ^{
|
||||||
|
[PearlAlert showAlertWithTitle:@"Failed To Load Sites" message:
|
||||||
|
@"Master Password was unable to open your sites history.\n"
|
||||||
|
@"This may be due to corruption. You can either reset Master Password and "
|
||||||
|
@"recreate your user, or E-Mail us your logs and leave your corrupt store as-is for now."
|
||||||
|
viewStyle:UIAlertViewStyleDefault initAlert:nil
|
||||||
|
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
|
||||||
|
if (buttonIndex == [alert cancelButtonIndex])
|
||||||
|
return;
|
||||||
|
if (buttonIndex == [alert firstOtherButtonIndex])
|
||||||
|
[self openFeedbackWithLogs:YES forVC:nil];
|
||||||
|
if (buttonIndex == [alert firstOtherButtonIndex] + 1)
|
||||||
|
[self deleteAndResetStore];
|
||||||
|
} cancelTitle:@"Ignore" otherTitles:@"E-Mail Logs", @"Reset", nil];
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
- (void)showExportForVC:(UIViewController *)viewController {
|
- (void)showExportForVC:(UIViewController *)viewController {
|
||||||
|
|
||||||
[PearlAlert showAlertWithTitle:@"Exporting Your Sites"
|
[PearlAlert showAlertWithTitle:@"Exporting Your Sites"
|
||||||
@ -409,12 +428,38 @@
|
|||||||
NSDateFormatter *exportDateFormatter = [NSDateFormatter new];
|
NSDateFormatter *exportDateFormatter = [NSDateFormatter new];
|
||||||
[exportDateFormatter setDateFormat:@"yyyy'-'MM'-'dd"];
|
[exportDateFormatter setDateFormat:@"yyyy'-'MM'-'dd"];
|
||||||
|
|
||||||
[PearlEMail sendEMailTo:nil fromVC:viewController subject:@"Master Password Export" body:message
|
NSString *exportFileName = strf( @"%@ (%@).mpsites",
|
||||||
attachments:[[PearlEMailAttachment alloc] initWithContent:[exportedSites dataUsingEncoding:NSUTF8StringEncoding]
|
[self activeUserForMainThread].name, [exportDateFormatter stringFromDate:[NSDate date]] );
|
||||||
mimeType:@"text/plain" fileName:
|
[PearlSheet showSheetWithTitle:@"Export Destination" viewStyle:UIActionSheetStyleBlackTranslucent initSheet:nil
|
||||||
strf( @"%@ (%@).mpsites", [self activeUserForMainThread].name,
|
tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) {
|
||||||
[exportDateFormatter stringFromDate:[NSDate date]] )],
|
if (buttonIndex == [sheet cancelButtonIndex])
|
||||||
nil];
|
return;
|
||||||
|
|
||||||
|
if (buttonIndex == [sheet firstOtherButtonIndex]) {
|
||||||
|
[PearlEMail sendEMailTo:nil fromVC:viewController subject:@"Master Password Export" body:message
|
||||||
|
attachments:[[PearlEMailAttachment alloc]
|
||||||
|
initWithContent:[exportedSites dataUsingEncoding:NSUTF8StringEncoding]
|
||||||
|
mimeType:@"text/plain" fileName:exportFileName],
|
||||||
|
nil];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSURL *applicationSupportURL = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory
|
||||||
|
inDomains:NSUserDomainMask] lastObject];
|
||||||
|
NSURL *exportURL = [[applicationSupportURL
|
||||||
|
URLByAppendingPathComponent:[NSBundle mainBundle].bundleIdentifier isDirectory:YES]
|
||||||
|
URLByAppendingPathComponent:exportFileName isDirectory:NO];
|
||||||
|
NSError *error = nil;
|
||||||
|
if (![[exportedSites dataUsingEncoding:NSUTF8StringEncoding]
|
||||||
|
writeToURL:exportURL options:NSDataWritingFileProtectionComplete error:&error])
|
||||||
|
err( @"Failed to write export data to URL %@: %@", exportURL, [error fullDescription] );
|
||||||
|
else {
|
||||||
|
self.interactionController = [UIDocumentInteractionController interactionControllerWithURL:exportURL];
|
||||||
|
self.interactionController.UTI = @"com.lyndir.masterpassword.sites";
|
||||||
|
self.interactionController.delegate = self;
|
||||||
|
[self.interactionController presentOpenInMenuFromRect:CGRectZero inView:viewController.view animated:YES];
|
||||||
|
}
|
||||||
|
} cancelTitle:@"Cancel" destructiveTitle:nil otherTitles:@"Send As E-Mail", @"Share / Airdrop", nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)changeMasterPasswordFor:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc didResetBlock:(void ( ^ )(void))didReset {
|
- (void)changeMasterPasswordFor:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc didResetBlock:(void ( ^ )(void))didReset {
|
||||||
@ -446,6 +491,13 @@
|
|||||||
otherTitles:[PearlStrings get].commonButtonContinue, nil];
|
otherTitles:[PearlStrings get].commonButtonContinue, nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark - UIDocumentInteractionControllerDelegate
|
||||||
|
|
||||||
|
- (void)documentInteractionController:(UIDocumentInteractionController *)controller didEndSendingToApplication:(NSString *)application {
|
||||||
|
|
||||||
|
// self.interactionController = nil;
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark - PearlConfigDelegate
|
#pragma mark - PearlConfigDelegate
|
||||||
|
|
||||||
- (void)didUpdateConfigForKey:(SEL)configKey fromValue:(id)value {
|
- (void)didUpdateConfigForKey:(SEL)configKey fromValue:(id)value {
|
||||||
@ -455,73 +507,6 @@
|
|||||||
|
|
||||||
- (void)updateConfigKey:(NSString *)key {
|
- (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
|
// Trace mode
|
||||||
[PearlLogger get].historyLevel = [[MPiOSConfig get].traceMode boolValue]? PearlLogLevelTrace: PearlLogLevelInfo;
|
[PearlLogger get].historyLevel = [[MPiOSConfig get].traceMode boolValue]? PearlLogLevelTrace: PearlLogLevelInfo;
|
||||||
|
|
||||||
@ -532,21 +517,18 @@
|
|||||||
|
|
||||||
#ifdef CRASHLYTICS
|
#ifdef CRASHLYTICS
|
||||||
[[Crashlytics sharedInstance] setBoolValue:[[MPConfig get].rememberLogin boolValue] forKey:@"rememberLogin"];
|
[[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].sendInfo boolValue] forKey:@"sendInfo"];
|
||||||
[[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].helpHidden boolValue] forKey:@"helpHidden"];
|
[[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].helpHidden boolValue] forKey:@"helpHidden"];
|
||||||
[[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].showSetup boolValue] forKey:@"showQuickStart"];
|
[[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].showSetup boolValue] forKey:@"showQuickStart"];
|
||||||
[[Crashlytics sharedInstance] setBoolValue:[[PearlConfig get].firstRun boolValue] forKey:@"firstRun"];
|
[[Crashlytics sharedInstance] setBoolValue:[[PearlConfig get].firstRun boolValue] forKey:@"firstRun"];
|
||||||
[[Crashlytics sharedInstance] setIntValue:[[PearlConfig get].launchCount intValue] forKey:@"launchCount"];
|
[[Crashlytics sharedInstance] setIntValue:[[PearlConfig get].launchCount intValue] forKey:@"launchCount"];
|
||||||
[[Crashlytics sharedInstance] setBoolValue:[[PearlConfig get].askForReviews boolValue] forKey:@"askForReviews"];
|
[[Crashlytics sharedInstance] setBoolValue:[[PearlConfig get].askForReviews boolValue] forKey:@"askForReviews"];
|
||||||
[[Crashlytics sharedInstance]
|
[[Crashlytics sharedInstance] setIntValue:[[PearlConfig get].reviewAfterLaunches intValue] forKey:@"reviewAfterLaunches"];
|
||||||
setIntValue:[[PearlConfig get].reviewAfterLaunches intValue] forKey:@"reviewAfterLaunches"];
|
|
||||||
[[Crashlytics sharedInstance] setObjectValue:[PearlConfig get].reviewedVersion forKey:@"reviewedVersion"];
|
[[Crashlytics sharedInstance] setObjectValue:[PearlConfig get].reviewedVersion forKey:@"reviewedVersion"];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
MPCheckpoint( MPCheckpointConfig, @{
|
MPCheckpoint( MPCheckpointConfig, @{
|
||||||
@"rememberLogin" : @([[MPConfig get].rememberLogin boolValue]),
|
@"rememberLogin" : @([[MPConfig get].rememberLogin boolValue]),
|
||||||
@"iCloudEnabled" : @([[MPiOSConfig get].iCloudEnabled boolValue]),
|
|
||||||
@"sendInfo" : @([[MPiOSConfig get].sendInfo boolValue]),
|
@"sendInfo" : @([[MPiOSConfig get].sendInfo boolValue]),
|
||||||
@"helpHidden" : @([[MPiOSConfig get].helpHidden boolValue]),
|
@"helpHidden" : @([[MPiOSConfig get].helpHidden boolValue]),
|
||||||
@"showQuickStart" : @([[MPiOSConfig get].showSetup 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
|
#pragma mark - Crashlytics
|
||||||
|
|
||||||
- (NSDictionary *)crashlyticsInfo {
|
- (NSDictionary *)crashlyticsInfo {
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
@property(nonatomic, retain) NSNumber *typeTipShown;
|
@property(nonatomic, retain) NSNumber *typeTipShown;
|
||||||
@property(nonatomic, retain) NSNumber *loginNameTipShown;
|
@property(nonatomic, retain) NSNumber *loginNameTipShown;
|
||||||
@property(nonatomic, retain) NSNumber *traceMode;
|
@property(nonatomic, retain) NSNumber *traceMode;
|
||||||
@property(nonatomic, retain) NSNumber *iCloudEnabled;
|
|
||||||
@property(nonatomic, retain) NSNumber *dictationSearch;
|
@property(nonatomic, retain) NSNumber *dictationSearch;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
@implementation MPiOSConfig
|
@implementation MPiOSConfig
|
||||||
|
|
||||||
@dynamic helpHidden, siteInfoHidden, showSetup, actionsTipShown, typeTipShown, loginNameTipShown, traceMode, iCloudEnabled, dictationSearch;
|
@dynamic helpHidden, siteInfoHidden, showSetup, actionsTipShown, typeTipShown, loginNameTipShown, traceMode, dictationSearch;
|
||||||
|
|
||||||
- (id)init {
|
- (id)init {
|
||||||
|
|
||||||
@ -24,7 +24,6 @@
|
|||||||
NSStringFromSelector( @selector(typeTipShown) ) : @(!self.firstRun),
|
NSStringFromSelector( @selector(typeTipShown) ) : @(!self.firstRun),
|
||||||
NSStringFromSelector( @selector(loginNameTipShown) ) : @NO,
|
NSStringFromSelector( @selector(loginNameTipShown) ) : @NO,
|
||||||
NSStringFromSelector( @selector(traceMode) ) : @NO,
|
NSStringFromSelector( @selector(traceMode) ) : @NO,
|
||||||
NSStringFromSelector( @selector(iCloudEnabled) ) : @NO,
|
|
||||||
NSStringFromSelector( @selector(dictationSearch) ) : @NO
|
NSStringFromSelector( @selector(dictationSearch) ) : @NO
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
<string>Owner</string>
|
<string>Owner</string>
|
||||||
<key>LSItemContentTypes</key>
|
<key>LSItemContentTypes</key>
|
||||||
<array>
|
<array>
|
||||||
<string>com.lyndir.lhunath.MasterPassword.sites</string>
|
<string>com.lyndir.masterpassword.sites</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
@ -39,36 +39,12 @@
|
|||||||
<string>[auto]</string>
|
<string>[auto]</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleURLTypes</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Editor</string>
|
|
||||||
<key>CFBundleURLName</key>
|
|
||||||
<string>com.lyndir.lhunath.MasterPassword</string>
|
|
||||||
<key>CFBundleURLSchemes</key>
|
|
||||||
<array>
|
|
||||||
<string>com.lyndir.lhunath.MasterPassword</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>[auto]</string>
|
<string>[auto]</string>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>NSHumanReadableCopyright</key>
|
<key>NSHumanReadableCopyright</key>
|
||||||
<string>© 2011-2013, Lyndir</string>
|
<string>© 2011-2013, Lyndir</string>
|
||||||
<key>ReplacementFonts</key>
|
|
||||||
<dict>
|
|
||||||
<key>AmericanTypewriter-Bold</key>
|
|
||||||
<string>SourceCodePro-Black</string>
|
|
||||||
<key>AmericanTypewriter-Light</key>
|
|
||||||
<string>SourceCodePro-ExtraLight</string>
|
|
||||||
<key>Futura-CondensedExtraBold</key>
|
|
||||||
<string>Exo-ExtraBold</string>
|
|
||||||
<key>Futura-Medium</key>
|
|
||||||
<string>Exo</string>
|
|
||||||
</dict>
|
|
||||||
<key>UIAppFonts</key>
|
<key>UIAppFonts</key>
|
||||||
<array>
|
<array>
|
||||||
<string>Exo2.0-Bold.otf</string>
|
<string>Exo2.0-Bold.otf</string>
|
||||||
@ -76,6 +52,7 @@
|
|||||||
<string>Exo2.0-Regular.otf</string>
|
<string>Exo2.0-Regular.otf</string>
|
||||||
<string>Exo2.0-Thin.otf</string>
|
<string>Exo2.0-Thin.otf</string>
|
||||||
<string>SourceCodePro-Black.otf</string>
|
<string>SourceCodePro-Black.otf</string>
|
||||||
|
<string>SourceCodePro-Regular.otf</string>
|
||||||
<string>SourceCodePro-ExtraLight.otf</string>
|
<string>SourceCodePro-ExtraLight.otf</string>
|
||||||
</array>
|
</array>
|
||||||
<key>UIMainStoryboardFile</key>
|
<key>UIMainStoryboardFile</key>
|
||||||
@ -119,10 +96,14 @@
|
|||||||
<key>UTExportedTypeDeclarations</key>
|
<key>UTExportedTypeDeclarations</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>UTTypeConformsTo</key>
|
||||||
|
<array>
|
||||||
|
<string>public.utf8-plain-text</string>
|
||||||
|
</array>
|
||||||
<key>UTTypeDescription</key>
|
<key>UTTypeDescription</key>
|
||||||
<string>Master Password sites</string>
|
<string>Master Password sites</string>
|
||||||
<key>UTTypeIdentifier</key>
|
<key>UTTypeIdentifier</key>
|
||||||
<string>com.lyndir.lhunath.MasterPassword.sites</string>
|
<string>com.lyndir.masterpassword.sites</string>
|
||||||
<key>UTTypeSize320IconFile</key>
|
<key>UTTypeSize320IconFile</key>
|
||||||
<string></string>
|
<string></string>
|
||||||
<key>UTTypeSize64IconFile</key>
|
<key>UTTypeSize64IconFile</key>
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "0510"
|
LastUpgradeVersion = "0600"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "0510"
|
LastUpgradeVersion = "0600"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
@ -1,17 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict/>
|
||||||
<key>com.apple.developer.ubiquity-container-identifiers</key>
|
|
||||||
<array>
|
|
||||||
<string>$(TeamIdentifierPrefix)com.lyndir.lhunath.MasterPassword</string>
|
|
||||||
<string>$(TeamIdentifierPrefix)com.lyndir.lhunath.MasterPassword.shared</string>
|
|
||||||
</array>
|
|
||||||
<key>com.apple.developer.ubiquity-kvstore-identifier</key>
|
|
||||||
<string>$(TeamIdentifierPrefix)com.lyndir.lhunath.MasterPassword.shared</string>
|
|
||||||
<key>keychain-access-groups</key>
|
|
||||||
<array>
|
|
||||||
<string>$(AppIdentifierPrefix)com.lyndir.lhunath.MasterPassword</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
</plist>
|
||||||
|
@ -146,24 +146,6 @@ To see a site's password anyway, tap and hold your finger down for a while
|
|||||||
<integer>2</integer>
|
<integer>2</integer>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
<dict>
|
|
||||||
<key>Type</key>
|
|
||||||
<string>PSGroupSpecifier</string>
|
|
||||||
<key>Title</key>
|
|
||||||
<string></string>
|
|
||||||
<key>FooterText</key>
|
|
||||||
<string>Synchronizes your sites with your other Apple devices. It's also a good way of keeping automatic backups.</string>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>Type</key>
|
|
||||||
<string>PSToggleSwitchSpecifier</string>
|
|
||||||
<key>Title</key>
|
|
||||||
<string>iCloud</string>
|
|
||||||
<key>Key</key>
|
|
||||||
<string>iCloudEnabled</string>
|
|
||||||
<key>DefaultValue</key>
|
|
||||||
<false/>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
<dict>
|
||||||
<key>Type</key>
|
<key>Type</key>
|
||||||
<string>PSGroupSpecifier</string>
|
<string>PSGroupSpecifier</string>
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -2,8 +2,18 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>MPElementGeneratedEntity</key>
|
<key>MPGeneratedSiteEntity</key>
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>Login Name</key>
|
||||||
|
<array>
|
||||||
|
<string>cvccvcvcv</string>
|
||||||
|
</array>
|
||||||
|
<key>Phrase</key>
|
||||||
|
<array>
|
||||||
|
<string>cvcc cvc cvccvcv cvc</string>
|
||||||
|
<string>cvc cvccvcvcv cvcv</string>
|
||||||
|
<string>cv cvccv cvc cvcvccv</string>
|
||||||
|
</array>
|
||||||
<key>Maximum Security Password</key>
|
<key>Maximum Security Password</key>
|
||||||
<array>
|
<array>
|
||||||
<string>anoxxxxxxxxxxxxxxxxx</string>
|
<string>anoxxxxxxxxxxxxxxxxx</string>
|
||||||
@ -73,6 +83,8 @@
|
|||||||
<string>@&%?,=[]_:-+*$#!'^~;()/.</string>
|
<string>@&%?,=[]_:-+*$#!'^~;()/.</string>
|
||||||
<key>x</key>
|
<key>x</key>
|
||||||
<string>AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()</string>
|
<string>AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()</string>
|
||||||
|
<key> </key>
|
||||||
|
<string> </string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
BIN
MasterPassword/Resources/Media/Fonts/SourceCodePro-Regular.otf
Normal file
BIN
MasterPassword/Resources/Media/Fonts/SourceCodePro-Regular.otf
Normal file
Binary file not shown.
@ -18,6 +18,11 @@
|
|||||||
"filename" : "Icon-60@2x.png",
|
"filename" : "Icon-60@2x.png",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"size" : "60x60",
|
||||||
|
"scale" : "3x"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"size" : "29x29",
|
"size" : "29x29",
|
||||||
"idiom" : "ipad",
|
"idiom" : "ipad",
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user