2
0

Compare commits

..

17 Commits

Author SHA1 Message Date
Maarten Billemont
c99252809d Disable ADHOC and tester functionality / bypasses. 2017-04-18 20:31:38 -04:00
Maarten Billemont
d704f451a3 Fixed issue causing emergency generator password button to not respond. 2017-04-17 22:27:36 -04:00
Maarten Billemont
2c9ab5d153 Fixed issue when cancelling touchID login. 2017-04-17 22:13:01 -04:00
Maarten Billemont
d5d33da12f Fixed UI issues with passwords list and drop-down animation + support for phrase and name default types.
[FIXED]     Fixed issues with animating changes in the passwords list during certain & multiple events.
[FIXED]     Slightly broken UI prior to drop-down animation & improved animation a bit.
[ADDED]     Phrase & Name default password types.
2017-04-17 21:57:08 -04:00
Maarten Billemont
cbef1a611b Update Mac binary to 2.5-mac-2 2017-04-16 13:03:15 -04:00
Maarten Billemont
0a1f215a1a Style login name, add login generated gear, improve logic for when to show login name. 2017-04-15 10:57:52 -04:00
Maarten Billemont
907d2a8ca6 Fixed key disappearing from NSCache after suspension and not being reloaded from keychain. 2017-04-15 02:28:11 -04:00
Maarten Billemont
89f6e77f67 Hack to ensure Xcode doesn't incorrectly link libsodium.dylib instead of libsodium.a 2017-04-14 17:23:41 -04:00
Maarten Billemont
f2fb16a0b9 Improved library check that doesn't depend on wording of error message. 2017-04-14 17:01:24 -04:00
Maarten Billemont
e3edd42b88 Fixed a bug in PearlMutableStaticTableViewController. 2017-04-14 16:10:11 -04:00
Maarten Billemont
cc5d246d7d Update Storyboard. 2017-04-14 13:20:13 -04:00
Maarten Billemont
ca320de6d9 Fix detection of ios platforms. 2017-04-14 13:08:23 -04:00
Maarten Billemont
ae979d7240 Fix up the benefits wording. 2017-04-14 10:13:44 -04:00
Maarten Billemont
eb1c443940 Small README tweaks. 2017-04-14 09:58:35 -04:00
Maarten Billemont
dadcefc9bf FAQ. 2017-04-14 09:51:44 -04:00
Maarten Billemont
cdbaec9751 Explain configuration requirements for the Android SDK. 2017-04-14 09:13:31 -04:00
Maarten Billemont
f48d480c77 Compacted build instructions a bit. 2017-04-14 09:01:04 -04:00
26 changed files with 901 additions and 928 deletions

View File

@@ -15,13 +15,23 @@ To skip the intro and go straight to the information on how to use the code, [cl
Master Password is available for [📲 iOS](https://itunes.apple.com/app/id510296984), [🖥 macOS](https://ssl.masterpasswordapp.com/masterpassword-mac.zip), [📲 Android](https://ssl.masterpasswordapp.com/masterpassword-android.apk), [🖥 Desktop](https://ssl.masterpasswordapp.com/masterpassword-gui.jar), and [⌨ Console](https://ssl.masterpasswordapp.com/masterpassword-cli.tar.gz). Master Password is available for [📲 iOS](https://itunes.apple.com/app/id510296984), [🖥 macOS](https://ssl.masterpasswordapp.com/masterpassword-mac.zip), [📲 Android](https://ssl.masterpasswordapp.com/masterpassword-android.apk), [🖥 Desktop](https://ssl.masterpasswordapp.com/masterpassword-gui.jar), and [⌨ Console](https://ssl.masterpasswordapp.com/masterpassword-cli.tar.gz).
Master Password is also available from the following package managers: [macOS: Homebrew](https://brew.sh/). Get in touch if you are interested in adding Master Password to any other package managers. Master Password is also available from the following package managers: [macOS: Homebrew](https://brew.sh/) (`brew install mpw`).
Get in touch if you are interested in adding Master Password to any other package managers.
There are many reasons for using Master Password instead of an ordinary password manager, read below for the details, but if you want my personal favourites, they would be:
- 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.
We also have a [Frequently Asked Questions](#faq).
## What is a password? ## What is a password?
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: 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. 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.
@@ -69,12 +79,12 @@ Master Password is *not* a password manager. It does not store your website pas
## Benefits ## Benefits
- You don't need to come up with a secure password every time you make a new account - Master Password gives you the key for it. - 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 to remember 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 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 that you can't get into that account you made at work when you come home because you don't have your work passwords with you - Master Password is always available. - 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 is always available. - 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 - Master Password is always available. - 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 keeps no records. - 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.
@@ -94,6 +104,35 @@ We standardize `user-name` as your full name, `site-name` as the domain name of
## 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?
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.
2. But what if I just forget my master password or I just want to change it to something else?
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`.
3. Doesn't this mean an attacker can reverse my master password from any of my site passwords?
Technically, yes. Practically, no.
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.
4. The second step is just a HMAC-SHA-256, doesn't that make the SCRYPT completely pointless?
No. They are used for different reasons and one is not weaker than the other.
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 GitHub issues](https://github.com/Lyndir/MasterPassword/issues).
# Source Code # Source Code
@@ -146,6 +185,10 @@ Go into the `gradle` directory and run `./gradlew build`. All Java components w
- `platform-android/build/outputs/apk`: - `platform-android/build/outputs/apk`:
contains the Android application package. Install it on your Android device. 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 ### Native CLI
@@ -158,7 +201,7 @@ For example:
./build && sudo ./install ./build && sudo ./install
mpw -h mpw -h
Normally, this is all you need to do, however note that there are a few dependencies that need to be met, depending on which targets you are building: Normally, this is all you need to do, however note that there are a few dependencies that need to be met, depending on which targets you are building (by default, only the `mpw` target is built):
- `mpw` - `mpw`
@@ -176,28 +219,24 @@ This tool compares the performance of a few cryptographic algorithms, including
This tool runs a suite of tests to ensure the correct passwords are being generated by the algorithm under various circumstances. The test suite is declared in `mpw-tests.xml` which needs to exist in the current working directory when running the tool. In addition, `libxml2` is used to parse the file, so this target depends on you having it installed when running `./build`. This tool runs a suite of tests to ensure the correct passwords are being generated by the algorithm under various circumstances. The test suite is declared in `mpw-tests.xml` which needs to exist in the current working directory when running the tool. In addition, `libxml2` is used to parse the file, so this target depends on you having it installed when running `./build`.
Finally, there are a few different ways you can modify the build process:
- You can change the targets that should be built. By default, only `mpw` is built. These are the available targets: Finally, there are a few different ways you can modify the build process.
- `mpw`: This is the standard command-line `mpw` tool which implements all Master Password features.
- `mpw-tests`: This is a tool to perform the standard tests script on the `mpw` implementation.
- `mpw-bench`: This is a tool to run a benchmark on the `mpw` implementation, comparing it to the performance of other algorithms.
- You can specify custom arguments to the compiler, pass them as arguments to the build script.
- The build process involves some optionals, they can by toggled from their default setting by passing variables:
- `mpw_color`: [default: 1] Colorized Identicon, depends on
To change the targets to build, use: To build additional targets, set the `targets` environment variable:
targets='mpw mpw-tests' ./build targets='mpw mpw-tests' ./build
To add a library search path, use: To pass additional compiler arguments, eg. add a library search path, pass them as arguments to the script:
./build -L/usr/local/lib ./build -L/usr/local/lib
Change an optional feature: There are a few toggleable features, to change them, pass them as environment variables:
mpw_color=0 ./build mpw_color=0 ./build
Currently, there is only one toggleable feature:
- `mpw_color`: [default: 1] Colorized Identicon, depends on `ncurses-dev`.
## Support ## Support

View File

@@ -12,6 +12,7 @@
93D3922A53E41A54832E90D9 /* PearlOverlay.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D390FADEB325D8D54A957D /* PearlOverlay.m */; }; 93D3922A53E41A54832E90D9 /* PearlOverlay.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D390FADEB325D8D54A957D /* PearlOverlay.m */; };
93D39262A8A97DB748213309 /* PearlEMail.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D393BB973253D4BAAC84AA /* PearlEMail.m */; }; 93D39262A8A97DB748213309 /* PearlEMail.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D393BB973253D4BAAC84AA /* PearlEMail.m */; };
93D3928D629EA563F9EC4909 /* NSPersistentStore+PearlMigration.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399C2F3D48E57C4803BDC /* NSPersistentStore+PearlMigration.m */; }; 93D3928D629EA563F9EC4909 /* NSPersistentStore+PearlMigration.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399C2F3D48E57C4803BDC /* NSPersistentStore+PearlMigration.m */; };
93D392A33CCE85431E910C7B /* NSOrderedSetOrArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39789AAF49338F8AC8B02 /* NSOrderedSetOrArray.m */; };
93D392A8777DC30C11361647 /* UITextView+PearlAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39AA10CD00D05937671B1 /* UITextView+PearlAttributes.h */; }; 93D392A8777DC30C11361647 /* UITextView+PearlAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39AA10CD00D05937671B1 /* UITextView+PearlAttributes.h */; };
93D392EC39DA43C46C692C12 /* NSDictionary+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */; }; 93D392EC39DA43C46C692C12 /* NSDictionary+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */; };
93D392FD5E2052F7D7DB3774 /* NSString+MPMarkDown.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39C41A27AA42D044D68AE /* NSString+MPMarkDown.m */; }; 93D392FD5E2052F7D7DB3774 /* NSString+MPMarkDown.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39C41A27AA42D044D68AE /* NSString+MPMarkDown.m */; };
@@ -41,6 +42,7 @@
93D399433EA75E50656040CB /* Twitter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93D394077F8FAB8167647187 /* Twitter.framework */; }; 93D399433EA75E50656040CB /* Twitter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93D394077F8FAB8167647187 /* Twitter.framework */; };
93D39943D01E70DAC3B0DF76 /* mpw-util.c in Sources */ = {isa = PBXBuildFile; fileRef = 93D396C311C3725870343EE0 /* mpw-util.c */; }; 93D39943D01E70DAC3B0DF76 /* mpw-util.c in Sources */ = {isa = PBXBuildFile; fileRef = 93D396C311C3725870343EE0 /* mpw-util.c */; };
93D399D7E08A142776A74CB8 /* MPOverlayViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D395105935859D71679931 /* MPOverlayViewController.m */; }; 93D399D7E08A142776A74CB8 /* MPOverlayViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D395105935859D71679931 /* MPOverlayViewController.m */; };
93D399E4BC1E092A8C8B12AE /* NSOrderedSetOrArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39FBF8FCEB4C106272334 /* NSOrderedSetOrArray.h */; };
93D39A27F2506C6FEEF9C588 /* MPAlgorithmV2.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399A8E3181B442D347CD7 /* MPAlgorithmV2.m */; }; 93D39A27F2506C6FEEF9C588 /* MPAlgorithmV2.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399A8E3181B442D347CD7 /* MPAlgorithmV2.m */; };
93D39A53D76CA70786423458 /* UICollectionView+PearlReloadFromArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadFromArray.h */; }; 93D39A53D76CA70786423458 /* UICollectionView+PearlReloadFromArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadFromArray.h */; };
93D39A5FF670957C0AF8298D /* MPPasswordCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39DEA995041A13DC9CAF7 /* MPPasswordCell.m */; }; 93D39A5FF670957C0AF8298D /* MPPasswordCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39DEA995041A13DC9CAF7 /* MPPasswordCell.m */; };
@@ -72,8 +74,8 @@
DA24EBEA19DAD6EE00FF010B /* Icon-Small.png in Resources */ = {isa = PBXBuildFile; fileRef = DA24EBBA19DAD4D000FF010B /* Icon-Small.png */; }; DA24EBEA19DAD6EE00FF010B /* Icon-Small.png in Resources */ = {isa = PBXBuildFile; fileRef = DA24EBBA19DAD4D000FF010B /* Icon-Small.png */; };
DA24EBEB19DAD6EE00FF010B /* Icon-Small@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA24EBBB19DAD4D000FF010B /* Icon-Small@2x.png */; }; DA24EBEB19DAD6EE00FF010B /* Icon-Small@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA24EBBB19DAD4D000FF010B /* Icon-Small@2x.png */; };
DA24EBEC19DAD6EE00FF010B /* Icon-Small@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA24EBBC19DAD4D000FF010B /* Icon-Small@3x.png */; }; DA24EBEC19DAD6EE00FF010B /* Icon-Small@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA24EBBC19DAD4D000FF010B /* Icon-Small@3x.png */; };
DA250A17195665A100AC23F1 /* UITableView+PearlReloadFromArray.m in Sources */ = {isa = PBXBuildFile; fileRef = DA250A13195665A100AC23F1 /* UITableView+PearlReloadFromArray.m */; }; DA250A17195665A100AC23F1 /* UITableView+PearlReloadItems.m in Sources */ = {isa = PBXBuildFile; fileRef = DA250A13195665A100AC23F1 /* UITableView+PearlReloadItems.m */; };
DA250A18195665A100AC23F1 /* UITableView+PearlReloadFromArray.h in Headers */ = {isa = PBXBuildFile; fileRef = DA250A14195665A100AC23F1 /* UITableView+PearlReloadFromArray.h */; }; DA250A18195665A100AC23F1 /* UITableView+PearlReloadItems.h in Headers */ = {isa = PBXBuildFile; fileRef = DA250A14195665A100AC23F1 /* UITableView+PearlReloadItems.h */; };
DA250A19195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m in Sources */ = {isa = PBXBuildFile; fileRef = DA250A15195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m */; }; DA250A19195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m in Sources */ = {isa = PBXBuildFile; fileRef = DA250A15195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m */; };
DA250A1A195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.h in Headers */ = {isa = PBXBuildFile; fileRef = DA250A16195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.h */; }; DA250A1A195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.h in Headers */ = {isa = PBXBuildFile; fileRef = DA250A16195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.h */; };
DA25C5F8197AFFB40046CDCF /* icon_tools.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD384C1711E29700CF925C /* icon_tools.png */; }; DA25C5F8197AFFB40046CDCF /* icon_tools.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD384C1711E29700CF925C /* icon_tools.png */; };
@@ -493,6 +495,7 @@
93D3971FE104BB4052484151 /* MPUsersViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUsersViewController.h; sourceTree = "<group>"; }; 93D3971FE104BB4052484151 /* MPUsersViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUsersViewController.h; sourceTree = "<group>"; };
93D39730673227EFF6DEFF19 /* MPSetupViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSetupViewController.h; sourceTree = "<group>"; }; 93D39730673227EFF6DEFF19 /* MPSetupViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSetupViewController.h; sourceTree = "<group>"; };
93D3977321EB249981821AB0 /* UITextView+PearlAttributes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITextView+PearlAttributes.m"; sourceTree = "<group>"; }; 93D3977321EB249981821AB0 /* UITextView+PearlAttributes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITextView+PearlAttributes.m"; sourceTree = "<group>"; };
93D39789AAF49338F8AC8B02 /* NSOrderedSetOrArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSOrderedSetOrArray.m; sourceTree = "<group>"; };
93D3979190DACEBD1F6AE9F4 /* MPLogsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPLogsViewController.m; sourceTree = "<group>"; }; 93D3979190DACEBD1F6AE9F4 /* MPLogsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPLogsViewController.m; sourceTree = "<group>"; };
93D397F4BAFFF7CF3F1B21A4 /* NSPersistentStore+PearlMigration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSPersistentStore+PearlMigration.h"; sourceTree = "<group>"; }; 93D397F4BAFFF7CF3F1B21A4 /* NSPersistentStore+PearlMigration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSPersistentStore+PearlMigration.h"; sourceTree = "<group>"; };
93D398567FD02DB2647B8CF3 /* PearlNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlNavigationController.h; sourceTree = "<group>"; }; 93D398567FD02DB2647B8CF3 /* PearlNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlNavigationController.h; sourceTree = "<group>"; };
@@ -542,6 +545,7 @@
93D39F556F2F142740A65E59 /* MPWebViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPWebViewController.h; sourceTree = "<group>"; }; 93D39F556F2F142740A65E59 /* MPWebViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPWebViewController.h; sourceTree = "<group>"; };
93D39F7C9F47BF6387FBC5C3 /* PearlEMail.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlEMail.h; sourceTree = "<group>"; }; 93D39F7C9F47BF6387FBC5C3 /* PearlEMail.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlEMail.h; sourceTree = "<group>"; };
93D39F9106F2CCFB94283188 /* NSError+PearlFullDescription.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+PearlFullDescription.m"; sourceTree = "<group>"; }; 93D39F9106F2CCFB94283188 /* NSError+PearlFullDescription.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+PearlFullDescription.m"; sourceTree = "<group>"; };
93D39FBF8FCEB4C106272334 /* NSOrderedSetOrArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSOrderedSetOrArray.h; sourceTree = "<group>"; };
93D39FD9623E8D5571C0AEB3 /* MPAlgorithmV3.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAlgorithmV3.m; sourceTree = "<group>"; }; 93D39FD9623E8D5571C0AEB3 /* MPAlgorithmV3.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAlgorithmV3.m; sourceTree = "<group>"; };
DA04E33D14B1E70400ECA4F3 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; }; DA04E33D14B1E70400ECA4F3 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; };
DA071BF1190187FE00179766 /* empty@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "empty@2x.png"; sourceTree = "<group>"; }; DA071BF1190187FE00179766 /* empty@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "empty@2x.png"; sourceTree = "<group>"; };
@@ -631,8 +635,8 @@
DA24EBCA19DAD4D000FF010B /* launch.sketch */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = launch.sketch; sourceTree = "<group>"; }; DA24EBCA19DAD4D000FF010B /* launch.sketch */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = launch.sketch; sourceTree = "<group>"; };
DA24EBE619DAD6DE00FF010B /* Icon-320.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-320.png"; sourceTree = "<group>"; }; DA24EBE619DAD6DE00FF010B /* Icon-320.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-320.png"; sourceTree = "<group>"; };
DA24EBE719DAD6DE00FF010B /* Icon-64.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-64.png"; sourceTree = "<group>"; }; DA24EBE719DAD6DE00FF010B /* Icon-64.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-64.png"; sourceTree = "<group>"; };
DA250A13195665A100AC23F1 /* UITableView+PearlReloadFromArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITableView+PearlReloadFromArray.m"; sourceTree = "<group>"; }; DA250A13195665A100AC23F1 /* UITableView+PearlReloadItems.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITableView+PearlReloadItems.m"; sourceTree = "<group>"; };
DA250A14195665A100AC23F1 /* UITableView+PearlReloadFromArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITableView+PearlReloadFromArray.h"; sourceTree = "<group>"; }; DA250A14195665A100AC23F1 /* UITableView+PearlReloadItems.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITableView+PearlReloadItems.h"; sourceTree = "<group>"; };
DA250A15195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionReusableView+PearlDequeue.m"; sourceTree = "<group>"; }; DA250A15195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionReusableView+PearlDequeue.m"; sourceTree = "<group>"; };
DA250A16195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionReusableView+PearlDequeue.h"; sourceTree = "<group>"; }; DA250A16195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionReusableView+PearlDequeue.h"; sourceTree = "<group>"; };
DA25C6B31980D3C10046CDCF /* openssl */ = {isa = PBXFileReference; lastKnownFileType = folder; path = openssl; sourceTree = "<group>"; }; DA25C6B31980D3C10046CDCF /* openssl */ = {isa = PBXFileReference; lastKnownFileType = folder; path = openssl; sourceTree = "<group>"; };
@@ -2983,6 +2987,8 @@
DAA1411C1922FF020032B392 /* PearlTween.m */, DAA1411C1922FF020032B392 /* PearlTween.m */,
DAFE45F815039823003ABA7C /* README */, DAFE45F815039823003ABA7C /* README */,
DAFE45F915039823003ABA7C /* Resources */, DAFE45F915039823003ABA7C /* Resources */,
93D39FBF8FCEB4C106272334 /* NSOrderedSetOrArray.h */,
93D39789AAF49338F8AC8B02 /* NSOrderedSetOrArray.m */,
); );
path = Pearl; path = Pearl;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -3080,8 +3086,8 @@
93D390FB3110DCCE68E600DC /* UIScrollView+PearlAdjustInsets.m */, 93D390FB3110DCCE68E600DC /* UIScrollView+PearlAdjustInsets.m */,
DAFE4A63150399FF003ABA8F /* UIScrollView+PearlFlashingIndicators.h */, DAFE4A63150399FF003ABA8F /* UIScrollView+PearlFlashingIndicators.h */,
DAFE4A63150399FF003ABA8D /* UIScrollView+PearlFlashingIndicators.m */, DAFE4A63150399FF003ABA8D /* UIScrollView+PearlFlashingIndicators.m */,
DA250A14195665A100AC23F1 /* UITableView+PearlReloadFromArray.h */, DA250A14195665A100AC23F1 /* UITableView+PearlReloadItems.h */,
DA250A13195665A100AC23F1 /* UITableView+PearlReloadFromArray.m */, DA250A13195665A100AC23F1 /* UITableView+PearlReloadItems.m */,
DAE2726219CE9CB3007C5262 /* UITableViewCell+PearlDeque.h */, DAE2726219CE9CB3007C5262 /* UITableViewCell+PearlDeque.h */,
DAE2726119CE9CB3007C5262 /* UITableViewCell+PearlDeque.m */, DAE2726119CE9CB3007C5262 /* UITableViewCell+PearlDeque.m */,
93D39AA10CD00D05937671B1 /* UITextView+PearlAttributes.h */, 93D39AA10CD00D05937671B1 /* UITextView+PearlAttributes.h */,
@@ -3143,7 +3149,7 @@
DAFE4A2615039824003ABA7C /* PearlLogger.h in Headers */, DAFE4A2615039824003ABA7C /* PearlLogger.h in Headers */,
DAFE4A2815039824003ABA7C /* PearlMathUtils.h in Headers */, DAFE4A2815039824003ABA7C /* PearlMathUtils.h in Headers */,
DAFE4A2A15039824003ABA7C /* PearlObjectUtils.h in Headers */, DAFE4A2A15039824003ABA7C /* PearlObjectUtils.h in Headers */,
DA250A18195665A100AC23F1 /* UITableView+PearlReloadFromArray.h in Headers */, DA250A18195665A100AC23F1 /* UITableView+PearlReloadItems.h in Headers */,
DAFE4A2C15039824003ABA7C /* PearlResettable.h in Headers */, DAFE4A2C15039824003ABA7C /* PearlResettable.h in Headers */,
DAFE4A2D15039824003ABA7C /* PearlStrings.h in Headers */, DAFE4A2D15039824003ABA7C /* PearlStrings.h in Headers */,
DAFE4A2F15039824003ABA7C /* PearlStringUtils.h in Headers */, DAFE4A2F15039824003ABA7C /* PearlStringUtils.h in Headers */,
@@ -3199,6 +3205,7 @@
93D392A8777DC30C11361647 /* UITextView+PearlAttributes.h in Headers */, 93D392A8777DC30C11361647 /* UITextView+PearlAttributes.h in Headers */,
93D39A53D76CA70786423458 /* UICollectionView+PearlReloadFromArray.h in Headers */, 93D39A53D76CA70786423458 /* UICollectionView+PearlReloadFromArray.h in Headers */,
93D39AA4A0BE66A872CCC02E /* NSPersistentStore+PearlMigration.h in Headers */, 93D39AA4A0BE66A872CCC02E /* NSPersistentStore+PearlMigration.h in Headers */,
93D399E4BC1E092A8C8B12AE /* NSOrderedSetOrArray.h in Headers */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -3853,7 +3860,7 @@
DAFE4A3515039824003ABA7C /* PearlCryptUtils.m in Sources */, DAFE4A3515039824003ABA7C /* PearlCryptUtils.m in Sources */,
DAFE4A3715039824003ABA7C /* PearlKeyChain.m in Sources */, DAFE4A3715039824003ABA7C /* PearlKeyChain.m in Sources */,
DA72BD7B19C1510C00E6ACFE /* UIView+FontScale.m in Sources */, DA72BD7B19C1510C00E6ACFE /* UIView+FontScale.m in Sources */,
DA250A17195665A100AC23F1 /* UITableView+PearlReloadFromArray.m in Sources */, DA250A17195665A100AC23F1 /* UITableView+PearlReloadItems.m in Sources */,
DAFE4A3915039824003ABA7C /* PearlRSAKey.m in Sources */, DAFE4A3915039824003ABA7C /* PearlRSAKey.m in Sources */,
DAFE4A3F15039824003ABA7C /* PearlAlert.m in Sources */, DAFE4A3F15039824003ABA7C /* PearlAlert.m in Sources */,
DAFE4A4115039824003ABA7C /* PearlArrayTVC.m in Sources */, DAFE4A4115039824003ABA7C /* PearlArrayTVC.m in Sources */,
@@ -3904,6 +3911,7 @@
93D39E34FD28D24FE3442C48 /* UITextView+PearlAttributes.m in Sources */, 93D39E34FD28D24FE3442C48 /* UITextView+PearlAttributes.m in Sources */,
93D39D47FC623E91FC39D20C /* UICollectionView+PearlReloadFromArray.m in Sources */, 93D39D47FC623E91FC39D20C /* UICollectionView+PearlReloadFromArray.m in Sources */,
93D3928D629EA563F9EC4909 /* NSPersistentStore+PearlMigration.m in Sources */, 93D3928D629EA563F9EC4909 /* NSPersistentStore+PearlMigration.m in Sources */,
93D392A33CCE85431E910C7B /* NSOrderedSetOrArray.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -3997,7 +4005,6 @@
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREPROCESSOR_DEFINITIONS = ( GCC_PREPROCESSOR_DEFINITIONS = (
"ADHOC=1",
"$(inherited)", "$(inherited)",
"NDEBUG=1", "NDEBUG=1",
"NS_BLOCK_ASSERTIONS=1", "NS_BLOCK_ASSERTIONS=1",
@@ -4265,7 +4272,6 @@
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREPROCESSOR_DEFINITIONS = ( GCC_PREPROCESSOR_DEFINITIONS = (
"ADHOC=1",
"$(inherited)", "$(inherited)",
"NDEBUG=1", "NDEBUG=1",
"NS_BLOCK_ASSERTIONS=1", "NS_BLOCK_ASSERTIONS=1",

View File

@@ -26,7 +26,6 @@
DA09745A1E99582900F0BFE8 /* mpw-tests-util.c in Sources */ = {isa = PBXBuildFile; fileRef = DA0974561E99582200F0BFE8 /* mpw-tests-util.c */; }; DA09745A1E99582900F0BFE8 /* mpw-tests-util.c in Sources */ = {isa = PBXBuildFile; fileRef = DA0974561E99582200F0BFE8 /* mpw-tests-util.c */; };
DA09745B1E99582900F0BFE8 /* mpw-tests.c in Sources */ = {isa = PBXBuildFile; fileRef = DA0974571E99582200F0BFE8 /* mpw-tests.c */; }; DA09745B1E99582900F0BFE8 /* mpw-tests.c in Sources */ = {isa = PBXBuildFile; fileRef = DA0974571E99582200F0BFE8 /* mpw-tests.c */; };
DA09745E1E99586600F0BFE8 /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA09745D1E99586600F0BFE8 /* libxml2.tbd */; }; DA09745E1E99586600F0BFE8 /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA09745D1E99586600F0BFE8 /* libxml2.tbd */; };
DA09795D1E9A824700F0BFE8 /* libsodium.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA0979571E9A824700F0BFE8 /* libsodium.a */; };
DA0979681E9A834C00F0BFE8 /* libsodium.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA0979571E9A824700F0BFE8 /* libsodium.a */; }; DA0979681E9A834C00F0BFE8 /* libsodium.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA0979571E9A824700F0BFE8 /* libsodium.a */; };
DA1000801998A4C6002B873F /* openssl in Headers */ = {isa = PBXBuildFile; fileRef = DAE8E65719867AF500416A0F /* openssl */; settings = {ATTRIBUTES = (Public, ); }; }; DA1000801998A4C6002B873F /* openssl in Headers */ = {isa = PBXBuildFile; fileRef = DAE8E65719867AF500416A0F /* openssl */; settings = {ATTRIBUTES = (Public, ); }; };
DA16B341170661DB000A0EAB /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA16B340170661DB000A0EAB /* Carbon.framework */; }; DA16B341170661DB000A0EAB /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA16B340170661DB000A0EAB /* Carbon.framework */; };
@@ -157,6 +156,7 @@
DACBFCDF1C59B22E007EF90F /* NSMutableSet+Pearl.m in Sources */ = {isa = PBXBuildFile; fileRef = DACBFCDC1C59B22E007EF90F /* NSMutableSet+Pearl.m */; }; DACBFCDF1C59B22E007EF90F /* NSMutableSet+Pearl.m in Sources */ = {isa = PBXBuildFile; fileRef = DACBFCDC1C59B22E007EF90F /* NSMutableSet+Pearl.m */; };
DAD9B5F01762CAA4001835F9 /* ServiceManagement.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAD9B5EF1762CAA4001835F9 /* ServiceManagement.framework */; }; DAD9B5F01762CAA4001835F9 /* ServiceManagement.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAD9B5EF1762CAA4001835F9 /* ServiceManagement.framework */; };
DAD9B5F11762CAB9001835F9 /* MasterPassword-Mac-LoginHelper.app in Copy LoginHelper */ = {isa = PBXBuildFile; fileRef = DAD9B5E6176299BA001835F9 /* MasterPassword-Mac-LoginHelper.app */; }; DAD9B5F11762CAB9001835F9 /* MasterPassword-Mac-LoginHelper.app in Copy LoginHelper */ = {isa = PBXBuildFile; fileRef = DAD9B5E6176299BA001835F9 /* MasterPassword-Mac-LoginHelper.app */; };
DADD5DFA1EA173B0005E7D96 /* libsodium.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA0979571E9A824700F0BFE8 /* libsodium.a */; };
DAEB942E18B47FB3000490CC /* MPInitialWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA0933C91747A56A00DE1CEF /* MPInitialWindow.xib */; }; DAEB942E18B47FB3000490CC /* MPInitialWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA0933C91747A56A00DE1CEF /* MPInitialWindow.xib */; };
DAF4EF56190A828100023C90 /* Exo2.0-Thin.otf in Resources */ = {isa = PBXBuildFile; fileRef = DAF4EF52190A828100023C90 /* Exo2.0-Thin.otf */; }; DAF4EF56190A828100023C90 /* Exo2.0-Thin.otf in Resources */ = {isa = PBXBuildFile; fileRef = DAF4EF52190A828100023C90 /* Exo2.0-Thin.otf */; };
DAF4EF57190A828100023C90 /* Exo2.0-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = DAF4EF53190A828100023C90 /* Exo2.0-Regular.otf */; }; DAF4EF57190A828100023C90 /* Exo2.0-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = DAF4EF53190A828100023C90 /* Exo2.0-Regular.otf */; };
@@ -291,7 +291,6 @@
DA09745C1E99583B00F0BFE8 /* mpw-tests-util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "mpw-tests-util.h"; path = "../../platform-independent/cli-c/cli/mpw-tests-util.h"; sourceTree = "<group>"; }; DA09745C1E99583B00F0BFE8 /* mpw-tests-util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "mpw-tests-util.h"; path = "../../platform-independent/cli-c/cli/mpw-tests-util.h"; sourceTree = "<group>"; };
DA09745D1E99586600F0BFE8 /* libxml2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libxml2.tbd; path = usr/lib/libxml2.tbd; sourceTree = SDKROOT; }; DA09745D1E99586600F0BFE8 /* libxml2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libxml2.tbd; path = usr/lib/libxml2.tbd; sourceTree = SDKROOT; };
DA09745F1E995EB500F0BFE8 /* mpw_tests.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; name = mpw_tests.xml; path = ../../core/java/tests/src/main/resources/mpw_tests.xml; sourceTree = "<group>"; }; DA09745F1E995EB500F0BFE8 /* mpw_tests.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; name = mpw_tests.xml; path = ../../core/java/tests/src/main/resources/mpw_tests.xml; sourceTree = "<group>"; };
DA0974611E9961F100F0BFE8 /* libsodium.18.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsodium.18.dylib; path = ../../../../../../../usr/local/Cellar/libsodium/1.0.12/lib/libsodium.18.dylib; sourceTree = "<group>"; };
DA09791B1E9A824700F0BFE8 /* core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = core.h; sourceTree = "<group>"; }; DA09791B1E9A824700F0BFE8 /* core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = core.h; sourceTree = "<group>"; };
DA09791C1E9A824700F0BFE8 /* crypto_aead_aes256gcm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = crypto_aead_aes256gcm.h; sourceTree = "<group>"; }; DA09791C1E9A824700F0BFE8 /* crypto_aead_aes256gcm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = crypto_aead_aes256gcm.h; sourceTree = "<group>"; };
DA09791D1E9A824700F0BFE8 /* crypto_aead_chacha20poly1305.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = crypto_aead_chacha20poly1305.h; sourceTree = "<group>"; }; DA09791D1E9A824700F0BFE8 /* crypto_aead_chacha20poly1305.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = crypto_aead_chacha20poly1305.h; sourceTree = "<group>"; };
@@ -350,11 +349,7 @@
DA0979521E9A824700F0BFE8 /* utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = utils.h; sourceTree = "<group>"; }; DA0979521E9A824700F0BFE8 /* utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = utils.h; sourceTree = "<group>"; };
DA0979531E9A824700F0BFE8 /* version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = version.h; sourceTree = "<group>"; }; DA0979531E9A824700F0BFE8 /* version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = version.h; sourceTree = "<group>"; };
DA0979541E9A824700F0BFE8 /* sodium.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sodium.h; sourceTree = "<group>"; }; DA0979541E9A824700F0BFE8 /* sodium.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sodium.h; sourceTree = "<group>"; };
DA0979561E9A824700F0BFE8 /* libsodium.18.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libsodium.18.dylib; sourceTree = "<group>"; };
DA0979571E9A824700F0BFE8 /* libsodium.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libsodium.a; sourceTree = "<group>"; }; DA0979571E9A824700F0BFE8 /* libsodium.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libsodium.a; sourceTree = "<group>"; };
DA0979581E9A824700F0BFE8 /* libsodium.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libsodium.dylib; sourceTree = "<group>"; };
DA0979591E9A824700F0BFE8 /* libsodium.la */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = libsodium.la; sourceTree = "<group>"; };
DA09795B1E9A824700F0BFE8 /* libsodium.pc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = libsodium.pc; sourceTree = "<group>"; };
DA16B340170661DB000A0EAB /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; }; DA16B340170661DB000A0EAB /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; };
DA16B343170661EE000A0EAB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; DA16B343170661EE000A0EAB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
DA2508F019511D3600AC23F1 /* MPPasswordWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MPPasswordWindowController.xib; sourceTree = "<group>"; }; DA2508F019511D3600AC23F1 /* MPPasswordWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MPPasswordWindowController.xib; sourceTree = "<group>"; };
@@ -995,8 +990,8 @@
DA9261561BE1A89600369DE5 /* libz.tbd in Frameworks */, DA9261561BE1A89600369DE5 /* libz.tbd in Frameworks */,
DA9261541BE1A88900369DE5 /* libc++.tbd in Frameworks */, DA9261541BE1A88900369DE5 /* libc++.tbd in Frameworks */,
DAADCC6A19FB00B500987B1D /* libKCOrderedAccessorFix.a in Frameworks */, DAADCC6A19FB00B500987B1D /* libKCOrderedAccessorFix.a in Frameworks */,
DADD5DFA1EA173B0005E7D96 /* libsodium.a in Frameworks */,
DA9261521BE1A86700369DE5 /* Fabric.framework in Frameworks */, DA9261521BE1A86700369DE5 /* Fabric.framework in Frameworks */,
DA09795D1E9A824700F0BFE8 /* libsodium.a in Frameworks */,
DA9261511BE1A86700369DE5 /* SystemConfiguration.framework in Frameworks */, DA9261511BE1A86700369DE5 /* SystemConfiguration.framework in Frameworks */,
DA250925195148E200AC23F1 /* QuartzCore.framework in Frameworks */, DA250925195148E200AC23F1 /* QuartzCore.framework in Frameworks */,
DAD9B5F01762CAA4001835F9 /* ServiceManagement.framework in Frameworks */, DAD9B5F01762CAA4001835F9 /* ServiceManagement.framework in Frameworks */,
@@ -1132,23 +1127,11 @@
DA0979551E9A824700F0BFE8 /* lib */ = { DA0979551E9A824700F0BFE8 /* lib */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DA0979561E9A824700F0BFE8 /* libsodium.18.dylib */,
DA0979571E9A824700F0BFE8 /* libsodium.a */, DA0979571E9A824700F0BFE8 /* libsodium.a */,
DA0979581E9A824700F0BFE8 /* libsodium.dylib */,
DA0979591E9A824700F0BFE8 /* libsodium.la */,
DA09795A1E9A824700F0BFE8 /* pkgconfig */,
); );
path = lib; path = lib;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
DA09795A1E9A824700F0BFE8 /* pkgconfig */ = {
isa = PBXGroup;
children = (
DA09795B1E9A824700F0BFE8 /* libsodium.pc */,
);
path = pkgconfig;
sourceTree = "<group>";
};
DA2508F819513C1400AC23F1 /* Other Frameworks */ = { DA2508F819513C1400AC23F1 /* Other Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -1196,7 +1179,6 @@
DA5BFA47147E415C00F98B1E /* Frameworks */ = { DA5BFA47147E415C00F98B1E /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DA0974611E9961F100F0BFE8 /* libsodium.18.dylib */,
DA09745D1E99586600F0BFE8 /* libxml2.tbd */, DA09745D1E99586600F0BFE8 /* libxml2.tbd */,
DA6701B716406A4100B61001 /* Accounts.framework */, DA6701B716406A4100B61001 /* Accounts.framework */,
DA16B340170661DB000A0EAB /* Carbon.framework */, DA16B340170661DB000A0EAB /* Carbon.framework */,
@@ -2345,7 +2327,7 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = "/bin/sh -e"; shellPath = "/bin/sh -e";
shellScript = "cd External/libsodium\n[[ -d libsodium-osx ]] && exit\n\n# Xcode misinterpretes autogen.sh's stderr output as errors so we try to silence it.\n[[ -e configure ]] || { err=$(./autogen.sh 2>&1 >&3); } 3>&1 || { x=$?; echo >&2 \"$err\"; exit $x; }\n./dist-build/osx.sh"; shellScript = "cd External/libsodium\nif ! [[ -d libsodium-osx ]]; then\n # Xcode misinterpretes autogen.sh's stderr output as errors so we try to silence it.\n [[ -e configure ]] || { err=$(./autogen.sh 2>&1 >&3); } 3>&1 || { x=$?; echo >&2 \"$err\"; exit $x; }\n./dist-build/osx.sh\nfi\n\n# Xcode incorrectly links the dynamic libraries if they're present instead of linking the .a.\nrm -f libsodium-osx/lib/*.dylib";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
DA4EF9CB19FD4B600032ECB5 /* Run Script: genassets */ = { DA4EF9CB19FD4B600032ECB5 /* Run Script: genassets */ = {
@@ -2586,7 +2568,6 @@
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREPROCESSOR_DEFINITIONS = ( GCC_PREPROCESSOR_DEFINITIONS = (
"ADHOC=1",
"$(inherited)", "$(inherited)",
"NDEBUG=1", "NDEBUG=1",
"NS_BLOCK_ASSERTIONS=1", "NS_BLOCK_ASSERTIONS=1",
@@ -2856,7 +2837,6 @@
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREPROCESSOR_DEFINITIONS = ( GCC_PREPROCESSOR_DEFINITIONS = (
"ADHOC=1",
"$(inherited)", "$(inherited)",
"NDEBUG=1", "NDEBUG=1",
"NS_BLOCK_ASSERTIONS=1", "NS_BLOCK_ASSERTIONS=1",

View File

@@ -37,7 +37,7 @@ setSettingWithTitle() {
case $PLATFORM_NAME in case $PLATFORM_NAME in
macosx) platform=mac ;; macosx) platform=mac ;;
ios) platform=ios ;; iphone*) platform=ios ;;
*) ftl 'ERROR: Unknown platform: %s.' "$PLATFORM_NAME"; exit 1 ;; *) ftl 'ERROR: Unknown platform: %s.' "$PLATFORM_NAME"; exit 1 ;;
esac esac

View File

@@ -430,58 +430,38 @@ NSOperationQueue *_mpwQueue = nil;
- (NSString *)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey { - (NSString *)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
dispatch_group_t group = dispatch_group_create(); return PearlAwait( ^(void (^setResult)(id)) {
dispatch_group_enter( group ); [self resolveLoginForSite:site usingKey:siteKey result:^(NSString *result_) {
__block NSString *result = nil; setResult( result_ );
[self resolveLoginForSite:site usingKey:siteKey result:^(NSString *result_) { }];
result = result_; } );
dispatch_group_leave( group );
}];
dispatch_group_wait( group, DISPATCH_TIME_FOREVER );
return result;
} }
- (NSString *)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey { - (NSString *)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
dispatch_group_t group = dispatch_group_create(); return PearlAwait( ^(void (^setResult)(id)) {
dispatch_group_enter( group ); [self resolvePasswordForSite:site usingKey:siteKey result:^(NSString *result_) {
__block NSString *result = nil; setResult( result_ );
[self resolvePasswordForSite:site usingKey:siteKey result:^(NSString *result_) { }];
result = result_; } );
dispatch_group_leave( group );
}];
dispatch_group_wait( group, DISPATCH_TIME_FOREVER );
return result;
} }
- (NSString *)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey { - (NSString *)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
dispatch_group_t group = dispatch_group_create(); return PearlAwait( ^(void (^setResult)(id)) {
dispatch_group_enter( group ); [self resolveAnswerForSite:site usingKey:siteKey result:^(NSString *result_) {
__block NSString *result = nil; setResult( result_ );
[self resolveAnswerForSite:site usingKey:siteKey result:^(NSString *result_) { }];
result = result_; } );
dispatch_group_leave( group );
}];
dispatch_group_wait( group, DISPATCH_TIME_FOREVER );
return result;
} }
- (NSString *)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)siteKey { - (NSString *)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)siteKey {
dispatch_group_t group = dispatch_group_create(); return PearlAwait( ^(void (^setResult)(id)) {
dispatch_group_enter( group ); [self resolveAnswerForQuestion:question usingKey:siteKey result:^(NSString *result_) {
__block NSString *result = nil; setResult( result_ );
[self resolveAnswerForQuestion:question usingKey:siteKey result:^(NSString *result_) { }];
result = result_; } );
dispatch_group_leave( group );
}];
dispatch_group_wait( group, DISPATCH_TIME_FOREVER );
return result;
} }
- (void)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock { - (void)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock {
@@ -498,10 +478,12 @@ NSOperationQueue *_mpwQueue = nil;
else else
algorithm = site.algorithm; algorithm = site.algorithm;
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{ if (!loginGenerated || [loginName length])
resultBlock( loginName || !loginGenerated? loginName: resultBlock( loginName );
[algorithm generateLoginForSiteNamed:name usingKey:siteKey] ); else
} ); PearlNotMainQueue( ^{
resultBlock( [algorithm generateLoginForSiteNamed:name usingKey:siteKey] );
} );
} }
- (void)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock { - (void)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock {
@@ -533,9 +515,8 @@ NSOperationQueue *_mpwQueue = nil;
else else
algorithm = site.algorithm; algorithm = site.algorithm;
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{ PearlNotMainQueue( ^{
NSString *result = [algorithm generatePasswordForSiteNamed:name ofType:type withCounter:counter usingKey:siteKey]; resultBlock( [algorithm generatePasswordForSiteNamed:name ofType:type withCounter:counter usingKey:siteKey] );
resultBlock( result );
} ); } );
break; break;
} }
@@ -549,9 +530,8 @@ NSOperationQueue *_mpwQueue = nil;
NSData *encryptedContent = ((MPStoredSiteEntity *)site).contentObject; NSData *encryptedContent = ((MPStoredSiteEntity *)site).contentObject;
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{ PearlNotMainQueue( ^{
NSString *result = [self decryptContent:encryptedContent usingKey:siteKey]; resultBlock( [self decryptContent:encryptedContent usingKey:siteKey] );
resultBlock( result );
} ); } );
break; break;
} }
@@ -563,9 +543,8 @@ NSOperationQueue *_mpwQueue = nil;
NSDictionary *siteQuery = [self queryForDevicePrivateSiteNamed:site.name]; NSDictionary *siteQuery = [self queryForDevicePrivateSiteNamed:site.name];
NSData *encryptedContent = [PearlKeyChain dataOfItemForQuery:siteQuery]; NSData *encryptedContent = [PearlKeyChain dataOfItemForQuery:siteQuery];
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{ PearlNotMainQueue( ^{
NSString *result = [self decryptContent:encryptedContent usingKey:siteKey]; resultBlock( [self decryptContent:encryptedContent usingKey:siteKey] );
resultBlock( result );
} ); } );
break; break;
} }
@@ -584,9 +563,8 @@ NSOperationQueue *_mpwQueue = nil;
else else
algorithm = site.algorithm; algorithm = site.algorithm;
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{ PearlNotMainQueue( ^{
NSString *result = [algorithm generateAnswerForSiteNamed:name onQuestion:nil usingKey:siteKey]; resultBlock( [algorithm generateAnswerForSiteNamed:name onQuestion:nil usingKey:siteKey] );
resultBlock( result );
} ); } );
} }
@@ -605,9 +583,8 @@ NSOperationQueue *_mpwQueue = nil;
else if ([[MPAppDelegate_Shared get] isFeatureUnlocked:MPProductGenerateAnswers]) else if ([[MPAppDelegate_Shared get] isFeatureUnlocked:MPProductGenerateAnswers])
algorithm = question.site.algorithm; algorithm = question.site.algorithm;
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{ PearlNotMainQueue( ^{
NSString *result = [algorithm generateAnswerForSiteNamed:name onQuestion:keyword usingKey:siteKey]; resultBlock( [algorithm generateAnswerForSiteNamed:name onQuestion:keyword usingKey:siteKey] );
resultBlock( result );
} ); } );
} }

View File

@@ -75,7 +75,7 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
// Consumable product. // Consumable product.
return NO; return NO;
#if ADHOC || DEBUG #if DEBUG
// All features are unlocked for beta / debug / mac versions. // All features are unlocked for beta / debug / mac versions.
return YES; return YES;
#else #else

View File

@@ -72,14 +72,23 @@ static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigi
MPKeyOrigin keyOrigin; MPKeyOrigin keyOrigin;
NSDictionary *keyQuery = createKeyQuery( user, NO, &keyOrigin ); NSDictionary *keyQuery = createKeyQuery( user, NO, &keyOrigin );
NSData *keyData = [PearlKeyChain dataOfItemForQuery:keyQuery]; id<MPAlgorithm> keyAlgorithm = user.algorithm;
if (!keyData) { MPKey *key = [[MPKey alloc] initForFullName:user.name withKeyResolver:^NSData *(id<MPAlgorithm> algorithm) {
return ![algorithm isEqual:keyAlgorithm]? nil:
PearlMainQueueAwait( (id)^{
return [PearlKeyChain dataOfItemForQuery:keyQuery];
} );
} keyOrigin:keyOrigin];
if ([key keyIDForAlgorithm:user.algorithm])
inf( @"Found key in keychain for user: %@", user.userID );
else {
inf( @"No key found in keychain for user: %@", user.userID ); inf( @"No key found in keychain for user: %@", user.userID );
return nil; key = nil;
} }
inf( @"Found key in keychain for user: %@", user.userID ); return key;
return [[MPKey alloc] initForFullName:user.name withKeyData:keyData forAlgorithm:user.algorithm keyOrigin:keyOrigin];
} }
- (void)storeSavedKeyFor:(MPUserEntity *)user { - (void)storeSavedKeyFor:(MPUserEntity *)user {
@@ -230,28 +239,22 @@ static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigi
NSString *content; NSString *content;
while (!(content = [site.algorithm storedPasswordForSite:(MPStoredSiteEntity *)site usingKey:recoverKey])) { while (!(content = [site.algorithm storedPasswordForSite:(MPStoredSiteEntity *)site usingKey:recoverKey])) {
// Failed to decrypt site with the current recoveryKey. Ask user for a new one to use. // Failed to decrypt site with the current recoveryKey. Ask user for a new one to use.
__block NSString *masterPassword = nil; NSString *masterPassword = nil;
#ifdef PEARL_UIKIT #ifdef PEARL_UIKIT
dispatch_group_t recoverPasswordGroup = dispatch_group_create(); masterPassword = PearlAwait( ^(void (^setResult)(id)) {
dispatch_group_enter( recoverPasswordGroup ); [PearlAlert showAlertWithTitle:@"Enter Old Master Password"
[PearlAlert showAlertWithTitle:@"Enter Old Master Password" message:PearlString(
message:PearlString( @"Your old master password is required to migrate the stored password for %@", @"Your old master password is required to migrate the stored password for %@",
site.name ) site.name )
viewStyle:UIAlertViewStyleSecureTextInput viewStyle:UIAlertViewStyleSecureTextInput
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) { initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
@try {
if (buttonIndex_ == [alert_ cancelButtonIndex]) if (buttonIndex_ == [alert_ cancelButtonIndex])
// Don't Migrate setResult( nil );
return; else
setResult( [alert_ textFieldAtIndex:0].text );
masterPassword = [alert_ textFieldAtIndex:0].text; } cancelTitle:@"Don't Migrate" otherTitles:@"Migrate", nil];
} } );
@finally {
dispatch_group_leave( recoverPasswordGroup );
}
} cancelTitle:@"Don't Migrate" otherTitles:@"Migrate", nil];
dispatch_group_wait( recoverPasswordGroup, DISPATCH_TIME_FOREVER );
#endif #endif
if (!masterPassword) if (!masterPassword)
// Don't Migrate // Don't Migrate

View File

@@ -28,12 +28,12 @@ typedef NS_ENUM( NSUInteger, MPKeyOrigin ) {
@interface MPKey : NSObject @interface MPKey : NSObject
@property(nonatomic, readonly) NSString *fullName;
@property(nonatomic, readonly) MPKeyOrigin origin; @property(nonatomic, readonly) MPKeyOrigin origin;
@property(nonatomic, readonly, copy) NSString *fullName;
- (instancetype)initForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword; - (instancetype)initForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword;
- (instancetype)initForFullName:(NSString *)fullName withKeyData:(NSData *)keyData - (instancetype)initForFullName:(NSString *)fullName withKeyResolver:(NSData *( ^ )(id<MPAlgorithm>))keyResolver
forAlgorithm:(id<MPAlgorithm>)algorithm keyOrigin:(MPKeyOrigin)origin; keyOrigin:(MPKeyOrigin)origin;
- (NSData *)keyIDForAlgorithm:(id<MPAlgorithm>)algorithm; - (NSData *)keyIDForAlgorithm:(id<MPAlgorithm>)algorithm;
- (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm; - (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm;

View File

@@ -19,9 +19,9 @@
@interface MPKey() @interface MPKey()
@property(nonatomic) NSString *fullName;
@property(nonatomic) MPKeyOrigin origin; @property(nonatomic) MPKeyOrigin origin;
@property(nonatomic) NSString *masterPassword; @property(nonatomic, copy) NSString *fullName;
@property(nonatomic, copy) NSData *( ^keyResolver )(id<MPAlgorithm>);
@end @end
@@ -31,25 +31,22 @@
- (instancetype)initForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword { - (instancetype)initForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword {
return [self initForFullName:fullName withKeyResolver:^NSData *(id<MPAlgorithm> algorithm) {
return [algorithm keyDataForFullName:self.fullName withMasterPassword:masterPassword];
} keyOrigin:MPKeyOriginMasterPassword];
}
- (instancetype)initForFullName:(NSString *)fullName withKeyResolver:(NSData *( ^ )(id<MPAlgorithm>))keyResolver
keyOrigin:(MPKeyOrigin)origin {
if (!(self = [super init])) if (!(self = [super init]))
return nil; return nil;
_keyCache = [NSCache new]; _keyCache = [NSCache new];
self.fullName = fullName;
self.origin = MPKeyOriginMasterPassword;
self.masterPassword = masterPassword;
return self;
}
- (instancetype)initForFullName:(NSString *)fullName withKeyData:(NSData *)keyData
forAlgorithm:(id<MPAlgorithm>)algorithm keyOrigin:(MPKeyOrigin)origin {
if (!(self = [self initForFullName:fullName withMasterPassword:nil]))
return nil;
self.origin = origin; self.origin = origin;
[_keyCache setObject:keyData forKey:algorithm]; self.fullName = fullName;
self.keyResolver = keyResolver;
return self; return self;
} }
@@ -61,15 +58,17 @@
- (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm { - (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm {
NSData *keyData = [_keyCache objectForKey:algorithm]; @synchronized (self) {
if (keyData) NSData *keyData = [_keyCache objectForKey:algorithm];
if (keyData)
return keyData;
keyData = self.keyResolver( algorithm );
if (keyData)
[_keyCache setObject:keyData forKey:algorithm];
return keyData; return keyData;
}
keyData = [algorithm keyDataForFullName:self.fullName withMasterPassword:self.masterPassword];
if (keyData)
[_keyCache setObject:keyData forKey:algorithm];
return keyData;
} }
- (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm trimmedLength:(NSUInteger)subKeyLength { - (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm trimmedLength:(NSUInteger)subKeyLength {
@@ -80,7 +79,7 @@
- (BOOL)isEqualToKey:(MPKey *)key { - (BOOL)isEqualToKey:(MPKey *)key {
return [self.fullName isEqualToString:key.fullName] && [self.masterPassword isEqualToString:self.masterPassword]; return [[self keyIDForAlgorithm:MPAlgorithmDefault] isEqualToData:[key keyIDForAlgorithm:MPAlgorithmDefault]];
} }
- (BOOL)isEqual:(id)object { - (BOOL)isEqual:(id)object {

View File

@@ -28,10 +28,10 @@
[super windowDidLoad]; [super windowDidLoad];
[[NSNotificationCenter defaultCenter] addObserverForName:NSWindowWillCloseNotification object:self.window PearlAddNotificationObserver( NSWindowWillCloseNotification, self.window, nil, ^(id host, NSNotification *note) {
queue:nil usingBlock:^(NSNotification *note) { PearlRemoveNotificationObserversFrom( host );
[MPMacAppDelegate get].initialWindowController = nil; [MPMacAppDelegate get].initialWindowController = nil;
}]; } );
} }
#pragma mark - Actions #pragma mark - Actions

View File

@@ -72,7 +72,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
NSString *crashlyticsAPIKey = [self crashlyticsAPIKey]; NSString *crashlyticsAPIKey = [self crashlyticsAPIKey];
if ([crashlyticsAPIKey length]) { if ([crashlyticsAPIKey length]) {
inf(@"Initializing Crashlytics"); inf(@"Initializing Crashlytics");
#if defined (DEBUG) || defined (ADHOC) #if DEBUG
[Crashlytics sharedInstance].debugMode = YES; [Crashlytics sharedInstance].debugMode = YES;
#endif #endif
[[Crashlytics sharedInstance] setUserIdentifier:[PearlKeyChain deviceIdentifier]]; [[Crashlytics sharedInstance] setUserIdentifier:[PearlKeyChain deviceIdentifier]];

View File

@@ -41,40 +41,38 @@
[self replaceFonts:self.window.contentView]; [self replaceFonts:self.window.contentView];
prof_rewind( @"replaceFonts" ); prof_rewind( @"replaceFonts" );
[[NSNotificationCenter defaultCenter] addObserverForName:NSWindowDidBecomeKeyNotification object:self.window PearlAddNotificationObserver( NSWindowDidBecomeKeyNotification, self.window, [NSOperationQueue mainQueue],
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { ^(id host, NSNotification *note) {
prof_new( @"didBecomeKey" ); prof_new( @"didBecomeKey" );
[self.window makeKeyAndOrderFront:nil]; [self.window makeKeyAndOrderFront:nil];
prof_rewind( @"fadeIn" ); prof_rewind( @"fadeIn" );
[self updateUser]; [self updateUser];
prof_finish( @"updateUser" ); prof_finish( @"updateUser" );
}]; } );
[[NSNotificationCenter defaultCenter] addObserverForName:NSWindowWillCloseNotification object:self.window PearlAddNotificationObserver( NSWindowWillCloseNotification, self.window, [NSOperationQueue mainQueue],
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { ^(id host, NSNotification *note) {
PearlRemoveNotificationObservers();
NSWindow *sheet = [self.window attachedSheet]; NSWindow *sheet = [self.window attachedSheet];
if (sheet) if (sheet)
[self.window endSheet:sheet]; [self.window endSheet:sheet];
}]; } );
[[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationWillResignActiveNotification object:nil PearlAddNotificationObserver( NSApplicationWillResignActiveNotification, nil, [NSOperationQueue mainQueue],
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { ^(id host, NSNotification *note) {
[self.window close]; [self.window close];
}]; } );
[[NSNotificationCenter defaultCenter] addObserverForName:MPSignedInNotification object:nil PearlAddNotificationObserver( MPSignedInNotification, nil, [NSOperationQueue mainQueue], ^(id host, NSNotification *note) {
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { [self updateUser];
[self updateUser]; } );
}]; PearlAddNotificationObserver( MPSignedOutNotification, nil, [NSOperationQueue mainQueue], ^(id host, NSNotification *note) {
[[NSNotificationCenter defaultCenter] addObserverForName:MPSignedOutNotification object:nil [self updateUser];
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { } );
[self updateUser];
}];
[self observeKeyPath:@"sitesController.selection" withBlock:^(id from, id to, NSKeyValueChange cause, id _self) { [self observeKeyPath:@"sitesController.selection" withBlock:^(id from, id to, NSKeyValueChange cause, id _self) {
prof_new( @"sitesController.selection" );
[_self updateSelection]; [_self updateSelection];
prof_finish( @"updateSelection" );
}]; }];
prof_rewind( @"observers" ); prof_rewind( @"observers" );
NSSearchFieldCell *siteFieldCell = (NSSearchFieldCell *)self.siteField.cell; NSSearchFieldCell *siteFieldCell = self.siteField.cell;
siteFieldCell.searchButtonCell = nil; siteFieldCell.searchButtonCell = nil;
siteFieldCell.cancelButtonCell = nil; siteFieldCell.cancelButtonCell = nil;

View File

@@ -28,7 +28,7 @@
@property(weak, nonatomic) IBOutlet UISegmentedControl *typeControl; @property(weak, nonatomic) IBOutlet UISegmentedControl *typeControl;
@property(weak, nonatomic) IBOutlet UILabel *counterLabel; @property(weak, nonatomic) IBOutlet UILabel *counterLabel;
@property(weak, nonatomic) IBOutlet UIActivityIndicatorView *activity; @property(weak, nonatomic) IBOutlet UIActivityIndicatorView *activity;
@property(weak, nonatomic) IBOutlet UILabel *passwordLabel; @property(weak, nonatomic) IBOutlet UIButton *passwordButton;
@property(weak, nonatomic) IBOutlet UIView *tipContainer; @property(weak, nonatomic) IBOutlet UIView *tipContainer;
- (IBAction)controlChanged:(UIControl *)control; - (IBAction)controlChanged:(UIControl *)control;

View File

@@ -81,21 +81,19 @@
[self updatePassword]; [self updatePassword];
} }
- (IBAction)copyPassword:(UITapGestureRecognizer *)recognizer { - (IBAction)copyPassword:(id)sender {
if (recognizer.state == UIGestureRecognizerStateEnded) { NSString *sitePassword = [self.passwordButton titleForState:UIControlStateNormal];
NSString *sitePassword = self.passwordLabel.text; if ([sitePassword length]) {
if ([sitePassword length]) { [UIPasteboard generalPasteboard].string = sitePassword;
[UIPasteboard generalPasteboard].string = sitePassword; [UIView animateWithDuration:0.3f animations:^{
[UIView animateWithDuration:0.3f animations:^{ self.tipContainer.alpha = 1;
self.tipContainer.alpha = 1; } completion:^(BOOL finished) {
} completion:^(BOOL finished) { if (finished)
if (finished) PearlMainQueueAfter( 3, ^{
PearlMainQueueAfter( 3, ^{ self.tipContainer.alpha = 0;
self.tipContainer.alpha = 0; } );
} ); }];
}];
}
} }
} }
@@ -106,7 +104,7 @@
NSString *fullName = self.fullNameField.text; NSString *fullName = self.fullNameField.text;
NSString *masterPassword = self.masterPasswordField.text; NSString *masterPassword = self.masterPasswordField.text;
self.passwordLabel.text = nil; [self.passwordButton setTitle:nil forState:UIControlStateNormal];
[self.activity startAnimating]; [self.activity startAnimating];
[_emergencyKeyQueue cancelAllOperations]; [_emergencyKeyQueue cancelAllOperations];
[_emergencyKeyQueue addOperationWithBlock:^{ [_emergencyKeyQueue addOperationWithBlock:^{
@@ -128,7 +126,7 @@
NSUInteger siteCounter = (NSUInteger)self.counterStepper.value; NSUInteger siteCounter = (NSUInteger)self.counterStepper.value;
self.counterLabel.text = strf( @"%lu", (unsigned long)siteCounter ); self.counterLabel.text = strf( @"%lu", (unsigned long)siteCounter );
self.passwordLabel.text = nil; [self.passwordButton setTitle:nil forState:UIControlStateNormal];
[self.activity startAnimating]; [self.activity startAnimating];
[_emergencyPasswordQueue cancelAllOperations]; [_emergencyPasswordQueue cancelAllOperations];
[_emergencyPasswordQueue addOperationWithBlock:^{ [_emergencyPasswordQueue addOperationWithBlock:^{
@@ -138,7 +136,7 @@
PearlMainQueue( ^{ PearlMainQueue( ^{
[self.activity stopAnimating]; [self.activity stopAnimating];
self.passwordLabel.text = sitePassword; [self.passwordButton setTitle:sitePassword forState:UIControlStateNormal];
} ); } );
}]; }];
} }

View File

@@ -28,6 +28,7 @@
@property(nonatomic, strong) IBOutlet UITextField *passwordField; @property(nonatomic, strong) IBOutlet UITextField *passwordField;
@property(nonatomic, strong) IBOutlet UIView *loginNameContainer; @property(nonatomic, strong) IBOutlet UIView *loginNameContainer;
@property(nonatomic, strong) IBOutlet UITextField *loginNameField; @property(nonatomic, strong) IBOutlet UITextField *loginNameField;
@property(nonatomic, strong) IBOutlet UILabel *loginNameGenerated;
@property(nonatomic, strong) IBOutlet UILabel *strengthLabel; @property(nonatomic, strong) IBOutlet UILabel *strengthLabel;
@property(nonatomic, strong) IBOutlet UILabel *counterLabel; @property(nonatomic, strong) IBOutlet UILabel *counterLabel;
@property(nonatomic, strong) IBOutlet UIButton *counterButton; @property(nonatomic, strong) IBOutlet UIButton *counterButton;
@@ -199,7 +200,7 @@
atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:YES]; atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:YES];
if (textField == self.loginNameField) if (textField == self.loginNameField)
self.loginNameButton.titleLabel.alpha = [self.loginNameField.text length] || self.loginNameField.enabled? 0: 1; self.loginNameButton.hidden = [self.loginNameField.attributedText length] || self.loginNameField.enabled;
} }
- (IBAction)textFieldDidChange:(UITextField *)textField { - (IBAction)textFieldDidChange:(UITextField *)textField {
@@ -224,7 +225,7 @@
if (textField == self.passwordField || textField == self.loginNameField) { if (textField == self.passwordField || textField == self.loginNameField) {
textField.enabled = NO; textField.enabled = NO;
NSString *text = textField.text; NSString *text = [textField.attributedText string]?: textField.text;
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPSiteEntity *site = [self siteInContext:context]; MPSiteEntity *site = [self siteInContext:context];
@@ -235,10 +236,8 @@
if ([site.algorithm savePassword:text toSite:site usingKey:[MPiOSAppDelegate get].key]) if ([site.algorithm savePassword:text toSite:site usingKey:[MPiOSAppDelegate get].key])
[PearlOverlay showTemporaryOverlayWithTitle:@"Password Updated" dismissAfter:2]; [PearlOverlay showTemporaryOverlayWithTitle:@"Password Updated" dismissAfter:2];
} }
else if (textField == self.loginNameField && else if (textField == self.loginNameField) {
((site.loginGenerated && ![text length]) || if (![text isEqualToString:[site.algorithm resolveLoginForSite:site usingKey:[MPiOSAppDelegate get].key]]) {
(!site.loginGenerated && ![text isEqualToString:site.loginName]))) {
if (site.loginGenerated || !([site.loginName isEqualToString:text] || (!text && !site.loginName))) {
site.loginGenerated = NO; site.loginGenerated = NO;
site.loginName = text; site.loginName = text;
@@ -508,7 +507,6 @@
self.answersButton.gone = ![[MPiOSAppDelegate get] isFeatureUnlocked:MPProductGenerateAnswers]; self.answersButton.gone = ![[MPiOSAppDelegate get] isFeatureUnlocked:MPProductGenerateAnswers];
BOOL settingsMode = self.mode == MPPasswordCellModeSettings; BOOL settingsMode = self.mode == MPPasswordCellModeSettings;
self.loginNameContainer.alpha = settingsMode || mainSite.loginGenerated || [mainSite.loginName length]? 0.7f: 0; self.loginNameContainer.alpha = settingsMode || mainSite.loginGenerated || [mainSite.loginName length]? 0.7f: 0;
self.loginNameField.textColor = [UIColor colorWithHexString:mainSite.loginGenerated? @"5E636D": @"6D5E63"];
self.modeButton.alpha = self.transientSite? 0: settingsMode? 0.5f: 0.1f; self.modeButton.alpha = self.transientSite? 0: settingsMode? 0.5f: 0.1f;
self.counterLabel.alpha = self.counterButton.alpha = mainSite.type & MPSiteTypeClassGenerated? 0.5f: 0; self.counterLabel.alpha = self.counterButton.alpha = mainSite.type & MPSiteTypeClassGenerated? 0.5f: 0;
self.modeButton.selected = settingsMode; self.modeButton.selected = settingsMode;
@@ -520,13 +518,21 @@
[self.passwordField resignFirstResponder]; [self.passwordField resignFirstResponder];
} }
if ([[MPiOSAppDelegate get] isFeatureUnlocked:MPProductGenerateLogins]) if ([[MPiOSAppDelegate get] isFeatureUnlocked:MPProductGenerateLogins])
[self.loginNameButton setTitle:@"Tap to generate username or use pencil to save one" forState:UIControlStateNormal]; [self.loginNameButton setTitle:@"Tap here to ⚙ generate username or the pencil to type one" forState:UIControlStateNormal];
else else
[self.loginNameButton setTitle:@"Tap the pencil to save a username" forState:UIControlStateNormal]; [self.loginNameButton setTitle:@"Tap the pencil to type a username" forState:UIControlStateNormal];
// Site Name // Site Name
[self updateSiteName:mainSite]; [self updateSiteName:mainSite];
// Site Counter
if ([mainSite isKindOfClass:[MPGeneratedSiteEntity class]])
self.counterLabel.text = strf( @"%lu", (unsigned long)((MPGeneratedSiteEntity *)mainSite).counter );
// Site Login Name
self.loginNameField.enabled = self.passwordField.enabled = //
[self.loginNameField isFirstResponder] || [self.passwordField isFirstResponder];
// Site Password // Site Password
self.passwordField.secureTextEntry = [[MPiOSConfig get].hidePasswords boolValue]; self.passwordField.secureTextEntry = [[MPiOSConfig get].hidePasswords boolValue];
self.passwordField.attributedPlaceholder = stra( self.passwordField.attributedPlaceholder = stra(
@@ -534,12 +540,15 @@
mainSite.type & MPSiteTypeClassGenerated? strl( @"..." ): @"", @{ mainSite.type & MPSiteTypeClassGenerated? strl( @"..." ): @"", @{
NSForegroundColorAttributeName: [UIColor whiteColor] NSForegroundColorAttributeName: [UIColor whiteColor]
} ); } );
// Calculate Fields
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPSiteEntity *site = [self siteInContext:context]; MPSiteEntity *site = [self siteInContext:context];
MPKey *key = [MPiOSAppDelegate get].key; MPKey *key = [MPiOSAppDelegate get].key;
if (!key) if (!key)
return; return;
BOOL loginGenerated = site.loginGenerated;
NSString *password = nil, *loginName = [site resolveLoginUsingKey:key]; NSString *password = nil, *loginName = [site resolveLoginUsingKey:key];
MPSiteType transientType = [[MPiOSAppDelegate get] activeUserInContext:context].defaultType?: MPSiteTypeGeneratedLong; MPSiteType transientType = [[MPiOSAppDelegate get] activeUserInContext:context].defaultType?: MPSiteTypeGeneratedLong;
if (self.transientSite && transientType & MPSiteTypeClassGenerated) if (self.transientSite && transientType & MPSiteTypeClassGenerated)
@@ -559,13 +568,15 @@
BOOL requiresExplicitMigration = site.requiresExplicitMigration; BOOL requiresExplicitMigration = site.requiresExplicitMigration;
PearlMainQueue( ^{ PearlMainQueue( ^{
self.loginNameField.text = loginName;
self.passwordField.text = password; self.passwordField.text = password;
self.strengthLabel.text = timeToCrackString; self.strengthLabel.text = timeToCrackString;
self.loginNameButton.titleLabel.alpha = [loginName length] || self.loginNameField.enabled? 0: 1; self.loginNameGenerated.hidden = !loginGenerated;
self.loginNameField.attributedText =
strarm( stra( loginName?: @"", self.siteNameLabel.textAttributes ), NSParagraphStyleAttributeName, nil );
self.loginNameButton.hidden = [loginName length] || self.loginNameField.enabled;
if (![password length]) { if (![password length]) {
self.indicatorView.alpha = 1; self.indicatorView.hidden = NO;
[self.indicatorView removeFromSuperview]; [self.indicatorView removeFromSuperview];
[self.modeScrollView addSubview:self.indicatorView]; [self.modeScrollView addSubview:self.indicatorView];
[self.contentView addConstraintsWithVisualFormat:@"V:[indicator][target]" options:NSLayoutFormatAlignAllCenterX [self.contentView addConstraintsWithVisualFormat:@"V:[indicator][target]" options:NSLayoutFormatAlignAllCenterX
@@ -575,7 +586,7 @@
}]; }];
} }
else if (requiresExplicitMigration) { else if (requiresExplicitMigration) {
self.indicatorView.alpha = 1; self.indicatorView.hidden = NO;
[self.indicatorView removeFromSuperview]; [self.indicatorView removeFromSuperview];
[self.modeScrollView addSubview:self.indicatorView]; [self.modeScrollView addSubview:self.indicatorView];
[self.contentView addConstraintsWithVisualFormat:@"V:[indicator][target]" options:NSLayoutFormatAlignAllCenterX [self.contentView addConstraintsWithVisualFormat:@"V:[indicator][target]" options:NSLayoutFormatAlignAllCenterX
@@ -585,18 +596,10 @@
}]; }];
} }
else else
self.indicatorView.alpha = 0; self.indicatorView.hidden = YES;
} ); } );
}]; }];
// Site Counter
if ([mainSite isKindOfClass:[MPGeneratedSiteEntity class]])
self.counterLabel.text = strf( @"%lu", (unsigned long)((MPGeneratedSiteEntity *)mainSite).counter );
// Site Login Name
self.loginNameField.enabled = self.passwordField.enabled = //
[self.loginNameField isFirstResponder] || [self.passwordField isFirstResponder];
[self.contentView layoutIfNeeded]; [self.contentView layoutIfNeeded];
}]; }];
} }
@@ -616,8 +619,9 @@
range:NSMakeRange( s, [self.fuzzyGroups[f] length] )]; range:NSMakeRange( s, [self.fuzzyGroups[f] length] )];
} }
[attributedSiteName appendAttributedString:stra( if (self.transientSite)
strf( @" - %@", self.transientSite? @"Tap to create": [site.algorithm shortNameOfType:site.type] ), @{} )]; [attributedSiteName appendAttributedString:stra( @" Tap to create", @{} )];
self.siteNameLabel.attributedText = attributedSiteName; self.siteNameLabel.attributedText = attributedSiteName;
} }

View File

@@ -46,6 +46,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
NSUInteger _transientItem; NSUInteger _transientItem;
NSCharacterSet *_siteNameAcceptableCharactersSet; NSCharacterSet *_siteNameAcceptableCharactersSet;
NSArray *_fuzzyGroups; NSArray *_fuzzyGroups;
NSMutableArray *_passwordCollectionViewUpdatesBatch;
} }
#pragma mark - Life #pragma mark - Life
@@ -62,6 +63,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
_backgroundColor = self.passwordCollectionView.backgroundColor; _backgroundColor = self.passwordCollectionView.backgroundColor;
_darkenedBackgroundColor = [_backgroundColor colorWithAlphaComponent:0.6f]; _darkenedBackgroundColor = [_backgroundColor colorWithAlphaComponent:0.6f];
_transientItem = NSNotFound; _transientItem = NSNotFound;
_passwordCollectionViewUpdatesBatch = [NSMutableArray arrayWithCapacity:4];
self.view.backgroundColor = [UIColor clearColor]; self.view.backgroundColor = [UIColor clearColor];
[self.passwordCollectionView automaticallyAdjustInsetsForKeyboard]; [self.passwordCollectionView automaticallyAdjustInsetsForKeyboard];
@@ -181,10 +183,13 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath
forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
Weakify( self );
if (controller == _fetchedResultsController) { if (controller == _fetchedResultsController) {
@try { @synchronized (_passwordCollectionViewUpdatesBatch) {
[self.passwordCollectionView performBatchUpdates:^{ [_passwordCollectionViewUpdatesBatch addObject:[^{
[self fetchedItemsDidUpdate]; Strongify( self );
switch (type) { switch (type) {
case NSFetchedResultsChangeInsert: case NSFetchedResultsChangeInsert:
[self.passwordCollectionView insertItemsAtIndexPaths:@[ newIndexPath ]]; [self.passwordCollectionView insertItemsAtIndexPaths:@[ newIndexPath ]];
@@ -193,18 +198,35 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
[self.passwordCollectionView deleteItemsAtIndexPaths:@[ indexPath ]]; [self.passwordCollectionView deleteItemsAtIndexPaths:@[ indexPath ]];
break; break;
case NSFetchedResultsChangeMove: case NSFetchedResultsChangeMove:
[self.passwordCollectionView moveItemAtIndexPath:indexPath toIndexPath:newIndexPath]; if (![indexPath isEqual:newIndexPath])
[self.passwordCollectionView moveItemAtIndexPath:indexPath toIndexPath:newIndexPath];
break; break;
case NSFetchedResultsChangeUpdate: case NSFetchedResultsChangeUpdate:
[self.passwordCollectionView reloadItemsAtIndexPaths:@[ indexPath ]]; [self.passwordCollectionView reloadItemsAtIndexPaths:@[ indexPath ]];
break; break;
} }
} completion:nil]; } copy]];
}
@catch (NSException *exception) {
wrn( @"While updating password cells: %@", [exception fullDescription] );
[self.passwordCollectionView reloadData];
} }
[controller.managedObjectContext performBlock:^{
PearlMainQueueOperation( ^{
@try {
[self.passwordCollectionView performBatchUpdates:^{
[self updateTransientItem];
@synchronized (_passwordCollectionViewUpdatesBatch) {
for (VoidBlock block in _passwordCollectionViewUpdatesBatch)
block();
[_passwordCollectionViewUpdatesBatch removeAllObjects];
}
} completion:nil];
}
@catch (NSException *exception) {
wrn( @"While updating password cells: %@", [exception fullDescription] );
[self.passwordCollectionView reloadData];
}
} );
}];
} }
} }
@@ -290,25 +312,24 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
}]; }];
} }
- (void)fetchedItemsDidUpdate { - (void)updateTransientItem {
NSString *query = self.query; NSString *query = self.query;
_showTransientItem = [query length] > 0; _showTransientItem = [query length] > 0 && ![[[self.fetchedResultsController.sections[0] objects] filteredArrayUsingPredicate:
NSUInteger objects = ((id<NSFetchedResultsSectionInfo>)self.fetchedResultsController.sections[0]).numberOfObjects; [NSPredicate predicateWithBlock:^BOOL(MPSiteEntity *site, NSDictionary<NSString *, id> *bindings) {
if (_showTransientItem && objects == 1 && return [site.name isEqualToString:query];
[[[self.fetchedResultsController.fetchedObjects firstObject] name] isEqualToString:query]) }]] count];
_showTransientItem = NO; if (!_showTransientItem && _transientItem != NSNotFound)
if ([self.passwordCollectionView numberOfSections] > 0) { [self.passwordCollectionView deleteItemsAtIndexPaths:
if (!_showTransientItem && _transientItem != NSNotFound) @[ [NSIndexPath indexPathForItem:_transientItem inSection:0] ]];
[self.passwordCollectionView deleteItemsAtIndexPaths: else if (_showTransientItem && _transientItem == NSNotFound) {
@[ [NSIndexPath indexPathForItem:_transientItem inSection:0] ]]; NSUInteger objects = [self.fetchedResultsController.sections[0] numberOfObjects];
else if (_showTransientItem && _transientItem == NSNotFound) [self.passwordCollectionView insertItemsAtIndexPaths:
[self.passwordCollectionView insertItemsAtIndexPaths: @[ [NSIndexPath indexPathForItem:objects inSection:0] ]];
@[ [NSIndexPath indexPathForItem:objects inSection:0] ]];
else if (_transientItem != NSNotFound)
[self.passwordCollectionView reloadItemsAtIndexPaths:
@[ [NSIndexPath indexPathForItem:_transientItem inSection:0] ]];
} }
else if (_transientItem != NSNotFound)
[self.passwordCollectionView reloadItemsAtIndexPaths:
@[ [NSIndexPath indexPathForItem:_transientItem inSection:0] ]];
} }
- (void)registerObservers { - (void)registerObservers {
@@ -361,6 +382,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
if (mainContext) if (mainContext)
PearlAddNotificationObserver( NSManagedObjectContextDidSaveNotification, mainContext, nil, PearlAddNotificationObserver( NSManagedObjectContextDidSaveNotification, mainContext, nil,
^(MPPasswordsViewController *self, NSNotification *note) { ^(MPPasswordsViewController *self, NSNotification *note) {
// TODO: either move this into the app delegate or remove the duplicate signOutAnimated: call from the app delegate.
if (![[MPiOSAppDelegate get] activeUserInContext:note.object]) if (![[MPiOSAppDelegate get] activeUserInContext:note.object])
[[MPiOSAppDelegate get] signOutAnimated:YES]; [[MPiOSAppDelegate get] signOutAnimated:YES];
} ); } );
@@ -421,7 +443,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
PearlMainQueue( ^{ PearlMainQueue( ^{
@try { @try {
[self.passwordCollectionView performBatchUpdates:^{ [self.passwordCollectionView performBatchUpdates:^{
[self fetchedItemsDidUpdate]; [self updateTransientItem];
NSInteger fromSections = self.passwordCollectionView.numberOfSections; NSInteger fromSections = self.passwordCollectionView.numberOfSections;
NSInteger toSections = [self numberOfSectionsInCollectionView:self.passwordCollectionView]; NSInteger toSections = [self numberOfSectionsInCollectionView:self.passwordCollectionView];

View File

@@ -22,8 +22,6 @@
@implementation MPPopdownSegue { @implementation MPPopdownSegue {
} }
static char UnwindingObserverKey;
- (void)perform { - (void)perform {
MPPasswordsViewController *passwordsVC; MPPasswordsViewController *passwordsVC;
@@ -39,19 +37,20 @@ static char UnwindingObserverKey;
[passwordsVC.popdownContainer addConstraintsWithVisualFormats:@[ @"H:|[popdownView]|", @"V:|[popdownView]|" ] options:0 [passwordsVC.popdownContainer addConstraintsWithVisualFormats:@[ @"H:|[popdownView]|", @"V:|[popdownView]|" ] options:0
metrics:nil views:NSDictionaryOfVariableBindings( popdownView )]; metrics:nil views:NSDictionaryOfVariableBindings( popdownView )];
[UIView animateWithDuration:0.3f animations:^{ [passwordsVC.popdownToTopConstraint layoutIfNeeded];
[[passwordsVC.popdownToTopConstraint updatePriority:1] layoutIfNeeded];
} completion:^(BOOL finished) {
[popdownVC didMoveToParentViewController:passwordsVC];
id<NSObject> observer = [[NSNotificationCenter defaultCenter] addObserverForName:MPSignedOutNotification object:nil [UIView animateWithDuration:0.6f delay:0 usingSpringWithDamping:0.75f initialSpringVelocity:1
queue:[NSOperationQueue mainQueue] usingBlock: options:UIViewAnimationOptionCurveEaseOut animations:^{
^(NSNotification *note) { [[passwordsVC.popdownToTopConstraint updatePriority:1] layoutIfNeeded];
[[[MPPopdownSegue alloc] initWithIdentifier:@"unwind-popdown" source:popdownVC } completion:^(BOOL finished) {
destination:passwordsVC] perform]; [popdownVC didMoveToParentViewController:passwordsVC];
}];
objc_setAssociatedObject( popdownVC, &UnwindingObserverKey, observer, OBJC_ASSOCIATION_RETAIN ); PearlAddNotificationObserverTo( popdownVC, MPSignedOutNotification, nil, [NSOperationQueue mainQueue],
}]; ^(id host, NSNotification *note) {
[[[MPPopdownSegue alloc] initWithIdentifier:@"unwind-popdown" source:popdownVC destination:passwordsVC]
perform];
} );
}];
} }
else { else {
popdownVC = self.sourceViewController; popdownVC = self.sourceViewController;
@@ -59,16 +58,16 @@ static char UnwindingObserverKey;
passwordsVC = (id)passwordsVC.parentViewController); passwordsVC = (id)passwordsVC.parentViewController);
NSAssert( passwordsVC, @"Couldn't find passwords VC to pop back to." ); NSAssert( passwordsVC, @"Couldn't find passwords VC to pop back to." );
[[NSNotificationCenter defaultCenter] removeObserver:objc_getAssociatedObject( popdownVC, &UnwindingObserverKey )]; PearlRemoveNotificationObserversFrom( popdownVC );
objc_setAssociatedObject( popdownVC, &UnwindingObserverKey, nil, OBJC_ASSOCIATION_RETAIN );
[popdownVC willMoveToParentViewController:nil]; [popdownVC willMoveToParentViewController:nil];
[UIView animateWithDuration:0.3f delay:0 options:UIViewAnimationOptionOverrideInheritedDuration animations:^{ [UIView animateWithDuration:0.4f delay:0 options:UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionOverrideInheritedDuration
[[passwordsVC.popdownToTopConstraint updatePriority:UILayoutPriorityDefaultHigh] layoutIfNeeded]; animations:^{
} completion:^(BOOL finished) { [[passwordsVC.popdownToTopConstraint updatePriority:UILayoutPriorityDefaultHigh] layoutIfNeeded];
[popdownVC.view removeFromSuperview]; } completion:^(BOOL finished) {
[popdownVC removeFromParentViewController]; [popdownVC.view removeFromSuperview];
}]; [popdownVC removeFromParentViewController];
}];
} }
} }

View File

@@ -29,8 +29,10 @@
@property(weak, nonatomic) IBOutlet UITableViewCell *exportCell; @property(weak, nonatomic) IBOutlet UITableViewCell *exportCell;
@property(weak, nonatomic) IBOutlet UITableViewCell *checkInconsistencies; @property(weak, nonatomic) IBOutlet UITableViewCell *checkInconsistencies;
@property(weak, nonatomic) IBOutlet UIImageView *avatarImage; @property(weak, nonatomic) IBOutlet UIImageView *avatarImage;
@property(weak, nonatomic) IBOutlet UISegmentedControl *generatedTypeControl; @property(weak, nonatomic) IBOutlet UISegmentedControl *generated1TypeControl;
@property(weak, nonatomic) IBOutlet UISegmentedControl *generated2TypeControl;
@property(weak, nonatomic) IBOutlet UISegmentedControl *storedTypeControl; @property(weak, nonatomic) IBOutlet UISegmentedControl *storedTypeControl;
@property(weak, nonatomic) IBOutlet UILabel *passwordTypeExample;
- (IBAction)previousAvatar:(id)sender; - (IBAction)previousAvatar:(id)sender;
- (IBAction)nextAvatar:(id)sender; - (IBAction)nextAvatar:(id)sender;

View File

@@ -55,12 +55,24 @@
- (void)reload { - (void)reload {
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserForMainThread]; MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserForMainThread];
self.generatedTypeControl.selectedSegmentIndex = [self generatedSegmentIndexForType:activeUser.defaultType];
self.storedTypeControl.selectedSegmentIndex = [self storedSegmentIndexForType:activeUser.defaultType];
self.avatarImage.image = [UIImage imageNamed:strf( @"avatar-%lu", (unsigned long)activeUser.avatar )]; self.avatarImage.image = [UIImage imageNamed:strf( @"avatar-%lu", (unsigned long)activeUser.avatar )];
self.savePasswordSwitch.on = activeUser.saveKey; self.savePasswordSwitch.on = activeUser.saveKey;
self.touchIDSwitch.on = activeUser.touchID; self.touchIDSwitch.on = activeUser.touchID;
self.touchIDSwitch.enabled = self.savePasswordSwitch.on && [[MPiOSAppDelegate get] isFeatureUnlocked:MPProductTouchID]; self.touchIDSwitch.enabled = self.savePasswordSwitch.on && [[MPiOSAppDelegate get] isFeatureUnlocked:MPProductTouchID];
MPSiteType defaultType = activeUser.defaultType;
self.generated1TypeControl.selectedSegmentIndex = [self generated1SegmentIndexForType:defaultType];
self.generated2TypeControl.selectedSegmentIndex = [self generated2SegmentIndexForType:defaultType];
self.storedTypeControl.selectedSegmentIndex = [self storedSegmentIndexForType:defaultType];
PearlNotMainQueue( ^{
NSString *examplePassword = nil;
if (defaultType & MPSiteTypeClassGenerated)
examplePassword = [MPAlgorithmDefault generatePasswordForSiteNamed:@"test" ofType:defaultType
withCounter:1 usingKey:[MPiOSAppDelegate get].key];
PearlMainQueue( ^{
self.passwordTypeExample.text = [examplePassword length]? [NSString stringWithFormat:@"eg. %@", examplePassword]: nil;
} );
} );
} }
#pragma mark - UITableViewDelegate #pragma mark - UITableViewDelegate
@@ -88,14 +100,18 @@
[self dismissPopup]; [self dismissPopup];
[[MPiOSAppDelegate get] signOutAnimated:YES]; [[MPiOSAppDelegate get] signOutAnimated:YES];
} }
if (cell == self.feedbackCell) if (cell == self.feedbackCell)
[[MPiOSAppDelegate get] showFeedbackWithLogs:YES forVC:self]; [[MPiOSAppDelegate get] showFeedbackWithLogs:YES forVC:self];
if (cell == self.exportCell) if (cell == self.exportCell)
[[MPiOSAppDelegate get] showExportForVC:self]; [[MPiOSAppDelegate get] showExportForVC:self];
if (cell == self.showHelpCell) { if (cell == self.showHelpCell) {
MPPasswordsViewController *passwordsVC = [self dismissPopup]; MPPasswordsViewController *passwordsVC = [self dismissPopup];
[passwordsVC performSegueWithIdentifier:@"guide" sender:self]; [passwordsVC performSegueWithIdentifier:@"guide" sender:self];
} }
if (cell == self.checkInconsistencies) if (cell == self.checkInconsistencies)
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
if ([[MPiOSAppDelegate get] findAndFixInconsistenciesSaveInContext:context] == MPFixableResultNoProblems) if ([[MPiOSAppDelegate get] findAndFixInconsistenciesSaveInContext:context] == MPFixableResultNoProblems)
@@ -140,11 +156,13 @@
} ); } );
}]; }];
if (sender == self.generatedTypeControl || sender == self.storedTypeControl) { if (sender == self.generated1TypeControl || sender == self.generated2TypeControl || sender == self.storedTypeControl) {
if (sender == self.generatedTypeControl) if (sender != self.generated1TypeControl)
self.generated1TypeControl.selectedSegmentIndex = -1;
if (sender != self.generated2TypeControl)
self.generated2TypeControl.selectedSegmentIndex = -1;
if (sender != self.storedTypeControl)
self.storedTypeControl.selectedSegmentIndex = -1; self.storedTypeControl.selectedSegmentIndex = -1;
else if (sender == self.storedTypeControl)
self.generatedTypeControl.selectedSegmentIndex = -1;
MPSiteType defaultType = [self typeForSelectedSegment]; MPSiteType defaultType = [self typeForSelectedSegment];
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
@@ -226,10 +244,17 @@
- (MPSiteType)typeForSelectedSegment { - (MPSiteType)typeForSelectedSegment {
NSInteger selectedGeneratedIndex = self.generatedTypeControl.selectedSegmentIndex; NSInteger selectedGenerated1Index = self.generated1TypeControl.selectedSegmentIndex;
NSInteger selectedGenerated2Index = self.generated2TypeControl.selectedSegmentIndex;
NSInteger selectedStoredIndex = self.storedTypeControl.selectedSegmentIndex; NSInteger selectedStoredIndex = self.storedTypeControl.selectedSegmentIndex;
switch (selectedGeneratedIndex) { switch (selectedGenerated1Index) {
case 0:
return MPSiteTypeGeneratedPhrase;
case 1:
return MPSiteTypeGeneratedName;
default:
switch (selectedGenerated2Index) {
case 0: case 0:
return MPSiteTypeGeneratedMaximum; return MPSiteTypeGeneratedMaximum;
case 1: case 1:
@@ -250,13 +275,26 @@
case 1: case 1:
return MPSiteTypeStoredDevicePrivate; return MPSiteTypeStoredDevicePrivate;
default: default:
Throw( @"unsupported selected type index: generated=%ld, stored=%ld", (long)selectedGeneratedIndex, Throw( @"unsupported selected type index: generated1=%ld, generated2=%ld, stored=%ld",
(long)selectedStoredIndex ); (long)selectedGenerated1Index, (long)selectedGenerated2Index, (long)selectedStoredIndex );
} }
} }
}
} }
- (NSInteger)generatedSegmentIndexForType:(MPSiteType)type { - (NSInteger)generated1SegmentIndexForType:(MPSiteType)type {
switch (type) {
case MPSiteTypeGeneratedPhrase:
return 0;
case MPSiteTypeGeneratedName:
return 1;
default:
return -1;
}
}
- (NSInteger)generated2SegmentIndexForType:(MPSiteType)type {
switch (type) { switch (type) {
case MPSiteTypeGeneratedMaximum: case MPSiteTypeGeneratedMaximum:

View File

@@ -245,7 +245,7 @@ PearlEnum( MPDevelopmentFuelConsumption,
NSMutableArray *showCells = [NSMutableArray array]; NSMutableArray *showCells = [NSMutableArray array];
NSMutableArray *hideCells = [NSMutableArray array]; NSMutableArray *hideCells = [NSMutableArray array];
[hideCells addObjectsFromArray:self.allCellsBySection[0]]; [hideCells addObjectsFromArray:[self.allCellsBySection[0] array]];
[hideCells addObject:self.loadingCell]; [hideCells addObject:self.loadingCell];
for (SKProduct *product in self.products) { for (SKProduct *product in self.products) {

View File

@@ -89,11 +89,11 @@
if ([selectedSite isKindOfClass:[MPGeneratedSiteEntity class]]) if ([selectedSite isKindOfClass:[MPGeneratedSiteEntity class]])
counter = ((MPGeneratedSiteEntity *)selectedSite).counter; counter = ((MPGeneratedSiteEntity *)selectedSite).counter;
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0 ), ^{ PearlNotMainQueue( ^{
NSString *typeContent = [MPAlgorithmDefault generatePasswordForSiteNamed:name ofType:cellType NSString *typeContent = [MPAlgorithmDefault generatePasswordForSiteNamed:name ofType:cellType
withCounter:counter usingKey:[MPiOSAppDelegate get].key]; withCounter:counter usingKey:[MPiOSAppDelegate get].key];
dispatch_async( dispatch_get_main_queue(), ^{ PearlMainQueue( ^{
[(UITextField *)[[tableView cellForRowAtIndexPath:indexPath] viewWithTag:2] setText:typeContent]; [(UITextField *)[[tableView cellForRowAtIndexPath:indexPath] viewWithTag:2] setText:typeContent];
} ); } );
} ); } );

View File

@@ -19,7 +19,6 @@
#import "MPiOSAppDelegate.h" #import "MPiOSAppDelegate.h"
#import "MPAppDelegate_Key.h" #import "MPAppDelegate_Key.h"
#import "MPAppDelegate_Store.h" #import "MPAppDelegate_Store.h"
#import "IASKSettingsReader.h"
#import "MPStoreViewController.h" #import "MPStoreViewController.h"
@interface MPiOSAppDelegate()<UIDocumentInteractionControllerDelegate> @interface MPiOSAppDelegate()<UIDocumentInteractionControllerDelegate>
@@ -53,7 +52,7 @@
NSString *crashlyticsAPIKey = [self crashlyticsAPIKey]; NSString *crashlyticsAPIKey = [self crashlyticsAPIKey];
if ([crashlyticsAPIKey length]) { if ([crashlyticsAPIKey length]) {
inf( @"Initializing Crashlytics" ); inf( @"Initializing Crashlytics" );
#if defined (DEBUG) || defined (ADHOC) #if DEBUG
[Crashlytics sharedInstance].debugMode = YES; [Crashlytics sharedInstance].debugMode = YES;
#endif #endif
[[Crashlytics sharedInstance] setUserIdentifier:[PearlKeyChain deviceIdentifier]]; [[Crashlytics sharedInstance] setUserIdentifier:[PearlKeyChain deviceIdentifier]];
@@ -89,18 +88,6 @@
PearlAddNotificationObserver( NSUserDefaultsDidChangeNotification, nil, nil, ^(id self, NSNotification *note) { PearlAddNotificationObserver( NSUserDefaultsDidChangeNotification, nil, nil, ^(id self, NSNotification *note) {
[[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:nil]; [[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:nil];
} ); } );
#ifdef ADHOC
[PearlAlert showAlertWithTitle:@"Welcome, tester!" message:
@"Thank you for taking the time to test Master Password.\n\n"
@"Please provide any feedback, however minor it may seem, via the Feedback action item accessible from the top right.\n\n"
@"Contact me directly at:\n"
@"lhunath@lyndir.com\n"
@"Or report detailed issues at:\n"
@"https://youtrack.lyndir.com\n"
viewStyle:UIAlertViewStyleDefault initAlert:nil tappedButtonBlock:nil
cancelTitle:nil otherTitles:[PearlStrings get].commonButtonOkay, nil];
#endif
} }
@catch (id exception) { @catch (id exception) {
err( @"During Config Test: %@", exception ); err( @"During Config Test: %@", exception );
@@ -164,26 +151,24 @@
return NO; return NO;
// Arbitrary URL to mpsites data. // Arbitrary URL to mpsites data.
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{ [[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:
NSError *error; ^(NSData *importedSitesData, NSURLResponse *response, NSError *error) {
NSURLResponse *response; if (error)
NSData *importedSitesData = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url] err( @"While reading imported sites from %@: %@", url, [error fullDescription] );
returningResponse:&response error:&error]; if (!importedSitesData) {
if (error) [PearlAlert showError:strf( @"Master Password couldn't read the import sites.\n\n%@",
err( @"While reading imported sites from %@: %@", url, [error fullDescription] ); [error localizedDescription]?: error )];
if (!importedSitesData) { return;
[PearlAlert showError:strf( @"Master Password couldn't read the import sites.\n\n%@", [error localizedDescription]?: error )]; }
return;
}
NSString *importedSitesString = [[NSString alloc] initWithData:importedSitesData encoding:NSUTF8StringEncoding]; NSString *importedSitesString = [[NSString alloc] initWithData:importedSitesData encoding:NSUTF8StringEncoding];
if (!importedSitesString) { if (!importedSitesString) {
[PearlAlert showError:@"Master Password couldn't understand the import file."]; [PearlAlert showError:@"Master Password couldn't understand the import file."];
return; return;
} }
[self importSites:importedSitesString]; [self importSites:importedSitesString];
} ); }] resume];
return YES; return YES;
} }
@@ -199,57 +184,33 @@
PearlOverlay *activityOverlay = [PearlOverlay showProgressOverlayWithTitle:@"Importing"]; PearlOverlay *activityOverlay = [PearlOverlay showProgressOverlayWithTitle:@"Importing"];
MPImportResult result = [self importSites:importedSitesString askImportPassword:^NSString *(NSString *userName) { MPImportResult result = [self importSites:importedSitesString askImportPassword:^NSString *(NSString *userName) {
__block NSString *masterPassword = nil; return PearlAwait( ^(void (^setResult)(id)) {
dispatch_group_t importPasswordGroup = dispatch_group_create();
dispatch_group_enter( importPasswordGroup );
dispatch_async( dispatch_get_main_queue(), ^{
[PearlAlert showAlertWithTitle:@"Import File's Master Password" [PearlAlert showAlertWithTitle:@"Import File's Master Password"
message:strf( @"%@'s export was done using a different master password.\n" message:strf( @"%@'s export was done using a different master password.\n"
@"Enter that master password to unlock the exported data.", userName ) @"Enter that master password to unlock the exported data.", userName )
viewStyle:UIAlertViewStyleSecureTextInput viewStyle:UIAlertViewStyleSecureTextInput
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) { initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
@try { if (buttonIndex_ == [alert_ cancelButtonIndex])
if (buttonIndex_ == [alert_ cancelButtonIndex]) setResult( nil );
return; else
setResult( [alert_ textFieldAtIndex:0].text );
masterPassword = [alert_ textFieldAtIndex:0].text;
}
@finally {
dispatch_group_leave( importPasswordGroup );
}
} }
cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Unlock Import", nil]; cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Unlock Import", nil];
} ); } );
dispatch_group_wait( importPasswordGroup, DISPATCH_TIME_FOREVER );
return masterPassword;
} askUserPassword:^NSString *(NSString *userName, NSUInteger importCount, NSUInteger deleteCount) { } askUserPassword:^NSString *(NSString *userName, NSUInteger importCount, NSUInteger deleteCount) {
__block NSString *masterPassword = nil; return PearlAwait( (id)^(void (^setResult)(id)) {
dispatch_group_t userPasswordGroup = dispatch_group_create();
dispatch_group_enter( userPasswordGroup );
dispatch_async( dispatch_get_main_queue(), ^{
[PearlAlert showAlertWithTitle:strf( @"Master Password for\n%@", userName ) [PearlAlert showAlertWithTitle:strf( @"Master Password for\n%@", userName )
message:strf( @"Imports %lu sites, overwriting %lu.", message:strf( @"Imports %lu sites, overwriting %lu.",
(unsigned long)importCount, (unsigned long)deleteCount ) (unsigned long)importCount, (unsigned long)deleteCount )
viewStyle:UIAlertViewStyleSecureTextInput viewStyle:UIAlertViewStyleSecureTextInput
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) { initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
@try { if (buttonIndex_ == [alert_ cancelButtonIndex])
if (buttonIndex_ == [alert_ cancelButtonIndex]) setResult( nil );
return; else
setResult( [alert_ textFieldAtIndex:0].text );
masterPassword = [alert_ textFieldAtIndex:0].text;
}
@finally {
dispatch_group_leave( userPasswordGroup );
}
} }
cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Import", nil]; cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Import", nil];
} ); } );
dispatch_group_wait( userPasswordGroup, DISPATCH_TIME_FOREVER );
return masterPassword;
}]; }];
switch (result) { switch (result) {

File diff suppressed because it is too large Load Diff

View File

@@ -339,7 +339,7 @@ mpw-tests() {
### TARGETS ### TARGETS
haslib() { haslib() {
! LC_ALL=C cc -l"$1" 2>&1 | grep -q 'library not found' return cc -l"$1" -x c -o /dev/null - <<< 'int main() { return 0; }'
} }
cc() { cc() {
if hash llvm-gcc 2>/dev/null; then if hash llvm-gcc 2>/dev/null; then