2
0

Compare commits

...

111 Commits

Author SHA1 Message Date
Maarten Billemont
0b6e43a18f The root of the users VC interferes with touches from the sites VC when it appears. 2021-11-03 21:13:04 -04:00
Maarten Billemont
c94c52f4b6 Fix migration tips no longer interactable when logged in. 2021-11-03 16:51:37 -04:00
Maarten Billemont
5de9b05299 Move Spectre migration dialogs fully global & make closeable. 2021-11-01 21:16:00 -04:00
Maarten Billemont
f27607e63c fixup! Support for unset login type. 2021-11-01 20:52:04 -04:00
Maarten Billemont
0b45dc584f Fix deadlock when loadStore posts notifications to the main thread. 2021-11-01 20:07:13 -04:00
Maarten Billemont
88a4d7ba4d Support for unset login type. 2021-11-01 20:06:37 -04:00
Maarten Billemont
94a6c925bc Update Sentry SDK. 2021-10-31 14:32:31 -04:00
Maarten Billemont
eda9749cf2 Time to crack script updates.
[ADDED]     Calculate cost for cracking a password.
[UPDATED]   Hardware cost data based on various GPUs with updated hashcat metrics.
[ADDED]     Ability to calculate the strength of an arbitrary password.
2021-10-31 14:17:21 -04:00
Maarten Billemont
4c096555d0 Spectre migration updates.
[ADDED]     Migration prompt to sign-in screen.
[FIXED]     Spectre Apple ID.
2021-10-31 14:15:46 -04:00
Maarten Billemont
403c45519a 2.7-java-12 2021-03-02 19:33:53 -05:00
Maarten Billemont
8d33ff8ec5 Fix password field manipulation bugs.
[FIXED]     By stubbing the password field's document, we broke some editing capabilities. Stub the document in a way that respects its length.
2021-03-02 19:31:47 -05:00
Maarten Billemont
c38f713f05 Update site after release. 2021-02-18 11:27:18 -05:00
Maarten Billemont
d59595824b Fix path for C release VERSION and TAG. 2021-02-18 10:15:40 -05:00
Maarten Billemont
2b78449a48 Update site after release. 2021-02-18 09:58:10 -05:00
Maarten Billemont
2eda9b1152 2.7-java-11 2021-02-18 09:44:11 -05:00
Maarten Billemont
8a032ba891 Master Password is moving to Spectre!
https://gitlab.com/spectre.app
https://spectre.app/

Master Password is no longer actively maintained from here on out.
2021-02-17 23:40:51 -05:00
Maarten Billemont
eda34f6b0b Update binaries for latest core API. 2021-02-11 15:19:51 -05:00
Maarten Billemont
6e1855b00c fixup! Fix ANSI C11 support. 2021-02-11 15:07:59 -05:00
Maarten Billemont
90aaf23bb5 Build script update.
- Fixed build on Windows
- Improved documentation
- Fixed arch logic, separate standard arch from host name
2021-02-11 14:12:06 -05:00
Maarten Billemont
2e9c79f6b3 Fix ANSI C11 support. 2021-02-11 14:11:09 -05:00
Maarten Billemont
83fa6c39bc Site toolbar buttons should toggle on site, not result. 2021-02-08 14:21:52 -05:00
Maarten Billemont
913208255e Temporarily disable findsecbugs due to bug.
https://stackoverflow.com/a/62894507/58803
2021-02-08 14:20:42 -05:00
Maarten Billemont
963a1222be Update SpotBugs. 2021-02-08 14:20:29 -05:00
Maarten Billemont
a1264e0f91 Move main thread assert to the right spot. 2020-10-14 09:24:15 -04:00
Maarten Billemont
4f0065fba8 Update to tighten warnings configuration. 2020-10-14 09:23:46 -04:00
Maarten Billemont
b2c688a1ce Always scope sites query to active user. 2020-09-03 14:31:10 -04:00
Maarten Billemont
aee1030758 Copy device identifier from macOS menu item. 2020-09-03 14:10:08 -04:00
Maarten Billemont
f665aeccc4 Load store synchronously to not return racy or invalid contexts. 2020-09-03 13:57:16 -04:00
Maarten Billemont
e58b9ef34f Fetch request syntax update. 2020-09-03 13:56:46 -04:00
Maarten Billemont
968de6026f Build fixes. 2020-09-03 13:56:10 -04:00
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
Maarten Billemont
14f919584b Project update. 2020-04-16 17:46:39 -04:00
Maarten Billemont
16f6c3c593 Update preference menu item state. 2020-04-16 17:13:23 -04:00
Maarten Billemont
63ca2ae83e Can lower deployment target to 10.10 2020-04-16 17:13:08 -04:00
Maarten Billemont
1c3ea3826f Move identicon and toID to mpw native.
Clean out all unused Java MPAlgorithm stuff.

Fix master password entries stuck in memory.
2020-04-15 19:09:02 -04:00
Maarten Billemont
ff9596aef0 Update log sink API. 2020-04-15 19:08:11 -04:00
Maarten Billemont
b79ed1ca0b Fix JNI native symbol names after refactor. 2020-04-14 20:07:21 -04:00
Maarten Billemont
9a362f13a3 Use new mpw log sink logging mechanism for JNI logging. 2020-04-14 19:13:49 -04:00
Maarten Billemont
11d6660e5a Sinks report success now & fallback sink triggers on failure.
We now trigger the fallback sink (currently, mpw_log_sink_file) if no
sinks successfully handled the log message.
2020-04-14 19:12:28 -04:00
Maarten Billemont
62e1563fa6 Ensure b64Cursor chars are unsigned when used as index. 2020-04-14 19:11:08 -04:00
Maarten Billemont
9b8ff7ad0c Kill debug code. 2020-04-13 21:33:28 -04:00
Maarten Billemont
f1fc07cf9e FaceID privacy message. 2020-04-13 18:56:57 -04:00
Maarten Billemont
00ac788f4f Consistent flow for enabling notifications. 2020-04-13 18:12:41 -04:00
Maarten Billemont
514c383310 Obsolete sources. 2020-04-12 22:38:40 -04:00
Maarten Billemont
9a3bcd1c6f Fix bad override of forKeyedSubscript: 2020-04-12 21:39:13 -04:00
Maarten Billemont
d30d469663 Sites list scroll & keyboard fixes. 2020-04-12 19:16:13 -04:00
Maarten Billemont
b428ee0003 Honour animated property better.
Also: don't need to force a layout if not in an animation block.
2020-04-12 19:14:25 -04:00
Maarten Billemont
f80ffd078b Move dependencies to Podfile. 2020-04-12 19:12:45 -04:00
Maarten Billemont
7f1a28ffa7 Fixes and updates to iOS.
Move dependencies into Podfile where possible.

IASK doesn't wrap properly; just removed it.

Fixes to safe area and content insets.
2020-04-11 12:13:48 -04:00
133 changed files with 3219 additions and 3553 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

15
.gitmodules vendored
View File

@ -1,21 +1,9 @@
[submodule "External/Pearl"] [submodule "External/Pearl"]
path = platform-darwin/External/Pearl path = platform-darwin/External/Pearl
url = git://github.com/Lyndir/Pearl.git url = git://github.com/Lyndir/Pearl.git
[submodule "External/InAppSettingsKit"]
path = platform-darwin/External/InAppSettingsKit
url = git://github.com/lhunath/InAppSettingsKit.git
[submodule "External/KCOrderedAccessorFix"]
path = platform-darwin/External/KCOrderedAccessorFix
url = https://github.com/lhunath/KCOrderedAccessorFix.git
[submodule "External/AttributedMarkdown"] [submodule "External/AttributedMarkdown"]
path = platform-darwin/External/AttributedMarkdown path = platform-darwin/External/AttributedMarkdown
url = https://github.com/dreamwieber/AttributedMarkdown.git url = https://github.com/dreamwieber/AttributedMarkdown.git
[submodule "External/uicolor-utilities"]
path = platform-darwin/External/uicolor-utilities
url = git://github.com/lhunath/uicolor-utilities.git
[submodule "External/jrswizzle"]
path = platform-darwin/External/jrswizzle
url = git://github.com/jonmarimba/jrswizzle.git
[submodule "MasterPassword/Web/js/mpw-js"] [submodule "MasterPassword/Web/js/mpw-js"]
path = platform-independent/web/js/mpw-js path = platform-independent/web/js/mpw-js
url = https://github.com/tmthrgd/mpw-js.git url = https://github.com/tmthrgd/mpw-js.git
@ -27,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

@ -7,6 +7,6 @@ FROM debian:stable-slim
RUN mkdir -p /usr/share/man/man1 RUN mkdir -p /usr/share/man/man1
RUN apt-get update && apt-get install -y default-jdk-headless git-core bash libtool automake autoconf make g++-multilib RUN apt-get update && apt-get install -y default-jdk-headless git-core bash libtool automake autoconf make g++-multilib
RUN git clone --depth=3 $(: --shallow-submodules) --recurse-submodules --branch rewrite https://gitlab.com/MasterPassword/MasterPassword.git /mpw RUN git clone --depth=3 $(: --shallow-submodules) --recurse-submodules https://gitlab.com/MasterPassword/MasterPassword.git /mpw
RUN cd /mpw && ./gradlew -i clean RUN cd /mpw && ./gradlew -i clean
RUN cd /mpw && git pull && git log -1 && ./gradlew -i check RUN cd /mpw && git pull && git log -1 && ./gradlew -i check

191
README.md
View File

@ -4,197 +4,38 @@ Master Password is a completely new way of thinking about passwords.
It consists of an algorithm that implements the core idea and applications for various platforms making the alogirthm available to users on a variety of devices and platforms. It consists of an algorithm that implements the core idea and applications for various platforms making the alogirthm available to users on a variety of devices and platforms.
To skip the intro and go straight to the information on how to use the code, [click here](#source-code).
Master Password is available for [📲 iOS](https://itunes.apple.com/app/id510296984), [🖥 macOS](https://masterpassword.app/masterpassword-mac.zip), [📲 Android](https://masterpassword.app/masterpassword-android.apk), [🖥 Desktop](https://masterpassword.app/masterpassword-gui.jar), and [⌨ Console](https://masterpassword.app/masterpassword-cli.tar.gz). ## PROJECT MOVED
Master Password is also available from: [macOS: Homebrew](https://brew.sh/) (`brew install mpw`) and [Docker](https://www.docker.com/) (`docker run -ti registry.gitlab.com/masterpassword/masterpassword`). Master Password is announcing a massive rewrite, modernizing the solution and clearing the way for exciting new capabilities.
Get in touch if you are interested in adding Master Password to any other package managers.
There are many reasons for using Master Password instead of an ordinary password manager, read below for the details, but if you want my personal favourites, they would be: The project is re-launching as [Spectre](https://gitlab.com/spectre.app), still fully open-source Free Software here on GitLab.
- I don't need to worry about keeping backups of my countless authentication credentials. Any interested parties are invited to participate in [the alpha or beta programs](https://spectre.app/#beta), to participate in the new Spectre identity platform.
- I don't need to worry that when I travel, I might not have access to my passwords vault.
- I don't need to trust an external party, proprietary code or a service to be online and stay online.
- If I feel at risk of my device being stolen or confiscated, I can set a fake master password, delete my user or wipe it worry-free.
We also have a [Frequently Asked Questions](#faq). The Beta program is now open for users with iOS devices. The Spectre Beta introduces a new app, rewritten under Swift, and new capabilities such as AutoFill.
## What is a password?
Ah, the "password". Somehow, passwords have become the default solution to authentication across the web. We've long since accepted this as the way things are, but let's stop to think for a moment about what passwords actually are:
A password is a secret that is known only to the party providing a service and the party that should be allowed access to this service.
Simple enough - a secret that you know and your website knows but nobody else, thereby guaranteeing that you and only you have access to your account on this website. Unfortunately, in practice, the ubiquitous use of passwords has us completely overwhelmed. And the only way we can cope with that is by finding ways of making the problem manageable.
## What's the problem?
Coming up with a secret password is pretty easy. Say you're organizing a secret meeting and will only let people in if they know the password at the door. You tell those you trust, the password for tonight's meeting is "purple oranges with a smile".
The problem we have in our daily lives, however, is the fact that we need secret passwords for almost everything now. A password for our email, twitter, 9gag, facebook, imgur, amazon, ebay, paypal, bank, reddit, etc. And every time we want to use a new site, we need another one. The problem now becomes clear: passwords are meant to be remembered and recalled with ease when needed, but this becomes impossible when we have secrets for every distinct activity in our lives.
We cannot recall passwords the way we are expected to when there are too many.
## Coping
Life gives us no advice on how to deal with this problem. So we find our own ways:
- We use a single personal secret for all our websites, thereby violating the secrecy of these passwords (eg. you've given your email secret to twitter).
- We use simple variations of a personal secret or pattern, thereby trivializing the complexity of these passwords (eg. google98, twitter98; reversals, eg. 8991elgoog)
- We use external means of remembering passwords, thereby increasing the risk of loss (both loss of access when we lose the tool and theft when a thief finds our tool)
These coping mechanisms come in various forms, and they all have down-sides, because at the root of each of these lies an undeniable truth:
Our passwords are no longer true to the original definition.
## Master Password's approach
The theory behind Master Password starts with accepting that it is impossible to keep track of passwords for all your accounts. Instead, we return to the core premise of the password: a secret phrase that you can remember easily, all by yourself.
Master Password solves this problem by letting you remember one and only one password. You use this password with Master Password only. Master Password then gives you access to any website or service you want by creating a website-specific key for it.
1. You sign into Master Password using your one password.
2. You ask Master Password for the key to enter your website, eg. twitter.
3. You log into twitter using your username and the key from Master Password.
Master Password is *not* a password manager. It does not store your website passwords. Therefore, there is zero risk of you losing your website passwords (or them falling in the wrong hands). Master Password simply uses your one password and the name of the site to generate a site-specific secret.
## Benefits
- You don't need to think up a new strong password every time you make a new account - Master Password gives you the key for it.
- You don't need to try remembering a password you created two years ago for that one account - Master Password just gives you the key for it.
- You don't need to worry about getting into that account you made at work after you come home because you don't have your office passwords with you - Master Password is availale everywhere, even offline.
- You don't need to try to keep password lists in sync or stored somewhere easily accessible - Master Password keys can be created anywhere.
- You don't need to worry what you'll do if your computer dies or you need to log into your bank while you're in the airport transit zone - your Master Password keys are always available, even when starting empty.
- You don't need to worry about your password manager website getting hacked, your phone getting duplicated, somebody taking a picture of your passwords book - Master Password stores no secrets.
## How does it work?
The details of how Master Password works [are available here](https://masterpassword.app/masterpassword-algorithm.pdf).
In short:
master-key = SCRYPT( user-name, master-password )
site-key = HMAC-SHA-256( site-name . site-counter, master-key )
site-password = PW-TEMPLATE( site-key, site-template )
Master Password can derive the `site-password` in an entirely stateless manner. It is therefore better defined as a calculator than a manager. It is the user's responsibility to remember the inputs: `user-name`, `master-password`, `site-name`, `site-counter` and `site-template`.
We standardize `user-name` as your full name, `site-name` as the domain name of the site, `site-counter` to `1` (unless you explicitly increment it) and `site-template` to `Long Password`; as a result the only token the user really needs to remember actively is `master-password`.
All development effort has moved to the Spectre project. Master Password is no longer actively maintained.
## FAQ ## FAQ
1. If I lose my master password and need to set a new one, will I need to change all of my site passwords? 1. Has there been a change in ownership?
Yes. If your master password is compromised, it is only sensible for you to change all of your site passwords. Just like if you lose the keys in your pocket, you'll have to change all the locks they open. Master Password effectively enforces this security practice. No. This project is still owned and maintained exclusively by [Maarten Billemont](https://gitlab.com/lhunath).
2. But what if I just forget my master password or I just want to change it to something else? 2. How can I trust Spectre?
Sorry, still yes. Your master password is the secret component to your Master Password identity. If it changes, your identity changes. I wholly encourage you to think very carefully about what makes for a really memorable and good master password before just diving in with something lazy. A short phrase works great, eg. `banana coloured duckling`. Spectre's code-base is based on the Master Password code-base. The algorithm is exactly the same. The license is the same. The author is the same.
3. Doesn't this mean an attacker can reverse my master password from any of my site passwords? The applications are being rewritten for modern platforms. Spectre has the exact same trust parameters as Master Password.
Technically, yes. Practically, no. 3. Why is the project changing?
You could argue that site passwords are "breadcrumbs" of your master password, but the same argument would suggest encrypted messages are breadcrumbs to the encryption key. Encryption works because it is computationally unfeasible to "guess" the encryption key that made the encrypted message, just like Master Password works because it is computationally unfeasible to "guess" your master password that made the site password. Several reasons, in fact. Master Password as a name is too ubiquitous in popular culture, which is a cause for confusion. We are also looking to evolve the capabilities of the platform beyond simply passwords, into a fully decentralized identity platform. We're also looking to be socially inclusive and conscious of the implicit biases present in terminology we've inherited from past societies.
4. The second step is just a HMAC-SHA-256, doesn't that make the SCRYPT completely pointless? All that said - Spectre is the mark of a complete refresh of the Master Password solution. Hope you'll love it!
No. They are used for different reasons and one is not weaker than the other. 4. How do I migrate?
HMAC-SHA-256 is much faster to compute than SCRYPT, which leads some people to think "all an attacker needs to do is brute-force the SHA and ignore the SCRYPT". The reality is that the HMAC-SHA-256 guards a 64-byte authentication key (the `master-key`) which makes the search space for brute-forcing the HMAC wildly too large to compute. Master Password export files are fully supported by Spectre. Migration mechanism exist in Master Password which will trigger as soon as you install Spectre; for instance, as soon as you install Spectre on your iOS device, Master Password will show a pop-up which will copy your user over at a tap.
The `master-password` on the other hand, is only a simple phrase, which means its search space is much smaller. This is why it is guarded by a much tougher SCRYPT operation.
5. I have another question.
Please don't hesitate to [get in touch](#support), we're more than happy to answer all your Master Password questions. Any problems or suggestions can be reported [as GitLab issues](https://gitlab.com/MasterPassword/MasterPassword/issues/).
# Source Code
Master Password's algorithm is [documented](https://masterpassword.app/masterpassword-algorithm.pdf) and its implementation is [Free Software (GPLv3)](LICENSE).
## Components
There are several components available here. As an end-user, you can currently use the iOS app, the Android app, the OS X app, the Java desktop app, the C CLI app or the Java CLI app. There are also several components that are useful for developers:
- `core/c`: This is the reference implementation of the Master Password algorithm, written in C.
- `core/java/algorithm`: This is a Java implementation of the Master Password algorithm.
- `core/java/model`: This is an object model to simplify use of Master Password by Java applications.
- `core/java/tests`: These are Java integration tests designed to ensure Master Password performs as expected.
- `platform-android`: This is the official Android implementation of Master Password in Java.
- `platform-darwin`: This is the official iOS and OS X implementation of Master Password in Objective-C.
- `platform-independent/cli-c`: This is the platform-independent console implementation of Master Password, written in C.
- `platform-independent/cli-java`: This is the platform-independent console implementation of Master Password, written in Java.
- `platform-independent/gui-java`: This is the platform-independent desktop implementation of Master Password, written in Java.
- `platform-independent/web-js`: This is the platform-independent browser application for Master Password, written in JavaScript.
## Building and running
### macOS or iOS
Make sure you have all relevant submodules checked out.
Go into `platform-darwin` and open `MasterPassword.xcworkspace` in Xcode. Select the desired target from the Scheme Selector and build, run or archive.
### Web
Make sure you have all relevant submodules checked out.
Go into `platform-independent/web-js` and open `index.html` in your browser. You should be able to run this locally, there is no need for hosting or an application server.
### Java
Go into the `gradle` directory and run `./gradlew build`. All Java components will then be built:
- `platform-independent/gui-java/build/distributions`:
contains an archive with the Master Password Java GUI. Unpack it and run the `gui` script.
- `platform-independent/cli-java/build/distributions`:
contains an archive with the Master Password Java command-line interface. Unpack it and run the `cli` script.
- `platform-android/build/outputs/apk`:
contains the Android application package. Install it on your Android device.
Note that in order to build the Android application, you will need to have the Android SDK installed and either have the environment variable `ANDROID_HOME` set to its location or a `gradle/local.properties` file with its location, eg. (for Homebrew users who installed the SDK using `brew install android-sdk`):
sdk.dir=/usr/local/opt/android-sdk
### Native CLI
Go into the `platform-independent/cli-c` directory and run `./build`. The native command-line client will then be built.
For detailed instructions, see [the native CLI instructions](platform-independent/c/README.md).
## Support
Feel free to contribute by forking the project, reporting issues or joining the discussion on:
- [Gitter](https://gitter.im/lyndir/MasterPassword)
- #masterpassword (on chat.freenode.net)
- #masterpassword:lyndir.com (on Matrix)
- masterpassword@lyndir.com

View File

@ -16,7 +16,7 @@ buildscript {
allprojects { allprojects {
group = 'com.lyndir.masterpassword' group = 'com.lyndir.masterpassword'
version = '2.7.10' version = '2.7.12'
} }
subprojects { subprojects {
@ -29,7 +29,7 @@ subprojects {
maven { url 'https://maven.lyndir.com' } maven { url 'https://maven.lyndir.com' }
} }
dependencies { dependencies {
spotbugsPlugins group: 'com.h3xstream.findsecbugs', name: 'findsecbugs-plugin', version: '1.9.0' //spotbugsPlugins group: 'com.h3xstream.findsecbugs', name: 'findsecbugs-plugin', version: '1.11.0'
//spotbugsPlugins group: 'com.mebigfatguy.sb-contrib', name: 'sb-contrib', version: '7.4.6' //spotbugsPlugins group: 'com.mebigfatguy.sb-contrib', name: 'sb-contrib', version: '7.4.6'
} }
spotbugs { spotbugs {

View File

@ -8,8 +8,9 @@
# - initialize # - initialize
# - needs # - needs
# - clean & exit (only if script was ran with "clean" argument) # - clean & exit (only if script was ran with "clean" argument)
# - check & exit (only if target has already been successfully built)
# - prepare # - prepare
# - clean # - create
# - config # - config
# - target # - target
# - prepare # - prepare
@ -23,12 +24,14 @@
# For example: # For example:
# target_prepare() { make -s distclean; } # target_prepare() { make -s distclean; }
# target_configure() { _target_configure "$@" --enable-minimal; } # target_configure() { _target_configure "$@" --enable-minimal; }
#
set -e set -e
PATH+=:/usr/local/bin PATH+=:/usr/local/bin
# needs <binary> ... # needs <binary> ...
# #
# Utility for ensuring all tools needed by the script are installed prior to starting. # Utility for ensuring all tools needed by the script are installed prior to starting.
#
needs() { _needs "$@"; } needs() { _needs "$@"; }
_needs() { _needs() {
local failed=0 local failed=0
@ -48,7 +51,8 @@ _needs() {
# initialize <prefix> <platform> # initialize <prefix> <platform>
# #
# The build script invokes this once prior to all other actions if the user wants a clean slate. # The build script invokes this once prior to all other actions.
#
initialize() { _initialize "$@"; } initialize() { _initialize "$@"; }
_initialize() { _initialize() {
initialize_needs "$@" initialize_needs "$@"
@ -56,15 +60,23 @@ _initialize() {
# initialize_needs <prefix> <platform> # initialize_needs <prefix> <platform>
# #
# Check if all tools needed for the default implementations are available. # Check if all tools required to configure and build for the platform are available.
#
# By default, this will check for:
# - Windows: MSBuild
# - Other: `libtool` (for libtoolize), `automake` (for aclocal), `autoconf` (for autoreconf) and make
# #
# By default, this will check for `libtool` (for libtoolize), `automake` (for aclocal), `autoconf` (for autoreconf) and make.
initialize_needs() { _initialize_needs "$@"; } initialize_needs() { _initialize_needs "$@"; }
_initialize_needs() { _initialize_needs() {
if [[ $platform = windows ]]; then if [[ $platform = windows ]]; then
needs cmd needs cmd
export VSINSTALLDIR="${VSINSTALLDIR:-$(cd "$(cygpath -F 0x002a)/Microsoft Visual Studio"/*/*/Common7/.. && pwd)}" for dir in "$VSINSTALLDIR" "$(cygpath -F 0x002a)/Microsoft Visual Studio"/*/*/Common7/..; do
[[ -e "$VSINSTALLDIR/Common7/Tools/VsMSBuildCmd.bat" ]] || { echo >&2 "Missing: msbuild. Please install 'Build Tools for Visual Studio'. See https://visualstudio.microsoft.com/downloads/?q=build+tools"; return 1; } dir=$( [[ $dir ]] && cd "$dir" && [[ -e "Common7/Tools/VsMSBuildCmd.bat" ]] && cygpath -w "$PWD" ) && \
export VSINSTALLDIR=$dir && echo "Using MSBuild: $VSINSTALLDIR" && return
done
echo >&2 "Missing: msbuild. Please install 'Build Tools for Visual Studio'. See https://visualstudio.microsoft.com/downloads/?q=build+tools"
return 1
else else
needs libtool:libtoolize,glibtoolize automake autoconf make needs libtool:libtoolize,glibtoolize automake autoconf make
fi fi
@ -74,13 +86,18 @@ _initialize_needs() {
# #
# Fully clean up the library code, restoring it to a pristine state. # Fully clean up the library code, restoring it to a pristine state.
# #
# By default, this will wipe the prefix, run `make distclean` and `git clean -fdx`. # By default, this will:
# - Windows: `msbuild /t:Clean`, or
# - Makefile: run `make distclean`, or
# - GIT: `git clean -fdx`
#
# Finally, it will wipe the prefix.
#
clean() { _clean "$@"; } clean() { _clean "$@"; }
_clean() { _clean() {
if [[ $platform = windows ]]; then if [[ $platform = windows ]]; then
printf '"%%VSINSTALLDIR%%\Common7\Tools\VsMSBuildCmd.bat" && msbuild /t:Clean' > .clean.bat PATH="$(cygpath "$VSINSTALLDIR")/Common7/Tools:$PATH" \
cmd //c .clean.bat cmd /v /c 'VsMSBuildCmd && for %s in (*.sln) do msbuild /t:Clean %s'
rm -f .clean.bat
elif [[ -e Makefile ]] && make -s distclean; then : elif [[ -e Makefile ]] && make -s distclean; then :
elif [[ -e .git ]] && git clean -fdx; then : elif [[ -e .git ]] && git clean -fdx; then :
fi fi
@ -88,26 +105,26 @@ _clean() {
rm -rf "$prefix" rm -rf "$prefix"
} }
# prepare <prefix> <platform> [ <arch> ... ] # prepare <prefix> <platform> [ <arch:host> ... ]
# #
# Configure the library for building the <arch>s on this machine. # Initialize the prefix in anticipation for building the <arch>s on this machine.
# The build script invokes this once prior to building each of its targets. # The build script invokes this once prior to building each of its targets.
# The <prefix> has been newly created.
# #
# By default, this will run `autoreconf`.
prepare() { _prepare "$@"; } prepare() { _prepare "$@"; }
_prepare() { _prepare() {
prepare_clean "$@" prepare_create "$@"
prepare_config "$@" prepare_config "$@"
} }
# prepare_clean <prefix> <platform> [ <arch> ... ] # prepare_create <prefix> <platform> [ <arch:host> ... ]
# #
# Perform any necessary clean-up of the library code prior to building. # Perform any necessary clean-up of the library code prior to building.
# #
# By default, this will wipe the build configuration and re-create the prefix. # By default, this will wipe the build configuration and re-create the prefix.
prepare_clean() { _prepare_clean "$@"; } # TODO: Should this differ from clean()?
_prepare_clean() { #
prepare_create() { _prepare_create "$@"; }
_prepare_create() {
local prefix=$1 platform=$2; shift 2 local prefix=$1 platform=$2; shift 2
if [[ $platform = windows ]]; then : if [[ $platform = windows ]]; then :
@ -119,11 +136,16 @@ _prepare_clean() {
install -d "$prefix/out" install -d "$prefix/out"
} }
# prepare_config <prefix> <platform> [ <arch> ... ] # prepare_config <prefix> <platform> [ <arch:host> ... ]
# #
# Configure the library for building the <arch>s on this machine. # Generate build solution for configuring a build on this machine.
# The <prefix> has been newly created.
#
# TODO: cmake support?
# By default, this will:
# - Windows: do nothing
# - Other: run `autoreconf`.
# #
# By default, this will run `autoreconf`.
prepare_config() { _prepare_config "$@"; } prepare_config() { _prepare_config "$@"; }
_prepare_config() { _prepare_config() {
local prefix=$1 platform=$2; shift 2 local prefix=$1 platform=$2; shift 2
@ -141,11 +163,11 @@ _prepare_config() {
touch "$prefix/out/.prepared" touch "$prefix/out/.prepared"
} }
# target <prefix> <platform> <arch> # target <prefix> <platform> <arch> <host>
# #
# Build the library for the given <arch> and <platform> into the given <prefix>. # Build the library to the <arch> binary for the <host> architecture on <platform> into the given <prefix>.
# The build script invokes this function when it's ready to build the library's code. # The build script invokes this function when it's ready to build the library's code.
# Generic platform-specific environment setup has been done. #
target() { _target "$@"; } target() { _target "$@"; }
_target() { _target() {
target_prepare "$@" target_prepare "$@"
@ -153,14 +175,71 @@ _target() {
target_build "$@" target_build "$@"
} }
# target_prepare <prefix> <platform> <arch> # target_prepare <prefix> <platform> <arch> <host>
# #
# Prepare the library configuration for building the target. # Any build-related work to be done in the prefix prior to building.
#
# By default, this will:
# - Windows: do nothing
# - macOS/iOS: Discover SDKROOT & build flags
# - Android: Prepare an NDK toolchain & build flags
# - Makefile: run `make clean`
# #
# By default, this will run `make clean` if a Makefile is found.
target_prepare() { _target_prepare "$@"; } target_prepare() { _target_prepare "$@"; }
_target_prepare() { _target_prepare() {
local prefix=$1 platform=$2 arch=$3; shift 3 local prefix=$1 platform=$2 arch=$3 host=$4; shift 3
case "$platform" in
'windows')
;;
'macos')
SDKROOT="$(xcrun --show-sdk-path --sdk macosx)"
export PATH="$(xcrun --show-sdk-platform-path --sdk macosx)/usr/bin:$PATH"
export CPPFLAGS="-arch $host -flto -O2 -g -isysroot $SDKROOT -mmacosx-version-min=${MACOSX_DEPLOYMENT_TARGET:-10.8} $CPPFLAGS"
export LDFLAGS="-arch $host -flto -isysroot $SDKROOT -mmacosx-version-min=${MACOSX_DEPLOYMENT_TARGET:-10.8} $LDFLAGS"
;;
'ios')
case "$arch" in
*'arm'*)
SDKROOT="$(xcrun --show-sdk-path --sdk iphoneos)"
export PATH="$(xcrun --show-sdk-platform-path --sdk iphoneos)/usr/bin:$PATH"
export CPPFLAGS="-arch $host -mthumb -fembed-bitcode -flto -O2 -g -isysroot $SDKROOT -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} $CPPFLAGS"
export LDFLAGS="-arch $host -mthumb -fembed-bitcode -flto -isysroot $SDKROOT -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} $LDFLAGS"
;;
*)
SDKROOT="$(xcrun --show-sdk-path --sdk iphonesimulator)"
export PATH="$(xcrun --show-sdk-platform-path --sdk iphonesimulator)/usr/bin:$PATH"
export CPPFLAGS="-arch $host -flto -O2 -g -isysroot $SDKROOT -mios-simulator-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} $CPPFLAGS"
export LDFLAGS="-arch $host -flto -isysroot $SDKROOT -mios-simulator-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} $LDFLAGS"
;;
esac
;;
'android')
[[ -x $ANDROID_NDK_HOME/build/ndk-build ]] || { echo >&2 "Android NDK not found. Please set ANDROID_NDK_HOME."; return 1; }
SDKROOT="$prefix/$arch/ndk"
# Platform 21 is lowest that supports x86_64
"$ANDROID_NDK_HOME/build/tools/make-standalone-toolchain.sh" --force --install-dir="$SDKROOT" --platform='android-21' --arch="$arch"
export PATH="$SDKROOT/bin:$PATH"
export CPPFLAGS="-O2 -g $CPPFLAGS"
export LDFLAGS="-avoid-version $LDFLAGS"
export CC='clang'
;;
*)
case "$arch" in
x86)
export CPPFLAGS="-m32 $CPPFLAGS" LDFLAGS="-m32 $LDFLAGS"
;;
x86_64)
export CPPFLAGS="-m64 $CPPFLAGS" LDFLAGS="-m64 $LDFLAGS"
;;
esac
;;
esac
if [[ $platform = windows ]]; then : if [[ $platform = windows ]]; then :
else else
@ -168,66 +247,74 @@ _target_prepare() {
fi fi
} }
# target_configure <prefix> <platform> <arch> [ <args> ... ] # target_configure <prefix> <platform> <arch> <host> [ <args> ... ]
# #
# Configure the library for building the target. # Configure the library for building the target. This generates the compiler configuration.
# #
# By default, this will run `./configure --host=<host> --prefix=<prefix>/<arch> <args>`. # By default, this will:
# By default, some platform-specific arguments will be passed in as well as # - Windows: do nothing
# - Other: run `./configure --host=<host> --prefix=<prefix>/<arch> <args>`.
#
# Some platform-specific configure arguments will be passed in as well.
# --enable-pic --disable-pie to ensure the resulting library can be linked again. # --enable-pic --disable-pie to ensure the resulting library can be linked again.
#
target_configure() { _target_configure "$@"; } target_configure() { _target_configure "$@"; }
_target_configure() { _target_configure() {
local prefix=$1 platform=$2 arch=$3; shift 3 local prefix=$1 platform=$2 arch=$3 host=$4; shift 4
local host=$arch build= local build=
[[ $arch = *arm* ]] && host=arm
[[ -x config.guess ]] && build=$(./config.guess) [[ -x config.guess ]] && build=$(./config.guess)
[[ -x build-aux/config.guess ]] && build=$(build-aux/config.guess) [[ -x build-aux/config.guess ]] && build=$(build-aux/config.guess)
case "$platform" in case "$platform" in
'windows') 'windows')
# doesn't use ./configure # doesn't use ./configure
return return 0
;; ;;
'ios'|'macos') 'ios'|'macos')
host+=-apple host+=-apple
set -- --enable-static --disable-shared set -- --enable-static --disable-shared "$@"
;; ;;
'android') 'android')
case "$arch" in
'arm') host='arm' ;;
'arm64') host='aarch64' ;;
'x86') host='i686' ;;
'x86_64') host='x86_64' ;;
esac
host=( "$SDKROOT/$host"*-android* ) host=${host##*/} host=( "$SDKROOT/$host"*-android* ) host=${host##*/}
set -- --disable-static --enable-shared --with-sysroot="$SDKROOT/sysroot" "$@" set -- --disable-static --enable-shared --with-sysroot="$SDKROOT/sysroot" "$@"
;; ;;
*) *)
set -- --enable-static --disable-shared set -- --enable-static --disable-shared "$@"
;; ;;
esac esac
./configure ${build:+--build="$build"} ${host:+--host="$host"} --prefix="$prefix/$arch" --enable-pic --disable-pie "$@" ./configure ${build:+--build="$build"} ${host:+--host="$host"} --prefix="$prefix/$arch" --enable-pic --disable-pie "$@"
} }
# target_build <prefix> <platform> <arch> # target_build <prefix> <platform> <arch> <host>
# #
# Build the library code for the target. # Build the library code for the target. This runs the compiler per the previous configuration.
#
# By default, this will:
# - Windows: run `msbuild /t:Rebuild /p:Configuration:Release;Platform=<host>`
# - Other: run `make check install`.
# #
# By default, this will run `make check install`.
target_build() { _target_build "$@"; } target_build() { _target_build "$@"; }
_target_build() { _target_build() {
local prefix=$1 platform=$2 arch=$3; shift 3 local prefix=$1 platform=$2 arch=$3 host=$4; shift 4
if [[ $platform = windows ]]; then if [[ $platform = windows ]]; then
# I cannot for the life of me figure out how to pass this command directly into cmd. if [[ -e CMakeLists.txt ]]; then
printf '"%%VSINSTALLDIR%%\Common7\Tools\VsMSBuildCmd.bat" && msbuild /t:Rebuild /p:Configuration=Release;Platform=%s;OutDir=%s' "$arch" "$(cygpath -w "${prefix##$PWD/}/$arch/")" > .build.bat ( projdir=$PWD; mkdir -p "$prefix/$arch/"; cd "$prefix/$arch/"
cmd //c .build.bat PATH="$(cygpath "$VSINSTALLDIR")/Common7/Tools:$(cygpath "$VSINSTALLDIR")/Common7/IDE/CommonExtensions/Microsoft/CMake/CMake/bin:$PATH" \
rm -f .build.bat cmd /v /c "$(printf 'VsMSBuildCmd && cmake -A %s %s && for %%s in (*.sln) do msbuild /m /t:Rebuild /p:Configuration=Release;Platform=%s;OutDir=. %%s' \
"$host" "$(cygpath -w "$projdir")" "$host")" )
else
PATH="$(cygpath "$VSINSTALLDIR")/Common7/Tools:$PATH" \
cmd /v /c "$(printf 'VsMSBuildCmd && for %%s in (*.sln) do msbuild /m /t:Rebuild /p:Configuration=Release;Platform=%s;OutDir=%s %%s' \
"$host" "$(cygpath -w "${prefix##$PWD/}/$arch/")")"
fi
else else
local cores=$(getconf NPROCESSORS_ONLN 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null ||:) local cores=$(getconf NPROCESSORS_ONLN 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null ||:)
make -j"${cores:-3}" install #make -j"${cores:-3}" check install # TODO: libjson-c breaks on parallel build for check and install
#make check install # TODO: libjson-c has a failing test atm
make install
fi fi
} }
@ -235,6 +322,7 @@ _target_build() {
# #
# Prepare the final build product. # Prepare the final build product.
# The build script invokes this once after a successful build of all targets. # The build script invokes this once after a successful build of all targets.
#
finalize() { _finalize "$@"; } finalize() { _finalize "$@"; }
_finalize() { _finalize() {
finalize_merge "$@" finalize_merge "$@"
@ -246,6 +334,7 @@ _finalize() {
# Merge all targets into a product the application can use, available at `<prefix>/out`. # Merge all targets into a product the application can use, available at `<prefix>/out`.
# #
# By default, this will copy the headers to `<prefix>/out/include`, install libraries into `<prefix>/out/lib` and mark the output product as successful. # By default, this will copy the headers to `<prefix>/out/include`, install libraries into `<prefix>/out/lib` and mark the output product as successful.
#
finalize_merge() { _finalize_merge "$@"; } finalize_merge() { _finalize_merge "$@"; }
_finalize_merge() { _finalize_merge() {
local prefix=$1 platform=$2; shift 2 local prefix=$1 platform=$2; shift 2
@ -296,6 +385,7 @@ _finalize_merge() {
# Clean up the library after a successful build (eg. housekeeping of temporary files). # Clean up the library after a successful build (eg. housekeeping of temporary files).
# #
# By default, this will run `make clean`. # By default, this will run `make clean`.
#
finalize_clean() { _finalize_clean "$@"; } finalize_clean() { _finalize_clean "$@"; }
_finalize_clean() { _finalize_clean() {
if [[ $platform = windows ]]; then : if [[ $platform = windows ]]; then :
@ -307,6 +397,7 @@ _finalize_clean() {
# build <name> [<platform>] # build <name> [<platform>]
# #
# Build the library <name> (found at ../<name>) for platform <platform> (or "host" if unspecified). # Build the library <name> (found at ../<name>) for platform <platform> (or "host" if unspecified).
#
build() { _build "$@"; } build() { _build "$@"; }
_build() { _build() {
local name=$1 platform=${2:-host} local name=$1 platform=${2:-host}
@ -322,10 +413,10 @@ _build() {
if (( ! ${#archs[@]} )); then if (( ! ${#archs[@]} )); then
case "$platform" in case "$platform" in
'macos') archs=( 'x86_64' ) ;; 'macos') archs=( 'x86_64' ) ;;
'ios') archs=( 'i386' 'x86_64' 'armv7' 'armv7s' 'arm64' ) ;; 'ios') archs=( 'x86:i386' 'x86_64' 'armv7' 'armv7s' 'arm64' ) ;;
'android') archs=( 'arm' 'arm64' 'x86' 'x86_64' ) ;; 'android') archs=( 'arm' 'arm64:aarch64' 'x86:i686' 'x86_64' ) ;;
'windows') archs=( 'Win32' 'x64' ) ;; 'windows') archs=( 'x86:Win32' 'x86_64:x64' ) ;;
*) archs=( 'i686' 'x86_64' ) ;; *) archs=( 'x86:i686' 'x86_64' ) ;;
esac esac
fi fi
@ -348,60 +439,13 @@ _build() {
# Repeat the build for each individual architecture. # Repeat the build for each individual architecture.
for arch in "${archs[@]}"; do ( for arch in "${archs[@]}"; do (
local host=${arch#*:} arch=${arch%%:*}
echo echo
echo " # $name ($platform: $arch) ..." echo " # $name [$platform: $arch ($host)] ..."
# Set up a base environment for the platform. target "$prefix" "$platform" "$arch" "$host"
case "$platform" in
'windows')
;;
'macos')
SDKROOT="$(xcrun --show-sdk-path --sdk macosx)"
export PATH="$(xcrun --show-sdk-platform-path --sdk macosx)/usr/bin:$PATH"
export CPPFLAGS="-arch $arch -flto -O2 -g -isysroot $SDKROOT -mmacosx-version-min=${MACOSX_DEPLOYMENT_TARGET:-10.8} $CPPFLAGS"
export LDFLAGS="-arch $arch -flto -isysroot $SDKROOT -mmacosx-version-min=${MACOSX_DEPLOYMENT_TARGET:-10.8} $LDFLAGS"
;;
'ios')
case "$arch" in
*'arm'*)
SDKROOT="$(xcrun --show-sdk-path --sdk iphoneos)"
export PATH="$(xcrun --show-sdk-platform-path --sdk iphoneos)/usr/bin:$PATH"
export CPPFLAGS="-arch $arch -mthumb -fembed-bitcode -flto -O2 -g -isysroot $SDKROOT -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} $CPPFLAGS"
export LDFLAGS="-arch $arch -mthumb -fembed-bitcode -flto -isysroot $SDKROOT -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} $LDFLAGS"
;;
*)
SDKROOT="$(xcrun --show-sdk-path --sdk iphonesimulator)"
export PATH="$(xcrun --show-sdk-platform-path --sdk iphonesimulator)/usr/bin:$PATH"
export CPPFLAGS="-arch $arch -flto -O2 -g -isysroot $SDKROOT -mios-simulator-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} $CPPFLAGS"
export LDFLAGS="-arch $arch -flto -isysroot $SDKROOT -mios-simulator-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} $LDFLAGS"
;;
esac
;;
'android')
[[ -x $ANDROID_NDK_HOME/build/ndk-build ]] || { echo >&2 "Android NDK not found. Please set ANDROID_NDK_HOME."; return 1; }
SDKROOT="$prefix/$arch/ndk"
# Platform 21 is lowest that supports x86_64
"$ANDROID_NDK_HOME/build/tools/make-standalone-toolchain.sh" --force --install-dir="$SDKROOT" --platform='android-21' --arch="$arch"
export PATH="$SDKROOT/bin:$PATH"
export CPPFLAGS="-O2 -g $CPPFLAGS"
export LDFLAGS="-avoid-version $LDFLAGS"
export CC='clang'
;;
*)
case "$arch" in
i686)
export CPPFLAGS="-m32 $CPPFLAGS" LDFLAGS="-m32 $LDFLAGS"
;;
x86_64)
export CPPFLAGS="-m64 $CPPFLAGS" LDFLAGS="-m64 $LDFLAGS"
;;
esac
;;
esac
target "$prefix" "$platform" "$arch"
); done ); done
finalize "$prefix" "$platform" "${archs[@]}" finalize "$prefix" "$platform" "${archs[@]%%:*}"
} }

@ -1 +1 @@
Subproject commit d598fb4f5e29f5aaa66e7e880a9857019865881b Subproject commit 4aef3c80e0ffe6c7011c20c7f703ea56af774861

@ -1 +0,0 @@
Subproject commit 2dcb598d18a64a61509e418079bc448ce6b5268a

@ -1 +0,0 @@
Subproject commit 1b8f8b79ad12b70976c7a417ff1a9d29e8c0ed73

@ -1 +1 @@
Subproject commit b9da35c5efb21f95838233ebf95ddd8de2c41886 Subproject commit 452d2a72c4e897e3141106ba6ca8544834128c9e

@ -1 +0,0 @@
Subproject commit 98d18aee73329321c320a2df85bacdb9f08a34a6

@ -1 +0,0 @@
Subproject commit ae96212a4903a2b9e1df1e3542c9962f0d64a74b

View File

@ -30,10 +30,18 @@
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
536DEA2BDA9F2F9583C53D8F /* Products */ = {
isa = PBXGroup;
children = (
);
name = Products;
sourceTree = "<group>";
};
DA1554D220B3924000EA92C5 = { DA1554D220B3924000EA92C5 = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DA1554DD20B3928E00EA92C5 /* core */, DA1554DD20B3928E00EA92C5 /* core */,
536DEA2BDA9F2F9583C53D8F /* Products */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
}; };
@ -105,6 +113,7 @@
Base, Base,
); );
mainGroup = DA1554D220B3924000EA92C5; mainGroup = DA1554D220B3924000EA92C5;
productRefGroup = 536DEA2BDA9F2F9583C53D8F /* Products */;
projectDirPath = ""; projectDirPath = "";
projectRoot = ""; projectRoot = "";
targets = ( targets = (

File diff suppressed because it is too large Load Diff

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

@ -7,21 +7,17 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
7352E972184B980C428B66A2 /* Pods_MasterPassword_macOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1E9477508F419F29008C7553 /* Pods_MasterPassword_macOS.framework */; };
93D390C676DF52DA7E459F19 /* MPSitesWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D9D0061FF1159998F06 /* MPSitesWindow.m */; }; 93D390C676DF52DA7E459F19 /* MPSitesWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D9D0061FF1159998F06 /* MPSitesWindow.m */; };
93D391E61DC23E128DA4446C /* NSView+Traversing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D393EE88DE554BCCBC1C2D /* NSView+Traversing.h */; }; 93D391E61DC23E128DA4446C /* NSView+Traversing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D393EE88DE554BCCBC1C2D /* NSView+Traversing.h */; };
93D392EC39DA43C46C692C12 /* NSDictionary+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */; };
93D393A1646430FAAC73E7FE /* MPMacApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39F83DD151985F2C7345A /* MPMacApplication.m */; }; 93D393A1646430FAAC73E7FE /* MPMacApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39F83DD151985F2C7345A /* MPMacApplication.m */; };
93D394C4254EEB45FB335AFB /* MPSitesTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39423D7BF4FD31FE6D27C /* MPSitesTableView.m */; }; 93D394C4254EEB45FB335AFB /* MPSitesTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39423D7BF4FD31FE6D27C /* MPSitesTableView.m */; };
93D395E4830290EBB6E71F34 /* MPNoStateButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39538C4CEFF46DF379254 /* MPNoStateButton.m */; }; 93D395E4830290EBB6E71F34 /* MPNoStateButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39538C4CEFF46DF379254 /* MPNoStateButton.m */; };
93D395F08A087F8A24689347 /* NSArray+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */; };
93D39784E725A34D1EE3FB3B /* MPInitialWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D3CB30874147D9A9E1B /* MPInitialWindowController.m */; }; 93D39784E725A34D1EE3FB3B /* MPInitialWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D3CB30874147D9A9E1B /* MPInitialWindowController.m */; };
93D3987F6D9046DBEE4D8364 /* NSView+Traversing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D392870DF659AFC1870521 /* NSView+Traversing.m */; }; 93D3987F6D9046DBEE4D8364 /* NSView+Traversing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D392870DF659AFC1870521 /* NSView+Traversing.m */; };
93D398D1F5D8CD5A22AF6929 /* MPGradientView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39934FD8D5BFABA46F41C /* MPGradientView.m */; }; 93D398D1F5D8CD5A22AF6929 /* MPGradientView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39934FD8D5BFABA46F41C /* MPGradientView.m */; };
93D39C34FE35830EF5BE1D2A /* NSArray+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D396D04E57792A54D437AC /* NSArray+Indexing.h */; };
93D39C5789EFA607CF788082 /* MPSiteModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39E73BF5CBF8E5B005CD3 /* MPSiteModel.m */; }; 93D39C5789EFA607CF788082 /* MPSiteModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39E73BF5CBF8E5B005CD3 /* MPSiteModel.m */; };
93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */; };
93D39F833DEC1C89B2F795AC /* MPSitesWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39A57A7823DE98A0FF83C /* MPSitesWindowController.m */; }; 93D39F833DEC1C89B2F795AC /* MPSitesWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39A57A7823DE98A0FF83C /* MPSitesWindowController.m */; };
C6D1011AA5C6C5391BFC9DC0 /* Pods_MasterPassword_macOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2C2AEAB82916C4A50EE4A4DE /* Pods_MasterPassword_macOS.framework */; };
DA0933CC1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png in Resources */ = {isa = PBXBuildFile; fileRef = DA0933CB1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png */; }; DA0933CC1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png in Resources */ = {isa = PBXBuildFile; fileRef = DA0933CB1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png */; };
DA0933D01747B91B00DE1CEF /* appstore.png in Resources */ = {isa = PBXBuildFile; fileRef = DA0933CF1747B91B00DE1CEF /* appstore.png */; }; DA0933D01747B91B00DE1CEF /* appstore.png in Resources */ = {isa = PBXBuildFile; fileRef = DA0933CF1747B91B00DE1CEF /* appstore.png */; };
DA09745E1E99586600F0BFE8 /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA09745D1E99586600F0BFE8 /* libxml2.tbd */; }; DA09745E1E99586600F0BFE8 /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA09745D1E99586600F0BFE8 /* libxml2.tbd */; };
@ -87,7 +83,6 @@
DA3B8453190FC86F00246EEA /* NSManagedObject+Pearl.h in Headers */ = {isa = PBXBuildFile; fileRef = DA3B8451190FC86F00246EEA /* NSManagedObject+Pearl.h */; }; DA3B8453190FC86F00246EEA /* NSManagedObject+Pearl.h in Headers */ = {isa = PBXBuildFile; fileRef = DA3B8451190FC86F00246EEA /* NSManagedObject+Pearl.h */; };
DA3B8456190FC89700246EEA /* MPFixable.m in Sources */ = {isa = PBXBuildFile; fileRef = DA3B8454190FC89700246EEA /* MPFixable.m */; }; DA3B8456190FC89700246EEA /* MPFixable.m in Sources */ = {isa = PBXBuildFile; fileRef = DA3B8454190FC89700246EEA /* MPFixable.m */; };
DA3BCFCD19BD09E0006B2681 /* SourceCodePro-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = DA3BCFCC19BD09E0006B2681 /* SourceCodePro-Regular.otf */; }; DA3BCFCD19BD09E0006B2681 /* SourceCodePro-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = DA3BCFCC19BD09E0006B2681 /* SourceCodePro-Regular.otf */; };
DA4DA1D91564471A00F6F596 /* libjrswizzle.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC6326C148680650075AEA5 /* libjrswizzle.a */; };
DA4DAE941A7D8117003E5423 /* MPAlgorithmV3.m in Sources */ = {isa = PBXBuildFile; fileRef = DA4DAE921A7D8117003E5423 /* MPAlgorithmV3.m */; }; DA4DAE941A7D8117003E5423 /* MPAlgorithmV3.m in Sources */ = {isa = PBXBuildFile; fileRef = DA4DAE921A7D8117003E5423 /* MPAlgorithmV3.m */; };
DA4DAE951A7D8117003E5423 /* MPTypes.m in Sources */ = {isa = PBXBuildFile; fileRef = DA4DAE931A7D8117003E5423 /* MPTypes.m */; }; DA4DAE951A7D8117003E5423 /* MPTypes.m in Sources */ = {isa = PBXBuildFile; fileRef = DA4DAE931A7D8117003E5423 /* MPTypes.m */; };
DA5180CA19FF2F9200A587E9 /* MPAlgorithmV2.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5180C719FF2F9200A587E9 /* MPAlgorithmV2.m */; }; DA5180CA19FF2F9200A587E9 /* MPAlgorithmV2.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5180C719FF2F9200A587E9 /* MPAlgorithmV2.m */; };
@ -106,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 */; };
@ -119,6 +113,7 @@
DA6774431A474A3B004F356A /* mpw-algorithm.c in Sources */ = {isa = PBXBuildFile; fileRef = DA6773BB1A4746AF004F356A /* mpw-algorithm.c */; }; DA6774431A474A3B004F356A /* mpw-algorithm.c in Sources */ = {isa = PBXBuildFile; fileRef = DA6773BB1A4746AF004F356A /* mpw-algorithm.c */; };
DA6774451A474A3B004F356A /* mpw-types.c in Sources */ = {isa = PBXBuildFile; fileRef = DA6773C21A4746AF004F356A /* mpw-types.c */; }; DA6774451A474A3B004F356A /* mpw-types.c in Sources */ = {isa = PBXBuildFile; fileRef = DA6773C21A4746AF004F356A /* mpw-types.c */; };
DA6774461A474A3B004F356A /* mpw-util.c in Sources */ = {isa = PBXBuildFile; fileRef = DA6773C51A4746AF004F356A /* mpw-util.c */; }; DA6774461A474A3B004F356A /* mpw-util.c in Sources */ = {isa = PBXBuildFile; fileRef = DA6773C51A4746AF004F356A /* mpw-util.c */; };
DA72E2272444081200676D4F /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DABB981515100B4000B05417 /* SystemConfiguration.framework */; };
DA7471A31F2B71AE005F3468 /* mpw-marshal-util.c in Sources */ = {isa = PBXBuildFile; fileRef = DA7471A01F2B71A9005F3468 /* mpw-marshal-util.c */; }; DA7471A31F2B71AE005F3468 /* mpw-marshal-util.c in Sources */ = {isa = PBXBuildFile; fileRef = DA7471A01F2B71A9005F3468 /* mpw-marshal-util.c */; };
DA7471A61F2B71B9005F3468 /* mpw-marshal-util.c in Sources */ = {isa = PBXBuildFile; fileRef = DA7471A01F2B71A9005F3468 /* mpw-marshal-util.c */; }; DA7471A61F2B71B9005F3468 /* mpw-marshal-util.c in Sources */ = {isa = PBXBuildFile; fileRef = DA7471A01F2B71A9005F3468 /* mpw-marshal-util.c */; };
DA89D4EC1A51EABD00AC64D7 /* Pearl-Cocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = DA89D4EA1A51EABD00AC64D7 /* Pearl-Cocoa.h */; }; DA89D4EC1A51EABD00AC64D7 /* Pearl-Cocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = DA89D4EA1A51EABD00AC64D7 /* Pearl-Cocoa.h */; };
@ -126,7 +121,6 @@
DA8ED895192906920099B726 /* PearlTween.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8ED891192906920099B726 /* PearlTween.m */; }; DA8ED895192906920099B726 /* PearlTween.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8ED891192906920099B726 /* PearlTween.m */; };
DA8ED896192906920099B726 /* PearlTween.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8ED892192906920099B726 /* PearlTween.h */; }; DA8ED896192906920099B726 /* PearlTween.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8ED892192906920099B726 /* PearlTween.h */; };
DA8ED897192906920099B726 /* map-macro.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8ED894192906920099B726 /* map-macro.h */; }; DA8ED897192906920099B726 /* map-macro.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8ED894192906920099B726 /* map-macro.h */; };
DA9261511BE1A86700369DE5 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA92614F1BE1A86700369DE5 /* SystemConfiguration.framework */; };
DA9261541BE1A88900369DE5 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA9261531BE1A88900369DE5 /* libc++.tbd */; }; DA9261541BE1A88900369DE5 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA9261531BE1A88900369DE5 /* libc++.tbd */; };
DA9261561BE1A89600369DE5 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA9261551BE1A89600369DE5 /* libz.tbd */; }; DA9261561BE1A89600369DE5 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA9261551BE1A89600369DE5 /* libz.tbd */; };
DAA449D51EEC4B6B00E7BDD5 /* mpw-marshal.c in Sources */ = {isa = PBXBuildFile; fileRef = DAA449D31EEC4B6B00E7BDD5 /* mpw-marshal.c */; }; DAA449D51EEC4B6B00E7BDD5 /* mpw-marshal.c in Sources */ = {isa = PBXBuildFile; fileRef = DAA449D31EEC4B6B00E7BDD5 /* mpw-marshal.c */; };
@ -136,8 +130,6 @@
DAADCC4819FAFFAD00987B1D /* NSPersistentStore+PearlMigration.h in Headers */ = {isa = PBXBuildFile; fileRef = DAADCC3F19FAFFAD00987B1D /* NSPersistentStore+PearlMigration.h */; }; DAADCC4819FAFFAD00987B1D /* NSPersistentStore+PearlMigration.h in Headers */ = {isa = PBXBuildFile; fileRef = DAADCC3F19FAFFAD00987B1D /* NSPersistentStore+PearlMigration.h */; };
DAADCC4919FAFFAD00987B1D /* NSPersistentStore+PearlMigration.m in Sources */ = {isa = PBXBuildFile; fileRef = DAADCC4019FAFFAD00987B1D /* NSPersistentStore+PearlMigration.m */; }; DAADCC4919FAFFAD00987B1D /* NSPersistentStore+PearlMigration.m in Sources */ = {isa = PBXBuildFile; fileRef = DAADCC4019FAFFAD00987B1D /* NSPersistentStore+PearlMigration.m */; };
DAADCC4B19FB000C00987B1D /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAEBC45214F6364500987BF6 /* QuartzCore.framework */; }; DAADCC4B19FB000C00987B1D /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAEBC45214F6364500987BF6 /* QuartzCore.framework */; };
DAADCC6919FB007F00987B1D /* NSManagedObjectModel+KCOrderedAccessorFix.m in Sources */ = {isa = PBXBuildFile; fileRef = DAADCC6719FB007F00987B1D /* NSManagedObjectModel+KCOrderedAccessorFix.m */; };
DAADCC6A19FB00B500987B1D /* libKCOrderedAccessorFix.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAADCC5019FB006500987B1D /* libKCOrderedAccessorFix.a */; };
DAAF16661F5CA3240013B8AE /* mpw-cli-util.c in Sources */ = {isa = PBXBuildFile; fileRef = DAAF16631F5897EA0013B8AE /* mpw-cli-util.c */; }; DAAF16661F5CA3240013B8AE /* mpw-cli-util.c in Sources */ = {isa = PBXBuildFile; fileRef = DAAF16631F5897EA0013B8AE /* mpw-cli-util.c */; };
DAB07CA01F7725D400CC6D43 /* aes.c in Sources */ = {isa = PBXBuildFile; fileRef = DAB07C9E1F7725D400CC6D43 /* aes.c */; }; DAB07CA01F7725D400CC6D43 /* aes.c in Sources */ = {isa = PBXBuildFile; fileRef = DAB07C9E1F7725D400CC6D43 /* aes.c */; };
DAB07CA31F77261400CC6D43 /* aes.c in Sources */ = {isa = PBXBuildFile; fileRef = DAB07C9E1F7725D400CC6D43 /* aes.c */; }; DAB07CA31F77261400CC6D43 /* aes.c in Sources */ = {isa = PBXBuildFile; fileRef = DAB07C9E1F7725D400CC6D43 /* aes.c */; };
@ -166,7 +158,6 @@
DABD5EF9242BBCE200200755 /* icon_512x512.png in Resources */ = {isa = PBXBuildFile; fileRef = DAD0C5FE19FD6034009CB08D /* icon_512x512.png */; }; DABD5EF9242BBCE200200755 /* icon_512x512.png in Resources */ = {isa = PBXBuildFile; fileRef = DAD0C5FE19FD6034009CB08D /* icon_512x512.png */; };
DABD5EFA242BBCE200200755 /* icon_512x512@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DAD0C5FF19FD6034009CB08D /* icon_512x512@2x.png */; }; DABD5EFA242BBCE200200755 /* icon_512x512@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DAD0C5FF19FD6034009CB08D /* icon_512x512@2x.png */; };
DABD5F00242BC32D00200755 /* MPSecrets.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD5EFE242BC32D00200755 /* MPSecrets.m */; }; DABD5F00242BC32D00200755 /* MPSecrets.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD5EFE242BC32D00200755 /* MPSecrets.m */; };
DAC6326D148680650075AEA5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
DAC77CAE148291A600BCF976 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; }; DAC77CAE148291A600BCF976 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
DACA26FE1705DF81002C6C22 /* logo-bare.png in Resources */ = {isa = PBXBuildFile; fileRef = DACA241C1705DF7D002C6C22 /* logo-bare.png */; }; DACA26FE1705DF81002C6C22 /* logo-bare.png in Resources */ = {isa = PBXBuildFile; fileRef = DACA241C1705DF7D002C6C22 /* logo-bare.png */; };
DACA27121705DF81002C6C22 /* avatar-13@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DACA24321705DF7D002C6C22 /* avatar-13@2x.png */; }; DACA27121705DF81002C6C22 /* avatar-13@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DACA24321705DF7D002C6C22 /* avatar-13@2x.png */; };
@ -211,8 +202,6 @@
DACA29671705DF81002C6C22 /* SourceCodePro-ExtraLight.otf in Resources */ = {isa = PBXBuildFile; fileRef = DACA268E1705DF81002C6C22 /* SourceCodePro-ExtraLight.otf */; }; DACA29671705DF81002C6C22 /* SourceCodePro-ExtraLight.otf in Resources */ = {isa = PBXBuildFile; fileRef = DACA268E1705DF81002C6C22 /* SourceCodePro-ExtraLight.otf */; };
DACA29681705DF81002C6C22 /* SourceCodePro-Black.otf in Resources */ = {isa = PBXBuildFile; fileRef = DACA268F1705DF81002C6C22 /* SourceCodePro-Black.otf */; }; DACA29681705DF81002C6C22 /* SourceCodePro-Black.otf in Resources */ = {isa = PBXBuildFile; fileRef = DACA268F1705DF81002C6C22 /* SourceCodePro-Black.otf */; };
DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */ = {isa = PBXBuildFile; fileRef = DACA29721705E1A8002C6C22 /* dictionary.lst */; }; DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */ = {isa = PBXBuildFile; fileRef = DACA29721705E1A8002C6C22 /* dictionary.lst */; };
DACA298D1705E2BD002C6C22 /* JRSwizzle.h in Headers */ = {isa = PBXBuildFile; fileRef = DACA29771705E2BD002C6C22 /* JRSwizzle.h */; };
DACA299A1705E2BD002C6C22 /* JRSwizzle.m in Sources */ = {isa = PBXBuildFile; fileRef = DACA298C1705E2BD002C6C22 /* JRSwizzle.m */; };
DAD9B5F01762CAA4001835F9 /* ServiceManagement.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAD9B5EF1762CAA4001835F9 /* ServiceManagement.framework */; }; DAD9B5F01762CAA4001835F9 /* ServiceManagement.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAD9B5EF1762CAA4001835F9 /* ServiceManagement.framework */; };
DAD9B5F11762CAB9001835F9 /* MasterPassword-Mac-LoginHelper.app in Copy LoginHelper */ = {isa = PBXBuildFile; fileRef = DAD9B5E6176299BA001835F9 /* MasterPassword-Mac-LoginHelper.app */; }; DAD9B5F11762CAB9001835F9 /* MasterPassword-Mac-LoginHelper.app in Copy LoginHelper */ = {isa = PBXBuildFile; fileRef = DAD9B5E6176299BA001835F9 /* MasterPassword-Mac-LoginHelper.app */; };
DAEB942E18B47FB3000490CC /* MPInitialWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA0933C91747A56A00DE1CEF /* MPInitialWindow.xib */; }; DAEB942E18B47FB3000490CC /* MPInitialWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA0933C91747A56A00DE1CEF /* MPInitialWindow.xib */; };
@ -308,13 +297,6 @@
remoteGlobalIDString = DAD9B5C0176299B9001835F9; remoteGlobalIDString = DAD9B5C0176299B9001835F9;
remoteInfo = "MasterPassword-Mac-LoginHelper"; remoteInfo = "MasterPassword-Mac-LoginHelper";
}; };
DAC63285148681200075AEA5 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = DA5BFA3B147E415C00F98B1E /* Project object */;
proxyType = 1;
remoteGlobalIDString = DAC6326B148680650075AEA5;
remoteInfo = jrswizzle;
};
DAD9B5E5176299BA001835F9 /* PBXContainerItemProxy */ = { DAD9B5E5176299BA001835F9 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = DAD9B5E1176299B9001835F9 /* MasterPassword-Mac-LoginHelper.xcodeproj */; containerPortal = DAD9B5E1176299B9001835F9 /* MasterPassword-Mac-LoginHelper.xcodeproj */;
@ -352,15 +334,6 @@
); );
runOnlyForDeploymentPostprocessing = 1; runOnlyForDeploymentPostprocessing = 1;
}; };
DAADCC4E19FB006500987B1D /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
DAD9B5EE1762CA3A001835F9 /* Copy LoginHelper */ = { DAD9B5EE1762CA3A001835F9 /* Copy LoginHelper */ = {
isa = PBXCopyFilesBuildPhase; isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@ -375,28 +348,21 @@
/* End PBXCopyFilesBuildPhase section */ /* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
04D86D0B84D29123756339FD /* Pods-MasterPassword.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MasterPassword.debug.xcconfig"; path = "Target Support Files/Pods-MasterPassword/Pods-MasterPassword.debug.xcconfig"; sourceTree = "<group>"; }; 2C2AEAB82916C4A50EE4A4DE /* Pods_MasterPassword_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MasterPassword_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
1E9477508F419F29008C7553 /* Pods_MasterPassword_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MasterPassword_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 6F97B456DA90E7D2E95B64A5 /* Pods-MasterPassword-macOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MasterPassword-macOS.debug.xcconfig"; path = "Target Support Files/Pods-MasterPassword-macOS/Pods-MasterPassword-macOS.debug.xcconfig"; sourceTree = "<group>"; };
1E9ED9547411486CE0898611 /* Pods-MasterPassword-macOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MasterPassword-macOS.debug.xcconfig"; path = "Target Support Files/Pods-MasterPassword-macOS/Pods-MasterPassword-macOS.debug.xcconfig"; sourceTree = "<group>"; };
28DD0F6129ECC496C8DFE6F8 /* Pods-MasterPassword-macOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MasterPassword-macOS.release.xcconfig"; path = "Target Support Files/Pods-MasterPassword-macOS/Pods-MasterPassword-macOS.release.xcconfig"; sourceTree = "<group>"; };
7791961245EBC3023523FDCD /* Pods-MasterPassword.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MasterPassword.release.xcconfig"; path = "Target Support Files/Pods-MasterPassword/Pods-MasterPassword.release.xcconfig"; sourceTree = "<group>"; };
93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Indexing.m"; sourceTree = "<group>"; };
93D39240B5143E01F0B75E96 /* MPSiteModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSiteModel.h; sourceTree = "<group>"; }; 93D39240B5143E01F0B75E96 /* MPSiteModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSiteModel.h; sourceTree = "<group>"; };
93D392870DF659AFC1870521 /* NSView+Traversing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSView+Traversing.m"; sourceTree = "<group>"; }; 93D392870DF659AFC1870521 /* NSView+Traversing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSView+Traversing.m"; sourceTree = "<group>"; };
93D392A4F3DE0BD758B9B056 /* MPNoStateButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPNoStateButton.h; sourceTree = "<group>"; }; 93D392A4F3DE0BD758B9B056 /* MPNoStateButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPNoStateButton.h; sourceTree = "<group>"; };
93D392C3918763B3B72CF366 /* MPSitesWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSitesWindowController.h; sourceTree = "<group>"; }; 93D392C3918763B3B72CF366 /* MPSitesWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSitesWindowController.h; sourceTree = "<group>"; };
93D392FD65A1DF0E8B2A45C6 /* MPGradientView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPGradientView.h; sourceTree = "<group>"; }; 93D392FD65A1DF0E8B2A45C6 /* MPGradientView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPGradientView.h; sourceTree = "<group>"; };
93D39368EF3CBFEF2AFCA15A /* MPInitialWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPInitialWindowController.h; sourceTree = "<group>"; }; 93D39368EF3CBFEF2AFCA15A /* MPInitialWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPInitialWindowController.h; sourceTree = "<group>"; };
93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Indexing.h"; sourceTree = "<group>"; };
93D393EE88DE554BCCBC1C2D /* NSView+Traversing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSView+Traversing.h"; sourceTree = "<group>"; }; 93D393EE88DE554BCCBC1C2D /* NSView+Traversing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSView+Traversing.h"; sourceTree = "<group>"; };
93D3942099C9AD0374B5777D /* MPMacApplication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPMacApplication.h; sourceTree = "<group>"; }; 93D3942099C9AD0374B5777D /* MPMacApplication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPMacApplication.h; sourceTree = "<group>"; };
93D39423D7BF4FD31FE6D27C /* MPSitesTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSitesTableView.m; sourceTree = "<group>"; }; 93D39423D7BF4FD31FE6D27C /* MPSitesTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSitesTableView.m; sourceTree = "<group>"; };
93D39538C4CEFF46DF379254 /* MPNoStateButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPNoStateButton.m; sourceTree = "<group>"; }; 93D39538C4CEFF46DF379254 /* MPNoStateButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPNoStateButton.m; sourceTree = "<group>"; };
93D396D04E57792A54D437AC /* NSArray+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Indexing.h"; sourceTree = "<group>"; };
93D3977484534E99F9BA579D /* MPSitesWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSitesWindow.h; sourceTree = "<group>"; }; 93D3977484534E99F9BA579D /* MPSitesWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSitesWindow.h; sourceTree = "<group>"; };
93D39934FD8D5BFABA46F41C /* MPGradientView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPGradientView.m; sourceTree = "<group>"; }; 93D39934FD8D5BFABA46F41C /* MPGradientView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPGradientView.m; sourceTree = "<group>"; };
93D39A57A7823DE98A0FF83C /* MPSitesWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSitesWindowController.m; sourceTree = "<group>"; }; 93D39A57A7823DE98A0FF83C /* MPSitesWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSitesWindowController.m; sourceTree = "<group>"; };
93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Indexing.m"; sourceTree = "<group>"; };
93D39AC6360DDC16AEAA4119 /* MPSitesTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSitesTableView.h; sourceTree = "<group>"; }; 93D39AC6360DDC16AEAA4119 /* MPSitesTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSitesTableView.h; sourceTree = "<group>"; };
93D39D3CB30874147D9A9E1B /* MPInitialWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPInitialWindowController.m; sourceTree = "<group>"; }; 93D39D3CB30874147D9A9E1B /* MPInitialWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPInitialWindowController.m; sourceTree = "<group>"; };
93D39D9D0061FF1159998F06 /* MPSitesWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSitesWindow.m; sourceTree = "<group>"; }; 93D39D9D0061FF1159998F06 /* MPSitesWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSitesWindow.m; sourceTree = "<group>"; };
@ -937,7 +903,6 @@
DA8ED891192906920099B726 /* PearlTween.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlTween.m; sourceTree = "<group>"; }; DA8ED891192906920099B726 /* PearlTween.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlTween.m; sourceTree = "<group>"; };
DA8ED892192906920099B726 /* PearlTween.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlTween.h; sourceTree = "<group>"; }; DA8ED892192906920099B726 /* PearlTween.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlTween.h; sourceTree = "<group>"; };
DA8ED894192906920099B726 /* map-macro.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "map-macro.h"; sourceTree = "<group>"; }; DA8ED894192906920099B726 /* map-macro.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "map-macro.h"; sourceTree = "<group>"; };
DA92614F1BE1A86700369DE5 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
DA9261531BE1A88900369DE5 /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; }; DA9261531BE1A88900369DE5 /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; };
DA9261551BE1A89600369DE5 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; DA9261551BE1A89600369DE5 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
DAA449D31EEC4B6B00E7BDD5 /* mpw-marshal.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-marshal.c"; sourceTree = "<group>"; }; DAA449D31EEC4B6B00E7BDD5 /* mpw-marshal.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-marshal.c"; sourceTree = "<group>"; };
@ -1032,9 +997,6 @@
DAADCC3E19FAFFAD00987B1D /* NSNotificationCenter+PearlEasyCleanup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNotificationCenter+PearlEasyCleanup.h"; sourceTree = "<group>"; }; DAADCC3E19FAFFAD00987B1D /* NSNotificationCenter+PearlEasyCleanup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNotificationCenter+PearlEasyCleanup.h"; sourceTree = "<group>"; };
DAADCC3F19FAFFAD00987B1D /* NSPersistentStore+PearlMigration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSPersistentStore+PearlMigration.h"; sourceTree = "<group>"; }; DAADCC3F19FAFFAD00987B1D /* NSPersistentStore+PearlMigration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSPersistentStore+PearlMigration.h"; sourceTree = "<group>"; };
DAADCC4019FAFFAD00987B1D /* NSPersistentStore+PearlMigration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSPersistentStore+PearlMigration.m"; sourceTree = "<group>"; }; DAADCC4019FAFFAD00987B1D /* NSPersistentStore+PearlMigration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSPersistentStore+PearlMigration.m"; sourceTree = "<group>"; };
DAADCC5019FB006500987B1D /* libKCOrderedAccessorFix.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libKCOrderedAccessorFix.a; sourceTree = BUILT_PRODUCTS_DIR; };
DAADCC6619FB007F00987B1D /* NSManagedObjectModel+KCOrderedAccessorFix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSManagedObjectModel+KCOrderedAccessorFix.h"; sourceTree = "<group>"; };
DAADCC6719FB007F00987B1D /* NSManagedObjectModel+KCOrderedAccessorFix.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSManagedObjectModel+KCOrderedAccessorFix.m"; sourceTree = "<group>"; };
DAAF16631F5897EA0013B8AE /* mpw-cli-util.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-cli-util.c"; sourceTree = "<group>"; }; DAAF16631F5897EA0013B8AE /* mpw-cli-util.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-cli-util.c"; sourceTree = "<group>"; };
DAAF16641F5897EA0013B8AE /* mpw-cli-util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "mpw-cli-util.h"; sourceTree = "<group>"; }; DAAF16641F5897EA0013B8AE /* mpw-cli-util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "mpw-cli-util.h"; sourceTree = "<group>"; };
DAB07C9E1F7725D400CC6D43 /* aes.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = aes.c; sourceTree = "<group>"; }; DAB07C9E1F7725D400CC6D43 /* aes.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = aes.c; sourceTree = "<group>"; };
@ -1050,7 +1012,6 @@
DABB981515100B4000B05417 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; DABB981515100B4000B05417 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
DABD5EFE242BC32D00200755 /* MPSecrets.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSecrets.m; sourceTree = "<group>"; }; DABD5EFE242BC32D00200755 /* MPSecrets.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSecrets.m; sourceTree = "<group>"; };
DABD5EFF242BC32D00200755 /* MPSecrets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSecrets.h; sourceTree = "<group>"; }; DABD5EFF242BC32D00200755 /* MPSecrets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSecrets.h; sourceTree = "<group>"; };
DAC6326C148680650075AEA5 /* libjrswizzle.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libjrswizzle.a; sourceTree = BUILT_PRODUCTS_DIR; };
DAC632871486D95D0075AEA5 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; DAC632871486D95D0075AEA5 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
DAC77CAD148291A600BCF976 /* libPearl.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPearl.a; sourceTree = BUILT_PRODUCTS_DIR; }; DAC77CAD148291A600BCF976 /* libPearl.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPearl.a; sourceTree = BUILT_PRODUCTS_DIR; };
DACA241C1705DF7D002C6C22 /* logo-bare.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "logo-bare.png"; sourceTree = "<group>"; }; DACA241C1705DF7D002C6C22 /* logo-bare.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "logo-bare.png"; sourceTree = "<group>"; };
@ -1096,8 +1057,6 @@
DACA268E1705DF81002C6C22 /* SourceCodePro-ExtraLight.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SourceCodePro-ExtraLight.otf"; sourceTree = "<group>"; }; DACA268E1705DF81002C6C22 /* SourceCodePro-ExtraLight.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SourceCodePro-ExtraLight.otf"; sourceTree = "<group>"; };
DACA268F1705DF81002C6C22 /* SourceCodePro-Black.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SourceCodePro-Black.otf"; sourceTree = "<group>"; }; DACA268F1705DF81002C6C22 /* SourceCodePro-Black.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SourceCodePro-Black.otf"; sourceTree = "<group>"; };
DACA29721705E1A8002C6C22 /* dictionary.lst */ = {isa = PBXFileReference; fileEncoding = 1; lastKnownFileType = text; path = dictionary.lst; sourceTree = "<group>"; }; DACA29721705E1A8002C6C22 /* dictionary.lst */ = {isa = PBXFileReference; fileEncoding = 1; lastKnownFileType = text; path = dictionary.lst; sourceTree = "<group>"; };
DACA29771705E2BD002C6C22 /* JRSwizzle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JRSwizzle.h; sourceTree = "<group>"; };
DACA298C1705E2BD002C6C22 /* JRSwizzle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JRSwizzle.m; sourceTree = "<group>"; };
DACBFCDB1C59B22E007EF90F /* NSMutableSet+Pearl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableSet+Pearl.h"; sourceTree = "<group>"; }; DACBFCDB1C59B22E007EF90F /* NSMutableSet+Pearl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableSet+Pearl.h"; sourceTree = "<group>"; };
DACBFCDC1C59B22E007EF90F /* NSMutableSet+Pearl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableSet+Pearl.m"; sourceTree = "<group>"; }; DACBFCDC1C59B22E007EF90F /* NSMutableSet+Pearl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableSet+Pearl.m"; sourceTree = "<group>"; };
DAD0C5F619FD6034009CB08D /* icon_128x128.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon_128x128.png; sourceTree = "<group>"; }; DAD0C5F619FD6034009CB08D /* icon_128x128.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon_128x128.png; sourceTree = "<group>"; };
@ -1157,6 +1116,7 @@
DAFE4A63150399FF003ABA87 /* NSObject+PearlKVO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+PearlKVO.h"; sourceTree = "<group>"; }; DAFE4A63150399FF003ABA87 /* NSObject+PearlKVO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+PearlKVO.h"; sourceTree = "<group>"; };
DAFE4A63150399FF003ABA91 /* NSDateFormatter+RFC3339.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDateFormatter+RFC3339.m"; sourceTree = "<group>"; }; DAFE4A63150399FF003ABA91 /* NSDateFormatter+RFC3339.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDateFormatter+RFC3339.m"; sourceTree = "<group>"; };
DAFE4A63150399FF003ABA93 /* NSDateFormatter+RFC3339.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDateFormatter+RFC3339.h"; sourceTree = "<group>"; }; DAFE4A63150399FF003ABA93 /* NSDateFormatter+RFC3339.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDateFormatter+RFC3339.h"; sourceTree = "<group>"; };
DF299A3DC20096A2D2942352 /* Pods-MasterPassword-macOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MasterPassword-macOS.release.xcconfig"; path = "Target Support Files/Pods-MasterPassword-macOS/Pods-MasterPassword-macOS.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@ -1185,15 +1145,14 @@
DAB7AE5B1F3D750B00C856B1 /* libjson-c.a in Frameworks */, DAB7AE5B1F3D750B00C856B1 /* libjson-c.a in Frameworks */,
DA9261561BE1A89600369DE5 /* libz.tbd in Frameworks */, DA9261561BE1A89600369DE5 /* libz.tbd in Frameworks */,
DA9261541BE1A88900369DE5 /* libc++.tbd in Frameworks */, DA9261541BE1A88900369DE5 /* libc++.tbd in Frameworks */,
DAADCC6A19FB00B500987B1D /* libKCOrderedAccessorFix.a in Frameworks */,
DA9261511BE1A86700369DE5 /* SystemConfiguration.framework in Frameworks */,
DA250925195148E200AC23F1 /* QuartzCore.framework in Frameworks */, DA250925195148E200AC23F1 /* QuartzCore.framework in Frameworks */,
DAD9B5F01762CAA4001835F9 /* ServiceManagement.framework in Frameworks */, DAD9B5F01762CAA4001835F9 /* ServiceManagement.framework in Frameworks */,
DA16B341170661DB000A0EAB /* Carbon.framework in Frameworks */, DA16B341170661DB000A0EAB /* Carbon.framework in Frameworks */,
DA16B342170661E0000A0EAB /* Security.framework in Frameworks */, DA16B342170661E0000A0EAB /* Security.framework in Frameworks */,
DA16B345170661F2000A0EAB /* libPearl.a in Frameworks */, DA16B345170661F2000A0EAB /* libPearl.a in Frameworks */,
DA16B344170661EE000A0EAB /* Cocoa.framework in Frameworks */, DA16B344170661EE000A0EAB /* Cocoa.framework in Frameworks */,
7352E972184B980C428B66A2 /* Pods_MasterPassword_macOS.framework in Frameworks */, DA72E2272444081200676D4F /* SystemConfiguration.framework in Frameworks */,
C6D1011AA5C6C5391BFC9DC0 /* Pods_MasterPassword_macOS.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -1205,27 +1164,11 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
DAADCC4D19FB006500987B1D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
DAC63269148680650075AEA5 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
DAC6326D148680650075AEA5 /* Foundation.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
DAC77CAA148291A600BCF976 /* Frameworks */ = { DAC77CAA148291A600BCF976 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
DAADCC4B19FB000C00987B1D /* QuartzCore.framework in Frameworks */, DAADCC4B19FB000C00987B1D /* QuartzCore.framework in Frameworks */,
DA4DA1D91564471A00F6F596 /* libjrswizzle.a in Frameworks */,
DAC77CAE148291A600BCF976 /* Foundation.framework in Frameworks */, DAC77CAE148291A600BCF976 /* Foundation.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@ -1236,10 +1179,8 @@
0C357E29DE20E18311095EBF /* Pods */ = { 0C357E29DE20E18311095EBF /* Pods */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
04D86D0B84D29123756339FD /* Pods-MasterPassword.debug.xcconfig */, 6F97B456DA90E7D2E95B64A5 /* Pods-MasterPassword-macOS.debug.xcconfig */,
7791961245EBC3023523FDCD /* Pods-MasterPassword.release.xcconfig */, DF299A3DC20096A2D2942352 /* Pods-MasterPassword-macOS.release.xcconfig */,
1E9ED9547411486CE0898611 /* Pods-MasterPassword-macOS.debug.xcconfig */,
28DD0F6129ECC496C8DFE6F8 /* Pods-MasterPassword-macOS.release.xcconfig */,
); );
path = Pods; path = Pods;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1272,14 +1213,6 @@
name = "Other Frameworks"; name = "Other Frameworks";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
DA3B8449190FC5A900246EEA /* Mac */ = {
isa = PBXGroup;
children = (
DA92614F1BE1A86700369DE5 /* SystemConfiguration.framework */,
);
path = Mac;
sourceTree = "<group>";
};
DA5BFA39147E415C00F98B1E = { DA5BFA39147E415C00F98B1E = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -1298,8 +1231,6 @@
children = ( children = (
DA5BFA44147E415C00F98B1E /* Master Password.app */, DA5BFA44147E415C00F98B1E /* Master Password.app */,
DAC77CAD148291A600BCF976 /* libPearl.a */, DAC77CAD148291A600BCF976 /* libPearl.a */,
DAC6326C148680650075AEA5 /* libjrswizzle.a */,
DAADCC5019FB006500987B1D /* libKCOrderedAccessorFix.a */,
DA67743B1A474A03004F356A /* mpw-test */, DA67743B1A474A03004F356A /* mpw-test */,
DA1C7AB61F1A8F24009A3551 /* mpw-cli */, DA1C7AB61F1A8F24009A3551 /* mpw-cli */,
DA1C7AD61F1A8FD8009A3551 /* mpw-bench */, DA1C7AD61F1A8FD8009A3551 /* mpw-bench */,
@ -1326,7 +1257,7 @@
DAD9B5EF1762CAA4001835F9 /* ServiceManagement.framework */, DAD9B5EF1762CAA4001835F9 /* ServiceManagement.framework */,
DA6701DD16406B7300B61001 /* Social.framework */, DA6701DD16406B7300B61001 /* Social.framework */,
DABB981515100B4000B05417 /* SystemConfiguration.framework */, DABB981515100B4000B05417 /* SystemConfiguration.framework */,
1E9477508F419F29008C7553 /* Pods_MasterPassword_macOS.framework */, 2C2AEAB82916C4A50EE4A4DE /* Pods_MasterPassword_macOS.framework */,
); );
name = Frameworks; name = Frameworks;
sourceTree = "<group>"; sourceTree = "<group>";
@ -2025,15 +1956,6 @@
path = lib; path = lib;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
DAADCC6819FB007F00987B1D /* KCOrderedAccessorFix */ = {
isa = PBXGroup;
children = (
DAADCC6619FB007F00987B1D /* NSManagedObjectModel+KCOrderedAccessorFix.h */,
DAADCC6719FB007F00987B1D /* NSManagedObjectModel+KCOrderedAccessorFix.m */,
);
path = KCOrderedAccessorFix;
sourceTree = "<group>";
};
DAC77CAF148291A600BCF976 /* Pearl */ = { DAC77CAF148291A600BCF976 /* Pearl */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -2050,9 +1972,6 @@
children = ( children = (
DAA7BC0D20C4C1B500101DC7 /* libjson-c-macos */, DAA7BC0D20C4C1B500101DC7 /* libjson-c-macos */,
DAA7BBC920C4C17300101DC7 /* libsodium-macos */, DAA7BBC920C4C17300101DC7 /* libsodium-macos */,
DACA29751705E2BD002C6C22 /* jrswizzle */,
DAADCC6819FB007F00987B1D /* KCOrderedAccessorFix */,
DA3B8449190FC5A900246EEA /* Mac */,
DAC77CAF148291A600BCF976 /* Pearl */, DAC77CAF148291A600BCF976 /* Pearl */,
); );
path = External; path = External;
@ -2152,15 +2071,6 @@
path = Data; path = Data;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
DACA29751705E2BD002C6C22 /* jrswizzle */ = {
isa = PBXGroup;
children = (
DACA29771705E2BD002C6C22 /* JRSwizzle.h */,
DACA298C1705E2BD002C6C22 /* JRSwizzle.m */,
);
path = jrswizzle;
sourceTree = "<group>";
};
DAD0C5F419FD6034009CB08D /* mac */ = { DAD0C5F419FD6034009CB08D /* mac */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -2205,14 +2115,10 @@
DAB4FBDD202FF93D002768FB /* PearlHangDetector.h */, DAB4FBDD202FF93D002768FB /* PearlHangDetector.h */,
DAB4FBDE202FF93D002768FB /* PearlHangDetector.m */, DAB4FBDE202FF93D002768FB /* PearlHangDetector.m */,
DA8ED893192906920099B726 /* include */, DA8ED893192906920099B726 /* include */,
93D396D04E57792A54D437AC /* NSArray+Indexing.h */,
93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */,
DA2CA4EA18D323D3007798F8 /* NSArray+Pearl.h */, DA2CA4EA18D323D3007798F8 /* NSArray+Pearl.h */,
DA2CA4E918D323D3007798F8 /* NSArray+Pearl.m */, DA2CA4E918D323D3007798F8 /* NSArray+Pearl.m */,
DAFE4A63150399FF003ABA93 /* NSDateFormatter+RFC3339.h */, DAFE4A63150399FF003ABA93 /* NSDateFormatter+RFC3339.h */,
DAFE4A63150399FF003ABA91 /* NSDateFormatter+RFC3339.m */, DAFE4A63150399FF003ABA91 /* NSDateFormatter+RFC3339.m */,
93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */,
93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */,
DA2CA4E818D323D3007798F8 /* NSError+PearlFullDescription.h */, DA2CA4E818D323D3007798F8 /* NSError+PearlFullDescription.h */,
DA2CA4E718D323D3007798F8 /* NSError+PearlFullDescription.m */, DA2CA4E718D323D3007798F8 /* NSError+PearlFullDescription.m */,
DA3B8451190FC86F00246EEA /* NSManagedObject+Pearl.h */, DA3B8451190FC86F00246EEA /* NSManagedObject+Pearl.h */,
@ -2292,14 +2198,6 @@
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */ /* Begin PBXHeadersBuildPhase section */
DAC6326A148680650075AEA5 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
DACA298D1705E2BD002C6C22 /* JRSwizzle.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
DAC77CAB148291A600BCF976 /* Headers */ = { DAC77CAB148291A600BCF976 /* Headers */ = {
isa = PBXHeadersBuildPhase; isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@ -2335,8 +2233,6 @@
DAADCC4719FAFFAD00987B1D /* NSNotificationCenter+PearlEasyCleanup.h in Headers */, DAADCC4719FAFFAD00987B1D /* NSNotificationCenter+PearlEasyCleanup.h in Headers */,
DAFE4A63150399FF003ABA88 /* NSObject+PearlKVO.h in Headers */, DAFE4A63150399FF003ABA88 /* NSObject+PearlKVO.h in Headers */,
DAFE4A63150399FF003ABA94 /* NSDateFormatter+RFC3339.h in Headers */, DAFE4A63150399FF003ABA94 /* NSDateFormatter+RFC3339.h in Headers */,
93D39C34FE35830EF5BE1D2A /* NSArray+Indexing.h in Headers */,
93D392EC39DA43C46C692C12 /* NSDictionary+Indexing.h in Headers */,
DA3509FE15F101A500C14A8E /* PearlQueue.h in Headers */, DA3509FE15F101A500C14A8E /* PearlQueue.h in Headers */,
DA2C3D681BD9665B001137B3 /* PearlProfiler.h in Headers */, DA2C3D681BD9665B001137B3 /* PearlProfiler.h in Headers */,
DA2CA4EE18D323D3007798F8 /* NSError+PearlFullDescription.h in Headers */, DA2CA4EE18D323D3007798F8 /* NSError+PearlFullDescription.h in Headers */,
@ -2420,15 +2316,15 @@
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = DA5BFA6D147E415C00F98B1E /* Build configuration list for PBXNativeTarget "MasterPassword-macOS" */; buildConfigurationList = DA5BFA6D147E415C00F98B1E /* Build configuration list for PBXNativeTarget "MasterPassword-macOS" */;
buildPhases = ( buildPhases = (
0DC4F66C2499C40A68A07D58 /* [CP] Check Pods Manifest.lock */, A4743CFBA7F5C60D2E440512 /* [CP] Check Pods Manifest.lock */,
DA4EF9CB19FD4B600032ECB5 /* Run Script: genassets */, DA4EF9CB19FD4B600032ECB5 /* Run Script: genassets */,
DA5BFA40147E415C00F98B1E /* Sources */, DA5BFA40147E415C00F98B1E /* Sources */,
DA5BFA41147E415C00F98B1E /* Frameworks */, DA5BFA41147E415C00F98B1E /* Frameworks */,
DA5BFA42147E415C00F98B1E /* Resources */, DA5BFA42147E415C00F98B1E /* Resources */,
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 */, DA3C4EB2243941AE00A6C4A8 /* Sentry dSYM Upload */,
DA3C4EB2243941AE00A6C4A8 /* Upload Sentry dSYM */, 85C937E180CD7620A471A930 /* [CP] Embed Pods Frameworks */,
); );
buildRules = ( buildRules = (
); );
@ -2460,40 +2356,6 @@
productReference = DA67743B1A474A03004F356A /* mpw-test */; productReference = DA67743B1A474A03004F356A /* mpw-test */;
productType = "com.apple.product-type.tool"; productType = "com.apple.product-type.tool";
}; };
DAADCC4F19FB006500987B1D /* KCOrderedAccessorFix */ = {
isa = PBXNativeTarget;
buildConfigurationList = DAADCC5E19FB006500987B1D /* Build configuration list for PBXNativeTarget "KCOrderedAccessorFix" */;
buildPhases = (
DAADCC4C19FB006500987B1D /* Sources */,
DAADCC4D19FB006500987B1D /* Frameworks */,
DAADCC4E19FB006500987B1D /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = KCOrderedAccessorFix;
productName = KCOrderedAccessorFix;
productReference = DAADCC5019FB006500987B1D /* libKCOrderedAccessorFix.a */;
productType = "com.apple.product-type.library.static";
};
DAC6326B148680650075AEA5 /* jrswizzle */ = {
isa = PBXNativeTarget;
buildConfigurationList = DAC63274148680650075AEA5 /* Build configuration list for PBXNativeTarget "jrswizzle" */;
buildPhases = (
DAC63268148680650075AEA5 /* Sources */,
DAC63269148680650075AEA5 /* Frameworks */,
DAC6326A148680650075AEA5 /* Headers */,
);
buildRules = (
);
dependencies = (
);
name = jrswizzle;
productName = jrswizzle;
productReference = DAC6326C148680650075AEA5 /* libjrswizzle.a */;
productType = "com.apple.product-type.library.static";
};
DAC77CAC148291A600BCF976 /* Pearl */ = { DAC77CAC148291A600BCF976 /* Pearl */ = {
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = DAC77CB7148291A600BCF976 /* Build configuration list for PBXNativeTarget "Pearl" */; buildConfigurationList = DAC77CB7148291A600BCF976 /* Build configuration list for PBXNativeTarget "Pearl" */;
@ -2505,7 +2367,6 @@
buildRules = ( buildRules = (
); );
dependencies = ( dependencies = (
DAC63286148681200075AEA5 /* PBXTargetDependency */,
); );
name = Pearl; name = Pearl;
productName = Pearl; productName = Pearl;
@ -2522,7 +2383,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 = {
@ -2549,18 +2410,11 @@
CreatedOnToolsVersion = 6.1.1; CreatedOnToolsVersion = 6.1.1;
DevelopmentTeam = HL3Q45LX9N; DevelopmentTeam = HL3Q45LX9N;
}; };
DAADCC4F19FB006500987B1D = {
CreatedOnToolsVersion = 6.0.1;
DevelopmentTeam = HL3Q45LX9N;
};
DAB7AE421F3D466D00C856B1 = { DAB7AE421F3D466D00C856B1 = {
CreatedOnToolsVersion = 8.3.3; CreatedOnToolsVersion = 8.3.3;
DevelopmentTeam = HL3Q45LX9N; DevelopmentTeam = HL3Q45LX9N;
ProvisioningStyle = Automatic; ProvisioningStyle = Automatic;
}; };
DAC6326B148680650075AEA5 = {
DevelopmentTeam = HL3Q45LX9N;
};
DAC77CAC148291A600BCF976 = { DAC77CAC148291A600BCF976 = {
DevelopmentTeam = HL3Q45LX9N; DevelopmentTeam = HL3Q45LX9N;
}; };
@ -2587,8 +2441,6 @@
targets = ( targets = (
DA5BFA43147E415C00F98B1E /* MasterPassword-macOS */, DA5BFA43147E415C00F98B1E /* MasterPassword-macOS */,
DAC77CAC148291A600BCF976 /* Pearl */, DAC77CAC148291A600BCF976 /* Pearl */,
DAC6326B148680650075AEA5 /* jrswizzle */,
DAADCC4F19FB006500987B1D /* KCOrderedAccessorFix */,
DA67743A1A474A03004F356A /* mpw-test */, DA67743A1A474A03004F356A /* mpw-test */,
DA1C7AC61F1A8FD8009A3551 /* mpw-bench */, DA1C7AC61F1A8FD8009A3551 /* mpw-bench */,
DA1C7AA61F1A8F24009A3551 /* mpw-cli */, DA1C7AA61F1A8F24009A3551 /* mpw-cli */,
@ -2678,7 +2530,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 */,
@ -2689,7 +2540,29 @@
/* End PBXResourcesBuildPhase section */ /* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */
0DC4F66C2499C40A68A07D58 /* [CP] Check Pods Manifest.lock */ = { 85C937E180CD7620A471A930 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-MasterPassword-macOS/Pods-MasterPassword-macOS-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/JRSwizzle-macOS/JRSwizzle.framework",
"${BUILT_PRODUCTS_DIR}/KCOrderedAccessorFix-macOS/KCOrderedAccessorFix.framework",
"${BUILT_PRODUCTS_DIR}/Sentry-macOS/Sentry.framework",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/JRSwizzle.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/KCOrderedAccessorFix.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Sentry.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-MasterPassword-macOS/Pods-MasterPassword-macOS-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
A4743CFBA7F5C60D2E440512 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
@ -2711,27 +2584,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;
}; };
43E5966C8C236E86824DDADE /* [CP] Embed Pods Frameworks */ = { DA3C4EB2243941AE00A6C4A8 /* Sentry dSYM Upload */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-MasterPassword-macOS/Pods-MasterPassword-macOS-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/Countly-macOS/Countly.framework",
"${BUILT_PRODUCTS_DIR}/Sentry-macOS/Sentry.framework",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Countly.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Sentry.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-MasterPassword-macOS/Pods-MasterPassword-macOS-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
DA3C4EB2243941AE00A6C4A8 /* Upload Sentry dSYM */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 8; buildActionMask = 8;
files = ( files = (
@ -2739,15 +2592,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 */ = {
@ -2891,22 +2745,6 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
DAADCC4C19FB006500987B1D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
DAADCC6919FB007F00987B1D /* NSManagedObjectModel+KCOrderedAccessorFix.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
DAC63268148680650075AEA5 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
DACA299A1705E2BD002C6C22 /* JRSwizzle.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
DAC77CA9148291A600BCF976 /* Sources */ = { DAC77CA9148291A600BCF976 /* Sources */ = {
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@ -2942,8 +2780,6 @@
DAB4FBEA202FF951002768FB /* NSMutableSet+Pearl.m in Sources */, DAB4FBEA202FF951002768FB /* NSMutableSet+Pearl.m in Sources */,
DAFE4A63150399FF003ABA86 /* NSObject+PearlKVO.m in Sources */, DAFE4A63150399FF003ABA86 /* NSObject+PearlKVO.m in Sources */,
DAFE4A63150399FF003ABA92 /* NSDateFormatter+RFC3339.m in Sources */, DAFE4A63150399FF003ABA92 /* NSDateFormatter+RFC3339.m in Sources */,
93D395F08A087F8A24689347 /* NSArray+Indexing.m in Sources */,
93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */,
DA3509FF15F101A500C14A8E /* PearlQueue.m in Sources */, DA3509FF15F101A500C14A8E /* PearlQueue.m in Sources */,
93D3987F6D9046DBEE4D8364 /* NSView+Traversing.m in Sources */, 93D3987F6D9046DBEE4D8364 /* NSView+Traversing.m in Sources */,
); );
@ -2987,11 +2823,6 @@
name = "MasterPassword-Mac-LoginHelper"; name = "MasterPassword-Mac-LoginHelper";
targetProxy = DABFA071176E3FDF00E83589 /* PBXContainerItemProxy */; targetProxy = DABFA071176E3FDF00E83589 /* PBXContainerItemProxy */;
}; };
DAC63286148681200075AEA5 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = DAC6326B148680650075AEA5 /* jrswizzle */;
targetProxy = DAC63285148681200075AEA5 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */ /* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */ /* Begin PBXVariantGroup section */
@ -3364,12 +3195,13 @@
}; };
DA5BFA6E147E415C00F98B1E /* Debug */ = { DA5BFA6E147E415C00F98B1E /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 1E9ED9547411486CE0898611 /* Pods-MasterPassword-macOS.debug.xcconfig */; baseConfigurationReference = 6F97B456DA90E7D2E95B64A5 /* Pods-MasterPassword-macOS.debug.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
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 = "Apple Development";
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";
@ -3389,6 +3221,7 @@
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib\"", "\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib\"",
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib\"", "\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib\"",
); );
MACOSX_DEPLOYMENT_TARGET = 10.10;
OTHER_CFLAGS = ( OTHER_CFLAGS = (
"-DMPW_SODIUM=1", "-DMPW_SODIUM=1",
"-DMPW_JSON=1", "-DMPW_JSON=1",
@ -3402,12 +3235,13 @@
}; };
DA5BFA6F147E415C00F98B1E /* Release */ = { DA5BFA6F147E415C00F98B1E /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 28DD0F6129ECC496C8DFE6F8 /* Pods-MasterPassword-macOS.release.xcconfig */; baseConfigurationReference = DF299A3DC20096A2D2942352 /* Pods-MasterPassword-macOS.release.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
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 = "Apple Development";
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";
@ -3427,6 +3261,7 @@
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib\"", "\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib\"",
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib\"", "\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib\"",
); );
MACOSX_DEPLOYMENT_TARGET = 10.10;
OTHER_CFLAGS = ( OTHER_CFLAGS = (
"-DMPW_SODIUM=1", "-DMPW_SODIUM=1",
"-DMPW_JSON=1", "-DMPW_JSON=1",
@ -3486,22 +3321,6 @@
}; };
name = Release; name = Release;
}; };
DAADCC5F19FB006500987B1D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES;
COMBINE_HIDPI_IMAGES = YES;
};
name = Debug;
};
DAADCC6019FB006500987B1D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES;
COMBINE_HIDPI_IMAGES = YES;
};
name = Release;
};
DAB7AE441F3D466D00C856B1 /* Debug */ = { DAB7AE441F3D466D00C856B1 /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
@ -3543,34 +3362,6 @@
}; };
name = Release; name = Release;
}; };
DAC63275148680650075AEA5 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_ARC = NO;
CLANG_ENABLE_OBJC_WEAK = YES;
COMBINE_HIDPI_IMAGES = YES;
DSTROOT = /tmp/jrswizzle.dst;
GCC_WARN_INHIBIT_ALL_WARNINGS = YES;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = Debug;
};
DAC63276148680650075AEA5 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_ARC = NO;
CLANG_ENABLE_OBJC_WEAK = YES;
COMBINE_HIDPI_IMAGES = YES;
DSTROOT = /tmp/jrswizzle.dst;
GCC_WARN_INHIBIT_ALL_WARNINGS = YES;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = Release;
};
DAC77CB5148291A600BCF976 /* Debug */ = { DAC77CB5148291A600BCF976 /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
@ -3654,15 +3445,6 @@
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Debug; defaultConfigurationName = Debug;
}; };
DAADCC5E19FB006500987B1D /* Build configuration list for PBXNativeTarget "KCOrderedAccessorFix" */ = {
isa = XCConfigurationList;
buildConfigurations = (
DAADCC5F19FB006500987B1D /* Debug */,
DAADCC6019FB006500987B1D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Debug;
};
DAB7AE431F3D466D00C856B1 /* Build configuration list for PBXLegacyTarget "libjson-c-macos" */ = { DAB7AE431F3D466D00C856B1 /* Build configuration list for PBXLegacyTarget "libjson-c-macos" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (
@ -3672,15 +3454,6 @@
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Debug; defaultConfigurationName = Debug;
}; };
DAC63274148680650075AEA5 /* Build configuration list for PBXNativeTarget "jrswizzle" */ = {
isa = XCConfigurationList;
buildConfigurations = (
DAC63275148680650075AEA5 /* Debug */,
DAC63276148680650075AEA5 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Debug;
};
DAC77CB7148291A600BCF976 /* Build configuration list for PBXNativeTarget "Pearl" */ = { DAC77CB7148291A600BCF976 /* Build configuration list for PBXNativeTarget "Pearl" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (

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

@ -11,6 +11,9 @@ target 'MasterPassword-iOS' do
pod 'Sentry', :git => 'https://github.com/getsentry/sentry-cocoa.git' pod 'Sentry', :git => 'https://github.com/getsentry/sentry-cocoa.git'
pod 'Countly' pod 'Countly'
pod 'UIColor-Utilities'
pod 'KCOrderedAccessorFix'
pod 'JRSwizzle'
end end
target 'MasterPassword-macOS' do target 'MasterPassword-macOS' do
@ -22,4 +25,17 @@ target 'MasterPassword-macOS' do
pod 'Sentry', :git => 'https://github.com/getsentry/sentry-cocoa.git' pod 'Sentry', :git => 'https://github.com/getsentry/sentry-cocoa.git'
pod 'Countly' pod 'Countly'
pod 'KCOrderedAccessorFix'
pod 'JRSwizzle'
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 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

@ -81,15 +81,14 @@ static NSOperationQueue *_mpwQueue = nil;
} }
NSOperation *operation = [NSBlockOperation blockOperationWithBlock:operationBlock]; NSOperation *operation = [NSBlockOperation blockOperationWithBlock:operationBlock];
if ([operation respondsToSelector:@selector( qualityOfService )]) operation.qualityOfService = NSQualityOfServiceUserInitiated;
operation.qualityOfService = NSQualityOfServiceUserInitiated;
[_mpwQueue addOperations:@[ operation ] waitUntilFinished:YES]; [_mpwQueue addOperations:@[ operation ] waitUntilFinished:YES];
} }
- (BOOL)tryMigrateUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc { - (BOOL)tryMigrateUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc {
NSError *error = nil; NSError *error = nil;
NSFetchRequest *migrationRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )]; NSFetchRequest *migrationRequest = [MPSiteEntity fetchRequest];
migrationRequest.predicate = [NSPredicate predicateWithFormat:@"version_ < %d AND user == %@", self.version, user]; migrationRequest.predicate = [NSPredicate predicateWithFormat:@"version_ < %d AND user == %@", self.version, user];
NSArray *migrationSites = [moc executeFetchRequest:migrationRequest error:&error]; NSArray *migrationSites = [moc executeFetchRequest:migrationRequest error:&error];
if (!migrationSites) { if (!migrationSites) {
@ -147,10 +146,10 @@ static NSOperationQueue *_mpwQueue = nil;
- (NSString *)nameOfType:(MPResultType)type { - (NSString *)nameOfType:(MPResultType)type {
if (!type)
return nil;
switch (type) { switch (type) {
case MPResultTypeNone:
return @"None";
case MPResultTypeTemplateMaximum: case MPResultTypeTemplateMaximum:
return @"Maximum Security Password"; return @"Maximum Security Password";
@ -190,10 +189,10 @@ static NSOperationQueue *_mpwQueue = nil;
- (NSString *)shortNameOfType:(MPResultType)type { - (NSString *)shortNameOfType:(MPResultType)type {
if (!type)
return nil;
switch (type) { switch (type) {
case MPResultTypeNone:
return @"None";
case MPResultTypeTemplateMaximum: case MPResultTypeTemplateMaximum:
return @"Maximum"; return @"Maximum";
@ -238,9 +237,6 @@ static NSOperationQueue *_mpwQueue = nil;
- (Class)classOfType:(MPResultType)type { - (Class)classOfType:(MPResultType)type {
if (!type)
Throw( @"No type given." );
switch (type) { switch (type) {
case MPResultTypeTemplateMaximum: case MPResultTypeTemplateMaximum:
return [MPGeneratedSiteEntity class]; return [MPGeneratedSiteEntity class];
@ -272,6 +268,7 @@ static NSOperationQueue *_mpwQueue = nil;
case MPResultTypeStatefulDevice: case MPResultTypeStatefulDevice:
return [MPStoredSiteEntity class]; return [MPStoredSiteEntity class];
case MPResultTypeNone:
case MPResultTypeDeriveKey: case MPResultTypeDeriveKey:
break; break;
} }
@ -323,6 +320,7 @@ static NSOperationQueue *_mpwQueue = nil;
return MPResultTypeStatefulDevice; return MPResultTypeStatefulDevice;
case MPResultTypeStatefulDevice: case MPResultTypeStatefulDevice:
return MPResultTypeTemplatePhrase; return MPResultTypeTemplatePhrase;
case MPResultTypeNone:
case MPResultTypeDeriveKey: case MPResultTypeDeriveKey:
break; break;
} }
@ -532,8 +530,13 @@ static NSOperationQueue *_mpwQueue = nil;
return; return;
} }
case MPResultTypeDeriveKey: case MPResultTypeNone:
case MPResultTypeDeriveKey: {
PearlNotMainQueue( ^{
resultBlock( nil );
} );
return; return;
}
} }
Throw( @"Type not supported: %lu", (long)type ); Throw( @"Type not supported: %lu", (long)type );
@ -622,7 +625,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,35 +158,27 @@ 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
} }
- (void)requestDidFinish:(SKRequest *)request {
dbg( @"StoreKit request (%@) finished.", request );
}
#pragma mark - SKPaymentTransactionObserver #pragma mark - SKPaymentTransactionObserver
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions { - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
for (SKPaymentTransaction *transaction in transactions) { for (SKPaymentTransaction *transaction in transactions) {
dbg( @"transaction updated: %@ -> %d", transaction.payment.productIdentifier, (int)(transaction.transactionState) );
switch (transaction.transactionState) { switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchased: { case SKPaymentTransactionStatePurchased: {
inf( @"Purchased: %@", transaction.payment.productIdentifier );
NSMutableDictionary *attributes = [NSMutableDictionary new]; NSMutableDictionary *attributes = [NSMutableDictionary new];
if ([transaction.payment.productIdentifier isEqualToString:MPProductFuel]) { if ([transaction.payment.productIdentifier isEqualToString:MPProductFuel]) {
@ -205,22 +197,19 @@ 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, @"name": product.localizedTitle,
@"name": product.localizedTitle, @"price": product.price.description,
@"price": product.price.description, @"currency": [product.priceLocale objectForKey:NSLocaleCurrencyCode],
@"currency": [product.priceLocale objectForKey:NSLocaleCurrencyCode], @"state" : @"success",
@"state" : @"success", @"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: {
inf( @"Restored: %@", transaction.payment.productIdentifier );
[[NSUserDefaults standardUserDefaults] setObject:transaction.transactionIdentifier [[NSUserDefaults standardUserDefaults] setObject:transaction.transactionIdentifier
forKey:transaction.payment.productIdentifier]; forKey:transaction.payment.productIdentifier];
[queue finishTransaction:transaction]; [queue finishTransaction:transaction];
@ -233,18 +222,27 @@ 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
SKProduct *product = self.products[transaction.payment.productIdentifier]; PearlMainQueue( ^{
[Countly.sharedInstance recordEvent:@"purchase" segmentation:@{ UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Purchase Failed" message:
@"id": product.productIdentifier, strf( @"%@\n\n%@", transaction.error.localizedDescription,
@"name": product.localizedTitle, @"Could not reach Apple's iTunes Store. Make sure you're connected to the Internet and try again." )
@"price": product.price.description, preferredStyle:UIAlertControllerStyleAlert];
@"currency": [product.priceLocale objectForKey:NSLocaleCurrencyCode], [controller addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]];
@"state" : @"failed", [self.window.rootViewController presentViewController:controller animated:YES completion:nil];
@"quantity": @(transaction.payment.quantity).description, } );
@"reason" : [transaction.error localizedFailureReason]?: [transaction.error localizedDescription], #endif
}];
} SKProduct *product = self.products[transaction.payment.productIdentifier];
[Countly.sharedInstance recordEvent:@"purchase" segmentation:@{
@"id": product.productIdentifier,
@"name": product.localizedTitle,
@"price": product.price.description,
@"currency": [product.priceLocale objectForKey:NSLocaleCurrencyCode],
@"state" : @"failed",
@"quantity": @(transaction.payment.quantity).description,
@"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,15 +201,14 @@
} }
@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:@{
@"method" : password? @"Password": @"Automatic", @"method" : password? @"Password": @"Automatic",
@"state" : @"success", @"state" : @"success",
@"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

@ -32,7 +32,7 @@
@end @end
MPLogSink mpw_log_sink_pearl; MPLogSink mpw_log_sink_pearl;
void mpw_log_sink_pearl(const MPLogEvent *record) { bool mpw_log_sink_pearl(const MPLogEvent *record) {
PearlLogLevel level = PearlLogLevelInfo; PearlLogLevel level = PearlLogLevelInfo;
switch (record->level) { switch (record->level) {
@ -58,6 +58,7 @@ void mpw_log_sink_pearl(const MPLogEvent *record) {
[[PearlLogger get] inFile:[@(record->file) lastPathComponent] atLine:record->line fromFunction:@(record->function) [[PearlLogger get] inFile:[@(record->file) lastPathComponent] atLine:record->line fromFunction:@(record->function)
withLevel:level text:@(record->message)]; withLevel:level text:@(record->message)];
return YES;
} }
@implementation MPAppDelegate_Shared @implementation MPAppDelegate_Shared
@ -112,18 +113,16 @@ static MPAppDelegate_Shared *instance;
- (void)setActiveUser:(MPUserEntity *)activeUser { - (void)setActiveUser:(MPUserEntity *)activeUser {
NSManagedObjectID *activeUserOID = activeUser.permanentObjectID; NSManagedObjectID *activeUserOID = activeUser.permanentObjectID;
if ([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];
[Countly.sharedInstance userLoggedOut];
if ([[MPConfig get].sendInfo boolValue])
[Countly.sharedInstance userLoggedOut];
[[NSNotificationCenter defaultCenter] postNotificationName:MPSignedOutNotification object:self];
self.activeUserOID = activeUserOID; self.activeUserOID = activeUserOID;
[[NSNotificationCenter defaultCenter] postNotificationName:MPSignedOutNotification object:self];
} }
- (void)handleCoordinatorError:(NSError *)error { - (void)handleCoordinatorError:(NSError *)error {

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,
@ -49,7 +51,8 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
+ (NSManagedObjectContext *)managedObjectContextForMainThreadIfReady { + (NSManagedObjectContext *)managedObjectContextForMainThreadIfReady {
NSAssert( [[NSThread currentThread] isMainThread], @"Can only access main MOC from the main thread." ); NSAssert( [[NSThread currentThread] isMainThread], @"Direct access to main MOC only allowed from the main thread." );
NSManagedObjectContext *mainManagedObjectContext = [[self get] mainManagedObjectContextIfReady]; NSManagedObjectContext *mainManagedObjectContext = [[self get] mainManagedObjectContextIfReady];
if (!mainManagedObjectContext || ![[NSThread currentThread] isMainThread]) if (!mainManagedObjectContext || ![[NSThread currentThread] isMainThread])
return nil; return nil;
@ -154,6 +157,39 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
- (NSManagedObjectContext *)mainManagedObjectContextIfReady { - (NSManagedObjectContext *)mainManagedObjectContextIfReady {
[self loadStore]; [self loadStore];
if (!self.mainManagedObjectContext && self.privateManagedObjectContext.persistentStoreCoordinator) {
self.mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
self.mainManagedObjectContext.parentContext = self.privateManagedObjectContext;
if (@available( iOS 10.0, macOS 10.12, * ))
self.mainManagedObjectContext.automaticallyMergesChangesFromParent = YES;
else
// When privateManagedObjectContext is saved, import the changes into mainManagedObjectContext.
PearlAddNotificationObserverTo( self.mainManagedObjectContext, NSManagedObjectContextDidSaveNotification,
self.privateManagedObjectContext, nil, ^(NSManagedObjectContext *mainContext, NSNotification *note) {
[mainContext performBlock:^{
@try {
[mainContext mergeChangesFromContextDidSaveNotification:note];
}
@catch (NSException *exception) {
err( @"While merging changes:\n%@", [exception fullDescription] );
}
}];
} );
#if TARGET_OS_IPHONE
PearlAddNotificationObserver( UIApplicationWillResignActiveNotification, UIApp, [NSOperationQueue mainQueue],
^(MPAppDelegate_Shared *self, NSNotification *note) {
[self.mainManagedObjectContext saveToStore];
} );
#else
PearlAddNotificationObserver( NSApplicationWillResignActiveNotification, NSApp, [NSOperationQueue mainQueue],
^(MPAppDelegate_Shared *self, NSNotification *note) {
[self.mainManagedObjectContext saveToStore];
} );
#endif
}
return self.mainManagedObjectContext; return self.mainManagedObjectContext;
} }
@ -181,12 +217,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.storeCoordinator.persistentStores count])
return; return;
[self.storeQueue addOperationWithBlock:^{ NSOperation *storeOperation = [NSBlockOperation blockOperationWithBlock:^{
// 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.storeCoordinator.persistentStores count])
return; return;
// Unregister any existing observers and contexts. // Unregister any existing observers and contexts.
@ -207,41 +243,15 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
// Check if migration is necessary. // Check if migration is necessary.
[self migrateStore]; [self migrateStore];
// Install managed object contexts and observers.
self.privateManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[self.privateManagedObjectContext performBlockAndWait:^{
self.privateManagedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
self.privateManagedObjectContext.persistentStoreCoordinator = self.storeCoordinator;
}];
self.mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
self.mainManagedObjectContext.parentContext = self.privateManagedObjectContext;
if (@available( iOS 10.0, macOS 10.12, * ))
self.mainManagedObjectContext.automaticallyMergesChangesFromParent = YES;
else
// When privateManagedObjectContext is saved, import the changes into mainManagedObjectContext.
PearlAddNotificationObserverTo( self.mainManagedObjectContext, NSManagedObjectContextDidSaveNotification,
self.privateManagedObjectContext, nil, ^(NSManagedObjectContext *mainContext, NSNotification *note) {
[mainContext performBlock:^{
@try {
[mainContext mergeChangesFromContextDidSaveNotification:note];
}
@catch (NSException *exception) {
err( @"While merging changes:\n%@", [exception fullDescription] );
}
}];
} );
// Create a new store coordinator. // Create a new store coordinator.
NSError *error = nil;
NSURL *localStoreURL = [self localStoreURL]; NSURL *localStoreURL = [self localStoreURL];
NSError *error = nil;
if (![[NSFileManager defaultManager] createDirectoryAtURL:[localStoreURL URLByDeletingLastPathComponent] if (![[NSFileManager defaultManager] createDirectoryAtURL:[localStoreURL URLByDeletingLastPathComponent]
withIntermediateDirectories:YES attributes:nil error:&error]) { withIntermediateDirectories:YES attributes:nil error:&error]) {
MPError( error, @"Couldn't create our application support directory." ); MPError( error, @"Couldn't create our application support directory." );
return; return;
} }
if (![self.storeCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[self localStoreURL] if (![self.storeCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:localStoreURL
options:@{ options:@{
NSMigratePersistentStoresAutomaticallyOption: @YES, NSMigratePersistentStoresAutomaticallyOption: @YES,
NSInferMappingModelAutomaticallyOption : @YES, NSInferMappingModelAutomaticallyOption : @YES,
@ -254,17 +264,10 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
} }
self.storeCorrupted = @NO; self.storeCorrupted = @NO;
#if TARGET_OS_IPHONE // Install managed object contexts and observers.
PearlAddNotificationObserver( UIApplicationWillResignActiveNotification, UIApp, [NSOperationQueue mainQueue], self.privateManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
^(MPAppDelegate_Shared *self, NSNotification *note) { self.privateManagedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
[self.mainManagedObjectContext saveToStore]; self.privateManagedObjectContext.persistentStoreCoordinator = self.storeCoordinator;
} );
#else
PearlAddNotificationObserver( NSApplicationWillResignActiveNotification, NSApp, [NSOperationQueue mainQueue],
^(MPAppDelegate_Shared *self, NSNotification *note) {
[self.mainManagedObjectContext saveToStore];
} );
#endif
// Perform a data sanity check on the newly loaded store to find and fix any issues. // Perform a data sanity check on the newly loaded store to find and fix any issues.
if ([[MPConfig get].checkInconsistency boolValue]) if ([[MPConfig get].checkInconsistency boolValue])
@ -272,6 +275,14 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
[self findAndFixInconsistenciesSaveInContext:context]; [self findAndFixInconsistenciesSaveInContext:context];
}]; }];
}]; }];
[self.storeQueue addOperations:@[ storeOperation ] waitUntilFinished:YES];
}
- (void)retryCorruptStore {
self.storeCorrupted = @NO;
[self loadStore];
} }
- (void)deleteAndResetStore { - (void)deleteAndResetStore {
@ -563,55 +574,51 @@ 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 );
if (!file) MPMarshalledUser *importUser = nil;
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{ @try {
@"type" : @(MPMarshalErrorInternal), if (!file)
NSLocalizedDescriptionKey: @"Could not process Master Password import data.", return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
}]), @"While importing sites." ); @"type" : @(MPMarshalErrorInternal),
if (file->error.type != MPMarshalSuccess) { NSLocalizedDescriptionKey: @"Could not process Master Password import data.",
MPMarshalErrorType type = file->error.type; }]), @"While importing sites." );
mpw_marshal_file_free( &file ); if (file->error.type != MPMarshalSuccess) {
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.", }]), @"While importing sites." );
}]), @"While importing sites." );
}
// Get master password for import file.
MPKey *importKey;
NSString *importMasterPassword;
do {
importMasterPassword = askImportPassword( @(file->info->fullName) );
if (!importMasterPassword) {
inf( @"Import cancelled." );
mpw_marshal_file_free( &file );
return MPError( ([NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil]), @"" );
} }
importKey = [[MPKey alloc] initForFullName:@(file->info->fullName) withMasterPassword:importMasterPassword]; // Get master password for import file.
} while ([[[importKey keyIDForAlgorithm:MPAlgorithmForVersion( file->info->algorithm )] encodeHex] MPKey *importKey;
caseInsensitiveCompare:@(file->info->keyID)] != NSOrderedSame); NSString *importMasterPassword;
do {
importMasterPassword = askImportPassword( @(file->info->fullName) );
if (!importMasterPassword) {
inf( @"Import cancelled." );
return MPError( ([NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil]), @"" );
}
// Parse import data. importKey = [[MPKey alloc] initForFullName:@(file->info->fullName) withMasterPassword:importMasterPassword];
MPMarshalledUser *importUser = mpw_marshal_auth( file, mpw_masterKeyProvider_str( importMasterPassword.UTF8String ) ); } while ([[[importKey keyIDForAlgorithm:MPAlgorithmForVersion( file->info->algorithm )] encodeHex]
caseInsensitiveCompare:@(file->info->keyID)] != NSOrderedSame);
@try { // Parse import data.
importUser = mpw_marshal_auth( file, mpw_masterKeyProvider_str( importMasterPassword.UTF8String ) );
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.
NSError *error = nil; NSError *error = nil;
NSFetchRequest *userFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )]; NSFetchRequest *userFetchRequest = [MPUserEntity fetchRequest];
userFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", @(importUser->fullName)]; userFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", @(importUser->fullName)];
NSArray *users = [context executeFetchRequest:userFetchRequest error:&error]; NSArray *users = [context executeFetchRequest:userFetchRequest error:&error];
if (!users) if (!users)
@ -643,24 +650,21 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
user.avatar = importUser->avatar; user.avatar = importUser->avatar;
user.defaultType = importUser->defaultType; user.defaultType = importUser->defaultType;
user.lastUsed = [NSDate dateWithTimeIntervalSince1970:MAX( user.lastUsed.timeIntervalSince1970, importUser->lastUsed )]; user.lastUsed = [NSDate dateWithTimeIntervalSince1970:MAX( user.lastUsed.timeIntervalSince1970, importUser->lastUsed )];
dbg( @"Importing user: %@", [user debugDescription] );
// Update or create sites. // Update or create sites.
for (size_t s = 0; s < importUser->sites_count; ++s) { for (size_t s = 0; s < importUser->sites_count; ++s) {
MPMarshalledSite *importSite = &importUser->sites[s]; MPMarshalledSite *importSite = &importUser->sites[s];
// Find an existing site to update. // Find an existing site to update.
NSFetchRequest *siteFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )]; NSFetchRequest *siteFetchRequest = [MPSiteEntity fetchRequest];
siteFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND user == %@", @(importSite->siteName), user]; siteFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND user == %@", @(importSite->siteName), user];
NSArray *existingSites = [context executeFetchRequest:siteFetchRequest error:&error]; NSArray *existingSites = [context executeFetchRequest:siteFetchRequest error:&error];
if (!existingSites) if (!existingSites)
return MPError( error, @"Lookup of existing sites failed for site: %@, user: %@", @(importSite->siteName), user.userID ); return MPError( error, @"Lookup of existing sites failed for site: %@, user: %@", @(importSite->siteName), user.userID );
if ([existingSites count]) if ([existingSites count])
// Update existing site. // Update existing site.
for (MPSiteEntity *site in existingSites) { for (MPSiteEntity *site in existingSites)
[self importSite:importSite protectedByKey:importKey intoSite:site usingKey:userKey]; [self importSite:importSite protectedByKey:importKey intoSite:site usingKey:userKey];
dbg( @"Updated site: %@", [site debugDescription] );
}
else { else {
// Create new site. // Create new site.
id<MPAlgorithm> algorithm = MPAlgorithmForVersion( importSite->algorithm ); id<MPAlgorithm> algorithm = MPAlgorithmForVersion( importSite->algorithm );
@ -673,7 +677,6 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
site.user = user; site.user = user;
[self importSite:importSite protectedByKey:importKey intoSite:site usingKey:userKey]; [self importSite:importSite protectedByKey:importKey intoSite:site usingKey:userKey];
dbg( @"Created site: %@", [site debugDescription] );
} }
} }
@ -712,31 +715,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;
@ -745,22 +762,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)
[NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{ *error = exportFile && exportFile->error.type == MPMarshalSuccess? nil:
@"type" : @(exportFile? exportFile->error.type: MPMarshalErrorInternal), [NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
NSLocalizedDescriptionKey: @(exportFile? exportFile->error.message: nil), @"type" : @(exportFile? exportFile->error.type: MPMarshalErrorInternal),
}] ); 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( siteAttacker ) ) : @(MPAttacker1),
NSStringFromSelector( @selector( checkInconsistency ) ): @NO,
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 = [[SentryMessage alloc] initWithFormatted: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

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="16096" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES"> <document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="16097.3" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies> <dependencies>
<deployment identifier="macosx"/> <deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="16096"/> <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="16097.3"/>
</dependencies> </dependencies>
<objects> <objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication"> <customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
@ -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"/>
@ -66,7 +63,7 @@
<action selector="exportSitesSecure:" target="494" id="LVH-es-imA"/> <action selector="exportSitesSecure:" target="494" id="LVH-es-imA"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Your passwords are hidden." enabled="NO" id="ybY-P3-eao"> <menuItem title="Your passwords are not visible." enabled="NO" id="ybY-P3-eao">
<attributedString key="attributedTitle"> <attributedString key="attributedTitle">
<fragment content="Your passwords are not visible."> <fragment content="Your passwords are not visible.">
<attributes> <attributes>
@ -83,7 +80,7 @@
<action selector="exportSitesReveal:" target="494" id="1IW-VT-Oeu"/> <action selector="exportSitesReveal:" target="494" id="1IW-VT-Oeu"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Handy for backups - keep it in a safe location!" enabled="NO" id="cQu-oR-SUa"> <menuItem title="Keep this file secure or delete it when you're done with it!" enabled="NO" id="cQu-oR-SUa">
<attributedString key="attributedTitle"> <attributedString key="attributedTitle">
<fragment content="Keep this file secure or delete it when you're done with it!"> <fragment content="Keep this file secure or delete it when you're done with it!">
<attributes> <attributes>
@ -202,6 +199,7 @@
</attributedString> </attributedString>
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
</menuItem> </menuItem>
<menuItem isSeparatorItem="YES" id="lhE-aV-1P4"/>
<menuItem title="Diagnostics" id="GSN-f0-q7s"> <menuItem title="Diagnostics" id="GSN-f0-q7s">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<connections> <connections>
@ -210,7 +208,24 @@
</menuItem> </menuItem>
<menuItem title="Crash and usage information is anonymized and sent to development." enabled="NO" id="WfD-lX-C93"> <menuItem title="Crash and usage information is anonymized and sent to development." enabled="NO" id="WfD-lX-C93">
<attributedString key="attributedTitle"> <attributedString key="attributedTitle">
<fragment content="Save the password in your keychain so you don't need to enter it again."> <fragment content="Crash and usage information is anonymized and sent to development.">
<attributes>
<font key="NSFont" size="11" name="Helvetica"/>
<paragraphStyle key="NSParagraphStyle" alignment="natural" lineBreakMode="wordWrapping" baseWritingDirection="natural" firstLineHeadIndent="8"/>
</attributes>
</fragment>
</attributedString>
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="Copy Device Identifier" id="c2c-Vy-iqg">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="copyIdentifier:" target="494" id="dDF-fR-z6h"/>
</connections>
</menuItem>
<menuItem title="Your anonymous device identifier allows support to find your diagnostic reports." enabled="NO" id="xMa-pq-Bqg">
<attributedString key="attributedTitle">
<fragment content="Your anonymous device identifier allows support to find your diagnostic reports.">
<attributes> <attributes>
<font key="NSFont" size="11" name="Helvetica"/> <font key="NSFont" size="11" name="Helvetica"/>
<paragraphStyle key="NSParagraphStyle" alignment="natural" lineBreakMode="wordWrapping" baseWritingDirection="natural" firstLineHeadIndent="8"/> <paragraphStyle key="NSParagraphStyle" alignment="natural" lineBreakMode="wordWrapping" baseWritingDirection="natural" firstLineHeadIndent="8"/>
@ -241,6 +256,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;
@ -42,6 +42,7 @@
- (IBAction)showPasswordWindow:(id)sender; - (IBAction)showPasswordWindow:(id)sender;
- (void)setLoginItemEnabled:(BOOL)enabled; - (void)setLoginItemEnabled:(BOOL)enabled;
- (IBAction)togglePreference:(id)sender; - (IBAction)togglePreference:(id)sender;
- (IBAction)copyIdentifier:(id)sender;
- (IBAction)newUser:(NSMenuItem *)sender; - (IBAction)newUser:(NSMenuItem *)sender;
- (IBAction)lock:(id)sender; - (IBAction)lock:(id)sender;
- (IBAction)terminate:(id)sender; - (IBAction)terminate:(id)sender;

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,23 +70,24 @@ 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),
@"environment": @"Public", @"environment" : @"Public",
#else #else
@"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),
}]; }];
[[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) {
@ -131,11 +133,11 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
countlyConfig.deviceID = [PearlKeyChain deviceIdentifier]; countlyConfig.deviceID = [PearlKeyChain deviceIdentifier];
countlyConfig.secretSalt = decrypt( countlySalt ); countlyConfig.secretSalt = decrypt( countlySalt );
#if DEBUG #if DEBUG
countlyConfig.pushTestMode = CLYPushTestModeDevelopment;
countlyConfig.enableDebug = YES; countlyConfig.enableDebug = YES;
#elif ! PUBLIC countlyConfig.pushTestMode = CLYPushTestModeDevelopment;
countlyConfig.pushTestMode = CLYPushTestModeTestFlightOrAdHoc; #elif !PUBLIC
countlyConfig.enableDebug = NO; countlyConfig.enableDebug = NO;
countlyConfig.pushTestMode = CLYPushTestModeTestFlightOrAdHoc;
#endif #endif
[Countly.sharedInstance startWithConfig:countlyConfig]; [Countly.sharedInstance startWithConfig:countlyConfig];
} }
@ -144,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(), ^{
@ -158,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) {
@ -203,11 +203,13 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
.window makeKeyAndOrderFront:self]; .window makeKeyAndOrderFront:self];
[NSApp activateIgnoringOtherApps:YES]; [NSApp activateIgnoringOtherApps:YES];
} }
[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];
} }
@ -228,6 +230,42 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
return NSTerminateNow; return NSTerminateNow;
} }
- (void)tryNotifications {
[Countly.sharedInstance giveConsentForFeature:CLYConsentPushNotifications];
if (@available( macOS 10.14, * )) {
[Countly.sharedInstance askForNotificationPermissionWithOptions:UNAuthorizationOptionProvisional | UNAuthorizationOptionAlert
completionHandler:^(BOOL granted, NSError *error) {
if (!granted)
err( @"No provisional notification permission: %@", error );
[self askNotifications];
}];
}
else {
[self askNotifications];
}
}
- (void)askNotifications {
if ([[MPMacConfig get].notificationsDecided boolValue])
return;
PearlMainQueue( ^{
if (@available( macOS 10.14, * )) {
[Countly.sharedInstance askForNotificationPermissionWithOptions:UNAuthorizationOptionAlert completionHandler:
^(BOOL granted, NSError *error) {
[MPMacConfig get].notificationsDecided = @(YES);
}];
}
else {
[Countly.sharedInstance askForNotificationPermission];
[MPMacConfig get].notificationsDecided = @(YES);
}
} );
}
#pragma mark - State #pragma mark - State
- (void)setActiveUser:(MPUserEntity *)activeUser { - (void)setActiveUser:(MPUserEntity *)activeUser {
@ -371,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) {
@ -397,13 +435,19 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
[self updateMenuItems]; [self updateMenuItems];
} }
- (IBAction)copyIdentifier:(id)sender {
[[NSPasteboard generalPasteboard] declareTypes:@[ NSStringPboardType ] owner:nil];
if (![[NSPasteboard generalPasteboard] setString:[PearlKeyChain deviceIdentifier] forType:NSPasteboardTypeString])
wrn( @"Couldn't copy device identifier to pasteboard." );
}
- (IBAction)newUser:(NSMenuItem *)sender { - (IBAction)newUser:(NSMenuItem *)sender {
NSAlert *alert = [NSAlert new]; NSAlert *alert = [NSAlert new];
[alert setMessageText:@"New User"]; [alert setMessageText:@"New User"];
[alert setInformativeText:@"To begin, enter your full name.\n\n" [alert setInformativeText:@"To begin, enter your full name.\n\n"
@"IMPORTANT: Enter your name correctly, including the right capitalization, " @"IMPORTANT: Enter your name correctly, including the right capitalization, "
@"as you would on an official document."]; @"as you would on an official document."];
[alert addButtonWithTitle:@"Create User"]; [alert addButtonWithTitle:@"Create User"];
[alert addButtonWithTitle:@"Cancel"]; [alert addButtonWithTitle:@"Cancel"];
NSTextField *nameField = [[NSTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )]; NSTextField *nameField = [[NSTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )];
@ -464,7 +508,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 {
@ -508,15 +552,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"];
@ -541,43 +576,46 @@ 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;
return PearlMainQueueAwait( ^id { NSString *exportedUser = [self exportSitesFor:[self activeUserInContext:context] revealPasswords:revealPasswords askExportPassword:
NSAlert *alert = [NSAlert new]; ^NSString *(NSString *userName) {
[alert addButtonWithTitle:@"Import"]; return PearlMainQueueAwait( ^id {
[alert addButtonWithTitle:@"Cancel"]; NSAlert *alert = [NSAlert new];
alert.messageText = strf( @"Master Password For\n%@", userName ); [alert addButtonWithTitle:@"Export"];
alert.informativeText = @"Enter the current master password for this user."; [alert addButtonWithTitle:@"Cancel"];
alert.accessoryView = [[NSSecureTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )]; alert.messageText = strf( @"Master Password For\n%@", userName );
[alert layout]; alert.informativeText = @"Enter the current master password for this user.";
if ([alert runModal] == NSAlertFirstButtonReturn) alert.accessoryView = [[NSSecureTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )];
return ((NSTextField *)alert.accessoryView).stringValue; [alert layout];
else if ([alert runModal] == NSAlertFirstButtonReturn)
return nil; return ((NSTextField *)alert.accessoryView).stringValue;
} ); else
} result:^(NSString *mpsites, NSError *error) { return nil;
if (!mpsites || error) {
PearlMainQueue( ^{
[[NSAlert alertWithError:MPError( error, @"Failed to export mpsites." )] runModal];
} ); } );
return; } error:&error];
}
NSError *coordinateError = nil; if (error)
[[[NSFileCoordinator alloc] initWithFilePresenter:nil] PearlMainQueue( ^{
coordinateWritingItemAtURL:savePanel.URL options:0 error:&coordinateError byAccessor:^(NSURL *newURL) { [[NSAlert alertWithError:MPError( error, @"Failed to export mpsites." )] runModal];
NSError *writeError = nil; } );
if (![mpsites writeToURL:newURL atomically:NO encoding:NSUTF8StringEncoding error:&writeError]) if (!exportedUser)
PearlMainQueue( ^{ return;
[[NSAlert alertWithError:MPError( writeError, @"Could not write to the export file." )] runModal];
} ); NSError *coordinateError = nil;
}]; [[[NSFileCoordinator alloc] initWithFilePresenter:nil]
if (coordinateError) coordinateWritingItemAtURL:savePanel.URL options:0 error:&coordinateError byAccessor:^(NSURL *newURL) {
PearlMainQueue( ^{ NSError *writeError = nil;
[[NSAlert alertWithError:MPError( coordinateError, @"Could not gain access to the export file." )] runModal]; if (![exportedUser writeToURL:newURL atomically:NO encoding:NSUTF8StringEncoding error:&writeError])
} ); PearlMainQueue( ^{
}]; [[NSAlert alertWithError:MPError( writeError, @"Could not write to the export file." )] runModal];
} );
}];
if (coordinateError)
PearlMainQueue( ^{
[[NSAlert alertWithError:MPError( coordinateError, @"Could not gain access to the export file." )] runModal];
} );
}];
} }
- (void)updateUsers { - (void)updateUsers {
@ -614,7 +652,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
self.deleteUserItem.toolTip = mainActiveUser? nil: @"First select the user to delete."; self.deleteUserItem.toolTip = mainActiveUser? nil: @"First select the user to delete.";
NSError *error = nil; NSError *error = nil;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )]; NSFetchRequest *fetchRequest = [MPUserEntity fetchRequest];
fetchRequest.sortDescriptors = @[ [NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO] ]; fetchRequest.sortDescriptors = @[ [NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO] ];
NSArray *users = [mainContext executeFetchRequest:fetchRequest error:&error]; NSArray *users = [mainContext executeFetchRequest:fetchRequest error:&error];
if (!users) if (!users)
@ -650,13 +688,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];
@ -685,10 +716,13 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
} }
BOOL loginItemEnabled = [self loginItemEnabled]; BOOL loginItemEnabled = [self loginItemEnabled];
self.initialWindowController.openAtLoginButton.state = loginItemEnabled? NSOnState: NSOffState;
self.openAtLoginItem.state = loginItemEnabled? NSOnState: NSOffState; self.openAtLoginItem.state = loginItemEnabled? NSOnState: NSOffState;
self.showFullScreenItem.state = [[MPMacConfig get].fullScreen boolValue]? NSOnState: NSOffState; self.showFullScreenItem.state = [[MPMacConfig get].fullScreen boolValue]? NSOnState: NSOffState;
self.initialWindowController.openAtLoginButton.state = loginItemEnabled? NSOnState: NSOffState; self.rememberPasswordItem.state = [[MPMacConfig get].rememberLogin boolValue]? NSOnState: NSOffState;
self.rememberPasswordItem.state = [[MPConfig get].rememberLogin boolValue]? NSOnState: NSOffState; self.diagnosticsItem.state = [[MPMacConfig get].sendInfo boolValue]? NSOnState: NSOffState;
self.hidePasswordsItem.state = [[MPMacConfig get].hidePasswords boolValue]? NSOnState: NSOffState;
self.rememberPasswordItem.state = [[MPMacConfig get].rememberLogin boolValue]? NSOnState: NSOffState;
self.savePasswordItem.state = activeUser.saveKey? NSOnState: NSOffState; self.savePasswordItem.state = activeUser.saveKey? NSOnState: NSOffState;
if (!activeUser) { if (!activeUser) {
@ -708,6 +742,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 {
@ -719,47 +760,41 @@ 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
if ([[MPConfig get].sendInfo boolValue]) { NSArray *countlyFeatures = @[
PearlMainQueue( ^{ CLYConsentSessions, CLYConsentEvents, CLYConsentUserDetails, CLYConsentCrashReporting, CLYConsentViewTracking, CLYConsentStarRating
[Countly.sharedInstance giveConsentForAllFeatures]; ];
[Countly.sharedInstance askForNotificationPermission]; if ([[MPMacConfig get].sendInfo boolValue] || ![[MPMacConfig get].sendInfoDecided boolValue]) {
});
if ([PearlLogger get].printLevel > PearlLogLevelInfo) if ([PearlLogger get].printLevel > PearlLogLevelInfo)
[PearlLogger get].printLevel = PearlLogLevelInfo; [PearlLogger get].printLevel = PearlLogLevelInfo;
NSMutableDictionary *prefs = [NSMutableDictionary new];
prefs[@"rememberLogin"] = [MPConfig get].rememberLogin;
prefs[@"sendInfo"] = [MPConfig get].sendInfo;
prefs[@"fullScreen"] = [MPMacConfig get].fullScreen;
prefs[@"firstRun"] = [PearlConfig get].firstRun;
prefs[@"launchCount"] = [PearlConfig get].launchCount;
prefs[@"askForReviews"] = [PearlConfig get].askForReviews;
prefs[@"reviewAfterLaunches"] = [PearlConfig get].reviewAfterLaunches;
prefs[@"reviewedVersion"] = [PearlConfig get].reviewedVersion;
prefs[@"simulator"] = @([PearlDeviceUtils isSimulator]);
prefs[@"encrypted"] = @([PearlDeviceUtils isAppEncrypted]);
prefs[@"platform"] = [PearlDeviceUtils platform];
[SentrySDK.currentHub getClient].options.enabled = @YES; [SentrySDK.currentHub getClient].options.enabled = @YES;
[SentrySDK configureScope:^(SentryScope *scope) { [SentrySDK configureScope:^(SentryScope *scope) {
for (NSString *pref in prefs.allKeys) [scope setExtraValue:[MPMacConfig get].rememberLogin forKey:@"rememberLogin"];
[scope setExtraValue:prefs[pref] forKey:pref]; [scope setExtraValue:[MPMacConfig get].sendInfo forKey:@"sendInfo"];
[scope setExtraValue:[MPMacConfig get].fullScreen forKey:@"fullScreen"];
[scope setExtraValue:[PearlConfig get].firstRun forKey:@"firstRun"];
[scope setExtraValue:[PearlConfig get].launchCount forKey:@"launchCount"];
[scope setExtraValue:[PearlConfig get].askForReviews forKey:@"askForReviews"];
[scope setExtraValue:[PearlConfig get].reviewAfterLaunches forKey:@"reviewAfterLaunches"];
[scope setExtraValue:[PearlConfig get].reviewedVersion forKey:@"reviewedVersion"];
[scope setExtraValue:@([PearlDeviceUtils isSimulator]) forKey:@"simulator"];
[scope setExtraValue:@([PearlDeviceUtils isAppEncrypted]) forKey:@"encrypted"];
[scope setExtraValue:[PearlDeviceUtils platform] forKey:@"platform"];
}]; }];
[Countly.sharedInstance giveConsentForFeatures:countlyFeatures];
} }
else { else {
[Countly.sharedInstance cancelConsentForFeatures:countlyFeatures];
[SentrySDK.currentHub getClient].options.enabled = @NO; [SentrySDK.currentHub getClient].options.enabled = @NO;
PearlMainQueue( ^{
[Countly.sharedInstance cancelConsentForAllFeatures];
});
} }
} }

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
if (s == NSNotFound) range:NSMakeRange( s, [siteName length] - s )].location;
break; if (s == NSNotFound)
break;
[attributedSiteName addAttribute:NSBackgroundColorAttributeName value:[NSColor alternateSelectedControlColor]
range:NSMakeRange( s, [queryGroups[f] length] )];
}
[attributedSiteName addAttribute:NSBackgroundColorAttributeName value:[NSColor alternateSelectedControlColor]
range:NSMakeRange( s, [fuzzyGroups[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)
@ -210,12 +213,12 @@
- (BOOL)generated { - (BOOL)generated {
return self.type & MPResultTypeClassTemplate; return (self.type & MPResultTypeClassTemplate) == MPResultTypeClassTemplate;
} }
- (BOOL)stored { - (BOOL)stored {
return self.type & MPResultTypeClassStateful; return (self.type & MPResultTypeClassStateful) == MPResultTypeClassStateful;
} }
- (BOOL)transient { - (BOOL)transient {

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];
if (selectedSite.loginName.length) notification.title = self.shiftPressed? @"Login Copied": @"Password Copied";
notification.subtitle = strf( @"%@ at %@", selectedSite.loginName, selectedSite.name ); if (selectedSite.loginName.length)
else notification.subtitle = strf( @"%@ at %@", selectedSite.loginName, selectedSite.name );
notification.subtitle = selectedSite.name; else
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification]; notification.subtitle = selectedSite.name;
[[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,26 +611,24 @@
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];
NSMutableString *queryPattern = [NSMutableString new];
[queryString enumerateSubstringsInRange: NSMakeRange(0, [queryString length]) options: NSStringEnumerationByComposedCharacterSequences
usingBlock: ^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
if (substringRange.location < 20) {
[queryGroups addObject:substring];
[queryPattern appendString:@"*"];
}
[queryPattern appendString:substring];
}];
[queryPattern appendString:@"*"];
prof_rewind( @"queryPattern" ); prof_rewind( @"queryPattern" );
NSMutableArray *fuzzyGroups = [NSMutableArray new];
[fuzzyRE enumerateMatchesInString:queryString options:0 range:NSMakeRange( 0, queryString.length )
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
[fuzzyGroups addObject:[queryString substringWithRange:result.range]];
}];
prof_rewind( @"fuzzyRE" );
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
prof_rewind( @"moc" ); prof_rewind( @"moc" );
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )]; NSFetchRequest *fetchRequest = [MPSiteEntity fetchRequest];
fetchRequest.sortDescriptors = @[ [[NSSortDescriptor alloc] initWithKey:@"lastUsed" ascending:NO] ]; fetchRequest.sortDescriptors = @[ [[NSSortDescriptor alloc] initWithKey:@"lastUsed" ascending:NO] ];
fetchRequest.predicate = fetchRequest.predicate =
[NSPredicate predicateWithFormat:@"name LIKE[cd] %@ AND user == %@", queryPattern, [MPMacAppDelegate get].activeUserOID]; [NSPredicate predicateWithFormat:@"name LIKE[cd] %@ AND user == %@", queryPattern, [MPMacAppDelegate get].activeUserOID];
@ -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,20 +31,20 @@
<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"/>
@ -72,7 +72,7 @@
</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"/>
@ -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,7 +339,7 @@
</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"/>
@ -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,10 +591,10 @@ 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"/>
@ -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,7 +917,7 @@ 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"/>
@ -923,7 +954,7 @@ 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"/>
@ -960,7 +991,7 @@ 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"/>
@ -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,7 +1047,7 @@ 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"/>
@ -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>

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

@ -1,34 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>en</string> <string>en</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string> <string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key> <key>CFBundleIconFile</key>
<string></string> <string></string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>${PRODUCT_NAME}</string> <string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.0</string> <string>1.0</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1</string> <string>1</string>
<key>LSBackgroundOnly</key> <key>LSApplicationCategoryType</key>
<true/> <string>public.app-category.productivity</string>
<key>LSMinimumSystemVersion</key> <key>LSBackgroundOnly</key>
<string>${MACOSX_DEPLOYMENT_TARGET}</string> <true/>
<key>NSHumanReadableCopyright</key> <key>LSMinimumSystemVersion</key>
<string>Copyright © 2013 Maarten Billemont. All rights reserved.</string> <string>${MACOSX_DEPLOYMENT_TARGET}</string>
<key>NSPrincipalClass</key> <key>NSHumanReadableCopyright</key>
<string>NSApplication</string> <string>Copyright © 2013 Maarten Billemont. All rights reserved.</string>
</dict> <key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist> </plist>

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>
#import "IASKAppSettingsViewController.h"
@interface MPAppSettingsViewController : IASKAppSettingsViewController
@end

View File

@ -1,45 +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 "MPAppSettingsViewController.h"
#import "UIColor+Expanded.h"
@implementation MPAppSettingsViewController
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.tableView.contentInset = UIEdgeInsetsMake( 64, 0, 49, 0 );
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
cell.backgroundColor = [UIColor clearColor];
cell.textLabel.textColor = [UIColor whiteColor];
if (cell.selectionStyle != UITableViewCellSelectionStyleNone) {
cell.selectedBackgroundView = [[UIView alloc] initWithFrame:cell.bounds];
cell.selectedBackgroundView.backgroundColor = [UIColor colorWithRGBAHex:0x78DDFB33];
}
return cell;
}
@end

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 {
@ -102,8 +101,6 @@
#pragma mark - Actions #pragma mark - Actions
- (IBAction)unwindToCombined:(UIStoryboardSegue *)sender { - (IBAction)unwindToCombined:(UIStoryboardSegue *)sender {
dbg( @"unwindToCombined:%@", sender );
} }
#pragma mark - State #pragma mark - State
@ -127,7 +124,7 @@
switch (self.mode) { switch (self.mode) {
case MPCombinedModeUserSelection: { case MPCombinedModeUserSelection: {
self.usersVC.view.userInteractionEnabled = YES; self.usersVC.userSelectionContainer.userInteractionEnabled = YES;
[self.usersVC setActive:YES animated:animated]; [self.usersVC setActive:YES animated:animated];
if (self.sitesVC) { if (self.sitesVC) {
MPSitesSegue *segue = [[MPSitesSegue alloc] initWithIdentifier:@"passwords" source:self.sitesVC destination:self]; MPSitesSegue *segue = [[MPSitesSegue alloc] initWithIdentifier:@"passwords" source:self.sitesVC destination:self];
@ -137,7 +134,7 @@
break; break;
} }
case MPCombinedModePasswordSelection: { case MPCombinedModePasswordSelection: {
self.usersVC.view.userInteractionEnabled = NO; self.usersVC.userSelectionContainer.userInteractionEnabled = NO;
[self.usersVC setActive:NO animated:animated]; [self.usersVC setActive:NO animated:animated];
[self performSegueWithIdentifier:@"passwords" sender:@{ @"animated": @(animated) }]; [self performSegueWithIdentifier:@"passwords" sender:@{ @"animated": @(animated) }];
break; break;

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,7 +18,6 @@
#import "MPLogsViewController.h" #import "MPLogsViewController.h"
#import "MPiOSAppDelegate.h" #import "MPiOSAppDelegate.h"
#import "MPAppDelegate_Store.h"
@implementation MPLogsViewController @implementation MPLogsViewController
@ -52,6 +51,17 @@
PearlRemoveNotificationObservers(); PearlRemoveNotificationObservers();
} }
#pragma clang diagnostic push
#pragma ide diagnostic ignored "UnavailableInDeploymentTarget"
- (void)viewSafeAreaInsetsDidChange {
[super viewSafeAreaInsetsDidChange];
self.logView.contentInset = UIEdgeInsetsMake( 44, 0, 0, 0 );
self.logView.scrollIndicatorInsets = UIEdgeInsetsMake( 44, 0, 0, 0 );
}
#pragma clang diagnostic pop
- (IBAction)toggleLevelControl:(UISegmentedControl *)sender { - (IBAction)toggleLevelControl:(UISegmentedControl *)sender {
BOOL traceEnabled = (BOOL)self.levelControl.selectedSegmentIndex; BOOL traceEnabled = (BOOL)self.levelControl.selectedSegmentIndex;

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

@ -52,6 +52,17 @@
[self reload]; [self reload];
} }
#pragma clang diagnostic push
#pragma ide diagnostic ignored "UnavailableInDeploymentTarget"
- (void)viewSafeAreaInsetsDidChange {
[super viewSafeAreaInsetsDidChange];
self.tableView.contentInset = UIEdgeInsetsMake( 44, 0, 0, 0 );
self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake( 44, 0, 0, 0 );
}
#pragma clang diagnostic pop
- (void)reload { - (void)reload {
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserForMainThread]; MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserForMainThread];

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]
@ -512,132 +515,140 @@
- (void)updateAnimated:(BOOL)animated { - (void)updateAnimated:(BOOL)animated {
Weakify( self );
if (![NSThread isMainThread]) { if (![NSThread isMainThread]) {
PearlMainQueueOperation( ^{ PearlMainQueueOperation( ^{
Strongify( self );
[self updateAnimated:animated]; [self updateAnimated:animated];
} ); } );
return; return;
} }
[UIView animateWithDuration:animated? .3f: 0 animations:^{ if (animated)
MPSiteEntity *mainSite = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]]; [UIView animateWithDuration:.3f animations:^{
[self updateWasAnimated:animated];
}];
else
[self updateWasAnimated:animated];
}
// UI - (void)updateWasAnimated:(BOOL)animated {
//self.backgroundColor = mainSite.url? [UIColor greenColor]: [UIColor redColor];
self.upgradeButton.gone = !mainSite.requiresExplicitMigration && ![[MPiOSConfig get].allowDowngrade boolValue];
self.answersButton.gone = ![[MPiOSAppDelegate get] isFeatureUnlocked:MPProductGenerateAnswers];
BOOL settingsMode = self.mode == MPPasswordCellModeSettings;
self.loginNameContainer.visible = settingsMode || mainSite.loginGenerated || [mainSite.loginName length];
self.modeButton.visible = !self.transientSite;
self.modeButton.alpha = settingsMode? 0.5f: 0.1f;
self.counterLabel.visible = self.counterButton.visible = mainSite.type & MPResultTypeClassTemplate;
self.modeButton.selected = settingsMode;
self.strengthLabel.gone = !settingsMode;
self.modeScrollView.scrollEnabled = !self.transientSite;
[self.modeScrollView setContentOffset:CGPointMake( self.mode * self.modeScrollView.frame.size.width, 0 ) animated:animated];
if (!settingsMode) {
[self.loginNameField resignFirstResponder];
[self.passwordField resignFirstResponder];
}
if ([[MPiOSAppDelegate get] isFeatureUnlocked:MPProductGenerateLogins])
self.loginNameHint.text = @"Tap here to ⚙ generate username or the pencil to type one";
else
self.loginNameHint.text = @"Tap the pencil to type a username";
// Site Name Weakify( self );
[self updateSiteName:mainSite]; MPSiteEntity *mainSite = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
// Site Counter // UI
if ([mainSite isKindOfClass:[MPGeneratedSiteEntity class]]) //self.backgroundColor = mainSite.url? [UIColor greenColor]: [UIColor redColor];
self.counterLabel.text = strf( @"%lu", (unsigned long)((MPGeneratedSiteEntity *)mainSite).counter ); self.upgradeButton.visible = mainSite.requiresExplicitMigration || [[MPiOSConfig get].allowDowngrade boolValue];
self.answersButton.visible = [[MPiOSAppDelegate get] isFeatureUnlocked:MPProductGenerateAnswers];
BOOL settingsMode = self.mode == MPPasswordCellModeSettings;
self.loginNameContainer.visible = settingsMode || mainSite.loginGenerated || [mainSite.loginName length];
self.modeButton.visible = !self.transientSite;
self.modeButton.alpha = settingsMode? 0.5f: 0.1f;
self.counterLabel.visible = self.counterButton.visible = (mainSite.type & MPResultTypeClassTemplate) == MPResultTypeClassTemplate;
self.modeButton.selected = settingsMode;
self.strengthLabel.gone = !settingsMode;
self.modeScrollView.scrollEnabled = !self.transientSite;
[self.modeScrollView setContentOffset:CGPointMake( self.mode * self.modeScrollView.frame.size.width, 0 ) animated:animated];
if (!settingsMode) {
[self.loginNameField resignFirstResponder];
[self.passwordField resignFirstResponder];
}
if ([[MPiOSAppDelegate get] isFeatureUnlocked:MPProductGenerateLogins])
self.loginNameHint.text = @"Tap here to ⚙ generate username or the pencil to type one";
else
self.loginNameHint.text = @"Tap the pencil to type a username";
// Site Login Name // Site Name
self.loginNameField.enabled = self.passwordField.enabled = // [self updateSiteName:mainSite];
[self.loginNameField isFirstResponder] || [self.passwordField isFirstResponder];
// Site Password // Site Counter
self.passwordField.secureTextEntry = [[MPiOSConfig get].hidePasswords boolValue]; if ([mainSite isKindOfClass:[MPGeneratedSiteEntity class]])
self.passwordField.attributedPlaceholder = stra( self.counterLabel.text = strf( @"%lu", (unsigned long)((MPGeneratedSiteEntity *)mainSite).counter );
mainSite.type & MPResultTypeClassStateful? strl( @"No password" ):
mainSite.type & MPResultTypeClassTemplate? strl( @"..." ): @"", @{
NSForegroundColorAttributeName: [UIColor whiteColor]
} );
// Calculate Fields // Site Login Name
if (![MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { self.loginNameField.enabled = self.passwordField.enabled = //
MPKey *key = [MPiOSAppDelegate get].key; [self.loginNameField isFirstResponder] || [self.passwordField isFirstResponder];
if (!key) {
wrn( @"Could not load cell content: key unavailable." );
PearlMainQueueOperation( ^{
Strongify( self );
[self updateAnimated:YES];
} );
return;
}
MPSiteEntity *site = [self siteInContext:context]; // Site Password
BOOL loginGenerated = site.loginGenerated; self.passwordField.secureTextEntry = [[MPiOSConfig get].hidePasswords boolValue];
NSString *password = nil, *loginName = [site resolveLoginUsingKey:key]; self.passwordField.attributedPlaceholder = stra(
MPResultType transientType = [[MPiOSAppDelegate get] activeUserInContext:context].defaultType?: MPAlgorithmDefault.defaultType; mainSite.type & MPResultTypeClassStateful? strl( @"No password" ):
if (self.transientSite && transientType & MPResultTypeClassTemplate) mainSite.type & MPResultTypeClassTemplate? strl( @"..." ): @"", @{
password = [MPAlgorithmDefault mpwTemplateForSiteNamed:self.transientSite ofType:transientType NSForegroundColorAttributeName: [UIColor whiteColor]
withCounter:1 usingKey:key];
else if (site)
password = [site resolvePasswordUsingKey:key];
TimeToCrack timeToCrack;
NSString *timeToCrackString = nil;
id<MPAlgorithm> algorithm = site.algorithm?: MPAlgorithmDefault;
MPAttacker attackHardware = [[MPConfig get].siteAttacker integerValue];
if ([algorithm timeToCrack:&timeToCrack passwordOfType:site.type byAttacker:attackHardware] ||
[algorithm timeToCrack:&timeToCrack passwordString:password byAttacker:attackHardware])
timeToCrackString = NSStringFromTimeToCrack( timeToCrack );
BOOL requiresExplicitMigration = site.requiresExplicitMigration;
PearlMainQueue( ^{
self.passwordField.text = password;
self.strengthLabel.text = timeToCrackString;
self.loginNameGenerated.hidden = !loginGenerated;
self.loginNameField.attributedText =
strarm( stra( loginName?: @"", self.siteNameLabel.textAttributes ), NSParagraphStyleAttributeName, nil );
self.loginNameHint.hidden = [loginName length] || self.loginNameField.enabled;
if (![password length]) {
self.indicatorView.hidden = NO;
[self.indicatorView removeFromSuperview];
[self.modeScrollView addSubview:self.indicatorView];
[self.contentView addConstraintsWithVisualFormat:@"V:[indicator][target]" options:NSLayoutFormatAlignAllCenterX
metrics:nil views:@{
@"indicator": self.indicatorView,
@"target" : settingsMode? self.editButton: self.modeButton
}];
}
else if (requiresExplicitMigration) {
self.indicatorView.hidden = NO;
[self.indicatorView removeFromSuperview];
[self.modeScrollView addSubview:self.indicatorView];
[self.contentView addConstraintsWithVisualFormat:@"V:[indicator][target]" options:NSLayoutFormatAlignAllCenterX
metrics:nil views:@{
@"indicator": self.indicatorView,
@"target" : settingsMode? self.upgradeButton: self.modeButton
}];
}
else
self.indicatorView.hidden = YES;
} ); } );
}]) {
wrn( @"Could not load cell content: store unavailable." ); // Calculate Fields
if (![MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPKey *key = [MPiOSAppDelegate get].key;
if (!key) {
wrn( @"Could not load cell content: key unavailable." );
PearlMainQueueOperation( ^{ PearlMainQueueOperation( ^{
Strongify( self ); Strongify( self );
[self updateAnimated:YES]; [self updateAnimated:animated];
} ); } );
return;
} }
MPSiteEntity *site = [self siteInContext:context];
BOOL loginGenerated = site.loginGenerated;
NSString *password = nil, *loginName = [site resolveLoginUsingKey:key];
MPResultType transientType = [[MPiOSAppDelegate get] activeUserInContext:context].defaultType?: MPAlgorithmDefault.defaultType;
if (self.transientSite && transientType & MPResultTypeClassTemplate)
password = [MPAlgorithmDefault mpwTemplateForSiteNamed:self.transientSite ofType:transientType
withCounter:1 usingKey:key];
else if (site)
password = [site resolvePasswordUsingKey:key];
TimeToCrack timeToCrack;
NSString *timeToCrackString = nil;
id<MPAlgorithm> algorithm = site.algorithm?: MPAlgorithmDefault;
MPAttacker attackHardware = [[MPConfig get].siteAttacker integerValue];
if ([algorithm timeToCrack:&timeToCrack passwordOfType:site.type byAttacker:attackHardware] ||
[algorithm timeToCrack:&timeToCrack passwordString:password byAttacker:attackHardware])
timeToCrackString = NSStringFromTimeToCrack( timeToCrack );
BOOL requiresExplicitMigration = site.requiresExplicitMigration;
PearlMainQueue( ^{
self.passwordField.text = password;
self.strengthLabel.text = timeToCrackString;
self.loginNameGenerated.hidden = !loginGenerated;
self.loginNameField.attributedText =
strarm( stra( loginName?: @"", self.siteNameLabel.textAttributes ), NSParagraphStyleAttributeName, nil );
self.loginNameHint.hidden = [loginName length] || self.loginNameField.enabled;
if (![password length]) {
self.indicatorView.hidden = NO;
[self.indicatorView removeFromSuperview];
[self.modeScrollView addSubview:self.indicatorView];
[self.contentView addConstraintsWithVisualFormat:@"V:[indicator][target]" options:NSLayoutFormatAlignAllCenterX
metrics:nil views:@{
@"indicator": self.indicatorView,
@"target" : settingsMode? self.editButton: self.modeButton
}];
}
else if (requiresExplicitMigration) {
self.indicatorView.hidden = NO;
[self.indicatorView removeFromSuperview];
[self.modeScrollView addSubview:self.indicatorView];
[self.contentView addConstraintsWithVisualFormat:@"V:[indicator][target]" options:NSLayoutFormatAlignAllCenterX
metrics:nil views:@{
@"indicator": self.indicatorView,
@"target" : settingsMode? self.upgradeButton: self.modeButton
}];
}
else
self.indicatorView.hidden = YES;
} );
}]) {
wrn( @"Could not load cell content: store unavailable." );
PearlMainQueueOperation( ^{
Strongify( self );
[self updateAnimated:animated];
} );
}
if (animated)
[self.contentView layoutIfNeeded]; [self.contentView layoutIfNeeded];
}];
} }
- (void)updateSiteName:(MPSiteEntity *)site { - (void)updateSiteName:(MPSiteEntity *)site {
@ -645,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,6 @@
@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 *voltoMigrateAlert;
@property(assign, nonatomic) BOOL active; @property(assign, nonatomic) BOOL active;
@ -39,6 +37,5 @@
- (void)reloadSites; - (void)reloadSites;
- (IBAction)dismissPopdown:(id)sender; - (IBAction)dismissPopdown:(id)sender;
- (IBAction)upgradeVolto:(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;
@ -64,13 +65,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
self.collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; self.collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
[self.collectionView automaticallyAdjustInsetsForKeyboard]; [self.collectionView automaticallyAdjustInsetsForKeyboard];
self.searchBar.autocapitalizationType = UITextAutocapitalizationTypeNone; self.searchBar.autocapitalizationType = UITextAutocapitalizationTypeNone;
if ([self.searchBar respondsToSelector:@selector( keyboardAppearance )]) self.searchBar.keyboardAppearance = UIKeyboardAppearanceDark;
self.searchBar.keyboardAppearance = UIKeyboardAppearanceDark;
else
[self.searchBar enumerateViews:^(UIView *subview, BOOL *stop, BOOL *recurse) {
if ([subview isKindOfClass:[UITextField class]])
((UITextField *)subview).keyboardAppearance = UIKeyboardAppearanceDark;
} recurse:YES];
} }
- (void)viewWillAppear:(BOOL)animated { - (void)viewWillAppear:(BOOL)animated {
@ -79,7 +74,6 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
[self registerObservers]; [self registerObservers];
[self updateConfigKey:nil]; [self updateConfigKey:nil];
[self updateVoltoAlerts];
static NSRegularExpression *bareHostRE = nil; static NSRegularExpression *bareHostRE = nil;
static dispatch_once_t once = 0; static dispatch_once_t once = 0;
@ -127,6 +121,16 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
PearlRemoveNotificationObservers(); PearlRemoveNotificationObservers();
} }
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
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 {
if ([segue.identifier isEqualToString:@"popdown"]) if ([segue.identifier isEqualToString:@"popdown"])
@ -144,33 +148,6 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
} }
#pragma mark - UICollectionViewDelegateFlowLayout
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)collectionViewLayout;
CGFloat itemWidth = UIEdgeInsetsInsetRect( collectionView.bounds, layout.sectionInset ).size.width;
return CGSizeMake( itemWidth, 100 );
}
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout
insetForSectionAtIndex:(NSInteger)section {
UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)collectionViewLayout;
UIEdgeInsets occludedInsets = [self.collectionView occludedInsets];
UIEdgeInsets insets = layout.sectionInset;
insets.top = insets.bottom; // Undo storyboard hack for manual top-occluded insets.
if (section == 0)
insets.top += occludedInsets.top;
if (section == collectionView.numberOfSections - 1)
insets.bottom += occludedInsets.bottom;
return insets;
}
#pragma mark - UICollectionViewDataSource #pragma mark - UICollectionViewDataSource
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
@ -186,7 +163,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];
@ -196,6 +173,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 {
@ -210,11 +200,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
@ -331,7 +317,6 @@ 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];
} ); } );
PearlAddNotificationObserver( MPSignedOutNotification, nil, nil, PearlAddNotificationObserver( MPSignedOutNotification, nil, nil,
^(MPSitesViewController *self, NSNotification *note) { ^(MPSitesViewController *self, NSNotification *note) {
@ -379,39 +364,42 @@ 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:@"*"];
self.fuzzyGroups = fuzzyGroups; }
[queryPattern appendString:substring];
}];
[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." );
PearlMainQueue( ^{ [self updateSites];
[self.collectionView updateDataSource:self.dataSource
toSections:[self createDataSource]
reloadItems:@[ MPTransientPasswordItem ] completion:^(BOOL finished) {
for (MPSiteCell *cell in self.collectionView.visibleCells)
[cell setFuzzyGroups:self.fuzzyGroups];
}];
} );
}]; }];
} }
- (void)updateSites {
PearlMainQueue( ^{
[self.collectionView updateDataSource:self.dataSource
toSections:[self createDataSource]
reloadItems:@[ MPTransientPasswordItem ] completion:^(BOOL finished) {
for (MPSiteCell *cell in self.collectionView.visibleCells)
[cell setQueryGroups:self.queryGroups];
}];
} );
}
#pragma mark - Properties #pragma mark - Properties
- (NSString *)query { - (NSString *)query {
@ -429,14 +417,20 @@ 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; fetchRequest.predicate = [NSPredicate predicateWithFormat:@"user == %@", [MPiOSAppDelegate get].activeUserOID];
(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];
} }
@ -460,13 +454,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 {
@ -477,77 +464,4 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
self.popdownToTopConstraint.priority = UILayoutPriorityDefaultHigh; self.popdownToTopConstraint.priority = UILayoutPriorityDefaultHigh;
} }
- (IBAction)upgradeVolto:(UIButton *)sender {
if ([UIApp canOpenURL:[[NSURL alloc] initWithString:@"volto:"]]) {
[[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
- (void)updateVoltoAlerts {
BOOL voltoInstalled = [UIApp canOpenURL:[[NSURL alloc] initWithString:@"volto:"]];
if (voltoInstalled) {
self.voltoInstallAlert.visible = NO;
self.voltoMigrateAlert.visible = YES;
}
else {
self.voltoInstallAlert.visible = NO;
self.voltoMigrateAlert.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;
}];
}];
}
}
@end @end

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 );
@ -64,6 +67,17 @@ PearlEnum( MPDevelopmentFuelConsumption,
[[MPiOSAppDelegate get] removeProductsObserver:self]; [[MPiOSAppDelegate get] removeProductsObserver:self];
} }
#pragma clang diagnostic push
#pragma ide diagnostic ignored "UnavailableInDeploymentTarget"
- (void)viewSafeAreaInsetsDidChange {
[super viewSafeAreaInsetsDidChange];
self.tableView.contentInset = UIEdgeInsetsMake( 44, 0, 0, 0 );
self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake( 44, 0, 0, 0 );
}
#pragma clang diagnostic pop
#pragma mark - UITableViewDataSource #pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

View File

@ -35,10 +35,14 @@
@property(weak, nonatomic) IBOutlet UIButton *nextAvatarButton; @property(weak, nonatomic) IBOutlet UIButton *nextAvatarButton;
@property(weak, nonatomic) IBOutlet UIButton *previousAvatarButton; @property(weak, nonatomic) IBOutlet UIButton *previousAvatarButton;
@property(weak, nonatomic) IBOutlet NSLayoutConstraint *keyboardHeightConstraint; @property(weak, nonatomic) IBOutlet NSLayoutConstraint *keyboardHeightConstraint;
@property(weak, nonatomic) IBOutlet UIView *spectreInstallAlert;
@property(weak, nonatomic) IBOutlet UIView *spectreMigrateAlert;
@property(assign, nonatomic, readonly) BOOL active; @property(assign, nonatomic, readonly) BOOL active;
- (void)setActive:(BOOL)active animated:(BOOL)animated; - (void)setActive:(BOOL)active animated:(BOOL)animated;
- (IBAction)changeAvatar:(UIButton *)sender; - (IBAction)changeAvatar:(UIButton *)sender;
- (IBAction)upgradeSpectre:(UIButton *)sender;
- (IBAction)dismissSpectre:(UIButton *)sender;
@end @end

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 {
@ -99,6 +100,7 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
[self registerObservers]; [self registerObservers];
[self reloadUsers]; [self reloadUsers];
[self updateSpectreAlerts];
[self.marqueeTipTimer invalidate]; [self.marqueeTipTimer invalidate];
self.marqueeTipTimer = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector( firedMarqueeTimer: ) self.marqueeTipTimer = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector( firedMarqueeTimer: )
@ -231,12 +233,10 @@ 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
@ -434,6 +434,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 +644,6 @@ referenceSizeForFooterInSection:(NSInteger)section {
[self removeKeyPathObservers]; [self removeKeyPathObservers];
PearlRemoveNotificationObservers(); PearlRemoveNotificationObservers();
[[NSNotificationCenter defaultCenter] removeObserver:self.contextChangedObserver];
} }
- (void)registerObservers { - (void)registerObservers {
@ -661,6 +661,7 @@ referenceSizeForFooterInSection:(NSInteger)section {
PearlAddNotificationObserver( UIApplicationWillEnterForegroundNotification, nil, [NSOperationQueue mainQueue], PearlAddNotificationObserver( UIApplicationWillEnterForegroundNotification, nil, [NSOperationQueue mainQueue],
^(MPUsersViewController *self, NSNotification *note) { ^(MPUsersViewController *self, NSNotification *note) {
[self reloadUsers]; [self reloadUsers];
[self updateSpectreAlerts];
} ); } );
PearlAddNotificationObserver( UIApplicationDidBecomeActiveNotification, nil, [NSOperationQueue mainQueue], PearlAddNotificationObserver( UIApplicationDidBecomeActiveNotification, nil, [NSOperationQueue mainQueue],
^(MPUsersViewController *self, NSNotification *note) { ^(MPUsersViewController *self, NSNotification *note) {
@ -675,24 +676,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 +687,67 @@ 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 = [MPUserEntity fetchRequest];
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
MPError( error, @"Failed to load users." ); sectionNameKeyPath:nil cacheName:nil];
self.userIDs = nil; self.userResultsController.delegate = self;
}
NSMutableArray *userIDs = [NSMutableArray arrayWithCapacity:[users count]]; NSError *error = nil;
for (MPUserEntity *user in users) if (![self.userResultsController performFetch:&error])
[userIDs addObject:user.permanentObjectID]; MPError( error, @"Failed to load users." );
self.userIDs = userIDs;
[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]];
for (MPUserEntity *user in users)
[userIDs addObject:user.permanentObjectID];
self.userIDs = userIDs;
}];
}
- (void)updateSpectreAlerts {
BOOL spectreInstalled = [UIApp canOpenURL:[[NSURL alloc] initWithString:@"spectre:"]];
if (spectreInstalled) {
self.spectreInstallAlert.visible = NO;
self.spectreMigrateAlert.visible = YES;
}
else {
self.spectreInstallAlert.visible = [MPiOSAppDelegate get].spectreViewController != nil;
self.spectreMigrateAlert.visible = NO;
}
}
#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 {
@ -904,4 +922,15 @@ referenceSizeForFooterInSection:(NSInteger)section {
++[self selectedAvatar].avatar; ++[self selectedAvatar].avatar;
} }
- (IBAction)upgradeSpectre:(UIButton *)sender {
[[MPiOSAppDelegate get] migrateFor:[MPiOSAppDelegate get].activeUserForMainThread];
}
- (IBAction)dismissSpectre:(UIButton *)sender {
self.spectreInstallAlert.visible = NO;
self.spectreMigrateAlert.visible = NO;
}
@end @end

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;
self.webNavigationItem.prompt = strl( @"Loading" );
}
return YES;
} }
- (void)webViewDidStartLoad:(UIWebView *)webView { - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
self.webNavigationItem.title = webView.URL.host;
self.webNavigationItem.prompt = strl( @"Loading" );
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,18 +23,35 @@
#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>
@property(nonatomic, strong) UIDocumentInteractionController *interactionController; @property(nonatomic, strong) UIDocumentInteractionController *interactionController;
@property(nonatomic, strong) PearlHangDetector *hangDetector; @property(nonatomic, strong) PearlHangDetector *hangDetector;
@end @end
@implementation MPiOSAppDelegate @implementation MPiOSAppDelegate
@synthesize window;
+ (void)initialize { + (void)initialize {
[MPiOSConfig get]; [MPiOSConfig get];
@ -43,21 +60,6 @@
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
@try { @try {
// Sentry
[SentrySDK initWithOptions:@{
@"dsn" : NilToNSNull( decrypt( sentryDSN ) ),
#ifdef DEBUG
@"debug" : @(YES),
@"environment": @"Development",
#elif PUBLIC
@"debug" : @(NO),
@"environment": @"Public",
#else
@"debug" : @(NO),
@"environment": @"Private",
#endif
@"enabled" : [MPiOSConfig get].sendInfo,
}];
[[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) { [[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) {
PearlLogLevel level = PearlLogLevelWarn; PearlLogLevel level = PearlLogLevelWarn;
if ([[MPConfig get].sendInfo boolValue]) if ([[MPConfig get].sendInfo boolValue])
@ -102,15 +104,16 @@
countlyConfig.appKey = decrypt( countlyKey ); countlyConfig.appKey = decrypt( countlyKey );
countlyConfig.features = @[ CLYPushNotifications, CLYAutoViewTracking ]; countlyConfig.features = @[ CLYPushNotifications, CLYAutoViewTracking ];
countlyConfig.requiresConsent = YES; countlyConfig.requiresConsent = YES;
#if DEBUG
countlyConfig.pushTestMode = CLYPushTestModeDevelopment;
#elif ! PUBLIC
countlyConfig.pushTestMode = CLYPushTestModeTestFlightOrAdHoc;
#endif
countlyConfig.alwaysUsePOST = YES; countlyConfig.alwaysUsePOST = YES;
countlyConfig.deviceID = [PearlKeyChain deviceIdentifier]; countlyConfig.deviceID = [PearlKeyChain deviceIdentifier];
countlyConfig.secretSalt = decrypt( countlySalt ); countlyConfig.secretSalt = decrypt( countlySalt );
countlyConfig.enableDebug = YES; #if DEBUG
//countlyConfig.enableDebug = YES;
countlyConfig.pushTestMode = CLYPushTestModeDevelopment;
#elif ! PUBLIC
countlyConfig.enableDebug = NO;
countlyConfig.pushTestMode = CLYPushTestModeTestFlightOrAdHoc;
#endif
[Countly.sharedInstance startWithConfig:countlyConfig]; [Countly.sharedInstance startWithConfig:countlyConfig];
#if ! DEBUG #if ! DEBUG
@ -129,18 +132,14 @@
[self updateConfigKey:note.object]; [self updateConfigKey:note.object];
} ); } );
PearlAddNotificationObserver( NSUserDefaultsDidChangeNotification, nil, nil, ^(id self, NSNotification *note) { PearlAddNotificationObserver( NSUserDefaultsDidChangeNotification, nil, nil, ^(id self, NSNotification *note) {
[[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:nil]; PearlMainQueueOperation( ^{
[[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:nil];
} );
} ); } );
} }
@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] );
@ -171,30 +170,29 @@
} }
} ); } );
if (@available( iOS 12, * )) { SKStoreProductViewController *migrateVC = [SKStoreProductViewController new];
[Countly.sharedInstance askForNotificationPermissionWithOptions:UNAuthorizationOptionProvisional completionHandler: [migrateVC loadProductWithParameters:@{
^(BOOL granted, NSError *error) { SKStoreProductParameterCampaignToken : @"app-masterpassword.ios", /* Campaign: From MasterPassword iOS */
inf( @"provisional: %d: %@", granted, error ); SKStoreProductParameterProviderToken : @153897, /* Provider: Maarten Billemont */
}]; // SKStoreProductParameterITunesItemIdentifier: @510296984, /* Application: MasterPassword iOS */
} SKStoreProductParameterITunesItemIdentifier: @1526402806, /* 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];
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"notificationsDecided"]) { [self consentFeatures];
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"
@"When it's time, we'll send you a notification to help you make an effortless transition."
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Thanks" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[Countly.sharedInstance askForNotificationPermission];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"notificationsDecided"];
}]];
[(self.navigationController.presentedViewController?: (UIViewController *)self.navigationController)
presentViewController:alert animated:YES completion:nil];
}
} ); } );
} }
@catch (id exception) { @catch (id exception) {
@ -211,6 +209,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) {
@ -224,7 +228,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;
} }
@ -236,7 +240,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;
} }
@ -247,6 +251,84 @@
return YES; return YES;
} }
- (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];
if (@available( iOS 12, * )) {
[Countly.sharedInstance askForNotificationPermissionWithOptions:UNAuthorizationOptionProvisional | UNAuthorizationOptionAlert
completionHandler:^(BOOL granted, NSError *error) {
if (!granted)
err( @"No provisional notification permission: %@", error );
[self askNotifications];
}];
return;
}
[self askNotifications];
}
- (void)askNotifications {
if ([[MPiOSConfig get].notificationsDecided boolValue])
return;
PearlMainQueue( ^{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Coming Soon" message:
@"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."
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Thanks" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
if (@available( iOS 12, * )) {
[Countly.sharedInstance askForNotificationPermissionWithOptions:UNAuthorizationOptionAlert completionHandler:
^(BOOL granted, NSError *error) {
[MPiOSConfig get].notificationsDecided = @(YES);
}];
}
else {
[Countly.sharedInstance askForNotificationPermission];
[MPiOSConfig get].notificationsDecided = @(YES);
}
}]];
[self.window.rootViewController presentViewController:alert animated:YES completion:nil];
} );
}
- (void)importSites:(NSString *)importData { - (void)importSites:(NSString *)importData {
if ([NSThread isMainThread]) { if ([NSThread isMainThread]) {
@ -272,7 +354,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) {
@ -290,7 +372,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) {
@ -301,7 +383,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];
} }
} ); } );
}]; }];
@ -311,15 +393,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;
@ -335,20 +409,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 {
@ -361,50 +437,45 @@
} }
[self.hangDetector stop]; [self.hangDetector stop];
// self.task = [application beginBackgroundTaskWithExpirationHandler:^{
// [application endBackgroundTask:self.task];
// dbg( @"background expiring" );
// }];
// PearlNotMainQueueOperation( ^{
// NSString *pbstring = [UIPasteboard generalPasteboard].string;
// while (YES) {
// NSString *newString = [UIPasteboard generalPasteboard].string;
// if (![newString isEqualToString:pbstring]) {
// dbg( @"pasteboard changed to: %@", newString );
// pbstring = newString;
// NSURL *url = [NSURL URLWithString:pbstring];
// if (url) {
// NSString *siteName = [url host];
// }
// MPKey *key = [MPiOSAppDelegate get].key;
// if (key)
// [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
// NSFetchRequest<MPSiteEntity *>
// *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
// fetchRequest.sortDescriptors = @[
// [[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector( lastUsed ) ) ascending:NO]
// ];
// fetchRequest.fetchBatchSize = 2;
// fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(name LIKE[cd] %@) AND user == %@", siteName,
// [[MPiOSAppDelegate get] activeUserOID]];
// NSError *error = nil;
// NSArray<MPSiteEntity *> *results = [fetchRequest execute:&error];
// dbg( @"site search, error: %@, results:\n%@", error, results );
// if ([results count]) {
// [UIPasteboard generalPasteboard].string = [[results firstObject] resolvePasswordUsingKey:key];
// }
// }];
// }
// [NSThread sleepForTimeInterval:5];
// }
// } );
[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 = [MPUserEntity fetchRequest];
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]) {
@ -414,7 +485,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:
@ -428,7 +499,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];
@ -474,15 +545,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];
[self openFeedbackWithLogs:YES forVC:nil]; }]];
}]]; [alert addAction:[UIAlertAction actionWithTitle:@"Send Logs" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[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];
} ); } );
} ); } );
} }
@ -499,7 +571,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];
}]]; }]];
@ -507,10 +579,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 {
@ -521,93 +593,172 @@
@"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) {
return PearlAwait( ^(void (^setResult)(id)) { NSError *error = nil;
PearlMainQueue( ^{ NSString *exportedUser = [self exportSitesFor:[self activeUserInContext:context] revealPasswords:revealPasswords askExportPassword:
UIAlertController *alert = [UIAlertController alertControllerWithTitle:strf( @"Master Password For:\n%@", userName ) ^NSString *(NSString *userName) {
message:@"Enter your master password to export the user." return PearlAwait( ^(void (^setResult)(id)) {
preferredStyle:UIAlertControllerStyleAlert]; PearlMainQueue( ^{
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) { UIAlertController *alert = [UIAlertController alertControllerWithTitle:strf(
textField.secureTextEntry = YES; @"Master Password For:\n%@", userName )
}]; message:@"Enter your master password to export the user."
[alert addAction:[UIAlertAction actionWithTitle:@"Export" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { preferredStyle:UIAlertControllerStyleAlert];
setResult( alert.textFields.firstObject.text ); [alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
}]]; textField.secureTextEntry = YES;
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) { }];
setResult( nil ); [alert addAction:[UIAlertAction actionWithTitle:@"Export" style:UIAlertActionStyleDefault
}]]; handler:^(UIAlertAction *action) {
[self.navigationController presentViewController:alert animated:YES completion:nil]; setResult( alert.textFields.firstObject.text );
} ); }]];
} ); [alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel
} result:^(NSString *exportedUser, NSError *error) { handler:^(UIAlertAction *action) {
if (!exportedUser || error) { setResult( nil );
MPError( error, @"Failed to export mpsites." ); }]];
PearlMainQueue( ^{ [self.window.rootViewController presentViewController:alert animated:YES completion:nil];
} );
} );
} error:&error];
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];
} ); }
if (!exportedUser)
return;
NSDateFormatter *exportDateFormatter = [NSDateFormatter new];
[exportDateFormatter setDateFormat:@"yyyy'-'MM'-'dd"];
NSString *exportFileName = strf( @"%@ (%@).mpsites",
[self activeUserForMainThread].name, [exportDateFormatter stringFromDate:[NSDate date]] );
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Export Destination" message:nil
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Send As E-Mail" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
NSString *message;
if (revealPasswords)
message = strf( @"Export of Master Password sites with passwords included.\n\n"
@"REMINDER: Make sure nobody else sees this file! Passwords are visible!\n\n\n"
@"--\n"
@"%@\n"
@"Master Password %@, build %@",
[self activeUserForMainThread].name,
[PearlInfoPlist get].CFBundleShortVersionString,
[PearlInfoPlist get].CFBundleVersion );
else
message = strf( @"Backup of Master Password sites.\n\n\n"
@"--\n"
@"%@\n"
@"Master Password %@, build %@",
[self activeUserForMainThread].name,
[PearlInfoPlist get].CFBundleShortVersionString,
[PearlInfoPlist get].CFBundleVersion );
[PearlEMail sendEMailTo:nil fromVC:viewController subject:@"Master Password Export" body:message
attachments:[[PearlEMailAttachment alloc] initWithContent:[exportedUser dataUsingEncoding:NSUTF8StringEncoding]
mimeType:@"text/plain"
fileName:exportFileName], nil];
return;
}]];
[alert addAction:[UIAlertAction actionWithTitle:@"Share / Export" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
NSURL *applicationSupportURL = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory
inDomains:NSUserDomainMask] lastObject];
NSURL *exportURL = [[applicationSupportURL
URLByAppendingPathComponent:[NSBundle mainBundle].bundleIdentifier isDirectory:YES]
URLByAppendingPathComponent:exportFileName isDirectory:NO];
NSError *writeError = nil;
if (![[exportedUser dataUsingEncoding:NSUTF8StringEncoding]
writeToURL:exportURL options:NSDataWritingFileProtectionComplete error:&writeError])
MPError( writeError, @"Failed to write export data to URL %@.", exportURL );
else {
self.interactionController = [UIDocumentInteractionController interactionControllerWithURL:exportURL];
self.interactionController.UTI = @"com.lyndir.masterpassword.sites";
self.interactionController.delegate = self;
[self.interactionController presentOpenInMenuFromRect:CGRectZero inView:viewController.view animated:YES];
}
}]];
[alert addAction:[UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleCancel handler: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; return;
} }
NSDateFormatter *exportDateFormatter = [NSDateFormatter new]; [MPAppDelegate_Shared managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
[exportDateFormatter setDateFormat:@"yyyy'-'MM'-'dd"]; NSError *error = nil;
NSString *exportFileName = strf( @"%@ (%@).mpsites", NSString *exportedUser = [[MPAppDelegate_Shared get] exportSitesFor:[MPUserEntity existingObjectWithID:user.objectID inContext:context]
[self activeUserForMainThread].name, [exportDateFormatter stringFromDate:[NSDate date]] ); 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];
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Export Destination" message:nil PearlMainQueue( ^{
preferredStyle:UIAlertControllerStyleActionSheet]; if (error) {
[alert addAction:[UIAlertAction actionWithTitle:@"Send As E-Mail" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { MPError( error, @"Failed to export user." );
NSString *message; UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Export Error"
if (revealPasswords) message:[error localizedDescription]
message = strf( @"Export of Master Password sites with passwords included.\n\n" preferredStyle:UIAlertControllerStyleAlert];
@"REMINDER: Make sure nobody else sees this file! Passwords are visible!\n\n\n" [alert addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]];
@"--\n" [self.window.rootViewController presentViewController:alert animated:YES completion:nil];
@"%@\n" }
@"Master Password %@, build %@", if (!exportedUser)
[self activeUserForMainThread].name, return;
[PearlInfoPlist get].CFBundleShortVersionString,
[PearlInfoPlist get].CFBundleVersion );
else
message = strf( @"Backup of Master Password sites.\n\n\n"
@"--\n"
@"%@\n"
@"Master Password %@, build %@",
[self activeUserForMainThread].name,
[PearlInfoPlist get].CFBundleShortVersionString,
[PearlInfoPlist get].CFBundleVersion );
[PearlEMail sendEMailTo:nil fromVC:viewController subject:@"Master Password Export" body:message NSURLComponents *components = [NSURLComponents new];
attachments:[[PearlEMailAttachment alloc] initWithContent:[exportedUser dataUsingEncoding:NSUTF8StringEncoding] components.scheme = @"spectre";
mimeType:@"text/plain" components.path = @"import";
fileName:exportFileName], nil]; components.queryItems = @[ [[NSURLQueryItem alloc] initWithName:@"data" value:exportedUser] ];
return; [UIApp openURL:components.URL];
}]]; } );
[alert addAction:[UIAlertAction actionWithTitle:@"Share / Export" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { }];
NSURL *applicationSupportURL = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory }
inDomains:NSUserDomainMask] lastObject];
NSURL *exportURL = [[applicationSupportURL else if (self.spectreViewController)
URLByAppendingPathComponent:[NSBundle mainBundle].bundleIdentifier isDirectory:YES] [self.window.rootViewController presentViewController:self.spectreViewController animated:YES completion:nil];
URLByAppendingPathComponent:exportFileName isDirectory:NO];
NSError *writeError = nil;
if (![[exportedUser dataUsingEncoding:NSUTF8StringEncoding]
writeToURL:exportURL options:NSDataWritingFileProtectionComplete error:&writeError])
MPError( writeError, @"Failed to write export data to URL %@.", exportURL );
else {
self.interactionController = [UIDocumentInteractionController interactionControllerWithURL:exportURL];
self.interactionController.UTI = @"com.lyndir.masterpassword.sites";
self.interactionController.delegate = self;
[self.interactionController presentOpenInMenuFromRect:CGRectZero inView:viewController.view animated:YES];
}
}]];
[alert addAction:[UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleCancel handler:nil]];
[self.navigationController presentViewController:alert 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 {
@ -618,7 +769,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;
@ -631,10 +782,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 {
@ -655,41 +813,55 @@
[PearlLogger get].historyLevel = [[MPiOSConfig get].traceMode boolValue]? PearlLogLevelTrace: PearlLogLevelInfo; [PearlLogger get].historyLevel = [[MPiOSConfig get].traceMode boolValue]? PearlLogLevelTrace: PearlLogLevelInfo;
// Send info // Send info
if ([[MPConfig get].sendInfo boolValue]) { NSArray *countlyFeatures = @[
[Countly.sharedInstance giveConsentForAllFeatures]; CLYConsentSessions, CLYConsentEvents, CLYConsentUserDetails, CLYConsentCrashReporting, CLYConsentViewTracking, CLYConsentStarRating
];
if ([[MPiOSConfig get].sendInfo boolValue] || ![[MPiOSConfig get].sendInfoDecided boolValue]) {
if ([PearlLogger get].printLevel > PearlLogLevelInfo) if ([PearlLogger get].printLevel > PearlLogLevelInfo)
[PearlLogger get].printLevel = PearlLogLevelInfo; [PearlLogger get].printLevel = PearlLogLevelInfo;
NSMutableDictionary *prefs = [NSMutableDictionary new]; // Sentry
prefs[@"rememberLogin"] = [MPConfig get].rememberLogin; if (!SentrySDK.isEnabled)
prefs[@"sendInfo"] = [MPConfig get].sendInfo; [SentrySDK startWithOptions:@{
prefs[@"helpHidden"] = [MPiOSConfig get].helpHidden; @"dsn" : NilToNSNull( decrypt( sentryDSN ) ),
prefs[@"showQuickStart"] = [MPiOSConfig get].showSetup; #ifdef DEBUG
prefs[@"firstRun"] = [PearlConfig get].firstRun; @"debug" : @(NO), //@(YES),
prefs[@"launchCount"] = [PearlConfig get].launchCount; @"environment" : @"Development",
prefs[@"askForReviews"] = [PearlConfig get].askForReviews; #elif PUBLIC
prefs[@"reviewAfterLaunches"] = [PearlConfig get].reviewAfterLaunches; @"debug" : @(NO),
prefs[@"reviewedVersion"] = [PearlConfig get].reviewedVersion; @"environment" : @"Public",
prefs[@"simulator"] = @([PearlDeviceUtils isSimulator]);
prefs[@"encrypted"] = @([PearlDeviceUtils isAppEncrypted]);
prefs[@"jailbroken"] = @([PearlDeviceUtils isJailbroken]);
prefs[@"platform"] = [PearlDeviceUtils platform];
#ifdef APPSTORE
prefs[@"reviewedVersion"] = @([PearlDeviceUtils isAppEncrypted]);
#else #else
prefs[@"reviewedVersion"] = @(YES); @"debug" : @(NO),
@"environment" : @"Private",
#endif #endif
@"enableAutoSessionTracking": @(YES),
}];
[SentrySDK.currentHub getClient].options.enabled = @YES;
[SentrySDK configureScope:^(SentryScope *scope) { [SentrySDK configureScope:^(SentryScope *scope) {
for (NSString *pref in prefs.allKeys) [scope setExtraValue:[MPiOSConfig get].rememberLogin forKey:@"rememberLogin"];
[scope setExtraValue:prefs[pref] forKey:pref]; [scope setExtraValue:[MPiOSConfig get].sendInfo forKey:@"sendInfo"];
[scope setExtraValue:[MPiOSConfig get].helpHidden forKey:@"helpHidden"];
[scope setExtraValue:[MPiOSConfig get].showSetup forKey:@"showQuickStart"];
[scope setExtraValue:[PearlConfig get].firstRun forKey:@"firstRun"];
[scope setExtraValue:[PearlConfig get].launchCount forKey:@"launchCount"];
[scope setExtraValue:[PearlConfig get].askForReviews forKey:@"askForReviews"];
[scope setExtraValue:[PearlConfig get].reviewAfterLaunches forKey:@"reviewAfterLaunches"];
[scope setExtraValue:[PearlConfig get].reviewedVersion forKey:@"reviewedVersion"];
[scope setExtraValue:@([PearlDeviceUtils isSimulator]) forKey:@"simulator"];
[scope setExtraValue:@([PearlDeviceUtils isAppEncrypted]) forKey:@"encrypted"];
[scope setExtraValue:@([PearlDeviceUtils isJailbroken]) forKey:@"jailbroken"];
[scope setExtraValue:[PearlDeviceUtils platform] forKey:@"platform"];
#ifdef APPSTORE
[scope setExtraValue:@([PearlDeviceUtils isAppEncrypted]) forKey:@"reviewedVersion"];
#else
[scope setExtraValue:@(NO) forKey:@"reviewedVersion"];
#endif
}]; }];
[Countly.sharedInstance giveConsentForFeatures:countlyFeatures];
} }
else { else {
[SentrySDK.currentHub getClient].options.enabled = @NO; [Countly.sharedInstance cancelConsentForFeatures:countlyFeatures];
[Countly.sharedInstance cancelConsentForAllFeatures]; [SentrySDK close];
} }
} }

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,12 +38,36 @@
<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>
<false/> <false/>
<key>NSFaceIDUsageDescription</key>
<string>Biometrics are used to load your master password from the device's keychain.</string>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>
<string>© 2011-2020</string> <string>© 2011-2020</string>
<key>UIAppFonts</key> <key>UIAppFonts</key>
@ -101,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>
@ -110,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>
@ -121,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

@ -18,7 +18,7 @@
<key>Title</key> <key>Title</key>
<string>Version</string> <string>Version</string>
<key>Key</key> <key>Key</key>
<string>unset</string> <string>version</string>
<key>Type</key> <key>Type</key>
<string>PSTitleValueSpecifier</string> <string>PSTitleValueSpecifier</string>
</dict> </dict>
@ -28,7 +28,7 @@
<key>Title</key> <key>Title</key>
<string>Build</string> <string>Build</string>
<key>Key</key> <key>Key</key>
<string>unset</string> <string>build</string>
<key>Type</key> <key>Type</key>
<string>PSTitleValueSpecifier</string> <string>PSTitleValueSpecifier</string>
</dict> </dict>
@ -40,7 +40,7 @@
<key>Type</key> <key>Type</key>
<string>PSTitleValueSpecifier</string> <string>PSTitleValueSpecifier</string>
<key>Key</key> <key>Key</key>
<string>unset</string> <string>copyright</string>
</dict> </dict>
<dict> <dict>
<key>Type</key> <key>Type</key>

File diff suppressed because it is too large Load Diff

View File

@ -14,9 +14,9 @@ echo "Cleaning .."
git clean -ffdx . git clean -ffdx .
echo "Creating archive $mpwArchive .." echo "Creating archive $mpwArchive .."
echo "$version" > VERSION echo "$version" > cli/VERSION
git show --show-signature --pretty=format:%H --quiet "$tag" > TAG git show --show-signature --pretty=format:%H --quiet "$tag" > cli/TAG
{ git ls-files -z .; printf '%s\0' VERSION TAG; } | xargs -0 tar -Lcvzf "$mpwArchive" { git ls-files -z .; printf '%s\0' cli/VERSION cli/TAG; } | xargs -0 tar -Lcvzf "$mpwArchive"
echo "Creating archive signature $mpwArchive.sig .." echo "Creating archive signature $mpwArchive.sig .."
gpg --detach-sign --local-user 5C2D1D61853F20F2FCDDCCB70EF21226F43EA6BC "$mpwArchive" gpg --detach-sign --local-user 5C2D1D61853F20F2FCDDCCB70EF21226F43EA6BC "$mpwArchive"

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,397 +79,429 @@ mpw_expect() {
esac esac
fi fi
[[ $file ]] && (( ! keep )) && rm "$file" [[ -e $file ]] && (( ! keep )) && rm "$file"
} }
# mpw_tests.xml # mpw_tests.xml
## V3 ## V3
printf "\nV%d, none: " 3 printf "\nV%d, none: " 3
mpw_expect 'CefoTiciJuba7@' -Fnone \ mpw_expect 'CefoTiciJuba7@' -Fnone \
-u 'test' -M 'test' 'test' -u 'test' -M 'test' 'test'
mpw_expect 'Tina0#NotaMahu' -Fnone \ mpw_expect 'Tina0#NotaMahu' -Fnone \
-u 'tesẗ' -M 'ẗest' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' 'ẗesẗ'
mpw_expect 'Tina0#NotaMahu' -Fnone \ mpw_expect 'Tina0#NotaMahu' -Fnone \
-u 'tesẗ' -M 'ẗest' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -C '' 'ẗesẗ'
mpw_expect 'Tina0#NotaMahu' -Fnone \ mpw_expect 'Tina0#NotaMahu' -Fnone \
-u 'tesẗ' -M 'ẗest' -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Tina0#NotaMahu' -Fnone \ mpw_expect 'Tina0#NotaMahu' -Fnone \
-u 'tesẗ' -M 'ẗest' -a3 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Tina0#NotaMahu' -Fnone \ mpw_expect 'Tina0#NotaMahu' -Fnone \
-u 'tesẗ' -M 'ẗest' -c1 -a3 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Tina0#NotaMahu' -Fnone \ mpw_expect 'Tina0#NotaMahu' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'KovxFipe5:Zatu' -Fnone \ mpw_expect 'KovxFipe5:Zatu' -Fnone \
-u '⛄' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ' -u '⛄' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'ModoLalhRapo6#' -Fnone \ mpw_expect 'ModoLalhRapo6#' -Fnone \
-u 'tesẗ' -M '⛄' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M '⛄' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
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 \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a3 -p 'recovery' -C 'quesẗion' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tphrase -c1 -a3 -p 'recovery' -C 'quesẗion' 'ẗesẗ'
mpw_expect 'j5TJ%G0WWwSMvYb)hr4)' -Fnone \ mpw_expect 'j5TJ%G0WWwSMvYb)hr4)' -Fnone \
-u 'tesẗ' -M 'ẗest' -tmax -c1 -a3 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tmax -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'TinRaz2?' -Fnone \ mpw_expect 'TinRaz2?' -Fnone \
-u 'tesẗ' -M 'ẗest' -tmed -c1 -a3 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tmed -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'jad0IQA3' -Fnone \ mpw_expect 'jad0IQA3' -Fnone \
-u 'tesẗ' -M 'ẗest' -tbasic -c1 -a3 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tbasic -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Tin0' -Fnone \ mpw_expect 'Tin0' -Fnone \
-u 'tesẗ' -M 'ẗest' -tshort -c1 -a3 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tshort -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect '1710' -Fnone \ mpw_expect '1710' -Fnone \
-u 'tesẗ' -M 'ẗest' -tpin -c1 -a3 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tpin -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'tinraziqu' -Fnone \ mpw_expect 'tinraziqu' -Fnone \
-u 'tesẗ' -M 'ẗest' -tname -c1 -a3 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tname -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
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
mpw_expect 'CefoTiciJuba7@' -Fnone \ mpw_expect 'CefoTiciJuba7@' -Fnone \
-u 'test' -M 'test' -tlong -c1 -a2 -p 'authentication' -C '' 'test' -u 'test' -M 'test' -tlong -c1 -a2 -p 'authentication' -C '' 'test'
mpw_expect "HuczFina3'Qatf" -Fnone \ mpw_expect "HuczFina3'Qatf" -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'SicrJuwaWaql0#' -Fnone \ mpw_expect 'SicrJuwaWaql0#' -Fnone \
-u '⛄' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ' -u '⛄' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'LokaJayp1@Faba' -Fnone \ mpw_expect 'LokaJayp1@Faba' -Fnone \
-u 'tesẗ' -M '⛄' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M '⛄' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'DoqaHulu8:Funh' -Fnone \ mpw_expect 'DoqaHulu8:Funh' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' '⛄' -u 'tesẗ' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' '⛄'
mpw_expect 'yiyguxoxe' -Fnone \ mpw_expect 'yiyguxoxe' -Fnone \
-u 'tesẗ' -M 'ẗest' -tname -c1 -a2 -p 'identification' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tname -c1 -a2 -p 'identification' -C '' 'ẗesẗ'
mpw_expect 'vu yelyo bat kujavmu' -Fnone \ mpw_expect 'vu yelyo bat kujavmu' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a2 -p 'recovery' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tphrase -c1 -a2 -p 'recovery' -C '' 'ẗesẗ'
mpw_expect 'ka deqce xad vomacgi' -Fnone \ mpw_expect 'ka deqce xad vomacgi' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a2 -p 'recovery' -C 'quesẗion' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tphrase -c1 -a2 -p 'recovery' -C 'quesẗion' 'ẗesẗ'
mpw_expect 'wRF$LmB@umWGLWeVlB0-' -Fnone \ mpw_expect 'wRF$LmB@umWGLWeVlB0-' -Fnone \
-u 'tesẗ' -M 'ẗest' -tmax -c1 -a2 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tmax -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'HucZuk0!' -Fnone \ mpw_expect 'HucZuk0!' -Fnone \
-u 'tesẗ' -M 'ẗest' -tmed -c1 -a2 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tmed -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'wb59VoB5' -Fnone \ mpw_expect 'wb59VoB5' -Fnone \
-u 'tesẗ' -M 'ẗest' -tbasic -c1 -a2 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tbasic -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Huc9' -Fnone \ mpw_expect 'Huc9' -Fnone \
-u 'tesẗ' -M 'ẗest' -tshort -c1 -a2 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tshort -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect '2959' -Fnone \ mpw_expect '2959' -Fnone \
-u 'tesẗ' -M 'ẗest' -tpin -c1 -a2 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tpin -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'huczukamo' -Fnone \ mpw_expect 'huczukamo' -Fnone \
-u 'tesẗ' -M 'ẗest' -tname -c1 -a2 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tname -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
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
mpw_expect 'CefoTiciJuba7@' -Fnone \ mpw_expect 'CefoTiciJuba7@' -Fnone \
-u 'test' -M 'test' -tlong -c1 -a1 -p 'authentication' -C '' 'test' -u 'test' -M 'test' -tlong -c1 -a1 -p 'authentication' -C '' 'test'
mpw_expect 'SuxiHoteCuwe3/' -Fnone \ mpw_expect 'SuxiHoteCuwe3/' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'CupaTixu8:Hetu' -Fnone \ mpw_expect 'CupaTixu8:Hetu' -Fnone \
-u '⛄' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ' -u '⛄' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'NaqmBanu9+Decs' -Fnone \ mpw_expect 'NaqmBanu9+Decs' -Fnone \
-u 'tesẗ' -M '⛄' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M '⛄' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'XowaDokoGeyu2)' -Fnone \ mpw_expect 'XowaDokoGeyu2)' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' '⛄' -u 'tesẗ' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' '⛄'
mpw_expect 'makmabivo' -Fnone \ mpw_expect 'makmabivo' -Fnone \
-u 'tesẗ' -M 'ẗest' -tname -c1 -a1 -p 'identification' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tname -c1 -a1 -p 'identification' -C '' 'ẗesẗ'
mpw_expect 'je mutbo buf puhiywo' -Fnone \ mpw_expect 'je mutbo buf puhiywo' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a1 -p 'recovery' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tphrase -c1 -a1 -p 'recovery' -C '' 'ẗesẗ'
mpw_expect 'ne hapfa dax qamayqo' -Fnone \ mpw_expect 'ne hapfa dax qamayqo' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a1 -p 'recovery' -C 'quesẗion' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tphrase -c1 -a1 -p 'recovery' -C 'quesẗion' 'ẗesẗ'
mpw_expect 'JlZo&eLhqgoxqtJ!NC5/' -Fnone \ mpw_expect 'JlZo&eLhqgoxqtJ!NC5/' -Fnone \
-u 'tesẗ' -M 'ẗest' -tmax -c1 -a1 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tmax -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'SuxHot2*' -Fnone \ mpw_expect 'SuxHot2*' -Fnone \
-u 'tesẗ' -M 'ẗest' -tmed -c1 -a1 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tmed -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Jly28Veh' -Fnone \ mpw_expect 'Jly28Veh' -Fnone \
-u 'tesẗ' -M 'ẗest' -tbasic -c1 -a1 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tbasic -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Sux2' -Fnone \ mpw_expect 'Sux2' -Fnone \
-u 'tesẗ' -M 'ẗest' -tshort -c1 -a1 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tshort -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect '4922' -Fnone \ mpw_expect '4922' -Fnone \
-u 'tesẗ' -M 'ẗest' -tpin -c1 -a1 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tpin -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'suxhotito' -Fnone \ mpw_expect 'suxhotito' -Fnone \
-u 'tesẗ' -M 'ẗest' -tname -c1 -a1 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tname -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
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
mpw_expect 'GeqoBigiFubh2!' -Fnone \ mpw_expect 'GeqoBigiFubh2!' -Fnone \
-u 'test' -M 'test' -tlong -c1 -a0 -p 'authentication' -C '' 'test' -u 'test' -M 'test' -tlong -c1 -a0 -p 'authentication' -C '' 'test'
mpw_expect 'WumiZobxGuhe8]' -Fnone \ mpw_expect 'WumiZobxGuhe8]' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'KuhaXimj8@Zebu' -Fnone \ mpw_expect 'KuhaXimj8@Zebu' -Fnone \
-u '⛄' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ' -u '⛄' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'CajtFayv9_Pego' -Fnone \ mpw_expect 'CajtFayv9_Pego' -Fnone \
-u 'tesẗ' -M '⛄' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M '⛄' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'QohaPokgYevu2!' -Fnone \ mpw_expect 'QohaPokgYevu2!' -Fnone \
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' '⛄' -u 'tesẗ' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' '⛄'
mpw_expect 'takxabico' -Fnone \ mpw_expect 'takxabico' -Fnone \
-u 'tesẗ' -M 'ẗest' -tname -c1 -a0 -p 'identification' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tname -c1 -a0 -p 'identification' -C '' 'ẗesẗ'
mpw_expect 'je tuxfo fut huzivlo' -Fnone \ mpw_expect 'je tuxfo fut huzivlo' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a0 -p 'recovery' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tphrase -c1 -a0 -p 'recovery' -C '' 'ẗesẗ'
mpw_expect 'ye zahqa lam jatavmo' -Fnone \ mpw_expect 'ye zahqa lam jatavmo' -Fnone \
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a0 -p 'recovery' -C 'quesẗion' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tphrase -c1 -a0 -p 'recovery' -C 'quesẗion' 'ẗesẗ'
mpw_expect 'g4@)4SlA#)cJ#ib)vvH3' -Fnone \ mpw_expect 'g4@)4SlA#)cJ#ib)vvH3' -Fnone \
-u 'tesẗ' -M 'ẗest' -tmax -c1 -a0 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tmax -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Wum7_Xix' -Fnone \ mpw_expect 'Wum7_Xix' -Fnone \
-u 'tesẗ' -M 'ẗest' -tmed -c1 -a0 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tmed -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'gAo78ARD' -Fnone \ mpw_expect 'gAo78ARD' -Fnone \
-u 'tesẗ' -M 'ẗest' -tbasic -c1 -a0 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tbasic -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'Wum7' -Fnone \ mpw_expect 'Wum7' -Fnone \
-u 'tesẗ' -M 'ẗest' -tshort -c1 -a0 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tshort -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect '9427' -Fnone \ mpw_expect '9427' -Fnone \
-u 'tesẗ' -M 'ẗest' -tpin -c1 -a0 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tpin -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
mpw_expect 'wumdoxixo' -Fnone \ mpw_expect 'wumdoxixo' -Fnone \
-u 'tesẗ' -M 'ẗest' -tname -c1 -a0 -p 'authentication' -C '' 'ẗesẗ' -u 'tesẗ' -M 'ẗest' -tname -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
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
mpw_expect 'IfHuAUUpqpKZDZlNvz8$' -Fflat -R0 \ mpw_expect 'IfHuAUUpqpKZDZlNvz8$' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tmax -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.max' -u 'tesẗ.v3' -M 'ẗest' -tmax -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.max'
mpw_expect 'FamiJirk1)Zehc' -Fflat -R0 \ mpw_expect 'FamiJirk1)Zehc' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.long' -u 'tesẗ.v3' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.long'
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 \
-u 'tesẗ.v3' -M 'ẗest' -tbasic -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.basic' -u 'tesẗ.v3' -M 'ẗest' -tbasic -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.basic'
mpw_expect 'Bug9' -Fflat -R0 \ mpw_expect 'Bug9' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tshort -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.short' -u 'tesẗ.v3' -M 'ẗest' -tshort -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.short'
mpw_expect '3560' -Fflat -R0 \ mpw_expect '3560' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tpin -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.pin' -u 'tesẗ.v3' -M 'ẗest' -tpin -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.pin'
mpw_expect 'jupxiqepi' -Fflat -R0 \ 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 'mophabiwe' -Fflat -R0 \ mpw_expect 'Cq5$TfH#OHmPS9yREp7)' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tname -c1 -a3 -p 'identification' -C '' 'ẗesẗ.c1a3pi' -u 'tesẗ.v3' -M 'ẗest' -tmax -c1 -a3 -p 'identification' -C '' 'ẗesẗ.c1a3pi.max'
mpw_expect 'mup wulbezaxa juca' -Fflat -R0 \ mpw_expect 'mophabiwe' -Fflat -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tphrase -c1 -a3 -p 'recovery' -C '' 'ẗesẗ.c1a3pr' -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 \
-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
mpw_expect 'i7@0M*DdP4DgD#jJIzyL' -Fflat -R0 \ mpw_expect 'i7@0M*DdP4DgD#jJIzyL' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tmax -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.max' -u 'tesẗ.v2' -M 'ẗest' -tmax -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.max'
mpw_expect 'Lifw5]DablSuga' -Fflat -R0 \ mpw_expect 'Lifw5]DablSuga' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.long' -u 'tesẗ.v2' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.long'
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 \
-u 'tesẗ.v2' -M 'ẗest' -tbasic -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.basic' -u 'tesẗ.v2' -M 'ẗest' -tbasic -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.basic'
mpw_expect 'Jif6' -Fflat -R0 \ mpw_expect 'Jif6' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tshort -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.short' -u 'tesẗ.v2' -M 'ẗest' -tshort -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.short'
mpw_expect '4001' -Fflat -R0 \ mpw_expect '4001' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tpin -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.pin' -u 'tesẗ.v2' -M 'ẗest' -tpin -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.pin'
mpw_expect 'rexmibace' -Fflat -R0 \ 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 'camfibeye' -Fflat -R0 \ mpw_expect 'T8+xi4NMd3HUGdV#GW*%' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tname -c1 -a2 -p 'identification' -C '' 'ẗesẗ.c1a2pi' -u 'tesẗ.v2' -M 'ẗest' -tmax -c1 -a2 -p 'identification' -C '' 'ẗesẗ.c1a2pi.max'
mpw_expect 'ye vemcu keq xepewmi' -Fflat -R0 \ mpw_expect 'camfibeye' -Fflat -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tphrase -c1 -a2 -p 'recovery' -C '' 'ẗesẗ.c1a2pr' -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 \
-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
mpw_expect 'a3~AiGkHk)Pgjbb)mk6H' -Fflat -R0 \ mpw_expect 'a3~AiGkHk)Pgjbb)mk6H' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tmax -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.max' -u 'tesẗ.v1' -M 'ẗest' -tmax -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.max'
mpw_expect 'Lojz6?VotaJall' -Fflat -R0 \ mpw_expect 'Lojz6?VotaJall' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.long' -u 'tesẗ.v1' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.long'
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 \
-u 'tesẗ.v1' -M 'ẗest' -tbasic -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.basic' -u 'tesẗ.v1' -M 'ẗest' -tbasic -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.basic'
mpw_expect 'Cij7' -Fflat -R0 \ mpw_expect 'Cij7' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tshort -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.short' -u 'tesẗ.v1' -M 'ẗest' -tshort -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.short'
mpw_expect '0020' -Fflat -R0 \ mpw_expect '0020' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tpin -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.pin' -u 'tesẗ.v1' -M 'ẗest' -tpin -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.pin'
mpw_expect 'vadxovezu' -Fflat -R0 \ 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 'qipberize' -Fflat -R0 \ mpw_expect 'z2U9)(uQ78TXqtaus)8.' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tname -c1 -a1 -p 'identification' -C '' 'ẗesẗ.c1a1pi' -u 'tesẗ.v1' -M 'ẗest' -tmax -c1 -a1 -p 'identification' -C '' 'ẗesẗ.c1a1pi.max'
mpw_expect 'sok torxibute reza' -Fflat -R0 \ mpw_expect 'qipberize' -Fflat -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tphrase -c1 -a1 -p 'recovery' -C '' 'ẗesẗ.c1a1pr' -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 \
-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
mpw_expect 'b5@ww@Jmb4cAioRbivb)' -Fflat -R0 \ mpw_expect 'b5@ww@Jmb4cAioRbivb)' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tmax -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.max' -u 'tesẗ.v0' -M 'ẗest' -tmax -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.max'
mpw_expect 'ZuceHazwLojz8!' -Fflat -R0 \ mpw_expect 'ZuceHazwLojz8!' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.long' -u 'tesẗ.v0' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.long'
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 \
-u 'tesẗ.v0' -M 'ẗest' -tbasic -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.basic' -u 'tesẗ.v0' -M 'ẗest' -tbasic -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.basic'
mpw_expect 'Fih8' -Fflat -R0 \ mpw_expect 'Fih8' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tshort -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.short' -u 'tesẗ.v0' -M 'ẗest' -tshort -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.short'
mpw_expect '6121' -Fflat -R0 \ mpw_expect '6121' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tpin -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.pin' -u 'tesẗ.v0' -M 'ẗest' -tpin -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.pin'
mpw_expect 'rivfutipe' -Fflat -R0 \ 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 'ragcoxudo' -Fflat -R0 \ mpw_expect "RoAm3bJSvo@#loHSRA6\'" -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tname -c1 -a0 -p 'identification' -C '' 'ẗesẗ.c1a0pi' -u 'tesẗ.v0' -M 'ẗest' -tmax -c1 -a0 -p 'identification' -C '' 'ẗesẗ.c1a0pi.max'
mpw_expect 'kokl hov lowmaya xaf' -Fflat -R0 \ mpw_expect 'ragcoxudo' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tphrase -c1 -a0 -p 'recovery' -C '' 'ẗesẗ.c1a0pr' -u 'tesẗ.v0' -M 'ẗest' -c1 -a0 -p 'identification' -C '' 'ẗesẗ.c1a0pi'
mpw_expect 'wi zanmu nug zuwidwe' -Fflat -R0 \ mpw_expect 'm8]SiJHiAS@H@Rbw))34' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tphrase -c1 -a0 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a0pr.quesẗion' -u 'tesẗ.v0' -M 'ẗest' -tmax -c1 -a0 -p 'recovery' -C '' 'ẗesẗ.c1a0pr.max'
mpw_expect 'kokl hov lowmaya xaf' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -c1 -a0 -p 'recovery' -C '' 'ẗesẗ.c1a0pr'
mpw_expect 'wi zanmu nug zuwidwe' -Fflat -R0 \
-u 'tesẗ.v0' -M 'ẗest' -c1 -a0 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a0pr.quesẗion'
## V3 ## V3
printf "\nV%d, json: " 3 printf "\nV%d, json: " 3
mpw_expect 'IfHuAUUpqpKZDZlNvz8$' -Fjson -R0 \ mpw_expect 'IfHuAUUpqpKZDZlNvz8$' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tmax -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.max' -u 'tesẗ.v3' -M 'ẗest' -tmax -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.max'
mpw_expect 'FamiJirk1)Zehc' -Fjson -R0 \ mpw_expect 'FamiJirk1)Zehc' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.long' -u 'tesẗ.v3' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.long'
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 \
-u 'tesẗ.v3' -M 'ẗest' -tbasic -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.basic' -u 'tesẗ.v3' -M 'ẗest' -tbasic -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.basic'
mpw_expect 'Bug9' -Fjson -R0 \ mpw_expect 'Bug9' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tshort -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.short' -u 'tesẗ.v3' -M 'ẗest' -tshort -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.short'
mpw_expect '3560' -Fjson -R0 \ mpw_expect '3560' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tpin -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.pin' -u 'tesẗ.v3' -M 'ẗest' -tpin -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.pin'
mpw_expect 'jupxiqepi' -Fjson -R0 \ 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 'mophabiwe' -Fjson -R0 \ mpw_expect 'Cq5$TfH#OHmPS9yREp7)' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tname -c1 -a3 -p 'identification' -C '' 'ẗesẗ.c1a3pi' -u 'tesẗ.v3' -M 'ẗest' -tmax -c1 -a3 -p 'identification' -C '' 'ẗesẗ.c1a3pi.max'
mpw_expect 'mup wulbezaxa juca' -Fjson -R0 \ mpw_expect 'mophabiwe' -Fjson -R0 \
-u 'tesẗ.v3' -M 'ẗest' -tphrase -c1 -a3 -p 'recovery' -C '' 'ẗesẗ.c1a3pr' -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 \
-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
mpw_expect 'i7@0M*DdP4DgD#jJIzyL' -Fjson -R0 \ mpw_expect 'i7@0M*DdP4DgD#jJIzyL' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tmax -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.max' -u 'tesẗ.v2' -M 'ẗest' -tmax -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.max'
mpw_expect 'Lifw5]DablSuga' -Fjson -R0 \ mpw_expect 'Lifw5]DablSuga' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.long' -u 'tesẗ.v2' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.long'
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 \
-u 'tesẗ.v2' -M 'ẗest' -tbasic -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.basic' -u 'tesẗ.v2' -M 'ẗest' -tbasic -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.basic'
mpw_expect 'Jif6' -Fjson -R0 \ mpw_expect 'Jif6' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tshort -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.short' -u 'tesẗ.v2' -M 'ẗest' -tshort -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.short'
mpw_expect '4001' -Fjson -R0 \ mpw_expect '4001' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tpin -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.pin' -u 'tesẗ.v2' -M 'ẗest' -tpin -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.pin'
mpw_expect 'rexmibace' -Fjson -R0 \ 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 'camfibeye' -Fjson -R0 \ mpw_expect 'T8+xi4NMd3HUGdV#GW*%' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tname -c1 -a2 -p 'identification' -C '' 'ẗesẗ.c1a2pi' -u 'tesẗ.v2' -M 'ẗest' -tmax -c1 -a2 -p 'identification' -C '' 'ẗesẗ.c1a2pi.max'
mpw_expect 'ye vemcu keq xepewmi' -Fjson -R0 \ mpw_expect 'camfibeye' -Fjson -R0 \
-u 'tesẗ.v2' -M 'ẗest' -tphrase -c1 -a2 -p 'recovery' -C '' 'ẗesẗ.c1a2pr' -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 \
-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
mpw_expect 'a3~AiGkHk)Pgjbb)mk6H' -Fjson -R0 \ mpw_expect 'a3~AiGkHk)Pgjbb)mk6H' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tmax -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.max' -u 'tesẗ.v1' -M 'ẗest' -tmax -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.max'
mpw_expect 'Lojz6?VotaJall' -Fjson -R0 \ mpw_expect 'Lojz6?VotaJall' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.long' -u 'tesẗ.v1' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.long'
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 \
-u 'tesẗ.v1' -M 'ẗest' -tbasic -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.basic' -u 'tesẗ.v1' -M 'ẗest' -tbasic -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.basic'
mpw_expect 'Cij7' -Fjson -R0 \ mpw_expect 'Cij7' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tshort -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.short' -u 'tesẗ.v1' -M 'ẗest' -tshort -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.short'
mpw_expect '0020' -Fjson -R0 \ mpw_expect '0020' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tpin -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.pin' -u 'tesẗ.v1' -M 'ẗest' -tpin -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.pin'
mpw_expect 'vadxovezu' -Fjson -R0 \ 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 'qipberize' -Fjson -R0 \ mpw_expect 'z2U9)(uQ78TXqtaus)8.' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tname -c1 -a1 -p 'identification' -C '' 'ẗesẗ.c1a1pi' -u 'tesẗ.v1' -M 'ẗest' -tmax -c1 -a1 -p 'identification' -C '' 'ẗesẗ.c1a1pi.max'
mpw_expect 'sok torxibute reza' -Fjson -R0 \ mpw_expect 'qipberize' -Fjson -R0 \
-u 'tesẗ.v1' -M 'ẗest' -tphrase -c1 -a1 -p 'recovery' -C '' 'ẗesẗ.c1a1pr' -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 \
-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
mpw_expect 'b5@ww@Jmb4cAioRbivb)' -Fjson -R0 \ mpw_expect 'b5@ww@Jmb4cAioRbivb)' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tmax -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.max' -u 'tesẗ.v0' -M 'ẗest' -tmax -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.max'
mpw_expect 'ZuceHazwLojz8!' -Fjson -R0 \ mpw_expect 'ZuceHazwLojz8!' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.long' -u 'tesẗ.v0' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.long'
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 \
-u 'tesẗ.v0' -M 'ẗest' -tbasic -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.basic' -u 'tesẗ.v0' -M 'ẗest' -tbasic -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.basic'
mpw_expect 'Fih8' -Fjson -R0 \ mpw_expect 'Fih8' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tshort -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.short' -u 'tesẗ.v0' -M 'ẗest' -tshort -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.short'
mpw_expect '6121' -Fjson -R0 \ mpw_expect '6121' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tpin -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.pin' -u 'tesẗ.v0' -M 'ẗest' -tpin -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.pin'
mpw_expect 'rivfutipe' -Fjson -R0 \ 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 'ragcoxudo' -Fjson -R0 \ mpw_expect "RoAm3bJSvo@#loHSRA6\'" -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tname -c1 -a0 -p 'identification' -C '' 'ẗesẗ.c1a0pi' -u 'tesẗ.v0' -M 'ẗest' -tmax -c1 -a0 -p 'identification' -C '' 'ẗesẗ.c1a0pi.max'
mpw_expect 'kokl hov lowmaya xaf' -Fjson -R0 \ mpw_expect 'ragcoxudo' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tphrase -c1 -a0 -p 'recovery' -C '' 'ẗesẗ.c1a0pr' -u 'tesẗ.v0' -M 'ẗest' -c1 -a0 -p 'identification' -C '' 'ẗesẗ.c1a0pi'
mpw_expect 'wi zanmu nug zuwidwe' -Fjson -R0 \ mpw_expect 'm8]SiJHiAS@H@Rbw))34' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -tphrase -c1 -a0 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a0pr.quesẗion' -u 'tesẗ.v0' -M 'ẗest' -tmax -c1 -a0 -p 'recovery' -C '' 'ẗesẗ.c1a0pr.max'
mpw_expect 'kokl hov lowmaya xaf' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -c1 -a0 -p 'recovery' -C '' 'ẗesẗ.c1a0pr'
mpw_expect 'wi zanmu nug zuwidwe' -Fjson -R0 \
-u 'tesẗ.v0' -M 'ẗest' -c1 -a0 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a0pr.quesẗion'
# Finish # Finish

View File

@ -44,7 +44,6 @@ library {
def arch = targetMachine.getArchitecture().getName().replace('-', '_') def arch = targetMachine.getArchitecture().getName().replace('-', '_')
compile.macros.put("MPW_SODIUM", "1") compile.macros.put("MPW_SODIUM", "1")
compile.macros.put("MPW_LOG", "mpw_log_app")
dependencies { dependencies {
// libsodium // libsodium
@ -75,7 +74,7 @@ library {
link.linkerArgs = ['-lc', '-nodefaultlibs', '-flto'] link.linkerArgs = ['-lc', '-nodefaultlibs', '-flto']
} else if (toolChain in VisualCpp) { } else if (toolChain in VisualCpp) {
// TODO: Should this be shared instead of static? // TODO: Should this be shared instead of static?
compile.compilerArgs = ['/TC', '/MT', '/Ox', '/DSODIUM_STATIC', '/DSODIUM_EXPORT='] compile.compilerArgs = ['/TC', '/MT', '/Ox', '/DSODIUM_STATIC', '/DSODIUM_EXPORT=', '/std:c11']
} }
} }
} }

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