Compare commits
30 Commits
2.7-androi
...
2.7-java
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
954c4f8d63 | ||
|
|
529f1feace | ||
|
|
5cdff6f155 | ||
|
|
81358c16f9 | ||
|
|
4a555748cd | ||
|
|
698566a914 | ||
|
|
64a69856ac | ||
|
|
f76de9520c | ||
|
|
4b3662bbe9 | ||
|
|
b25130f4d2 | ||
|
|
d6617563fc | ||
|
|
8d24ec3250 | ||
|
|
bbcc250a5c | ||
|
|
4abb50ad9b | ||
|
|
30dac64d5d | ||
|
|
e4e2aaad95 | ||
|
|
835acf45eb | ||
|
|
c3f6796833 | ||
|
|
86f4e8ec06 | ||
|
|
0dddcef28e | ||
|
|
cc583c789d | ||
|
|
42d78da74e | ||
|
|
b5040a7786 | ||
|
|
b1698ee339 | ||
|
|
8ffc0ae350 | ||
|
|
8276d2f4e5 | ||
|
|
11cf86bc73 | ||
|
|
5084511404 | ||
|
|
fffec56d4e | ||
|
|
9a0828c1eb |
28
.dockerignore
Normal file
28
.dockerignore
Normal file
@@ -0,0 +1,28 @@
|
||||
# OS-Specific junk.
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# IntelliJ
|
||||
.idea
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
|
||||
# Xcode IDE
|
||||
xcuserdata/
|
||||
DerivedData/
|
||||
|
||||
# Generated
|
||||
/platform-darwin/Resources/Media/Images.xcassets/
|
||||
/platform-darwin/Podfile.lock
|
||||
/platform-darwin/Pods/
|
||||
|
||||
# Gradle
|
||||
build
|
||||
.gradle
|
||||
local.properties
|
||||
/gradle/builds
|
||||
/platform-android/.externalNativeBuild
|
||||
|
||||
# Git
|
||||
.git
|
||||
@@ -6,9 +6,10 @@ build_project:
|
||||
stage: build
|
||||
script:
|
||||
- "( brew bundle )"
|
||||
- "( ./lib/bin/build_libsodium-macos )"
|
||||
- "( ./lib/bin/build_libsodium-macos clean && ./lib/bin/build_libsodium-macos )"
|
||||
- "( ./lib/bin/build_libjson-c-macos clean && ./lib/bin/build_libjson-c-macos )"
|
||||
- "( cd ./platform-independent/c/cli && ./clean && targets=all ./build && ./mpw-tests && ./mpw-cli-tests )"
|
||||
- "( cd ./gradle && ./gradlew --info clean test )"
|
||||
- "( cd ./gradle && ./gradlew --stacktrace clean test )"
|
||||
- "( xcodebuild -workspace platform-darwin/MasterPassword.xcworkspace -configuration 'Test' -scheme 'MasterPassword iOS' -sdk iphonesimulator clean build )"
|
||||
- "( xcodebuild -workspace platform-darwin/MasterPassword.xcworkspace -configuration 'Test' -scheme 'MasterPassword macOS' clean build )"
|
||||
tags:
|
||||
|
||||
1
Brewfile
1
Brewfile
@@ -1,5 +1,6 @@
|
||||
brew "libsodium"
|
||||
brew "json-c"
|
||||
|
||||
brew "libtool"
|
||||
brew "automake"
|
||||
brew "autoconf"
|
||||
|
||||
9
Dockerfile
Normal file
9
Dockerfile
Normal file
@@ -0,0 +1,9 @@
|
||||
FROM alpine
|
||||
|
||||
# For i386
|
||||
#FROM i386/alpine
|
||||
#ENTRYPOINT ["linux32", "--"]
|
||||
|
||||
RUN apk add --no-cache git libtool automake autoconf make g++ bash openjdk8
|
||||
RUN git clone --depth=3 $(: --shallow-submodules) --recurse-submodules https://gitlab.com/MasterPassword/MasterPassword.git /mpw
|
||||
RUN cd /mpw/gradle && ./gradlew -i clean build
|
||||
@@ -122,7 +122,7 @@ The `master-password` on the other hand, is only a simple phrase, which means it
|
||||
|
||||
5. I have another question.
|
||||
|
||||
Please don't hesitate to [get in touch](#support), we're more than happy to answer all your Master Password questions. Any problems or suggestions can be reported [as GitHub issues](https://github.com/Lyndir/MasterPassword/issues).
|
||||
Please don't hesitate to [get in touch](#support), we're more than happy to answer all your Master Password questions. Any problems or suggestions can be reported [as GitLab issues](https://gitlab.com/MasterPassword/MasterPassword/issues/).
|
||||
|
||||
|
||||
|
||||
@@ -187,7 +187,7 @@ Note that in order to build the Android application, you will need to have the A
|
||||
|
||||
Go into the `platform-independent/cli-c` directory and run `./build`. The native command-line client will then be built.
|
||||
|
||||
For detailed instructions, see [the native CLI instructions](platform-independent/cli-c/README.md).
|
||||
For detailed instructions, see [the native CLI instructions](platform-independent/c/README.md).
|
||||
|
||||
|
||||
## Support
|
||||
|
||||
9
gradle/.idea/codeStyleSettings.xml
generated
9
gradle/.idea/codeStyleSettings.xml
generated
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectCodeStyleSettingsManager">
|
||||
<option name="PER_PROJECT_SETTINGS">
|
||||
<value />
|
||||
</option>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Lhunath" />
|
||||
</component>
|
||||
</project>
|
||||
7
gradle/.idea/copyright/GPLv3.xml
generated
7
gradle/.idea/copyright/GPLv3.xml
generated
@@ -1,7 +0,0 @@
|
||||
<component name="CopyrightManager">
|
||||
<copyright>
|
||||
<option name="keyword" value="Copyright|License|WARRANTY" />
|
||||
<option name="myName" value="GPLv3" />
|
||||
<option name="notice" value="This file is part of &#36;project.name. Copyright (c) &#36;today.year. &#36;project.name is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. &#36;project.name is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You can find a copy of the GNU General Public License in the LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>." />
|
||||
</copyright>
|
||||
</component>
|
||||
13
gradle/.idea/copyright/profiles_settings.xml
generated
13
gradle/.idea/copyright/profiles_settings.xml
generated
@@ -1,13 +0,0 @@
|
||||
<component name="CopyrightManager">
|
||||
<settings>
|
||||
<module2copyright>
|
||||
<element module="masterpassword" copyright="Master Password" />
|
||||
</module2copyright>
|
||||
<LanguageOptions name="__TEMPLATE__">
|
||||
<option name="block" value="false" />
|
||||
<option name="separateBefore" value="true" />
|
||||
<option name="separateAfter" value="true" />
|
||||
<option name="filler" value="=" />
|
||||
</LanguageOptions>
|
||||
</settings>
|
||||
</component>
|
||||
@@ -1,9 +0,0 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="projectProfile" value="Lhunath" />
|
||||
<option name="useProjectProfile" value="false" />
|
||||
<option name="PROJECT_PROFILE" value="Lhunath" />
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
18
gradle/.idea/misc.xml
generated
18
gradle/.idea/misc.xml
generated
@@ -1,17 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
<type id="jpa" />
|
||||
<type id="web" />
|
||||
</component>
|
||||
<component name="MavenProjectsManager">
|
||||
<option name="originalFiles">
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/../../opal/pom.xml" />
|
||||
<option value="$PROJECT_DIR$/../../pom.xml" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="NullableNotNullManager">
|
||||
<option name="myDefaultNullable" value="javax.annotation.Nullable" />
|
||||
<option name="myDefaultNotNull" value="javax.annotation.Nonnull" />
|
||||
@@ -37,9 +25,9 @@
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/classes" />
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ThriftCompiler">
|
||||
<compilers />
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
</project>
|
||||
28
gradle/.idea/runConfigurations/Android.xml
generated
28
gradle/.idea/runConfigurations/Android.xml
generated
@@ -1,28 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Android" type="AndroidRunConfigurationType" factoryName="Android App">
|
||||
<module name="android" />
|
||||
<option name="DEPLOY" value="true" />
|
||||
<option name="ARTIFACT_NAME" value="" />
|
||||
<option name="PM_INSTALL_OPTIONS" value="" />
|
||||
<option name="ACTIVITY_EXTRA_FLAGS" value="" />
|
||||
<option name="MODE" value="default_activity" />
|
||||
<option name="PREFERRED_AVD" value="" />
|
||||
<option name="CLEAR_LOGCAT" value="false" />
|
||||
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="false" />
|
||||
<option name="SKIP_NOOP_APK_INSTALLATIONS" value="true" />
|
||||
<option name="FORCE_STOP_RUNNING_APP" value="true" />
|
||||
<option name="TARGET_SELECTION_MODE" value="SHOW_DIALOG" />
|
||||
<option name="USE_LAST_SELECTED_DEVICE" value="false" />
|
||||
<option name="PREFERRED_AVD" value="" />
|
||||
<option name="DEBUGGER_TYPE" value="Java" />
|
||||
<Java />
|
||||
<Profilers>
|
||||
<option name="ENABLE_ADVANCED_PROFILING" value="false" />
|
||||
<option name="SUPPORT_LIB_ENABLED" value="true" />
|
||||
<option name="INSTRUMENTATION_ENABLED" value="true" />
|
||||
</Profilers>
|
||||
<option name="DEEP_LINK" value="" />
|
||||
<option name="ACTIVITY_CLASS" value="" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
||||
3
gradle/.idea/scopes/masterpassword.xml
generated
3
gradle/.idea/scopes/masterpassword.xml
generated
@@ -1,3 +0,0 @@
|
||||
<component name="DependencyValidationManager">
|
||||
<scope name="masterpassword" pattern="com.lyndir.masterpassword..*" />
|
||||
</component>
|
||||
@@ -1 +1,3 @@
|
||||
org.gradle.daemon=true
|
||||
org.gradle.configureondemand=true
|
||||
org.gradle.jvmargs=-Xmx1536M
|
||||
|
||||
@@ -4,13 +4,14 @@ class Mpw < Formula
|
||||
url "https://masterpassword.app/mpw-2.6-cli-5-0-g344771db.tar.gz"
|
||||
version "2.6-cli-5"
|
||||
sha256 "954c07b1713ecc2b30a07bead9c11e6204dd774ca67b5bdf7d2d6ad1c4eec170"
|
||||
head "https://github.com/Lyndir/MasterPassword.git"
|
||||
revision 1
|
||||
head "https://gitlab.com/MasterPassword/MasterPassword.git"
|
||||
|
||||
bottle do
|
||||
cellar :any
|
||||
sha256 "ae8b265936797778a7cde788377eed89d9eacd267755a0b1186790057a10ff3b" => :high_sierra
|
||||
sha256 "b8a106c3c84ff939e928613d4a6ccf7b5234e40ebae1edf15e3cac52d8c2e5ea" => :sierra
|
||||
sha256 "9b58425b028a2598932474e1d0c17c13aad57e0a53ae7308c1b38404da8f3331" => :el_capitan
|
||||
sha256 "46677cf8649983d5b77103d2ca56d9ad3697808ecc406f626a3462a089f932da" => :high_sierra
|
||||
sha256 "19bf22915b3c534ad3ee6f1dfc20f142d53ae6c0c88757ae2632b7b1daa6667f" => :sierra
|
||||
sha256 "7090c3d31289d2ac5529bd0a6bae2632a36ba7fcd4bb7974248bb36a15f67c7e" => :el_capitan
|
||||
end
|
||||
|
||||
option "without-json-c", "Disable JSON configuration support"
|
||||
|
||||
@@ -1,29 +1,27 @@
|
||||
rootProject.name = 'masterpassword'
|
||||
|
||||
def local = new Properties();
|
||||
try {
|
||||
local.load(file('local.properties').newDataInputStream())
|
||||
} catch (FileNotFoundException ignored) {
|
||||
}
|
||||
def local = new Properties()
|
||||
def localFile = file( 'local.properties' )
|
||||
localFile.exists() && local.load( localFile.newDataInputStream() )
|
||||
|
||||
include 'masterpassword-core'
|
||||
project(':masterpassword-core').projectDir = new File( '../platform-independent/c/core' )
|
||||
project( ':masterpassword-core' ).projectDir = new File( '../platform-independent/c/core' )
|
||||
|
||||
include 'masterpassword-algorithm'
|
||||
project(':masterpassword-algorithm').projectDir = new File( '../platform-independent/java/algorithm' )
|
||||
project( ':masterpassword-algorithm' ).projectDir = new File( '../platform-independent/java/algorithm' )
|
||||
|
||||
include 'masterpassword-model'
|
||||
project(':masterpassword-model').projectDir = new File( '../platform-independent/java/model' )
|
||||
project( ':masterpassword-model' ).projectDir = new File( '../platform-independent/java/model' )
|
||||
|
||||
include 'masterpassword-tests'
|
||||
project(':masterpassword-tests').projectDir = new File( '../platform-independent/java/tests' )
|
||||
project( ':masterpassword-tests' ).projectDir = new File( '../platform-independent/java/tests' )
|
||||
|
||||
include 'masterpassword-gui'
|
||||
project(':masterpassword-gui').projectDir = new File( '../platform-independent/java/gui' )
|
||||
project( ':masterpassword-gui' ).projectDir = new File( '../platform-independent/java/gui' )
|
||||
|
||||
if (local.containsKey('sdk.dir')) {
|
||||
if (local.containsKey( 'sdk.dir' ) && file( local.getProperty( 'sdk.dir' ) ).exists()) {
|
||||
include 'masterpassword-android'
|
||||
project(':masterpassword-android').projectDir = new File( '../platform-android' )
|
||||
project( ':masterpassword-android' ).projectDir = new File( '../platform-android' )
|
||||
} else {
|
||||
logger.warn( "Skipping masterpassword-android since sdk.dir is not defined in local.properties." )
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# Your build script should simply source this script, optionally override any build hooks and then invoke `build`.
|
||||
# The build product should be available under `build-<platform>~/out`, under the library path.
|
||||
#
|
||||
#
|
||||
# Hook lifecycle:
|
||||
# - build
|
||||
# - initialize
|
||||
@@ -50,10 +50,16 @@ _initialize() {
|
||||
#
|
||||
# Check if all tools needed for the default implementations are available.
|
||||
#
|
||||
# By default, this will check for `automake` (for aclocal) and `autoconf` (for autoreconf).
|
||||
# By default, this will check for `libtool` (for libtoolize), `automake` (for aclocal) and `autoconf` (for autoreconf).
|
||||
initialize_needs() { _initialize_needs "$@"; }
|
||||
_initialize_needs() {
|
||||
needs automake autoconf
|
||||
if [[ $platform = windows ]]; then
|
||||
needs cmd
|
||||
export VSINSTALLDIR="${VSINSTALLDIR:-$(cd "$(cygpath -F 0x002a)/Microsoft Visual Studio"/*/*/Common7/.. && pwd)}"
|
||||
[[ -e "$VSINSTALLDIR/Common7/Tools/VsMSBuildCmd.bat" ]] || { echo >&2 "Missing: msbuild. Please install 'Build Tools for Visual Studio'."; return 1; }
|
||||
else
|
||||
needs libtool automake autoconf
|
||||
fi
|
||||
}
|
||||
|
||||
# clean <prefix> <platform>
|
||||
@@ -63,9 +69,15 @@ _initialize_needs() {
|
||||
# By default, this will wipe the prefix, run `make distclean` and `git clean -fdx`.
|
||||
clean() { _clean "$@"; }
|
||||
_clean() {
|
||||
if [[ $platform = windows ]]; then
|
||||
printf '"%%VSINSTALLDIR%%\Common7\Tools\VsMSBuildCmd.bat" && msbuild /t:Clean' > .clean.bat
|
||||
cmd //c .clean.bat
|
||||
rm -f .clean.bat
|
||||
elif [[ -e Makefile ]] && make -s distclean; then :
|
||||
elif [[ -e .git ]] && git clean -fdx; then :
|
||||
fi
|
||||
|
||||
rm -rf "$prefix"
|
||||
[[ ! -e Makefile ]] || make -s distclean
|
||||
[[ ! -e .git ]] || git clean -fdx
|
||||
}
|
||||
|
||||
# prepare <prefix> <platform> [ <arch> ... ]
|
||||
@@ -91,7 +103,7 @@ _prepare_clean() {
|
||||
local prefix=$1 platform=$2; shift 2
|
||||
|
||||
rm -rf "$prefix"
|
||||
install -d "$prefix"
|
||||
install -d "$prefix/out"
|
||||
}
|
||||
|
||||
# prepare_config <prefix> <platform> [ <arch> ... ]
|
||||
@@ -103,7 +115,14 @@ prepare_config() { _prepare_config "$@"; }
|
||||
_prepare_config() {
|
||||
local prefix=$1 platform=$2; shift 2
|
||||
|
||||
[[ -e configure ]] || autoreconf --verbose --install --symlink 2> >(sed 's/^\([^:]*\):[0-9]\{1,\}: /\1: /')
|
||||
[[ -e "$prefix/out/.prepared" ]] && return
|
||||
|
||||
if [[ $platform = windows ]]; then :
|
||||
else
|
||||
autoreconf --verbose --install --force 2> >(sed 's/^\([^:]*\):[0-9]\{1,\}: /\1: /')
|
||||
fi
|
||||
|
||||
touch "$prefix/out/.prepared"
|
||||
}
|
||||
|
||||
# target <prefix> <platform> <arch>
|
||||
@@ -127,19 +146,27 @@ target_prepare() { _target_prepare "$@"; }
|
||||
_target_prepare() {
|
||||
local prefix=$1 platform=$2 arch=$3; shift 3
|
||||
|
||||
[[ ! -e Makefile ]] || make -s clean
|
||||
if [[ $platform = windows ]]; then :
|
||||
else
|
||||
[[ ! -e Makefile ]] || make -s clean
|
||||
fi
|
||||
}
|
||||
|
||||
# target_configure <prefix> <platform> <arch> [ <args> ... ]
|
||||
#
|
||||
# Configure the library for building the target.
|
||||
#
|
||||
# By default, this will run `./configure --host=<host> --prefix=<prefix>/<arch> --disable-shared <args>`.
|
||||
# By default, this will run `./configure --host=<host> --prefix=<prefix>/<arch> <args>`.
|
||||
# By default, some platform-specific arguments will be passed in as well as
|
||||
# --enable-pic --disable-pie to ensure the resulting library can be linked again.
|
||||
target_configure() { _target_configure "$@"; }
|
||||
_target_configure() {
|
||||
local prefix=$1 platform=$2 arch=$3; shift 3
|
||||
|
||||
case "$platform" in
|
||||
'windows')
|
||||
return
|
||||
;;
|
||||
'android')
|
||||
host=( "$SDKROOT"/*-android* ) host=${host##*/}
|
||||
|
||||
@@ -153,7 +180,7 @@ _target_configure() {
|
||||
;;
|
||||
esac
|
||||
|
||||
./configure ${host:+--host="$host"} --prefix="$prefix/$arch" "$@"
|
||||
./configure ${host:+--host="$host"} --enable-pic --disable-pie --prefix="$prefix/$arch" "$@"
|
||||
}
|
||||
|
||||
# target_build <prefix> <platform> <arch>
|
||||
@@ -164,10 +191,16 @@ _target_configure() {
|
||||
target_build() { _target_build "$@"; }
|
||||
_target_build() {
|
||||
local prefix=$1 platform=$2 arch=$3; shift 3
|
||||
#make -j3 check
|
||||
|
||||
cores=$(getconf NPROCESSORS_ONLN 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null ||:)
|
||||
make -j"${cores:-3}" install
|
||||
if [[ $platform = windows ]]; then
|
||||
# I cannot for the life of me figure out how to pass this command directly into cmd.
|
||||
printf '"%%VSINSTALLDIR%%\Common7\Tools\VsMSBuildCmd.bat" && msbuild /t:Rebuild /p:Configuration=ReleaseDLL;Platform=%s;OutDir=%s' "$arch" "$(cygpath -w "${prefix##$PWD/}/$arch/")" > .build.bat
|
||||
cmd //c .build.bat
|
||||
rm -f .build.bat
|
||||
else
|
||||
local cores=$(getconf NPROCESSORS_ONLN 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null ||:)
|
||||
make -j"${cores:-3}" install
|
||||
fi
|
||||
}
|
||||
|
||||
# finalize <prefix> <platform> [ <arch> ... ]
|
||||
@@ -188,21 +221,34 @@ _finalize() {
|
||||
finalize_merge() { _finalize_merge "$@"; }
|
||||
_finalize_merge() {
|
||||
local prefix=$1 platform=$2; shift 2
|
||||
local archs=( "$@" )
|
||||
|
||||
mv -f -- "$prefix/$1/include" "$prefix/out/"
|
||||
[[ -e "$prefix/$archs/include" ]] && mv -f -- "$prefix/$archs/include" "$prefix/out/"
|
||||
|
||||
mkdir -p "$prefix/out/lib"
|
||||
install -d "$prefix/out/lib"
|
||||
case "$platform" in
|
||||
'linux')
|
||||
for arch in "${archs[@]}"; do
|
||||
install -d "$prefix/out/lib/$arch"
|
||||
install -p "$prefix/$arch/lib/"*.a "$prefix/out/lib/$arch/"
|
||||
done
|
||||
;;
|
||||
'windows')
|
||||
for arch in "${archs[@]}"; do
|
||||
install -d "$prefix/out/lib/$arch"
|
||||
install -p "$prefix/$arch/"*.lib "$prefix/out/lib/$arch/"
|
||||
done
|
||||
;;
|
||||
'macos'|'ios')
|
||||
for lib in "$prefix/$1/lib/"*; do
|
||||
for lib in "$prefix/$archs/lib/"*; do
|
||||
if lipo -info "$lib" >/dev/null 2>&1; then
|
||||
local lib=("${lib##*/}") libs=("${@/#/$prefix/}") libs=("${libs[@]/%//lib/$lib}")
|
||||
local lib=("${lib##*/}") libs=("${archs[@]/#/$prefix/}") libs=("${libs[@]/%//lib/$lib}")
|
||||
lipo -create "${libs[@]}" -output "$prefix/out/lib/$lib"
|
||||
fi
|
||||
done
|
||||
;;
|
||||
'android')
|
||||
for arch; do
|
||||
for arch in "${archs[@]}"; do
|
||||
local abi=$arch
|
||||
case "$arch" in
|
||||
'arm') abi='armeabi-v7a' ;;
|
||||
@@ -247,6 +293,8 @@ _build() {
|
||||
'macos') archs=( 'x86_64' ) ;;
|
||||
'ios') archs=( 'i386' 'x86_64' 'armv7' 'armv7s' 'arm64' ) ;;
|
||||
'android') archs=( 'arm' 'arm64' 'x86' 'x86_64' ) ;;
|
||||
'windows') archs=( 'Win32' 'x64' ) ;;
|
||||
*) archs=( 'i386' 'x86_64' ) ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
@@ -270,6 +318,8 @@ _build() {
|
||||
|
||||
# Set up a base environment for the platform.
|
||||
case "$platform" in
|
||||
'windows')
|
||||
;;
|
||||
'macos')
|
||||
SDKROOT="$(xcrun --show-sdk-path --sdk macosx)"
|
||||
export PATH="$(xcrun --show-sdk-platform-path --sdk macosx)/usr/bin:$PATH"
|
||||
@@ -319,4 +369,3 @@ _build() {
|
||||
|
||||
finalize "$prefix" "$platform" "${archs[@]}"
|
||||
}
|
||||
|
||||
|
||||
8
lib/bin/build_libjson-c-linux
Executable file
8
lib/bin/build_libjson-c-linux
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
source "${BASH_SOURCE%/*}/build_lib"
|
||||
|
||||
autoreconf() {
|
||||
command autoreconf -Iautoconf-archive/m4 "$@"
|
||||
}
|
||||
|
||||
build libjson-c linux
|
||||
8
lib/bin/build_libjson-c-windows
Normal file
8
lib/bin/build_libjson-c-windows
Normal file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
source "${BASH_SOURCE%/*}/build_lib"
|
||||
|
||||
autoreconf() {
|
||||
command autoreconf -Iautoconf-archive/m4 "$@"
|
||||
}
|
||||
|
||||
build libjson-c windows
|
||||
4
lib/bin/build_libsodium-linux
Executable file
4
lib/bin/build_libsodium-linux
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
source "${BASH_SOURCE%/*}/build_lib"
|
||||
|
||||
build libsodium linux
|
||||
4
lib/bin/build_libsodium-windows
Normal file
4
lib/bin/build_libsodium-windows
Normal file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
source "${BASH_SOURCE%/*}/build_lib"
|
||||
|
||||
build libsodium windows
|
||||
Submodule lib/libjson-c updated: e3752b5894...3df1f98b4a
Submodule lib/libsodium updated: cfb0f94704...850edc1175
@@ -13,12 +13,12 @@ add_library( mpw SHARED
|
||||
|
||||
add_library( sodium SHARED IMPORTED )
|
||||
set_target_properties( sodium PROPERTIES IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../lib/libsodium/build-android~/out/lib/${ANDROID_ABI}/libsodium.so" )
|
||||
target_include_directories( mpw PRIVATE "${PROJECT_SOURCE_DIR}/../lib/libsodium/build-android~/out" )
|
||||
target_include_directories( mpw PRIVATE "${PROJECT_SOURCE_DIR}/../lib/libsodium/build-android~/out/include" )
|
||||
target_compile_definitions( mpw PRIVATE -DMPW_SODIUM=1 )
|
||||
target_link_libraries( mpw PRIVATE sodium )
|
||||
|
||||
add_library( json-c SHARED IMPORTED )
|
||||
set_target_properties( json-c PROPERTIES IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../lib/libjson-c/build-android~/out/lib/${ANDROID_ABI}/libjson-c.so" )
|
||||
target_include_directories( mpw PRIVATE "${PROJECT_SOURCE_DIR}/../lib/libjson-c/build-android~/out" )
|
||||
target_include_directories( mpw PRIVATE "${PROJECT_SOURCE_DIR}/../lib/libjson-c/build-android~/out/include" )
|
||||
target_compile_definitions( mpw PRIVATE -DMPW_JSON=1 )
|
||||
target_link_libraries( mpw PRIVATE json-c )
|
||||
|
||||
@@ -22,8 +22,8 @@ android {
|
||||
}
|
||||
sourceSets {
|
||||
main {
|
||||
jniLibs.srcDirs "$projectDir/../lib/libsodium/build-android~/out/lib",
|
||||
"$projectDir/../lib/libjson-c/build-android~/out/lib"
|
||||
jniLibs.srcDirs "$rootDir/../lib/libsodium/build-android~/out/lib",
|
||||
"$rootDir/../lib/libjson-c/build-android~/out/lib"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,11 +55,23 @@ dependencies {
|
||||
}
|
||||
|
||||
preBuild {
|
||||
dependsOn task( type: Exec, 'buildLibSodium', {
|
||||
commandLine "$projectDir/../lib/bin/build_libsodium-android"
|
||||
dependsOn task( type: Exec, 'build_libsodium-android', {
|
||||
commandLine 'bash', "$rootDir/../lib/bin/build_libsodium-android"
|
||||
environment 'ANDROID_NDK_HOME', android.ndkDirectory
|
||||
} )
|
||||
dependsOn task( type: Exec, 'buildLibJson-c', {
|
||||
commandLine "$projectDir/../lib/bin/build_libjson-c-android"
|
||||
dependsOn task( type: Exec, 'build_libjson-c-android', {
|
||||
commandLine 'bash', "$rootDir/../lib/bin/build_libjson-c-android"
|
||||
environment 'ANDROID_NDK_HOME', android.ndkDirectory
|
||||
} )
|
||||
}
|
||||
|
||||
clean {
|
||||
dependsOn task( type: Exec, 'clean_libsodium-android', {
|
||||
commandLine 'bash', "$rootDir/../lib/bin/build_libsodium-android", 'clean'
|
||||
environment 'ANDROID_NDK_HOME', android.ndkDirectory
|
||||
} )
|
||||
dependsOn task( type: Exec, 'clean_libjson-c-android', {
|
||||
commandLine 'bash', "$rootDir/../lib/bin/build_libjson-c-android", 'clean'
|
||||
environment 'ANDROID_NDK_HOME', android.ndkDirectory
|
||||
} )
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
DA1554ED20B3928E00EA92C5 /* mpw-util.h */,
|
||||
);
|
||||
name = core;
|
||||
path = ../platform-independent/c/core/src;
|
||||
path = "../platform-independent/c/core/src";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
@@ -129,8 +129,9 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"/Library/Java/JavaVirtualMachines/jdk1.8.0_161.jdk/Contents/Home/include/**",
|
||||
../lib/libsodium/src/libsodium/include,
|
||||
"$(inherited)",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-ios~/out/include\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-ios~/out/include\"",
|
||||
);
|
||||
JAVA_HOME = /Library/Java/JavaVirtualMachines/jdk1.8.0_161.jdk/Contents/Home;
|
||||
OTHER_CFLAGS = (
|
||||
@@ -144,8 +145,9 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"/Library/Java/JavaVirtualMachines/jdk1.8.0_161.jdk/Contents/Home/include/**",
|
||||
../lib/libsodium/src/libsodium/include,
|
||||
"$(inherited)",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-ios~/out/include\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-ios~/out/include\"",
|
||||
);
|
||||
JAVA_HOME = /Library/Java/JavaVirtualMachines/jdk1.8.0_161.jdk/Contents/Home;
|
||||
OTHER_CFLAGS = (
|
||||
|
||||
@@ -1783,7 +1783,7 @@
|
||||
93D39CF7DB942C69D1C5D6BE /* mpw-util.h */,
|
||||
);
|
||||
name = core;
|
||||
path = ../platform-independent/c/core/src;
|
||||
path = "../platform-independent/c/core/src";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DA0CC4F41EAB99BA009A8ED9 /* Resources */ = {
|
||||
@@ -4309,12 +4309,6 @@
|
||||
GCC_WARN_UNUSED_LABEL = YES;
|
||||
GCC_WARN_UNUSED_VALUE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"",
|
||||
"\"$(PROJECT_DIR)/External/libsodium/libsodium-ios/include\"",
|
||||
"\"$(PROJECT_DIR)/External/libjson-c/libjson-c-ios/include\"",
|
||||
"$(inherited)",
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
@@ -4348,13 +4342,18 @@
|
||||
GCC_C_LANGUAGE_STANDARD = c11;
|
||||
GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";
|
||||
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-ios~/out/include\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-ios~/out/include\"",
|
||||
);
|
||||
INFOPLIST_FILE = "Source/iOS/MasterPassword-Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/../lib/libsodium/build-ios~/out/lib",
|
||||
"$(PROJECT_DIR)/../lib/libjson-c/build-ios~/out/lib",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-ios~/out/lib\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-ios~/out/lib\"",
|
||||
);
|
||||
OTHER_CFLAGS = (
|
||||
"-DMPW_SODIUM=1",
|
||||
@@ -4524,12 +4523,6 @@
|
||||
GCC_WARN_UNUSED_LABEL = YES;
|
||||
GCC_WARN_UNUSED_VALUE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"",
|
||||
"\"$(PROJECT_DIR)/External/libsodium/libsodium-ios/include\"",
|
||||
"\"$(PROJECT_DIR)/External/libjson-c/libjson-c-ios/include\"",
|
||||
"$(inherited)",
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
@@ -4635,12 +4628,6 @@
|
||||
GCC_WARN_UNUSED_LABEL = YES;
|
||||
GCC_WARN_UNUSED_VALUE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"",
|
||||
"\"$(PROJECT_DIR)/External/libsodium/libsodium-ios/include\"",
|
||||
"\"$(PROJECT_DIR)/External/libjson-c/libjson-c-ios/include\"",
|
||||
"$(inherited)",
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
@@ -4673,13 +4660,18 @@
|
||||
GCC_C_LANGUAGE_STANDARD = c11;
|
||||
GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";
|
||||
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-ios~/out/include\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-ios~/out/include\"",
|
||||
);
|
||||
INFOPLIST_FILE = "Source/iOS/MasterPassword-Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/../lib/libsodium/build-ios~/out/lib",
|
||||
"$(PROJECT_DIR)/../lib/libjson-c/build-ios~/out/lib",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-ios~/out/lib\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-ios~/out/lib\"",
|
||||
);
|
||||
OTHER_CFLAGS = (
|
||||
"-DMPW_SODIUM=1",
|
||||
@@ -4712,13 +4704,18 @@
|
||||
GCC_C_LANGUAGE_STANDARD = c11;
|
||||
GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";
|
||||
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-ios~/out/include\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-ios~/out/include\"",
|
||||
);
|
||||
INFOPLIST_FILE = "Source/iOS/MasterPassword-Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/../lib/libsodium/build-ios~/out/lib",
|
||||
"$(PROJECT_DIR)/../lib/libjson-c/build-ios~/out/lib",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-ios~/out/lib\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-ios~/out/lib\"",
|
||||
);
|
||||
OTHER_CFLAGS = (
|
||||
"-DMPW_SODIUM=1",
|
||||
|
||||
@@ -1224,7 +1224,7 @@
|
||||
DA1C7ABC1F1A8F6E009A3551 /* mpw-tests.c */,
|
||||
);
|
||||
name = cli;
|
||||
path = "../../platform-independent/cli-c/cli";
|
||||
path = ../../cli/src;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DA2508F819513C1400AC23F1 /* Other Frameworks */ = {
|
||||
@@ -1820,7 +1820,7 @@
|
||||
DA6773C61A4746AF004F356A /* mpw-util.h */,
|
||||
);
|
||||
name = core;
|
||||
path = ../platform-independent/c/core/src;
|
||||
path = "../platform-independent/c/core/src";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DA89D4E51A51E53100AC64D7 /* Pearl-Cocoa */ = {
|
||||
@@ -2992,15 +2992,8 @@
|
||||
GCC_WARN_UNUSED_LABEL = YES;
|
||||
GCC_WARN_UNUSED_VALUE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"",
|
||||
"\"$(PROJECT_DIR)/External/libsodium/libsodium-osx/include\"",
|
||||
"\"$(PROJECT_DIR)/External/libjson-c/libjson-c-osx/include\"",
|
||||
"$(inherited)",
|
||||
);
|
||||
LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)";
|
||||
LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = "$(inherit)";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
@@ -3028,12 +3021,17 @@
|
||||
);
|
||||
GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";
|
||||
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/include\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/include\"",
|
||||
);
|
||||
INFOPLIST_FILE = "Source/Mac/MasterPassword-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib",
|
||||
"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib\"",
|
||||
);
|
||||
OTHER_CFLAGS = (
|
||||
"-DMPW_SODIUM=1",
|
||||
@@ -3084,13 +3082,15 @@
|
||||
buildSettings = {
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"",
|
||||
/usr/include/libxml2,
|
||||
"$(inherited)",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/include\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/include\"",
|
||||
/usr/include/libxml2,
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib\"",
|
||||
);
|
||||
OTHER_CFLAGS = (
|
||||
"-DMPW_SODIUM=1",
|
||||
@@ -3103,10 +3103,15 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/include\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/include\"",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib",
|
||||
"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib\"",
|
||||
);
|
||||
OTHER_CFLAGS = (
|
||||
"-DMPW_SODIUM=1",
|
||||
@@ -3122,10 +3127,15 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/include\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/include\"",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib",
|
||||
"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib\"",
|
||||
);
|
||||
OTHER_CFLAGS = (
|
||||
"-DMPW_SODIUM=1",
|
||||
@@ -3141,10 +3151,15 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/include\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/include\"",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib",
|
||||
"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib\"",
|
||||
);
|
||||
OTHER_CFLAGS = (
|
||||
"-DMPW_SODIUM=1",
|
||||
@@ -3160,9 +3175,15 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/include\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/include\"",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib\"",
|
||||
);
|
||||
OTHER_CFLAGS = (
|
||||
"-DMPW_SODIUM=1",
|
||||
@@ -3176,9 +3197,15 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/include\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/include\"",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib\"",
|
||||
);
|
||||
OTHER_CFLAGS = (
|
||||
"-DMPW_SODIUM=1",
|
||||
@@ -3192,9 +3219,15 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/include\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/include\"",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib\"",
|
||||
);
|
||||
OTHER_CFLAGS = (
|
||||
"-DMPW_SODIUM=1",
|
||||
@@ -3348,15 +3381,8 @@
|
||||
GCC_WARN_UNUSED_LABEL = YES;
|
||||
GCC_WARN_UNUSED_VALUE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"",
|
||||
"\"$(PROJECT_DIR)/External/libsodium/libsodium-osx/include\"",
|
||||
"\"$(PROJECT_DIR)/External/libjson-c/libjson-c-osx/include\"",
|
||||
"$(inherited)",
|
||||
);
|
||||
LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)";
|
||||
LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = "$(inherit)";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
@@ -3455,15 +3481,8 @@
|
||||
GCC_WARN_UNUSED_LABEL = YES;
|
||||
GCC_WARN_UNUSED_VALUE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"",
|
||||
"\"$(PROJECT_DIR)/External/libsodium/libsodium-osx/include\"",
|
||||
"\"$(PROJECT_DIR)/External/libjson-c/libjson-c-osx/include\"",
|
||||
"$(inherited)",
|
||||
);
|
||||
LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)";
|
||||
LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = "$(inherit)";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
@@ -3491,12 +3510,17 @@
|
||||
);
|
||||
GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";
|
||||
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/include\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/include\"",
|
||||
);
|
||||
INFOPLIST_FILE = "Source/Mac/MasterPassword-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib",
|
||||
"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib\"",
|
||||
);
|
||||
OTHER_CFLAGS = (
|
||||
"-DMPW_SODIUM=1",
|
||||
@@ -3523,12 +3547,17 @@
|
||||
);
|
||||
GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";
|
||||
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/include\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/include\"",
|
||||
);
|
||||
INFOPLIST_FILE = "Source/Mac/MasterPassword-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib",
|
||||
"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib\"",
|
||||
);
|
||||
OTHER_CFLAGS = (
|
||||
"-DMPW_SODIUM=1",
|
||||
@@ -3545,13 +3574,15 @@
|
||||
buildSettings = {
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"",
|
||||
/usr/include/libxml2,
|
||||
"$(inherited)",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/include\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/include\"",
|
||||
/usr/include/libxml2,
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib\"",
|
||||
);
|
||||
OTHER_CFLAGS = (
|
||||
"-DMPW_SODIUM=1",
|
||||
@@ -3565,13 +3596,15 @@
|
||||
buildSettings = {
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"",
|
||||
/usr/include/libxml2,
|
||||
"$(inherited)",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/include\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/include\"",
|
||||
/usr/include/libxml2,
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/lib\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib\"",
|
||||
);
|
||||
OTHER_CFLAGS = (
|
||||
"-DMPW_SODIUM=1",
|
||||
|
||||
@@ -219,7 +219,7 @@
|
||||
- (IBAction)sourceButton:(id)sender {
|
||||
|
||||
[[self dismissPopup].navigationController performSegueWithIdentifier:@"web" sender:
|
||||
[NSURL URLWithString:@"https://github.com/Lyndir/MasterPassword/"]];
|
||||
[NSURL URLWithString:@"https://gitlab.com/MasterPassword/MasterPassword/"]];
|
||||
}
|
||||
|
||||
- (IBAction)thanksButton:(id)sender {
|
||||
|
||||
@@ -9,71 +9,107 @@ plugins {
|
||||
description = 'Master Password Algorithm Implementation'
|
||||
|
||||
artifacts {
|
||||
'default' task( type: Zip, "archive" ) {
|
||||
'default' task( type: Zip, 'archive' ) {
|
||||
// TODO: exclude lib files that are produced by the build.
|
||||
from 'lib'
|
||||
|
||||
components.withType( ComponentWithRuntimeFile ) {
|
||||
if (isOptimized()) {
|
||||
from getRuntimeFile()
|
||||
into standardOperatingSystem( linkTask.get().targetPlatform.get() ) + '/' +
|
||||
standardArchitecture( linkTask.get().targetPlatform.get() )
|
||||
from runtimeFile, {
|
||||
into standardOperatingSystem( targetPlatform ) + '/' + standardArchitecture( targetPlatform )
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
library {
|
||||
baseName.set( "mpw" )
|
||||
linkage.set( [Linkage.STATIC, Linkage.SHARED] )
|
||||
baseName.set( 'mpw' )
|
||||
linkage.set( [Linkage.SHARED] )
|
||||
|
||||
// Reconfigure the toolchain from C++ to C.
|
||||
toolChains {
|
||||
withType( GccCompatibleToolChain ) {
|
||||
withType( VisualCpp ) {
|
||||
eachPlatform {
|
||||
cppCompiler.withArguments { addAll( ["-x", "c", "-std=c11", "-Werror", "-DMPW_SODIUM=1"] ) }
|
||||
cppCompiler.withArguments { addAll( ['/TC', '/MT', '/Ox', '/DMPW_SODIUM=1', '/DSODIUM_STATIC', '/DSODIUM_EXPORT='] ) }
|
||||
}
|
||||
}
|
||||
withType( GccCompatibleToolChain ) {
|
||||
eachPlatform {
|
||||
cppCompiler.withArguments { addAll( ['-x', 'c', '-O3', '-std=c11', '-Werror', '-DMPW_SODIUM=1'] ) }
|
||||
}
|
||||
}
|
||||
}
|
||||
components.withType( CppComponent ) {
|
||||
cppSource.from fileTree( dir: "src", include: "**/*.c" )
|
||||
}
|
||||
|
||||
// Cross-compile for these host platforms.
|
||||
// TODO: Cross-compiling, blocked by: https://github.com/gradle/gradle-native/issues/169 - CppLibraryPlugin.java:163
|
||||
operatingSystems.set( [objects.named( OperatingSystemFamily, OperatingSystemFamily.WINDOWS ),
|
||||
objects.named( OperatingSystemFamily, OperatingSystemFamily.LINUX ),
|
||||
objects.named( OperatingSystemFamily, OperatingSystemFamily.MAC_OS )] )
|
||||
operatingSystems.set( [objects.named( OperatingSystemFamily, OperatingSystemFamily.LINUX ),
|
||||
objects.named( OperatingSystemFamily, OperatingSystemFamily.MAC_OS ),
|
||||
objects.named( OperatingSystemFamily, OperatingSystemFamily.WINDOWS )] )
|
||||
|
||||
binaries.configureEach {
|
||||
// Resolve a standard name for the platform.
|
||||
def platform = standardOperatingSystem( targetPlatform )
|
||||
components.withType( CppComponent ) {
|
||||
cppSource.from fileTree( 'src' )
|
||||
|
||||
project.dependencies {
|
||||
// Depend on JDK for JNI support.
|
||||
add( includePathConfiguration.name,
|
||||
files( new File( Jvm.current().javaHome, "include" ) ) { first().eachDir { from it } } )
|
||||
privateHeaders {
|
||||
// JDK for JNI support.
|
||||
from files( new File( Jvm.current().javaHome, 'include' ) ) { first().eachDir { from it } }
|
||||
}
|
||||
|
||||
// Depend on libsodium from `lib`; run `lib/bin/build_libsodium-${platform}` first.
|
||||
add( includePathConfiguration.name,
|
||||
files( "../../../lib/libsodium/build-${platform}~/out/include" ) )
|
||||
add( linkLibraries.name,
|
||||
fileTree( "../../../lib/libsodium/build-${platform}~/out/lib" ) )
|
||||
binaries.configureEach {
|
||||
def system = standardOperatingSystem( targetPlatform )
|
||||
|
||||
project.dependencies {
|
||||
add( linkLibraries.name,
|
||||
fileTree( "$rootDir/../lib/libsodium/build-${system}~/out/lib" ) )
|
||||
}
|
||||
|
||||
archive.dependsOn project.tasks.maybeCreate( "build_libsodium-${system}", Exec.class ).configure {
|
||||
commandLine 'bash', "$rootDir/../lib/bin/build_libsodium-${system}"
|
||||
privateHeaders.from "$rootDir/../lib/libsodium/build-${system}~/out/include"
|
||||
}
|
||||
clean.dependsOn project.tasks.maybeCreate( "clean_libsodium-${system}", Exec.class ).configure {
|
||||
commandLine 'bash', "$rootDir/../lib/bin/build_libsodium-${system}", 'clean'
|
||||
}
|
||||
archive.dependsOn project.tasks.maybeCreate( "build_libjson-c-${system}", Exec.class ).configure {
|
||||
commandLine 'bash', "$rootDir/../lib/bin/build_libjson-c-${system}"
|
||||
privateHeaders.from "$rootDir/../lib/libjson-c/build-${system}~/out/include"
|
||||
}
|
||||
clean.dependsOn project.tasks.maybeCreate( "clean_libjson-c-${system}", Exec.class ).configure {
|
||||
commandLine 'bash', "$rootDir/../lib/bin/build_libjson-c-${system}", 'clean'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static String standardOperatingSystem(NativePlatform platform) {
|
||||
OperatingSystem os = platform.getOperatingSystem()
|
||||
if (os.isWindows()) {
|
||||
if (os.isWindows())
|
||||
return OperatingSystemFamily.WINDOWS
|
||||
} else if (os.isLinux()) {
|
||||
return OperatingSystemFamily.LINUX
|
||||
} else if (os.isMacOsX()) {
|
||||
return OperatingSystemFamily.MAC_OS
|
||||
}
|
||||
|
||||
return os.name.toLowerCase()
|
||||
else if (os.isMacOsX())
|
||||
return OperatingSystemFamily.MAC_OS
|
||||
|
||||
else if (os.isLinux())
|
||||
return OperatingSystemFamily.LINUX
|
||||
|
||||
// Other systems will need to use a Linux compatibility layer.
|
||||
return OperatingSystemFamily.LINUX
|
||||
}
|
||||
|
||||
static String standardArchitecture(NativePlatform platform) {
|
||||
Architecture arch = platform.getArchitecture()
|
||||
return arch.name.toLowerCase().replaceAll( "-", "_" )
|
||||
if (arch.isArm())
|
||||
return 'arm'
|
||||
|
||||
else if (arch.name.toLowerCase( Locale.ROOT ).startsWith( 'arm' ))
|
||||
return 'arm64'
|
||||
|
||||
else if (arch.isAmd64())
|
||||
return 'x86_64'
|
||||
|
||||
else if (arch.isI386())
|
||||
return 'x86'
|
||||
|
||||
// Other systems will need to be compatible with the x86 architecture.
|
||||
return 'x86'
|
||||
}
|
||||
|
||||
BIN
platform-independent/c/core/lib/linux/x86/libmpw.so
Executable file
BIN
platform-independent/c/core/lib/linux/x86/libmpw.so
Executable file
Binary file not shown.
BIN
platform-independent/c/core/lib/linux/x86_64/libmpw.so
Executable file
BIN
platform-independent/c/core/lib/linux/x86_64/libmpw.so
Executable file
Binary file not shown.
BIN
platform-independent/c/core/lib/windows/x86_64/mpw.dll
Normal file
BIN
platform-independent/c/core/lib/windows/x86_64/mpw.dll
Normal file
Binary file not shown.
@@ -38,14 +38,16 @@ char *mpw_get_token(const char **in, const char *eol, char *delim) {
|
||||
time_t mpw_mktime(
|
||||
const char *time) {
|
||||
|
||||
// TODO: Support parsing timezone into tm_gmtoff
|
||||
// TODO: Support for parsing non-UTC time strings
|
||||
struct tm tm = { .tm_isdst = -1 };
|
||||
if (time && sscanf( time, "%4d-%2d-%2dT%2d:%2d:%2dZ",
|
||||
&tm.tm_year, &tm.tm_mon, &tm.tm_mday,
|
||||
&tm.tm_hour, &tm.tm_min, &tm.tm_sec ) == 6) {
|
||||
tm.tm_year -= 1900; // tm_year 0 = rfc3339 year 1900
|
||||
tm.tm_mon -= 1; // tm_mon 0 = rfc3339 month 1
|
||||
return mktime( &tm );
|
||||
|
||||
// mktime converts tm to local, setting tm_gmtoff; use it to offset the result back to UTC.
|
||||
return mktime( &tm ) + tm.tm_gmtoff;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -206,7 +206,7 @@ static bool mpw_marshal_write_flat(
|
||||
if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &site->lastUsed ) ))
|
||||
mpw_string_pushf( out, "%s %8ld %lu:%lu:%lu %25s\t%25s\t%s\n",
|
||||
dateString, (long)site->uses, (long)site->type, (long)site->algorithm, (long)site->counter,
|
||||
loginContent?: "", site->name, content?: "" );
|
||||
loginContent? loginContent: "", site->name, content? content: "" );
|
||||
mpw_free_strings( &content, &loginContent, NULL );
|
||||
}
|
||||
mpw_free( &masterKey, MPMasterKeySize );
|
||||
@@ -304,39 +304,43 @@ static bool mpw_marshal_write_json(
|
||||
if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &site->lastUsed ) ))
|
||||
json_object_object_add( json_site, "last_used", json_object_new_string( dateString ) );
|
||||
|
||||
json_object *json_site_questions = json_object_new_object();
|
||||
json_object_object_add( json_site, "questions", json_site_questions );
|
||||
for (size_t q = 0; q < site->questions_count; ++q) {
|
||||
MPMarshalledQuestion *question = &site->questions[q];
|
||||
if (!question->keyword)
|
||||
continue;
|
||||
if (site->questions_count) {
|
||||
json_object *json_site_questions = json_object_new_object();
|
||||
json_object_object_add( json_site, "questions", json_site_questions );
|
||||
for (size_t q = 0; q < site->questions_count; ++q) {
|
||||
MPMarshalledQuestion *question = &site->questions[q];
|
||||
if (!question->keyword)
|
||||
continue;
|
||||
|
||||
json_object *json_site_question = json_object_new_object();
|
||||
json_object_object_add( json_site_questions, question->keyword, json_site_question );
|
||||
json_object_object_add( json_site_question, "type", json_object_new_int( (int32_t)question->type ) );
|
||||
json_object *json_site_question = json_object_new_object();
|
||||
json_object_object_add( json_site_questions, question->keyword, json_site_question );
|
||||
json_object_object_add( json_site_question, "type", json_object_new_int( (int32_t)question->type ) );
|
||||
|
||||
if (!user->redacted) {
|
||||
// Clear Text
|
||||
const char *answerContent = mpw_siteResult( masterKey, site->name, MPCounterValueInitial,
|
||||
MPKeyPurposeRecovery, question->keyword, question->type, question->content, site->algorithm );
|
||||
json_object_object_add( json_site_question, "answer", json_object_new_string( answerContent ) );
|
||||
}
|
||||
else {
|
||||
// Redacted
|
||||
if (site->type & MPSiteFeatureExportContent && question->content && strlen( question->content ))
|
||||
json_object_object_add( json_site_question, "answer", json_object_new_string( question->content ) );
|
||||
if (!user->redacted) {
|
||||
// Clear Text
|
||||
const char *answerContent = mpw_siteResult( masterKey, site->name, MPCounterValueInitial,
|
||||
MPKeyPurposeRecovery, question->keyword, question->type, question->content, site->algorithm );
|
||||
json_object_object_add( json_site_question, "answer", json_object_new_string( answerContent ) );
|
||||
}
|
||||
else {
|
||||
// Redacted
|
||||
if (site->type & MPSiteFeatureExportContent && question->content && strlen( question->content ))
|
||||
json_object_object_add( json_site_question, "answer", json_object_new_string( question->content ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
json_object *json_site_mpw = json_object_new_object();
|
||||
json_object_object_add( json_site, "_ext_mpw", json_site_mpw );
|
||||
if (site->url)
|
||||
json_object_object_add( json_site_mpw, "url", json_object_new_string( site->url ) );
|
||||
if (json_object_object_length( json_site_mpw ))
|
||||
json_object_object_add( json_site, "_ext_mpw", json_site_mpw );
|
||||
|
||||
mpw_free_strings( &content, &loginContent, NULL );
|
||||
}
|
||||
|
||||
mpw_string_pushf( out, "%s\n", json_object_to_json_string_ext( json_file, JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED ) );
|
||||
mpw_string_pushf( out, "%s\n", json_object_to_json_string_ext( json_file,
|
||||
JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_NOSLASHESCAPE ) );
|
||||
mpw_free( &masterKey, MPMasterKeySize );
|
||||
json_object_put( json_file );
|
||||
|
||||
@@ -797,23 +801,25 @@ static MPMarshalledUser *mpw_marshal_read_json(
|
||||
site->loginContent = mpw_strdup( siteLoginName );
|
||||
}
|
||||
|
||||
json_object_iter json_site_question;
|
||||
json_object *json_site_questions = mpw_get_json_section( json_site.val, "questions" );
|
||||
json_object_object_foreachC( json_site_questions, json_site_question ) {
|
||||
MPMarshalledQuestion *question = mpw_marshal_question( site, json_site_question.key );
|
||||
const char *answerContent = mpw_get_json_string( json_site_question.val, "answer", NULL );
|
||||
question->type = (MPResultType)mpw_get_json_int( json_site_question.val, "type", MPResultTypeTemplatePhrase );
|
||||
if (json_site_questions) {
|
||||
json_object_iter json_site_question;
|
||||
json_object_object_foreachC( json_site_questions, json_site_question ) {
|
||||
MPMarshalledQuestion *question = mpw_marshal_question( site, json_site_question.key );
|
||||
const char *answerContent = mpw_get_json_string( json_site_question.val, "answer", NULL );
|
||||
question->type = (MPResultType)mpw_get_json_int( json_site_question.val, "type", MPResultTypeTemplatePhrase );
|
||||
|
||||
if (!user->redacted) {
|
||||
// Clear Text
|
||||
if (answerContent && strlen( answerContent ))
|
||||
question->content = mpw_siteState( masterKey, site->name, MPCounterValueInitial,
|
||||
MPKeyPurposeRecovery, question->keyword, question->type, answerContent, site->algorithm );
|
||||
}
|
||||
else {
|
||||
// Redacted
|
||||
if (answerContent && strlen( answerContent ))
|
||||
question->content = mpw_strdup( answerContent );
|
||||
if (!user->redacted) {
|
||||
// Clear Text
|
||||
if (answerContent && strlen( answerContent ))
|
||||
question->content = mpw_siteState( masterKey, site->name, MPCounterValueInitial,
|
||||
MPKeyPurposeRecovery, question->keyword, question->type, answerContent, site->algorithm );
|
||||
}
|
||||
else {
|
||||
// Redacted
|
||||
if (answerContent && strlen( answerContent ))
|
||||
question->content = mpw_strdup( answerContent );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -871,21 +877,14 @@ const MPMarshalFormat mpw_formatWithName(
|
||||
if (!formatName || !strlen( formatName ))
|
||||
return MPMarshalFormatNone;
|
||||
|
||||
// Lower-case to standardize it.
|
||||
size_t stdFormatNameSize = strlen( formatName );
|
||||
char stdFormatName[stdFormatNameSize + 1];
|
||||
for (size_t c = 0; c < stdFormatNameSize; ++c)
|
||||
stdFormatName[c] = (char)tolower( formatName[c] );
|
||||
stdFormatName[stdFormatNameSize] = '\0';
|
||||
|
||||
if (strncmp( mpw_nameForFormat( MPMarshalFormatNone ), stdFormatName, strlen( stdFormatName ) ) == 0)
|
||||
if (mpw_strncasecmp( mpw_nameForFormat( MPMarshalFormatNone ), formatName, strlen( formatName ) ) == 0)
|
||||
return MPMarshalFormatNone;
|
||||
if (strncmp( mpw_nameForFormat( MPMarshalFormatFlat ), stdFormatName, strlen( stdFormatName ) ) == 0)
|
||||
if (mpw_strncasecmp( mpw_nameForFormat( MPMarshalFormatFlat ), formatName, strlen( formatName ) ) == 0)
|
||||
return MPMarshalFormatFlat;
|
||||
if (strncmp( mpw_nameForFormat( MPMarshalFormatJSON ), stdFormatName, strlen( stdFormatName ) ) == 0)
|
||||
if (mpw_strncasecmp( mpw_nameForFormat( MPMarshalFormatJSON ), formatName, strlen( formatName ) ) == 0)
|
||||
return MPMarshalFormatJSON;
|
||||
|
||||
dbg( "Not a format name: %s", stdFormatName );
|
||||
dbg( "Not a format name: %s", formatName );
|
||||
return (MPMarshalFormat)ERR;
|
||||
}
|
||||
|
||||
|
||||
@@ -53,38 +53,31 @@ const MPResultType mpw_typeWithName(const char *typeName) {
|
||||
return MPResultTypeDeriveKey;
|
||||
}
|
||||
|
||||
// Lower-case typeName to standardize it.
|
||||
size_t stdTypeNameSize = strlen( typeName );
|
||||
char stdTypeName[stdTypeNameSize + 1];
|
||||
for (size_t c = 0; c < stdTypeNameSize; ++c)
|
||||
stdTypeName[c] = (char)tolower( typeName[c] );
|
||||
stdTypeName[stdTypeNameSize] = '\0';
|
||||
|
||||
// Find what password type is represented by the type name.
|
||||
if (strncmp( mpw_nameForType( MPResultTypeTemplateMaximum ), stdTypeName, strlen( stdTypeName ) ) == 0)
|
||||
if (mpw_strncasecmp( mpw_nameForType( MPResultTypeTemplateMaximum ), typeName, strlen( typeName ) ) == 0)
|
||||
return MPResultTypeTemplateMaximum;
|
||||
if (strncmp( mpw_nameForType( MPResultTypeTemplateLong ), stdTypeName, strlen( stdTypeName ) ) == 0)
|
||||
if (mpw_strncasecmp( mpw_nameForType( MPResultTypeTemplateLong ), typeName, strlen( typeName ) ) == 0)
|
||||
return MPResultTypeTemplateLong;
|
||||
if (strncmp( mpw_nameForType( MPResultTypeTemplateMedium ), stdTypeName, strlen( stdTypeName ) ) == 0)
|
||||
if (mpw_strncasecmp( mpw_nameForType( MPResultTypeTemplateMedium ), typeName, strlen( typeName ) ) == 0)
|
||||
return MPResultTypeTemplateMedium;
|
||||
if (strncmp( mpw_nameForType( MPResultTypeTemplateBasic ), stdTypeName, strlen( stdTypeName ) ) == 0)
|
||||
if (mpw_strncasecmp( mpw_nameForType( MPResultTypeTemplateBasic ), typeName, strlen( typeName ) ) == 0)
|
||||
return MPResultTypeTemplateBasic;
|
||||
if (strncmp( mpw_nameForType( MPResultTypeTemplateShort ), stdTypeName, strlen( stdTypeName ) ) == 0)
|
||||
if (mpw_strncasecmp( mpw_nameForType( MPResultTypeTemplateShort ), typeName, strlen( typeName ) ) == 0)
|
||||
return MPResultTypeTemplateShort;
|
||||
if (strncmp( mpw_nameForType( MPResultTypeTemplatePIN ), stdTypeName, strlen( stdTypeName ) ) == 0)
|
||||
if (mpw_strncasecmp( mpw_nameForType( MPResultTypeTemplatePIN ), typeName, strlen( typeName ) ) == 0)
|
||||
return MPResultTypeTemplatePIN;
|
||||
if (strncmp( mpw_nameForType( MPResultTypeTemplateName ), stdTypeName, strlen( stdTypeName ) ) == 0)
|
||||
if (mpw_strncasecmp( mpw_nameForType( MPResultTypeTemplateName ), typeName, strlen( typeName ) ) == 0)
|
||||
return MPResultTypeTemplateName;
|
||||
if (strncmp( mpw_nameForType( MPResultTypeTemplatePhrase ), stdTypeName, strlen( stdTypeName ) ) == 0)
|
||||
if (mpw_strncasecmp( mpw_nameForType( MPResultTypeTemplatePhrase ), typeName, strlen( typeName ) ) == 0)
|
||||
return MPResultTypeTemplatePhrase;
|
||||
if (strncmp( mpw_nameForType( MPResultTypeStatefulPersonal ), stdTypeName, strlen( stdTypeName ) ) == 0)
|
||||
if (mpw_strncasecmp( mpw_nameForType( MPResultTypeStatefulPersonal ), typeName, strlen( typeName ) ) == 0)
|
||||
return MPResultTypeStatefulPersonal;
|
||||
if (strncmp( mpw_nameForType( MPResultTypeStatefulDevice ), stdTypeName, strlen( stdTypeName ) ) == 0)
|
||||
if (mpw_strncasecmp( mpw_nameForType( MPResultTypeStatefulDevice ), typeName, strlen( typeName ) ) == 0)
|
||||
return MPResultTypeStatefulDevice;
|
||||
if (strncmp( mpw_nameForType( MPResultTypeDeriveKey ), stdTypeName, strlen( stdTypeName ) ) == 0)
|
||||
if (mpw_strncasecmp( mpw_nameForType( MPResultTypeDeriveKey ), typeName, strlen( typeName ) ) == 0)
|
||||
return MPResultTypeDeriveKey;
|
||||
|
||||
dbg( "Not a generated type name: %s", stdTypeName );
|
||||
dbg( "Not a generated type name: %s", typeName );
|
||||
return (MPResultType)ERR;
|
||||
}
|
||||
|
||||
@@ -129,35 +122,35 @@ const char **mpw_templatesForType(MPResultType type, size_t *count) {
|
||||
|
||||
switch (type) {
|
||||
case MPResultTypeTemplateMaximum:
|
||||
return mpw_alloc_array( count, const char *,
|
||||
"anoxxxxxxxxxxxxxxxxx", "axxxxxxxxxxxxxxxxxno" );
|
||||
return mpw_strings( count,
|
||||
"anoxxxxxxxxxxxxxxxxx", "axxxxxxxxxxxxxxxxxno", NULL );
|
||||
case MPResultTypeTemplateLong:
|
||||
return mpw_alloc_array( count, const char *,
|
||||
return mpw_strings( count,
|
||||
"CvcvnoCvcvCvcv", "CvcvCvcvnoCvcv", "CvcvCvcvCvcvno",
|
||||
"CvccnoCvcvCvcv", "CvccCvcvnoCvcv", "CvccCvcvCvcvno",
|
||||
"CvcvnoCvccCvcv", "CvcvCvccnoCvcv", "CvcvCvccCvcvno",
|
||||
"CvcvnoCvcvCvcc", "CvcvCvcvnoCvcc", "CvcvCvcvCvccno",
|
||||
"CvccnoCvccCvcv", "CvccCvccnoCvcv", "CvccCvccCvcvno",
|
||||
"CvcvnoCvccCvcc", "CvcvCvccnoCvcc", "CvcvCvccCvccno",
|
||||
"CvccnoCvcvCvcc", "CvccCvcvnoCvcc", "CvccCvcvCvccno" );
|
||||
"CvccnoCvcvCvcc", "CvccCvcvnoCvcc", "CvccCvcvCvccno", NULL );
|
||||
case MPResultTypeTemplateMedium:
|
||||
return mpw_alloc_array( count, const char *,
|
||||
"CvcnoCvc", "CvcCvcno" );
|
||||
return mpw_strings( count,
|
||||
"CvcnoCvc", "CvcCvcno", NULL );
|
||||
case MPResultTypeTemplateShort:
|
||||
return mpw_alloc_array( count, const char *,
|
||||
"Cvcn" );
|
||||
return mpw_strings( count,
|
||||
"Cvcn", NULL );
|
||||
case MPResultTypeTemplateBasic:
|
||||
return mpw_alloc_array( count, const char *,
|
||||
"aaanaaan", "aannaaan", "aaannaaa" );
|
||||
return mpw_strings( count,
|
||||
"aaanaaan", "aannaaan", "aaannaaa", NULL );
|
||||
case MPResultTypeTemplatePIN:
|
||||
return mpw_alloc_array( count, const char *,
|
||||
"nnnn" );
|
||||
return mpw_strings( count,
|
||||
"nnnn", NULL );
|
||||
case MPResultTypeTemplateName:
|
||||
return mpw_alloc_array( count, const char *,
|
||||
"cvccvcvcv" );
|
||||
return mpw_strings( count,
|
||||
"cvccvcvcv", NULL );
|
||||
case MPResultTypeTemplatePhrase:
|
||||
return mpw_alloc_array( count, const char *,
|
||||
"cvcc cvc cvccvcv cvc", "cvc cvccvcvcv cvcv", "cv cvccv cvc cvcvccv" );
|
||||
return mpw_strings( count,
|
||||
"cvcc cvc cvccvcv cvc", "cvc cvccvcvcv cvcv", "cv cvccv cvc cvcvccv", NULL );
|
||||
default: {
|
||||
dbg( "Unknown generated type: %d", type );
|
||||
return NULL;
|
||||
@@ -177,21 +170,14 @@ const char *mpw_templateForType(MPResultType type, uint8_t templateIndex) {
|
||||
|
||||
const MPKeyPurpose mpw_purposeWithName(const char *purposeName) {
|
||||
|
||||
// Lower-case and trim optionally leading "generated" string from typeName to standardize it.
|
||||
size_t stdPurposeNameSize = strlen( purposeName );
|
||||
char stdPurposeName[stdPurposeNameSize + 1];
|
||||
for (size_t c = 0; c < stdPurposeNameSize; ++c)
|
||||
stdPurposeName[c] = (char)tolower( purposeName[c] );
|
||||
stdPurposeName[stdPurposeNameSize] = '\0';
|
||||
|
||||
if (strncmp( mpw_nameForPurpose( MPKeyPurposeAuthentication ), stdPurposeName, strlen( stdPurposeName ) ) == 0)
|
||||
if (mpw_strncasecmp( mpw_nameForPurpose( MPKeyPurposeAuthentication ), purposeName, strlen( purposeName ) ) == 0)
|
||||
return MPKeyPurposeAuthentication;
|
||||
if (strncmp( mpw_nameForPurpose( MPKeyPurposeIdentification ), stdPurposeName, strlen( stdPurposeName ) ) == 0)
|
||||
if (mpw_strncasecmp( mpw_nameForPurpose( MPKeyPurposeIdentification ), purposeName, strlen( purposeName ) ) == 0)
|
||||
return MPKeyPurposeIdentification;
|
||||
if (strncmp( mpw_nameForPurpose( MPKeyPurposeRecovery ), stdPurposeName, strlen( stdPurposeName ) ) == 0)
|
||||
if (mpw_strncasecmp( mpw_nameForPurpose( MPKeyPurposeRecovery ), purposeName, strlen( purposeName ) ) == 0)
|
||||
return MPKeyPurposeRecovery;
|
||||
|
||||
dbg( "Not a purpose name: %s", stdPurposeName );
|
||||
dbg( "Not a purpose name: %s", purposeName );
|
||||
return (MPKeyPurpose)ERR;
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,27 @@ void mpw_uint64(const uint64_t number, uint8_t buf[8]) {
|
||||
buf[7] = (uint8_t)((number >> 0L) & UINT8_MAX);
|
||||
}
|
||||
|
||||
const char **mpw_strings(size_t *count, const char *strings, ...) {
|
||||
|
||||
va_list args;
|
||||
va_start( args, strings );
|
||||
const char **array = NULL;
|
||||
size_t size = 0;
|
||||
for (const char *string = strings; string; (string = va_arg( args, const char * ))) {
|
||||
size_t cursor = size / sizeof( *array );
|
||||
if (!mpw_realloc( &array, &size, sizeof( string ) )) {
|
||||
mpw_free( &array, size );
|
||||
*count = 0;
|
||||
return NULL;
|
||||
}
|
||||
array[cursor] = string;
|
||||
}
|
||||
va_end( args );
|
||||
|
||||
*count = size / sizeof( *array );
|
||||
return array;
|
||||
}
|
||||
|
||||
bool mpw_push_buf(uint8_t **buffer, size_t *bufferSize, const void *pushBuffer, const size_t pushSize) {
|
||||
|
||||
if (!buffer || !bufferSize || !pushBuffer || !pushSize)
|
||||
@@ -275,14 +296,15 @@ static uint8_t const *mpw_aes(bool encrypt, const uint8_t *key, const size_t key
|
||||
return NULL;
|
||||
|
||||
// IV = zero
|
||||
uint8_t iv[AES_BLOCKLEN];
|
||||
mpw_zero( iv, sizeof iv );
|
||||
static uint8_t *iv = NULL;
|
||||
if (!iv)
|
||||
iv = calloc( AES_BLOCKLEN, sizeof( uint8_t ) );
|
||||
|
||||
// Add PKCS#7 padding
|
||||
uint32_t aesSize = ((uint32_t)*bufSize + AES_BLOCKLEN - 1) & -AES_BLOCKLEN; // round up to block size.
|
||||
if (encrypt && !(*bufSize % AES_BLOCKLEN)) // add pad block if plain text fits block size.
|
||||
encrypt += AES_BLOCKLEN;
|
||||
uint8_t aesBuf[aesSize];
|
||||
uint8_t *aesBuf = malloc( aesSize );
|
||||
memcpy( aesBuf, buf, *bufSize );
|
||||
memset( aesBuf + *bufSize, aesSize - *bufSize, aesSize - *bufSize );
|
||||
uint8_t *resultBuf = malloc( aesSize );
|
||||
@@ -291,8 +313,7 @@ static uint8_t const *mpw_aes(bool encrypt, const uint8_t *key, const size_t key
|
||||
AES_CBC_encrypt_buffer( resultBuf, aesBuf, aesSize, key, iv );
|
||||
else
|
||||
AES_CBC_decrypt_buffer( resultBuf, aesBuf, aesSize, key, iv );
|
||||
mpw_zero( aesBuf, aesSize );
|
||||
mpw_zero( iv, AES_BLOCKLEN );
|
||||
mpw_free( &aesBuf, aesSize );
|
||||
|
||||
// Truncate PKCS#7 padding
|
||||
if (encrypt)
|
||||
@@ -496,3 +517,12 @@ char *mpw_strndup(const char *src, size_t max) {
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
int mpw_strncasecmp(const char *s1, const char *s2, size_t max) {
|
||||
|
||||
int cmp = 0;
|
||||
for (; !cmp && max-- > 0 && s1 && s2; ++s1, ++s2)
|
||||
cmp = tolower( *(unsigned char *)s1 ) - tolower( *(unsigned char *)s2 );
|
||||
|
||||
return cmp;
|
||||
}
|
||||
|
||||
@@ -33,36 +33,36 @@ extern int mpw_verbosity;
|
||||
#endif
|
||||
|
||||
#ifndef mpw_log
|
||||
#define mpw_log(level, ...) ({ \
|
||||
#define mpw_log(level, format, ...) do { \
|
||||
if (mpw_verbosity >= level) { \
|
||||
mpw_log_do( level, ##__VA_ARGS__ ); \
|
||||
}; })
|
||||
mpw_log_do( level, format, ##__VA_ARGS__ ); \
|
||||
}; } while (0)
|
||||
#endif
|
||||
|
||||
#ifndef trc
|
||||
/** Logging internal state. */
|
||||
#define trc_level 3
|
||||
#define trc(...) mpw_log( trc_level, ##__VA_ARGS__ )
|
||||
#define trc(format, ...) mpw_log( trc_level, format, ##__VA_ARGS__ )
|
||||
|
||||
/** Logging state and events interesting when investigating issues. */
|
||||
#define dbg_level 2
|
||||
#define dbg(...) mpw_log( dbg_level, ##__VA_ARGS__ )
|
||||
#define dbg(format, ...) mpw_log( dbg_level, format, ##__VA_ARGS__ )
|
||||
|
||||
/** User messages. */
|
||||
#define inf_level 1
|
||||
#define inf(...) mpw_log( inf_level, ##__VA_ARGS__ )
|
||||
#define inf(format, ...) mpw_log( inf_level, format, ##__VA_ARGS__ )
|
||||
|
||||
/** Recoverable issues and user suggestions. */
|
||||
#define wrn_level 0
|
||||
#define wrn(...) mpw_log( wrn_level, ##__VA_ARGS__ )
|
||||
#define wrn(format, ...) mpw_log( wrn_level, format, ##__VA_ARGS__ )
|
||||
|
||||
/** Unrecoverable issues. */
|
||||
#define err_level -1
|
||||
#define err(...) mpw_log( err_level, ##__VA_ARGS__ )
|
||||
#define err(format, ...) mpw_log( err_level, format, ##__VA_ARGS__ )
|
||||
|
||||
/** Issues that lead to abortion. */
|
||||
#define ftl_level -2
|
||||
#define ftl(...) mpw_log( ftl_level, ##__VA_ARGS__ )
|
||||
#define ftl(format, ...) mpw_log( ftl_level, format, ##__VA_ARGS__ )
|
||||
#endif
|
||||
|
||||
#ifndef min
|
||||
@@ -98,14 +98,8 @@ void mpw_uint32(const uint32_t number, uint8_t buf[4]);
|
||||
void mpw_uint64(const uint64_t number, uint8_t buf[8]);
|
||||
|
||||
/** Allocate a new array of _type, assign its element count to _count if not NULL and populate it with the varargs. */
|
||||
#define mpw_alloc_array(_count, _type, ...) ({ \
|
||||
_type stackElements[] = { __VA_ARGS__ }; \
|
||||
if (_count) \
|
||||
*_count = sizeof( stackElements ) / sizeof( _type ); \
|
||||
_type *allocElements = malloc( sizeof( stackElements ) ); \
|
||||
memcpy( allocElements, stackElements, sizeof( stackElements ) ); \
|
||||
allocElements; \
|
||||
})
|
||||
const char **mpw_strings(
|
||||
size_t *count, const char *strings, ...);
|
||||
|
||||
/** Push a buffer onto a buffer. reallocs the given buffer and appends the given buffer. */
|
||||
bool mpw_push_buf(
|
||||
@@ -124,9 +118,9 @@ bool mpw_push_int(
|
||||
/** Reallocate the given buffer from the given size by adding the delta size.
|
||||
* On success, the buffer size pointer will be updated to the buffer's new size
|
||||
* and the buffer pointer may be updated to a new memory address.
|
||||
* On failure, the buffer and pointers will remain unaffected.
|
||||
* On failure, the pointers will remain unaffected.
|
||||
* @param buffer A pointer to the buffer to reallocate.
|
||||
* @param bufferSize A pointer to the buffer's actual size.
|
||||
* @param bufferSize A pointer to the buffer's current size.
|
||||
* @param deltaSize The amount to increase the buffer's size by.
|
||||
* @return true if successful, false if reallocation failed.
|
||||
*/
|
||||
@@ -150,6 +144,20 @@ bool __mpw_free_string(
|
||||
({ __typeof__(strings) _s = strings; const char *__s = *_s; (void)__s; __mpw_free_strings( (char **)_s, __VA_ARGS__ ); })
|
||||
bool __mpw_free_strings(
|
||||
char **strings, ...);
|
||||
#ifdef _MSC_VER
|
||||
#undef mpw_realloc
|
||||
#define mpw_realloc(buffer, bufferSize, deltaSize) \
|
||||
__mpw_realloc( (const void **)buffer, bufferSize, deltaSize )
|
||||
#undef mpw_free
|
||||
#define mpw_free(buffer, bufferSize) \
|
||||
__mpw_free( (void **)buffer, bufferSize )
|
||||
#undef mpw_free_string
|
||||
#define mpw_free_string(string) \
|
||||
__mpw_free_string( (char **)string )
|
||||
#undef mpw_free_strings
|
||||
#define mpw_free_strings(strings, ...) \
|
||||
__mpw_free_strings( (char **)strings, __VA_ARGS__ )
|
||||
#endif
|
||||
|
||||
//// Cryptographic functions.
|
||||
|
||||
@@ -207,5 +215,7 @@ const size_t mpw_utf8_strlen(const char *utf8String);
|
||||
char *mpw_strdup(const char *src);
|
||||
/** Drop-in for POSIX strndup(3). */
|
||||
char *mpw_strndup(const char *src, size_t max);
|
||||
/** Drop-in for POSIX strncasecmp(3). */
|
||||
int mpw_strncasecmp(const char *s1, const char *s2, size_t max);
|
||||
|
||||
#endif // _MPW_UTIL_H
|
||||
|
||||
@@ -20,11 +20,12 @@ dependencies {
|
||||
}
|
||||
|
||||
processResources {
|
||||
dependsOn task( type: Sync, 'copyResources', {
|
||||
dependsOn task( type: Sync, 'processResources-lib', {
|
||||
into new File( processResources.outputs.files.singleFile, "lib" )
|
||||
|
||||
dependsOn configurations.lib {
|
||||
files.each { libFile ->
|
||||
from zipTree( libFile )
|
||||
into new File( processResources.outputs.files.singleFile, "lib" )
|
||||
from( zipTree( libFile ) )
|
||||
}
|
||||
}
|
||||
} )
|
||||
@@ -33,7 +34,7 @@ processResources {
|
||||
compileJava {
|
||||
doLast {
|
||||
ant.javah( class: 'com.lyndir.masterpassword.impl.MPAlgorithmV0',
|
||||
outputFile: '../../c/core/src/mpw-jni.h',
|
||||
outputFile: new File( project( ':masterpassword-core' ).projectDir, 'src/mpw-jni.h' ),
|
||||
classpath: files( sourceSets.main.compileClasspath, sourceSets.main.output ).asPath )
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,8 +45,6 @@ public class MPAlgorithmV0 extends MPAlgorithm {
|
||||
Native.load( MPAlgorithmV0.class, "mpw" );
|
||||
}
|
||||
|
||||
public final Version version = MPAlgorithm.Version.V0;
|
||||
|
||||
protected final Logger logger = Logger.get( getClass() );
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -18,12 +18,16 @@
|
||||
|
||||
package com.lyndir.masterpassword.impl;
|
||||
|
||||
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import java.io.*;
|
||||
import java.util.Locale;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
||||
/**
|
||||
@@ -45,7 +49,8 @@ public final class Native {
|
||||
try {
|
||||
System.loadLibrary( name );
|
||||
return;
|
||||
} catch (@SuppressWarnings("ErrorNotRethrown") final UnsatisfiedLinkError ignored) {
|
||||
}
|
||||
catch (@SuppressWarnings("ErrorNotRethrown") final UnsatisfiedLinkError ignored) {
|
||||
}
|
||||
|
||||
// Try to find and open a stream to the packaged library resource.
|
||||
@@ -82,10 +87,26 @@ public final class Native {
|
||||
|
||||
@Nonnull
|
||||
private static String getLibraryResource(final String library) {
|
||||
String system = System.getProperty( "os.name" ).toLowerCase( Locale.ROOT );
|
||||
String architecture = System.getProperty( "os.arch" ).toLowerCase( Locale.ROOT );
|
||||
if ("Mac OS X".equalsIgnoreCase( system ))
|
||||
String system = ifNotNullElse( System.getProperty( "os.name" ), "linux" ).toLowerCase( Locale.ROOT );
|
||||
String architecture = ifNotNullElse( System.getProperty( "os.arch" ), "x86" ).toLowerCase( Locale.ROOT );
|
||||
|
||||
// Standardize system naming in accordance with masterpassword-core.
|
||||
if (system.contains( "windows" ))
|
||||
system = "windows";
|
||||
else if (system.contains( "mac os x" ) || system.contains( "darwin" ) || system.contains( "osx" ))
|
||||
system = "macos";
|
||||
else
|
||||
system = "linux";
|
||||
|
||||
// Standardize architecture naming in accordance with masterpassword-core.
|
||||
if (ImmutableList.of( "arm", "arm-v7", "armv7", "arm32" ).contains( architecture ))
|
||||
architecture = "arm";
|
||||
else if (architecture.startsWith( "arm" ))
|
||||
architecture = "arm64";
|
||||
else if (ImmutableList.of( "x86_64", "amd64", "x64", "x86-64" ).contains( architecture ))
|
||||
architecture = "x86_64";
|
||||
else
|
||||
architecture = "x86";
|
||||
|
||||
return Joiner.on( RESOURCE_SEPARATOR ).join( "", NATIVES_PATH, system, architecture, library );
|
||||
}
|
||||
|
||||
@@ -21,8 +21,7 @@ package com.lyndir.masterpassword.gui;
|
||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.io.CharSource;
|
||||
import com.google.common.io.Resources;
|
||||
import com.google.common.io.ByteSource;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import com.lyndir.lhunath.opal.system.util.TypeUtils;
|
||||
import com.lyndir.masterpassword.gui.view.PasswordFrame;
|
||||
@@ -30,8 +29,7 @@ import com.lyndir.masterpassword.gui.view.UnlockFrame;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.*;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Optional;
|
||||
import java.util.jar.*;
|
||||
@@ -52,6 +50,9 @@ public class GUI implements UnlockFrame.SignInCallback {
|
||||
private PasswordFrame<?, ?> passwordFrame;
|
||||
|
||||
public static void main(final String... args) {
|
||||
Thread.setDefaultUncaughtExceptionHandler(
|
||||
(t, e) -> logger.err( e, "Uncaught: %s", e.getLocalizedMessage() ) );
|
||||
|
||||
if (Config.get().checkForUpdates())
|
||||
checkUpdate();
|
||||
|
||||
@@ -62,18 +63,24 @@ public class GUI implements UnlockFrame.SignInCallback {
|
||||
catch (final UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException ignored) {
|
||||
}
|
||||
|
||||
create().open();
|
||||
}
|
||||
|
||||
private static GUI create() {
|
||||
try {
|
||||
// AppleGUI adds support for macOS features.
|
||||
Optional<Class<GUI>> appleGUI = TypeUtils.loadClass( "com.lyndir.masterpassword.gui.platform.mac.AppleGUI" );
|
||||
if (appleGUI.isPresent())
|
||||
appleGUI.get().getConstructor().newInstance().open();
|
||||
|
||||
else // No special platform handling.
|
||||
new GUI().open();
|
||||
return appleGUI.get().getConstructor().newInstance();
|
||||
}
|
||||
catch (@SuppressWarnings("ErrorNotRethrown") final LinkageError ignored) {
|
||||
}
|
||||
catch (final IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) {
|
||||
throw logger.bug( e );
|
||||
}
|
||||
|
||||
// Use platform-independent GUI.
|
||||
return new GUI();
|
||||
}
|
||||
|
||||
private static void checkUpdate() {
|
||||
@@ -85,10 +92,18 @@ public class GUI implements UnlockFrame.SignInCallback {
|
||||
if (!GUI.class.getCanonicalName().equals( attributes.getValue( Attributes.Name.MAIN_CLASS ) ))
|
||||
continue;
|
||||
|
||||
String manifestRevision = attributes.getValue( Attributes.Name.IMPLEMENTATION_VERSION );
|
||||
String upstreamRevisionURL = "https://masterpassword.app/masterpassword-gui.jar.rev";
|
||||
CharSource upstream = Resources.asCharSource( URI.create( upstreamRevisionURL ).toURL(), Charsets.UTF_8 );
|
||||
String upstreamRevision = upstream.readFirstLine();
|
||||
String manifestRevision = attributes.getValue( Attributes.Name.IMPLEMENTATION_VERSION );
|
||||
String upstreamRevision = new ByteSource() {
|
||||
@Override
|
||||
public InputStream openStream()
|
||||
throws IOException {
|
||||
URL url = URI.create( "https://masterpassword.app/masterpassword-gui.jar.rev" ).toURL();
|
||||
URLConnection conn = url.openConnection();
|
||||
conn.addRequestProperty( "User-Agent", "masterpassword-gui" );
|
||||
return conn.getInputStream();
|
||||
}
|
||||
}.asCharSource( Charsets.UTF_8 ).readFirstLine();
|
||||
|
||||
if ((manifestRevision != null) && (upstreamRevision != null) && !manifestRevision.equalsIgnoreCase(
|
||||
upstreamRevision )) {
|
||||
logger.inf( "Local Revision: <%s>", manifestRevision );
|
||||
|
||||
@@ -21,7 +21,6 @@ package com.lyndir.masterpassword.gui;
|
||||
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
|
||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.io.Resources;
|
||||
import com.google.common.util.concurrent.JdkFutureAdapters;
|
||||
@@ -50,17 +49,19 @@ import org.jetbrains.annotations.NonNls;
|
||||
@SuppressWarnings({ "HardcodedFileSeparator", "MethodReturnAlwaysConstant", "SpellCheckingInspection" })
|
||||
public abstract class Res {
|
||||
|
||||
private static final int AVATAR_COUNT = 19;
|
||||
private static final Map<Window, ScheduledExecutorService> executorByWindow = new WeakHashMap<>();
|
||||
private static final Logger logger = Logger.get( Res.class );
|
||||
private static final Colors colors = new Colors();
|
||||
private static final int AVATAR_COUNT = 19;
|
||||
private static final Map<Window, ScheduledExecutorService> jobExecutorByWindow = new WeakHashMap<>();
|
||||
private static final Executor immediateUiExecutor = new SwingExecutorService( true );
|
||||
private static final Executor laterUiExecutor = new SwingExecutorService( false );
|
||||
private static final Logger logger = Logger.get( Res.class );
|
||||
private static final Colors colors = new Colors();
|
||||
|
||||
public static Future<?> execute(final Window host, final Runnable job) {
|
||||
return schedule( host, job, 0, TimeUnit.MILLISECONDS );
|
||||
public static Future<?> job(final Window host, final Runnable job) {
|
||||
return job( host, job, 0, TimeUnit.MILLISECONDS );
|
||||
}
|
||||
|
||||
public static Future<?> schedule(final Window host, final Runnable job, final long delay, final TimeUnit timeUnit) {
|
||||
return getExecutor( host ).schedule( () -> {
|
||||
public static Future<?> job(final Window host, final Runnable job, final long delay, final TimeUnit timeUnit) {
|
||||
return jobExecutor( host ).schedule( () -> {
|
||||
try {
|
||||
job.run();
|
||||
}
|
||||
@@ -70,33 +71,29 @@ public abstract class Res {
|
||||
}, delay, timeUnit );
|
||||
}
|
||||
|
||||
public static <V> ListenableFuture<V> execute(final Window host, final Callable<V> job) {
|
||||
return schedule( host, job, 0, TimeUnit.MILLISECONDS );
|
||||
public static <V> ListenableFuture<V> job(final Window host, final Callable<V> job) {
|
||||
return job( host, job, 0, TimeUnit.MILLISECONDS );
|
||||
}
|
||||
|
||||
public static <V> ListenableFuture<V> schedule(final Window host, final Callable<V> job, final long delay, final TimeUnit timeUnit) {
|
||||
ScheduledExecutorService executor = getExecutor( host );
|
||||
return JdkFutureAdapters.listenInPoolThread( executor.schedule( () -> {
|
||||
try {
|
||||
return job.call();
|
||||
}
|
||||
catch (final Throwable t) {
|
||||
logger.err( t, "Unexpected: %s", t.getLocalizedMessage() );
|
||||
throw Throwables.propagate( t );
|
||||
}
|
||||
}, delay, timeUnit ), executor );
|
||||
public static <V> ListenableFuture<V> job(final Window host, final Callable<V> job, final long delay, final TimeUnit timeUnit) {
|
||||
ScheduledExecutorService executor = jobExecutor( host );
|
||||
return JdkFutureAdapters.listenInPoolThread( executor.schedule( job::call, delay, timeUnit ), executor );
|
||||
}
|
||||
|
||||
private static ScheduledExecutorService getExecutor(final Window host) {
|
||||
ScheduledExecutorService executor = executorByWindow.get( host );
|
||||
public static Executor uiExecutor(final boolean immediate) {
|
||||
return immediate? immediateUiExecutor: laterUiExecutor;
|
||||
}
|
||||
|
||||
public static ScheduledExecutorService jobExecutor(final Window host) {
|
||||
ScheduledExecutorService executor = jobExecutorByWindow.get( host );
|
||||
|
||||
if (executor == null) {
|
||||
executorByWindow.put( host, executor = Executors.newSingleThreadScheduledExecutor() );
|
||||
jobExecutorByWindow.put( host, executor = Executors.newSingleThreadScheduledExecutor() );
|
||||
|
||||
host.addWindowListener( new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosed(final WindowEvent e) {
|
||||
ExecutorService executor = executorByWindow.remove( host );
|
||||
ExecutorService executor = jobExecutorByWindow.remove( host );
|
||||
if (executor != null)
|
||||
executor.shutdownNow();
|
||||
}
|
||||
@@ -204,7 +201,7 @@ public abstract class Res {
|
||||
font = Font.createFont( Font.TRUETYPE_FONT, Resources.getResource( fontResourceName ).openStream() ) ) );
|
||||
}
|
||||
catch (final FontFormatException | IOException e) {
|
||||
throw Throwables.propagate( e );
|
||||
throw logger.bug( e );
|
||||
}
|
||||
|
||||
return font;
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.lyndir.masterpassword.gui;
|
||||
|
||||
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
|
||||
|
||||
import com.google.common.collect.*;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.*;
|
||||
import javax.swing.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
/**
|
||||
* @author lhunath, 2018-07-08
|
||||
*/
|
||||
public class SwingExecutorService extends AbstractExecutorService {
|
||||
|
||||
private final List<Runnable> pendingCommands = Lists.newLinkedList();
|
||||
private final BlockingQueue<Boolean> terminated = Queues.newLinkedBlockingDeque( 1 );
|
||||
private final boolean immediate;
|
||||
private boolean shutdown;
|
||||
|
||||
/**
|
||||
* @param immediate Allow immediate execution of the job in {@link #execute(Runnable)} if already on the right thread.
|
||||
* If {@code false}, jobs are always posted for later execution on the event thread.
|
||||
*/
|
||||
public SwingExecutorService(final boolean immediate) {
|
||||
this.immediate = immediate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
shutdown = true;
|
||||
|
||||
synchronized (pendingCommands) {
|
||||
if (pendingCommands.isEmpty())
|
||||
terminated.offer( true );
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<Runnable> shutdownNow() {
|
||||
shutdown();
|
||||
|
||||
synchronized (pendingCommands) {
|
||||
return ImmutableList.copyOf( pendingCommands );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShutdown() {
|
||||
return shutdown;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTerminated() {
|
||||
return ifNotNullElse( terminated.peek(), false );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean awaitTermination(final long timeout, @NotNull final TimeUnit unit)
|
||||
throws InterruptedException {
|
||||
return ifNotNullElse( terminated.poll( timeout, unit ), false );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(@NotNull final Runnable command) {
|
||||
if (shutdown)
|
||||
throw new RejectedExecutionException( "Executor is shut down." );
|
||||
|
||||
synchronized (pendingCommands) {
|
||||
pendingCommands.add( command );
|
||||
}
|
||||
|
||||
if (immediate && SwingUtilities.isEventDispatchThread())
|
||||
run( command );
|
||||
else
|
||||
SwingUtilities.invokeLater( () -> run( command ) );
|
||||
}
|
||||
|
||||
private void run(final Runnable command) {
|
||||
command.run();
|
||||
|
||||
synchronized (pendingCommands) {
|
||||
pendingCommands.remove( command );
|
||||
|
||||
if (shutdown && pendingCommands.isEmpty())
|
||||
terminated.offer( true );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import com.google.common.primitives.UnsignedInteger;
|
||||
import com.lyndir.masterpassword.MPAlgorithm;
|
||||
import com.lyndir.masterpassword.MPResultType;
|
||||
import com.lyndir.masterpassword.model.impl.MPBasicSite;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
||||
@@ -44,6 +45,7 @@ public class MPIncognitoSite extends MPBasicSite<MPIncognitoQuestion> {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public MPIncognitoUser getUser() {
|
||||
return user;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
package com.lyndir.masterpassword.gui.platform.mac;
|
||||
|
||||
import com.apple.eawt.*;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.lyndir.masterpassword.gui.GUI;
|
||||
|
||||
|
||||
@@ -27,25 +28,30 @@ import com.lyndir.masterpassword.gui.GUI;
|
||||
*/
|
||||
public class AppleGUI extends GUI {
|
||||
|
||||
static Application application;
|
||||
|
||||
static {
|
||||
application = Preconditions.checkNotNull( Application.getApplication(), "Not an Apple Java application." );
|
||||
}
|
||||
|
||||
public AppleGUI() {
|
||||
application.addAppEventListener( new ApplicationListener() );
|
||||
}
|
||||
|
||||
Application application = Application.getApplication();
|
||||
application.addAppEventListener( new AppForegroundListener() {
|
||||
private class ApplicationListener implements AppForegroundListener, AppReOpenedListener {
|
||||
|
||||
@Override
|
||||
public void appMovedToBackground(final AppEvent.AppForegroundEvent arg0) {
|
||||
}
|
||||
@Override
|
||||
public void appMovedToBackground(final AppEvent.AppForegroundEvent arg0) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appRaisedToForeground(final AppEvent.AppForegroundEvent arg0) {
|
||||
open();
|
||||
}
|
||||
} );
|
||||
application.addAppEventListener( new AppReOpenedListener() {
|
||||
@Override
|
||||
public void appReOpened(final AppEvent.AppReOpenedEvent arg0) {
|
||||
open();
|
||||
}
|
||||
} );
|
||||
@Override
|
||||
public void appRaisedToForeground(final AppEvent.AppForegroundEvent arg0) {
|
||||
open();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appReOpened(final AppEvent.AppReOpenedEvent arg0) {
|
||||
open();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.lyndir.masterpassword.gui.util;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
|
||||
|
||||
/**
|
||||
* @author lhunath, 2018-07-08
|
||||
*/
|
||||
public abstract class FailableCallback<T> implements FutureCallback<T> {
|
||||
|
||||
private final Logger logger;
|
||||
|
||||
protected FailableCallback(final Logger logger) {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
logger.err( t, "Future failed." );
|
||||
onSuccess( null );
|
||||
}
|
||||
}
|
||||
@@ -23,11 +23,12 @@ import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.primitives.UnsignedInteger;
|
||||
import com.google.common.util.concurrent.*;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import com.lyndir.masterpassword.*;
|
||||
import com.lyndir.masterpassword.gui.Res;
|
||||
import com.lyndir.masterpassword.gui.util.Components;
|
||||
import com.lyndir.masterpassword.gui.util.UnsignedIntegerModel;
|
||||
import com.lyndir.masterpassword.gui.util.*;
|
||||
import com.lyndir.masterpassword.model.MPSite;
|
||||
import com.lyndir.masterpassword.model.MPUser;
|
||||
import com.lyndir.masterpassword.model.impl.MPFileSite;
|
||||
@@ -35,7 +36,7 @@ import com.lyndir.masterpassword.model.impl.MPFileUser;
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.awt.event.*;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.Nonnull;
|
||||
@@ -50,6 +51,8 @@ import javax.swing.event.DocumentListener;
|
||||
*/
|
||||
public abstract class PasswordFrame<U extends MPUser<S>, S extends MPSite<?>> extends JFrame implements DocumentListener {
|
||||
|
||||
private static final Logger logger = Logger.get( PasswordFrame.class );
|
||||
|
||||
@SuppressWarnings("FieldCanBeLocal")
|
||||
private final Components.GradientPanel root;
|
||||
private final JTextField siteNameField;
|
||||
@@ -96,40 +99,33 @@ public abstract class PasswordFrame<U extends MPUser<S>, S extends MPSite<?>> ex
|
||||
siteNameField = Components.textField(), Components.stud(),
|
||||
siteActionButton = Components.button( "Add Site" ) );
|
||||
siteNameField.getDocument().addDocumentListener( this );
|
||||
siteNameField.addActionListener( new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
Futures.addCallback( updatePassword( true ), new FutureCallback<String>() {
|
||||
siteNameField.addActionListener(
|
||||
e -> Futures.addCallback( updatePassword( true ), new FailableCallback<String>( logger ) {
|
||||
@Override
|
||||
public void onSuccess(@Nullable final String sitePassword) {
|
||||
if (sitePassword == null)
|
||||
return;
|
||||
|
||||
if (currentSite instanceof MPFileSite)
|
||||
((MPFileSite) currentSite).use();
|
||||
|
||||
Transferable clipboardContents = new StringSelection( sitePassword );
|
||||
Toolkit.getDefaultToolkit().getSystemClipboard().setContents( clipboardContents, null );
|
||||
|
||||
SwingUtilities.invokeLater( () -> {
|
||||
passwordField.setText( null );
|
||||
siteNameField.setText( null );
|
||||
|
||||
dispatchEvent( new WindowEvent( PasswordFrame.this, WindowEvent.WINDOW_CLOSING ) );
|
||||
} );
|
||||
dispatchEvent( new WindowEvent( PasswordFrame.this, WindowEvent.WINDOW_CLOSING ) );
|
||||
}
|
||||
}, Res.uiExecutor( false ) ) );
|
||||
siteActionButton.addActionListener(
|
||||
e -> {
|
||||
if (currentSite == null)
|
||||
return;
|
||||
if (currentSite instanceof MPFileSite)
|
||||
this.user.deleteSite( currentSite );
|
||||
else
|
||||
this.user.addSite( currentSite );
|
||||
siteNameField.requestFocus();
|
||||
|
||||
@Override
|
||||
public void onFailure(@Nonnull final Throwable t) {
|
||||
}
|
||||
updatePassword( true );
|
||||
} );
|
||||
}
|
||||
} );
|
||||
siteActionButton.addActionListener( e -> {
|
||||
if (currentSite == null)
|
||||
return;
|
||||
if (currentSite instanceof MPFileSite)
|
||||
this.user.deleteSite( currentSite );
|
||||
else
|
||||
this.user.addSite( currentSite );
|
||||
siteNameField.requestFocus();
|
||||
|
||||
updatePassword( true );
|
||||
} );
|
||||
sitePanel.add( siteControls );
|
||||
sitePanel.add( Components.stud() );
|
||||
|
||||
@@ -229,34 +225,36 @@ public abstract class PasswordFrame<U extends MPUser<S>, S extends MPSite<?>> ex
|
||||
site.setCounter( siteCounter );
|
||||
}
|
||||
|
||||
ListenableFuture<String> passwordFuture = Res.execute( this, () -> site.getResult( MPKeyPurpose.Authentication, null, null ) );
|
||||
Futures.addCallback( passwordFuture, new FutureCallback<String>() {
|
||||
@Override
|
||||
public void onSuccess(@Nullable final String sitePassword) {
|
||||
SwingUtilities.invokeLater( () -> {
|
||||
updatingUI = true;
|
||||
currentSite = site;
|
||||
siteActionButton.setVisible( user instanceof MPFileUser );
|
||||
if (currentSite instanceof MPFileSite)
|
||||
siteActionButton.setText( "Delete Site" );
|
||||
else
|
||||
siteActionButton.setText( "Add Site" );
|
||||
resultTypeField.setSelectedItem( currentSite.getResultType() );
|
||||
siteVersionField.setSelectedItem( currentSite.getAlgorithm() );
|
||||
siteCounterField.setValue( currentSite.getCounter() );
|
||||
siteNameField.setText( currentSite.getName() );
|
||||
if (siteNameField.getText().startsWith( siteNameQuery ))
|
||||
siteNameField.select( siteNameQuery.length(), siteNameField.getText().length() );
|
||||
ListenableFuture<String> passwordFuture = Res.job( this, () ->
|
||||
site.getResult( MPKeyPurpose.Authentication, null, null ) );
|
||||
|
||||
SwingUtilities.invokeLater( () -> {
|
||||
updatingUI = true;
|
||||
currentSite = site;
|
||||
siteActionButton.setVisible( user instanceof MPFileUser );
|
||||
if (currentSite instanceof MPFileSite)
|
||||
siteActionButton.setText( "Delete Site" );
|
||||
else
|
||||
siteActionButton.setText( "Add Site" );
|
||||
resultTypeField.setSelectedItem( currentSite.getResultType() );
|
||||
siteVersionField.setSelectedItem( currentSite.getAlgorithm().version() );
|
||||
siteCounterField.setValue( currentSite.getCounter() );
|
||||
siteNameField.setText( currentSite.getName() );
|
||||
if (siteNameField.getText().startsWith( siteNameQuery ))
|
||||
siteNameField.select( siteNameQuery.length(), siteNameField.getText().length() );
|
||||
passwordField.setText( null );
|
||||
tipLabel.setText( "Getting password..." );
|
||||
|
||||
Futures.addCallback( passwordFuture, new FailableCallback<String>( logger ) {
|
||||
@Override
|
||||
public void onSuccess(@Nullable final String sitePassword) {
|
||||
if (sitePassword != null)
|
||||
tipLabel.setText( "Press [Enter] to copy the password. Then paste it into the password field." );
|
||||
|
||||
passwordField.setText( sitePassword );
|
||||
tipLabel.setText( "Press [Enter] to copy the password. Then paste it into the password field." );
|
||||
updatingUI = false;
|
||||
} );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@Nonnull final Throwable t) {
|
||||
}
|
||||
}
|
||||
}, Res.uiExecutor( true ) );
|
||||
} );
|
||||
|
||||
return passwordFuture;
|
||||
|
||||
@@ -153,7 +153,7 @@ public class UnlockFrame extends JFrame {
|
||||
boolean checkSignIn() {
|
||||
if (identiconFuture != null)
|
||||
identiconFuture.cancel( false );
|
||||
identiconFuture = Res.schedule( this, () -> SwingUtilities.invokeLater( () -> {
|
||||
identiconFuture = Res.job( this, () -> SwingUtilities.invokeLater( () -> {
|
||||
String fullName = (user == null)? "": user.getFullName();
|
||||
char[] masterPassword = authenticationPanel.getMasterPassword();
|
||||
|
||||
@@ -186,7 +186,7 @@ public class UnlockFrame extends JFrame {
|
||||
signInButton.setEnabled( false );
|
||||
signInButton.setText( "Signing In..." );
|
||||
|
||||
Res.execute( this, () -> {
|
||||
Res.job( this, () -> {
|
||||
try {
|
||||
user.authenticate( authenticationPanel.getMasterPassword() );
|
||||
|
||||
|
||||
@@ -18,7 +18,8 @@ dependencies {
|
||||
compileOnly group: 'com.google.auto.value', name: 'auto-value', version: '1.2'
|
||||
apt group: 'com.google.auto.value', name: 'auto-value', version: '1.2'
|
||||
|
||||
testCompile group: 'org.testng', name: 'testng', version: '6.8.5'
|
||||
testCompile group: 'ch.qos.logback', name: 'logback-classic', version: '1.1.2'
|
||||
testImplementation group: 'org.testng', name: 'testng', version: '6.8.5'
|
||||
testImplementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.1.2'
|
||||
}
|
||||
|
||||
test.useTestNG()
|
||||
|
||||
@@ -21,6 +21,7 @@ package com.lyndir.masterpassword.model;
|
||||
import com.google.common.primitives.UnsignedInteger;
|
||||
import com.lyndir.masterpassword.*;
|
||||
import java.util.Collection;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
||||
@@ -31,41 +32,50 @@ public interface MPSite<Q extends MPQuestion> extends Comparable<MPSite<?>> {
|
||||
|
||||
// - Meta
|
||||
|
||||
@Nonnull
|
||||
String getName();
|
||||
|
||||
void setName(String name);
|
||||
|
||||
// - Algorithm
|
||||
|
||||
@Nonnull
|
||||
MPAlgorithm getAlgorithm();
|
||||
|
||||
void setAlgorithm(MPAlgorithm algorithm);
|
||||
|
||||
@Nonnull
|
||||
UnsignedInteger getCounter();
|
||||
|
||||
void setCounter(UnsignedInteger counter);
|
||||
|
||||
@Nonnull
|
||||
MPResultType getResultType();
|
||||
|
||||
void setResultType(MPResultType resultType);
|
||||
|
||||
@Nonnull
|
||||
MPResultType getLoginType();
|
||||
|
||||
void setLoginType(@Nullable MPResultType loginType);
|
||||
|
||||
@Nonnull
|
||||
String getResult(MPKeyPurpose keyPurpose, @Nullable String keyContext, @Nullable String state)
|
||||
throws MPKeyUnavailableException, MPAlgorithmException;
|
||||
|
||||
@Nonnull
|
||||
String getLogin(@Nullable String state)
|
||||
throws MPKeyUnavailableException, MPAlgorithmException;
|
||||
|
||||
// - Relations
|
||||
|
||||
MPUser<? extends MPSite<?>> getUser();
|
||||
@Nonnull
|
||||
MPUser<?> getUser();
|
||||
|
||||
void addQuestion(Q question);
|
||||
|
||||
void deleteQuestion(Q question);
|
||||
|
||||
@Nonnull
|
||||
Collection<Q> getQuestions();
|
||||
}
|
||||
|
||||
@@ -18,8 +18,7 @@
|
||||
|
||||
package com.lyndir.masterpassword.model;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.*;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -37,7 +36,7 @@ public abstract class MPUserManager<U extends MPUser<?>> {
|
||||
}
|
||||
|
||||
public Collection<U> getUsers() {
|
||||
return ImmutableList.copyOf( usersByName.values() );
|
||||
return ImmutableSortedSet.copyOf( usersByName.values() );
|
||||
}
|
||||
|
||||
public U getUserNamed(final String fullName) {
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.lyndir.masterpassword.model.impl;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
|
||||
/**
|
||||
* @author lhunath, 2018-07-08
|
||||
*/
|
||||
public class Changeable {
|
||||
|
||||
private static final ExecutorService changeExecutor = Executors.newSingleThreadExecutor();
|
||||
|
||||
private boolean changed;
|
||||
private boolean batchingChanges;
|
||||
|
||||
void setChanged() {
|
||||
synchronized (changeExecutor) {
|
||||
if (changed)
|
||||
return;
|
||||
changed = true;
|
||||
|
||||
if (batchingChanges)
|
||||
return;
|
||||
|
||||
changeExecutor.submit( () -> {
|
||||
synchronized (changeExecutor) {
|
||||
if (batchingChanges)
|
||||
return;
|
||||
changed = false;
|
||||
}
|
||||
|
||||
onChanged();
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
protected void onChanged() {
|
||||
}
|
||||
|
||||
public void beginChanges() {
|
||||
synchronized (changeExecutor) {
|
||||
batchingChanges = true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean endChanges() {
|
||||
synchronized (changeExecutor) {
|
||||
batchingChanges = false;
|
||||
|
||||
if (changed) {
|
||||
this.changed = false;
|
||||
setChanged();
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
/**
|
||||
* @author lhunath, 2018-05-14
|
||||
*/
|
||||
public abstract class MPBasicQuestion implements MPQuestion {
|
||||
public abstract class MPBasicQuestion extends Changeable implements MPQuestion {
|
||||
|
||||
private final String keyword;
|
||||
private MPResultType type;
|
||||
@@ -56,6 +56,8 @@ public abstract class MPBasicQuestion implements MPQuestion {
|
||||
@Override
|
||||
public void setType(final MPResultType type) {
|
||||
this.type = type;
|
||||
|
||||
setChanged();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@@ -70,6 +72,13 @@ public abstract class MPBasicQuestion implements MPQuestion {
|
||||
@Override
|
||||
public abstract MPBasicSite<?> getSite();
|
||||
|
||||
@Override
|
||||
protected void onChanged() {
|
||||
super.onChanged();
|
||||
|
||||
getSite().setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode( getKeyword() );
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.lyndir.masterpassword.*;
|
||||
import com.lyndir.masterpassword.model.MPQuestion;
|
||||
import com.lyndir.masterpassword.model.MPSite;
|
||||
import java.util.*;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -33,7 +34,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
/**
|
||||
* @author lhunath, 14-12-16
|
||||
*/
|
||||
public abstract class MPBasicSite<Q extends MPQuestion> implements MPSite<Q> {
|
||||
public abstract class MPBasicSite<Q extends MPQuestion> extends Changeable implements MPSite<Q> {
|
||||
|
||||
private String name;
|
||||
private MPAlgorithm algorithm;
|
||||
@@ -56,6 +57,7 @@ public abstract class MPBasicSite<Q extends MPQuestion> implements MPSite<Q> {
|
||||
this.loginType = (loginType == null)? algorithm.mpw_default_login_type(): loginType;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
@@ -64,8 +66,11 @@ public abstract class MPBasicSite<Q extends MPQuestion> implements MPSite<Q> {
|
||||
@Override
|
||||
public void setName(final String name) {
|
||||
this.name = name;
|
||||
|
||||
setChanged();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public MPAlgorithm getAlgorithm() {
|
||||
return algorithm;
|
||||
@@ -74,8 +79,11 @@ public abstract class MPBasicSite<Q extends MPQuestion> implements MPSite<Q> {
|
||||
@Override
|
||||
public void setAlgorithm(final MPAlgorithm algorithm) {
|
||||
this.algorithm = algorithm;
|
||||
|
||||
setChanged();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public UnsignedInteger getCounter() {
|
||||
return counter;
|
||||
@@ -84,8 +92,11 @@ public abstract class MPBasicSite<Q extends MPQuestion> implements MPSite<Q> {
|
||||
@Override
|
||||
public void setCounter(final UnsignedInteger counter) {
|
||||
this.counter = counter;
|
||||
|
||||
setChanged();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public MPResultType getResultType() {
|
||||
return resultType;
|
||||
@@ -94,8 +105,11 @@ public abstract class MPBasicSite<Q extends MPQuestion> implements MPSite<Q> {
|
||||
@Override
|
||||
public void setResultType(final MPResultType resultType) {
|
||||
this.resultType = resultType;
|
||||
|
||||
setChanged();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public MPResultType getLoginType() {
|
||||
return loginType;
|
||||
@@ -104,8 +118,11 @@ public abstract class MPBasicSite<Q extends MPQuestion> implements MPSite<Q> {
|
||||
@Override
|
||||
public void setLoginType(@Nullable final MPResultType loginType) {
|
||||
this.loginType = ifNotNullElse( loginType, getAlgorithm().mpw_default_login_type() );
|
||||
|
||||
setChanged();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getResult(final MPKeyPurpose keyPurpose, @Nullable final String keyContext, @Nullable final String state)
|
||||
throws MPKeyUnavailableException, MPAlgorithmException {
|
||||
@@ -131,6 +148,7 @@ public abstract class MPBasicSite<Q extends MPQuestion> implements MPSite<Q> {
|
||||
keyPurpose, keyContext, type, state );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getLogin(@Nullable final String state)
|
||||
throws MPKeyUnavailableException, MPAlgorithmException {
|
||||
@@ -141,18 +159,34 @@ public abstract class MPBasicSite<Q extends MPQuestion> implements MPSite<Q> {
|
||||
@Override
|
||||
public void addQuestion(final Q question) {
|
||||
questions.add( question );
|
||||
|
||||
setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteQuestion(final Q question) {
|
||||
questions.remove( question );
|
||||
|
||||
setChanged();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Collection<Q> getQuestions() {
|
||||
return Collections.unmodifiableCollection( questions );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public abstract MPBasicUser<?> getUser();
|
||||
|
||||
@Override
|
||||
protected void onChanged() {
|
||||
super.onChanged();
|
||||
|
||||
getUser().setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode( getName() );
|
||||
|
||||
@@ -20,7 +20,7 @@ package com.lyndir.masterpassword.model.impl;
|
||||
|
||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSortedSet;
|
||||
import com.lyndir.lhunath.opal.system.CodeUtils;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import com.lyndir.masterpassword.*;
|
||||
@@ -34,7 +34,7 @@ import javax.annotation.Nullable;
|
||||
/**
|
||||
* @author lhunath, 2014-06-08
|
||||
*/
|
||||
public abstract class MPBasicUser<S extends MPBasicSite<?>> implements MPUser<S> {
|
||||
public abstract class MPBasicUser<S extends MPBasicSite<?>> extends Changeable implements MPUser<S> {
|
||||
|
||||
protected final Logger logger = Logger.get( getClass() );
|
||||
|
||||
@@ -64,6 +64,8 @@ public abstract class MPBasicUser<S extends MPBasicSite<?>> implements MPUser<S>
|
||||
@Override
|
||||
public void setAvatar(final int avatar) {
|
||||
this.avatar = avatar;
|
||||
|
||||
setChanged();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@@ -81,6 +83,8 @@ public abstract class MPBasicUser<S extends MPBasicSite<?>> implements MPUser<S>
|
||||
@Override
|
||||
public void setAlgorithm(final MPAlgorithm algorithm) {
|
||||
this.algorithm = algorithm;
|
||||
|
||||
setChanged();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -136,7 +140,7 @@ public abstract class MPBasicUser<S extends MPBasicSite<?>> implements MPUser<S>
|
||||
public MPMasterKey getMasterKey()
|
||||
throws MPKeyUnavailableException {
|
||||
if (masterKey == null)
|
||||
throw new MPKeyUnavailableException( "Master key was not yet set." );
|
||||
throw new MPKeyUnavailableException( "Master key was not yet set for: " + this );
|
||||
|
||||
return masterKey;
|
||||
}
|
||||
@@ -144,11 +148,15 @@ public abstract class MPBasicUser<S extends MPBasicSite<?>> implements MPUser<S>
|
||||
@Override
|
||||
public void addSite(final S site) {
|
||||
sites.add( site );
|
||||
|
||||
setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteSite(final S site) {
|
||||
sites.remove( site );
|
||||
|
||||
setChanged();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@@ -160,7 +168,7 @@ public abstract class MPBasicUser<S extends MPBasicSite<?>> implements MPUser<S>
|
||||
@Nonnull
|
||||
@Override
|
||||
public Collection<S> findSites(final String query) {
|
||||
ImmutableList.Builder<S> results = ImmutableList.builder();
|
||||
ImmutableSortedSet.Builder<S> results = ImmutableSortedSet.naturalOrder();
|
||||
for (final S site : getSites())
|
||||
if (site.getName().startsWith( query ))
|
||||
results.add( site );
|
||||
|
||||
@@ -33,19 +33,24 @@ public class MPFileQuestion extends MPBasicQuestion {
|
||||
private final MPFileSite site;
|
||||
|
||||
@Nullable
|
||||
private String state;
|
||||
private String answerState;
|
||||
|
||||
public MPFileQuestion(final MPFileSite site, final String keyword,
|
||||
@Nullable final MPResultType type, @Nullable final String state) {
|
||||
@Nullable final MPResultType type, @Nullable final String answerState) {
|
||||
super( keyword, ifNotNullElse( type, site.getAlgorithm().mpw_default_answer_type() ) );
|
||||
|
||||
this.site = site;
|
||||
this.state = state;
|
||||
this.answerState = answerState;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getAnswerState() {
|
||||
return answerState;
|
||||
}
|
||||
|
||||
public String getAnswer()
|
||||
throws MPKeyUnavailableException, MPAlgorithmException {
|
||||
return getAnswer( state );
|
||||
return getAnswer( answerState );
|
||||
}
|
||||
|
||||
public void setAnswer(final MPResultType type, @Nullable final String answer)
|
||||
@@ -53,10 +58,12 @@ public class MPFileQuestion extends MPBasicQuestion {
|
||||
setType( type );
|
||||
|
||||
if (answer == null)
|
||||
this.state = null;
|
||||
this.answerState = null;
|
||||
else
|
||||
this.state = getSite().getState(
|
||||
this.answerState = getSite().getState(
|
||||
MPKeyPurpose.Recovery, getKeyword(), null, getType(), answer );
|
||||
|
||||
setChanged();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
||||
@@ -20,6 +20,7 @@ package com.lyndir.masterpassword.model.impl;
|
||||
|
||||
import com.google.common.primitives.UnsignedInteger;
|
||||
import com.lyndir.masterpassword.*;
|
||||
import com.lyndir.masterpassword.model.MPSite;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.Instant;
|
||||
@@ -29,6 +30,7 @@ import org.joda.time.ReadableInstant;
|
||||
/**
|
||||
* @author lhunath, 14-12-05
|
||||
*/
|
||||
@SuppressWarnings("ComparableImplementedButEqualsNotOverridden")
|
||||
public class MPFileSite extends MPBasicSite<MPFileQuestion> {
|
||||
|
||||
private final MPFileUser user;
|
||||
@@ -77,6 +79,8 @@ public class MPFileSite extends MPBasicSite<MPFileQuestion> {
|
||||
|
||||
public void setUrl(@Nullable final String url) {
|
||||
this.url = url;
|
||||
|
||||
setChanged();
|
||||
}
|
||||
|
||||
public int getUses() {
|
||||
@@ -125,6 +129,8 @@ public class MPFileSite extends MPBasicSite<MPFileQuestion> {
|
||||
else
|
||||
this.resultState = getState(
|
||||
MPKeyPurpose.Authentication, null, getCounter(), getResultType(), password );
|
||||
|
||||
setChanged();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -141,10 +147,22 @@ public class MPFileSite extends MPBasicSite<MPFileQuestion> {
|
||||
else
|
||||
this.loginState = getState(
|
||||
MPKeyPurpose.Identification, null, null, getLoginType(), loginName );
|
||||
|
||||
setChanged();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public MPFileUser getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(final MPSite<?> o) {
|
||||
int comparison = (o instanceof MPFileSite)? -getLastUsed().compareTo( ((MPFileSite) o).getLastUsed() ): 0;
|
||||
if (comparison != 0)
|
||||
return comparison;
|
||||
|
||||
return super.compareTo( o );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
package com.lyndir.masterpassword.model.impl;
|
||||
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import com.lyndir.masterpassword.*;
|
||||
import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException;
|
||||
import com.lyndir.masterpassword.model.MPUser;
|
||||
@@ -34,9 +33,6 @@ import org.joda.time.ReadableInstant;
|
||||
@SuppressWarnings("ComparableImplementedButEqualsNotOverridden")
|
||||
public class MPFileUser extends MPBasicUser<MPFileSite> {
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
private static final Logger logger = Logger.get( MPFileUser.class );
|
||||
|
||||
@Nullable
|
||||
private byte[] keyID;
|
||||
private MPMarshalFormat format;
|
||||
@@ -101,6 +97,8 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
|
||||
|
||||
public void setFormat(final MPMarshalFormat format) {
|
||||
this.format = format;
|
||||
|
||||
setChanged();
|
||||
}
|
||||
|
||||
public MPMarshaller.ContentMode getContentMode() {
|
||||
@@ -109,6 +107,8 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
|
||||
|
||||
public void setContentMode(final MPMarshaller.ContentMode contentMode) {
|
||||
this.contentMode = contentMode;
|
||||
|
||||
setChanged();
|
||||
}
|
||||
|
||||
public MPResultType getDefaultType() {
|
||||
@@ -117,6 +117,8 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
|
||||
|
||||
public void setDefaultType(final MPResultType defaultType) {
|
||||
this.defaultType = defaultType;
|
||||
|
||||
setChanged();
|
||||
}
|
||||
|
||||
public ReadableInstant getLastUsed() {
|
||||
@@ -125,6 +127,8 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
|
||||
|
||||
public void use() {
|
||||
lastUsed = new Instant();
|
||||
|
||||
setChanged();
|
||||
}
|
||||
|
||||
public void setJSON(final MPJSONFile json) {
|
||||
@@ -141,8 +145,23 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
|
||||
throws MPIncorrectMasterPasswordException, MPKeyUnavailableException, MPAlgorithmException {
|
||||
super.authenticate( masterKey );
|
||||
|
||||
if (keyID == null)
|
||||
if (keyID == null) {
|
||||
keyID = masterKey.getKeyID( getAlgorithm() );
|
||||
|
||||
setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onChanged() {
|
||||
super.onChanged();
|
||||
|
||||
try {
|
||||
save();
|
||||
}
|
||||
catch (final MPKeyUnavailableException | MPAlgorithmException e) {
|
||||
logger.wrn( e, "Couldn't save change." );
|
||||
}
|
||||
}
|
||||
|
||||
void save()
|
||||
@@ -152,7 +171,7 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
|
||||
|
||||
@Override
|
||||
public int compareTo(final MPUser<?> o) {
|
||||
int comparison = (o instanceof MPFileUser)? getLastUsed().compareTo( ((MPFileUser) o).getLastUsed() ): 0;
|
||||
int comparison = (o instanceof MPFileUser)? -getLastUsed().compareTo( ((MPFileUser) o).getLastUsed() ): 0;
|
||||
if (comparison != 0)
|
||||
return comparison;
|
||||
|
||||
|
||||
@@ -18,14 +18,14 @@
|
||||
|
||||
package com.lyndir.masterpassword.model.impl;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAnyGetter;
|
||||
import com.fasterxml.jackson.annotation.JsonAnySetter;
|
||||
import com.fasterxml.jackson.annotation.*;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
* @author lhunath, 2018-05-14
|
||||
*/
|
||||
@JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = MPJSONAnyObject.MPJSONEmptyValue.class)
|
||||
class MPJSONAnyObject {
|
||||
|
||||
@JsonAnySetter
|
||||
@@ -35,4 +35,21 @@ class MPJSONAnyObject {
|
||||
public Map<String, Object> getAny() {
|
||||
return Collections.unmodifiableMap( any );
|
||||
}
|
||||
|
||||
@SuppressWarnings("EqualsAndHashcode")
|
||||
public static class MPJSONEmptyValue {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "ChainOfInstanceofChecks", "Contract" })
|
||||
public boolean equals(final Object obj) {
|
||||
if (obj instanceof Collection<?>)
|
||||
return ((Collection<?>) obj).isEmpty();
|
||||
if (obj instanceof Map<?, ?>)
|
||||
return ((Map<?, ?>) obj).isEmpty();
|
||||
if (obj instanceof MPJSONFile.Site.Ext)
|
||||
return ((MPJSONAnyObject) obj).any.isEmpty();
|
||||
|
||||
return obj == null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,10 @@ package com.lyndir.masterpassword.model.impl;
|
||||
|
||||
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
|
||||
|
||||
import com.fasterxml.jackson.annotation.*;
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
|
||||
import com.fasterxml.jackson.core.util.Separators;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.common.primitives.UnsignedInteger;
|
||||
import com.lyndir.lhunath.opal.system.CodeUtils;
|
||||
@@ -37,18 +40,28 @@ import org.joda.time.Instant;
|
||||
/**
|
||||
* @author lhunath, 2018-04-27
|
||||
*/
|
||||
@SuppressFBWarnings( "URF_UNREAD_FIELD" )
|
||||
@SuppressFBWarnings("URF_UNREAD_FIELD")
|
||||
public class MPJSONFile extends MPJSONAnyObject {
|
||||
|
||||
protected static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
static {
|
||||
objectMapper.setSerializationInclusion( JsonInclude.Include.NON_EMPTY );
|
||||
objectMapper.setDefaultPrettyPrinter( new DefaultPrettyPrinter() {
|
||||
private static final long serialVersionUID = 1;
|
||||
|
||||
@Override
|
||||
public DefaultPrettyPrinter withSeparators(final Separators separators) {
|
||||
super.withSeparators( separators );
|
||||
_objectFieldValueSeparatorWithSpaces = separators.getObjectFieldValueSeparator() + " ";
|
||||
return this;
|
||||
}
|
||||
} );
|
||||
objectMapper.setVisibility( PropertyAccessor.FIELD, JsonAutoDetect.Visibility.NON_PRIVATE );
|
||||
}
|
||||
|
||||
public MPJSONFile write(final MPFileUser modelUser)
|
||||
throws MPKeyUnavailableException, MPAlgorithmException {
|
||||
|
||||
// Section: "export"
|
||||
if (export == null)
|
||||
export = new Export();
|
||||
@@ -98,38 +111,27 @@ public class MPJSONFile extends MPJSONAnyObject {
|
||||
site.uses = modelSite.getUses();
|
||||
site.last_used = MPConstants.dateTimeFormatter.print( modelSite.getLastUsed() );
|
||||
|
||||
if (site.questions == null)
|
||||
site.questions = new LinkedHashMap<>();
|
||||
for (final MPFileQuestion question : modelSite.getQuestions())
|
||||
site.questions.put( question.getKeyword(), new Site.Question() {
|
||||
{
|
||||
type = question.getType();
|
||||
|
||||
if (!export.redacted) {
|
||||
// Clear Text
|
||||
answer = question.getAnswer();
|
||||
} else {
|
||||
// Redacted
|
||||
if (question.getType().supportsTypeFeature( MPSiteFeature.ExportContent ))
|
||||
answer = question.getAnswerState();
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
if (site._ext_mpw == null)
|
||||
site._ext_mpw = new Site.Ext();
|
||||
site._ext_mpw.url = modelSite.getUrl();
|
||||
|
||||
if (site.questions == null)
|
||||
site.questions = new LinkedHashMap<>();
|
||||
// for (size_t q = 0; q < site.questions_count; ++q) {
|
||||
// MPMarshalledQuestion *question = &site.questions[q];
|
||||
// if (!question.keyword)
|
||||
// continue;
|
||||
//
|
||||
// json_object *json_site_question = json_object_new_object();
|
||||
// json_object_object_add( json_site_questions, question.keyword, json_site_question );
|
||||
// json_object_object_add( json_site_question, "type = question.type;
|
||||
//
|
||||
// if (!user.redacted) {
|
||||
// // Clear Text
|
||||
// const char *answerContent = mpw_siteResult( masterKey, site.name, MPCounterValueInitial,
|
||||
// MPKeyPurposeRecovery, question.keyword, question.type, question.content, site.algorithm );
|
||||
// json_object_object_add( json_site_question, "answer = answerContent;
|
||||
// }
|
||||
// else {
|
||||
// // Redacted
|
||||
// if (site.type & MPSiteFeatureExportContent && question.content && strlen( question.content ))
|
||||
// json_object_object_add( json_site_question, "answer = question.content;
|
||||
// }
|
||||
// }
|
||||
|
||||
// json_object *json_site_mpw = json_object_new_object();
|
||||
// fileSite._ext_mpw = json_site_mpw;
|
||||
// if (site.url)
|
||||
// json_object_object_add( json_site_mpw, "url", site.url );
|
||||
}
|
||||
|
||||
return this;
|
||||
@@ -143,6 +145,7 @@ public class MPJSONFile extends MPJSONAnyObject {
|
||||
(user.default_type != null)? user.default_type: algorithm.mpw_default_result_type(),
|
||||
(user.last_used != null)? MPConstants.dateTimeFormatter.parseDateTime( user.last_used ): new Instant(),
|
||||
MPMarshalFormat.JSON, export.redacted? MPMarshaller.ContentMode.PROTECTED: MPMarshaller.ContentMode.VISIBLE );
|
||||
model.beginChanges();
|
||||
model.setJSON( this );
|
||||
if (masterPassword != null)
|
||||
model.authenticate( masterPassword );
|
||||
@@ -167,6 +170,7 @@ public class MPJSONFile extends MPJSONAnyObject {
|
||||
|
||||
model.addSite( site );
|
||||
}
|
||||
model.endChanges();
|
||||
|
||||
return model;
|
||||
}
|
||||
@@ -193,26 +197,26 @@ public class MPJSONFile extends MPJSONAnyObject {
|
||||
String full_name;
|
||||
String last_used;
|
||||
@Nullable
|
||||
MPAlgorithm.Version algorithm;
|
||||
@Nullable
|
||||
String key_id;
|
||||
@Nullable
|
||||
MPAlgorithm.Version algorithm;
|
||||
@Nullable
|
||||
MPResultType default_type;
|
||||
}
|
||||
|
||||
|
||||
public static class Site extends MPJSONAnyObject {
|
||||
|
||||
@Nullable
|
||||
MPResultType type;
|
||||
long counter;
|
||||
MPAlgorithm.Version algorithm;
|
||||
@Nullable
|
||||
MPResultType type;
|
||||
@Nullable
|
||||
String password;
|
||||
@Nullable
|
||||
MPResultType login_type;
|
||||
@Nullable
|
||||
String login_name;
|
||||
@Nullable
|
||||
MPResultType login_type;
|
||||
|
||||
int uses;
|
||||
@Nullable
|
||||
|
||||
@@ -37,7 +37,7 @@ public class MPJSONMarshaller implements MPMarshaller {
|
||||
throws MPKeyUnavailableException, MPMarshalException, MPAlgorithmException {
|
||||
|
||||
try {
|
||||
return objectMapper.writeValueAsString( user.getJSON().write( user ) );
|
||||
return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString( user.getJSON().write( user ) );
|
||||
}
|
||||
catch (final JsonProcessingException e) {
|
||||
throw new MPMarshalException( "Couldn't compose JSON for: " + user, e );
|
||||
|
||||
@@ -7,13 +7,11 @@ description = 'Master Password Test Suite'
|
||||
dependencies {
|
||||
implementation group: 'com.lyndir.lhunath.opal', name: 'opal-system', version: '1.7-p2'
|
||||
|
||||
compile project( ':masterpassword-algorithm' )
|
||||
compile project( ':masterpassword-model' )
|
||||
implementation project( ':masterpassword-algorithm' )
|
||||
implementation project( ':masterpassword-model' )
|
||||
|
||||
testCompile group: 'org.testng', name: 'testng', version: '6.8.5'
|
||||
testCompile group: 'ch.qos.logback', name: 'logback-classic', version: '1.1.2'
|
||||
testImplementation group: 'org.testng', name: 'testng', version: '6.8.5'
|
||||
testImplementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.1.2'
|
||||
}
|
||||
//tasks.withType(Test) {
|
||||
// systemProperty "java.library.path", "/Users/lhunath/Documents/workspace/lyndir/MasterPassword/core/java/algorithm/build/libs/mpw/shared"
|
||||
//}
|
||||
|
||||
test.useTestNG()
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<body>
|
||||
<header>
|
||||
<p><strong>BETA</strong> - This site may not yet work on your browser.<br>
|
||||
Passwords are generated locally, your master password is not sent to any server. See <a href="https://github.com/Lyndir/MasterPassword/tree/master/platform-independent/web-js">the source</a>.</p>
|
||||
Passwords are generated locally, your master password is not sent to any server. See <a href="https://gitlab.com/MasterPassword/MasterPassword/tree/master/platform-independent/web">the source</a>.</p>
|
||||
<span id="error"></span>
|
||||
</header>
|
||||
<section id="identity" class="active"><form action="#">
|
||||
|
||||
Submodule public/site updated: 6cde8d9db8...7421e1b0a5
Reference in New Issue
Block a user