2
0

Compare commits

...

63 Commits

Author SHA1 Message Date
Maarten Billemont
2886e040a1 Warning fix. 2020-09-03 11:04:59 -04:00
Maarten Billemont
01cea659ca Bump Pearl for nullability fixes. 2020-09-03 10:52:56 -04:00
Maarten Billemont
3a18e02a87 Revert "Xcode 12 update of xcdatamodel."
This reverts commit 2de57984b2.

NSSecureUnarchiveFromDataTransformer is not compatible with iOS 9-11
2020-09-03 10:41:05 -04:00
Maarten Billemont
2de57984b2 Xcode 12 update of xcdatamodel. 2020-09-03 10:39:23 -04:00
Maarten Billemont
c7201c7d90 Update for Xcode 12 & build fixes. 2020-09-03 09:53:08 -04:00
Maarten Billemont
d62c6b4594 Sites no longer load with batch requests & load improvements. 2020-09-03 09:52:08 -04:00
Maarten Billemont
57f275c471 Update for Xcode 12 & add device identifier to UI. 2020-09-02 16:40:41 -04:00
Maarten Billemont
b1d8296396 Add nonstandard output type tests for i,r + fix indentation. 2020-08-29 09:48:14 -04:00
Maarten Billemont
6d25463de0 Rename next-gen to Spectre. 2020-07-21 21:21:52 -04:00
Maarten Billemont
029041dcf7 Expand the maximum length of query searches. 2020-07-11 21:43:34 -04:00
Maarten Billemont
cfbf1f5cac Use visibility instead of gone so constraints are managed by stack view. 2020-07-11 10:48:19 -04:00
Maarten Billemont
acbd2dc2cc Include purchased features in export file. 2020-07-06 22:28:15 -04:00
Maarten Billemont
8fcac65fd5 Additional documentation for parameter contracts. 2020-07-06 22:27:47 -04:00
Maarten Billemont
9904f4c715 Try to detect if cipherText is plainText.
In some situations, the cipherText that was passed in is actually
plainText.  Old mpsites files used to store the login name as plain text
even though the file was redacted.  Newer versions of the file store the
login name as ciphertext.  There is no clear way to distinguish between
the two cases.
2020-07-06 14:18:47 -04:00
Maarten Billemont
b51a3de32c Check pasteboard when app enters foreground, not activation. 2020-07-05 20:24:59 -04:00
Maarten Billemont
9e91f0a9d6 More reliable monitoring of changes using NSFetchedResultsController. 2020-07-05 20:24:18 -04:00
Maarten Billemont
7368b1be90 Source is button item, not a view. 2020-05-24 10:54:25 -04:00
Maarten Billemont
5db294bdb3 Show purchase transaction failures to the user. 2020-05-23 19:58:47 -04:00
Maarten Billemont
fee7bc7401 Resolve site cell sizing issues across window sizes. 2020-05-23 19:08:43 -04:00
Maarten Billemont
21968f4ba6 Fix messages for password reset. 2020-05-23 12:35:55 -04:00
Maarten Billemont
8582c934c2 Limit fuzzy searching to a depth of 10.
Avoids choking when query string becomes long and there are excessively
long site name entries.
2020-05-23 12:14:22 -04:00
Maarten Billemont
7091e2ee1b Disable automatic font scaling.
It's causing issues with pop-up alerts.
2020-05-23 10:12:07 -04:00
Maarten Billemont
d5d455ee57 Fix issues with content insets for sites across OS versions. 2020-05-22 23:04:36 -04:00
Maarten Billemont
e6ae06798b Handle store opening errors more gracefully.
Store opening can fail for example when hard-locking the device while
it's opening up.
2020-05-22 22:26:43 -04:00
Maarten Billemont
1cae4c754b Group MPErrors together, ignoring the actual inline values. 2020-05-22 22:26:18 -04:00
Maarten Billemont
93ad86e63c Remove PearlAppDelegate. 2020-05-22 17:34:04 -04:00
Maarten Billemont
cf74dc5cc2 Updated NSMenu API. 2020-05-19 13:28:23 -04:00
Maarten Billemont
981bdb3ab4 Fix isDescendantOfView bug & Sentry script error on failure. 2020-05-19 09:21:06 -04:00
Maarten Billemont
9bea8bcbdf Sheets need a source view on iPad. 2020-05-19 08:21:44 -04:00
Maarten Billemont
363d6f6639 Test configuration was removed for Release. 2020-05-18 13:01:16 -04:00
Maarten Billemont
eb1632cb62 Install cocoapods dependencies & gradle works on JDK 11 now. 2020-05-18 12:43:37 -04:00
Maarten Billemont
73fadaef7f iOS uses Xcode 11 now. 2020-05-18 12:15:36 -04:00
Maarten Billemont
60200f6302 Fix all versions advertising themselves as V0. 2020-05-18 12:14:44 -04:00
Maarten Billemont
cce8db5c48 Purge unused and deprecated UISearchDisplayController. 2020-05-18 11:10:23 -04:00
Maarten Billemont
6f3da5ccf0 Harmonize consent features. 2020-05-16 22:34:49 -04:00
Maarten Billemont
52c87eaeca Keep sites sorted by name on export to ensure consistency. 2020-05-16 16:03:42 -04:00
Maarten Billemont
1dccdd0a3c Improvements to algorithm and counter steppers. 2020-05-16 15:40:59 -04:00
Maarten Billemont
eb8d10ed05 Use new UN notification API. 2020-05-14 10:32:04 -04:00
Maarten Billemont
d9e5f77bee Resolve export state generation. 2020-05-14 00:04:29 -04:00
Maarten Billemont
60f60d087e Embed views using embed segue instead of MPRootSegue. 2020-05-12 23:27:56 -04:00
Maarten Billemont
df97dec2fe Sentry user login & MPError arguments. 2020-05-12 10:54:15 -04:00
Maarten Billemont
3bac8d9e0a iPad fixes & log initial start-up crashes. 2020-05-11 21:28:27 -04:00
Maarten Billemont
3fa7e1e8a1 Ensure Countly does not use the IDFA. 2020-05-02 12:31:54 -04:00
Maarten Billemont
d1104e4028 Need icon 1x icon for iPad. 2020-04-30 09:32:17 -04:00
Maarten Billemont
e9f2a25c9c Project icons update. 2020-04-28 13:49:34 -04:00
Maarten Billemont
171a3f0978 Icon update. 2020-04-28 12:24:41 -04:00
Maarten Billemont
8cfb9a83c5 mpjson support on iOS. 2020-04-27 16:33:10 -04:00
Maarten Billemont
5717375e75 Improve language. 2020-04-27 16:33:01 -04:00
Maarten Billemont
cc2dca3bd0 Report missing support when trying to parse unknown format. 2020-04-27 16:32:30 -04:00
Maarten Billemont
7575924d80 Bump site links to macOS app. 2020-04-26 00:39:08 -04:00
Maarten Billemont
8bedcedfaf Enable support for internal actions from URLs. 2020-04-25 17:13:48 -04:00
Maarten Billemont
10b205c541 Open URLs in external browser. 2020-04-25 11:44:27 -04:00
Maarten Billemont
774f183ac0 Improved web support & trigger notification URLs. 2020-04-25 10:44:59 -04:00
Maarten Billemont
2279aacb5a Change the user's default type from the Mac UI. 2020-04-21 13:27:06 -04:00
Maarten Billemont
1bd654621c Fix persistence of sendInfoDecided.
Don't log it as an event, event logging is probably disabled at this
point anyway.
2020-04-21 13:26:12 -04:00
Maarten Billemont
c4f60e325d Harmonize consent flow on both platforms. 2020-04-21 11:33:31 -04:00
Maarten Billemont
d4de3afb72 Show internal reason for why import fails. 2020-04-20 17:09:38 -04:00
Maarten Billemont
694b5ea227 Make marshal error messages owned by the file.
Error message lifecycle was limited to the static mpw_str buffer, which
is far too limited and also dangerous.  Own the message by the
MPMarshalFile object, freed in mpw_marshal_file_free.
2020-04-20 17:07:35 -04:00
Maarten Billemont
66dd78797b We use bounds-checked interfaces, so need ext1. 2020-04-20 17:05:37 -04:00
Maarten Billemont
61d1660560 Consent tweaks.
Don't conditional Countly events on sendInfo, it already turns Countly
on/off.

Keep Sentry enabled for longer while turning on/off for sendInfo to
cache more errors.

Use sendInfoDecided event only for initial sendInfo prompt, can use
Countly's censent tools to view overall opt-in/opt-outs.
2020-04-19 17:40:28 -04:00
Maarten Billemont
c3568e4744 Enable minor & build numbers > 9. 2020-04-18 22:13:36 -04:00
Maarten Billemont
0c921d4318 Update fonts to stay consistent with system UIs. 2020-04-18 21:50:06 -04:00
Maarten Billemont
0178efaaf7 Ask for consent to sendInfo. 2020-04-18 21:48:55 -04:00
86 changed files with 1914 additions and 1424 deletions

View File

@@ -9,10 +9,12 @@ build_project:
- "( ./lib/bin/build_libsodium-macos clean && ./lib/bin/build_libsodium-macos )" - "( ./lib/bin/build_libsodium-macos clean && ./lib/bin/build_libsodium-macos )"
- "( ./lib/bin/build_libjson-c-macos clean && ./lib/bin/build_libjson-c-macos )" - "( ./lib/bin/build_libjson-c-macos clean && ./lib/bin/build_libjson-c-macos )"
- "( cd ./platform-independent/c/cli && ./clean && targets=all ./build && ./mpw-tests && ./mpw-cli-tests )" - "( cd ./platform-independent/c/cli && ./clean && targets=all ./build && ./mpw-tests && ./mpw-cli-tests )"
- "( export JAVA_HOME=$(java_home -Fv 10 || java_home -Fv 9* ) && ./gradlew --stacktrace clean test )" - "( ./gradlew --stacktrace --info clean test )"
- "( xcodebuild -workspace platform-darwin/MasterPassword.xcworkspace -configuration 'Test' -scheme 'MasterPassword iOS' -sdk iphonesimulator clean build )" - "( cd platform-darwin && pod install )"
- "( xcodebuild -workspace platform-darwin/MasterPassword.xcworkspace -configuration 'Test' -scheme 'MasterPassword macOS' clean build )" - "( xcodebuild -workspace platform-darwin/MasterPassword.xcworkspace -configuration 'Release' -scheme 'MasterPassword iOS' -sdk iphonesimulator clean build )"
- "( xcodebuild -workspace platform-darwin/MasterPassword.xcworkspace -configuration 'Release' -scheme 'MasterPassword macOS' clean build )"
tags: tags:
- brew - brew
- java_9 - java
- xcode_9 - cocoapods
- xcode

3
.gitmodules vendored
View File

@@ -15,7 +15,6 @@
url = https://github.com/json-c/json-c.git url = https://github.com/json-c/json-c.git
[submodule "public/site"] [submodule "public/site"]
path = public/site path = public/site
url = https://github.com/Lyndir/MasterPassword.git url = https://gitlab.com/MasterPassword/MasterPassword.git
branch = gh-pages branch = gh-pages
shallow = true shallow = true
update = none

View File

@@ -49,7 +49,6 @@
93D39A5FF670957C0AF8298D /* MPSiteCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39DEA995041A13DC9CAF7 /* MPSiteCell.m */; }; 93D39A5FF670957C0AF8298D /* MPSiteCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39DEA995041A13DC9CAF7 /* MPSiteCell.m */; };
93D39A8EA1C49CE43B63F47B /* PearlUICollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D8A953779B35403AF6E /* PearlUICollectionView.m */; }; 93D39A8EA1C49CE43B63F47B /* PearlUICollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D8A953779B35403AF6E /* PearlUICollectionView.m */; };
93D39AA4A0BE66A872CCC02E /* NSPersistentStore+PearlMigration.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D397F4BAFFF7CF3F1B21A4 /* NSPersistentStore+PearlMigration.h */; }; 93D39AA4A0BE66A872CCC02E /* NSPersistentStore+PearlMigration.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D397F4BAFFF7CF3F1B21A4 /* NSPersistentStore+PearlMigration.h */; };
93D39B429C67A62E29DC02DA /* MPRootSegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399493FEDDE74DD1A0C15 /* MPRootSegue.m */; };
93D39B76DD5AB108BA8928E8 /* UIScrollView+PearlAdjustInsets.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39DE2CB351D4E3789462B /* UIScrollView+PearlAdjustInsets.h */; }; 93D39B76DD5AB108BA8928E8 /* UIScrollView+PearlAdjustInsets.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39DE2CB351D4E3789462B /* UIScrollView+PearlAdjustInsets.h */; };
93D39B842AB9A5D072810D76 /* NSError+PearlFullDescription.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D398C95847261903D781D3 /* NSError+PearlFullDescription.h */; }; 93D39B842AB9A5D072810D76 /* NSError+PearlFullDescription.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D398C95847261903D781D3 /* NSError+PearlFullDescription.h */; };
93D39B8F90F58A5D158DDBA3 /* MPSitesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3924EE15017F8A12CB436 /* MPSitesViewController.m */; }; 93D39B8F90F58A5D158DDBA3 /* MPSitesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3924EE15017F8A12CB436 /* MPSitesViewController.m */; };
@@ -77,11 +76,6 @@
DA0CC5951EB6B030009A8ED9 /* MPSiteEntity+CoreDataProperties.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC58B1EB6B030009A8ED9 /* MPSiteEntity+CoreDataProperties.m */; }; DA0CC5951EB6B030009A8ED9 /* MPSiteEntity+CoreDataProperties.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC58B1EB6B030009A8ED9 /* MPSiteEntity+CoreDataProperties.m */; };
DA24EBAE19DAD08900FF010B /* tip_basic_black_top.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38941711E29700CF925C /* tip_basic_black_top.png */; }; DA24EBAE19DAD08900FF010B /* tip_basic_black_top.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38941711E29700CF925C /* tip_basic_black_top.png */; };
DA24EBAF19DAD08C00FF010B /* tip_basic_black_top@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38951711E29700CF925C /* tip_basic_black_top@2x.png */; }; DA24EBAF19DAD08C00FF010B /* tip_basic_black_top@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38951711E29700CF925C /* tip_basic_black_top@2x.png */; };
DA24EBE819DAD6DE00FF010B /* Icon-320.png in Resources */ = {isa = PBXBuildFile; fileRef = DA24EBE619DAD6DE00FF010B /* Icon-320.png */; };
DA24EBE919DAD6DE00FF010B /* Icon-64.png in Resources */ = {isa = PBXBuildFile; fileRef = DA24EBE719DAD6DE00FF010B /* Icon-64.png */; };
DA24EBEA19DAD6EE00FF010B /* Icon-Small.png in Resources */ = {isa = PBXBuildFile; fileRef = DA24EBBA19DAD4D000FF010B /* Icon-Small.png */; };
DA24EBEB19DAD6EE00FF010B /* Icon-Small@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA24EBBB19DAD4D000FF010B /* Icon-Small@2x.png */; };
DA24EBEC19DAD6EE00FF010B /* Icon-Small@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA24EBBC19DAD4D000FF010B /* Icon-Small@3x.png */; };
DA250A17195665A100AC23F1 /* UITableView+PearlReloadItems.m in Sources */ = {isa = PBXBuildFile; fileRef = DA250A13195665A100AC23F1 /* UITableView+PearlReloadItems.m */; }; DA250A17195665A100AC23F1 /* UITableView+PearlReloadItems.m in Sources */ = {isa = PBXBuildFile; fileRef = DA250A13195665A100AC23F1 /* UITableView+PearlReloadItems.m */; };
DA250A18195665A100AC23F1 /* UITableView+PearlReloadItems.h in Headers */ = {isa = PBXBuildFile; fileRef = DA250A14195665A100AC23F1 /* UITableView+PearlReloadItems.h */; }; DA250A18195665A100AC23F1 /* UITableView+PearlReloadItems.h in Headers */ = {isa = PBXBuildFile; fileRef = DA250A14195665A100AC23F1 /* UITableView+PearlReloadItems.h */; };
DA250A19195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m in Sources */ = {isa = PBXBuildFile; fileRef = DA250A15195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m */; }; DA250A19195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m in Sources */ = {isa = PBXBuildFile; fileRef = DA250A15195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m */; };
@@ -155,6 +149,21 @@
DA5BFA4B147E415C00F98B1E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; }; DA5BFA4B147E415C00F98B1E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
DA5BFA4D147E415C00F98B1E /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4C147E415C00F98B1E /* CoreGraphics.framework */; }; DA5BFA4D147E415C00F98B1E /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4C147E415C00F98B1E /* CoreGraphics.framework */; };
DA5BFA4F147E415C00F98B1E /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4E147E415C00F98B1E /* CoreData.framework */; }; DA5BFA4F147E415C00F98B1E /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4E147E415C00F98B1E /* CoreData.framework */; };
DA5E0E5524589C9B0007FBA7 /* Icon-Small@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E0E4624589C9A0007FBA7 /* Icon-Small@3x.png */; };
DA5E0E5624589C9B0007FBA7 /* Icon-Small-40.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E0E4724589C9A0007FBA7 /* Icon-Small-40.png */; };
DA5E0E5724589C9B0007FBA7 /* Icon-Small-20.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E0E4824589C9A0007FBA7 /* Icon-Small-20.png */; };
DA5E0E5824589C9B0007FBA7 /* Icon-76.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E0E4924589C9A0007FBA7 /* Icon-76.png */; };
DA5E0E5924589C9B0007FBA7 /* Icon-Small@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E0E4A24589C9A0007FBA7 /* Icon-Small@2x.png */; };
DA5E0E5A24589C9B0007FBA7 /* Icon-Small-40@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E0E4B24589C9A0007FBA7 /* Icon-Small-40@2x.png */; };
DA5E0E5B24589C9B0007FBA7 /* Icon-76@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E0E4C24589C9A0007FBA7 /* Icon-76@2x.png */; };
DA5E0E5C24589C9B0007FBA7 /* Icon-Small-20@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E0E4D24589C9A0007FBA7 /* Icon-Small-20@3x.png */; };
DA5E0E5D24589C9B0007FBA7 /* Icon-Small.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E0E4E24589C9A0007FBA7 /* Icon-Small.png */; };
DA5E0E5E24589C9B0007FBA7 /* Icon-83@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E0E4F24589C9A0007FBA7 /* Icon-83@2x.png */; };
DA5E0E5F24589C9B0007FBA7 /* Icon-Small-40@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E0E5024589C9A0007FBA7 /* Icon-Small-40@3x.png */; };
DA5E0E6024589C9B0007FBA7 /* Icon-60@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E0E5124589C9A0007FBA7 /* Icon-60@3x.png */; };
DA5E0E6124589C9B0007FBA7 /* iTunesArtwork@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E0E5224589C9B0007FBA7 /* iTunesArtwork@2x.png */; };
DA5E0E6224589C9B0007FBA7 /* Icon-Small-20@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E0E5324589C9B0007FBA7 /* Icon-Small-20@2x.png */; };
DA5E0E6324589C9B0007FBA7 /* Icon-60@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E0E5424589C9B0007FBA7 /* Icon-60@2x.png */; };
DA6701B816406A4100B61001 /* Accounts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA6701B716406A4100B61001 /* Accounts.framework */; settings = {ATTRIBUTES = (Required, ); }; }; DA6701B816406A4100B61001 /* Accounts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA6701B716406A4100B61001 /* Accounts.framework */; settings = {ATTRIBUTES = (Required, ); }; };
DA6701DE16406B7300B61001 /* Social.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA6701DD16406B7300B61001 /* Social.framework */; settings = {ATTRIBUTES = (Required, ); }; }; DA6701DE16406B7300B61001 /* Social.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA6701DD16406B7300B61001 /* Social.framework */; settings = {ATTRIBUTES = (Required, ); }; };
DA6701E016406BB400B61001 /* AdSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA6701DF16406BB400B61001 /* AdSupport.framework */; settings = {ATTRIBUTES = (Required, ); }; }; DA6701E016406BB400B61001 /* AdSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA6701DF16406BB400B61001 /* AdSupport.framework */; settings = {ATTRIBUTES = (Required, ); }; };
@@ -165,7 +174,7 @@
DA67461018DE7F0C00DFE240 /* Exo2.0-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = DA67460C18DE7F0C00DFE240 /* Exo2.0-Bold.otf */; }; DA67461018DE7F0C00DFE240 /* Exo2.0-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = DA67460C18DE7F0C00DFE240 /* Exo2.0-Bold.otf */; };
DA69540617D975D900BF294E /* icon_gears.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37841711E29500CF925C /* icon_gears.png */; }; DA69540617D975D900BF294E /* icon_gears.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37841711E29500CF925C /* icon_gears.png */; };
DA69540717D975D900BF294E /* icon_gears@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37851711E29500CF925C /* icon_gears@2x.png */; }; DA69540717D975D900BF294E /* icon_gears@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37851711E29500CF925C /* icon_gears@2x.png */; };
DA72BD7B19C1510C00E6ACFE /* UIView+FontScale.m in Sources */ = {isa = PBXBuildFile; fileRef = DACE2F6719BA6A2A0010F92E /* UIView+FontScale.m */; }; DA72E2302453B91700676D4F /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA72E22F2453B91700676D4F /* WebKit.framework */; };
DA73049D194E022700E72520 /* ui_spinner.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD36511711E29400CF925C /* ui_spinner.png */; }; DA73049D194E022700E72520 /* ui_spinner.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD36511711E29400CF925C /* ui_spinner.png */; };
DA73049E194E022700E72520 /* ui_spinner@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD36521711E29400CF925C /* ui_spinner@2x.png */; }; DA73049E194E022700E72520 /* ui_spinner@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD36521711E29400CF925C /* ui_spinner@2x.png */; };
DA73049F194E022B00E72520 /* ui_textfield.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD365B1711E29400CF925C /* ui_textfield.png */; }; DA73049F194E022B00E72520 /* ui_textfield.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD365B1711E29400CF925C /* ui_textfield.png */; };
@@ -323,7 +332,6 @@
DABD3C1E1711E2DC00CF925C /* MPPreferencesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BEB1711E2DC00CF925C /* MPPreferencesViewController.m */; }; DABD3C1E1711E2DC00CF925C /* MPPreferencesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BEB1711E2DC00CF925C /* MPPreferencesViewController.m */; };
DABD3C1F1711E2DC00CF925C /* MPTypeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BED1711E2DC00CF925C /* MPTypeViewController.m */; }; DABD3C1F1711E2DC00CF925C /* MPTypeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BED1711E2DC00CF925C /* MPTypeViewController.m */; };
DABD3C211711E2DC00CF925C /* MPiOSConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BF11711E2DC00CF925C /* MPiOSConfig.m */; }; DABD3C211711E2DC00CF925C /* MPiOSConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BF11711E2DC00CF925C /* MPiOSConfig.m */; };
DABD3C241711E2DC00CF925C /* MasterPassword.entitlements in Resources */ = {isa = PBXBuildFile; fileRef = DABD3BF81711E2DC00CF925C /* MasterPassword.entitlements */; };
DABD3C251711E2DC00CF925C /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = DABD3BF91711E2DC00CF925C /* Settings.bundle */; }; DABD3C251711E2DC00CF925C /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = DABD3BF91711E2DC00CF925C /* Settings.bundle */; };
DABD3C261711E2DC00CF925C /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DABD3BFA1711E2DC00CF925C /* InfoPlist.strings */; }; DABD3C261711E2DC00CF925C /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DABD3BFA1711E2DC00CF925C /* InfoPlist.strings */; };
DABD3C271711E2DC00CF925C /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BFC1711E2DC00CF925C /* main.m */; }; DABD3C271711E2DC00CF925C /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BFC1711E2DC00CF925C /* main.m */; };
@@ -465,7 +473,6 @@
93D391943675426839501BB8 /* MPLogsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPLogsViewController.h; sourceTree = "<group>"; }; 93D391943675426839501BB8 /* MPLogsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPLogsViewController.h; sourceTree = "<group>"; };
93D391AA32F24290C424438E /* NSNotificationCenter+PearlEasyCleanup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNotificationCenter+PearlEasyCleanup.h"; sourceTree = "<group>"; }; 93D391AA32F24290C424438E /* NSNotificationCenter+PearlEasyCleanup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNotificationCenter+PearlEasyCleanup.h"; sourceTree = "<group>"; };
93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadItems.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionView+PearlReloadItems.h"; sourceTree = "<group>"; }; 93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadItems.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionView+PearlReloadItems.h"; sourceTree = "<group>"; };
93D3924D6F77E6BF41AC32D3 /* MPRootSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPRootSegue.h; sourceTree = "<group>"; };
93D3924EE15017F8A12CB436 /* MPSitesViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSitesViewController.m; sourceTree = "<group>"; }; 93D3924EE15017F8A12CB436 /* MPSitesViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSitesViewController.m; sourceTree = "<group>"; };
93D392876BE5C011DE73B43F /* MPPopdownSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPopdownSegue.h; sourceTree = "<group>"; }; 93D392876BE5C011DE73B43F /* MPPopdownSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPopdownSegue.h; sourceTree = "<group>"; };
93D392C5A6572DB0EB5B82C8 /* mpw-types.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-types.c"; sourceTree = "<group>"; }; 93D392C5A6572DB0EB5B82C8 /* mpw-types.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-types.c"; sourceTree = "<group>"; };
@@ -494,7 +501,6 @@
93D398C95847261903D781D3 /* NSError+PearlFullDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+PearlFullDescription.h"; sourceTree = "<group>"; }; 93D398C95847261903D781D3 /* NSError+PearlFullDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+PearlFullDescription.h"; sourceTree = "<group>"; };
93D3990D850D76A94C6B7A4D /* mpw-algorithm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "mpw-algorithm.h"; sourceTree = "<group>"; }; 93D3990D850D76A94C6B7A4D /* mpw-algorithm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "mpw-algorithm.h"; sourceTree = "<group>"; };
93D3990E0CD1B5CF9FBB2C07 /* MPWebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPWebViewController.m; sourceTree = "<group>"; }; 93D3990E0CD1B5CF9FBB2C07 /* MPWebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPWebViewController.m; sourceTree = "<group>"; };
93D399493FEDDE74DD1A0C15 /* MPRootSegue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPRootSegue.m; sourceTree = "<group>"; };
93D3995B1D4DCE5A30D882BA /* MPCoachmarkViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCoachmarkViewController.m; sourceTree = "<group>"; }; 93D3995B1D4DCE5A30D882BA /* MPCoachmarkViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCoachmarkViewController.m; sourceTree = "<group>"; };
93D39975CE5AEC99E3F086C7 /* MPSiteCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSiteCell.h; sourceTree = "<group>"; }; 93D39975CE5AEC99E3F086C7 /* MPSiteCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSiteCell.h; sourceTree = "<group>"; };
93D3999693660C89A7465F4E /* MPCoachmarkViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCoachmarkViewController.h; sourceTree = "<group>"; }; 93D3999693660C89A7465F4E /* MPCoachmarkViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCoachmarkViewController.h; sourceTree = "<group>"; };
@@ -573,19 +579,6 @@
DA0CC5891EB6B030009A8ED9 /* MPSiteEntity+CoreDataClass.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MPSiteEntity+CoreDataClass.m"; sourceTree = "<group>"; }; DA0CC5891EB6B030009A8ED9 /* MPSiteEntity+CoreDataClass.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MPSiteEntity+CoreDataClass.m"; sourceTree = "<group>"; };
DA0CC58A1EB6B030009A8ED9 /* MPSiteEntity+CoreDataProperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MPSiteEntity+CoreDataProperties.h"; sourceTree = "<group>"; }; DA0CC58A1EB6B030009A8ED9 /* MPSiteEntity+CoreDataProperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MPSiteEntity+CoreDataProperties.h"; sourceTree = "<group>"; };
DA0CC58B1EB6B030009A8ED9 /* MPSiteEntity+CoreDataProperties.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MPSiteEntity+CoreDataProperties.m"; sourceTree = "<group>"; }; DA0CC58B1EB6B030009A8ED9 /* MPSiteEntity+CoreDataProperties.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MPSiteEntity+CoreDataProperties.m"; sourceTree = "<group>"; };
DA24EBB219DAD4D000FF010B /* Icon-60.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-60.png"; sourceTree = "<group>"; };
DA24EBB319DAD4D000FF010B /* Icon-60@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-60@2x.png"; sourceTree = "<group>"; };
DA24EBB419DAD4D000FF010B /* Icon-60@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-60@3x.png"; sourceTree = "<group>"; };
DA24EBB519DAD4D000FF010B /* Icon-76.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-76.png"; sourceTree = "<group>"; };
DA24EBB619DAD4D000FF010B /* Icon-76@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-76@2x.png"; sourceTree = "<group>"; };
DA24EBB719DAD4D000FF010B /* Icon-Small-40.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small-40.png"; sourceTree = "<group>"; };
DA24EBB819DAD4D000FF010B /* Icon-Small-40@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small-40@2x.png"; sourceTree = "<group>"; };
DA24EBB919DAD4D000FF010B /* Icon-Small-40@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small-40@3x.png"; sourceTree = "<group>"; };
DA24EBBA19DAD4D000FF010B /* Icon-Small.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small.png"; sourceTree = "<group>"; };
DA24EBBB19DAD4D000FF010B /* Icon-Small@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small@2x.png"; sourceTree = "<group>"; };
DA24EBBC19DAD4D000FF010B /* Icon-Small@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small@3x.png"; sourceTree = "<group>"; };
DA24EBBD19DAD4D000FF010B /* iTunesArtwork.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = iTunesArtwork.png; sourceTree = "<group>"; };
DA24EBBE19DAD4D000FF010B /* iTunesArtwork@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "iTunesArtwork@2x.png"; sourceTree = "<group>"; };
DA24EBBF19DAD4D000FF010B /* icon.sketch */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = icon.sketch; sourceTree = "<group>"; }; DA24EBBF19DAD4D000FF010B /* icon.sketch */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = icon.sketch; sourceTree = "<group>"; };
DA24EBC119DAD4D000FF010B /* 32.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = 32.png; sourceTree = "<group>"; }; DA24EBC119DAD4D000FF010B /* 32.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = 32.png; sourceTree = "<group>"; };
DA24EBC219DAD4D000FF010B /* 320-480.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "320-480.png"; sourceTree = "<group>"; }; DA24EBC219DAD4D000FF010B /* 320-480.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "320-480.png"; sourceTree = "<group>"; };
@@ -597,8 +590,6 @@
DA24EBC819DAD4D000FF010B /* background@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "background@2x.png"; sourceTree = "<group>"; }; DA24EBC819DAD4D000FF010B /* background@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "background@2x.png"; sourceTree = "<group>"; };
DA24EBC919DAD4D000FF010B /* background@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "background@3x.png"; sourceTree = "<group>"; }; DA24EBC919DAD4D000FF010B /* background@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "background@3x.png"; sourceTree = "<group>"; };
DA24EBCA19DAD4D000FF010B /* launch.sketch */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = launch.sketch; sourceTree = "<group>"; }; DA24EBCA19DAD4D000FF010B /* launch.sketch */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = launch.sketch; sourceTree = "<group>"; };
DA24EBE619DAD6DE00FF010B /* Icon-320.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-320.png"; sourceTree = "<group>"; };
DA24EBE719DAD6DE00FF010B /* Icon-64.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-64.png"; sourceTree = "<group>"; };
DA250A13195665A100AC23F1 /* UITableView+PearlReloadItems.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITableView+PearlReloadItems.m"; sourceTree = "<group>"; }; DA250A13195665A100AC23F1 /* UITableView+PearlReloadItems.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITableView+PearlReloadItems.m"; sourceTree = "<group>"; };
DA250A14195665A100AC23F1 /* UITableView+PearlReloadItems.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITableView+PearlReloadItems.h"; sourceTree = "<group>"; }; DA250A14195665A100AC23F1 /* UITableView+PearlReloadItems.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITableView+PearlReloadItems.h"; sourceTree = "<group>"; };
DA250A15195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionReusableView+PearlDequeue.m"; sourceTree = "<group>"; }; DA250A15195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionReusableView+PearlDequeue.m"; sourceTree = "<group>"; };
@@ -653,6 +644,21 @@
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; };
DA5BFA4C147E415C00F98B1E /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; DA5BFA4C147E415C00F98B1E /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
DA5BFA4E147E415C00F98B1E /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; DA5BFA4E147E415C00F98B1E /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
DA5E0E4624589C9A0007FBA7 /* Icon-Small@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small@3x.png"; sourceTree = "<group>"; };
DA5E0E4724589C9A0007FBA7 /* Icon-Small-40.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small-40.png"; sourceTree = "<group>"; };
DA5E0E4824589C9A0007FBA7 /* Icon-Small-20.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small-20.png"; sourceTree = "<group>"; };
DA5E0E4924589C9A0007FBA7 /* Icon-76.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-76.png"; sourceTree = "<group>"; };
DA5E0E4A24589C9A0007FBA7 /* Icon-Small@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small@2x.png"; sourceTree = "<group>"; };
DA5E0E4B24589C9A0007FBA7 /* Icon-Small-40@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small-40@2x.png"; sourceTree = "<group>"; };
DA5E0E4C24589C9A0007FBA7 /* Icon-76@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-76@2x.png"; sourceTree = "<group>"; };
DA5E0E4D24589C9A0007FBA7 /* Icon-Small-20@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small-20@3x.png"; sourceTree = "<group>"; };
DA5E0E4E24589C9A0007FBA7 /* Icon-Small.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small.png"; sourceTree = "<group>"; };
DA5E0E4F24589C9A0007FBA7 /* Icon-83@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-83@2x.png"; sourceTree = "<group>"; };
DA5E0E5024589C9A0007FBA7 /* Icon-Small-40@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small-40@3x.png"; sourceTree = "<group>"; };
DA5E0E5124589C9A0007FBA7 /* Icon-60@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-60@3x.png"; sourceTree = "<group>"; };
DA5E0E5224589C9B0007FBA7 /* iTunesArtwork@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "iTunesArtwork@2x.png"; sourceTree = "<group>"; };
DA5E0E5324589C9B0007FBA7 /* Icon-Small-20@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-Small-20@2x.png"; sourceTree = "<group>"; };
DA5E0E5424589C9B0007FBA7 /* Icon-60@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-60@2x.png"; sourceTree = "<group>"; };
DA5E5C3C1723681B003798D8 /* Square-bottom.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Square-bottom.png"; path = "Dividers/Square-bottom.png"; sourceTree = "<group>"; }; DA5E5C3C1723681B003798D8 /* Square-bottom.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Square-bottom.png"; path = "Dividers/Square-bottom.png"; sourceTree = "<group>"; };
DA6701B716406A4100B61001 /* Accounts.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accounts.framework; path = System/Library/Frameworks/Accounts.framework; sourceTree = SDKROOT; }; DA6701B716406A4100B61001 /* Accounts.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accounts.framework; path = System/Library/Frameworks/Accounts.framework; sourceTree = SDKROOT; };
DA6701DD16406B7300B61001 /* Social.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Social.framework; path = System/Library/Frameworks/Social.framework; sourceTree = SDKROOT; }; DA6701DD16406B7300B61001 /* Social.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Social.framework; path = System/Library/Frameworks/Social.framework; sourceTree = SDKROOT; };
@@ -662,6 +668,7 @@
DA67460B18DE7F0C00DFE240 /* Exo2.0-ExtraBold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Exo2.0-ExtraBold.otf"; sourceTree = "<group>"; }; DA67460B18DE7F0C00DFE240 /* Exo2.0-ExtraBold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Exo2.0-ExtraBold.otf"; sourceTree = "<group>"; };
DA67460C18DE7F0C00DFE240 /* Exo2.0-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Exo2.0-Bold.otf"; sourceTree = "<group>"; }; DA67460C18DE7F0C00DFE240 /* Exo2.0-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Exo2.0-Bold.otf"; sourceTree = "<group>"; };
DA70EC7F1811B13C00F65DB2 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; }; DA70EC7F1811B13C00F65DB2 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; };
DA72E22F2453B91700676D4F /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; };
DA771FE41E6E1595004D7EDE /* MasterPassword-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MasterPassword-Prefix.pch"; sourceTree = "<group>"; }; DA771FE41E6E1595004D7EDE /* MasterPassword-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MasterPassword-Prefix.pch"; sourceTree = "<group>"; };
DA854C8118D4CFBF00106317 /* avatar-add@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "avatar-add@2x.png"; sourceTree = "<group>"; }; DA854C8118D4CFBF00106317 /* avatar-add@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "avatar-add@2x.png"; sourceTree = "<group>"; };
DA854C8218D4CFBF00106317 /* avatar-add.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "avatar-add.png"; sourceTree = "<group>"; }; DA854C8218D4CFBF00106317 /* avatar-add.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "avatar-add.png"; sourceTree = "<group>"; };
@@ -1597,6 +1604,7 @@
DAE2725A19C93B8E007C5262 /* StoreKit.framework in Frameworks */, DAE2725A19C93B8E007C5262 /* StoreKit.framework in Frameworks */,
DA6701E016406BB400B61001 /* AdSupport.framework in Frameworks */, DA6701E016406BB400B61001 /* AdSupport.framework in Frameworks */,
DA6701DE16406B7300B61001 /* Social.framework in Frameworks */, DA6701DE16406B7300B61001 /* Social.framework in Frameworks */,
DA72E2302453B91700676D4F /* WebKit.framework in Frameworks */,
DA6701B816406A4100B61001 /* Accounts.framework in Frameworks */, DA6701B816406A4100B61001 /* Accounts.framework in Frameworks */,
DABB981615100B4000B05417 /* SystemConfiguration.framework in Frameworks */, DABB981615100B4000B05417 /* SystemConfiguration.framework in Frameworks */,
DA672D3014F9413D004A189C /* libPearl.a in Frameworks */, DA672D3014F9413D004A189C /* libPearl.a in Frameworks */,
@@ -1685,21 +1693,21 @@
DA24EBB119DAD4D000FF010B /* icon */ = { DA24EBB119DAD4D000FF010B /* icon */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DA24EBB219DAD4D000FF010B /* Icon-60.png */, DA5E0E5424589C9B0007FBA7 /* Icon-60@2x.png */,
DA24EBB319DAD4D000FF010B /* Icon-60@2x.png */, DA5E0E5124589C9A0007FBA7 /* Icon-60@3x.png */,
DA24EBB419DAD4D000FF010B /* Icon-60@3x.png */, DA5E0E4924589C9A0007FBA7 /* Icon-76.png */,
DA24EBE719DAD6DE00FF010B /* Icon-64.png */, DA5E0E4C24589C9A0007FBA7 /* Icon-76@2x.png */,
DA24EBB519DAD4D000FF010B /* Icon-76.png */, DA5E0E4F24589C9A0007FBA7 /* Icon-83@2x.png */,
DA24EBB619DAD4D000FF010B /* Icon-76@2x.png */, DA5E0E4824589C9A0007FBA7 /* Icon-Small-20.png */,
DA24EBE619DAD6DE00FF010B /* Icon-320.png */, DA5E0E5324589C9B0007FBA7 /* Icon-Small-20@2x.png */,
DA24EBB719DAD4D000FF010B /* Icon-Small-40.png */, DA5E0E4D24589C9A0007FBA7 /* Icon-Small-20@3x.png */,
DA24EBB819DAD4D000FF010B /* Icon-Small-40@2x.png */, DA5E0E4724589C9A0007FBA7 /* Icon-Small-40.png */,
DA24EBB919DAD4D000FF010B /* Icon-Small-40@3x.png */, DA5E0E4B24589C9A0007FBA7 /* Icon-Small-40@2x.png */,
DA24EBBA19DAD4D000FF010B /* Icon-Small.png */, DA5E0E5024589C9A0007FBA7 /* Icon-Small-40@3x.png */,
DA24EBBB19DAD4D000FF010B /* Icon-Small@2x.png */, DA5E0E4E24589C9A0007FBA7 /* Icon-Small.png */,
DA24EBBC19DAD4D000FF010B /* Icon-Small@3x.png */, DA5E0E4A24589C9A0007FBA7 /* Icon-Small@2x.png */,
DA24EBBD19DAD4D000FF010B /* iTunesArtwork.png */, DA5E0E4624589C9A0007FBA7 /* Icon-Small@3x.png */,
DA24EBBE19DAD4D000FF010B /* iTunesArtwork@2x.png */, DA5E0E5224589C9B0007FBA7 /* iTunesArtwork@2x.png */,
); );
path = icon; path = icon;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -1775,6 +1783,7 @@
DA5BFA47147E415C00F98B1E /* Frameworks */ = { DA5BFA47147E415C00F98B1E /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DA72E22F2453B91700676D4F /* WebKit.framework */,
DAFC1F6F2439795000D1CD66 /* CoreServices.framework */, DAFC1F6F2439795000D1CD66 /* CoreServices.framework */,
DAB7AE5C1F3D752900C856B1 /* libjson-c.a */, DAB7AE5C1F3D752900C856B1 /* libjson-c.a */,
DA6701B716406A4100B61001 /* Accounts.framework */, DA6701B716406A4100B61001 /* Accounts.framework */,
@@ -2770,8 +2779,6 @@
93D39B050DD5F55E9794EFD4 /* MPPopdownSegue.m */, 93D39B050DD5F55E9794EFD4 /* MPPopdownSegue.m */,
DABD3BEA1711E2DC00CF925C /* MPPreferencesViewController.h */, DABD3BEA1711E2DC00CF925C /* MPPreferencesViewController.h */,
DABD3BEB1711E2DC00CF925C /* MPPreferencesViewController.m */, DABD3BEB1711E2DC00CF925C /* MPPreferencesViewController.m */,
93D3924D6F77E6BF41AC32D3 /* MPRootSegue.h */,
93D399493FEDDE74DD1A0C15 /* MPRootSegue.m */,
93D39730673227EFF6DEFF19 /* MPSetupViewController.h */, 93D39730673227EFF6DEFF19 /* MPSetupViewController.h */,
93D39A28369954D147E239BA /* MPSetupViewController.m */, 93D39A28369954D147E239BA /* MPSetupViewController.m */,
93D39975CE5AEC99E3F086C7 /* MPSiteCell.h */, 93D39975CE5AEC99E3F086C7 /* MPSiteCell.h */,
@@ -3152,7 +3159,7 @@
DA5BFA42147E415C00F98B1E /* Resources */, DA5BFA42147E415C00F98B1E /* Resources */,
DA6556E314D55F3000841C99 /* Run Script: GIT version -> Info.plist */, DA6556E314D55F3000841C99 /* Run Script: GIT version -> Info.plist */,
4A87858EE3659604089E2F9F /* [CP] Embed Pods Frameworks */, 4A87858EE3659604089E2F9F /* [CP] Embed Pods Frameworks */,
DA3C4EB32439438B00A6C4A8 /* Upload Sentry dSYM */, DA3C4EB32439438B00A6C4A8 /* Sentry dSYM Upload */,
); );
buildRules = ( buildRules = (
); );
@@ -3286,6 +3293,7 @@
DA69540617D975D900BF294E /* icon_gears.png in Resources */, DA69540617D975D900BF294E /* icon_gears.png in Resources */,
DA67460D18DE7F0C00DFE240 /* Exo2.0-Thin.otf in Resources */, DA67460D18DE7F0C00DFE240 /* Exo2.0-Thin.otf in Resources */,
DA4522451902355C008F650A /* icon_book@2x.png in Resources */, DA4522451902355C008F650A /* icon_book@2x.png in Resources */,
DA5E0E5F24589C9B0007FBA7 /* Icon-Small-40@3x.png in Resources */,
DA32D04919D2F417004F3F0E /* thumb_fuel@2x.png in Resources */, DA32D04919D2F417004F3F0E /* thumb_fuel@2x.png in Resources */,
DAFC1F73243989FB00D1CD66 /* Launch Screen.storyboard in Resources */, DAFC1F73243989FB00D1CD66 /* Launch Screen.storyboard in Resources */,
DABD39371711E29700CF925C /* avatar-0.png in Resources */, DABD39371711E29700CF925C /* avatar-0.png in Resources */,
@@ -3305,11 +3313,9 @@
DA67461018DE7F0C00DFE240 /* Exo2.0-Bold.otf in Resources */, DA67461018DE7F0C00DFE240 /* Exo2.0-Bold.otf in Resources */,
DABD39401711E29700CF925C /* avatar-13.png in Resources */, DABD39401711E29700CF925C /* avatar-13.png in Resources */,
DA32D07C19D7D784004F3F0E /* background.png in Resources */, DA32D07C19D7D784004F3F0E /* background.png in Resources */,
DA24EBE919DAD6DE00FF010B /* Icon-64.png in Resources */,
DABD39411711E29700CF925C /* avatar-13@2x.png in Resources */, DABD39411711E29700CF925C /* avatar-13@2x.png in Resources */,
DABD39421711E29700CF925C /* avatar-14.png in Resources */, DABD39421711E29700CF925C /* avatar-14.png in Resources */,
DABD39431711E29700CF925C /* avatar-14@2x.png in Resources */, DABD39431711E29700CF925C /* avatar-14@2x.png in Resources */,
DA24EBE819DAD6DE00FF010B /* Icon-320.png in Resources */,
DAA1764419D8B82B0044227B /* personal_pw.png in Resources */, DAA1764419D8B82B0044227B /* personal_pw.png in Resources */,
DABD39441711E29700CF925C /* avatar-15.png in Resources */, DABD39441711E29700CF925C /* avatar-15.png in Resources */,
DABD39451711E29700CF925C /* avatar-15@2x.png in Resources */, DABD39451711E29700CF925C /* avatar-15@2x.png in Resources */,
@@ -3363,11 +3369,14 @@
DA32D00919CF5C55004F3F0E /* icon_question.png in Resources */, DA32D00919CF5C55004F3F0E /* icon_question.png in Resources */,
DABD39561711E29700CF925C /* avatar-6@2x.png in Resources */, DABD39561711E29700CF925C /* avatar-6@2x.png in Resources */,
DA32D07B19D7D784004F3F0E /* background@2x.png in Resources */, DA32D07B19D7D784004F3F0E /* background@2x.png in Resources */,
DA5E0E6224589C9B0007FBA7 /* Icon-Small-20@2x.png in Resources */,
DABD39571711E29700CF925C /* avatar-7.png in Resources */, DABD39571711E29700CF925C /* avatar-7.png in Resources */,
DABD39581711E29700CF925C /* avatar-7@2x.png in Resources */, DABD39581711E29700CF925C /* avatar-7@2x.png in Resources */,
DA5E0E5C24589C9B0007FBA7 /* Icon-Small-20@3x.png in Resources */,
DABD39591711E29700CF925C /* avatar-8.png in Resources */, DABD39591711E29700CF925C /* avatar-8.png in Resources */,
DA32D00A19CF5C55004F3F0E /* icon_question@2x.png in Resources */, DA32D00A19CF5C55004F3F0E /* icon_question@2x.png in Resources */,
DAA1764019D8B82B0044227B /* site_new.png in Resources */, DAA1764019D8B82B0044227B /* site_new.png in Resources */,
DA5E0E5924589C9B0007FBA7 /* Icon-Small@2x.png in Resources */,
DABD395A1711E29700CF925C /* avatar-8@2x.png in Resources */, DABD395A1711E29700CF925C /* avatar-8@2x.png in Resources */,
DABD395B1711E29700CF925C /* avatar-9.png in Resources */, DABD395B1711E29700CF925C /* avatar-9.png in Resources */,
DAA1765419D8B82B0044227B /* choose_type.png in Resources */, DAA1765419D8B82B0044227B /* choose_type.png in Resources */,
@@ -3377,23 +3386,30 @@
DA945C8717E3F3FD0053236B /* Images.xcassets in Resources */, DA945C8717E3F3FD0053236B /* Images.xcassets in Resources */,
DA32D04E19D2F59B004F3F0E /* meter_fuel@3x.png in Resources */, DA32D04E19D2F59B004F3F0E /* meter_fuel@3x.png in Resources */,
DA25C5FE197DBF200046CDCF /* icon_thumbs-up.png in Resources */, DA25C5FE197DBF200046CDCF /* icon_thumbs-up.png in Resources */,
DA5E0E5624589C9B0007FBA7 /* Icon-Small-40.png in Resources */,
DABD39871711E29700CF925C /* SourceCodePro-Black.otf in Resources */, DABD39871711E29700CF925C /* SourceCodePro-Black.otf in Resources */,
DABD39881711E29700CF925C /* SourceCodePro-ExtraLight.otf in Resources */, DABD39881711E29700CF925C /* SourceCodePro-ExtraLight.otf in Resources */,
DABD39A01711E29700CF925C /* icon_action.png in Resources */, DABD39A01711E29700CF925C /* icon_action.png in Resources */,
DA5E0E5524589C9B0007FBA7 /* Icon-Small@3x.png in Resources */,
DABD39A11711E29700CF925C /* icon_action@2x.png in Resources */, DABD39A11711E29700CF925C /* icon_action@2x.png in Resources */,
DABD39F21711E29700CF925C /* icon_cancel.png in Resources */, DABD39F21711E29700CF925C /* icon_cancel.png in Resources */,
DA25C5FB197CCAE00046CDCF /* icon_delete@2x.png in Resources */, DA25C5FB197CCAE00046CDCF /* icon_delete@2x.png in Resources */,
DA5E0E5724589C9B0007FBA7 /* Icon-Small-20.png in Resources */,
DA29993219C9132F00AF7DF1 /* thumb_generated_login@3x.png in Resources */, DA29993219C9132F00AF7DF1 /* thumb_generated_login@3x.png in Resources */,
DA29992F19C86F5700AF7DF1 /* thumb_generated_login@2x.png in Resources */, DA29992F19C86F5700AF7DF1 /* thumb_generated_login@2x.png in Resources */,
DA73049F194E022B00E72520 /* ui_textfield.png in Resources */, DA73049F194E022B00E72520 /* ui_textfield.png in Resources */,
DABD39F31711E29700CF925C /* icon_cancel@2x.png in Resources */, DABD39F31711E29700CF925C /* icon_cancel@2x.png in Resources */,
DA5E0E5B24589C9B0007FBA7 /* Icon-76@2x.png in Resources */,
DABD3A261711E29700CF925C /* icon_edit.png in Resources */, DABD3A261711E29700CF925C /* icon_edit.png in Resources */,
DABD3A271711E29700CF925C /* icon_edit@2x.png in Resources */, DABD3A271711E29700CF925C /* icon_edit@2x.png in Resources */,
DA24EBAF19DAD08C00FF010B /* tip_basic_black_top@2x.png in Resources */, DA24EBAF19DAD08C00FF010B /* tip_basic_black_top@2x.png in Resources */,
DABD3A3A1711E29700CF925C /* icon_find.png in Resources */, DABD3A3A1711E29700CF925C /* icon_find.png in Resources */,
DA5E0E6324589C9B0007FBA7 /* Icon-60@2x.png in Resources */,
DABD3A3B1711E29700CF925C /* icon_find@2x.png in Resources */, DABD3A3B1711E29700CF925C /* icon_find@2x.png in Resources */,
DAA1765319D8B82B0044227B /* choose_type@2x.png in Resources */, DAA1765319D8B82B0044227B /* choose_type@2x.png in Resources */,
DA5E0E6124589C9B0007FBA7 /* iTunesArtwork@2x.png in Resources */,
DABD3AA01711E29800CF925C /* icon_pause.png in Resources */, DABD3AA01711E29800CF925C /* icon_pause.png in Resources */,
DA5E0E5A24589C9B0007FBA7 /* Icon-Small-40@2x.png in Resources */,
DABD3AA11711E29800CF925C /* icon_pause@2x.png in Resources */, DABD3AA11711E29800CF925C /* icon_pause@2x.png in Resources */,
DAA1764219D8B82B0044227B /* settings.png in Resources */, DAA1764219D8B82B0044227B /* settings.png in Resources */,
DABD3AAA1711E29800CF925C /* icon_person.png in Resources */, DABD3AAA1711E29800CF925C /* icon_person.png in Resources */,
@@ -3418,7 +3434,6 @@
DABD3B951711E29800CF925C /* pull-down.png in Resources */, DABD3B951711E29800CF925C /* pull-down.png in Resources */,
DABD3B961711E29800CF925C /* pull-down@2x.png in Resources */, DABD3B961711E29800CF925C /* pull-down@2x.png in Resources */,
DABD3B971711E29800CF925C /* pull-up.png in Resources */, DABD3B971711E29800CF925C /* pull-up.png in Resources */,
DA24EBEA19DAD6EE00FF010B /* Icon-Small.png in Resources */,
DA8495321A93049300B3053D /* icon_down@2x.png in Resources */, DA8495321A93049300B3053D /* icon_down@2x.png in Resources */,
DABD3B981711E29800CF925C /* pull-up@2x.png in Resources */, DABD3B981711E29800CF925C /* pull-up@2x.png in Resources */,
DA7304A0194E022B00E72520 /* ui_textfield@2x.png in Resources */, DA7304A0194E022B00E72520 /* ui_textfield@2x.png in Resources */,
@@ -3427,10 +3442,9 @@
DA452249190628A1008F650A /* icon_wrench.png in Resources */, DA452249190628A1008F650A /* icon_wrench.png in Resources */,
DAA1764519D8B82B0044227B /* name_new@2x.png in Resources */, DAA1764519D8B82B0044227B /* name_new@2x.png in Resources */,
DA45224819062899008F650A /* icon_settings@2x.png in Resources */, DA45224819062899008F650A /* icon_settings@2x.png in Resources */,
DA5E0E5E24589C9B0007FBA7 /* Icon-83@2x.png in Resources */,
DA854C8418D4CFBF00106317 /* avatar-add.png in Resources */, DA854C8418D4CFBF00106317 /* avatar-add.png in Resources */,
DA24EBEB19DAD6EE00FF010B /* Icon-Small@2x.png in Resources */,
DAA1764B19D8B82B0044227B /* login_name@2x.png in Resources */, DAA1764B19D8B82B0044227B /* login_name@2x.png in Resources */,
DABD3C241711E2DC00CF925C /* MasterPassword.entitlements in Resources */,
DABD3C251711E2DC00CF925C /* Settings.bundle in Resources */, DABD3C251711E2DC00CF925C /* Settings.bundle in Resources */,
DABD3C261711E2DC00CF925C /* InfoPlist.strings in Resources */, DABD3C261711E2DC00CF925C /* InfoPlist.strings in Resources */,
DA32D05119D3D107004F3F0E /* icon_meter.png in Resources */, DA32D05119D3D107004F3F0E /* icon_meter.png in Resources */,
@@ -3444,16 +3458,18 @@
DAA1764619D8B82B0044227B /* name_new.png in Resources */, DAA1764619D8B82B0044227B /* name_new.png in Resources */,
DA45224B190628B2008F650A /* icon_gear.png in Resources */, DA45224B190628B2008F650A /* icon_gear.png in Resources */,
DAAA1D4123CD145000F3DF56 /* Storyboard.storyboard in Resources */, DAAA1D4123CD145000F3DF56 /* Storyboard.storyboard in Resources */,
DA5E0E6024589C9B0007FBA7 /* Icon-60@3x.png in Resources */,
DA8495311A93049300B3053D /* icon_down.png in Resources */, DA8495311A93049300B3053D /* icon_down.png in Resources */,
DA5E0E5D24589C9B0007FBA7 /* Icon-Small.png in Resources */,
DA25C5FF197DBF200046CDCF /* icon_thumbs-up@2x.png in Resources */, DA25C5FF197DBF200046CDCF /* icon_thumbs-up@2x.png in Resources */,
DAE1EF2217E942DE00BC0086 /* Localizable.strings in Resources */, DAE1EF2217E942DE00BC0086 /* Localizable.strings in Resources */,
DA5A09DF171A70E4005284AB /* play.png in Resources */, DA5A09DF171A70E4005284AB /* play.png in Resources */,
DA24EBEC19DAD6EE00FF010B /* Icon-Small@3x.png in Resources */,
DA5A09E0171A70E4005284AB /* play@2x.png in Resources */, DA5A09E0171A70E4005284AB /* play@2x.png in Resources */,
DA5A09EA171BB0F7005284AB /* unlocked.png in Resources */, DA5A09EA171BB0F7005284AB /* unlocked.png in Resources */,
DAA1762819D89B610044227B /* thumb_ios_integration.png in Resources */, DAA1762819D89B610044227B /* thumb_ios_integration.png in Resources */,
DA5A09EB171BB0F7005284AB /* unlocked@2x.png in Resources */, DA5A09EB171BB0F7005284AB /* unlocked@2x.png in Resources */,
DAA1764F19D8B82B0044227B /* counter@2x.png in Resources */, DAA1764F19D8B82B0044227B /* counter@2x.png in Resources */,
DA5E0E5824589C9B0007FBA7 /* Icon-76.png in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -3499,7 +3515,7 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
DA3C4EB32439438B00A6C4A8 /* Upload Sentry dSYM */ = { DA3C4EB32439438B00A6C4A8 /* Sentry dSYM Upload */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 8; buildActionMask = 8;
files = ( files = (
@@ -3507,15 +3523,16 @@
inputFileListPaths = ( inputFileListPaths = (
); );
inputPaths = ( inputPaths = (
"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Resources/DWARF/${TARGET_NAME}",
); );
name = "Upload Sentry dSYM"; name = "Sentry dSYM Upload";
outputFileListPaths = ( outputFileListPaths = (
); );
outputPaths = ( outputPaths = (
); );
runOnlyForDeploymentPostprocessing = 1; runOnlyForDeploymentPostprocessing = 1;
shellPath = "/bin/sh -e"; shellPath = "/bin/sh -e";
shellScript = "if hash sentry-cli 2>/dev/null; then\n if ! ERROR=$(SENTRY_ORG=lyndir SENTRY_PROJECT=masterpassword-ios sentry-cli upload-dif --log-level info \"$DWARF_DSYM_FOLDER_PATH\" 2>&1 >/dev/null); then\n echo >&2 \"warning: sentry-cli: $ERROR\"\n fi\nelse\n echo >&2 \"warning: sentry-cli not installed: try brew install getsentry/tools/sentry-cli\"\nfi\n"; shellScript = "if ! hash sentry-cli 2>/dev/null; then\n echo >&2 \"error: sentry-cli not installed. Try brew install getsentry/tools/sentry-cli\"\n exit 1\nfi\n\nSENTRY_ORG=lyndir SENTRY_PROJECT=masterpassword-ios sentry-cli upload-dif --log-level info \"$DWARF_DSYM_FOLDER_PATH\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
DA6556E314D55F3000841C99 /* Run Script: GIT version -> Info.plist */ = { DA6556E314D55F3000841C99 /* Run Script: GIT version -> Info.plist */ = {
@@ -3618,7 +3635,6 @@
93D399D7E08A142776A74CB8 /* MPOverlayViewController.m in Sources */, 93D399D7E08A142776A74CB8 /* MPOverlayViewController.m in Sources */,
DA46021D23D5E30B00398FF4 /* MPSecrets.m in Sources */, DA46021D23D5E30B00398FF4 /* MPSecrets.m in Sources */,
93D39A27F2506C6FEEF9C588 /* MPAlgorithmV2.m in Sources */, 93D39A27F2506C6FEEF9C588 /* MPAlgorithmV2.m in Sources */,
93D39B429C67A62E29DC02DA /* MPRootSegue.m in Sources */,
93D392FD5E2052F7D7DB3774 /* NSString+MPMarkDown.m in Sources */, 93D392FD5E2052F7D7DB3774 /* NSString+MPMarkDown.m in Sources */,
93D395B715D15F2B56F2A2EE /* mpw-types.c in Sources */, 93D395B715D15F2B56F2A2EE /* mpw-types.c in Sources */,
93D39943D01E70DAC3B0DF76 /* mpw-util.c in Sources */, 93D39943D01E70DAC3B0DF76 /* mpw-util.c in Sources */,
@@ -3664,7 +3680,6 @@
DAFE4A2E15039824003ABA7C /* PearlStrings.m in Sources */, DAFE4A2E15039824003ABA7C /* PearlStrings.m in Sources */,
DAFE4A3015039824003ABA7C /* PearlStringUtils.m in Sources */, DAFE4A3015039824003ABA7C /* PearlStringUtils.m in Sources */,
DAFE4A3715039824003ABA7C /* PearlKeyChain.m in Sources */, DAFE4A3715039824003ABA7C /* PearlKeyChain.m in Sources */,
DA72BD7B19C1510C00E6ACFE /* UIView+FontScale.m in Sources */,
DA250A17195665A100AC23F1 /* UITableView+PearlReloadItems.m in Sources */, DA250A17195665A100AC23F1 /* UITableView+PearlReloadItems.m in Sources */,
DAFE4A4115039824003ABA7C /* PearlArrayTVC.m in Sources */, DAFE4A4115039824003ABA7C /* PearlArrayTVC.m in Sources */,
DAFE4A4315039824003ABA7C /* PearlBoxView.m in Sources */, DAFE4A4315039824003ABA7C /* PearlBoxView.m in Sources */,
@@ -3865,7 +3880,6 @@
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
STRIP_INSTALLED_PRODUCT = NO; STRIP_INSTALLED_PRODUCT = NO;
STRIP_SWIFT_SYMBOLS = NO; STRIP_SWIFT_SYMBOLS = NO;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
WARNING_CFLAGS = "-Wno-float-conversion"; WARNING_CFLAGS = "-Wno-float-conversion";
}; };
@@ -3994,10 +4008,6 @@
"\"$(PROJECT_DIR)/../lib/libjson-c/build-ios~/out/include\"", "\"$(PROJECT_DIR)/../lib/libjson-c/build-ios~/out/include\"",
); );
INFOPLIST_FILE = "Source/iOS/MasterPassword-Info.plist"; INFOPLIST_FILE = "Source/iOS/MasterPassword-Info.plist";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"\"$(PROJECT_DIR)/../lib/libsodium/build-ios~/out/lib\"", "\"$(PROJECT_DIR)/../lib/libsodium/build-ios~/out/lib\"",
@@ -4005,7 +4015,7 @@
); );
OTHER_CFLAGS = ( OTHER_CFLAGS = (
"-DMPW_SODIUM=1", "-DMPW_SODIUM=1",
"-DMPW_CPERCIVA=0", "-DMPW_JSON=1",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.lyndir.lhunath.MasterPassword; PRODUCT_BUNDLE_IDENTIFIER = com.lyndir.lhunath.MasterPassword;
PRODUCT_NAME = MasterPassword; PRODUCT_NAME = MasterPassword;
@@ -4013,9 +4023,6 @@
SKIP_INSTALL = NO; SKIP_INSTALL = NO;
STRIP_INSTALLED_PRODUCT = YES; STRIP_INSTALLED_PRODUCT = YES;
STRIP_SWIFT_SYMBOLS = YES; STRIP_SWIFT_SYMBOLS = YES;
SWIFT_OBJC_BRIDGING_HEADER = "Source/iOS/MasterPassword-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 4.0;
}; };
name = Debug; name = Debug;
}; };
@@ -4028,7 +4035,6 @@
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = NO; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = NO;
CODE_SIGN_ENTITLEMENTS = Source/iOS/MasterPassword.entitlements; CODE_SIGN_ENTITLEMENTS = Source/iOS/MasterPassword.entitlements;
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Manual;
EXCLUDED_SOURCE_FILE_NAMES = libDCIntrospect.a;
GCC_C_LANGUAGE_STANDARD = c11; GCC_C_LANGUAGE_STANDARD = c11;
GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch"; GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
@@ -4038,10 +4044,6 @@
"\"$(PROJECT_DIR)/../lib/libjson-c/build-ios~/out/include\"", "\"$(PROJECT_DIR)/../lib/libjson-c/build-ios~/out/include\"",
); );
INFOPLIST_FILE = "Source/iOS/MasterPassword-Info.plist"; INFOPLIST_FILE = "Source/iOS/MasterPassword-Info.plist";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"\"$(PROJECT_DIR)/../lib/libsodium/build-ios~/out/lib\"", "\"$(PROJECT_DIR)/../lib/libsodium/build-ios~/out/lib\"",
@@ -4049,7 +4051,7 @@
); );
OTHER_CFLAGS = ( OTHER_CFLAGS = (
"-DMPW_SODIUM=1", "-DMPW_SODIUM=1",
"-DMPW_CPERCIVA=0", "-DMPW_JSON=1",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.lyndir.lhunath.MasterPassword; PRODUCT_BUNDLE_IDENTIFIER = com.lyndir.lhunath.MasterPassword;
PRODUCT_NAME = MasterPassword; PRODUCT_NAME = MasterPassword;
@@ -4057,10 +4059,6 @@
SKIP_INSTALL = NO; SKIP_INSTALL = NO;
STRIP_INSTALLED_PRODUCT = YES; STRIP_INSTALLED_PRODUCT = YES;
STRIP_SWIFT_SYMBOLS = YES; STRIP_SWIFT_SYMBOLS = YES;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OBJC_BRIDGING_HEADER = "Source/iOS/MasterPassword-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 4.0;
}; };
name = Release; name = Release;
}; };

View File

@@ -73,6 +73,13 @@
identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier" identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier"
referenceType = "1"> referenceType = "1">
</LocationScenarioReference> </LocationScenarioReference>
<EnvironmentVariables>
<EnvironmentVariable
key = "TERM"
value = "color"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
</LaunchAction> </LaunchAction>
<ProfileAction <ProfileAction
buildConfiguration = "Debug" buildConfiguration = "Debug"

View File

@@ -101,7 +101,6 @@
DA5E5D011724A667003798D8 /* MPKey.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CAE1724A667003798D8 /* MPKey.m */; }; DA5E5D011724A667003798D8 /* MPKey.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CAE1724A667003798D8 /* MPKey.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 */; };
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 */; };
@@ -2329,7 +2328,7 @@
DAD9B5EE1762CA3A001835F9 /* Copy LoginHelper */, DAD9B5EE1762CA3A001835F9 /* Copy LoginHelper */,
DA6556E314D55F3000841C99 /* Run Script: GIT version -> Info.plist */, DA6556E314D55F3000841C99 /* Run Script: GIT version -> Info.plist */,
43E5966C8C236E86824DDADE /* [CP] Embed Pods Frameworks */, 43E5966C8C236E86824DDADE /* [CP] Embed Pods Frameworks */,
DA3C4EB2243941AE00A6C4A8 /* Upload Sentry dSYM */, DA3C4EB2243941AE00A6C4A8 /* Sentry dSYM Upload */,
); );
buildRules = ( buildRules = (
); );
@@ -2388,7 +2387,7 @@
CLASSPREFIX = MP; CLASSPREFIX = MP;
LastSwiftUpdateCheck = 0720; LastSwiftUpdateCheck = 0720;
LastTestingUpgradeCheck = 0510; LastTestingUpgradeCheck = 0510;
LastUpgradeCheck = 1140; LastUpgradeCheck = 1200;
ORGANIZATIONNAME = Lyndir; ORGANIZATIONNAME = Lyndir;
TargetAttributes = { TargetAttributes = {
DA1C7AA61F1A8F24009A3551 = { DA1C7AA61F1A8F24009A3551 = {
@@ -2535,7 +2534,6 @@
DACA29671705DF81002C6C22 /* SourceCodePro-ExtraLight.otf in Resources */, DACA29671705DF81002C6C22 /* SourceCodePro-ExtraLight.otf in Resources */,
DACA29681705DF81002C6C22 /* SourceCodePro-Black.otf in Resources */, DACA29681705DF81002C6C22 /* SourceCodePro-Black.otf in Resources */,
DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */, DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */,
DA5E5D081724A667003798D8 /* MasterPassword.entitlements in Resources */,
DA5E5D0A1724A667003798D8 /* InfoPlist.strings in Resources */, DA5E5D0A1724A667003798D8 /* InfoPlist.strings in Resources */,
DA5E5D0B1724A667003798D8 /* MainMenu.xib in Resources */, DA5E5D0B1724A667003798D8 /* MainMenu.xib in Resources */,
DA0933CC1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png in Resources */, DA0933CC1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png in Resources */,
@@ -2585,7 +2583,7 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-MasterPassword-macOS/Pods-MasterPassword-macOS-frameworks.sh\"\n"; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-MasterPassword-macOS/Pods-MasterPassword-macOS-frameworks.sh\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
DA3C4EB2243941AE00A6C4A8 /* Upload Sentry dSYM */ = { DA3C4EB2243941AE00A6C4A8 /* Sentry dSYM Upload */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 8; buildActionMask = 8;
files = ( files = (
@@ -2593,15 +2591,16 @@
inputFileListPaths = ( inputFileListPaths = (
); );
inputPaths = ( inputPaths = (
"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Resources/DWARF/${TARGET_NAME}",
); );
name = "Upload Sentry dSYM"; name = "Sentry dSYM Upload";
outputFileListPaths = ( outputFileListPaths = (
); );
outputPaths = ( outputPaths = (
); );
runOnlyForDeploymentPostprocessing = 1; runOnlyForDeploymentPostprocessing = 1;
shellPath = "/bin/sh -e"; shellPath = "/bin/sh -e";
shellScript = "if hash sentry-cli 2>/dev/null; then\n if ! ERROR=$(SENTRY_ORG=lyndir SENTRY_PROJECT=masterpassword-macos sentry-cli upload-dif --log-level info \"$DWARF_DSYM_FOLDER_PATH\" 2>&1 >/dev/null); then\n echo >&2 \"warning: sentry-cli: $ERROR\"\n fi\nelse\n echo >&2 \"warning: sentry-cli not installed: try brew install getsentry/tools/sentry-cli\"\nfi\n"; shellScript = "if ! hash sentry-cli 2>/dev/null; then\n echo >&2 \"error: sentry-cli not installed. Try brew install getsentry/tools/sentry-cli\"\n exit 1\nfi\n\nSENTRY_ORG=lyndir SENTRY_PROJECT=masterpassword-macos sentry-cli upload-dif --log-level info \"$DWARF_DSYM_FOLDER_PATH\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
DA4EF9CB19FD4B600032ECB5 /* Run Script: genassets */ = { DA4EF9CB19FD4B600032ECB5 /* Run Script: genassets */ = {
@@ -3025,6 +3024,7 @@
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_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
@@ -3126,6 +3126,7 @@
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_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
@@ -3201,6 +3202,7 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CODE_SIGN_ENTITLEMENTS = Source/Mac/MasterPassword.entitlements; CODE_SIGN_ENTITLEMENTS = Source/Mac/MasterPassword.entitlements;
CODE_SIGN_IDENTITY = "-";
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch"; GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";
@@ -3240,6 +3242,7 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CODE_SIGN_ENTITLEMENTS = Source/Mac/MasterPassword.entitlements; CODE_SIGN_ENTITLEMENTS = Source/Mac/MasterPassword.entitlements;
CODE_SIGN_IDENTITY = "-";
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch"; GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "1140" LastUpgradeVersion = "1200"
version = "1.3"> version = "1.7">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"> buildImplicitDependencies = "YES">
@@ -27,15 +27,6 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
BuildableName = "Master Password.app"
BlueprintName = "MasterPassword-macOS"
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables> <Testables>
</Testables> </Testables>
</TestAction> </TestAction>
@@ -59,6 +50,17 @@
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj"> ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildableProductRunnable> </BuildableProductRunnable>
<EnvironmentVariables>
<EnvironmentVariable
key = "TERM"
value = "color"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<LocationScenarioReference
identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier"
referenceType = "1">
</LocationScenarioReference>
</LaunchAction> </LaunchAction>
<ProfileAction <ProfileAction
buildConfiguration = "Debug" buildConfiguration = "Debug"

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "1140" LastUpgradeVersion = "1200"
version = "1.3"> version = "1.7">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"> buildImplicitDependencies = "YES">
@@ -27,15 +27,6 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA1C7AC61F1A8FD8009A3551"
BuildableName = "mpw-bench"
BlueprintName = "mpw-bench"
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables> <Testables>
</Testables> </Testables>
</TestAction> </TestAction>
@@ -59,6 +50,17 @@
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj"> ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildableProductRunnable> </BuildableProductRunnable>
<EnvironmentVariables>
<EnvironmentVariable
key = "TERM"
value = "color"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<LocationScenarioReference
identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier"
referenceType = "1">
</LocationScenarioReference>
</LaunchAction> </LaunchAction>
<ProfileAction <ProfileAction
buildConfiguration = "Release" buildConfiguration = "Release"

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "1140" LastUpgradeVersion = "1200"
version = "1.7"> version = "1.7">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
@@ -27,15 +27,6 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA1C7AA61F1A8F24009A3551"
BuildableName = "mpw-cli"
BlueprintName = "mpw-cli"
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables> <Testables>
</Testables> </Testables>
</TestAction> </TestAction>
@@ -81,6 +72,13 @@
isEnabled = "YES"> isEnabled = "YES">
</CommandLineArgument> </CommandLineArgument>
</CommandLineArguments> </CommandLineArguments>
<EnvironmentVariables>
<EnvironmentVariable
key = "TERM"
value = "color"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<LocationScenarioReference <LocationScenarioReference
identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier" identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier"
referenceType = "1"> referenceType = "1">

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "1140" LastUpgradeVersion = "1200"
version = "1.3"> version = "1.7">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"> buildImplicitDependencies = "YES">
@@ -27,15 +27,6 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA67743A1A474A03004F356A"
BuildableName = "mpw-test"
BlueprintName = "mpw-test"
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables> <Testables>
</Testables> </Testables>
</TestAction> </TestAction>
@@ -60,6 +51,17 @@
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj"> ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildableProductRunnable> </BuildableProductRunnable>
<EnvironmentVariables>
<EnvironmentVariable
key = "TERM"
value = "color"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<LocationScenarioReference
identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier"
referenceType = "1">
</LocationScenarioReference>
</LaunchAction> </LaunchAction>
<ProfileAction <ProfileAction
buildConfiguration = "Debug" buildConfiguration = "Debug"

View File

@@ -28,3 +28,14 @@ target 'MasterPassword-macOS' do
pod 'KCOrderedAccessorFix' pod 'KCOrderedAccessorFix'
pod 'JRSwizzle' pod 'JRSwizzle'
end end
post_install do |installer|
installer.pods_project.targets.each do |target|
if target.name == 'Countly-iOS' || target.name == 'Countly-macOS'
target.build_configurations.each do |config|
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)']
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] << 'COUNTLY_EXCLUDE_IDFA=1'
end
end
end
end

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 843 B

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 879 KiB

After

Width:  |  Height:  |  Size: 879 KiB

View File

@@ -34,7 +34,7 @@ icons=(
# 57@1@iphone@:Icon.png # 57@1@iphone@:Icon.png
# 29@1@iphone@:Icon-Small.png # 29@1@iphone@:Icon-Small.png
# iPad # iPad
# 76@1@ipad@7.0:Icon-76.png 76@1@ipad@7.0:Icon-76.png
152@2@ipad@7.0:Icon-76@2x.png 152@2@ipad@7.0:Icon-76@2x.png
167@2@ipad@9.0:Icon-83@2x.png 167@2@ipad@9.0:Icon-83@2x.png
20@1@ipad@7.0:Icon-Small-20.png 20@1@ipad@7.0:Icon-Small-20.png

View File

@@ -46,9 +46,10 @@ esac
description=$(git describe --always --dirty --long --match "*-$platform*") description=$(git describe --always --dirty --long --match "*-$platform*")
version=${description%-g*} build=${version##*-} version=${version%-$build} version=${description%-g*} build=${version##*-} version=${version%-$build}
version=${version//-$platform/} version=${version//-/.} commit=${description##*-g} version=${version//-$platform/} version=${version//-/.} commit=${description##*-g}
IFS=. read major minor build <<< "$version"
addPlistWithKey GITDescription string "$description" addPlistWithKey GITDescription string "$description"
setPlistWithKey CFBundleVersion "$(hr "${version%%.*}" 14).${version#*.}" setPlistWithKey CFBundleVersion "$(hr "$major" 14)$(printf '%02d' "$minor")$(printf '%02d' "$build")"
setPlistWithKey CFBundleShortVersionString "$version" setPlistWithKey CFBundleShortVersionString "$version"
setSettingWithTitle "Build" "$commit" setSettingWithTitle "Build" "$commit"

View File

@@ -621,7 +621,7 @@ static NSOperationQueue *_mpwQueue = nil;
- (NSString *)exportLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)key { - (NSString *)exportLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)key {
if (!(site.type & MPSiteFeatureExportContent) || site.loginGenerated || ![site.loginName length]) if (site.loginGenerated || ![site.loginName length])
return nil; return nil;
__block NSData *state = nil; __block NSData *state = nil;

View File

@@ -117,7 +117,7 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
[self performPurchaseProductWithIdentifier:productIdentifier quantity:quantity]; [self performPurchaseProductWithIdentifier:productIdentifier quantity:quantity];
}]]; }]];
[controller addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]]; [controller addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:controller animated:YES completion:nil]; [self.window.rootViewController presentViewController:controller animated:YES completion:nil];
return; return;
} }
#endif #endif
@@ -158,16 +158,16 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error { - (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
MPError( error, @"StoreKit request (%@) failed.", request ); MPError( error, @"StoreKit request failed." );
#if TARGET_OS_IPHONE #if TARGET_OS_IPHONE
PearlMainQueue( ^{ PearlMainQueue( ^{
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Purchase Failed" message: UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Purchase Failed" message:
strf( @"%@\n\n%@", error.localizedDescription, strf( @"%@\n\n%@", error.localizedDescription,
@"Ensure you are online and try logging out and back into iTunes from your device's Settings." ) @"Could not reach Apple's iTunes Store. Make sure you're connected to the Internet and try again." )
preferredStyle:UIAlertControllerStyleAlert]; preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]]; [controller addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:controller animated:YES completion:nil]; [self.window.rootViewController presentViewController:controller animated:YES completion:nil];
} ); } );
#endif #endif
} }
@@ -197,7 +197,6 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
forKey:transaction.payment.productIdentifier]; forKey:transaction.payment.productIdentifier];
[queue finishTransaction:transaction]; [queue finishTransaction:transaction];
if ([[MPConfig get].sendInfo boolValue]) {
SKProduct *product = self.products[transaction.payment.productIdentifier]; SKProduct *product = self.products[transaction.payment.productIdentifier];
[attributes addEntriesFromDictionary:@{ [attributes addEntriesFromDictionary:@{
@"id": product.productIdentifier, @"id": product.productIdentifier,
@@ -208,7 +207,6 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
@"quantity": @(transaction.payment.quantity).description, @"quantity": @(transaction.payment.quantity).description,
}]; }];
[Countly.sharedInstance recordEvent:@"purchase" segmentation:attributes]; [Countly.sharedInstance recordEvent:@"purchase" segmentation:attributes];
}
break; break;
} }
case SKPaymentTransactionStateRestored: { case SKPaymentTransactionStateRestored: {
@@ -224,7 +222,17 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
MPError( transaction.error, @"Transaction failed: %@.", transaction.payment.productIdentifier ); MPError( transaction.error, @"Transaction failed: %@.", transaction.payment.productIdentifier );
[queue finishTransaction:transaction]; [queue finishTransaction:transaction];
if ([[MPConfig get].sendInfo boolValue]) { #if TARGET_OS_IPHONE
PearlMainQueue( ^{
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Purchase Failed" message:
strf( @"%@\n\n%@", transaction.error.localizedDescription,
@"Could not reach Apple's iTunes Store. Make sure you're connected to the Internet and try again." )
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]];
[self.window.rootViewController presentViewController:controller animated:YES completion:nil];
} );
#endif
SKProduct *product = self.products[transaction.payment.productIdentifier]; SKProduct *product = self.products[transaction.payment.productIdentifier];
[Countly.sharedInstance recordEvent:@"purchase" segmentation:@{ [Countly.sharedInstance recordEvent:@"purchase" segmentation:@{
@"id": product.productIdentifier, @"id": product.productIdentifier,
@@ -235,7 +243,6 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
@"quantity": @(transaction.payment.quantity).description, @"quantity": @(transaction.payment.quantity).description,
@"reason" : [transaction.error localizedFailureReason]?: [transaction.error localizedDescription], @"reason" : [transaction.error localizedFailureReason]?: [transaction.error localizedDescription],
}]; }];
}
break; break;
} }
} }

View File

@@ -173,13 +173,11 @@
else else
dbg( @"Automatic login failed for user: %@", user.userID ); dbg( @"Automatic login failed for user: %@", user.userID );
if ([[MPConfig get].sendInfo boolValue]) {
[Countly.sharedInstance recordEvent:@"login" segmentation:@{ [Countly.sharedInstance recordEvent:@"login" segmentation:@{
@"method" : password? @"Password": @"Automatic", @"method" : password? @"Password": @"Automatic",
@"state" : @"failed", @"state" : @"failed",
@"algorithm": @(user.algorithm.version).description, @"algorithm": @(user.algorithm.version).description,
}]; }];
}
return NO; return NO;
} }
@@ -203,7 +201,7 @@
} }
@try { @try {
if ([[MPConfig get].sendInfo boolValue]) { [SentrySDK setUser:[[SentryUser alloc] initWithUserId:user.userID]];
[Countly.sharedInstance userLoggedIn:user.userID]; [Countly.sharedInstance userLoggedIn:user.userID];
[Countly.sharedInstance recordEvent:@"login" segmentation:@{ [Countly.sharedInstance recordEvent:@"login" segmentation:@{
@@ -212,7 +210,6 @@
@"algorithm": @(user.algorithm.version).description, @"algorithm": @(user.algorithm.version).description,
}]; }];
} }
}
@catch (id exception) { @catch (id exception) {
err( @"While setting username: %@", exception ); err( @"While setting username: %@", exception );
} }
@@ -252,18 +249,18 @@
masterPassword = PearlAwait( ^(void (^setResult)(id)) { masterPassword = PearlAwait( ^(void (^setResult)(id)) {
PearlMainQueue( ^{ PearlMainQueue( ^{
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Enter Old Master Password" message: UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Enter Old Master Password" message:
strf( @"Your old master password is required to migrate the stored password for %@", site.name ) strf( @"Your old master password is required to unlock the stored password for: <%@>", site.name )
preferredStyle:UIAlertControllerStyleAlert]; preferredStyle:UIAlertControllerStyleAlert];
[controller addTextFieldWithConfigurationHandler:nil]; [controller addTextFieldWithConfigurationHandler:nil];
[controller addAction:[UIAlertAction actionWithTitle:@"Migrate" style:UIAlertActionStyleDefault handler: [controller addAction:[UIAlertAction actionWithTitle:@"Migrate" style:UIAlertActionStyleDefault handler:
^(UIAlertAction *_Nonnull action) { ^(UIAlertAction *_Nonnull action) {
setResult( controller.textFields.firstObject.text ); setResult( controller.textFields.firstObject.text );
}]]; }]];
[controller addAction:[UIAlertAction actionWithTitle:@"Don't Migrate" style:UIAlertActionStyleCancel handler: [controller addAction:[UIAlertAction actionWithTitle:@"Leave It" style:UIAlertActionStyleCancel handler:
^(UIAlertAction *_Nonnull action) { ^(UIAlertAction *_Nonnull action) {
setResult( nil ); setResult( nil );
}]]; }]];
[self.navigationController presentViewController:controller animated:YES completion:nil]; [self.window.rootViewController presentViewController:controller animated:YES completion:nil];
} ); } );
} ); } );
#endif #endif

View File

@@ -20,7 +20,7 @@
#if TARGET_OS_IPHONE #if TARGET_OS_IPHONE
@interface MPAppDelegate_Shared : PearlAppDelegate @interface MPAppDelegate_Shared : UIResponder<UIApplicationDelegate, PearlConfigDelegate>
#else #else

View File

@@ -116,10 +116,8 @@ static MPAppDelegate_Shared *instance;
if (self.activeUserOID == activeUserOID || [self.activeUserOID isEqual:activeUserOID]) if (self.activeUserOID == activeUserOID || [self.activeUserOID isEqual:activeUserOID])
return; return;
if (self.key)
self.key = nil; self.key = nil;
[SentrySDK setUser:nil];
if ([[MPConfig get].sendInfo boolValue])
[Countly.sharedInstance userLoggedOut]; [Countly.sharedInstance userLoggedOut];
self.activeUserOID = activeUserOID; self.activeUserOID = activeUserOID;

View File

@@ -30,6 +30,7 @@
- (id)managedObjectContextChanged:(void ( ^ )(NSDictionary<NSManagedObjectID *, NSString *> *affectedObjects))changedBlock; - (id)managedObjectContextChanged:(void ( ^ )(NSDictionary<NSManagedObjectID *, NSString *> *affectedObjects))changedBlock;
- (MPFixableResult)findAndFixInconsistenciesSaveInContext:(NSManagedObjectContext *)context; - (MPFixableResult)findAndFixInconsistenciesSaveInContext:(NSManagedObjectContext *)context;
- (void)retryCorruptStore;
- (void)deleteAndResetStore; - (void)deleteAndResetStore;
/** @param completion The block to execute after adding the site, executed from the main thread with the new site 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. */
@@ -39,8 +40,9 @@
askImportPassword:(NSString *( ^ )(NSString *userName))importPassword askImportPassword:(NSString *( ^ )(NSString *userName))importPassword
askUserPassword:(NSString *( ^ )(NSString *userName))userPassword askUserPassword:(NSString *( ^ )(NSString *userName))userPassword
result:(void ( ^ )(NSError *error))resultBlock; result:(void ( ^ )(NSError *error))resultBlock;
- (void)exportSitesRevealPasswords:(BOOL)revealPasswords - (NSString *)exportSitesFor:(MPUserEntity *)user
askExportPassword:(NSString *( ^ )(NSString *userName))askImportPassword revealPasswords:(BOOL)revealPasswords
result:(void ( ^ )(NSString *exportedUser, NSError *error))resultBlock; askExportPassword:(NSString *( ^ )(NSString *userName))askExportPassword
error:(__autoreleasing NSError **)error;
@end @end

View File

@@ -19,6 +19,8 @@
#import "MPAppDelegate_Store.h" #import "MPAppDelegate_Store.h"
#import "mpw-marshal.h" #import "mpw-marshal.h"
#import "mpw-util.h" #import "mpw-util.h"
#import "MPAppDelegate_InApp.h"
#import "MPSecrets.h"
#if TARGET_OS_IPHONE #if TARGET_OS_IPHONE
#define STORE_OPTIONS NSPersistentStoreFileProtectionKey : NSFileProtectionComplete, #define STORE_OPTIONS NSPersistentStoreFileProtectionKey : NSFileProtectionComplete,
@@ -181,12 +183,12 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
} ); } );
// Do nothing if already fully set up, otherwise (re-)load the store. // Do nothing if already fully set up, otherwise (re-)load the store.
if (self.storeCoordinator && self.mainManagedObjectContext && self.privateManagedObjectContext) if (self.mainManagedObjectContext && self.privateManagedObjectContext)
return; return;
[self.storeQueue addOperationWithBlock:^{ [self.storeQueue addOperationWithBlock:^{
// Do nothing if already fully set up, otherwise (re-)load the store. // Do nothing if already fully set up, otherwise (re-)load the store.
if (self.storeCoordinator && self.mainManagedObjectContext && self.privateManagedObjectContext) if (self.mainManagedObjectContext && self.privateManagedObjectContext)
return; return;
// Unregister any existing observers and contexts. // Unregister any existing observers and contexts.
@@ -199,6 +201,12 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
[self.privateManagedObjectContext reset]; [self.privateManagedObjectContext reset];
self.privateManagedObjectContext = nil; self.privateManagedObjectContext = nil;
}]; }];
NSError *error = nil;
for (NSPersistentStore *store in self.storeCoordinator.persistentStores)
if (![self.storeCoordinator removePersistentStore:store error:&error] || error) {
MPError( error, @"Couldn't remove persistence store from coordinator." );
return;
}
// Don't load when the store is corrupted. // Don't load when the store is corrupted.
if ([self.storeCorrupted boolValue]) if ([self.storeCorrupted boolValue])
@@ -207,12 +215,36 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
// Check if migration is necessary. // Check if migration is necessary.
[self migrateStore]; [self migrateStore];
// Create a new store coordinator.
NSURL *localStoreURL = [self localStoreURL];
if (![[NSFileManager defaultManager] createDirectoryAtURL:[localStoreURL URLByDeletingLastPathComponent]
withIntermediateDirectories:YES attributes:nil error:&error]) {
MPError( error, @"Couldn't create our application support directory." );
PearlRemoveNotificationObserversFrom( self.mainManagedObjectContext );
self.mainManagedObjectContext = nil;
self.privateManagedObjectContext = nil;
return;
}
if (![self.storeCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:localStoreURL
options:@{
NSMigratePersistentStoresAutomaticallyOption: @YES,
NSInferMappingModelAutomaticallyOption : @YES,
STORE_OPTIONS
} error:&error]) {
MPError( error, @"Failed to open store." );
PearlRemoveNotificationObserversFrom( self.mainManagedObjectContext );
self.mainManagedObjectContext = nil;
self.privateManagedObjectContext = nil;
self.storeCorrupted = @YES;
[self handleCoordinatorError:error];
return;
}
self.storeCorrupted = @NO;
// Install managed object contexts and observers. // Install managed object contexts and observers.
self.privateManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; self.privateManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[self.privateManagedObjectContext performBlockAndWait:^{
self.privateManagedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy; self.privateManagedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
self.privateManagedObjectContext.persistentStoreCoordinator = self.storeCoordinator; self.privateManagedObjectContext.persistentStoreCoordinator = self.storeCoordinator;
}];
self.mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; self.mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
self.mainManagedObjectContext.parentContext = self.privateManagedObjectContext; self.mainManagedObjectContext.parentContext = self.privateManagedObjectContext;
@@ -232,28 +264,6 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
}]; }];
} ); } );
// Create a new store coordinator.
NSError *error = nil;
NSURL *localStoreURL = [self localStoreURL];
if (![[NSFileManager defaultManager] createDirectoryAtURL:[localStoreURL URLByDeletingLastPathComponent]
withIntermediateDirectories:YES attributes:nil error:&error]) {
MPError( error, @"Couldn't create our application support directory." );
return;
}
if (![self.storeCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[self localStoreURL]
options:@{
NSMigratePersistentStoresAutomaticallyOption: @YES,
NSInferMappingModelAutomaticallyOption : @YES,
STORE_OPTIONS
} error:&error]) {
MPError( error, @"Failed to open store." );
self.storeCorrupted = @YES;
[self handleCoordinatorError:error];
return;
}
self.storeCorrupted = @NO;
#if TARGET_OS_IPHONE #if TARGET_OS_IPHONE
PearlAddNotificationObserver( UIApplicationWillResignActiveNotification, UIApp, [NSOperationQueue mainQueue], PearlAddNotificationObserver( UIApplicationWillResignActiveNotification, UIApp, [NSOperationQueue mainQueue],
^(MPAppDelegate_Shared *self, NSNotification *note) { ^(MPAppDelegate_Shared *self, NSNotification *note) {
@@ -274,6 +284,12 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
}]; }];
} }
- (void)retryCorruptStore {
self.storeCorrupted = @NO;
[self loadStore];
}
- (void)deleteAndResetStore { - (void)deleteAndResetStore {
@synchronized (self) { @synchronized (self) {
@@ -563,21 +579,20 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
// Read metadata for the import file. // Read metadata for the import file.
MPMarshalledFile *file = mpw_marshal_read( NULL, importData.UTF8String ); MPMarshalledFile *file = mpw_marshal_read( NULL, importData.UTF8String );
MPMarshalledUser *importUser = nil;
@try {
if (!file) if (!file)
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{ return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
@"type" : @(MPMarshalErrorInternal), @"type" : @(MPMarshalErrorInternal),
NSLocalizedDescriptionKey: @"Could not process Master Password import data.", NSLocalizedDescriptionKey: @"Could not process Master Password import data.",
}]), @"While importing sites." ); }]), @"While importing sites." );
if (file->error.type != MPMarshalSuccess) { if (file->error.type != MPMarshalSuccess) {
MPMarshalErrorType type = file->error.type;
mpw_marshal_file_free( &file );
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{ return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
@"type" : @(type), @"type" : @(file->error.type),
NSLocalizedDescriptionKey: @"Could not parse Master Password import data.", NSLocalizedDescriptionKey: strf( @"Could not parse Master Password import data:\n%@", @(file->error.message) ),
}]), @"While importing sites." ); }]), @"While importing sites." );
} }
if (file->info->format == MPMarshalFormatNone) { if (file->info->format == MPMarshalFormatNone) {
mpw_marshal_file_free( &file );
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{ return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
@"type" : @(MPMarshalErrorFormat), @"type" : @(MPMarshalErrorFormat),
NSLocalizedDescriptionKey: @"This is not a Master Password import file.", NSLocalizedDescriptionKey: @"This is not a Master Password import file.",
@@ -591,7 +606,6 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
importMasterPassword = askImportPassword( @(file->info->fullName) ); importMasterPassword = askImportPassword( @(file->info->fullName) );
if (!importMasterPassword) { if (!importMasterPassword) {
inf( @"Import cancelled." ); inf( @"Import cancelled." );
mpw_marshal_file_free( &file );
return MPError( ([NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil]), @"" ); return MPError( ([NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil]), @"" );
} }
@@ -600,13 +614,11 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
caseInsensitiveCompare:@(file->info->keyID)] != NSOrderedSame); caseInsensitiveCompare:@(file->info->keyID)] != NSOrderedSame);
// Parse import data. // Parse import data.
MPMarshalledUser *importUser = mpw_marshal_auth( file, mpw_masterKeyProvider_str( importMasterPassword.UTF8String ) ); importUser = mpw_marshal_auth( file, mpw_masterKeyProvider_str( importMasterPassword.UTF8String ) );
@try {
if (!importUser || file->error.type != MPMarshalSuccess) if (!importUser || file->error.type != MPMarshalSuccess)
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{ return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
@"type" : @(file->error.type), @"type" : @(file->error.type),
NSLocalizedDescriptionKey: @(file->error.message), NSLocalizedDescriptionKey: strf( @"Could not authenticate Master Password import:\n%@", @(file->error.message) ),
}]), @"While importing sites." ); }]), @"While importing sites." );
// Find an existing user to update. // Find an existing user to update.
@@ -708,31 +720,45 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
site.lastUsed = [NSDate dateWithTimeIntervalSince1970:importSite->lastUsed]; site.lastUsed = [NSDate dateWithTimeIntervalSince1970:importSite->lastUsed];
} }
- (void)exportSitesRevealPasswords:(BOOL)revealPasswords - (NSString *)exportSitesFor:(MPUserEntity *)user
askExportPassword:(NSString *( ^ )(NSString *userName))askImportPassword revealPasswords:(BOOL)revealPasswords
result:(void ( ^ )(NSString *exportedUser, NSError *error))resultBlock { askExportPassword:(NSString *( ^ )(NSString *userName))askExportPassword
error:(__autoreleasing NSError **)error {
[MPAppDelegate_Shared managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPUserEntity *user = [self activeUserInContext:context];
MPMarshalledUser *exportUser = NULL;
MPMarshalledFile *exportFile = mpw_marshal_file( NULL, NULL, mpw_marshal_data_new() );
@try {
inf( @"Exporting sites, %@, for user: %@", revealPasswords? @"revealing passwords": @"omitting passwords", user.userID ); inf( @"Exporting sites, %@, for user: %@", revealPasswords? @"revealing passwords": @"omitting passwords", user.userID );
MPMarshalledUser *exportUser = mpw_marshal_user( user.name.UTF8String, NSString *masterPassword = askExportPassword( user.name );
mpw_masterKeyProvider_str( askImportPassword( user.name ).UTF8String ), user.algorithm.version ); if (!masterPassword) {
inf( @"Export cancelled." );
return nil;
}
for (NSString *feature in @[MPProductGenerateLogins, MPProductGenerateAnswers, MPProductOSIntegration, MPProductTouchID])
if ([[MPAppDelegate_Shared get] isFeatureUnlocked:feature])
mpw_marshal_data_set_str( digest( strf( @"%@/%@", user.name, feature )).UTF8String, exportFile->data,
"user", "_ext_mpw", feature.UTF8String, nil );
MPKey *key = [[MPKey alloc] initForFullName:user.name withMasterPassword:masterPassword];
exportUser = mpw_marshal_user( user.name.UTF8String,
mpw_masterKeyProvider_str( masterPassword.UTF8String ), user.algorithm.version );
exportUser->redacted = !revealPasswords; exportUser->redacted = !revealPasswords;
exportUser->avatar = (unsigned int)user.avatar; exportUser->avatar = (unsigned int)user.avatar;
exportUser->keyID = mpw_strdup( [user.keyID encodeHex].UTF8String ); exportUser->keyID = mpw_strdup( [user.keyID encodeHex].UTF8String );
exportUser->defaultType = user.defaultType; exportUser->defaultType = user.defaultType;
exportUser->lastUsed = (time_t)user.lastUsed.timeIntervalSince1970; exportUser->lastUsed = (time_t)user.lastUsed.timeIntervalSince1970;
for (MPSiteEntity *site in user.sites) { for (MPSiteEntity *site in [user.sites sortedArrayUsingDescriptors:@[
[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]
]]) {
MPCounterValue counter = MPCounterValueInitial; MPCounterValue counter = MPCounterValueInitial;
if ([site isKindOfClass:[MPGeneratedSiteEntity class]]) if ([site isKindOfClass:[MPGeneratedSiteEntity class]])
counter = ((MPGeneratedSiteEntity *)site).counter; counter = ((MPGeneratedSiteEntity *)site).counter;
MPMarshalledSite *exportSite = mpw_marshal_site( exportUser, MPMarshalledSite *exportSite = mpw_marshal_site( exportUser, site.name.UTF8String, site.type, counter, site.algorithm.version );
site.name.UTF8String, site.type, counter, site.algorithm.version ); exportSite->resultState = mpw_strdup( [site.algorithm exportPasswordForSite:site usingKey:key].UTF8String );
exportSite->resultState = mpw_strdup( [site.algorithm exportPasswordForSite:site usingKey:self.key].UTF8String ); exportSite->loginState = mpw_strdup( [site.algorithm exportLoginForSite:site usingKey:key].UTF8String );
exportSite->loginState = mpw_strdup( [site.algorithm exportLoginForSite:site usingKey:self.key].UTF8String ); exportSite->loginType = site.loginGenerated || !exportSite->loginState? MPResultTypeTemplateName: MPResultTypeStatefulPersonal;
exportSite->loginType = site.loginGenerated? MPResultTypeTemplateName: MPResultTypeStatefulPersonal;
exportSite->url = mpw_strdup( site.url.UTF8String ); exportSite->url = mpw_strdup( site.url.UTF8String );
exportSite->uses = (unsigned int)site.uses; exportSite->uses = (unsigned int)site.uses;
exportSite->lastUsed = (time_t)site.lastUsed.timeIntervalSince1970; exportSite->lastUsed = (time_t)site.lastUsed.timeIntervalSince1970;
@@ -741,22 +767,26 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
mpw_marshal_question( exportSite, siteQuestion.keyword.UTF8String ); mpw_marshal_question( exportSite, siteQuestion.keyword.UTF8String );
} }
MPMarshalledFile *exportFile = NULL;
const char *export = mpw_marshal_write( MPMarshalFormatDefault, &exportFile, exportUser ); const char *export = mpw_marshal_write( MPMarshalFormatDefault, &exportFile, exportUser );
NSString *exportedUser = nil; NSString *exportedUser = nil;
if (export && exportFile && exportFile->error.type == MPMarshalSuccess) if (export && exportFile && exportFile->error.type == MPMarshalSuccess)
exportedUser = [NSString stringWithCString:export encoding:NSUTF8StringEncoding]; exportedUser = [NSString stringWithCString:export encoding:NSUTF8StringEncoding];
mpw_free_string( &export ); mpw_free_string( &export );
resultBlock( exportedUser, exportFile && exportFile->error.type == MPMarshalSuccess? nil: if (error)
*error = exportFile && exportFile->error.type == MPMarshalSuccess? nil:
[NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{ [NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
@"type" : @(exportFile? exportFile->error.type: MPMarshalErrorInternal), @"type" : @(exportFile? exportFile->error.type: MPMarshalErrorInternal),
NSLocalizedDescriptionKey: @(exportFile? exportFile->error.message: nil), NSLocalizedDescriptionKey: @(exportFile? exportFile->error.message: nil),
}] ); }];
return exportedUser;
}
@finally {
mpw_marshal_file_free( &exportFile ); mpw_marshal_file_free( &exportFile );
mpw_marshal_user_free( &exportUser ); mpw_marshal_user_free( &exportUser );
mpw_masterKeyProvider_free(); mpw_masterKeyProvider_free();
}]; }
} }
@end @end

View File

@@ -21,11 +21,13 @@
@interface MPConfig : PearlConfig @interface MPConfig : PearlConfig
@property(nonatomic, retain) NSNumber *sendInfo; @property(nonatomic, retain) NSNumber *sendInfo;
@property(nonatomic, retain) NSNumber *sendInfoDecided;
@property(nonatomic, retain) NSNumber *notificationsDecided;
@property(nonatomic, retain) NSNumber *rememberLogin; @property(nonatomic, retain) NSNumber *rememberLogin;
@property(nonatomic, retain) NSNumber *hidePasswords; @property(nonatomic, retain) NSNumber *hidePasswords;
@property(nonatomic, strong) NSNumber *siteAttacker;
@property(nonatomic, retain) NSNumber *checkInconsistency; @property(nonatomic, retain) NSNumber *checkInconsistency;
@property(nonatomic, strong) NSNumber *siteAttacker;
@end @end

View File

@@ -21,7 +21,7 @@
@implementation MPConfig @implementation MPConfig
@dynamic sendInfo, rememberLogin, checkInconsistency, hidePasswords, siteAttacker; @dynamic sendInfo, sendInfoDecided, notificationsDecided, rememberLogin, hidePasswords, siteAttacker, checkInconsistency;
- (id)init { - (id)init {
@@ -29,13 +29,16 @@
return nil; return nil;
[self.defaults registerDefaults:@{ [self.defaults registerDefaults:@{
NSStringFromSelector( @selector( askForReviews ) ) : @YES, NSStringFromSelector( @selector( sendInfo ) ) : @NO,
NSStringFromSelector( @selector( sendInfoDecided ) ) : @NO,
NSStringFromSelector( @selector( notificationsDecided ) ): @NO,
NSStringFromSelector( @selector( sendInfo ) ) : @YES,
NSStringFromSelector( @selector( rememberLogin ) ) : @NO, NSStringFromSelector( @selector( rememberLogin ) ) : @NO,
NSStringFromSelector( @selector( hidePasswords ) ) : @NO, NSStringFromSelector( @selector( hidePasswords ) ) : @NO,
NSStringFromSelector( @selector( checkInconsistency ) ): @NO,
NSStringFromSelector( @selector( siteAttacker ) ) : @(MPAttacker1), NSStringFromSelector( @selector( siteAttacker ) ) : @(MPAttacker1),
NSStringFromSelector( @selector( checkInconsistency ) ) : @NO,
NSStringFromSelector( @selector( askForReviews ) ) : @YES,
}]; }];
self.delegate = [MPAppDelegate_Shared get]; self.delegate = [MPAppDelegate_Shared get];

View File

@@ -29,6 +29,8 @@
if ([self hasChanges]) if ([self hasChanges])
[self performBlockAndWait:^{ [self performBlockAndWait:^{
@try { @try {
[self processPendingChanges];
NSError *error = nil; NSError *error = nil;
if (!(success = [self save:&error])) if (!(success = [self save:&error]))
MPError( error, @"While saving." ); MPError( error, @"While saving." );

View File

@@ -20,11 +20,19 @@
#import "base64.h" #import "base64.h"
// printf <secret> | openssl enc -[ed] -aes-128-cbc -a -A -K <appSecret> -iv 0 // printf <secret> | openssl enc -[ed] -aes-128-cbc -a -A -K <appSecret> -iv 0
NSString *appSecret = @""; #if TARGET_OS_IOS
NSString *appSalt = @""; NSString *appSecret = @"946a6b12e6e6e004cc35bad1ea11478c";
NSString *sentryDSN = @""; NSString *appSalt = @"uBcsbZeTB8TfSS7dDw4yUq6wMZD/2nREvR0mqzqsNXvv9guh+62hkt99ly6QcJ5n";
NSString *countlyKey = @""; NSString *sentryDSN = @"tmVjdMN9DpZ+0EIrrvHi44hWfaBkwrlrxjBkdeau2rDk+zlvgSdAZkAvNj7m1V+5NUR7i8Y/NumNKOaYlWJvPynEMJ4ZBvPepSbivgVvmr8=";
NSString *countlySalt = @""; NSString *countlyKey = @"mDnMZyxwoq4ENgYnGYTzW8wsyiJQlmNKxkRLj88/nrs0mzE+zVjs6Y5LAT3+AYBB";
NSString *countlySalt = @"2COFsZd+4FNAU6jvI/HUu297mkZALzRIyKv5mD3vs55BHXDowh62A7FursCYS+cG";
#elif TARGET_OS_MAC
NSString *appSecret = @"24fcbadccb5789b2a969c0c811f86702";
NSString *appSalt = @"0N1fzSanIOCb7OQ4hEshXSjwEPXAXMhPBKQJeEcYPor8FWz76IpdB8ZHa3Wyb7o9";
NSString *sentryDSN = @"2RbeS9wfzQEOKB9MG3EWLDe+N8iXYNtWc8tovMcBmhuMIeyAHYKqo5eclSEYyM6lA73Y7FFHqUyTLbEmOR6MAU2PtWAitLdxOZlq3VnbXjI=";
NSString *countlyKey = @"uiasXoQNtkPQHvpvNqEE5N/tw/F1Hnzm+4ViSJ38EMeoWGvDQPJ+Kt9zPhb8Qans";
NSString *countlySalt = @"/raQUNxKQdxXRR5VFmCDJdyyJE8f6SPrTO5Y4z0kJH+wCrjaZ1VvCq+JSmOsBkz2";
#endif
NSString *decrypt(NSString *secret) { NSString *decrypt(NSString *secret) {

View File

@@ -16,7 +16,9 @@
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>. // LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//============================================================================== //==============================================================================
MP_LIBS_BEGIN
#import <Sentry/Sentry.h> #import <Sentry/Sentry.h>
MP_LIBS_END
__BEGIN_DECLS __BEGIN_DECLS
extern NSString *const MPErrorDomain; extern NSString *const MPErrorDomain;
@@ -41,8 +43,9 @@ __END_DECLS
\ \
if (__error && [[MPConfig get].sendInfo boolValue]) { \ if (__error && [[MPConfig get].sendInfo boolValue]) { \
SentryEvent *event = [[SentryEvent alloc] initWithLevel:kSentryLevelError]; \ SentryEvent *event = [[SentryEvent alloc] initWithLevel:kSentryLevelError]; \
event.message = strf(@"%@: %@", message_, [__error localizedDescription]); \ event.message = strf( message_ @": %@", ##__VA_ARGS__, [__error localizedDescription]); \
event.logger = @"MPError"; \ event.logger = @"MPError"; \
event.fingerprint = @[ message_, __error.domain, @(__error.code) ]; \
[SentrySDK captureEvent:event]; \ [SentrySDK captureEvent:event]; \
} \ } \
__error; \ __error; \

View File

@@ -12,9 +12,6 @@
</customObject> </customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/> <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/> <customObject id="-3" userLabel="Application" customClass="NSObject"/>
<menu title="AMainMenu" systemMenu="main" id="29">
<point key="canvasLocation" x="139" y="155"/>
</menu>
<customObject id="494" customClass="MPMacAppDelegate"> <customObject id="494" customClass="MPMacAppDelegate">
<connections> <connections>
<outlet property="createUserItem" destination="757" id="763"/> <outlet property="createUserItem" destination="757" id="763"/>
@@ -241,6 +238,9 @@
</connections> </connections>
</menuItem> </menuItem>
</items> </items>
<connections>
<outlet property="delegate" destination="494" id="Slu-zT-yO4"/>
</connections>
<point key="canvasLocation" x="140" y="23"/> <point key="canvasLocation" x="140" y="23"/>
</menu> </menu>
</objects> </objects>

View File

@@ -21,9 +21,9 @@
#import "MPSitesWindowController.h" #import "MPSitesWindowController.h"
#import "MPInitialWindowController.h" #import "MPInitialWindowController.h"
@interface MPMacAppDelegate : MPAppDelegate_Shared<NSApplicationDelegate> @interface MPMacAppDelegate : MPAppDelegate_Shared<NSApplicationDelegate, NSMenuDelegate>
@property(nonatomic, strong) NSStatusItem *statusView; @property(nonatomic, strong) NSStatusItem *statusItem;
@property(nonatomic, strong) MPSitesWindowController *sitesWindowController; @property(nonatomic, strong) MPSitesWindowController *sitesWindowController;
@property(nonatomic, strong) MPInitialWindowController *initialWindowController; @property(nonatomic, strong) MPInitialWindowController *initialWindowController;
@property(nonatomic, weak) IBOutlet NSMenuItem *lockItem; @property(nonatomic, weak) IBOutlet NSMenuItem *lockItem;

View File

@@ -22,14 +22,15 @@
#import "MPSecrets.h" #import "MPSecrets.h"
#import "mpw-marshal.h" #import "mpw-marshal.h"
MP_LIBS_BEGIN
#import <Carbon/Carbon.h> #import <Carbon/Carbon.h>
#import <ServiceManagement/ServiceManagement.h> #import <ServiceManagement/ServiceManagement.h>
#import <Sentry/Sentry.h> #import <Sentry/Sentry.h>
#import <Countly/Countly.h> #import <Countly/Countly.h>
MP_LIBS_END
#define LOGIN_HELPER_BUNDLE_ID @"com.lyndir.lhunath.MasterPassword.Mac.LoginHelper" #define LOGIN_HELPER_BUNDLE_ID @"com.lyndir.lhunath.MasterPassword.Mac.LoginHelper"
@implementation MPMacAppDelegate @implementation MPMacAppDelegate
#pragma clang diagnostic push #pragma clang diagnostic push
@@ -69,10 +70,10 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
@try { @try {
// Sentry // Sentry
[SentrySDK initWithOptions:@{ [SentrySDK startWithOptions:@{
@"dsn" : NilToNSNull( decrypt( sentryDSN ) ), @"dsn" : NilToNSNull( decrypt( sentryDSN ) ),
#ifdef DEBUG #ifdef DEBUG
@"debug" : @(YES), @"debug" : @(NO),
@"environment" : @"Development", @"environment" : @"Development",
#elif PUBLIC #elif PUBLIC
@"debug" : @(NO), @"debug" : @(NO),
@@ -81,12 +82,12 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
@"debug" : @(NO), @"debug" : @(NO),
@"environment" : @"Private", @"environment" : @"Private",
#endif #endif
@"enabled" : [MPMacConfig get].sendInfo, @"enabled" : @([[MPMacConfig get].sendInfo boolValue] || ![[MPMacConfig get].sendInfoDecided boolValue]),
@"enableAutoSessionTracking": @(YES), @"enableAutoSessionTracking": @(YES),
}]; }];
[[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) { [[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) {
PearlLogLevel level = PearlLogLevelWarn; PearlLogLevel level = PearlLogLevelWarn;
if ([[MPConfig get].sendInfo boolValue]) if ([[MPMacConfig get].sendInfo boolValue])
level = PearlLogLevelDebug; level = PearlLogLevelDebug;
if (message.level >= level) { if (message.level >= level) {
@@ -145,7 +146,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
} }
// Setup delegates and listeners. // Setup delegates and listeners.
[MPConfig get].delegate = self; [MPMacConfig get].delegate = self;
__weak id weakSelf = self; __weak id weakSelf = self;
[self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) { [self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
dispatch_async( dispatch_get_main_queue(), ^{ dispatch_async( dispatch_get_main_queue(), ^{
@@ -159,12 +160,10 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
} forKeyPath:@"activeUser" options:0 context:nil]; } forKeyPath:@"activeUser" options:0 context:nil];
// Status item. // Status item.
self.statusView = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength]; self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
self.statusView.image = [NSImage imageNamed:@"menu-icon"]; self.statusItem.menu = self.statusMenu;
self.statusView.image.template = YES; self.statusItem.button.image = [NSImage imageNamed:@"menu-icon"];
self.statusView.menu = self.statusMenu; self.statusItem.button.image.template = YES;
self.statusView.target = self;
self.statusView.action = @selector( showMenu );
PearlAddNotificationObserver( NSPersistentStoreCoordinatorStoresWillChangeNotification, self.storeCoordinator, nil, PearlAddNotificationObserver( NSPersistentStoreCoordinatorStoresWillChangeNotification, self.storeCoordinator, nil,
^(id self, NSNotification *note) { ^(id self, NSNotification *note) {
@@ -205,12 +204,12 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
[NSApp activateIgnoringOtherApps:YES]; [NSApp activateIgnoringOtherApps:YES];
} }
[self enableNotifications]; [self tryNotifications];
} }
- (void)applicationWillResignActive:(NSNotification *)notification { - (void)applicationWillResignActive:(NSNotification *)notification {
if (![[MPConfig get].rememberLogin boolValue]) if (![[MPMacConfig get].rememberLogin boolValue])
[self lock:nil]; [self lock:nil];
} }
@@ -231,7 +230,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
return NSTerminateNow; return NSTerminateNow;
} }
- (void)enableNotifications { - (void)tryNotifications {
[Countly.sharedInstance giveConsentForFeature:CLYConsentPushNotifications]; [Countly.sharedInstance giveConsentForFeature:CLYConsentPushNotifications];
if (@available( macOS 10.14, * )) { if (@available( macOS 10.14, * )) {
@@ -250,18 +249,19 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
- (void)askNotifications { - (void)askNotifications {
if ([[MPMacConfig get].notificationsDecided boolValue])
return;
PearlMainQueue( ^{ PearlMainQueue( ^{
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"notificationsDecided"]) {
if (@available( macOS 10.14, * )) { if (@available( macOS 10.14, * )) {
[Countly.sharedInstance askForNotificationPermissionWithOptions:UNAuthorizationOptionAlert completionHandler: [Countly.sharedInstance askForNotificationPermissionWithOptions:UNAuthorizationOptionAlert completionHandler:
^(BOOL granted, NSError *error) { ^(BOOL granted, NSError *error) {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"notificationsDecided"]; [MPMacConfig get].notificationsDecided = @(YES);
}]; }];
} }
else { else {
[Countly.sharedInstance askForNotificationPermission]; [Countly.sharedInstance askForNotificationPermission];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"notificationsDecided"]; [MPMacConfig get].notificationsDecided = @(YES);
}
} }
} ); } );
} }
@@ -409,11 +409,11 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
- (IBAction)togglePreference:(id)sender { - (IBAction)togglePreference:(id)sender {
if (sender == self.diagnosticsItem) if (sender == self.diagnosticsItem)
[MPConfig get].sendInfo = @(self.diagnosticsItem.state != NSOnState); [MPMacConfig get].sendInfo = @(self.diagnosticsItem.state != NSOnState);
if (sender == self.hidePasswordsItem) if (sender == self.hidePasswordsItem)
[MPConfig get].hidePasswords = @(self.hidePasswordsItem.state != NSOnState); [MPMacConfig get].hidePasswords = @(self.hidePasswordsItem.state != NSOnState);
if (sender == self.rememberPasswordItem) if (sender == self.rememberPasswordItem)
[MPConfig get].rememberLogin = @(self.rememberPasswordItem.state != NSOnState); [MPMacConfig get].rememberLogin = @(self.rememberPasswordItem.state != NSOnState);
if (sender == self.openAtLoginItem) if (sender == self.openAtLoginItem)
[self setLoginItemEnabled:self.openAtLoginItem.state != NSOnState]; [self setLoginItemEnabled:self.openAtLoginItem.state != NSOnState];
if (sender == self.showFullScreenItem) { if (sender == self.showFullScreenItem) {
@@ -502,7 +502,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
- (IBAction)showPopup:(id)sender { - (IBAction)showPopup:(id)sender {
[self.statusView popUpStatusItemMenu:self.statusView.menu]; [[self.statusItem button] performClick:sender];
} }
- (IBAction)showPasswordWindow:(id)sender { - (IBAction)showPasswordWindow:(id)sender {
@@ -546,15 +546,6 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
return; return;
} }
if (!self.key) {
NSAlert *alert = [NSAlert new];
alert.messageText = @"User Locked";
alert.informativeText = @"To export your sites, first unlock your user by opening Master Password.";
[alert runModal];
[self showPopup:nil];
return;
}
NSDateFormatter *exportDateFormatter = [NSDateFormatter new]; NSDateFormatter *exportDateFormatter = [NSDateFormatter new];
[exportDateFormatter setDateFormat:@"yyyy'-'MM'-'dd"]; [exportDateFormatter setDateFormat:@"yyyy'-'MM'-'dd"];
@@ -579,11 +570,13 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
if ([savePanel runModal] == NSFileHandlingPanelCancelButton) if ([savePanel runModal] == NSFileHandlingPanelCancelButton)
return; return;
[self exportSitesRevealPasswords:revealPasswords [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
askExportPassword:^NSString *(NSString *userName) { NSError *error = nil;
NSString *exportedUser = [self exportSitesFor:[self activeUserInContext:context] revealPasswords:revealPasswords askExportPassword:
^NSString *(NSString *userName) {
return PearlMainQueueAwait( ^id { return PearlMainQueueAwait( ^id {
NSAlert *alert = [NSAlert new]; NSAlert *alert = [NSAlert new];
[alert addButtonWithTitle:@"Import"]; [alert addButtonWithTitle:@"Export"];
[alert addButtonWithTitle:@"Cancel"]; [alert addButtonWithTitle:@"Cancel"];
alert.messageText = strf( @"Master Password For\n%@", userName ); alert.messageText = strf( @"Master Password For\n%@", userName );
alert.informativeText = @"Enter the current master password for this user."; alert.informativeText = @"Enter the current master password for this user.";
@@ -594,19 +587,20 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
else else
return nil; return nil;
} ); } );
} result:^(NSString *mpsites, NSError *error) { } error:&error];
if (!mpsites || error) {
if (error)
PearlMainQueue( ^{ PearlMainQueue( ^{
[[NSAlert alertWithError:MPError( error, @"Failed to export mpsites." )] runModal]; [[NSAlert alertWithError:MPError( error, @"Failed to export mpsites." )] runModal];
} ); } );
if (!exportedUser)
return; return;
}
NSError *coordinateError = nil; NSError *coordinateError = nil;
[[[NSFileCoordinator alloc] initWithFilePresenter:nil] [[[NSFileCoordinator alloc] initWithFilePresenter:nil]
coordinateWritingItemAtURL:savePanel.URL options:0 error:&coordinateError byAccessor:^(NSURL *newURL) { coordinateWritingItemAtURL:savePanel.URL options:0 error:&coordinateError byAccessor:^(NSURL *newURL) {
NSError *writeError = nil; NSError *writeError = nil;
if (![mpsites writeToURL:newURL atomically:NO encoding:NSUTF8StringEncoding error:&writeError]) if (![exportedUser writeToURL:newURL atomically:NO encoding:NSUTF8StringEncoding error:&writeError])
PearlMainQueue( ^{ PearlMainQueue( ^{
[[NSAlert alertWithError:MPError( writeError, @"Could not write to the export file." )] runModal]; [[NSAlert alertWithError:MPError( writeError, @"Could not write to the export file." )] runModal];
} ); } );
@@ -688,13 +682,6 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
[self updateMenuItems]; [self updateMenuItems];
} }
- (void)showMenu {
[self updateMenuItems];
[self.statusView popUpStatusItemMenu:self.statusView.menu];
}
- (void)updateMenuItems { - (void)updateMenuItems {
MPUserEntity *activeUser = [self activeUserForMainThread]; MPUserEntity *activeUser = [self activeUserForMainThread];
@@ -749,6 +736,13 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
} }
} }
#pragma mark - NSMenuDelegate
- (void)menuNeedsUpdate:(NSMenu *)menu {
[self updateMenuItems];
}
#pragma mark - PearlConfigDelegate #pragma mark - PearlConfigDelegate
- (void)didUpdateConfigForKey:(SEL)configKey fromValue:(id)oldValue { - (void)didUpdateConfigForKey:(SEL)configKey fromValue:(id)oldValue {
@@ -760,26 +754,25 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
PearlMainQueue( ^{ PearlMainQueue( ^{
if (!key || [key isEqualToString:NSStringFromSelector( @selector( sendInfo ) )]) if (!key || [key isEqualToString:NSStringFromSelector( @selector( sendInfo ) )])
self.diagnosticsItem.state = [[MPConfig get].sendInfo boolValue]? NSOnState: NSOffState; self.diagnosticsItem.state = [[MPMacConfig get].sendInfo boolValue]? NSOnState: NSOffState;
if (!key || [key isEqualToString:NSStringFromSelector( @selector( hidePasswords ) )]) if (!key || [key isEqualToString:NSStringFromSelector( @selector( hidePasswords ) )])
self.hidePasswordsItem.state = [[MPConfig get].hidePasswords boolValue]? NSOnState: NSOffState; self.hidePasswordsItem.state = [[MPMacConfig get].hidePasswords boolValue]? NSOnState: NSOffState;
if (!key || [key isEqualToString:NSStringFromSelector( @selector( rememberLogin ) )]) if (!key || [key isEqualToString:NSStringFromSelector( @selector( rememberLogin ) )])
self.rememberPasswordItem.state = [[MPConfig get].rememberLogin boolValue]? NSOnState: NSOffState; self.rememberPasswordItem.state = [[MPMacConfig get].rememberLogin boolValue]? NSOnState: NSOffState;
} ); } );
// Send info // Send info
NSArray *countlyFeatures = @[ NSArray *countlyFeatures = @[
CLYConsentSessions, CLYConsentEvents, CLYConsentUserDetails, CLYConsentCrashReporting, CLYConsentViewTracking, CLYConsentStarRating CLYConsentSessions, CLYConsentEvents, CLYConsentUserDetails, CLYConsentCrashReporting, CLYConsentViewTracking, CLYConsentStarRating
]; ];
if ([[MPConfig get].sendInfo boolValue]) { if ([[MPMacConfig get].sendInfo boolValue] || ![[MPMacConfig get].sendInfoDecided boolValue]) {
[Countly.sharedInstance giveConsentForFeatures:countlyFeatures];
if ([PearlLogger get].printLevel > PearlLogLevelInfo) if ([PearlLogger get].printLevel > PearlLogLevelInfo)
[PearlLogger get].printLevel = PearlLogLevelInfo; [PearlLogger get].printLevel = PearlLogLevelInfo;
[SentrySDK.currentHub getClient].options.enabled = @YES; [SentrySDK.currentHub getClient].options.enabled = @YES;
[SentrySDK configureScope:^(SentryScope *scope) { [SentrySDK configureScope:^(SentryScope *scope) {
[scope setExtraValue:[MPConfig get].rememberLogin forKey:@"rememberLogin"]; [scope setExtraValue:[MPMacConfig get].rememberLogin forKey:@"rememberLogin"];
[scope setExtraValue:[MPConfig get].sendInfo forKey:@"sendInfo"]; [scope setExtraValue:[MPMacConfig get].sendInfo forKey:@"sendInfo"];
[scope setExtraValue:[MPMacConfig get].fullScreen forKey:@"fullScreen"]; [scope setExtraValue:[MPMacConfig get].fullScreen forKey:@"fullScreen"];
[scope setExtraValue:[PearlConfig get].firstRun forKey:@"firstRun"]; [scope setExtraValue:[PearlConfig get].firstRun forKey:@"firstRun"];
[scope setExtraValue:[PearlConfig get].launchCount forKey:@"launchCount"]; [scope setExtraValue:[PearlConfig get].launchCount forKey:@"launchCount"];
@@ -790,10 +783,12 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
[scope setExtraValue:@([PearlDeviceUtils isAppEncrypted]) forKey:@"encrypted"]; [scope setExtraValue:@([PearlDeviceUtils isAppEncrypted]) forKey:@"encrypted"];
[scope setExtraValue:[PearlDeviceUtils platform] forKey:@"platform"]; [scope setExtraValue:[PearlDeviceUtils platform] forKey:@"platform"];
}]; }];
[Countly.sharedInstance giveConsentForFeatures:countlyFeatures];
} }
else { else {
[SentrySDK.currentHub getClient].options.enabled = @NO;
[Countly.sharedInstance cancelConsentForFeatures:countlyFeatures]; [Countly.sharedInstance cancelConsentForFeatures:countlyFeatures];
[SentrySDK.currentHub getClient].options.enabled = @NO;
} }
} }

View File

@@ -45,7 +45,7 @@
@property(nonatomic, readonly) BOOL stored; @property(nonatomic, readonly) BOOL stored;
@property(nonatomic, readonly) BOOL transient; @property(nonatomic, readonly) BOOL transient;
- (instancetype)initWithEntity:(MPSiteEntity *)entity fuzzyGroups:(NSArray *)fuzzyGroups; - (instancetype)initWithEntity:(MPSiteEntity *)entity queryGroups:(NSArray *)queryGroups;
- (instancetype)initWithName:(NSString *)siteName forUser:(MPUserEntity *)user; - (instancetype)initWithName:(NSString *)siteName forUser:(MPUserEntity *)user;
- (MPSiteEntity *)entityInContext:(NSManagedObjectContext *)moc; - (MPSiteEntity *)entityInContext:(NSManagedObjectContext *)moc;

View File

@@ -31,12 +31,12 @@
@implementation MPSiteModel @implementation MPSiteModel
- (instancetype)initWithEntity:(MPSiteEntity *)entity fuzzyGroups:(NSArray *)fuzzyGroups { - (instancetype)initWithEntity:(MPSiteEntity *)entity queryGroups:(NSArray *)queryGroups {
if (!(self = [super init])) if (!(self = [super init]))
return nil; return nil;
[self setEntity:entity fuzzyGroups:fuzzyGroups]; [self setEntity:entity queryGroups:queryGroups];
self.initialized = YES; self.initialized = YES;
return self; return self;
@@ -53,23 +53,25 @@
return self; return self;
} }
- (void)setEntity:(MPSiteEntity *)entity fuzzyGroups:(NSArray *)fuzzyGroups { - (void)setEntity:(MPSiteEntity *)entity queryGroups:(NSArray *)queryGroups {
if ([self.entityOID isEqual:entity.permanentObjectID]) if ([self.entityOID isEqual:entity.permanentObjectID])
return; return;
self.entityOID = entity.permanentObjectID; self.entityOID = entity.permanentObjectID;
NSString *siteName = entity.name; NSString *siteName = entity.name;
NSMutableAttributedString *attributedSiteName = [[NSMutableAttributedString alloc] initWithString:siteName]; NSMutableAttributedString *attributedSiteName = [[NSMutableAttributedString alloc] initWithString:siteName?: @""];
for (NSUInteger f = 0, s = (NSUInteger)-1; f < [fuzzyGroups count]; ++f) { if ([attributedSiteName length])
s = [siteName rangeOfString:fuzzyGroups[f] options:NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch for (NSUInteger f = 0, s = 0; f < [queryGroups count]; ++f, ++s) {
range:NSMakeRange( s + 1, [siteName length] - (s + 1) )].location; s = [siteName rangeOfString:queryGroups[f] options:NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch
range:NSMakeRange( s, [siteName length] - s )].location;
if (s == NSNotFound) if (s == NSNotFound)
break; break;
[attributedSiteName addAttribute:NSBackgroundColorAttributeName value:[NSColor alternateSelectedControlColor] [attributedSiteName addAttribute:NSBackgroundColorAttributeName value:[NSColor alternateSelectedControlColor]
range:NSMakeRange( s, [fuzzyGroups[f] length] )]; range:NSMakeRange( s, [queryGroups[f] length] )];
} }
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new]; NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
paragraphStyle.alignment = NSCenterTextAlignment; paragraphStyle.alignment = NSCenterTextAlignment;
[attributedSiteName addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange( 0, [siteName length] )]; [attributedSiteName addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange( 0, [siteName length] )];
@@ -179,7 +181,8 @@
if (algorithmVersion == self.algorithm.version) if (algorithmVersion == self.algorithm.version)
return; return;
[self willChangeValueForKey:@"outdated"]; [self willChangeValueForKey:@"outdated"];
self.algorithm = MPAlgorithmForVersion( algorithmVersion )?: self.algorithm; self.algorithm =
MPAlgorithmForVersion( MIN( MPAlgorithmVersionLast, MAX( MPAlgorithmVersionFirst, algorithmVersion ) ) )?: self.algorithm;
[self didChangeValueForKey:@"outdated"]; [self didChangeValueForKey:@"outdated"];
if (self.entityOID) if (self.entityOID)

View File

@@ -17,6 +17,8 @@
//============================================================================== //==============================================================================
#import <QuartzCore/QuartzCore.h> #import <QuartzCore/QuartzCore.h>
#import <Countly/Countly.h>
#import <UserNotifications/UserNotifications.h>
#import "MPSitesWindowController.h" #import "MPSitesWindowController.h"
#import "MPMacAppDelegate.h" #import "MPMacAppDelegate.h"
#import "MPAppDelegate_Store.h" #import "MPAppDelegate_Store.h"
@@ -42,13 +44,27 @@
prof_rewind( @"replaceFonts" ); prof_rewind( @"replaceFonts" );
PearlAddNotificationObserver( NSWindowDidBecomeKeyNotification, self.window, [NSOperationQueue mainQueue], PearlAddNotificationObserver( NSWindowDidBecomeKeyNotification, self.window, [NSOperationQueue mainQueue],
^(id host, NSNotification *note) { (^(id host, NSNotification *note) {
prof_new( @"didBecomeKey" ); prof_new( @"didBecomeKey" );
[self.window makeKeyAndOrderFront:nil]; [self.window makeKeyAndOrderFront:nil];
prof_rewind( @"fadeIn" ); prof_rewind( @"fadeIn" );
[self updateUser]; [self updateUser];
prof_finish( @"updateUser" ); prof_rewind( @"updateUser" );
} );
if (![[MPMacConfig get].sendInfoDecided boolValue]) {
NSAlert *alert = [NSAlert new];
alert.messageText = @"Diagnostics";
alert.informativeText = @"We look for bugs, sudden crashes, runtime issues & statistics.\n\n"
@"Diagnostics are scrubbed and personal details will never leave your device.";
[alert addButtonWithTitle:@"Engage"];
[alert addButtonWithTitle:@"Disable"];
[alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
[MPMacConfig get].sendInfo = @(returnCode != NSAlertSecondButtonReturn);
[MPMacConfig get].sendInfoDecided = @(YES);
}];
}
prof_finish( @"sendInfoDecided" );
}) );
PearlAddNotificationObserver( NSWindowWillCloseNotification, self.window, [NSOperationQueue mainQueue], PearlAddNotificationObserver( NSWindowWillCloseNotification, self.window, [NSOperationQueue mainQueue],
^(id host, NSNotification *note) { ^(id host, NSNotification *note) {
NSWindow *sheet = [self.window attachedSheet]; NSWindow *sheet = [self.window attachedSheet];
@@ -81,7 +97,16 @@
self.siteTable.superview.superview.layer.mask = self.siteGradient; self.siteTable.superview.superview.layer.mask = self.siteGradient;
self.siteTable.controller = self; self.siteTable.controller = self;
prof_finish( @"ui" ); prof_rewind( @"ui" );
if (@available( macOS 10.14, * )) {
[[UNUserNotificationCenter currentNotificationCenter]
requestAuthorizationWithOptions:UNAuthorizationOptionAlert completionHandler:^(BOOL granted, NSError *error) {
if (!granted)
err( @"Couldn't obtain notification authorization: %@", error );
}];
}
prof_finish( @"notifications" );
} }
- (void)dealloc { - (void)dealloc {
@@ -376,7 +401,52 @@
}]; }];
} }
- (IBAction)changeType:(id)sender { - (IBAction)changeDefaultType:(id)sender {
MPSiteModel *site = self.selectedSite;
MPUserEntity *user = [MPMacAppDelegate get].activeUserForMainThread;
NSArray *types = [user.algorithm allTypes];
[self.passwordTypesMatrix renewRows:(NSInteger)[types count] columns:1];
for (NSUInteger t = 0; t < [types count]; ++t) {
MPResultType type = (MPResultType)[types[t] unsignedIntegerValue];
NSString *title = [user.algorithm nameOfType:type];
if (type & MPResultTypeClassTemplate)
title = strf( @"%@ %@", [user.algorithm mpwTemplateForSiteNamed:site.name?: @"masterpassword.app" ofType:type
withCounter:site.counter?: MPCounterValueDefault
usingKey:[MPMacAppDelegate get].key], title );
NSButtonCell *cell = [self.passwordTypesMatrix cellAtRow:(NSInteger)t column:0];
cell.tag = type;
cell.state = type == site.type? NSOnState: NSOffState;
cell.title = title;
}
self.passwordTypesBox.title = strf( @"Choose a password type for new sites of %@:", user.name );
NSAlert *alert = [NSAlert new];
[alert addButtonWithTitle:@"Save"];
[alert addButtonWithTitle:@"Cancel"];
[alert setMessageText:@"Change Default Type"];
[alert setAccessoryView:self.passwordTypesBox];
[alert layout];
[alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
switch (returnCode) {
case NSAlertFirstButtonReturn: {
// "Save" button.
MPResultType type = (MPResultType)[self.passwordTypesMatrix.selectedCell tag];
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
[[MPMacAppDelegate get] activeUserInContext:context].defaultType = type;
[context saveToStore];
}];
break;
}
default:
break;
}
}];
}
- (IBAction)changeSiteType:(id)sender {
MPSiteModel *site = self.selectedSite; MPSiteModel *site = self.selectedSite;
NSArray *types = [site.algorithm allTypes]; NSArray *types = [site.algorithm allTypes];
@@ -487,13 +557,19 @@
[self copyContent:self.shiftPressed? selectedSite.loginName: selectedSite.content]; [self copyContent:self.shiftPressed? selectedSite.loginName: selectedSite.content];
[NSApp hide:nil]; [NSApp hide:nil];
NSUserNotification *notification = [NSUserNotification new]; if (@available( macOS 10.14, * )) {
notification.title = @"Password Copied"; UNMutableNotificationContent *notification = [UNMutableNotificationContent new];
notification.title = self.shiftPressed? @"Login Copied": @"Password Copied";
if (selectedSite.loginName.length) if (selectedSite.loginName.length)
notification.subtitle = strf( @"%@ at %@", selectedSite.loginName, selectedSite.name ); notification.subtitle = strf( @"%@ at %@", selectedSite.loginName, selectedSite.name );
else else
notification.subtitle = selectedSite.name; notification.subtitle = selectedSite.name;
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification]; [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:
[UNNotificationRequest requestWithIdentifier:selectedSite.name content:notification trigger:nil]
withCompletionHandler:^(NSError *error) {
dbg( @"notification: %@, completed w/errror: %@", notification, error );
}];
}
} }
- (void)updateUser { - (void)updateUser {
@@ -535,22 +611,20 @@
return; return;
} }
static NSRegularExpression *fuzzyRE;
static dispatch_once_t once = 0;
dispatch_once( &once, ^{
fuzzyRE = [NSRegularExpression regularExpressionWithPattern:@"(.)" options:0 error:nil];
} );
prof_new( @"updateSites" ); prof_new( @"updateSites" );
NSString *queryString = self.siteField.stringValue; NSString *queryString = self.siteField.stringValue;
NSString *queryPattern = [[queryString stringByReplacingMatchesOfExpression:fuzzyRE withTemplate:@"*$1"] stringByAppendingString:@"*"]; NSMutableArray *queryGroups = [NSMutableArray new];
prof_rewind( @"queryPattern" ); NSMutableString *queryPattern = [NSMutableString new];
NSMutableArray *fuzzyGroups = [NSMutableArray new]; [queryString enumerateSubstringsInRange: NSMakeRange(0, [queryString length]) options: NSStringEnumerationByComposedCharacterSequences
[fuzzyRE enumerateMatchesInString:queryString options:0 range:NSMakeRange( 0, queryString.length ) usingBlock: ^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) { if (substringRange.location < 20) {
[fuzzyGroups addObject:[queryString substringWithRange:result.range]]; [queryGroups addObject:substring];
[queryPattern appendString:@"*"];
}
[queryPattern appendString:substring];
}]; }];
prof_rewind( @"fuzzyRE" ); [queryPattern appendString:@"*"];
prof_rewind( @"queryPattern" );
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
prof_rewind( @"moc" ); prof_rewind( @"moc" );
@@ -572,7 +646,7 @@
BOOL exact = NO; BOOL exact = NO;
NSMutableArray *newSites = [NSMutableArray arrayWithCapacity:[siteResults count]]; NSMutableArray *newSites = [NSMutableArray arrayWithCapacity:[siteResults count]];
for (MPSiteEntity *site in siteResults) { for (MPSiteEntity *site in siteResults) {
[newSites addObject:[[MPSiteModel alloc] initWithEntity:site fuzzyGroups:fuzzyGroups]]; [newSites addObject:[[MPSiteModel alloc] initWithEntity:site queryGroups:queryGroups]];
exact |= [site.name isEqualToString:queryString]; exact |= [site.name isEqualToString:queryString];
} }
prof_rewind( @"newSites: %u, exact: %d", (uint)[siteResults count], exact ); prof_rewind( @"newSites: %u, exact: %d", (uint)[siteResults count], exact );

View File

@@ -31,26 +31,26 @@
<rect key="contentRect" x="0.0" y="0.0" width="640" height="577"/> <rect key="contentRect" x="0.0" y="0.0" width="640" height="577"/>
<rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/> <rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ" userLabel="Root"> <view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ" userLabel="Root">
<rect key="frame" x="0.0" y="0.0" width="640" height="557"/> <rect key="frame" x="0.0" y="0.0" width="738" height="553"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<visualEffectView blendingMode="behindWindow" material="popover" state="followsWindowActiveState" translatesAutoresizingMaskIntoConstraints="NO" id="eRe-Ef-AZx"> <visualEffectView blendingMode="behindWindow" material="popover" state="followsWindowActiveState" translatesAutoresizingMaskIntoConstraints="NO" id="eRe-Ef-AZx">
<rect key="frame" x="0.0" y="0.0" width="640" height="557"/> <rect key="frame" x="0.0" y="0.0" width="738" height="553"/>
</visualEffectView> </visualEffectView>
<progressIndicator hidden="YES" wantsLayer="YES" horizontalHuggingPriority="750" verticalHuggingPriority="750" maxValue="100" displayedWhenStopped="NO" bezeled="NO" indeterminate="YES" controlSize="small" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="oSh-Ec-8Nf" userLabel="Progress Spinner"> <progressIndicator hidden="YES" wantsLayer="YES" horizontalHuggingPriority="750" verticalHuggingPriority="750" maxValue="100" displayedWhenStopped="NO" bezeled="NO" indeterminate="YES" controlSize="small" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="oSh-Ec-8Nf" userLabel="Progress Spinner">
<rect key="frame" x="312" y="521" width="16" height="16"/> <rect key="frame" x="361" y="517" width="16" height="16"/>
</progressIndicator> </progressIndicator>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="NGk-Io-Buc" userLabel="Top Box"> <customView translatesAutoresizingMaskIntoConstraints="NO" id="NGk-Io-Buc" userLabel="Top Box">
<rect key="frame" x="20" y="381" width="600" height="132"/> <rect key="frame" x="20" y="377" width="698" height="132"/>
<subviews> <subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Ond-dT-x5d" userLabel="Site Password Label"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Ond-dT-x5d" userLabel="Site Password Label">
<rect key="frame" x="157" y="98" width="287" height="14"/> <rect key="frame" x="206" y="98" width="287" height="14"/>
<shadow key="shadow" blurRadius="0.5"> <shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/> <size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/> <color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow> </shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Your password for apple.com (hit &lt;⏎ enter&gt; to copy):" id="CgJ-XZ-6Hy"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Your password for apple.com (hit &lt;⏎ enter&gt; to copy):" id="CgJ-XZ-6Hy">
<font key="font" metaFont="menu" size="11"/> <font key="font" metaFont="message" size="11"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
@@ -72,13 +72,13 @@
</connections> </connections>
</textField> </textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Ia6-7b-dFr"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Ia6-7b-dFr">
<rect key="frame" x="127" y="59" width="347" height="14"/> <rect key="frame" x="176" y="59" width="347" height="14"/>
<shadow key="shadow" blurRadius="0.5"> <shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/> <size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/> <color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow> </shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="No password set. Click &quot;Set Password&quot; on the bottom to set one." id="eDQ-iz-97a"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="No password set. Click &quot;Set Password&quot; on the bottom to set one." id="eDQ-iz-97a">
<font key="font" metaFont="menu" size="11"/> <font key="font" metaFont="message" size="11"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
@@ -108,13 +108,13 @@
</connections> </connections>
</textField> </textField>
<textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="OaQ-of-zmb" userLabel="Initial Help"> <textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="OaQ-of-zmb" userLabel="Initial Help">
<rect key="frame" x="39" y="109" width="522" height="18"/> <rect key="frame" x="113" y="109" width="473" height="17"/>
<shadow key="shadow" blurRadius="0.5"> <shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/> <size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/> <color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow> </shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Master Password generates passwords for your sites (and other things)." id="YyD-hd-wi3"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Master Password generates passwords for your sites (and other things)." id="YyD-hd-wi3">
<font key="font" size="16" name="HelveticaNeue"/> <font key="font" metaFont="menu" size="14"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
@@ -136,18 +136,18 @@
</connections> </connections>
</textField> </textField>
<textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="sYt-eL-uwt" userLabel="Initial Tip"> <textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="sYt-eL-uwt" userLabel="Initial Tip">
<rect key="frame" x="-26" y="31" width="652" height="70"/> <rect key="frame" x="-2" y="31" width="702" height="70"/>
<shadow key="shadow" blurRadius="0.5"> <shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/> <size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/> <color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow> </shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" id="9c4-NI-NM0"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" id="9c4-NI-NM0">
<font key="font" size="12" name="HelveticaNeue"/> <font key="font" metaFont="message" size="11"/>
<string key="title"> When you create an account with a site, use Master Password to create a password for it. <string key="title"> When you create an account on a site, open Master Password to create your account password.
For accounts you already have, change the password to that created by Master Password for it. ⑵ Consider changing all your existing account passwords to the password Master Password creates for those sites.
To get a password, enter the site's bare domain name in the "site name" field (eg. apple.com). To get the password for a site, just enter its domain name in the "site name" field (eg. "apple.com").
For your master password, think of a strong password (eg. a short sentence). Tell *nobody*. ⑷ When chosing a master password, make it easy but long (eg. a short sentence).
It's OK to share your site passwords. They can be changed if necessary.</string> ⑸ Tell *nobody* your master password. It's OK to share your site passwords with people you trust: they can be changed if necessary.</string>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
@@ -169,7 +169,7 @@
</connections> </connections>
</textField> </textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="XUV-zU-Y9c" userLabel="Site Password"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="XUV-zU-Y9c" userLabel="Site Password">
<rect key="frame" x="-2" y="26" width="604" height="80"/> <rect key="frame" x="-2" y="26" width="702" height="80"/>
<shadow key="shadow" blurRadius="0.5"> <shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/> <size key="offset" width="0.0" height="1"/>
<color key="color" white="0.0" alpha="1" colorSpace="calibratedWhite"/> <color key="color" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
@@ -202,6 +202,7 @@
<constraint firstItem="XUV-zU-Y9c" firstAttribute="leading" secondItem="NGk-Io-Buc" secondAttribute="leading" id="6Kl-7u-r90"/> <constraint firstItem="XUV-zU-Y9c" firstAttribute="leading" secondItem="NGk-Io-Buc" secondAttribute="leading" id="6Kl-7u-r90"/>
<constraint firstAttribute="centerY" secondItem="XUV-zU-Y9c" secondAttribute="centerY" id="6Qf-5O-Cvk"/> <constraint firstAttribute="centerY" secondItem="XUV-zU-Y9c" secondAttribute="centerY" id="6Qf-5O-Cvk"/>
<constraint firstAttribute="centerX" secondItem="XUV-zU-Y9c" secondAttribute="centerX" id="7sl-qi-HY9"/> <constraint firstAttribute="centerX" secondItem="XUV-zU-Y9c" secondAttribute="centerX" id="7sl-qi-HY9"/>
<constraint firstItem="sYt-eL-uwt" firstAttribute="width" secondItem="NGk-Io-Buc" secondAttribute="width" id="Beb-mW-KDP"/>
<constraint firstItem="Ond-dT-x5d" firstAttribute="top" secondItem="NGk-Io-Buc" secondAttribute="top" constant="20" symbolic="YES" id="IX4-cd-VkJ"/> <constraint firstItem="Ond-dT-x5d" firstAttribute="top" secondItem="NGk-Io-Buc" secondAttribute="top" constant="20" symbolic="YES" id="IX4-cd-VkJ"/>
<constraint firstItem="Ia6-7b-dFr" firstAttribute="centerY" secondItem="XUV-zU-Y9c" secondAttribute="centerY" id="KqM-uR-Obm"/> <constraint firstItem="Ia6-7b-dFr" firstAttribute="centerY" secondItem="XUV-zU-Y9c" secondAttribute="centerY" id="KqM-uR-Obm"/>
<constraint firstItem="Ia6-7b-dFr" firstAttribute="centerX" secondItem="XUV-zU-Y9c" secondAttribute="centerX" id="NFQ-aw-8tm"/> <constraint firstItem="Ia6-7b-dFr" firstAttribute="centerX" secondItem="XUV-zU-Y9c" secondAttribute="centerX" id="NFQ-aw-8tm"/>
@@ -209,14 +210,13 @@
<constraint firstAttribute="trailing" secondItem="XUV-zU-Y9c" secondAttribute="trailing" id="TdB-QV-9JK"/> <constraint firstAttribute="trailing" secondItem="XUV-zU-Y9c" secondAttribute="trailing" id="TdB-QV-9JK"/>
<constraint firstItem="Ond-dT-x5d" firstAttribute="bottom" secondItem="XUV-zU-Y9c" secondAttribute="top" constant="8" symbolic="YES" id="UgV-J6-B5T"/> <constraint firstItem="Ond-dT-x5d" firstAttribute="bottom" secondItem="XUV-zU-Y9c" secondAttribute="top" constant="8" symbolic="YES" id="UgV-J6-B5T"/>
<constraint firstItem="Ond-dT-x5d" firstAttribute="centerX" secondItem="XUV-zU-Y9c" secondAttribute="centerX" id="UhT-LQ-aZ8"/> <constraint firstItem="Ond-dT-x5d" firstAttribute="centerX" secondItem="XUV-zU-Y9c" secondAttribute="centerX" id="UhT-LQ-aZ8"/>
<constraint firstItem="sYt-eL-uwt" firstAttribute="leading" secondItem="NGk-Io-Buc" secondAttribute="leading" constant="-24" id="eaC-ow-ren"/>
<constraint firstItem="sYt-eL-uwt" firstAttribute="top" secondItem="OaQ-of-zmb" secondAttribute="bottom" constant="8" symbolic="YES" id="hjJ-f1-mFv"/> <constraint firstItem="sYt-eL-uwt" firstAttribute="top" secondItem="OaQ-of-zmb" secondAttribute="bottom" constant="8" symbolic="YES" id="hjJ-f1-mFv"/>
<constraint firstItem="sYt-eL-uwt" firstAttribute="centerX" secondItem="OaQ-of-zmb" secondAttribute="centerX" id="mu2-se-Mtn"/> <constraint firstItem="sYt-eL-uwt" firstAttribute="centerX" secondItem="OaQ-of-zmb" secondAttribute="centerX" id="mu2-se-Mtn"/>
<constraint firstAttribute="centerY" secondItem="sYt-eL-uwt" secondAttribute="centerY" id="zLS-QG-MKS"/> <constraint firstAttribute="centerY" secondItem="sYt-eL-uwt" secondAttribute="centerY" id="zLS-QG-MKS"/>
</constraints> </constraints>
</customView> </customView>
<scrollView focusRingType="none" borderType="none" autohidesScrollers="YES" horizontalLineScroll="34" horizontalPageScroll="10" verticalLineScroll="34" verticalPageScroll="10" hasHorizontalScroller="NO" hasVerticalScroller="NO" horizontalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="Bme-XK-MMc" userLabel="Sites Table"> <scrollView focusRingType="none" borderType="none" autohidesScrollers="YES" horizontalLineScroll="34" horizontalPageScroll="10" verticalLineScroll="34" verticalPageScroll="10" hasHorizontalScroller="NO" hasVerticalScroller="NO" horizontalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="Bme-XK-MMc" userLabel="Sites Table">
<rect key="frame" x="64" y="40" width="512" height="177"/> <rect key="frame" x="113" y="40" width="512" height="177"/>
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="e11-59-xSS"> <clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="e11-59-xSS">
<rect key="frame" x="0.0" y="0.0" width="512" height="177"/> <rect key="frame" x="0.0" y="0.0" width="512" height="177"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@@ -251,7 +251,7 @@
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/> <color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow> </shadow>
<textFieldCell key="cell" lineBreakMode="truncatingTail" alignment="center" title="apple.com" id="o0g-Zv-pH4"> <textFieldCell key="cell" lineBreakMode="truncatingTail" alignment="center" title="apple.com" id="o0g-Zv-pH4">
<font key="font" size="24" name="HelveticaNeue-Thin"/> <font key="font" metaFont="systemUltraLight" size="24"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
@@ -310,7 +310,7 @@
</connections> </connections>
</scrollView> </scrollView>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="nM8-O3-spM" customClass="MPGradientView"> <customView translatesAutoresizingMaskIntoConstraints="NO" id="nM8-O3-spM" customClass="MPGradientView">
<rect key="frame" x="0.0" y="0.0" width="640" height="212"/> <rect key="frame" x="0.0" y="0.0" width="738" height="238"/>
<userDefinedRuntimeAttributes> <userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="color" keyPath="startingColor"> <userDefinedRuntimeAttribute type="color" keyPath="startingColor">
<color key="value" red="0.11764705882352941" green="0.11764705882352941" blue="0.11764705882352941" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/> <color key="value" red="0.11764705882352941" green="0.11764705882352941" blue="0.11764705882352941" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
@@ -324,7 +324,7 @@
</userDefinedRuntimeAttributes> </userDefinedRuntimeAttributes>
</customView> </customView>
<button translatesAutoresizingMaskIntoConstraints="NO" id="Aue-Zx-6Mf" userLabel="Settings Gear"> <button translatesAutoresizingMaskIntoConstraints="NO" id="Aue-Zx-6Mf" userLabel="Settings Gear">
<rect key="frame" x="585" y="493" width="35" height="44"/> <rect key="frame" x="683" y="489" width="35" height="44"/>
<shadow key="shadow"> <shadow key="shadow">
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/> <color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow> </shadow>
@@ -339,13 +339,13 @@
</connections> </connections>
</button> </button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gAU-xs-aae"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gAU-xs-aae">
<rect key="frame" x="594" y="479" width="18" height="14"/> <rect key="frame" x="692" y="475" width="18" height="14"/>
<shadow key="shadow" blurRadius="0.5"> <shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/> <size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/> <color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow> </shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="⌘," id="Xm1-qb-6EP"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="⌘," id="Xm1-qb-6EP">
<font key="font" metaFont="menu" size="11"/> <font key="font" metaFont="message" size="11"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
@@ -358,7 +358,7 @@
</connections> </connections>
</textField> </textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="OnR-s6-d4P" userLabel="Site Name Label"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="OnR-s6-d4P" userLabel="Site Name Label">
<rect key="frame" x="209" y="308" width="223" height="16"/> <rect key="frame" x="258" y="306" width="223" height="16"/>
<shadow key="shadow" blurRadius="0.5"> <shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/> <size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/> <color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
@@ -370,7 +370,7 @@
</textFieldCell> </textFieldCell>
</textField> </textField>
<secureTextField focusRingType="none" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="iGR-wo-ual" userLabel="Secure Master Password"> <secureTextField focusRingType="none" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="iGR-wo-ual" userLabel="Secure Master Password">
<rect key="frame" x="18" y="257" width="604" height="43"/> <rect key="frame" x="18" y="255" width="702" height="43"/>
<shadow key="shadow" blurRadius="0.5"> <shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/> <size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/> <color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
@@ -405,7 +405,7 @@
</connections> </connections>
</secureTextField> </secureTextField>
<textField focusRingType="none" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="v80-wd-hUR" userLabel="Revealed Master Password"> <textField focusRingType="none" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="v80-wd-hUR" userLabel="Revealed Master Password">
<rect key="frame" x="18" y="257" width="604" height="43"/> <rect key="frame" x="18" y="255" width="702" height="43"/>
<shadow key="shadow" blurRadius="0.5"> <shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/> <size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/> <color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
@@ -444,13 +444,13 @@
</connections> </connections>
</textField> </textField>
<textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="lW3-2z-cEa" userLabel="Master Password Tip"> <textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="lW3-2z-cEa" userLabel="Master Password Tip">
<rect key="frame" x="195" y="237" width="251" height="12"/> <rect key="frame" x="248" y="236" width="243" height="11"/>
<shadow key="shadow" blurRadius="0.5"> <shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/> <size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/> <color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow> </shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Hold alt ⌥ to reveal tips and unmask ●●●●●●●●." id="4Ep-xX-Ky8"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Hold alt ⌥ to reveal tips and unmask ●●●●●●●●." id="4Ep-xX-Ky8">
<font key="font" size="11" name="HelveticaNeue"/> <font key="font" metaFont="miniSystem"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
@@ -471,13 +471,13 @@
</connections> </connections>
</textField> </textField>
<textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Oy5-B7-hdN" userLabel="Revealed Master Password Tip"> <textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Oy5-B7-hdN" userLabel="Revealed Master Password Tip">
<rect key="frame" x="195" y="237" width="250" height="12"/> <rect key="frame" x="258" y="236" width="222" height="11"/>
<shadow key="shadow" blurRadius="0.5"> <shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/> <size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/> <color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow> </shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Hold alt ⌥ to reveal tips and unmask passwords." id="bQ4-AN-S1A"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Hold alt ⌥ to reveal tips and unmask passwords." id="bQ4-AN-S1A">
<font key="font" size="11" name="HelveticaNeue"/> <font key="font" metaFont="miniSystem"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
@@ -499,7 +499,7 @@
</connections> </connections>
</textField> </textField>
<searchField focusRingType="none" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="CnS-iI-dhr" userLabel="Site Name"> <searchField focusRingType="none" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="CnS-iI-dhr" userLabel="Site Name">
<rect key="frame" x="62" y="257" width="516" height="43"/> <rect key="frame" x="111" y="255" width="516" height="43"/>
<constraints> <constraints>
<constraint firstAttribute="width" constant="512" id="rW7-Vq-4Xy"/> <constraint firstAttribute="width" constant="512" id="rW7-Vq-4Xy"/>
</constraints> </constraints>
@@ -508,7 +508,7 @@
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/> <color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow> </shadow>
<searchFieldCell key="cell" selectable="YES" editable="YES" focusRingType="none" alignment="center" placeholderString="Site Name" sendsSearchStringImmediately="YES" id="ppl-2c-1E9"> <searchFieldCell key="cell" selectable="YES" editable="YES" focusRingType="none" alignment="center" placeholderString="Site Name" sendsSearchStringImmediately="YES" id="ppl-2c-1E9">
<font key="font" size="36" name="HelveticaNeue-Thin"/> <font key="font" metaFont="system" size="36"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</searchFieldCell> </searchFieldCell>
@@ -519,13 +519,13 @@
</connections> </connections>
</searchField> </searchField>
<textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="npC-Kk-gUM" userLabel="Site Name Tip"> <textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="npC-Kk-gUM" userLabel="Site Name Tip">
<rect key="frame" x="101" y="237" width="438" height="12"/> <rect key="frame" x="172" y="236" width="395" height="11"/>
<shadow key="shadow" blurRadius="0.5"> <shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/> <size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/> <color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow> </shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Type the name of your site (eg. apple.com), then hit enter ⏎ to create a password for it." id="QTI-cz-Onx"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Type the name of your site (eg. apple.com), then hit enter ⏎ to create a password for it." id="QTI-cz-Onx">
<font key="font" size="11" name="HelveticaNeue"/> <font key="font" metaFont="miniSystem"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
@@ -542,13 +542,13 @@
</connections> </connections>
</textField> </textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rhm-sC-xFS" userLabel="Site Name Tip"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rhm-sC-xFS" userLabel="Site Name Tip">
<rect key="frame" x="138" y="225" width="365" height="24"/> <rect key="frame" x="206" y="225" width="326" height="22"/>
<shadow key="shadow" blurRadius="0.5"> <shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/> <size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/> <color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow> </shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" id="n3W-XU-dya"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" id="n3W-XU-dya">
<font key="font" size="11" name="HelveticaNeue"/> <font key="font" metaFont="miniSystem"/>
<string key="title">Hit enter ⏎ to copy the site's password, hold shift ⇧ for the login name. <string key="title">Hit enter ⏎ to copy the site's password, hold shift ⇧ for the login name.
Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string> Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
@@ -568,7 +568,7 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
</connections> </connections>
</textField> </textField>
<stackView distribution="fill" orientation="horizontal" alignment="bottom" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="pHt-gg-ZNX"> <stackView distribution="fill" orientation="horizontal" alignment="bottom" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="pHt-gg-ZNX">
<rect key="frame" x="73" y="20" width="495" height="152"/> <rect key="frame" x="122" y="20" width="495" height="178"/>
<subviews> <subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="1Qo-iG-CQt"> <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="1Qo-iG-CQt">
<rect key="frame" x="0.0" y="-1" width="85" height="19"/> <rect key="frame" x="0.0" y="-1" width="85" height="19"/>
@@ -591,16 +591,16 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
</connections> </connections>
</button> </button>
<stackView distribution="fill" orientation="vertical" alignment="centerX" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DT0-RU-3LT"> <stackView distribution="fill" orientation="vertical" alignment="centerX" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DT0-RU-3LT">
<rect key="frame" x="93" y="0.0" width="177" height="152"/> <rect key="frame" x="93" y="0.0" width="177" height="178"/>
<subviews> <subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="uol-dE-I8H"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="uol-dE-I8H">
<rect key="frame" x="77" y="138" width="22" height="14"/> <rect key="frame" x="77" y="164" width="22" height="14"/>
<shadow key="shadow" blurRadius="0.5"> <shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/> <size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/> <color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow> </shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="⌘P" id="MyN-x6-dMk"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="⌘P" id="MyN-x6-dMk">
<font key="font" metaFont="menu" size="11"/> <font key="font" metaFont="message" size="11"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
@@ -631,7 +631,7 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
</connections> </connections>
</textField> </textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="brI-fg-Kav"> <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="brI-fg-Kav">
<rect key="frame" x="40" y="111" width="96" height="19"/> <rect key="frame" x="40" y="137" width="96" height="19"/>
<shadow key="shadow"> <shadow key="shadow">
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/> <color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow> </shadow>
@@ -659,6 +659,35 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
</binding> </binding>
</connections> </connections>
</button> </button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="jl4-sS-xbm">
<rect key="frame" x="12" y="111" width="153" height="19"/>
<shadow key="shadow">
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow>
<buttonCell key="cell" type="recessed" title="Default Password Type" bezelStyle="recessed" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Ah6-gK-Rm7" customClass="MPNoStateButtonCell">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES" changeBackground="YES" changeGray="YES"/>
<font key="font" metaFont="systemBold" size="12"/>
<string key="keyEquivalent">p</string>
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</buttonCell>
<connections>
<action selector="changeDefaultType:" target="-2" id="3Qg-xV-ewc"/>
<binding destination="-2" name="hidden2" keyPath="alternatePressed" previousBinding="2Mv-lM-iXB" id="6QO-NJ-Uyo">
<dictionary key="options">
<integer key="NSMultipleValuesPlaceholder" value="-1"/>
<integer key="NSNoSelectionPlaceholder" value="-1"/>
<integer key="NSNotApplicablePlaceholder" value="-1"/>
<integer key="NSNullPlaceholder" value="-1"/>
<string key="NSValueTransformerName">NSNegateBoolean</string>
</dictionary>
</binding>
<binding destination="mcS-ik-b0n" name="hidden" keyPath="canRemove" id="2Mv-lM-iXB">
<dictionary key="options">
<string key="NSValueTransformerName">NSNegateBoolean</string>
</dictionary>
</binding>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="R46-fx-n14"> <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="R46-fx-n14">
<rect key="frame" x="0.0" y="85" width="177" height="19"/> <rect key="frame" x="0.0" y="85" width="177" height="19"/>
<shadow key="shadow"> <shadow key="shadow">
@@ -686,26 +715,26 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
</connections> </connections>
</button> </button>
<stackView distribution="fill" orientation="horizontal" alignment="top" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Bgn-Ne-fQ7" userLabel="Version Container"> <stackView distribution="fill" orientation="horizontal" alignment="top" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Bgn-Ne-fQ7" userLabel="Version Container">
<rect key="frame" x="70" y="56" width="36" height="22"/> <rect key="frame" x="71" y="56" width="34" height="22"/>
<subviews> <subviews>
<stepper horizontalHuggingPriority="750" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="mcq-qD-yte"> <stepper horizontalHuggingPriority="750" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="mcq-qD-yte">
<rect key="frame" x="-3" y="-3" width="19" height="28"/> <rect key="frame" x="-3" y="-3" width="19" height="28"/>
<shadow key="shadow"> <shadow key="shadow">
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/> <color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow> </shadow>
<stepperCell key="cell" continuous="YES" alignment="left" minValue="1" maxValue="1000" doubleValue="1" id="73y-03-zHt"/> <stepperCell key="cell" continuous="YES" alignment="left" maxValue="4294967295" doubleValue="1" valueWraps="YES" id="73y-03-zHt"/>
<connections> <connections>
<binding destination="mcS-ik-b0n" name="value" keyPath="selection.algorithmVersion" id="GyA-hK-6cD"/> <binding destination="mcS-ik-b0n" name="value" keyPath="selection.algorithmVersion" id="GyA-hK-6cD"/>
</connections> </connections>
</stepper> </stepper>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gyg-Fh-yn7"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gyg-Fh-yn7">
<rect key="frame" x="19" y="7" width="19" height="15"/> <rect key="frame" x="19" y="8" width="17" height="14"/>
<shadow key="shadow" blurRadius="0.5"> <shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/> <size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/> <color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow> </shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="V1" id="Pjy-Fm-zwB"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="V1" id="Pjy-Fm-zwB">
<font key="font" size="12" name="HelveticaNeue-Medium"/> <font key="font" metaFont="message" size="11"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="calibratedRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="calibratedRGB"/>
</textFieldCell> </textFieldCell>
@@ -740,26 +769,26 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
</connections> </connections>
</stackView> </stackView>
<stackView distribution="fill" orientation="horizontal" alignment="centerY" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6II-KA-cNi" userLabel="Counter Container"> <stackView distribution="fill" orientation="horizontal" alignment="centerY" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6II-KA-cNi" userLabel="Counter Container">
<rect key="frame" x="74" y="26" width="28" height="22"/> <rect key="frame" x="75" y="26" width="27" height="22"/>
<subviews> <subviews>
<stepper horizontalHuggingPriority="750" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="XgA-Vl-CKh" userLabel="Counter Stepper"> <stepper horizontalHuggingPriority="750" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="XgA-Vl-CKh" userLabel="Counter Stepper">
<rect key="frame" x="-3" y="-3" width="19" height="28"/> <rect key="frame" x="-3" y="-3" width="19" height="28"/>
<shadow key="shadow"> <shadow key="shadow">
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/> <color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow> </shadow>
<stepperCell key="cell" continuous="YES" alignment="left" minValue="1" maxValue="1000" doubleValue="1" id="ikF-n4-xiI"/> <stepperCell key="cell" continuous="YES" alignment="left" minValue="1" maxValue="4294967295" doubleValue="1" valueWraps="YES" id="ikF-n4-xiI"/>
<connections> <connections>
<binding destination="mcS-ik-b0n" name="value" keyPath="selection.counter" id="qmm-6z-boy"/> <binding destination="mcS-ik-b0n" name="value" keyPath="selection.counter" id="qmm-6z-boy"/>
</connections> </connections>
</stepper> </stepper>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="NvO-kt-eZ2" userLabel="Counter Field"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="NvO-kt-eZ2" userLabel="Counter Field">
<rect key="frame" x="19" y="4" width="11" height="15"/> <rect key="frame" x="19" y="4" width="10" height="14"/>
<shadow key="shadow" blurRadius="0.5"> <shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/> <size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/> <color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow> </shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="1" id="dhQ-bJ-rn3"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="1" id="dhQ-bJ-rn3">
<font key="font" size="12" name="HelveticaNeue-Medium"/> <font key="font" metaFont="message" size="11"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="calibratedRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="calibratedRGB"/>
</textFieldCell> </textFieldCell>
@@ -805,7 +834,7 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
<modifierMask key="keyEquivalentModifierMask" command="YES"/> <modifierMask key="keyEquivalentModifierMask" command="YES"/>
</buttonCell> </buttonCell>
<connections> <connections>
<action selector="changeType:" target="-2" id="6Jj-7p-da9"/> <action selector="changeSiteType:" target="-2" id="6Jj-7p-da9"/>
<binding destination="mcS-ik-b0n" name="hidden" keyPath="canRemove" id="Hat-GU-hcQ"> <binding destination="mcS-ik-b0n" name="hidden" keyPath="canRemove" id="Hat-GU-hcQ">
<dictionary key="options"> <dictionary key="options">
<string key="NSValueTransformerName">NSNegateBoolean</string> <string key="NSValueTransformerName">NSNegateBoolean</string>
@@ -821,6 +850,7 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
<integer value="1000"/> <integer value="1000"/>
<integer value="1000"/> <integer value="1000"/>
<integer value="1000"/> <integer value="1000"/>
<integer value="1000"/>
</visibilityPriorities> </visibilityPriorities>
<customSpacing> <customSpacing>
<real value="3.4028234663852886e+38"/> <real value="3.4028234663852886e+38"/>
@@ -829,6 +859,7 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
<real value="3.4028234663852886e+38"/> <real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/> <real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/> <real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
</customSpacing> </customSpacing>
</stackView> </stackView>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="whJ-Bw-pr4"> <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="whJ-Bw-pr4">
@@ -886,13 +917,13 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
</customSpacing> </customSpacing>
</stackView> </stackView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="luC-0j-BeV"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="luC-0j-BeV">
<rect key="frame" x="135" y="50" width="103" height="14"/> <rect key="frame" x="185" y="50" width="103" height="14"/>
<shadow key="shadow" blurRadius="0.5"> <shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/> <size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/> <color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow> </shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Password Number:" id="weR-33-U49"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Password Number:" id="weR-33-U49">
<font key="font" metaFont="menu" size="11"/> <font key="font" metaFont="message" size="11"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
@@ -923,13 +954,13 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
</connections> </connections>
</textField> </textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gjx-bt-fKM"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gjx-bt-fKM">
<rect key="frame" x="134" y="80" width="100" height="14"/> <rect key="frame" x="184" y="80" width="100" height="14"/>
<shadow key="shadow" blurRadius="0.5"> <shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/> <size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/> <color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow> </shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Password Version:" id="E77-5t-EKJ"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Password Version:" id="E77-5t-EKJ">
<font key="font" metaFont="menu" size="11"/> <font key="font" metaFont="message" size="11"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
@@ -960,13 +991,13 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
</connections> </connections>
</textField> </textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="dbM-ja-dKO" userLabel="Version Tip"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="dbM-ja-dKO" userLabel="Version Tip">
<rect key="frame" x="88" y="106" width="332" height="14"/> <rect key="frame" x="137" y="106" width="332" height="14"/>
<shadow key="shadow" blurRadius="0.5"> <shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/> <size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/> <color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow> </shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Upgrade your site's algorithm version for maximum protection:" id="3ds-qG-YNd"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Upgrade your site's algorithm version for maximum protection:" id="3ds-qG-YNd">
<font key="font" metaFont="menu" size="11"/> <font key="font" metaFont="message" size="11"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
@@ -988,13 +1019,13 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
</connections> </connections>
</textField> </textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Ido-NQ-3MY"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Ido-NQ-3MY">
<rect key="frame" x="104" y="4" width="22" height="12"/> <rect key="frame" x="154" y="2" width="21" height="14"/>
<shadow key="shadow" blurRadius="0.5"> <shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/> <size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/> <color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow> </shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="⌘L" id="fUB-rF-7x8"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="⌘L" id="fUB-rF-7x8">
<font key="font" size="11" name="HelveticaNeue"/> <font key="font" metaFont="message" size="11"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
@@ -1016,13 +1047,13 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
</connections> </connections>
</textField> </textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="9b3-wy-KBb"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="9b3-wy-KBb">
<rect key="frame" x="243" y="2" width="22" height="14"/> <rect key="frame" x="292" y="2" width="22" height="14"/>
<shadow key="shadow" blurRadius="0.5"> <shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/> <size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/> <color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow> </shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="⌘T" id="HFM-Bk-akx"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="⌘T" id="HFM-Bk-akx">
<font key="font" metaFont="menu" size="11"/> <font key="font" metaFont="message" size="11"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
@@ -1044,13 +1075,13 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
</connections> </connections>
</textField> </textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="qal-PP-YtO"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="qal-PP-YtO">
<rect key="frame" x="516" y="4" width="23" height="12"/> <rect key="frame" x="565" y="2" width="23" height="14"/>
<shadow key="shadow" blurRadius="0.5"> <shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/> <size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/> <color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow> </shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="⌘D" id="PPC-be-w4E"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="⌘D" id="PPC-be-w4E">
<font key="font" size="11" name="HelveticaNeue"/> <font key="font" metaFont="message" size="11"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
@@ -1072,13 +1103,13 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
</connections> </connections>
</textField> </textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="qLF-A6-ThX"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="qLF-A6-ThX">
<rect key="frame" x="404" y="4" width="23" height="12"/> <rect key="frame" x="453" y="2" width="22" height="14"/>
<shadow key="shadow" blurRadius="0.5"> <shadow key="shadow" blurRadius="0.5">
<size key="offset" width="0.0" height="1"/> <size key="offset" width="0.0" height="1"/>
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/> <color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
</shadow> </shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="⌘S" id="qh6-k2-MUr"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="⌘S" id="qh6-k2-MUr">
<font key="font" size="11" name="HelveticaNeue"/> <font key="font" metaFont="message" size="11"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
@@ -1178,7 +1209,7 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="kCO-1M-Wz1"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="kCO-1M-Wz1">
<rect key="frame" x="10" y="12" width="390" height="92"/> <rect key="frame" x="10" y="12" width="390" height="92"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" id="kl0-P1-6ZY"> <textFieldCell key="cell" sendsActionOnEndEditing="YES" id="kl0-P1-6ZY">
<font key="font" metaFont="menu" size="11"/> <font key="font" metaFont="message" size="11"/>
<string key="title">"Personal password" allows you to store your own password. It cannot be regenerated in the event of loss. "Device private password" is similar but will never leave your device: it cannot be synced, backed up or exported.</string> <string key="title">"Personal password" allows you to store your own password. It cannot be regenerated in the event of loss. "Device private password" is similar but will never leave your device: it cannot be synced, backed up or exported.</string>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>

View File

@@ -102,7 +102,7 @@
attributes = { attributes = {
BuildIndependentTargetsInParallel = YES; BuildIndependentTargetsInParallel = YES;
CLASSPREFIX = MP; CLASSPREFIX = MP;
LastUpgradeCheck = 1140; LastUpgradeCheck = 1200;
ORGANIZATIONNAME = "Maarten Billemont"; ORGANIZATIONNAME = "Maarten Billemont";
TargetAttributes = { TargetAttributes = {
DAD9B5C0176299B9001835F9 = { DAD9B5C0176299B9001835F9 = {
@@ -185,6 +185,7 @@
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO; CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
@@ -271,6 +272,7 @@
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO; CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;

View File

@@ -58,22 +58,6 @@ 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:@"bounds" withBlock:^(id from, id to, NSKeyValueChange cause, MPAvatarCell *self) {
self.contentView.frame = self.bounds;
}];
[self observeKeyPath:@"selected" withBlock:^(id from, id to, NSKeyValueChange cause, MPAvatarCell *self) {
[self updateAnimated:self.superview != nil];
}];
[self observeKeyPath:@"highlighted" withBlock:^(id from, id to, NSKeyValueChange cause, MPAvatarCell *self) {
[self updateAnimated:self.superview != nil];
}];
PearlAddNotificationObserver( UIKeyboardWillShowNotification, nil, [NSOperationQueue mainQueue],
^(MPAvatarCell *self, NSNotification *note) {
CGRect keyboardRect = [note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGFloat keyboardHeight = CGRectGetHeight( self.window.screen.bounds ) - CGRectGetMinY( keyboardRect );
[self.keyboardHeightConstraint updateConstant:keyboardHeight];
} );
CABasicAnimation *toShadowOpacityAnimation = [CABasicAnimation animationWithKeyPath:@"shadowOpacity"]; CABasicAnimation *toShadowOpacityAnimation = [CABasicAnimation animationWithKeyPath:@"shadowOpacity"];
toShadowOpacityAnimation.toValue = @0.2f; toShadowOpacityAnimation.toValue = @0.2f;
toShadowOpacityAnimation.duration = 0.5f; toShadowOpacityAnimation.duration = 0.5f;
@@ -91,6 +75,22 @@ const long MPAvatarAdd = 10000;
self.targetedShadowAnimation.duration = MAXFLOAT; self.targetedShadowAnimation.duration = MAXFLOAT;
self.avatarImageView.layer.shadowColor = [UIColor whiteColor].CGColor; self.avatarImageView.layer.shadowColor = [UIColor whiteColor].CGColor;
self.avatarImageView.layer.shadowOffset = CGSizeZero; self.avatarImageView.layer.shadowOffset = CGSizeZero;
[self observeKeyPath:@"bounds" withBlock:^(id from, id to, NSKeyValueChange cause, MPAvatarCell *self) {
self.contentView.frame = self.bounds;
}];
[self observeKeyPath:@"selected" withBlock:^(id from, id to, NSKeyValueChange cause, MPAvatarCell *self) {
[self updateAnimated:self.superview != nil];
}];
[self observeKeyPath:@"highlighted" withBlock:^(id from, id to, NSKeyValueChange cause, MPAvatarCell *self) {
[self updateAnimated:self.superview != nil];
}];
PearlAddNotificationObserver( UIKeyboardWillShowNotification, nil, [NSOperationQueue mainQueue],
^(MPAvatarCell *self, NSNotification *note) {
CGRect keyboardRect = [note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGFloat keyboardHeight = CGRectGetHeight( self.window.screen.bounds ) - CGRectGetMinY( keyboardRect );
[self.keyboardHeightConstraint updateConstant:keyboardHeight];
} );
} }
- (void)prepareForReuse { - (void)prepareForReuse {

View File

@@ -31,7 +31,6 @@
[super viewDidLoad]; [super viewDidLoad];
self.mode = MPCombinedModeUserSelection; self.mode = MPCombinedModeUserSelection;
[self performSegueWithIdentifier:@"users" sender:self];
} }
- (void)viewWillAppear:(BOOL)animated { - (void)viewWillAppear:(BOOL)animated {

View File

@@ -30,8 +30,10 @@
@property(weak, nonatomic) IBOutlet UIActivityIndicatorView *activity; @property(weak, nonatomic) IBOutlet UIActivityIndicatorView *activity;
@property(weak, nonatomic) IBOutlet UIButton *passwordButton; @property(weak, nonatomic) IBOutlet UIButton *passwordButton;
@property(weak, nonatomic) IBOutlet UIView *tipContainer; @property(weak, nonatomic) IBOutlet UIView *tipContainer;
@property(weak, nonatomic) IBOutlet UIButton *deviceButton;
- (IBAction)controlChanged:(UIControl *)control; - (IBAction)controlChanged:(UIControl *)control;
- (IBAction)copyPassword:(UITapGestureRecognizer *)recognizer; - (IBAction)copyPassword:(UITapGestureRecognizer *)recognizer;
- (IBAction)copyDevice:(id)sender;
@end @end

View File

@@ -38,6 +38,8 @@
self.view.backgroundColor = [UIColor clearColor]; self.view.backgroundColor = [UIColor clearColor];
self.dialogView.layer.cornerRadius = 5; self.dialogView.layer.cornerRadius = 5;
[self.deviceButton setTitle:[PearlKeyChain deviceIdentifier] forState:UIControlStateNormal];
} }
- (void)viewWillAppear:(BOOL)animated { - (void)viewWillAppear:(BOOL)animated {
@@ -112,6 +114,11 @@
}]; }];
} }
- (IBAction)copyDevice:(id)sender {
[UIPasteboard generalPasteboard].string = [PearlKeyChain deviceIdentifier];
[PearlOverlay showTemporaryOverlayWithTitle:strl( @"Device Identifier Copied" ) dismissAfter:2];
}
#pragma mark - Private #pragma mark - Private
- (void)updateKey { - (void)updateKey {

View File

@@ -17,7 +17,6 @@
//============================================================================== //==============================================================================
#import "MPGuideViewController.h" #import "MPGuideViewController.h"
#import "markdown_lib.h"
#import "NSString+MPMarkDown.h" #import "NSString+MPMarkDown.h"
@interface MPGuideStep : NSObject @interface MPGuideStep : NSObject

View File

@@ -18,6 +18,7 @@
#import "MPNavigationController.h" #import "MPNavigationController.h"
#import "MPWebViewController.h" #import "MPWebViewController.h"
#import "MPiOSAppDelegate.h"
@implementation MPNavigationController @implementation MPNavigationController
@@ -29,6 +30,16 @@
[self performSegueWithIdentifier:@"setup" sender:self]; [self performSegueWithIdentifier:@"setup" sender:self];
} }
- (void)performSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
if ([identifier isEqualToString:@"web"] && [[(NSURL *)sender scheme] isEqualToString:@"masterpassword"]) {
[[MPiOSAppDelegate get] openURL:sender];
return;
}
[super performSegueWithIdentifier:identifier sender:sender];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@"web"]) if ([segue.identifier isEqualToString:@"web"])

View File

@@ -33,13 +33,6 @@
self.dismissSegueByButton = [NSMutableDictionary dictionary]; self.dismissSegueByButton = [NSMutableDictionary dictionary];
} }
- (void)viewDidLoad {
[super viewDidLoad];
[self performSegueWithIdentifier:@"root" sender:self];
}
- (UIViewController *)childViewControllerForStatusBarStyle { - (UIViewController *)childViewControllerForStatusBarStyle {
return [self.childViewControllers lastObject]; return [self.childViewControllers lastObject];

View File

@@ -1,23 +0,0 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
#import <Foundation/Foundation.h>
@interface MPRootSegue : UIStoryboardSegue
@end

View File

@@ -1,35 +0,0 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
#import "MPRootSegue.h"
@implementation MPRootSegue
- (void)perform {
UIViewController *sourceViewController = self.sourceViewController;
UIViewController *destinationViewController = self.destinationViewController;
[sourceViewController addChildViewController:destinationViewController];
destinationViewController.view.frame = sourceViewController.view.bounds;
destinationViewController.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[sourceViewController.view addSubview:destinationViewController.view];
[destinationViewController didMoveToParentViewController:sourceViewController];
[sourceViewController setNeedsStatusBarAppearanceUpdate];
}
@end

View File

@@ -27,7 +27,7 @@ typedef NS_ENUM ( NSUInteger, MPSiteCellMode ) {
@interface MPSiteCell : MPCell<UIScrollViewDelegate, UITextFieldDelegate> @interface MPSiteCell : MPCell<UIScrollViewDelegate, UITextFieldDelegate>
@property(nonatomic) NSArray *fuzzyGroups; @property(nonatomic) NSArray *queryGroups;
- (void)setSite:(MPSiteEntity *)site animated:(BOOL)animated; - (void)setSite:(MPSiteEntity *)site animated:(BOOL)animated;
- (void)setTransientSite:(NSString *)siteName animated:(BOOL)animated; - (void)setTransientSite:(NSString *)siteName animated:(BOOL)animated;

View File

@@ -135,7 +135,7 @@
[super prepareForReuse]; [super prepareForReuse];
self.siteOID = nil; self.siteOID = nil;
self.fuzzyGroups = nil; self.queryGroups = nil;
self.transientSite = nil; self.transientSite = nil;
self.mode = MPPasswordCellModePassword; self.mode = MPPasswordCellModePassword;
[self updateAnimated:NO]; [self updateAnimated:NO];
@@ -150,11 +150,11 @@
#pragma mark - State #pragma mark - State
- (void)setFuzzyGroups:(NSArray *)fuzzyGroups { - (void)setQueryGroups:(NSArray *)queryGroups {
if (self.fuzzyGroups == fuzzyGroups) if (self.queryGroups == queryGroups)
return; return;
_fuzzyGroups = fuzzyGroups; _queryGroups = queryGroups;
[self updateSiteName:[self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]]]; [self updateSiteName:[self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]]];
} }
@@ -263,6 +263,7 @@
UIAlertController *controller = [UIAlertController alertControllerWithTitle:strf( @"Delete %@?", site.name ) message:nil UIAlertController *controller = [UIAlertController alertControllerWithTitle:strf( @"Delete %@?", site.name ) message:nil
preferredStyle:UIAlertControllerStyleActionSheet]; preferredStyle:UIAlertControllerStyleActionSheet];
[controller.popoverPresentationController setSourceView:sender];
[controller addAction:[UIAlertAction actionWithTitle:@"Delete Site" style:UIAlertActionStyleDestructive [controller addAction:[UIAlertAction actionWithTitle:@"Delete Site" style:UIAlertActionStyleDestructive
handler:^(UIAlertAction *_Nonnull action) { handler:^(UIAlertAction *_Nonnull action) {
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
@@ -284,6 +285,7 @@
MPSiteEntity *mainSite = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]]; MPSiteEntity *mainSite = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Change Password Type" message:nil UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Change Password Type" message:nil
preferredStyle:UIAlertControllerStyleActionSheet]; preferredStyle:UIAlertControllerStyleActionSheet];
[controller.popoverPresentationController setSourceView:sender];
for (NSNumber *typeNumber in [mainSite.algorithm allTypes]) { for (NSNumber *typeNumber in [mainSite.algorithm allTypes]) {
MPResultType type = (MPResultType)[typeNumber unsignedIntegerValue]; MPResultType type = (MPResultType)[typeNumber unsignedIntegerValue];
NSString *typeName = [mainSite.algorithm nameOfType:type]; NSString *typeName = [mainSite.algorithm nameOfType:type];
@@ -425,7 +427,7 @@
if (!site || ![site isKindOfClass:[MPGeneratedSiteEntity class]]) if (!site || ![site isKindOfClass:[MPGeneratedSiteEntity class]])
return; return;
((MPGeneratedSiteEntity *)site).counter = 1; ((MPGeneratedSiteEntity *)site).counter = MPCounterValueInitial;
[context saveToStore]; [context saveToStore];
[PearlOverlay showTemporaryOverlayWithTitle:@"Counter Reset" dismissAfter:2]; [PearlOverlay showTemporaryOverlayWithTitle:@"Counter Reset" dismissAfter:2];
@@ -433,7 +435,7 @@
}]; }];
} }
- (IBAction)doContent:(id)sender { - (IBAction)doContent:(UIButton *)sender {
[UIView animateWithDuration:.2f animations:^{ [UIView animateWithDuration:.2f animations:^{
self.contentButton.selected = YES; self.contentButton.selected = YES;
@@ -444,6 +446,7 @@
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Create Site" message: UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Create Site" message:
strf( @"Remember site named:\n%@", self.transientSite ) strf( @"Remember site named:\n%@", self.transientSite )
preferredStyle:UIAlertControllerStyleActionSheet]; preferredStyle:UIAlertControllerStyleActionSheet];
[controller.popoverPresentationController setSourceView:sender];
[controller addAction:[UIAlertAction actionWithTitle:@"Yes" style:UIAlertActionStyleDefault handler: [controller addAction:[UIAlertAction actionWithTitle:@"Yes" style:UIAlertActionStyleDefault handler:
^(UIAlertAction *_Nonnull action) { ^(UIAlertAction *_Nonnull action) {
[[MPiOSAppDelegate get] [[MPiOSAppDelegate get]
@@ -534,13 +537,13 @@
// UI // UI
//self.backgroundColor = mainSite.url? [UIColor greenColor]: [UIColor redColor]; //self.backgroundColor = mainSite.url? [UIColor greenColor]: [UIColor redColor];
self.upgradeButton.gone = !mainSite.requiresExplicitMigration && ![[MPiOSConfig get].allowDowngrade boolValue]; self.upgradeButton.visible = mainSite.requiresExplicitMigration || [[MPiOSConfig get].allowDowngrade boolValue];
self.answersButton.gone = ![[MPiOSAppDelegate get] isFeatureUnlocked:MPProductGenerateAnswers]; self.answersButton.visible = [[MPiOSAppDelegate get] isFeatureUnlocked:MPProductGenerateAnswers];
BOOL settingsMode = self.mode == MPPasswordCellModeSettings; BOOL settingsMode = self.mode == MPPasswordCellModeSettings;
self.loginNameContainer.visible = settingsMode || mainSite.loginGenerated || [mainSite.loginName length]; self.loginNameContainer.visible = settingsMode || mainSite.loginGenerated || [mainSite.loginName length];
self.modeButton.visible = !self.transientSite; self.modeButton.visible = !self.transientSite;
self.modeButton.alpha = settingsMode? 0.5f: 0.1f; self.modeButton.alpha = settingsMode? 0.5f: 0.1f;
self.counterLabel.visible = self.counterButton.visible = mainSite.type & MPResultTypeClassTemplate; self.counterLabel.visible = self.counterButton.visible = (mainSite.type & MPResultTypeClassTemplate) == MPResultTypeClassTemplate;
self.modeButton.selected = settingsMode; self.modeButton.selected = settingsMode;
self.strengthLabel.gone = !settingsMode; self.strengthLabel.gone = !settingsMode;
self.modeScrollView.scrollEnabled = !self.transientSite; self.modeScrollView.scrollEnabled = !self.transientSite;
@@ -653,14 +656,14 @@
NSString *siteName = self.transientSite?: site.name; NSString *siteName = self.transientSite?: site.name;
NSMutableAttributedString *attributedSiteName = [[NSMutableAttributedString alloc] initWithString:siteName?: @""]; NSMutableAttributedString *attributedSiteName = [[NSMutableAttributedString alloc] initWithString:siteName?: @""];
if ([attributedSiteName length]) if ([attributedSiteName length])
for (NSUInteger f = 0, s = (NSUInteger)-1; f < [self.fuzzyGroups count]; ++f) { for (NSUInteger f = 0, s = 0; f < [self.queryGroups count]; ++f, ++s) {
s = [siteName rangeOfString:self.fuzzyGroups[f] options:NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch s = [siteName rangeOfString:self.queryGroups[f] options:NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch
range:NSMakeRange( s + 1, [siteName length] - (s + 1) )].location; range:NSMakeRange( s, [siteName length] - s )].location;
if (s == NSNotFound) if (s == NSNotFound)
break; break;
[attributedSiteName addAttribute:NSBackgroundColorAttributeName value:[UIColor redColor] [attributedSiteName addAttribute:NSBackgroundColorAttributeName value:[UIColor redColor]
range:NSMakeRange( s, [self.fuzzyGroups[f] length] )]; range:NSMakeRange( s, [self.queryGroups[f] length] )];
} }
if (self.transientSite) if (self.transientSite)

View File

@@ -43,7 +43,7 @@
UIView *sitesView = sitesVC.view; UIView *sitesView = sitesVC.view;
sitesView.frame = combinedVC.view.bounds; sitesView.frame = combinedVC.view.bounds;
sitesView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; sitesView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[combinedVC.view insertSubview:sitesView belowSubview:combinedVC.usersVC.view]; [combinedVC.view insertSubview:sitesView belowSubview:combinedVC.usersVC.view.superview];
[sitesVC setActive:YES animated:self.animated completion:^(BOOL finished) { [sitesVC setActive:YES animated:self.animated completion:^(BOOL finished) {
if (!finished) if (!finished)

View File

@@ -30,8 +30,8 @@
@property(nonatomic, strong) IBOutlet UIView *badNameTipContainer; @property(nonatomic, strong) IBOutlet UIView *badNameTipContainer;
@property(nonatomic, strong) IBOutlet UIView *popdownView; @property(nonatomic, strong) IBOutlet UIView *popdownView;
@property(nonatomic, strong) IBOutlet UIView *popdownContainer; @property(nonatomic, strong) IBOutlet UIView *popdownContainer;
@property(nonatomic, strong) IBOutlet UIView *voltoInstallAlert; @property(nonatomic, strong) IBOutlet UIView *spectreInstallAlert;
@property(nonatomic, strong) IBOutlet UIView *voltoMigrateAlert; @property(nonatomic, strong) IBOutlet UIView *spectreMigrateAlert;
@property(assign, nonatomic) BOOL active; @property(assign, nonatomic) BOOL active;
@@ -39,6 +39,6 @@
- (void)reloadSites; - (void)reloadSites;
- (IBAction)dismissPopdown:(id)sender; - (IBAction)dismissPopdown:(id)sender;
- (IBAction)upgradeVolto:(UIButton *)sender; - (IBAction)upgradeSpectre:(UIButton *)sender;
@end @end

View File

@@ -16,7 +16,9 @@
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>. // LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//============================================================================== //==============================================================================
MP_LIBS_BEGIN
#import <StoreKit/StoreKit.h> #import <StoreKit/StoreKit.h>
MP_LIBS_END
#import "MPSitesViewController.h" #import "MPSitesViewController.h"
#import "MPiOSAppDelegate.h" #import "MPiOSAppDelegate.h"
@@ -33,11 +35,10 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
MPPasswordsBadNameTip = 1 << 0, MPPasswordsBadNameTip = 1 << 0,
}; };
@interface MPSitesViewController()<NSFetchedResultsControllerDelegate, SKStoreProductViewControllerDelegate> @interface MPSitesViewController()<NSFetchedResultsControllerDelegate>
@property(nonatomic, strong) SKStoreProductViewController *voltoViewController;
@property(nonatomic, strong) NSFetchedResultsController *fetchedResultsController; @property(nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
@property(nonatomic, strong) NSArray *fuzzyGroups; @property(nonatomic, strong) NSArray *queryGroups;
@property(nonatomic, strong) NSCharacterSet *siteNameAcceptableCharactersSet; @property(nonatomic, strong) NSCharacterSet *siteNameAcceptableCharactersSet;
@property(nonatomic, strong) NSMutableArray<NSMutableArray *> *dataSource; @property(nonatomic, strong) NSMutableArray<NSMutableArray *> *dataSource;
@property(nonatomic, weak) UIViewController *popdownVC; @property(nonatomic, weak) UIViewController *popdownVC;
@@ -73,7 +74,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
[self registerObservers]; [self registerObservers];
[self updateConfigKey:nil]; [self updateConfigKey:nil];
[self updateVoltoAlerts]; [self updateSpectreAlerts];
static NSRegularExpression *bareHostRE = nil; static NSRegularExpression *bareHostRE = nil;
static dispatch_once_t once = 0; static dispatch_once_t once = 0;
@@ -121,12 +122,14 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
PearlRemoveNotificationObservers(); PearlRemoveNotificationObservers();
} }
- (void)viewWillLayoutSubviews { - (void)viewDidLayoutSubviews {
self.collectionView.contentInset = [self.collectionView occludedInsets]; [super viewDidLayoutSubviews];
self.collectionView.scrollIndicatorInsets = self.collectionView.contentInset;
[super viewWillLayoutSubviews]; if (@available( iOS 11, * )) {
self.collectionView.layoutMargins =
UIEdgeInsetsMake( [self.collectionView occludedInsets].top - self.view.safeAreaInsets.top, 0, 0, 0 );
}
} }
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
@@ -161,7 +164,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
MPSiteCell *cell = [MPSiteCell dequeueFromCollectionView:collectionView indexPath:indexPath]; MPSiteCell *cell = [MPSiteCell dequeueFromCollectionView:collectionView indexPath:indexPath];
[cell setFuzzyGroups:self.fuzzyGroups]; [cell setQueryGroups:self.queryGroups];
id item = self.dataSource[(NSUInteger)indexPath.section][(NSUInteger)indexPath.item]; id item = self.dataSource[(NSUInteger)indexPath.section][(NSUInteger)indexPath.item];
if ([item isKindOfClass:[MPSiteEntity class]]) if ([item isKindOfClass:[MPSiteEntity class]])
[cell setSite:item animated:NO]; [cell setSite:item animated:NO];
@@ -171,6 +174,19 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
return cell; return cell;
} }
#pragma mark - UICollectionViewDelegateFlowLayout
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)collectionViewLayout;
CGFloat availableWidth = collectionView.bounds.size.width
- collectionView.layoutMargins.left - collectionView.layoutMargins.right
- layout.sectionInset.left - layout.sectionInset.right;
CGFloat cells = MAX( 1, (int)((availableWidth + layout.minimumInteritemSpacing) / (318 + layout.minimumInteritemSpacing)) );
return CGSizeMake( (availableWidth - layout.minimumInteritemSpacing * (cells - 1)) / cells, 100 );
}
#pragma mark - UIScrollDelegate #pragma mark - UIScrollDelegate
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
@@ -185,11 +201,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
if (controller == self.fetchedResultsController) if (controller == self.fetchedResultsController)
PearlMainQueue( ^{ [self updateSites];
[self.collectionView updateDataSource:self.dataSource
toSections:[self createDataSource]
reloadItems:nil completion:nil];
} );
} }
#pragma mark - UISearchBarDelegate #pragma mark - UISearchBarDelegate
@@ -306,7 +318,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
PearlAddNotificationObserver( UIApplicationWillEnterForegroundNotification, nil, [NSOperationQueue mainQueue], PearlAddNotificationObserver( UIApplicationWillEnterForegroundNotification, nil, [NSOperationQueue mainQueue],
^(MPSitesViewController *self, NSNotification *note) { ^(MPSitesViewController *self, NSNotification *note) {
[self viewWillAppear:YES]; [self viewWillAppear:YES];
[self updateVoltoAlerts]; [self updateSpectreAlerts];
} ); } );
PearlAddNotificationObserver( MPSignedOutNotification, nil, nil, PearlAddNotificationObserver( MPSignedOutNotification, nil, nil,
^(MPSitesViewController *self, NSNotification *note) { ^(MPSitesViewController *self, NSNotification *note) {
@@ -354,37 +366,40 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
- (void)reloadSites { - (void)reloadSites {
[self.fetchedResultsController.managedObjectContext performBlock:^{ [self.fetchedResultsController.managedObjectContext performBlock:^{
static NSRegularExpression *fuzzyRE;
static dispatch_once_t once = 0;
dispatch_once( &once, ^{
fuzzyRE = [NSRegularExpression regularExpressionWithPattern:@"(.)" options:0 error:nil];
} );
NSString *queryString = self.query; NSString *queryString = self.query;
NSString *queryPattern = [[queryString stringByReplacingMatchesOfExpression:fuzzyRE withTemplate:@"*$1"] NSMutableArray *queryGroups = [NSMutableArray new];
stringByAppendingString:@"*"]; NSMutableString *queryPattern = [NSMutableString new];
NSMutableArray *fuzzyGroups = [NSMutableArray new]; [queryString enumerateSubstringsInRange: NSMakeRange(0, [queryString length]) options: NSStringEnumerationByComposedCharacterSequences
[fuzzyRE enumerateMatchesInString:queryString options:0 range:NSMakeRange( 0, queryString.length ) usingBlock: ^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) { if (substringRange.location < 20) {
[fuzzyGroups addObject:[queryString substringWithRange:result.range]]; [queryGroups addObject:substring];
[queryPattern appendString:@"*"];
}
[queryPattern appendString:substring];
}]; }];
self.fuzzyGroups = fuzzyGroups; [queryPattern appendString:@"*"];
self.queryGroups = queryGroups;
NSError *error = nil; NSError *error = nil;
self.fetchedResultsController.fetchRequest.predicate = self.fetchedResultsController.fetchRequest.predicate =
[NSPredicate predicateWithFormat:@"name LIKE[cd] %@ AND user == %@", queryPattern, [MPiOSAppDelegate get].activeUserOID]; [NSPredicate predicateWithFormat:@"name LIKE[cd] %@ AND user == %@", queryPattern, [MPiOSAppDelegate get].activeUserOID];
if (![self.fetchedResultsController performFetch:&error]) if (![self.fetchedResultsController performFetch:&error] || error)
MPError( error, @"Couldn't fetch sites." ); MPError( error, @"Couldn't fetch sites." );
[self updateSites];
}];
}
- (void)updateSites {
PearlMainQueue( ^{ PearlMainQueue( ^{
[self.collectionView updateDataSource:self.dataSource [self.collectionView updateDataSource:self.dataSource
toSections:[self createDataSource] toSections:[self createDataSource]
reloadItems:@[ MPTransientPasswordItem ] completion:^(BOOL finished) { reloadItems:@[ MPTransientPasswordItem ] completion:^(BOOL finished) {
for (MPSiteCell *cell in self.collectionView.visibleCells) for (MPSiteCell *cell in self.collectionView.visibleCells)
[cell setFuzzyGroups:self.fuzzyGroups]; [cell setQueryGroups:self.queryGroups];
}]; }];
} ); } );
}];
} }
#pragma mark - Properties #pragma mark - Properties
@@ -404,14 +419,19 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
if (!_fetchedResultsController) { if (!_fetchedResultsController) {
[MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) { [MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )]; NSFetchRequest *fetchRequest = [MPSiteEntity fetchRequest];
fetchRequest.sortDescriptors = @[ fetchRequest.sortDescriptors = @[
[[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector( lastUsed ) ) ascending:NO] [[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector( lastUsed ) ) ascending:NO]
]; ];
fetchRequest.fetchBatchSize = 10;
(self.fetchedResultsController = [[NSFetchedResultsController alloc] (self.fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest managedObjectContext:mainContext initWithFetchRequest:fetchRequest managedObjectContext:mainContext
sectionNameKeyPath:nil cacheName:nil]).delegate = self; sectionNameKeyPath:nil cacheName:nil]).delegate = self;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error] || error)
MPError( error, @"Couldn't fetch sites." );
[self updateSites];
}]; }];
[self registerObservers]; [self registerObservers];
} }
@@ -435,13 +455,6 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
} completion:completion]; } completion:completion];
} }
#pragma mark - SKStoreProductViewControllerDelegate
- (void)productViewControllerDidFinish:(SKStoreProductViewController *)viewController {
[viewController dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - Actions #pragma mark - Actions
- (IBAction)dismissPopdown:(id)sender { - (IBAction)dismissPopdown:(id)sender {
@@ -452,76 +465,23 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
self.popdownToTopConstraint.priority = UILayoutPriorityDefaultHigh; self.popdownToTopConstraint.priority = UILayoutPriorityDefaultHigh;
} }
- (IBAction)upgradeVolto:(UIButton *)sender { - (IBAction)upgradeSpectre:(UIButton *)sender {
if ([UIApp canOpenURL:[[NSURL alloc] initWithString:@"volto:"]]) { [[MPiOSAppDelegate get] migrateFor:[MPiOSAppDelegate get].activeUserForMainThread];
[[MPiOSAppDelegate get] exportSitesRevealPasswords:NO askExportPassword:^NSString *(NSString *userName) {
return PearlAwait( ^(void (^setResult)(id)) {
PearlMainQueue( ^{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:strf( @"Master Password For:\n%@", userName )
message:@"Enter your master password to export the user."
preferredStyle:UIAlertControllerStyleAlert];
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.secureTextEntry = YES;
}];
[alert addAction:[UIAlertAction actionWithTitle:@"Export" style:UIAlertActionStyleDefault handler:
^(UIAlertAction *action) { setResult( alert.textFields.firstObject.text ); }]];
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:
^(UIAlertAction *action) { setResult( nil ); }]];
[self.navigationController presentViewController:alert animated:YES completion:nil];
} );
} );
} result:^(NSString *exportedUser, NSError *error) {
if (!exportedUser || error) {
MPError( error, @"Failed to export user." );
PearlMainQueue( ^{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Export Error"
message:[error localizedDescription]
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:alert animated:YES completion:nil];
} );
return;
}
NSURLComponents *components = [NSURLComponents new];
components.scheme = @"volto";
components.path = @"import";
components.queryItems = @[ [[NSURLQueryItem alloc] initWithName:@"data" value:exportedUser] ];
[UIApp openURL:components.URL];
}];
}
else if (self.voltoViewController)
[self presentViewController:self.voltoViewController animated:YES completion:nil];
} }
#pragma mark - Private #pragma mark - Private
- (void)updateVoltoAlerts { - (void)updateSpectreAlerts {
BOOL voltoInstalled = [UIApp canOpenURL:[[NSURL alloc] initWithString:@"volto:"]]; BOOL spectreInstalled = [UIApp canOpenURL:[[NSURL alloc] initWithString:@"spectre:"]];
if (voltoInstalled) { if (spectreInstalled) {
self.voltoInstallAlert.visible = NO; self.spectreInstallAlert.visible = NO;
self.voltoMigrateAlert.visible = YES; self.spectreMigrateAlert.visible = YES;
} }
else { else {
self.voltoInstallAlert.visible = NO; self.spectreInstallAlert.visible = [MPiOSAppDelegate get].spectreViewController != nil;
self.voltoMigrateAlert.visible = NO; self.spectreMigrateAlert.visible = NO;
self.voltoViewController = [SKStoreProductViewController new];
self.voltoViewController.delegate = self;
[self.voltoViewController loadProductWithParameters:@{
SKStoreProductParameterCampaignToken : @"app-masterpassword.ios", /* Campaign: From MasterPassword iOS */
SKStoreProductParameterProviderToken : @153897, /* Provider: Maarten Billemont */
SKStoreProductParameterITunesItemIdentifier: @510296984, /* Application: MasterPassword iOS */
//SKStoreProductParameterITunesItemIdentifier: @1500430196, /* Application: Volto iOS */
} completionBlock:^(BOOL result, NSError *error) {
if (error)
err( @"Failed loading Volto product information: %@", error );
[UIView animateWithDuration:0.3f animations:^{
self.voltoInstallAlert.visible = result;
}];
}];
} }
} }

View File

@@ -18,10 +18,13 @@
#import "MPStoreViewController.h" #import "MPStoreViewController.h"
#import "MPiOSAppDelegate.h" #import "MPiOSAppDelegate.h"
#import "UIColor+Expanded.h"
#import "MPAppDelegate_InApp.h" #import "MPAppDelegate_InApp.h"
#import "MPSitesViewController.h" #import "MPSitesViewController.h"
MP_LIBS_BEGIN
#import "UIColor+Expanded.h"
MP_LIBS_END
PearlEnum( MPDevelopmentFuelConsumption, PearlEnum( MPDevelopmentFuelConsumption,
MPDevelopmentFuelConsumptionQuarterly, MPDevelopmentFuelConsumptionMonthly, MPDevelopmentFuelWeekly ); MPDevelopmentFuelConsumptionQuarterly, MPDevelopmentFuelConsumptionMonthly, MPDevelopmentFuelWeekly );

View File

@@ -48,7 +48,7 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
MPActiveUserStateMinimized, MPActiveUserStateMinimized,
}; };
@interface MPUsersViewController() @interface MPUsersViewController()<NSFetchedResultsControllerDelegate>
@property(nonatomic) MPActiveUserState activeUserState; @property(nonatomic) MPActiveUserState activeUserState;
@property(nonatomic, strong) NSArray *userIDs; @property(nonatomic, strong) NSArray *userIDs;
@@ -57,7 +57,7 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
@property(nonatomic) NSUInteger marqueeTipTextIndex; @property(nonatomic) NSUInteger marqueeTipTextIndex;
@property(nonatomic, copy) NSString *masterPasswordChoice; @property(nonatomic, copy) NSString *masterPasswordChoice;
@property(nonatomic, strong) NSOperationQueue *afterUpdates; @property(nonatomic, strong) NSOperationQueue *afterUpdates;
@property(nonatomic, weak) id contextChangedObserver; @property(nonatomic, strong) NSFetchedResultsController *userResultsController;
@end @end
@@ -91,6 +91,7 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
[super viewWillAppear:animated]; [super viewWillAppear:animated];
self.userSelectionContainer.visible = NO; self.userSelectionContainer.visible = NO;
[self.storeLoadingActivity startAnimating];
} }
- (void)viewDidAppear:(BOOL)animated { - (void)viewDidAppear:(BOOL)animated {
@@ -231,13 +232,11 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
user.avatar = newUserAvatar; user.avatar = newUserAvatar;
user.name = newUserName; user.name = newUserName;
if ([[MPConfig get].sendInfo boolValue]) {
[Countly.sharedInstance recordEvent:@"new-user" segmentation:@{ [Countly.sharedInstance recordEvent:@"new-user" segmentation:@{
@"algorithm": @(user.algorithm.version).description, @"algorithm": @(user.algorithm.version).description,
@"avatar" : @(user.avatar).description, @"avatar" : @(user.avatar).description,
}]; }];
} }
}
BOOL signedIn = [[MPiOSAppDelegate get] signInAsUser:user saveInContext:context BOOL signedIn = [[MPiOSAppDelegate get] signInAsUser:user saveInContext:context
usingMasterPassword:masterPassword]; usingMasterPassword:masterPassword];
@@ -434,6 +433,7 @@ referenceSizeForFooterInSection:(NSInteger)section {
NSManagedObjectID *userID = user.permanentObjectID; NSManagedObjectID *userID = user.permanentObjectID;
UIAlertController *controller = [UIAlertController alertControllerWithTitle:user.name message:nil preferredStyle:UIAlertControllerStyleActionSheet]; UIAlertController *controller = [UIAlertController alertControllerWithTitle:user.name message:nil preferredStyle:UIAlertControllerStyleActionSheet];
[controller.popoverPresentationController setSourceView:avatarCell];
[controller addAction:[UIAlertAction actionWithTitle:@"Delete User" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) { [controller addAction:[UIAlertAction actionWithTitle:@"Delete User" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
UIAlertController *controller_ = [UIAlertController alertControllerWithTitle:@"Deleting User" message: UIAlertController *controller_ = [UIAlertController alertControllerWithTitle:@"Deleting User" message:
@"The user and its sites will be deleted." preferredStyle:UIAlertControllerStyleAlert]; @"The user and its sites will be deleted." preferredStyle:UIAlertControllerStyleAlert];
@@ -643,7 +643,6 @@ referenceSizeForFooterInSection:(NSInteger)section {
[self removeKeyPathObservers]; [self removeKeyPathObservers];
PearlRemoveNotificationObservers(); PearlRemoveNotificationObservers();
[[NSNotificationCenter defaultCenter] removeObserver:self.contextChangedObserver];
} }
- (void)registerObservers { - (void)registerObservers {
@@ -675,24 +674,6 @@ referenceSizeForFooterInSection:(NSInteger)section {
[self.keyboardHeightConstraint updateConstant:keyboardHeight]; [self.keyboardHeightConstraint updateConstant:keyboardHeight];
} ); } );
if ((self.contextChangedObserver
= [[MPiOSAppDelegate get] managedObjectContextChanged:^(NSDictionary<NSManagedObjectID *, NSString *> *affectedObjects) {
if ([[[affectedObjects allKeys] filteredArrayUsingPredicate:
[NSPredicate predicateWithBlock:^BOOL(NSManagedObjectID *objectID, NSDictionary *bindings) {
return [objectID.entity.name isEqualToString:NSStringFromClass( [MPUserEntity class] )];
}]] count])
[self reloadUsers];
}]))
[UIView animateWithDuration:0.3f animations:^{
self.avatarCollectionView.visible = YES;
[self.storeLoadingActivity stopAnimating];
}];
else
[UIView animateWithDuration:0.3f animations:^{
self.avatarCollectionView.visible = NO;
[self.storeLoadingActivity startAnimating];
}];
PearlAddNotificationObserver( NSPersistentStoreCoordinatorStoresWillChangeNotification, [MPiOSAppDelegate get].storeCoordinator, nil, PearlAddNotificationObserver( NSPersistentStoreCoordinatorStoresWillChangeNotification, [MPiOSAppDelegate get].storeCoordinator, nil,
^(MPUsersViewController *self, NSNotification *note) { ^(MPUsersViewController *self, NSNotification *note) {
self.userIDs = nil; self.userIDs = nil;
@@ -704,32 +685,54 @@ referenceSizeForFooterInSection:(NSInteger)section {
[self reloadUsers]; [self reloadUsers];
} ); } );
} ); } );
[UIView animateWithDuration:0.3f animations:^{
self.avatarCollectionView.visible = YES;
[self.storeLoadingActivity stopAnimating];
}];
} }
- (void)reloadUsers { - (void)reloadUsers {
[self afterUpdatesMainQueue:^{ [self afterUpdatesMainQueue:^{
if (![MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) { if (![MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) {
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]; self.userResultsController = [[NSFetchedResultsController alloc]
if (!users) { initWithFetchRequest:fetchRequest managedObjectContext:mainContext
sectionNameKeyPath:nil cacheName:nil];
self.userResultsController.delegate = self;
NSError *error = nil;
if (![self.userResultsController performFetch:&error])
MPError( error, @"Failed to load users." ); MPError( error, @"Failed to load users." );
[self updateUsers];
}])
self.userIDs = nil; self.userIDs = nil;
}];
} }
- (void)updateUsers {
[self.userResultsController.managedObjectContext performBlock:^{
NSArray *users = self.userResultsController.fetchedObjects;
NSMutableArray *userIDs = [NSMutableArray arrayWithCapacity:[users count]]; NSMutableArray *userIDs = [NSMutableArray arrayWithCapacity:[users count]];
for (MPUserEntity *user in users) for (MPUserEntity *user in users)
[userIDs addObject:user.permanentObjectID]; [userIDs addObject:user.permanentObjectID];
self.userIDs = userIDs; self.userIDs = userIDs;
}])
self.userIDs = nil;
}]; }];
} }
#pragma mark - NSFetchedResultsControllerDelegate
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self updateUsers];
}
#pragma mark - Properties #pragma mark - Properties
- (void)setActive:(BOOL)active animated:(BOOL)animated { - (void)setActive:(BOOL)active animated:(BOOL)animated {

View File

@@ -17,10 +17,12 @@
//============================================================================== //==============================================================================
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import <WebKit/WebKit.h>
@interface MPWebViewController : UIViewController<UIWebViewDelegate> @interface MPWebViewController : UIViewController<WKNavigationDelegate>
@property(nonatomic) IBOutlet UIWebView *webView; @property(nonatomic) IBOutlet WKWebView *webView;
@property(nonatomic) IBOutlet UINavigationBar *webNavigationBar;
@property(nonatomic) IBOutlet UINavigationItem *webNavigationItem; @property(nonatomic) IBOutlet UINavigationItem *webNavigationItem;
@property(nonatomic) NSURL *initialURL; @property(nonatomic) NSURL *initialURL;

View File

@@ -17,6 +17,7 @@
//============================================================================== //==============================================================================
#import "MPWebViewController.h" #import "MPWebViewController.h"
#import "MPiOSAppDelegate.h"
@implementation MPWebViewController @implementation MPWebViewController
@@ -24,20 +25,30 @@
[super viewDidLoad]; [super viewDidLoad];
[self.webView.scrollView insetOcclusion]; if (!self.initialURL)
self.initialURL = [NSURL URLWithString:@"https://masterpassword.app"];
self.webNavigationItem.title = self.initialURL.host;
// WKWebView can't be on the storyboard for iOS pre 11 due to an NSCoding bug.
[self.view insertSubview:self.webView = [WKWebView new] atIndex:0];
[self.webView setNavigationDelegate:self];
[self.webView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.webView.topAnchor constraintEqualToAnchor:self.view.topAnchor].active = YES;
[self.webView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor].active = YES;
[self.webView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor].active = YES;
[self.webView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor].active = YES;
[self.webView loadRequest:[[NSURLRequest alloc] initWithURL:self.initialURL]];
} }
- (void)viewWillAppear:(BOOL)animated { - (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated]; [super viewWillAppear:animated];
}
if (!self.initialURL) - (void)viewDidLayoutSubviews {
self.initialURL = [NSURL URLWithString:@"https://masterpassword.app"]; [self.webView.scrollView insetOcclusion];
[super viewDidLayoutSubviews];
self.webNavigationItem.title = self.initialURL.host;
self.webView.visible = NO;
[self.webView loadRequest:[[NSURLRequest alloc] initWithURL:self.initialURL]];
} }
- (UIStatusBarStyle)preferredStatusBarStyle { - (UIStatusBarStyle)preferredStatusBarStyle {
@@ -45,26 +56,24 @@
return UIStatusBarStyleLightContent; return UIStatusBarStyleLightContent;
} }
#pragma mark - UIWebViewDelegate #pragma mark - WKNavigationDelegate
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
navigationType:(UIWebViewNavigationType)navigationType { decisionHandler:(void ( ^ )(WKNavigationActionPolicy))decisionHandler {
if ([[request.URL absoluteString] rangeOfString:@"thanks.lhunath.com"].location != NSNotFound) { if ([navigationAction.request.mainDocumentURL.scheme isEqualToString:@"masterpassword"]) {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"tipped.thanks"]; [[MPiOSAppDelegate get] openURL:navigationAction.request.mainDocumentURL];
if (![[NSUserDefaults standardUserDefaults] synchronize]) decisionHandler( WKNavigationActionPolicyCancel );
wrn( @"Couldn't synchronize thanks tip." ); return;
} }
if ([request.URL isEqual:request.mainDocumentURL]) { decisionHandler( WKNavigationActionPolicyAllow );
self.webNavigationItem.title = request.URL.host; }
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
self.webNavigationItem.title = webView.URL.host;
self.webNavigationItem.prompt = strl( @"Loading" ); self.webNavigationItem.prompt = strl( @"Loading" );
}
return YES;
}
- (void)webViewDidStartLoad:(UIWebView *)webView {
UIActivityIndicatorView *activityView = UIActivityIndicatorView *activityView =
[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite]; [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
@@ -72,19 +81,57 @@
[activityView startAnimating]; [activityView startAnimating];
} }
- (void)webViewDidFinishLoad:(UIWebView *)webView { - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
[UIView animateWithDuration:0.3 animations:^{ if ([[webView.URL absoluteString] rangeOfString:@"thanks.lhunath.com"].location != NSNotFound) {
self.webView.visible = YES; [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"tipped.thanks"];
if (![[NSUserDefaults standardUserDefaults] synchronize])
wrn( @"Couldn't synchronize thanks tip." );
}
[self.webNavigationItem setLeftBarButtonItem:[[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:@selector( action: )]];
[webView evaluateJavaScript:@"document.title" completionHandler:^(id o, NSError *error) {
self.webNavigationItem.prompt = [o description];
}]; }];
[self.webNavigationItem setLeftBarButtonItem:[webView canGoBack]? [[UIBarButtonItem alloc]
initWithTitle:@"⬅︎" style:UIBarButtonItemStylePlain target:webView action:@selector( goBack )]: nil];
self.webNavigationItem.prompt = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
} }
#pragma mark - Actions #pragma mark - Actions
- (IBAction)action:(UIBarButtonItem *)sender {
UIAlertController *controller = [UIAlertController alertControllerWithTitle:self.webView.URL.host
message:self.webView.URL.absoluteString
preferredStyle:UIAlertControllerStyleActionSheet];
[controller.popoverPresentationController setBarButtonItem:sender];
[controller addAction:[UIAlertAction actionWithTitle:@"Safari" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[UIApp openURL:self.webView.URL];
}]];
if ([UIApp canOpenURL:[NSURL URLWithString:@"firefox:"]]) {
[controller addAction:[UIAlertAction actionWithTitle:@"Firefox" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[UIApp openURL:[NSURL URLWithString:strf( @"firefox://open-url?url=%@",
[self.webView.URL.absoluteString stringByAddingPercentEncodingWithAllowedCharacters:
[NSCharacterSet URLQueryAllowedCharacterSet]] )]];
}]];
}
if ([UIApp canOpenURL:[NSURL URLWithString:@"googlechrome:"]]) {
NSURL *url = [[NSURL alloc] initWithScheme:[self.webView.URL.scheme isEqualToString:@"http"]? @"googlechrome": @"googlechromes"
host:self.webView.URL.host path:self.webView.URL.path];
[controller addAction:[UIAlertAction actionWithTitle:@"Chrome" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[UIApp openURL:url];
}]];
}
if ([UIApp canOpenURL:[NSURL URLWithString:@"opera-http:"]]) {
NSURL *url = [[NSURL alloc] initWithScheme:[self.webView.URL.scheme isEqualToString:@"http"]? @"opera-http": @"opera-https"
host:self.webView.URL.host path:self.webView.URL.path];
[controller addAction:[UIAlertAction actionWithTitle:@"Opera" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[UIApp openURL:url];
}]];
}
[controller addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:controller animated:YES completion:nil];
}
- (IBAction)done:(id)sender { - (IBAction)done:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil]; [self dismissViewControllerAnimated:YES completion:nil];

View File

@@ -17,15 +17,23 @@
//============================================================================== //==============================================================================
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import <StoreKit/StoreKit.h>
#import "MPAppDelegate_Shared.h" #import "MPAppDelegate_Shared.h"
@interface MPiOSAppDelegate : MPAppDelegate_Shared @interface MPiOSAppDelegate : MPAppDelegate_Shared <SKStoreProductViewControllerDelegate>
@property(nonatomic, strong) UIWindow *window;
@property(nonatomic, strong) SKStoreProductViewController *spectreViewController;
- (void)openURL:(NSURL *)url;
- (void)showFeedbackWithLogs:(BOOL)logs forVC:(UIViewController *)viewController; - (void)showFeedbackWithLogs:(BOOL)logs forVC:(UIViewController *)viewController;
- (void)openFeedbackWithLogs:(BOOL)logs forVC:(UIViewController *)viewController; - (void)openFeedbackWithLogs:(BOOL)logs forVC:(UIViewController *)viewController;
- (void)showExportForVC:(UIViewController *)viewController; - (void)showExportForVC:(UIViewController *)viewController;
- (void)migrateFor:(MPUserEntity *)user;
- (void)changeMasterPasswordFor:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc didResetBlock:(void ( ^ )(void))didReset; - (void)changeMasterPasswordFor:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc didResetBlock:(void ( ^ )(void))didReset;
@end @end

View File

@@ -23,8 +23,24 @@
#import "mpw-marshal.h" #import "mpw-marshal.h"
#import "MPSecrets.h" #import "MPSecrets.h"
MP_LIBS_BEGIN
#import <Sentry/Sentry.h> #import <Sentry/Sentry.h>
#import <Countly/Countly.h> #import <Countly/Countly.h>
MP_LIBS_END
@interface CountlyPushNotifications
@end
@interface CountlyPushNotifications(MPNotifications)
@end
@implementation CountlyPushNotifications(MPNotifications)
- (void)openURL:(NSString *)URLString {
[[MPiOSAppDelegate get].window.rootViewController performSegueWithIdentifier:@"web" sender:[NSURL URLWithString:URLString]];
}
@end
@interface MPiOSAppDelegate()<UIDocumentInteractionControllerDelegate> @interface MPiOSAppDelegate()<UIDocumentInteractionControllerDelegate>
@@ -34,6 +50,8 @@
@implementation MPiOSAppDelegate @implementation MPiOSAppDelegate
@synthesize window;
+ (void)initialize { + (void)initialize {
[MPiOSConfig get]; [MPiOSConfig get];
@@ -43,10 +61,10 @@
@try { @try {
// Sentry // Sentry
[SentrySDK initWithOptions:@{ [SentrySDK startWithOptions:@{
@"dsn" : NilToNSNull( decrypt( sentryDSN ) ), @"dsn" : NilToNSNull( decrypt( sentryDSN ) ),
#ifdef DEBUG #ifdef DEBUG
@"debug" : @(YES), @"debug" : @(NO), //@(YES),
@"environment" : @"Development", @"environment" : @"Development",
#elif PUBLIC #elif PUBLIC
@"debug" : @(NO), @"debug" : @(NO),
@@ -55,7 +73,7 @@
@"debug" : @(NO), @"debug" : @(NO),
@"environment" : @"Private", @"environment" : @"Private",
#endif #endif
@"enabled" : [MPiOSConfig get].sendInfo, @"enabled" : @([[MPiOSConfig get].sendInfo boolValue] || ![[MPiOSConfig get].sendInfoDecided boolValue]),
@"enableAutoSessionTracking": @(YES), @"enableAutoSessionTracking": @(YES),
}]; }];
[[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) { [[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) {
@@ -106,7 +124,7 @@
countlyConfig.deviceID = [PearlKeyChain deviceIdentifier]; countlyConfig.deviceID = [PearlKeyChain deviceIdentifier];
countlyConfig.secretSalt = decrypt( countlySalt ); countlyConfig.secretSalt = decrypt( countlySalt );
#if DEBUG #if DEBUG
countlyConfig.enableDebug = YES; //countlyConfig.enableDebug = YES;
countlyConfig.pushTestMode = CLYPushTestModeDevelopment; countlyConfig.pushTestMode = CLYPushTestModeDevelopment;
#elif ! PUBLIC #elif ! PUBLIC
countlyConfig.enableDebug = NO; countlyConfig.enableDebug = NO;
@@ -136,12 +154,6 @@
@catch (id exception) { @catch (id exception) {
err( @"During Config Test: %@", exception ); err( @"During Config Test: %@", exception );
} }
@try {
[super application:application didFinishLaunchingWithOptions:launchOptions];
}
@catch (id exception) {
err( @"During Pearl Application Launch: %@", exception );
}
@try { @try {
inf( @"Started up with device identifier: %@", [PearlKeyChain deviceIdentifier] ); inf( @"Started up with device identifier: %@", [PearlKeyChain deviceIdentifier] );
@@ -172,11 +184,29 @@
} }
} ); } );
SKStoreProductViewController *migrateVC = [SKStoreProductViewController new];
[migrateVC loadProductWithParameters:@{
SKStoreProductParameterCampaignToken : @"app-masterpassword.ios", /* Campaign: From MasterPassword iOS */
SKStoreProductParameterProviderToken : @153897, /* Provider: Maarten Billemont */
// SKStoreProductParameterITunesItemIdentifier: @510296984, /* Application: MasterPassword iOS */
SKStoreProductParameterITunesItemIdentifier: @1500430196, /* Application: Spectre iOS */
} completionBlock:^(BOOL result, NSError *error) {
if (error)
err( @"Failed loading Spectre product information: %@", error );
if (result) {
self.spectreViewController = migrateVC;
self.spectreViewController.delegate = self;
} else {
self.spectreViewController = nil;
}
}];
PearlMainQueueOperation( ^{ PearlMainQueueOperation( ^{
if ([[MPiOSConfig get].showSetup boolValue]) if ([[MPiOSConfig get].showSetup boolValue])
[self.navigationController performSegueWithIdentifier:@"setup" sender:self]; [self.window.rootViewController performSegueWithIdentifier:@"setup" sender:self];
[self enableNotifications]; [self consentFeatures];
} ); } );
} }
@catch (id exception) { @catch (id exception) {
@@ -193,6 +223,12 @@
if (!url) if (!url)
return NO; return NO;
// masterpassword: URLs.
if ([url.scheme isEqualToString:@"masterpassword"]) {
[self openURL:url];
return YES;
}
// Arbitrary URL to mpsites data. // Arbitrary URL to mpsites data.
[[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler: [[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:
^(NSData *importedSitesData, NSURLResponse *response, NSError *error) { ^(NSData *importedSitesData, NSURLResponse *response, NSError *error) {
@@ -206,7 +242,7 @@
(id)[error localizedDescription]?: error ) (id)[error localizedDescription]?: error )
preferredStyle:UIAlertControllerStyleAlert]; preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleCancel handler:nil]]; [alert addAction:[UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:alert animated:YES completion:nil]; [self.window.rootViewController presentViewController:alert animated:YES completion:nil];
} ); } );
return; return;
} }
@@ -218,7 +254,7 @@
@"Master Password couldn't understand the import file." @"Master Password couldn't understand the import file."
preferredStyle:UIAlertControllerStyleAlert]; preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleCancel handler:nil]]; [alert addAction:[UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:alert animated:YES completion:nil]; [self.window.rootViewController presentViewController:alert animated:YES completion:nil];
} ); } );
return; return;
} }
@@ -229,7 +265,42 @@
return YES; return YES;
} }
- (void)enableNotifications { - (void)consentFeatures {
if ([self askDiagnostics])
return;
[self tryNotifications];
}
- (BOOL)askDiagnostics {
if ([[MPiOSConfig get].sendInfoDecided boolValue])
return NO;
PearlMainQueue( ^{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Diagnostics" message:
@"We look for bugs, sudden crashes, runtime issues & statistics.\n\n"
@"Diagnostics are scrubbed and personal details will never leave your device."
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Disable" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
[MPiOSConfig get].sendInfo = @(NO);
[MPiOSConfig get].sendInfoDecided = @(YES);
[self consentFeatures];
}]];
[alert addAction:[UIAlertAction actionWithTitle:@"Engage" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[MPiOSConfig get].sendInfo = @(YES);
[MPiOSConfig get].sendInfoDecided = @(YES);
[self consentFeatures];
}]];
[self.window.rootViewController presentViewController:alert animated:YES completion:nil];
} );
return YES;
}
- (void)tryNotifications {
[Countly.sharedInstance giveConsentForFeature:CLYConsentPushNotifications]; [Countly.sharedInstance giveConsentForFeature:CLYConsentPushNotifications];
if (@available( iOS 12, * )) { if (@available( iOS 12, * )) {
@@ -240,35 +311,35 @@
[self askNotifications]; [self askNotifications];
}]; }];
return;
} }
else {
[self askNotifications]; [self askNotifications];
} }
}
- (void)askNotifications { - (void)askNotifications {
if ([[MPiOSConfig get].notificationsDecided boolValue])
return;
PearlMainQueue( ^{ PearlMainQueue( ^{
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"notificationsDecided"]) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Coming Soon" message: UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Coming Soon" message:
@"Master Password is rolling out a new modern personal security platform and we're excited to bring you along.\n\n" @"Master Password is rolling out a brand new, updated version and we're excited to bring you along.\n\n"
@"When it's time, we'll send you a notification to help you make an effortless transition." @"When it's time, we'll send you a notification to help you make an effortless transition."
preferredStyle:UIAlertControllerStyleAlert]; preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Thanks" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { [alert addAction:[UIAlertAction actionWithTitle:@"Thanks" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
if (@available( iOS 12, * )) { if (@available( iOS 12, * )) {
[Countly.sharedInstance askForNotificationPermissionWithOptions:UNAuthorizationOptionAlert completionHandler: [Countly.sharedInstance askForNotificationPermissionWithOptions:UNAuthorizationOptionAlert completionHandler:
^(BOOL granted, NSError *error) { ^(BOOL granted, NSError *error) {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"notificationsDecided"]; [MPiOSConfig get].notificationsDecided = @(YES);
}]; }];
} }
else { else {
[Countly.sharedInstance askForNotificationPermission]; [Countly.sharedInstance askForNotificationPermission];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"notificationsDecided"]; [MPiOSConfig get].notificationsDecided = @(YES);
} }
}]]; }]];
[(self.navigationController.presentedViewController?: (UIViewController *)self.navigationController) [self.window.rootViewController presentViewController:alert animated:YES completion:nil];
presentViewController:alert animated:YES completion:nil];
}
} ); } );
} }
@@ -297,7 +368,7 @@
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) { [alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
setResult( nil ); setResult( nil );
}]]; }]];
[self.navigationController presentViewController:alert animated:YES completion:nil]; [self.window.rootViewController presentViewController:alert animated:YES completion:nil];
} ); } );
} ); } );
} askUserPassword:^NSString *(NSString *userName) { } askUserPassword:^NSString *(NSString *userName) {
@@ -315,7 +386,7 @@
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) { [alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
setResult( nil ); setResult( nil );
}]]; }]];
[self.navigationController presentViewController:alert animated:YES completion:nil]; [self.window.rootViewController presentViewController:alert animated:YES completion:nil];
} ); } );
} ); } );
} result:^(NSError *error) { } result:^(NSError *error) {
@@ -326,7 +397,7 @@
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Error" message:[error localizedDescription] UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Error" message:[error localizedDescription]
preferredStyle:UIAlertControllerStyleAlert]; preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleCancel handler:nil]]; [controller addAction:[UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:controller animated:YES completion:nil]; [self.window.rootViewController presentViewController:controller animated:YES completion:nil];
} }
} ); } );
}]; }];
@@ -336,15 +407,7 @@
inf( @"Will foreground" ); inf( @"Will foreground" );
[super applicationWillEnterForeground:application];
[self.hangDetector start]; [self.hangDetector start];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
inf( @"Re-activated" );
[[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:nil];
PearlNotMainQueue( ^{ PearlNotMainQueue( ^{
NSString *importData = [UIPasteboard generalPasteboard].string; NSString *importData = [UIPasteboard generalPasteboard].string;
@@ -360,20 +423,22 @@
[UIPasteboard generalPasteboard].string = @""; [UIPasteboard generalPasteboard].string = @"";
}]]; }]];
[alert addAction:[UIAlertAction actionWithTitle:@"No" style:UIAlertActionStyleCancel handler:nil]]; [alert addAction:[UIAlertAction actionWithTitle:@"No" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:alert animated:YES completion:nil]; [self.window.rootViewController presentViewController:alert animated:YES completion:nil];
} ); } );
} }
mpw_marshal_file_free( &importFile ); mpw_marshal_file_free( &importFile );
} ); } );
}
[super applicationDidBecomeActive:application]; - (void)applicationDidBecomeActive:(UIApplication *)application {
inf( @"Re-activated" );
[[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:nil];
} }
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application { - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
inf( @"Received memory warning." ); inf( @"Received memory warning." );
[super applicationDidReceiveMemoryWarning:application];
} }
- (void)applicationDidEnterBackground:(UIApplication *)application { - (void)applicationDidEnterBackground:(UIApplication *)application {
@@ -386,12 +451,46 @@
} }
[self.hangDetector stop]; [self.hangDetector stop];
[super applicationDidEnterBackground:application];
} }
#pragma mark - Behavior #pragma mark - Behavior
- (void)openURL:(NSURL *)url {
if ([url.scheme isEqualToString:@"masterpassword"]) {
if ([url.host isEqualToString:@"open-url"]) {
for (NSURLQueryItem *item in [NSURLComponents componentsWithString:[url absoluteString]].queryItems)
if ([item.name isEqualToString:@"url"]) {
[UIApp openURL:[NSURL URLWithString:item.value]];
return;
}
}
else if ([url.host isEqualToString:@"show-url"]) {
for (NSURLQueryItem *item in [NSURLComponents componentsWithString:[url absoluteString]].queryItems)
if ([item.name isEqualToString:@"url"]) {
[self.window.rootViewController performSegueWithIdentifier:@"web" sender:[NSURL URLWithString:item.value]];
return;
}
}
else if ([url.host isEqualToString:@"migrate"]) {
for (NSURLQueryItem *item in [NSURLComponents componentsWithString:[url absoluteString]].queryItems)
if ([item.name isEqualToString:@"fullName"]) {
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
NSFetchRequest
*fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", item.value];
NSArray *users = [context executeFetchRequest:fetchRequest error:nil];
[self migrateFor:users.firstObject];
}];
return;
}
[self migrateFor:nil];
return;
}
} else
[UIApp openURL:url];
}
- (void)showFeedbackWithLogs:(BOOL)logs forVC:(UIViewController *)viewController { - (void)showFeedbackWithLogs:(BOOL)logs forVC:(UIViewController *)viewController {
if (![PearlEMail canSendMail]) { if (![PearlEMail canSendMail]) {
@@ -401,7 +500,7 @@
@"help@masterpassword.app" @"help@masterpassword.app"
preferredStyle:UIAlertControllerStyleAlert]; preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]]; [alert addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:alert animated:YES completion:nil]; [self.window.rootViewController presentViewController:alert animated:YES completion:nil];
} }
else if (logs) { else if (logs) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Feedback" message: UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Feedback" message:
@@ -415,7 +514,7 @@
[alert addAction:[UIAlertAction actionWithTitle:@"No Logs" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { [alert addAction:[UIAlertAction actionWithTitle:@"No Logs" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self openFeedbackWithLogs:NO forVC:viewController]; [self openFeedbackWithLogs:NO forVC:viewController];
}]]; }]];
[self.navigationController presentViewController:alert animated:YES completion:nil]; [self.window.rootViewController presentViewController:alert animated:YES completion:nil];
} }
else else
[self openFeedbackWithLogs:NO forVC:viewController]; [self openFeedbackWithLogs:NO forVC:viewController];
@@ -461,15 +560,16 @@
@"This may be due to corruption. You can either reset Master Password and " @"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." @"recreate your user, or E-Mail us your logs and leave your corrupt store as-is for now."
preferredStyle:UIAlertControllerStyleAlert]; preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"E-Mail Logs" style:UIAlertActionStyleDefault [alert addAction:[UIAlertAction actionWithTitle:@"Try Again" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
handler:^(UIAlertAction *action) { [self retryCorruptStore];
}]];
[alert addAction:[UIAlertAction actionWithTitle:@"Send Logs" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self openFeedbackWithLogs:YES forVC:nil]; [self openFeedbackWithLogs:YES forVC:nil];
}]]; }]];
[alert addAction:[UIAlertAction actionWithTitle:@"Reset" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { [alert addAction:[UIAlertAction actionWithTitle:@"Reset" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self deleteAndResetStore]; [self deleteAndResetStore];
}]]; }]];
[alert addAction:[UIAlertAction actionWithTitle:@"Ignore" style:UIAlertActionStyleCancel handler:nil]]; [self.window.rootViewController presentViewController:alert animated:YES completion:nil];
[self.navigationController presentViewController:alert animated:YES completion:nil];
} ); } );
} ); } );
} }
@@ -486,7 +586,7 @@
@"Would you like to make all your passwords visible in the export file?\n\n" @"Would you like to make all your passwords visible in the export file?\n\n"
@"A safe export will include all sites but make their passwords invisible.\n" @"A safe export will include all sites but make their passwords invisible.\n"
@"It is great as a backup and remains safe when fallen in the wrong hands." @"It is great as a backup and remains safe when fallen in the wrong hands."
preferredStyle:UIAlertControllerStyleActionSheet]; preferredStyle:UIAlertControllerStyleAlert];
[sheet addAction:[UIAlertAction actionWithTitle:@"Safe Export" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { [sheet addAction:[UIAlertAction actionWithTitle:@"Safe Export" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self showExportRevealPasswords:NO forVC:viewController]; [self showExportRevealPasswords:NO forVC:viewController];
}]]; }]];
@@ -494,10 +594,10 @@
[self showExportRevealPasswords:YES forVC:viewController]; [self showExportRevealPasswords:YES forVC:viewController];
}]]; }]];
[sheet addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]]; [sheet addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:sheet animated:YES completion:nil]; [self.window.rootViewController presentViewController:sheet animated:YES completion:nil];
}]]; }]];
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]]; [alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:alert animated:YES completion:nil]; [self.window.rootViewController presentViewController:alert animated:YES completion:nil];
} }
- (void)showExportRevealPasswords:(BOOL)revealPasswords forVC:(UIViewController *)viewController { - (void)showExportRevealPasswords:(BOOL)revealPasswords forVC:(UIViewController *)viewController {
@@ -508,39 +608,46 @@
@"Close Master Password, go into Settings and add a Mail account." @"Close Master Password, go into Settings and add a Mail account."
preferredStyle:UIAlertControllerStyleAlert]; preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]]; [alert addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:alert animated:YES completion:nil]; [self.window.rootViewController presentViewController:alert animated:YES completion:nil];
return; return;
} }
[self exportSitesRevealPasswords:revealPasswords askExportPassword:^NSString *(NSString *userName) { [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
NSError *error = nil;
NSString *exportedUser = [self exportSitesFor:[self activeUserInContext:context] revealPasswords:revealPasswords askExportPassword:
^NSString *(NSString *userName) {
return PearlAwait( ^(void (^setResult)(id)) { return PearlAwait( ^(void (^setResult)(id)) {
PearlMainQueue( ^{ PearlMainQueue( ^{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:strf( @"Master Password For:\n%@", userName ) UIAlertController *alert = [UIAlertController alertControllerWithTitle:strf(
@"Master Password For:\n%@", userName )
message:@"Enter your master password to export the user." message:@"Enter your master password to export the user."
preferredStyle:UIAlertControllerStyleAlert]; preferredStyle:UIAlertControllerStyleAlert];
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) { [alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.secureTextEntry = YES; textField.secureTextEntry = YES;
}]; }];
[alert addAction:[UIAlertAction actionWithTitle:@"Export" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { [alert addAction:[UIAlertAction actionWithTitle:@"Export" style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
setResult( alert.textFields.firstObject.text ); setResult( alert.textFields.firstObject.text );
}]]; }]];
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) { [alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action) {
setResult( nil ); setResult( nil );
}]]; }]];
[self.navigationController presentViewController:alert animated:YES completion:nil]; [self.window.rootViewController presentViewController:alert animated:YES completion:nil];
} ); } );
} ); } );
} result:^(NSString *exportedUser, NSError *error) { } error:&error];
if (!exportedUser || error) {
MPError( error, @"Failed to export mpsites." );
PearlMainQueue( ^{ PearlMainQueue( ^{
if (error) {
MPError( error, @"Failed to export mpsites." );
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Export Error" message:[error localizedDescription] UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Export Error" message:[error localizedDescription]
preferredStyle:UIAlertControllerStyleAlert]; preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]]; [alert addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:alert animated:YES completion:nil]; [self.window.rootViewController presentViewController:alert animated:YES completion:nil];
} );
return;
} }
if (!exportedUser)
return;
NSDateFormatter *exportDateFormatter = [NSDateFormatter new]; NSDateFormatter *exportDateFormatter = [NSDateFormatter new];
[exportDateFormatter setDateFormat:@"yyyy'-'MM'-'dd"]; [exportDateFormatter setDateFormat:@"yyyy'-'MM'-'dd"];
@@ -548,7 +655,7 @@
[self activeUserForMainThread].name, [exportDateFormatter stringFromDate:[NSDate date]] ); [self activeUserForMainThread].name, [exportDateFormatter stringFromDate:[NSDate date]] );
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Export Destination" message:nil UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Export Destination" message:nil
preferredStyle:UIAlertControllerStyleActionSheet]; preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Send As E-Mail" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { [alert addAction:[UIAlertAction actionWithTitle:@"Send As E-Mail" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
NSString *message; NSString *message;
if (revealPasswords) if (revealPasswords)
@@ -593,10 +700,82 @@
} }
}]]; }]];
[alert addAction:[UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleCancel handler:nil]]; [alert addAction:[UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:alert animated:YES completion:nil]; [self.window.rootViewController presentViewController:alert animated:YES completion:nil];
} );
}]; }];
} }
- (void)migrateFor:(MPUserEntity *)user {
if ([UIApp canOpenURL:[[NSURL alloc] initWithString:@"spectre:"]]) {
if (!user) {
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
NSArray *users = [context executeFetchRequest:fetchRequest error:nil];
if (![users count])
return;
UIAlertController *usersSheet = [UIAlertController alertControllerWithTitle:@"Migrate User"
message:@"Choose a user to migrate out to Spectre."
preferredStyle:UIAlertControllerStyleAlert];
[usersSheet addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
for (MPUserEntity *user_ in users)
[usersSheet addAction:[UIAlertAction actionWithTitle:user_.name style:UIAlertActionStyleDefault handler:
^(UIAlertAction *action) { [self migrateFor:user_]; }]];
PearlMainQueue( ^{
[self.window.rootViewController presentViewController:usersSheet animated:YES completion:nil];
} );
}];
return;
}
[MPAppDelegate_Shared managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
NSError *error = nil;
NSString *exportedUser = [[MPAppDelegate_Shared get] exportSitesFor:[MPUserEntity existingObjectWithID:user.objectID inContext:context]
revealPasswords:NO askExportPassword:^NSString *(NSString *userName) {
return PearlAwait( ^(void (^setResult)(id)) {
PearlMainQueue( ^{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:strf( @"Master Password For:\n%@", userName )
message:@"Enter your master password to export the user."
preferredStyle:UIAlertControllerStyleAlert];
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.secureTextEntry = YES;
}];
[alert addAction:[UIAlertAction actionWithTitle:@"Export" style:UIAlertActionStyleDefault handler:
^(UIAlertAction *action) { setResult( alert.textFields.firstObject.text ); }]];
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:
^(UIAlertAction *action) { setResult( nil ); }]];
[self.window.rootViewController presentViewController:alert animated:YES completion:nil];
} );
} );
} error:&error];
PearlMainQueue( ^{
if (error) {
MPError( error, @"Failed to export user." );
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Export Error"
message:[error localizedDescription]
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]];
[self.window.rootViewController presentViewController:alert animated:YES completion:nil];
}
if (!exportedUser)
return;
NSURLComponents *components = [NSURLComponents new];
components.scheme = @"spectre";
components.path = @"import";
components.queryItems = @[ [[NSURLQueryItem alloc] initWithName:@"data" value:exportedUser] ];
[UIApp openURL:components.URL];
} );
}];
}
else if (self.spectreViewController)
[self.window.rootViewController presentViewController:self.spectreViewController animated:YES completion:nil];
}
- (void)changeMasterPasswordFor:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc didResetBlock:(void ( ^ )(void))didReset { - (void)changeMasterPasswordFor:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc didResetBlock:(void ( ^ )(void))didReset {
PearlMainQueue( ^{ PearlMainQueue( ^{
@@ -605,7 +784,7 @@
@"Changing your master password will cause all your generated passwords to change!\n" @"Changing your master password will cause all your generated passwords to change!\n"
@"Changing the master password back to the old one will cause your passwords to revert as well." @"Changing the master password back to the old one will cause your passwords to revert as well."
preferredStyle:UIAlertControllerStyleAlert]; preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Abort" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { [alert addAction:[UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[moc performBlockAndWait:^{ [moc performBlockAndWait:^{
inf( @"Clearing keyID for user: %@.", user.userID ); inf( @"Clearing keyID for user: %@.", user.userID );
user.keyID = nil; user.keyID = nil;
@@ -618,10 +797,17 @@
didReset(); didReset();
}]]; }]];
[alert addAction:[UIAlertAction actionWithTitle:@"Abort" style:UIAlertActionStyleCancel handler:nil]]; [alert addAction:[UIAlertAction actionWithTitle:@"Abort" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:alert animated:YES completion:nil]; [self.window.rootViewController presentViewController:alert animated:YES completion:nil];
} ); } );
} }
#pragma mark - SKStoreProductViewControllerDelegate
- (void)productViewControllerDidFinish:(SKStoreProductViewController *)viewController {
[viewController dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - UIDocumentInteractionControllerDelegate #pragma mark - UIDocumentInteractionControllerDelegate
- (void)documentInteractionController:(UIDocumentInteractionController *)controller didEndSendingToApplication:(NSString *)application { - (void)documentInteractionController:(UIDocumentInteractionController *)controller didEndSendingToApplication:(NSString *)application {
@@ -645,15 +831,14 @@
NSArray *countlyFeatures = @[ NSArray *countlyFeatures = @[
CLYConsentSessions, CLYConsentEvents, CLYConsentUserDetails, CLYConsentCrashReporting, CLYConsentViewTracking, CLYConsentStarRating CLYConsentSessions, CLYConsentEvents, CLYConsentUserDetails, CLYConsentCrashReporting, CLYConsentViewTracking, CLYConsentStarRating
]; ];
if ([[MPConfig get].sendInfo boolValue]) { if ([[MPiOSConfig get].sendInfo boolValue] || ![[MPiOSConfig get].sendInfoDecided boolValue]) {
[Countly.sharedInstance giveConsentForFeatures:countlyFeatures];
if ([PearlLogger get].printLevel > PearlLogLevelInfo) if ([PearlLogger get].printLevel > PearlLogLevelInfo)
[PearlLogger get].printLevel = PearlLogLevelInfo; [PearlLogger get].printLevel = PearlLogLevelInfo;
[SentrySDK.currentHub getClient].options.enabled = @YES; [SentrySDK.currentHub getClient].options.enabled = @YES;
[SentrySDK configureScope:^(SentryScope *scope) { [SentrySDK configureScope:^(SentryScope *scope) {
[scope setExtraValue:[MPConfig get].rememberLogin forKey:@"rememberLogin"]; [scope setExtraValue:[MPiOSConfig get].rememberLogin forKey:@"rememberLogin"];
[scope setExtraValue:[MPConfig get].sendInfo forKey:@"sendInfo"]; [scope setExtraValue:[MPiOSConfig get].sendInfo forKey:@"sendInfo"];
[scope setExtraValue:[MPiOSConfig get].helpHidden forKey:@"helpHidden"]; [scope setExtraValue:[MPiOSConfig get].helpHidden forKey:@"helpHidden"];
[scope setExtraValue:[MPiOSConfig get].showSetup forKey:@"showQuickStart"]; [scope setExtraValue:[MPiOSConfig get].showSetup forKey:@"showQuickStart"];
[scope setExtraValue:[PearlConfig get].firstRun forKey:@"firstRun"]; [scope setExtraValue:[PearlConfig get].firstRun forKey:@"firstRun"];
@@ -671,10 +856,11 @@
[scope setExtraValue:@(NO) forKey:@"reviewedVersion"]; [scope setExtraValue:@(NO) forKey:@"reviewedVersion"];
#endif #endif
}]; }];
[Countly.sharedInstance giveConsentForFeatures:countlyFeatures];
} }
else { else {
[SentrySDK.currentHub getClient].options.enabled = @NO;
[Countly.sharedInstance cancelConsentForFeatures:countlyFeatures]; [Countly.sharedInstance cancelConsentForFeatures:countlyFeatures];
[SentrySDK.currentHub getClient].options.enabled = @NO;
} }
} }

View File

@@ -33,8 +33,8 @@
NSStringFromSelector( @selector( siteInfoHidden ) ) : @YES, NSStringFromSelector( @selector( siteInfoHidden ) ) : @YES,
NSStringFromSelector( @selector( showSetup ) ) : @YES, NSStringFromSelector( @selector( showSetup ) ) : @YES,
NSStringFromSelector( @selector( appleID ) ) : @"510296984", NSStringFromSelector( @selector( appleID ) ) : @"510296984",
NSStringFromSelector( @selector( actionsTipShown ) ) : @(!self.firstRun), NSStringFromSelector( @selector( actionsTipShown ) ) : @(![self.firstRun boolValue]),
NSStringFromSelector( @selector( typeTipShown ) ) : @(!self.firstRun), NSStringFromSelector( @selector( typeTipShown ) ) : @(![self.firstRun boolValue]),
NSStringFromSelector( @selector( loginNameTipShown ) ): @NO, NSStringFromSelector( @selector( loginNameTipShown ) ): @NO,
NSStringFromSelector( @selector( traceMode ) ) : @NO, NSStringFromSelector( @selector( traceMode ) ) : @NO,
NSStringFromSelector( @selector( dictationSearch ) ) : @NO, NSStringFromSelector( @selector( dictationSearch ) ) : @NO,

View File

@@ -11,9 +11,9 @@
<dict> <dict>
<key>CFBundleTypeIconFiles</key> <key>CFBundleTypeIconFiles</key>
<array> <array>
<string>Icon-Small</string> <string>Icon-Small-40.png</string>
<string>Icon-64.png</string> <string>Icon-Small-40@2x.png</string>
<string>Icon-320.png</string> <string>Icon-Small-40@3x.png</string>
</array> </array>
<key>CFBundleTypeName</key> <key>CFBundleTypeName</key>
<string>Master Password sites</string> <string>Master Password sites</string>
@@ -21,6 +21,7 @@
<string>Alternate</string> <string>Alternate</string>
<key>LSItemContentTypes</key> <key>LSItemContentTypes</key>
<array> <array>
<string>com.lyndir.masterpassword.json</string>
<string>com.lyndir.masterpassword.sites</string> <string>com.lyndir.masterpassword.sites</string>
</array> </array>
</dict> </dict>
@@ -37,8 +38,30 @@
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>[auto]</string> <string>[auto]</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLIconFile</key>
<string>Icon-Small-40@3x</string>
<key>CFBundleURLName</key>
<string>com.lyndir.masterpassword</string>
<key>CFBundleURLSchemes</key>
<array>
<string>masterpassword</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>[auto]</string> <string>[auto]</string>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>firefox</string>
<string>googlechrome</string>
<string>opera-http</string>
<string>spectre</string>
</array>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>LSSupportsOpeningDocumentsInPlace</key> <key>LSSupportsOpeningDocumentsInPlace</key>
@@ -103,6 +126,34 @@
</array> </array>
<key>UTExportedTypeDeclarations</key> <key>UTExportedTypeDeclarations</key>
<array> <array>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeDescription</key>
<string>Master Password JSON export</string>
<key>UTTypeIconFiles</key>
<array>
<string>Icon-Small-40.png</string>
<string>Icon-Small-40@2x.png</string>
<string>Icon-Small-40@3x.png</string>
</array>
<key>UTTypeIdentifier</key>
<string>com.lyndir.masterpassword.json</string>
<key>UTTypeReferenceURL</key>
<string>https://gitlab.com/MasterPassword/MasterPassword/-/wikis/File-Format</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>mpjson</string>
<string>mpsites.json</string>
</array>
<key>public.mime-type</key>
<string>text/plain</string>
</dict>
</dict>
<dict> <dict>
<key>UTTypeConformsTo</key> <key>UTTypeConformsTo</key>
<array> <array>
@@ -112,9 +163,9 @@
<string>Master Password sites</string> <string>Master Password sites</string>
<key>UTTypeIconFiles</key> <key>UTTypeIconFiles</key>
<array> <array>
<string>Icon-Small.png</string> <string>Icon-Small-40.png</string>
<string>Icon-64.png</string> <string>Icon-Small-40@2x.png</string>
<string>Icon-320.png</string> <string>Icon-Small-40@3x.png</string>
</array> </array>
<key>UTTypeIdentifier</key> <key>UTTypeIdentifier</key>
<string>com.lyndir.masterpassword.sites</string> <string>com.lyndir.masterpassword.sites</string>
@@ -123,6 +174,7 @@
<key>public.filename-extension</key> <key>public.filename-extension</key>
<array> <array>
<string>mpsites</string> <string>mpsites</string>
<string>mpsites.txt</string>
</array> </array>
</dict> </dict>
</dict> </dict>

View File

@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES" initialViewController="Q1S-vU-GGO"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17154" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES" initialViewController="Q1S-vU-GGO">
<device id="retina6_1" orientation="portrait" appearance="light"/> <device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16086"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17124"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<customFonts key="customFonts"> <customFonts key="customFonts">
@@ -97,10 +97,10 @@
</constraints> </constraints>
</imageView> </imageView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="0Sa-Vg-EEI" userLabel="Name Backdrop"> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="0Sa-Vg-EEI" userLabel="Name Backdrop">
<rect key="frame" x="43.5" y="263" width="128.5" height="16"/> <rect key="frame" x="43.5" y="263" width="128" height="16"/>
<subviews> <subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalCompressionResistancePriority="1000" text="Maarten Billemont" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cLT-s0-4SQ" userLabel="Name Field"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalCompressionResistancePriority="1000" text="Maarten Billemont" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cLT-s0-4SQ" userLabel="Name Field">
<rect key="frame" x="5" y="0.0" width="118.5" height="16"/> <rect key="frame" x="5" y="0.0" width="118" height="16"/>
<fontDescription key="fontDescription" name="Exo2.0-ExtraBold" family="Exo 2.0" pointSize="13"/> <fontDescription key="fontDescription" name="Exo2.0-ExtraBold" family="Exo 2.0" pointSize="13"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
@@ -164,7 +164,7 @@
<outlet property="delegate" destination="S8q-YF-Kt9" id="det-Eh-phM"/> <outlet property="delegate" destination="S8q-YF-Kt9" id="det-Eh-phM"/>
</connections> </connections>
</collectionView> </collectionView>
<button opaque="NO" alpha="0.69999999999999996" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="9u7-pu-Wtv" userLabel="Previous Avatar"> <button opaque="NO" alpha="0.69999999999999996" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="9u7-pu-Wtv" userLabel="Previous Avatar">
<rect key="frame" x="0.0" y="439.5" width="44" height="53"/> <rect key="frame" x="0.0" y="439.5" width="44" height="53"/>
<constraints> <constraints>
<constraint firstAttribute="width" constant="44" id="Ay6-Jg-c3T"/> <constraint firstAttribute="width" constant="44" id="Ay6-Jg-c3T"/>
@@ -177,7 +177,7 @@
<action selector="changeAvatar:" destination="S8q-YF-Kt9" eventType="touchUpInside" id="lNu-mK-3zD"/> <action selector="changeAvatar:" destination="S8q-YF-Kt9" eventType="touchUpInside" id="lNu-mK-3zD"/>
</connections> </connections>
</button> </button>
<button opaque="NO" alpha="0.69999999999999996" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="fUK-gJ-NRE" userLabel="Next Avatar"> <button opaque="NO" alpha="0.69999999999999996" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="fUK-gJ-NRE" userLabel="Next Avatar">
<rect key="frame" x="370" y="439.5" width="44" height="53"/> <rect key="frame" x="370" y="439.5" width="44" height="53"/>
<constraints> <constraints>
<constraint firstAttribute="width" constant="44" id="oAm-YX-Fx5"/> <constraint firstAttribute="width" constant="44" id="oAm-YX-Fx5"/>
@@ -276,7 +276,7 @@
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="XEP-O3-ayG" userLabel="Footer"> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="XEP-O3-ayG" userLabel="Footer">
<rect key="frame" x="0.0" y="824" width="414" height="72"/> <rect key="frame" x="0.0" y="824" width="414" height="72"/>
<subviews> <subviews>
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="4md-Gp-SLG"> <button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="4md-Gp-SLG">
<rect key="frame" x="20" y="48" width="374" height="24"/> <rect key="frame" x="20" y="48" width="374" height="24"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="10"/> <fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="10"/>
<state key="normal" title="Thanks, lhunath ➚"> <state key="normal" title="Thanks, lhunath ➚">
@@ -288,14 +288,14 @@
</connections> </connections>
</button> </button>
<view userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="069-Pu-yXe" userLabel="Thanks Tip"> <view userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="069-Pu-yXe" userLabel="Thanks Tip">
<rect key="frame" x="91" y="0.0" width="232.5" height="60"/> <rect key="frame" x="90.5" y="0.0" width="233" height="60"/>
<subviews> <subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="tip_basic_black.png" translatesAutoresizingMaskIntoConstraints="NO" id="Z8P-ZK-aS0"> <imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="tip_basic_black.png" translatesAutoresizingMaskIntoConstraints="NO" id="Z8P-ZK-aS0">
<rect key="frame" x="0.0" y="0.0" width="232.5" height="60"/> <rect key="frame" x="0.0" y="0.0" width="233" height="60"/>
<rect key="contentStretch" x="0.15000000000000002" y="0.0" width="0.69999999999999973" height="1"/> <rect key="contentStretch" x="0.15000000000000002" y="0.0" width="0.69999999999999973" height="1"/>
</imageView> </imageView>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Why is Master Password free?" textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="BLV-3x-Q0z"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Why is Master Password free?" textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="BLV-3x-Q0z">
<rect key="frame" x="20" y="11.5" width="192.5" height="17"/> <rect key="frame" x="20" y="11.5" width="193" height="17"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/> <fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
@@ -442,21 +442,12 @@
<outlet property="nextAvatarButton" destination="fUK-gJ-NRE" id="5qo-lK-rSa"/> <outlet property="nextAvatarButton" destination="fUK-gJ-NRE" id="5qo-lK-rSa"/>
<outlet property="preferencesTipContainer" destination="0Um-Ot-hI6" id="Cv8-Bp-ZZs"/> <outlet property="preferencesTipContainer" destination="0Um-Ot-hI6" id="Cv8-Bp-ZZs"/>
<outlet property="previousAvatarButton" destination="9u7-pu-Wtv" id="Hgv-lN-S1n"/> <outlet property="previousAvatarButton" destination="9u7-pu-Wtv" id="Hgv-lN-S1n"/>
<outlet property="searchDisplayController" destination="h98-GT-FoS" id="VvS-JO-rqq"/>
<outlet property="storeLoadingActivity" destination="VDd-oM-ZOO" id="MJ7-2f-e8n"/> <outlet property="storeLoadingActivity" destination="VDd-oM-ZOO" id="MJ7-2f-e8n"/>
<outlet property="thanksTipContainer" destination="069-Pu-yXe" id="wWf-2X-Ryw"/> <outlet property="thanksTipContainer" destination="069-Pu-yXe" id="wWf-2X-Ryw"/>
<outlet property="userSelectionContainer" destination="rWM-08-aab" id="Yme-hX-8P0"/> <outlet property="userSelectionContainer" destination="rWM-08-aab" id="Yme-hX-8P0"/>
</connections> </connections>
</viewController> </viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="8hZ-Tb-wZw" userLabel="First Responder" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="8hZ-Tb-wZw" userLabel="First Responder" sceneMemberID="firstResponder"/>
<searchDisplayController id="h98-GT-FoS">
<connections>
<outlet property="delegate" destination="S8q-YF-Kt9" id="hyY-rf-2x2"/>
<outlet property="searchContentsController" destination="S8q-YF-Kt9" id="2RA-rs-GhH"/>
<outlet property="searchResultsDataSource" destination="S8q-YF-Kt9" id="Cdu-go-UBQ"/>
<outlet property="searchResultsDelegate" destination="S8q-YF-Kt9" id="xxe-xE-sFM"/>
</connections>
</searchDisplayController>
</objects> </objects>
<point key="canvasLocation" x="2041" y="226"/> <point key="canvasLocation" x="2041" y="226"/>
</scene> </scene>
@@ -472,17 +463,11 @@
<rect key="frame" x="0.0" y="0.0" width="414" height="842"/> <rect key="frame" x="0.0" y="0.0" width="414" height="842"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews> <subviews>
<webView contentMode="scaleToFill" scalesPageToFit="YES" suppressesIncrementalRendering="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3aB-Hk-Fgd">
<rect key="frame" x="0.0" y="0.0" width="414" height="842"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<outlet property="delegate" destination="Sd5-eW-Cx2" id="ePX-qz-XXR"/>
</connections>
</webView>
<navigationBar contentMode="scaleToFill" barStyle="black" translatesAutoresizingMaskIntoConstraints="NO" id="03x-KT-JQN"> <navigationBar contentMode="scaleToFill" barStyle="black" translatesAutoresizingMaskIntoConstraints="NO" id="03x-KT-JQN">
<rect key="frame" x="0.0" y="0.0" width="414" height="90"/> <rect key="frame" x="0.0" y="0.0" width="414" height="90"/>
<items> <items>
<navigationItem title="masterpassword.app" prompt="Loading" id="Wpf-6b-UJb"> <navigationItem title="masterpassword.app" prompt="Loading" id="Wpf-6b-UJb">
<barButtonItem key="leftBarButtonItem" systemItem="action" id="d9P-Hd-rdw"/>
<barButtonItem key="rightBarButtonItem" systemItem="done" id="Tbg-c3-qOh"> <barButtonItem key="rightBarButtonItem" systemItem="done" id="Tbg-c3-qOh">
<connections> <connections>
<action selector="done:" destination="Sd5-eW-Cx2" id="cuN-bV-cwl"/> <action selector="done:" destination="Sd5-eW-Cx2" id="cuN-bV-cwl"/>
@@ -494,19 +479,14 @@
</subviews> </subviews>
<color key="backgroundColor" red="0.12549020350000001" green="0.1411764771" blue="0.14901961389999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.12549020350000001" green="0.1411764771" blue="0.14901961389999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstItem="3aB-Hk-Fgd" firstAttribute="top" secondItem="zXV-QC-CZm" secondAttribute="bottom" id="3Od-Xa-Ssc"/>
<constraint firstItem="03x-KT-JQN" firstAttribute="leading" secondItem="2Jz-8H-3nG" secondAttribute="leading" id="BAn-wU-k2A"/> <constraint firstItem="03x-KT-JQN" firstAttribute="leading" secondItem="2Jz-8H-3nG" secondAttribute="leading" id="BAn-wU-k2A"/>
<constraint firstItem="03x-KT-JQN" firstAttribute="top" secondItem="zXV-QC-CZm" secondAttribute="bottom" id="JU6-uj-GA3"/> <constraint firstItem="03x-KT-JQN" firstAttribute="top" secondItem="zXV-QC-CZm" secondAttribute="bottom" id="JU6-uj-GA3"/>
<constraint firstAttribute="bottom" secondItem="3aB-Hk-Fgd" secondAttribute="bottom" id="S01-AA-E0a"/>
<constraint firstAttribute="trailing" secondItem="03x-KT-JQN" secondAttribute="trailing" id="sQs-u3-M3r"/> <constraint firstAttribute="trailing" secondItem="03x-KT-JQN" secondAttribute="trailing" id="sQs-u3-M3r"/>
<constraint firstItem="3aB-Hk-Fgd" firstAttribute="leading" secondItem="2Jz-8H-3nG" secondAttribute="leading" id="w1l-x3-qCi"/>
<constraint firstAttribute="trailing" secondItem="3aB-Hk-Fgd" secondAttribute="trailing" id="xdJ-WF-yw0"/>
</constraints> </constraints>
</view> </view>
<navigationItem key="navigationItem" id="gmy-ym-z4I"/> <navigationItem key="navigationItem" id="gmy-ym-z4I"/>
<connections> <connections>
<outlet property="webNavigationItem" destination="Wpf-6b-UJb" id="8gM-LU-pHk"/> <outlet property="webNavigationItem" destination="Wpf-6b-UJb" id="8gM-LU-pHk"/>
<outlet property="webView" destination="3aB-Hk-Fgd" id="Mie-rI-l4h"/>
</connections> </connections>
</viewController> </viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="hNt-WS-DuE" userLabel="First Responder" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="hNt-WS-DuE" userLabel="First Responder" sceneMemberID="firstResponder"/>
@@ -542,12 +522,23 @@
<view key="view" contentMode="scaleToFill" id="c4P-nn-PjR"> <view key="view" contentMode="scaleToFill" id="c4P-nn-PjR">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/> <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="qca-aL-Un8">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<connections>
<segue destination="Ac5-na-hOV" kind="embed" identifier="root" id="x4h-Hl-KXD"/>
</connections>
</containerView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="qca-aL-Un8" firstAttribute="top" secondItem="c4P-nn-PjR" secondAttribute="top" id="7ix-Oo-ELN"/>
<constraint firstAttribute="bottom" secondItem="qca-aL-Un8" secondAttribute="bottom" id="JRU-fk-cPG"/>
<constraint firstItem="qca-aL-Un8" firstAttribute="leading" secondItem="c4P-nn-PjR" secondAttribute="leading" id="OAO-XC-DQ1"/>
<constraint firstAttribute="trailing" secondItem="qca-aL-Un8" secondAttribute="trailing" id="R9n-bd-GVl"/>
</constraints>
</view> </view>
<navigationItem key="navigationItem" id="V6W-ql-3TD"/> <navigationItem key="navigationItem" id="V6W-ql-3TD"/>
<connections>
<segue destination="Ac5-na-hOV" kind="custom" identifier="root" customClass="MPRootSegue" id="UKS-gd-oD2"/>
</connections>
</viewController> </viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="fQY-fV-sIe" userLabel="First Responder" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="fQY-fV-sIe" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects> </objects>
@@ -568,12 +559,25 @@
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" image="background.png" translatesAutoresizingMaskIntoConstraints="NO" id="Lkg-xn-bce" userLabel="Background"> <imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" image="background.png" translatesAutoresizingMaskIntoConstraints="NO" id="Lkg-xn-bce" userLabel="Background">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/> <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
</imageView> </imageView>
<containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="91I-wN-JQb">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="ignoreTouches" value="YES"/>
</userDefinedRuntimeAttributes>
<connections>
<segue destination="S8q-YF-Kt9" kind="embed" identifier="users" id="GzD-Zv-DzW"/>
</connections>
</containerView>
</subviews> </subviews>
<constraints> <constraints>
<constraint firstItem="91I-wN-JQb" firstAttribute="leading" secondItem="fkJ-D0-yue" secondAttribute="leading" id="E4w-Dw-Vip"/>
<constraint firstItem="Lkg-xn-bce" firstAttribute="top" secondItem="fkJ-D0-yue" secondAttribute="top" id="EIy-Cd-0vW"/> <constraint firstItem="Lkg-xn-bce" firstAttribute="top" secondItem="fkJ-D0-yue" secondAttribute="top" id="EIy-Cd-0vW"/>
<constraint firstItem="91I-wN-JQb" firstAttribute="top" secondItem="fkJ-D0-yue" secondAttribute="top" id="EeB-9X-PEK"/>
<constraint firstAttribute="trailing" secondItem="Lkg-xn-bce" secondAttribute="trailing" id="ROW-fK-z92"/> <constraint firstAttribute="trailing" secondItem="Lkg-xn-bce" secondAttribute="trailing" id="ROW-fK-z92"/>
<constraint firstItem="Lkg-xn-bce" firstAttribute="leading" secondItem="fkJ-D0-yue" secondAttribute="leading" id="UH5-Kk-taJ"/> <constraint firstItem="Lkg-xn-bce" firstAttribute="leading" secondItem="fkJ-D0-yue" secondAttribute="leading" id="UH5-Kk-taJ"/>
<constraint firstAttribute="bottom" secondItem="91I-wN-JQb" secondAttribute="bottom" id="mxv-9s-jxT"/>
<constraint firstAttribute="bottom" secondItem="Lkg-xn-bce" secondAttribute="bottom" id="txR-pf-v3l"/> <constraint firstAttribute="bottom" secondItem="Lkg-xn-bce" secondAttribute="bottom" id="txR-pf-v3l"/>
<constraint firstAttribute="trailing" secondItem="91I-wN-JQb" secondAttribute="trailing" id="uef-ig-RRl"/>
</constraints> </constraints>
</view> </view>
<navigationItem key="navigationItem" id="MPa-zX-Kaq"/> <navigationItem key="navigationItem" id="MPa-zX-Kaq"/>
@@ -581,7 +585,6 @@
<connections> <connections>
<segue destination="osn-5H-SWW" kind="custom" identifier="emergency" customClass="MPOverlaySegue" id="gtX-Cx-AA2"/> <segue destination="osn-5H-SWW" kind="custom" identifier="emergency" customClass="MPOverlaySegue" id="gtX-Cx-AA2"/>
<segue destination="nkY-z6-8jd" kind="custom" identifier="passwords" customClass="MPSitesSegue" id="Ozp-YT-Utx"/> <segue destination="nkY-z6-8jd" kind="custom" identifier="passwords" customClass="MPSitesSegue" id="Ozp-YT-Utx"/>
<segue destination="S8q-YF-Kt9" kind="custom" identifier="users" customClass="MPRootSegue" id="StK-nr-nps"/>
</connections> </connections>
</viewController> </viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="F33-Fe-Tb6" userLabel="First Responder" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="F33-Fe-Tb6" userLabel="First Responder" sceneMemberID="firstResponder"/>
@@ -795,7 +798,7 @@
<constraint firstAttribute="height" constant="110" id="zBf-EA-iDN"/> <constraint firstAttribute="height" constant="110" id="zBf-EA-iDN"/>
</constraints> </constraints>
</imageView> </imageView>
<button opaque="NO" alpha="0.69999998807907104" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="DzC-Ts-gew" userLabel="Previous Avatar"> <button opaque="NO" alpha="0.69999998807907104" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="DzC-Ts-gew" userLabel="Previous Avatar">
<rect key="frame" x="20" y="148.5" width="44" height="53"/> <rect key="frame" x="20" y="148.5" width="44" height="53"/>
<constraints> <constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="44" id="1Wu-gS-flK"/> <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="44" id="1Wu-gS-flK"/>
@@ -809,7 +812,7 @@
<action selector="previousAvatar:" destination="JFc-sj-awD" eventType="touchUpInside" id="D92-6I-zFd"/> <action selector="previousAvatar:" destination="JFc-sj-awD" eventType="touchUpInside" id="D92-6I-zFd"/>
</connections> </connections>
</button> </button>
<button opaque="NO" alpha="0.69999998807907104" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="yAf-fc-SKl" userLabel="Next Avatar"> <button opaque="NO" alpha="0.69999998807907104" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="yAf-fc-SKl" userLabel="Next Avatar">
<rect key="frame" x="350" y="148.5" width="44" height="53"/> <rect key="frame" x="350" y="148.5" width="44" height="53"/>
<constraints> <constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="44" id="pEf-Vp-D7s"/> <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="44" id="pEf-Vp-D7s"/>
@@ -1036,7 +1039,7 @@ Note that this feature requires you enable the Save Password option and have pur
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Rl7-cr-FHf"> <button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Rl7-cr-FHf">
<rect key="frame" x="20" y="118" width="374" height="27"/> <rect key="frame" x="20" y="118" width="374" height="27"/>
<fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="12"/> <fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="12"/>
<state key="normal" title="Home Page"> <state key="normal" title="Home Page">
@@ -1046,7 +1049,7 @@ Note that this feature requires you enable the Save Password option and have pur
<action selector="homePageButton:" destination="JFc-sj-awD" eventType="touchUpInside" id="ptD-cv-NMr"/> <action selector="homePageButton:" destination="JFc-sj-awD" eventType="touchUpInside" id="ptD-cv-NMr"/>
</connections> </connections>
</button> </button>
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="epW-Rm-9St"> <button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="epW-Rm-9St">
<rect key="frame" x="20" y="153" width="374" height="27"/> <rect key="frame" x="20" y="153" width="374" height="27"/>
<fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="12"/> <fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="12"/>
<state key="normal" title="Understanding Master Password's Security"> <state key="normal" title="Understanding Master Password's Security">
@@ -1056,7 +1059,7 @@ Note that this feature requires you enable the Save Password option and have pur
<action selector="securityButton:" destination="JFc-sj-awD" eventType="touchUpInside" id="Efv-cp-Xfh"/> <action selector="securityButton:" destination="JFc-sj-awD" eventType="touchUpInside" id="Efv-cp-Xfh"/>
</connections> </connections>
</button> </button>
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="LTN-ch-h8D"> <button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="LTN-ch-h8D">
<rect key="frame" x="20" y="188" width="374" height="27"/> <rect key="frame" x="20" y="188" width="374" height="27"/>
<fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="12"/> <fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="12"/>
<state key="normal" title="Get the Master Password source code"> <state key="normal" title="Get the Master Password source code">
@@ -1066,7 +1069,7 @@ Note that this feature requires you enable the Save Password option and have pur
<action selector="sourceButton:" destination="JFc-sj-awD" eventType="touchUpInside" id="Y3O-di-CZo"/> <action selector="sourceButton:" destination="JFc-sj-awD" eventType="touchUpInside" id="Y3O-di-CZo"/>
</connections> </connections>
</button> </button>
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Z60-lc-Nka"> <button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Z60-lc-Nka">
<rect key="frame" x="20" y="223" width="374" height="27"/> <rect key="frame" x="20" y="223" width="374" height="27"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="12"/> <fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="12"/>
<state key="normal" title="Send Thanks"> <state key="normal" title="Send Thanks">
@@ -1146,7 +1149,8 @@ Note that this feature requires you enable the Save Password option and have pur
<collectionView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" minimumZoomScale="0.0" maximumZoomScale="0.0" keyboardDismissMode="interactive" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="aXw-tn-8Sj" userLabel="Password Collection"> <collectionView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" minimumZoomScale="0.0" maximumZoomScale="0.0" keyboardDismissMode="interactive" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="aXw-tn-8Sj" userLabel="Password Collection">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/> <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" sectionInsetReference="safeArea" id="Mv1-29-TWx"> <edgeInsets key="layoutMargins" top="100" left="0.0" bottom="0.0" right="0.0"/>
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" sectionInsetReference="layoutMargins" id="Mv1-29-TWx">
<size key="itemSize" width="355" height="100"/> <size key="itemSize" width="355" height="100"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/> <size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/> <size key="footerReferenceSize" width="0.0" height="0.0"/>
@@ -1154,7 +1158,7 @@ Note that this feature requires you enable the Save Password option and have pur
</collectionViewFlowLayout> </collectionViewFlowLayout>
<cells> <cells>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="MPSiteCell" id="W2g-yv-V3V" customClass="MPSiteCell"> <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="MPSiteCell" id="W2g-yv-V3V" customClass="MPSiteCell">
<rect key="frame" x="29.5" y="10" width="355" height="100"/> <rect key="frame" x="29.5" y="110" width="355" height="100"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO"> <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
<rect key="frame" x="0.0" y="0.0" width="355" height="100"/> <rect key="frame" x="0.0" y="0.0" width="355" height="100"/>
@@ -1671,16 +1675,16 @@ eg. apple.com, rmitchell@twitter.com</string>
<view opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3ve-eR-1IW"> <view opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3ve-eR-1IW">
<rect key="frame" x="0.0" y="740" width="414" height="156"/> <rect key="frame" x="0.0" y="740" width="414" height="156"/>
<subviews> <subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="PWC-I5-RSl"> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="PWC-I5-RSl">
<rect key="frame" x="8" y="8" width="398" height="106"/> <rect key="frame" x="8" y="8" width="398" height="106"/>
<state key="normal" backgroundImage="tip_alert_black"/> <state key="normal" backgroundImage="tip_alert_black"/>
<connections> <connections>
<action selector="upgradeVolto:" destination="nkY-z6-8jd" eventType="touchUpInside" id="8KJ-vm-3D0"/> <action selector="upgradeSpectre:" destination="nkY-z6-8jd" eventType="touchUpInside" id="8KJ-vm-3D0"/>
</connections> </connections>
</button> </button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="XgC-Ky-oVC"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="XgC-Ky-oVC">
<rect key="frame" x="80" y="15" width="315" height="84"/> <rect key="frame" x="80" y="15" width="315" height="84"/>
<string key="text">The next generation of password security is now called «Volto»: <string key="text">The next generation of password security is now called «Spectre»:
Tap to install the upgrade. Tap to install the upgrade.
This app is now out of maintenance.</string> This app is now out of maintenance.</string>
@@ -1703,17 +1707,17 @@ This app is now out of maintenance.</string>
<view opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="67c-5R-Foa"> <view opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="67c-5R-Foa">
<rect key="frame" x="0.0" y="740" width="414" height="156"/> <rect key="frame" x="0.0" y="740" width="414" height="156"/>
<subviews> <subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="EZK-er-8ql"> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="EZK-er-8ql">
<rect key="frame" x="8" y="8" width="398" height="106"/> <rect key="frame" x="8" y="8" width="398" height="106"/>
<state key="normal" backgroundImage="tip_alert_black"/> <state key="normal" backgroundImage="tip_alert_black"/>
<connections> <connections>
<action selector="upgradeVolto:" destination="nkY-z6-8jd" eventType="touchUpInside" id="P4a-tL-9yX"/> <action selector="upgradeSpectre:" destination="nkY-z6-8jd" eventType="touchUpInside" id="P4a-tL-9yX"/>
</connections> </connections>
</button> </button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fLB-gA-32p"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fLB-gA-32p">
<rect key="frame" x="80" y="15" width="315" height="84"/> <rect key="frame" x="80" y="15" width="315" height="84"/>
<string key="text">The next generation of password security is now called «Volto»: <string key="text">The next generation of password security is now called «Spectre»:
Tap to copy this user into Volto. Tap to copy this user into Spectre.
This app is now out of maintenance.</string> This app is now out of maintenance.</string>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/> <fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
@@ -1812,8 +1816,8 @@ This app is now out of maintenance.</string>
<outlet property="popdownView" destination="XNM-XQ-rMe" id="FaW-4m-Fff"/> <outlet property="popdownView" destination="XNM-XQ-rMe" id="FaW-4m-Fff"/>
<outlet property="searchBar" destination="aGs-1S-aC3" id="rTp-DP-rIz"/> <outlet property="searchBar" destination="aGs-1S-aC3" id="rTp-DP-rIz"/>
<outlet property="sitesToBottomConstraint" destination="dNt-uf-8BC" id="Ta6-eL-z7w"/> <outlet property="sitesToBottomConstraint" destination="dNt-uf-8BC" id="Ta6-eL-z7w"/>
<outlet property="voltoInstallAlert" destination="3ve-eR-1IW" id="Ah5-OX-XhQ"/> <outlet property="spectreInstallAlert" destination="3ve-eR-1IW" id="Ah5-OX-XhQ"/>
<outlet property="voltoMigrateAlert" destination="67c-5R-Foa" id="aTi-fu-opO"/> <outlet property="spectreMigrateAlert" destination="67c-5R-Foa" id="aTi-fu-opO"/>
<segue destination="z9O-w0-6oR" kind="modal" identifier="guide" id="Ql4-wf-T8u"/> <segue destination="z9O-w0-6oR" kind="modal" identifier="guide" id="Ql4-wf-T8u"/>
<segue destination="Foa-Er-RBr" kind="custom" identifier="message" customClass="MPOverlaySegue" id="Xne-Sm-HQt"/> <segue destination="Foa-Er-RBr" kind="custom" identifier="message" customClass="MPOverlaySegue" id="Xne-Sm-HQt"/>
</connections> </connections>
@@ -1842,10 +1846,10 @@ This app is now out of maintenance.</string>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
</view> </view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="1lc-e7-Qme" userLabel="Emergency Generator"> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="1lc-e7-Qme" userLabel="Emergency Generator">
<rect key="frame" x="20" y="240.5" width="374" height="415.5"/> <rect key="frame" x="20" y="240" width="374" height="416"/>
<subviews> <subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Emergency Generator" textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="4Lh-s0-Dbt"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Emergency Generator" textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="4Lh-s0-Dbt">
<rect key="frame" x="20" y="20" width="334" height="21"/> <rect key="frame" x="20" y="20" width="334" height="21.5"/>
<fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="17"/> <fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="17"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
@@ -1853,7 +1857,7 @@ This app is now out of maintenance.</string>
<size key="shadowOffset" width="0.0" height="1"/> <size key="shadowOffset" width="0.0" height="1"/>
</label> </label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Generate your password without logging in. Great for if you're borrowing a friend's device or are having trouble logging in." lineBreakMode="tailTruncation" numberOfLines="0" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="vHS-3A-Tae"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Generate your password without logging in. Great for if you're borrowing a friend's device or are having trouble logging in." lineBreakMode="tailTruncation" numberOfLines="0" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="vHS-3A-Tae">
<rect key="frame" x="20" y="49" width="334" height="51.5"/> <rect key="frame" x="20" y="49.5" width="334" height="51.5"/>
<fontDescription key="fontDescription" name="Exo2.0-Thin" family="Exo 2.0" pointSize="14"/> <fontDescription key="fontDescription" name="Exo2.0-Thin" family="Exo 2.0" pointSize="14"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
@@ -1861,7 +1865,7 @@ This app is now out of maintenance.</string>
<size key="shadowOffset" width="0.0" height="1"/> <size key="shadowOffset" width="0.0" height="1"/>
</label> </label>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Your Name" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="XAC-Da-lpf" userLabel="User Name"> <textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Your Name" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="XAC-Da-lpf" userLabel="User Name">
<rect key="frame" x="20" y="108.5" width="334" height="34"/> <rect key="frame" x="20" y="109" width="334" height="34"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/> <fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="words" keyboardAppearance="alert" returnKeyType="next" enablesReturnKeyAutomatically="YES"/> <textInputTraits key="textInputTraits" autocapitalizationType="words" keyboardAppearance="alert" returnKeyType="next" enablesReturnKeyAutomatically="YES"/>
<connections> <connections>
@@ -1870,7 +1874,7 @@ This app is now out of maintenance.</string>
</connections> </connections>
</textField> </textField>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Your Master Password" clearsOnBeginEditing="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="J46-0E-no3" userLabel="Master Password"> <textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Your Master Password" clearsOnBeginEditing="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="J46-0E-no3" userLabel="Master Password">
<rect key="frame" x="20" y="150.5" width="334" height="34"/> <rect key="frame" x="20" y="151" width="334" height="34"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/> <fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" keyboardAppearance="alert" returnKeyType="next" enablesReturnKeyAutomatically="YES" secureTextEntry="YES"/> <textInputTraits key="textInputTraits" autocorrectionType="no" keyboardAppearance="alert" returnKeyType="next" enablesReturnKeyAutomatically="YES" secureTextEntry="YES"/>
<connections> <connections>
@@ -1879,7 +1883,7 @@ This app is now out of maintenance.</string>
</connections> </connections>
</textField> </textField>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Site Name" clearsOnBeginEditing="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="56H-xR-09J" userLabel="Site Name"> <textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Site Name" clearsOnBeginEditing="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="56H-xR-09J" userLabel="Site Name">
<rect key="frame" x="20" y="192.5" width="334" height="34"/> <rect key="frame" x="20" y="193" width="334" height="34"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/> <fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" keyboardType="URL" keyboardAppearance="alert" returnKeyType="done" enablesReturnKeyAutomatically="YES"/> <textInputTraits key="textInputTraits" autocorrectionType="no" keyboardType="URL" keyboardAppearance="alert" returnKeyType="done" enablesReturnKeyAutomatically="YES"/>
<connections> <connections>
@@ -1888,7 +1892,7 @@ This app is now out of maintenance.</string>
</connections> </connections>
</textField> </textField>
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="bar" selectedSegmentIndex="1" translatesAutoresizingMaskIntoConstraints="NO" id="e4b-Iv-Pk9" userLabel="Type"> <segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="bar" selectedSegmentIndex="1" translatesAutoresizingMaskIntoConstraints="NO" id="e4b-Iv-Pk9" userLabel="Type">
<rect key="frame" x="20" y="234.5" width="334" height="32"/> <rect key="frame" x="20" y="235" width="334" height="32"/>
<segments> <segments>
<segment title="Max"/> <segment title="Max"/>
<segment title="Long"/> <segment title="Long"/>
@@ -1903,7 +1907,7 @@ This app is now out of maintenance.</string>
</connections> </connections>
</segmentedControl> </segmentedControl>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Counter" lineBreakMode="tailTruncation" numberOfLines="0" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="cAo-K2-E23"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Counter" lineBreakMode="tailTruncation" numberOfLines="0" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="cAo-K2-E23">
<rect key="frame" x="20" y="277.5" width="64" height="21.5"/> <rect key="frame" x="20" y="278.5" width="64" height="21.5"/>
<fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="17"/> <fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="17"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
@@ -1911,13 +1915,13 @@ This app is now out of maintenance.</string>
<size key="shadowOffset" width="0.0" height="1"/> <size key="shadowOffset" width="0.0" height="1"/>
</label> </label>
<stepper opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" maximumValue="100" translatesAutoresizingMaskIntoConstraints="NO" id="ZPT-EI-yuv" userLabel="Counter"> <stepper opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" maximumValue="100" translatesAutoresizingMaskIntoConstraints="NO" id="ZPT-EI-yuv" userLabel="Counter">
<rect key="frame" x="260" y="273.5" width="94" height="32"/> <rect key="frame" x="260" y="274" width="94" height="32"/>
<connections> <connections>
<action selector="controlChanged:" destination="osn-5H-SWW" eventType="valueChanged" id="eQA-3X-uc9"/> <action selector="controlChanged:" destination="osn-5H-SWW" eventType="valueChanged" id="eQA-3X-uc9"/>
</connections> </connections>
</stepper> </stepper>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="1" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="3Cd-XH-Wau"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="1" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="3Cd-XH-Wau">
<rect key="frame" x="245" y="278.5" width="7" height="20"/> <rect key="frame" x="245" y="279" width="7" height="20.5"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="17"/> <fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="17"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@@ -1958,10 +1962,10 @@ This app is now out of maintenance.</string>
</state> </state>
</button> </button>
<activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" hidesWhenStopped="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="4sN-hm-xio"> <activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" hidesWhenStopped="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="4sN-hm-xio">
<rect key="frame" x="168.5" y="352" width="37" height="37"/> <rect key="frame" x="168.5" y="352.5" width="37" height="37"/>
</activityIndicatorView> </activityIndicatorView>
<button opaque="NO" clipsSubviews="YES" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="clip" translatesAutoresizingMaskIntoConstraints="NO" id="bHR-he-dnZ"> <button opaque="NO" clipsSubviews="YES" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="clip" translatesAutoresizingMaskIntoConstraints="NO" id="bHR-he-dnZ">
<rect key="frame" x="20" y="345.5" width="334" height="50"/> <rect key="frame" x="20" y="346" width="334" height="50"/>
<gestureRecognizers/> <gestureRecognizers/>
<fontDescription key="fontDescription" name="SourceCodePro-Black" family="Source Code Pro" pointSize="30"/> <fontDescription key="fontDescription" name="SourceCodePro-Black" family="Source Code Pro" pointSize="30"/>
<state key="normal" title="XapaNuwjFihn6$"> <state key="normal" title="XapaNuwjFihn6$">
@@ -1973,7 +1977,7 @@ This app is now out of maintenance.</string>
</connections> </connections>
</button> </button>
<view userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="beo-cJ-jIn" userLabel="View - Content Tip"> <view userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="beo-cJ-jIn" userLabel="View - Content Tip">
<rect key="frame" x="82" y="310.5" width="210" height="60"/> <rect key="frame" x="82" y="311" width="210" height="60"/>
<subviews> <subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="tip_basic_black.png" translatesAutoresizingMaskIntoConstraints="NO" id="nyL-cO-aPa"> <imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="tip_basic_black.png" translatesAutoresizingMaskIntoConstraints="NO" id="nyL-cO-aPa">
<rect key="frame" x="0.0" y="0.0" width="210" height="60"/> <rect key="frame" x="0.0" y="0.0" width="210" height="60"/>
@@ -2052,14 +2056,41 @@ This app is now out of maintenance.</string>
<constraint firstItem="1lc-e7-Qme" firstAttribute="leading" secondItem="whU-l0-2bU" secondAttribute="leading" constant="20" id="ztv-2f-RAc"/> <constraint firstItem="1lc-e7-Qme" firstAttribute="leading" secondItem="whU-l0-2bU" secondAttribute="leading" constant="20" id="ztv-2f-RAc"/>
</constraints> </constraints>
</scrollView> </scrollView>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="cbe-hc-9K5">
<rect key="frame" x="20" y="820" width="374" height="42"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Anonymous Device Identifier" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="VyZ-qP-VkG">
<rect key="frame" x="0.0" y="0.0" width="374" height="18"/>
<fontDescription key="fontDescription" name="Exo2.0-Thin" family="Exo 2.0" pointSize="14"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
<color key="shadowColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<size key="shadowOffset" width="0.0" height="1"/>
</label>
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="DVF-Im-H2I">
<rect key="frame" x="0.0" y="18" width="374" height="24"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="10"/>
<state key="normal" title="0000000000000000000000000000000000000000">
<color key="titleColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="copyDevice:" destination="osn-5H-SWW" eventType="touchUpInside" id="0dD-IS-Ktn"/>
</connections>
</button>
</subviews>
</stackView>
</subviews> </subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstItem="whU-l0-2bU" firstAttribute="height" secondItem="GiS-3g-cDj" secondAttribute="height" multiplier="1:2" id="4oQ-JI-wQv"/> <constraint firstItem="whU-l0-2bU" firstAttribute="height" secondItem="GiS-3g-cDj" secondAttribute="height" multiplier="1:2" id="4oQ-JI-wQv"/>
<constraint firstAttribute="width" secondItem="whU-l0-2bU" secondAttribute="width" id="AiQ-LE-3bh"/> <constraint firstAttribute="width" secondItem="whU-l0-2bU" secondAttribute="width" id="AiQ-LE-3bh"/>
<constraint firstItem="IV3-lc-Fnf" firstAttribute="top" secondItem="gRG-Ys-94p" secondAttribute="bottom" id="IMb-wl-Eeb"/> <constraint firstItem="IV3-lc-Fnf" firstAttribute="top" secondItem="gRG-Ys-94p" secondAttribute="bottom" id="IMb-wl-Eeb"/>
<constraint firstItem="cbe-hc-9K5" firstAttribute="leading" secondItem="GiS-3g-cDj" secondAttribute="leadingMargin" id="KNZ-jb-npt"/>
<constraint firstAttribute="trailing" secondItem="gRG-Ys-94p" secondAttribute="trailing" id="Pb1-eY-0FG"/> <constraint firstAttribute="trailing" secondItem="gRG-Ys-94p" secondAttribute="trailing" id="Pb1-eY-0FG"/>
<constraint firstItem="IV3-lc-Fnf" firstAttribute="top" secondItem="cbe-hc-9K5" secondAttribute="bottom" id="YWv-H4-Ij3"/>
<constraint firstItem="gRG-Ys-94p" firstAttribute="top" secondItem="GiS-3g-cDj" secondAttribute="top" id="oyk-Xr-zTZ"/> <constraint firstItem="gRG-Ys-94p" firstAttribute="top" secondItem="GiS-3g-cDj" secondAttribute="top" id="oyk-Xr-zTZ"/>
<constraint firstAttribute="trailingMargin" secondItem="cbe-hc-9K5" secondAttribute="trailing" id="wss-B8-PT6"/>
<constraint firstItem="gRG-Ys-94p" firstAttribute="leading" secondItem="GiS-3g-cDj" secondAttribute="leading" id="zhC-jf-5dY"/> <constraint firstItem="gRG-Ys-94p" firstAttribute="leading" secondItem="GiS-3g-cDj" secondAttribute="leading" id="zhC-jf-5dY"/>
</constraints> </constraints>
<userDefinedRuntimeAttributes> <userDefinedRuntimeAttributes>
@@ -2074,6 +2105,7 @@ This app is now out of maintenance.</string>
<outlet property="containerView" destination="GiS-3g-cDj" id="01o-PU-SNZ"/> <outlet property="containerView" destination="GiS-3g-cDj" id="01o-PU-SNZ"/>
<outlet property="counterLabel" destination="3Cd-XH-Wau" id="wv7-jZ-6tX"/> <outlet property="counterLabel" destination="3Cd-XH-Wau" id="wv7-jZ-6tX"/>
<outlet property="counterStepper" destination="ZPT-EI-yuv" id="w5c-hO-T51"/> <outlet property="counterStepper" destination="ZPT-EI-yuv" id="w5c-hO-T51"/>
<outlet property="deviceButton" destination="DVF-Im-H2I" id="TMU-Vo-bDo"/>
<outlet property="dialogView" destination="1lc-e7-Qme" id="JYt-mv-XV2"/> <outlet property="dialogView" destination="1lc-e7-Qme" id="JYt-mv-XV2"/>
<outlet property="fullNameField" destination="XAC-Da-lpf" id="XCk-0H-IcI"/> <outlet property="fullNameField" destination="XAC-Da-lpf" id="XCk-0H-IcI"/>
<outlet property="masterPasswordField" destination="J46-0E-no3" id="DfH-4n-cop"/> <outlet property="masterPasswordField" destination="J46-0E-no3" id="DfH-4n-cop"/>
@@ -2115,10 +2147,10 @@ This app is now out of maintenance.</string>
</items> </items>
</navigationBar> </navigationBar>
<pageControl opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" contentHorizontalAlignment="center" contentVerticalAlignment="center" numberOfPages="3" translatesAutoresizingMaskIntoConstraints="NO" id="8A2-ly-WTX"> <pageControl opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" contentHorizontalAlignment="center" contentVerticalAlignment="center" numberOfPages="3" translatesAutoresizingMaskIntoConstraints="NO" id="8A2-ly-WTX">
<rect key="frame" x="187.5" y="771" width="39" height="37"/> <rect key="frame" x="141" y="778.5" width="132" height="29.5"/>
</pageControl> </pageControl>
<collectionView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" pagingEnabled="YES" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" minimumZoomScale="0.0" maximumZoomScale="0.0" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="i2y-lo-HXR"> <collectionView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" pagingEnabled="YES" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" minimumZoomScale="0.0" maximumZoomScale="0.0" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="i2y-lo-HXR">
<rect key="frame" x="0.0" y="44" width="414" height="637"/> <rect key="frame" x="0.0" y="44" width="414" height="644.5"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" minimumLineSpacing="0.0" minimumInteritemSpacing="0.0" id="m8O-kY-22j"> <collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" minimumLineSpacing="0.0" minimumInteritemSpacing="0.0" id="m8O-kY-22j">
<size key="itemSize" width="320" height="458"/> <size key="itemSize" width="320" height="458"/>
@@ -2128,7 +2160,7 @@ This app is now out of maintenance.</string>
</collectionViewFlowLayout> </collectionViewFlowLayout>
<cells> <cells>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="MPGuideStepCell" id="oGu-pJ-3bQ" customClass="MPGuideStepCell"> <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="MPGuideStepCell" id="oGu-pJ-3bQ" customClass="MPGuideStepCell">
<rect key="frame" x="0.0" y="89.5" width="320" height="458"/> <rect key="frame" x="0.0" y="93.5" width="320" height="458"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center"> <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="320" height="458"/> <rect key="frame" x="0.0" y="0.0" width="320" height="458"/>
@@ -2157,13 +2189,13 @@ This app is now out of maintenance.</string>
</connections> </connections>
</collectionView> </collectionView>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="To begin, tap the &quot;New User&quot; icon and add yourself as a user to the application." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ciw-56-nNy" userLabel="Caption"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="To begin, tap the &quot;New User&quot; icon and add yourself as a user to the application." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ciw-56-nNy" userLabel="Caption">
<rect key="frame" x="8" y="689" width="398" height="82"/> <rect key="frame" x="8" y="696.5" width="398" height="82"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="13"/> <fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="13"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label hidden="YES" opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Oop-Ff-gbz" userLabel="Caption Height Strut"> <label hidden="YES" opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Oop-Ff-gbz" userLabel="Caption Height Strut">
<rect key="frame" x="8" y="689" width="1" height="82"/> <rect key="frame" x="8" y="696.5" width="1" height="82"/>
<string key="text" base64-UTF8="YES"> <string key="text" base64-UTF8="YES">
CgoKCgoKCgoKCgoKCg CgoKCgoKCgoKCgoKCg
</string> </string>
@@ -2324,7 +2356,7 @@ Suspendisse potenti. Etiam ut nisi id augue tempor ultrices et sit amet sapien.
<rect key="frame" x="0.0" y="0.0" width="414" height="842"/> <rect key="frame" x="0.0" y="0.0" width="414" height="842"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<imageView userInteractionEnabled="NO" contentMode="center" image="background.png" translatesAutoresizingMaskIntoConstraints="NO" id="OsH-Mq-gpM"> <imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" image="background.png" translatesAutoresizingMaskIntoConstraints="NO" id="OsH-Mq-gpM">
<rect key="frame" x="0.0" y="0.0" width="414" height="842"/> <rect key="frame" x="0.0" y="0.0" width="414" height="842"/>
</imageView> </imageView>
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Ol8-3I-X99"> <scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Ol8-3I-X99">
@@ -2412,7 +2444,7 @@ However, it means that anyone who finds your device unlocked can do the same.</s
<rect key="frame" x="0.0" y="0.0" width="414" height="842"/> <rect key="frame" x="0.0" y="0.0" width="414" height="842"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<imageView userInteractionEnabled="NO" contentMode="center" image="background.png" translatesAutoresizingMaskIntoConstraints="NO" id="Hrg-vp-M2J"> <imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" image="background.png" translatesAutoresizingMaskIntoConstraints="NO" id="Hrg-vp-M2J">
<rect key="frame" x="0.0" y="0.0" width="414" height="842"/> <rect key="frame" x="0.0" y="0.0" width="414" height="842"/>
</imageView> </imageView>
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fJc-xU-1xA"> <scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fJc-xU-1xA">
@@ -2752,7 +2784,7 @@ Invested: 3.7 work hours</string>
<rect key="frame" x="188.5" y="20" width="37" height="37"/> <rect key="frame" x="188.5" y="20" width="37" height="37"/>
</activityIndicatorView> </activityIndicatorView>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" text="Loading products..." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="12" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="NPg-it-juF"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" text="Loading products..." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="12" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="NPg-it-juF">
<rect key="frame" x="134.5" y="97" width="145.5" height="25"/> <rect key="frame" x="134" y="97" width="146" height="25"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="17"/> <fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="17"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
@@ -2781,7 +2813,7 @@ Invested: 3.7 work hours</string>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" verticalCompressionResistancePriority="751" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="lQ1-b9-Xp4"> <button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" verticalCompressionResistancePriority="751" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="lQ1-b9-Xp4">
<rect key="frame" x="20" y="34" width="374" height="27"/> <rect key="frame" x="20" y="34" width="374" height="27"/>
<fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="12"/> <fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="12"/>
<state key="normal" title="Restore Previous Purchases"> <state key="normal" title="Restore Previous Purchases">
@@ -2791,7 +2823,7 @@ Invested: 3.7 work hours</string>
<action selector="restorePurchases:" destination="pdl-xv-zjX" eventType="touchUpInside" id="Q8M-ud-OHL"/> <action selector="restorePurchases:" destination="pdl-xv-zjX" eventType="touchUpInside" id="Q8M-ud-OHL"/>
</connections> </connections>
</button> </button>
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="eus-kQ-emn"> <button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="eus-kQ-emn">
<rect key="frame" x="8" y="69" width="398" height="27"/> <rect key="frame" x="8" y="69" width="398" height="27"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="12"/> <fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="12"/>
<state key="normal" title="Send Thanks"> <state key="normal" title="Send Thanks">
@@ -3036,7 +3068,7 @@ Invested: 3.7 work hours</string>
<rect key="frame" x="0.0" y="0.0" width="414" height="842"/> <rect key="frame" x="0.0" y="0.0" width="414" height="842"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<imageView userInteractionEnabled="NO" contentMode="center" image="background.png" translatesAutoresizingMaskIntoConstraints="NO" id="rvb-7K-MHe"> <imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" image="background.png" translatesAutoresizingMaskIntoConstraints="NO" id="rvb-7K-MHe">
<rect key="frame" x="0.0" y="0.0" width="414" height="842"/> <rect key="frame" x="0.0" y="0.0" width="414" height="842"/>
</imageView> </imageView>
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="7A7-Yn-F6L"> <scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="7A7-Yn-F6L">
@@ -3269,7 +3301,7 @@ Ut in geometria, prima si dederis, danda sunt omnia. Nonne igitur tibi videntur,
</scenes> </scenes>
<inferredMetricsTieBreakers> <inferredMetricsTieBreakers>
<segue reference="Ql4-wf-T8u"/> <segue reference="Ql4-wf-T8u"/>
<segue reference="GZk-I4-JyH"/> <segue reference="gtb-zE-u9H"/>
</inferredMetricsTieBreakers> </inferredMetricsTieBreakers>
<color key="tintColor" white="1" alpha="1" colorSpace="calibratedWhite"/> <color key="tintColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<resources> <resources>

View File

@@ -28,7 +28,7 @@ mpw_expect() {
json) file=~/.mpw.d/"$user.mpjson" ;; json) file=~/.mpw.d/"$user.mpjson" ;;
esac esac
fi fi
[[ $file ]] && (( ! keep )) && rm "$file" [[ -e $file ]] && (( ! keep )) && rm "$file"
printf '.' printf '.'
local result=$(./mpw -q "${args[@]}") err=$? local result=$(./mpw -q "${args[@]}") err=$?
@@ -79,7 +79,7 @@ mpw_expect() {
esac esac
fi fi
[[ $file ]] && (( ! keep )) && rm "$file" [[ -e $file ]] && (( ! keep )) && rm "$file"
} }
@@ -107,11 +107,11 @@ mpw_expect 'ModoLalhRapo6#' -Fnone \
mpw_expect 'CudmTecuPune7:' -Fnone \ mpw_expect 'CudmTecuPune7:' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' '⛄' -u 'tesẗ' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' '⛄'
mpw_expect 'yubfalago' -Fnone \ mpw_expect 'yubfalago' -Fnone \
-u 'tesẗ' -M 'ẗest' -p 'identification' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -p 'identification' -C '' 'ẗesẗ'
mpw_expect 'yubfalago' -Fnone \ mpw_expect 'yubfalago' -Fnone \
-u 'tesẗ' -M 'ẗest' -tname -c1 -a3 -p 'identification' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tname -c1 -a3 -p 'identification' -C '' 'ẗesẗ'
mpw_expect 'jip nodwoqude dizo' -Fnone \ mpw_expect 'jip nodwoqude dizo' -Fnone \
-u 'tesẗ' -M 'ẗest' -p 'recovery' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -p 'recovery' -C '' 'ẗesẗ'
mpw_expect 'jip nodwoqude dizo' -Fnone \ mpw_expect 'jip nodwoqude dizo' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a3 -p 'recovery' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tphrase -c1 -a3 -p 'recovery' -C '' 'ẗesẗ'
mpw_expect 'dok sorkicoyu ruya' -Fnone \ mpw_expect 'dok sorkicoyu ruya' -Fnone \
@@ -131,7 +131,7 @@ mpw_expect 'tinraziqu' -Fnone \
mpw_expect 'tinr ziq taghuye zuj' -Fnone \ mpw_expect 'tinr ziq taghuye zuj' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a3 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tphrase -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'HidiLonoFopt9&' -Fnone \ mpw_expect 'HidiLonoFopt9&' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c4294967295 -a3 -p 'authentication' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tlong -c4294967295 -a3 -p 'authentication' -C '' 'ẗesẗ'
## V2 ## V2
printf "\nV%d, none: " 2 printf "\nV%d, none: " 2
@@ -166,7 +166,7 @@ mpw_expect 'huczukamo' -Fnone \
mpw_expect 'huc finmokozi fota' -Fnone \ mpw_expect 'huc finmokozi fota' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a2 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tphrase -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Mixa1~BulgNijo' -Fnone \ mpw_expect 'Mixa1~BulgNijo' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c4294967295 -a2 -p 'authentication' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tlong -c4294967295 -a2 -p 'authentication' -C '' 'ẗesẗ'
## V1 ## V1
printf "\nV%d, none: " 1 printf "\nV%d, none: " 1
@@ -201,7 +201,7 @@ mpw_expect 'suxhotito' -Fnone \
mpw_expect 'su hotte pav calewxo' -Fnone \ mpw_expect 'su hotte pav calewxo' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a1 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tphrase -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Luxn2#JapiXopa' -Fnone \ mpw_expect 'Luxn2#JapiXopa' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c4294967295 -a1 -p 'authentication' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tlong -c4294967295 -a1 -p 'authentication' -C '' 'ẗesẗ'
## V0 ## V0
printf "\nV%d, none: " 0 printf "\nV%d, none: " 0
@@ -236,7 +236,7 @@ mpw_expect 'wumdoxixo' -Fnone \
mpw_expect 'wu doxbe hac kaselqo' -Fnone \ mpw_expect 'wu doxbe hac kaselqo' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a0 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tphrase -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Pumy7.JadjQoda' -Fnone \ mpw_expect 'Pumy7.JadjQoda' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c4294967295 -a0 -p 'authentication' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tlong -c4294967295 -a0 -p 'authentication' -C '' 'ẗesẗ'
## V3 ## V3
printf "\nV%d, flat: " 3 printf "\nV%d, flat: " 3
@@ -247,7 +247,7 @@ mpw_expect 'FamiJirk1)Zehc' -Fflat -R0 \
mpw_expect 'NofhMusw8+Cebo' -Fflat -R0 \ mpw_expect 'NofhMusw8+Cebo' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.⛄' -u 'tesẗ.v3' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.⛄'
mpw_expect 'Necx1$LagaRizu' -Fflat -R0 \ mpw_expect 'Necx1$LagaRizu' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tlong -c4294967295 -a3 -p 'authentication' 'ẗesẗ.c+a3pa' -u 'tesẗ.v3' -M 'ẗest' -c4294967295 -a3 -p 'authentication' -C '' 'ẗesẗ.c+a3pa'
mpw_expect 'Poq2)Tey' -Fflat -R0 \ mpw_expect 'Poq2)Tey' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tmed -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.med' -u 'tesẗ.v3' -M 'ẗest' -tmed -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.med'
mpw_expect 'Wr07Okx0' -Fflat -R0 \ mpw_expect 'Wr07Okx0' -Fflat -R0 \
@@ -260,12 +260,16 @@ mpw_expect 'jupxiqepi' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tname -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.name' -u 'tesẗ.v3' -M 'ẗest' -tname -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.name'
mpw_expect 'vuh buxtukewo puhe' -Fflat -R0 \ mpw_expect 'vuh buxtukewo puhe' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tphrase -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.phrase' -u 'tesẗ.v3' -M 'ẗest' -tphrase -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.phrase'
mpw_expect 'Cq5$TfH#OHmPS9yREp7)' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tmax -c1 -a3 -p 'identification' -C '' 'ẗesẗ.c1a3pi.max'
mpw_expect 'mophabiwe' -Fflat -R0 \ mpw_expect 'mophabiwe' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tname -c1 -a3 -p 'identification' -C '' 'ẗesẗ.c1a3pi' -u 'tesẗ.v3' -M 'ẗest' -c1 -a3 -p 'identification' -C '' 'ẗesẗ.c1a3pi'
mpw_expect 'lA^ul!%9&TD%fj6icT1[' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tmax -c1 -a3 -p 'recovery' -C '' 'ẗesẗ.c1a3pr.max'
mpw_expect 'mup wulbezaxa juca' -Fflat -R0 \ mpw_expect 'mup wulbezaxa juca' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tphrase -c1 -a3 -p 'recovery' -C '' 'ẗesẗ.c1a3pr' -u 'tesẗ.v3' -M 'ẗest' -c1 -a3 -p 'recovery' -C '' 'ẗesẗ.c1a3pr'
mpw_expect 'molg rux kaczuvi ror' -Fflat -R0 \ mpw_expect 'molg rux kaczuvi ror' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tphrase -c1 -a3 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a3pr.quesẗion' -u 'tesẗ.v3' -M 'ẗest' -c1 -a3 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a3pr.quesẗion'
## V2 ## V2
printf "\nV%d, flat: " 2 printf "\nV%d, flat: " 2
@@ -276,7 +280,7 @@ mpw_expect 'Lifw5]DablSuga' -Fflat -R0 \
mpw_expect 'Leja5%RavoZapa' -Fflat -R0 \ mpw_expect 'Leja5%RavoZapa' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.⛄' -u 'tesẗ.v2' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.⛄'
mpw_expect 'NejnGazo8?Seqo' -Fflat -R0 \ mpw_expect 'NejnGazo8?Seqo' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tlong -c4294967295 -a2 -p 'authentication' 'ẗesẗ.c+a2pa' -u 'tesẗ.v2' -M 'ẗest' -c4294967295 -a2 -p 'authentication' -C '' 'ẗesẗ.c+a2pa'
mpw_expect 'XicSux2&' -Fflat -R0 \ mpw_expect 'XicSux2&' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tmed -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.med' -u 'tesẗ.v2' -M 'ẗest' -tmed -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.med'
mpw_expect 'uEY50hcZ' -Fflat -R0 \ mpw_expect 'uEY50hcZ' -Fflat -R0 \
@@ -289,12 +293,16 @@ mpw_expect 'rexmibace' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tname -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.name' -u 'tesẗ.v2' -M 'ẗest' -tname -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.name'
mpw_expect 'cez fexlemozo yula' -Fflat -R0 \ mpw_expect 'cez fexlemozo yula' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tphrase -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.phrase' -u 'tesẗ.v2' -M 'ẗest' -tphrase -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.phrase'
mpw_expect 'T8+xi4NMd3HUGdV#GW*%' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tmax -c1 -a2 -p 'identification' -C '' 'ẗesẗ.c1a2pi.max'
mpw_expect 'camfibeye' -Fflat -R0 \ mpw_expect 'camfibeye' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tname -c1 -a2 -p 'identification' -C '' 'ẗesẗ.c1a2pi' -u 'tesẗ.v2' -M 'ẗest' -c1 -a2 -p 'identification' -C '' 'ẗesẗ.c1a2pi'
mpw_expect 'YLcoWeBwyiBf2*irFq1.' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tmax -c1 -a2 -p 'recovery' -C '' 'ẗesẗ.c1a2pr.max'
mpw_expect 'ye vemcu keq xepewmi' -Fflat -R0 \ mpw_expect 'ye vemcu keq xepewmi' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tphrase -c1 -a2 -p 'recovery' -C '' 'ẗesẗ.c1a2pr' -u 'tesẗ.v2' -M 'ẗest' -c1 -a2 -p 'recovery' -C '' 'ẗesẗ.c1a2pr'
mpw_expect 'yi qazne tid najuvme' -Fflat -R0 \ mpw_expect 'yi qazne tid najuvme' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tphrase -c1 -a2 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a2pr.quesẗion' -u 'tesẗ.v2' -M 'ẗest' -c1 -a2 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a2pr.quesẗion'
## V1 ## V1
printf "\nV%d, flat: " 1 printf "\nV%d, flat: " 1
@@ -305,7 +313,7 @@ mpw_expect 'Lojz6?VotaJall' -Fflat -R0 \
mpw_expect 'Yoqu7)NiziFito' -Fflat -R0 \ mpw_expect 'Yoqu7)NiziFito' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.⛄' -u 'tesẗ.v1' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.⛄'
mpw_expect 'Foha4[TojmXanc' -Fflat -R0 \ mpw_expect 'Foha4[TojmXanc' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tlong -c4294967295 -a1 -p 'authentication' 'ẗesẗ.c+a1pa' -u 'tesẗ.v1' -M 'ẗest' -c4294967295 -a1 -p 'authentication' -C '' 'ẗesẗ.c+a1pa'
mpw_expect 'Hiy3*Zag' -Fflat -R0 \ mpw_expect 'Hiy3*Zag' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tmed -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.med' -u 'tesẗ.v1' -M 'ẗest' -tmed -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.med'
mpw_expect 'UJR7HpG0' -Fflat -R0 \ mpw_expect 'UJR7HpG0' -Fflat -R0 \
@@ -318,12 +326,16 @@ mpw_expect 'vadxovezu' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tname -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.name' -u 'tesẗ.v1' -M 'ẗest' -tname -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.name'
mpw_expect 'sij jihloyenu kizi' -Fflat -R0 \ mpw_expect 'sij jihloyenu kizi' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tphrase -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.phrase' -u 'tesẗ.v1' -M 'ẗest' -tphrase -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.phrase'
mpw_expect 'z2U9)(uQ78TXqtaus)8.' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tmax -c1 -a1 -p 'identification' -C '' 'ẗesẗ.c1a1pi.max'
mpw_expect 'qipberize' -Fflat -R0 \ mpw_expect 'qipberize' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tname -c1 -a1 -p 'identification' -C '' 'ẗesẗ.c1a1pi' -u 'tesẗ.v1' -M 'ẗest' -c1 -a1 -p 'identification' -C '' 'ẗesẗ.c1a1pi'
mpw_expect 'QMciaKyi1&I*g%tHz99,' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tmax -c1 -a1 -p 'recovery' -C '' 'ẗesẗ.c1a1pr.max'
mpw_expect 'sok torxibute reza' -Fflat -R0 \ mpw_expect 'sok torxibute reza' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tphrase -c1 -a1 -p 'recovery' -C '' 'ẗesẗ.c1a1pr' -u 'tesẗ.v1' -M 'ẗest' -c1 -a1 -p 'recovery' -C '' 'ẗesẗ.c1a1pr'
mpw_expect 'xacp qaw qutbece gan' -Fflat -R0 \ mpw_expect 'xacp qaw qutbece gan' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tphrase -c1 -a1 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a1pr.quesẗion' -u 'tesẗ.v1' -M 'ẗest' -c1 -a1 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a1pr.quesẗion'
## V0 ## V0
printf "\nV%d, flat: " 0 printf "\nV%d, flat: " 0
@@ -334,7 +346,7 @@ mpw_expect 'ZuceHazwLojz8!' -Fflat -R0 \
mpw_expect 'Boxj2!YabePodp' -Fflat -R0 \ mpw_expect 'Boxj2!YabePodp' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.⛄' -u 'tesẗ.v0' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.⛄'
mpw_expect 'PeblLuqc6]Cala' -Fflat -R0 \ mpw_expect 'PeblLuqc6]Cala' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tlong -c4294967295 -a0 -p 'authentication' 'ẗesẗ.c+a0pa' -u 'tesẗ.v0' -M 'ẗest' -c4294967295 -a0 -p 'authentication' -C '' 'ẗesẗ.c+a0pa'
mpw_expect 'XelQac0@' -Fflat -R0 \ mpw_expect 'XelQac0@' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tmed -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.med' -u 'tesẗ.v0' -M 'ẗest' -tmed -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.med'
mpw_expect 'qS07SRc8' -Fflat -R0 \ mpw_expect 'qS07SRc8' -Fflat -R0 \
@@ -347,12 +359,16 @@ mpw_expect 'rivfutipe' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tname -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.name' -u 'tesẗ.v0' -M 'ẗest' -tname -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.name'
mpw_expect 'xir qebdohogo buno' -Fflat -R0 \ mpw_expect 'xir qebdohogo buno' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tphrase -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.phrase' -u 'tesẗ.v0' -M 'ẗest' -tphrase -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.phrase'
mpw_expect "RoAm3bJSvo@#loHSRA6\'" -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tmax -c1 -a0 -p 'identification' -C '' 'ẗesẗ.c1a0pi.max'
mpw_expect 'ragcoxudo' -Fflat -R0 \ mpw_expect 'ragcoxudo' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tname -c1 -a0 -p 'identification' -C '' 'ẗesẗ.c1a0pi' -u 'tesẗ.v0' -M 'ẗest' -c1 -a0 -p 'identification' -C '' 'ẗesẗ.c1a0pi'
mpw_expect 'm8]SiJHiAS@H@Rbw))34' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tmax -c1 -a0 -p 'recovery' -C '' 'ẗesẗ.c1a0pr.max'
mpw_expect 'kokl hov lowmaya xaf' -Fflat -R0 \ mpw_expect 'kokl hov lowmaya xaf' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tphrase -c1 -a0 -p 'recovery' -C '' 'ẗesẗ.c1a0pr' -u 'tesẗ.v0' -M 'ẗest' -c1 -a0 -p 'recovery' -C '' 'ẗesẗ.c1a0pr'
mpw_expect 'wi zanmu nug zuwidwe' -Fflat -R0 \ mpw_expect 'wi zanmu nug zuwidwe' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tphrase -c1 -a0 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a0pr.quesẗion' -u 'tesẗ.v0' -M 'ẗest' -c1 -a0 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a0pr.quesẗion'
## V3 ## V3
@@ -364,7 +380,7 @@ mpw_expect 'FamiJirk1)Zehc' -Fjson -R0 \
mpw_expect 'NofhMusw8+Cebo' -Fjson -R0 \ mpw_expect 'NofhMusw8+Cebo' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.⛄' -u 'tesẗ.v3' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.⛄'
mpw_expect 'Necx1$LagaRizu' -Fjson -R0 \ mpw_expect 'Necx1$LagaRizu' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tlong -c4294967295 -a3 -p 'authentication' 'ẗesẗ.c+a3pa' -u 'tesẗ.v3' -M 'ẗest' -c4294967295 -a3 -p 'authentication' -C '' 'ẗesẗ.c+a3pa'
mpw_expect 'Poq2)Tey' -Fjson -R0 \ mpw_expect 'Poq2)Tey' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tmed -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.med' -u 'tesẗ.v3' -M 'ẗest' -tmed -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.med'
mpw_expect 'Wr07Okx0' -Fjson -R0 \ mpw_expect 'Wr07Okx0' -Fjson -R0 \
@@ -377,12 +393,16 @@ mpw_expect 'jupxiqepi' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tname -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.name' -u 'tesẗ.v3' -M 'ẗest' -tname -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.name'
mpw_expect 'vuh buxtukewo puhe' -Fjson -R0 \ mpw_expect 'vuh buxtukewo puhe' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tphrase -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.phrase' -u 'tesẗ.v3' -M 'ẗest' -tphrase -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.phrase'
mpw_expect 'Cq5$TfH#OHmPS9yREp7)' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tmax -c1 -a3 -p 'identification' -C '' 'ẗesẗ.c1a3pi.max'
mpw_expect 'mophabiwe' -Fjson -R0 \ mpw_expect 'mophabiwe' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tname -c1 -a3 -p 'identification' -C '' 'ẗesẗ.c1a3pi' -u 'tesẗ.v3' -M 'ẗest' -c1 -a3 -p 'identification' -C '' 'ẗesẗ.c1a3pi'
mpw_expect 'lA^ul!%9&TD%fj6icT1[' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tmax -c1 -a3 -p 'recovery' -C '' 'ẗesẗ.c1a3pr.max'
mpw_expect 'mup wulbezaxa juca' -Fjson -R0 \ mpw_expect 'mup wulbezaxa juca' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tphrase -c1 -a3 -p 'recovery' -C '' 'ẗesẗ.c1a3pr' -u 'tesẗ.v3' -M 'ẗest' -c1 -a3 -p 'recovery' -C '' 'ẗesẗ.c1a3pr'
mpw_expect 'molg rux kaczuvi ror' -Fjson -R0 \ mpw_expect 'molg rux kaczuvi ror' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tphrase -c1 -a3 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a3pr.quesẗion' -u 'tesẗ.v3' -M 'ẗest' -c1 -a3 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a3pr.quesẗion'
## V2 ## V2
printf "\nV%d, json: " 2 printf "\nV%d, json: " 2
@@ -393,7 +413,7 @@ mpw_expect 'Lifw5]DablSuga' -Fjson -R0 \
mpw_expect 'Leja5%RavoZapa' -Fjson -R0 \ mpw_expect 'Leja5%RavoZapa' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.⛄' -u 'tesẗ.v2' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.⛄'
mpw_expect 'NejnGazo8?Seqo' -Fjson -R0 \ mpw_expect 'NejnGazo8?Seqo' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tlong -c4294967295 -a2 -p 'authentication' 'ẗesẗ.c+a2pa' -u 'tesẗ.v2' -M 'ẗest' -c4294967295 -a2 -p 'authentication' -C '' 'ẗesẗ.c+a2pa'
mpw_expect 'XicSux2&' -Fjson -R0 \ mpw_expect 'XicSux2&' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tmed -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.med' -u 'tesẗ.v2' -M 'ẗest' -tmed -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.med'
mpw_expect 'uEY50hcZ' -Fjson -R0 \ mpw_expect 'uEY50hcZ' -Fjson -R0 \
@@ -406,12 +426,16 @@ mpw_expect 'rexmibace' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tname -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.name' -u 'tesẗ.v2' -M 'ẗest' -tname -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.name'
mpw_expect 'cez fexlemozo yula' -Fjson -R0 \ mpw_expect 'cez fexlemozo yula' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tphrase -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.phrase' -u 'tesẗ.v2' -M 'ẗest' -tphrase -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.phrase'
mpw_expect 'T8+xi4NMd3HUGdV#GW*%' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tmax -c1 -a2 -p 'identification' -C '' 'ẗesẗ.c1a2pi.max'
mpw_expect 'camfibeye' -Fjson -R0 \ mpw_expect 'camfibeye' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tname -c1 -a2 -p 'identification' -C '' 'ẗesẗ.c1a2pi' -u 'tesẗ.v2' -M 'ẗest' -c1 -a2 -p 'identification' -C '' 'ẗesẗ.c1a2pi'
mpw_expect 'YLcoWeBwyiBf2*irFq1.' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tmax -c1 -a2 -p 'recovery' -C '' 'ẗesẗ.c1a2pr.max'
mpw_expect 'ye vemcu keq xepewmi' -Fjson -R0 \ mpw_expect 'ye vemcu keq xepewmi' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tphrase -c1 -a2 -p 'recovery' -C '' 'ẗesẗ.c1a2pr' -u 'tesẗ.v2' -M 'ẗest' -c1 -a2 -p 'recovery' -C '' 'ẗesẗ.c1a2pr'
mpw_expect 'yi qazne tid najuvme' -Fjson -R0 \ mpw_expect 'yi qazne tid najuvme' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tphrase -c1 -a2 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a2pr.quesẗion' -u 'tesẗ.v2' -M 'ẗest' -c1 -a2 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a2pr.quesẗion'
## V1 ## V1
printf "\nV%d, json: " 1 printf "\nV%d, json: " 1
@@ -422,7 +446,7 @@ mpw_expect 'Lojz6?VotaJall' -Fjson -R0 \
mpw_expect 'Yoqu7)NiziFito' -Fjson -R0 \ mpw_expect 'Yoqu7)NiziFito' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.⛄' -u 'tesẗ.v1' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.⛄'
mpw_expect 'Foha4[TojmXanc' -Fjson -R0 \ mpw_expect 'Foha4[TojmXanc' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tlong -c4294967295 -a1 -p 'authentication' 'ẗesẗ.c+a1pa' -u 'tesẗ.v1' -M 'ẗest' -c4294967295 -a1 -p 'authentication' -C '' 'ẗesẗ.c+a1pa'
mpw_expect 'Hiy3*Zag' -Fjson -R0 \ mpw_expect 'Hiy3*Zag' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tmed -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.med' -u 'tesẗ.v1' -M 'ẗest' -tmed -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.med'
mpw_expect 'UJR7HpG0' -Fjson -R0 \ mpw_expect 'UJR7HpG0' -Fjson -R0 \
@@ -435,12 +459,16 @@ mpw_expect 'vadxovezu' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tname -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.name' -u 'tesẗ.v1' -M 'ẗest' -tname -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.name'
mpw_expect 'sij jihloyenu kizi' -Fjson -R0 \ mpw_expect 'sij jihloyenu kizi' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tphrase -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.phrase' -u 'tesẗ.v1' -M 'ẗest' -tphrase -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.phrase'
mpw_expect 'z2U9)(uQ78TXqtaus)8.' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tmax -c1 -a1 -p 'identification' -C '' 'ẗesẗ.c1a1pi.max'
mpw_expect 'qipberize' -Fjson -R0 \ mpw_expect 'qipberize' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tname -c1 -a1 -p 'identification' -C '' 'ẗesẗ.c1a1pi' -u 'tesẗ.v1' -M 'ẗest' -c1 -a1 -p 'identification' -C '' 'ẗesẗ.c1a1pi'
mpw_expect 'QMciaKyi1&I*g%tHz99,' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tmax -c1 -a1 -p 'recovery' -C '' 'ẗesẗ.c1a1pr.max'
mpw_expect 'sok torxibute reza' -Fjson -R0 \ mpw_expect 'sok torxibute reza' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tphrase -c1 -a1 -p 'recovery' -C '' 'ẗesẗ.c1a1pr' -u 'tesẗ.v1' -M 'ẗest' -c1 -a1 -p 'recovery' -C '' 'ẗesẗ.c1a1pr'
mpw_expect 'xacp qaw qutbece gan' -Fjson -R0 \ mpw_expect 'xacp qaw qutbece gan' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tphrase -c1 -a1 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a1pr.quesẗion' -u 'tesẗ.v1' -M 'ẗest' -c1 -a1 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a1pr.quesẗion'
## V0 ## V0
printf "\nV%d, json: " 0 printf "\nV%d, json: " 0
@@ -451,7 +479,7 @@ mpw_expect 'ZuceHazwLojz8!' -Fjson -R0 \
mpw_expect 'Boxj2!YabePodp' -Fjson -R0 \ mpw_expect 'Boxj2!YabePodp' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.⛄' -u 'tesẗ.v0' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.⛄'
mpw_expect 'PeblLuqc6]Cala' -Fjson -R0 \ mpw_expect 'PeblLuqc6]Cala' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tlong -c4294967295 -a0 -p 'authentication' 'ẗesẗ.c+a0pa' -u 'tesẗ.v0' -M 'ẗest' -c4294967295 -a0 -p 'authentication' -C '' 'ẗesẗ.c+a0pa'
mpw_expect 'XelQac0@' -Fjson -R0 \ mpw_expect 'XelQac0@' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tmed -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.med' -u 'tesẗ.v0' -M 'ẗest' -tmed -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.med'
mpw_expect 'qS07SRc8' -Fjson -R0 \ mpw_expect 'qS07SRc8' -Fjson -R0 \
@@ -464,12 +492,16 @@ mpw_expect 'rivfutipe' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tname -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.name' -u 'tesẗ.v0' -M 'ẗest' -tname -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.name'
mpw_expect 'xir qebdohogo buno' -Fjson -R0 \ mpw_expect 'xir qebdohogo buno' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tphrase -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.phrase' -u 'tesẗ.v0' -M 'ẗest' -tphrase -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.phrase'
mpw_expect "RoAm3bJSvo@#loHSRA6\'" -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tmax -c1 -a0 -p 'identification' -C '' 'ẗesẗ.c1a0pi.max'
mpw_expect 'ragcoxudo' -Fjson -R0 \ mpw_expect 'ragcoxudo' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tname -c1 -a0 -p 'identification' -C '' 'ẗesẗ.c1a0pi' -u 'tesẗ.v0' -M 'ẗest' -c1 -a0 -p 'identification' -C '' 'ẗesẗ.c1a0pi'
mpw_expect 'm8]SiJHiAS@H@Rbw))34' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tmax -c1 -a0 -p 'recovery' -C '' 'ẗesẗ.c1a0pr.max'
mpw_expect 'kokl hov lowmaya xaf' -Fjson -R0 \ mpw_expect 'kokl hov lowmaya xaf' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tphrase -c1 -a0 -p 'recovery' -C '' 'ẗesẗ.c1a0pr' -u 'tesẗ.v0' -M 'ẗest' -c1 -a0 -p 'recovery' -C '' 'ẗesẗ.c1a0pr'
mpw_expect 'wi zanmu nug zuwidwe' -Fjson -R0 \ mpw_expect 'wi zanmu nug zuwidwe' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tphrase -c1 -a0 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a0pr.quesẗion' -u 'tesẗ.v0' -M 'ẗest' -c1 -a0 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a0pr.quesẗion'
# Finish # Finish

View File

@@ -166,6 +166,11 @@ const char *mpw_site_crypted_password_v0(
err( "Missing encrypted state." ); err( "Missing encrypted state." );
return NULL; return NULL;
} }
if (strlen( cipherText ) % 4 != 0) {
wrn( "Malformed encrypted state, not base64." );
// This can happen if state was stored in a non-encrypted form, eg. login in old mpsites.
return strdup( cipherText );
}
// Base64-decode // Base64-decode
uint8_t *cipherBuf = calloc( 1, mpw_base64_decode_max( cipherText ) ); uint8_t *cipherBuf = calloc( 1, mpw_base64_decode_max( cipherText ) );
@@ -184,8 +189,10 @@ const char *mpw_site_crypted_password_v0(
mpw_free( &plainBytes, bufSize ); mpw_free( &plainBytes, bufSize );
if (!plainText) if (!plainText)
err( "AES decryption error: %s", strerror( errno ) ); err( "AES decryption error: %s", strerror( errno ) );
else if (!mpw_utf8_strchars( plainText ))
wrn( "decrypted -> plainText: %zu bytes = illegal UTF-8 = %s", strlen( plainText ), mpw_hex( plainText, bufSize ) );
else else
trc( "decrypted -> plainText: %zu bytes = %s = %s", strlen( plainText ), plainText, mpw_hex( plainText, strlen( plainText ) ) ); trc( "decrypted -> plainText: %zu bytes = %s = %s", strlen( plainText ), plainText, mpw_hex( plainText, bufSize ) );
return plainText; return plainText;
} }

View File

@@ -176,6 +176,21 @@ MPMarshalledFile *mpw_marshal_file(
return file; return file;
} }
MPMarshalledFile *mpw_marshal_error(
MPMarshalledFile *file, MPMarshalErrorType type, const char *format, ...) {
file = mpw_marshal_file( file, NULL, NULL );
if (!file)
return NULL;
va_list args;
va_start( args, format );
file->error = (MPMarshalError){ type, mpw_strdup( mpw_vstr( format, args ) ) };
va_end( args );
return file;
}
void mpw_marshal_info_free( void mpw_marshal_info_free(
MPMarshalledInfo **info) { MPMarshalledInfo **info) {
@@ -228,6 +243,7 @@ void mpw_marshal_file_free(
mpw_marshal_info_free( &(*file)->info ); mpw_marshal_info_free( &(*file)->info );
mpw_marshal_data_free( &(*file)->data ); mpw_marshal_data_free( &(*file)->data );
mpw_free_string( &(*file)->error.message );
mpw_free( file, sizeof( MPMarshalledFile ) ); mpw_free( file, sizeof( MPMarshalledFile ) );
} }
@@ -539,7 +555,7 @@ static const char *mpw_marshal_write_flat(
const MPMarshalledData *data = file->data; const MPMarshalledData *data = file->data;
if (!data) { if (!data) {
file->error = (MPMarshalError){ MPMarshalErrorMissing, "Missing data." }; mpw_marshal_error( file, MPMarshalErrorMissing, "Missing data." );
return NULL; return NULL;
} }
@@ -583,9 +599,9 @@ static const char *mpw_marshal_write_flat(
} }
if (!out) if (!out)
file->error = (MPMarshalError){ MPMarshalErrorFormat, "Couldn't encode JSON." }; mpw_marshal_error( file, MPMarshalErrorFormat, "Couldn't encode JSON." );
else else
file->error = (MPMarshalError){ MPMarshalSuccess, NULL }; mpw_marshal_error( file, MPMarshalSuccess, NULL );
return out; return out;
} }
@@ -637,7 +653,7 @@ static const char *mpw_marshal_write_json(
// Section: "export" // Section: "export"
json_object *json_file = mpw_get_json_data( file->data ); json_object *json_file = mpw_get_json_data( file->data );
if (!json_file) { if (!json_file) {
file->error = (MPMarshalError){ MPMarshalErrorFormat, "Couldn't serialize export data." }; mpw_marshal_error( file, MPMarshalErrorFormat, "Couldn't serialize export data." );
return NULL; return NULL;
} }
@@ -650,9 +666,9 @@ static const char *mpw_marshal_write_json(
json_object_put( json_file ); json_object_put( json_file );
if (!out) if (!out)
file->error = (MPMarshalError){ MPMarshalErrorFormat, "Couldn't encode JSON." }; mpw_marshal_error( file, MPMarshalErrorFormat, "Couldn't encode JSON." );
else else
file->error = (MPMarshalError){ MPMarshalSuccess, NULL }; mpw_marshal_error( file, MPMarshalSuccess, NULL );
return out; return out;
} }
@@ -698,17 +714,17 @@ const char *mpw_marshal_write(
if (!file_) if (!file_)
mpw_marshal_file_free( &file ); mpw_marshal_file_free( &file );
else else
file->error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate data." }; mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't allocate data." );
return NULL; return NULL;
} }
if (!user->fullName || !strlen( user->fullName )) { if (!user->fullName || !strlen( user->fullName )) {
if (!file_) if (!file_)
mpw_marshal_file_free( &file ); mpw_marshal_file_free( &file );
else else
file->error = (MPMarshalError){ MPMarshalErrorMissing, "Missing full name." }; mpw_marshal_error( file, MPMarshalErrorMissing, "Missing full name." );
return NULL; return NULL;
} }
file->error = (MPMarshalError){ MPMarshalSuccess, NULL }; mpw_marshal_error( file, MPMarshalSuccess, NULL );
MPMasterKey masterKey = NULL; MPMasterKey masterKey = NULL;
if (user->masterKeyProvider) if (user->masterKeyProvider)
@@ -749,7 +765,7 @@ const char *mpw_marshal_write(
if (!file_) if (!file_)
mpw_marshal_file_free( &file ); mpw_marshal_file_free( &file );
else else
file->error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." }; mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't derive master key." );
return NULL; return NULL;
} }
@@ -807,7 +823,7 @@ const char *mpw_marshal_write(
const char *out = NULL; const char *out = NULL;
switch (outFormat) { switch (outFormat) {
case MPMarshalFormatNone: case MPMarshalFormatNone:
file->error = (MPMarshalError){ MPMarshalSuccess, NULL }; mpw_marshal_error( file, MPMarshalSuccess, NULL );
break; break;
case MPMarshalFormatFlat: case MPMarshalFormatFlat:
out = mpw_marshal_write_flat( file ); out = mpw_marshal_write_flat( file );
@@ -818,7 +834,7 @@ const char *mpw_marshal_write(
break; break;
#endif #endif
default: default:
file->error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Unsupported output format: %u", outFormat ) }; mpw_marshal_error( file, MPMarshalErrorFormat, "Unsupported output format: %u", outFormat );
break; break;
} }
if (out && file->error.type == MPMarshalSuccess) if (out && file->error.type == MPMarshalSuccess)
@@ -839,7 +855,7 @@ static void mpw_marshal_read_flat(
mpw_marshal_file( file, NULL, mpw_marshal_data_new() ); mpw_marshal_file( file, NULL, mpw_marshal_data_new() );
if (!file->data) { if (!file->data) {
file->error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate data." }; mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't allocate data." );
return; return;
} }
@@ -891,10 +907,7 @@ static void mpw_marshal_read_flat(
const char *headerName = mpw_get_token( &positionInLine, endOfLine, ":\n" ); const char *headerName = mpw_get_token( &positionInLine, endOfLine, ":\n" );
const char *headerValue = mpw_get_token( &positionInLine, endOfLine, "\n" ); const char *headerValue = mpw_get_token( &positionInLine, endOfLine, "\n" );
if (!headerName || !headerValue) { if (!headerName || !headerValue) {
file->error = (MPMarshalError){ mpw_marshal_error( file, MPMarshalErrorStructure, "Invalid header: %s", mpw_strndup( line, (size_t)(endOfLine - line) ) );
MPMarshalErrorStructure,
mpw_str( "Invalid header: %s", mpw_strndup( line, (size_t)(endOfLine - line) ) )
};
mpw_free_strings( &headerName, &headerValue, NULL ); mpw_free_strings( &headerName, &headerValue, NULL );
continue; continue;
} }
@@ -908,7 +921,7 @@ static void mpw_marshal_read_flat(
if (mpw_strcasecmp( headerName, "Algorithm" ) == OK) { if (mpw_strcasecmp( headerName, "Algorithm" ) == OK) {
unsigned long value = strtoul( headerValue, NULL, 10 ); unsigned long value = strtoul( headerValue, NULL, 10 );
if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast) if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast)
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user algorithm version: %s", headerValue ) }; mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid user algorithm version: %s", headerValue );
else else
algorithm = (MPAlgorithmVersion)value; algorithm = (MPAlgorithmVersion)value;
} }
@@ -923,7 +936,7 @@ static void mpw_marshal_read_flat(
if (mpw_strcasecmp( headerName, "Default Type" ) == OK) { if (mpw_strcasecmp( headerName, "Default Type" ) == OK) {
unsigned long value = strtoul( headerValue, NULL, 10 ); unsigned long value = strtoul( headerValue, NULL, 10 );
if (!mpw_type_short_name( (MPResultType)value )) if (!mpw_type_short_name( (MPResultType)value ))
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user default type: %s", headerValue ) }; mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid user default type: %s", headerValue );
else else
defaultType = (MPResultType)value; defaultType = (MPResultType)value;
} }
@@ -934,7 +947,7 @@ static void mpw_marshal_read_flat(
if (!headerEnded) if (!headerEnded)
continue; continue;
if (!fullName) if (!fullName)
file->error = (MPMarshalError){ MPMarshalErrorMissing, "Missing header: Full Name" }; mpw_marshal_error( file, MPMarshalErrorMissing, "Missing header: Full Name" );
if (positionInLine >= endOfLine) if (positionInLine >= endOfLine)
continue; continue;
@@ -967,13 +980,13 @@ static void mpw_marshal_read_flat(
str_counter = mpw_strdup( strtok( NULL, "" ) ); str_counter = mpw_strdup( strtok( NULL, "" ) );
mpw_free_string( &typeAndVersionAndCounter ); mpw_free_string( &typeAndVersionAndCounter );
} }
siteLoginState = mpw_get_token( &positionInLine, endOfLine, "\t\n" ); // TODO: Needs to be encoded if redacted? siteLoginState = mpw_get_token( &positionInLine, endOfLine, "\t\n" );
siteName = mpw_get_token( &positionInLine, endOfLine, "\t\n" ); siteName = mpw_get_token( &positionInLine, endOfLine, "\t\n" );
siteResultState = mpw_get_token( &positionInLine, endOfLine, "\n" ); siteResultState = mpw_get_token( &positionInLine, endOfLine, "\n" );
break; break;
} }
default: { default: {
file->error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Unexpected import format: %u", format ) }; mpw_marshal_error( file, MPMarshalErrorFormat, "Unexpected import format: %u", format );
continue; continue;
} }
} }
@@ -981,28 +994,24 @@ static void mpw_marshal_read_flat(
if (siteName && str_type && str_counter && str_algorithm && str_uses && str_lastUsed) { if (siteName && str_type && str_counter && str_algorithm && str_uses && str_lastUsed) {
MPResultType siteType = (MPResultType)strtoul( str_type, NULL, 10 ); MPResultType siteType = (MPResultType)strtoul( str_type, NULL, 10 );
if (!mpw_type_short_name( siteType )) { if (!mpw_type_short_name( siteType )) {
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site type: %s: %s", siteName, str_type ) }; mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid site type: %s: %s", siteName, str_type );
continue; continue;
} }
long long int value = strtoll( str_counter, NULL, 10 ); long long int value = strtoll( str_counter, NULL, 10 );
if (value < MPCounterValueFirst || value > MPCounterValueLast) { if (value < MPCounterValueFirst || value > MPCounterValueLast) {
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site counter: %s: %s", siteName, str_counter ) }; mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid site counter: %s: %s", siteName, str_counter );
continue; continue;
} }
MPCounterValue siteCounter = (MPCounterValue)value; MPCounterValue siteCounter = (MPCounterValue)value;
value = strtoll( str_algorithm, NULL, 0 ); value = strtoll( str_algorithm, NULL, 0 );
if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast) { if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast) {
file->error = (MPMarshalError){ mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid site algorithm: %s: %s", siteName, str_algorithm );
MPMarshalErrorIllegal, mpw_str( "Invalid site algorithm: %s: %s", siteName, str_algorithm )
};
continue; continue;
} }
MPAlgorithmVersion siteAlgorithm = (MPAlgorithmVersion)value; MPAlgorithmVersion siteAlgorithm = (MPAlgorithmVersion)value;
time_t siteLastUsed = mpw_timegm( str_lastUsed ); time_t siteLastUsed = mpw_timegm( str_lastUsed );
if (!siteLastUsed) { if (!siteLastUsed) {
file->error = (MPMarshalError){ mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid site last used: %s: %s", siteName, str_lastUsed );
MPMarshalErrorIllegal, mpw_str( "Invalid site last used: %s: %s", siteName, str_lastUsed )
};
continue; continue;
} }
@@ -1010,21 +1019,17 @@ static void mpw_marshal_read_flat(
mpw_marshal_data_set_num( siteCounter, file->data, "sites", siteName, "counter", NULL ); mpw_marshal_data_set_num( siteCounter, file->data, "sites", siteName, "counter", NULL );
mpw_marshal_data_set_num( siteAlgorithm, file->data, "sites", siteName, "algorithm", NULL ); mpw_marshal_data_set_num( siteAlgorithm, file->data, "sites", siteName, "algorithm", NULL );
mpw_marshal_data_set_num( siteType, file->data, "sites", siteName, "type", NULL ); mpw_marshal_data_set_num( siteType, file->data, "sites", siteName, "type", NULL );
mpw_marshal_data_set_str( siteResultState && strlen( siteResultState )? siteResultState: NULL, file->data, "sites", siteName, mpw_marshal_data_set_str( siteResultState, file->data, "sites", siteName, "password", NULL );
"password", NULL );
mpw_marshal_data_set_num( MPResultTypeDefault, file->data, "sites", siteName, "login_type", NULL ); mpw_marshal_data_set_num( MPResultTypeDefault, file->data, "sites", siteName, "login_type", NULL );
mpw_marshal_data_set_str( siteLoginState && strlen( siteLoginState )? siteLoginState: NULL, file->data, "sites", siteName, mpw_marshal_data_set_str( siteLoginState, file->data, "sites", siteName, "login_name", NULL );
"login_name", NULL );
mpw_marshal_data_set_num( strtol( str_uses, NULL, 10 ), file->data, "sites", siteName, "uses", NULL ); mpw_marshal_data_set_num( strtol( str_uses, NULL, 10 ), file->data, "sites", siteName, "uses", NULL );
if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &siteLastUsed ) )) if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &siteLastUsed ) ))
mpw_marshal_data_set_str( dateString, file->data, "sites", siteName, "last_used", NULL ); mpw_marshal_data_set_str( dateString, file->data, "sites", siteName, "last_used", NULL );
} }
else { else {
file->error = (MPMarshalError){ mpw_marshal_error( file, MPMarshalErrorMissing,
MPMarshalErrorMissing, "Missing one of: lastUsed=%s, uses=%s, type=%s, version=%s, counter=%s, loginName=%s, siteName=%s",
mpw_str( "Missing one of: lastUsed=%s, uses=%s, type=%s, version=%s, counter=%s, loginName=%s, siteName=%s", str_lastUsed, str_uses, str_type, str_algorithm, str_counter, siteLoginState, siteName );
str_lastUsed, str_uses, str_type, str_algorithm, str_counter, siteLoginState, siteName )
};
continue; continue;
} }
@@ -1044,7 +1049,7 @@ static void mpw_marshal_read_json(
mpw_marshal_file( file, NULL, mpw_marshal_data_new() ); mpw_marshal_file( file, NULL, mpw_marshal_data_new() );
if (!file->data) { if (!file->data) {
file->error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate data." }; mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't allocate data." );
return; return;
} }
@@ -1052,7 +1057,7 @@ static void mpw_marshal_read_json(
enum json_tokener_error json_error = json_tokener_success; enum json_tokener_error json_error = json_tokener_success;
json_object *json_file = json_tokener_parse_verbose( in, &json_error ); json_object *json_file = json_tokener_parse_verbose( in, &json_error );
if (!json_file || json_error != json_tokener_success) { if (!json_file || json_error != json_tokener_success) {
file->error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Couldn't parse JSON: %s", json_tokener_error_desc( json_error ) ) }; mpw_marshal_error( file, MPMarshalErrorFormat, "Couldn't parse JSON: %s", json_tokener_error_desc( json_error ) );
return; return;
} }
@@ -1074,9 +1079,9 @@ MPMarshalledFile *mpw_marshal_read(
if (!file) if (!file)
return NULL; return NULL;
file->error = (MPMarshalError){ MPMarshalSuccess, NULL }; mpw_marshal_error( file, MPMarshalSuccess, NULL );
if (!info) { if (!info) {
file->error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate info." }; mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't allocate info." );
return file; return file;
} }
@@ -1090,6 +1095,8 @@ MPMarshalledFile *mpw_marshal_read(
info->format = MPMarshalFormatJSON; info->format = MPMarshalFormatJSON;
#if MPW_JSON #if MPW_JSON
mpw_marshal_read_json( file, in ); mpw_marshal_read_json( file, in );
#else
mpw_marshal_error( file, MPMarshalErrorFormat, "JSON support is not enabled." );
#endif #endif
} }
} }
@@ -1116,13 +1123,13 @@ MPMarshalledUser *mpw_marshal_auth(
if (!file) if (!file)
return NULL; return NULL;
file->error = (MPMarshalError){ MPMarshalSuccess, NULL }; mpw_marshal_error( file, MPMarshalSuccess, NULL );
if (!file->info) { if (!file->info) {
file->error = (MPMarshalError){ MPMarshalErrorMissing, "File wasn't parsed yet." }; mpw_marshal_error( file, MPMarshalErrorMissing, "File wasn't parsed yet." );
return NULL; return NULL;
} }
if (!file->data) { if (!file->data) {
file->error = (MPMarshalError){ MPMarshalErrorMissing, "No input data." }; mpw_marshal_error( file, MPMarshalErrorMissing, "No input data." );
return NULL; return NULL;
} }
@@ -1132,43 +1139,43 @@ MPMarshalledUser *mpw_marshal_auth(
MPAlgorithmVersion algorithm = MPAlgorithmVersion algorithm =
mpw_default_n( MPAlgorithmVersionCurrent, mpw_marshal_data_get_num( file->data, "user", "algorithm", NULL ) ); mpw_default_n( MPAlgorithmVersionCurrent, mpw_marshal_data_get_num( file->data, "user", "algorithm", NULL ) );
if (algorithm < MPAlgorithmVersionFirst || algorithm > MPAlgorithmVersionLast) { if (algorithm < MPAlgorithmVersionFirst || algorithm > MPAlgorithmVersionLast) {
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user algorithm: %u", algorithm ) }; mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid user algorithm: %u", algorithm );
return NULL; return NULL;
} }
unsigned int avatar = mpw_default_n( 0U, mpw_marshal_data_get_num( file->data, "user", "avatar", NULL ) ); unsigned int avatar = mpw_default_n( 0U, mpw_marshal_data_get_num( file->data, "user", "avatar", NULL ) );
const char *fullName = mpw_marshal_data_get_str( file->data, "user", "full_name", NULL ); const char *fullName = mpw_marshal_data_get_str( file->data, "user", "full_name", NULL );
if (!fullName || !strlen( fullName )) { if (!fullName || !strlen( fullName )) {
file->error = (MPMarshalError){ MPMarshalErrorMissing, "Missing value for full name." }; mpw_marshal_error( file, MPMarshalErrorMissing, "Missing value for full name." );
return NULL; return NULL;
} }
MPIdenticon identicon = mpw_identicon_encoded( mpw_marshal_data_get_str( file->data, "user", "identicon", NULL ) ); MPIdenticon identicon = mpw_identicon_encoded( mpw_marshal_data_get_str( file->data, "user", "identicon", NULL ) );
const char *keyID = mpw_marshal_data_get_str( file->data, "user", "key_id", NULL ); const char *keyID = mpw_marshal_data_get_str( file->data, "user", "key_id", NULL );
MPResultType defaultType = mpw_default_n( MPResultTypeDefault, mpw_marshal_data_get_num( file->data, "user", "default_type", NULL ) ); MPResultType defaultType = mpw_default_n( MPResultTypeDefault, mpw_marshal_data_get_num( file->data, "user", "default_type", NULL ) );
if (!mpw_type_short_name( defaultType )) { if (!mpw_type_short_name( defaultType )) {
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user default type: %u", defaultType ) }; mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid user default type: %u", defaultType );
return NULL; return NULL;
} }
const char *str_lastUsed = mpw_marshal_data_get_str( file->data, "user", "last_used", NULL ); const char *str_lastUsed = mpw_marshal_data_get_str( file->data, "user", "last_used", NULL );
time_t lastUsed = mpw_timegm( str_lastUsed ); time_t lastUsed = mpw_timegm( str_lastUsed );
if (!lastUsed) { if (!lastUsed) {
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user last used: %s", str_lastUsed ) }; mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid user last used: %s", str_lastUsed );
return NULL; return NULL;
} }
MPMasterKey masterKey = NULL; MPMasterKey masterKey = NULL;
if (masterKeyProvider && !(masterKey = masterKeyProvider( algorithm, fullName ))) { if (masterKeyProvider && !(masterKey = masterKeyProvider( algorithm, fullName ))) {
file->error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." }; mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't derive master key." );
return NULL; return NULL;
} }
if (keyID && masterKey && !mpw_id_buf_equals( keyID, mpw_id_buf( masterKey, MPMasterKeySize ) )) { if (keyID && masterKey && !mpw_id_buf_equals( keyID, mpw_id_buf( masterKey, MPMasterKeySize ) )) {
file->error = (MPMarshalError){ MPMarshalErrorMasterPassword, "Master key doesn't match key ID." }; mpw_marshal_error( file, MPMarshalErrorMasterPassword, "Master key doesn't match key ID." );
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
return NULL; return NULL;
} }
MPMarshalledUser *user = NULL; MPMarshalledUser *user = NULL;
if (!(user = mpw_marshal_user( fullName, masterKeyProvider, algorithm ))) { if (!(user = mpw_marshal_user( fullName, masterKeyProvider, algorithm ))) {
file->error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate a new user." }; mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't allocate a new user." );
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_user_free( &user ); mpw_marshal_user_free( &user );
return NULL; return NULL;
@@ -1189,21 +1196,21 @@ MPMarshalledUser *mpw_marshal_auth(
algorithm = mpw_default_n( user->algorithm, mpw_marshal_data_get_num( siteData, "algorithm", NULL ) ); algorithm = mpw_default_n( user->algorithm, mpw_marshal_data_get_num( siteData, "algorithm", NULL ) );
if (algorithm < MPAlgorithmVersionFirst || algorithm > MPAlgorithmVersionLast) { if (algorithm < MPAlgorithmVersionFirst || algorithm > MPAlgorithmVersionLast) {
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site algorithm: %s: %u", siteName, algorithm ) }; mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid site algorithm: %s: %u", siteName, algorithm );
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_user_free( &user ); mpw_marshal_user_free( &user );
return NULL; return NULL;
} }
MPCounterValue siteCounter = mpw_default_n( MPCounterValueDefault, mpw_marshal_data_get_num( siteData, "counter", NULL ) ); MPCounterValue siteCounter = mpw_default_n( MPCounterValueDefault, mpw_marshal_data_get_num( siteData, "counter", NULL ) );
if (siteCounter < MPCounterValueFirst || siteCounter > MPCounterValueLast) { if (siteCounter < MPCounterValueFirst || siteCounter > MPCounterValueLast) {
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site counter: %s: %d", siteName, siteCounter ) }; mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid site counter: %s: %d", siteName, siteCounter );
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_user_free( &user ); mpw_marshal_user_free( &user );
return NULL; return NULL;
} }
MPResultType siteType = mpw_default_n( user->defaultType, mpw_marshal_data_get_num( siteData, "type", NULL ) ); MPResultType siteType = mpw_default_n( user->defaultType, mpw_marshal_data_get_num( siteData, "type", NULL ) );
if (!mpw_type_short_name( siteType )) { if (!mpw_type_short_name( siteType )) {
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site type: %s: %u", siteName, siteType ) }; mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid site type: %s: %u", siteName, siteType );
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_user_free( &user ); mpw_marshal_user_free( &user );
return NULL; return NULL;
@@ -1211,7 +1218,7 @@ MPMarshalledUser *mpw_marshal_auth(
const char *siteResultState = mpw_marshal_data_get_str( siteData, "password", NULL ); const char *siteResultState = mpw_marshal_data_get_str( siteData, "password", NULL );
MPResultType siteLoginType = mpw_default_n( MPResultTypeTemplateName, mpw_marshal_data_get_num( siteData, "login_type", NULL ) ); MPResultType siteLoginType = mpw_default_n( MPResultTypeTemplateName, mpw_marshal_data_get_num( siteData, "login_type", NULL ) );
if (!mpw_type_short_name( siteLoginType )) { if (!mpw_type_short_name( siteLoginType )) {
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site login type: %s: %u", siteName, siteLoginType ) }; mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid site login type: %s: %u", siteName, siteLoginType );
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_user_free( &user ); mpw_marshal_user_free( &user );
return NULL; return NULL;
@@ -1221,7 +1228,7 @@ MPMarshalledUser *mpw_marshal_auth(
str_lastUsed = mpw_marshal_data_get_str( siteData, "last_used", NULL ); str_lastUsed = mpw_marshal_data_get_str( siteData, "last_used", NULL );
time_t siteLastUsed = mpw_timegm( str_lastUsed ); time_t siteLastUsed = mpw_timegm( str_lastUsed );
if (!siteLastUsed) { if (!siteLastUsed) {
file->error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site last used: %s: %s", siteName, str_lastUsed ) }; mpw_marshal_error( file, MPMarshalErrorIllegal, "Invalid site last used: %s: %s", siteName, str_lastUsed );
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_user_free( &user ); mpw_marshal_user_free( &user );
return NULL; return NULL;
@@ -1231,7 +1238,7 @@ MPMarshalledUser *mpw_marshal_auth(
MPMarshalledSite *site = mpw_marshal_site( user, siteName, siteType, siteCounter, algorithm ); MPMarshalledSite *site = mpw_marshal_site( user, siteName, siteType, siteCounter, algorithm );
if (!site) { if (!site) {
file->error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate a new site." }; mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't allocate a new site." );
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_user_free( &user ); mpw_marshal_user_free( &user );
return NULL; return NULL;
@@ -1245,10 +1252,7 @@ MPMarshalledUser *mpw_marshal_auth(
// Clear Text // Clear Text
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
if (!masterKeyProvider || !(masterKey = masterKeyProvider( site->algorithm, user->fullName ))) { if (!masterKeyProvider || !(masterKey = masterKeyProvider( site->algorithm, user->fullName ))) {
file->error = (MPMarshalError){ mpw_marshal_error( file, MPMarshalErrorInternal, "Couldn't derive master key." );
MPMarshalErrorInternal,
"Couldn't derive master key."
};
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
mpw_marshal_user_free( &user ); mpw_marshal_user_free( &user );
return NULL; return NULL;

View File

@@ -223,6 +223,7 @@ const char *mpw_marshal_write(
MPMarshalledFile *mpw_marshal_read( MPMarshalledFile *mpw_marshal_read(
MPMarshalledFile *file, const char *in); MPMarshalledFile *file, const char *in);
/** Authenticate as the user identified by the given marshalled file. /** Authenticate as the user identified by the given marshalled file.
* @note This object stores a reference to the given key provider.
* @return A user object (allocated), or NULL if the file format provides no marshalling or a format error occurred. */ * @return A user object (allocated), or NULL if the file format provides no marshalling or a format error occurred. */
MPMarshalledUser *mpw_marshal_auth( MPMarshalledUser *mpw_marshal_auth(
MPMarshalledFile *file, const MPMasterKeyProvider masterKeyProvider); MPMarshalledFile *file, const MPMasterKeyProvider masterKeyProvider);
@@ -230,22 +231,32 @@ MPMarshalledUser *mpw_marshal_auth(
//// Creating. //// Creating.
/** Create a new user object ready for marshalling. /** Create a new user object ready for marshalling.
* @note This object stores copies of the strings assigned to it and manages their deallocation internally.
* @return A user object (allocated), or NULL if the fullName is missing or the marshalled user couldn't be allocated. */ * @return A user object (allocated), or NULL if the fullName is missing or the marshalled user couldn't be allocated. */
MPMarshalledUser *mpw_marshal_user( MPMarshalledUser *mpw_marshal_user(
const char *fullName, const MPMasterKeyProvider masterKeyProvider, const MPAlgorithmVersion algorithmVersion); const char *fullName, const MPMasterKeyProvider masterKeyProvider, const MPAlgorithmVersion algorithmVersion);
/** Create a new site attached to the given user object, ready for marshalling. /** Create a new site attached to the given user object, ready for marshalling.
* @note This object stores copies of the strings assigned to it and manages their deallocation internally.
* @return A site object (allocated), or NULL if the siteName is missing or the marshalled site couldn't be allocated. */ * @return A site object (allocated), or NULL if the siteName is missing or the marshalled site couldn't be allocated. */
MPMarshalledSite *mpw_marshal_site( MPMarshalledSite *mpw_marshal_site(
MPMarshalledUser *user, MPMarshalledUser *user,
const char *siteName, const MPResultType resultType, const MPCounterValue siteCounter, const MPAlgorithmVersion algorithmVersion); const char *siteName, const MPResultType resultType, const MPCounterValue siteCounter, const MPAlgorithmVersion algorithmVersion);
/** Create a new question attached to the given site object, ready for marshalling. /** Create a new question attached to the given site object, ready for marshalling.
* @note This object stores copies of the strings assigned to it and manages their deallocation internally.
* @return A question object (allocated), or NULL if the marshalled question couldn't be allocated. */ * @return A question object (allocated), or NULL if the marshalled question couldn't be allocated. */
MPMarshalledQuestion *mpw_marshal_question( MPMarshalledQuestion *mpw_marshal_question(
MPMarshalledSite *site, const char *keyword); MPMarshalledSite *site, const char *keyword);
/** Create or update a marshal file descriptor. /** Create or update a marshal file descriptor.
* @param file If NULL, a new file will be allocated. Otherwise, the given file will be updated and the updated file returned.
* @param info If NULL, the file's info will be left as-is, otherwise it will be replaced by the given one. The file will manage the info's deallocation.
* @param data If NULL, the file's data will be left as-is, otherwise it will be replaced by the given one. The file will manage the data's deallocation.
* @return The given file or new (allocated) if file is NULL; or NULL if the user is missing or the file couldn't be allocated. */ * @return The given file or new (allocated) if file is NULL; or NULL if the user is missing or the file couldn't be allocated. */
MPMarshalledFile *mpw_marshal_file( MPMarshalledFile *mpw_marshal_file(
MPMarshalledFile *file, MPMarshalledInfo *info, MPMarshalledData *data); MPMarshalledFile *file, MPMarshalledInfo *info, MPMarshalledData *data);
/** Record a marshal error.
* @return The given file or new (allocated) if file is NULL; or NULL if the file couldn't be allocated. */
MPMarshalledFile *mpw_marshal_error(
MPMarshalledFile *file, MPMarshalErrorType type, const char *format, ...);
//// Disposing. //// Disposing.

View File

@@ -25,6 +25,7 @@
#endif #endif
MP_LIBS_BEGIN MP_LIBS_BEGIN
#define __STDC_WANT_LIB_EXT1__ 1
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>

View File

@@ -607,6 +607,9 @@ const uint8_t *mpw_unhex(const char *hex, size_t *length) {
size_t mpw_utf8_charlen(const char *utf8String) { size_t mpw_utf8_charlen(const char *utf8String) {
if (!utf8String)
return 0;
// Legal UTF-8 byte sequences: <http://www.unicode.org/unicode/uni2errata/UTF-8_Corrigendum.html> // Legal UTF-8 byte sequences: <http://www.unicode.org/unicode/uni2errata/UTF-8_Corrigendum.html>
unsigned char utf8Char = (unsigned char)*utf8String; unsigned char utf8Char = (unsigned char)*utf8String;
if (utf8Char >= 0x00 && utf8Char <= 0x7F) if (utf8Char >= 0x00 && utf8Char <= 0x7F)
@@ -624,8 +627,9 @@ size_t mpw_utf8_charlen(const char *utf8String) {
size_t mpw_utf8_strchars(const char *utf8String) { size_t mpw_utf8_strchars(const char *utf8String) {
size_t strchars = 0, charlen; size_t strchars = 0, charlen;
for (char *remaining = (char *)utf8String; (charlen = mpw_utf8_charlen( remaining )); remaining += charlen) for (char *remaining = (char *)utf8String; remaining && *remaining; remaining += charlen, ++strchars)
++strchars; if (!(charlen = mpw_utf8_charlen( remaining )))
return 0;
return strchars; return strchars;
} }

View File

@@ -256,9 +256,9 @@ bool mpw_id_buf_equals(MPKeyID id1, MPKeyID id2);
//// String utilities. //// String utilities.
/** @return The byte length of the UTF-8 character at the start of the given string. */ /** @return The byte length of the UTF-8 character at the start of the given string or 0 if it is NULL, empty or not a legal UTF-8 character. */
size_t mpw_utf8_charlen(const char *utf8String); size_t mpw_utf8_charlen(const char *utf8String);
/** @return The amount of UTF-8 characters in the given string. */ /** @return The amount of UTF-8 characters in the given string or 0 if it is NULL, empty, or contains bytes that are not legal in UTF-8. */
size_t mpw_utf8_strchars(const char *utf8String); size_t mpw_utf8_strchars(const char *utf8String);
/** Drop-in for memdup(3). /** Drop-in for memdup(3).
* @return A buffer (allocated, len) with len bytes copied from src or NULL if src is missing or the buffer could not be allocated. */ * @return A buffer (allocated, len) with len bytes copied from src or NULL if src is missing or the buffer could not be allocated. */

View File

@@ -346,7 +346,7 @@ public interface MPAlgorithm {
@Nonnull @Nonnull
@Override @Override
public Version version() { public Version version() {
return MPAlgorithm.Version.V0; return this;
} }
@Nonnull @Nonnull

View File

@@ -20,7 +20,6 @@ package com.lyndir.masterpassword;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.primitives.UnsignedInteger; import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import java.util.Arrays; import java.util.Arrays;
import java.util.EnumMap; import java.util.EnumMap;
@@ -56,6 +55,7 @@ public class MPMasterKey {
} }
@Override @Override
@SuppressWarnings("deprecation")
protected void finalize() protected void finalize()
throws Throwable { throws Throwable {