Compare commits
51 Commits
2.2-androi
...
2.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f5bb9e114 | ||
|
|
4c4aaac08b | ||
|
|
5320e079ce | ||
|
|
636c337c78 | ||
|
|
c2b08678df | ||
|
|
7117d82aa4 | ||
|
|
4fa6436de2 | ||
|
|
3e30087856 | ||
|
|
6bd2d70270 | ||
|
|
cf11b01ed6 | ||
|
|
7e11f01331 | ||
|
|
aeca5e18fe | ||
|
|
615e7c98a8 | ||
|
|
e713776123 | ||
|
|
7d5b7e53d4 | ||
|
|
950c68437a | ||
|
|
300a04f5c7 | ||
|
|
8faf6b48dd | ||
|
|
5f0367ad29 | ||
|
|
e126a55912 | ||
|
|
d05c5eedd8 | ||
|
|
6819a2ace5 | ||
|
|
645b6c5f54 | ||
|
|
d3c09fd979 | ||
|
|
a41ae1814a | ||
|
|
70f7fa1345 | ||
|
|
ea9d8cc275 | ||
|
|
20d1811b5c | ||
|
|
634ef062f3 | ||
|
|
a424531a8a | ||
|
|
9cffe53993 | ||
|
|
fd35fea8cf | ||
|
|
01c21e95bb | ||
|
|
5af3ffa178 | ||
|
|
651d07f982 | ||
|
|
a383d0eee7 | ||
|
|
ca8f14fd3e | ||
|
|
fd855bb025 | ||
|
|
af340806af | ||
|
|
cdeee2576d | ||
|
|
779d2776a0 | ||
|
|
563aab9a81 | ||
|
|
73de98c5e2 | ||
|
|
c330728ac3 | ||
|
|
2db601475f | ||
|
|
d898646097 | ||
|
|
afaa17948f | ||
|
|
8514a58e64 | ||
|
|
f80bbff46e | ||
|
|
f7d595b0e7 | ||
|
|
422066ad4a |
1
.gitignore
vendored
@@ -1,5 +1,6 @@
|
|||||||
# OS-Specific junk.
|
# OS-Specific junk.
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
# IntelliJ
|
# IntelliJ
|
||||||
/MasterPassword/Java/.idea
|
/MasterPassword/Java/.idea
|
||||||
|
|||||||
8
.idea/encodings.xml
generated
Normal file → Executable file
@@ -1,5 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
|
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false">
|
||||||
|
<file url="file://$PROJECT_DIR$/MasterPassword/Java" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/MasterPassword/Java/masterpassword-algorithm" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/MasterPassword/Java/masterpassword-cli" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/MasterPassword/Java/masterpassword-gui" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/MasterPassword/Java/masterpassword-model" charset="UTF-8" />
|
||||||
|
</component>
|
||||||
</project>
|
</project>
|
||||||
|
|
||||||
|
|||||||
2
External/Pearl
vendored
@@ -218,3 +218,8 @@ OBJC_EXTERN void CLSNSLogv(NSString *format, va_list args) NS_FORMAT_FUNCTION(1,
|
|||||||
- (void)crashlytics:(Crashlytics *)crashlytics didDetectCrashDuringPreviousExecution:(id <CLSCrashReport>)crash;
|
- (void)crashlytics:(Crashlytics *)crashlytics didDetectCrashDuringPreviousExecution:(id <CLSCrashReport>)crash;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `CrashlyticsKit` can be used as a parameter to `[Fabric with:@[CrashlyticsKit]];` in Objective-C. In Swift, simply use `Crashlytics()`
|
||||||
|
*/
|
||||||
|
#define CrashlyticsKit [Crashlytics sharedInstance]
|
||||||
|
|||||||
@@ -15,13 +15,13 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>FMWK</string>
|
<string>FMWK</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>2.2.5</string>
|
<string>2.2.9</string>
|
||||||
<key>CFBundleSupportedPlatforms</key>
|
<key>CFBundleSupportedPlatforms</key>
|
||||||
<array>
|
<array>
|
||||||
<string>iPhoneOS</string>
|
<string>iPhoneOS</string>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>40</string>
|
<string>44</string>
|
||||||
<key>DTPlatformName</key>
|
<key>DTPlatformName</key>
|
||||||
<string>iphoneos</string>
|
<string>iphoneos</string>
|
||||||
<key>MinimumOSVersion</key>
|
<key>MinimumOSVersion</key>
|
||||||
|
|||||||
BIN
External/iOS/Crashlytics.framework/run
vendored
BIN
External/iOS/Crashlytics.framework/submit
vendored
61
MasterPassword/C/bashcomplib
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# FIXME
|
||||||
|
# partials are currently readline words, but these can't be reliably compared against literal data. We need to make them literal first.. in a safe way. Currently using xargs' quote parser as a hack.
|
||||||
|
|
||||||
|
# Process literal completion options in COMPREPLY
|
||||||
|
#
|
||||||
|
# 1. Filter COMPREPLY by excluding the options that do not match the word that is being completed.
|
||||||
|
# 2. Shell-escape the COMPREPLY words so they remain syntactical words when injected into the completed command.
|
||||||
|
# 3. Add a space after the words so successful completions advance to the next word
|
||||||
|
# (we disabled this default behavior with -o nospace so we can do completions that don't want this, eg. directory names)
|
||||||
|
_comp_finish_completions() {
|
||||||
|
local partial=$(xargs <<< "${COMP_WORDS[COMP_CWORD]}") # FIXME
|
||||||
|
local word words=( "${COMPREPLY[@]}" )
|
||||||
|
|
||||||
|
COMPREPLY=()
|
||||||
|
for word in "${words[@]}"; do
|
||||||
|
( shopt -s nocasematch; [[ $word = $partial* ]] ) && COMPREPLY+=( "$(printf '%q ' "$word")" )
|
||||||
|
done
|
||||||
|
|
||||||
|
if (( ${#COMPREPLY[@]} > 1 )) && [[ $_comp_title ]]; then
|
||||||
|
printf '\n%s:' "$_comp_title"
|
||||||
|
unset _comp_title
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Perform pathname completion.
|
||||||
|
#
|
||||||
|
# 1. Populate COMPREPLY with pathnames.
|
||||||
|
# 2. Shell-escape the COMPREPLY words so they remain syntactical words when injected into the completed command.
|
||||||
|
# 3. Add a space after file names so successful completions advance to the next word.
|
||||||
|
# Directory names are suffixed with a / instead so we can keep completing the files inside.
|
||||||
|
_comp_complete_path() {
|
||||||
|
local partial=$(xargs <<< "${COMP_WORDS[COMP_CWORD]}")
|
||||||
|
local path
|
||||||
|
|
||||||
|
COMPREPLY=()
|
||||||
|
for path in "$partial"*; do
|
||||||
|
if [[ -d $path ]]; then
|
||||||
|
COMPREPLY+=( "$(printf '%q/' "$path")" )
|
||||||
|
|
||||||
|
elif [[ -e $path ]]; then
|
||||||
|
COMPREPLY+=( "$(printf '%q ' "$path")" )
|
||||||
|
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
_show_args() {
|
||||||
|
echo
|
||||||
|
local i=0
|
||||||
|
for arg; do
|
||||||
|
printf "arg %d: %s\n" "$((i++))" "$arg"
|
||||||
|
done
|
||||||
|
|
||||||
|
i=0
|
||||||
|
for word in "${COMP_WORDS[@]}"; do
|
||||||
|
printf "word %d: %s -> %s %s\n" "$i" "$word" "$(xargs <<< "$word")" "$( ((i == $COMP_CWORD)) && echo '<CWORD>' )"
|
||||||
|
let i++
|
||||||
|
done
|
||||||
|
}
|
||||||
@@ -39,7 +39,7 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Optional features.
|
# Optional features.
|
||||||
mpw_color=0 # Colorized Identicon, requires libncurses-dev
|
mpw_color=1 # Colorized Identicon, requires libncurses-dev
|
||||||
|
|
||||||
|
|
||||||
### DEPENDENCIES
|
### DEPENDENCIES
|
||||||
@@ -204,11 +204,11 @@ mpw() {
|
|||||||
|
|
||||||
echo
|
echo
|
||||||
echo "Building target: $target..."
|
echo "Building target: $target..."
|
||||||
CFLAGS=(
|
local CFLAGS=(
|
||||||
# include paths
|
# include paths
|
||||||
-I"lib/include"
|
-I"lib/include"
|
||||||
)
|
)
|
||||||
LDFLAGS=(
|
local LDFLAGS=(
|
||||||
# scrypt
|
# scrypt
|
||||||
"lib/scrypt/scrypt-crypto_aesctr.o"
|
"lib/scrypt/scrypt-crypto_aesctr.o"
|
||||||
"lib/scrypt/scrypt-sha256.o"
|
"lib/scrypt/scrypt-sha256.o"
|
||||||
@@ -240,11 +240,11 @@ mpw-bench() {
|
|||||||
|
|
||||||
echo
|
echo
|
||||||
echo "Building target: $target..."
|
echo "Building target: $target..."
|
||||||
CFLAGS=(
|
local CFLAGS=(
|
||||||
# include paths
|
# include paths
|
||||||
-I"lib/include"
|
-I"lib/include"
|
||||||
)
|
)
|
||||||
LDFLAGS=(
|
local LDFLAGS=(
|
||||||
# scrypt
|
# scrypt
|
||||||
"lib/scrypt/scrypt-crypto_aesctr.o"
|
"lib/scrypt/scrypt-crypto_aesctr.o"
|
||||||
"lib/scrypt/scrypt-sha256.o"
|
"lib/scrypt/scrypt-sha256.o"
|
||||||
@@ -264,6 +264,9 @@ mpw-bench() {
|
|||||||
-l"crypto"
|
-l"crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc "${CFLAGS[@]}" "$@" -c mpw-algorithm.c -o mpw-algorithm.o
|
||||||
|
cc "${CFLAGS[@]}" "$@" -c mpw-types.c -o mpw-types.o
|
||||||
|
cc "${CFLAGS[@]}" "$@" -c mpw-util.c -o mpw-util.o
|
||||||
cc "${CFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" \
|
cc "${CFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" \
|
||||||
"${LDFLAGS[@]}" "mpw-bench.c" -o "mpw-bench"
|
"${LDFLAGS[@]}" "mpw-bench.c" -o "mpw-bench"
|
||||||
echo "done! Now use ./mpw-bench"
|
echo "done! Now use ./mpw-bench"
|
||||||
@@ -276,13 +279,13 @@ mpw-tests() {
|
|||||||
|
|
||||||
echo
|
echo
|
||||||
echo "Building target: $target..."
|
echo "Building target: $target..."
|
||||||
CFLAGS=(
|
local CFLAGS=(
|
||||||
# include paths
|
# include paths
|
||||||
-I"lib/include"
|
-I"lib/include"
|
||||||
-I"/usr/include/libxml2"
|
-I"/usr/include/libxml2"
|
||||||
-I"/usr/local/include/libxml2"
|
-I"/usr/local/include/libxml2"
|
||||||
)
|
)
|
||||||
LDFLAGS=(
|
local LDFLAGS=(
|
||||||
# scrypt
|
# scrypt
|
||||||
"lib/scrypt/scrypt-crypto_aesctr.o"
|
"lib/scrypt/scrypt-crypto_aesctr.o"
|
||||||
"lib/scrypt/scrypt-sha256.o"
|
"lib/scrypt/scrypt-sha256.o"
|
||||||
@@ -296,7 +299,10 @@ mpw-tests() {
|
|||||||
-l"crypto" -l"xml2"
|
-l"crypto" -l"xml2"
|
||||||
)
|
)
|
||||||
|
|
||||||
cc "${CFLAGS[@]}" "$@" -c mpw-tests-util.c -o mpw-tests-util.o
|
cc "${CFLAGS[@]}" "$@" -c mpw-algorithm.c -o mpw-algorithm.o
|
||||||
|
cc "${CFLAGS[@]}" "$@" -c mpw-types.c -o mpw-types.o
|
||||||
|
cc "${CFLAGS[@]}" "$@" -c mpw-util.c -o mpw-util.o
|
||||||
|
cc "${CFLAGS[@]}" "$@" -c mpw-tests-util.c -o mpw-tests-util.o
|
||||||
cc "${CFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" "mpw-tests-util.o" \
|
cc "${CFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" "mpw-tests-util.o" \
|
||||||
"${LDFLAGS[@]}" "mpw-tests.c" -o "mpw-tests"
|
"${LDFLAGS[@]}" "mpw-tests.c" -o "mpw-tests"
|
||||||
echo "done! Now use ./mpw-tests"
|
echo "done! Now use ./mpw-tests"
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
// NOTE: mpw is currently NOT thread-safe.
|
||||||
#include "mpw-types.h"
|
#include "mpw-types.h"
|
||||||
|
|
||||||
typedef enum(unsigned int, MPAlgorithmVersion) {
|
typedef enum(unsigned int, MPAlgorithmVersion) {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include "mpw-types.h"
|
||||||
#include "mpw-util.h"
|
#include "mpw-util.h"
|
||||||
|
|
||||||
#define MP_N 32768
|
#define MP_N 32768
|
||||||
@@ -37,7 +38,8 @@ static const char mpw_characterFromClass_v0(char characterClass, uint16_t seedBy
|
|||||||
static const uint8_t *mpw_masterKeyForUser_v0(const char *fullName, const char *masterPassword) {
|
static const uint8_t *mpw_masterKeyForUser_v0(const char *fullName, const char *masterPassword) {
|
||||||
|
|
||||||
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword );
|
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword );
|
||||||
trc( "fullName: %s\n", fullName );
|
trc( "algorithm: v%d\n", 0 );
|
||||||
|
trc( "fullName: %s (%zu)\n", fullName, mpw_charlen( fullName ) );
|
||||||
trc( "masterPassword: %s\n", masterPassword );
|
trc( "masterPassword: %s\n", masterPassword );
|
||||||
trc( "key scope: %s\n", mpKeyScope );
|
trc( "key scope: %s\n", mpKeyScope );
|
||||||
|
|
||||||
@@ -71,11 +73,16 @@ static const char *mpw_passwordForSite_v0(const uint8_t *masterKey, const char *
|
|||||||
const MPSiteVariant siteVariant, const char *siteContext) {
|
const MPSiteVariant siteVariant, const char *siteContext) {
|
||||||
|
|
||||||
const char *siteScope = mpw_scopeForVariant( siteVariant );
|
const char *siteScope = mpw_scopeForVariant( siteVariant );
|
||||||
|
trc( "algorithm: v%d\n", 0 );
|
||||||
trc( "siteName: %s\n", siteName );
|
trc( "siteName: %s\n", siteName );
|
||||||
trc( "siteCounter: %d\n", siteCounter );
|
trc( "siteCounter: %d\n", siteCounter );
|
||||||
trc( "siteVariant: %d\n", siteVariant );
|
trc( "siteVariant: %d\n", siteVariant );
|
||||||
trc( "siteType: %d\n", siteType );
|
trc( "siteType: %d\n", siteType );
|
||||||
trc( "site scope: %s, context: %s\n", siteScope, siteContext == NULL? "<empty>": siteContext );
|
trc( "site scope: %s, context: %s\n", siteScope, siteContext? "<empty>": siteContext );
|
||||||
|
trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)\n",
|
||||||
|
siteScope, mpw_hex_l( htonl( strlen( siteName ) ) ), siteName,
|
||||||
|
mpw_hex_l( htonl( siteCounter ) ),
|
||||||
|
mpw_hex_l( htonl( siteContext? strlen( siteContext ): 0 ) ), siteContext? "(null)": siteContext );
|
||||||
|
|
||||||
// Calculate the site seed.
|
// Calculate the site seed.
|
||||||
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
|
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include "mpw-types.h"
|
||||||
#include "mpw-util.h"
|
#include "mpw-util.h"
|
||||||
|
|
||||||
#define MP_N 32768
|
#define MP_N 32768
|
||||||
@@ -21,7 +22,8 @@
|
|||||||
static const uint8_t *mpw_masterKeyForUser_v1(const char *fullName, const char *masterPassword) {
|
static const uint8_t *mpw_masterKeyForUser_v1(const char *fullName, const char *masterPassword) {
|
||||||
|
|
||||||
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword );
|
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword );
|
||||||
trc( "fullName: %s\n", fullName );
|
trc( "algorithm: v%d\n", 1 );
|
||||||
|
trc( "fullName: %s (%zu)\n", fullName, mpw_charlen( fullName ) );
|
||||||
trc( "masterPassword: %s\n", masterPassword );
|
trc( "masterPassword: %s\n", masterPassword );
|
||||||
trc( "key scope: %s\n", mpKeyScope );
|
trc( "key scope: %s\n", mpKeyScope );
|
||||||
|
|
||||||
@@ -55,11 +57,16 @@ static const char *mpw_passwordForSite_v1(const uint8_t *masterKey, const char *
|
|||||||
const MPSiteVariant siteVariant, const char *siteContext) {
|
const MPSiteVariant siteVariant, const char *siteContext) {
|
||||||
|
|
||||||
const char *siteScope = mpw_scopeForVariant( siteVariant );
|
const char *siteScope = mpw_scopeForVariant( siteVariant );
|
||||||
|
trc( "algorithm: v%d\n", 1 );
|
||||||
trc( "siteName: %s\n", siteName );
|
trc( "siteName: %s\n", siteName );
|
||||||
trc( "siteCounter: %d\n", siteCounter );
|
trc( "siteCounter: %d\n", siteCounter );
|
||||||
trc( "siteVariant: %d\n", siteVariant );
|
trc( "siteVariant: %d\n", siteVariant );
|
||||||
trc( "siteType: %d\n", siteType );
|
trc( "siteType: %d\n", siteType );
|
||||||
trc( "site scope: %s, context: %s\n", siteScope, siteContext == NULL? "<empty>": siteContext );
|
trc( "site scope: %s, context: %s\n", siteScope, siteContext? "<empty>": siteContext );
|
||||||
|
trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)\n",
|
||||||
|
siteScope, mpw_hex_l( htonl( strlen( siteName ) ) ), siteName,
|
||||||
|
mpw_hex_l( htonl( siteCounter ) ),
|
||||||
|
mpw_hex_l( htonl( siteContext? strlen( siteContext ): 0 ) ), siteContext? "(null)": siteContext );
|
||||||
|
|
||||||
// Calculate the site seed.
|
// Calculate the site seed.
|
||||||
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
|
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include "mpw-types.h"
|
||||||
#include "mpw-util.h"
|
#include "mpw-util.h"
|
||||||
|
|
||||||
#define MP_N 32768
|
#define MP_N 32768
|
||||||
@@ -21,7 +22,8 @@
|
|||||||
static const uint8_t *mpw_masterKeyForUser_v2(const char *fullName, const char *masterPassword) {
|
static const uint8_t *mpw_masterKeyForUser_v2(const char *fullName, const char *masterPassword) {
|
||||||
|
|
||||||
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword );
|
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword );
|
||||||
trc( "fullName: %s\n", fullName );
|
trc( "algorithm: v%d\n", 2 );
|
||||||
|
trc( "fullName: %s (%zu)\n", fullName, mpw_charlen( fullName ) );
|
||||||
trc( "masterPassword: %s\n", masterPassword );
|
trc( "masterPassword: %s\n", masterPassword );
|
||||||
trc( "key scope: %s\n", mpKeyScope );
|
trc( "key scope: %s\n", mpKeyScope );
|
||||||
|
|
||||||
@@ -55,11 +57,16 @@ static const char *mpw_passwordForSite_v2(const uint8_t *masterKey, const char *
|
|||||||
const MPSiteVariant siteVariant, const char *siteContext) {
|
const MPSiteVariant siteVariant, const char *siteContext) {
|
||||||
|
|
||||||
const char *siteScope = mpw_scopeForVariant( siteVariant );
|
const char *siteScope = mpw_scopeForVariant( siteVariant );
|
||||||
|
trc( "algorithm: v%d\n", 2 );
|
||||||
trc( "siteName: %s\n", siteName );
|
trc( "siteName: %s\n", siteName );
|
||||||
trc( "siteCounter: %d\n", siteCounter );
|
trc( "siteCounter: %d\n", siteCounter );
|
||||||
trc( "siteVariant: %d\n", siteVariant );
|
trc( "siteVariant: %d\n", siteVariant );
|
||||||
trc( "siteType: %d\n", siteType );
|
trc( "siteType: %d\n", siteType );
|
||||||
trc( "site scope: %s, context: %s\n", siteScope, siteContext == NULL? "<empty>": siteContext );
|
trc( "site scope: %s, context: %s\n", siteScope, siteContext? "<empty>": siteContext );
|
||||||
|
trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)\n",
|
||||||
|
siteScope, mpw_hex_l( htonl( strlen( siteName ) ) ), siteName,
|
||||||
|
mpw_hex_l( htonl( siteCounter ) ),
|
||||||
|
mpw_hex_l( htonl( siteContext? strlen( siteContext ): 0 ) ), siteContext? "(null)": siteContext );
|
||||||
|
|
||||||
// Calculate the site seed.
|
// Calculate the site seed.
|
||||||
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
|
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include "mpw-types.h"
|
||||||
#include "mpw-util.h"
|
#include "mpw-util.h"
|
||||||
|
|
||||||
#define MP_N 32768
|
#define MP_N 32768
|
||||||
@@ -21,7 +22,8 @@
|
|||||||
static const uint8_t *mpw_masterKeyForUser_v3(const char *fullName, const char *masterPassword) {
|
static const uint8_t *mpw_masterKeyForUser_v3(const char *fullName, const char *masterPassword) {
|
||||||
|
|
||||||
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword );
|
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword );
|
||||||
trc( "fullName: %s\n", fullName );
|
trc( "algorithm: v%d\n", 3 );
|
||||||
|
trc( "fullName: %s (%zu)\n", fullName, strlen( fullName ) );
|
||||||
trc( "masterPassword: %s\n", masterPassword );
|
trc( "masterPassword: %s\n", masterPassword );
|
||||||
trc( "key scope: %s\n", mpKeyScope );
|
trc( "key scope: %s\n", mpKeyScope );
|
||||||
|
|
||||||
@@ -55,11 +57,16 @@ static const char *mpw_passwordForSite_v3(const uint8_t *masterKey, const char *
|
|||||||
const MPSiteVariant siteVariant, const char *siteContext) {
|
const MPSiteVariant siteVariant, const char *siteContext) {
|
||||||
|
|
||||||
const char *siteScope = mpw_scopeForVariant( siteVariant );
|
const char *siteScope = mpw_scopeForVariant( siteVariant );
|
||||||
|
trc( "algorithm: v%d\n", 3 );
|
||||||
trc( "siteName: %s\n", siteName );
|
trc( "siteName: %s\n", siteName );
|
||||||
trc( "siteCounter: %d\n", siteCounter );
|
trc( "siteCounter: %d\n", siteCounter );
|
||||||
trc( "siteVariant: %d\n", siteVariant );
|
trc( "siteVariant: %d\n", siteVariant );
|
||||||
trc( "siteType: %d\n", siteType );
|
trc( "siteType: %d\n", siteType );
|
||||||
trc( "site scope: %s, context: %s\n", siteScope, siteContext == NULL? "<empty>": siteContext );
|
trc( "site scope: %s, context: %s\n", siteScope, siteContext? "<empty>": siteContext );
|
||||||
|
trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)\n",
|
||||||
|
siteScope, mpw_hex_l( htonl( strlen( siteName ) ) ), siteName,
|
||||||
|
mpw_hex_l( htonl( siteCounter ) ),
|
||||||
|
mpw_hex_l( htonl( siteContext? strlen( siteContext ): 0 ) ), siteContext? "(null)": siteContext );
|
||||||
|
|
||||||
// Calculate the site seed.
|
// Calculate the site seed.
|
||||||
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
|
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
static void usage() {
|
static void usage() {
|
||||||
|
|
||||||
fprintf( stderr, "Usage: mpw [-u name] [-t type] [-c counter] site\n\n" );
|
fprintf( stderr, "Usage: mpw [-u name] [-t type] [-c counter] [-V version] [-v variant] [-C context] [-h] site\n\n" );
|
||||||
fprintf( stderr, " -u name Specify the full name of the user.\n"
|
fprintf( stderr, " -u name Specify the full name of the user.\n"
|
||||||
" Defaults to %s in env.\n\n", MP_env_fullname );
|
" Defaults to %s in env.\n\n", MP_env_fullname );
|
||||||
fprintf( stderr, " -t type Specify the password's template.\n"
|
fprintf( stderr, " -t type Specify the password's template.\n"
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ int main(int argc, char *const argv[]) {
|
|||||||
|
|
||||||
// Read in the test case.
|
// Read in the test case.
|
||||||
xmlChar *id = mpw_xmlTestCaseString( testCase, "id" );
|
xmlChar *id = mpw_xmlTestCaseString( testCase, "id" );
|
||||||
|
uint32_t algorithm = mpw_xmlTestCaseInteger( testCase, "algorithm" );
|
||||||
xmlChar *fullName = mpw_xmlTestCaseString( testCase, "fullName" );
|
xmlChar *fullName = mpw_xmlTestCaseString( testCase, "fullName" );
|
||||||
xmlChar *masterPassword = mpw_xmlTestCaseString( testCase, "masterPassword" );
|
xmlChar *masterPassword = mpw_xmlTestCaseString( testCase, "masterPassword" );
|
||||||
xmlChar *keyID = mpw_xmlTestCaseString( testCase, "keyID" );
|
xmlChar *keyID = mpw_xmlTestCaseString( testCase, "keyID" );
|
||||||
@@ -36,16 +37,20 @@ int main(int argc, char *const argv[]) {
|
|||||||
|
|
||||||
// Run the test case.
|
// Run the test case.
|
||||||
fprintf( stdout, "test case %s... ", id );
|
fprintf( stdout, "test case %s... ", id );
|
||||||
|
if (!xmlStrlen( result )) {
|
||||||
|
fprintf( stdout, "abstract.\n" );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// 1. calculate the master key.
|
// 1. calculate the master key.
|
||||||
const uint8_t *masterKey = mpw_masterKeyForUser(
|
const uint8_t *masterKey = mpw_masterKeyForUser(
|
||||||
(char *)fullName, (char *)masterPassword, MPAlgorithmVersionCurrent );
|
(char *)fullName, (char *)masterPassword, algorithm );
|
||||||
if (!masterKey)
|
if (!masterKey)
|
||||||
ftl( "Couldn't derive master key." );
|
ftl( "Couldn't derive master key." );
|
||||||
|
|
||||||
// 2. calculate the site password.
|
// 2. calculate the site password.
|
||||||
const char *sitePassword = mpw_passwordForSite(
|
const char *sitePassword = mpw_passwordForSite(
|
||||||
masterKey, (char *)siteName, siteType, siteCounter, siteVariant, (char *)siteContext, MPAlgorithmVersionCurrent );
|
masterKey, (char *)siteName, siteType, siteCounter, siteVariant, (char *)siteContext, algorithm );
|
||||||
mpw_free( masterKey, MP_dkLen );
|
mpw_free( masterKey, MP_dkLen );
|
||||||
if (!sitePassword)
|
if (!sitePassword)
|
||||||
ftl( "Couldn't derive site password." );
|
ftl( "Couldn't derive site password." );
|
||||||
@@ -56,7 +61,7 @@ int main(int argc, char *const argv[]) {
|
|||||||
|
|
||||||
else {
|
else {
|
||||||
++failedTests;
|
++failedTests;
|
||||||
fprintf( stdout, "FAILED! (result %s != expected %s)\n", result, sitePassword );
|
fprintf( stdout, "FAILED! (got %s != expected %s)\n", sitePassword, result );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free test case.
|
// Free test case.
|
||||||
|
|||||||
@@ -21,15 +21,17 @@
|
|||||||
|
|
||||||
const MPSiteType mpw_typeWithName(const char *typeName) {
|
const MPSiteType mpw_typeWithName(const char *typeName) {
|
||||||
|
|
||||||
|
// Lower-case and trim optionally leading "Generated" string from typeName to standardize it.
|
||||||
|
size_t stdTypeNameOffset = 0;
|
||||||
size_t stdTypeNameSize = strlen( typeName );
|
size_t stdTypeNameSize = strlen( typeName );
|
||||||
char stdTypeName[strlen( typeName )];
|
if (strstr(typeName, "Generated" ) == typeName)
|
||||||
if (stdTypeNameSize > strlen( "generated" ))
|
stdTypeNameSize -= (stdTypeNameOffset = strlen( "Generated" ));
|
||||||
strcpy( stdTypeName, typeName + strlen( "generated" ) );
|
char stdTypeName[stdTypeNameSize + 1];
|
||||||
else
|
for (size_t c = 0; c < stdTypeNameSize; ++c)
|
||||||
strcpy( stdTypeName, typeName );
|
stdTypeName[c] = (char)tolower( typeName[c + stdTypeNameOffset] );
|
||||||
for (char *tN = stdTypeName; *tN; ++tN)
|
stdTypeName[stdTypeNameSize] = '\0';
|
||||||
*tN = (char)tolower( *tN );
|
|
||||||
|
|
||||||
|
// Find what site type is represented by the type name.
|
||||||
if (0 == strcmp( stdTypeName, "x" ) || 0 == strcmp( stdTypeName, "max" ) || 0 == strcmp( stdTypeName, "maximum" ))
|
if (0 == strcmp( stdTypeName, "x" ) || 0 == strcmp( stdTypeName, "max" ) || 0 == strcmp( stdTypeName, "maximum" ))
|
||||||
return MPSiteTypeGeneratedMaximum;
|
return MPSiteTypeGeneratedMaximum;
|
||||||
if (0 == strcmp( stdTypeName, "l" ) || 0 == strcmp( stdTypeName, "long" ))
|
if (0 == strcmp( stdTypeName, "l" ) || 0 == strcmp( stdTypeName, "long" ))
|
||||||
@@ -47,11 +49,10 @@ const MPSiteType mpw_typeWithName(const char *typeName) {
|
|||||||
if (0 == strcmp( stdTypeName, "p" ) || 0 == strcmp( stdTypeName, "phrase" ))
|
if (0 == strcmp( stdTypeName, "p" ) || 0 == strcmp( stdTypeName, "phrase" ))
|
||||||
return MPSiteTypeGeneratedPhrase;
|
return MPSiteTypeGeneratedPhrase;
|
||||||
|
|
||||||
fprintf( stderr, "Not a generated type name: %s", stdTypeName );
|
ftl( "Not a generated type name: %s", stdTypeName );
|
||||||
abort();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const char **mpw_templatesForType(MPSiteType type, size_t *count) {
|
const char **mpw_templatesForType(MPSiteType type, size_t *count) {
|
||||||
|
|
||||||
if (!(type & MPSiteTypeClassGenerated)) {
|
if (!(type & MPSiteTypeClassGenerated)) {
|
||||||
ftl( "Not a generated type: %d", type );
|
ftl( "Not a generated type: %d", type );
|
||||||
@@ -61,42 +62,42 @@ inline const char **mpw_templatesForType(MPSiteType type, size_t *count) {
|
|||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case MPSiteTypeGeneratedMaximum: {
|
case MPSiteTypeGeneratedMaximum: {
|
||||||
*count = 2;
|
return alloc_array( *count, const char *,
|
||||||
return (const char *[]){ "anoxxxxxxxxxxxxxxxxx", "axxxxxxxxxxxxxxxxxno" };
|
"anoxxxxxxxxxxxxxxxxx", "axxxxxxxxxxxxxxxxxno" );
|
||||||
}
|
}
|
||||||
case MPSiteTypeGeneratedLong: {
|
case MPSiteTypeGeneratedLong: {
|
||||||
*count = 21;
|
return alloc_array( *count, const char *,
|
||||||
return (const char *[]){ "CvcvnoCvcvCvcv", "CvcvCvcvnoCvcv", "CvcvCvcvCvcvno",
|
"CvcvnoCvcvCvcv", "CvcvCvcvnoCvcv", "CvcvCvcvCvcvno",
|
||||||
"CvccnoCvcvCvcv", "CvccCvcvnoCvcv", "CvccCvcvCvcvno",
|
"CvccnoCvcvCvcv", "CvccCvcvnoCvcv", "CvccCvcvCvcvno",
|
||||||
"CvcvnoCvccCvcv", "CvcvCvccnoCvcv", "CvcvCvccCvcvno",
|
"CvcvnoCvccCvcv", "CvcvCvccnoCvcv", "CvcvCvccCvcvno",
|
||||||
"CvcvnoCvcvCvcc", "CvcvCvcvnoCvcc", "CvcvCvcvCvccno",
|
"CvcvnoCvcvCvcc", "CvcvCvcvnoCvcc", "CvcvCvcvCvccno",
|
||||||
"CvccnoCvccCvcv", "CvccCvccnoCvcv", "CvccCvccCvcvno",
|
"CvccnoCvccCvcv", "CvccCvccnoCvcv", "CvccCvccCvcvno",
|
||||||
"CvcvnoCvccCvcc", "CvcvCvccnoCvcc", "CvcvCvccCvccno",
|
"CvcvnoCvccCvcc", "CvcvCvccnoCvcc", "CvcvCvccCvccno",
|
||||||
"CvccnoCvcvCvcc", "CvccCvcvnoCvcc", "CvccCvcvCvccno" };
|
"CvccnoCvcvCvcc", "CvccCvcvnoCvcc", "CvccCvcvCvccno" );
|
||||||
}
|
}
|
||||||
case MPSiteTypeGeneratedMedium: {
|
case MPSiteTypeGeneratedMedium: {
|
||||||
*count = 2;
|
return alloc_array( *count, const char *,
|
||||||
return (const char *[]){ "CvcnoCvc", "CvcCvcno" };
|
"CvcnoCvc", "CvcCvcno" );
|
||||||
}
|
}
|
||||||
case MPSiteTypeGeneratedBasic: {
|
case MPSiteTypeGeneratedBasic: {
|
||||||
*count = 3;
|
return alloc_array( *count, const char *,
|
||||||
return (const char *[]){ "aaanaaan", "aannaaan", "aaannaaa" };
|
"aaanaaan", "aannaaan", "aaannaaa" );
|
||||||
}
|
}
|
||||||
case MPSiteTypeGeneratedShort: {
|
case MPSiteTypeGeneratedShort: {
|
||||||
*count = 1;
|
return alloc_array( *count, const char *,
|
||||||
return (const char *[]){"Cvcn"};
|
"Cvcn" );
|
||||||
}
|
}
|
||||||
case MPSiteTypeGeneratedPIN: {
|
case MPSiteTypeGeneratedPIN: {
|
||||||
*count = 1;
|
return alloc_array( *count, const char *,
|
||||||
return (const char *[]){ "nnnn" };
|
"nnnn" );
|
||||||
}
|
}
|
||||||
case MPSiteTypeGeneratedName: {
|
case MPSiteTypeGeneratedName: {
|
||||||
*count = 1;
|
return alloc_array( *count, const char *,
|
||||||
return (const char *[]) {"cvccvcvcv"};
|
"cvccvcvcv" );
|
||||||
}
|
}
|
||||||
case MPSiteTypeGeneratedPhrase: {
|
case MPSiteTypeGeneratedPhrase: {
|
||||||
*count = 3;
|
return alloc_array( *count, const char *,
|
||||||
return (const char *[]){ "cvcc cvc cvccvcv cvc", "cvc cvccvcvcv cvcv", "cv cvccv cvc cvcvccv" };
|
"cvcc cvc cvccvcv cvc", "cvc cvccvcvcv cvcv", "cv cvccv cvc cvcvccv" );
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
ftl( "Unknown generated type: %d", type );
|
ftl( "Unknown generated type: %d", type );
|
||||||
@@ -113,15 +114,19 @@ const char *mpw_templateForType(MPSiteType type, uint8_t seedByte) {
|
|||||||
if (!count)
|
if (!count)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
return templates[seedByte % count];
|
char const *template = templates[seedByte % count];
|
||||||
|
free( templates );
|
||||||
|
return template;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MPSiteVariant mpw_variantWithName(const char *variantName) {
|
const MPSiteVariant mpw_variantWithName(const char *variantName) {
|
||||||
|
|
||||||
char stdVariantName[strlen( variantName )];
|
// Lower-case and trim optionally leading "generated" string from typeName to standardize it.
|
||||||
strcpy( stdVariantName, variantName );
|
size_t stdVariantNameSize = strlen( variantName );
|
||||||
for (char *vN = stdVariantName; *vN; ++vN)
|
char stdVariantName[stdVariantNameSize + 1];
|
||||||
*vN = (char)tolower( *vN );
|
for (size_t c = 0; c < stdVariantNameSize; ++c)
|
||||||
|
stdVariantName[c] = (char)tolower( variantName[c] );
|
||||||
|
stdVariantName[stdVariantNameSize] = '\0';
|
||||||
|
|
||||||
if (0 == strcmp( stdVariantName, "p" ) || 0 == strcmp( stdVariantName, "password" ))
|
if (0 == strcmp( stdVariantName, "p" ) || 0 == strcmp( stdVariantName, "password" ))
|
||||||
return MPSiteVariantPassword;
|
return MPSiteVariantPassword;
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#ifndef _MPW_TYPES_H
|
||||||
|
#define _MPW_TYPES_H
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
@@ -73,9 +75,10 @@ const char *mpw_scopeForVariant(MPSiteVariant variant);
|
|||||||
const MPSiteType mpw_typeWithName(const char *typeName);
|
const MPSiteType mpw_typeWithName(const char *typeName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return An array of internal strings that express the templates to use for the given type.
|
* @return A newly allocated array of internal strings that express the templates to use for the given type.
|
||||||
* The amount of elements in the array is stored in count.
|
* The amount of elements in the array is stored in count.
|
||||||
* If an unsupported type is given, count will be 0 and will return NULL.
|
* If an unsupported type is given, count will be 0 and will return NULL.
|
||||||
|
* The array needs to be free'ed, the strings themselves must not be free'ed or modified.
|
||||||
*/
|
*/
|
||||||
const char **mpw_templatesForType(MPSiteType type, size_t *count);
|
const char **mpw_templatesForType(MPSiteType type, size_t *count);
|
||||||
/**
|
/**
|
||||||
@@ -93,3 +96,4 @@ const char *mpw_charactersInClass(char characterClass);
|
|||||||
*/
|
*/
|
||||||
const char mpw_characterFromClass(char characterClass, uint8_t seedByte);
|
const char mpw_characterFromClass(char characterClass, uint8_t seedByte);
|
||||||
|
|
||||||
|
#endif // _MPW_TYPES_H
|
||||||
|
|||||||
@@ -9,7 +9,12 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <locale.h>
|
|
||||||
|
#ifdef COLOR
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <curses.h>
|
||||||
|
#include <term.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <scrypt/sha256.h>
|
#include <scrypt/sha256.h>
|
||||||
#include <scrypt/crypto_scrypt.h>
|
#include <scrypt/crypto_scrypt.h>
|
||||||
@@ -75,7 +80,7 @@ uint8_t const *mpw_scrypt(const size_t keySize, const char *secret, const uint8_
|
|||||||
|
|
||||||
uint8_t const *mpw_hmac_sha256(const uint8_t *key, const size_t keySize, const uint8_t *salt, const size_t saltSize) {
|
uint8_t const *mpw_hmac_sha256(const uint8_t *key, const size_t keySize, const uint8_t *salt, const size_t saltSize) {
|
||||||
|
|
||||||
uint8_t *const buffer = malloc(32);
|
uint8_t *const buffer = malloc( 32 );
|
||||||
if (!buffer)
|
if (!buffer)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@@ -91,20 +96,35 @@ const char *mpw_idForBuf(const void *buf, size_t length) {
|
|||||||
return mpw_hex( hash, 32 );
|
return mpw_hex( hash, 32 );
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *mpw_hex_buf = NULL;
|
static char **mpw_hex_buf = NULL;
|
||||||
|
static unsigned int mpw_hex_buf_i = 0;
|
||||||
|
|
||||||
const char *mpw_hex(const void *buf, size_t length) {
|
const char *mpw_hex(const void *buf, size_t length) {
|
||||||
|
|
||||||
mpw_hex_buf = realloc( mpw_hex_buf, length * 2 + 1 );
|
// FIXME
|
||||||
for (size_t kH = 0; kH < length; kH++)
|
if (!mpw_hex_buf) {
|
||||||
sprintf( &(mpw_hex_buf[kH * 2]), "%02X", ((const uint8_t *)buf)[kH] );
|
mpw_hex_buf = malloc( 10 * sizeof( char * ) );
|
||||||
|
for (uint8_t i = 0; i < 10; ++i)
|
||||||
|
mpw_hex_buf[i] = NULL;
|
||||||
|
}
|
||||||
|
mpw_hex_buf_i = (mpw_hex_buf_i + 1) % 10;
|
||||||
|
|
||||||
return mpw_hex_buf;
|
mpw_hex_buf[mpw_hex_buf_i] = realloc( mpw_hex_buf[mpw_hex_buf_i], length * 2 + 1 );
|
||||||
|
for (size_t kH = 0; kH < length; kH++)
|
||||||
|
sprintf( &(mpw_hex_buf[mpw_hex_buf_i][kH * 2]), "%02X", ((const uint8_t *)buf)[kH] );
|
||||||
|
|
||||||
|
return mpw_hex_buf[mpw_hex_buf_i];
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *mpw_hex_l(uint32_t number) {
|
||||||
|
|
||||||
|
return mpw_hex( &number, sizeof( number ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef COLOR
|
#ifdef COLOR
|
||||||
static int putvari;
|
static int putvari;
|
||||||
static char *putvarc = NULL;
|
static char *putvarc = NULL;
|
||||||
static bool istermsetup = false;
|
static int istermsetup = 0;
|
||||||
static void initputvar() {
|
static void initputvar() {
|
||||||
if (putvarc)
|
if (putvarc)
|
||||||
free(putvarc);
|
free(putvarc);
|
||||||
@@ -128,7 +148,8 @@ const char *mpw_identicon(const char *fullName, const char *masterPassword) {
|
|||||||
const char *accessory[] = {
|
const char *accessory[] = {
|
||||||
"◈", "◎", "◐", "◑", "◒", "◓", "☀", "☁", "☂", "☃", "☄", "★", "☆", "☎", "☏", "⎈", "⌂", "☘", "☢", "☣",
|
"◈", "◎", "◐", "◑", "◒", "◓", "☀", "☁", "☂", "☃", "☄", "★", "☆", "☎", "☏", "⎈", "⌂", "☘", "☢", "☣",
|
||||||
"☕", "⌚", "⌛", "⏰", "⚡", "⛄", "⛅", "☔", "♔", "♕", "♖", "♗", "♘", "♙", "♚", "♛", "♜", "♝", "♞", "♟",
|
"☕", "⌚", "⌛", "⏰", "⚡", "⛄", "⛅", "☔", "♔", "♕", "♖", "♗", "♘", "♙", "♚", "♛", "♜", "♝", "♞", "♟",
|
||||||
"♨", "♩", "♪", "♫", "⚐", "⚑", "⚔", "⚖", "⚙", "⚠", "⌘", "⏎", "✄", "✆", "✈", "✉", "✌" };
|
"♨", "♩", "♪", "♫", "⚐", "⚑", "⚔", "⚖", "⚙", "⚠", "⌘", "⏎", "✄", "✆", "✈", "✉", "✌"
|
||||||
|
};
|
||||||
|
|
||||||
uint8_t identiconSeed[32];
|
uint8_t identiconSeed[32];
|
||||||
HMAC_SHA256_Buf( masterPassword, strlen( masterPassword ), fullName, strlen( fullName ), identiconSeed );
|
HMAC_SHA256_Buf( masterPassword, strlen( masterPassword ), fullName, strlen( fullName ), identiconSeed );
|
||||||
@@ -165,8 +186,33 @@ const char *mpw_identicon(const char *fullName, const char *masterPassword) {
|
|||||||
return identicon;
|
return identicon;
|
||||||
}
|
}
|
||||||
|
|
||||||
const size_t mpw_charlen(const char *string) {
|
/**
|
||||||
|
* @return the amount of bytes used by UTF-8 to encode a single character that starts with the given byte.
|
||||||
|
*/
|
||||||
|
static int mpw_charByteSize(unsigned char utf8Byte) {
|
||||||
|
|
||||||
setlocale( LC_ALL, "en_US.UTF-8" );
|
if (!utf8Byte)
|
||||||
return mbstowcs( NULL, string, strlen( string ) );
|
return 0;
|
||||||
|
if ((utf8Byte & 0x80) == 0)
|
||||||
|
return 1;
|
||||||
|
if ((utf8Byte & 0xC0) != 0xC0)
|
||||||
|
return 0;
|
||||||
|
if ((utf8Byte & 0xE0) == 0xC0)
|
||||||
|
return 2;
|
||||||
|
if ((utf8Byte & 0xF0) == 0xE0)
|
||||||
|
return 3;
|
||||||
|
if ((utf8Byte & 0xF8) == 0xF0)
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t mpw_charlen(const char *utf8String) {
|
||||||
|
|
||||||
|
size_t charlen = 0;
|
||||||
|
char *remainingString = (char *)utf8String;
|
||||||
|
for (int charByteSize; (charByteSize = mpw_charByteSize( (unsigned char)*remainingString )); remainingString += charByteSize)
|
||||||
|
++charlen;
|
||||||
|
|
||||||
|
return charlen;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,14 @@
|
|||||||
|
|
||||||
//// Buffers and memory.
|
//// Buffers and memory.
|
||||||
|
|
||||||
|
#define alloc_array(_count, _type, ...) ({ \
|
||||||
|
_type stackElements[] = { __VA_ARGS__ }; \
|
||||||
|
_count = sizeof( stackElements ) / sizeof( _type ); \
|
||||||
|
_type *allocElements = malloc( sizeof( stackElements ) ); \
|
||||||
|
memcpy( allocElements, stackElements, sizeof( stackElements ) ); \
|
||||||
|
allocElements; \
|
||||||
|
})
|
||||||
|
|
||||||
/** Push a buffer onto a buffer. reallocs the given buffer and appends the given buffer. */
|
/** Push a buffer onto a buffer. reallocs the given buffer and appends the given buffer. */
|
||||||
void mpw_pushBuf(
|
void mpw_pushBuf(
|
||||||
uint8_t **const buffer, size_t *const bufferSize, const void *pushBuffer, const size_t pushSize);
|
uint8_t **const buffer, size_t *const bufferSize, const void *pushBuffer, const size_t pushSize);
|
||||||
@@ -58,6 +66,7 @@ uint8_t const *mpw_hmac_sha256(
|
|||||||
/** Encode a buffer as a string of hexadecimal characters.
|
/** Encode a buffer as a string of hexadecimal characters.
|
||||||
* @return A C-string in a reused buffer, do not free or store it. */
|
* @return A C-string in a reused buffer, do not free or store it. */
|
||||||
const char *mpw_hex(const void *buf, size_t length);
|
const char *mpw_hex(const void *buf, size_t length);
|
||||||
|
const char *mpw_hex_l(uint32_t number);
|
||||||
/** Encode a fingerprint for a buffer.
|
/** Encode a fingerprint for a buffer.
|
||||||
* @return A C-string in a reused buffer, do not free or store it. */
|
* @return A C-string in a reused buffer, do not free or store it. */
|
||||||
const char *mpw_idForBuf(const void *buf, size_t length);
|
const char *mpw_idForBuf(const void *buf, size_t length);
|
||||||
@@ -67,4 +76,5 @@ const char *mpw_identicon(const char *fullName, const char *masterPassword);
|
|||||||
|
|
||||||
//// String utilities.
|
//// String utilities.
|
||||||
|
|
||||||
const size_t mpw_charlen(const char *string);
|
/** @return The amount of display characters in the given UTF-8 string. */
|
||||||
|
const size_t mpw_charlen(const char *utf8String);
|
||||||
|
|||||||
69
MasterPassword/C/mpw.completion.bash
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
source bashcomplib
|
||||||
|
|
||||||
|
# completing the 'mpw' command.
|
||||||
|
_comp_mpw() {
|
||||||
|
local optarg= cword=${COMP_WORDS[COMP_CWORD]} pcword=
|
||||||
|
(( COMP_CWORD )) && pcword=${COMP_WORDS[COMP_CWORD - 1]}
|
||||||
|
|
||||||
|
case $pcword in
|
||||||
|
-u) # complete full names.
|
||||||
|
COMPREPLY=( ~/.mpw.d/*.mpsites )
|
||||||
|
[[ -e $COMPREPLY ]] || COMPREPLY=()
|
||||||
|
COMPREPLY=( "${COMPREPLY[@]##*/}" ) COMPREPLY=( "${COMPREPLY[@]%.mpsites}" )
|
||||||
|
;;
|
||||||
|
-t) # complete types.
|
||||||
|
COMPREPLY=( maximum long medium basic short pin name phrase )
|
||||||
|
;;
|
||||||
|
-c) # complete counter.
|
||||||
|
COMPREPLY=( 1 )
|
||||||
|
;;
|
||||||
|
-V) # complete versions.
|
||||||
|
COMPREPLY=( 0 1 2 3 )
|
||||||
|
;;
|
||||||
|
-v) # complete variants.
|
||||||
|
COMPREPLY=( password login answer )
|
||||||
|
;;
|
||||||
|
-C) # complete context.
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# previous word is not an option we can complete, complete site name (or option if leading -)
|
||||||
|
if [[ $cword = -* ]]; then
|
||||||
|
COMPREPLY=( -u -t -c -V -v -C )
|
||||||
|
else
|
||||||
|
local w fullName=$MP_FULLNAME
|
||||||
|
for (( w = 0; w < ${#COMP_WORDS[@]}; ++w )); do
|
||||||
|
[[ ${COMP_WORDS[w]} = -u ]] && fullName=$(xargs <<< "${COMP_WORDS[w + 1]}") && break
|
||||||
|
done
|
||||||
|
if [[ -e ~/.mpw.d/"$fullName.mpsites" ]]; then
|
||||||
|
IFS=$'\n' read -d '' -ra COMPREPLY < <(awk -F$'\t' '!/^ *#/{sub(/^ */, "", $2); print $2}' ~/.mpw.d/"$fullName.mpsites")
|
||||||
|
printf -v _comp_title 'Sites for %s' "$fullName"
|
||||||
|
else
|
||||||
|
# Default list from the Alexa Top 500
|
||||||
|
COMPREPLY=(
|
||||||
|
163.com 360.cn 9gag.com adobe.com alibaba.com aliexpress.com amazon.com
|
||||||
|
apple.com archive.org ask.com baidu.com battle.net booking.com buzzfeed.com
|
||||||
|
chase.com cnn.com comcast.net craigslist.org dailymotion.com dell.com
|
||||||
|
deviantart.com diply.com disqus.com dropbox.com ebay.com engadget.com
|
||||||
|
espn.go.com evernote.com facebook.com fedex.com feedly.com flickr.com
|
||||||
|
flipkart.com github.com gizmodo.com go.com goodreads.com google.com
|
||||||
|
huffingtonpost.com hulu.com ign.com ikea.com imdb.com imgur.com
|
||||||
|
indiatimes.com instagram.com jd.com kickass.to kickstarter.com linkedin.com
|
||||||
|
live.com livedoor.com mail.ru mozilla.org naver.com netflix.com newegg.com
|
||||||
|
nicovideo.jp nytimes.com pandora.com paypal.com pinterest.com pornhub.com
|
||||||
|
qq.com rakuten.co.jp reddit.com redtube.com shutterstock.com skype.com
|
||||||
|
soso.com spiegel.de spotify.com stackexchange.com steampowered.com
|
||||||
|
stumbleupon.com taobao.com target.com thepiratebay.se tmall.com
|
||||||
|
torrentz.eu tripadvisor.com tube8.com tubecup.com tudou.com tumblr.com
|
||||||
|
twitter.com uol.com.br vimeo.com vk.com walmart.com weibo.com whatsapp.com
|
||||||
|
wikia.com wikipedia.org wired.com wordpress.com xhamster.com xinhuanet.com
|
||||||
|
xvideos.com yahoo.com yandex.ru yelp.com youku.com youporn.com ziddu.com
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
fi ;;
|
||||||
|
esac
|
||||||
|
_comp_finish_completions
|
||||||
|
}
|
||||||
|
|
||||||
|
#complete -F _show_args mpw
|
||||||
|
complete -o nospace -F _comp_mpw mpw
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.lyndir.masterpassword</groupId>
|
<groupId>com.lyndir.masterpassword</groupId>
|
||||||
<artifactId>masterpassword</artifactId>
|
<artifactId>masterpassword</artifactId>
|
||||||
<version>2.2</version>
|
<version>2.3</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<name>Master Password Algorithm Implementation</name>
|
<name>Master Password Algorithm Implementation</name>
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
package com.lyndir.masterpassword;
|
||||||
|
|
||||||
|
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.lyndir.lhunath.opal.system.MessageAuthenticationDigests;
|
||||||
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.nio.*;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lhunath, 15-03-29
|
||||||
|
*/
|
||||||
|
public class MPIdenticon {
|
||||||
|
|
||||||
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
|
private static final Logger logger = Logger.get( MPIdenticon.class );
|
||||||
|
|
||||||
|
private static final Charset charset = StandardCharsets.UTF_8;
|
||||||
|
private static final Color[] colors = new Color[]{
|
||||||
|
Color.RED, Color.GREEN, Color.YELLOW, Color.BLUE, Color.MAGENTA, Color.CYAN, Color.MONO };
|
||||||
|
private static final char[] leftArm = new char[]{ '╔', '╚', '╰', '═' };
|
||||||
|
private static final char[] rightArm = new char[]{ '╗', '╝', '╯', '═' };
|
||||||
|
private static final char[] body = new char[]{ '█', '░', '▒', '▓', '☺', '☻' };
|
||||||
|
private static final char[] accessory = new char[]{
|
||||||
|
'◈', '◎', '◐', '◑', '◒', '◓', '☀', '☁', '☂', '☃', '☄', '★', '☆', '☎', '☏', '⎈', '⌂', '☘', '☢', '☣', '☕', '⌚', '⌛', '⏰', '⚡',
|
||||||
|
'⛄', '⛅', '☔', '♔', '♕', '♖', '♗', '♘', '♙', '♚', '♛', '♜', '♝', '♞', '♟', '♨', '♩', '♪', '♫', '⚐', '⚑', '⚔', '⚖', '⚙', '⚠',
|
||||||
|
'⌘', '⏎', '✄', '✆', '✈', '✉', '✌' };
|
||||||
|
|
||||||
|
private final String fullName;
|
||||||
|
private final Color color;
|
||||||
|
private final String text;
|
||||||
|
|
||||||
|
public MPIdenticon(String fullName, String masterPassword) {
|
||||||
|
this( fullName, masterPassword.toCharArray() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public MPIdenticon(String fullName, char[] masterPassword) {
|
||||||
|
this.fullName = fullName;
|
||||||
|
|
||||||
|
byte[] masterPasswordBytes = charset.encode( CharBuffer.wrap( masterPassword ) ).array();
|
||||||
|
ByteBuffer identiconSeedBytes = ByteBuffer.wrap(
|
||||||
|
MessageAuthenticationDigests.HmacSHA256.of( masterPasswordBytes, fullName.getBytes( charset ) ) );
|
||||||
|
Arrays.fill( masterPasswordBytes, (byte) 0 );
|
||||||
|
|
||||||
|
IntBuffer identiconSeedBuffer = IntBuffer.allocate( identiconSeedBytes.capacity() );
|
||||||
|
while (identiconSeedBytes.hasRemaining())
|
||||||
|
identiconSeedBuffer.put( identiconSeedBytes.get() & 0xFF );
|
||||||
|
int[] identiconSeed = identiconSeedBuffer.array();
|
||||||
|
|
||||||
|
color = colors[identiconSeed[4] % colors.length];
|
||||||
|
text = strf( "%c%c%c%c", leftArm[identiconSeed[0] % leftArm.length], body[identiconSeed[1] % body.length],
|
||||||
|
rightArm[identiconSeed[2] % rightArm.length], accessory[identiconSeed[3] % accessory.length] );
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFullName() {
|
||||||
|
return fullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color getColor() {
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum BackgroundMode {
|
||||||
|
DARK, LIGHT
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public enum Color {
|
||||||
|
RED( "#dc322f", "#dc322f" ),
|
||||||
|
GREEN( "#859900", "#859900" ),
|
||||||
|
YELLOW( "#b58900", "#b58900" ),
|
||||||
|
BLUE( "#268bd2", "#268bd2" ),
|
||||||
|
MAGENTA( "#d33682", "#d33682" ),
|
||||||
|
CYAN( "#2aa198", "#2aa198" ),
|
||||||
|
MONO( "#93a1a1", "#586e75" );
|
||||||
|
|
||||||
|
private final String rgbDark;
|
||||||
|
private final String rgbLight;
|
||||||
|
|
||||||
|
Color(final String rgbDark, final String rgbLight) {
|
||||||
|
this.rgbDark = rgbDark;
|
||||||
|
this.rgbLight = rgbLight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public java.awt.Color getAWTColor(BackgroundMode backgroundMode) {
|
||||||
|
switch (backgroundMode) {
|
||||||
|
case DARK:
|
||||||
|
return new java.awt.Color( Integer.decode( rgbDark ) );
|
||||||
|
case LIGHT:
|
||||||
|
return new java.awt.Color( Integer.decode( rgbLight ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new UnsupportedOperationException( "Unsupported background mode: " + backgroundMode );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import com.lyndir.lhunath.opal.system.logging.Logger;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import org.jetbrains.annotations.Contract;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -147,6 +148,7 @@ public enum MPSiteType {
|
|||||||
*
|
*
|
||||||
* @return The type registered with the given name.
|
* @return The type registered with the given name.
|
||||||
*/
|
*/
|
||||||
|
@Contract("!null -> !null, null -> null")
|
||||||
public static MPSiteType forName(@Nullable final String name) {
|
public static MPSiteType forName(@Nullable final String name) {
|
||||||
|
|
||||||
if (name == null)
|
if (name == null)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableList;
|
|||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import org.jetbrains.annotations.Contract;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,6 +67,7 @@ public enum MPSiteVariant {
|
|||||||
*
|
*
|
||||||
* @return The variant registered with the given name.
|
* @return The variant registered with the given name.
|
||||||
*/
|
*/
|
||||||
|
@Contract("!null -> !null, null -> null")
|
||||||
public static MPSiteVariant forName(@Nullable final String name) {
|
public static MPSiteVariant forName(@Nullable final String name) {
|
||||||
|
|
||||||
if (name == null)
|
if (name == null)
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ public abstract class MasterKey {
|
|||||||
@Nonnull
|
@Nonnull
|
||||||
protected byte[] getKey() {
|
protected byte[] getKey() {
|
||||||
|
|
||||||
|
Preconditions.checkState( isValid() );
|
||||||
return Preconditions.checkNotNull( masterKey );
|
return Preconditions.checkNotNull( masterKey );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,8 +58,11 @@ public class MasterKeyV0 extends MasterKey {
|
|||||||
logger.trc( "key scope: %s", mpKeyScope );
|
logger.trc( "key scope: %s", mpKeyScope );
|
||||||
logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
|
logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
|
||||||
|
|
||||||
CharBuffer mpChars = CharBuffer.wrap( masterPassword );
|
ByteBuffer mpBytesBuf = MP_charset.encode( CharBuffer.wrap( masterPassword ) );
|
||||||
byte[] mpBytes = MP_charset.encode( mpChars ).array();
|
byte[] mpBytes = new byte[mpBytesBuf.remaining()];
|
||||||
|
mpBytesBuf.get( mpBytes, 0, mpBytes.length );
|
||||||
|
Arrays.fill( mpBytesBuf.array(), (byte) 0 );
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return SCrypt.scrypt( mpBytes, masterKeySalt, MP_N, MP_r, MP_p, MP_dkLen );
|
return SCrypt.scrypt( mpBytes, masterKeySalt, MP_N, MP_r, MP_p, MP_dkLen );
|
||||||
}
|
}
|
||||||
@@ -89,29 +92,37 @@ public class MasterKeyV0 extends MasterKey {
|
|||||||
byte[] siteNameBytes = siteName.getBytes( MP_charset );
|
byte[] siteNameBytes = siteName.getBytes( MP_charset );
|
||||||
byte[] siteNameLengthBytes = bytesForInt( siteName.length() );
|
byte[] siteNameLengthBytes = bytesForInt( siteName.length() );
|
||||||
byte[] siteCounterBytes = bytesForInt( siteCounter );
|
byte[] siteCounterBytes = bytesForInt( siteCounter );
|
||||||
byte[] siteContextBytes = siteContext == null? null: siteContext.getBytes( MP_charset );
|
byte[] siteContextBytes = siteContext == null || siteContext.isEmpty()? null: siteContext.getBytes( MP_charset );
|
||||||
byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
|
byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
|
||||||
logger.trc( "site scope: %s, context: %s", siteScope, siteContext == null? "<empty>": siteContext );
|
logger.trc( "site scope: %s, context: %s", siteScope, siteContextBytes == null? "<empty>": siteContext );
|
||||||
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
|
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
|
||||||
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
|
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
|
||||||
siteContext == null? "(null)": siteContext );
|
siteContextBytes == null? "(null)": siteContext );
|
||||||
|
|
||||||
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
|
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
|
||||||
if (siteContextBytes != null)
|
if (siteContextBytes != null)
|
||||||
sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
|
sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
|
||||||
logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
|
logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
|
||||||
|
|
||||||
byte[] sitePasswordSeed = MP_mac.of( getKey(), sitePasswordInfo );
|
byte[] sitePasswordSeedBytes = MP_mac.of( getKey(), sitePasswordInfo );
|
||||||
logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) );
|
int[] sitePasswordSeed = new int[sitePasswordSeedBytes.length];
|
||||||
|
for (int i = 0; i < sitePasswordSeedBytes.length; ++i) {
|
||||||
|
ByteBuffer buf = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( ByteOrder.BIG_ENDIAN );
|
||||||
|
Arrays.fill( buf.array(), sitePasswordSeedBytes[i] > 0? (byte)0x00: (byte) 0xFF );
|
||||||
|
buf.position( 2 );
|
||||||
|
buf.put( sitePasswordSeedBytes[i] ).rewind();
|
||||||
|
sitePasswordSeed[i] = buf.getInt() & 0xFFFF;
|
||||||
|
}
|
||||||
|
logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeedBytes ) ) );
|
||||||
|
|
||||||
Preconditions.checkState( sitePasswordSeed.length > 0 );
|
Preconditions.checkState( sitePasswordSeed.length > 0 );
|
||||||
int templateIndex = sitePasswordSeed[0] & 0xFFFF;
|
int templateIndex = sitePasswordSeed[0];
|
||||||
MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex );
|
MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex );
|
||||||
logger.trc( "type %s, template: %s", siteType, template.getTemplateString() );
|
logger.trc( "type %s, template: %s", siteType, template.getTemplateString() );
|
||||||
|
|
||||||
StringBuilder password = new StringBuilder( template.length() );
|
StringBuilder password = new StringBuilder( template.length() );
|
||||||
for (int i = 0; i < template.length(); ++i) {
|
for (int i = 0; i < template.length(); ++i) {
|
||||||
int characterIndex = sitePasswordSeed[i + 1] & 0xFFFF;
|
int characterIndex = sitePasswordSeed[i + 1];
|
||||||
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
|
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
|
||||||
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
|
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
|
||||||
logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex,
|
logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex,
|
||||||
|
|||||||
@@ -46,12 +46,12 @@ public class MasterKeyV1 extends MasterKeyV0 {
|
|||||||
byte[] siteNameBytes = siteName.getBytes( MP_charset );
|
byte[] siteNameBytes = siteName.getBytes( MP_charset );
|
||||||
byte[] siteNameLengthBytes = bytesForInt( siteName.length() );
|
byte[] siteNameLengthBytes = bytesForInt( siteName.length() );
|
||||||
byte[] siteCounterBytes = bytesForInt( siteCounter );
|
byte[] siteCounterBytes = bytesForInt( siteCounter );
|
||||||
byte[] siteContextBytes = siteContext == null? null: siteContext.getBytes( MP_charset );
|
byte[] siteContextBytes = siteContext == null || siteContext.isEmpty()? null: siteContext.getBytes( MP_charset );
|
||||||
byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
|
byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
|
||||||
logger.trc( "site scope: %s, context: %s", siteScope, siteContext == null? "<empty>": siteContext );
|
logger.trc( "site scope: %s, context: %s", siteScope, siteContextBytes == null? "<empty>": siteContext );
|
||||||
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
|
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
|
||||||
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
|
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
|
||||||
siteContext == null? "(null)": siteContext );
|
siteContextBytes == null? "(null)": siteContext );
|
||||||
|
|
||||||
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
|
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
|
||||||
if (siteContextBytes != null)
|
if (siteContextBytes != null)
|
||||||
|
|||||||
@@ -45,12 +45,12 @@ public class MasterKeyV2 extends MasterKeyV1 {
|
|||||||
byte[] siteNameBytes = siteName.getBytes( MP_charset );
|
byte[] siteNameBytes = siteName.getBytes( MP_charset );
|
||||||
byte[] siteNameLengthBytes = bytesForInt( siteNameBytes.length );
|
byte[] siteNameLengthBytes = bytesForInt( siteNameBytes.length );
|
||||||
byte[] siteCounterBytes = bytesForInt( siteCounter );
|
byte[] siteCounterBytes = bytesForInt( siteCounter );
|
||||||
byte[] siteContextBytes = siteContext == null? null: siteContext.getBytes( MP_charset );
|
byte[] siteContextBytes = siteContext == null || siteContext.isEmpty()? null: siteContext.getBytes( MP_charset );
|
||||||
byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
|
byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
|
||||||
logger.trc( "site scope: %s, context: %s", siteScope, siteContext == null? "<empty>": siteContext );
|
logger.trc( "site scope: %s, context: %s", siteScope, siteContextBytes == null? "<empty>": siteContext );
|
||||||
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
|
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
|
||||||
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
|
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
|
||||||
siteContext == null? "(null)": siteContext );
|
siteContextBytes == null? "(null)": siteContext );
|
||||||
|
|
||||||
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
|
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
|
||||||
if (siteContextBytes != null)
|
if (siteContextBytes != null)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.google.common.primitives.Bytes;
|
|||||||
import com.lambdaworks.crypto.SCrypt;
|
import com.lambdaworks.crypto.SCrypt;
|
||||||
import com.lyndir.lhunath.opal.system.CodeUtils;
|
import com.lyndir.lhunath.opal.system.CodeUtils;
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.CharBuffer;
|
import java.nio.CharBuffer;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -42,8 +43,11 @@ public class MasterKeyV3 extends MasterKeyV2 {
|
|||||||
logger.trc( "key scope: %s", mpKeyScope );
|
logger.trc( "key scope: %s", mpKeyScope );
|
||||||
logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
|
logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
|
||||||
|
|
||||||
CharBuffer mpChars = CharBuffer.wrap( masterPassword );
|
ByteBuffer mpBytesBuf = MP_charset.encode( CharBuffer.wrap( masterPassword ) );
|
||||||
byte[] mpBytes = MP_charset.encode( mpChars ).array();
|
byte[] mpBytes = new byte[mpBytesBuf.remaining()];
|
||||||
|
mpBytesBuf.get( mpBytes, 0, mpBytes.length );
|
||||||
|
Arrays.fill( mpBytesBuf.array(), (byte)0 );
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return SCrypt.scrypt( mpBytes, masterKeySalt, MP_N, MP_r, MP_p, MP_dkLen );
|
return SCrypt.scrypt( mpBytes, masterKeySalt, MP_N, MP_r, MP_p, MP_dkLen );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package com.lyndir.masterpassword;
|
package com.lyndir.masterpassword;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
|
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
|
||||||
|
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
import com.lyndir.lhunath.opal.system.util.NNSupplier;
|
import com.lyndir.lhunath.opal.system.util.*;
|
||||||
import com.lyndir.lhunath.opal.system.util.NSupplier;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@@ -25,8 +25,9 @@ public class MPWTests {
|
|||||||
@XmlElement(name = "case")
|
@XmlElement(name = "case")
|
||||||
private List<Case> cases;
|
private List<Case> cases;
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
public List<Case> getCases() {
|
public List<Case> getCases() {
|
||||||
return cases;
|
return checkNotNull( cases );
|
||||||
}
|
}
|
||||||
|
|
||||||
public Case getCase(String identifier) {
|
public Case getCase(String identifier) {
|
||||||
@@ -45,6 +46,8 @@ public class MPWTests {
|
|||||||
@XmlAttribute
|
@XmlAttribute
|
||||||
private String parent;
|
private String parent;
|
||||||
@XmlElement
|
@XmlElement
|
||||||
|
private String algorithm;
|
||||||
|
@XmlElement
|
||||||
private String fullName;
|
private String fullName;
|
||||||
@XmlElement
|
@XmlElement
|
||||||
private String masterPassword;
|
private String masterPassword;
|
||||||
@@ -65,76 +68,86 @@ public class MPWTests {
|
|||||||
|
|
||||||
private transient Case parentCase;
|
private transient Case parentCase;
|
||||||
|
|
||||||
public void setTests(MPWTests tests) {
|
public void initializeParentHierarchy(MPWTests tests) {
|
||||||
|
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
parentCase = tests.getCase( parent );
|
parentCase = tests.getCase( parent );
|
||||||
fullName = ifNotNullElse( fullName, new NNSupplier<String>() {
|
parentCase.initializeParentHierarchy( tests );
|
||||||
@Nonnull
|
|
||||||
@Override
|
|
||||||
public String get() {
|
|
||||||
return parentCase.getFullName();
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
masterPassword = ifNotNullElse( masterPassword, new NNSupplier<String>() {
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
|
||||||
public String get() {
|
|
||||||
return new String( parentCase.getMasterPassword() );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
keyID = ifNotNullElse( keyID, new NNSupplier<String>() {
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
|
||||||
public String get() {
|
|
||||||
return parentCase.getKeyID();
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
siteName = ifNotNullElse( siteName, new NNSupplier<String>() {
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
|
||||||
public String get() {
|
|
||||||
return parentCase.getSiteName();
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
siteCounter = ifNotNullElse( siteCounter, new NNSupplier<Integer>() {
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
|
||||||
public Integer get() {
|
|
||||||
return parentCase.getSiteCounter();
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
siteType = ifNotNullElse( siteType, new NNSupplier<String>() {
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
|
||||||
public String get() {
|
|
||||||
return parentCase.getSiteType().name();
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
siteVariant = ifNotNullElse( siteVariant, new NNSupplier<String>() {
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
|
||||||
public String get() {
|
|
||||||
return parentCase.getSiteVariant().name();
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
siteContext = ifNotNullElseNullable( siteContext, new NSupplier<String>() {
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
|
||||||
public String get() {
|
|
||||||
return parentCase.getSiteContext();
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
result = ifNotNullElse( result, new NNSupplier<String>() {
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
|
||||||
public String get() {
|
|
||||||
return parentCase.getResult();
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
algorithm = ifNotNullElse( algorithm, new NNSupplier<String>() {
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return checkNotNull( parentCase.algorithm );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
fullName = ifNotNullElse( fullName, new NNSupplier<String>() {
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return checkNotNull( parentCase.fullName );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
masterPassword = ifNotNullElse( masterPassword, new NNSupplier<String>() {
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return checkNotNull( parentCase.masterPassword );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
keyID = ifNotNullElse( keyID, new NNSupplier<String>() {
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return checkNotNull( parentCase.keyID );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
siteName = ifNotNullElse( siteName, new NNSupplier<String>() {
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return checkNotNull( parentCase.siteName );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
siteCounter = ifNotNullElse( siteCounter, new NNSupplier<Integer>() {
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public Integer get() {
|
||||||
|
return checkNotNull( parentCase.siteCounter );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
siteType = ifNotNullElse( siteType, new NNSupplier<String>() {
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return checkNotNull( parentCase.siteType );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
siteVariant = ifNotNullElse( siteVariant, new NNSupplier<String>() {
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return checkNotNull( parentCase.siteVariant );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
siteContext = ifNotNullElse( siteContext, new NNSupplier<String>() {
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return parentCase == null? "": checkNotNull( parentCase.siteContext );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
result = ifNotNullElse( result, new NNSupplier<String>() {
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return parentCase == null? "": checkNotNull( parentCase.result );
|
||||||
|
}
|
||||||
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
public String getIdentifier() {
|
public String getIdentifier() {
|
||||||
return identifier;
|
return identifier;
|
||||||
}
|
}
|
||||||
@@ -144,40 +157,53 @@ public class MPWTests {
|
|||||||
return parentCase;
|
return parentCase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public MasterKey.Version getAlgorithm() {
|
||||||
|
return MasterKey.Version.fromInt( ConversionUtils.toIntegerNN( algorithm ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
public String getFullName() {
|
public String getFullName() {
|
||||||
return fullName;
|
return checkNotNull( fullName );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
public char[] getMasterPassword() {
|
public char[] getMasterPassword() {
|
||||||
return masterPassword == null? null: masterPassword.toCharArray();
|
return checkNotNull( masterPassword ).toCharArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
public String getKeyID() {
|
public String getKeyID() {
|
||||||
return keyID;
|
return checkNotNull( keyID );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
public String getSiteName() {
|
public String getSiteName() {
|
||||||
return siteName;
|
return checkNotNull( siteName );
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSiteCounter() {
|
public int getSiteCounter() {
|
||||||
return ifNotNullElse( siteCounter, 1 );
|
return ifNotNullElse( siteCounter, 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
public MPSiteType getSiteType() {
|
public MPSiteType getSiteType() {
|
||||||
return MPSiteType.forName( siteType );
|
return MPSiteType.forName( checkNotNull( siteType ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
public MPSiteVariant getSiteVariant() {
|
public MPSiteVariant getSiteVariant() {
|
||||||
return MPSiteVariant.forName( siteVariant );
|
return MPSiteVariant.forName( checkNotNull( siteVariant ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
public String getSiteContext() {
|
public String getSiteContext() {
|
||||||
return siteContext;
|
return checkNotNull( siteContext );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
public String getResult() {
|
public String getResult() {
|
||||||
return result;
|
return checkNotNull( result );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import static org.testng.Assert.*;
|
|||||||
import com.google.common.io.Resources;
|
import com.google.common.io.Resources;
|
||||||
import com.lyndir.lhunath.opal.system.CodeUtils;
|
import com.lyndir.lhunath.opal.system.CodeUtils;
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
|
import com.lyndir.lhunath.opal.system.util.StringUtils;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import javax.xml.bind.JAXBContext;
|
import javax.xml.bind.JAXBContext;
|
||||||
import org.testng.annotations.BeforeMethod;
|
import org.testng.annotations.BeforeMethod;
|
||||||
@@ -26,7 +27,7 @@ public class MasterKeyTest {
|
|||||||
URL testCasesResource = Resources.getResource( "mpw_tests.xml" );
|
URL testCasesResource = Resources.getResource( "mpw_tests.xml" );
|
||||||
tests = (MPWTests) JAXBContext.newInstance( MPWTests.class ).createUnmarshaller().unmarshal( testCasesResource );
|
tests = (MPWTests) JAXBContext.newInstance( MPWTests.class ).createUnmarshaller().unmarshal( testCasesResource );
|
||||||
for (MPWTests.Case testCase : tests.getCases())
|
for (MPWTests.Case testCase : tests.getCases())
|
||||||
testCase.setTests( tests );
|
testCase.initializeParentHierarchy( tests );
|
||||||
defaultCase = tests.getCase( MPWTests.ID_DEFAULT );
|
defaultCase = tests.getCase( MPWTests.ID_DEFAULT );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,10 +36,15 @@ public class MasterKeyTest {
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
for (MPWTests.Case testCase : tests.getCases()) {
|
for (MPWTests.Case testCase : tests.getCases()) {
|
||||||
MasterKey masterKey = MasterKey.create( testCase.getFullName(), testCase.getMasterPassword() );
|
if (testCase.getResult().isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
logger.inf( "Running test case: %s [testEncode]", testCase.getIdentifier() );
|
||||||
|
MasterKey masterKey = MasterKey.create( testCase.getAlgorithm(), testCase.getFullName(), testCase.getMasterPassword() );
|
||||||
assertEquals(
|
assertEquals(
|
||||||
masterKey.encode( testCase.getSiteName(), testCase.getSiteType(), testCase.getSiteCounter(), testCase.getSiteVariant(),
|
masterKey.encode( testCase.getSiteName(), testCase.getSiteType(), testCase.getSiteCounter(), testCase.getSiteVariant(),
|
||||||
testCase.getSiteContext() ), testCase.getResult(), "Failed test case: " + testCase );
|
testCase.getSiteContext() ), testCase.getResult(), "Failed test case: " + testCase );
|
||||||
|
logger.inf( "passed!" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,8 +61,13 @@ public class MasterKeyTest {
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
for (MPWTests.Case testCase : tests.getCases()) {
|
for (MPWTests.Case testCase : tests.getCases()) {
|
||||||
|
if (testCase.getResult().isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
logger.inf( "Running test case: %s [testGetKeyID]", testCase.getIdentifier() );
|
||||||
MasterKey masterKey = MasterKey.create( testCase.getFullName(), testCase.getMasterPassword() );
|
MasterKey masterKey = MasterKey.create( testCase.getFullName(), testCase.getMasterPassword() );
|
||||||
assertEquals( CodeUtils.encodeHex( masterKey.getKeyID() ), testCase.getKeyID(), "Failed test case: " + testCase );
|
assertEquals( CodeUtils.encodeHex( masterKey.getKeyID() ), testCase.getKeyID(), "Failed test case: " + testCase );
|
||||||
|
logger.inf( "passed!" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<tests>
|
<tests>
|
||||||
|
<!-- Default values for all parameters. -->
|
||||||
<case id="default">
|
<case id="default">
|
||||||
|
<algorithm><!-- current --></algorithm>
|
||||||
<fullName>Robert Lee Mitchell</fullName>
|
<fullName>Robert Lee Mitchell</fullName>
|
||||||
<masterPassword>banana colored duckling</masterPassword>
|
<masterPassword>banana colored duckling</masterPassword>
|
||||||
<keyID>98EEF4D1DF46D849574A82A03C3177056B15DFFCA29BB3899DE4628453675302</keyID>
|
<keyID>98EEF4D1DF46D849574A82A03C3177056B15DFFCA29BB3899DE4628453675302</keyID>
|
||||||
@@ -7,67 +9,271 @@
|
|||||||
<siteCounter>1</siteCounter>
|
<siteCounter>1</siteCounter>
|
||||||
<siteType>GeneratedLong</siteType>
|
<siteType>GeneratedLong</siteType>
|
||||||
<siteVariant>Password</siteVariant>
|
<siteVariant>Password</siteVariant>
|
||||||
|
<result><!-- abstract --></result>
|
||||||
|
</case>
|
||||||
|
|
||||||
|
<!-- Algorithm 3 -->
|
||||||
|
<case id="v3" parent="default">
|
||||||
|
<algorithm>3</algorithm>
|
||||||
<result>Jejr5[RepuSosp</result>
|
<result>Jejr5[RepuSosp</result>
|
||||||
</case>
|
</case>
|
||||||
<case id="mb_fullName" parent="default">
|
<case id="v3_mb_fullName" parent="v3">
|
||||||
<fullName>⛄</fullName>
|
<fullName>⛄</fullName>
|
||||||
<keyID>1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8</keyID>
|
<keyID>1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8</keyID>
|
||||||
<result>NopaDajh8=Fene</result>
|
<result>NopaDajh8=Fene</result>
|
||||||
</case>
|
</case>
|
||||||
<case id="mb_masterPassword" parent="default">
|
<case id="v3_mb_masterPassword" parent="v3">
|
||||||
<masterPassword>⛄</masterPassword>
|
<masterPassword>⛄</masterPassword>
|
||||||
<keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
|
<keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
|
||||||
<result>QesuHirv5-Xepl</result>
|
<result>QesuHirv5-Xepl</result>
|
||||||
</case>
|
</case>
|
||||||
<case id="mb_siteName" parent="default">
|
<case id="v3_mb_siteName" parent="v3">
|
||||||
<siteName>⛄</siteName>
|
<siteName>⛄</siteName>
|
||||||
<result>LiheCuwhSerz6)</result>
|
<result>LiheCuwhSerz6)</result>
|
||||||
</case>
|
</case>
|
||||||
<case id="loginName" parent="default">
|
<case id="v3_loginName" parent="v3">
|
||||||
<siteVariant>Login</siteVariant>
|
<siteVariant>Login</siteVariant>
|
||||||
<siteType>GeneratedName</siteType>
|
<siteType>GeneratedName</siteType>
|
||||||
<result>wohzaqage</result>
|
<result>wohzaqage</result>
|
||||||
</case>
|
</case>
|
||||||
<case id="securityAnswer" parent="default">
|
<case id="v3_securityAnswer" parent="v3">
|
||||||
<siteVariant>Answer</siteVariant>
|
<siteVariant>Answer</siteVariant>
|
||||||
<siteType>GeneratedPhrase</siteType>
|
<siteType>GeneratedPhrase</siteType>
|
||||||
<result>xin diyjiqoja hubu</result>
|
<result>xin diyjiqoja hubu</result>
|
||||||
</case>
|
</case>
|
||||||
<case id="securityAnswer_context" parent="securityAnswer">
|
<case id="v3_securityAnswer_context" parent="v3_securityAnswer">
|
||||||
<siteContext>question</siteContext>
|
<siteContext>question</siteContext>
|
||||||
<result>xogx tem cegyiva jab</result>
|
<result>xogx tem cegyiva jab</result>
|
||||||
</case>
|
</case>
|
||||||
<case id="type_maximum" parent="default">
|
<case id="v3_type_maximum" parent="v3">
|
||||||
<siteType>GeneratedMaximum</siteType>
|
<siteType>GeneratedMaximum</siteType>
|
||||||
<result>W6@692^B1#&@gVdSdLZ@</result>
|
<result>W6@692^B1#&@gVdSdLZ@</result>
|
||||||
</case>
|
</case>
|
||||||
<case id="type_medium" parent="default">
|
<case id="v3_type_medium" parent="v3">
|
||||||
<siteType>GeneratedMedium</siteType>
|
<siteType>GeneratedMedium</siteType>
|
||||||
<result>Jej2$Quv</result>
|
<result>Jej2$Quv</result>
|
||||||
</case>
|
</case>
|
||||||
<case id="type_basic" parent="default">
|
<case id="v3_type_basic" parent="v3">
|
||||||
<siteType>GeneratedBasic</siteType>
|
<siteType>GeneratedBasic</siteType>
|
||||||
<result>WAo2xIg6</result>
|
<result>WAo2xIg6</result>
|
||||||
</case>
|
</case>
|
||||||
<case id="type_short" parent="default">
|
<case id="v3_type_short" parent="v3">
|
||||||
<siteType>GeneratedShort</siteType>
|
<siteType>GeneratedShort</siteType>
|
||||||
<result>Jej2</result>
|
<result>Jej2</result>
|
||||||
</case>
|
</case>
|
||||||
<case id="type_pin" parent="default">
|
<case id="v3_type_pin" parent="v3">
|
||||||
<siteType>GeneratedPIN</siteType>
|
<siteType>GeneratedPIN</siteType>
|
||||||
<result>7662</result>
|
<result>7662</result>
|
||||||
</case>
|
</case>
|
||||||
<case id="type_name" parent="default">
|
<case id="v3_type_name" parent="v3">
|
||||||
<siteType>GeneratedName</siteType>
|
<siteType>GeneratedName</siteType>
|
||||||
<result>jejraquvo</result>
|
<result>jejraquvo</result>
|
||||||
</case>
|
</case>
|
||||||
<case id="type_phrase" parent="default">
|
<case id="v3_type_phrase" parent="v3">
|
||||||
<siteType>GeneratedPhrase</siteType>
|
<siteType>GeneratedPhrase</siteType>
|
||||||
<result>jejr quv cabsibu tam</result>
|
<result>jejr quv cabsibu tam</result>
|
||||||
</case>
|
</case>
|
||||||
<case id="counter_ceiling" parent="default">
|
<case id="v3_counter_ceiling" parent="v3">
|
||||||
<siteCounter>4294967295</siteCounter>
|
<siteCounter>4294967295</siteCounter>
|
||||||
<result>XambHoqo6[Peni</result>
|
<result>XambHoqo6[Peni</result>
|
||||||
</case>
|
</case>
|
||||||
|
|
||||||
|
<!-- Algorithm 2 -->
|
||||||
|
<case id="v2" parent="default">
|
||||||
|
<algorithm>2</algorithm>
|
||||||
|
<result>Jejr5[RepuSosp</result>
|
||||||
|
</case>
|
||||||
|
<case id="v2_mb_fullName" parent="v2">
|
||||||
|
<fullName>⛄</fullName>
|
||||||
|
<keyID>1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8</keyID>
|
||||||
|
<result>WaqoGuho2[Xaxw</result>
|
||||||
|
</case>
|
||||||
|
<case id="v2_mb_masterPassword" parent="v2">
|
||||||
|
<masterPassword>⛄</masterPassword>
|
||||||
|
<keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
|
||||||
|
<result>QesuHirv5-Xepl</result>
|
||||||
|
</case>
|
||||||
|
<case id="v2_mb_siteName" parent="v2">
|
||||||
|
<siteName>⛄</siteName>
|
||||||
|
<result>LiheCuwhSerz6)</result>
|
||||||
|
</case>
|
||||||
|
<case id="v2_loginName" parent="v2">
|
||||||
|
<siteVariant>Login</siteVariant>
|
||||||
|
<siteType>GeneratedName</siteType>
|
||||||
|
<result>wohzaqage</result>
|
||||||
|
</case>
|
||||||
|
<case id="v2_securityAnswer" parent="v2">
|
||||||
|
<siteVariant>Answer</siteVariant>
|
||||||
|
<siteType>GeneratedPhrase</siteType>
|
||||||
|
<result>xin diyjiqoja hubu</result>
|
||||||
|
</case>
|
||||||
|
<case id="v2_securityAnswer_context" parent="v2_securityAnswer">
|
||||||
|
<siteContext>question</siteContext>
|
||||||
|
<result>xogx tem cegyiva jab</result>
|
||||||
|
</case>
|
||||||
|
<case id="v2_type_maximum" parent="v2">
|
||||||
|
<siteType>GeneratedMaximum</siteType>
|
||||||
|
<result>W6@692^B1#&@gVdSdLZ@</result>
|
||||||
|
</case>
|
||||||
|
<case id="v2_type_medium" parent="v2">
|
||||||
|
<siteType>GeneratedMedium</siteType>
|
||||||
|
<result>Jej2$Quv</result>
|
||||||
|
</case>
|
||||||
|
<case id="v2_type_basic" parent="v2">
|
||||||
|
<siteType>GeneratedBasic</siteType>
|
||||||
|
<result>WAo2xIg6</result>
|
||||||
|
</case>
|
||||||
|
<case id="v2_type_short" parent="v2">
|
||||||
|
<siteType>GeneratedShort</siteType>
|
||||||
|
<result>Jej2</result>
|
||||||
|
</case>
|
||||||
|
<case id="v2_type_pin" parent="v2">
|
||||||
|
<siteType>GeneratedPIN</siteType>
|
||||||
|
<result>7662</result>
|
||||||
|
</case>
|
||||||
|
<case id="v2_type_name" parent="v2">
|
||||||
|
<siteType>GeneratedName</siteType>
|
||||||
|
<result>jejraquvo</result>
|
||||||
|
</case>
|
||||||
|
<case id="v2_type_phrase" parent="v2">
|
||||||
|
<siteType>GeneratedPhrase</siteType>
|
||||||
|
<result>jejr quv cabsibu tam</result>
|
||||||
|
</case>
|
||||||
|
<case id="v2_counter_ceiling" parent="v2">
|
||||||
|
<siteCounter>4294967295</siteCounter>
|
||||||
|
<result>XambHoqo6[Peni</result>
|
||||||
|
</case>
|
||||||
|
|
||||||
|
<!-- Algorithm 1 -->
|
||||||
|
<case id="v1" parent="default">
|
||||||
|
<algorithm>1</algorithm>
|
||||||
|
<result>Jejr5[RepuSosp</result>
|
||||||
|
</case>
|
||||||
|
<case id="v1_mb_fullName" parent="v1">
|
||||||
|
<fullName>⛄</fullName>
|
||||||
|
<keyID>1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8</keyID>
|
||||||
|
<result>WaqoGuho2[Xaxw</result>
|
||||||
|
</case>
|
||||||
|
<case id="v1_mb_masterPassword" parent="v1">
|
||||||
|
<masterPassword>⛄</masterPassword>
|
||||||
|
<keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
|
||||||
|
<result>QesuHirv5-Xepl</result>
|
||||||
|
</case>
|
||||||
|
<case id="v1_mb_siteName" parent="v1">
|
||||||
|
<siteName>⛄</siteName>
|
||||||
|
<result>WawiYarp2@Kodh</result>
|
||||||
|
</case>
|
||||||
|
<case id="v1_loginName" parent="v1">
|
||||||
|
<siteVariant>Login</siteVariant>
|
||||||
|
<siteType>GeneratedName</siteType>
|
||||||
|
<result>wohzaqage</result>
|
||||||
|
</case>
|
||||||
|
<case id="v1_securityAnswer" parent="v1">
|
||||||
|
<siteVariant>Answer</siteVariant>
|
||||||
|
<siteType>GeneratedPhrase</siteType>
|
||||||
|
<result>xin diyjiqoja hubu</result>
|
||||||
|
</case>
|
||||||
|
<case id="v1_securityAnswer_context" parent="v1_securityAnswer">
|
||||||
|
<siteContext>question</siteContext>
|
||||||
|
<result>xogx tem cegyiva jab</result>
|
||||||
|
</case>
|
||||||
|
<case id="v1_type_maximum" parent="v1">
|
||||||
|
<siteType>GeneratedMaximum</siteType>
|
||||||
|
<result>W6@692^B1#&@gVdSdLZ@</result>
|
||||||
|
</case>
|
||||||
|
<case id="v1_type_medium" parent="v1">
|
||||||
|
<siteType>GeneratedMedium</siteType>
|
||||||
|
<result>Jej2$Quv</result>
|
||||||
|
</case>
|
||||||
|
<case id="v1_type_basic" parent="v1">
|
||||||
|
<siteType>GeneratedBasic</siteType>
|
||||||
|
<result>WAo2xIg6</result>
|
||||||
|
</case>
|
||||||
|
<case id="v1_type_short" parent="v1">
|
||||||
|
<siteType>GeneratedShort</siteType>
|
||||||
|
<result>Jej2</result>
|
||||||
|
</case>
|
||||||
|
<case id="v1_type_pin" parent="v1">
|
||||||
|
<siteType>GeneratedPIN</siteType>
|
||||||
|
<result>7662</result>
|
||||||
|
</case>
|
||||||
|
<case id="v1_type_name" parent="v1">
|
||||||
|
<siteType>GeneratedName</siteType>
|
||||||
|
<result>jejraquvo</result>
|
||||||
|
</case>
|
||||||
|
<case id="v1_type_phrase" parent="v1">
|
||||||
|
<siteType>GeneratedPhrase</siteType>
|
||||||
|
<result>jejr quv cabsibu tam</result>
|
||||||
|
</case>
|
||||||
|
<case id="v1_counter_ceiling" parent="v1">
|
||||||
|
<siteCounter>4294967295</siteCounter>
|
||||||
|
<result>XambHoqo6[Peni</result>
|
||||||
|
</case>
|
||||||
|
|
||||||
|
<!-- Algorithm 0 -->
|
||||||
|
<case id="v0" parent="default">
|
||||||
|
<algorithm>0</algorithm>
|
||||||
|
<result>Feji5@ReduWosh</result>
|
||||||
|
</case>
|
||||||
|
<case id="v0_mb_fullName" parent="v0">
|
||||||
|
<fullName>⛄</fullName>
|
||||||
|
<keyID>1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8</keyID>
|
||||||
|
<result>HajrYudo7@Mamh</result>
|
||||||
|
</case>
|
||||||
|
<case id="v0_mb_masterPassword" parent="v0">
|
||||||
|
<masterPassword>⛄</masterPassword>
|
||||||
|
<keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
|
||||||
|
<result>MewmDini0]Meho</result>
|
||||||
|
</case>
|
||||||
|
<case id="v0_mb_siteName" parent="v0">
|
||||||
|
<siteName>⛄</siteName>
|
||||||
|
<result>HahiVana2@Nole</result>
|
||||||
|
</case>
|
||||||
|
<case id="v0_loginName" parent="v0">
|
||||||
|
<siteVariant>Login</siteVariant>
|
||||||
|
<siteType>GeneratedName</siteType>
|
||||||
|
<result>lozwajave</result>
|
||||||
|
</case>
|
||||||
|
<case id="v0_securityAnswer" parent="v0">
|
||||||
|
<siteVariant>Answer</siteVariant>
|
||||||
|
<siteType>GeneratedPhrase</siteType>
|
||||||
|
<result>miy lirfijoja dubu</result>
|
||||||
|
</case>
|
||||||
|
<case id="v0_securityAnswer_context" parent="v0_securityAnswer">
|
||||||
|
<siteContext>question</siteContext>
|
||||||
|
<result>movm bex gevrica jaf</result>
|
||||||
|
</case>
|
||||||
|
<case id="v0_type_maximum" parent="v0">
|
||||||
|
<siteType>GeneratedMaximum</siteType>
|
||||||
|
<result>w1!3bA3icmRAc)SS@lwl</result>
|
||||||
|
</case>
|
||||||
|
<case id="v0_type_medium" parent="v0">
|
||||||
|
<siteType>GeneratedMedium</siteType>
|
||||||
|
<result>Fej7]Jug</result>
|
||||||
|
</case>
|
||||||
|
<case id="v0_type_basic" parent="v0">
|
||||||
|
<siteType>GeneratedBasic</siteType>
|
||||||
|
<result>wvH7irC1</result>
|
||||||
|
</case>
|
||||||
|
<case id="v0_type_short" parent="v0">
|
||||||
|
<siteType>GeneratedShort</siteType>
|
||||||
|
<result>Fej7</result>
|
||||||
|
</case>
|
||||||
|
<case id="v0_type_pin" parent="v0">
|
||||||
|
<siteType>GeneratedPIN</siteType>
|
||||||
|
<result>2117</result>
|
||||||
|
</case>
|
||||||
|
<case id="v0_type_name" parent="v0">
|
||||||
|
<siteType>GeneratedName</siteType>
|
||||||
|
<result>fejrajugo</result>
|
||||||
|
</case>
|
||||||
|
<case id="v0_type_phrase" parent="v0">
|
||||||
|
<siteType>GeneratedPhrase</siteType>
|
||||||
|
<result>fejr jug gabsibu bax</result>
|
||||||
|
</case>
|
||||||
|
<case id="v0_counter_ceiling" parent="v0">
|
||||||
|
<siteCounter>4294967295</siteCounter>
|
||||||
|
<result>QateDojh1@Hecn</result>
|
||||||
|
</case>
|
||||||
</tests>
|
</tests>
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.lyndir.masterpassword"
|
package="com.lyndir.masterpassword"
|
||||||
android:versionCode="1"
|
android:versionCode="2003"
|
||||||
android:versionName="2.2">
|
android:versionName="2.3">
|
||||||
|
|
||||||
<uses-sdk
|
<uses-sdk
|
||||||
android:minSdkVersion="19"
|
android:minSdkVersion="19"
|
||||||
|
|||||||
13
MasterPassword/Java/masterpassword-android/README
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
To build this module, please ensure you've done the following setup:
|
||||||
|
|
||||||
|
1. Installed the Android SDK and fully downloaded the Android SDK platform 21 in it.
|
||||||
|
2. Set the environment variable ANDROID_HOME in your shell or in ~/.mavenrc to point to the root of your Android SDK install.
|
||||||
|
3. Installed the Android SDK into your Maven's local repository.
|
||||||
|
3a. Clone the maven-android-sdk-deployer available from here: https://github.com/mosabua/maven-android-sdk-deployer.git
|
||||||
|
3b. In the root of this project, run: mvn install -P 5.0
|
||||||
|
|
||||||
|
To build this module:
|
||||||
|
|
||||||
|
1. Build the parent, by going into 'MasterPassword/Java' and running: mvn clean install
|
||||||
|
2. Build this module, by going into 'MasterPassword/Java/masterpassword-android' and running: mvn clean install
|
||||||
|
3. You can then find the APK in: 'MasterPassword/Java/masterpassword-android/target'
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.lyndir.masterpassword</groupId>
|
<groupId>com.lyndir.masterpassword</groupId>
|
||||||
<artifactId>masterpassword</artifactId>
|
<artifactId>masterpassword</artifactId>
|
||||||
<version>2.2</version>
|
<version>2.3</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<name>Master Password Android</name>
|
<name>Master Password Android</name>
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.lyndir.masterpassword</groupId>
|
<groupId>com.lyndir.masterpassword</groupId>
|
||||||
<artifactId>masterpassword-algorithm</artifactId>
|
<artifactId>masterpassword-algorithm</artifactId>
|
||||||
<version>2.2</version>
|
<version>2.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -115,13 +115,13 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
<artifactId>slf4j-android</artifactId>
|
<artifactId>slf4j-android</artifactId>
|
||||||
|
<version>1.7.13-underscore</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- clone https://github.com/mosabua/maven-android-sdk-deployer.git
|
|
||||||
run mvn install -P 4.4 -->
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>android</groupId>
|
<groupId>android</groupId>
|
||||||
<artifactId>android</artifactId>
|
<artifactId>android</artifactId>
|
||||||
|
<version>5.0.1_r2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
@@ -85,7 +85,7 @@
|
|||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<Button
|
||||||
android:id="@id/sitePasswordField"
|
android:id="@id/sitePasswordField"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ package com.lyndir.masterpassword;
|
|||||||
|
|
||||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
|
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.*;
|
||||||
import android.content.*;
|
import android.content.*;
|
||||||
import android.content.ClipboardManager;
|
import android.content.ClipboardManager;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.*;
|
import android.text.*;
|
||||||
import android.text.method.PasswordTransformationMethod;
|
import android.text.method.PasswordTransformationMethod;
|
||||||
@@ -18,7 +19,7 @@ import com.google.common.base.Throwables;
|
|||||||
import com.google.common.util.concurrent.*;
|
import com.google.common.util.concurrent.*;
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
import com.lyndir.lhunath.opal.system.util.ConversionUtils;
|
import com.lyndir.lhunath.opal.system.util.ConversionUtils;
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
@@ -26,7 +27,9 @@ import javax.annotation.Nullable;
|
|||||||
public class EmergencyActivity extends Activity {
|
public class EmergencyActivity extends Activity {
|
||||||
|
|
||||||
@SuppressWarnings("UnusedDeclaration")
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
private static final Logger logger = Logger.get( EmergencyActivity.class );
|
private static final Logger logger = Logger.get( EmergencyActivity.class );
|
||||||
|
private static final ClipData EMPTY_CLIP = new ClipData( new ClipDescription( "", new String[0] ), new ClipData.Item( "" ) );
|
||||||
|
private static final int PASSWORD_NOTIFICATION = 0;
|
||||||
|
|
||||||
private final ListeningExecutorService executor = MoreExecutors.listeningDecorator( Executors.newSingleThreadExecutor() );
|
private final ListeningExecutorService executor = MoreExecutors.listeningDecorator( Executors.newSingleThreadExecutor() );
|
||||||
private final ValueChangedListener updateMasterKey = new ValueChangedListener() {
|
private final ValueChangedListener updateMasterKey = new ValueChangedListener() {
|
||||||
@@ -66,7 +69,7 @@ public class EmergencyActivity extends Activity {
|
|||||||
Spinner siteVersionField;
|
Spinner siteVersionField;
|
||||||
|
|
||||||
@InjectView(R.id.sitePasswordField)
|
@InjectView(R.id.sitePasswordField)
|
||||||
TextView sitePasswordField;
|
Button sitePasswordField;
|
||||||
|
|
||||||
@InjectView(R.id.sitePasswordTip)
|
@InjectView(R.id.sitePasswordTip)
|
||||||
TextView sitePasswordTip;
|
TextView sitePasswordTip;
|
||||||
@@ -306,13 +309,40 @@ public class EmergencyActivity extends Activity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void copySitePassword(View view) {
|
public void copySitePassword(View view) {
|
||||||
if (TextUtils.isEmpty( sitePassword ))
|
final String currentSitePassword = this.sitePassword;
|
||||||
|
if (TextUtils.isEmpty( currentSitePassword ))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ClipDescription description = new ClipDescription( strf( "Password for %s", siteNameField.getText() ),
|
final ClipboardManager clipboardManager = (ClipboardManager) getSystemService( CLIPBOARD_SERVICE );
|
||||||
new String[]{ ClipDescription.MIMETYPE_TEXT_PLAIN } );
|
final NotificationManager notificationManager = (NotificationManager) getSystemService( Context.NOTIFICATION_SERVICE );
|
||||||
ClipData clipData = new ClipData( description, new ClipData.Item( sitePassword ) );
|
|
||||||
((ClipboardManager) getSystemService( CLIPBOARD_SERVICE )).setPrimaryClip( clipData );
|
String title = strf( "Password for %s", siteNameField.getText() );
|
||||||
|
ClipDescription description = new ClipDescription( title, new String[]{ ClipDescription.MIMETYPE_TEXT_PLAIN } );
|
||||||
|
clipboardManager.setPrimaryClip( new ClipData( description, new ClipData.Item( currentSitePassword ) ) );
|
||||||
|
|
||||||
|
Notification.Builder notificationBuilder = new Notification.Builder( this ).setContentTitle( title )
|
||||||
|
.setContentText( "Paste the password into your app." )
|
||||||
|
.setSmallIcon( R.drawable.icon )
|
||||||
|
.setAutoCancel( true );
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
notificationBuilder.setVisibility( Notification.VISIBILITY_SECRET )
|
||||||
|
.setCategory( Notification.CATEGORY_RECOMMENDATION )
|
||||||
|
.setLocalOnly( true );
|
||||||
|
notificationManager.notify( PASSWORD_NOTIFICATION, notificationBuilder.build() );
|
||||||
|
final Timer timer = new Timer();
|
||||||
|
timer.schedule( new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
ClipData clip = clipboardManager.getPrimaryClip();
|
||||||
|
for (int i = 0; i < clip.getItemCount(); ++i)
|
||||||
|
if (currentSitePassword.equals( clip.getItemAt( i ).coerceToText( EmergencyActivity.this ) )) {
|
||||||
|
clipboardManager.setPrimaryClip( EMPTY_CLIP );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
notificationManager.cancel( PASSWORD_NOTIFICATION );
|
||||||
|
timer.cancel();
|
||||||
|
}
|
||||||
|
}, 20000 );
|
||||||
|
|
||||||
Intent startMain = new Intent( Intent.ACTION_MAIN );
|
Intent startMain = new Intent( Intent.ACTION_MAIN );
|
||||||
startMain.addCategory( Intent.CATEGORY_HOME );
|
startMain.addCategory( Intent.CATEGORY_HOME );
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.lyndir.masterpassword</groupId>
|
<groupId>com.lyndir.masterpassword</groupId>
|
||||||
<artifactId>masterpassword</artifactId>
|
<artifactId>masterpassword</artifactId>
|
||||||
<version>2.2</version>
|
<version>2.3</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<name>Master Password CLI</name>
|
<name>Master Password CLI</name>
|
||||||
@@ -85,7 +85,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.lyndir.masterpassword</groupId>
|
<groupId>com.lyndir.masterpassword</groupId>
|
||||||
<artifactId>masterpassword-algorithm</artifactId>
|
<artifactId>masterpassword-algorithm</artifactId>
|
||||||
<version>2.2</version>
|
<version>2.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.lyndir.masterpassword</groupId>
|
<groupId>com.lyndir.masterpassword</groupId>
|
||||||
<artifactId>masterpassword</artifactId>
|
<artifactId>masterpassword</artifactId>
|
||||||
<version>2.2</version>
|
<version>2.3</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<name>Master Password GUI</name>
|
<name>Master Password GUI</name>
|
||||||
@@ -134,7 +134,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.lyndir.masterpassword</groupId>
|
<groupId>com.lyndir.masterpassword</groupId>
|
||||||
<artifactId>masterpassword-model</artifactId>
|
<artifactId>masterpassword-model</artifactId>
|
||||||
<version>2.2</version>
|
<version>2.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- EXTERNAL DEPENDENCIES -->
|
<!-- EXTERNAL DEPENDENCIES -->
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
package com.lyndir.masterpassword.gui;
|
package com.lyndir.masterpassword.gui;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.lyndir.masterpassword.MPIdenticon;
|
||||||
import com.lyndir.masterpassword.gui.util.Components;
|
import com.lyndir.masterpassword.gui.util.Components;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -29,6 +33,8 @@ public abstract class AuthenticationPanel extends Components.GradientPanel {
|
|||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
add( Box.createVerticalGlue() );
|
add( Box.createVerticalGlue() );
|
||||||
|
|
||||||
|
avatarLabel.setToolTipText( "The avatar for your user. Click to change it." );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void updateUser(boolean repack) {
|
protected void updateUser(boolean repack) {
|
||||||
@@ -39,8 +45,11 @@ public abstract class AuthenticationPanel extends Components.GradientPanel {
|
|||||||
unlockFrame.repack();
|
unlockFrame.repack();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
protected abstract User getSelectedUser();
|
protected abstract User getSelectedUser();
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Nonnull
|
||||||
public abstract char[] getMasterPassword();
|
public abstract char[] getMasterPassword();
|
||||||
|
|
||||||
public Component getFocusComponent() {
|
public Component getFocusComponent() {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import com.google.common.base.Charsets;
|
|||||||
import com.google.common.io.*;
|
import com.google.common.io.*;
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
import com.lyndir.lhunath.opal.system.util.TypeUtils;
|
import com.lyndir.lhunath.opal.system.util.TypeUtils;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
@@ -48,7 +49,13 @@ public class GUI implements UnlockFrame.SignInCallback {
|
|||||||
if (Config.get().checkForUpdates())
|
if (Config.get().checkForUpdates())
|
||||||
checkUpdate();
|
checkUpdate();
|
||||||
|
|
||||||
TypeUtils.<GUI>newInstance( AppleGUI.class ).or( new GUI() ).open();
|
try {
|
||||||
|
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||||
|
}
|
||||||
|
catch (UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeUtils.<GUI>newInstance( "com.lyndir.masterpassword.gui.platform.mac.AppleGUI" ).or( new GUI() ).open();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void checkUpdate() {
|
private static void checkUpdate() {
|
||||||
@@ -80,7 +87,7 @@ public class GUI implements UnlockFrame.SignInCallback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void open() {
|
protected void open() {
|
||||||
SwingUtilities.invokeLater( new Runnable() {
|
SwingUtilities.invokeLater( new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import java.awt.event.ActionListener;
|
|||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.event.DocumentEvent;
|
import javax.swing.event.DocumentEvent;
|
||||||
import javax.swing.event.DocumentListener;
|
import javax.swing.event.DocumentListener;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,6 +59,7 @@ public class IncognitoAuthenticationPanel extends AuthenticationPanel implements
|
|||||||
return new IncognitoUser( fullNameField.getText() );
|
return new IncognitoUser( fullNameField.getText() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public char[] getMasterPassword() {
|
public char[] getMasterPassword() {
|
||||||
return masterPasswordField.getPassword();
|
return masterPasswordField.getPassword();
|
||||||
|
|||||||
4
MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/IncognitoUser.java
Normal file → Executable file
@@ -41,4 +41,8 @@ public class IncognitoUser extends User {
|
|||||||
@Override
|
@Override
|
||||||
public void addSite(final Site site) {
|
public void addSite(final Site site) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteSite(Site site) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.lyndir.masterpassword.gui;
|
package com.lyndir.masterpassword.gui;
|
||||||
|
|
||||||
|
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.*;
|
import com.google.common.collect.*;
|
||||||
@@ -13,6 +15,8 @@ import javax.annotation.Nullable;
|
|||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.event.DocumentEvent;
|
import javax.swing.event.DocumentEvent;
|
||||||
import javax.swing.event.DocumentListener;
|
import javax.swing.event.DocumentListener;
|
||||||
|
import javax.swing.plaf.metal.MetalComboBoxEditor;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -51,6 +55,15 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
|
|||||||
userField.setFont( Res.valueFont().deriveFont( 12f ) );
|
userField.setFont( Res.valueFont().deriveFont( 12f ) );
|
||||||
userField.addItemListener( this );
|
userField.addItemListener( this );
|
||||||
userField.addActionListener( this );
|
userField.addActionListener( this );
|
||||||
|
userField.setEditor( new MetalComboBoxEditor() {
|
||||||
|
@Override
|
||||||
|
protected JTextField createEditorComponent() {
|
||||||
|
JTextField editorComponents = Components.textField();
|
||||||
|
editorComponents.setForeground( Color.red );
|
||||||
|
return editorComponents;
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
add( userField );
|
add( userField );
|
||||||
add( Components.stud() );
|
add( Components.stud() );
|
||||||
|
|
||||||
@@ -94,6 +107,7 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
|
|||||||
return userField.getModel().getElementAt( selectedIndex );
|
return userField.getModel().getElementAt( selectedIndex );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public char[] getMasterPassword() {
|
public char[] getMasterPassword() {
|
||||||
return masterPasswordField.getPassword();
|
return masterPasswordField.getPassword();
|
||||||
@@ -114,6 +128,29 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
|
|||||||
updateUser( true );
|
updateUser( true );
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
setToolTipText( "Add a new user to the list." );
|
||||||
|
}
|
||||||
|
}, new JButton( Res.iconDelete() ) {
|
||||||
|
{
|
||||||
|
addActionListener( new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(final ActionEvent e) {
|
||||||
|
ModelUser deleteUser = getSelectedUser();
|
||||||
|
if (deleteUser == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (JOptionPane.showConfirmDialog( ModelAuthenticationPanel.this, //
|
||||||
|
strf( "Are you sure you want to delete the user and sites remembered for:\n%s.",
|
||||||
|
deleteUser.getFullName() ), //
|
||||||
|
"Delete User", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE ) == JOptionPane.CANCEL_OPTION)
|
||||||
|
return;
|
||||||
|
|
||||||
|
MPUserFileManager.get().deleteUser( deleteUser.getModel() );
|
||||||
|
userField.setModel( new DefaultComboBoxModel<>( readConfigUsers() ) );
|
||||||
|
updateUser( true );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
setToolTipText( "Delete the selected user." );
|
||||||
}
|
}
|
||||||
}, new JButton( Res.iconQuestion() ) {
|
}, new JButton( Res.iconQuestion() ) {
|
||||||
{
|
{
|
||||||
@@ -121,10 +158,12 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
|
|||||||
@Override
|
@Override
|
||||||
public void actionPerformed(final ActionEvent e) {
|
public void actionPerformed(final ActionEvent e) {
|
||||||
JOptionPane.showMessageDialog( ModelAuthenticationPanel.this, //
|
JOptionPane.showMessageDialog( ModelAuthenticationPanel.this, //
|
||||||
"Reads users and sites from the directory at ~/.mpw.", //
|
strf( "Reads users and sites from the directory at:\n%s",
|
||||||
|
MPUserFileManager.get().getPath().getAbsolutePath() ), //
|
||||||
"Help", JOptionPane.INFORMATION_MESSAGE );
|
"Help", JOptionPane.INFORMATION_MESSAGE );
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
setToolTipText( "More information." );
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|||||||
4
MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelSite.java
Normal file → Executable file
@@ -16,6 +16,10 @@ public class ModelSite extends Site {
|
|||||||
this.model = result.getSite();
|
this.model = result.getSite();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MPSite getModel() {
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
public String getSiteName() {
|
public String getSiteName() {
|
||||||
return model.getSiteName();
|
return model.getSiteName();
|
||||||
}
|
}
|
||||||
|
|||||||
14
MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelUser.java
Normal file → Executable file
@@ -42,7 +42,7 @@ public class ModelUser extends User {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setAvatar(final int avatar) {
|
public void setAvatar(final int avatar) {
|
||||||
model.setAvatar( avatar % Res.avatars() );
|
model.setAvatar(avatar % Res.avatars());
|
||||||
MPUserFileManager.get().save();
|
MPUserFileManager.get().save();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,8 +67,8 @@ public class ModelUser extends User {
|
|||||||
return FluentIterable.from( model.findSitesByName( query ) ).transform( new Function<MPSiteResult, Site>() {
|
return FluentIterable.from( model.findSitesByName( query ) ).transform( new Function<MPSiteResult, Site>() {
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public Site apply(@Nullable final MPSiteResult result) {
|
public Site apply(@Nullable final MPSiteResult site) {
|
||||||
return new ModelSite( Preconditions.checkNotNull( result ) );
|
return new ModelSite( Preconditions.checkNotNull( site ) );
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
@@ -80,6 +80,14 @@ public class ModelUser extends User {
|
|||||||
MPUserFileManager.get().save();
|
MPUserFileManager.get().save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteSite(Site site) {
|
||||||
|
if (site instanceof ModelSite) {
|
||||||
|
model.deleteSite(((ModelSite) site).getModel());
|
||||||
|
MPUserFileManager.get().save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean keySaved() {
|
public boolean keySaved() {
|
||||||
// TODO
|
// TODO
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
121
MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/PasswordFrame.java
Normal file → Executable file
@@ -1,9 +1,13 @@
|
|||||||
package com.lyndir.masterpassword.gui;
|
package com.lyndir.masterpassword.gui;
|
||||||
|
|
||||||
|
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.ifNotNullElse;
|
||||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
|
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
|
||||||
|
|
||||||
|
import com.google.common.collect.FluentIterable;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.util.concurrent.*;
|
import com.google.common.util.concurrent.*;
|
||||||
|
import com.lyndir.lhunath.opal.system.util.NNSupplier;
|
||||||
|
import com.lyndir.lhunath.opal.system.util.PredicateNN;
|
||||||
import com.lyndir.masterpassword.*;
|
import com.lyndir.masterpassword.*;
|
||||||
import com.lyndir.masterpassword.gui.util.Components;
|
import com.lyndir.masterpassword.gui.util.Components;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
@@ -24,7 +28,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
|||||||
private final User user;
|
private final User user;
|
||||||
private final Components.GradientPanel root;
|
private final Components.GradientPanel root;
|
||||||
private final JTextField siteNameField;
|
private final JTextField siteNameField;
|
||||||
private final JButton siteAddButton;
|
private final JButton siteActionButton;
|
||||||
private final JComboBox<MPSiteType> siteTypeField;
|
private final JComboBox<MPSiteType> siteTypeField;
|
||||||
private final JComboBox<MasterKey.Version> siteVersionField;
|
private final JComboBox<MasterKey.Version> siteVersionField;
|
||||||
private final JSpinner siteCounterField;
|
private final JSpinner siteCounterField;
|
||||||
@@ -33,8 +37,10 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
|||||||
private final JCheckBox maskPasswordField;
|
private final JCheckBox maskPasswordField;
|
||||||
private final char passwordEchoChar;
|
private final char passwordEchoChar;
|
||||||
private final Font passwordEchoFont;
|
private final Font passwordEchoFont;
|
||||||
private boolean updatingUI;
|
|
||||||
|
@Nullable
|
||||||
private Site currentSite;
|
private Site currentSite;
|
||||||
|
private boolean updatingUI;
|
||||||
|
|
||||||
public PasswordFrame(User user)
|
public PasswordFrame(User user)
|
||||||
throws HeadlessException {
|
throws HeadlessException {
|
||||||
@@ -42,7 +48,8 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
|||||||
this.user = user;
|
this.user = user;
|
||||||
|
|
||||||
setDefaultCloseOperation( DISPOSE_ON_CLOSE );
|
setDefaultCloseOperation( DISPOSE_ON_CLOSE );
|
||||||
setContentPane( root = Components.gradientPanel( new BorderLayout( 20, 20 ), Res.colors().frameBg() ) );
|
setContentPane( root = Components.gradientPanel( new FlowLayout(), Res.colors().frameBg() ) );
|
||||||
|
root.setLayout( new BoxLayout( root, BoxLayout.PAGE_AXIS ) );
|
||||||
root.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) );
|
root.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) );
|
||||||
|
|
||||||
// Site
|
// Site
|
||||||
@@ -50,52 +57,55 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
|||||||
sitePanel.setOpaque( true );
|
sitePanel.setOpaque( true );
|
||||||
sitePanel.setBackground( Res.colors().controlBg() );
|
sitePanel.setBackground( Res.colors().controlBg() );
|
||||||
sitePanel.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) );
|
sitePanel.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) );
|
||||||
add( Components.borderPanel( sitePanel, BorderFactory.createRaisedBevelBorder(), Res.colors().frameBg() ), BorderLayout.CENTER );
|
root.add( Components.borderPanel( sitePanel, BorderFactory.createRaisedBevelBorder(), Res.colors().frameBg() ) );
|
||||||
|
|
||||||
// User
|
// User
|
||||||
sitePanel.add( Components.label( strf( "Generating passwords for: %s", user.getFullName() ), JLabel.CENTER ) );
|
sitePanel.add( Components.label( strf( "Generating passwords for: %s", user.getFullName() ), SwingConstants.CENTER ) );
|
||||||
sitePanel.add( Components.stud() );
|
sitePanel.add( Components.stud() );
|
||||||
|
|
||||||
// Site Name
|
// Site Name
|
||||||
sitePanel.add( Components.label( "Site Name:" ) );
|
sitePanel.add(Components.label("Site Name:"));
|
||||||
JComponent siteControls = Components.boxLayout( BoxLayout.LINE_AXIS, //
|
JComponent siteControls = Components.boxLayout( BoxLayout.LINE_AXIS, //
|
||||||
siteNameField = Components.textField(), Components.stud(),
|
siteNameField = Components.textField(), Components.stud(),
|
||||||
siteAddButton = Components.button( "Add Site" ) );
|
siteActionButton = Components.button( "Add Site" ) );
|
||||||
siteNameField.getDocument().addDocumentListener( this );
|
siteNameField.getDocument().addDocumentListener(this);
|
||||||
siteNameField.addActionListener( new ActionListener() {
|
siteNameField.addActionListener(new ActionListener() {
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(final ActionEvent e) {
|
public void actionPerformed(final ActionEvent e) {
|
||||||
Futures.addCallback( updatePassword(), new FutureCallback<String>() {
|
Futures.addCallback(updatePassword(true), new FutureCallback<String>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(@Nullable final String sitePassword) {
|
public void onSuccess(@Nullable final String sitePassword) {
|
||||||
StringSelection clipboardContents = new StringSelection( sitePassword );
|
StringSelection clipboardContents = new StringSelection(sitePassword);
|
||||||
Toolkit.getDefaultToolkit().getSystemClipboard().setContents( clipboardContents, null );
|
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(clipboardContents, null);
|
||||||
|
|
||||||
SwingUtilities.invokeLater( new Runnable() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
passwordField.setText( null );
|
passwordField.setText(null);
|
||||||
siteNameField.setText( null );
|
siteNameField.setText(null);
|
||||||
|
|
||||||
dispatchEvent( new WindowEvent( PasswordFrame.this, WindowEvent.WINDOW_CLOSING ) );
|
dispatchEvent(new WindowEvent(PasswordFrame.this, WindowEvent.WINDOW_CLOSING));
|
||||||
}
|
}
|
||||||
} );
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(final Throwable t) {
|
public void onFailure(final Throwable t) {
|
||||||
}
|
}
|
||||||
} );
|
});
|
||||||
}
|
}
|
||||||
} );
|
});
|
||||||
siteAddButton.setVisible( false );
|
siteActionButton.addActionListener(new ActionListener() {
|
||||||
siteAddButton.addActionListener( new ActionListener() {
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(final ActionEvent e) {
|
public void actionPerformed(final ActionEvent e) {
|
||||||
PasswordFrame.this.user.addSite( currentSite );
|
if (currentSite instanceof ModelSite)
|
||||||
siteAddButton.setVisible( false );
|
PasswordFrame.this.user.deleteSite(currentSite);
|
||||||
|
else
|
||||||
|
PasswordFrame.this.user.addSite(currentSite);
|
||||||
|
|
||||||
|
updatePassword(true);
|
||||||
}
|
}
|
||||||
} );
|
});
|
||||||
sitePanel.add( siteControls );
|
sitePanel.add( siteControls );
|
||||||
sitePanel.add( Components.stud() );
|
sitePanel.add( Components.stud() );
|
||||||
|
|
||||||
@@ -114,7 +124,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
|||||||
siteTypeField.addItemListener( new ItemListener() {
|
siteTypeField.addItemListener( new ItemListener() {
|
||||||
@Override
|
@Override
|
||||||
public void itemStateChanged(final ItemEvent e) {
|
public void itemStateChanged(final ItemEvent e) {
|
||||||
updatePassword();
|
updatePassword(true);
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
@@ -124,7 +134,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
|||||||
siteVersionField.addItemListener( new ItemListener() {
|
siteVersionField.addItemListener( new ItemListener() {
|
||||||
@Override
|
@Override
|
||||||
public void itemStateChanged(final ItemEvent e) {
|
public void itemStateChanged(final ItemEvent e) {
|
||||||
updatePassword();
|
updatePassword(true);
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
@@ -133,7 +143,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
|||||||
siteCounterField.addChangeListener( new ChangeListener() {
|
siteCounterField.addChangeListener( new ChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void stateChanged(final ChangeEvent e) {
|
public void stateChanged(final ChangeEvent e) {
|
||||||
updatePassword();
|
updatePassword(true);
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
@@ -149,25 +159,27 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
|||||||
} );
|
} );
|
||||||
|
|
||||||
// Password
|
// Password
|
||||||
passwordField = new JPasswordField();
|
passwordField = Components.passwordField();
|
||||||
passwordField.setAlignmentX( Component.CENTER_ALIGNMENT );
|
passwordField.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||||
passwordField.setEditable( false );
|
passwordField.setHorizontalAlignment(JTextField.CENTER);
|
||||||
passwordField.setHorizontalAlignment( JTextField.CENTER );
|
passwordField.putClientProperty("JPasswordField.cutCopyAllowed", true);
|
||||||
passwordField.putClientProperty( "JPasswordField.cutCopyAllowed", true );
|
passwordField.setEditable(false);
|
||||||
|
passwordField.setBackground(null);
|
||||||
passwordField.setBorder( null );
|
passwordField.setBorder( null );
|
||||||
passwordEchoChar = passwordField.getEchoChar();
|
passwordEchoChar = passwordField.getEchoChar();
|
||||||
passwordEchoFont = passwordField.getFont().deriveFont( 40f );
|
passwordEchoFont = passwordField.getFont().deriveFont( 40f );
|
||||||
updateMask();
|
updateMask();
|
||||||
|
|
||||||
// Tip
|
// Tip
|
||||||
tipLabel = Components.label( " ", JLabel.CENTER );
|
tipLabel = Components.label( " ", SwingConstants.CENTER );
|
||||||
tipLabel.setAlignmentX( Component.CENTER_ALIGNMENT );
|
tipLabel.setAlignmentX( Component.CENTER_ALIGNMENT );
|
||||||
JPanel passwordContainer = Components.boxLayout( BoxLayout.PAGE_AXIS, maskPasswordField, passwordField, tipLabel );
|
JPanel passwordContainer = Components.boxLayout( BoxLayout.PAGE_AXIS, maskPasswordField, Box.createGlue(), passwordField, Box.createGlue(), tipLabel );
|
||||||
passwordContainer.setOpaque( true );
|
passwordContainer.setOpaque( true );
|
||||||
passwordContainer.setBackground( Color.white );
|
passwordContainer.setBackground( Color.white );
|
||||||
passwordContainer.setBorder( BorderFactory.createEmptyBorder( 8, 8, 8, 8 ) );
|
passwordContainer.setBorder( BorderFactory.createEmptyBorder( 8, 8, 8, 8 ) );
|
||||||
add( Components.borderPanel( passwordContainer, BorderFactory.createLoweredSoftBevelBorder(), Res.colors().frameBg() ),
|
root.add( Box.createVerticalStrut( 8 ) );
|
||||||
BorderLayout.SOUTH );
|
root.add( Components.borderPanel( passwordContainer, BorderFactory.createLoweredSoftBevelBorder(), Res.colors().frameBg() ),
|
||||||
|
BorderLayout.SOUTH );
|
||||||
|
|
||||||
pack();
|
pack();
|
||||||
setMinimumSize( new Dimension( Math.max( 600, getPreferredSize().width ), Math.max( 300, getPreferredSize().height ) ) );
|
setMinimumSize( new Dimension( Math.max( 600, getPreferredSize().width ), Math.max( 300, getPreferredSize().height ) ) );
|
||||||
@@ -183,25 +195,33 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private ListenableFuture<String> updatePassword() {
|
private ListenableFuture<String> updatePassword(boolean allowNameCompletion) {
|
||||||
|
|
||||||
final String siteNameQuery = siteNameField.getText();
|
final String siteNameQuery = siteNameField.getText();
|
||||||
if (updatingUI)
|
if (updatingUI)
|
||||||
return Futures.immediateCancelledFuture();
|
return Futures.immediateCancelledFuture();
|
||||||
if (siteNameQuery == null || siteNameQuery.isEmpty() || !user.isKeyAvailable()) {
|
if (siteNameQuery == null || siteNameQuery.isEmpty() || !user.isKeyAvailable()) {
|
||||||
|
siteActionButton.setVisible(false);
|
||||||
tipLabel.setText( null );
|
tipLabel.setText( null );
|
||||||
passwordField.setText( null );
|
passwordField.setText( null );
|
||||||
return Futures.immediateCancelledFuture();
|
return Futures.immediateCancelledFuture();
|
||||||
}
|
}
|
||||||
|
|
||||||
final MPSiteType siteType = siteTypeField.getModel().getElementAt( siteTypeField.getSelectedIndex() );
|
final MPSiteType siteType = siteTypeField.getModel().getElementAt(siteTypeField.getSelectedIndex());
|
||||||
final MasterKey.Version siteVersion = siteVersionField.getItemAt( siteVersionField.getSelectedIndex() );
|
final MasterKey.Version siteVersion = siteVersionField.getItemAt(siteVersionField.getSelectedIndex());
|
||||||
final int siteCounter = (Integer) siteCounterField.getValue();
|
final int siteCounter = (Integer) siteCounterField.getValue();
|
||||||
final Site site = currentSite != null && currentSite.getSiteName().equals( siteNameQuery )? currentSite
|
|
||||||
: Iterables.getFirst( user.findSitesByName( siteNameQuery ),
|
Iterable<Site> siteResults = user.findSitesByName(siteNameQuery);
|
||||||
new IncognitoSite( siteNameQuery, siteType, siteCounter, siteVersion ) );
|
if (!allowNameCompletion)
|
||||||
assert site != null;
|
siteResults = FluentIterable.from(siteResults).filter(new PredicateNN<Site>() {
|
||||||
if (site == currentSite) {
|
@Override
|
||||||
|
public boolean apply(Site input) {
|
||||||
|
return siteNameQuery.equals(input.getSiteName());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
final Site site = Iterables.getFirst(siteResults,
|
||||||
|
new IncognitoSite(siteNameQuery, siteType, siteCounter, siteVersion) );
|
||||||
|
if (currentSite != null && site.getSiteName().equals(currentSite.getSiteName())) {
|
||||||
site.setSiteType( siteType );
|
site.setSiteType( siteType );
|
||||||
site.setAlgorithmVersion( siteVersion );
|
site.setAlgorithmVersion( siteVersion );
|
||||||
site.setSiteCounter( siteCounter );
|
site.setSiteCounter( siteCounter );
|
||||||
@@ -223,8 +243,12 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
|||||||
public void run() {
|
public void run() {
|
||||||
updatingUI = true;
|
updatingUI = true;
|
||||||
currentSite = site;
|
currentSite = site;
|
||||||
siteAddButton.setVisible( user instanceof ModelUser && !(currentSite instanceof ModelSite) );
|
siteActionButton.setVisible(user instanceof ModelUser);
|
||||||
siteTypeField.setSelectedItem( currentSite.getSiteType() );
|
if (currentSite instanceof ModelSite)
|
||||||
|
siteActionButton.setText("Delete Site");
|
||||||
|
else
|
||||||
|
siteActionButton.setText("Add Site");
|
||||||
|
siteTypeField.setSelectedItem(currentSite.getSiteType());
|
||||||
siteVersionField.setSelectedItem( currentSite.getAlgorithmVersion() );
|
siteVersionField.setSelectedItem( currentSite.getAlgorithmVersion() );
|
||||||
siteCounterField.setValue( currentSite.getSiteCounter() );
|
siteCounterField.setValue( currentSite.getSiteCounter() );
|
||||||
siteNameField.setText( currentSite.getSiteName() );
|
siteNameField.setText( currentSite.getSiteName() );
|
||||||
@@ -248,15 +272,16 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void insertUpdate(final DocumentEvent e) {
|
public void insertUpdate(final DocumentEvent e) {
|
||||||
updatePassword();
|
updatePassword(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeUpdate(final DocumentEvent e) {
|
public void removeUpdate(final DocumentEvent e) {
|
||||||
|
updatePassword(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void changedUpdate(final DocumentEvent e) {
|
public void changedUpdate(final DocumentEvent e) {
|
||||||
updatePassword();
|
updatePassword(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,6 +85,10 @@ public abstract class Res {
|
|||||||
return new RetinaIcon( Resources.getResource( "media/icon_add@2x.png" ) );
|
return new RetinaIcon( Resources.getResource( "media/icon_add@2x.png" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Icon iconDelete() {
|
||||||
|
return new RetinaIcon( Resources.getResource( "media/icon_delete@2x.png" ) );
|
||||||
|
}
|
||||||
|
|
||||||
public static Icon iconQuestion() {
|
public static Icon iconQuestion() {
|
||||||
return new RetinaIcon( Resources.getResource( "media/icon_question@2x.png" ) );
|
return new RetinaIcon( Resources.getResource( "media/icon_question@2x.png" ) );
|
||||||
}
|
}
|
||||||
@@ -97,6 +101,10 @@ public abstract class Res {
|
|||||||
return 19;
|
return 19;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Font emoticonsFont() {
|
||||||
|
return emoticonsRegular();
|
||||||
|
}
|
||||||
|
|
||||||
public static Font controlFont() {
|
public static Font controlFont() {
|
||||||
return arimoRegular();
|
return arimoRegular();
|
||||||
}
|
}
|
||||||
@@ -109,6 +117,10 @@ public abstract class Res {
|
|||||||
return sourceSansProBlack();
|
return sourceSansProBlack();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Font emoticonsRegular() {
|
||||||
|
return font( "fonts/Emoticons-Regular.otf" );
|
||||||
|
}
|
||||||
|
|
||||||
public static Font sourceCodeProRegular() {
|
public static Font sourceCodeProRegular() {
|
||||||
return font( "fonts/SourceCodePro-Regular.otf" );
|
return font( "fonts/SourceCodePro-Regular.otf" );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ package com.lyndir.masterpassword.gui;
|
|||||||
|
|
||||||
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
|
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
|
||||||
|
|
||||||
|
import com.lyndir.masterpassword.MPIdenticon;
|
||||||
import com.lyndir.masterpassword.gui.util.Components;
|
import com.lyndir.masterpassword.gui.util.Components;
|
||||||
import com.lyndir.masterpassword.model.IncorrectMasterPasswordException;
|
import com.lyndir.masterpassword.model.IncorrectMasterPasswordException;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.*;
|
import java.awt.event.*;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
|
|
||||||
@@ -16,6 +18,7 @@ public class UnlockFrame extends JFrame {
|
|||||||
|
|
||||||
private final SignInCallback signInCallback;
|
private final SignInCallback signInCallback;
|
||||||
private final Components.GradientPanel root;
|
private final Components.GradientPanel root;
|
||||||
|
private final JLabel identiconLabel;
|
||||||
private final JButton signInButton;
|
private final JButton signInButton;
|
||||||
private final JPanel authenticationContainer;
|
private final JPanel authenticationContainer;
|
||||||
private AuthenticationPanel authenticationPanel;
|
private AuthenticationPanel authenticationPanel;
|
||||||
@@ -28,20 +31,39 @@ public class UnlockFrame extends JFrame {
|
|||||||
this.signInCallback = signInCallback;
|
this.signInCallback = signInCallback;
|
||||||
|
|
||||||
setDefaultCloseOperation( DISPOSE_ON_CLOSE );
|
setDefaultCloseOperation( DISPOSE_ON_CLOSE );
|
||||||
setContentPane( root = Components.gradientPanel( new BorderLayout( 20, 20 ), Res.colors().frameBg() ) );
|
addWindowFocusListener( new WindowAdapter() {
|
||||||
root.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) );
|
@Override
|
||||||
|
public void windowGainedFocus(WindowEvent e) {
|
||||||
|
root.setGradientColor( Res.colors().frameBg() );
|
||||||
|
}
|
||||||
|
|
||||||
authenticationContainer = Components.boxLayout( BoxLayout.PAGE_AXIS );
|
@Override
|
||||||
authenticationContainer.setOpaque( true );
|
public void windowLostFocus(WindowEvent e) {
|
||||||
authenticationContainer.setBackground( Res.colors().controlBg() );
|
root.setGradientColor( Color.RED );
|
||||||
authenticationContainer.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) );
|
}
|
||||||
add( Components.borderPanel( authenticationContainer, BorderFactory.createRaisedBevelBorder(), Res.colors().frameBg() ) );
|
} );
|
||||||
|
|
||||||
// Sign In
|
// Sign In
|
||||||
JPanel signInBox = Components.boxLayout( BoxLayout.LINE_AXIS, Box.createGlue(), signInButton = Components.button( "Sign In" ),
|
JPanel signInBox = Components.boxLayout( BoxLayout.LINE_AXIS, Box.createGlue(), signInButton = Components.button( "Sign In" ),
|
||||||
Box.createGlue() );
|
Box.createGlue() );
|
||||||
signInBox.setBackground( null );
|
signInBox.setBackground( null );
|
||||||
root.add( signInBox, BorderLayout.SOUTH );
|
|
||||||
|
setContentPane( root = Components.gradientPanel( new FlowLayout(), Res.colors().frameBg() ) );
|
||||||
|
root.setLayout( new BoxLayout( root, BoxLayout.PAGE_AXIS ) );
|
||||||
|
root.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) );
|
||||||
|
root.add( Components.borderPanel( authenticationContainer = Components.boxLayout( BoxLayout.PAGE_AXIS ),
|
||||||
|
BorderFactory.createRaisedBevelBorder(), Res.colors().frameBg() ) );
|
||||||
|
root.add( Box.createVerticalStrut( 8 ) );
|
||||||
|
root.add( identiconLabel = Components.label( " ", SwingConstants.CENTER ) );
|
||||||
|
root.add( Box.createVerticalStrut( 8 ) );
|
||||||
|
root.add( signInBox );
|
||||||
|
|
||||||
|
authenticationContainer.setOpaque( true );
|
||||||
|
authenticationContainer.setBackground( Res.colors().controlBg() );
|
||||||
|
authenticationContainer.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) );
|
||||||
|
identiconLabel.setFont( Res.emoticonsFont().deriveFont( 14.f ) );
|
||||||
|
identiconLabel.setToolTipText(
|
||||||
|
"A representation of your identity across all Master Password apps.\nIt should always be the same." );
|
||||||
signInButton.addActionListener( new AbstractAction() {
|
signInButton.addActionListener( new AbstractAction() {
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(final ActionEvent e) {
|
public void actionPerformed(final ActionEvent e) {
|
||||||
@@ -74,6 +96,7 @@ public class UnlockFrame extends JFrame {
|
|||||||
authenticationContainer.add( Components.stud() );
|
authenticationContainer.add( Components.stud() );
|
||||||
|
|
||||||
final JCheckBox incognitoCheckBox = Components.checkBox( "Incognito" );
|
final JCheckBox incognitoCheckBox = Components.checkBox( "Incognito" );
|
||||||
|
incognitoCheckBox.setToolTipText( "Log in without saving any information." );
|
||||||
incognitoCheckBox.setSelected( incognito );
|
incognitoCheckBox.setSelected( incognito );
|
||||||
incognitoCheckBox.addItemListener( new ItemListener() {
|
incognitoCheckBox.addItemListener( new ItemListener() {
|
||||||
@Override
|
@Override
|
||||||
@@ -91,10 +114,11 @@ public class UnlockFrame extends JFrame {
|
|||||||
JComponent toolsPanel = Components.boxLayout( BoxLayout.LINE_AXIS, incognitoCheckBox, Box.createGlue() );
|
JComponent toolsPanel = Components.boxLayout( BoxLayout.LINE_AXIS, incognitoCheckBox, Box.createGlue() );
|
||||||
authenticationContainer.add( toolsPanel );
|
authenticationContainer.add( toolsPanel );
|
||||||
for (JButton button : authenticationPanel.getButtons()) {
|
for (JButton button : authenticationPanel.getButtons()) {
|
||||||
|
toolsPanel.add( button );
|
||||||
|
button.setBorder( BorderFactory.createEmptyBorder() );
|
||||||
button.setMargin( new Insets( 0, 0, 0, 0 ) );
|
button.setMargin( new Insets( 0, 0, 0, 0 ) );
|
||||||
button.setAlignmentX( RIGHT_ALIGNMENT );
|
button.setAlignmentX( RIGHT_ALIGNMENT );
|
||||||
button.setBorder( null );
|
button.setContentAreaFilled( false );
|
||||||
toolsPanel.add( button );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
checkSignIn();
|
checkSignIn();
|
||||||
@@ -109,13 +133,24 @@ public class UnlockFrame extends JFrame {
|
|||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateUser(User user) {
|
void updateUser(@Nullable User user) {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
checkSignIn();
|
checkSignIn();
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean checkSignIn() {
|
boolean checkSignIn() {
|
||||||
boolean enabled = user != null && !user.getFullName().isEmpty() && authenticationPanel.getMasterPassword().length > 0;
|
String fullName = user == null? "": user.getFullName();
|
||||||
|
char[] masterPassword = authenticationPanel.getMasterPassword();
|
||||||
|
boolean enabled = !fullName.isEmpty() && masterPassword.length > 0;
|
||||||
|
|
||||||
|
if (fullName.isEmpty() || masterPassword.length == 0)
|
||||||
|
identiconLabel.setText( " " );
|
||||||
|
else {
|
||||||
|
MPIdenticon identicon = new MPIdenticon( fullName, masterPassword );
|
||||||
|
identiconLabel.setText( identicon.getText() );
|
||||||
|
identiconLabel.setForeground( identicon.getColor().getAWTColor( MPIdenticon.BackgroundMode.DARK ) );
|
||||||
|
}
|
||||||
|
|
||||||
signInButton.setEnabled( enabled );
|
signInButton.setEnabled( enabled );
|
||||||
|
|
||||||
return enabled;
|
return enabled;
|
||||||
@@ -158,7 +193,6 @@ public class UnlockFrame extends JFrame {
|
|||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|||||||
2
MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/User.java
Normal file → Executable file
@@ -62,6 +62,8 @@ public abstract class User {
|
|||||||
|
|
||||||
public abstract void addSite(final Site site);
|
public abstract void addSite(final Site site);
|
||||||
|
|
||||||
|
public abstract void deleteSite(final Site site);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(final Object obj) {
|
public boolean equals(final Object obj) {
|
||||||
return this == obj || obj instanceof User && Objects.equals( getFullName(), ((User) obj).getFullName() );
|
return this == obj || obj instanceof User && Objects.equals( getFullName(), ((User) obj).getFullName() );
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
package com.lyndir.masterpassword.gui;
|
package com.lyndir.masterpassword.gui.platform.mac;
|
||||||
|
|
||||||
import com.apple.eawt.*;
|
import com.apple.eawt.*;
|
||||||
|
import com.lyndir.masterpassword.gui.GUI;
|
||||||
|
import com.lyndir.masterpassword.gui.PasswordFrame;
|
||||||
|
import com.lyndir.masterpassword.gui.User;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
|
|
||||||
@@ -116,6 +116,7 @@ public abstract class Components {
|
|||||||
((DefaultEditor) getEditor()).getTextField().setBorder( editorBorder );
|
((DefaultEditor) getEditor()).getTextField().setBorder( editorBorder );
|
||||||
setAlignmentX( LEFT_ALIGNMENT );
|
setAlignmentX( LEFT_ALIGNMENT );
|
||||||
setAlignmentY( BOTTOM_ALIGNMENT );
|
setAlignmentY( BOTTOM_ALIGNMENT );
|
||||||
|
setBorder( null );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -125,12 +126,21 @@ public abstract class Components {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JLabel label(final String label) {
|
public static JLabel label(@Nullable String label) {
|
||||||
return label( label, JLabel.LEADING );
|
return label( label, SwingConstants.LEADING );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JLabel label(final String label, final int alignment) {
|
/**
|
||||||
return new JLabel( label, alignment ) {
|
* @param horizontalAlignment One of the following constants
|
||||||
|
* defined in <code>SwingConstants</code>:
|
||||||
|
* <code>LEFT</code>,
|
||||||
|
* <code>CENTER</code>,
|
||||||
|
* <code>RIGHT</code>,
|
||||||
|
* <code>LEADING</code> or
|
||||||
|
* <code>TRAILING</code>.
|
||||||
|
*/
|
||||||
|
public static JLabel label(@Nullable final String label, final int horizontalAlignment) {
|
||||||
|
return new JLabel( label, horizontalAlignment ) {
|
||||||
{
|
{
|
||||||
setFont( Res.controlFont().deriveFont( 12f ) );
|
setFont( Res.controlFont().deriveFont( 12f ) );
|
||||||
setAlignmentX( LEFT_ALIGNMENT );
|
setAlignmentX( LEFT_ALIGNMENT );
|
||||||
@@ -148,6 +158,7 @@ public abstract class Components {
|
|||||||
return new JCheckBox( label ) {
|
return new JCheckBox( label ) {
|
||||||
{
|
{
|
||||||
setFont( Res.controlFont().deriveFont( 12f ) );
|
setFont( Res.controlFont().deriveFont( 12f ) );
|
||||||
|
setBackground( null );
|
||||||
setAlignmentX( LEFT_ALIGNMENT );
|
setAlignmentX( LEFT_ALIGNMENT );
|
||||||
setAlignmentY( BOTTOM_ALIGNMENT );
|
setAlignmentY( BOTTOM_ALIGNMENT );
|
||||||
}
|
}
|
||||||
@@ -162,9 +173,14 @@ public abstract class Components {
|
|||||||
public static <M> JComboBox<M> comboBox(final ComboBoxModel<M> model) {
|
public static <M> JComboBox<M> comboBox(final ComboBoxModel<M> model) {
|
||||||
return new JComboBox<M>( model ) {
|
return new JComboBox<M>( model ) {
|
||||||
{
|
{
|
||||||
|
// CompoundBorder editorBorder = BorderFactory.createCompoundBorder(
|
||||||
|
// BorderFactory.createLineBorder( Res.colors().controlBorder(), 1, true ),
|
||||||
|
// BorderFactory.createEmptyBorder( 4, 4, 4, 4 ) );
|
||||||
|
// ((JComponent) ((BasicComboBoxEditor) getEditor()).getEditorComponent()).setBorder(editorBorder);
|
||||||
setFont( Res.controlFont().deriveFont( 12f ) );
|
setFont( Res.controlFont().deriveFont( 12f ) );
|
||||||
setAlignmentX( LEFT_ALIGNMENT );
|
setAlignmentX( LEFT_ALIGNMENT );
|
||||||
setAlignmentY( BOTTOM_ALIGNMENT );
|
setAlignmentY( BOTTOM_ALIGNMENT );
|
||||||
|
// setBorder(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -195,14 +211,17 @@ public abstract class Components {
|
|||||||
|
|
||||||
public void setGradientColor(@Nullable final Color gradientColor) {
|
public void setGradientColor(@Nullable final Color gradientColor) {
|
||||||
this.gradientColor = gradientColor;
|
this.gradientColor = gradientColor;
|
||||||
|
revalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doLayout() {
|
public void doLayout() {
|
||||||
super.doLayout();
|
super.doLayout();
|
||||||
|
|
||||||
if (gradientColor != null)
|
if (gradientColor != null) {
|
||||||
paint = new GradientPaint( new Point( 0, 0 ), gradientColor, new Point( getWidth(), getHeight() ), gradientColor.darker() );
|
paint = new GradientPaint( new Point( 0, 0 ), gradientColor, new Point( getWidth(), getHeight() ), gradientColor.darker() );
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
@@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.lyndir.masterpassword</groupId>
|
<groupId>com.lyndir.masterpassword</groupId>
|
||||||
<artifactId>masterpassword</artifactId>
|
<artifactId>masterpassword</artifactId>
|
||||||
<version>2.2</version>
|
<version>2.3</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<name>Master Password Site Model</name>
|
<name>Master Password Site Model</name>
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.lyndir.masterpassword</groupId>
|
<groupId>com.lyndir.masterpassword</groupId>
|
||||||
<artifactId>masterpassword-algorithm</artifactId>
|
<artifactId>masterpassword-algorithm</artifactId>
|
||||||
<version>2.2</version>
|
<version>2.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
4
MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPUser.java
Normal file → Executable file
@@ -59,6 +59,10 @@ public class MPUser implements Comparable<MPUser> {
|
|||||||
sites.add( site );
|
sites.add( site );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void deleteSite(final MPSite site) {
|
||||||
|
sites.remove( site );
|
||||||
|
}
|
||||||
|
|
||||||
public String getFullName() {
|
public String getFullName() {
|
||||||
return fullName;
|
return fullName;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import com.google.common.collect.*;
|
|||||||
import com.google.common.io.CharSink;
|
import com.google.common.io.CharSink;
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.util.SortedSet;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
|
||||||
@@ -15,7 +16,7 @@ public class MPUserFileManager extends MPUserManager {
|
|||||||
|
|
||||||
@SuppressWarnings("UnusedDeclaration")
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
private static final Logger logger = Logger.get( MPUserFileManager.class );
|
private static final Logger logger = Logger.get( MPUserFileManager.class );
|
||||||
private static final File mpwd = new File( System.getProperty( "user.home" ), ".mpw.d" );
|
private static final File mpwd = new File( System.getProperty( "user.home" ), ".mpw.d" );
|
||||||
private static final MPUserFileManager instance;
|
private static final MPUserFileManager instance;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@@ -70,19 +71,53 @@ public class MPUserFileManager extends MPUserManager {
|
|||||||
} ).filter( Predicates.notNull() );
|
} ).filter( Predicates.notNull() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addUser(final MPUser user) {
|
||||||
|
super.addUser( user );
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteUser(final MPUser user) {
|
||||||
|
super.deleteUser( user );
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the current user state to disk.
|
||||||
|
*/
|
||||||
public void save() {
|
public void save() {
|
||||||
|
// Save existing users.
|
||||||
for (final MPUser user : getUsers())
|
for (final MPUser user : getUsers())
|
||||||
try {
|
try {
|
||||||
new CharSink() {
|
new CharSink() {
|
||||||
@Override
|
@Override
|
||||||
public Writer openStream()
|
public Writer openStream()
|
||||||
throws IOException {
|
throws IOException {
|
||||||
return new FileWriter( new File(userFilesDirectory, user.getFullName() + ".mpsites" ) );
|
return new FileWriter( new File( userFilesDirectory, user.getFullName() + ".mpsites" ) );
|
||||||
}
|
}
|
||||||
}.write( MPSiteMarshaller.marshallSafe( user ).getExport() );
|
}.write( MPSiteMarshaller.marshallSafe( user ).getExport() );
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
logger.err( e, "Unable to save sites for user: %s", user );
|
logger.err( e, "Unable to save sites for user: %s", user );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove deleted users.
|
||||||
|
for (File userFile : userFilesDirectory.listFiles( new FilenameFilter() {
|
||||||
|
@Override
|
||||||
|
public boolean accept(final File dir, final String name) {
|
||||||
|
return name.endsWith( ".mpsites" );
|
||||||
|
}
|
||||||
|
} ))
|
||||||
|
if (getUserNamed( userFile.getName().replaceFirst( "\\.mpsites$", "" ) ) == null)
|
||||||
|
if (!userFile.delete())
|
||||||
|
logger.err( "Couldn't delete file: %s", userFile );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The location on the file system where the user models are stored.
|
||||||
|
*/
|
||||||
|
public File getPath() {
|
||||||
|
return mpwd;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,14 +18,22 @@ public abstract class MPUserManager {
|
|||||||
|
|
||||||
protected MPUserManager(final Iterable<MPUser> users) {
|
protected MPUserManager(final Iterable<MPUser> users) {
|
||||||
for (MPUser user : users)
|
for (MPUser user : users)
|
||||||
addUser( user );
|
usersByName.put( user.getFullName(), user );
|
||||||
}
|
}
|
||||||
|
|
||||||
public SortedSet<MPUser> getUsers() {
|
public SortedSet<MPUser> getUsers() {
|
||||||
return FluentIterable.from( usersByName.values() ).toSortedSet( Ordering.natural() );
|
return FluentIterable.from( usersByName.values() ).toSortedSet( Ordering.natural() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MPUser getUserNamed(String fullName) {
|
||||||
|
return usersByName.get( fullName );
|
||||||
|
}
|
||||||
|
|
||||||
public void addUser(final MPUser user) {
|
public void addUser(final MPUser user) {
|
||||||
usersByName.put( user.getFullName(), user );
|
usersByName.put( user.getFullName(), user );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void deleteUser(final MPUser user) {
|
||||||
|
usersByName.remove( user.getFullName() );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.lyndir.lhunath</groupId>
|
<groupId>com.lyndir.lhunath</groupId>
|
||||||
<artifactId>lyndir</artifactId>
|
<artifactId>lyndir</artifactId>
|
||||||
<version>1.20</version>
|
<version>1.22</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<name>Master Password</name>
|
<name>Master Password</name>
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
<groupId>com.lyndir.masterpassword</groupId>
|
<groupId>com.lyndir.masterpassword</groupId>
|
||||||
<artifactId>masterpassword</artifactId>
|
<artifactId>masterpassword</artifactId>
|
||||||
<version>2.2</version>
|
<version>2.3</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
@@ -26,6 +26,12 @@
|
|||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<profiles>
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>release</id>
|
||||||
|
<modules>
|
||||||
|
<module>masterpassword-android</module>
|
||||||
|
</modules>
|
||||||
|
</profile>
|
||||||
<profile>
|
<profile>
|
||||||
<id>mod:android</id>
|
<id>mod:android</id>
|
||||||
<modules>
|
<modules>
|
||||||
@@ -52,4 +58,7 @@
|
|||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
|
<scm>
|
||||||
|
<tag>2.3</tag>
|
||||||
|
</scm>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -48,9 +48,8 @@ NSString *NSStringFromTimeToCrack(TimeToCrack timeToCrack);
|
|||||||
- (BOOL)tryMigrateUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc;
|
- (BOOL)tryMigrateUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc;
|
||||||
- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit;
|
- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit;
|
||||||
|
|
||||||
- (MPKey *)keyForPassword:(NSString *)password ofUserNamed:(NSString *)userName;
|
|
||||||
- (MPKey *)keyFromKeyData:(NSData *)keyData;
|
|
||||||
- (NSData *)keyIDForKeyData:(NSData *)keyData;
|
- (NSData *)keyIDForKeyData:(NSData *)keyData;
|
||||||
|
- (NSData *)keyDataForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword;
|
||||||
|
|
||||||
- (NSString *)nameOfType:(MPSiteType)type;
|
- (NSString *)nameOfType:(MPSiteType)type;
|
||||||
- (NSString *)shortNameOfType:(MPSiteType)type;
|
- (NSString *)shortNameOfType:(MPSiteType)type;
|
||||||
|
|||||||
@@ -28,8 +28,10 @@
|
|||||||
#define CRACKING_PER_SECOND 2495000000UL
|
#define CRACKING_PER_SECOND 2495000000UL
|
||||||
#define CRACKING_PRICE 350
|
#define CRACKING_PRICE 350
|
||||||
|
|
||||||
|
NSOperationQueue *_mpwQueue = nil;
|
||||||
|
|
||||||
@implementation MPAlgorithmV0 {
|
@implementation MPAlgorithmV0 {
|
||||||
BN_CTX *ctx;
|
BN_CTX *_ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)init {
|
- (id)init {
|
||||||
@@ -37,15 +39,22 @@
|
|||||||
if (!(self = [super init]))
|
if (!(self = [super init]))
|
||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
ctx = BN_CTX_new();
|
_ctx = BN_CTX_new();
|
||||||
|
|
||||||
|
static dispatch_once_t once = 0;
|
||||||
|
dispatch_once( &once, ^{
|
||||||
|
_mpwQueue = [NSOperationQueue new];
|
||||||
|
_mpwQueue.maxConcurrentOperationCount = 1;
|
||||||
|
_mpwQueue.name = @"mpw queue";
|
||||||
|
} );
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
|
|
||||||
BN_CTX_free( ctx );
|
BN_CTX_free( _ctx );
|
||||||
ctx = NULL;
|
_ctx = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MPAlgorithmVersion)version {
|
- (MPAlgorithmVersion)version {
|
||||||
@@ -68,6 +77,19 @@
|
|||||||
return [(id<MPAlgorithm>)other version] == [self version];
|
return [(id<MPAlgorithm>)other version] == [self version];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)mpw_perform:(void ( ^ )(void))operationBlock {
|
||||||
|
|
||||||
|
if ([NSOperationQueue currentQueue] == _mpwQueue) {
|
||||||
|
operationBlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSOperation *operation = [NSBlockOperation blockOperationWithBlock:operationBlock];
|
||||||
|
if ([operation respondsToSelector:@selector( qualityOfService )])
|
||||||
|
operation.qualityOfService = NSQualityOfServiceUserInitiated;
|
||||||
|
[_mpwQueue addOperations:@[ operation ] waitUntilFinished:YES];
|
||||||
|
}
|
||||||
|
|
||||||
- (BOOL)tryMigrateUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc {
|
- (BOOL)tryMigrateUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc {
|
||||||
|
|
||||||
NSError *error = nil;
|
NSError *error = nil;
|
||||||
@@ -89,7 +111,7 @@
|
|||||||
|
|
||||||
- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit {
|
- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit {
|
||||||
|
|
||||||
if (site.version != [self version] - 1)
|
if ([site.algorithm version] != [self version] - 1)
|
||||||
// Only migrate from previous version.
|
// Only migrate from previous version.
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
@@ -101,24 +123,23 @@
|
|||||||
|
|
||||||
// Apply migration.
|
// Apply migration.
|
||||||
site.requiresExplicitMigration = NO;
|
site.requiresExplicitMigration = NO;
|
||||||
site.version = [self version];
|
site.algorithm = self;
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MPKey *)keyForPassword:(NSString *)password ofUserNamed:(NSString *)userName {
|
- (NSData *)keyDataForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword {
|
||||||
|
|
||||||
NSDate *start = [NSDate date];
|
__block NSData *keyData;
|
||||||
uint8_t const *masterKeyBytes = mpw_masterKeyForUser( userName.UTF8String, password.UTF8String, [self version] );
|
[self mpw_perform:^{
|
||||||
MPKey *masterKey = [self keyFromKeyData:[NSData dataWithBytes:masterKeyBytes length:MP_dkLen]];
|
NSDate *start = [NSDate date];
|
||||||
mpw_free( masterKeyBytes, MP_dkLen );
|
uint8_t const *masterKeyBytes = mpw_masterKeyForUser( fullName.UTF8String, masterPassword.UTF8String, [self version] );
|
||||||
trc( @"User: %@, password: %@ derives to key ID: %@ (took %0.2fs)", userName, password, [masterKey.keyID encodeHex],
|
keyData = [NSData dataWithBytes:masterKeyBytes length:MP_dkLen];
|
||||||
-[start timeIntervalSinceNow] );
|
trc( @"User: %@, password: %@ derives to key ID: %@ (took %0.2fs)", //
|
||||||
return masterKey;
|
fullName, masterPassword, [self keyIDForKeyData:keyData], -[start timeIntervalSinceNow] );
|
||||||
}
|
mpw_free( masterKeyBytes, MP_dkLen );
|
||||||
|
}];
|
||||||
|
|
||||||
- (MPKey *)keyFromKeyData:(NSData *)keyData {
|
return keyData;
|
||||||
|
|
||||||
return [[MPKey alloc] initWithKeyData:keyData algorithm:self];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSData *)keyIDForKeyData:(NSData *)keyData {
|
- (NSData *)keyIDForKeyData:(NSData *)keyData {
|
||||||
@@ -322,10 +343,13 @@
|
|||||||
- (NSString *)generateContentForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter
|
- (NSString *)generateContentForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter
|
||||||
variant:(MPSiteVariant)variant context:(NSString *)context usingKey:(MPKey *)key {
|
variant:(MPSiteVariant)variant context:(NSString *)context usingKey:(MPKey *)key {
|
||||||
|
|
||||||
char const *contentBytes = mpw_passwordForSite( key.keyData.bytes, name.UTF8String, type, (uint32_t)counter,
|
__block NSString *content;
|
||||||
variant, context.UTF8String, [self version] );
|
[self mpw_perform:^{
|
||||||
NSString *content = [NSString stringWithCString:contentBytes encoding:NSUTF8StringEncoding];
|
char const *contentBytes = mpw_passwordForSite( [key keyDataForAlgorithm:self].bytes,
|
||||||
mpw_freeString( contentBytes );
|
name.UTF8String, type, (uint32_t)counter, variant, context.UTF8String, [self version] );
|
||||||
|
content = [NSString stringWithCString:contentBytes encoding:NSUTF8StringEncoding];
|
||||||
|
mpw_freeString( contentBytes );
|
||||||
|
}];
|
||||||
|
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
@@ -342,7 +366,7 @@
|
|||||||
|
|
||||||
- (BOOL)savePassword:(NSString *)clearContent toSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
- (BOOL)savePassword:(NSString *)clearContent toSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
||||||
|
|
||||||
NSAssert( [siteKey.keyID isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
||||||
switch (site.type) {
|
switch (site.type) {
|
||||||
case MPSiteTypeGeneratedMaximum:
|
case MPSiteTypeGeneratedMaximum:
|
||||||
case MPSiteTypeGeneratedLong:
|
case MPSiteTypeGeneratedLong:
|
||||||
@@ -363,8 +387,9 @@
|
|||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NSData *encryptionKey = [siteKey keyDataForAlgorithm:self trimmedLength:PearlCryptKeySize];
|
||||||
NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding]
|
NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding]
|
||||||
encryptWithSymmetricKey:[siteKey subKeyOfLength:PearlCryptKeySize].keyData padding:YES];
|
encryptWithSymmetricKey:encryptionKey padding:YES];
|
||||||
if ([((MPStoredSiteEntity *)site).contentObject isEqualToData:encryptedContent])
|
if ([((MPStoredSiteEntity *)site).contentObject isEqualToData:encryptedContent])
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
@@ -378,14 +403,15 @@
|
|||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NSData *encryptionKey = [siteKey keyDataForAlgorithm:self trimmedLength:PearlCryptKeySize];
|
||||||
NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding]
|
NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding]
|
||||||
encryptWithSymmetricKey:[siteKey subKeyOfLength:PearlCryptKeySize].keyData padding:YES];
|
encryptWithSymmetricKey:encryptionKey padding:YES];
|
||||||
NSDictionary *siteQuery = [self queryForDevicePrivateSiteNamed:site.name];
|
NSDictionary *siteQuery = [self queryForDevicePrivateSiteNamed:site.name];
|
||||||
if (!encryptedContent)
|
if (!encryptedContent)
|
||||||
[PearlKeyChain deleteItemForQuery:siteQuery];
|
[PearlKeyChain deleteItemForQuery:siteQuery];
|
||||||
else
|
else
|
||||||
[PearlKeyChain addOrUpdateItemForQuery:siteQuery withAttributes:@{
|
[PearlKeyChain addOrUpdateItemForQuery:siteQuery withAttributes:@{
|
||||||
(__bridge id)kSecValueData : encryptedContent,
|
(__bridge id)kSecValueData : encryptedContent,
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
(__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
|
(__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
|
||||||
#endif
|
#endif
|
||||||
@@ -456,14 +482,14 @@
|
|||||||
|
|
||||||
- (void)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock {
|
- (void)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock {
|
||||||
|
|
||||||
NSAssert( [siteKey.keyID isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
||||||
NSString *name = site.name;
|
NSString *name = site.name;
|
||||||
BOOL loginGenerated = site.loginGenerated && [[MPAppDelegate_Shared get] isFeatureUnlocked:MPProductGenerateLogins];
|
BOOL loginGenerated = site.loginGenerated && [[MPAppDelegate_Shared get] isFeatureUnlocked:MPProductGenerateLogins];
|
||||||
NSString *loginName = loginGenerated? nil: site.loginName;
|
NSString *loginName = loginGenerated? nil: site.loginName;
|
||||||
id<MPAlgorithm> algorithm = nil;
|
id<MPAlgorithm> algorithm = nil;
|
||||||
if (!name.length)
|
if (!name.length)
|
||||||
err( @"Missing name." );
|
err( @"Missing name." );
|
||||||
else if (!siteKey.keyData.length)
|
else if (!siteKey)
|
||||||
err( @"Missing key." );
|
err( @"Missing key." );
|
||||||
else
|
else
|
||||||
algorithm = site.algorithm;
|
algorithm = site.algorithm;
|
||||||
@@ -478,7 +504,7 @@
|
|||||||
|
|
||||||
- (void)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock {
|
- (void)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock {
|
||||||
|
|
||||||
NSAssert( [siteKey.keyID isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
||||||
switch (site.type) {
|
switch (site.type) {
|
||||||
case MPSiteTypeGeneratedMaximum:
|
case MPSiteTypeGeneratedMaximum:
|
||||||
case MPSiteTypeGeneratedLong:
|
case MPSiteTypeGeneratedLong:
|
||||||
@@ -500,7 +526,7 @@
|
|||||||
id<MPAlgorithm> algorithm = nil;
|
id<MPAlgorithm> algorithm = nil;
|
||||||
if (!site.name.length)
|
if (!site.name.length)
|
||||||
err( @"Missing name." );
|
err( @"Missing name." );
|
||||||
else if (!siteKey.keyData.length)
|
else if (!siteKey)
|
||||||
err( @"Missing key." );
|
err( @"Missing key." );
|
||||||
else
|
else
|
||||||
algorithm = site.algorithm;
|
algorithm = site.algorithm;
|
||||||
@@ -546,12 +572,12 @@
|
|||||||
|
|
||||||
- (void)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock {
|
- (void)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock {
|
||||||
|
|
||||||
NSAssert( [siteKey.keyID isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
||||||
NSString *name = site.name;
|
NSString *name = site.name;
|
||||||
id<MPAlgorithm> algorithm = nil;
|
id<MPAlgorithm> algorithm = nil;
|
||||||
if (!site.name.length)
|
if (!site.name.length)
|
||||||
err( @"Missing name." );
|
err( @"Missing name." );
|
||||||
else if (!siteKey.keyData.length)
|
else if (!siteKey)
|
||||||
err( @"Missing key." );
|
err( @"Missing key." );
|
||||||
else
|
else
|
||||||
algorithm = site.algorithm;
|
algorithm = site.algorithm;
|
||||||
@@ -565,13 +591,14 @@
|
|||||||
- (void)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)siteKey
|
- (void)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)siteKey
|
||||||
result:(void ( ^ )(NSString *result))resultBlock {
|
result:(void ( ^ )(NSString *result))resultBlock {
|
||||||
|
|
||||||
NSAssert( [siteKey.keyID isEqualToData:question.site.user.keyID], @"Site does not belong to current user." );
|
NSAssert( [[siteKey keyIDForAlgorithm:question.site.user.algorithm] isEqualToData:question.site.user.keyID],
|
||||||
|
@"Site does not belong to current user." );
|
||||||
NSString *name = question.site.name;
|
NSString *name = question.site.name;
|
||||||
NSString *keyword = question.keyword;
|
NSString *keyword = question.keyword;
|
||||||
id<MPAlgorithm> algorithm = nil;
|
id<MPAlgorithm> algorithm = nil;
|
||||||
if (!name.length)
|
if (!name.length)
|
||||||
err( @"Missing name." );
|
err( @"Missing name." );
|
||||||
else if (!siteKey.keyData.length)
|
else if (!siteKey)
|
||||||
err( @"Missing key." );
|
err( @"Missing key." );
|
||||||
else
|
else
|
||||||
algorithm = question.site.algorithm;
|
algorithm = question.site.algorithm;
|
||||||
@@ -585,7 +612,7 @@
|
|||||||
- (void)importProtectedPassword:(NSString *)protectedContent protectedByKey:(MPKey *)importKey
|
- (void)importProtectedPassword:(NSString *)protectedContent protectedByKey:(MPKey *)importKey
|
||||||
intoSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
intoSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
||||||
|
|
||||||
NSAssert( [siteKey.keyID isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
||||||
switch (site.type) {
|
switch (site.type) {
|
||||||
case MPSiteTypeGeneratedMaximum:
|
case MPSiteTypeGeneratedMaximum:
|
||||||
case MPSiteTypeGeneratedLong:
|
case MPSiteTypeGeneratedLong:
|
||||||
@@ -603,7 +630,7 @@
|
|||||||
(long)site.type, [site class] );
|
(long)site.type, [site class] );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if ([importKey.keyID isEqualToData:siteKey.keyID])
|
if ([[importKey keyIDForAlgorithm:self] isEqualToData:[siteKey keyIDForAlgorithm:self]])
|
||||||
((MPStoredSiteEntity *)site).contentObject = [protectedContent decodeBase64];
|
((MPStoredSiteEntity *)site).contentObject = [protectedContent decodeBase64];
|
||||||
|
|
||||||
else {
|
else {
|
||||||
@@ -620,7 +647,7 @@
|
|||||||
|
|
||||||
- (void)importClearTextPassword:(NSString *)clearContent intoSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
- (void)importClearTextPassword:(NSString *)clearContent intoSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
||||||
|
|
||||||
NSAssert( [siteKey.keyID isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
||||||
switch (site.type) {
|
switch (site.type) {
|
||||||
case MPSiteTypeGeneratedMaximum:
|
case MPSiteTypeGeneratedMaximum:
|
||||||
case MPSiteTypeGeneratedLong:
|
case MPSiteTypeGeneratedLong:
|
||||||
@@ -644,7 +671,7 @@
|
|||||||
|
|
||||||
- (NSString *)exportPasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
- (NSString *)exportPasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
||||||
|
|
||||||
NSAssert( [siteKey.keyID isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
||||||
if (!(site.type & MPSiteFeatureExportContent))
|
if (!(site.type & MPSiteFeatureExportContent))
|
||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
@@ -701,8 +728,10 @@
|
|||||||
if (!key)
|
if (!key)
|
||||||
return nil;
|
return nil;
|
||||||
NSData *decryptedContent = nil;
|
NSData *decryptedContent = nil;
|
||||||
if ([encryptedContent length])
|
if ([encryptedContent length]) {
|
||||||
decryptedContent = [encryptedContent decryptWithSymmetricKey:[key subKeyOfLength:PearlCryptKeySize].keyData padding:YES];
|
NSData *encryptionKey = [key keyDataForAlgorithm:self trimmedLength:PearlCryptKeySize];
|
||||||
|
decryptedContent = [encryptedContent decryptWithSymmetricKey:encryptionKey padding:YES];
|
||||||
|
}
|
||||||
if (!decryptedContent)
|
if (!decryptedContent)
|
||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
@@ -711,7 +740,7 @@
|
|||||||
|
|
||||||
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordOfType:(MPSiteType)type byAttacker:(MPAttacker)attacker {
|
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordOfType:(MPSiteType)type byAttacker:(MPAttacker)attacker {
|
||||||
|
|
||||||
if (!type)
|
if (!(type & MPSiteTypeClassGenerated))
|
||||||
return NO;
|
return NO;
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
const char **templates = mpw_templatesForType( type, &count );
|
const char **templates = mpw_templatesForType( type, &count );
|
||||||
@@ -730,6 +759,7 @@
|
|||||||
BN_add( permutations, permutations, templatePermutations );
|
BN_add( permutations, permutations, templatePermutations );
|
||||||
}
|
}
|
||||||
BN_free( templatePermutations );
|
BN_free( templatePermutations );
|
||||||
|
free( templates );
|
||||||
|
|
||||||
return [self timeToCrack:timeToCrack permutations:permutations forAttacker:attacker];
|
return [self timeToCrack:timeToCrack permutations:permutations forAttacker:attacker];
|
||||||
}
|
}
|
||||||
@@ -748,7 +778,7 @@
|
|||||||
|
|
||||||
if (strchr( charactersForClass, passwordCharacter )) {
|
if (strchr( charactersForClass, passwordCharacter )) {
|
||||||
// Found class for password character.
|
// Found class for password character.
|
||||||
characterEntropy = (BN_ULONG)strlen(charactersForClass);
|
characterEntropy = (BN_ULONG)strlen( charactersForClass );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit {
|
- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit {
|
||||||
|
|
||||||
if (site.version != [self version] - 1)
|
if ([site.algorithm version] != [self version] - 1)
|
||||||
// Only migrate from previous version.
|
// Only migrate from previous version.
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
|
|
||||||
// Apply migration.
|
// Apply migration.
|
||||||
site.requiresExplicitMigration = NO;
|
site.requiresExplicitMigration = NO;
|
||||||
site.version = [self version];
|
site.algorithm = self;
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
|
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#import <objc/runtime.h>
|
|
||||||
#import "MPAlgorithmV2.h"
|
#import "MPAlgorithmV2.h"
|
||||||
#import "MPEntities.h"
|
#import "MPEntities.h"
|
||||||
|
|
||||||
@@ -28,7 +27,7 @@
|
|||||||
|
|
||||||
- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit {
|
- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit {
|
||||||
|
|
||||||
if (site.version != [self version] - 1)
|
if ([site.algorithm version] != [self version] - 1)
|
||||||
// Only migrate from previous version.
|
// Only migrate from previous version.
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
@@ -42,7 +41,7 @@
|
|||||||
|
|
||||||
// Apply migration.
|
// Apply migration.
|
||||||
site.requiresExplicitMigration = NO;
|
site.requiresExplicitMigration = NO;
|
||||||
site.version = [self version];
|
site.algorithm = self;
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,12 +27,13 @@
|
|||||||
|
|
||||||
- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit {
|
- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit {
|
||||||
|
|
||||||
if (site.version != [self version] - 1)
|
if ([site.algorithm version] != [self version] - 1)
|
||||||
// Only migrate from previous version.
|
// Only migrate from previous version.
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
if (!explicit) {
|
if (!explicit) {
|
||||||
if (site.type & MPSiteTypeClassGenerated && site.name.length != [site.name dataUsingEncoding:NSUTF8StringEncoding].length) {
|
if (site.type & MPSiteTypeClassGenerated &&
|
||||||
|
site.user.name.length != [site.user.name dataUsingEncoding:NSUTF8StringEncoding].length) {
|
||||||
// This migration requires explicit permission for types of the generated class.
|
// This migration requires explicit permission for types of the generated class.
|
||||||
site.requiresExplicitMigration = YES;
|
site.requiresExplicitMigration = YES;
|
||||||
return NO;
|
return NO;
|
||||||
@@ -41,7 +42,7 @@
|
|||||||
|
|
||||||
// Apply migration.
|
// Apply migration.
|
||||||
site.requiresExplicitMigration = NO;
|
site.requiresExplicitMigration = NO;
|
||||||
site.version = [self version];
|
site.algorithm = self;
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,12 +31,12 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
|
|||||||
|
|
||||||
NSData *keyData = [PearlKeyChain dataOfItemForQuery:keyQuery( user )];
|
NSData *keyData = [PearlKeyChain dataOfItemForQuery:keyQuery( user )];
|
||||||
if (!keyData) {
|
if (!keyData) {
|
||||||
inf( @"No key found in keychain for: %@", user.userID );
|
inf( @"No key found in keychain for user: %@", user.userID );
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
inf( @"Found key in keychain for: %@", user.userID );
|
inf( @"Found key in keychain for user: %@", user.userID );
|
||||||
return [MPAlgorithmDefault keyFromKeyData:keyData];
|
return [[MPKey alloc] initForFullName:user.name withKeyData:keyData forAlgorithm:user.algorithm];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)storeSavedKeyFor:(MPUserEntity *)user {
|
- (void)storeSavedKeyFor:(MPUserEntity *)user {
|
||||||
@@ -44,12 +44,12 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
|
|||||||
if (user.saveKey) {
|
if (user.saveKey) {
|
||||||
NSData *existingKeyData = [PearlKeyChain dataOfItemForQuery:keyQuery( user )];
|
NSData *existingKeyData = [PearlKeyChain dataOfItemForQuery:keyQuery( user )];
|
||||||
|
|
||||||
if (![existingKeyData isEqualToData:self.key.keyData]) {
|
if (![existingKeyData isEqualToData:[self.key keyDataForAlgorithm:user.algorithm]]) {
|
||||||
inf( @"Saving key in keychain for: %@", user.userID );
|
inf( @"Saving key in keychain for user: %@", user.userID );
|
||||||
|
|
||||||
[PearlKeyChain addOrUpdateItemForQuery:keyQuery( user )
|
[PearlKeyChain addOrUpdateItemForQuery:keyQuery( user )
|
||||||
withAttributes:@{
|
withAttributes:@{
|
||||||
(__bridge id)kSecValueData : self.key.keyData,
|
(__bridge id)kSecValueData : [self.key keyDataForAlgorithm:user.algorithm],
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
(__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
|
(__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
|
||||||
#endif
|
#endif
|
||||||
@@ -62,7 +62,7 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
|
|||||||
|
|
||||||
OSStatus result = [PearlKeyChain deleteItemForQuery:keyQuery( user )];
|
OSStatus result = [PearlKeyChain deleteItemForQuery:keyQuery( user )];
|
||||||
if (result == noErr) {
|
if (result == noErr) {
|
||||||
inf( @"Removed key from keychain for: %@", user.userID );
|
inf( @"Removed key from keychain for user: %@", user.userID );
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPKeyForgottenNotification object:self];
|
[[NSNotificationCenter defaultCenter] postNotificationName:MPKeyForgottenNotification object:self];
|
||||||
}
|
}
|
||||||
@@ -88,8 +88,8 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
|
|||||||
|
|
||||||
// Method 1: When the user has no keyID set, set a new key from the given master password.
|
// Method 1: When the user has no keyID set, set a new key from the given master password.
|
||||||
if (!user.keyID) {
|
if (!user.keyID) {
|
||||||
if ([password length] && (tryKey = [MPAlgorithmDefault keyForPassword:password ofUserNamed:user.name])) {
|
if ([password length] && (tryKey = [[MPKey alloc] initForFullName:user.name withMasterPassword:password])) {
|
||||||
user.keyID = tryKey.keyID;
|
user.keyID = [tryKey keyIDForAlgorithm:MPAlgorithmDefault];
|
||||||
|
|
||||||
// Migrate existing sites.
|
// Migrate existing sites.
|
||||||
[self migrateSitesForUser:user saveInContext:moc toKey:tryKey];
|
[self migrateSitesForUser:user saveInContext:moc toKey:tryKey];
|
||||||
@@ -103,9 +103,11 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
|
|||||||
|
|
||||||
else if (!tryKey) {
|
else if (!tryKey) {
|
||||||
// Key should be saved in keychain. Load it.
|
// Key should be saved in keychain. Load it.
|
||||||
if ((tryKey = [self loadSavedKeyFor:user]) && ![user.keyID isEqual:tryKey.keyID]) {
|
if ((tryKey = [self loadSavedKeyFor:user]) && ![user.keyID isEqual:[tryKey keyIDForAlgorithm:user.algorithm]]) {
|
||||||
// Loaded password doesn't match user's keyID. Forget saved password: it is incorrect.
|
// Loaded password doesn't match user's keyID. Forget saved password: it is incorrect.
|
||||||
inf( @"Saved password doesn't match keyID for: %@", user.userID );
|
inf( @"Saved password doesn't match keyID for user: %@", user.userID );
|
||||||
|
trc( @"user keyID: %@ (version: %d) != authentication keyID: %@",
|
||||||
|
user.keyID, user.algorithm.version, [tryKey keyIDForAlgorithm:user.algorithm] );
|
||||||
|
|
||||||
tryKey = nil;
|
tryKey = nil;
|
||||||
[self forgetSavedKeyFor:user];
|
[self forgetSavedKeyFor:user];
|
||||||
@@ -113,9 +115,11 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Method 3: Check the given master password string.
|
// Method 3: Check the given master password string.
|
||||||
if (!tryKey && [password length] && (tryKey = [MPAlgorithmDefault keyForPassword:password ofUserNamed:user.name]) &&
|
if (!tryKey && [password length] && (tryKey = [[MPKey alloc] initForFullName:user.name withMasterPassword:password]) &&
|
||||||
![user.keyID isEqual:tryKey.keyID]) {
|
![user.keyID isEqual:[tryKey keyIDForAlgorithm:user.algorithm]]) {
|
||||||
inf( @"Key derived from password doesn't match keyID for: %@", user.userID );
|
inf( @"Key derived from password doesn't match keyID for user: %@", user.userID );
|
||||||
|
trc( @"user keyID: %@ (version: %u) != authentication keyID: %@",
|
||||||
|
user.keyID, user.algorithm.version, [tryKey keyIDForAlgorithm:user.algorithm] );
|
||||||
|
|
||||||
tryKey = nil;
|
tryKey = nil;
|
||||||
}
|
}
|
||||||
@@ -123,13 +127,22 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
|
|||||||
// No more methods left, fail if key still not known.
|
// No more methods left, fail if key still not known.
|
||||||
if (!tryKey) {
|
if (!tryKey) {
|
||||||
if (password)
|
if (password)
|
||||||
inf( @"Login failed for: %@", user.userID );
|
inf( @"Password login failed for user: %@", user.userID );
|
||||||
|
else
|
||||||
|
dbg( @"Automatic login failed for user: %@", user.userID );
|
||||||
|
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
inf( @"Logged in: %@", user.userID );
|
inf( @"Logged in user: %@", user.userID );
|
||||||
|
|
||||||
if (![self.key isEqualToKey:tryKey]) {
|
if (![self.key isEqualToKey:tryKey]) {
|
||||||
|
// Upgrade the user's keyID if not at the default version yet.
|
||||||
|
if (user.algorithm.version != MPAlgorithmDefaultVersion) {
|
||||||
|
user.algorithm = MPAlgorithmDefault;
|
||||||
|
user.keyID = [tryKey keyIDForAlgorithm:user.algorithm];
|
||||||
|
inf( @"Upgraded keyID to version %u for user: %@", user.algorithm.version, user.userID );
|
||||||
|
}
|
||||||
|
|
||||||
self.key = tryKey;
|
self.key = tryKey;
|
||||||
[self storeSavedKeyFor:user];
|
[self storeSavedKeyFor:user];
|
||||||
}
|
}
|
||||||
@@ -205,7 +218,7 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
|
|||||||
// Don't Migrate
|
// Don't Migrate
|
||||||
break;
|
break;
|
||||||
|
|
||||||
recoverKey = [site.algorithm keyForPassword:masterPassword ofUserNamed:user.name];
|
recoverKey = [[MPKey alloc] initForFullName:user.name withMasterPassword:masterPassword];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!content)
|
if (!content)
|
||||||
|
|||||||
@@ -190,14 +190,14 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
|||||||
// When privateManagedObjectContext is saved, import the changes into mainManagedObjectContext.
|
// When privateManagedObjectContext is saved, import the changes into mainManagedObjectContext.
|
||||||
PearlAddNotificationObserverTo( self.mainManagedObjectContext, NSManagedObjectContextDidSaveNotification,
|
PearlAddNotificationObserverTo( self.mainManagedObjectContext, NSManagedObjectContextDidSaveNotification,
|
||||||
self.privateManagedObjectContext, nil, ^(NSManagedObjectContext *mainManagedObjectContext, NSNotification *note) {
|
self.privateManagedObjectContext, nil, ^(NSManagedObjectContext *mainManagedObjectContext, NSNotification *note) {
|
||||||
[mainManagedObjectContext performBlock:^{
|
[mainManagedObjectContext performBlock:^{
|
||||||
@try {
|
@try {
|
||||||
[mainManagedObjectContext mergeChangesFromContextDidSaveNotification:note];
|
[mainManagedObjectContext mergeChangesFromContextDidSaveNotification:note];
|
||||||
}
|
}
|
||||||
@catch (NSException *exception) {
|
@catch (NSException *exception) {
|
||||||
err( @"While merging changes:\n%@",[exception fullDescription] );
|
err( @"While merging changes:\n%@", [exception fullDescription] );
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
|
||||||
@@ -423,14 +423,15 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
|||||||
}
|
}
|
||||||
|
|
||||||
MPSiteType type = activeUser.defaultType;
|
MPSiteType type = activeUser.defaultType;
|
||||||
NSString *typeEntityName = [MPAlgorithmDefault classNameOfType:type];
|
id<MPAlgorithm> algorithm = MPAlgorithmDefault;
|
||||||
|
Class entityType = [algorithm classOfType:type];
|
||||||
|
|
||||||
MPSiteEntity *site = [NSEntityDescription insertNewObjectForEntityForName:typeEntityName inManagedObjectContext:context];
|
MPSiteEntity *site = (MPSiteEntity *)[entityType insertNewObjectInContext:context];
|
||||||
site.name = siteName;
|
site.name = siteName;
|
||||||
site.user = activeUser;
|
site.user = activeUser;
|
||||||
site.type = type;
|
site.type = type;
|
||||||
site.lastUsed = [NSDate date];
|
site.lastUsed = [NSDate date];
|
||||||
site.version = MPAlgorithmDefaultVersion;
|
site.algorithm = algorithm;
|
||||||
|
|
||||||
NSError *error = nil;
|
NSError *error = nil;
|
||||||
if (site.objectID.isTemporaryID && ![context obtainPermanentIDsForObjects:@[ site ] error:&error])
|
if (site.objectID.isTemporaryID && ![context obtainPermanentIDsForObjects:@[ site ] error:&error])
|
||||||
@@ -454,14 +455,14 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
|||||||
|
|
||||||
else {
|
else {
|
||||||
// Type requires a different class of site. Recreate the site.
|
// Type requires a different class of site. Recreate the site.
|
||||||
NSString *typeEntityName = [site.algorithm classNameOfType:type];
|
Class entityType = [site.algorithm classOfType:type];
|
||||||
MPSiteEntity *newSite = [NSEntityDescription insertNewObjectForEntityForName:typeEntityName inManagedObjectContext:context];
|
MPSiteEntity *newSite = (MPSiteEntity *)[entityType insertNewObjectInContext:context];
|
||||||
newSite.type = type;
|
newSite.type = type;
|
||||||
newSite.name = site.name;
|
newSite.name = site.name;
|
||||||
newSite.user = site.user;
|
newSite.user = site.user;
|
||||||
newSite.uses = site.uses;
|
newSite.uses = site.uses;
|
||||||
newSite.lastUsed = site.lastUsed;
|
newSite.lastUsed = site.lastUsed;
|
||||||
newSite.version = site.version;
|
newSite.algorithm = site.algorithm;
|
||||||
newSite.loginName = site.loginName;
|
newSite.loginName = site.loginName;
|
||||||
|
|
||||||
NSError *error = nil;
|
NSError *error = nil;
|
||||||
@@ -685,13 +686,13 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
|||||||
inf( @"Import cancelled." );
|
inf( @"Import cancelled." );
|
||||||
return MPImportResultCancelled;
|
return MPImportResultCancelled;
|
||||||
}
|
}
|
||||||
MPKey *userKey = [MPAlgorithmDefault keyForPassword:userMasterPassword ofUserNamed:user? user.name: importUserName];
|
MPKey *userKey = [[MPKey alloc] initForFullName:user? user.name: importUserName withMasterPassword:userMasterPassword];
|
||||||
if (user && ![userKey.keyID isEqualToData:user.keyID])
|
if (user && ![[userKey keyIDForAlgorithm:user.algorithm] isEqualToData:user.keyID])
|
||||||
return MPImportResultInvalidPassword;
|
return MPImportResultInvalidPassword;
|
||||||
__block MPKey *importKey = userKey;
|
__block MPKey *importKey = userKey;
|
||||||
if (importKeyID && ![importKey.keyID isEqualToData:importKeyID])
|
if (importKeyID && ![[importKey keyIDForAlgorithm:importAlgorithm] isEqualToData:importKeyID])
|
||||||
importKey = [importAlgorithm keyForPassword:askImportPassword( importUserName ) ofUserNamed:importUserName];
|
importKey = [[MPKey alloc] initForFullName:importUserName withMasterPassword:askImportPassword( importUserName )];
|
||||||
if (importKeyID && ![importKey.keyID isEqualToData:importKeyID])
|
if (importKeyID && ![[importKey keyIDForAlgorithm:importAlgorithm] isEqualToData:importKeyID])
|
||||||
return MPImportResultInvalidPassword;
|
return MPImportResultInvalidPassword;
|
||||||
|
|
||||||
// Delete existing sites.
|
// Delete existing sites.
|
||||||
@@ -710,7 +711,8 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
|||||||
else {
|
else {
|
||||||
user = [MPUserEntity insertNewObjectInContext:context];
|
user = [MPUserEntity insertNewObjectInContext:context];
|
||||||
user.name = importUserName;
|
user.name = importUserName;
|
||||||
user.keyID = [userKey keyID];
|
user.algorithm = MPAlgorithmDefault;
|
||||||
|
user.keyID = [userKey keyIDForAlgorithm:user.algorithm];
|
||||||
if (importAvatar != NSNotFound)
|
if (importAvatar != NSNotFound)
|
||||||
user.avatar = importAvatar;
|
user.avatar = importAvatar;
|
||||||
dbg( @"Created User: %@", [user debugDescription] );
|
dbg( @"Created User: %@", [user debugDescription] );
|
||||||
@@ -728,19 +730,20 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
|||||||
NSString *exportContent = siteElements[7];
|
NSString *exportContent = siteElements[7];
|
||||||
|
|
||||||
// Create new site.
|
// Create new site.
|
||||||
NSString *typeEntityName = [MPAlgorithmForVersion( version ) classNameOfType:type];
|
id<MPAlgorithm> algorithm = MPAlgorithmForVersion( version );
|
||||||
if (!typeEntityName) {
|
Class entityType = [algorithm classOfType:type];
|
||||||
|
if (!entityType) {
|
||||||
err( @"Invalid site type in import file: %@ has type %lu", siteName, (long)type );
|
err( @"Invalid site type in import file: %@ has type %lu", siteName, (long)type );
|
||||||
return MPImportResultInternalError;
|
return MPImportResultInternalError;
|
||||||
}
|
}
|
||||||
MPSiteEntity *site = [NSEntityDescription insertNewObjectForEntityForName:typeEntityName inManagedObjectContext:context];
|
MPSiteEntity *site = (MPSiteEntity *)[entityType insertNewObjectInContext:context];
|
||||||
site.name = siteName;
|
site.name = siteName;
|
||||||
site.loginName = loginName;
|
site.loginName = loginName;
|
||||||
site.user = user;
|
site.user = user;
|
||||||
site.type = type;
|
site.type = type;
|
||||||
site.uses = uses;
|
site.uses = uses;
|
||||||
site.lastUsed = lastUsed;
|
site.lastUsed = lastUsed;
|
||||||
site.version = version;
|
site.algorithm = algorithm;
|
||||||
if ([exportContent length]) {
|
if ([exportContent length]) {
|
||||||
if (clearText)
|
if (clearText)
|
||||||
[site.algorithm importClearTextPassword:exportContent intoSite:site usingKey:userKey];
|
[site.algorithm importClearTextPassword:exportContent intoSite:site usingKey:userKey];
|
||||||
@@ -768,7 +771,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
|||||||
- (NSString *)exportSitesRevealPasswords:(BOOL)revealPasswords {
|
- (NSString *)exportSitesRevealPasswords:(BOOL)revealPasswords {
|
||||||
|
|
||||||
MPUserEntity *activeUser = [self activeUserForMainThread];
|
MPUserEntity *activeUser = [self activeUserForMainThread];
|
||||||
inf( @"Exporting sites, %@, for: %@", revealPasswords? @"revealing passwords": @"omitting passwords", activeUser.userID );
|
inf( @"Exporting sites, %@, for user: %@", revealPasswords? @"revealing passwords": @"omitting passwords", activeUser.userID );
|
||||||
|
|
||||||
// Header.
|
// Header.
|
||||||
NSMutableString *export = [NSMutableString new];
|
NSMutableString *export = [NSMutableString new];
|
||||||
@@ -799,7 +802,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
|||||||
NSDate *lastUsed = site.lastUsed;
|
NSDate *lastUsed = site.lastUsed;
|
||||||
NSUInteger uses = site.uses;
|
NSUInteger uses = site.uses;
|
||||||
MPSiteType type = site.type;
|
MPSiteType type = site.type;
|
||||||
NSUInteger version = site.version;
|
id<MPAlgorithm> algorithm = site.algorithm;
|
||||||
NSUInteger counter = 0;
|
NSUInteger counter = 0;
|
||||||
NSString *loginName = site.loginName;
|
NSString *loginName = site.loginName;
|
||||||
NSString *siteName = site.name;
|
NSString *siteName = site.name;
|
||||||
@@ -818,10 +821,17 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
|||||||
content = [site.algorithm exportPasswordForSite:site usingKey:self.key];
|
content = [site.algorithm exportPasswordForSite:site usingKey:self.key];
|
||||||
}
|
}
|
||||||
|
|
||||||
[export appendFormat:@"%@ %8ld %8s %25s\t%25s\t%@\n",
|
NSString *lastUsedExport = [[NSDateFormatter rfc3339DateFormatter] stringFromDate:lastUsed];
|
||||||
[[NSDateFormatter rfc3339DateFormatter] stringFromDate:lastUsed], (long)uses,
|
long usesExport = (long)uses;
|
||||||
[strf( @"%lu:%lu:%lu", (long)type, (long)version, (long)counter ) UTF8String],
|
NSString *typeExport = strf( @"%lu:%lu:%lu", (long)type, (long)[algorithm version], (long)counter );
|
||||||
[(loginName?: @"") UTF8String], [siteName UTF8String], content?: @""];
|
NSString *loginNameExport = loginName?: @"";
|
||||||
|
NSString *contentExport = content?: @"";
|
||||||
|
[export appendFormat:@"%@ %8ld %8S %25S\t%25S\t%@\n",
|
||||||
|
lastUsedExport, usesExport,
|
||||||
|
(const unichar *)[typeExport cStringUsingEncoding:NSUTF16StringEncoding],
|
||||||
|
(const unichar *)[loginNameExport cStringUsingEncoding:NSUTF16StringEncoding],
|
||||||
|
(const unichar *)[siteName cStringUsingEncoding:NSUTF16StringEncoding],
|
||||||
|
contentExport];
|
||||||
}
|
}
|
||||||
|
|
||||||
return export;
|
return export;
|
||||||
|
|||||||
@@ -31,9 +31,8 @@
|
|||||||
@property(readonly) NSString *typeClassName;
|
@property(readonly) NSString *typeClassName;
|
||||||
@property(readonly) Class typeClass;
|
@property(readonly) Class typeClass;
|
||||||
@property(assign) NSUInteger uses;
|
@property(assign) NSUInteger uses;
|
||||||
@property(assign) NSUInteger version;
|
|
||||||
@property(assign) BOOL requiresExplicitMigration;
|
@property(assign) BOOL requiresExplicitMigration;
|
||||||
@property(readonly) id<MPAlgorithm> algorithm;
|
@property(strong) id<MPAlgorithm> algorithm;
|
||||||
|
|
||||||
- (NSUInteger)use;
|
- (NSUInteger)use;
|
||||||
- (BOOL)tryMigrateExplicitly:(BOOL)explicit;
|
- (BOOL)tryMigrateExplicitly:(BOOL)explicit;
|
||||||
@@ -56,6 +55,7 @@
|
|||||||
@property(assign) BOOL saveKey;
|
@property(assign) BOOL saveKey;
|
||||||
@property(assign) MPSiteType defaultType;
|
@property(assign) MPSiteType defaultType;
|
||||||
@property(readonly) NSString *userID;
|
@property(readonly) NSString *userID;
|
||||||
|
@property(strong) id<MPAlgorithm> algorithm;
|
||||||
|
|
||||||
+ (NSString *)idFor:(NSString *)userName;
|
+ (NSString *)idFor:(NSString *)userName;
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#import "MPEntities.h"
|
#import "MPEntities.h"
|
||||||
#import "MPAppDelegate_Shared.h"
|
#import "MPAppDelegate_Shared.h"
|
||||||
|
#import "MPAppDelegate_Key.h"
|
||||||
|
|
||||||
@implementation NSManagedObjectContext(MP)
|
@implementation NSManagedObjectContext(MP)
|
||||||
|
|
||||||
@@ -90,14 +91,14 @@
|
|||||||
self.uses_ = @(anUses);
|
self.uses_ = @(anUses);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSUInteger)version {
|
- (id<MPAlgorithm>)algorithm {
|
||||||
|
|
||||||
return [self.version_ unsignedIntegerValue];
|
return MPAlgorithmForVersion( MIN( MPAlgorithmVersionCurrent, MAX( MPAlgorithmVersion0, [self.version_ unsignedIntegerValue] ) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setVersion:(NSUInteger)version {
|
- (void)setAlgorithm:(id<MPAlgorithm>)algorithm {
|
||||||
|
|
||||||
self.version_ = @(version);
|
self.version_ = @([algorithm version]);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)requiresExplicitMigration {
|
- (BOOL)requiresExplicitMigration {
|
||||||
@@ -110,11 +111,6 @@
|
|||||||
self.requiresExplicitMigration_ = @(requiresExplicitMigration);
|
self.requiresExplicitMigration_ = @(requiresExplicitMigration);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id<MPAlgorithm>)algorithm {
|
|
||||||
|
|
||||||
return MPAlgorithmForVersion( self.version );
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSUInteger)use {
|
- (NSUInteger)use {
|
||||||
|
|
||||||
self.lastUsed = [NSDate date];
|
self.lastUsed = [NSDate date];
|
||||||
@@ -128,21 +124,31 @@
|
|||||||
|
|
||||||
- (NSString *)debugDescription {
|
- (NSString *)debugDescription {
|
||||||
|
|
||||||
@try {
|
__block NSString *debugDescription = strf( @"{%@: [recursing]}", [self class] );
|
||||||
return strf( @"{%@: name=%@, user=%@, type=%lu, uses=%ld, lastUsed=%@, version=%ld, loginName=%@, requiresExplicitMigration=%d}",
|
|
||||||
NSStringFromClass( [self class] ), self.name, self.user.name, (long)self.type, (long)self.uses, self.lastUsed,
|
static BOOL recursing = NO;
|
||||||
(long)self.version,
|
PearlIfNotRecursing( &recursing, ^{
|
||||||
self.loginName, self.requiresExplicitMigration );
|
@try {
|
||||||
} @catch (NSException *exception) {
|
debugDescription = strf(
|
||||||
return strf( @"{%@: inaccessible: %@}",
|
@"{%@: name=%@, user=%@, type=%lu, uses=%ld, lastUsed=%@, version=%ld, loginName=%@, requiresExplicitMigration=%d}",
|
||||||
NSStringFromClass( [self class] ), [exception fullDescription] );
|
NSStringFromClass( [self class] ), self.name, self.user.name, (long)self.type, (long)self.uses, self.lastUsed,
|
||||||
}
|
(long)[self.algorithm version],
|
||||||
|
self.loginName, self.requiresExplicitMigration );
|
||||||
|
}
|
||||||
|
@catch (NSException *exception) {
|
||||||
|
debugDescription = strf( @"{%@: inaccessible: %@}",
|
||||||
|
NSStringFromClass( [self class] ), [exception fullDescription] );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
return debugDescription;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)tryMigrateExplicitly:(BOOL)explicit {
|
- (BOOL)tryMigrateExplicitly:(BOOL)explicit {
|
||||||
|
|
||||||
while (self.version < MPAlgorithmDefaultVersion) {
|
MPAlgorithmVersion algorithmVersion;
|
||||||
NSUInteger toVersion = self.version + 1;
|
while ((algorithmVersion = [self.algorithm version]) < MPAlgorithmDefaultVersion) {
|
||||||
|
NSUInteger toVersion = algorithmVersion + 1;
|
||||||
if (![MPAlgorithmForVersion( toVersion ) tryMigrateSite:self explicit:explicit]) {
|
if (![MPAlgorithmForVersion( toVersion ) tryMigrateSite:self explicit:explicit]) {
|
||||||
wrn( @"%@ migration to version: %ld failed for site: %@",
|
wrn( @"%@ migration to version: %ld failed for site: %@",
|
||||||
explicit? @"Explicit": @"Automatic", (long)toVersion, self );
|
explicit? @"Explicit": @"Automatic", (long)toVersion, self );
|
||||||
@@ -287,6 +293,17 @@
|
|||||||
self.defaultType_ = @(aDefaultType);
|
self.defaultType_ = @(aDefaultType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (id<MPAlgorithm>)algorithm {
|
||||||
|
|
||||||
|
return MPAlgorithmForVersion( MIN( MPAlgorithmVersionCurrent, MAX( MPAlgorithmVersion0, [self.version_ unsignedIntegerValue] ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setAlgorithm:(id<MPAlgorithm>)version {
|
||||||
|
|
||||||
|
self.version_ = @([version version]);
|
||||||
|
[[MPAppDelegate_Shared get] forgetSavedKeyFor:self];
|
||||||
|
}
|
||||||
|
|
||||||
- (NSString *)userID {
|
- (NSString *)userID {
|
||||||
|
|
||||||
return [MPUserEntity idFor:self.name];
|
return [MPUserEntity idFor:self.name];
|
||||||
|
|||||||
@@ -16,17 +16,21 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
#import "MPAlgorithm.h"
|
||||||
|
|
||||||
@protocol MPAlgorithm;
|
@protocol MPAlgorithm;
|
||||||
|
|
||||||
@interface MPKey : NSObject
|
@interface MPKey : NSObject
|
||||||
|
|
||||||
@property(nonatomic, readonly, strong) id<MPAlgorithm> algorithm;
|
@property(nonatomic, readonly) NSString *fullName;
|
||||||
@property(nonatomic, readonly, strong) NSData *keyData;
|
|
||||||
@property(nonatomic, readonly, strong) NSData *keyID;
|
- (instancetype)initForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword;
|
||||||
|
- (instancetype)initForFullName:(NSString *)fullName withKeyData:(NSData *)keyData forAlgorithm:(id<MPAlgorithm>)algorithm;
|
||||||
|
|
||||||
|
- (NSData *)keyIDForAlgorithm:(id<MPAlgorithm>)algorithm;
|
||||||
|
- (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm;
|
||||||
|
- (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm trimmedLength:(NSUInteger)subKeyLength;
|
||||||
|
|
||||||
- (id)initWithKeyData:(NSData *)keyData algorithm:(id<MPAlgorithm>)algorithm;
|
|
||||||
- (MPKey *)subKeyOfLength:(NSUInteger)subKeyLength;
|
|
||||||
- (BOOL)isEqualToKey:(MPKey *)key;
|
- (BOOL)isEqualToKey:(MPKey *)key;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||||
*
|
*
|
||||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
*
|
*
|
||||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//
|
//
|
||||||
// MPKey
|
// MPKey
|
||||||
@@ -19,38 +19,61 @@
|
|||||||
|
|
||||||
@interface MPKey()
|
@interface MPKey()
|
||||||
|
|
||||||
@property(nonatomic, readwrite, strong) id<MPAlgorithm> algorithm;
|
@property(nonatomic) NSString *fullName;
|
||||||
@property(nonatomic, readwrite, strong) NSData *keyData;
|
@property(nonatomic) NSString *masterPassword;
|
||||||
@property(nonatomic, readwrite, strong) NSData *keyID;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation MPKey
|
@implementation MPKey {
|
||||||
|
NSCache *_keyCache;
|
||||||
|
};
|
||||||
|
|
||||||
@synthesize algorithm = _algorithm, keyData = _keyData, keyID = _keyID;
|
- (instancetype)initForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword {
|
||||||
|
|
||||||
- (id)initWithKeyData:(NSData *)keyData algorithm:(id<MPAlgorithm>)algorithm {
|
|
||||||
|
|
||||||
if (!(self = [super init]))
|
if (!(self = [super init]))
|
||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
self.keyData = keyData;
|
_keyCache = [NSCache new];
|
||||||
self.algorithm = algorithm;
|
self.fullName = fullName;
|
||||||
self.keyID = [self.algorithm keyIDForKeyData:keyData];
|
self.masterPassword = masterPassword;
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MPKey *)subKeyOfLength:(NSUInteger)subKeyLength {
|
- (instancetype)initForFullName:(NSString *)fullName withKeyData:(NSData *)keyData forAlgorithm:(id<MPAlgorithm>)algorithm {
|
||||||
|
|
||||||
NSData *subKeyData = [self.keyData subdataWithRange:NSMakeRange( 0, MIN(subKeyLength, self.keyData.length) )];
|
if (!(self = [self initForFullName:fullName withMasterPassword:nil]))
|
||||||
|
return nil;
|
||||||
|
|
||||||
return [self.algorithm keyFromKeyData:subKeyData];
|
[_keyCache setObject:keyData forKey:algorithm];
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSData *)keyIDForAlgorithm:(id<MPAlgorithm>)algorithm {
|
||||||
|
|
||||||
|
return [algorithm keyIDForKeyData:[self keyDataForAlgorithm:algorithm]];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm {
|
||||||
|
|
||||||
|
NSData *keyData = [_keyCache objectForKey:algorithm];
|
||||||
|
if (!keyData)
|
||||||
|
[_keyCache setObject:keyData = [algorithm keyDataForFullName:self.fullName withMasterPassword:self.masterPassword]
|
||||||
|
forKey:algorithm];
|
||||||
|
|
||||||
|
return keyData;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm trimmedLength:(NSUInteger)subKeyLength {
|
||||||
|
|
||||||
|
NSData *keyData = [self keyDataForAlgorithm:algorithm];
|
||||||
|
return [keyData subdataWithRange:NSMakeRange( 0, MIN( subKeyLength, keyData.length ) )];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isEqualToKey:(MPKey *)key {
|
- (BOOL)isEqualToKey:(MPKey *)key {
|
||||||
|
|
||||||
return [self.keyID isEqualToData:key.keyID];
|
return [self.fullName isEqualToString:key.fullName] && [self.masterPassword isEqualToString:self.masterPassword];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isEqual:(id)object {
|
- (BOOL)isEqual:(id)object {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
@property (nonatomic, retain) NSDate * lastUsed;
|
@property (nonatomic, retain) NSDate * lastUsed;
|
||||||
@property (nonatomic, retain) NSString * name;
|
@property (nonatomic, retain) NSString * name;
|
||||||
@property (nonatomic, retain) NSNumber * saveKey_;
|
@property (nonatomic, retain) NSNumber * saveKey_;
|
||||||
|
@property (nonatomic, retain) NSNumber * version_;
|
||||||
@property (nonatomic, retain) NSSet *sites;
|
@property (nonatomic, retain) NSSet *sites;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
@dynamic lastUsed;
|
@dynamic lastUsed;
|
||||||
@dynamic name;
|
@dynamic name;
|
||||||
@dynamic saveKey_;
|
@dynamic saveKey_;
|
||||||
|
@dynamic version_;
|
||||||
@dynamic sites;
|
@dynamic sites;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#import <QuartzCore/QuartzCore.h>
|
#import <QuartzCore/QuartzCore.h>
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
#import "MPPasswordWindowController.h"
|
#import "MPPasswordWindowController.h"
|
||||||
#import "MPMacAppDelegate.h"
|
#import "MPMacAppDelegate.h"
|
||||||
#import "MPAppDelegate_Store.h"
|
#import "MPAppDelegate_Store.h"
|
||||||
@@ -463,26 +464,26 @@
|
|||||||
- (void)useSite {
|
- (void)useSite {
|
||||||
|
|
||||||
MPSiteModel *selectedSite = [self selectedSite];
|
MPSiteModel *selectedSite = [self selectedSite];
|
||||||
if (selectedSite) {
|
if (!selectedSite)
|
||||||
// Performing action while content is available. Copy it.
|
return;
|
||||||
[self copyContent:selectedSite.content];
|
|
||||||
|
|
||||||
[self fadeOut];
|
if (selectedSite.transient) {
|
||||||
|
[self createNewSite:selectedSite.name];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
NSUserNotification *notification = [NSUserNotification new];
|
// Performing action while content is available. Copy it.
|
||||||
notification.title = @"Password Copied";
|
[self copyContent:selectedSite.content];
|
||||||
if (selectedSite.loginName.length)
|
|
||||||
notification.subtitle = strf( @"%@ at %@", selectedSite.loginName, selectedSite.name );
|
[self fadeOut];
|
||||||
else
|
|
||||||
notification.subtitle = selectedSite.name;
|
NSUserNotification *notification = [NSUserNotification new];
|
||||||
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
|
notification.title = @"Password Copied";
|
||||||
}
|
if (selectedSite.loginName.length)
|
||||||
else {
|
notification.subtitle = strf( @"%@ at %@", selectedSite.loginName, selectedSite.name );
|
||||||
NSString *siteName = [self.siteField stringValue];
|
else
|
||||||
if ([siteName length])
|
notification.subtitle = selectedSite.name;
|
||||||
// Performing action without content but a site name is written.
|
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
|
||||||
[self createNewSite:siteName];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)updateUser {
|
- (void)updateUser {
|
||||||
@@ -518,6 +519,7 @@
|
|||||||
|
|
||||||
- (void)updateSites {
|
- (void)updateSites {
|
||||||
|
|
||||||
|
NSAssert( [NSOperationQueue currentQueue] == [NSOperationQueue mainQueue], @"updateSites should be called on the main queue." );
|
||||||
if (![MPMacAppDelegate get].key) {
|
if (![MPMacAppDelegate get].key) {
|
||||||
self.sites = nil;
|
self.sites = nil;
|
||||||
return;
|
return;
|
||||||
@@ -530,13 +532,18 @@
|
|||||||
} );
|
} );
|
||||||
|
|
||||||
NSString *queryString = self.siteField.stringValue;
|
NSString *queryString = self.siteField.stringValue;
|
||||||
NSString *queryPattern = [queryString stringByReplacingMatchesOfExpression:fuzzyRE withTemplate:@"*$1*"];
|
NSString *queryPattern;
|
||||||
|
if ([queryString length] < 13)
|
||||||
|
queryPattern = [queryString stringByReplacingMatchesOfExpression:fuzzyRE withTemplate:@"*$1*"];
|
||||||
|
else
|
||||||
|
// If query is too long, a wildcard per character makes the CoreData fetch take excessively long.
|
||||||
|
queryPattern = strf( @"*%@*", queryString );
|
||||||
NSMutableArray *fuzzyGroups = [NSMutableArray new];
|
NSMutableArray *fuzzyGroups = [NSMutableArray new];
|
||||||
[fuzzyRE enumerateMatchesInString:queryString options:0 range:NSMakeRange( 0, queryString.length )
|
[fuzzyRE enumerateMatchesInString:queryString options:0 range:NSMakeRange( 0, queryString.length )
|
||||||
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
|
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
|
||||||
[fuzzyGroups addObject:[queryString substringWithRange:result.range]];
|
[fuzzyGroups addObject:[queryString substringWithRange:result.range]];
|
||||||
}];
|
}];
|
||||||
[MPMacAppDelegate managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) {
|
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
|
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
|
||||||
fetchRequest.sortDescriptors = @[ [[NSSortDescriptor alloc] initWithKey:@"lastUsed" ascending:NO] ];
|
fetchRequest.sortDescriptors = @[ [[NSSortDescriptor alloc] initWithKey:@"lastUsed" ascending:NO] ];
|
||||||
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(%@ == '' OR name LIKE[cd] %@) AND user == %@",
|
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(%@ == '' OR name LIKE[cd] %@) AND user == %@",
|
||||||
@@ -549,10 +556,22 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOL exact = NO;
|
||||||
NSMutableArray *newSites = [NSMutableArray arrayWithCapacity:[siteResults count]];
|
NSMutableArray *newSites = [NSMutableArray arrayWithCapacity:[siteResults count]];
|
||||||
for (MPSiteEntity *site in siteResults)
|
for (MPSiteEntity *site in siteResults) {
|
||||||
[newSites addObject:[[MPSiteModel alloc] initWithEntity:site fuzzyGroups:fuzzyGroups]];
|
[newSites addObject:[[MPSiteModel alloc] initWithEntity:site fuzzyGroups:fuzzyGroups]];
|
||||||
self.sites = newSites;
|
exact |= [site.name isEqualToString:queryString];
|
||||||
|
}
|
||||||
|
if (!exact && [queryString length]) {
|
||||||
|
MPUserEntity *activeUser = [[MPAppDelegate_Shared get] activeUserInContext:context];
|
||||||
|
[newSites addObject:[[MPSiteModel alloc] initWithName:queryString forUser:activeUser]];
|
||||||
|
}
|
||||||
|
|
||||||
|
dbg( @"newSites: %@", newSites );
|
||||||
|
if (![newSites isEqualToArray:self.sites])
|
||||||
|
PearlMainQueue( ^{
|
||||||
|
self.sites = newSites;
|
||||||
|
} );
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -605,6 +624,8 @@
|
|||||||
CGDirectDisplayID displayID = [self.window.screen.deviceDescription[@"NSScreenNumber"] unsignedIntValue];
|
CGDirectDisplayID displayID = [self.window.screen.deviceDescription[@"NSScreenNumber"] unsignedIntValue];
|
||||||
CGImageRef capturedImage = CGDisplayCreateImage( displayID );
|
CGImageRef capturedImage = CGDisplayCreateImage( displayID );
|
||||||
if (!capturedImage || CGImageGetWidth( capturedImage ) <= 1) {
|
if (!capturedImage || CGImageGetWidth( capturedImage ) <= 1) {
|
||||||
|
if (capturedImage)
|
||||||
|
CFRelease( capturedImage );
|
||||||
wrn( @"Failed to capture screen image for display: %d", displayID );
|
wrn( @"Failed to capture screen image for display: %d", displayID );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import "MPSiteEntity.h"
|
#import "MPSiteEntity.h"
|
||||||
#import "MPAlgorithm.h"
|
#import "MPAlgorithm.h"
|
||||||
|
#import "MPUserEntity.h"
|
||||||
|
|
||||||
@class MPSiteEntity;
|
@class MPSiteEntity;
|
||||||
|
|
||||||
@@ -35,10 +36,12 @@
|
|||||||
@property (nonatomic) NSUInteger counter;
|
@property (nonatomic) NSUInteger counter;
|
||||||
@property (nonatomic) NSDate *lastUsed;
|
@property (nonatomic) NSDate *lastUsed;
|
||||||
@property (nonatomic) id<MPAlgorithm> algorithm;
|
@property (nonatomic) id<MPAlgorithm> algorithm;
|
||||||
@property (nonatomic) BOOL generated;
|
@property (nonatomic, readonly) BOOL generated;
|
||||||
@property (nonatomic) BOOL stored;
|
@property (nonatomic, readonly) BOOL stored;
|
||||||
|
@property (nonatomic, readonly) BOOL transient;
|
||||||
|
|
||||||
- (instancetype)initWithEntity:(MPSiteEntity *)entity fuzzyGroups:(NSArray *)fuzzyGroups;
|
- (instancetype)initWithEntity:(MPSiteEntity *)entity fuzzyGroups:(NSArray *)fuzzyGroups;
|
||||||
|
- (instancetype)initWithName:(NSString *)siteName forUser:(MPUserEntity *)user;
|
||||||
- (MPSiteEntity *)entityInContext:(NSManagedObjectContext *)moc;
|
- (MPSiteEntity *)entityInContext:(NSManagedObjectContext *)moc;
|
||||||
|
|
||||||
- (void)updateContent;
|
- (void)updateContent;
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
BOOL _initialized;
|
BOOL _initialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithEntity:(MPSiteEntity *)entity fuzzyGroups:(NSArray *)fuzzyGroups {
|
- (instancetype)initWithEntity:(MPSiteEntity *)entity fuzzyGroups:(NSArray *)fuzzyGroups {
|
||||||
|
|
||||||
if (!(self = [super init]))
|
if (!(self = [super init]))
|
||||||
return nil;
|
return nil;
|
||||||
@@ -39,6 +39,17 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithName:(NSString *)siteName forUser:(MPUserEntity *)user {
|
||||||
|
|
||||||
|
if (!(self = [super init]))
|
||||||
|
return nil;
|
||||||
|
|
||||||
|
[self setTransientSiteName:siteName forUser:user];
|
||||||
|
_initialized = YES;
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)setEntity:(MPSiteEntity *)entity fuzzyGroups:(NSArray *)fuzzyGroups {
|
- (void)setEntity:(MPSiteEntity *)entity fuzzyGroups:(NSArray *)fuzzyGroups {
|
||||||
|
|
||||||
if ([_entityOID isEqual:entity.objectID])
|
if ([_entityOID isEqual:entity.objectID])
|
||||||
@@ -59,7 +70,7 @@
|
|||||||
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
|
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
|
||||||
paragraphStyle.alignment = NSCenterTextAlignment;
|
paragraphStyle.alignment = NSCenterTextAlignment;
|
||||||
[attributedSiteName addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange( 0, [siteName length] )];
|
[attributedSiteName addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange( 0, [siteName length] )];
|
||||||
|
|
||||||
self.displayedName = attributedSiteName;
|
self.displayedName = attributedSiteName;
|
||||||
self.name = siteName;
|
self.name = siteName;
|
||||||
self.algorithm = entity.algorithm;
|
self.algorithm = entity.algorithm;
|
||||||
@@ -73,6 +84,28 @@
|
|||||||
[self updateContent:entity];
|
[self updateContent:entity];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setTransientSiteName:(NSString *)siteName forUser:(MPUserEntity *)user {
|
||||||
|
|
||||||
|
_entityOID = nil;
|
||||||
|
|
||||||
|
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
|
||||||
|
paragraphStyle.alignment = NSCenterTextAlignment;
|
||||||
|
self.displayedName = stra( siteName, @{
|
||||||
|
NSBackgroundColorAttributeName : [NSColor alternateSelectedControlColor],
|
||||||
|
NSParagraphStyleAttributeName : paragraphStyle,
|
||||||
|
} );
|
||||||
|
self.name = siteName;
|
||||||
|
self.algorithm = MPAlgorithmDefault;
|
||||||
|
self.lastUsed = nil;
|
||||||
|
self.type = user.defaultType;
|
||||||
|
self.typeName = [self.algorithm nameOfType:self.type];
|
||||||
|
self.uses = @0;
|
||||||
|
self.counter = 1;
|
||||||
|
|
||||||
|
// Find all password types and the index of the current type amongst them.
|
||||||
|
[self updateContent];
|
||||||
|
}
|
||||||
|
|
||||||
- (MPSiteEntity *)entityInContext:(NSManagedObjectContext *)moc {
|
- (MPSiteEntity *)entityInContext:(NSManagedObjectContext *)moc {
|
||||||
|
|
||||||
if (!_entityOID)
|
if (!_entityOID)
|
||||||
@@ -96,15 +129,18 @@
|
|||||||
// This wasn't a change to the entity.
|
// This wasn't a change to the entity.
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
if (_entityOID)
|
||||||
MPSiteEntity *entity = [self entityInContext:context];
|
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
if ([entity isKindOfClass:[MPGeneratedSiteEntity class]]) {
|
MPSiteEntity *entity = [self entityInContext:context];
|
||||||
((MPGeneratedSiteEntity *)entity).counter = counter;
|
if ([entity isKindOfClass:[MPGeneratedSiteEntity class]]) {
|
||||||
[context saveToStore];
|
((MPGeneratedSiteEntity *)entity).counter = counter;
|
||||||
|
[context saveToStore];
|
||||||
|
|
||||||
[self updateContent:entity];
|
[self updateContent:entity];
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
|
else
|
||||||
|
[self updateContent];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)generated {
|
- (BOOL)generated {
|
||||||
@@ -117,36 +153,60 @@
|
|||||||
return self.type & MPSiteTypeClassStored;
|
return self.type & MPSiteTypeClassStored;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (BOOL)transient {
|
||||||
|
|
||||||
|
return _entityOID == nil;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)updateContent {
|
- (void)updateContent {
|
||||||
|
|
||||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
if (_entityOID)
|
||||||
[self updateContent:[MPSiteEntity existingObjectWithID:_entityOID inContext:context]];
|
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
}];
|
[self updateContent:[MPSiteEntity existingObjectWithID:_entityOID inContext:context]];
|
||||||
|
}];
|
||||||
|
else
|
||||||
|
PearlNotMainQueue( ^{
|
||||||
|
NSString *password = [self.algorithm generatePasswordForSiteNamed:self.name ofType:self.type withCounter:self.counter
|
||||||
|
usingKey:[MPAppDelegate_Shared get].key];
|
||||||
|
NSString *loginName = [self.algorithm generateLoginForSiteNamed:self.name usingKey:[MPAppDelegate_Shared get].key];
|
||||||
|
[self updatePasswordWithResult:password];
|
||||||
|
[self updateLoginNameWithResult:loginName];
|
||||||
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)updateContent:(MPSiteEntity *)entity {
|
- (void)updateContent:(MPSiteEntity *)entity {
|
||||||
|
|
||||||
|
[entity resolvePasswordUsingKey:[MPAppDelegate_Shared get].key result:^(NSString *result) {
|
||||||
|
[self updatePasswordWithResult:result];
|
||||||
|
}];
|
||||||
|
[entity resolveLoginUsingKey:[MPAppDelegate_Shared get].key result:^(NSString *result) {
|
||||||
|
[self updateLoginNameWithResult:result];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)updatePasswordWithResult:(NSString *)result {
|
||||||
|
|
||||||
static NSRegularExpression *re_anyChar;
|
static NSRegularExpression *re_anyChar;
|
||||||
static dispatch_once_t once = 0;
|
static dispatch_once_t once = 0;
|
||||||
dispatch_once( &once, ^{
|
dispatch_once( &once, ^{
|
||||||
re_anyChar = [NSRegularExpression regularExpressionWithPattern:@"." options:0 error:nil];
|
re_anyChar = [NSRegularExpression regularExpressionWithPattern:@"." options:0 error:nil];
|
||||||
} );
|
} );
|
||||||
|
|
||||||
[entity resolvePasswordUsingKey:[MPAppDelegate_Shared get].key result:^(NSString *result) {
|
NSString *displayResult = result;
|
||||||
NSString *displayResult = result;
|
if ([[MPConfig get].hidePasswords boolValue] && !([NSEvent modifierFlags] & NSAlternateKeyMask))
|
||||||
if ([[MPConfig get].hidePasswords boolValue] && !([NSEvent modifierFlags] & NSAlternateKeyMask))
|
displayResult = [displayResult stringByReplacingMatchesOfExpression:re_anyChar withTemplate:@"●"];
|
||||||
displayResult = [displayResult stringByReplacingMatchesOfExpression:re_anyChar withTemplate:@"●"];
|
|
||||||
|
|
||||||
PearlMainQueue( ^{
|
PearlMainQueue( ^{
|
||||||
self.content = result;
|
self.content = result;
|
||||||
self.displayedContent = displayResult;
|
self.displayedContent = displayResult;
|
||||||
} );
|
} );
|
||||||
}];
|
}
|
||||||
[entity resolveLoginUsingKey:[MPAppDelegate_Shared get].key result:^(NSString *result) {
|
|
||||||
PearlMainQueue( ^{
|
- (void)updateLoginNameWithResult:(NSString *)loginName {
|
||||||
self.loginName = result;
|
|
||||||
} );
|
PearlMainQueue( ^{
|
||||||
}];
|
self.loginName = loginName;
|
||||||
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -31,7 +31,6 @@
|
|||||||
DA16B345170661F2000A0EAB /* libPearl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC77CAD148291A600BCF976 /* libPearl.a */; };
|
DA16B345170661F2000A0EAB /* libPearl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC77CAD148291A600BCF976 /* libPearl.a */; };
|
||||||
DA2508F119511D3600AC23F1 /* MPPasswordWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA2508F019511D3600AC23F1 /* MPPasswordWindowController.xib */; };
|
DA2508F119511D3600AC23F1 /* MPPasswordWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA2508F019511D3600AC23F1 /* MPPasswordWindowController.xib */; };
|
||||||
DA250925195148E200AC23F1 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAEBC45214F6364500987BF6 /* QuartzCore.framework */; };
|
DA250925195148E200AC23F1 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAEBC45214F6364500987BF6 /* QuartzCore.framework */; };
|
||||||
DA29992C19C6A89900AF7DF1 /* MasterPassword.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DA29992619C6A89900AF7DF1 /* MasterPassword.xcdatamodeld */; };
|
|
||||||
DA2CA4ED18D323D3007798F8 /* NSError+PearlFullDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = DA2CA4E718D323D3007798F8 /* NSError+PearlFullDescription.m */; };
|
DA2CA4ED18D323D3007798F8 /* NSError+PearlFullDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = DA2CA4E718D323D3007798F8 /* NSError+PearlFullDescription.m */; };
|
||||||
DA2CA4EE18D323D3007798F8 /* NSError+PearlFullDescription.h in Headers */ = {isa = PBXBuildFile; fileRef = DA2CA4E818D323D3007798F8 /* NSError+PearlFullDescription.h */; };
|
DA2CA4EE18D323D3007798F8 /* NSError+PearlFullDescription.h in Headers */ = {isa = PBXBuildFile; fileRef = DA2CA4E818D323D3007798F8 /* NSError+PearlFullDescription.h */; };
|
||||||
DA2CA4EF18D323D3007798F8 /* NSArray+Pearl.m in Sources */ = {isa = PBXBuildFile; fileRef = DA2CA4E918D323D3007798F8 /* NSArray+Pearl.m */; };
|
DA2CA4EF18D323D3007798F8 /* NSArray+Pearl.m in Sources */ = {isa = PBXBuildFile; fileRef = DA2CA4E918D323D3007798F8 /* NSArray+Pearl.m */; };
|
||||||
@@ -92,6 +91,7 @@
|
|||||||
DA6774451A474A3B004F356A /* mpw-types.c in Sources */ = {isa = PBXBuildFile; fileRef = DA6773C21A4746AF004F356A /* mpw-types.c */; };
|
DA6774451A474A3B004F356A /* mpw-types.c in Sources */ = {isa = PBXBuildFile; fileRef = DA6773C21A4746AF004F356A /* mpw-types.c */; };
|
||||||
DA6774461A474A3B004F356A /* mpw-util.c in Sources */ = {isa = PBXBuildFile; fileRef = DA6773C51A4746AF004F356A /* mpw-util.c */; };
|
DA6774461A474A3B004F356A /* mpw-util.c in Sources */ = {isa = PBXBuildFile; fileRef = DA6773C51A4746AF004F356A /* mpw-util.c */; };
|
||||||
DA67744A1A47C8F7004F356A /* mpw-tests-util.c in Sources */ = {isa = PBXBuildFile; fileRef = DA6774481A47C8F7004F356A /* mpw-tests-util.c */; };
|
DA67744A1A47C8F7004F356A /* mpw-tests-util.c in Sources */ = {isa = PBXBuildFile; fileRef = DA6774481A47C8F7004F356A /* mpw-tests-util.c */; };
|
||||||
|
DA8495301A915EF400B3053D /* MasterPassword.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DA8495281A915EF400B3053D /* MasterPassword.xcdatamodeld */; };
|
||||||
DA89D4EC1A51EABD00AC64D7 /* Pearl-Cocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = DA89D4EA1A51EABD00AC64D7 /* Pearl-Cocoa.h */; };
|
DA89D4EC1A51EABD00AC64D7 /* Pearl-Cocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = DA89D4EA1A51EABD00AC64D7 /* Pearl-Cocoa.h */; };
|
||||||
DA89D4ED1A51EABD00AC64D7 /* Pearl-Cocoa.m in Sources */ = {isa = PBXBuildFile; fileRef = DA89D4EB1A51EABD00AC64D7 /* Pearl-Cocoa.m */; };
|
DA89D4ED1A51EABD00AC64D7 /* Pearl-Cocoa.m in Sources */ = {isa = PBXBuildFile; fileRef = DA89D4EB1A51EABD00AC64D7 /* Pearl-Cocoa.m */; };
|
||||||
DA8ED895192906920099B726 /* PearlTween.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8ED891192906920099B726 /* PearlTween.m */; };
|
DA8ED895192906920099B726 /* PearlTween.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8ED891192906920099B726 /* PearlTween.m */; };
|
||||||
@@ -294,11 +294,6 @@
|
|||||||
DA25090719513C1400AC23F1 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
|
DA25090719513C1400AC23F1 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
|
||||||
DA2509261951B86C00AC23F1 /* small-screen.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "small-screen.png"; sourceTree = "<group>"; };
|
DA2509261951B86C00AC23F1 /* small-screen.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "small-screen.png"; sourceTree = "<group>"; };
|
||||||
DA2509271951B86C00AC23F1 /* screen.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = screen.png; sourceTree = "<group>"; };
|
DA2509271951B86C00AC23F1 /* screen.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = screen.png; sourceTree = "<group>"; };
|
||||||
DA29992719C6A89900AF7DF1 /* MasterPassword 1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 1.xcdatamodel"; sourceTree = "<group>"; };
|
|
||||||
DA29992819C6A89900AF7DF1 /* MasterPassword 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 2.xcdatamodel"; sourceTree = "<group>"; };
|
|
||||||
DA29992919C6A89900AF7DF1 /* MasterPassword 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 3.xcdatamodel"; sourceTree = "<group>"; };
|
|
||||||
DA29992A19C6A89900AF7DF1 /* MasterPassword 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 4.xcdatamodel"; sourceTree = "<group>"; };
|
|
||||||
DA29992B19C6A89900AF7DF1 /* MasterPassword 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 5.xcdatamodel"; sourceTree = "<group>"; };
|
|
||||||
DA2CA4E718D323D3007798F8 /* NSError+PearlFullDescription.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+PearlFullDescription.m"; sourceTree = "<group>"; };
|
DA2CA4E718D323D3007798F8 /* NSError+PearlFullDescription.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+PearlFullDescription.m"; sourceTree = "<group>"; };
|
||||||
DA2CA4E818D323D3007798F8 /* NSError+PearlFullDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+PearlFullDescription.h"; sourceTree = "<group>"; };
|
DA2CA4E818D323D3007798F8 /* NSError+PearlFullDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+PearlFullDescription.h"; sourceTree = "<group>"; };
|
||||||
DA2CA4E918D323D3007798F8 /* NSArray+Pearl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Pearl.m"; sourceTree = "<group>"; };
|
DA2CA4E918D323D3007798F8 /* NSArray+Pearl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Pearl.m"; sourceTree = "<group>"; };
|
||||||
@@ -321,7 +316,6 @@
|
|||||||
DA32CFE119CF1C71004F3F0E /* MPSiteQuestionEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSiteQuestionEntity.m; sourceTree = "<group>"; };
|
DA32CFE119CF1C71004F3F0E /* MPSiteQuestionEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSiteQuestionEntity.m; sourceTree = "<group>"; };
|
||||||
DA32CFE319CF1C71004F3F0E /* MPUserEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUserEntity.h; sourceTree = "<group>"; };
|
DA32CFE319CF1C71004F3F0E /* MPUserEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUserEntity.h; sourceTree = "<group>"; };
|
||||||
DA32CFE419CF1C71004F3F0E /* MPUserEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUserEntity.m; sourceTree = "<group>"; };
|
DA32CFE419CF1C71004F3F0E /* MPUserEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUserEntity.m; sourceTree = "<group>"; };
|
||||||
DA32D00019CF470E004F3F0E /* MasterPassword 6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 6.xcdatamodel"; sourceTree = "<group>"; };
|
|
||||||
DA3509FC15F101A500C14A8E /* PearlQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlQueue.h; sourceTree = "<group>"; };
|
DA3509FC15F101A500C14A8E /* PearlQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlQueue.h; sourceTree = "<group>"; };
|
||||||
DA3509FD15F101A500C14A8E /* PearlQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlQueue.m; sourceTree = "<group>"; };
|
DA3509FD15F101A500C14A8E /* PearlQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlQueue.m; sourceTree = "<group>"; };
|
||||||
DA3B844A190FC5A900246EEA /* Crashlytics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Crashlytics.framework; sourceTree = "<group>"; };
|
DA3B844A190FC5A900246EEA /* Crashlytics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Crashlytics.framework; sourceTree = "<group>"; };
|
||||||
@@ -796,6 +790,13 @@
|
|||||||
DA831A281A6E1146000AC234 /* mpw-algorithm_v1.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = "mpw-algorithm_v1.c"; sourceTree = "<group>"; };
|
DA831A281A6E1146000AC234 /* mpw-algorithm_v1.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = "mpw-algorithm_v1.c"; sourceTree = "<group>"; };
|
||||||
DA831A291A6E1146000AC234 /* mpw-algorithm_v2.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = "mpw-algorithm_v2.c"; sourceTree = "<group>"; };
|
DA831A291A6E1146000AC234 /* mpw-algorithm_v2.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = "mpw-algorithm_v2.c"; sourceTree = "<group>"; };
|
||||||
DA831A2A1A6E1146000AC234 /* mpw-algorithm_v3.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = "mpw-algorithm_v3.c"; sourceTree = "<group>"; };
|
DA831A2A1A6E1146000AC234 /* mpw-algorithm_v3.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = "mpw-algorithm_v3.c"; sourceTree = "<group>"; };
|
||||||
|
DA8495291A915EF400B3053D /* MasterPassword 1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 1.xcdatamodel"; sourceTree = "<group>"; };
|
||||||
|
DA84952A1A915EF400B3053D /* MasterPassword 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 2.xcdatamodel"; sourceTree = "<group>"; };
|
||||||
|
DA84952B1A915EF400B3053D /* MasterPassword 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 3.xcdatamodel"; sourceTree = "<group>"; };
|
||||||
|
DA84952C1A915EF400B3053D /* MasterPassword 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 4.xcdatamodel"; sourceTree = "<group>"; };
|
||||||
|
DA84952D1A915EF400B3053D /* MasterPassword 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 5.xcdatamodel"; sourceTree = "<group>"; };
|
||||||
|
DA84952E1A915EF400B3053D /* MasterPassword 6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 6.xcdatamodel"; sourceTree = "<group>"; };
|
||||||
|
DA84952F1A915EF400B3053D /* MasterPassword 7.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 7.xcdatamodel"; sourceTree = "<group>"; };
|
||||||
DA89D4EA1A51EABD00AC64D7 /* Pearl-Cocoa.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Pearl-Cocoa.h"; sourceTree = "<group>"; };
|
DA89D4EA1A51EABD00AC64D7 /* Pearl-Cocoa.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Pearl-Cocoa.h"; sourceTree = "<group>"; };
|
||||||
DA89D4EB1A51EABD00AC64D7 /* Pearl-Cocoa.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "Pearl-Cocoa.m"; sourceTree = "<group>"; };
|
DA89D4EB1A51EABD00AC64D7 /* Pearl-Cocoa.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "Pearl-Cocoa.m"; sourceTree = "<group>"; };
|
||||||
DA8ED891192906920099B726 /* PearlTween.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlTween.m; sourceTree = "<group>"; };
|
DA8ED891192906920099B726 /* PearlTween.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlTween.m; sourceTree = "<group>"; };
|
||||||
@@ -1060,8 +1061,8 @@
|
|||||||
DA5E5C961724A667003798D8 /* ObjC */ = {
|
DA5E5C961724A667003798D8 /* ObjC */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
DA8495281A915EF400B3053D /* MasterPassword.xcdatamodeld */,
|
||||||
DA5E5CB21724A667003798D8 /* Mac */,
|
DA5E5CB21724A667003798D8 /* Mac */,
|
||||||
DA29992619C6A89900AF7DF1 /* MasterPassword.xcdatamodeld */,
|
|
||||||
DA5E5C971724A667003798D8 /* MPAlgorithm.h */,
|
DA5E5C971724A667003798D8 /* MPAlgorithm.h */,
|
||||||
DA5E5C981724A667003798D8 /* MPAlgorithm.m */,
|
DA5E5C981724A667003798D8 /* MPAlgorithm.m */,
|
||||||
DA5E5C991724A667003798D8 /* MPAlgorithmV0.h */,
|
DA5E5C991724A667003798D8 /* MPAlgorithmV0.h */,
|
||||||
@@ -2245,7 +2246,6 @@
|
|||||||
DA5180CE19FF307E00A587E9 /* MPAppDelegate_Store.m in Sources */,
|
DA5180CE19FF307E00A587E9 /* MPAppDelegate_Store.m in Sources */,
|
||||||
DA5E5CFA1724A667003798D8 /* MPAppDelegate_Shared.m in Sources */,
|
DA5E5CFA1724A667003798D8 /* MPAppDelegate_Shared.m in Sources */,
|
||||||
DA5E5CFC1724A667003798D8 /* MPConfig.m in Sources */,
|
DA5E5CFC1724A667003798D8 /* MPConfig.m in Sources */,
|
||||||
DA29992C19C6A89900AF7DF1 /* MasterPassword.xcdatamodeld in Sources */,
|
|
||||||
DA3B8456190FC89700246EEA /* MPFixable.m in Sources */,
|
DA3B8456190FC89700246EEA /* MPFixable.m in Sources */,
|
||||||
DA5E5D001724A667003798D8 /* MPEntities.m in Sources */,
|
DA5E5D001724A667003798D8 /* MPEntities.m in Sources */,
|
||||||
DA5E5D011724A667003798D8 /* MPKey.m in Sources */,
|
DA5E5D011724A667003798D8 /* MPKey.m in Sources */,
|
||||||
@@ -2262,6 +2262,7 @@
|
|||||||
93D39784E725A34D1EE3FB3B /* MPInitialWindowController.m in Sources */,
|
93D39784E725A34D1EE3FB3B /* MPInitialWindowController.m in Sources */,
|
||||||
DA32CFDF19CF1C70004F3F0E /* MPSiteEntity.m in Sources */,
|
DA32CFDF19CF1C70004F3F0E /* MPSiteEntity.m in Sources */,
|
||||||
93D394C4254EEB45FB335AFB /* MPSitesTableView.m in Sources */,
|
93D394C4254EEB45FB335AFB /* MPSitesTableView.m in Sources */,
|
||||||
|
DA8495301A915EF400B3053D /* MasterPassword.xcdatamodeld in Sources */,
|
||||||
DA6774291A4746AF004F356A /* mpw-algorithm.c in Sources */,
|
DA6774291A4746AF004F356A /* mpw-algorithm.c in Sources */,
|
||||||
93D395E4830290EBB6E71F34 /* MPNoStateButton.m in Sources */,
|
93D395E4830290EBB6E71F34 /* MPNoStateButton.m in Sources */,
|
||||||
DA4DAE941A7D8117003E5423 /* MPAlgorithmV3.m in Sources */,
|
DA4DAE941A7D8117003E5423 /* MPAlgorithmV3.m in Sources */,
|
||||||
@@ -2943,17 +2944,18 @@
|
|||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
/* Begin XCVersionGroup section */
|
/* Begin XCVersionGroup section */
|
||||||
DA29992619C6A89900AF7DF1 /* MasterPassword.xcdatamodeld */ = {
|
DA8495281A915EF400B3053D /* MasterPassword.xcdatamodeld */ = {
|
||||||
isa = XCVersionGroup;
|
isa = XCVersionGroup;
|
||||||
children = (
|
children = (
|
||||||
DA29992719C6A89900AF7DF1 /* MasterPassword 1.xcdatamodel */,
|
DA8495291A915EF400B3053D /* MasterPassword 1.xcdatamodel */,
|
||||||
DA29992819C6A89900AF7DF1 /* MasterPassword 2.xcdatamodel */,
|
DA84952A1A915EF400B3053D /* MasterPassword 2.xcdatamodel */,
|
||||||
DA29992919C6A89900AF7DF1 /* MasterPassword 3.xcdatamodel */,
|
DA84952B1A915EF400B3053D /* MasterPassword 3.xcdatamodel */,
|
||||||
DA29992A19C6A89900AF7DF1 /* MasterPassword 4.xcdatamodel */,
|
DA84952C1A915EF400B3053D /* MasterPassword 4.xcdatamodel */,
|
||||||
DA29992B19C6A89900AF7DF1 /* MasterPassword 5.xcdatamodel */,
|
DA84952D1A915EF400B3053D /* MasterPassword 5.xcdatamodel */,
|
||||||
DA32D00019CF470E004F3F0E /* MasterPassword 6.xcdatamodel */,
|
DA84952E1A915EF400B3053D /* MasterPassword 6.xcdatamodel */,
|
||||||
|
DA84952F1A915EF400B3053D /* MasterPassword 7.xcdatamodel */,
|
||||||
);
|
);
|
||||||
currentVersion = DA32D00019CF470E004F3F0E /* MasterPassword 6.xcdatamodel */;
|
currentVersion = DA84952F1A915EF400B3053D /* MasterPassword 7.xcdatamodel */;
|
||||||
path = MasterPassword.xcdatamodeld;
|
path = MasterPassword.xcdatamodeld;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
versionGroupType = wrapper.xcdatamodel;
|
versionGroupType = wrapper.xcdatamodel;
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>_XCCurrentVersionName</key>
|
<key>_XCCurrentVersionName</key>
|
||||||
<string>MasterPassword 6.xcdatamodel</string>
|
<string>MasterPassword 7.xcdatamodel</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="6244" systemVersion="13E28" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
|
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="6254" systemVersion="14C109" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
|
||||||
<entity name="MPGeneratedSiteEntity" representedClassName="MPGeneratedSiteEntity" parentEntity="MPSiteEntity" elementID="789304EA-070D-4982-8C20-54EECFC20CB6" syncable="YES">
|
<entity name="MPGeneratedSiteEntity" representedClassName="MPGeneratedSiteEntity" parentEntity="MPSiteEntity" elementID="789304EA-070D-4982-8C20-54EECFC20CB6" syncable="YES">
|
||||||
<attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/>
|
<attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/>
|
||||||
</entity>
|
</entity>
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="6254" systemVersion="14C109" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
|
||||||
|
<entity name="MPGeneratedSiteEntity" representedClassName="MPGeneratedSiteEntity" parentEntity="MPSiteEntity" elementID="789304EA-070D-4982-8C20-54EECFC20CB6" syncable="YES">
|
||||||
|
<attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" syncable="YES"/>
|
||||||
|
</entity>
|
||||||
|
<entity name="MPSiteEntity" representedClassName="MPSiteEntity" isAbstract="YES" elementID="58EE245C-6827-4C11-BB7E-5722F2426EC2" syncable="YES">
|
||||||
|
<attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/>
|
||||||
|
<attribute name="lastUsed" attributeType="Date" indexed="YES" syncable="YES"/>
|
||||||
|
<attribute name="loginGenerated_" attributeType="Boolean" defaultValueString="NO" syncable="YES"/>
|
||||||
|
<attribute name="loginName" optional="YES" attributeType="String" elementID="A1B9F981-D33C-4BFE-9F94-C9D3E1F78E51" syncable="YES"/>
|
||||||
|
<attribute name="name" attributeType="String" minValueString="1" indexed="YES" syncable="YES"/>
|
||||||
|
<attribute name="requiresExplicitMigration_" attributeType="Boolean" defaultValueString="NO">
|
||||||
|
<userInfo/>
|
||||||
|
</attribute>
|
||||||
|
<attribute name="type_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/>
|
||||||
|
<attribute name="uses_" attributeType="Integer 16" defaultValueString="0" indexed="YES" syncable="YES"/>
|
||||||
|
<attribute name="version_" attributeType="Integer 16" minValueString="0" defaultValueString="0" syncable="YES"/>
|
||||||
|
<relationship name="questions" optional="YES" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="MPSiteQuestionEntity" inverseName="site" inverseEntity="MPSiteQuestionEntity" syncable="YES"/>
|
||||||
|
<relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="sites" inverseEntity="MPUserEntity" elementID="FC8AE32E-7BE3-4FA6-8611-B7DC0DB063EF" syncable="YES"/>
|
||||||
|
</entity>
|
||||||
|
<entity name="MPSiteQuestionEntity" representedClassName="MPSiteQuestionEntity" syncable="YES">
|
||||||
|
<attribute name="keyword" attributeType="String" syncable="YES"/>
|
||||||
|
<relationship name="site" maxCount="1" deletionRule="Nullify" destinationEntity="MPSiteEntity" inverseName="questions" inverseEntity="MPSiteEntity" syncable="YES"/>
|
||||||
|
</entity>
|
||||||
|
<entity name="MPStoredSiteEntity" representedClassName="MPStoredSiteEntity" parentEntity="MPSiteEntity" elementID="BEEF1688-0CCD-4699-A86A-4D860FE2CEB8" syncable="YES">
|
||||||
|
<attribute name="contentObject" optional="YES" attributeType="Transformable" storedInTruthFile="YES" syncable="YES"/>
|
||||||
|
</entity>
|
||||||
|
<entity name="MPUserEntity" representedClassName="MPUserEntity" syncable="YES">
|
||||||
|
<attribute name="avatar_" attributeType="Integer 16" defaultValueString="0" syncable="YES"/>
|
||||||
|
<attribute name="defaultType_" attributeType="Integer 16" defaultValueString="17" syncable="YES"/>
|
||||||
|
<attribute name="keyID" optional="YES" attributeType="Binary" syncable="YES"/>
|
||||||
|
<attribute name="lastUsed" optional="YES" attributeType="Date" syncable="YES"/>
|
||||||
|
<attribute name="name" attributeType="String" syncable="YES"/>
|
||||||
|
<attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO">
|
||||||
|
<userInfo/>
|
||||||
|
</attribute>
|
||||||
|
<attribute name="version_" attributeType="Integer 16" minValueString="0" defaultValueString="2" syncable="YES"/>
|
||||||
|
<relationship name="sites" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPSiteEntity" inverseName="user" inverseEntity="MPSiteEntity" elementID="D18D6772-040E-4CFE-8F32-A34B08E9E9BC" syncable="YES"/>
|
||||||
|
</entity>
|
||||||
|
<elements>
|
||||||
|
<element name="MPGeneratedSiteEntity" positionX="216" positionY="-288" width="128" height="58"/>
|
||||||
|
<element name="MPSiteEntity" positionX="-0" positionY="-286" width="128" height="208"/>
|
||||||
|
<element name="MPSiteQuestionEntity" positionX="-2" positionY="-9" width="128" height="73"/>
|
||||||
|
<element name="MPStoredSiteEntity" positionX="214" positionY="-171" width="128" height="58"/>
|
||||||
|
<element name="MPUserEntity" positionX="-218" positionY="-288" width="128" height="165"/>
|
||||||
|
</elements>
|
||||||
|
</model>
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
@property(weak, nonatomic) IBOutlet UIScrollView *scrollView;
|
@property(weak, nonatomic) IBOutlet UIScrollView *scrollView;
|
||||||
@property(weak, nonatomic) IBOutlet UIView *dialogView;
|
@property(weak, nonatomic) IBOutlet UIView *dialogView;
|
||||||
@property(weak, nonatomic) IBOutlet UIView *containerView;
|
@property(weak, nonatomic) IBOutlet UIView *containerView;
|
||||||
@property(weak, nonatomic) IBOutlet UITextField *userNameField;
|
@property(weak, nonatomic) IBOutlet UITextField *fullNameField;
|
||||||
@property(weak, nonatomic) IBOutlet UITextField *masterPasswordField;
|
@property(weak, nonatomic) IBOutlet UITextField *masterPasswordField;
|
||||||
@property(weak, nonatomic) IBOutlet UITextField *siteField;
|
@property(weak, nonatomic) IBOutlet UITextField *siteField;
|
||||||
@property(weak, nonatomic) IBOutlet UIStepper *counterStepper;
|
@property(weak, nonatomic) IBOutlet UIStepper *counterStepper;
|
||||||
|
|||||||
@@ -75,7 +75,7 @@
|
|||||||
|
|
||||||
- (IBAction)controlChanged:(UIControl *)control {
|
- (IBAction)controlChanged:(UIControl *)control {
|
||||||
|
|
||||||
if (control == self.userNameField || control == self.masterPasswordField)
|
if (control == self.fullNameField || control == self.masterPasswordField)
|
||||||
[self updateKey];
|
[self updateKey];
|
||||||
else
|
else
|
||||||
[self updatePassword];
|
[self updatePassword];
|
||||||
@@ -103,15 +103,15 @@
|
|||||||
|
|
||||||
- (void)updateKey {
|
- (void)updateKey {
|
||||||
|
|
||||||
NSString *userName = self.userNameField.text;
|
NSString *fullName = self.fullNameField.text;
|
||||||
NSString *masterPassword = self.masterPasswordField.text;
|
NSString *masterPassword = self.masterPasswordField.text;
|
||||||
|
|
||||||
self.passwordLabel.text = nil;
|
self.passwordLabel.text = nil;
|
||||||
[self.activity startAnimating];
|
[self.activity startAnimating];
|
||||||
[_emergencyKeyQueue cancelAllOperations];
|
[_emergencyKeyQueue cancelAllOperations];
|
||||||
[_emergencyKeyQueue addOperationWithBlock:^{
|
[_emergencyKeyQueue addOperationWithBlock:^{
|
||||||
if ([masterPassword length] && [userName length])
|
if ([masterPassword length] && [fullName length])
|
||||||
_key = [MPAlgorithmDefault keyForPassword:masterPassword ofUserNamed:userName];
|
_key = [[MPKey alloc] initForFullName:fullName withMasterPassword:masterPassword];
|
||||||
else
|
else
|
||||||
_key = nil;
|
_key = nil;
|
||||||
|
|
||||||
@@ -165,7 +165,7 @@
|
|||||||
|
|
||||||
- (void)reset {
|
- (void)reset {
|
||||||
|
|
||||||
self.userNameField.text = nil;
|
self.fullNameField.text = nil;
|
||||||
self.masterPasswordField.text = nil;
|
self.masterPasswordField.text = nil;
|
||||||
self.siteField.text = nil;
|
self.siteField.text = nil;
|
||||||
self.counterStepper.value = 1;
|
self.counterStepper.value = 1;
|
||||||
|
|||||||
25
MasterPassword/ObjC/iOS/MPMessageViewController.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
//
|
||||||
|
// MPPreferencesViewController.h
|
||||||
|
// MasterPassword-iOS
|
||||||
|
//
|
||||||
|
// Created by Maarten Billemont on 04/06/12.
|
||||||
|
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
@interface MPMessage : NSObject
|
||||||
|
|
||||||
|
@property(nonatomic) NSString *title;
|
||||||
|
@property(nonatomic) NSString *text;
|
||||||
|
@property(nonatomic) BOOL info;
|
||||||
|
|
||||||
|
+ (instancetype)messageWithTitle:(NSString *)title text:(NSString *)text info:(BOOL)info;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface MPMessageViewController : UIViewController
|
||||||
|
|
||||||
|
@property (nonatomic) MPMessage *message;
|
||||||
|
|
||||||
|
@end
|
||||||
78
MasterPassword/ObjC/iOS/MPMessageViewController.m
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
//
|
||||||
|
// MPPreferencesViewController.m
|
||||||
|
// MasterPassword-iOS
|
||||||
|
//
|
||||||
|
// Created by Maarten Billemont on 04/06/12.
|
||||||
|
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MPMessageViewController.h"
|
||||||
|
#import "MPiOSAppDelegate.h"
|
||||||
|
#import "MPAppDelegate_Store.h"
|
||||||
|
#import "MPOverlayViewController.h"
|
||||||
|
|
||||||
|
@interface MPMessageViewController()
|
||||||
|
|
||||||
|
@property(nonatomic) IBOutlet UILabel *titleLabel;
|
||||||
|
@property(nonatomic) IBOutlet UILabel *messageLabel;
|
||||||
|
@property(nonatomic) IBOutlet UIView *infoView;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation MPMessage
|
||||||
|
|
||||||
|
+ (instancetype)messageWithTitle:(NSString *)title text:(NSString *)text info:(BOOL)info {
|
||||||
|
|
||||||
|
MPMessage *message = [MPMessage new];
|
||||||
|
message.title = title;
|
||||||
|
message.text = text;
|
||||||
|
message.info = info;
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation MPMessageViewController
|
||||||
|
|
||||||
|
#pragma mark - Life
|
||||||
|
|
||||||
|
- (void)viewDidLoad {
|
||||||
|
|
||||||
|
[super viewDidLoad];
|
||||||
|
|
||||||
|
self.view.backgroundColor = [UIColor clearColor];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)viewWillAppear:(BOOL)animated {
|
||||||
|
|
||||||
|
[super viewWillAppear:animated];
|
||||||
|
|
||||||
|
self.titleLabel.text = self.message.title;
|
||||||
|
self.messageLabel.text = self.message.text;
|
||||||
|
self.infoView.gone = !self.message.info;
|
||||||
|
|
||||||
|
PearlAddNotificationObserver( MPSignedOutNotification, nil, [NSOperationQueue mainQueue],
|
||||||
|
^(MPMessageViewController *self, NSNotification *note) {
|
||||||
|
if (![note.userInfo[@"animated"] boolValue])
|
||||||
|
[UIView setAnimationsEnabled:NO];
|
||||||
|
[[MPOverlaySegue dismissViewController:self] perform];
|
||||||
|
[UIView setAnimationsEnabled:YES];
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)viewWillDisappear:(BOOL)animated {
|
||||||
|
|
||||||
|
[super viewWillDisappear:animated];
|
||||||
|
|
||||||
|
PearlRemoveNotificationObservers();
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIStatusBarStyle)preferredStatusBarStyle {
|
||||||
|
|
||||||
|
return UIStatusBarStyleLightContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - State
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -59,6 +59,8 @@
|
|||||||
[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector( doRevealPassword: )]];
|
[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector( doRevealPassword: )]];
|
||||||
[self.counterButton addGestureRecognizer:
|
[self.counterButton addGestureRecognizer:
|
||||||
[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector( doResetCounter: )]];
|
[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector( doResetCounter: )]];
|
||||||
|
[self.upgradeButton addGestureRecognizer:
|
||||||
|
[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector( doDowngrade: )]];
|
||||||
|
|
||||||
[self setupLayer];
|
[self setupLayer];
|
||||||
|
|
||||||
@@ -331,13 +333,36 @@
|
|||||||
- (IBAction)doUpgrade:(UIButton *)sender {
|
- (IBAction)doUpgrade:(UIButton *)sender {
|
||||||
|
|
||||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
if (![[self siteInContext:context] tryMigrateExplicitly:YES]) {
|
MPSiteEntity *siteEntity = [self siteInContext:context];
|
||||||
|
if (![siteEntity tryMigrateExplicitly:YES]) {
|
||||||
[PearlOverlay showTemporaryOverlayWithTitle:@"Couldn't Upgrade Site" dismissAfter:2];
|
[PearlOverlay showTemporaryOverlayWithTitle:@"Couldn't Upgrade Site" dismissAfter:2];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
[context saveToStore];
|
[context saveToStore];
|
||||||
[PearlOverlay showTemporaryOverlayWithTitle:@"Site Upgraded" dismissAfter:2];
|
[PearlOverlay showTemporaryOverlayWithTitle:strf( @"Site Upgraded to V%d", siteEntity.algorithm.version )
|
||||||
|
dismissAfter:2];
|
||||||
|
[self updateAnimated:YES];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (IBAction)doDowngrade:(UILongPressGestureRecognizer *)recognizer {
|
||||||
|
|
||||||
|
if (recognizer.state != UIGestureRecognizerStateBegan)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (![[MPiOSConfig get].allowDowngrade boolValue])
|
||||||
|
return;
|
||||||
|
|
||||||
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
|
MPSiteEntity *siteEntity = [self siteInContext:context];
|
||||||
|
if (siteEntity.algorithm.version <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
siteEntity.algorithm = MPAlgorithmForVersion( siteEntity.algorithm.version - 1 );
|
||||||
|
[context saveToStore];
|
||||||
|
[PearlOverlay showTemporaryOverlayWithTitle:strf( @"Site Downgraded to V%d", siteEntity.algorithm.version )
|
||||||
|
dismissAfter:2];
|
||||||
[self updateAnimated:YES];
|
[self updateAnimated:YES];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -479,7 +504,7 @@
|
|||||||
MPSiteEntity *mainSite = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
|
MPSiteEntity *mainSite = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
|
||||||
|
|
||||||
// UI
|
// UI
|
||||||
self.upgradeButton.gone = !mainSite.requiresExplicitMigration;
|
self.upgradeButton.gone = !mainSite.requiresExplicitMigration && ![[MPiOSConfig get].allowDowngrade boolValue];
|
||||||
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;
|
||||||
@@ -533,15 +558,15 @@
|
|||||||
[algorithm timeToCrack:&timeToCrack passwordString:password byAttacker:attackHardware])
|
[algorithm timeToCrack:&timeToCrack passwordString:password byAttacker:attackHardware])
|
||||||
timeToCrackString = NSStringFromTimeToCrack( timeToCrack );
|
timeToCrackString = NSStringFromTimeToCrack( timeToCrack );
|
||||||
|
|
||||||
|
BOOL requiresExplicitMigration = site.requiresExplicitMigration;
|
||||||
|
|
||||||
PearlMainQueue( ^{
|
PearlMainQueue( ^{
|
||||||
self.loginNameField.text = loginName;
|
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.loginNameButton.titleLabel.alpha = [loginName length] || self.loginNameField.enabled? 0: 1;
|
||||||
|
|
||||||
if ([password length])
|
if (![password length]) {
|
||||||
self.indicatorView.alpha = 0;
|
|
||||||
else {
|
|
||||||
self.indicatorView.alpha = 1;
|
self.indicatorView.alpha = 1;
|
||||||
[self.indicatorView removeFromSuperview];
|
[self.indicatorView removeFromSuperview];
|
||||||
[self.modeScrollView addSubview:self.indicatorView];
|
[self.modeScrollView addSubview:self.indicatorView];
|
||||||
@@ -551,6 +576,18 @@
|
|||||||
@"target" : settingsMode? self.editButton: self.modeButton
|
@"target" : settingsMode? self.editButton: self.modeButton
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
else if (requiresExplicitMigration) {
|
||||||
|
self.indicatorView.alpha = 1;
|
||||||
|
[self.indicatorView removeFromSuperview];
|
||||||
|
[self.modeScrollView addSubview:self.indicatorView];
|
||||||
|
[self.contentView addConstraintsWithVisualFormat:@"V:[indicator][target]" options:NSLayoutFormatAlignAllCenterX
|
||||||
|
metrics:nil views:@{
|
||||||
|
@"indicator" : self.indicatorView,
|
||||||
|
@"target" : settingsMode? self.upgradeButton: self.modeButton
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
self.indicatorView.alpha = 0;
|
||||||
} );
|
} );
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#import "MPAppDelegate_Key.h"
|
#import "MPAppDelegate_Key.h"
|
||||||
#import "MPPasswordCell.h"
|
#import "MPPasswordCell.h"
|
||||||
#import "MPAnswersViewController.h"
|
#import "MPAnswersViewController.h"
|
||||||
|
#import "MPMessageViewController.h"
|
||||||
|
|
||||||
typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||||
MPPasswordsBadNameTip = 1 << 0,
|
MPPasswordsBadNameTip = 1 << 0,
|
||||||
@@ -90,7 +91,13 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
|||||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserInContext:context];
|
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserInContext:context];
|
||||||
if (![MPAlgorithmDefault tryMigrateUser:activeUser inContext:context])
|
if (![MPAlgorithmDefault tryMigrateUser:activeUser inContext:context])
|
||||||
[PearlOverlay showTemporaryOverlayWithTitle:@"Some Sites Need Upgrade" dismissAfter:2];
|
[self performSegueWithIdentifier:@"message" sender:
|
||||||
|
[MPMessage messageWithTitle:@"You have sites that can be upgraded." text:
|
||||||
|
@"Upgrading a site allows it to take advantage of the latest improvements in the Master Password algorithm.\n\n"
|
||||||
|
"When you upgrade a site, a new and stronger password will be generated for it. To upgrade a site, first log into the site, navigate to your account preferences where you can change the site's password. Make sure you fill in any \"current password\" fields on the website first, then press the upgrade button here to get your new site password.\n\n"
|
||||||
|
"You can then update your site's account with the new and stronger password.\n\n"
|
||||||
|
"The upgrade button can be found in the site's settings and looks like this:"
|
||||||
|
info:YES]];
|
||||||
[context saveToStore];
|
[context saveToStore];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -109,6 +116,8 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
|||||||
if ([segue.identifier isEqualToString:@"answers"])
|
if ([segue.identifier isEqualToString:@"answers"])
|
||||||
((MPAnswersViewController *)segue.destinationViewController).site =
|
((MPAnswersViewController *)segue.destinationViewController).site =
|
||||||
[[MPPasswordCell findAsSuperviewOf:sender] siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
|
[[MPPasswordCell findAsSuperviewOf:sender] siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
|
||||||
|
if ([segue.identifier isEqualToString:@"message"])
|
||||||
|
((MPMessageViewController *)segue.destinationViewController).message = sender;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
|
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
|
||||||
@@ -383,7 +392,12 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
|||||||
} );
|
} );
|
||||||
|
|
||||||
NSString *queryString = self.query;
|
NSString *queryString = self.query;
|
||||||
NSString *queryPattern = [queryString stringByReplacingMatchesOfExpression:fuzzyRE withTemplate:@"*$1*"];
|
NSString *queryPattern;
|
||||||
|
if ([queryString length] < 13)
|
||||||
|
queryPattern = [queryString stringByReplacingMatchesOfExpression:fuzzyRE withTemplate:@"*$1*"];
|
||||||
|
else
|
||||||
|
// If query is too long, a wildcard per character makes the CoreData fetch take excessively long.
|
||||||
|
queryPattern = strf( @"*%@*", queryString );
|
||||||
NSMutableArray *fuzzyGroups = [NSMutableArray arrayWithCapacity:[queryString length]];
|
NSMutableArray *fuzzyGroups = [NSMutableArray arrayWithCapacity:[queryString length]];
|
||||||
[fuzzyRE enumerateMatchesInString:queryString options:0 range:NSMakeRange( 0, queryString.length )
|
[fuzzyRE enumerateMatchesInString:queryString options:0 range:NSMakeRange( 0, queryString.length )
|
||||||
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
|
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
|
||||||
|
|||||||
@@ -214,6 +214,7 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
|
|||||||
MPUserEntity *user = [self userForAvatar:avatarCell inContext:context isNew:&isNew];
|
MPUserEntity *user = [self userForAvatar:avatarCell inContext:context isNew:&isNew];
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
user = [MPUserEntity insertNewObjectInContext:context];
|
user = [MPUserEntity insertNewObjectInContext:context];
|
||||||
|
user.algorithm = MPAlgorithmDefault;
|
||||||
user.avatar = avatarCell.avatar;
|
user.avatar = avatarCell.avatar;
|
||||||
user.name = avatarCell.name;
|
user.name = avatarCell.name;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,9 +73,9 @@
|
|||||||
PearlAddNotificationObserver( MPCheckConfigNotification, nil, [NSOperationQueue mainQueue], ^(id self, NSNotification *note) {
|
PearlAddNotificationObserver( MPCheckConfigNotification, nil, [NSOperationQueue mainQueue], ^(id self, NSNotification *note) {
|
||||||
[self updateConfigKey:note.object];
|
[self updateConfigKey:note.object];
|
||||||
} );
|
} );
|
||||||
PearlAddNotificationObserver( kIASKAppSettingChanged, nil, nil, ^(id self, NSNotification *note) {
|
// PearlAddNotificationObserver( kIASKAppSettingChanged, nil, nil, ^(id self, NSNotification *note) {
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:note.object];
|
// [[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:note.object];
|
||||||
} );
|
// } );
|
||||||
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];
|
||||||
} );
|
} );
|
||||||
@@ -496,7 +496,7 @@
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
[moc performBlockAndWait:^{
|
[moc performBlockAndWait:^{
|
||||||
inf( @"Unsetting master password for: %@.", user.userID );
|
inf( @"Clearing keyID for user: %@.", user.userID );
|
||||||
user.keyID = nil;
|
user.keyID = nil;
|
||||||
[self forgetSavedKeyFor:user];
|
[self forgetSavedKeyFor:user];
|
||||||
[moc saveToStore];
|
[moc saveToStore];
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
@property(nonatomic, retain) NSNumber *loginNameTipShown;
|
@property(nonatomic, retain) NSNumber *loginNameTipShown;
|
||||||
@property(nonatomic, retain) NSNumber *traceMode;
|
@property(nonatomic, retain) NSNumber *traceMode;
|
||||||
@property(nonatomic, retain) NSNumber *dictationSearch;
|
@property(nonatomic, retain) NSNumber *dictationSearch;
|
||||||
|
@property(nonatomic, retain) NSNumber *allowDowngrade;
|
||||||
@property(nonatomic, retain) NSNumber *developmentFuelRemaining;
|
@property(nonatomic, retain) NSNumber *developmentFuelRemaining;
|
||||||
@property(nonatomic, retain) NSNumber *developmentFuelInvested;
|
@property(nonatomic, retain) NSNumber *developmentFuelInvested;
|
||||||
@property(nonatomic, retain) NSNumber *developmentFuelConsumption;
|
@property(nonatomic, retain) NSNumber *developmentFuelConsumption;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
@implementation MPiOSConfig
|
@implementation MPiOSConfig
|
||||||
|
|
||||||
@dynamic helpHidden, siteInfoHidden, showSetup, actionsTipShown, typeTipShown, loginNameTipShown, traceMode, dictationSearch;
|
@dynamic helpHidden, siteInfoHidden, showSetup, actionsTipShown, typeTipShown, loginNameTipShown, traceMode, dictationSearch, allowDowngrade;
|
||||||
@dynamic developmentFuelRemaining, developmentFuelInvested, developmentFuelConsumption, developmentFuelChecked;
|
@dynamic developmentFuelRemaining, developmentFuelInvested, developmentFuelConsumption, developmentFuelChecked;
|
||||||
|
|
||||||
- (id)init {
|
- (id)init {
|
||||||
@@ -26,6 +26,7 @@
|
|||||||
NSStringFromSelector( @selector( loginNameTipShown ) ) : @NO,
|
NSStringFromSelector( @selector( loginNameTipShown ) ) : @NO,
|
||||||
NSStringFromSelector( @selector( traceMode ) ) : @NO,
|
NSStringFromSelector( @selector( traceMode ) ) : @NO,
|
||||||
NSStringFromSelector( @selector( dictationSearch ) ) : @NO,
|
NSStringFromSelector( @selector( dictationSearch ) ) : @NO,
|
||||||
|
NSStringFromSelector( @selector( allowDowngrade ) ) : @NO,
|
||||||
}];
|
}];
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
93D393DB5325820241BA90A7 /* PearlSizedTextView.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39A4759186F6D2D34AA6B /* PearlSizedTextView.h */; };
|
93D393DB5325820241BA90A7 /* PearlSizedTextView.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39A4759186F6D2D34AA6B /* PearlSizedTextView.h */; };
|
||||||
93D394982CBD25D46692DD7C /* MPWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3990E0CD1B5CF9FBB2C07 /* MPWebViewController.m */; };
|
93D394982CBD25D46692DD7C /* MPWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3990E0CD1B5CF9FBB2C07 /* MPWebViewController.m */; };
|
||||||
93D394B5036C882B33C71872 /* MPPasswordsSegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39E7A12CC352B2825AA66 /* MPPasswordsSegue.m */; };
|
93D394B5036C882B33C71872 /* MPPasswordsSegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39E7A12CC352B2825AA66 /* MPPasswordsSegue.m */; };
|
||||||
|
93D39508A6814612A5B3C226 /* MPMessageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399B36CDB2004D7C51391 /* MPMessageViewController.m */; };
|
||||||
93D39536EB550E811CCD04BC /* UIResponder+PearlFirstResponder.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D394482BB07F90E8FD1314 /* UIResponder+PearlFirstResponder.h */; };
|
93D39536EB550E811CCD04BC /* UIResponder+PearlFirstResponder.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D394482BB07F90E8FD1314 /* UIResponder+PearlFirstResponder.h */; };
|
||||||
93D3954E96236384AFA00453 /* UIScrollView+PearlAdjustInsets.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D390FB3110DCCE68E600DC /* UIScrollView+PearlAdjustInsets.m */; };
|
93D3954E96236384AFA00453 /* UIScrollView+PearlAdjustInsets.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D390FB3110DCCE68E600DC /* UIScrollView+PearlAdjustInsets.m */; };
|
||||||
93D3954FCE045A3CC7E804B7 /* MPUsersViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399E571F61E50A9BF8FAF /* MPUsersViewController.m */; };
|
93D3954FCE045A3CC7E804B7 /* MPUsersViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399E571F61E50A9BF8FAF /* MPUsersViewController.m */; };
|
||||||
@@ -179,6 +180,8 @@
|
|||||||
DA7304E5194E025900E72520 /* tip_basic_black.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38901711E29700CF925C /* tip_basic_black.png */; };
|
DA7304E5194E025900E72520 /* tip_basic_black.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38901711E29700CF925C /* tip_basic_black.png */; };
|
||||||
DA7304E6194E025900E72520 /* tip_basic_black@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38911711E29700CF925C /* tip_basic_black@2x.png */; };
|
DA7304E6194E025900E72520 /* tip_basic_black@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38911711E29700CF925C /* tip_basic_black@2x.png */; };
|
||||||
DA7304E7194E027C00E72520 /* Square-bottom.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E5C3C1723681B003798D8 /* Square-bottom.png */; };
|
DA7304E7194E027C00E72520 /* Square-bottom.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E5C3C1723681B003798D8 /* Square-bottom.png */; };
|
||||||
|
DA8495311A93049300B3053D /* icon_down.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD375C1711E29500CF925C /* icon_down.png */; };
|
||||||
|
DA8495321A93049300B3053D /* icon_down@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD375D1711E29500CF925C /* icon_down@2x.png */; };
|
||||||
DA854C8318D4CFBF00106317 /* avatar-add@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA854C8118D4CFBF00106317 /* avatar-add@2x.png */; };
|
DA854C8318D4CFBF00106317 /* avatar-add@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA854C8118D4CFBF00106317 /* avatar-add@2x.png */; };
|
||||||
DA854C8418D4CFBF00106317 /* avatar-add.png in Resources */ = {isa = PBXBuildFile; fileRef = DA854C8218D4CFBF00106317 /* avatar-add.png */; };
|
DA854C8418D4CFBF00106317 /* avatar-add.png in Resources */ = {isa = PBXBuildFile; fileRef = DA854C8218D4CFBF00106317 /* avatar-add.png */; };
|
||||||
DA945C8717E3F3FD0053236B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DA945C8617E3F3FD0053236B /* Images.xcassets */; };
|
DA945C8717E3F3FD0053236B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DA945C8617E3F3FD0053236B /* Images.xcassets */; };
|
||||||
@@ -515,6 +518,7 @@
|
|||||||
93D39975CE5AEC99E3F086C7 /* MPPasswordCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordCell.h; sourceTree = "<group>"; };
|
93D39975CE5AEC99E3F086C7 /* MPPasswordCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordCell.h; sourceTree = "<group>"; };
|
||||||
93D3999693660C89A7465F4E /* MPCoachmarkViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCoachmarkViewController.h; sourceTree = "<group>"; };
|
93D3999693660C89A7465F4E /* MPCoachmarkViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCoachmarkViewController.h; sourceTree = "<group>"; };
|
||||||
93D399A8E3181B442D347CD7 /* MPAlgorithmV2.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAlgorithmV2.m; sourceTree = "<group>"; };
|
93D399A8E3181B442D347CD7 /* MPAlgorithmV2.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAlgorithmV2.m; sourceTree = "<group>"; };
|
||||||
|
93D399B36CDB2004D7C51391 /* MPMessageViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPMessageViewController.m; sourceTree = "<group>"; };
|
||||||
93D399C2F3D48E57C4803BDC /* NSPersistentStore+PearlMigration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSPersistentStore+PearlMigration.m"; sourceTree = "<group>"; };
|
93D399C2F3D48E57C4803BDC /* NSPersistentStore+PearlMigration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSPersistentStore+PearlMigration.m"; sourceTree = "<group>"; };
|
||||||
93D399E571F61E50A9BF8FAF /* MPUsersViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUsersViewController.m; sourceTree = "<group>"; };
|
93D399E571F61E50A9BF8FAF /* MPUsersViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUsersViewController.m; sourceTree = "<group>"; };
|
||||||
93D399F244BB522A317811BB /* MPFixable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPFixable.h; sourceTree = "<group>"; };
|
93D399F244BB522A317811BB /* MPFixable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPFixable.h; sourceTree = "<group>"; };
|
||||||
@@ -540,6 +544,7 @@
|
|||||||
93D39C426E03358384018E85 /* MPAnswersViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAnswersViewController.m; sourceTree = "<group>"; };
|
93D39C426E03358384018E85 /* MPAnswersViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAnswersViewController.m; sourceTree = "<group>"; };
|
||||||
93D39C44361BE57AF0B3071F /* MPPasswordsSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordsSegue.h; sourceTree = "<group>"; };
|
93D39C44361BE57AF0B3071F /* MPPasswordsSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordsSegue.h; sourceTree = "<group>"; };
|
||||||
93D39C86E984EC65DA5ACB1D /* MPAppSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAppSettingsViewController.h; sourceTree = "<group>"; };
|
93D39C86E984EC65DA5ACB1D /* MPAppSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAppSettingsViewController.h; sourceTree = "<group>"; };
|
||||||
|
93D39CB0EABD2748740992D8 /* MPMessageViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPMessageViewController.h; sourceTree = "<group>"; };
|
||||||
93D39CC01630D0421205C4C4 /* MPNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPNavigationController.m; sourceTree = "<group>"; };
|
93D39CC01630D0421205C4C4 /* MPNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPNavigationController.m; sourceTree = "<group>"; };
|
||||||
93D39CDD434AFD6E1B0DA359 /* MPEmergencyViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPEmergencyViewController.h; sourceTree = "<group>"; };
|
93D39CDD434AFD6E1B0DA359 /* MPEmergencyViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPEmergencyViewController.h; sourceTree = "<group>"; };
|
||||||
93D39CECA10BCCB0BA581BF1 /* MPAppDelegate_InApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAppDelegate_InApp.h; sourceTree = "<group>"; };
|
93D39CECA10BCCB0BA581BF1 /* MPAppDelegate_InApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAppDelegate_InApp.h; sourceTree = "<group>"; };
|
||||||
@@ -669,6 +674,7 @@
|
|||||||
DA70EC7F1811B13C00F65DB2 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; };
|
DA70EC7F1811B13C00F65DB2 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; };
|
||||||
DA72BD7419C133BF00E6ACFE /* libscryptenc-ios-sim.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libscryptenc-ios-sim.a"; sourceTree = "<group>"; };
|
DA72BD7419C133BF00E6ACFE /* libscryptenc-ios-sim.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libscryptenc-ios-sim.a"; sourceTree = "<group>"; };
|
||||||
DA72BD7719C137D500E6ACFE /* libopensslcrypto-ios-dev.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libopensslcrypto-ios-dev.a"; sourceTree = "<group>"; };
|
DA72BD7719C137D500E6ACFE /* libopensslcrypto-ios-dev.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libopensslcrypto-ios-dev.a"; sourceTree = "<group>"; };
|
||||||
|
DA8495271A9146E600B3053D /* MasterPassword 7.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 7.xcdatamodel"; sourceTree = "<group>"; };
|
||||||
DA854C8118D4CFBF00106317 /* avatar-add@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "avatar-add@2x.png"; sourceTree = "<group>"; };
|
DA854C8118D4CFBF00106317 /* avatar-add@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "avatar-add@2x.png"; sourceTree = "<group>"; };
|
||||||
DA854C8218D4CFBF00106317 /* avatar-add.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "avatar-add.png"; sourceTree = "<group>"; };
|
DA854C8218D4CFBF00106317 /* avatar-add.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "avatar-add.png"; sourceTree = "<group>"; };
|
||||||
DA945C8617E3F3FD0053236B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
|
DA945C8617E3F3FD0053236B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
|
||||||
@@ -1745,6 +1751,8 @@
|
|||||||
93D3924D6F77E6BF41AC32D3 /* MPRootSegue.h */,
|
93D3924D6F77E6BF41AC32D3 /* MPRootSegue.h */,
|
||||||
93D39C41A27AA42D044D68AE /* NSString+MPMarkDown.m */,
|
93D39C41A27AA42D044D68AE /* NSString+MPMarkDown.m */,
|
||||||
93D393CB0B1F4EC8C17CFE43 /* NSString+MPMarkDown.h */,
|
93D393CB0B1F4EC8C17CFE43 /* NSString+MPMarkDown.h */,
|
||||||
|
93D399B36CDB2004D7C51391 /* MPMessageViewController.m */,
|
||||||
|
93D39CB0EABD2748740992D8 /* MPMessageViewController.h */,
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
@@ -2606,55 +2614,55 @@
|
|||||||
DABD3BD71711E2DC00CF925C /* iOS */ = {
|
DABD3BD71711E2DC00CF925C /* iOS */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
DA38D6A218CCB5BF009AEB3E /* Storyboard.storyboard */,
|
DABD3BFA1711E2DC00CF925C /* InfoPlist.strings */,
|
||||||
93D39975CE5AEC99E3F086C7 /* MPPasswordCell.h */,
|
DABD3BFC1711E2DC00CF925C /* main.m */,
|
||||||
93D39DEA995041A13DC9CAF7 /* MPPasswordCell.m */,
|
|
||||||
93D39ACBA9F4878B6A1CC33B /* MPEmergencyViewController.m */,
|
|
||||||
93D39CDD434AFD6E1B0DA359 /* MPEmergencyViewController.h */,
|
|
||||||
DABD3BD81711E2DC00CF925C /* MPiOSAppDelegate.h */,
|
|
||||||
DABD3BD91711E2DC00CF925C /* MPiOSAppDelegate.m */,
|
|
||||||
DABD3BE61711E2DC00CF925C /* MPGuideViewController.h */,
|
|
||||||
DABD3BE71711E2DC00CF925C /* MPGuideViewController.m */,
|
|
||||||
DABD3BEA1711E2DC00CF925C /* MPPreferencesViewController.h */,
|
|
||||||
DABD3BEB1711E2DC00CF925C /* MPPreferencesViewController.m */,
|
|
||||||
DABD3BEC1711E2DC00CF925C /* MPTypeViewController.h */,
|
|
||||||
DABD3BED1711E2DC00CF925C /* MPTypeViewController.m */,
|
|
||||||
DABD3BF01711E2DC00CF925C /* MPiOSConfig.h */,
|
|
||||||
DABD3BF11711E2DC00CF925C /* MPiOSConfig.m */,
|
|
||||||
DABD3BF31711E2DC00CF925C /* MasterPassword-Info.plist */,
|
DABD3BF31711E2DC00CF925C /* MasterPassword-Info.plist */,
|
||||||
DABD3BF41711E2DC00CF925C /* MasterPassword-Prefix.pch */,
|
DABD3BF41711E2DC00CF925C /* MasterPassword-Prefix.pch */,
|
||||||
DABD3BF81711E2DC00CF925C /* MasterPassword.entitlements */,
|
DABD3BF81711E2DC00CF925C /* MasterPassword.entitlements */,
|
||||||
DABD3BF91711E2DC00CF925C /* Settings.bundle */,
|
|
||||||
DABD3BFA1711E2DC00CF925C /* InfoPlist.strings */,
|
|
||||||
DABD3BFC1711E2DC00CF925C /* main.m */,
|
|
||||||
93D39A28369954D147E239BA /* MPSetupViewController.m */,
|
|
||||||
93D39730673227EFF6DEFF19 /* MPSetupViewController.h */,
|
|
||||||
93D3979190DACEBD1F6AE9F4 /* MPLogsViewController.m */,
|
|
||||||
93D391943675426839501BB8 /* MPLogsViewController.h */,
|
|
||||||
93D3924EE15017F8A12CB436 /* MPPasswordsViewController.m */,
|
|
||||||
93D3914D7597F9A28DB9D85E /* MPPasswordsViewController.h */,
|
|
||||||
93D393310223DDB35218467A /* MPCombinedViewController.m */,
|
|
||||||
93D39CF8ADF4542CDC4CD385 /* MPCombinedViewController.h */,
|
|
||||||
93D39B381350802A194BF332 /* MPAvatarCell.m */,
|
|
||||||
93D39DA27D768B53C8B1330C /* MPAvatarCell.h */,
|
|
||||||
93D399E571F61E50A9BF8FAF /* MPUsersViewController.m */,
|
|
||||||
93D3971FE104BB4052484151 /* MPUsersViewController.h */,
|
|
||||||
93D39BAA71DE51B4D8A1286C /* MPCell.m */,
|
|
||||||
93D390519405B76CC6A57C4F /* MPCell.h */,
|
|
||||||
93D39E7A12CC352B2825AA66 /* MPPasswordsSegue.m */,
|
|
||||||
93D39C44361BE57AF0B3071F /* MPPasswordsSegue.h */,
|
|
||||||
93D39B050DD5F55E9794EFD4 /* MPPopdownSegue.m */,
|
|
||||||
93D392876BE5C011DE73B43F /* MPPopdownSegue.h */,
|
|
||||||
93D3916C1D8F1427DFBDEBCA /* MPAppSettingsViewController.m */,
|
|
||||||
93D39C86E984EC65DA5ACB1D /* MPAppSettingsViewController.h */,
|
93D39C86E984EC65DA5ACB1D /* MPAppSettingsViewController.h */,
|
||||||
93D3995B1D4DCE5A30D882BA /* MPCoachmarkViewController.m */,
|
93D3916C1D8F1427DFBDEBCA /* MPAppSettingsViewController.m */,
|
||||||
|
93D39DA27D768B53C8B1330C /* MPAvatarCell.h */,
|
||||||
|
93D39B381350802A194BF332 /* MPAvatarCell.m */,
|
||||||
|
93D390519405B76CC6A57C4F /* MPCell.h */,
|
||||||
|
93D39BAA71DE51B4D8A1286C /* MPCell.m */,
|
||||||
93D3999693660C89A7465F4E /* MPCoachmarkViewController.h */,
|
93D3999693660C89A7465F4E /* MPCoachmarkViewController.h */,
|
||||||
93D3990E0CD1B5CF9FBB2C07 /* MPWebViewController.m */,
|
93D3995B1D4DCE5A30D882BA /* MPCoachmarkViewController.m */,
|
||||||
93D39F556F2F142740A65E59 /* MPWebViewController.h */,
|
93D39CF8ADF4542CDC4CD385 /* MPCombinedViewController.h */,
|
||||||
93D39CC01630D0421205C4C4 /* MPNavigationController.m */,
|
93D393310223DDB35218467A /* MPCombinedViewController.m */,
|
||||||
|
93D39CDD434AFD6E1B0DA359 /* MPEmergencyViewController.h */,
|
||||||
|
93D39ACBA9F4878B6A1CC33B /* MPEmergencyViewController.m */,
|
||||||
|
DABD3BE61711E2DC00CF925C /* MPGuideViewController.h */,
|
||||||
|
DABD3BE71711E2DC00CF925C /* MPGuideViewController.m */,
|
||||||
|
DABD3BD81711E2DC00CF925C /* MPiOSAppDelegate.h */,
|
||||||
|
DABD3BD91711E2DC00CF925C /* MPiOSAppDelegate.m */,
|
||||||
|
DABD3BF01711E2DC00CF925C /* MPiOSConfig.h */,
|
||||||
|
DABD3BF11711E2DC00CF925C /* MPiOSConfig.m */,
|
||||||
|
93D391943675426839501BB8 /* MPLogsViewController.h */,
|
||||||
|
93D3979190DACEBD1F6AE9F4 /* MPLogsViewController.m */,
|
||||||
93D3970502644794E8A027BE /* MPNavigationController.h */,
|
93D3970502644794E8A027BE /* MPNavigationController.h */,
|
||||||
93D395105935859D71679931 /* MPOverlayViewController.m */,
|
93D39CC01630D0421205C4C4 /* MPNavigationController.m */,
|
||||||
93D39B455A71EC98C749E623 /* MPOverlayViewController.h */,
|
93D39B455A71EC98C749E623 /* MPOverlayViewController.h */,
|
||||||
|
93D395105935859D71679931 /* MPOverlayViewController.m */,
|
||||||
|
93D39975CE5AEC99E3F086C7 /* MPPasswordCell.h */,
|
||||||
|
93D39DEA995041A13DC9CAF7 /* MPPasswordCell.m */,
|
||||||
|
93D39C44361BE57AF0B3071F /* MPPasswordsSegue.h */,
|
||||||
|
93D39E7A12CC352B2825AA66 /* MPPasswordsSegue.m */,
|
||||||
|
93D3914D7597F9A28DB9D85E /* MPPasswordsViewController.h */,
|
||||||
|
93D3924EE15017F8A12CB436 /* MPPasswordsViewController.m */,
|
||||||
|
93D392876BE5C011DE73B43F /* MPPopdownSegue.h */,
|
||||||
|
93D39B050DD5F55E9794EFD4 /* MPPopdownSegue.m */,
|
||||||
|
DABD3BEA1711E2DC00CF925C /* MPPreferencesViewController.h */,
|
||||||
|
DABD3BEB1711E2DC00CF925C /* MPPreferencesViewController.m */,
|
||||||
|
93D39730673227EFF6DEFF19 /* MPSetupViewController.h */,
|
||||||
|
93D39A28369954D147E239BA /* MPSetupViewController.m */,
|
||||||
|
DABD3BEC1711E2DC00CF925C /* MPTypeViewController.h */,
|
||||||
|
DABD3BED1711E2DC00CF925C /* MPTypeViewController.m */,
|
||||||
|
93D3971FE104BB4052484151 /* MPUsersViewController.h */,
|
||||||
|
93D399E571F61E50A9BF8FAF /* MPUsersViewController.m */,
|
||||||
|
93D39F556F2F142740A65E59 /* MPWebViewController.h */,
|
||||||
|
93D3990E0CD1B5CF9FBB2C07 /* MPWebViewController.m */,
|
||||||
|
DABD3BF91711E2DC00CF925C /* Settings.bundle */,
|
||||||
|
DA38D6A218CCB5BF009AEB3E /* Storyboard.storyboard */,
|
||||||
);
|
);
|
||||||
path = iOS;
|
path = iOS;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -3503,6 +3511,7 @@
|
|||||||
DABD3B961711E29800CF925C /* pull-down@2x.png in Resources */,
|
DABD3B961711E29800CF925C /* pull-down@2x.png in Resources */,
|
||||||
DABD3B971711E29800CF925C /* pull-up.png in Resources */,
|
DABD3B971711E29800CF925C /* pull-up.png in Resources */,
|
||||||
DA24EBEA19DAD6EE00FF010B /* Icon-Small.png in Resources */,
|
DA24EBEA19DAD6EE00FF010B /* Icon-Small.png in Resources */,
|
||||||
|
DA8495321A93049300B3053D /* icon_down@2x.png in Resources */,
|
||||||
DABD3B981711E29800CF925C /* pull-up@2x.png in Resources */,
|
DABD3B981711E29800CF925C /* pull-up@2x.png in Resources */,
|
||||||
DA7304A0194E022B00E72520 /* ui_textfield@2x.png in Resources */,
|
DA7304A0194E022B00E72520 /* ui_textfield@2x.png in Resources */,
|
||||||
DAA1765119D8B82B0044227B /* copy_pw@2x.png in Resources */,
|
DAA1765119D8B82B0044227B /* copy_pw@2x.png in Resources */,
|
||||||
@@ -3526,6 +3535,7 @@
|
|||||||
DABD3FCF1714F45C00CF925C /* identity@2x.png in Resources */,
|
DABD3FCF1714F45C00CF925C /* identity@2x.png in Resources */,
|
||||||
DAA1764619D8B82B0044227B /* name_new.png in Resources */,
|
DAA1764619D8B82B0044227B /* name_new.png in Resources */,
|
||||||
DA45224B190628B2008F650A /* icon_gear.png in Resources */,
|
DA45224B190628B2008F650A /* icon_gear.png in Resources */,
|
||||||
|
DA8495311A93049300B3053D /* icon_down.png in Resources */,
|
||||||
DA25C5FF197DBF200046CDCF /* icon_thumbs-up@2x.png in Resources */,
|
DA25C5FF197DBF200046CDCF /* icon_thumbs-up@2x.png in Resources */,
|
||||||
DAE1EF2217E942DE00BC0086 /* Localizable.strings in Resources */,
|
DAE1EF2217E942DE00BC0086 /* Localizable.strings in Resources */,
|
||||||
DA38D6A318CCB5BF009AEB3E /* Storyboard.storyboard in Resources */,
|
DA38D6A318CCB5BF009AEB3E /* Storyboard.storyboard in Resources */,
|
||||||
@@ -3656,6 +3666,7 @@
|
|||||||
93D39943D01E70DAC3B0DF76 /* mpw-util.c in Sources */,
|
93D39943D01E70DAC3B0DF76 /* mpw-util.c in Sources */,
|
||||||
93D39577FD8BB0945DB2F0A3 /* MPAlgorithmV3.m in Sources */,
|
93D39577FD8BB0945DB2F0A3 /* MPAlgorithmV3.m in Sources */,
|
||||||
93D39E5F7F6D7F5C0FAD090F /* MPTypes.m in Sources */,
|
93D39E5F7F6D7F5C0FAD090F /* MPTypes.m in Sources */,
|
||||||
|
93D39508A6814612A5B3C226 /* MPMessageViewController.m in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -4423,6 +4434,7 @@
|
|||||||
DA32D00119CF4735004F3F0E /* MasterPassword.xcdatamodeld */ = {
|
DA32D00119CF4735004F3F0E /* MasterPassword.xcdatamodeld */ = {
|
||||||
isa = XCVersionGroup;
|
isa = XCVersionGroup;
|
||||||
children = (
|
children = (
|
||||||
|
DA8495271A9146E600B3053D /* MasterPassword 7.xcdatamodel */,
|
||||||
DA32D00219CF4735004F3F0E /* MasterPassword 1.xcdatamodel */,
|
DA32D00219CF4735004F3F0E /* MasterPassword 1.xcdatamodel */,
|
||||||
DA32D00319CF4735004F3F0E /* MasterPassword 2.xcdatamodel */,
|
DA32D00319CF4735004F3F0E /* MasterPassword 2.xcdatamodel */,
|
||||||
DA32D00419CF4735004F3F0E /* MasterPassword 3.xcdatamodel */,
|
DA32D00419CF4735004F3F0E /* MasterPassword 3.xcdatamodel */,
|
||||||
@@ -4430,7 +4442,7 @@
|
|||||||
DA32D00619CF4735004F3F0E /* MasterPassword 5.xcdatamodel */,
|
DA32D00619CF4735004F3F0E /* MasterPassword 5.xcdatamodel */,
|
||||||
DA32D00719CF4735004F3F0E /* MasterPassword 6.xcdatamodel */,
|
DA32D00719CF4735004F3F0E /* MasterPassword 6.xcdatamodel */,
|
||||||
);
|
);
|
||||||
currentVersion = DA32D00719CF4735004F3F0E /* MasterPassword 6.xcdatamodel */;
|
currentVersion = DA8495271A9146E600B3053D /* MasterPassword 7.xcdatamodel */;
|
||||||
path = MasterPassword.xcdatamodeld;
|
path = MasterPassword.xcdatamodeld;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
versionGroupType = wrapper.xcdatamodel;
|
versionGroupType = wrapper.xcdatamodel;
|
||||||
|
|||||||
@@ -147,6 +147,24 @@ To see a site's password anyway, tap and hold your finger down for a while
|
|||||||
<key>Type</key>
|
<key>Type</key>
|
||||||
<string>PSToggleSwitchSpecifier</string>
|
<string>PSToggleSwitchSpecifier</string>
|
||||||
</dict>
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>FooterText</key>
|
||||||
|
<string>When site downgrades are enabled, a long tap on the upgrade button will downgrade the site instead. This is useful if you accidentally upgraded a site and need to downgrade it again temporarily to see your old password.</string>
|
||||||
|
<key>Title</key>
|
||||||
|
<string></string>
|
||||||
|
<key>Type</key>
|
||||||
|
<string>PSGroupSpecifier</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>DefaultValue</key>
|
||||||
|
<false/>
|
||||||
|
<key>Key</key>
|
||||||
|
<string>allowDowngrade</string>
|
||||||
|
<key>Title</key>
|
||||||
|
<string>Allow Downgrade</string>
|
||||||
|
<key>Type</key>
|
||||||
|
<string>PSToggleSwitchSpecifier</string>
|
||||||
|
</dict>
|
||||||
<dict>
|
<dict>
|
||||||
<key>Type</key>
|
<key>Type</key>
|
||||||
<string>PSGroupSpecifier</string>
|
<string>PSGroupSpecifier</string>
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6254" systemVersion="14B25" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" initialViewController="Q1S-vU-GGO">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6254" systemVersion="14C109" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" initialViewController="Q1S-vU-GGO">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="iOS"/>
|
<deployment identifier="iOS"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6247"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6247"/>
|
||||||
<capability name="Alignment constraints with different attributes" minToolsVersion="5.1"/>
|
<capability name="Alignment constraints with different attributes" minToolsVersion="5.1"/>
|
||||||
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
|
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
|
||||||
|
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
|
||||||
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
|
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
|
||||||
<capability name="Unknown constraint types" minToolsVersion="5.1"/>
|
<capability name="Unknown constraint types" minToolsVersion="5.1"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
@@ -31,6 +32,7 @@
|
|||||||
<string>Exo2.0-Bold</string>
|
<string>Exo2.0-Bold</string>
|
||||||
<string>Exo2.0-Bold</string>
|
<string>Exo2.0-Bold</string>
|
||||||
<string>Exo2.0-Bold</string>
|
<string>Exo2.0-Bold</string>
|
||||||
|
<string>Exo2.0-Bold</string>
|
||||||
</mutableArray>
|
</mutableArray>
|
||||||
<mutableArray key="Exo2.0-ExtraBold.otf">
|
<mutableArray key="Exo2.0-ExtraBold.otf">
|
||||||
<string>Exo2.0-ExtraBold</string>
|
<string>Exo2.0-ExtraBold</string>
|
||||||
@@ -182,10 +184,10 @@
|
|||||||
<constraint firstAttribute="width" secondItem="Aca-he-7Qi" secondAttribute="height" multiplier="1:1" id="a8Q-UO-SH0"/>
|
<constraint firstAttribute="width" secondItem="Aca-he-7Qi" secondAttribute="height" multiplier="1:1" id="a8Q-UO-SH0"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</imageView>
|
</imageView>
|
||||||
<view contentMode="scaleToFill" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="0Sa-Vg-EEI" userLabel="Name Backdrop">
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="0Sa-Vg-EEI" userLabel="Name Backdrop">
|
||||||
<rect key="frame" x="43" y="263" width="128.5" height="16"/>
|
<rect key="frame" x="43" y="263" width="128.5" height="16"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalCompressionResistancePriority="1000" misplaced="YES" text="Maarten Billemont" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cLT-s0-4SQ" userLabel="Name Field">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalCompressionResistancePriority="1000" text="Maarten Billemont" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cLT-s0-4SQ" userLabel="Name Field">
|
||||||
<rect key="frame" x="5" y="0.0" width="118.5" height="16"/>
|
<rect key="frame" x="5" y="0.0" width="118.5" height="16"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-ExtraBold" family="Exo 2.0" pointSize="13"/>
|
<fontDescription key="fontDescription" name="Exo2.0-ExtraBold" family="Exo 2.0" pointSize="13"/>
|
||||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
@@ -249,7 +251,7 @@
|
|||||||
<outlet property="delegate" destination="S8q-YF-Kt9" id="det-Eh-phM"/>
|
<outlet property="delegate" destination="S8q-YF-Kt9" id="det-Eh-phM"/>
|
||||||
</connections>
|
</connections>
|
||||||
</collectionView>
|
</collectionView>
|
||||||
<button opaque="NO" alpha="0.69999999999999996" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="9u7-pu-Wtv" userLabel="Previous Avatar">
|
<button opaque="NO" alpha="0.69999999999999996" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="9u7-pu-Wtv" userLabel="Previous Avatar">
|
||||||
<rect key="frame" x="0.0" y="244" width="44" height="53"/>
|
<rect key="frame" x="0.0" y="244" width="44" height="53"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="width" constant="44" id="Ay6-Jg-c3T"/>
|
<constraint firstAttribute="width" constant="44" id="Ay6-Jg-c3T"/>
|
||||||
@@ -262,7 +264,7 @@
|
|||||||
<action selector="changeAvatar:" destination="S8q-YF-Kt9" eventType="touchUpInside" id="lNu-mK-3zD"/>
|
<action selector="changeAvatar:" destination="S8q-YF-Kt9" eventType="touchUpInside" id="lNu-mK-3zD"/>
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<button opaque="NO" alpha="0.69999999999999996" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="fUK-gJ-NRE" userLabel="Next Avatar">
|
<button opaque="NO" alpha="0.69999999999999996" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="fUK-gJ-NRE" userLabel="Next Avatar">
|
||||||
<rect key="frame" x="331" y="244" width="44" height="53"/>
|
<rect key="frame" x="331" y="244" width="44" height="53"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="width" constant="44" id="oAm-YX-Fx5"/>
|
<constraint firstAttribute="width" constant="44" id="oAm-YX-Fx5"/>
|
||||||
@@ -297,20 +299,20 @@
|
|||||||
<outlet property="delegate" destination="S8q-YF-Kt9" id="5u3-XN-LOe"/>
|
<outlet property="delegate" destination="S8q-YF-Kt9" id="5u3-XN-LOe"/>
|
||||||
</connections>
|
</connections>
|
||||||
</textField>
|
</textField>
|
||||||
<view userInteractionEnabled="NO" alpha="0.0" contentMode="scaleToFill" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fdS-zb-K9I" userLabel="Entry Tip">
|
<view userInteractionEnabled="NO" alpha="0.0" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fdS-zb-K9I" userLabel="Entry Tip">
|
||||||
<rect key="frame" x="71" y="-33" width="234.5" height="82.5"/>
|
<rect key="frame" x="71" y="-33" width="234.5" height="82.5"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" misplaced="YES" image="tip_basic_black.png" translatesAutoresizingMaskIntoConstraints="NO" id="g2g-5i-er4">
|
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="tip_basic_black.png" translatesAutoresizingMaskIntoConstraints="NO" id="g2g-5i-er4">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="234.5" height="82.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="234.5" height="82.5"/>
|
||||||
<rect key="contentStretch" x="0.15000000000000002" y="0.14999999999999999" width="0.69999999999999973" height="0.44999999999999996"/>
|
<rect key="contentStretch" x="0.15000000000000002" y="0.14999999999999999" width="0.69999999999999973" height="0.44999999999999996"/>
|
||||||
</imageView>
|
</imageView>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" misplaced="YES" text="Looks like a typo!" textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="ZI7-qg-7OW">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Looks like a typo!" textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="ZI7-qg-7OW">
|
||||||
<rect key="frame" x="20" y="12" width="194.5" height="17"/>
|
<rect key="frame" x="20" y="12" width="194.5" height="17"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
|
||||||
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
|
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" misplaced="YES" text="Try again; the password was wrong." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" minimumFontSize="10" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="KhE-Yj-Kvm">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Try again; the password was wrong." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" minimumFontSize="10" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="KhE-Yj-Kvm">
|
||||||
<rect key="frame" x="20" y="37" width="194.5" height="14.5"/>
|
<rect key="frame" x="20" y="37" width="194.5" height="14.5"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="12"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="12"/>
|
||||||
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
|
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
|
||||||
@@ -369,14 +371,14 @@
|
|||||||
<segue destination="Sd5-eW-Cx2" kind="modal" identifier="web" id="gtb-zE-u9H"/>
|
<segue destination="Sd5-eW-Cx2" kind="modal" identifier="web" id="gtb-zE-u9H"/>
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<view userInteractionEnabled="NO" alpha="0.0" contentMode="scaleToFill" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="069-Pu-yXe" userLabel="Thanks Tip">
|
<view userInteractionEnabled="NO" alpha="0.0" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="069-Pu-yXe" userLabel="Thanks Tip">
|
||||||
<rect key="frame" x="72" y="0.0" width="232.5" height="60"/>
|
<rect key="frame" x="72" y="0.0" width="232.5" height="60"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" misplaced="YES" image="tip_basic_black.png" translatesAutoresizingMaskIntoConstraints="NO" id="Z8P-ZK-aS0">
|
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="tip_basic_black.png" translatesAutoresizingMaskIntoConstraints="NO" id="Z8P-ZK-aS0">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="232.5" height="60"/>
|
<rect key="frame" x="0.0" y="0.0" width="232.5" height="60"/>
|
||||||
<rect key="contentStretch" x="0.15000000000000002" y="0.0" width="0.69999999999999973" height="1"/>
|
<rect key="contentStretch" x="0.15000000000000002" y="0.0" width="0.69999999999999973" height="1"/>
|
||||||
</imageView>
|
</imageView>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" misplaced="YES" text="Why is Master Password free?" textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="BLV-3x-Q0z">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Why is Master Password free?" textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="BLV-3x-Q0z">
|
||||||
<rect key="frame" x="20" y="12" width="192.5" height="17"/>
|
<rect key="frame" x="20" y="12" width="192.5" height="17"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
|
||||||
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
|
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
|
||||||
@@ -407,14 +409,14 @@
|
|||||||
<userDefinedRuntimeAttribute type="boolean" keyPath="ignoreTouches" value="YES"/>
|
<userDefinedRuntimeAttribute type="boolean" keyPath="ignoreTouches" value="YES"/>
|
||||||
</userDefinedRuntimeAttributes>
|
</userDefinedRuntimeAttributes>
|
||||||
</view>
|
</view>
|
||||||
<view userInteractionEnabled="NO" alpha="0.0" contentMode="scaleToFill" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cF4-TE-GEj" userLabel="Avatar Tip">
|
<view userInteractionEnabled="NO" alpha="0.0" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="cF4-TE-GEj" userLabel="Avatar Tip">
|
||||||
<rect key="frame" x="49.5" y="184" width="276" height="60"/>
|
<rect key="frame" x="49.5" y="184" width="276" height="60"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" misplaced="YES" image="tip_basic_black.png" translatesAutoresizingMaskIntoConstraints="NO" id="V4W-bK-age">
|
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="tip_basic_black.png" translatesAutoresizingMaskIntoConstraints="NO" id="V4W-bK-age">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="276" height="60"/>
|
<rect key="frame" x="0.0" y="0.0" width="276" height="60"/>
|
||||||
<rect key="contentStretch" x="0.15000000000000002" y="0.0" width="0.69999999999999973" height="1"/>
|
<rect key="contentStretch" x="0.15000000000000002" y="0.0" width="0.69999999999999973" height="1"/>
|
||||||
</imageView>
|
</imageView>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" misplaced="YES" text="Change your avatar using the arrows." textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="MoM-8d-jlm">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Change your avatar using the arrows." textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="MoM-8d-jlm">
|
||||||
<rect key="frame" x="20" y="12" width="236" height="17"/>
|
<rect key="frame" x="20" y="12" width="236" height="17"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
|
||||||
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
|
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
|
||||||
@@ -432,14 +434,14 @@
|
|||||||
<constraint firstAttribute="trailing" secondItem="MoM-8d-jlm" secondAttribute="trailing" constant="20" id="Hc3-Mb-x5c"/>
|
<constraint firstAttribute="trailing" secondItem="MoM-8d-jlm" secondAttribute="trailing" constant="20" id="Hc3-Mb-x5c"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
<view userInteractionEnabled="NO" alpha="0.0" contentMode="scaleToFill" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="0Um-Ot-hI6" userLabel="Preferences Tip">
|
<view userInteractionEnabled="NO" alpha="0.0" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="0Um-Ot-hI6" userLabel="Preferences Tip">
|
||||||
<rect key="frame" x="72" y="42" width="230" height="60"/>
|
<rect key="frame" x="72" y="42" width="230" height="60"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" misplaced="YES" image="tip_basic_black_top.png" translatesAutoresizingMaskIntoConstraints="NO" id="5H0-ml-Uso">
|
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="tip_basic_black_top.png" translatesAutoresizingMaskIntoConstraints="NO" id="5H0-ml-Uso">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="230" height="60"/>
|
<rect key="frame" x="0.0" y="0.0" width="230" height="60"/>
|
||||||
<rect key="contentStretch" x="0.15000000000000002" y="0.0" width="0.69999999999999973" height="1"/>
|
<rect key="contentStretch" x="0.15000000000000002" y="0.0" width="0.69999999999999973" height="1"/>
|
||||||
</imageView>
|
</imageView>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" misplaced="YES" text="Tap for preferences and more." textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="Er5-X1-ejQ">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Tap for preferences and more." textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="Er5-X1-ejQ">
|
||||||
<rect key="frame" x="20" y="31.5" width="190" height="17"/>
|
<rect key="frame" x="20" y="31.5" width="190" height="17"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
|
||||||
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
|
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
|
||||||
@@ -847,7 +849,7 @@
|
|||||||
<constraint firstAttribute="height" constant="110" id="zBf-EA-iDN"/>
|
<constraint firstAttribute="height" constant="110" id="zBf-EA-iDN"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</imageView>
|
</imageView>
|
||||||
<button opaque="NO" alpha="0.69999998807907104" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="DzC-Ts-gew" userLabel="Previous Avatar">
|
<button opaque="NO" alpha="0.69999998807907104" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="DzC-Ts-gew" userLabel="Previous Avatar">
|
||||||
<rect key="frame" x="20" y="117" width="44" height="53"/>
|
<rect key="frame" x="20" y="117" width="44" height="53"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="44" id="1Wu-gS-flK"/>
|
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="44" id="1Wu-gS-flK"/>
|
||||||
@@ -861,7 +863,7 @@
|
|||||||
<action selector="previousAvatar:" destination="JFc-sj-awD" eventType="touchUpInside" id="D92-6I-zFd"/>
|
<action selector="previousAvatar:" destination="JFc-sj-awD" eventType="touchUpInside" id="D92-6I-zFd"/>
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<button opaque="NO" alpha="0.69999998807907104" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="yAf-fc-SKl" userLabel="Next Avatar">
|
<button opaque="NO" alpha="0.69999998807907104" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="yAf-fc-SKl" userLabel="Next Avatar">
|
||||||
<rect key="frame" x="311" y="117" width="44" height="53"/>
|
<rect key="frame" x="311" y="117" width="44" height="53"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="44" id="pEf-Vp-D7s"/>
|
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="44" id="pEf-Vp-D7s"/>
|
||||||
@@ -1040,13 +1042,13 @@
|
|||||||
<rect key="frame" x="0.0" y="0.0" width="320" height="152"/>
|
<rect key="frame" x="0.0" y="0.0" width="320" height="152"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" ambiguous="YES" misplaced="YES" text="© 2012-2014, Maarten Billemont (lhunath)" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sPw-mV-mFF">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" text="© 2012-2014, Maarten Billemont (lhunath)" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sPw-mV-mFF">
|
||||||
<rect key="frame" x="20" y="4" width="335" height="12"/>
|
<rect key="frame" x="20" y="4" width="335" height="12"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Thin" family="Exo 2.0" pointSize="10"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Thin" family="Exo 2.0" pointSize="10"/>
|
||||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" ambiguous="YES" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Rl7-cr-FHf">
|
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Rl7-cr-FHf">
|
||||||
<rect key="frame" x="20" y="24" width="335" height="26"/>
|
<rect key="frame" x="20" y="24" width="335" height="26"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="11"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="11"/>
|
||||||
<state key="normal" title="Home Page">
|
<state key="normal" title="Home Page">
|
||||||
@@ -1056,7 +1058,7 @@
|
|||||||
<action selector="homePageButton:" destination="JFc-sj-awD" eventType="touchUpInside" id="ptD-cv-NMr"/>
|
<action selector="homePageButton:" destination="JFc-sj-awD" eventType="touchUpInside" id="ptD-cv-NMr"/>
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" ambiguous="YES" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="epW-Rm-9St">
|
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="epW-Rm-9St">
|
||||||
<rect key="frame" x="20" y="58" width="335" height="26"/>
|
<rect key="frame" x="20" y="58" width="335" height="26"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="11"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="11"/>
|
||||||
<state key="normal" title="Understanding Master Password's Security">
|
<state key="normal" title="Understanding Master Password's Security">
|
||||||
@@ -1066,7 +1068,7 @@
|
|||||||
<action selector="securityButton:" destination="JFc-sj-awD" eventType="touchUpInside" id="Efv-cp-Xfh"/>
|
<action selector="securityButton:" destination="JFc-sj-awD" eventType="touchUpInside" id="Efv-cp-Xfh"/>
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" ambiguous="YES" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="LTN-ch-h8D">
|
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="LTN-ch-h8D">
|
||||||
<rect key="frame" x="20" y="92" width="335" height="26"/>
|
<rect key="frame" x="20" y="92" width="335" height="26"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="11"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="11"/>
|
||||||
<state key="normal" title="Get the Master Password source code">
|
<state key="normal" title="Get the Master Password source code">
|
||||||
@@ -1076,7 +1078,7 @@
|
|||||||
<action selector="sourceButton:" destination="JFc-sj-awD" eventType="touchUpInside" id="Y3O-di-CZo"/>
|
<action selector="sourceButton:" destination="JFc-sj-awD" eventType="touchUpInside" id="Y3O-di-CZo"/>
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" ambiguous="YES" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Z60-lc-Nka">
|
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Z60-lc-Nka">
|
||||||
<rect key="frame" x="20" y="126" width="335" height="26"/>
|
<rect key="frame" x="20" y="126" width="335" height="26"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="11"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="11"/>
|
||||||
<state key="normal" title="Send Thanks">
|
<state key="normal" title="Send Thanks">
|
||||||
@@ -1158,7 +1160,7 @@
|
|||||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||||
<inset key="scrollIndicatorInsets" minX="0.0" minY="108" maxX="0.0" maxY="0.0"/>
|
<inset key="scrollIndicatorInsets" minX="0.0" minY="108" maxX="0.0" maxY="0.0"/>
|
||||||
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="Mv1-29-TWx">
|
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="Mv1-29-TWx">
|
||||||
<size key="itemSize" width="300" height="100"/>
|
<size key="itemSize" width="355" height="100"/>
|
||||||
<size key="headerReferenceSize" width="0.0" height="0.0"/>
|
<size key="headerReferenceSize" width="0.0" height="0.0"/>
|
||||||
<size key="footerReferenceSize" width="0.0" height="0.0"/>
|
<size key="footerReferenceSize" width="0.0" height="0.0"/>
|
||||||
<inset key="sectionInset" minX="10" minY="118" maxX="10" maxY="10"/>
|
<inset key="sectionInset" minX="10" minY="118" maxX="10" maxY="10"/>
|
||||||
@@ -1168,34 +1170,34 @@
|
|||||||
<rect key="frame" x="10" y="118" width="300" height="100"/>
|
<rect key="frame" x="10" y="118" width="300" height="100"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
|
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="300" height="100"/>
|
<rect key="frame" x="0.0" y="0.0" width="355" height="100"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="xph-TW-9QO" userLabel="Content">
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="xph-TW-9QO" userLabel="Content">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="300" height="100"/>
|
<rect key="frame" x="0.0" y="0.0" width="355" height="100"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" directionalLockEnabled="YES" pagingEnabled="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bff-RU-OcY">
|
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" directionalLockEnabled="YES" pagingEnabled="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bff-RU-OcY">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="300" height="100"/>
|
<rect key="frame" x="0.0" y="0.0" width="355" height="100"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="aDw-qY-VjU" userLabel="Copy Content">
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="aDw-qY-VjU" userLabel="Copy Content">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="300" height="100"/>
|
<rect key="frame" x="0.0" y="0.0" width="355" height="100"/>
|
||||||
<color key="backgroundColor" red="0.18431372549019609" green="0.15686274509803921" blue="0.15686274509803921" alpha="0.5" colorSpace="calibratedRGB"/>
|
<color key="backgroundColor" red="0.18431372549019609" green="0.15686274509803921" blue="0.15686274509803921" alpha="0.5" colorSpace="calibratedRGB"/>
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="doContent:" destination="W2g-yv-V3V" eventType="touchUpInside" id="ukg-D8-8O3"/>
|
<action selector="doContent:" destination="W2g-yv-V3V" eventType="touchUpInside" id="ukg-D8-8O3"/>
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<view alpha="0.59999999999999998" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="w2g-zN-1wZ" userLabel="Login Container">
|
<view alpha="0.59999999999999998" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="w2g-zN-1wZ" userLabel="Login Container">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="300" height="21"/>
|
<rect key="frame" x="0.0" y="0.0" width="355" height="21"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<view alpha="0.80000000000000004" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="q3g-CJ-LbN" userLabel="Separator">
|
<view alpha="0.80000000000000004" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="q3g-CJ-LbN" userLabel="Separator">
|
||||||
<rect key="frame" x="0.0" y="20" width="300" height="1"/>
|
<rect key="frame" x="0.0" y="20" width="355" height="1"/>
|
||||||
<color key="backgroundColor" red="0.14901960784313725" green="0.14901960784313725" blue="0.14901960784313725" alpha="1" colorSpace="calibratedRGB"/>
|
<color key="backgroundColor" red="0.14901960784313725" green="0.14901960784313725" blue="0.14901960784313725" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="height" constant="1" id="jyk-dC-QLb"/>
|
<constraint firstAttribute="height" constant="1" id="jyk-dC-QLb"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="top" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Iwe-rQ-ma0" userLabel="Copy Login">
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="top" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Iwe-rQ-ma0" userLabel="Copy Login">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="300" height="33"/>
|
<rect key="frame" x="0.0" y="0.0" width="355" height="33"/>
|
||||||
<color key="backgroundColor" red="0.18431372549019609" green="0.15686274509803921" blue="0.15686274509803921" alpha="0.01" colorSpace="calibratedRGB"/>
|
<color key="backgroundColor" red="0.18431372549019609" green="0.15686274509803921" blue="0.15686274509803921" alpha="0.01" colorSpace="calibratedRGB"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="11"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="11"/>
|
||||||
<inset key="titleEdgeInsets" minX="0.0" minY="-4" maxX="0.0" maxY="0.0"/>
|
<inset key="titleEdgeInsets" minX="0.0" minY="-4" maxX="0.0" maxY="0.0"/>
|
||||||
@@ -1207,7 +1209,7 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="left" contentVerticalAlignment="center" textAlignment="center" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="3I9-vf-IZK" userLabel="Login">
|
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="left" contentVerticalAlignment="center" textAlignment="center" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="3I9-vf-IZK" userLabel="Login">
|
||||||
<rect key="frame" x="8" y="0.0" width="284" height="20"/>
|
<rect key="frame" x="8" y="0.0" width="339" height="20"/>
|
||||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="height" constant="20" id="9gA-Ti-g1e"/>
|
<constraint firstAttribute="height" constant="20" id="9gA-Ti-g1e"/>
|
||||||
@@ -1235,11 +1237,11 @@
|
|||||||
<constraint firstItem="Iwe-rQ-ma0" firstAttribute="top" secondItem="w2g-zN-1wZ" secondAttribute="top" id="q2j-Aa-lEd"/>
|
<constraint firstItem="Iwe-rQ-ma0" firstAttribute="top" secondItem="w2g-zN-1wZ" secondAttribute="top" id="q2j-Aa-lEd"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
<view contentMode="scaleToFill" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2tX-WK-ASq" userLabel="Password Container">
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="2tX-WK-ASq" userLabel="Password Container">
|
||||||
<rect key="frame" x="0.0" y="20" width="300" height="43"/>
|
<rect key="frame" x="0.0" y="20" width="355" height="43"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" misplaced="YES" enabled="NO" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="CuzaSasy3*Rimo" textAlignment="center" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="blw-Ou-8I8" userLabel="Password">
|
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="CuzaSasy3*Rimo" textAlignment="center" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="blw-Ou-8I8" userLabel="Password">
|
||||||
<rect key="frame" x="8" y="0.0" width="284" height="31"/>
|
<rect key="frame" x="8" y="0.0" width="339" height="31"/>
|
||||||
<color key="textColor" red="0.40000000600000002" green="0.80000001190000003" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
<color key="textColor" red="0.40000000600000002" green="0.80000001190000003" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
<fontDescription key="fontDescription" name="SourceCodePro-Black" family="Source Code Pro" pointSize="24"/>
|
<fontDescription key="fontDescription" name="SourceCodePro-Black" family="Source Code Pro" pointSize="24"/>
|
||||||
<textInputTraits key="textInputTraits" autocorrectionType="no" keyboardType="alphabet" keyboardAppearance="alert" returnKeyType="next"/>
|
<textInputTraits key="textInputTraits" autocorrectionType="no" keyboardType="alphabet" keyboardAppearance="alert" returnKeyType="next"/>
|
||||||
@@ -1248,8 +1250,8 @@
|
|||||||
<outlet property="delegate" destination="W2g-yv-V3V" id="YKp-IE-zEQ"/>
|
<outlet property="delegate" destination="W2g-yv-V3V" id="YKp-IE-zEQ"/>
|
||||||
</connections>
|
</connections>
|
||||||
</textField>
|
</textField>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="3" contentMode="left" misplaced="YES" text="> age of the universe" textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="wfM-xz-roR" userLabel="Strength">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="3" contentMode="left" text="> age of the universe" textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="wfM-xz-roR" userLabel="Strength">
|
||||||
<rect key="frame" x="0.0" y="31" width="300" height="12"/>
|
<rect key="frame" x="0.0" y="31" width="355" height="12"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="10"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="10"/>
|
||||||
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
@@ -1271,10 +1273,10 @@
|
|||||||
</userDefinedRuntimeAttributes>
|
</userDefinedRuntimeAttributes>
|
||||||
</view>
|
</view>
|
||||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="tl3-hd-x35" userLabel="Main Container">
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="tl3-hd-x35" userLabel="Main Container">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="300" height="100"/>
|
<rect key="frame" x="0.0" y="0.0" width="355" height="100"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.20000000000000001" contentMode="left" text="apple.com - Long" lineBreakMode="tailTruncation" minimumFontSize="8" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="OwP-sb-Wxl" userLabel="Site Name">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.20000000000000001" contentMode="left" text="apple.com - Long" lineBreakMode="tailTruncation" minimumFontSize="8" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="OwP-sb-Wxl" userLabel="Site Name">
|
||||||
<rect key="frame" x="8" y="76" width="248" height="15.5"/>
|
<rect key="frame" x="8" y="76" width="303" height="15.5"/>
|
||||||
<accessibility key="accessibilityConfiguration" label="">
|
<accessibility key="accessibilityConfiguration" label="">
|
||||||
<accessibilityTraits key="traits" none="YES" staticText="YES" summaryElement="YES"/>
|
<accessibilityTraits key="traits" none="YES" staticText="YES" summaryElement="YES"/>
|
||||||
</accessibility>
|
</accessibility>
|
||||||
@@ -1295,12 +1297,13 @@
|
|||||||
<userDefinedRuntimeAttribute type="boolean" keyPath="ignoreTouches" value="YES"/>
|
<userDefinedRuntimeAttribute type="boolean" keyPath="ignoreTouches" value="YES"/>
|
||||||
</userDefinedRuntimeAttributes>
|
</userDefinedRuntimeAttributes>
|
||||||
</view>
|
</view>
|
||||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="LvK-28-fbm" userLabel="Settings Container">
|
<view contentMode="scaleToFill" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LvK-28-fbm" userLabel="Settings Container">
|
||||||
<rect key="frame" x="300" y="0.0" width="300" height="100"/>
|
<rect key="frame" x="0.0" y="0.0" width="355" height="100"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="IKd-Ot-0n4" userLabel="Upgrade">
|
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="IKd-Ot-0n4" userLabel="Upgrade">
|
||||||
<rect key="frame" x="-15" y="56" width="44" height="44"/>
|
<rect key="frame" x="40" y="56" width="44" height="44"/>
|
||||||
<accessibility key="accessibilityConfiguration" hint="Upgrades the password."/>
|
<accessibility key="accessibilityConfiguration" hint="Upgrades the password."/>
|
||||||
|
<gestureRecognizers/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="width" constant="44" id="5DT-m6-RYu"/>
|
<constraint firstAttribute="width" constant="44" id="5DT-m6-RYu"/>
|
||||||
<constraint firstAttribute="height" constant="44" id="kyZ-o1-ntZ"/>
|
<constraint firstAttribute="height" constant="44" id="kyZ-o1-ntZ"/>
|
||||||
@@ -1318,8 +1321,8 @@
|
|||||||
<action selector="doUpgrade:" destination="W2g-yv-V3V" eventType="touchUpInside" id="kTZ-AM-qGa"/>
|
<action selector="doUpgrade:" destination="W2g-yv-V3V" eventType="touchUpInside" id="kTZ-AM-qGa"/>
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="vGk-t6-hZn" userLabel="Answers">
|
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="vGk-t6-hZn" userLabel="Answers">
|
||||||
<rect key="frame" x="29" y="56" width="44" height="44"/>
|
<rect key="frame" x="84" y="56" width="44" height="44"/>
|
||||||
<accessibility key="accessibilityConfiguration" hint="Upgrades the password."/>
|
<accessibility key="accessibilityConfiguration" hint="Upgrades the password."/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="width" constant="44" id="8gK-8v-Q0K"/>
|
<constraint firstAttribute="width" constant="44" id="8gK-8v-Q0K"/>
|
||||||
@@ -1338,8 +1341,8 @@
|
|||||||
<segue destination="aow-In-vb8" kind="custom" identifier="answers" customClass="MPOverlaySegue" id="5Wo-YZ-8HZ"/>
|
<segue destination="aow-In-vb8" kind="custom" identifier="answers" customClass="MPOverlaySegue" id="5Wo-YZ-8HZ"/>
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.5" contentMode="left" misplaced="YES" text="1" textAlignment="right" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="PKP-M9-T8E" userLabel="Counter">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.5" contentMode="left" text="1" textAlignment="right" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="PKP-M9-T8E" userLabel="Counter">
|
||||||
<rect key="frame" x="73" y="67" width="7" height="21.5"/>
|
<rect key="frame" x="128" y="67" width="7" height="21.5"/>
|
||||||
<accessibility key="accessibilityConfiguration" hint="Site's counter."/>
|
<accessibility key="accessibilityConfiguration" hint="Site's counter."/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="17"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="17"/>
|
||||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
@@ -1348,7 +1351,7 @@
|
|||||||
<size key="shadowOffset" width="0.0" height="1"/>
|
<size key="shadowOffset" width="0.0" height="1"/>
|
||||||
</label>
|
</label>
|
||||||
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="uZi-FT-Fe8" userLabel="Incrementer">
|
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="uZi-FT-Fe8" userLabel="Incrementer">
|
||||||
<rect key="frame" x="80" y="56" width="44" height="44"/>
|
<rect key="frame" x="135" y="56" width="44" height="44"/>
|
||||||
<accessibility key="accessibilityConfiguration" hint="Increments the site counter."/>
|
<accessibility key="accessibilityConfiguration" hint="Increments the site counter."/>
|
||||||
<gestureRecognizers/>
|
<gestureRecognizers/>
|
||||||
<constraints>
|
<constraints>
|
||||||
@@ -1369,7 +1372,7 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="qBo-Kw-vN9" userLabel="Edit">
|
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="qBo-Kw-vN9" userLabel="Edit">
|
||||||
<rect key="frame" x="124" y="56" width="44" height="44"/>
|
<rect key="frame" x="179" y="56" width="44" height="44"/>
|
||||||
<accessibility key="accessibilityConfiguration" hint="Edits the password."/>
|
<accessibility key="accessibilityConfiguration" hint="Edits the password."/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="width" constant="44" id="j1z-K8-OXJ"/>
|
<constraint firstAttribute="width" constant="44" id="j1z-K8-OXJ"/>
|
||||||
@@ -1389,7 +1392,7 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="I0v-ou-hDb" userLabel="Delete">
|
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="I0v-ou-hDb" userLabel="Delete">
|
||||||
<rect key="frame" x="168" y="56" width="44" height="44"/>
|
<rect key="frame" x="223" y="56" width="44" height="44"/>
|
||||||
<accessibility key="accessibilityConfiguration" hint="Edits the password."/>
|
<accessibility key="accessibilityConfiguration" hint="Edits the password."/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="height" constant="44" id="XvV-Lr-Z21"/>
|
<constraint firstAttribute="height" constant="44" id="XvV-Lr-Z21"/>
|
||||||
@@ -1409,7 +1412,7 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="K8q-bM-tzh" userLabel="Type">
|
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="K8q-bM-tzh" userLabel="Type">
|
||||||
<rect key="frame" x="212" y="56" width="44" height="44"/>
|
<rect key="frame" x="267" y="56" width="44" height="44"/>
|
||||||
<accessibility key="accessibilityConfiguration" hint="Edits the password."/>
|
<accessibility key="accessibilityConfiguration" hint="Edits the password."/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="height" constant="44" id="K3c-Ok-krB"/>
|
<constraint firstAttribute="height" constant="44" id="K3c-Ok-krB"/>
|
||||||
@@ -1446,7 +1449,7 @@
|
|||||||
</userDefinedRuntimeAttributes>
|
</userDefinedRuntimeAttributes>
|
||||||
</view>
|
</view>
|
||||||
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="b5f-wN-2xb" userLabel="Site Mode">
|
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="b5f-wN-2xb" userLabel="Site Mode">
|
||||||
<rect key="frame" x="256" y="56" width="44" height="44"/>
|
<rect key="frame" x="311" y="56" width="44" height="44"/>
|
||||||
<accessibility key="accessibilityConfiguration" hint="Upgrades the password."/>
|
<accessibility key="accessibilityConfiguration" hint="Upgrades the password."/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="width" constant="44" id="I1C-Ok-DUv"/>
|
<constraint firstAttribute="width" constant="44" id="I1C-Ok-DUv"/>
|
||||||
@@ -1467,7 +1470,7 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="⬇︎" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="tuh-Au-J9k">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="⬇︎" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="tuh-Au-J9k">
|
||||||
<rect key="frame" x="270" y="35" width="17" height="20.5"/>
|
<rect key="frame" x="325" y="35" width="17" height="20.5"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="17"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="17"/>
|
||||||
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
|
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
@@ -1599,14 +1602,14 @@
|
|||||||
<outlet property="delegate" destination="nkY-z6-8jd" id="ENG-q5-XwX"/>
|
<outlet property="delegate" destination="nkY-z6-8jd" id="ENG-q5-XwX"/>
|
||||||
</connections>
|
</connections>
|
||||||
</searchBar>
|
</searchBar>
|
||||||
<view userInteractionEnabled="NO" alpha="0.0" contentMode="scaleToFill" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LEX-BK-PdS" userLabel="Bad Name Tip">
|
<view userInteractionEnabled="NO" alpha="0.0" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="LEX-BK-PdS" userLabel="Bad Name Tip">
|
||||||
<rect key="frame" x="37" y="86" width="300.5" height="75.5"/>
|
<rect key="frame" x="37" y="86" width="300.5" height="75.5"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" misplaced="YES" image="tip_basic_black_top.png" translatesAutoresizingMaskIntoConstraints="NO" id="Rt5-v4-I0R">
|
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="tip_basic_black_top.png" translatesAutoresizingMaskIntoConstraints="NO" id="Rt5-v4-I0R">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="300.5" height="75.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="300.5" height="75.5"/>
|
||||||
<rect key="contentStretch" x="0.050000000000000003" y="0.49999999999999994" width="0.90000000000000002" height="0.20000000000000001"/>
|
<rect key="contentStretch" x="0.050000000000000003" y="0.49999999999999994" width="0.90000000000000002" height="0.20000000000000001"/>
|
||||||
</imageView>
|
</imageView>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" verticalCompressionResistancePriority="1000" misplaced="YES" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="Eie-8u-hV2">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" verticalCompressionResistancePriority="1000" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="Eie-8u-hV2">
|
||||||
<rect key="frame" x="20" y="26" width="260.5" height="43.5"/>
|
<rect key="frame" x="20" y="26" width="260.5" height="43.5"/>
|
||||||
<string key="text">Try using exclusively bare domain names.
|
<string key="text">Try using exclusively bare domain names.
|
||||||
Avoid capitals and use @ to include a user name.
|
Avoid capitals and use @ to include a user name.
|
||||||
@@ -1725,6 +1728,7 @@ eg. apple.com, rmitchell@twitter.com</string>
|
|||||||
<outlet property="popdownToTopConstraint" destination="BdD-Kc-eHl" id="59Y-ap-Yn4"/>
|
<outlet property="popdownToTopConstraint" destination="BdD-Kc-eHl" id="59Y-ap-Yn4"/>
|
||||||
<outlet property="popdownView" destination="XNM-XQ-rMe" id="FaW-4m-Fff"/>
|
<outlet property="popdownView" destination="XNM-XQ-rMe" id="FaW-4m-Fff"/>
|
||||||
<segue destination="z9O-w0-6oR" kind="modal" identifier="guide" id="Ql4-wf-T8u"/>
|
<segue destination="z9O-w0-6oR" kind="modal" identifier="guide" id="Ql4-wf-T8u"/>
|
||||||
|
<segue destination="Foa-Er-RBr" kind="custom" identifier="message" customClass="MPOverlaySegue" id="Xne-Sm-HQt"/>
|
||||||
</connections>
|
</connections>
|
||||||
</viewController>
|
</viewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="APh-u5-vFI" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="APh-u5-vFI" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
@@ -1750,7 +1754,7 @@ eg. apple.com, rmitchell@twitter.com</string>
|
|||||||
<rect key="frame" x="0.0" y="0.5" width="375" height="333.5"/>
|
<rect key="frame" x="0.0" y="0.5" width="375" height="333.5"/>
|
||||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||||
</view>
|
</view>
|
||||||
<view contentMode="scaleToFill" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="1lc-e7-Qme" userLabel="Emergency Generator">
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="1lc-e7-Qme" userLabel="Emergency Generator">
|
||||||
<rect key="frame" x="20" y="140" width="335" height="387"/>
|
<rect key="frame" x="20" y="140" width="335" height="387"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Emergency Generator" textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="4Lh-s0-Dbt">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Emergency Generator" textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="4Lh-s0-Dbt">
|
||||||
@@ -1761,7 +1765,7 @@ eg. apple.com, rmitchell@twitter.com</string>
|
|||||||
<color key="shadowColor" cocoaTouchSystemColor="darkTextColor"/>
|
<color key="shadowColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||||
<size key="shadowOffset" width="0.0" height="1"/>
|
<size key="shadowOffset" width="0.0" height="1"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" misplaced="YES" text="Generate your password without logging in. Great for if you're borrowing a friend's device or are having trouble logging in." lineBreakMode="tailTruncation" numberOfLines="0" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="vHS-3A-Tae">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Generate your password without logging in. Great for if you're borrowing a friend's device or are having trouble logging in." lineBreakMode="tailTruncation" numberOfLines="0" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="vHS-3A-Tae">
|
||||||
<rect key="frame" x="20" y="49" width="295" height="51.5"/>
|
<rect key="frame" x="20" y="49" width="295" height="51.5"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Thin" family="Exo 2.0" pointSize="14"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Thin" family="Exo 2.0" pointSize="14"/>
|
||||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
@@ -1769,7 +1773,7 @@ eg. apple.com, rmitchell@twitter.com</string>
|
|||||||
<color key="shadowColor" cocoaTouchSystemColor="darkTextColor"/>
|
<color key="shadowColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||||
<size key="shadowOffset" width="0.0" height="1"/>
|
<size key="shadowOffset" width="0.0" height="1"/>
|
||||||
</label>
|
</label>
|
||||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Your Name" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="XAC-Da-lpf" userLabel="User Name">
|
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Your Name" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="XAC-Da-lpf" userLabel="User Name">
|
||||||
<rect key="frame" x="20" y="109" width="295" height="30"/>
|
<rect key="frame" x="20" y="109" width="295" height="30"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
|
||||||
<textInputTraits key="textInputTraits" autocapitalizationType="words" keyboardAppearance="alert" returnKeyType="next" enablesReturnKeyAutomatically="YES"/>
|
<textInputTraits key="textInputTraits" autocapitalizationType="words" keyboardAppearance="alert" returnKeyType="next" enablesReturnKeyAutomatically="YES"/>
|
||||||
@@ -1778,7 +1782,7 @@ eg. apple.com, rmitchell@twitter.com</string>
|
|||||||
<outlet property="delegate" destination="osn-5H-SWW" id="VQI-Lq-GWG"/>
|
<outlet property="delegate" destination="osn-5H-SWW" id="VQI-Lq-GWG"/>
|
||||||
</connections>
|
</connections>
|
||||||
</textField>
|
</textField>
|
||||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Your Master Password" clearsOnBeginEditing="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="J46-0E-no3" userLabel="Master Password">
|
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Your Master Password" clearsOnBeginEditing="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="J46-0E-no3" userLabel="Master Password">
|
||||||
<rect key="frame" x="20" y="147" width="295" height="30"/>
|
<rect key="frame" x="20" y="147" width="295" height="30"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
|
||||||
<textInputTraits key="textInputTraits" autocorrectionType="no" keyboardAppearance="alert" returnKeyType="next" enablesReturnKeyAutomatically="YES" secureTextEntry="YES"/>
|
<textInputTraits key="textInputTraits" autocorrectionType="no" keyboardAppearance="alert" returnKeyType="next" enablesReturnKeyAutomatically="YES" secureTextEntry="YES"/>
|
||||||
@@ -1787,7 +1791,7 @@ eg. apple.com, rmitchell@twitter.com</string>
|
|||||||
<outlet property="delegate" destination="osn-5H-SWW" id="bpf-YA-5XP"/>
|
<outlet property="delegate" destination="osn-5H-SWW" id="bpf-YA-5XP"/>
|
||||||
</connections>
|
</connections>
|
||||||
</textField>
|
</textField>
|
||||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Site Name" clearsOnBeginEditing="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="56H-xR-09J" userLabel="Site Name">
|
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Site Name" clearsOnBeginEditing="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="56H-xR-09J" userLabel="Site Name">
|
||||||
<rect key="frame" x="20" y="185" width="295" height="30"/>
|
<rect key="frame" x="20" y="185" width="295" height="30"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
|
||||||
<textInputTraits key="textInputTraits" autocorrectionType="no" keyboardType="URL" keyboardAppearance="alert" returnKeyType="done" enablesReturnKeyAutomatically="YES"/>
|
<textInputTraits key="textInputTraits" autocorrectionType="no" keyboardType="URL" keyboardAppearance="alert" returnKeyType="done" enablesReturnKeyAutomatically="YES"/>
|
||||||
@@ -1796,7 +1800,7 @@ eg. apple.com, rmitchell@twitter.com</string>
|
|||||||
<outlet property="delegate" destination="osn-5H-SWW" id="QgA-TS-5KG"/>
|
<outlet property="delegate" destination="osn-5H-SWW" id="QgA-TS-5KG"/>
|
||||||
</connections>
|
</connections>
|
||||||
</textField>
|
</textField>
|
||||||
<segmentedControl opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="bar" selectedSegmentIndex="1" translatesAutoresizingMaskIntoConstraints="NO" id="e4b-Iv-Pk9" userLabel="Type">
|
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="bar" selectedSegmentIndex="1" translatesAutoresizingMaskIntoConstraints="NO" id="e4b-Iv-Pk9" userLabel="Type">
|
||||||
<rect key="frame" x="20" y="223" width="295" height="29"/>
|
<rect key="frame" x="20" y="223" width="295" height="29"/>
|
||||||
<segments>
|
<segments>
|
||||||
<segment title="Max"/>
|
<segment title="Max"/>
|
||||||
@@ -1811,7 +1815,7 @@ eg. apple.com, rmitchell@twitter.com</string>
|
|||||||
<action selector="controlChanged:" destination="osn-5H-SWW" eventType="valueChanged" id="sRc-3g-wqY"/>
|
<action selector="controlChanged:" destination="osn-5H-SWW" eventType="valueChanged" id="sRc-3g-wqY"/>
|
||||||
</connections>
|
</connections>
|
||||||
</segmentedControl>
|
</segmentedControl>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" misplaced="YES" text="Counter" lineBreakMode="tailTruncation" numberOfLines="0" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="cAo-K2-E23">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Counter" lineBreakMode="tailTruncation" numberOfLines="0" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="cAo-K2-E23">
|
||||||
<rect key="frame" x="20" y="262.5" width="64" height="21.5"/>
|
<rect key="frame" x="20" y="262.5" width="64" height="21.5"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="17"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="17"/>
|
||||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
@@ -1819,13 +1823,13 @@ eg. apple.com, rmitchell@twitter.com</string>
|
|||||||
<color key="shadowColor" cocoaTouchSystemColor="darkTextColor"/>
|
<color key="shadowColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||||
<size key="shadowOffset" width="0.0" height="1"/>
|
<size key="shadowOffset" width="0.0" height="1"/>
|
||||||
</label>
|
</label>
|
||||||
<stepper opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" maximumValue="100" translatesAutoresizingMaskIntoConstraints="NO" id="ZPT-EI-yuv" userLabel="Counter">
|
<stepper opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" maximumValue="100" translatesAutoresizingMaskIntoConstraints="NO" id="ZPT-EI-yuv" userLabel="Counter">
|
||||||
<rect key="frame" x="221" y="259" width="94" height="29"/>
|
<rect key="frame" x="221" y="259" width="94" height="29"/>
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="controlChanged:" destination="osn-5H-SWW" eventType="valueChanged" id="eQA-3X-uc9"/>
|
<action selector="controlChanged:" destination="osn-5H-SWW" eventType="valueChanged" id="eQA-3X-uc9"/>
|
||||||
</connections>
|
</connections>
|
||||||
</stepper>
|
</stepper>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" misplaced="YES" text="1" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="3Cd-XH-Wau">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="1" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="3Cd-XH-Wau">
|
||||||
<rect key="frame" x="206" y="262.5" width="7" height="20.5"/>
|
<rect key="frame" x="206" y="262.5" width="7" height="20.5"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="17"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="17"/>
|
||||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
@@ -1866,10 +1870,10 @@ eg. apple.com, rmitchell@twitter.com</string>
|
|||||||
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
</state>
|
</state>
|
||||||
</button>
|
</button>
|
||||||
<activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" misplaced="YES" hidesWhenStopped="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="4sN-hm-xio">
|
<activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" hidesWhenStopped="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="4sN-hm-xio">
|
||||||
<rect key="frame" x="149" y="329" width="37" height="37"/>
|
<rect key="frame" x="149" y="329" width="37" height="37"/>
|
||||||
</activityIndicatorView>
|
</activityIndicatorView>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="XapaNuwjFihn6$" textAlignment="center" lineBreakMode="clip" baselineAdjustment="alignBaselines" minimumFontSize="10" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bHR-he-dnZ" userLabel="Password Label">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="XapaNuwjFihn6$" textAlignment="center" lineBreakMode="clip" baselineAdjustment="alignBaselines" minimumFontSize="10" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bHR-he-dnZ" userLabel="Password Label">
|
||||||
<rect key="frame" x="20" y="328" width="295" height="39"/>
|
<rect key="frame" x="20" y="328" width="295" height="39"/>
|
||||||
<gestureRecognizers/>
|
<gestureRecognizers/>
|
||||||
<fontDescription key="fontDescription" name="SourceCodePro-Black" family="Source Code Pro" pointSize="30"/>
|
<fontDescription key="fontDescription" name="SourceCodePro-Black" family="Source Code Pro" pointSize="30"/>
|
||||||
@@ -1880,7 +1884,7 @@ eg. apple.com, rmitchell@twitter.com</string>
|
|||||||
<outletCollection property="gestureRecognizers" destination="gJb-50-mjy" appends="YES" id="3Ho-tp-mDE"/>
|
<outletCollection property="gestureRecognizers" destination="gJb-50-mjy" appends="YES" id="3Ho-tp-mDE"/>
|
||||||
</connections>
|
</connections>
|
||||||
</label>
|
</label>
|
||||||
<view userInteractionEnabled="NO" alpha="0.0" contentMode="scaleToFill" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="beo-cJ-jIn" userLabel="View - Content Tip">
|
<view userInteractionEnabled="NO" alpha="0.0" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="beo-cJ-jIn" userLabel="View - Content Tip">
|
||||||
<rect key="frame" x="62" y="287.5" width="210" height="60"/>
|
<rect key="frame" x="62" y="287.5" width="210" height="60"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="tip_basic_black.png" translatesAutoresizingMaskIntoConstraints="NO" id="nyL-cO-aPa">
|
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="tip_basic_black.png" translatesAutoresizingMaskIntoConstraints="NO" id="nyL-cO-aPa">
|
||||||
@@ -1984,13 +1988,13 @@ eg. apple.com, rmitchell@twitter.com</string>
|
|||||||
<outlet property="counterLabel" destination="3Cd-XH-Wau" id="wv7-jZ-6tX"/>
|
<outlet property="counterLabel" destination="3Cd-XH-Wau" id="wv7-jZ-6tX"/>
|
||||||
<outlet property="counterStepper" destination="ZPT-EI-yuv" id="w5c-hO-T51"/>
|
<outlet property="counterStepper" destination="ZPT-EI-yuv" id="w5c-hO-T51"/>
|
||||||
<outlet property="dialogView" destination="1lc-e7-Qme" id="JYt-mv-XV2"/>
|
<outlet property="dialogView" destination="1lc-e7-Qme" id="JYt-mv-XV2"/>
|
||||||
|
<outlet property="fullNameField" destination="XAC-Da-lpf" id="XCk-0H-IcI"/>
|
||||||
<outlet property="masterPasswordField" destination="J46-0E-no3" id="DfH-4n-cop"/>
|
<outlet property="masterPasswordField" destination="J46-0E-no3" id="DfH-4n-cop"/>
|
||||||
<outlet property="passwordLabel" destination="bHR-he-dnZ" id="0Mo-gc-Ls2"/>
|
<outlet property="passwordLabel" destination="bHR-he-dnZ" id="0Mo-gc-Ls2"/>
|
||||||
<outlet property="scrollView" destination="gRG-Ys-94p" id="K0v-cS-3VW"/>
|
<outlet property="scrollView" destination="gRG-Ys-94p" id="K0v-cS-3VW"/>
|
||||||
<outlet property="siteField" destination="56H-xR-09J" id="8no-IN-nsH"/>
|
<outlet property="siteField" destination="56H-xR-09J" id="8no-IN-nsH"/>
|
||||||
<outlet property="tipContainer" destination="beo-cJ-jIn" id="BdT-0M-8qC"/>
|
<outlet property="tipContainer" destination="beo-cJ-jIn" id="BdT-0M-8qC"/>
|
||||||
<outlet property="typeControl" destination="e4b-Iv-Pk9" id="S69-yO-7bv"/>
|
<outlet property="typeControl" destination="e4b-Iv-Pk9" id="S69-yO-7bv"/>
|
||||||
<outlet property="userNameField" destination="XAC-Da-lpf" id="XCk-0H-IcI"/>
|
|
||||||
<segue destination="p6o-h3-NRH" kind="unwind" identifier="unwind-popover" unwindAction="unwindToCombined:" id="XI2-ax-Rkg"/>
|
<segue destination="p6o-h3-NRH" kind="unwind" identifier="unwind-popover" unwindAction="unwindToCombined:" id="XI2-ax-Rkg"/>
|
||||||
</connections>
|
</connections>
|
||||||
</viewController>
|
</viewController>
|
||||||
@@ -2082,7 +2086,7 @@ eg. apple.com, rmitchell@twitter.com</string>
|
|||||||
<pageControl opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" contentHorizontalAlignment="center" contentVerticalAlignment="center" numberOfPages="3" translatesAutoresizingMaskIntoConstraints="NO" id="8A2-ly-WTX">
|
<pageControl opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" contentHorizontalAlignment="center" contentVerticalAlignment="center" numberOfPages="3" translatesAutoresizingMaskIntoConstraints="NO" id="8A2-ly-WTX">
|
||||||
<rect key="frame" x="168" y="630" width="39" height="37"/>
|
<rect key="frame" x="168" y="630" width="39" height="37"/>
|
||||||
</pageControl>
|
</pageControl>
|
||||||
<collectionView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" misplaced="YES" pagingEnabled="YES" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" minimumZoomScale="0.0" maximumZoomScale="0.0" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="i2y-lo-HXR">
|
<collectionView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" pagingEnabled="YES" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" minimumZoomScale="0.0" maximumZoomScale="0.0" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="i2y-lo-HXR">
|
||||||
<rect key="frame" x="0.0" y="64" width="375" height="476"/>
|
<rect key="frame" x="0.0" y="64" width="375" height="476"/>
|
||||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||||
<collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" minimumLineSpacing="0.0" minimumInteritemSpacing="0.0" id="m8O-kY-22j">
|
<collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" minimumLineSpacing="0.0" minimumInteritemSpacing="0.0" id="m8O-kY-22j">
|
||||||
@@ -2122,13 +2126,13 @@ eg. apple.com, rmitchell@twitter.com</string>
|
|||||||
<outlet property="delegate" destination="z9O-w0-6oR" id="RcD-gy-C1W"/>
|
<outlet property="delegate" destination="z9O-w0-6oR" id="RcD-gy-C1W"/>
|
||||||
</connections>
|
</connections>
|
||||||
</collectionView>
|
</collectionView>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" misplaced="YES" text="To begin, tap the "New User" icon and add yourself as a user to the application." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ciw-56-nNy" userLabel="Caption">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="To begin, tap the "New User" icon and add yourself as a user to the application." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ciw-56-nNy" userLabel="Caption">
|
||||||
<rect key="frame" x="8" y="548" width="359" height="82"/>
|
<rect key="frame" x="8" y="548" width="359" height="82"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="13"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="13"/>
|
||||||
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
|
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label hidden="YES" opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Oop-Ff-gbz" userLabel="Caption Height Strut">
|
<label hidden="YES" opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Oop-Ff-gbz" userLabel="Caption Height Strut">
|
||||||
<rect key="frame" x="8" y="548" width="1" height="82"/>
|
<rect key="frame" x="8" y="548" width="1" height="82"/>
|
||||||
<string key="text" base64-UTF8="YES">
|
<string key="text" base64-UTF8="YES">
|
||||||
CgoKCgoKCgoKCgoKCg
|
CgoKCgoKCgoKCgoKCg
|
||||||
@@ -2308,7 +2312,7 @@ Suspendisse potenti. Etiam ut nisi id augue tempor ultrices et sit amet sapien.
|
|||||||
<rect key="frame" x="163" y="532" width="51" height="31"/>
|
<rect key="frame" x="163" y="532" width="51" height="31"/>
|
||||||
<color key="onTintColor" red="0.37254901959999998" green="0.3921568627" blue="0.42745098040000001" alpha="1" colorSpace="calibratedRGB"/>
|
<color key="onTintColor" red="0.37254901959999998" green="0.3921568627" blue="0.42745098040000001" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
</switch>
|
</switch>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="e80-98-V6D">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="e80-98-V6D">
|
||||||
<rect key="frame" x="20" y="137" width="335" height="151.5"/>
|
<rect key="frame" x="20" y="137" width="335" height="151.5"/>
|
||||||
<string key="text">The right balance between security and convenience is often very personal.
|
<string key="text">The right balance between security and convenience is often very personal.
|
||||||
|
|
||||||
@@ -2392,7 +2396,7 @@ However, it means that anyone who finds your device unlocked can do the same.</s
|
|||||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" usesAttributedText="YES" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="J90-SQ-ljR">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" usesAttributedText="YES" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="J90-SQ-ljR">
|
||||||
<rect key="frame" x="20" y="136" width="335" height="522.5"/>
|
<rect key="frame" x="20" y="136" width="335" height="522.5"/>
|
||||||
<attributedString key="attributedText">
|
<attributedString key="attributedText">
|
||||||
<fragment content="The passwords generated by this app are not stored but ">
|
<fragment content="The passwords generated by this app are not stored but ">
|
||||||
@@ -2580,7 +2584,7 @@ See </string>
|
|||||||
<activityIndicatorView hidden="YES" opaque="NO" tag="2" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="cef-sc-aph">
|
<activityIndicatorView hidden="YES" opaque="NO" tag="2" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="cef-sc-aph">
|
||||||
<rect key="frame" x="168" y="100" width="37" height="37"/>
|
<rect key="frame" x="168" y="100" width="37" height="37"/>
|
||||||
</activityIndicatorView>
|
</activityIndicatorView>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" tag="3" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="✔︎" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FWu-V6-mLT">
|
<label opaque="NO" userInteractionEnabled="NO" tag="3" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="✔︎" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FWu-V6-mLT">
|
||||||
<rect key="frame" x="200" y="-6" width="92.5" height="132"/>
|
<rect key="frame" x="200" y="-6" width="92.5" height="132"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="110"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="110"/>
|
||||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
@@ -2645,7 +2649,7 @@ See </string>
|
|||||||
<activityIndicatorView hidden="YES" opaque="NO" tag="2" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="X2g-Go-2Hz">
|
<activityIndicatorView hidden="YES" opaque="NO" tag="2" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="X2g-Go-2Hz">
|
||||||
<rect key="frame" x="169" y="100" width="37" height="37"/>
|
<rect key="frame" x="169" y="100" width="37" height="37"/>
|
||||||
</activityIndicatorView>
|
</activityIndicatorView>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" tag="2" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="✔︎" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="N9y-ue-L8d">
|
<label opaque="NO" userInteractionEnabled="NO" tag="2" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="✔︎" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="N9y-ue-L8d">
|
||||||
<rect key="frame" x="200" y="-6" width="92.5" height="132"/>
|
<rect key="frame" x="200" y="-6" width="92.5" height="132"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="110"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="110"/>
|
||||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
@@ -2691,7 +2695,7 @@ See </string>
|
|||||||
<rect key="frame" x="0.0" y="0.0" width="287" height="96"/>
|
<rect key="frame" x="0.0" y="0.0" width="287" height="96"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" misplaced="YES" text="iOS Integration" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="12" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Zch-DS-J3I">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" text="iOS Integration" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="12" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Zch-DS-J3I">
|
||||||
<rect key="frame" x="20" y="226" width="244" height="20.5"/>
|
<rect key="frame" x="20" y="226" width="244" height="20.5"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="17"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="17"/>
|
||||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
@@ -2710,13 +2714,13 @@ See </string>
|
|||||||
<activityIndicatorView hidden="YES" opaque="NO" tag="2" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="yUe-TX-fli">
|
<activityIndicatorView hidden="YES" opaque="NO" tag="2" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="yUe-TX-fli">
|
||||||
<rect key="frame" x="169" y="100" width="37" height="37"/>
|
<rect key="frame" x="169" y="100" width="37" height="37"/>
|
||||||
</activityIndicatorView>
|
</activityIndicatorView>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" tag="2" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="✔︎" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ec8-P9-KPY">
|
<label opaque="NO" userInteractionEnabled="NO" tag="2" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="✔︎" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ec8-P9-KPY">
|
||||||
<rect key="frame" x="200" y="-6" width="92.5" height="132"/>
|
<rect key="frame" x="200" y="-6" width="92.5" height="132"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="110"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="110"/>
|
||||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="1" contentMode="left" horizontalHuggingPriority="750" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" misplaced="YES" text="Coming Soon" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3jH-eX-9N2">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="1" contentMode="left" horizontalHuggingPriority="750" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" text="Coming Soon" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3jH-eX-9N2">
|
||||||
<rect key="frame" x="272" y="226" width="83" height="20.5"/>
|
<rect key="frame" x="272" y="226" width="83" height="20.5"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
|
||||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
@@ -2756,7 +2760,7 @@ See </string>
|
|||||||
<rect key="frame" x="0.0" y="0.0" width="287" height="96"/>
|
<rect key="frame" x="0.0" y="0.0" width="287" height="96"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" misplaced="YES" text="TouchID Login" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="12" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="e1D-jp-GBs">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" text="TouchID Login" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="12" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="e1D-jp-GBs">
|
||||||
<rect key="frame" x="20" y="226" width="244.5" height="20.5"/>
|
<rect key="frame" x="20" y="226" width="244.5" height="20.5"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="17"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="17"/>
|
||||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
@@ -2775,13 +2779,13 @@ See </string>
|
|||||||
<activityIndicatorView hidden="YES" opaque="NO" tag="2" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="Dv5-t7-lL1">
|
<activityIndicatorView hidden="YES" opaque="NO" tag="2" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="Dv5-t7-lL1">
|
||||||
<rect key="frame" x="169" y="100" width="37" height="37"/>
|
<rect key="frame" x="169" y="100" width="37" height="37"/>
|
||||||
</activityIndicatorView>
|
</activityIndicatorView>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" tag="2" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="✔︎" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="yZX-ns-8oV">
|
<label opaque="NO" userInteractionEnabled="NO" tag="2" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="✔︎" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="yZX-ns-8oV">
|
||||||
<rect key="frame" x="200" y="-6" width="92.5" height="132"/>
|
<rect key="frame" x="200" y="-6" width="92.5" height="132"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="110"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="110"/>
|
||||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="1" contentMode="left" horizontalHuggingPriority="750" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" misplaced="YES" text="Coming Soon" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ZGg-O6-rsg">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="1" contentMode="left" horizontalHuggingPriority="750" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" text="Coming Soon" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ZGg-O6-rsg">
|
||||||
<rect key="frame" x="272" y="226" width="83" height="20.5"/>
|
<rect key="frame" x="272" y="226" width="83" height="20.5"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
|
||||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
@@ -2821,13 +2825,13 @@ See </string>
|
|||||||
<rect key="frame" x="0.0" y="0.0" width="287" height="96"/>
|
<rect key="frame" x="0.0" y="0.0" width="287" height="96"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" ambiguous="YES" misplaced="YES" text="Fuel Top-Up" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="12" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Jnv-uN-xeg">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" text="Fuel Top-Up" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="12" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Jnv-uN-xeg">
|
||||||
<rect key="frame" x="20" y="226" width="292" height="20.5"/>
|
<rect key="frame" x="20" y="226" width="292" height="20.5"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="17"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="17"/>
|
||||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" ambiguous="YES" misplaced="YES" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fz2-AO-aGW">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fz2-AO-aGW">
|
||||||
<rect key="frame" x="20" y="254" width="335" height="132"/>
|
<rect key="frame" x="20" y="254" width="335" height="132"/>
|
||||||
<string key="text">You really love Master Password and how it's solving your password problems. You're eager to encourage the maintenance, technical support and development of new features. I am a one-man shop, fuel enables me to allocate more work hours to Master Password.
|
<string key="text">You really love Master Password and how it's solving your password problems. You're eager to encourage the maintenance, technical support and development of new features. I am a one-man shop, fuel enables me to allocate more work hours to Master Password.
|
||||||
|
|
||||||
@@ -2840,22 +2844,22 @@ UPCOMING:
|
|||||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" misplaced="YES" image="thumb_fuel.png" translatesAutoresizingMaskIntoConstraints="NO" id="PnG-hP-syh">
|
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="thumb_fuel.png" translatesAutoresizingMaskIntoConstraints="NO" id="PnG-hP-syh">
|
||||||
<rect key="frame" x="88" y="20" width="198" height="198"/>
|
<rect key="frame" x="88" y="20" width="198" height="198"/>
|
||||||
</imageView>
|
</imageView>
|
||||||
<activityIndicatorView hidden="YES" opaque="NO" tag="2" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" ambiguous="YES" misplaced="YES" hidesWhenStopped="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="eS4-59-Xny">
|
<activityIndicatorView hidden="YES" opaque="NO" tag="2" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="eS4-59-Xny">
|
||||||
<rect key="frame" x="169" y="100" width="37" height="37"/>
|
<rect key="frame" x="169" y="100" width="37" height="37"/>
|
||||||
</activityIndicatorView>
|
</activityIndicatorView>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="1" contentMode="left" horizontalHuggingPriority="750" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" ambiguous="YES" misplaced="YES" text="$2.95" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="EbU-DV-fKF">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="1" contentMode="left" horizontalHuggingPriority="750" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" text="$2.95" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="EbU-DV-fKF">
|
||||||
<rect key="frame" x="320" y="226" width="34.5" height="20.5"/>
|
<rect key="frame" x="320" y="226" width="34.5" height="20.5"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
|
||||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" misplaced="YES" image="meter_fuel.png" translatesAutoresizingMaskIntoConstraints="NO" id="aGb-QC-A92">
|
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="meter_fuel.png" translatesAutoresizingMaskIntoConstraints="NO" id="aGb-QC-A92">
|
||||||
<rect key="frame" x="261" y="208" width="12" height="10"/>
|
<rect key="frame" x="261" y="208" width="12" height="10"/>
|
||||||
</imageView>
|
</imageView>
|
||||||
<button opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="dsR-fr-dY4">
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="dsR-fr-dY4">
|
||||||
<rect key="frame" x="20" y="20" width="107" height="44"/>
|
<rect key="frame" x="20" y="20" width="107" height="44"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="height" constant="44" id="W6p-kB-VBX"/>
|
<constraint firstAttribute="height" constant="44" id="W6p-kB-VBX"/>
|
||||||
@@ -2868,7 +2872,7 @@ UPCOMING:
|
|||||||
<action selector="toggleFuelConsumption:" destination="pdl-xv-zjX" eventType="touchUpInside" id="NkB-Dy-IeY"/>
|
<action selector="toggleFuelConsumption:" destination="pdl-xv-zjX" eventType="touchUpInside" id="NkB-Dy-IeY"/>
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" misplaced="YES" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="kYb-j4-32C">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="kYb-j4-32C">
|
||||||
<rect key="frame" x="20" y="64" width="117.5" height="26.5"/>
|
<rect key="frame" x="20" y="64" width="117.5" height="26.5"/>
|
||||||
<string key="text">fuel left: 0.3 work hours
|
<string key="text">fuel left: 0.3 work hours
|
||||||
invested: 3.7 work hours</string>
|
invested: 3.7 work hours</string>
|
||||||
@@ -2920,7 +2924,7 @@ invested: 3.7 work hours</string>
|
|||||||
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" animating="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="Vjt-7c-BJ4">
|
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" animating="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="Vjt-7c-BJ4">
|
||||||
<rect key="frame" x="169" y="20" width="37" height="37"/>
|
<rect key="frame" x="169" y="20" width="37" height="37"/>
|
||||||
</activityIndicatorView>
|
</activityIndicatorView>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" misplaced="YES" text="Loading products..." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="12" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ARC-xH-0U0">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" text="Loading products..." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="12" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ARC-xH-0U0">
|
||||||
<rect key="frame" x="114.5" y="97" width="145.5" height="20.5"/>
|
<rect key="frame" x="114.5" y="97" width="145.5" height="20.5"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="17"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="17"/>
|
||||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
@@ -2944,7 +2948,7 @@ invested: 3.7 work hours</string>
|
|||||||
<rect key="frame" x="0.0" y="0.0" width="320" height="152"/>
|
<rect key="frame" x="0.0" y="0.0" width="320" height="152"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" ambiguous="YES" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="IOk-WZ-HJ8">
|
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="IOk-WZ-HJ8">
|
||||||
<rect key="frame" x="20" y="58" width="335" height="26"/>
|
<rect key="frame" x="20" y="58" width="335" height="26"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="11"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="11"/>
|
||||||
<state key="normal" title="Restore Previous Purchases">
|
<state key="normal" title="Restore Previous Purchases">
|
||||||
@@ -2954,7 +2958,7 @@ invested: 3.7 work hours</string>
|
|||||||
<action selector="restorePurchases:" destination="pdl-xv-zjX" eventType="touchUpInside" id="lKu-PL-PfX"/>
|
<action selector="restorePurchases:" destination="pdl-xv-zjX" eventType="touchUpInside" id="lKu-PL-PfX"/>
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" ambiguous="YES" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="bCe-a3-cDC">
|
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="bCe-a3-cDC">
|
||||||
<rect key="frame" x="8" y="24" width="359" height="26"/>
|
<rect key="frame" x="8" y="24" width="359" height="26"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="11"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="11"/>
|
||||||
<state key="normal" title="Send Thanks">
|
<state key="normal" title="Send Thanks">
|
||||||
@@ -2964,7 +2968,7 @@ invested: 3.7 work hours</string>
|
|||||||
<action selector="sendThanks:" destination="pdl-xv-zjX" eventType="touchUpInside" id="r5l-3U-jCA"/>
|
<action selector="sendThanks:" destination="pdl-xv-zjX" eventType="touchUpInside" id="r5l-3U-jCA"/>
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" ambiguous="YES" text="© 2012-2014, Maarten Billemont (lhunath)" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="DxV-2L-bxL">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" text="© 2012-2014, Maarten Billemont (lhunath)" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="DxV-2L-bxL">
|
||||||
<rect key="frame" x="20" y="4" width="335" height="12"/>
|
<rect key="frame" x="20" y="4" width="335" height="12"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Thin" family="Exo 2.0" pointSize="10"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Thin" family="Exo 2.0" pointSize="10"/>
|
||||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
@@ -3042,13 +3046,13 @@ invested: 3.7 work hours</string>
|
|||||||
<rect key="frame" x="0.0" y="0.0" width="287" height="96"/>
|
<rect key="frame" x="0.0" y="0.0" width="287" height="96"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" misplaced="YES" text="Answer for lyndir.com:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Tal-1I-HQw" userLabel="Title Label">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" text="Answer for lyndir.com:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Tal-1I-HQw" userLabel="Title Label">
|
||||||
<rect key="frame" x="8" y="8" width="180" height="20.5"/>
|
<rect key="frame" x="8" y="8" width="180" height="20.5"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="17"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="17"/>
|
||||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" misplaced="YES" enabled="NO" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="hok petwuvaqu xixo" textAlignment="center" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="GfC-j4-Qx7" userLabel="Answer Field">
|
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="hok petwuvaqu xixo" textAlignment="center" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="GfC-j4-Qx7" userLabel="Answer Field">
|
||||||
<rect key="frame" x="8" y="48" width="359" height="43"/>
|
<rect key="frame" x="8" y="48" width="359" height="43"/>
|
||||||
<color key="textColor" red="0.40000000600000002" green="0.80000001190000003" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
<color key="textColor" red="0.40000000600000002" green="0.80000001190000003" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
<fontDescription key="fontDescription" name="SourceCodePro-Black" family="Source Code Pro" pointSize="24"/>
|
<fontDescription key="fontDescription" name="SourceCodePro-Black" family="Source Code Pro" pointSize="24"/>
|
||||||
@@ -3132,7 +3136,7 @@ invested: 3.7 work hours</string>
|
|||||||
<rect key="frame" x="0.0" y="0.0" width="287" height="96"/>
|
<rect key="frame" x="0.0" y="0.0" width="287" height="96"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="mother" textAlignment="center" minimumFontSize="14" clearButtonMode="unlessEditing" translatesAutoresizingMaskIntoConstraints="NO" id="T2F-PD-Nw8" userLabel="Question Field">
|
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="mother" textAlignment="center" minimumFontSize="14" clearButtonMode="unlessEditing" translatesAutoresizingMaskIntoConstraints="NO" id="T2F-PD-Nw8" userLabel="Question Field">
|
||||||
<rect key="frame" x="8" y="19" width="359" height="30"/>
|
<rect key="frame" x="8" y="19" width="359" height="30"/>
|
||||||
<color key="backgroundColor" red="0.37254901959999998" green="0.3921568627" blue="0.42745098040000001" alpha="0.5" colorSpace="calibratedRGB"/>
|
<color key="backgroundColor" red="0.37254901959999998" green="0.3921568627" blue="0.42745098040000001" alpha="0.5" colorSpace="calibratedRGB"/>
|
||||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
@@ -3143,13 +3147,13 @@ invested: 3.7 work hours</string>
|
|||||||
<outlet property="delegate" destination="iFm-3w-hOv" id="olS-Rx-56Y"/>
|
<outlet property="delegate" destination="iFm-3w-hOv" id="olS-Rx-56Y"/>
|
||||||
</connections>
|
</connections>
|
||||||
</textField>
|
</textField>
|
||||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" misplaced="YES" enabled="NO" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="pifm gup balvabi yiz" textAlignment="center" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="3xA-ez-efa" userLabel="Answer Field">
|
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="pifm gup balvabi yiz" textAlignment="center" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="3xA-ez-efa" userLabel="Answer Field">
|
||||||
<rect key="frame" x="20" y="90" width="335" height="31"/>
|
<rect key="frame" x="20" y="90" width="335" height="31"/>
|
||||||
<color key="textColor" red="0.40000000600000002" green="0.80000001190000003" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
<color key="textColor" red="0.40000000600000002" green="0.80000001190000003" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
<fontDescription key="fontDescription" name="SourceCodePro-Black" family="Source Code Pro" pointSize="24"/>
|
<fontDescription key="fontDescription" name="SourceCodePro-Black" family="Source Code Pro" pointSize="24"/>
|
||||||
<textInputTraits key="textInputTraits" autocorrectionType="no" keyboardType="alphabet" keyboardAppearance="alert" returnKeyType="next"/>
|
<textInputTraits key="textInputTraits" autocorrectionType="no" keyboardType="alphabet" keyboardAppearance="alert" returnKeyType="next"/>
|
||||||
</textField>
|
</textField>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" misplaced="YES" text="Enter the single most significant word in the question above." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Qqg-Ny-7Po">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" text="Enter the single most significant word in the question above." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Qqg-Ny-7Po">
|
||||||
<rect key="frame" x="8" y="57" width="359" height="13.5"/>
|
<rect key="frame" x="8" y="57" width="359" height="13.5"/>
|
||||||
<fontDescription key="fontDescription" name="Exo2.0-Thin" family="Exo 2.0" pointSize="11"/>
|
<fontDescription key="fontDescription" name="Exo2.0-Thin" family="Exo 2.0" pointSize="11"/>
|
||||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
@@ -3242,7 +3246,7 @@ invested: 3.7 work hours</string>
|
|||||||
<rect key="frame" x="163" y="532" width="51" height="31"/>
|
<rect key="frame" x="163" y="532" width="51" height="31"/>
|
||||||
<color key="onTintColor" red="0.37254901959999998" green="0.3921568627" blue="0.42745098040000001" alpha="1" colorSpace="calibratedRGB"/>
|
<color key="onTintColor" red="0.37254901959999998" green="0.3921568627" blue="0.42745098040000001" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
</switch>
|
</switch>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="yx2-Eh-hM0">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="yx2-Eh-hM0">
|
||||||
<rect key="frame" x="20" y="137" width="335" height="202"/>
|
<rect key="frame" x="20" y="137" width="335" height="202"/>
|
||||||
<string key="text">To make it easy for you to recognize and copy passwords manually using a keyboard or other means, Master Password makes your site passwords visible on your screen by default.
|
<string key="text">To make it easy for you to recognize and copy passwords manually using a keyboard or other means, Master Password makes your site passwords visible on your screen by default.
|
||||||
|
|
||||||
@@ -3299,6 +3303,198 @@ You can temporarily reveal a password by holding your finger down on the site's
|
|||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="-152.5" y="2175.5"/>
|
<point key="canvasLocation" x="-152.5" y="2175.5"/>
|
||||||
</scene>
|
</scene>
|
||||||
|
<!--Message View Controller-->
|
||||||
|
<scene sceneID="5u0-2d-uNC">
|
||||||
|
<objects>
|
||||||
|
<viewController id="Foa-Er-RBr" customClass="MPMessageViewController" sceneMemberID="viewController">
|
||||||
|
<layoutGuides>
|
||||||
|
<viewControllerLayoutGuide type="top" id="TXr-gp-hra"/>
|
||||||
|
<viewControllerLayoutGuide type="bottom" id="Ol8-pt-KDt"/>
|
||||||
|
</layoutGuides>
|
||||||
|
<view key="view" contentMode="scaleToFill" id="LFy-pG-nMQ" userLabel="Root">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<toolbar opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" barStyle="black" translatesAutoresizingMaskIntoConstraints="NO" id="aNU-Nq-clY">
|
||||||
|
<rect key="frame" x="0.0" y="341" width="375" height="325.5"/>
|
||||||
|
<items/>
|
||||||
|
</toolbar>
|
||||||
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" text="You have sites that can be upgraded." lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3Qi-GN-vhQ" userLabel="Title Label">
|
||||||
|
<rect key="frame" x="16" y="361" width="343" height="20.5"/>
|
||||||
|
<fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="17"/>
|
||||||
|
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" verticalCompressionResistancePriority="1000" usesAttributedText="YES" lineBreakMode="tailTruncation" numberOfLines="0" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rnV-lT-dTT">
|
||||||
|
<rect key="frame" x="16" y="390" width="343" height="216.5"/>
|
||||||
|
<attributedString key="attributedText">
|
||||||
|
<fragment>
|
||||||
|
<string key="content">Upgrading a site allows it to take advantage of
|
||||||
|
the latest improvements in the Master Password algorithm.
|
||||||
|
</string>
|
||||||
|
<attributes>
|
||||||
|
<color key="NSBackgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||||
|
<color key="NSColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
|
<font key="NSFont" size="12" name="Exo2.0-Regular"/>
|
||||||
|
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
|
||||||
|
</attributes>
|
||||||
|
</fragment>
|
||||||
|
<fragment>
|
||||||
|
<string key="content">
|
||||||
|
When you upgrade a site, </string>
|
||||||
|
<attributes>
|
||||||
|
<color key="NSBackgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||||
|
<color key="NSColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
|
<font key="NSFont" size="12" name="Exo2.0-Regular"/>
|
||||||
|
<paragraphStyle key="NSParagraphStyle" alignment="justified" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
|
||||||
|
</attributes>
|
||||||
|
</fragment>
|
||||||
|
<fragment content="a new and stronger password will be generated for it">
|
||||||
|
<attributes>
|
||||||
|
<color key="NSBackgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||||
|
<color key="NSColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
|
<font key="NSFont" size="12" name="Exo2.0-Bold"/>
|
||||||
|
<paragraphStyle key="NSParagraphStyle" alignment="justified" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
|
||||||
|
</attributes>
|
||||||
|
</fragment>
|
||||||
|
<fragment>
|
||||||
|
<string key="content">. To upgrade a site, first log into the site, navigate to your account preferences where you can change the site's password. Make sure you fill in any "current password" fields on the website first, then press the upgrade button here to get your new site password.
|
||||||
|
</string>
|
||||||
|
<attributes>
|
||||||
|
<color key="NSBackgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||||
|
<color key="NSColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
|
<font key="NSFont" size="12" name="Exo2.0-Regular"/>
|
||||||
|
<paragraphStyle key="NSParagraphStyle" alignment="justified" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
|
||||||
|
</attributes>
|
||||||
|
</fragment>
|
||||||
|
<fragment>
|
||||||
|
<string key="content">
|
||||||
|
You can then update your site's account with the new and stronger password.
|
||||||
|
|
||||||
|
</string>
|
||||||
|
<attributes>
|
||||||
|
<color key="NSBackgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||||
|
<color key="NSColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
|
<font key="NSFont" size="12" name="Exo2.0-Regular"/>
|
||||||
|
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
|
||||||
|
</attributes>
|
||||||
|
</fragment>
|
||||||
|
<fragment content="The upgrade button can be found in the site's settings and looks like this:">
|
||||||
|
<attributes>
|
||||||
|
<color key="NSBackgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||||
|
<color key="NSColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
|
<font key="NSFont" size="12" name="Exo2.0-Regular"/>
|
||||||
|
<paragraphStyle key="NSParagraphStyle" alignment="center" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
|
||||||
|
</attributes>
|
||||||
|
</fragment>
|
||||||
|
</attributedString>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="kPc-D9-n24" userLabel="Info">
|
||||||
|
<rect key="frame" x="16" y="615" width="343" height="44"/>
|
||||||
|
<subviews>
|
||||||
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3Ld-qt-w7n" userLabel="Stud">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="120.5" height="44"/>
|
||||||
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||||
|
</view>
|
||||||
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="wkN-yK-VVb" userLabel="Stud">
|
||||||
|
<rect key="frame" x="222.5" y="0.0" width="120.5" height="44"/>
|
||||||
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||||
|
</view>
|
||||||
|
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="hMB-S7-ldC" userLabel="Site Mode">
|
||||||
|
<rect key="frame" x="120.5" y="0.0" width="44" height="44"/>
|
||||||
|
<accessibility key="accessibilityConfiguration" hint="Upgrades the password."/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="44" id="HIL-wf-3AX"/>
|
||||||
|
<constraint firstAttribute="width" constant="44" id="WBC-Zk-skQ"/>
|
||||||
|
</constraints>
|
||||||
|
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
|
||||||
|
<inset key="contentEdgeInsets" minX="6" minY="6" maxX="6" maxY="6"/>
|
||||||
|
<state key="normal" image="icon_tools.png">
|
||||||
|
<color key="titleColor" red="0.19607843459999999" green="0.30980393290000002" blue="0.52156865600000002" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
|
</state>
|
||||||
|
<state key="selected" image="icon_thumbs-up.png"/>
|
||||||
|
<state key="highlighted">
|
||||||
|
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
|
</state>
|
||||||
|
</button>
|
||||||
|
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="xbh-Rx-L5u" userLabel="Upgrade">
|
||||||
|
<rect key="frame" x="179" y="0.0" width="44" height="44"/>
|
||||||
|
<accessibility key="accessibilityConfiguration" hint="Upgrades the password."/>
|
||||||
|
<gestureRecognizers/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="44" id="O4i-vP-Uf4"/>
|
||||||
|
<constraint firstAttribute="width" constant="44" id="kMf-yo-8c5"/>
|
||||||
|
</constraints>
|
||||||
|
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
|
||||||
|
<inset key="contentEdgeInsets" minX="6" minY="6" maxX="6" maxY="6"/>
|
||||||
|
<state key="normal" image="icon_up.png">
|
||||||
|
<color key="titleColor" red="0.19607843459999999" green="0.30980393290000002" blue="0.52156865600000002" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
|
</state>
|
||||||
|
<state key="highlighted">
|
||||||
|
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
|
</state>
|
||||||
|
</button>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="➙" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="kta-kZ-FbX">
|
||||||
|
<rect key="frame" x="164.5" y="12" width="14.5" height="20.5"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="3Ld-qt-w7n" firstAttribute="leading" secondItem="kPc-D9-n24" secondAttribute="leading" id="1BM-jA-NHO"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="xbh-Rx-L5u" secondAttribute="bottom" id="1wo-8P-DpL"/>
|
||||||
|
<constraint firstItem="3Ld-qt-w7n" firstAttribute="top" secondItem="kPc-D9-n24" secondAttribute="top" id="2v6-tn-vzT"/>
|
||||||
|
<constraint firstItem="wkN-yK-VVb" firstAttribute="width" secondItem="3Ld-qt-w7n" secondAttribute="width" id="AHa-IC-DwT"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="3Ld-qt-w7n" secondAttribute="bottom" id="Cu9-Dt-vRu"/>
|
||||||
|
<constraint firstAttribute="centerY" secondItem="kta-kZ-FbX" secondAttribute="centerY" id="EgL-EZ-xNL"/>
|
||||||
|
<constraint firstItem="hMB-S7-ldC" firstAttribute="top" secondItem="kPc-D9-n24" secondAttribute="top" id="HVS-r6-AFH"/>
|
||||||
|
<constraint firstItem="xbh-Rx-L5u" firstAttribute="leading" secondItem="kta-kZ-FbX" secondAttribute="trailing" id="Ksm-jT-qOL"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="wkN-yK-VVb" secondAttribute="bottom" id="LyD-YV-Yoh"/>
|
||||||
|
<constraint firstItem="kta-kZ-FbX" firstAttribute="leading" secondItem="hMB-S7-ldC" secondAttribute="trailing" id="Mqi-c7-yB1"/>
|
||||||
|
<constraint firstItem="wkN-yK-VVb" firstAttribute="leading" secondItem="xbh-Rx-L5u" secondAttribute="trailing" id="TYI-ve-ABO"/>
|
||||||
|
<constraint firstItem="xbh-Rx-L5u" firstAttribute="top" secondItem="kPc-D9-n24" secondAttribute="top" id="aTm-i3-Niu"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="wkN-yK-VVb" secondAttribute="trailing" id="dY8-BI-M6Q"/>
|
||||||
|
<constraint firstItem="wkN-yK-VVb" firstAttribute="top" secondItem="kPc-D9-n24" secondAttribute="top" id="hUt-8M-m1o"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="hMB-S7-ldC" secondAttribute="bottom" id="t4s-92-9p4"/>
|
||||||
|
<constraint firstItem="hMB-S7-ldC" firstAttribute="leading" secondItem="3Ld-qt-w7n" secondAttribute="trailing" id="wiD-xK-udZ"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="trailingMargin" secondItem="3Qi-GN-vhQ" secondAttribute="trailing" id="01x-aR-50B"/>
|
||||||
|
<constraint firstItem="rnV-lT-dTT" firstAttribute="leading" secondItem="LFy-pG-nMQ" secondAttribute="leadingMargin" id="5sg-Cn-A5f"/>
|
||||||
|
<constraint firstItem="3Qi-GN-vhQ" firstAttribute="leading" secondItem="LFy-pG-nMQ" secondAttribute="leadingMargin" id="B93-hf-itk"/>
|
||||||
|
<constraint firstItem="kPc-D9-n24" firstAttribute="top" secondItem="rnV-lT-dTT" secondAttribute="bottom" constant="8" id="Jd6-5C-J2q"/>
|
||||||
|
<constraint firstItem="aNU-Nq-clY" firstAttribute="leading" secondItem="LFy-pG-nMQ" secondAttribute="leading" id="Jo0-Nd-LRa"/>
|
||||||
|
<constraint firstAttribute="trailingMargin" secondItem="rnV-lT-dTT" secondAttribute="trailing" id="LsP-s5-0Cd"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="aNU-Nq-clY" secondAttribute="trailing" id="OOa-cS-g9q"/>
|
||||||
|
<constraint firstItem="Ol8-pt-KDt" firstAttribute="top" secondItem="aNU-Nq-clY" secondAttribute="bottom" id="T27-2g-NuB"/>
|
||||||
|
<constraint firstAttribute="trailingMargin" secondItem="kPc-D9-n24" secondAttribute="trailing" id="gVh-xu-hF3"/>
|
||||||
|
<constraint firstItem="rnV-lT-dTT" firstAttribute="top" secondItem="3Qi-GN-vhQ" secondAttribute="bottom" constant="8" id="gz3-TT-ylK"/>
|
||||||
|
<constraint firstItem="3Qi-GN-vhQ" firstAttribute="top" secondItem="aNU-Nq-clY" secondAttribute="top" constant="20" id="ozH-hI-ID9"/>
|
||||||
|
<constraint firstItem="kPc-D9-n24" firstAttribute="leading" secondItem="LFy-pG-nMQ" secondAttribute="leadingMargin" id="pTx-Ay-1WY"/>
|
||||||
|
<constraint firstItem="Ol8-pt-KDt" firstAttribute="top" secondItem="kPc-D9-n24" secondAttribute="bottom" constant="8" id="sNM-dC-2ES"/>
|
||||||
|
</constraints>
|
||||||
|
<userDefinedRuntimeAttributes>
|
||||||
|
<userDefinedRuntimeAttribute type="boolean" keyPath="ignoreTouches" value="YES"/>
|
||||||
|
</userDefinedRuntimeAttributes>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="infoView" destination="kPc-D9-n24" id="e7c-dG-hqT"/>
|
||||||
|
<outlet property="messageLabel" destination="rnV-lT-dTT" id="AdF-oe-XmL"/>
|
||||||
|
<outlet property="titleLabel" destination="3Qi-GN-vhQ" id="w2W-Tk-eMH"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="gdm-wd-QhM" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="1408.5" y="2995.5"/>
|
||||||
|
</scene>
|
||||||
</scenes>
|
</scenes>
|
||||||
<resources>
|
<resources>
|
||||||
<image name="avatar-0.png" width="110" height="110"/>
|
<image name="avatar-0.png" width="110" height="110"/>
|
||||||
|
|||||||
@@ -51,10 +51,11 @@ setSettingWithTitle "Copyright" "$(getPlistWithKey NSHumanReadableCopyright)"
|
|||||||
|
|
||||||
if [[ $DEPLOYMENT_LOCATION = YES ]]; then
|
if [[ $DEPLOYMENT_LOCATION = YES ]]; then
|
||||||
# This build is a release. Do some release checks.
|
# This build is a release. Do some release checks.
|
||||||
|
crashlyticsPlist="$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Crashlytics.plist"
|
||||||
passed=1
|
passed=1
|
||||||
[[ $description != *-dirty ]] || \
|
[[ $description != *-dirty ]] || \
|
||||||
{ passed=0; err 'ERROR: Cannot release a dirty version, first commit any changes.'; }
|
{ passed=0; err 'ERROR: Cannot release a dirty version, first commit any changes.'; }
|
||||||
[[ $(PlistBuddy -c "Print :'API Key'" "$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Crashlytics.plist") ]] || \
|
[[ -r "$crashlyticsPlist" && $(PlistBuddy -c "Print :'API Key'" "$crashlyticsPlist" 2>/dev/null) ]] || \
|
||||||
{ passed=0; err 'ERROR: Cannot release: Crashlytics API key is missing.'; }
|
{ passed=0; err 'ERROR: Cannot release: Crashlytics API key is missing.'; }
|
||||||
(( passed )) || \
|
(( passed )) || \
|
||||||
{ ftl "Failed to pass release checks. Fix the above errors and re-try. Aborting."; exit 1; }
|
{ ftl "Failed to pass release checks. Fix the above errors and re-try. Aborting."; exit 1; }
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 284 KiB After Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 79 KiB |
BIN
Site/2013-05/img/mac-homebrew.png
Normal file
|
After Width: | Height: | Size: 364 KiB |
@@ -80,9 +80,10 @@
|
|||||||
<h4>
|
<h4>
|
||||||
Get it for:
|
Get it for:
|
||||||
<a title="iPhone, iPad, iPod touch" href="https://itunes.apple.com/app/id510296984" onclick="_gaq.push(['_trackPageview', '/outbound/ios']);">iPhone / iPad<img class="popup" src="img/ios.png" /></a> |
|
<a title="iPhone, iPad, iPod touch" href="https://itunes.apple.com/app/id510296984" onclick="_gaq.push(['_trackPageview', '/outbound/ios']);">iPhone / iPad<img class="popup" src="img/ios.png" /></a> |
|
||||||
<a title="Mac (graphical interface)" href="https://itunes.apple.com/app/id662763204" onclick="_gaq.push(['_trackPageview', '/outbound/gui/mac']);">Mac<img class="popup border" src="img/mac-gui.png" /></a> |
|
Mac <a title="Mac (graphical interface)" href="https://itunes.apple.com/app/id662763204" onclick="_gaq.push(['_trackPageview', '/outbound/gui/mac']);">App Store<img class="popup border" src="img/mac-gui.png" /></a>,
|
||||||
|
<a title="Mac (command-line interface)" href="#">Homebrew<img class="popup" src="img/mac-homebrew.png" /></a> |
|
||||||
<a title="Mac, Linux, UNIX, Windows (graphical interface)" href="https://ssl.masterpasswordapp.com/masterpassword-gui.jar" onclick="_gaq.push(['_trackPageview', '/outbound/gui/java']);">Desktop (Java)<img class="popup" src="img/java-gui.png" /></a> |
|
<a title="Mac, Linux, UNIX, Windows (graphical interface)" href="https://ssl.masterpasswordapp.com/masterpassword-gui.jar" onclick="_gaq.push(['_trackPageview', '/outbound/gui/java']);">Desktop (Java)<img class="popup" src="img/java-gui.png" /></a> |
|
||||||
Terminal (<a title="Mac, Linux, POSIX (command line interface)" href="https://ssl.masterpasswordapp.com/masterpassword-cli.tar.gz" onclick="_gaq.push(['_trackPageview', '/outbound/cli/c']);">Native C<img class="popup" src="img/c-cli.png" /></a>) |
|
<a title="Mac, Linux, POSIX (command line interface)" href="https://ssl.masterpasswordapp.com/masterpassword-cli.tar.gz" onclick="_gaq.push(['_trackPageview', '/outbound/cli/c']);">Terminal (C)<img class="popup" src="img/c-cli.png" /></a> |
|
||||||
<a title="Android" href="https://ssl.masterpasswordapp.com/masterpassword-android.apk" onclick="_gaq.push(['_trackPageview', '/outbound/android']);">Android (Beta)<img class="popup" src="img/android.png" /></a> |
|
<a title="Android" href="https://ssl.masterpasswordapp.com/masterpassword-android.apk" onclick="_gaq.push(['_trackPageview', '/outbound/android']);">Android (Beta)<img class="popup" src="img/android.png" /></a> |
|
||||||
<a title="JavaScript" href="https://js.masterpasswordapp.com/" onclick="_gaq.push(['_trackPageview', '/outbound/js']);">Web (Beta)<img class="popup border" src="img/web.png" /></a>
|
<a title="JavaScript" href="https://js.masterpasswordapp.com/" onclick="_gaq.push(['_trackPageview', '/outbound/js']);">Web (Beta)<img class="popup border" src="img/web.png" /></a>
|
||||||
</h4>
|
</h4>
|
||||||
|
|||||||