Compare commits
113 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
0b6e43a18f | ||
|
c94c52f4b6 | ||
|
5de9b05299 | ||
|
f27607e63c | ||
|
0b45dc584f | ||
|
88a4d7ba4d | ||
|
94a6c925bc | ||
|
eda9749cf2 | ||
|
4c096555d0 | ||
|
403c45519a | ||
|
8d33ff8ec5 | ||
|
c38f713f05 | ||
|
d59595824b | ||
|
2b78449a48 | ||
|
2eda9b1152 | ||
|
8a032ba891 | ||
|
eda34f6b0b | ||
|
6e1855b00c | ||
|
90aaf23bb5 | ||
|
2e9c79f6b3 | ||
|
83fa6c39bc | ||
|
913208255e | ||
|
963a1222be | ||
|
a1264e0f91 | ||
|
4f0065fba8 | ||
|
b2c688a1ce | ||
|
aee1030758 | ||
|
f665aeccc4 | ||
|
e58b9ef34f | ||
|
968de6026f | ||
|
2886e040a1 | ||
|
01cea659ca | ||
|
3a18e02a87 | ||
|
2de57984b2 | ||
|
c7201c7d90 | ||
|
d62c6b4594 | ||
|
57f275c471 | ||
|
b1d8296396 | ||
|
6d25463de0 | ||
|
029041dcf7 | ||
|
cfbf1f5cac | ||
|
acbd2dc2cc | ||
|
8fcac65fd5 | ||
|
9904f4c715 | ||
|
b51a3de32c | ||
|
9e91f0a9d6 | ||
|
7368b1be90 | ||
|
5db294bdb3 | ||
|
fee7bc7401 | ||
|
21968f4ba6 | ||
|
8582c934c2 | ||
|
7091e2ee1b | ||
|
d5d455ee57 | ||
|
e6ae06798b | ||
|
1cae4c754b | ||
|
93ad86e63c | ||
|
cf74dc5cc2 | ||
|
981bdb3ab4 | ||
|
9bea8bcbdf | ||
|
363d6f6639 | ||
|
eb1632cb62 | ||
|
73fadaef7f | ||
|
60200f6302 | ||
|
cce8db5c48 | ||
|
6f3da5ccf0 | ||
|
52c87eaeca | ||
|
1dccdd0a3c | ||
|
eb8d10ed05 | ||
|
d9e5f77bee | ||
|
60f60d087e | ||
|
df97dec2fe | ||
|
3bac8d9e0a | ||
|
3fa7e1e8a1 | ||
|
d1104e4028 | ||
|
e9f2a25c9c | ||
|
171a3f0978 | ||
|
8cfb9a83c5 | ||
|
5717375e75 | ||
|
cc2dca3bd0 | ||
|
7575924d80 | ||
|
8bedcedfaf | ||
|
10b205c541 | ||
|
774f183ac0 | ||
|
2279aacb5a | ||
|
1bd654621c | ||
|
c4f60e325d | ||
|
d4de3afb72 | ||
|
694b5ea227 | ||
|
66dd78797b | ||
|
61d1660560 | ||
|
c3568e4744 | ||
|
0c921d4318 | ||
|
0178efaaf7 | ||
|
14f919584b | ||
|
16f6c3c593 | ||
|
63ca2ae83e | ||
|
1c3ea3826f | ||
|
ff9596aef0 | ||
|
b79ed1ca0b | ||
|
9a362f13a3 | ||
|
11d6660e5a | ||
|
62e1563fa6 | ||
|
9b8ff7ad0c | ||
|
f1fc07cf9e | ||
|
00ac788f4f | ||
|
514c383310 | ||
|
9a3bcd1c6f | ||
|
d30d469663 | ||
|
b428ee0003 | ||
|
f80ffd078b | ||
|
7f1a28ffa7 | ||
|
8eeba2e005 | ||
|
fd6cbaa9a5 |
1
.gitignore
vendored
@ -1,6 +1,7 @@
|
||||
# OS-Specific junk.
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
*~
|
||||
|
||||
# IntelliJ
|
||||
.idea
|
||||
|
@ -9,10 +9,12 @@ build_project:
|
||||
- "( ./lib/bin/build_libsodium-macos clean && ./lib/bin/build_libsodium-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 )"
|
||||
- "( export JAVA_HOME=$(java_home -Fv 10 || java_home -Fv 9* ) && ./gradlew --stacktrace clean test )"
|
||||
- "( xcodebuild -workspace platform-darwin/MasterPassword.xcworkspace -configuration 'Test' -scheme 'MasterPassword iOS' -sdk iphonesimulator clean build )"
|
||||
- "( xcodebuild -workspace platform-darwin/MasterPassword.xcworkspace -configuration 'Test' -scheme 'MasterPassword macOS' clean build )"
|
||||
- "( ./gradlew --stacktrace --info clean test )"
|
||||
- "( cd platform-darwin && pod install )"
|
||||
- "( 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:
|
||||
- brew
|
||||
- java_9
|
||||
- xcode_9
|
||||
- java
|
||||
- cocoapods
|
||||
- xcode
|
||||
|
15
.gitmodules
vendored
@ -1,21 +1,9 @@
|
||||
[submodule "External/Pearl"]
|
||||
path = platform-darwin/External/Pearl
|
||||
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"]
|
||||
path = platform-darwin/External/AttributedMarkdown
|
||||
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"]
|
||||
path = platform-independent/web/js/mpw-js
|
||||
url = https://github.com/tmthrgd/mpw-js.git
|
||||
@ -27,7 +15,6 @@
|
||||
url = https://github.com/json-c/json-c.git
|
||||
[submodule "public/site"]
|
||||
path = public/site
|
||||
url = https://github.com/Lyndir/MasterPassword.git
|
||||
url = https://gitlab.com/MasterPassword/MasterPassword.git
|
||||
branch = gh-pages
|
||||
shallow = true
|
||||
update = none
|
||||
|
@ -7,6 +7,6 @@ FROM debian:stable-slim
|
||||
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 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 && git pull && git log -1 && ./gradlew -i check
|
||||
|
191
README.md
@ -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.
|
||||
|
||||
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`).
|
||||
Get in touch if you are interested in adding Master Password to any other package managers.
|
||||
Master Password is announcing a massive rewrite, modernizing the solution and clearing the way for exciting new capabilities.
|
||||
|
||||
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.
|
||||
- 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.
|
||||
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.
|
||||
|
||||
We also have a [Frequently Asked Questions](#faq).
|
||||
|
||||
|
||||
|
||||
## 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`.
|
||||
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.
|
||||
|
||||
All development effort has moved to the Spectre project. Master Password is no longer actively maintained.
|
||||
|
||||
|
||||
## 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.
|
||||
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
|
||||
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.
|
||||
|
@ -16,7 +16,7 @@ buildscript {
|
||||
|
||||
allprojects {
|
||||
group = 'com.lyndir.masterpassword'
|
||||
version = '2.7.10'
|
||||
version = '2.7.12'
|
||||
}
|
||||
|
||||
subprojects {
|
||||
@ -29,7 +29,7 @@ subprojects {
|
||||
maven { url 'https://maven.lyndir.com' }
|
||||
}
|
||||
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'
|
||||
}
|
||||
spotbugs {
|
||||
|
@ -8,8 +8,9 @@
|
||||
# - initialize
|
||||
# - needs
|
||||
# - clean & exit (only if script was ran with "clean" argument)
|
||||
# - check & exit (only if target has already been successfully built)
|
||||
# - prepare
|
||||
# - clean
|
||||
# - create
|
||||
# - config
|
||||
# - target
|
||||
# - prepare
|
||||
@ -23,12 +24,14 @@
|
||||
# For example:
|
||||
# target_prepare() { make -s distclean; }
|
||||
# target_configure() { _target_configure "$@" --enable-minimal; }
|
||||
#
|
||||
set -e
|
||||
PATH+=:/usr/local/bin
|
||||
|
||||
# needs <binary> ...
|
||||
#
|
||||
# Utility for ensuring all tools needed by the script are installed prior to starting.
|
||||
#
|
||||
needs() { _needs "$@"; }
|
||||
_needs() {
|
||||
local failed=0
|
||||
@ -48,7 +51,8 @@ _needs() {
|
||||
|
||||
# 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_needs "$@"
|
||||
@ -56,15 +60,23 @@ _initialize() {
|
||||
|
||||
# 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() {
|
||||
if [[ $platform = windows ]]; then
|
||||
needs cmd
|
||||
export VSINSTALLDIR="${VSINSTALLDIR:-$(cd "$(cygpath -F 0x002a)/Microsoft Visual Studio"/*/*/Common7/.. && pwd)}"
|
||||
[[ -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; }
|
||||
for dir in "$VSINSTALLDIR" "$(cygpath -F 0x002a)/Microsoft Visual Studio"/*/*/Common7/..; do
|
||||
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
|
||||
needs libtool:libtoolize,glibtoolize automake autoconf make
|
||||
fi
|
||||
@ -74,13 +86,18 @@ _initialize_needs() {
|
||||
#
|
||||
# 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() {
|
||||
if [[ $platform = windows ]]; then
|
||||
printf '"%%VSINSTALLDIR%%\Common7\Tools\VsMSBuildCmd.bat" && msbuild /t:Clean' > .clean.bat
|
||||
cmd //c .clean.bat
|
||||
rm -f .clean.bat
|
||||
PATH="$(cygpath "$VSINSTALLDIR")/Common7/Tools:$PATH" \
|
||||
cmd /v /c 'VsMSBuildCmd && for %s in (*.sln) do msbuild /t:Clean %s'
|
||||
elif [[ -e Makefile ]] && make -s distclean; then :
|
||||
elif [[ -e .git ]] && git clean -fdx; then :
|
||||
fi
|
||||
@ -88,26 +105,26 @@ _clean() {
|
||||
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 <prefix> has been newly created.
|
||||
#
|
||||
# By default, this will run `autoreconf`.
|
||||
prepare() { _prepare "$@"; }
|
||||
_prepare() {
|
||||
prepare_clean "$@"
|
||||
prepare_create "$@"
|
||||
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.
|
||||
#
|
||||
# By default, this will wipe the build configuration and re-create the prefix.
|
||||
prepare_clean() { _prepare_clean "$@"; }
|
||||
_prepare_clean() {
|
||||
# TODO: Should this differ from clean()?
|
||||
#
|
||||
prepare_create() { _prepare_create "$@"; }
|
||||
_prepare_create() {
|
||||
local prefix=$1 platform=$2; shift 2
|
||||
|
||||
if [[ $platform = windows ]]; then :
|
||||
@ -119,11 +136,16 @@ _prepare_clean() {
|
||||
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() {
|
||||
local prefix=$1 platform=$2; shift 2
|
||||
@ -141,11 +163,11 @@ _prepare_config() {
|
||||
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.
|
||||
# Generic platform-specific environment setup has been done.
|
||||
#
|
||||
target() { _target "$@"; }
|
||||
_target() {
|
||||
target_prepare "$@"
|
||||
@ -153,14 +175,71 @@ _target() {
|
||||
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() {
|
||||
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 :
|
||||
else
|
||||
@ -168,66 +247,74 @@ _target_prepare() {
|
||||
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, some platform-specific arguments will be passed in as well as
|
||||
# By default, this will:
|
||||
# - 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.
|
||||
#
|
||||
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=
|
||||
[[ $arch = *arm* ]] && host=arm
|
||||
local build=
|
||||
[[ -x config.guess ]] && build=$(./config.guess)
|
||||
[[ -x build-aux/config.guess ]] && build=$(build-aux/config.guess)
|
||||
|
||||
case "$platform" in
|
||||
'windows')
|
||||
# doesn't use ./configure
|
||||
return
|
||||
return 0
|
||||
;;
|
||||
'ios'|'macos')
|
||||
host+=-apple
|
||||
set -- --enable-static --disable-shared
|
||||
set -- --enable-static --disable-shared "$@"
|
||||
;;
|
||||
'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##*/}
|
||||
set -- --disable-static --enable-shared --with-sysroot="$SDKROOT/sysroot" "$@"
|
||||
;;
|
||||
*)
|
||||
set -- --enable-static --disable-shared
|
||||
set -- --enable-static --disable-shared "$@"
|
||||
;;
|
||||
esac
|
||||
|
||||
./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() {
|
||||
local prefix=$1 platform=$2 arch=$3; shift 3
|
||||
local prefix=$1 platform=$2 arch=$3 host=$4; shift 4
|
||||
|
||||
if [[ $platform = windows ]]; then
|
||||
# I cannot for the life of me figure out how to pass this command directly into cmd.
|
||||
printf '"%%VSINSTALLDIR%%\Common7\Tools\VsMSBuildCmd.bat" && msbuild /t:Rebuild /p:Configuration=Release;Platform=%s;OutDir=%s' "$arch" "$(cygpath -w "${prefix##$PWD/}/$arch/")" > .build.bat
|
||||
cmd //c .build.bat
|
||||
rm -f .build.bat
|
||||
if [[ -e CMakeLists.txt ]]; then
|
||||
( projdir=$PWD; mkdir -p "$prefix/$arch/"; cd "$prefix/$arch/"
|
||||
PATH="$(cygpath "$VSINSTALLDIR")/Common7/Tools:$(cygpath "$VSINSTALLDIR")/Common7/IDE/CommonExtensions/Microsoft/CMake/CMake/bin:$PATH" \
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
@ -235,6 +322,7 @@ _target_build() {
|
||||
#
|
||||
# Prepare the final build product.
|
||||
# The build script invokes this once after a successful build of all targets.
|
||||
#
|
||||
finalize() { _finalize "$@"; }
|
||||
_finalize() {
|
||||
finalize_merge "$@"
|
||||
@ -246,6 +334,7 @@ _finalize() {
|
||||
# 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.
|
||||
#
|
||||
finalize_merge() { _finalize_merge "$@"; }
|
||||
_finalize_merge() {
|
||||
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).
|
||||
#
|
||||
# By default, this will run `make clean`.
|
||||
#
|
||||
finalize_clean() { _finalize_clean "$@"; }
|
||||
_finalize_clean() {
|
||||
if [[ $platform = windows ]]; then :
|
||||
@ -307,6 +397,7 @@ _finalize_clean() {
|
||||
# build <name> [<platform>]
|
||||
#
|
||||
# Build the library <name> (found at ../<name>) for platform <platform> (or "host" if unspecified).
|
||||
#
|
||||
build() { _build "$@"; }
|
||||
_build() {
|
||||
local name=$1 platform=${2:-host}
|
||||
@ -322,10 +413,10 @@ _build() {
|
||||
if (( ! ${#archs[@]} )); then
|
||||
case "$platform" in
|
||||
'macos') archs=( 'x86_64' ) ;;
|
||||
'ios') archs=( 'i386' 'x86_64' 'armv7' 'armv7s' 'arm64' ) ;;
|
||||
'android') archs=( 'arm' 'arm64' 'x86' 'x86_64' ) ;;
|
||||
'windows') archs=( 'Win32' 'x64' ) ;;
|
||||
*) archs=( 'i686' 'x86_64' ) ;;
|
||||
'ios') archs=( 'x86:i386' 'x86_64' 'armv7' 'armv7s' 'arm64' ) ;;
|
||||
'android') archs=( 'arm' 'arm64:aarch64' 'x86:i686' 'x86_64' ) ;;
|
||||
'windows') archs=( 'x86:Win32' 'x86_64:x64' ) ;;
|
||||
*) archs=( 'x86:i686' 'x86_64' ) ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
@ -348,60 +439,13 @@ _build() {
|
||||
|
||||
# Repeat the build for each individual architecture.
|
||||
for arch in "${archs[@]}"; do (
|
||||
local host=${arch#*:} arch=${arch%%:*}
|
||||
|
||||
echo
|
||||
echo " # $name ($platform: $arch) ..."
|
||||
echo " # $name [$platform: $arch ($host)] ..."
|
||||
|
||||
# Set up a base environment for the platform.
|
||||
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"
|
||||
target "$prefix" "$platform" "$arch" "$host"
|
||||
); done
|
||||
|
||||
finalize "$prefix" "$platform" "${archs[@]}"
|
||||
finalize "$prefix" "$platform" "${archs[@]%%:*}"
|
||||
}
|
||||
|
2
platform-darwin/External/AttributedMarkdown
vendored
@ -1 +1 @@
|
||||
Subproject commit d598fb4f5e29f5aaa66e7e880a9857019865881b
|
||||
Subproject commit 4aef3c80e0ffe6c7011c20c7f703ea56af774861
|
1
platform-darwin/External/InAppSettingsKit
vendored
@ -1 +0,0 @@
|
||||
Subproject commit 2dcb598d18a64a61509e418079bc448ce6b5268a
|
@ -1 +0,0 @@
|
||||
Subproject commit 1b8f8b79ad12b70976c7a417ff1a9d29e8c0ed73
|
2
platform-darwin/External/Pearl
vendored
@ -1 +1 @@
|
||||
Subproject commit b9da35c5efb21f95838233ebf95ddd8de2c41886
|
||||
Subproject commit 452d2a72c4e897e3141106ba6ca8544834128c9e
|
1
platform-darwin/External/jrswizzle
vendored
@ -1 +0,0 @@
|
||||
Subproject commit 98d18aee73329321c320a2df85bacdb9f08a34a6
|
1
platform-darwin/External/uicolor-utilities
vendored
@ -1 +0,0 @@
|
||||
Subproject commit ae96212a4903a2b9e1df1e3542c9962f0d64a74b
|
@ -30,10 +30,18 @@
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
536DEA2BDA9F2F9583C53D8F /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DA1554D220B3924000EA92C5 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DA1554DD20B3928E00EA92C5 /* core */,
|
||||
536DEA2BDA9F2F9583C53D8F /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@ -105,6 +113,7 @@
|
||||
Base,
|
||||
);
|
||||
mainGroup = DA1554D220B3924000EA92C5;
|
||||
productRefGroup = 536DEA2BDA9F2F9583C53D8F /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
|
@ -73,6 +73,13 @@
|
||||
identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier"
|
||||
referenceType = "1">
|
||||
</LocationScenarioReference>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "TERM"
|
||||
value = "color"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Debug"
|
||||
|
@ -7,21 +7,17 @@
|
||||
objects = {
|
||||
|
||||
/* 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 */; };
|
||||
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 */; };
|
||||
93D394C4254EEB45FB335AFB /* MPSitesTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39423D7BF4FD31FE6D27C /* MPSitesTableView.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 */; };
|
||||
93D3987F6D9046DBEE4D8364 /* NSView+Traversing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D392870DF659AFC1870521 /* NSView+Traversing.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 */; };
|
||||
93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.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 */; };
|
||||
DA0933D01747B91B00DE1CEF /* appstore.png in Resources */ = {isa = PBXBuildFile; fileRef = DA0933CF1747B91B00DE1CEF /* appstore.png */; };
|
||||
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 */; };
|
||||
DA3B8456190FC89700246EEA /* MPFixable.m in Sources */ = {isa = PBXBuildFile; fileRef = DA3B8454190FC89700246EEA /* MPFixable.m */; };
|
||||
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 */; };
|
||||
DA4DAE951A7D8117003E5423 /* MPTypes.m in Sources */ = {isa = PBXBuildFile; fileRef = DA4DAE931A7D8117003E5423 /* MPTypes.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 */; };
|
||||
DA5E5D031724A667003798D8 /* MPMacAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CB41724A667003798D8 /* MPMacAppDelegate.m */; };
|
||||
DA5E5D041724A667003798D8 /* MPMacConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CB61724A667003798D8 /* MPMacConfig.m */; };
|
||||
DA5E5D081724A667003798D8 /* MasterPassword.entitlements in Resources */ = {isa = PBXBuildFile; fileRef = DA5E5CBF1724A667003798D8 /* MasterPassword.entitlements */; };
|
||||
DA5E5D0A1724A667003798D8 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DA5E5CC21724A667003798D8 /* InfoPlist.strings */; };
|
||||
DA5E5D0B1724A667003798D8 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA5E5CC41724A667003798D8 /* MainMenu.xib */; };
|
||||
DA5E5D0C1724A667003798D8 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CC61724A667003798D8 /* main.m */; };
|
||||
@ -119,6 +113,7 @@
|
||||
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 */; };
|
||||
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 */; };
|
||||
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 */; };
|
||||
@ -126,7 +121,6 @@
|
||||
DA8ED895192906920099B726 /* PearlTween.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8ED891192906920099B726 /* PearlTween.m */; };
|
||||
DA8ED896192906920099B726 /* PearlTween.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8ED892192906920099B726 /* PearlTween.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 */; };
|
||||
DA9261561BE1A89600369DE5 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA9261551BE1A89600369DE5 /* libz.tbd */; };
|
||||
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 */; };
|
||||
DAADCC4919FAFFAD00987B1D /* NSPersistentStore+PearlMigration.m in Sources */ = {isa = PBXBuildFile; fileRef = DAADCC4019FAFFAD00987B1D /* NSPersistentStore+PearlMigration.m */; };
|
||||
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 */; };
|
||||
DAB07CA01F7725D400CC6D43 /* 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 */; };
|
||||
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 */; };
|
||||
DAC6326D148680650075AEA5 /* 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 */; };
|
||||
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 */; };
|
||||
DACA29681705DF81002C6C22 /* SourceCodePro-Black.otf in Resources */ = {isa = PBXBuildFile; fileRef = DACA268F1705DF81002C6C22 /* SourceCodePro-Black.otf */; };
|
||||
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 */; };
|
||||
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 */; };
|
||||
@ -308,13 +297,6 @@
|
||||
remoteGlobalIDString = DAD9B5C0176299B9001835F9;
|
||||
remoteInfo = "MasterPassword-Mac-LoginHelper";
|
||||
};
|
||||
DAC63285148681200075AEA5 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = DA5BFA3B147E415C00F98B1E /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = DAC6326B148680650075AEA5;
|
||||
remoteInfo = jrswizzle;
|
||||
};
|
||||
DAD9B5E5176299BA001835F9 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = DAD9B5E1176299B9001835F9 /* MasterPassword-Mac-LoginHelper.xcodeproj */;
|
||||
@ -352,15 +334,6 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 1;
|
||||
};
|
||||
DAADCC4E19FB006500987B1D /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "include/$(PRODUCT_NAME)";
|
||||
dstSubfolderSpec = 16;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
DAD9B5EE1762CA3A001835F9 /* Copy LoginHelper */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@ -375,28 +348,21 @@
|
||||
/* End PBXCopyFilesBuildPhase 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>"; };
|
||||
1E9477508F419F29008C7553 /* Pods_MasterPassword_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MasterPassword_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
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>"; };
|
||||
2C2AEAB82916C4A50EE4A4DE /* 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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -937,7 +903,6 @@
|
||||
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>"; };
|
||||
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; };
|
||||
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>"; };
|
||||
@ -1032,9 +997,6 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -1050,7 +1012,6 @@
|
||||
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>"; };
|
||||
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; };
|
||||
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>"; };
|
||||
@ -1096,8 +1057,6 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -1157,6 +1116,7 @@
|
||||
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>"; };
|
||||
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 */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@ -1185,15 +1145,14 @@
|
||||
DAB7AE5B1F3D750B00C856B1 /* libjson-c.a in Frameworks */,
|
||||
DA9261561BE1A89600369DE5 /* libz.tbd in Frameworks */,
|
||||
DA9261541BE1A88900369DE5 /* libc++.tbd in Frameworks */,
|
||||
DAADCC6A19FB00B500987B1D /* libKCOrderedAccessorFix.a in Frameworks */,
|
||||
DA9261511BE1A86700369DE5 /* SystemConfiguration.framework in Frameworks */,
|
||||
DA250925195148E200AC23F1 /* QuartzCore.framework in Frameworks */,
|
||||
DAD9B5F01762CAA4001835F9 /* ServiceManagement.framework in Frameworks */,
|
||||
DA16B341170661DB000A0EAB /* Carbon.framework in Frameworks */,
|
||||
DA16B342170661E0000A0EAB /* Security.framework in Frameworks */,
|
||||
DA16B345170661F2000A0EAB /* libPearl.a 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;
|
||||
};
|
||||
@ -1205,27 +1164,11 @@
|
||||
);
|
||||
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 */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
DAADCC4B19FB000C00987B1D /* QuartzCore.framework in Frameworks */,
|
||||
DA4DA1D91564471A00F6F596 /* libjrswizzle.a in Frameworks */,
|
||||
DAC77CAE148291A600BCF976 /* Foundation.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@ -1236,10 +1179,8 @@
|
||||
0C357E29DE20E18311095EBF /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
04D86D0B84D29123756339FD /* Pods-MasterPassword.debug.xcconfig */,
|
||||
7791961245EBC3023523FDCD /* Pods-MasterPassword.release.xcconfig */,
|
||||
1E9ED9547411486CE0898611 /* Pods-MasterPassword-macOS.debug.xcconfig */,
|
||||
28DD0F6129ECC496C8DFE6F8 /* Pods-MasterPassword-macOS.release.xcconfig */,
|
||||
6F97B456DA90E7D2E95B64A5 /* Pods-MasterPassword-macOS.debug.xcconfig */,
|
||||
DF299A3DC20096A2D2942352 /* Pods-MasterPassword-macOS.release.xcconfig */,
|
||||
);
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
@ -1272,14 +1213,6 @@
|
||||
name = "Other Frameworks";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DA3B8449190FC5A900246EEA /* Mac */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DA92614F1BE1A86700369DE5 /* SystemConfiguration.framework */,
|
||||
);
|
||||
path = Mac;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DA5BFA39147E415C00F98B1E = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -1298,8 +1231,6 @@
|
||||
children = (
|
||||
DA5BFA44147E415C00F98B1E /* Master Password.app */,
|
||||
DAC77CAD148291A600BCF976 /* libPearl.a */,
|
||||
DAC6326C148680650075AEA5 /* libjrswizzle.a */,
|
||||
DAADCC5019FB006500987B1D /* libKCOrderedAccessorFix.a */,
|
||||
DA67743B1A474A03004F356A /* mpw-test */,
|
||||
DA1C7AB61F1A8F24009A3551 /* mpw-cli */,
|
||||
DA1C7AD61F1A8FD8009A3551 /* mpw-bench */,
|
||||
@ -1326,7 +1257,7 @@
|
||||
DAD9B5EF1762CAA4001835F9 /* ServiceManagement.framework */,
|
||||
DA6701DD16406B7300B61001 /* Social.framework */,
|
||||
DABB981515100B4000B05417 /* SystemConfiguration.framework */,
|
||||
1E9477508F419F29008C7553 /* Pods_MasterPassword_macOS.framework */,
|
||||
2C2AEAB82916C4A50EE4A4DE /* Pods_MasterPassword_macOS.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
@ -2025,15 +1956,6 @@
|
||||
path = lib;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DAADCC6819FB007F00987B1D /* KCOrderedAccessorFix */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DAADCC6619FB007F00987B1D /* NSManagedObjectModel+KCOrderedAccessorFix.h */,
|
||||
DAADCC6719FB007F00987B1D /* NSManagedObjectModel+KCOrderedAccessorFix.m */,
|
||||
);
|
||||
path = KCOrderedAccessorFix;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DAC77CAF148291A600BCF976 /* Pearl */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -2050,9 +1972,6 @@
|
||||
children = (
|
||||
DAA7BC0D20C4C1B500101DC7 /* libjson-c-macos */,
|
||||
DAA7BBC920C4C17300101DC7 /* libsodium-macos */,
|
||||
DACA29751705E2BD002C6C22 /* jrswizzle */,
|
||||
DAADCC6819FB007F00987B1D /* KCOrderedAccessorFix */,
|
||||
DA3B8449190FC5A900246EEA /* Mac */,
|
||||
DAC77CAF148291A600BCF976 /* Pearl */,
|
||||
);
|
||||
path = External;
|
||||
@ -2152,15 +2071,6 @@
|
||||
path = Data;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DACA29751705E2BD002C6C22 /* jrswizzle */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DACA29771705E2BD002C6C22 /* JRSwizzle.h */,
|
||||
DACA298C1705E2BD002C6C22 /* JRSwizzle.m */,
|
||||
);
|
||||
path = jrswizzle;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DAD0C5F419FD6034009CB08D /* mac */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -2205,14 +2115,10 @@
|
||||
DAB4FBDD202FF93D002768FB /* PearlHangDetector.h */,
|
||||
DAB4FBDE202FF93D002768FB /* PearlHangDetector.m */,
|
||||
DA8ED893192906920099B726 /* include */,
|
||||
93D396D04E57792A54D437AC /* NSArray+Indexing.h */,
|
||||
93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */,
|
||||
DA2CA4EA18D323D3007798F8 /* NSArray+Pearl.h */,
|
||||
DA2CA4E918D323D3007798F8 /* NSArray+Pearl.m */,
|
||||
DAFE4A63150399FF003ABA93 /* NSDateFormatter+RFC3339.h */,
|
||||
DAFE4A63150399FF003ABA91 /* NSDateFormatter+RFC3339.m */,
|
||||
93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */,
|
||||
93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */,
|
||||
DA2CA4E818D323D3007798F8 /* NSError+PearlFullDescription.h */,
|
||||
DA2CA4E718D323D3007798F8 /* NSError+PearlFullDescription.m */,
|
||||
DA3B8451190FC86F00246EEA /* NSManagedObject+Pearl.h */,
|
||||
@ -2292,14 +2198,6 @@
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
DAC6326A148680650075AEA5 /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
DACA298D1705E2BD002C6C22 /* JRSwizzle.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
DAC77CAB148291A600BCF976 /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@ -2335,8 +2233,6 @@
|
||||
DAADCC4719FAFFAD00987B1D /* NSNotificationCenter+PearlEasyCleanup.h in Headers */,
|
||||
DAFE4A63150399FF003ABA88 /* NSObject+PearlKVO.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 */,
|
||||
DA2C3D681BD9665B001137B3 /* PearlProfiler.h in Headers */,
|
||||
DA2CA4EE18D323D3007798F8 /* NSError+PearlFullDescription.h in Headers */,
|
||||
@ -2420,15 +2316,15 @@
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = DA5BFA6D147E415C00F98B1E /* Build configuration list for PBXNativeTarget "MasterPassword-macOS" */;
|
||||
buildPhases = (
|
||||
0DC4F66C2499C40A68A07D58 /* [CP] Check Pods Manifest.lock */,
|
||||
A4743CFBA7F5C60D2E440512 /* [CP] Check Pods Manifest.lock */,
|
||||
DA4EF9CB19FD4B600032ECB5 /* Run Script: genassets */,
|
||||
DA5BFA40147E415C00F98B1E /* Sources */,
|
||||
DA5BFA41147E415C00F98B1E /* Frameworks */,
|
||||
DA5BFA42147E415C00F98B1E /* Resources */,
|
||||
DAD9B5EE1762CA3A001835F9 /* Copy LoginHelper */,
|
||||
DA6556E314D55F3000841C99 /* Run Script: GIT version -> Info.plist */,
|
||||
43E5966C8C236E86824DDADE /* [CP] Embed Pods Frameworks */,
|
||||
DA3C4EB2243941AE00A6C4A8 /* Upload Sentry dSYM */,
|
||||
DA3C4EB2243941AE00A6C4A8 /* Sentry dSYM Upload */,
|
||||
85C937E180CD7620A471A930 /* [CP] Embed Pods Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@ -2460,40 +2356,6 @@
|
||||
productReference = DA67743B1A474A03004F356A /* mpw-test */;
|
||||
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 */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = DAC77CB7148291A600BCF976 /* Build configuration list for PBXNativeTarget "Pearl" */;
|
||||
@ -2505,7 +2367,6 @@
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
DAC63286148681200075AEA5 /* PBXTargetDependency */,
|
||||
);
|
||||
name = Pearl;
|
||||
productName = Pearl;
|
||||
@ -2522,7 +2383,7 @@
|
||||
CLASSPREFIX = MP;
|
||||
LastSwiftUpdateCheck = 0720;
|
||||
LastTestingUpgradeCheck = 0510;
|
||||
LastUpgradeCheck = 1140;
|
||||
LastUpgradeCheck = 1200;
|
||||
ORGANIZATIONNAME = Lyndir;
|
||||
TargetAttributes = {
|
||||
DA1C7AA61F1A8F24009A3551 = {
|
||||
@ -2549,18 +2410,11 @@
|
||||
CreatedOnToolsVersion = 6.1.1;
|
||||
DevelopmentTeam = HL3Q45LX9N;
|
||||
};
|
||||
DAADCC4F19FB006500987B1D = {
|
||||
CreatedOnToolsVersion = 6.0.1;
|
||||
DevelopmentTeam = HL3Q45LX9N;
|
||||
};
|
||||
DAB7AE421F3D466D00C856B1 = {
|
||||
CreatedOnToolsVersion = 8.3.3;
|
||||
DevelopmentTeam = HL3Q45LX9N;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
DAC6326B148680650075AEA5 = {
|
||||
DevelopmentTeam = HL3Q45LX9N;
|
||||
};
|
||||
DAC77CAC148291A600BCF976 = {
|
||||
DevelopmentTeam = HL3Q45LX9N;
|
||||
};
|
||||
@ -2587,8 +2441,6 @@
|
||||
targets = (
|
||||
DA5BFA43147E415C00F98B1E /* MasterPassword-macOS */,
|
||||
DAC77CAC148291A600BCF976 /* Pearl */,
|
||||
DAC6326B148680650075AEA5 /* jrswizzle */,
|
||||
DAADCC4F19FB006500987B1D /* KCOrderedAccessorFix */,
|
||||
DA67743A1A474A03004F356A /* mpw-test */,
|
||||
DA1C7AC61F1A8FD8009A3551 /* mpw-bench */,
|
||||
DA1C7AA61F1A8F24009A3551 /* mpw-cli */,
|
||||
@ -2678,7 +2530,6 @@
|
||||
DACA29671705DF81002C6C22 /* SourceCodePro-ExtraLight.otf in Resources */,
|
||||
DACA29681705DF81002C6C22 /* SourceCodePro-Black.otf in Resources */,
|
||||
DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */,
|
||||
DA5E5D081724A667003798D8 /* MasterPassword.entitlements in Resources */,
|
||||
DA5E5D0A1724A667003798D8 /* InfoPlist.strings in Resources */,
|
||||
DA5E5D0B1724A667003798D8 /* MainMenu.xib in Resources */,
|
||||
DA0933CC1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png in Resources */,
|
||||
@ -2689,7 +2540,29 @@
|
||||
/* End PBXResourcesBuildPhase 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;
|
||||
buildActionMask = 2147483647;
|
||||
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";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
43E5966C8C236E86824DDADE /* [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}/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 */ = {
|
||||
DA3C4EB2243941AE00A6C4A8 /* Sentry dSYM Upload */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 8;
|
||||
files = (
|
||||
@ -2739,15 +2592,16 @@
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Resources/DWARF/${TARGET_NAME}",
|
||||
);
|
||||
name = "Upload Sentry dSYM";
|
||||
name = "Sentry dSYM Upload";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 1;
|
||||
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;
|
||||
};
|
||||
DA4EF9CB19FD4B600032ECB5 /* Run Script: genassets */ = {
|
||||
@ -2891,22 +2745,6 @@
|
||||
);
|
||||
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 */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@ -2942,8 +2780,6 @@
|
||||
DAB4FBEA202FF951002768FB /* NSMutableSet+Pearl.m in Sources */,
|
||||
DAFE4A63150399FF003ABA86 /* NSObject+PearlKVO.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 */,
|
||||
93D3987F6D9046DBEE4D8364 /* NSView+Traversing.m in Sources */,
|
||||
);
|
||||
@ -2987,11 +2823,6 @@
|
||||
name = "MasterPassword-Mac-LoginHelper";
|
||||
targetProxy = DABFA071176E3FDF00E83589 /* PBXContainerItemProxy */;
|
||||
};
|
||||
DAC63286148681200075AEA5 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = DAC6326B148680650075AEA5 /* jrswizzle */;
|
||||
targetProxy = DAC63285148681200075AEA5 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
@ -3364,12 +3195,13 @@
|
||||
};
|
||||
DA5BFA6E147E415C00F98B1E /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 1E9ED9547411486CE0898611 /* Pods-MasterPassword-macOS.debug.xcconfig */;
|
||||
baseConfigurationReference = 6F97B456DA90E7D2E95B64A5 /* Pods-MasterPassword-macOS.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Source/Mac/MasterPassword.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";
|
||||
@ -3389,6 +3221,7 @@
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib\"",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
OTHER_CFLAGS = (
|
||||
"-DMPW_SODIUM=1",
|
||||
"-DMPW_JSON=1",
|
||||
@ -3402,12 +3235,13 @@
|
||||
};
|
||||
DA5BFA6F147E415C00F98B1E /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 28DD0F6129ECC496C8DFE6F8 /* Pods-MasterPassword-macOS.release.xcconfig */;
|
||||
baseConfigurationReference = DF299A3DC20096A2D2942352 /* Pods-MasterPassword-macOS.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Source/Mac/MasterPassword.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";
|
||||
@ -3427,6 +3261,7 @@
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib\"",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
OTHER_CFLAGS = (
|
||||
"-DMPW_SODIUM=1",
|
||||
"-DMPW_JSON=1",
|
||||
@ -3486,22 +3321,6 @@
|
||||
};
|
||||
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 */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
@ -3543,34 +3362,6 @@
|
||||
};
|
||||
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 */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
@ -3654,15 +3445,6 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
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" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
@ -3672,15 +3454,6 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
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" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1140"
|
||||
version = "1.3">
|
||||
LastUpgradeVersion = "1200"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
@ -27,15 +27,6 @@
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
|
||||
BuildableName = "Master Password.app"
|
||||
BlueprintName = "MasterPassword-macOS"
|
||||
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
@ -59,6 +50,17 @@
|
||||
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "TERM"
|
||||
value = "color"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
<LocationScenarioReference
|
||||
identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier"
|
||||
referenceType = "1">
|
||||
</LocationScenarioReference>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Debug"
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1140"
|
||||
version = "1.3">
|
||||
LastUpgradeVersion = "1200"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
@ -27,15 +27,6 @@
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DA1C7AC61F1A8FD8009A3551"
|
||||
BuildableName = "mpw-bench"
|
||||
BlueprintName = "mpw-bench"
|
||||
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
@ -59,6 +50,17 @@
|
||||
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "TERM"
|
||||
value = "color"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
<LocationScenarioReference
|
||||
identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier"
|
||||
referenceType = "1">
|
||||
</LocationScenarioReference>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1140"
|
||||
LastUpgradeVersion = "1200"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@ -27,15 +27,6 @@
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DA1C7AA61F1A8F24009A3551"
|
||||
BuildableName = "mpw-cli"
|
||||
BlueprintName = "mpw-cli"
|
||||
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
@ -81,6 +72,13 @@
|
||||
isEnabled = "YES">
|
||||
</CommandLineArgument>
|
||||
</CommandLineArguments>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "TERM"
|
||||
value = "color"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
<LocationScenarioReference
|
||||
identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier"
|
||||
referenceType = "1">
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1140"
|
||||
version = "1.3">
|
||||
LastUpgradeVersion = "1200"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
@ -27,15 +27,6 @@
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DA67743A1A474A03004F356A"
|
||||
BuildableName = "mpw-test"
|
||||
BlueprintName = "mpw-test"
|
||||
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
@ -60,6 +51,17 @@
|
||||
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "TERM"
|
||||
value = "color"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
<LocationScenarioReference
|
||||
identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier"
|
||||
referenceType = "1">
|
||||
</LocationScenarioReference>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Debug"
|
||||
|
@ -11,6 +11,9 @@ target 'MasterPassword-iOS' do
|
||||
|
||||
pod 'Sentry', :git => 'https://github.com/getsentry/sentry-cocoa.git'
|
||||
pod 'Countly'
|
||||
pod 'UIColor-Utilities'
|
||||
pod 'KCOrderedAccessorFix'
|
||||
pod 'JRSwizzle'
|
||||
end
|
||||
|
||||
target 'MasterPassword-macOS' do
|
||||
@ -22,4 +25,17 @@ target 'MasterPassword-macOS' do
|
||||
|
||||
pod 'Sentry', :git => 'https://github.com/getsentry/sentry-cocoa.git'
|
||||
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
|
||||
|
Before Width: | Height: | Size: 102 KiB |
Before Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 843 B After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 8.0 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 9.5 KiB |
Before Width: | Height: | Size: 236 KiB |
Before Width: | Height: | Size: 879 KiB After Width: | Height: | Size: 879 KiB |
@ -34,7 +34,7 @@ icons=(
|
||||
# 57@1@iphone@:Icon.png
|
||||
# 29@1@iphone@:Icon-Small.png
|
||||
# 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
|
||||
167@2@ipad@9.0:Icon-83@2x.png
|
||||
20@1@ipad@7.0:Icon-Small-20.png
|
||||
|
@ -46,9 +46,10 @@ esac
|
||||
description=$(git describe --always --dirty --long --match "*-$platform*")
|
||||
version=${description%-g*} build=${version##*-} version=${version%-$build}
|
||||
version=${version//-$platform/} version=${version//-/.} commit=${description##*-g}
|
||||
IFS=. read major minor build <<< "$version"
|
||||
|
||||
addPlistWithKey GITDescription string "$description"
|
||||
setPlistWithKey CFBundleVersion "$(hr "${version%%.*}" 14).${version#*.}"
|
||||
setPlistWithKey CFBundleVersion "$(hr "$major" 14)$(printf '%02d' "$minor")$(printf '%02d' "$build")"
|
||||
setPlistWithKey CFBundleShortVersionString "$version"
|
||||
|
||||
setSettingWithTitle "Build" "$commit"
|
||||
|
@ -81,15 +81,14 @@ static NSOperationQueue *_mpwQueue = nil;
|
||||
}
|
||||
|
||||
NSOperation *operation = [NSBlockOperation blockOperationWithBlock:operationBlock];
|
||||
if ([operation respondsToSelector:@selector( qualityOfService )])
|
||||
operation.qualityOfService = NSQualityOfServiceUserInitiated;
|
||||
operation.qualityOfService = NSQualityOfServiceUserInitiated;
|
||||
[_mpwQueue addOperations:@[ operation ] waitUntilFinished:YES];
|
||||
}
|
||||
|
||||
- (BOOL)tryMigrateUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc {
|
||||
|
||||
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];
|
||||
NSArray *migrationSites = [moc executeFetchRequest:migrationRequest error:&error];
|
||||
if (!migrationSites) {
|
||||
@ -147,10 +146,10 @@ static NSOperationQueue *_mpwQueue = nil;
|
||||
|
||||
- (NSString *)nameOfType:(MPResultType)type {
|
||||
|
||||
if (!type)
|
||||
return nil;
|
||||
|
||||
switch (type) {
|
||||
case MPResultTypeNone:
|
||||
return @"None";
|
||||
|
||||
case MPResultTypeTemplateMaximum:
|
||||
return @"Maximum Security Password";
|
||||
|
||||
@ -190,10 +189,10 @@ static NSOperationQueue *_mpwQueue = nil;
|
||||
|
||||
- (NSString *)shortNameOfType:(MPResultType)type {
|
||||
|
||||
if (!type)
|
||||
return nil;
|
||||
|
||||
switch (type) {
|
||||
case MPResultTypeNone:
|
||||
return @"None";
|
||||
|
||||
case MPResultTypeTemplateMaximum:
|
||||
return @"Maximum";
|
||||
|
||||
@ -238,9 +237,6 @@ static NSOperationQueue *_mpwQueue = nil;
|
||||
|
||||
- (Class)classOfType:(MPResultType)type {
|
||||
|
||||
if (!type)
|
||||
Throw( @"No type given." );
|
||||
|
||||
switch (type) {
|
||||
case MPResultTypeTemplateMaximum:
|
||||
return [MPGeneratedSiteEntity class];
|
||||
@ -272,6 +268,7 @@ static NSOperationQueue *_mpwQueue = nil;
|
||||
case MPResultTypeStatefulDevice:
|
||||
return [MPStoredSiteEntity class];
|
||||
|
||||
case MPResultTypeNone:
|
||||
case MPResultTypeDeriveKey:
|
||||
break;
|
||||
}
|
||||
@ -323,6 +320,7 @@ static NSOperationQueue *_mpwQueue = nil;
|
||||
return MPResultTypeStatefulDevice;
|
||||
case MPResultTypeStatefulDevice:
|
||||
return MPResultTypeTemplatePhrase;
|
||||
case MPResultTypeNone:
|
||||
case MPResultTypeDeriveKey:
|
||||
break;
|
||||
}
|
||||
@ -532,8 +530,13 @@ static NSOperationQueue *_mpwQueue = nil;
|
||||
return;
|
||||
}
|
||||
|
||||
case MPResultTypeDeriveKey:
|
||||
case MPResultTypeNone:
|
||||
case MPResultTypeDeriveKey: {
|
||||
PearlNotMainQueue( ^{
|
||||
resultBlock( nil );
|
||||
} );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Throw( @"Type not supported: %lu", (long)type );
|
||||
@ -622,7 +625,7 @@ static NSOperationQueue *_mpwQueue = nil;
|
||||
|
||||
- (NSString *)exportLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)key {
|
||||
|
||||
if (!(site.type & MPSiteFeatureExportContent) || site.loginGenerated || ![site.loginName length])
|
||||
if (site.loginGenerated || ![site.loginName length])
|
||||
return nil;
|
||||
|
||||
__block NSData *state = nil;
|
||||
|
@ -117,7 +117,7 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
|
||||
[self performPurchaseProductWithIdentifier:productIdentifier quantity:quantity];
|
||||
}]];
|
||||
[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;
|
||||
}
|
||||
#endif
|
||||
@ -158,35 +158,27 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
|
||||
|
||||
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
|
||||
|
||||
MPError( error, @"StoreKit request (%@) failed.", request );
|
||||
MPError( error, @"StoreKit request failed." );
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
PearlMainQueue( ^{
|
||||
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Purchase Failed" message:
|
||||
strf( @"%@\n\n%@", error.localizedDescription,
|
||||
@"Ensure you are online and try logging out and back into iTunes from your device's Settings." )
|
||||
@"Could not reach Apple's iTunes Store. Make sure you're connected to the Internet and try again." )
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[controller addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]];
|
||||
[self.navigationController presentViewController:controller animated:YES completion:nil];
|
||||
[self.window.rootViewController presentViewController:controller animated:YES completion:nil];
|
||||
} );
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)requestDidFinish:(SKRequest *)request {
|
||||
|
||||
dbg( @"StoreKit request (%@) finished.", request );
|
||||
}
|
||||
|
||||
#pragma mark - SKPaymentTransactionObserver
|
||||
|
||||
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
|
||||
|
||||
for (SKPaymentTransaction *transaction in transactions) {
|
||||
dbg( @"transaction updated: %@ -> %d", transaction.payment.productIdentifier, (int)(transaction.transactionState) );
|
||||
|
||||
switch (transaction.transactionState) {
|
||||
case SKPaymentTransactionStatePurchased: {
|
||||
inf( @"Purchased: %@", transaction.payment.productIdentifier );
|
||||
NSMutableDictionary *attributes = [NSMutableDictionary new];
|
||||
|
||||
if ([transaction.payment.productIdentifier isEqualToString:MPProductFuel]) {
|
||||
@ -205,22 +197,19 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
|
||||
forKey:transaction.payment.productIdentifier];
|
||||
[queue finishTransaction:transaction];
|
||||
|
||||
if ([[MPConfig get].sendInfo boolValue]) {
|
||||
SKProduct *product = self.products[transaction.payment.productIdentifier];
|
||||
[attributes addEntriesFromDictionary:@{
|
||||
@"id": product.productIdentifier,
|
||||
@"name": product.localizedTitle,
|
||||
@"price": product.price.description,
|
||||
@"currency": [product.priceLocale objectForKey:NSLocaleCurrencyCode],
|
||||
@"state" : @"success",
|
||||
@"quantity": @(transaction.payment.quantity).description,
|
||||
}];
|
||||
[Countly.sharedInstance recordEvent:@"purchase" segmentation:attributes];
|
||||
}
|
||||
SKProduct *product = self.products[transaction.payment.productIdentifier];
|
||||
[attributes addEntriesFromDictionary:@{
|
||||
@"id": product.productIdentifier,
|
||||
@"name": product.localizedTitle,
|
||||
@"price": product.price.description,
|
||||
@"currency": [product.priceLocale objectForKey:NSLocaleCurrencyCode],
|
||||
@"state" : @"success",
|
||||
@"quantity": @(transaction.payment.quantity).description,
|
||||
}];
|
||||
[Countly.sharedInstance recordEvent:@"purchase" segmentation:attributes];
|
||||
break;
|
||||
}
|
||||
case SKPaymentTransactionStateRestored: {
|
||||
inf( @"Restored: %@", transaction.payment.productIdentifier );
|
||||
[[NSUserDefaults standardUserDefaults] setObject:transaction.transactionIdentifier
|
||||
forKey:transaction.payment.productIdentifier];
|
||||
[queue finishTransaction:transaction];
|
||||
@ -233,18 +222,27 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
|
||||
MPError( transaction.error, @"Transaction failed: %@.", transaction.payment.productIdentifier );
|
||||
[queue finishTransaction:transaction];
|
||||
|
||||
if ([[MPConfig get].sendInfo boolValue]) {
|
||||
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],
|
||||
}];
|
||||
}
|
||||
#if TARGET_OS_IPHONE
|
||||
PearlMainQueue( ^{
|
||||
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Purchase Failed" message:
|
||||
strf( @"%@\n\n%@", transaction.error.localizedDescription,
|
||||
@"Could not reach Apple's iTunes Store. Make sure you're connected to the Internet and try again." )
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[controller addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]];
|
||||
[self.window.rootViewController presentViewController:controller animated:YES completion:nil];
|
||||
} );
|
||||
#endif
|
||||
|
||||
SKProduct *product = self.products[transaction.payment.productIdentifier];
|
||||
[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;
|
||||
}
|
||||
}
|
||||
|
@ -173,13 +173,11 @@
|
||||
else
|
||||
dbg( @"Automatic login failed for user: %@", user.userID );
|
||||
|
||||
if ([[MPConfig get].sendInfo boolValue]) {
|
||||
[Countly.sharedInstance recordEvent:@"login" segmentation:@{
|
||||
@"method" : password? @"Password": @"Automatic",
|
||||
@"state" : @"failed",
|
||||
@"algorithm": @(user.algorithm.version).description,
|
||||
}];
|
||||
}
|
||||
[Countly.sharedInstance recordEvent:@"login" segmentation:@{
|
||||
@"method" : password? @"Password": @"Automatic",
|
||||
@"state" : @"failed",
|
||||
@"algorithm": @(user.algorithm.version).description,
|
||||
}];
|
||||
|
||||
return NO;
|
||||
}
|
||||
@ -203,15 +201,14 @@
|
||||
}
|
||||
|
||||
@try {
|
||||
if ([[MPConfig get].sendInfo boolValue]) {
|
||||
[Countly.sharedInstance userLoggedIn:user.userID];
|
||||
[SentrySDK setUser:[[SentryUser alloc] initWithUserId:user.userID]];
|
||||
[Countly.sharedInstance userLoggedIn:user.userID];
|
||||
|
||||
[Countly.sharedInstance recordEvent:@"login" segmentation:@{
|
||||
@"method" : password? @"Password": @"Automatic",
|
||||
@"state" : @"success",
|
||||
@"algorithm": @(user.algorithm.version).description,
|
||||
}];
|
||||
}
|
||||
[Countly.sharedInstance recordEvent:@"login" segmentation:@{
|
||||
@"method" : password? @"Password": @"Automatic",
|
||||
@"state" : @"success",
|
||||
@"algorithm": @(user.algorithm.version).description,
|
||||
}];
|
||||
}
|
||||
@catch (id exception) {
|
||||
err( @"While setting username: %@", exception );
|
||||
@ -252,18 +249,18 @@
|
||||
masterPassword = PearlAwait( ^(void (^setResult)(id)) {
|
||||
PearlMainQueue( ^{
|
||||
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];
|
||||
[controller addTextFieldWithConfigurationHandler:nil];
|
||||
[controller addAction:[UIAlertAction actionWithTitle:@"Migrate" style:UIAlertActionStyleDefault handler:
|
||||
^(UIAlertAction *_Nonnull action) {
|
||||
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) {
|
||||
setResult( nil );
|
||||
}]];
|
||||
[self.navigationController presentViewController:controller animated:YES completion:nil];
|
||||
[self.window.rootViewController presentViewController:controller animated:YES completion:nil];
|
||||
} );
|
||||
} );
|
||||
#endif
|
||||
|
@ -20,7 +20,7 @@
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
|
||||
@interface MPAppDelegate_Shared : PearlAppDelegate
|
||||
@interface MPAppDelegate_Shared : UIResponder<UIApplicationDelegate, PearlConfigDelegate>
|
||||
|
||||
#else
|
||||
|
||||
|
@ -32,7 +32,7 @@
|
||||
@end
|
||||
|
||||
MPLogSink mpw_log_sink_pearl;
|
||||
void mpw_log_sink_pearl(const MPLogEvent *record) {
|
||||
bool mpw_log_sink_pearl(const MPLogEvent *record) {
|
||||
|
||||
PearlLogLevel level = PearlLogLevelInfo;
|
||||
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)
|
||||
withLevel:level text:@(record->message)];
|
||||
return YES;
|
||||
}
|
||||
|
||||
@implementation MPAppDelegate_Shared
|
||||
@ -112,18 +113,16 @@ static MPAppDelegate_Shared *instance;
|
||||
- (void)setActiveUser:(MPUserEntity *)activeUser {
|
||||
|
||||
NSManagedObjectID *activeUserOID = activeUser.permanentObjectID;
|
||||
if ([self.activeUserOID isEqual:activeUserOID])
|
||||
if (self.activeUserOID == activeUserOID || [self.activeUserOID isEqual:activeUserOID])
|
||||
return;
|
||||
|
||||
if (self.key)
|
||||
self.key = nil;
|
||||
|
||||
if ([[MPConfig get].sendInfo boolValue])
|
||||
[Countly.sharedInstance userLoggedOut];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPSignedOutNotification object:self];
|
||||
self.key = nil;
|
||||
[SentrySDK setUser:nil];
|
||||
[Countly.sharedInstance userLoggedOut];
|
||||
|
||||
self.activeUserOID = activeUserOID;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPSignedOutNotification object:self];
|
||||
}
|
||||
|
||||
- (void)handleCoordinatorError:(NSError *)error {
|
||||
|
@ -30,6 +30,7 @@
|
||||
- (id)managedObjectContextChanged:(void ( ^ )(NSDictionary<NSManagedObjectID *, NSString *> *affectedObjects))changedBlock;
|
||||
|
||||
- (MPFixableResult)findAndFixInconsistenciesSaveInContext:(NSManagedObjectContext *)context;
|
||||
- (void)retryCorruptStore;
|
||||
- (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. */
|
||||
@ -39,8 +40,9 @@
|
||||
askImportPassword:(NSString *( ^ )(NSString *userName))importPassword
|
||||
askUserPassword:(NSString *( ^ )(NSString *userName))userPassword
|
||||
result:(void ( ^ )(NSError *error))resultBlock;
|
||||
- (void)exportSitesRevealPasswords:(BOOL)revealPasswords
|
||||
askExportPassword:(NSString *( ^ )(NSString *userName))askImportPassword
|
||||
result:(void ( ^ )(NSString *exportedUser, NSError *error))resultBlock;
|
||||
- (NSString *)exportSitesFor:(MPUserEntity *)user
|
||||
revealPasswords:(BOOL)revealPasswords
|
||||
askExportPassword:(NSString *( ^ )(NSString *userName))askExportPassword
|
||||
error:(__autoreleasing NSError **)error;
|
||||
|
||||
@end
|
||||
|
@ -19,6 +19,8 @@
|
||||
#import "MPAppDelegate_Store.h"
|
||||
#import "mpw-marshal.h"
|
||||
#import "mpw-util.h"
|
||||
#import "MPAppDelegate_InApp.h"
|
||||
#import "MPSecrets.h"
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#define STORE_OPTIONS NSPersistentStoreFileProtectionKey : NSFileProtectionComplete,
|
||||
@ -49,7 +51,8 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
|
||||
+ (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];
|
||||
if (!mainManagedObjectContext || ![[NSThread currentThread] isMainThread])
|
||||
return nil;
|
||||
@ -154,6 +157,39 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
- (NSManagedObjectContext *)mainManagedObjectContextIfReady {
|
||||
|
||||
[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;
|
||||
}
|
||||
|
||||
@ -181,12 +217,12 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
} );
|
||||
|
||||
// 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;
|
||||
|
||||
[self.storeQueue addOperationWithBlock:^{
|
||||
NSOperation *storeOperation = [NSBlockOperation blockOperationWithBlock:^{
|
||||
// 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;
|
||||
|
||||
// Unregister any existing observers and contexts.
|
||||
@ -207,41 +243,15 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
// Check if migration is necessary.
|
||||
[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.
|
||||
NSError *error = nil;
|
||||
NSURL *localStoreURL = [self localStoreURL];
|
||||
NSError *error = nil;
|
||||
if (![[NSFileManager defaultManager] createDirectoryAtURL:[localStoreURL URLByDeletingLastPathComponent]
|
||||
withIntermediateDirectories:YES attributes:nil error:&error]) {
|
||||
MPError( error, @"Couldn't create our application support directory." );
|
||||
return;
|
||||
}
|
||||
if (![self.storeCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[self localStoreURL]
|
||||
if (![self.storeCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:localStoreURL
|
||||
options:@{
|
||||
NSMigratePersistentStoresAutomaticallyOption: @YES,
|
||||
NSInferMappingModelAutomaticallyOption : @YES,
|
||||
@ -254,17 +264,10 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
}
|
||||
self.storeCorrupted = @NO;
|
||||
|
||||
#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
|
||||
// Install managed object contexts and observers.
|
||||
self.privateManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
|
||||
self.privateManagedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
|
||||
self.privateManagedObjectContext.persistentStoreCoordinator = self.storeCoordinator;
|
||||
|
||||
// Perform a data sanity check on the newly loaded store to find and fix any issues.
|
||||
if ([[MPConfig get].checkInconsistency boolValue])
|
||||
@ -272,6 +275,14 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
[self findAndFixInconsistenciesSaveInContext:context];
|
||||
}];
|
||||
}];
|
||||
|
||||
[self.storeQueue addOperations:@[ storeOperation ] waitUntilFinished:YES];
|
||||
}
|
||||
|
||||
- (void)retryCorruptStore {
|
||||
|
||||
self.storeCorrupted = @NO;
|
||||
[self loadStore];
|
||||
}
|
||||
|
||||
- (void)deleteAndResetStore {
|
||||
@ -563,55 +574,51 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
|
||||
// Read metadata for the import file.
|
||||
MPMarshalledFile *file = mpw_marshal_read( NULL, importData.UTF8String );
|
||||
if (!file)
|
||||
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
|
||||
@"type" : @(MPMarshalErrorInternal),
|
||||
NSLocalizedDescriptionKey: @"Could not process Master Password import data.",
|
||||
}]), @"While importing sites." );
|
||||
if (file->error.type != MPMarshalSuccess) {
|
||||
MPMarshalErrorType type = file->error.type;
|
||||
mpw_marshal_file_free( &file );
|
||||
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
|
||||
@"type" : @(type),
|
||||
NSLocalizedDescriptionKey: @"Could not parse Master Password import data.",
|
||||
}]), @"While importing sites." );
|
||||
}
|
||||
if (file->info->format == MPMarshalFormatNone) {
|
||||
mpw_marshal_file_free( &file );
|
||||
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
|
||||
@"type" : @(MPMarshalErrorFormat),
|
||||
NSLocalizedDescriptionKey: @"This is not a Master Password import file.",
|
||||
}]), @"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]), @"" );
|
||||
MPMarshalledUser *importUser = nil;
|
||||
@try {
|
||||
if (!file)
|
||||
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
|
||||
@"type" : @(MPMarshalErrorInternal),
|
||||
NSLocalizedDescriptionKey: @"Could not process Master Password import data.",
|
||||
}]), @"While importing sites." );
|
||||
if (file->error.type != MPMarshalSuccess) {
|
||||
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
|
||||
@"type" : @(file->error.type),
|
||||
NSLocalizedDescriptionKey: strf( @"Could not parse Master Password import data:\n%@", @(file->error.message) ),
|
||||
}]), @"While importing sites." );
|
||||
}
|
||||
if (file->info->format == MPMarshalFormatNone) {
|
||||
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
|
||||
@"type" : @(MPMarshalErrorFormat),
|
||||
NSLocalizedDescriptionKey: @"This is not a Master Password import file.",
|
||||
}]), @"While importing sites." );
|
||||
}
|
||||
|
||||
importKey = [[MPKey alloc] initForFullName:@(file->info->fullName) withMasterPassword:importMasterPassword];
|
||||
} while ([[[importKey keyIDForAlgorithm:MPAlgorithmForVersion( file->info->algorithm )] encodeHex]
|
||||
caseInsensitiveCompare:@(file->info->keyID)] != NSOrderedSame);
|
||||
// Get master password for import file.
|
||||
MPKey *importKey;
|
||||
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.
|
||||
MPMarshalledUser *importUser = mpw_marshal_auth( file, mpw_masterKeyProvider_str( importMasterPassword.UTF8String ) );
|
||||
importKey = [[MPKey alloc] initForFullName:@(file->info->fullName) withMasterPassword:importMasterPassword];
|
||||
} 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)
|
||||
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
|
||||
@"type" : @(file->error.type),
|
||||
NSLocalizedDescriptionKey: @(file->error.message),
|
||||
NSLocalizedDescriptionKey: strf( @"Could not authenticate Master Password import:\n%@", @(file->error.message) ),
|
||||
}]), @"While importing sites." );
|
||||
|
||||
// Find an existing user to update.
|
||||
NSError *error = nil;
|
||||
NSFetchRequest *userFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
|
||||
NSFetchRequest *userFetchRequest = [MPUserEntity fetchRequest];
|
||||
userFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", @(importUser->fullName)];
|
||||
NSArray *users = [context executeFetchRequest:userFetchRequest error:&error];
|
||||
if (!users)
|
||||
@ -643,24 +650,21 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
user.avatar = importUser->avatar;
|
||||
user.defaultType = importUser->defaultType;
|
||||
user.lastUsed = [NSDate dateWithTimeIntervalSince1970:MAX( user.lastUsed.timeIntervalSince1970, importUser->lastUsed )];
|
||||
dbg( @"Importing user: %@", [user debugDescription] );
|
||||
|
||||
// Update or create sites.
|
||||
for (size_t s = 0; s < importUser->sites_count; ++s) {
|
||||
MPMarshalledSite *importSite = &importUser->sites[s];
|
||||
|
||||
// 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];
|
||||
NSArray *existingSites = [context executeFetchRequest:siteFetchRequest error:&error];
|
||||
if (!existingSites)
|
||||
return MPError( error, @"Lookup of existing sites failed for site: %@, user: %@", @(importSite->siteName), user.userID );
|
||||
if ([existingSites count])
|
||||
// Update existing site.
|
||||
for (MPSiteEntity *site in existingSites) {
|
||||
for (MPSiteEntity *site in existingSites)
|
||||
[self importSite:importSite protectedByKey:importKey intoSite:site usingKey:userKey];
|
||||
dbg( @"Updated site: %@", [site debugDescription] );
|
||||
}
|
||||
else {
|
||||
// Create new site.
|
||||
id<MPAlgorithm> algorithm = MPAlgorithmForVersion( importSite->algorithm );
|
||||
@ -673,7 +677,6 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
site.user = user;
|
||||
|
||||
[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];
|
||||
}
|
||||
|
||||
- (void)exportSitesRevealPasswords:(BOOL)revealPasswords
|
||||
askExportPassword:(NSString *( ^ )(NSString *userName))askImportPassword
|
||||
result:(void ( ^ )(NSString *exportedUser, NSError *error))resultBlock {
|
||||
|
||||
[MPAppDelegate_Shared managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPUserEntity *user = [self activeUserInContext:context];
|
||||
- (NSString *)exportSitesFor:(MPUserEntity *)user
|
||||
revealPasswords:(BOOL)revealPasswords
|
||||
askExportPassword:(NSString *( ^ )(NSString *userName))askExportPassword
|
||||
error:(__autoreleasing NSError **)error {
|
||||
|
||||
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 );
|
||||
MPMarshalledUser *exportUser = mpw_marshal_user( user.name.UTF8String,
|
||||
mpw_masterKeyProvider_str( askImportPassword( user.name ).UTF8String ), user.algorithm.version );
|
||||
NSString *masterPassword = askExportPassword( user.name );
|
||||
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->avatar = (unsigned int)user.avatar;
|
||||
exportUser->keyID = mpw_strdup( [user.keyID encodeHex].UTF8String );
|
||||
exportUser->defaultType = user.defaultType;
|
||||
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;
|
||||
if ([site isKindOfClass:[MPGeneratedSiteEntity class]])
|
||||
counter = ((MPGeneratedSiteEntity *)site).counter;
|
||||
MPMarshalledSite *exportSite = mpw_marshal_site( exportUser,
|
||||
site.name.UTF8String, site.type, counter, site.algorithm.version );
|
||||
exportSite->resultState = mpw_strdup( [site.algorithm exportPasswordForSite:site usingKey:self.key].UTF8String );
|
||||
exportSite->loginState = mpw_strdup( [site.algorithm exportLoginForSite:site usingKey:self.key].UTF8String );
|
||||
exportSite->loginType = site.loginGenerated? MPResultTypeTemplateName: MPResultTypeStatefulPersonal;
|
||||
MPMarshalledSite *exportSite = mpw_marshal_site( exportUser, site.name.UTF8String, site.type, counter, site.algorithm.version );
|
||||
exportSite->resultState = mpw_strdup( [site.algorithm exportPasswordForSite:site usingKey:key].UTF8String );
|
||||
exportSite->loginState = mpw_strdup( [site.algorithm exportLoginForSite:site usingKey:key].UTF8String );
|
||||
exportSite->loginType = site.loginGenerated || !exportSite->loginState? MPResultTypeTemplateName: MPResultTypeStatefulPersonal;
|
||||
exportSite->url = mpw_strdup( site.url.UTF8String );
|
||||
exportSite->uses = (unsigned int)site.uses;
|
||||
exportSite->lastUsed = (time_t)site.lastUsed.timeIntervalSince1970;
|
||||
@ -745,22 +762,26 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
mpw_marshal_question( exportSite, siteQuestion.keyword.UTF8String );
|
||||
}
|
||||
|
||||
MPMarshalledFile *exportFile = NULL;
|
||||
const char *export = mpw_marshal_write( MPMarshalFormatDefault, &exportFile, exportUser );
|
||||
NSString *exportedUser = nil;
|
||||
if (export && exportFile && exportFile->error.type == MPMarshalSuccess)
|
||||
exportedUser = [NSString stringWithCString:export encoding:NSUTF8StringEncoding];
|
||||
mpw_free_string( &export );
|
||||
|
||||
resultBlock( exportedUser, exportFile && exportFile->error.type == MPMarshalSuccess? nil:
|
||||
[NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
|
||||
@"type" : @(exportFile? exportFile->error.type: MPMarshalErrorInternal),
|
||||
NSLocalizedDescriptionKey: @(exportFile? exportFile->error.message: nil),
|
||||
}] );
|
||||
if (error)
|
||||
*error = exportFile && exportFile->error.type == MPMarshalSuccess? nil:
|
||||
[NSError errorWithDomain:MPErrorDomain code:MPErrorMarshalCode userInfo:@{
|
||||
@"type" : @(exportFile? exportFile->error.type: MPMarshalErrorInternal),
|
||||
NSLocalizedDescriptionKey: @(exportFile? exportFile->error.message: nil),
|
||||
}];
|
||||
|
||||
return exportedUser;
|
||||
}
|
||||
@finally {
|
||||
mpw_marshal_file_free( &exportFile );
|
||||
mpw_marshal_user_free( &exportUser );
|
||||
mpw_masterKeyProvider_free();
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -21,11 +21,13 @@
|
||||
@interface MPConfig : PearlConfig
|
||||
|
||||
@property(nonatomic, retain) NSNumber *sendInfo;
|
||||
@property(nonatomic, retain) NSNumber *sendInfoDecided;
|
||||
@property(nonatomic, retain) NSNumber *notificationsDecided;
|
||||
|
||||
@property(nonatomic, retain) NSNumber *rememberLogin;
|
||||
@property(nonatomic, retain) NSNumber *hidePasswords;
|
||||
@property(nonatomic, strong) NSNumber *siteAttacker;
|
||||
|
||||
@property(nonatomic, retain) NSNumber *checkInconsistency;
|
||||
|
||||
@property(nonatomic, strong) NSNumber *siteAttacker;
|
||||
|
||||
@end
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
@implementation MPConfig
|
||||
|
||||
@dynamic sendInfo, rememberLogin, checkInconsistency, hidePasswords, siteAttacker;
|
||||
@dynamic sendInfo, sendInfoDecided, notificationsDecided, rememberLogin, hidePasswords, siteAttacker, checkInconsistency;
|
||||
|
||||
- (id)init {
|
||||
|
||||
@ -29,13 +29,16 @@
|
||||
return nil;
|
||||
|
||||
[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( hidePasswords ) ) : @NO,
|
||||
NSStringFromSelector( @selector( checkInconsistency ) ): @NO,
|
||||
NSStringFromSelector( @selector( siteAttacker ) ) : @(MPAttacker1),
|
||||
NSStringFromSelector( @selector( rememberLogin ) ) : @NO,
|
||||
NSStringFromSelector( @selector( hidePasswords ) ) : @NO,
|
||||
NSStringFromSelector( @selector( siteAttacker ) ) : @(MPAttacker1),
|
||||
|
||||
NSStringFromSelector( @selector( checkInconsistency ) ) : @NO,
|
||||
NSStringFromSelector( @selector( askForReviews ) ) : @YES,
|
||||
}];
|
||||
|
||||
self.delegate = [MPAppDelegate_Shared get];
|
||||
|
@ -29,6 +29,8 @@
|
||||
if ([self hasChanges])
|
||||
[self performBlockAndWait:^{
|
||||
@try {
|
||||
[self processPendingChanges];
|
||||
|
||||
NSError *error = nil;
|
||||
if (!(success = [self save:&error]))
|
||||
MPError( error, @"While saving." );
|
||||
|
@ -20,11 +20,19 @@
|
||||
#import "base64.h"
|
||||
|
||||
// printf <secret> | openssl enc -[ed] -aes-128-cbc -a -A -K <appSecret> -iv 0
|
||||
NSString *appSecret = @"";
|
||||
NSString *appSalt = @"";
|
||||
NSString *sentryDSN = @"";
|
||||
NSString *countlyKey = @"";
|
||||
NSString *countlySalt = @"";
|
||||
#if TARGET_OS_IOS
|
||||
NSString *appSecret = @"946a6b12e6e6e004cc35bad1ea11478c";
|
||||
NSString *appSalt = @"uBcsbZeTB8TfSS7dDw4yUq6wMZD/2nREvR0mqzqsNXvv9guh+62hkt99ly6QcJ5n";
|
||||
NSString *sentryDSN = @"tmVjdMN9DpZ+0EIrrvHi44hWfaBkwrlrxjBkdeau2rDk+zlvgSdAZkAvNj7m1V+5NUR7i8Y/NumNKOaYlWJvPynEMJ4ZBvPepSbivgVvmr8=";
|
||||
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) {
|
||||
|
||||
|
@ -16,7 +16,9 @@
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
MP_LIBS_BEGIN
|
||||
#import <Sentry/Sentry.h>
|
||||
MP_LIBS_END
|
||||
|
||||
__BEGIN_DECLS
|
||||
extern NSString *const MPErrorDomain;
|
||||
@ -41,8 +43,9 @@ __END_DECLS
|
||||
\
|
||||
if (__error && [[MPConfig get].sendInfo boolValue]) { \
|
||||
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.fingerprint = @[ message_, __error.domain, @(__error.code) ]; \
|
||||
[SentrySDK captureEvent:event]; \
|
||||
} \
|
||||
__error; \
|
||||
|
@ -1,8 +1,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>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="16096"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="16097.3"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
||||
@ -12,9 +12,6 @@
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<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">
|
||||
<connections>
|
||||
<outlet property="createUserItem" destination="757" id="763"/>
|
||||
@ -66,7 +63,7 @@
|
||||
<action selector="exportSitesSecure:" target="494" id="LVH-es-imA"/>
|
||||
</connections>
|
||||
</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">
|
||||
<fragment content="Your passwords are not visible.">
|
||||
<attributes>
|
||||
@ -83,7 +80,7 @@
|
||||
<action selector="exportSitesReveal:" target="494" id="1IW-VT-Oeu"/>
|
||||
</connections>
|
||||
</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">
|
||||
<fragment content="Keep this file secure or delete it when you're done with it!">
|
||||
<attributes>
|
||||
@ -202,6 +199,7 @@
|
||||
</attributedString>
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="lhE-aV-1P4"/>
|
||||
<menuItem title="Diagnostics" id="GSN-f0-q7s">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
@ -210,7 +208,24 @@
|
||||
</menuItem>
|
||||
<menuItem title="Crash and usage information is anonymized and sent to development." enabled="NO" id="WfD-lX-C93">
|
||||
<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>
|
||||
<font key="NSFont" size="11" name="Helvetica"/>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="natural" lineBreakMode="wordWrapping" baseWritingDirection="natural" firstLineHeadIndent="8"/>
|
||||
@ -241,6 +256,9 @@
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="494" id="Slu-zT-yO4"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="140" y="23"/>
|
||||
</menu>
|
||||
</objects>
|
||||
|
@ -21,9 +21,9 @@
|
||||
#import "MPSitesWindowController.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) MPInitialWindowController *initialWindowController;
|
||||
@property(nonatomic, weak) IBOutlet NSMenuItem *lockItem;
|
||||
@ -42,6 +42,7 @@
|
||||
- (IBAction)showPasswordWindow:(id)sender;
|
||||
- (void)setLoginItemEnabled:(BOOL)enabled;
|
||||
- (IBAction)togglePreference:(id)sender;
|
||||
- (IBAction)copyIdentifier:(id)sender;
|
||||
- (IBAction)newUser:(NSMenuItem *)sender;
|
||||
- (IBAction)lock:(id)sender;
|
||||
- (IBAction)terminate:(id)sender;
|
||||
|
@ -22,14 +22,15 @@
|
||||
#import "MPSecrets.h"
|
||||
#import "mpw-marshal.h"
|
||||
|
||||
MP_LIBS_BEGIN
|
||||
#import <Carbon/Carbon.h>
|
||||
#import <ServiceManagement/ServiceManagement.h>
|
||||
#import <Sentry/Sentry.h>
|
||||
#import <Countly/Countly.h>
|
||||
MP_LIBS_END
|
||||
|
||||
#define LOGIN_HELPER_BUNDLE_ID @"com.lyndir.lhunath.MasterPassword.Mac.LoginHelper"
|
||||
|
||||
|
||||
@implementation MPMacAppDelegate
|
||||
|
||||
#pragma clang diagnostic push
|
||||
@ -69,23 +70,24 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
|
||||
@try {
|
||||
// Sentry
|
||||
[SentrySDK initWithOptions:@{
|
||||
@"dsn" : decrypt( sentryDSN ),
|
||||
[SentrySDK startWithOptions:@{
|
||||
@"dsn" : NilToNSNull( decrypt( sentryDSN ) ),
|
||||
#ifdef DEBUG
|
||||
@"debug" : @(YES),
|
||||
@"environment": @"Development",
|
||||
@"debug" : @(NO),
|
||||
@"environment" : @"Development",
|
||||
#elif PUBLIC
|
||||
@"debug" : @(NO),
|
||||
@"environment": @"Public",
|
||||
@"debug" : @(NO),
|
||||
@"environment" : @"Public",
|
||||
#else
|
||||
@"debug" : @(NO),
|
||||
@"environment": @"Private",
|
||||
@"debug" : @(NO),
|
||||
@"environment" : @"Private",
|
||||
#endif
|
||||
@"enabled" : [MPMacConfig get].sendInfo,
|
||||
@"enabled" : @([[MPMacConfig get].sendInfo boolValue] || ![[MPMacConfig get].sendInfoDecided boolValue]),
|
||||
@"enableAutoSessionTracking": @(YES),
|
||||
}];
|
||||
[[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) {
|
||||
PearlLogLevel level = PearlLogLevelWarn;
|
||||
if ([[MPConfig get].sendInfo boolValue])
|
||||
if ([[MPMacConfig get].sendInfo boolValue])
|
||||
level = PearlLogLevelDebug;
|
||||
|
||||
if (message.level >= level) {
|
||||
@ -131,11 +133,11 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
countlyConfig.deviceID = [PearlKeyChain deviceIdentifier];
|
||||
countlyConfig.secretSalt = decrypt( countlySalt );
|
||||
#if DEBUG
|
||||
countlyConfig.pushTestMode = CLYPushTestModeDevelopment;
|
||||
countlyConfig.enableDebug = YES;
|
||||
#elif ! PUBLIC
|
||||
countlyConfig.pushTestMode = CLYPushTestModeTestFlightOrAdHoc;
|
||||
countlyConfig.pushTestMode = CLYPushTestModeDevelopment;
|
||||
#elif !PUBLIC
|
||||
countlyConfig.enableDebug = NO;
|
||||
countlyConfig.pushTestMode = CLYPushTestModeTestFlightOrAdHoc;
|
||||
#endif
|
||||
[Countly.sharedInstance startWithConfig:countlyConfig];
|
||||
}
|
||||
@ -144,7 +146,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
}
|
||||
|
||||
// Setup delegates and listeners.
|
||||
[MPConfig get].delegate = self;
|
||||
[MPMacConfig get].delegate = self;
|
||||
__weak id weakSelf = self;
|
||||
[self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
|
||||
dispatch_async( dispatch_get_main_queue(), ^{
|
||||
@ -158,12 +160,10 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
} forKeyPath:@"activeUser" options:0 context:nil];
|
||||
|
||||
// Status item.
|
||||
self.statusView = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
|
||||
self.statusView.image = [NSImage imageNamed:@"menu-icon"];
|
||||
self.statusView.image.template = YES;
|
||||
self.statusView.menu = self.statusMenu;
|
||||
self.statusView.target = self;
|
||||
self.statusView.action = @selector( showMenu );
|
||||
self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
|
||||
self.statusItem.menu = self.statusMenu;
|
||||
self.statusItem.button.image = [NSImage imageNamed:@"menu-icon"];
|
||||
self.statusItem.button.image.template = YES;
|
||||
|
||||
PearlAddNotificationObserver( NSPersistentStoreCoordinatorStoresWillChangeNotification, self.storeCoordinator, nil,
|
||||
^(id self, NSNotification *note) {
|
||||
@ -203,11 +203,13 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
.window makeKeyAndOrderFront:self];
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
}
|
||||
|
||||
[self tryNotifications];
|
||||
}
|
||||
|
||||
- (void)applicationWillResignActive:(NSNotification *)notification {
|
||||
|
||||
if (![[MPConfig get].rememberLogin boolValue])
|
||||
if (![[MPMacConfig get].rememberLogin boolValue])
|
||||
[self lock:nil];
|
||||
}
|
||||
|
||||
@ -228,6 +230,42 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
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
|
||||
|
||||
- (void)setActiveUser:(MPUserEntity *)activeUser {
|
||||
@ -371,11 +409,11 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
- (IBAction)togglePreference:(id)sender {
|
||||
|
||||
if (sender == self.diagnosticsItem)
|
||||
[MPConfig get].sendInfo = @(self.diagnosticsItem.state != NSOnState);
|
||||
[MPMacConfig get].sendInfo = @(self.diagnosticsItem.state != NSOnState);
|
||||
if (sender == self.hidePasswordsItem)
|
||||
[MPConfig get].hidePasswords = @(self.hidePasswordsItem.state != NSOnState);
|
||||
[MPMacConfig get].hidePasswords = @(self.hidePasswordsItem.state != NSOnState);
|
||||
if (sender == self.rememberPasswordItem)
|
||||
[MPConfig get].rememberLogin = @(self.rememberPasswordItem.state != NSOnState);
|
||||
[MPMacConfig get].rememberLogin = @(self.rememberPasswordItem.state != NSOnState);
|
||||
if (sender == self.openAtLoginItem)
|
||||
[self setLoginItemEnabled:self.openAtLoginItem.state != NSOnState];
|
||||
if (sender == self.showFullScreenItem) {
|
||||
@ -397,13 +435,19 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
[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 {
|
||||
|
||||
NSAlert *alert = [NSAlert new];
|
||||
[alert setMessageText:@"New User"];
|
||||
[alert setInformativeText:@"To begin, enter your full name.\n\n"
|
||||
@"IMPORTANT: Enter your name correctly, including the right capitalization, "
|
||||
@"as you would on an official document."];
|
||||
@"IMPORTANT: Enter your name correctly, including the right capitalization, "
|
||||
@"as you would on an official document."];
|
||||
[alert addButtonWithTitle:@"Create User"];
|
||||
[alert addButtonWithTitle:@"Cancel"];
|
||||
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 {
|
||||
|
||||
[self.statusView popUpStatusItemMenu:self.statusView.menu];
|
||||
[[self.statusItem button] performClick:sender];
|
||||
}
|
||||
|
||||
- (IBAction)showPasswordWindow:(id)sender {
|
||||
@ -508,15 +552,6 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
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];
|
||||
[exportDateFormatter setDateFormat:@"yyyy'-'MM'-'dd"];
|
||||
|
||||
@ -541,43 +576,46 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
if ([savePanel runModal] == NSFileHandlingPanelCancelButton)
|
||||
return;
|
||||
|
||||
[self exportSitesRevealPasswords:revealPasswords
|
||||
askExportPassword:^NSString *(NSString *userName) {
|
||||
return PearlMainQueueAwait( ^id {
|
||||
NSAlert *alert = [NSAlert new];
|
||||
[alert addButtonWithTitle:@"Import"];
|
||||
[alert addButtonWithTitle:@"Cancel"];
|
||||
alert.messageText = strf( @"Master Password For\n%@", userName );
|
||||
alert.informativeText = @"Enter the current master password for this user.";
|
||||
alert.accessoryView = [[NSSecureTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )];
|
||||
[alert layout];
|
||||
if ([alert runModal] == NSAlertFirstButtonReturn)
|
||||
return ((NSTextField *)alert.accessoryView).stringValue;
|
||||
else
|
||||
return nil;
|
||||
} );
|
||||
} result:^(NSString *mpsites, NSError *error) {
|
||||
if (!mpsites || error) {
|
||||
PearlMainQueue( ^{
|
||||
[[NSAlert alertWithError:MPError( error, @"Failed to export mpsites." )] runModal];
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
NSError *error = nil;
|
||||
NSString *exportedUser = [self exportSitesFor:[self activeUserInContext:context] revealPasswords:revealPasswords askExportPassword:
|
||||
^NSString *(NSString *userName) {
|
||||
return PearlMainQueueAwait( ^id {
|
||||
NSAlert *alert = [NSAlert new];
|
||||
[alert addButtonWithTitle:@"Export"];
|
||||
[alert addButtonWithTitle:@"Cancel"];
|
||||
alert.messageText = strf( @"Master Password For\n%@", userName );
|
||||
alert.informativeText = @"Enter the current master password for this user.";
|
||||
alert.accessoryView = [[NSSecureTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )];
|
||||
[alert layout];
|
||||
if ([alert runModal] == NSAlertFirstButtonReturn)
|
||||
return ((NSTextField *)alert.accessoryView).stringValue;
|
||||
else
|
||||
return nil;
|
||||
} );
|
||||
return;
|
||||
}
|
||||
} error:&error];
|
||||
|
||||
NSError *coordinateError = nil;
|
||||
[[[NSFileCoordinator alloc] initWithFilePresenter:nil]
|
||||
coordinateWritingItemAtURL:savePanel.URL options:0 error:&coordinateError byAccessor:^(NSURL *newURL) {
|
||||
NSError *writeError = nil;
|
||||
if (![mpsites 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];
|
||||
} );
|
||||
}];
|
||||
if (error)
|
||||
PearlMainQueue( ^{
|
||||
[[NSAlert alertWithError:MPError( error, @"Failed to export mpsites." )] runModal];
|
||||
} );
|
||||
if (!exportedUser)
|
||||
return;
|
||||
|
||||
NSError *coordinateError = nil;
|
||||
[[[NSFileCoordinator alloc] initWithFilePresenter:nil]
|
||||
coordinateWritingItemAtURL:savePanel.URL options:0 error:&coordinateError byAccessor:^(NSURL *newURL) {
|
||||
NSError *writeError = nil;
|
||||
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 {
|
||||
@ -614,7 +652,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
self.deleteUserItem.toolTip = mainActiveUser? nil: @"First select the user to delete.";
|
||||
|
||||
NSError *error = nil;
|
||||
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
|
||||
NSFetchRequest *fetchRequest = [MPUserEntity fetchRequest];
|
||||
fetchRequest.sortDescriptors = @[ [NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO] ];
|
||||
NSArray *users = [mainContext executeFetchRequest:fetchRequest error:&error];
|
||||
if (!users)
|
||||
@ -650,13 +688,6 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
[self updateMenuItems];
|
||||
}
|
||||
|
||||
- (void)showMenu {
|
||||
|
||||
[self updateMenuItems];
|
||||
|
||||
[self.statusView popUpStatusItemMenu:self.statusView.menu];
|
||||
}
|
||||
|
||||
- (void)updateMenuItems {
|
||||
|
||||
MPUserEntity *activeUser = [self activeUserForMainThread];
|
||||
@ -685,10 +716,13 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
}
|
||||
|
||||
BOOL loginItemEnabled = [self loginItemEnabled];
|
||||
self.initialWindowController.openAtLoginButton.state = loginItemEnabled? NSOnState: NSOffState;
|
||||
self.openAtLoginItem.state = loginItemEnabled? NSOnState: NSOffState;
|
||||
self.showFullScreenItem.state = [[MPMacConfig get].fullScreen boolValue]? NSOnState: NSOffState;
|
||||
self.initialWindowController.openAtLoginButton.state = loginItemEnabled? NSOnState: NSOffState;
|
||||
self.rememberPasswordItem.state = [[MPConfig get].rememberLogin boolValue]? NSOnState: NSOffState;
|
||||
self.rememberPasswordItem.state = [[MPMacConfig 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;
|
||||
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
|
||||
|
||||
- (void)didUpdateConfigForKey:(SEL)configKey fromValue:(id)oldValue {
|
||||
@ -719,47 +760,41 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
|
||||
PearlMainQueue( ^{
|
||||
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 ) )])
|
||||
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 ) )])
|
||||
self.rememberPasswordItem.state = [[MPConfig get].rememberLogin boolValue]? NSOnState: NSOffState;
|
||||
self.rememberPasswordItem.state = [[MPMacConfig get].rememberLogin boolValue]? NSOnState: NSOffState;
|
||||
} );
|
||||
|
||||
// Send info
|
||||
if ([[MPConfig get].sendInfo boolValue]) {
|
||||
PearlMainQueue( ^{
|
||||
[Countly.sharedInstance giveConsentForAllFeatures];
|
||||
[Countly.sharedInstance askForNotificationPermission];
|
||||
});
|
||||
|
||||
NSArray *countlyFeatures = @[
|
||||
CLYConsentSessions, CLYConsentEvents, CLYConsentUserDetails, CLYConsentCrashReporting, CLYConsentViewTracking, CLYConsentStarRating
|
||||
];
|
||||
if ([[MPMacConfig get].sendInfo boolValue] || ![[MPMacConfig get].sendInfoDecided boolValue]) {
|
||||
if ([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 configureScope:^(SentryScope *scope) {
|
||||
for (NSString *pref in prefs.allKeys)
|
||||
[scope setExtraValue:prefs[pref] forKey:pref];
|
||||
[scope setExtraValue:[MPMacConfig get].rememberLogin forKey:@"rememberLogin"];
|
||||
[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 {
|
||||
[Countly.sharedInstance cancelConsentForFeatures:countlyFeatures];
|
||||
[SentrySDK.currentHub getClient].options.enabled = @NO;
|
||||
PearlMainQueue( ^{
|
||||
[Countly.sharedInstance cancelConsentForAllFeatures];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@
|
||||
@property(nonatomic, readonly) BOOL stored;
|
||||
@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;
|
||||
- (MPSiteEntity *)entityInContext:(NSManagedObjectContext *)moc;
|
||||
|
||||
|
@ -31,12 +31,12 @@
|
||||
|
||||
@implementation MPSiteModel
|
||||
|
||||
- (instancetype)initWithEntity:(MPSiteEntity *)entity fuzzyGroups:(NSArray *)fuzzyGroups {
|
||||
- (instancetype)initWithEntity:(MPSiteEntity *)entity queryGroups:(NSArray *)queryGroups {
|
||||
|
||||
if (!(self = [super init]))
|
||||
return nil;
|
||||
|
||||
[self setEntity:entity fuzzyGroups:fuzzyGroups];
|
||||
[self setEntity:entity queryGroups:queryGroups];
|
||||
self.initialized = YES;
|
||||
|
||||
return self;
|
||||
@ -53,23 +53,25 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setEntity:(MPSiteEntity *)entity fuzzyGroups:(NSArray *)fuzzyGroups {
|
||||
- (void)setEntity:(MPSiteEntity *)entity queryGroups:(NSArray *)queryGroups {
|
||||
|
||||
if ([self.entityOID isEqual:entity.permanentObjectID])
|
||||
return;
|
||||
self.entityOID = entity.permanentObjectID;
|
||||
|
||||
NSString *siteName = entity.name;
|
||||
NSMutableAttributedString *attributedSiteName = [[NSMutableAttributedString alloc] initWithString:siteName];
|
||||
for (NSUInteger f = 0, s = (NSUInteger)-1; f < [fuzzyGroups count]; ++f) {
|
||||
s = [siteName rangeOfString:fuzzyGroups[f] options:NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch
|
||||
range:NSMakeRange( s + 1, [siteName length] - (s + 1) )].location;
|
||||
if (s == NSNotFound)
|
||||
break;
|
||||
NSMutableAttributedString *attributedSiteName = [[NSMutableAttributedString alloc] initWithString:siteName?: @""];
|
||||
if ([attributedSiteName length])
|
||||
for (NSUInteger f = 0, s = 0; f < [queryGroups count]; ++f, ++s) {
|
||||
s = [siteName rangeOfString:queryGroups[f] options:NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch
|
||||
range:NSMakeRange( s, [siteName length] - s )].location;
|
||||
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];
|
||||
paragraphStyle.alignment = NSCenterTextAlignment;
|
||||
[attributedSiteName addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange( 0, [siteName length] )];
|
||||
@ -179,7 +181,8 @@
|
||||
if (algorithmVersion == self.algorithm.version)
|
||||
return;
|
||||
[self willChangeValueForKey:@"outdated"];
|
||||
self.algorithm = MPAlgorithmForVersion( algorithmVersion )?: self.algorithm;
|
||||
self.algorithm =
|
||||
MPAlgorithmForVersion( MIN( MPAlgorithmVersionLast, MAX( MPAlgorithmVersionFirst, algorithmVersion ) ) )?: self.algorithm;
|
||||
[self didChangeValueForKey:@"outdated"];
|
||||
|
||||
if (self.entityOID)
|
||||
@ -210,12 +213,12 @@
|
||||
|
||||
- (BOOL)generated {
|
||||
|
||||
return self.type & MPResultTypeClassTemplate;
|
||||
return (self.type & MPResultTypeClassTemplate) == MPResultTypeClassTemplate;
|
||||
}
|
||||
|
||||
- (BOOL)stored {
|
||||
|
||||
return self.type & MPResultTypeClassStateful;
|
||||
return (self.type & MPResultTypeClassStateful) == MPResultTypeClassStateful;
|
||||
}
|
||||
|
||||
- (BOOL)transient {
|
||||
|
@ -17,6 +17,8 @@
|
||||
//==============================================================================
|
||||
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#import <Countly/Countly.h>
|
||||
#import <UserNotifications/UserNotifications.h>
|
||||
#import "MPSitesWindowController.h"
|
||||
#import "MPMacAppDelegate.h"
|
||||
#import "MPAppDelegate_Store.h"
|
||||
@ -42,13 +44,27 @@
|
||||
prof_rewind( @"replaceFonts" );
|
||||
|
||||
PearlAddNotificationObserver( NSWindowDidBecomeKeyNotification, self.window, [NSOperationQueue mainQueue],
|
||||
^(id host, NSNotification *note) {
|
||||
(^(id host, NSNotification *note) {
|
||||
prof_new( @"didBecomeKey" );
|
||||
[self.window makeKeyAndOrderFront:nil];
|
||||
prof_rewind( @"fadeIn" );
|
||||
[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],
|
||||
^(id host, NSNotification *note) {
|
||||
NSWindow *sheet = [self.window attachedSheet];
|
||||
@ -81,7 +97,16 @@
|
||||
self.siteTable.superview.superview.layer.mask = self.siteGradient;
|
||||
|
||||
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 {
|
||||
@ -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;
|
||||
NSArray *types = [site.algorithm allTypes];
|
||||
@ -487,13 +557,19 @@
|
||||
[self copyContent:self.shiftPressed? selectedSite.loginName: selectedSite.content];
|
||||
[NSApp hide:nil];
|
||||
|
||||
NSUserNotification *notification = [NSUserNotification new];
|
||||
notification.title = @"Password Copied";
|
||||
if (selectedSite.loginName.length)
|
||||
notification.subtitle = strf( @"%@ at %@", selectedSite.loginName, selectedSite.name );
|
||||
else
|
||||
notification.subtitle = selectedSite.name;
|
||||
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
|
||||
if (@available( macOS 10.14, * )) {
|
||||
UNMutableNotificationContent *notification = [UNMutableNotificationContent new];
|
||||
notification.title = self.shiftPressed? @"Login Copied": @"Password Copied";
|
||||
if (selectedSite.loginName.length)
|
||||
notification.subtitle = strf( @"%@ at %@", selectedSite.loginName, selectedSite.name );
|
||||
else
|
||||
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 {
|
||||
@ -535,26 +611,24 @@
|
||||
return;
|
||||
}
|
||||
|
||||
static NSRegularExpression *fuzzyRE;
|
||||
static dispatch_once_t once = 0;
|
||||
dispatch_once( &once, ^{
|
||||
fuzzyRE = [NSRegularExpression regularExpressionWithPattern:@"(.)" options:0 error:nil];
|
||||
} );
|
||||
|
||||
prof_new( @"updateSites" );
|
||||
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" );
|
||||
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) {
|
||||
prof_rewind( @"moc" );
|
||||
|
||||
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
|
||||
NSFetchRequest *fetchRequest = [MPSiteEntity fetchRequest];
|
||||
fetchRequest.sortDescriptors = @[ [[NSSortDescriptor alloc] initWithKey:@"lastUsed" ascending:NO] ];
|
||||
fetchRequest.predicate =
|
||||
[NSPredicate predicateWithFormat:@"name LIKE[cd] %@ AND user == %@", queryPattern, [MPMacAppDelegate get].activeUserOID];
|
||||
@ -572,7 +646,7 @@
|
||||
BOOL exact = NO;
|
||||
NSMutableArray *newSites = [NSMutableArray arrayWithCapacity:[siteResults count]];
|
||||
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];
|
||||
}
|
||||
prof_rewind( @"newSites: %u, exact: %d", (uint)[siteResults count], exact );
|
||||
|
@ -31,20 +31,20 @@
|
||||
<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"/>
|
||||
<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"/>
|
||||
<subviews>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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">
|
||||
<size key="offset" width="0.0" height="1"/>
|
||||
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -72,7 +72,7 @@
|
||||
</connections>
|
||||
</textField>
|
||||
<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">
|
||||
<size key="offset" width="0.0" height="1"/>
|
||||
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -108,13 +108,13 @@
|
||||
</connections>
|
||||
</textField>
|
||||
<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">
|
||||
<size key="offset" width="0.0" height="1"/>
|
||||
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
||||
</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">
|
||||
<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="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
@ -136,18 +136,18 @@
|
||||
</connections>
|
||||
</textField>
|
||||
<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">
|
||||
<size key="offset" width="0.0" height="1"/>
|
||||
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
||||
</shadow>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" id="9c4-NI-NM0">
|
||||
<font key="font" size="12" name="HelveticaNeue"/>
|
||||
<string key="title">– When you create an account with a site, use Master Password to create a password for it.
|
||||
– For accounts you already have, change the password to that created by Master Password for it.
|
||||
– To get a password, enter the site's bare 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*.
|
||||
– It's OK to share your site passwords. They can be changed if necessary.</string>
|
||||
<font key="font" metaFont="message" size="11"/>
|
||||
<string key="title">⑴ When you create an account on a site, open Master Password to create your account password.
|
||||
⑵ Consider changing all your existing account passwords to the password Master Password creates for those sites.
|
||||
⑶ To get the password for a site, just enter its domain name in the "site name" field (eg. "apple.com").
|
||||
⑷ When chosing a master password, make it easy but long (eg. a short sentence).
|
||||
⑸ 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="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
@ -169,7 +169,7 @@
|
||||
</connections>
|
||||
</textField>
|
||||
<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">
|
||||
<size key="offset" width="0.0" height="1"/>
|
||||
<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 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 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="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"/>
|
||||
@ -209,14 +210,13 @@
|
||||
<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="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="centerX" secondItem="OaQ-of-zmb" secondAttribute="centerX" id="mu2-se-Mtn"/>
|
||||
<constraint firstAttribute="centerY" secondItem="sYt-eL-uwt" secondAttribute="centerY" id="zLS-QG-MKS"/>
|
||||
</constraints>
|
||||
</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">
|
||||
<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">
|
||||
<rect key="frame" x="0.0" y="0.0" width="512" height="177"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
@ -251,7 +251,7 @@
|
||||
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
||||
</shadow>
|
||||
<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="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
@ -310,7 +310,7 @@
|
||||
</connections>
|
||||
</scrollView>
|
||||
<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>
|
||||
<userDefinedRuntimeAttribute type="color" keyPath="startingColor">
|
||||
<color key="value" red="0.11764705882352941" green="0.11764705882352941" blue="0.11764705882352941" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
@ -324,7 +324,7 @@
|
||||
</userDefinedRuntimeAttributes>
|
||||
</customView>
|
||||
<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">
|
||||
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
||||
</shadow>
|
||||
@ -339,7 +339,7 @@
|
||||
</connections>
|
||||
</button>
|
||||
<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">
|
||||
<size key="offset" width="0.0" height="1"/>
|
||||
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -358,7 +358,7 @@
|
||||
</connections>
|
||||
</textField>
|
||||
<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">
|
||||
<size key="offset" width="0.0" height="1"/>
|
||||
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -370,7 +370,7 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<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">
|
||||
<size key="offset" width="0.0" height="1"/>
|
||||
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -405,7 +405,7 @@
|
||||
</connections>
|
||||
</secureTextField>
|
||||
<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">
|
||||
<size key="offset" width="0.0" height="1"/>
|
||||
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -444,13 +444,13 @@
|
||||
</connections>
|
||||
</textField>
|
||||
<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">
|
||||
<size key="offset" width="0.0" height="1"/>
|
||||
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
||||
</shadow>
|
||||
<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="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
@ -471,13 +471,13 @@
|
||||
</connections>
|
||||
</textField>
|
||||
<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">
|
||||
<size key="offset" width="0.0" height="1"/>
|
||||
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
||||
</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">
|
||||
<font key="font" size="11" name="HelveticaNeue"/>
|
||||
<font key="font" metaFont="miniSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
@ -499,7 +499,7 @@
|
||||
</connections>
|
||||
</textField>
|
||||
<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>
|
||||
<constraint firstAttribute="width" constant="512" id="rW7-Vq-4Xy"/>
|
||||
</constraints>
|
||||
@ -508,7 +508,7 @@
|
||||
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
||||
</shadow>
|
||||
<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="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</searchFieldCell>
|
||||
@ -519,13 +519,13 @@
|
||||
</connections>
|
||||
</searchField>
|
||||
<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">
|
||||
<size key="offset" width="0.0" height="1"/>
|
||||
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
||||
</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">
|
||||
<font key="font" size="11" name="HelveticaNeue"/>
|
||||
<font key="font" metaFont="miniSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
@ -542,13 +542,13 @@
|
||||
</connections>
|
||||
</textField>
|
||||
<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">
|
||||
<size key="offset" width="0.0" height="1"/>
|
||||
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
||||
</shadow>
|
||||
<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.
|
||||
Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
|
||||
<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>
|
||||
</textField>
|
||||
<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>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="1Qo-iG-CQt">
|
||||
<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>
|
||||
</button>
|
||||
<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>
|
||||
<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">
|
||||
<size key="offset" width="0.0" height="1"/>
|
||||
<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>
|
||||
</textField>
|
||||
<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">
|
||||
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
||||
</shadow>
|
||||
@ -659,6 +659,35 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
|
||||
</binding>
|
||||
</connections>
|
||||
</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">
|
||||
<rect key="frame" x="0.0" y="85" width="177" height="19"/>
|
||||
<shadow key="shadow">
|
||||
@ -686,26 +715,26 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
|
||||
</connections>
|
||||
</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">
|
||||
<rect key="frame" x="70" y="56" width="36" height="22"/>
|
||||
<rect key="frame" x="71" y="56" width="34" height="22"/>
|
||||
<subviews>
|
||||
<stepper horizontalHuggingPriority="750" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="mcq-qD-yte">
|
||||
<rect key="frame" x="-3" y="-3" width="19" height="28"/>
|
||||
<shadow key="shadow">
|
||||
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
||||
</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>
|
||||
<binding destination="mcS-ik-b0n" name="value" keyPath="selection.algorithmVersion" id="GyA-hK-6cD"/>
|
||||
</connections>
|
||||
</stepper>
|
||||
<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">
|
||||
<size key="offset" width="0.0" height="1"/>
|
||||
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
||||
</shadow>
|
||||
<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="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="calibratedRGB"/>
|
||||
</textFieldCell>
|
||||
@ -740,26 +769,26 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
|
||||
</connections>
|
||||
</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">
|
||||
<rect key="frame" x="74" y="26" width="28" height="22"/>
|
||||
<rect key="frame" x="75" y="26" width="27" height="22"/>
|
||||
<subviews>
|
||||
<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"/>
|
||||
<shadow key="shadow">
|
||||
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
||||
</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>
|
||||
<binding destination="mcS-ik-b0n" name="value" keyPath="selection.counter" id="qmm-6z-boy"/>
|
||||
</connections>
|
||||
</stepper>
|
||||
<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">
|
||||
<size key="offset" width="0.0" height="1"/>
|
||||
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
||||
</shadow>
|
||||
<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="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="calibratedRGB"/>
|
||||
</textFieldCell>
|
||||
@ -805,7 +834,7 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
|
||||
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
|
||||
</buttonCell>
|
||||
<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">
|
||||
<dictionary key="options">
|
||||
<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"/>
|
||||
</visibilityPriorities>
|
||||
<customSpacing>
|
||||
<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"/>
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
<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>
|
||||
</stackView>
|
||||
<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">
|
||||
<size key="offset" width="0.0" height="1"/>
|
||||
<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>
|
||||
</textField>
|
||||
<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">
|
||||
<size key="offset" width="0.0" height="1"/>
|
||||
<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>
|
||||
</textField>
|
||||
<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">
|
||||
<size key="offset" width="0.0" height="1"/>
|
||||
<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>
|
||||
</textField>
|
||||
<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">
|
||||
<size key="offset" width="0.0" height="1"/>
|
||||
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
||||
</shadow>
|
||||
<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="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
@ -1016,7 +1047,7 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
|
||||
</connections>
|
||||
</textField>
|
||||
<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">
|
||||
<size key="offset" width="0.0" height="1"/>
|
||||
<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>
|
||||
</textField>
|
||||
<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">
|
||||
<size key="offset" width="0.0" height="1"/>
|
||||
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
||||
</shadow>
|
||||
<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="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
@ -1072,13 +1103,13 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit.</string>
|
||||
</connections>
|
||||
</textField>
|
||||
<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">
|
||||
<size key="offset" width="0.0" height="1"/>
|
||||
<color key="color" name="controlLightHighlightColor" catalog="System" colorSpace="catalog"/>
|
||||
</shadow>
|
||||
<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="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
|
@ -102,7 +102,7 @@
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = YES;
|
||||
CLASSPREFIX = MP;
|
||||
LastUpgradeCheck = 1140;
|
||||
LastUpgradeCheck = 1200;
|
||||
ORGANIZATIONNAME = "Maarten Billemont";
|
||||
TargetAttributes = {
|
||||
DAD9B5C0176299B9001835F9 = {
|
||||
@ -185,6 +185,7 @@
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
||||
@ -271,6 +272,7 @@
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
||||
|
@ -1,34 +1,36 @@
|
||||
<?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">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSBackgroundOnly</key>
|
||||
<true/>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2013 Maarten Billemont. All rights reserved.</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.productivity</string>
|
||||
<key>LSBackgroundOnly</key>
|
||||
<true/>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2013 Maarten Billemont. All rights reserved.</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
@ -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
|
@ -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
|
@ -58,22 +58,6 @@ const long MPAvatarAdd = 10000;
|
||||
self.avatarImageView.layer.masksToBounds = NO;
|
||||
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"];
|
||||
toShadowOpacityAnimation.toValue = @0.2f;
|
||||
toShadowOpacityAnimation.duration = 0.5f;
|
||||
@ -91,6 +75,22 @@ const long MPAvatarAdd = 10000;
|
||||
self.targetedShadowAnimation.duration = MAXFLOAT;
|
||||
self.avatarImageView.layer.shadowColor = [UIColor whiteColor].CGColor;
|
||||
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 {
|
||||
|
@ -31,7 +31,6 @@
|
||||
[super viewDidLoad];
|
||||
|
||||
self.mode = MPCombinedModeUserSelection;
|
||||
[self performSegueWithIdentifier:@"users" sender:self];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
@ -102,8 +101,6 @@
|
||||
#pragma mark - Actions
|
||||
|
||||
- (IBAction)unwindToCombined:(UIStoryboardSegue *)sender {
|
||||
|
||||
dbg( @"unwindToCombined:%@", sender );
|
||||
}
|
||||
|
||||
#pragma mark - State
|
||||
@ -127,7 +124,7 @@
|
||||
|
||||
switch (self.mode) {
|
||||
case MPCombinedModeUserSelection: {
|
||||
self.usersVC.view.userInteractionEnabled = YES;
|
||||
self.usersVC.userSelectionContainer.userInteractionEnabled = YES;
|
||||
[self.usersVC setActive:YES animated:animated];
|
||||
if (self.sitesVC) {
|
||||
MPSitesSegue *segue = [[MPSitesSegue alloc] initWithIdentifier:@"passwords" source:self.sitesVC destination:self];
|
||||
@ -137,7 +134,7 @@
|
||||
break;
|
||||
}
|
||||
case MPCombinedModePasswordSelection: {
|
||||
self.usersVC.view.userInteractionEnabled = NO;
|
||||
self.usersVC.userSelectionContainer.userInteractionEnabled = NO;
|
||||
[self.usersVC setActive:NO animated:animated];
|
||||
[self performSegueWithIdentifier:@"passwords" sender:@{ @"animated": @(animated) }];
|
||||
break;
|
||||
|
@ -30,8 +30,10 @@
|
||||
@property(weak, nonatomic) IBOutlet UIActivityIndicatorView *activity;
|
||||
@property(weak, nonatomic) IBOutlet UIButton *passwordButton;
|
||||
@property(weak, nonatomic) IBOutlet UIView *tipContainer;
|
||||
@property(weak, nonatomic) IBOutlet UIButton *deviceButton;
|
||||
|
||||
- (IBAction)controlChanged:(UIControl *)control;
|
||||
- (IBAction)copyPassword:(UITapGestureRecognizer *)recognizer;
|
||||
- (IBAction)copyDevice:(id)sender;
|
||||
|
||||
@end
|
||||
|
@ -38,6 +38,8 @@
|
||||
|
||||
self.view.backgroundColor = [UIColor clearColor];
|
||||
self.dialogView.layer.cornerRadius = 5;
|
||||
|
||||
[self.deviceButton setTitle:[PearlKeyChain deviceIdentifier] forState:UIControlStateNormal];
|
||||
}
|
||||
|
||||
- (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
|
||||
|
||||
- (void)updateKey {
|
||||
|
@ -17,7 +17,6 @@
|
||||
//==============================================================================
|
||||
|
||||
#import "MPGuideViewController.h"
|
||||
#import "markdown_lib.h"
|
||||
#import "NSString+MPMarkDown.h"
|
||||
|
||||
@interface MPGuideStep : NSObject
|
||||
|
@ -18,7 +18,6 @@
|
||||
|
||||
#import "MPLogsViewController.h"
|
||||
#import "MPiOSAppDelegate.h"
|
||||
#import "MPAppDelegate_Store.h"
|
||||
|
||||
@implementation MPLogsViewController
|
||||
|
||||
@ -52,6 +51,17 @@
|
||||
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 {
|
||||
|
||||
BOOL traceEnabled = (BOOL)self.levelControl.selectedSegmentIndex;
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
#import "MPNavigationController.h"
|
||||
#import "MPWebViewController.h"
|
||||
#import "MPiOSAppDelegate.h"
|
||||
|
||||
@implementation MPNavigationController
|
||||
|
||||
@ -29,6 +30,16 @@
|
||||
[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 {
|
||||
|
||||
if ([segue.identifier isEqualToString:@"web"])
|
||||
|
@ -33,13 +33,6 @@
|
||||
self.dismissSegueByButton = [NSMutableDictionary dictionary];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
|
||||
[super viewDidLoad];
|
||||
|
||||
[self performSegueWithIdentifier:@"root" sender:self];
|
||||
}
|
||||
|
||||
- (UIViewController *)childViewControllerForStatusBarStyle {
|
||||
|
||||
return [self.childViewControllers lastObject];
|
||||
|
@ -52,6 +52,17 @@
|
||||
[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 {
|
||||
|
||||
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserForMainThread];
|
||||
|
@ -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
|
@ -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
|
@ -27,7 +27,7 @@ typedef NS_ENUM ( NSUInteger, MPSiteCellMode ) {
|
||||
|
||||
@interface MPSiteCell : MPCell<UIScrollViewDelegate, UITextFieldDelegate>
|
||||
|
||||
@property(nonatomic) NSArray *fuzzyGroups;
|
||||
@property(nonatomic) NSArray *queryGroups;
|
||||
|
||||
- (void)setSite:(MPSiteEntity *)site animated:(BOOL)animated;
|
||||
- (void)setTransientSite:(NSString *)siteName animated:(BOOL)animated;
|
||||
|
@ -135,7 +135,7 @@
|
||||
[super prepareForReuse];
|
||||
|
||||
self.siteOID = nil;
|
||||
self.fuzzyGroups = nil;
|
||||
self.queryGroups = nil;
|
||||
self.transientSite = nil;
|
||||
self.mode = MPPasswordCellModePassword;
|
||||
[self updateAnimated:NO];
|
||||
@ -150,11 +150,11 @@
|
||||
|
||||
#pragma mark - State
|
||||
|
||||
- (void)setFuzzyGroups:(NSArray *)fuzzyGroups {
|
||||
- (void)setQueryGroups:(NSArray *)queryGroups {
|
||||
|
||||
if (self.fuzzyGroups == fuzzyGroups)
|
||||
if (self.queryGroups == queryGroups)
|
||||
return;
|
||||
_fuzzyGroups = fuzzyGroups;
|
||||
_queryGroups = queryGroups;
|
||||
|
||||
[self updateSiteName:[self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]]];
|
||||
}
|
||||
@ -263,6 +263,7 @@
|
||||
|
||||
UIAlertController *controller = [UIAlertController alertControllerWithTitle:strf( @"Delete %@?", site.name ) message:nil
|
||||
preferredStyle:UIAlertControllerStyleActionSheet];
|
||||
[controller.popoverPresentationController setSourceView:sender];
|
||||
[controller addAction:[UIAlertAction actionWithTitle:@"Delete Site" style:UIAlertActionStyleDestructive
|
||||
handler:^(UIAlertAction *_Nonnull action) {
|
||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
@ -284,6 +285,7 @@
|
||||
MPSiteEntity *mainSite = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
|
||||
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Change Password Type" message:nil
|
||||
preferredStyle:UIAlertControllerStyleActionSheet];
|
||||
[controller.popoverPresentationController setSourceView:sender];
|
||||
for (NSNumber *typeNumber in [mainSite.algorithm allTypes]) {
|
||||
MPResultType type = (MPResultType)[typeNumber unsignedIntegerValue];
|
||||
NSString *typeName = [mainSite.algorithm nameOfType:type];
|
||||
@ -425,7 +427,7 @@
|
||||
if (!site || ![site isKindOfClass:[MPGeneratedSiteEntity class]])
|
||||
return;
|
||||
|
||||
((MPGeneratedSiteEntity *)site).counter = 1;
|
||||
((MPGeneratedSiteEntity *)site).counter = MPCounterValueInitial;
|
||||
[context saveToStore];
|
||||
|
||||
[PearlOverlay showTemporaryOverlayWithTitle:@"Counter Reset" dismissAfter:2];
|
||||
@ -433,7 +435,7 @@
|
||||
}];
|
||||
}
|
||||
|
||||
- (IBAction)doContent:(id)sender {
|
||||
- (IBAction)doContent:(UIButton *)sender {
|
||||
|
||||
[UIView animateWithDuration:.2f animations:^{
|
||||
self.contentButton.selected = YES;
|
||||
@ -444,6 +446,7 @@
|
||||
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Create Site" message:
|
||||
strf( @"Remember site named:\n%@", self.transientSite )
|
||||
preferredStyle:UIAlertControllerStyleActionSheet];
|
||||
[controller.popoverPresentationController setSourceView:sender];
|
||||
[controller addAction:[UIAlertAction actionWithTitle:@"Yes" style:UIAlertActionStyleDefault handler:
|
||||
^(UIAlertAction *_Nonnull action) {
|
||||
[[MPiOSAppDelegate get]
|
||||
@ -512,132 +515,140 @@
|
||||
|
||||
- (void)updateAnimated:(BOOL)animated {
|
||||
|
||||
Weakify( self );
|
||||
if (![NSThread isMainThread]) {
|
||||
PearlMainQueueOperation( ^{
|
||||
Strongify( self );
|
||||
[self updateAnimated:animated];
|
||||
} );
|
||||
return;
|
||||
}
|
||||
|
||||
[UIView animateWithDuration:animated? .3f: 0 animations:^{
|
||||
MPSiteEntity *mainSite = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
|
||||
if (animated)
|
||||
[UIView animateWithDuration:.3f animations:^{
|
||||
[self updateWasAnimated:animated];
|
||||
}];
|
||||
else
|
||||
[self updateWasAnimated:animated];
|
||||
}
|
||||
|
||||
// UI
|
||||
//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";
|
||||
- (void)updateWasAnimated:(BOOL)animated {
|
||||
|
||||
// Site Name
|
||||
[self updateSiteName:mainSite];
|
||||
Weakify( self );
|
||||
MPSiteEntity *mainSite = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
|
||||
|
||||
// Site Counter
|
||||
if ([mainSite isKindOfClass:[MPGeneratedSiteEntity class]])
|
||||
self.counterLabel.text = strf( @"%lu", (unsigned long)((MPGeneratedSiteEntity *)mainSite).counter );
|
||||
// UI
|
||||
//self.backgroundColor = mainSite.url? [UIColor greenColor]: [UIColor redColor];
|
||||
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
|
||||
self.loginNameField.enabled = self.passwordField.enabled = //
|
||||
[self.loginNameField isFirstResponder] || [self.passwordField isFirstResponder];
|
||||
// Site Name
|
||||
[self updateSiteName:mainSite];
|
||||
|
||||
// Site Password
|
||||
self.passwordField.secureTextEntry = [[MPiOSConfig get].hidePasswords boolValue];
|
||||
self.passwordField.attributedPlaceholder = stra(
|
||||
mainSite.type & MPResultTypeClassStateful? strl( @"No password" ):
|
||||
mainSite.type & MPResultTypeClassTemplate? strl( @"..." ): @"", @{
|
||||
NSForegroundColorAttributeName: [UIColor whiteColor]
|
||||
} );
|
||||
// Site Counter
|
||||
if ([mainSite isKindOfClass:[MPGeneratedSiteEntity class]])
|
||||
self.counterLabel.text = strf( @"%lu", (unsigned long)((MPGeneratedSiteEntity *)mainSite).counter );
|
||||
|
||||
// Calculate Fields
|
||||
if (![MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPKey *key = [MPiOSAppDelegate get].key;
|
||||
if (!key) {
|
||||
wrn( @"Could not load cell content: key unavailable." );
|
||||
PearlMainQueueOperation( ^{
|
||||
Strongify( self );
|
||||
[self updateAnimated:YES];
|
||||
} );
|
||||
return;
|
||||
}
|
||||
// Site Login Name
|
||||
self.loginNameField.enabled = self.passwordField.enabled = //
|
||||
[self.loginNameField isFirstResponder] || [self.passwordField isFirstResponder];
|
||||
|
||||
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;
|
||||
// Site Password
|
||||
self.passwordField.secureTextEntry = [[MPiOSConfig get].hidePasswords boolValue];
|
||||
self.passwordField.attributedPlaceholder = stra(
|
||||
mainSite.type & MPResultTypeClassStateful? strl( @"No password" ):
|
||||
mainSite.type & MPResultTypeClassTemplate? strl( @"..." ): @"", @{
|
||||
NSForegroundColorAttributeName: [UIColor whiteColor]
|
||||
} );
|
||||
}]) {
|
||||
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( ^{
|
||||
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];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)updateSiteName:(MPSiteEntity *)site {
|
||||
@ -645,14 +656,14 @@
|
||||
NSString *siteName = self.transientSite?: site.name;
|
||||
NSMutableAttributedString *attributedSiteName = [[NSMutableAttributedString alloc] initWithString:siteName?: @""];
|
||||
if ([attributedSiteName length])
|
||||
for (NSUInteger f = 0, s = (NSUInteger)-1; f < [self.fuzzyGroups count]; ++f) {
|
||||
s = [siteName rangeOfString:self.fuzzyGroups[f] options:NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch
|
||||
range:NSMakeRange( s + 1, [siteName length] - (s + 1) )].location;
|
||||
for (NSUInteger f = 0, s = 0; f < [self.queryGroups count]; ++f, ++s) {
|
||||
s = [siteName rangeOfString:self.queryGroups[f] options:NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch
|
||||
range:NSMakeRange( s, [siteName length] - s )].location;
|
||||
if (s == NSNotFound)
|
||||
break;
|
||||
|
||||
[attributedSiteName addAttribute:NSBackgroundColorAttributeName value:[UIColor redColor]
|
||||
range:NSMakeRange( s, [self.fuzzyGroups[f] length] )];
|
||||
range:NSMakeRange( s, [self.queryGroups[f] length] )];
|
||||
}
|
||||
|
||||
if (self.transientSite)
|
||||
|
@ -43,7 +43,7 @@
|
||||
UIView *sitesView = sitesVC.view;
|
||||
sitesView.frame = combinedVC.view.bounds;
|
||||
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) {
|
||||
if (!finished)
|
||||
|
@ -30,8 +30,6 @@
|
||||
@property(nonatomic, strong) IBOutlet UIView *badNameTipContainer;
|
||||
@property(nonatomic, strong) IBOutlet UIView *popdownView;
|
||||
@property(nonatomic, strong) IBOutlet UIView *popdownContainer;
|
||||
@property(nonatomic, strong) IBOutlet UIView *voltoInstallAlert;
|
||||
@property(nonatomic, strong) IBOutlet UIView *voltoMigrateAlert;
|
||||
|
||||
@property(assign, nonatomic) BOOL active;
|
||||
|
||||
@ -39,6 +37,5 @@
|
||||
- (void)reloadSites;
|
||||
|
||||
- (IBAction)dismissPopdown:(id)sender;
|
||||
- (IBAction)upgradeVolto:(UIButton *)sender;
|
||||
|
||||
@end
|
||||
|
@ -16,7 +16,9 @@
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
MP_LIBS_BEGIN
|
||||
#import <StoreKit/StoreKit.h>
|
||||
MP_LIBS_END
|
||||
|
||||
#import "MPSitesViewController.h"
|
||||
#import "MPiOSAppDelegate.h"
|
||||
@ -33,11 +35,10 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
MPPasswordsBadNameTip = 1 << 0,
|
||||
};
|
||||
|
||||
@interface MPSitesViewController()<NSFetchedResultsControllerDelegate, SKStoreProductViewControllerDelegate>
|
||||
@interface MPSitesViewController()<NSFetchedResultsControllerDelegate>
|
||||
|
||||
@property(nonatomic, strong) SKStoreProductViewController *voltoViewController;
|
||||
@property(nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
|
||||
@property(nonatomic, strong) NSArray *fuzzyGroups;
|
||||
@property(nonatomic, strong) NSArray *queryGroups;
|
||||
@property(nonatomic, strong) NSCharacterSet *siteNameAcceptableCharactersSet;
|
||||
@property(nonatomic, strong) NSMutableArray<NSMutableArray *> *dataSource;
|
||||
@property(nonatomic, weak) UIViewController *popdownVC;
|
||||
@ -64,13 +65,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
self.collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
||||
[self.collectionView automaticallyAdjustInsetsForKeyboard];
|
||||
self.searchBar.autocapitalizationType = UITextAutocapitalizationTypeNone;
|
||||
if ([self.searchBar respondsToSelector:@selector( keyboardAppearance )])
|
||||
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];
|
||||
self.searchBar.keyboardAppearance = UIKeyboardAppearanceDark;
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
@ -79,7 +74,6 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
|
||||
[self registerObservers];
|
||||
[self updateConfigKey:nil];
|
||||
[self updateVoltoAlerts];
|
||||
|
||||
static NSRegularExpression *bareHostRE = nil;
|
||||
static dispatch_once_t once = 0;
|
||||
@ -127,6 +121,16 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
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 {
|
||||
|
||||
if ([segue.identifier isEqualToString:@"popdown"])
|
||||
@ -144,33 +148,6 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
[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
|
||||
|
||||
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
|
||||
@ -186,7 +163,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)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];
|
||||
if ([item isKindOfClass:[MPSiteEntity class]])
|
||||
[cell setSite:item animated:NO];
|
||||
@ -196,6 +173,19 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
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
|
||||
|
||||
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
|
||||
@ -210,11 +200,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
|
||||
|
||||
if (controller == self.fetchedResultsController)
|
||||
PearlMainQueue( ^{
|
||||
[self.collectionView updateDataSource:self.dataSource
|
||||
toSections:[self createDataSource]
|
||||
reloadItems:nil completion:nil];
|
||||
} );
|
||||
[self updateSites];
|
||||
}
|
||||
|
||||
#pragma mark - UISearchBarDelegate
|
||||
@ -331,7 +317,6 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
PearlAddNotificationObserver( UIApplicationWillEnterForegroundNotification, nil, [NSOperationQueue mainQueue],
|
||||
^(MPSitesViewController *self, NSNotification *note) {
|
||||
[self viewWillAppear:YES];
|
||||
[self updateVoltoAlerts];
|
||||
} );
|
||||
PearlAddNotificationObserver( MPSignedOutNotification, nil, nil,
|
||||
^(MPSitesViewController *self, NSNotification *note) {
|
||||
@ -379,39 +364,42 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
- (void)reloadSites {
|
||||
|
||||
[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 *queryPattern = [[queryString stringByReplacingMatchesOfExpression:fuzzyRE withTemplate:@"*$1"]
|
||||
stringByAppendingString:@"*"];
|
||||
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]];
|
||||
}];
|
||||
self.fuzzyGroups = fuzzyGroups;
|
||||
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:@"*"];
|
||||
self.queryGroups = queryGroups;
|
||||
|
||||
NSError *error = nil;
|
||||
self.fetchedResultsController.fetchRequest.predicate =
|
||||
[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." );
|
||||
|
||||
PearlMainQueue( ^{
|
||||
[self.collectionView updateDataSource:self.dataSource
|
||||
toSections:[self createDataSource]
|
||||
reloadItems:@[ MPTransientPasswordItem ] completion:^(BOOL finished) {
|
||||
for (MPSiteCell *cell in self.collectionView.visibleCells)
|
||||
[cell setFuzzyGroups:self.fuzzyGroups];
|
||||
}];
|
||||
} );
|
||||
[self updateSites];
|
||||
}];
|
||||
}
|
||||
|
||||
- (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
|
||||
|
||||
- (NSString *)query {
|
||||
@ -429,14 +417,20 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
|
||||
if (!_fetchedResultsController) {
|
||||
[MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) {
|
||||
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
|
||||
NSFetchRequest *fetchRequest = [MPSiteEntity fetchRequest];
|
||||
fetchRequest.sortDescriptors = @[
|
||||
[[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector( lastUsed ) ) ascending:NO]
|
||||
];
|
||||
fetchRequest.fetchBatchSize = 10;
|
||||
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"user == %@", [MPiOSAppDelegate get].activeUserOID];
|
||||
|
||||
(self.fetchedResultsController = [[NSFetchedResultsController alloc]
|
||||
initWithFetchRequest:fetchRequest managedObjectContext:mainContext
|
||||
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];
|
||||
}
|
||||
@ -460,13 +454,6 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
} completion:completion];
|
||||
}
|
||||
|
||||
#pragma mark - SKStoreProductViewControllerDelegate
|
||||
|
||||
- (void)productViewControllerDidFinish:(SKStoreProductViewController *)viewController {
|
||||
|
||||
[viewController dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (IBAction)dismissPopdown:(id)sender {
|
||||
@ -477,77 +464,4 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
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
|
||||
|
@ -18,10 +18,13 @@
|
||||
|
||||
#import "MPStoreViewController.h"
|
||||
#import "MPiOSAppDelegate.h"
|
||||
#import "UIColor+Expanded.h"
|
||||
#import "MPAppDelegate_InApp.h"
|
||||
#import "MPSitesViewController.h"
|
||||
|
||||
MP_LIBS_BEGIN
|
||||
#import "UIColor+Expanded.h"
|
||||
MP_LIBS_END
|
||||
|
||||
PearlEnum( MPDevelopmentFuelConsumption,
|
||||
MPDevelopmentFuelConsumptionQuarterly, MPDevelopmentFuelConsumptionMonthly, MPDevelopmentFuelWeekly );
|
||||
|
||||
@ -64,6 +67,17 @@ PearlEnum( MPDevelopmentFuelConsumption,
|
||||
[[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
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
||||
|
@ -35,10 +35,14 @@
|
||||
@property(weak, nonatomic) IBOutlet UIButton *nextAvatarButton;
|
||||
@property(weak, nonatomic) IBOutlet UIButton *previousAvatarButton;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint *keyboardHeightConstraint;
|
||||
@property(weak, nonatomic) IBOutlet UIView *spectreInstallAlert;
|
||||
@property(weak, nonatomic) IBOutlet UIView *spectreMigrateAlert;
|
||||
|
||||
@property(assign, nonatomic, readonly) BOOL active;
|
||||
|
||||
- (void)setActive:(BOOL)active animated:(BOOL)animated;
|
||||
- (IBAction)changeAvatar:(UIButton *)sender;
|
||||
- (IBAction)upgradeSpectre:(UIButton *)sender;
|
||||
- (IBAction)dismissSpectre:(UIButton *)sender;
|
||||
|
||||
@end
|
||||
|
@ -48,7 +48,7 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
|
||||
MPActiveUserStateMinimized,
|
||||
};
|
||||
|
||||
@interface MPUsersViewController()
|
||||
@interface MPUsersViewController()<NSFetchedResultsControllerDelegate>
|
||||
|
||||
@property(nonatomic) MPActiveUserState activeUserState;
|
||||
@property(nonatomic, strong) NSArray *userIDs;
|
||||
@ -57,7 +57,7 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
|
||||
@property(nonatomic) NSUInteger marqueeTipTextIndex;
|
||||
@property(nonatomic, copy) NSString *masterPasswordChoice;
|
||||
@property(nonatomic, strong) NSOperationQueue *afterUpdates;
|
||||
@property(nonatomic, weak) id contextChangedObserver;
|
||||
@property(nonatomic, strong) NSFetchedResultsController *userResultsController;
|
||||
|
||||
@end
|
||||
|
||||
@ -91,6 +91,7 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
self.userSelectionContainer.visible = NO;
|
||||
[self.storeLoadingActivity startAnimating];
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
@ -99,6 +100,7 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
|
||||
|
||||
[self registerObservers];
|
||||
[self reloadUsers];
|
||||
[self updateSpectreAlerts];
|
||||
|
||||
[self.marqueeTipTimer invalidate];
|
||||
self.marqueeTipTimer = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector( firedMarqueeTimer: )
|
||||
@ -231,12 +233,10 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
|
||||
user.avatar = newUserAvatar;
|
||||
user.name = newUserName;
|
||||
|
||||
if ([[MPConfig get].sendInfo boolValue]) {
|
||||
[Countly.sharedInstance recordEvent:@"new-user" segmentation:@{
|
||||
@"algorithm": @(user.algorithm.version).description,
|
||||
@"avatar" : @(user.avatar).description,
|
||||
}];
|
||||
}
|
||||
[Countly.sharedInstance recordEvent:@"new-user" segmentation:@{
|
||||
@"algorithm": @(user.algorithm.version).description,
|
||||
@"avatar" : @(user.avatar).description,
|
||||
}];
|
||||
}
|
||||
|
||||
BOOL signedIn = [[MPiOSAppDelegate get] signInAsUser:user saveInContext:context
|
||||
@ -434,6 +434,7 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
|
||||
NSManagedObjectID *userID = user.permanentObjectID;
|
||||
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) {
|
||||
UIAlertController *controller_ = [UIAlertController alertControllerWithTitle:@"Deleting User" message:
|
||||
@"The user and its sites will be deleted." preferredStyle:UIAlertControllerStyleAlert];
|
||||
@ -643,7 +644,6 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
|
||||
[self removeKeyPathObservers];
|
||||
PearlRemoveNotificationObservers();
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self.contextChangedObserver];
|
||||
}
|
||||
|
||||
- (void)registerObservers {
|
||||
@ -661,6 +661,7 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
PearlAddNotificationObserver( UIApplicationWillEnterForegroundNotification, nil, [NSOperationQueue mainQueue],
|
||||
^(MPUsersViewController *self, NSNotification *note) {
|
||||
[self reloadUsers];
|
||||
[self updateSpectreAlerts];
|
||||
} );
|
||||
PearlAddNotificationObserver( UIApplicationDidBecomeActiveNotification, nil, [NSOperationQueue mainQueue],
|
||||
^(MPUsersViewController *self, NSNotification *note) {
|
||||
@ -675,24 +676,6 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
[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,
|
||||
^(MPUsersViewController *self, NSNotification *note) {
|
||||
self.userIDs = nil;
|
||||
@ -704,32 +687,67 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
[self reloadUsers];
|
||||
} );
|
||||
} );
|
||||
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
self.avatarCollectionView.visible = YES;
|
||||
[self.storeLoadingActivity stopAnimating];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)reloadUsers {
|
||||
|
||||
[self afterUpdatesMainQueue:^{
|
||||
if (![MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) {
|
||||
NSError *error = nil;
|
||||
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
|
||||
NSFetchRequest *fetchRequest = [MPUserEntity fetchRequest];
|
||||
fetchRequest.sortDescriptors = @[
|
||||
[NSSortDescriptor sortDescriptorWithKey:NSStringFromSelector( @selector( lastUsed ) ) ascending:NO]
|
||||
];
|
||||
NSArray *users = [mainContext executeFetchRequest:fetchRequest error:&error];
|
||||
if (!users) {
|
||||
MPError( error, @"Failed to load users." );
|
||||
self.userIDs = nil;
|
||||
}
|
||||
self.userResultsController = [[NSFetchedResultsController alloc]
|
||||
initWithFetchRequest:fetchRequest managedObjectContext:mainContext
|
||||
sectionNameKeyPath:nil cacheName:nil];
|
||||
self.userResultsController.delegate = self;
|
||||
|
||||
NSMutableArray *userIDs = [NSMutableArray arrayWithCapacity:[users count]];
|
||||
for (MPUserEntity *user in users)
|
||||
[userIDs addObject:user.permanentObjectID];
|
||||
self.userIDs = userIDs;
|
||||
NSError *error = nil;
|
||||
if (![self.userResultsController performFetch:&error])
|
||||
MPError( error, @"Failed to load users." );
|
||||
|
||||
[self updateUsers];
|
||||
}])
|
||||
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
|
||||
|
||||
- (void)setActive:(BOOL)active animated:(BOOL)animated {
|
||||
@ -904,4 +922,15 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
++[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
|
||||
|
@ -17,10 +17,12 @@
|
||||
//==============================================================================
|
||||
|
||||
#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) NSURL *initialURL;
|
||||
|
@ -17,6 +17,7 @@
|
||||
//==============================================================================
|
||||
|
||||
#import "MPWebViewController.h"
|
||||
#import "MPiOSAppDelegate.h"
|
||||
|
||||
@implementation MPWebViewController
|
||||
|
||||
@ -24,20 +25,30 @@
|
||||
|
||||
[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 {
|
||||
|
||||
[super viewWillAppear:animated];
|
||||
}
|
||||
|
||||
if (!self.initialURL)
|
||||
self.initialURL = [NSURL URLWithString:@"https://masterpassword.app"];
|
||||
|
||||
self.webNavigationItem.title = self.initialURL.host;
|
||||
|
||||
self.webView.visible = NO;
|
||||
[self.webView loadRequest:[[NSURLRequest alloc] initWithURL:self.initialURL]];
|
||||
- (void)viewDidLayoutSubviews {
|
||||
[self.webView.scrollView insetOcclusion];
|
||||
[super viewDidLayoutSubviews];
|
||||
}
|
||||
|
||||
- (UIStatusBarStyle)preferredStatusBarStyle {
|
||||
@ -45,26 +56,24 @@
|
||||
return UIStatusBarStyleLightContent;
|
||||
}
|
||||
|
||||
#pragma mark - UIWebViewDelegate
|
||||
#pragma mark - WKNavigationDelegate
|
||||
|
||||
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request
|
||||
navigationType:(UIWebViewNavigationType)navigationType {
|
||||
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
|
||||
decisionHandler:(void ( ^ )(WKNavigationActionPolicy))decisionHandler {
|
||||
|
||||
if ([[request.URL absoluteString] rangeOfString:@"thanks.lhunath.com"].location != NSNotFound) {
|
||||
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"tipped.thanks"];
|
||||
if (![[NSUserDefaults standardUserDefaults] synchronize])
|
||||
wrn( @"Couldn't synchronize thanks tip." );
|
||||
if ([navigationAction.request.mainDocumentURL.scheme isEqualToString:@"masterpassword"]) {
|
||||
[[MPiOSAppDelegate get] openURL:navigationAction.request.mainDocumentURL];
|
||||
decisionHandler( WKNavigationActionPolicyCancel );
|
||||
return;
|
||||
}
|
||||
|
||||
if ([request.URL isEqual:request.mainDocumentURL]) {
|
||||
self.webNavigationItem.title = request.URL.host;
|
||||
self.webNavigationItem.prompt = strl( @"Loading" );
|
||||
}
|
||||
|
||||
return YES;
|
||||
decisionHandler( WKNavigationActionPolicyAllow );
|
||||
}
|
||||
|
||||
- (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 alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
|
||||
@ -72,19 +81,57 @@
|
||||
[activityView startAnimating];
|
||||
}
|
||||
|
||||
- (void)webViewDidFinishLoad:(UIWebView *)webView {
|
||||
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
|
||||
|
||||
[UIView animateWithDuration:0.3 animations:^{
|
||||
self.webView.visible = YES;
|
||||
if ([[webView.URL absoluteString] rangeOfString:@"thanks.lhunath.com"].location != NSNotFound) {
|
||||
[[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
|
||||
|
||||
- (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 {
|
||||
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
|
@ -17,15 +17,23 @@
|
||||
//==============================================================================
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <StoreKit/StoreKit.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)openFeedbackWithLogs:(BOOL)logs forVC:(UIViewController *)viewController;
|
||||
|
||||
- (void)showExportForVC:(UIViewController *)viewController;
|
||||
- (void)migrateFor:(MPUserEntity *)user;
|
||||
|
||||
- (void)changeMasterPasswordFor:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc didResetBlock:(void ( ^ )(void))didReset;
|
||||
|
||||
@end
|
||||
|
@ -23,18 +23,35 @@
|
||||
#import "mpw-marshal.h"
|
||||
#import "MPSecrets.h"
|
||||
|
||||
MP_LIBS_BEGIN
|
||||
#import <Sentry/Sentry.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>
|
||||
|
||||
@property(nonatomic, strong) UIDocumentInteractionController *interactionController;
|
||||
@property(nonatomic, strong) PearlHangDetector *hangDetector;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPiOSAppDelegate
|
||||
|
||||
@synthesize window;
|
||||
|
||||
+ (void)initialize {
|
||||
|
||||
[MPiOSConfig get];
|
||||
@ -43,21 +60,6 @@
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
||||
|
||||
@try {
|
||||
// Sentry
|
||||
[SentrySDK initWithOptions:@{
|
||||
@"dsn" : 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) {
|
||||
PearlLogLevel level = PearlLogLevelWarn;
|
||||
if ([[MPConfig get].sendInfo boolValue])
|
||||
@ -102,15 +104,16 @@
|
||||
countlyConfig.appKey = decrypt( countlyKey );
|
||||
countlyConfig.features = @[ CLYPushNotifications, CLYAutoViewTracking ];
|
||||
countlyConfig.requiresConsent = YES;
|
||||
#if DEBUG
|
||||
countlyConfig.pushTestMode = CLYPushTestModeDevelopment;
|
||||
#elif ! PUBLIC
|
||||
countlyConfig.pushTestMode = CLYPushTestModeTestFlightOrAdHoc;
|
||||
#endif
|
||||
countlyConfig.alwaysUsePOST = YES;
|
||||
countlyConfig.deviceID = [PearlKeyChain deviceIdentifier];
|
||||
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];
|
||||
|
||||
#if ! DEBUG
|
||||
@ -129,18 +132,14 @@
|
||||
[self updateConfigKey:note.object];
|
||||
} );
|
||||
PearlAddNotificationObserver( NSUserDefaultsDidChangeNotification, nil, nil, ^(id self, NSNotification *note) {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:nil];
|
||||
PearlMainQueueOperation( ^{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:nil];
|
||||
} );
|
||||
} );
|
||||
}
|
||||
@catch (id exception) {
|
||||
err( @"During Config Test: %@", exception );
|
||||
}
|
||||
@try {
|
||||
[super application:application didFinishLaunchingWithOptions:launchOptions];
|
||||
}
|
||||
@catch (id exception) {
|
||||
err( @"During Pearl Application Launch: %@", exception );
|
||||
}
|
||||
@try {
|
||||
inf( @"Started up with device identifier: %@", [PearlKeyChain deviceIdentifier] );
|
||||
|
||||
@ -171,30 +170,29 @@
|
||||
}
|
||||
} );
|
||||
|
||||
if (@available( iOS 12, * )) {
|
||||
[Countly.sharedInstance askForNotificationPermissionWithOptions:UNAuthorizationOptionProvisional completionHandler:
|
||||
^(BOOL granted, NSError *error) {
|
||||
inf( @"provisional: %d: %@", granted, error );
|
||||
}];
|
||||
}
|
||||
SKStoreProductViewController *migrateVC = [SKStoreProductViewController new];
|
||||
[migrateVC loadProductWithParameters:@{
|
||||
SKStoreProductParameterCampaignToken : @"app-masterpassword.ios", /* Campaign: From MasterPassword iOS */
|
||||
SKStoreProductParameterProviderToken : @153897, /* Provider: Maarten Billemont */
|
||||
// SKStoreProductParameterITunesItemIdentifier: @510296984, /* Application: MasterPassword iOS */
|
||||
SKStoreProductParameterITunesItemIdentifier: @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( ^{
|
||||
if ([[MPiOSConfig get].showSetup boolValue])
|
||||
[self.navigationController performSegueWithIdentifier:@"setup" sender:self];
|
||||
[self.window.rootViewController performSegueWithIdentifier:@"setup" sender:self];
|
||||
|
||||
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"notificationsDecided"]) {
|
||||
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];
|
||||
}
|
||||
[self consentFeatures];
|
||||
} );
|
||||
}
|
||||
@catch (id exception) {
|
||||
@ -211,6 +209,12 @@
|
||||
if (!url)
|
||||
return NO;
|
||||
|
||||
// masterpassword: URLs.
|
||||
if ([url.scheme isEqualToString:@"masterpassword"]) {
|
||||
[self openURL:url];
|
||||
return YES;
|
||||
}
|
||||
|
||||
// Arbitrary URL to mpsites data.
|
||||
[[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:
|
||||
^(NSData *importedSitesData, NSURLResponse *response, NSError *error) {
|
||||
@ -224,7 +228,7 @@
|
||||
(id)[error localizedDescription]?: error )
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[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;
|
||||
}
|
||||
@ -236,7 +240,7 @@
|
||||
@"Master Password couldn't understand the import file."
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[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;
|
||||
}
|
||||
@ -247,6 +251,84 @@
|
||||
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 {
|
||||
|
||||
if ([NSThread isMainThread]) {
|
||||
@ -272,7 +354,7 @@
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
|
||||
setResult( nil );
|
||||
}]];
|
||||
[self.navigationController presentViewController:alert animated:YES completion:nil];
|
||||
[self.window.rootViewController presentViewController:alert animated:YES completion:nil];
|
||||
} );
|
||||
} );
|
||||
} askUserPassword:^NSString *(NSString *userName) {
|
||||
@ -290,7 +372,7 @@
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
|
||||
setResult( nil );
|
||||
}]];
|
||||
[self.navigationController presentViewController:alert animated:YES completion:nil];
|
||||
[self.window.rootViewController presentViewController:alert animated:YES completion:nil];
|
||||
} );
|
||||
} );
|
||||
} result:^(NSError *error) {
|
||||
@ -301,7 +383,7 @@
|
||||
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Error" message:[error localizedDescription]
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[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" );
|
||||
|
||||
[super applicationWillEnterForeground:application];
|
||||
|
||||
[self.hangDetector start];
|
||||
}
|
||||
|
||||
- (void)applicationDidBecomeActive:(UIApplication *)application {
|
||||
|
||||
inf( @"Re-activated" );
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:nil];
|
||||
|
||||
PearlNotMainQueue( ^{
|
||||
NSString *importData = [UIPasteboard generalPasteboard].string;
|
||||
@ -335,20 +409,22 @@
|
||||
[UIPasteboard generalPasteboard].string = @"";
|
||||
}]];
|
||||
[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 );
|
||||
} );
|
||||
}
|
||||
|
||||
[super applicationDidBecomeActive:application];
|
||||
- (void)applicationDidBecomeActive:(UIApplication *)application {
|
||||
|
||||
inf( @"Re-activated" );
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:nil];
|
||||
}
|
||||
|
||||
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
|
||||
|
||||
inf( @"Received memory warning." );
|
||||
|
||||
[super applicationDidReceiveMemoryWarning:application];
|
||||
}
|
||||
|
||||
- (void)applicationDidEnterBackground:(UIApplication *)application {
|
||||
@ -361,50 +437,45 @@
|
||||
}
|
||||
|
||||
[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
|
||||
|
||||
- (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 {
|
||||
|
||||
if (![PearlEMail canSendMail]) {
|
||||
@ -414,7 +485,7 @@
|
||||
@"help@masterpassword.app"
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[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) {
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Feedback" message:
|
||||
@ -428,7 +499,7 @@
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"No Logs" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
|
||||
[self openFeedbackWithLogs:NO forVC:viewController];
|
||||
}]];
|
||||
[self.navigationController presentViewController:alert animated:YES completion:nil];
|
||||
[self.window.rootViewController presentViewController:alert animated:YES completion:nil];
|
||||
}
|
||||
else
|
||||
[self openFeedbackWithLogs:NO forVC:viewController];
|
||||
@ -474,15 +545,16 @@
|
||||
@"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."
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"E-Mail Logs" style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *action) {
|
||||
[self openFeedbackWithLogs:YES forVC:nil];
|
||||
}]];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"Try Again" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
|
||||
[self retryCorruptStore];
|
||||
}]];
|
||||
[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) {
|
||||
[self deleteAndResetStore];
|
||||
}]];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"Ignore" style:UIAlertActionStyleCancel handler:nil]];
|
||||
[self.navigationController presentViewController:alert animated:YES completion:nil];
|
||||
[self.window.rootViewController 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"
|
||||
@"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."
|
||||
preferredStyle:UIAlertControllerStyleActionSheet];
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[sheet addAction:[UIAlertAction actionWithTitle:@"Safe Export" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
|
||||
[self showExportRevealPasswords:NO forVC:viewController];
|
||||
}]];
|
||||
@ -507,10 +579,10 @@
|
||||
[self showExportRevealPasswords:YES forVC:viewController];
|
||||
}]];
|
||||
[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]];
|
||||
[self.navigationController presentViewController:alert animated:YES completion:nil];
|
||||
[self.window.rootViewController presentViewController:alert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)showExportRevealPasswords:(BOOL)revealPasswords forVC:(UIViewController *)viewController {
|
||||
@ -521,93 +593,172 @@
|
||||
@"Close Master Password, go into Settings and add a Mail account."
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[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;
|
||||
}
|
||||
|
||||
[self exportSitesRevealPasswords:revealPasswords 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 mpsites." );
|
||||
PearlMainQueue( ^{
|
||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
NSError *error = nil;
|
||||
NSString *exportedUser = [self exportSitesFor:[self activeUserInContext:context] revealPasswords:revealPasswords askExportPassword:
|
||||
^NSString *(NSString *userName) {
|
||||
return PearlAwait( ^(void (^setResult)(id)) {
|
||||
PearlMainQueue( ^{
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:strf(
|
||||
@"Master Password For:\n%@", userName )
|
||||
message:@"Enter your master password to export the user."
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
|
||||
textField.secureTextEntry = YES;
|
||||
}];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"Export" style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *action) {
|
||||
setResult( alert.textFields.firstObject.text );
|
||||
}]];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel
|
||||
handler:^(UIAlertAction *action) {
|
||||
setResult( nil );
|
||||
}]];
|
||||
[self.window.rootViewController presentViewController:alert animated:YES completion:nil];
|
||||
} );
|
||||
} );
|
||||
} error:&error];
|
||||
|
||||
PearlMainQueue( ^{
|
||||
if (error) {
|
||||
MPError( error, @"Failed to export mpsites." );
|
||||
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];
|
||||
} );
|
||||
[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;
|
||||
}
|
||||
|
||||
NSDateFormatter *exportDateFormatter = [NSDateFormatter new];
|
||||
[exportDateFormatter setDateFormat:@"yyyy'-'MM'-'dd"];
|
||||
NSString *exportFileName = strf( @"%@ (%@).mpsites",
|
||||
[self activeUserForMainThread].name, [exportDateFormatter stringFromDate:[NSDate date]] );
|
||||
[MPAppDelegate_Shared managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
NSError *error = nil;
|
||||
NSString *exportedUser = [[MPAppDelegate_Shared get] exportSitesFor:[MPUserEntity existingObjectWithID:user.objectID inContext:context]
|
||||
revealPasswords:NO askExportPassword:^NSString *(NSString *userName) {
|
||||
return PearlAwait( ^(void (^setResult)(id)) {
|
||||
PearlMainQueue( ^{
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:strf( @"Master Password For:\n%@", userName )
|
||||
message:@"Enter your master password to export the user."
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
|
||||
textField.secureTextEntry = YES;
|
||||
}];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"Export" style:UIAlertActionStyleDefault handler:
|
||||
^(UIAlertAction *action) { setResult( alert.textFields.firstObject.text ); }]];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:
|
||||
^(UIAlertAction *action) { setResult( nil ); }]];
|
||||
[self.window.rootViewController presentViewController:alert animated:YES completion:nil];
|
||||
} );
|
||||
} );
|
||||
} error:&error];
|
||||
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Export Destination" message:nil
|
||||
preferredStyle:UIAlertControllerStyleActionSheet];
|
||||
[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 );
|
||||
PearlMainQueue( ^{
|
||||
if (error) {
|
||||
MPError( error, @"Failed to export user." );
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Export Error"
|
||||
message:[error localizedDescription]
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"Okay" style:UIAlertActionStyleCancel handler:nil]];
|
||||
[self.window.rootViewController presentViewController:alert animated:YES completion:nil];
|
||||
}
|
||||
if (!exportedUser)
|
||||
return;
|
||||
|
||||
[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.navigationController presentViewController:alert animated:YES completion:nil];
|
||||
}];
|
||||
NSURLComponents *components = [NSURLComponents new];
|
||||
components.scheme = @"spectre";
|
||||
components.path = @"import";
|
||||
components.queryItems = @[ [[NSURLQueryItem alloc] initWithName:@"data" value:exportedUser] ];
|
||||
[UIApp openURL:components.URL];
|
||||
} );
|
||||
}];
|
||||
}
|
||||
|
||||
else if (self.spectreViewController)
|
||||
[self.window.rootViewController presentViewController:self.spectreViewController animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)changeMasterPasswordFor:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc didResetBlock:(void ( ^ )(void))didReset {
|
||||
@ -618,7 +769,7 @@
|
||||
@"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."
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"Abort" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"Continue" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
|
||||
[moc performBlockAndWait:^{
|
||||
inf( @"Clearing keyID for user: %@.", user.userID );
|
||||
user.keyID = nil;
|
||||
@ -631,10 +782,17 @@
|
||||
didReset();
|
||||
}]];
|
||||
[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
|
||||
|
||||
- (void)documentInteractionController:(UIDocumentInteractionController *)controller didEndSendingToApplication:(NSString *)application {
|
||||
@ -655,41 +813,55 @@
|
||||
[PearlLogger get].historyLevel = [[MPiOSConfig get].traceMode boolValue]? PearlLogLevelTrace: PearlLogLevelInfo;
|
||||
|
||||
// Send info
|
||||
if ([[MPConfig get].sendInfo boolValue]) {
|
||||
[Countly.sharedInstance giveConsentForAllFeatures];
|
||||
|
||||
NSArray *countlyFeatures = @[
|
||||
CLYConsentSessions, CLYConsentEvents, CLYConsentUserDetails, CLYConsentCrashReporting, CLYConsentViewTracking, CLYConsentStarRating
|
||||
];
|
||||
if ([[MPiOSConfig get].sendInfo boolValue] || ![[MPiOSConfig get].sendInfoDecided boolValue]) {
|
||||
if ([PearlLogger get].printLevel > PearlLogLevelInfo)
|
||||
[PearlLogger get].printLevel = PearlLogLevelInfo;
|
||||
|
||||
NSMutableDictionary *prefs = [NSMutableDictionary new];
|
||||
prefs[@"rememberLogin"] = [MPConfig get].rememberLogin;
|
||||
prefs[@"sendInfo"] = [MPConfig get].sendInfo;
|
||||
prefs[@"helpHidden"] = [MPiOSConfig get].helpHidden;
|
||||
prefs[@"showQuickStart"] = [MPiOSConfig get].showSetup;
|
||||
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[@"jailbroken"] = @([PearlDeviceUtils isJailbroken]);
|
||||
prefs[@"platform"] = [PearlDeviceUtils platform];
|
||||
#ifdef APPSTORE
|
||||
prefs[@"reviewedVersion"] = @([PearlDeviceUtils isAppEncrypted]);
|
||||
// Sentry
|
||||
if (!SentrySDK.isEnabled)
|
||||
[SentrySDK startWithOptions:@{
|
||||
@"dsn" : NilToNSNull( decrypt( sentryDSN ) ),
|
||||
#ifdef DEBUG
|
||||
@"debug" : @(NO), //@(YES),
|
||||
@"environment" : @"Development",
|
||||
#elif PUBLIC
|
||||
@"debug" : @(NO),
|
||||
@"environment" : @"Public",
|
||||
#else
|
||||
prefs[@"reviewedVersion"] = @(YES);
|
||||
@"debug" : @(NO),
|
||||
@"environment" : @"Private",
|
||||
#endif
|
||||
@"enableAutoSessionTracking": @(YES),
|
||||
}];
|
||||
|
||||
[SentrySDK.currentHub getClient].options.enabled = @YES;
|
||||
[SentrySDK configureScope:^(SentryScope *scope) {
|
||||
for (NSString *pref in prefs.allKeys)
|
||||
[scope setExtraValue:prefs[pref] forKey:pref];
|
||||
[scope setExtraValue:[MPiOSConfig get].rememberLogin forKey:@"rememberLogin"];
|
||||
[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 {
|
||||
[SentrySDK.currentHub getClient].options.enabled = @NO;
|
||||
[Countly.sharedInstance cancelConsentForAllFeatures];
|
||||
[Countly.sharedInstance cancelConsentForFeatures:countlyFeatures];
|
||||
[SentrySDK close];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,8 +33,8 @@
|
||||
NSStringFromSelector( @selector( siteInfoHidden ) ) : @YES,
|
||||
NSStringFromSelector( @selector( showSetup ) ) : @YES,
|
||||
NSStringFromSelector( @selector( appleID ) ) : @"510296984",
|
||||
NSStringFromSelector( @selector( actionsTipShown ) ) : @(!self.firstRun),
|
||||
NSStringFromSelector( @selector( typeTipShown ) ) : @(!self.firstRun),
|
||||
NSStringFromSelector( @selector( actionsTipShown ) ) : @(![self.firstRun boolValue]),
|
||||
NSStringFromSelector( @selector( typeTipShown ) ) : @(![self.firstRun boolValue]),
|
||||
NSStringFromSelector( @selector( loginNameTipShown ) ): @NO,
|
||||
NSStringFromSelector( @selector( traceMode ) ) : @NO,
|
||||
NSStringFromSelector( @selector( dictationSearch ) ) : @NO,
|
||||
|
@ -11,9 +11,9 @@
|
||||
<dict>
|
||||
<key>CFBundleTypeIconFiles</key>
|
||||
<array>
|
||||
<string>Icon-Small</string>
|
||||
<string>Icon-64.png</string>
|
||||
<string>Icon-320.png</string>
|
||||
<string>Icon-Small-40.png</string>
|
||||
<string>Icon-Small-40@2x.png</string>
|
||||
<string>Icon-Small-40@3x.png</string>
|
||||
</array>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Master Password sites</string>
|
||||
@ -21,6 +21,7 @@
|
||||
<string>Alternate</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>com.lyndir.masterpassword.json</string>
|
||||
<string>com.lyndir.masterpassword.sites</string>
|
||||
</array>
|
||||
</dict>
|
||||
@ -37,12 +38,36 @@
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<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>
|
||||
<string>[auto]</string>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>firefox</string>
|
||||
<string>googlechrome</string>
|
||||
<string>opera-http</string>
|
||||
<string>spectre</string>
|
||||
</array>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||
<false/>
|
||||
<key>NSFaceIDUsageDescription</key>
|
||||
<string>Biometrics are used to load your master password from the device's keychain.</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>© 2011-2020</string>
|
||||
<key>UIAppFonts</key>
|
||||
@ -101,6 +126,34 @@
|
||||
</array>
|
||||
<key>UTExportedTypeDeclarations</key>
|
||||
<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>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
@ -110,9 +163,9 @@
|
||||
<string>Master Password sites</string>
|
||||
<key>UTTypeIconFiles</key>
|
||||
<array>
|
||||
<string>Icon-Small.png</string>
|
||||
<string>Icon-64.png</string>
|
||||
<string>Icon-320.png</string>
|
||||
<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.sites</string>
|
||||
@ -121,6 +174,7 @@
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>mpsites</string>
|
||||
<string>mpsites.txt</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
|
@ -18,7 +18,7 @@
|
||||
<key>Title</key>
|
||||
<string>Version</string>
|
||||
<key>Key</key>
|
||||
<string>unset</string>
|
||||
<string>version</string>
|
||||
<key>Type</key>
|
||||
<string>PSTitleValueSpecifier</string>
|
||||
</dict>
|
||||
@ -28,7 +28,7 @@
|
||||
<key>Title</key>
|
||||
<string>Build</string>
|
||||
<key>Key</key>
|
||||
<string>unset</string>
|
||||
<string>build</string>
|
||||
<key>Type</key>
|
||||
<string>PSTitleValueSpecifier</string>
|
||||
</dict>
|
||||
@ -40,7 +40,7 @@
|
||||
<key>Type</key>
|
||||
<string>PSTitleValueSpecifier</string>
|
||||
<key>Key</key>
|
||||
<string>unset</string>
|
||||
<string>copyright</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Type</key>
|
||||
|
@ -14,9 +14,9 @@ echo "Cleaning .."
|
||||
git clean -ffdx .
|
||||
|
||||
echo "Creating archive $mpwArchive .."
|
||||
echo "$version" > VERSION
|
||||
git show --show-signature --pretty=format:%H --quiet "$tag" > TAG
|
||||
{ git ls-files -z .; printf '%s\0' VERSION TAG; } | xargs -0 tar -Lcvzf "$mpwArchive"
|
||||
echo "$version" > cli/VERSION
|
||||
git show --show-signature --pretty=format:%H --quiet "$tag" > cli/TAG
|
||||
{ git ls-files -z .; printf '%s\0' cli/VERSION cli/TAG; } | xargs -0 tar -Lcvzf "$mpwArchive"
|
||||
|
||||
echo "Creating archive signature $mpwArchive.sig .."
|
||||
gpg --detach-sign --local-user 5C2D1D61853F20F2FCDDCCB70EF21226F43EA6BC "$mpwArchive"
|
||||
|
@ -28,7 +28,7 @@ mpw_expect() {
|
||||
json) file=~/.mpw.d/"$user.mpjson" ;;
|
||||
esac
|
||||
fi
|
||||
[[ $file ]] && (( ! keep )) && rm "$file"
|
||||
[[ -e $file ]] && (( ! keep )) && rm "$file"
|
||||
|
||||
printf '.'
|
||||
local result=$(./mpw -q "${args[@]}") err=$?
|
||||
@ -79,397 +79,429 @@ mpw_expect() {
|
||||
esac
|
||||
fi
|
||||
|
||||
[[ $file ]] && (( ! keep )) && rm "$file"
|
||||
[[ -e $file ]] && (( ! keep )) && rm "$file"
|
||||
}
|
||||
|
||||
|
||||
# mpw_tests.xml
|
||||
## V3
|
||||
printf "\nV%d, none: " 3
|
||||
mpw_expect 'CefoTiciJuba7@' -Fnone \
|
||||
-u 'test' -M 'test' 'test'
|
||||
mpw_expect 'Tina0#NotaMahu' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' 'ẗesẗ'
|
||||
mpw_expect 'Tina0#NotaMahu' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -C '' 'ẗesẗ'
|
||||
mpw_expect 'Tina0#NotaMahu' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'Tina0#NotaMahu' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -a3 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'Tina0#NotaMahu' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'Tina0#NotaMahu' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'KovxFipe5:Zatu' -Fnone \
|
||||
-u '⛄' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'ModoLalhRapo6#' -Fnone \
|
||||
-u 'tesẗ' -M '⛄' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'CudmTecuPune7:' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' '⛄'
|
||||
mpw_expect 'yubfalago' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -p 'identification' 'ẗesẗ'
|
||||
mpw_expect 'yubfalago' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tname -c1 -a3 -p 'identification' -C '' 'ẗesẗ'
|
||||
mpw_expect 'jip nodwoqude dizo' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -p 'recovery' 'ẗesẗ'
|
||||
mpw_expect 'jip nodwoqude dizo' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a3 -p 'recovery' -C '' 'ẗesẗ'
|
||||
mpw_expect 'dok sorkicoyu ruya' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a3 -p 'recovery' -C 'quesẗion' 'ẗesẗ'
|
||||
mpw_expect 'j5TJ%G0WWwSMvYb)hr4)' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tmax -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'TinRaz2?' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tmed -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'jad0IQA3' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tbasic -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'Tin0' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tshort -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect '1710' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tpin -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'tinraziqu' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tname -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'tinr ziq taghuye zuj' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'HidiLonoFopt9&' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tlong -c4294967295 -a3 -p 'authentication' 'ẗesẗ'
|
||||
mpw_expect 'CefoTiciJuba7@' -Fnone \
|
||||
-u 'test' -M 'test' 'test'
|
||||
mpw_expect 'Tina0#NotaMahu' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' 'ẗesẗ'
|
||||
mpw_expect 'Tina0#NotaMahu' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -C '' 'ẗesẗ'
|
||||
mpw_expect 'Tina0#NotaMahu' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'Tina0#NotaMahu' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -a3 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'Tina0#NotaMahu' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'Tina0#NotaMahu' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'KovxFipe5:Zatu' -Fnone \
|
||||
-u '⛄' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'ModoLalhRapo6#' -Fnone \
|
||||
-u 'tesẗ' -M '⛄' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'CudmTecuPune7:' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' '⛄'
|
||||
mpw_expect 'yubfalago' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -p 'identification' -C '' 'ẗesẗ'
|
||||
mpw_expect 'yubfalago' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tname -c1 -a3 -p 'identification' -C '' 'ẗesẗ'
|
||||
mpw_expect 'jip nodwoqude dizo' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -p 'recovery' -C '' 'ẗesẗ'
|
||||
mpw_expect 'jip nodwoqude dizo' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a3 -p 'recovery' -C '' 'ẗesẗ'
|
||||
mpw_expect 'dok sorkicoyu ruya' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a3 -p 'recovery' -C 'quesẗion' 'ẗesẗ'
|
||||
mpw_expect 'j5TJ%G0WWwSMvYb)hr4)' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tmax -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'TinRaz2?' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tmed -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'jad0IQA3' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tbasic -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'Tin0' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tshort -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect '1710' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tpin -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'tinraziqu' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tname -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'tinr ziq taghuye zuj' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a3 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'HidiLonoFopt9&' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tlong -c4294967295 -a3 -p 'authentication' -C '' 'ẗesẗ'
|
||||
|
||||
## V2
|
||||
printf "\nV%d, none: " 2
|
||||
mpw_expect 'CefoTiciJuba7@' -Fnone \
|
||||
-u 'test' -M 'test' -tlong -c1 -a2 -p 'authentication' -C '' 'test'
|
||||
mpw_expect "HuczFina3'Qatf" -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'SicrJuwaWaql0#' -Fnone \
|
||||
-u '⛄' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'LokaJayp1@Faba' -Fnone \
|
||||
-u 'tesẗ' -M '⛄' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'DoqaHulu8:Funh' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' '⛄'
|
||||
mpw_expect 'yiyguxoxe' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tname -c1 -a2 -p 'identification' -C '' 'ẗesẗ'
|
||||
mpw_expect 'CefoTiciJuba7@' -Fnone \
|
||||
-u 'test' -M 'test' -tlong -c1 -a2 -p 'authentication' -C '' 'test'
|
||||
mpw_expect "HuczFina3'Qatf" -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'SicrJuwaWaql0#' -Fnone \
|
||||
-u '⛄' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'LokaJayp1@Faba' -Fnone \
|
||||
-u 'tesẗ' -M '⛄' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'DoqaHulu8:Funh' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' '⛄'
|
||||
mpw_expect 'yiyguxoxe' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tname -c1 -a2 -p 'identification' -C '' 'ẗesẗ'
|
||||
mpw_expect 'vu yelyo bat kujavmu' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a2 -p 'recovery' -C '' 'ẗesẗ'
|
||||
mpw_expect 'ka deqce xad vomacgi' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a2 -p 'recovery' -C 'quesẗion' 'ẗesẗ'
|
||||
mpw_expect 'wRF$LmB@umWGLWeVlB0-' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tmax -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'HucZuk0!' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tmed -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'wb59VoB5' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tbasic -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'Huc9' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tshort -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect '2959' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tpin -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'huczukamo' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tname -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'huc finmokozi fota' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'Mixa1~BulgNijo' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tlong -c4294967295 -a2 -p 'authentication' 'ẗesẗ'
|
||||
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a2 -p 'recovery' -C '' 'ẗesẗ'
|
||||
mpw_expect 'ka deqce xad vomacgi' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a2 -p 'recovery' -C 'quesẗion' 'ẗesẗ'
|
||||
mpw_expect 'wRF$LmB@umWGLWeVlB0-' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tmax -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'HucZuk0!' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tmed -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'wb59VoB5' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tbasic -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'Huc9' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tshort -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect '2959' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tpin -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'huczukamo' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tname -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'huc finmokozi fota' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a2 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'Mixa1~BulgNijo' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tlong -c4294967295 -a2 -p 'authentication' -C '' 'ẗesẗ'
|
||||
|
||||
## V1
|
||||
printf "\nV%d, none: " 1
|
||||
mpw_expect 'CefoTiciJuba7@' -Fnone \
|
||||
-u 'test' -M 'test' -tlong -c1 -a1 -p 'authentication' -C '' 'test'
|
||||
mpw_expect 'SuxiHoteCuwe3/' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'CupaTixu8:Hetu' -Fnone \
|
||||
-u '⛄' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'NaqmBanu9+Decs' -Fnone \
|
||||
-u 'tesẗ' -M '⛄' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'XowaDokoGeyu2)' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' '⛄'
|
||||
mpw_expect 'makmabivo' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tname -c1 -a1 -p 'identification' -C '' 'ẗesẗ'
|
||||
mpw_expect 'CefoTiciJuba7@' -Fnone \
|
||||
-u 'test' -M 'test' -tlong -c1 -a1 -p 'authentication' -C '' 'test'
|
||||
mpw_expect 'SuxiHoteCuwe3/' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'CupaTixu8:Hetu' -Fnone \
|
||||
-u '⛄' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'NaqmBanu9+Decs' -Fnone \
|
||||
-u 'tesẗ' -M '⛄' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'XowaDokoGeyu2)' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' '⛄'
|
||||
mpw_expect 'makmabivo' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tname -c1 -a1 -p 'identification' -C '' 'ẗesẗ'
|
||||
mpw_expect 'je mutbo buf puhiywo' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a1 -p 'recovery' -C '' 'ẗesẗ'
|
||||
mpw_expect 'ne hapfa dax qamayqo' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a1 -p 'recovery' -C 'quesẗion' 'ẗesẗ'
|
||||
mpw_expect 'JlZo&eLhqgoxqtJ!NC5/' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tmax -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'SuxHot2*' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tmed -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'Jly28Veh' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tbasic -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'Sux2' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tshort -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect '4922' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tpin -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'suxhotito' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tname -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'su hotte pav calewxo' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'Luxn2#JapiXopa' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tlong -c4294967295 -a1 -p 'authentication' 'ẗesẗ'
|
||||
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a1 -p 'recovery' -C '' 'ẗesẗ'
|
||||
mpw_expect 'ne hapfa dax qamayqo' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a1 -p 'recovery' -C 'quesẗion' 'ẗesẗ'
|
||||
mpw_expect 'JlZo&eLhqgoxqtJ!NC5/' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tmax -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'SuxHot2*' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tmed -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'Jly28Veh' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tbasic -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'Sux2' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tshort -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect '4922' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tpin -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'suxhotito' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tname -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'su hotte pav calewxo' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a1 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'Luxn2#JapiXopa' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tlong -c4294967295 -a1 -p 'authentication' -C '' 'ẗesẗ'
|
||||
|
||||
## V0
|
||||
printf "\nV%d, none: " 0
|
||||
mpw_expect 'GeqoBigiFubh2!' -Fnone \
|
||||
-u 'test' -M 'test' -tlong -c1 -a0 -p 'authentication' -C '' 'test'
|
||||
mpw_expect 'WumiZobxGuhe8]' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'KuhaXimj8@Zebu' -Fnone \
|
||||
-u '⛄' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'CajtFayv9_Pego' -Fnone \
|
||||
-u 'tesẗ' -M '⛄' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'QohaPokgYevu2!' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' '⛄'
|
||||
mpw_expect 'takxabico' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tname -c1 -a0 -p 'identification' -C '' 'ẗesẗ'
|
||||
mpw_expect 'GeqoBigiFubh2!' -Fnone \
|
||||
-u 'test' -M 'test' -tlong -c1 -a0 -p 'authentication' -C '' 'test'
|
||||
mpw_expect 'WumiZobxGuhe8]' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'KuhaXimj8@Zebu' -Fnone \
|
||||
-u '⛄' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'CajtFayv9_Pego' -Fnone \
|
||||
-u 'tesẗ' -M '⛄' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'QohaPokgYevu2!' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' '⛄'
|
||||
mpw_expect 'takxabico' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tname -c1 -a0 -p 'identification' -C '' 'ẗesẗ'
|
||||
mpw_expect 'je tuxfo fut huzivlo' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a0 -p 'recovery' -C '' 'ẗesẗ'
|
||||
mpw_expect 'ye zahqa lam jatavmo' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a0 -p 'recovery' -C 'quesẗion' 'ẗesẗ'
|
||||
mpw_expect 'g4@)4SlA#)cJ#ib)vvH3' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tmax -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'Wum7_Xix' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tmed -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'gAo78ARD' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tbasic -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'Wum7' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tshort -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect '9427' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tpin -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'wumdoxixo' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tname -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'wu doxbe hac kaselqo' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'Pumy7.JadjQoda' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tlong -c4294967295 -a0 -p 'authentication' 'ẗesẗ'
|
||||
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a0 -p 'recovery' -C '' 'ẗesẗ'
|
||||
mpw_expect 'ye zahqa lam jatavmo' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a0 -p 'recovery' -C 'quesẗion' 'ẗesẗ'
|
||||
mpw_expect 'g4@)4SlA#)cJ#ib)vvH3' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tmax -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'Wum7_Xix' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tmed -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'gAo78ARD' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tbasic -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'Wum7' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tshort -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect '9427' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tpin -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'wumdoxixo' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tname -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'wu doxbe hac kaselqo' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tphrase -c1 -a0 -p 'authentication' -C '' 'ẗesẗ'
|
||||
mpw_expect 'Pumy7.JadjQoda' -Fnone \
|
||||
-u 'tesẗ' -M 'ẗest' -tlong -c4294967295 -a0 -p 'authentication' -C '' 'ẗesẗ'
|
||||
|
||||
## V3
|
||||
printf "\nV%d, flat: " 3
|
||||
mpw_expect 'IfHuAUUpqpKZDZlNvz8$' -Fflat -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tmax -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.max'
|
||||
mpw_expect 'FamiJirk1)Zehc' -Fflat -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.long'
|
||||
mpw_expect 'NofhMusw8+Cebo' -Fflat -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.⛄'
|
||||
mpw_expect 'Necx1$LagaRizu' -Fflat -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tlong -c4294967295 -a3 -p 'authentication' 'ẗesẗ.c+a3pa'
|
||||
mpw_expect 'Poq2)Tey' -Fflat -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tmed -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.med'
|
||||
mpw_expect 'Wr07Okx0' -Fflat -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tbasic -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.basic'
|
||||
mpw_expect 'Bug9' -Fflat -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tshort -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.short'
|
||||
mpw_expect '3560' -Fflat -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tpin -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.pin'
|
||||
mpw_expect 'jupxiqepi' -Fflat -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tname -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.name'
|
||||
mpw_expect 'vuh buxtukewo puhe' -Fflat -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tphrase -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.phrase'
|
||||
mpw_expect 'mophabiwe' -Fflat -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tname -c1 -a3 -p 'identification' -C '' 'ẗesẗ.c1a3pi'
|
||||
mpw_expect 'mup wulbezaxa juca' -Fflat -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tphrase -c1 -a3 -p 'recovery' -C '' 'ẗesẗ.c1a3pr'
|
||||
mpw_expect 'IfHuAUUpqpKZDZlNvz8$' -Fflat -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tmax -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.max'
|
||||
mpw_expect 'FamiJirk1)Zehc' -Fflat -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.long'
|
||||
mpw_expect 'NofhMusw8+Cebo' -Fflat -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.⛄'
|
||||
mpw_expect 'Necx1$LagaRizu' -Fflat -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -c4294967295 -a3 -p 'authentication' -C '' 'ẗesẗ.c+a3pa'
|
||||
mpw_expect 'Poq2)Tey' -Fflat -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tmed -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.med'
|
||||
mpw_expect 'Wr07Okx0' -Fflat -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tbasic -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.basic'
|
||||
mpw_expect 'Bug9' -Fflat -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tshort -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.short'
|
||||
mpw_expect '3560' -Fflat -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tpin -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.pin'
|
||||
mpw_expect 'jupxiqepi' -Fflat -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tname -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.name'
|
||||
mpw_expect 'vuh buxtukewo puhe' -Fflat -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tphrase -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.phrase'
|
||||
mpw_expect 'Cq5$TfH#OHmPS9yREp7)' -Fflat -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tmax -c1 -a3 -p 'identification' -C '' 'ẗesẗ.c1a3pi.max'
|
||||
mpw_expect 'mophabiwe' -Fflat -R0 \
|
||||
-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 \
|
||||
-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
|
||||
printf "\nV%d, flat: " 2
|
||||
mpw_expect 'i7@0M*DdP4DgD#jJIzyL' -Fflat -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tmax -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.max'
|
||||
mpw_expect 'Lifw5]DablSuga' -Fflat -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.long'
|
||||
mpw_expect 'Leja5%RavoZapa' -Fflat -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.⛄'
|
||||
mpw_expect 'NejnGazo8?Seqo' -Fflat -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tlong -c4294967295 -a2 -p 'authentication' 'ẗesẗ.c+a2pa'
|
||||
mpw_expect 'XicSux2&' -Fflat -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tmed -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.med'
|
||||
mpw_expect 'uEY50hcZ' -Fflat -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tbasic -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.basic'
|
||||
mpw_expect 'Jif6' -Fflat -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tshort -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.short'
|
||||
mpw_expect '4001' -Fflat -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tpin -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.pin'
|
||||
mpw_expect 'rexmibace' -Fflat -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tname -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.name'
|
||||
mpw_expect 'cez fexlemozo yula' -Fflat -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tphrase -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.phrase'
|
||||
mpw_expect 'camfibeye' -Fflat -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tname -c1 -a2 -p 'identification' -C '' 'ẗesẗ.c1a2pi'
|
||||
mpw_expect 'ye vemcu keq xepewmi' -Fflat -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tphrase -c1 -a2 -p 'recovery' -C '' 'ẗesẗ.c1a2pr'
|
||||
mpw_expect 'i7@0M*DdP4DgD#jJIzyL' -Fflat -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tmax -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.max'
|
||||
mpw_expect 'Lifw5]DablSuga' -Fflat -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.long'
|
||||
mpw_expect 'Leja5%RavoZapa' -Fflat -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.⛄'
|
||||
mpw_expect 'NejnGazo8?Seqo' -Fflat -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -c4294967295 -a2 -p 'authentication' -C '' 'ẗesẗ.c+a2pa'
|
||||
mpw_expect 'XicSux2&' -Fflat -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tmed -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.med'
|
||||
mpw_expect 'uEY50hcZ' -Fflat -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tbasic -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.basic'
|
||||
mpw_expect 'Jif6' -Fflat -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tshort -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.short'
|
||||
mpw_expect '4001' -Fflat -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tpin -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.pin'
|
||||
mpw_expect 'rexmibace' -Fflat -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tname -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.name'
|
||||
mpw_expect 'cez fexlemozo yula' -Fflat -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tphrase -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.phrase'
|
||||
mpw_expect 'T8+xi4NMd3HUGdV#GW*%' -Fflat -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tmax -c1 -a2 -p 'identification' -C '' 'ẗesẗ.c1a2pi.max'
|
||||
mpw_expect 'camfibeye' -Fflat -R0 \
|
||||
-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 \
|
||||
-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
|
||||
printf "\nV%d, flat: " 1
|
||||
mpw_expect 'a3~AiGkHk)Pgjbb)mk6H' -Fflat -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tmax -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.max'
|
||||
mpw_expect 'Lojz6?VotaJall' -Fflat -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.long'
|
||||
mpw_expect 'Yoqu7)NiziFito' -Fflat -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.⛄'
|
||||
mpw_expect 'Foha4[TojmXanc' -Fflat -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tlong -c4294967295 -a1 -p 'authentication' 'ẗesẗ.c+a1pa'
|
||||
mpw_expect 'Hiy3*Zag' -Fflat -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tmed -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.med'
|
||||
mpw_expect 'UJR7HpG0' -Fflat -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tbasic -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.basic'
|
||||
mpw_expect 'Cij7' -Fflat -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tshort -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.short'
|
||||
mpw_expect '0020' -Fflat -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tpin -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.pin'
|
||||
mpw_expect 'vadxovezu' -Fflat -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tname -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.name'
|
||||
mpw_expect 'sij jihloyenu kizi' -Fflat -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tphrase -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.phrase'
|
||||
mpw_expect 'qipberize' -Fflat -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tname -c1 -a1 -p 'identification' -C '' 'ẗesẗ.c1a1pi'
|
||||
mpw_expect 'sok torxibute reza' -Fflat -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tphrase -c1 -a1 -p 'recovery' -C '' 'ẗesẗ.c1a1pr'
|
||||
mpw_expect 'a3~AiGkHk)Pgjbb)mk6H' -Fflat -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tmax -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.max'
|
||||
mpw_expect 'Lojz6?VotaJall' -Fflat -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.long'
|
||||
mpw_expect 'Yoqu7)NiziFito' -Fflat -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.⛄'
|
||||
mpw_expect 'Foha4[TojmXanc' -Fflat -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -c4294967295 -a1 -p 'authentication' -C '' 'ẗesẗ.c+a1pa'
|
||||
mpw_expect 'Hiy3*Zag' -Fflat -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tmed -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.med'
|
||||
mpw_expect 'UJR7HpG0' -Fflat -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tbasic -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.basic'
|
||||
mpw_expect 'Cij7' -Fflat -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tshort -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.short'
|
||||
mpw_expect '0020' -Fflat -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tpin -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.pin'
|
||||
mpw_expect 'vadxovezu' -Fflat -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tname -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.name'
|
||||
mpw_expect 'sij jihloyenu kizi' -Fflat -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tphrase -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.phrase'
|
||||
mpw_expect 'z2U9)(uQ78TXqtaus)8.' -Fflat -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tmax -c1 -a1 -p 'identification' -C '' 'ẗesẗ.c1a1pi.max'
|
||||
mpw_expect 'qipberize' -Fflat -R0 \
|
||||
-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 \
|
||||
-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
|
||||
printf "\nV%d, flat: " 0
|
||||
mpw_expect 'b5@ww@Jmb4cAioRbivb)' -Fflat -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tmax -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.max'
|
||||
mpw_expect 'ZuceHazwLojz8!' -Fflat -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.long'
|
||||
mpw_expect 'Boxj2!YabePodp' -Fflat -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.⛄'
|
||||
mpw_expect 'PeblLuqc6]Cala' -Fflat -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tlong -c4294967295 -a0 -p 'authentication' 'ẗesẗ.c+a0pa'
|
||||
mpw_expect 'XelQac0@' -Fflat -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tmed -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.med'
|
||||
mpw_expect 'qS07SRc8' -Fflat -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tbasic -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.basic'
|
||||
mpw_expect 'Fih8' -Fflat -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tshort -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.short'
|
||||
mpw_expect '6121' -Fflat -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tpin -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.pin'
|
||||
mpw_expect 'rivfutipe' -Fflat -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tname -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.name'
|
||||
mpw_expect 'xir qebdohogo buno' -Fflat -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tphrase -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.phrase'
|
||||
mpw_expect 'ragcoxudo' -Fflat -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tname -c1 -a0 -p 'identification' -C '' 'ẗesẗ.c1a0pi'
|
||||
mpw_expect 'kokl hov lowmaya xaf' -Fflat -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tphrase -c1 -a0 -p 'recovery' -C '' 'ẗesẗ.c1a0pr'
|
||||
mpw_expect 'wi zanmu nug zuwidwe' -Fflat -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tphrase -c1 -a0 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a0pr.quesẗion'
|
||||
mpw_expect 'b5@ww@Jmb4cAioRbivb)' -Fflat -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tmax -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.max'
|
||||
mpw_expect 'ZuceHazwLojz8!' -Fflat -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.long'
|
||||
mpw_expect 'Boxj2!YabePodp' -Fflat -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.⛄'
|
||||
mpw_expect 'PeblLuqc6]Cala' -Fflat -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -c4294967295 -a0 -p 'authentication' -C '' 'ẗesẗ.c+a0pa'
|
||||
mpw_expect 'XelQac0@' -Fflat -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tmed -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.med'
|
||||
mpw_expect 'qS07SRc8' -Fflat -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tbasic -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.basic'
|
||||
mpw_expect 'Fih8' -Fflat -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tshort -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.short'
|
||||
mpw_expect '6121' -Fflat -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tpin -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.pin'
|
||||
mpw_expect 'rivfutipe' -Fflat -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tname -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.name'
|
||||
mpw_expect 'xir qebdohogo buno' -Fflat -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tphrase -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.phrase'
|
||||
mpw_expect "RoAm3bJSvo@#loHSRA6\'" -Fflat -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tmax -c1 -a0 -p 'identification' -C '' 'ẗesẗ.c1a0pi.max'
|
||||
mpw_expect 'ragcoxudo' -Fflat -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -c1 -a0 -p 'identification' -C '' 'ẗesẗ.c1a0pi'
|
||||
mpw_expect 'm8]SiJHiAS@H@Rbw))34' -Fflat -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tmax -c1 -a0 -p 'recovery' -C '' 'ẗesẗ.c1a0pr.max'
|
||||
mpw_expect 'kokl hov lowmaya xaf' -Fflat -R0 \
|
||||
-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
|
||||
printf "\nV%d, json: " 3
|
||||
mpw_expect 'IfHuAUUpqpKZDZlNvz8$' -Fjson -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tmax -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.max'
|
||||
mpw_expect 'FamiJirk1)Zehc' -Fjson -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.long'
|
||||
mpw_expect 'NofhMusw8+Cebo' -Fjson -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.⛄'
|
||||
mpw_expect 'Necx1$LagaRizu' -Fjson -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tlong -c4294967295 -a3 -p 'authentication' 'ẗesẗ.c+a3pa'
|
||||
mpw_expect 'Poq2)Tey' -Fjson -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tmed -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.med'
|
||||
mpw_expect 'Wr07Okx0' -Fjson -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tbasic -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.basic'
|
||||
mpw_expect 'Bug9' -Fjson -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tshort -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.short'
|
||||
mpw_expect '3560' -Fjson -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tpin -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.pin'
|
||||
mpw_expect 'jupxiqepi' -Fjson -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tname -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.name'
|
||||
mpw_expect 'vuh buxtukewo puhe' -Fjson -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tphrase -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.phrase'
|
||||
mpw_expect 'mophabiwe' -Fjson -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tname -c1 -a3 -p 'identification' -C '' 'ẗesẗ.c1a3pi'
|
||||
mpw_expect 'mup wulbezaxa juca' -Fjson -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tphrase -c1 -a3 -p 'recovery' -C '' 'ẗesẗ.c1a3pr'
|
||||
mpw_expect 'IfHuAUUpqpKZDZlNvz8$' -Fjson -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tmax -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.max'
|
||||
mpw_expect 'FamiJirk1)Zehc' -Fjson -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.long'
|
||||
mpw_expect 'NofhMusw8+Cebo' -Fjson -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tlong -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.⛄'
|
||||
mpw_expect 'Necx1$LagaRizu' -Fjson -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -c4294967295 -a3 -p 'authentication' -C '' 'ẗesẗ.c+a3pa'
|
||||
mpw_expect 'Poq2)Tey' -Fjson -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tmed -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.med'
|
||||
mpw_expect 'Wr07Okx0' -Fjson -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tbasic -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.basic'
|
||||
mpw_expect 'Bug9' -Fjson -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tshort -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.short'
|
||||
mpw_expect '3560' -Fjson -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tpin -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.pin'
|
||||
mpw_expect 'jupxiqepi' -Fjson -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tname -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.name'
|
||||
mpw_expect 'vuh buxtukewo puhe' -Fjson -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tphrase -c1 -a3 -p 'authentication' -C '' 'ẗesẗ.c1a3pa.phrase'
|
||||
mpw_expect 'Cq5$TfH#OHmPS9yREp7)' -Fjson -R0 \
|
||||
-u 'tesẗ.v3' -M 'ẗest' -tmax -c1 -a3 -p 'identification' -C '' 'ẗesẗ.c1a3pi.max'
|
||||
mpw_expect 'mophabiwe' -Fjson -R0 \
|
||||
-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 \
|
||||
-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
|
||||
printf "\nV%d, json: " 2
|
||||
mpw_expect 'i7@0M*DdP4DgD#jJIzyL' -Fjson -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tmax -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.max'
|
||||
mpw_expect 'Lifw5]DablSuga' -Fjson -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.long'
|
||||
mpw_expect 'Leja5%RavoZapa' -Fjson -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.⛄'
|
||||
mpw_expect 'NejnGazo8?Seqo' -Fjson -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tlong -c4294967295 -a2 -p 'authentication' 'ẗesẗ.c+a2pa'
|
||||
mpw_expect 'XicSux2&' -Fjson -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tmed -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.med'
|
||||
mpw_expect 'uEY50hcZ' -Fjson -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tbasic -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.basic'
|
||||
mpw_expect 'Jif6' -Fjson -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tshort -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.short'
|
||||
mpw_expect '4001' -Fjson -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tpin -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.pin'
|
||||
mpw_expect 'rexmibace' -Fjson -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tname -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.name'
|
||||
mpw_expect 'cez fexlemozo yula' -Fjson -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tphrase -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.phrase'
|
||||
mpw_expect 'camfibeye' -Fjson -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tname -c1 -a2 -p 'identification' -C '' 'ẗesẗ.c1a2pi'
|
||||
mpw_expect 'ye vemcu keq xepewmi' -Fjson -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tphrase -c1 -a2 -p 'recovery' -C '' 'ẗesẗ.c1a2pr'
|
||||
mpw_expect 'i7@0M*DdP4DgD#jJIzyL' -Fjson -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tmax -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.max'
|
||||
mpw_expect 'Lifw5]DablSuga' -Fjson -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.long'
|
||||
mpw_expect 'Leja5%RavoZapa' -Fjson -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tlong -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.⛄'
|
||||
mpw_expect 'NejnGazo8?Seqo' -Fjson -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -c4294967295 -a2 -p 'authentication' -C '' 'ẗesẗ.c+a2pa'
|
||||
mpw_expect 'XicSux2&' -Fjson -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tmed -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.med'
|
||||
mpw_expect 'uEY50hcZ' -Fjson -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tbasic -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.basic'
|
||||
mpw_expect 'Jif6' -Fjson -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tshort -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.short'
|
||||
mpw_expect '4001' -Fjson -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tpin -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.pin'
|
||||
mpw_expect 'rexmibace' -Fjson -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tname -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.name'
|
||||
mpw_expect 'cez fexlemozo yula' -Fjson -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tphrase -c1 -a2 -p 'authentication' -C '' 'ẗesẗ.c1a2pa.phrase'
|
||||
mpw_expect 'T8+xi4NMd3HUGdV#GW*%' -Fjson -R0 \
|
||||
-u 'tesẗ.v2' -M 'ẗest' -tmax -c1 -a2 -p 'identification' -C '' 'ẗesẗ.c1a2pi.max'
|
||||
mpw_expect 'camfibeye' -Fjson -R0 \
|
||||
-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 \
|
||||
-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
|
||||
printf "\nV%d, json: " 1
|
||||
mpw_expect 'a3~AiGkHk)Pgjbb)mk6H' -Fjson -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tmax -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.max'
|
||||
mpw_expect 'Lojz6?VotaJall' -Fjson -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.long'
|
||||
mpw_expect 'Yoqu7)NiziFito' -Fjson -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.⛄'
|
||||
mpw_expect 'Foha4[TojmXanc' -Fjson -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tlong -c4294967295 -a1 -p 'authentication' 'ẗesẗ.c+a1pa'
|
||||
mpw_expect 'Hiy3*Zag' -Fjson -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tmed -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.med'
|
||||
mpw_expect 'UJR7HpG0' -Fjson -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tbasic -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.basic'
|
||||
mpw_expect 'Cij7' -Fjson -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tshort -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.short'
|
||||
mpw_expect '0020' -Fjson -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tpin -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.pin'
|
||||
mpw_expect 'vadxovezu' -Fjson -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tname -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.name'
|
||||
mpw_expect 'sij jihloyenu kizi' -Fjson -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tphrase -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.phrase'
|
||||
mpw_expect 'qipberize' -Fjson -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tname -c1 -a1 -p 'identification' -C '' 'ẗesẗ.c1a1pi'
|
||||
mpw_expect 'sok torxibute reza' -Fjson -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tphrase -c1 -a1 -p 'recovery' -C '' 'ẗesẗ.c1a1pr'
|
||||
mpw_expect 'a3~AiGkHk)Pgjbb)mk6H' -Fjson -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tmax -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.max'
|
||||
mpw_expect 'Lojz6?VotaJall' -Fjson -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.long'
|
||||
mpw_expect 'Yoqu7)NiziFito' -Fjson -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tlong -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.⛄'
|
||||
mpw_expect 'Foha4[TojmXanc' -Fjson -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -c4294967295 -a1 -p 'authentication' -C '' 'ẗesẗ.c+a1pa'
|
||||
mpw_expect 'Hiy3*Zag' -Fjson -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tmed -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.med'
|
||||
mpw_expect 'UJR7HpG0' -Fjson -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tbasic -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.basic'
|
||||
mpw_expect 'Cij7' -Fjson -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tshort -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.short'
|
||||
mpw_expect '0020' -Fjson -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tpin -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.pin'
|
||||
mpw_expect 'vadxovezu' -Fjson -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tname -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.name'
|
||||
mpw_expect 'sij jihloyenu kizi' -Fjson -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tphrase -c1 -a1 -p 'authentication' -C '' 'ẗesẗ.c1a1pa.phrase'
|
||||
mpw_expect 'z2U9)(uQ78TXqtaus)8.' -Fjson -R0 \
|
||||
-u 'tesẗ.v1' -M 'ẗest' -tmax -c1 -a1 -p 'identification' -C '' 'ẗesẗ.c1a1pi.max'
|
||||
mpw_expect 'qipberize' -Fjson -R0 \
|
||||
-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 \
|
||||
-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
|
||||
printf "\nV%d, json: " 0
|
||||
mpw_expect 'b5@ww@Jmb4cAioRbivb)' -Fjson -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tmax -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.max'
|
||||
mpw_expect 'ZuceHazwLojz8!' -Fjson -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.long'
|
||||
mpw_expect 'Boxj2!YabePodp' -Fjson -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.⛄'
|
||||
mpw_expect 'PeblLuqc6]Cala' -Fjson -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tlong -c4294967295 -a0 -p 'authentication' 'ẗesẗ.c+a0pa'
|
||||
mpw_expect 'XelQac0@' -Fjson -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tmed -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.med'
|
||||
mpw_expect 'qS07SRc8' -Fjson -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tbasic -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.basic'
|
||||
mpw_expect 'Fih8' -Fjson -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tshort -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.short'
|
||||
mpw_expect '6121' -Fjson -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tpin -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.pin'
|
||||
mpw_expect 'rivfutipe' -Fjson -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tname -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.name'
|
||||
mpw_expect 'xir qebdohogo buno' -Fjson -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tphrase -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.phrase'
|
||||
mpw_expect 'ragcoxudo' -Fjson -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tname -c1 -a0 -p 'identification' -C '' 'ẗesẗ.c1a0pi'
|
||||
mpw_expect 'kokl hov lowmaya xaf' -Fjson -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tphrase -c1 -a0 -p 'recovery' -C '' 'ẗesẗ.c1a0pr'
|
||||
mpw_expect 'wi zanmu nug zuwidwe' -Fjson -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tphrase -c1 -a0 -p 'recovery' -C 'quesẗion' 'ẗesẗ.c1a0pr.quesẗion'
|
||||
mpw_expect 'b5@ww@Jmb4cAioRbivb)' -Fjson -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tmax -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.max'
|
||||
mpw_expect 'ZuceHazwLojz8!' -Fjson -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.long'
|
||||
mpw_expect 'Boxj2!YabePodp' -Fjson -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tlong -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.⛄'
|
||||
mpw_expect 'PeblLuqc6]Cala' -Fjson -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -c4294967295 -a0 -p 'authentication' -C '' 'ẗesẗ.c+a0pa'
|
||||
mpw_expect 'XelQac0@' -Fjson -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tmed -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.med'
|
||||
mpw_expect 'qS07SRc8' -Fjson -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tbasic -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.basic'
|
||||
mpw_expect 'Fih8' -Fjson -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tshort -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.short'
|
||||
mpw_expect '6121' -Fjson -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tpin -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.pin'
|
||||
mpw_expect 'rivfutipe' -Fjson -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tname -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.name'
|
||||
mpw_expect 'xir qebdohogo buno' -Fjson -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tphrase -c1 -a0 -p 'authentication' -C '' 'ẗesẗ.c1a0pa.phrase'
|
||||
mpw_expect "RoAm3bJSvo@#loHSRA6\'" -Fjson -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tmax -c1 -a0 -p 'identification' -C '' 'ẗesẗ.c1a0pi.max'
|
||||
mpw_expect 'ragcoxudo' -Fjson -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -c1 -a0 -p 'identification' -C '' 'ẗesẗ.c1a0pi'
|
||||
mpw_expect 'm8]SiJHiAS@H@Rbw))34' -Fjson -R0 \
|
||||
-u 'tesẗ.v0' -M 'ẗest' -tmax -c1 -a0 -p 'recovery' -C '' 'ẗesẗ.c1a0pr.max'
|
||||
mpw_expect 'kokl hov lowmaya xaf' -Fjson -R0 \
|
||||
-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
|
||||
|
@ -44,7 +44,6 @@ library {
|
||||
def arch = targetMachine.getArchitecture().getName().replace('-', '_')
|
||||
|
||||
compile.macros.put("MPW_SODIUM", "1")
|
||||
compile.macros.put("MPW_LOG", "mpw_log_app")
|
||||
|
||||
dependencies {
|
||||
// libsodium
|
||||
@ -75,7 +74,7 @@ library {
|
||||
link.linkerArgs = ['-lc', '-nodefaultlibs', '-flto']
|
||||
} else if (toolChain in VisualCpp) {
|
||||
// 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']
|
||||
}
|
||||
}
|
||||
}
|
||||
|