2
0

Compare commits

..

30 Commits

Author SHA1 Message Date
Maarten Billemont
954c4f8d63 Java improvements.
UI threading improvements.
Save user/site changes to file.
Ordering of user / site fixes.
Add questions to JSON output.
Bring JSON output format in line with C.
2018-07-09 01:13:25 -04:00
Maarten Billemont
529f1feace Omit empty _mpw_ext & unneeded escaping. 2018-07-09 01:12:28 -04:00
Maarten Billemont
5cdff6f155 Some fixes to search paths. 2018-07-08 20:50:32 -04:00
Maarten Billemont
81358c16f9 Omit questions if none present and fix parsing sites with no questions. 2018-07-08 20:49:42 -04:00
Maarten Billemont
4a555748cd UTC times were parsed in as local time by mktime. 2018-07-08 20:49:31 -04:00
Maarten Billemont
698566a914 Update stale GitHub references with new GitLab home. 2018-07-05 17:14:50 -04:00
Maarten Billemont
64a69856ac Fix libsodium include paths. 2018-07-04 14:14:49 -04:00
Maarten Billemont
f76de9520c Merge branch 'master' of gitlab.com:MasterPassword/MasterPassword 2018-07-04 12:17:04 -04:00
Maarten Billemont
4b3662bbe9 Support for x86 Linux. 2018-07-04 12:16:37 -04:00
Maarten Billemont
b25130f4d2 Tweak include paths & add clean support to android libs. 2018-07-03 11:21:48 -04:00
Maarten Billemont
d6617563fc Clean libs when cleaning build. 2018-07-01 21:20:56 -04:00
Maarten Billemont
8d24ec3250 Merge branch 'patch-1' into 'master'
Update README.md Fix broken link

See merge request MasterPassword/MasterPassword!249
2018-07-01 20:31:14 +00:00
Jakob Kukla
bbcc250a5c Update README.md
Fix broken link to native CLI instructions
2018-07-01 18:04:19 +00:00
Maarten Billemont
4abb50ad9b Fix lipo regression in build script. 2018-06-30 23:54:35 -04:00
Maarten Billemont
30dac64d5d Support for building on Linux. 2018-06-30 23:35:29 -04:00
Maarten Billemont
e4e2aaad95 Ensure GitLab CI does clean builds. 2018-06-30 20:53:29 -04:00
Maarten Billemont
835acf45eb Improve check for platform-specific GUI support & url access for CF needs user-agent. 2018-06-30 15:24:58 -04:00
Maarten Billemont
c3f6796833 Pre-built library files for different platforms. 2018-06-30 12:35:40 -04:00
Maarten Billemont
86f4e8ec06 Fix gradle build. 2018-06-30 11:57:36 -04:00
Maarten Billemont
0dddcef28e libsodium and masterpassword-core must be linked statically + windows build configuration fixes. 2018-06-27 02:54:31 -04:00
Maarten Billemont
cc583c789d Merge branch 'master' of gitlab.com:MasterPassword/MasterPassword 2018-06-25 13:22:36 -04:00
Maarten Billemont
42d78da74e Fix some bugs in the new mpw_strings & mpw_strncasecmp. 2018-06-25 13:19:26 -04:00
Maarten Billemont
b5040a7786 Harmonize standardized platform naming between Native class and gradle. 2018-06-25 12:25:38 -04:00
Maarten Billemont
b1698ee339 C type fixes. 2018-06-25 12:25:17 -04:00
Maarten Billemont
8ffc0ae350 Execute gradle scripts with bash. 2018-06-25 10:34:56 -04:00
Maarten Billemont
8276d2f4e5 Build dependencies with a task & all windows archs. 2018-06-25 02:02:51 -04:00
Maarten Billemont
11cf86bc73 Use brew version of libtoolize. 2018-06-24 16:48:22 -04:00
Maarten Billemont
5084511404 Merge branch 'master' of gitlab.com:MasterPassword/MasterPassword 2018-06-24 16:31:22 -04:00
Maarten Billemont
fffec56d4e Build support for Microsoft Windows. 2018-06-24 16:20:42 -04:00
Maarten Billemont
9a0828c1eb Bump site for Android release. 2018-06-21 10:31:10 -04:00
64 changed files with 1036 additions and 562 deletions

28
.dockerignore Normal file
View 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

View File

@@ -6,9 +6,10 @@ build_project:
stage: build stage: build
script: script:
- "( brew bundle )" - "( 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 ./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 iOS' -sdk iphonesimulator clean build )"
- "( xcodebuild -workspace platform-darwin/MasterPassword.xcworkspace -configuration 'Test' -scheme 'MasterPassword macOS' clean build )" - "( xcodebuild -workspace platform-darwin/MasterPassword.xcworkspace -configuration 'Test' -scheme 'MasterPassword macOS' clean build )"
tags: tags:

View File

@@ -1,5 +1,6 @@
brew "libsodium" brew "libsodium"
brew "json-c" brew "json-c"
brew "libtool"
brew "automake" brew "automake"
brew "autoconf" brew "autoconf"

9
Dockerfile Normal file
View 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

View File

@@ -122,7 +122,7 @@ The `master-password` on the other hand, is only a simple phrase, which means it
5. I have another question. 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. 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 ## Support

View File

@@ -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>

View File

@@ -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 &amp;#36;project.name.&#10;Copyright (c) &amp;#36;today.year.&#10;&#10;&amp;#36;project.name is free software: you can redistribute it and/or modify&#10;it under the terms of the GNU General Public License as published by&#10;the Free Software Foundation, either version 3 of the License, or&#10;(at your option) any later version.&#10;&#10;&amp;#36;project.name is distributed in the hope that it will be useful,&#10;but WITHOUT ANY WARRANTY; without even the implied warranty of&#10;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the&#10;GNU General Public License for more details.&#10;&#10;You can find a copy of the GNU General Public License in the&#10;LICENSE file. Alternatively, see &lt;http://www.gnu.org/licenses/&gt;." />
</copyright>
</component>

View File

@@ -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>

View File

@@ -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
View File

@@ -1,17 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <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"> <component name="NullableNotNullManager">
<option name="myDefaultNullable" value="javax.annotation.Nullable" /> <option name="myDefaultNullable" value="javax.annotation.Nullable" />
<option name="myDefaultNotNull" value="javax.annotation.Nonnull" /> <option name="myDefaultNotNull" value="javax.annotation.Nonnull" />
@@ -37,9 +25,9 @@
</option> </option>
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" project-jdk-name="1.8" project-jdk-type="JavaSDK"> <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>
<component name="ThriftCompiler"> <component name="ProjectType">
<compilers /> <option name="id" value="Android" />
</component> </component>
</project> </project>

View File

@@ -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>

View File

@@ -1,3 +0,0 @@
<component name="DependencyValidationManager">
<scope name="masterpassword" pattern="com.lyndir.masterpassword..*" />
</component>

View File

@@ -1 +1,3 @@
org.gradle.daemon=true
org.gradle.configureondemand=true
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M

View File

@@ -4,13 +4,14 @@ class Mpw < Formula
url "https://masterpassword.app/mpw-2.6-cli-5-0-g344771db.tar.gz" url "https://masterpassword.app/mpw-2.6-cli-5-0-g344771db.tar.gz"
version "2.6-cli-5" version "2.6-cli-5"
sha256 "954c07b1713ecc2b30a07bead9c11e6204dd774ca67b5bdf7d2d6ad1c4eec170" sha256 "954c07b1713ecc2b30a07bead9c11e6204dd774ca67b5bdf7d2d6ad1c4eec170"
head "https://github.com/Lyndir/MasterPassword.git" revision 1
head "https://gitlab.com/MasterPassword/MasterPassword.git"
bottle do bottle do
cellar :any cellar :any
sha256 "ae8b265936797778a7cde788377eed89d9eacd267755a0b1186790057a10ff3b" => :high_sierra sha256 "46677cf8649983d5b77103d2ca56d9ad3697808ecc406f626a3462a089f932da" => :high_sierra
sha256 "b8a106c3c84ff939e928613d4a6ccf7b5234e40ebae1edf15e3cac52d8c2e5ea" => :sierra sha256 "19bf22915b3c534ad3ee6f1dfc20f142d53ae6c0c88757ae2632b7b1daa6667f" => :sierra
sha256 "9b58425b028a2598932474e1d0c17c13aad57e0a53ae7308c1b38404da8f3331" => :el_capitan sha256 "7090c3d31289d2ac5529bd0a6bae2632a36ba7fcd4bb7974248bb36a15f67c7e" => :el_capitan
end end
option "without-json-c", "Disable JSON configuration support" option "without-json-c", "Disable JSON configuration support"

View File

@@ -1,10 +1,8 @@
rootProject.name = 'masterpassword' rootProject.name = 'masterpassword'
def local = new Properties(); def local = new Properties()
try { def localFile = file( 'local.properties' )
local.load(file('local.properties').newDataInputStream()) localFile.exists() && local.load( localFile.newDataInputStream() )
} catch (FileNotFoundException ignored) {
}
include 'masterpassword-core' include 'masterpassword-core'
project( ':masterpassword-core' ).projectDir = new File( '../platform-independent/c/core' ) project( ':masterpassword-core' ).projectDir = new File( '../platform-independent/c/core' )
@@ -21,7 +19,7 @@ project(':masterpassword-tests').projectDir = new File( '../platform-independent
include 'masterpassword-gui' 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' include 'masterpassword-android'
project( ':masterpassword-android' ).projectDir = new File( '../platform-android' ) project( ':masterpassword-android' ).projectDir = new File( '../platform-android' )
} else { } else {

View File

@@ -50,10 +50,16 @@ _initialize() {
# #
# Check if all tools needed for the default implementations are available. # 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() { _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> # clean <prefix> <platform>
@@ -63,9 +69,15 @@ _initialize_needs() {
# By default, this will wipe the prefix, run `make distclean` and `git clean -fdx`. # By default, this will wipe the prefix, run `make distclean` and `git clean -fdx`.
clean() { _clean "$@"; } clean() { _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" rm -rf "$prefix"
[[ ! -e Makefile ]] || make -s distclean
[[ ! -e .git ]] || git clean -fdx
} }
# prepare <prefix> <platform> [ <arch> ... ] # prepare <prefix> <platform> [ <arch> ... ]
@@ -91,7 +103,7 @@ _prepare_clean() {
local prefix=$1 platform=$2; shift 2 local prefix=$1 platform=$2; shift 2
rm -rf "$prefix" rm -rf "$prefix"
install -d "$prefix" install -d "$prefix/out"
} }
# prepare_config <prefix> <platform> [ <arch> ... ] # prepare_config <prefix> <platform> [ <arch> ... ]
@@ -103,7 +115,14 @@ prepare_config() { _prepare_config "$@"; }
_prepare_config() { _prepare_config() {
local prefix=$1 platform=$2; shift 2 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> # target <prefix> <platform> <arch>
@@ -127,19 +146,27 @@ target_prepare() { _target_prepare "$@"; }
_target_prepare() { _target_prepare() {
local prefix=$1 platform=$2 arch=$3; shift 3 local prefix=$1 platform=$2 arch=$3; shift 3
if [[ $platform = windows ]]; then :
else
[[ ! -e Makefile ]] || make -s clean [[ ! -e Makefile ]] || make -s clean
fi
} }
# target_configure <prefix> <platform> <arch> [ <args> ... ] # target_configure <prefix> <platform> <arch> [ <args> ... ]
# #
# Configure the library for building the target. # 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() { _target_configure "$@"; }
_target_configure() { _target_configure() {
local prefix=$1 platform=$2 arch=$3; shift 3 local prefix=$1 platform=$2 arch=$3; shift 3
case "$platform" in case "$platform" in
'windows')
return
;;
'android') 'android')
host=( "$SDKROOT"/*-android* ) host=${host##*/} host=( "$SDKROOT"/*-android* ) host=${host##*/}
@@ -153,7 +180,7 @@ _target_configure() {
;; ;;
esac esac
./configure ${host:+--host="$host"} --prefix="$prefix/$arch" "$@" ./configure ${host:+--host="$host"} --enable-pic --disable-pie --prefix="$prefix/$arch" "$@"
} }
# target_build <prefix> <platform> <arch> # target_build <prefix> <platform> <arch>
@@ -164,10 +191,16 @@ _target_configure() {
target_build() { _target_build "$@"; } target_build() { _target_build "$@"; }
_target_build() { _target_build() {
local prefix=$1 platform=$2 arch=$3; shift 3 local prefix=$1 platform=$2 arch=$3; shift 3
#make -j3 check
cores=$(getconf NPROCESSORS_ONLN 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null ||:) 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 make -j"${cores:-3}" install
fi
} }
# finalize <prefix> <platform> [ <arch> ... ] # finalize <prefix> <platform> [ <arch> ... ]
@@ -188,21 +221,34 @@ _finalize() {
finalize_merge() { _finalize_merge "$@"; } finalize_merge() { _finalize_merge "$@"; }
_finalize_merge() { _finalize_merge() {
local prefix=$1 platform=$2; shift 2 local prefix=$1 platform=$2; shift 2
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 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') '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 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" lipo -create "${libs[@]}" -output "$prefix/out/lib/$lib"
fi fi
done done
;; ;;
'android') 'android')
for arch; do for arch in "${archs[@]}"; do
local abi=$arch local abi=$arch
case "$arch" in case "$arch" in
'arm') abi='armeabi-v7a' ;; 'arm') abi='armeabi-v7a' ;;
@@ -247,6 +293,8 @@ _build() {
'macos') archs=( 'x86_64' ) ;; 'macos') archs=( 'x86_64' ) ;;
'ios') archs=( 'i386' 'x86_64' 'armv7' 'armv7s' 'arm64' ) ;; 'ios') archs=( 'i386' 'x86_64' 'armv7' 'armv7s' 'arm64' ) ;;
'android') archs=( 'arm' 'arm64' 'x86' 'x86_64' ) ;; 'android') archs=( 'arm' 'arm64' 'x86' 'x86_64' ) ;;
'windows') archs=( 'Win32' 'x64' ) ;;
*) archs=( 'i386' 'x86_64' ) ;;
esac esac
fi fi
@@ -270,6 +318,8 @@ _build() {
# Set up a base environment for the platform. # Set up a base environment for the platform.
case "$platform" in case "$platform" in
'windows')
;;
'macos') 'macos')
SDKROOT="$(xcrun --show-sdk-path --sdk macosx)" SDKROOT="$(xcrun --show-sdk-path --sdk macosx)"
export PATH="$(xcrun --show-sdk-platform-path --sdk macosx)/usr/bin:$PATH" export PATH="$(xcrun --show-sdk-platform-path --sdk macosx)/usr/bin:$PATH"
@@ -319,4 +369,3 @@ _build() {
finalize "$prefix" "$platform" "${archs[@]}" finalize "$prefix" "$platform" "${archs[@]}"
} }

8
lib/bin/build_libjson-c-linux Executable file
View File

@@ -0,0 +1,8 @@
#!/usr/bin/env bash
source "${BASH_SOURCE%/*}/build_lib"
autoreconf() {
command autoreconf -Iautoconf-archive/m4 "$@"
}
build libjson-c linux

View 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
View File

@@ -0,0 +1,4 @@
#!/usr/bin/env bash
source "${BASH_SOURCE%/*}/build_lib"
build libsodium linux

View File

@@ -0,0 +1,4 @@
#!/usr/bin/env bash
source "${BASH_SOURCE%/*}/build_lib"
build libsodium windows

View File

@@ -13,12 +13,12 @@ add_library( mpw SHARED
add_library( sodium SHARED IMPORTED ) 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" ) 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_compile_definitions( mpw PRIVATE -DMPW_SODIUM=1 )
target_link_libraries( mpw PRIVATE sodium ) target_link_libraries( mpw PRIVATE sodium )
add_library( json-c SHARED IMPORTED ) 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" ) 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_compile_definitions( mpw PRIVATE -DMPW_JSON=1 )
target_link_libraries( mpw PRIVATE json-c ) target_link_libraries( mpw PRIVATE json-c )

View File

@@ -22,8 +22,8 @@ android {
} }
sourceSets { sourceSets {
main { main {
jniLibs.srcDirs "$projectDir/../lib/libsodium/build-android~/out/lib", jniLibs.srcDirs "$rootDir/../lib/libsodium/build-android~/out/lib",
"$projectDir/../lib/libjson-c/build-android~/out/lib" "$rootDir/../lib/libjson-c/build-android~/out/lib"
} }
} }
@@ -55,11 +55,23 @@ dependencies {
} }
preBuild { preBuild {
dependsOn task( type: Exec, 'buildLibSodium', { dependsOn task( type: Exec, 'build_libsodium-android', {
commandLine "$projectDir/../lib/bin/build_libsodium-android" commandLine 'bash', "$rootDir/../lib/bin/build_libsodium-android"
environment 'ANDROID_NDK_HOME', android.ndkDirectory
} ) } )
dependsOn task( type: Exec, 'buildLibJson-c', { dependsOn task( type: Exec, 'build_libjson-c-android', {
commandLine "$projectDir/../lib/bin/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
} )
}

View File

@@ -62,7 +62,7 @@
DA1554ED20B3928E00EA92C5 /* mpw-util.h */, DA1554ED20B3928E00EA92C5 /* mpw-util.h */,
); );
name = core; name = core;
path = ../platform-independent/c/core/src; path = "../platform-independent/c/core/src";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
/* End PBXGroup section */ /* End PBXGroup section */
@@ -129,8 +129,9 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
HEADER_SEARCH_PATHS = ( HEADER_SEARCH_PATHS = (
"/Library/Java/JavaVirtualMachines/jdk1.8.0_161.jdk/Contents/Home/include/**", "$(inherited)",
../lib/libsodium/src/libsodium/include, "\"$(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; JAVA_HOME = /Library/Java/JavaVirtualMachines/jdk1.8.0_161.jdk/Contents/Home;
OTHER_CFLAGS = ( OTHER_CFLAGS = (
@@ -144,8 +145,9 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
HEADER_SEARCH_PATHS = ( HEADER_SEARCH_PATHS = (
"/Library/Java/JavaVirtualMachines/jdk1.8.0_161.jdk/Contents/Home/include/**", "$(inherited)",
../lib/libsodium/src/libsodium/include, "\"$(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; JAVA_HOME = /Library/Java/JavaVirtualMachines/jdk1.8.0_161.jdk/Contents/Home;
OTHER_CFLAGS = ( OTHER_CFLAGS = (

View File

@@ -1783,7 +1783,7 @@
93D39CF7DB942C69D1C5D6BE /* mpw-util.h */, 93D39CF7DB942C69D1C5D6BE /* mpw-util.h */,
); );
name = core; name = core;
path = ../platform-independent/c/core/src; path = "../platform-independent/c/core/src";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
DA0CC4F41EAB99BA009A8ED9 /* Resources */ = { DA0CC4F41EAB99BA009A8ED9 /* Resources */ = {
@@ -4309,12 +4309,6 @@
GCC_WARN_UNUSED_LABEL = YES; GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_VALUE = YES; GCC_WARN_UNUSED_VALUE = YES;
GCC_WARN_UNUSED_VARIABLE = 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; IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
OTHER_LDFLAGS = "-ObjC"; OTHER_LDFLAGS = "-ObjC";
@@ -4348,13 +4342,18 @@
GCC_C_LANGUAGE_STANDARD = c11; GCC_C_LANGUAGE_STANDARD = c11;
GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch"; GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; 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"; INFOPLIST_FILE = "Source/iOS/MasterPassword-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 9.0; IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/../lib/libsodium/build-ios~/out/lib", "\"$(PROJECT_DIR)/../lib/libsodium/build-ios~/out/lib\"",
"$(PROJECT_DIR)/../lib/libjson-c/build-ios~/out/lib", "\"$(PROJECT_DIR)/../lib/libjson-c/build-ios~/out/lib\"",
); );
OTHER_CFLAGS = ( OTHER_CFLAGS = (
"-DMPW_SODIUM=1", "-DMPW_SODIUM=1",
@@ -4524,12 +4523,6 @@
GCC_WARN_UNUSED_LABEL = YES; GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_VALUE = YES; GCC_WARN_UNUSED_VALUE = YES;
GCC_WARN_UNUSED_VARIABLE = 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; IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
@@ -4635,12 +4628,6 @@
GCC_WARN_UNUSED_LABEL = YES; GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_VALUE = YES; GCC_WARN_UNUSED_VALUE = YES;
GCC_WARN_UNUSED_VARIABLE = 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; IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
OTHER_LDFLAGS = "-ObjC"; OTHER_LDFLAGS = "-ObjC";
@@ -4673,13 +4660,18 @@
GCC_C_LANGUAGE_STANDARD = c11; GCC_C_LANGUAGE_STANDARD = c11;
GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch"; GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; 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"; INFOPLIST_FILE = "Source/iOS/MasterPassword-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 9.0; IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/../lib/libsodium/build-ios~/out/lib", "\"$(PROJECT_DIR)/../lib/libsodium/build-ios~/out/lib\"",
"$(PROJECT_DIR)/../lib/libjson-c/build-ios~/out/lib", "\"$(PROJECT_DIR)/../lib/libjson-c/build-ios~/out/lib\"",
); );
OTHER_CFLAGS = ( OTHER_CFLAGS = (
"-DMPW_SODIUM=1", "-DMPW_SODIUM=1",
@@ -4712,13 +4704,18 @@
GCC_C_LANGUAGE_STANDARD = c11; GCC_C_LANGUAGE_STANDARD = c11;
GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch"; GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; 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"; INFOPLIST_FILE = "Source/iOS/MasterPassword-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 9.0; IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/../lib/libsodium/build-ios~/out/lib", "\"$(PROJECT_DIR)/../lib/libsodium/build-ios~/out/lib\"",
"$(PROJECT_DIR)/../lib/libjson-c/build-ios~/out/lib", "\"$(PROJECT_DIR)/../lib/libjson-c/build-ios~/out/lib\"",
); );
OTHER_CFLAGS = ( OTHER_CFLAGS = (
"-DMPW_SODIUM=1", "-DMPW_SODIUM=1",

View File

@@ -1224,7 +1224,7 @@
DA1C7ABC1F1A8F6E009A3551 /* mpw-tests.c */, DA1C7ABC1F1A8F6E009A3551 /* mpw-tests.c */,
); );
name = cli; name = cli;
path = "../../platform-independent/cli-c/cli"; path = ../../cli/src;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
DA2508F819513C1400AC23F1 /* Other Frameworks */ = { DA2508F819513C1400AC23F1 /* Other Frameworks */ = {
@@ -1820,7 +1820,7 @@
DA6773C61A4746AF004F356A /* mpw-util.h */, DA6773C61A4746AF004F356A /* mpw-util.h */,
); );
name = core; name = core;
path = ../platform-independent/c/core/src; path = "../platform-independent/c/core/src";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
DA89D4E51A51E53100AC64D7 /* Pearl-Cocoa */ = { DA89D4E51A51E53100AC64D7 /* Pearl-Cocoa */ = {
@@ -2992,15 +2992,8 @@
GCC_WARN_UNUSED_LABEL = YES; GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_VALUE = YES; GCC_WARN_UNUSED_VALUE = YES;
GCC_WARN_UNUSED_VARIABLE = 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_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)";
LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks"; LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks";
LIBRARY_SEARCH_PATHS = "$(inherit)";
MACOSX_DEPLOYMENT_TARGET = 10.11; MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
OTHER_LDFLAGS = "-ObjC"; OTHER_LDFLAGS = "-ObjC";
@@ -3028,12 +3021,17 @@
); );
GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch"; GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; 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"; INFOPLIST_FILE = "Source/Mac/MasterPassword-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(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", "\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib\"",
); );
OTHER_CFLAGS = ( OTHER_CFLAGS = (
"-DMPW_SODIUM=1", "-DMPW_SODIUM=1",
@@ -3084,13 +3082,15 @@
buildSettings = { buildSettings = {
CLANG_WARN_DOCUMENTATION_COMMENTS = NO; CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
HEADER_SEARCH_PATHS = ( HEADER_SEARCH_PATHS = (
"\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"",
/usr/include/libxml2,
"$(inherited)", "$(inherited)",
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/include\"",
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/include\"",
/usr/include/libxml2,
); );
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(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 = ( OTHER_CFLAGS = (
"-DMPW_SODIUM=1", "-DMPW_SODIUM=1",
@@ -3103,10 +3103,15 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CLANG_WARN_DOCUMENTATION_COMMENTS = NO; 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 = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(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", "\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib\"",
); );
OTHER_CFLAGS = ( OTHER_CFLAGS = (
"-DMPW_SODIUM=1", "-DMPW_SODIUM=1",
@@ -3122,10 +3127,15 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CLANG_WARN_DOCUMENTATION_COMMENTS = NO; 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 = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(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", "\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib\"",
); );
OTHER_CFLAGS = ( OTHER_CFLAGS = (
"-DMPW_SODIUM=1", "-DMPW_SODIUM=1",
@@ -3141,10 +3151,15 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CLANG_WARN_DOCUMENTATION_COMMENTS = NO; 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 = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(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", "\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib\"",
); );
OTHER_CFLAGS = ( OTHER_CFLAGS = (
"-DMPW_SODIUM=1", "-DMPW_SODIUM=1",
@@ -3160,9 +3175,15 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CLANG_WARN_DOCUMENTATION_COMMENTS = NO; 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 = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(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 = ( OTHER_CFLAGS = (
"-DMPW_SODIUM=1", "-DMPW_SODIUM=1",
@@ -3176,9 +3197,15 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CLANG_WARN_DOCUMENTATION_COMMENTS = NO; 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 = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(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 = ( OTHER_CFLAGS = (
"-DMPW_SODIUM=1", "-DMPW_SODIUM=1",
@@ -3192,9 +3219,15 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CLANG_WARN_DOCUMENTATION_COMMENTS = NO; 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 = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(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 = ( OTHER_CFLAGS = (
"-DMPW_SODIUM=1", "-DMPW_SODIUM=1",
@@ -3348,15 +3381,8 @@
GCC_WARN_UNUSED_LABEL = YES; GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_VALUE = YES; GCC_WARN_UNUSED_VALUE = YES;
GCC_WARN_UNUSED_VARIABLE = 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_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)";
LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks"; LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks";
LIBRARY_SEARCH_PATHS = "$(inherit)";
MACOSX_DEPLOYMENT_TARGET = 10.11; MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
@@ -3455,15 +3481,8 @@
GCC_WARN_UNUSED_LABEL = YES; GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_VALUE = YES; GCC_WARN_UNUSED_VALUE = YES;
GCC_WARN_UNUSED_VARIABLE = 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_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)";
LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks"; LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks";
LIBRARY_SEARCH_PATHS = "$(inherit)";
MACOSX_DEPLOYMENT_TARGET = 10.11; MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
OTHER_LDFLAGS = "-ObjC"; OTHER_LDFLAGS = "-ObjC";
@@ -3491,12 +3510,17 @@
); );
GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch"; GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; 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"; INFOPLIST_FILE = "Source/Mac/MasterPassword-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(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", "\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib\"",
); );
OTHER_CFLAGS = ( OTHER_CFLAGS = (
"-DMPW_SODIUM=1", "-DMPW_SODIUM=1",
@@ -3523,12 +3547,17 @@
); );
GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch"; GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; 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"; INFOPLIST_FILE = "Source/Mac/MasterPassword-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(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", "\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/lib\"",
); );
OTHER_CFLAGS = ( OTHER_CFLAGS = (
"-DMPW_SODIUM=1", "-DMPW_SODIUM=1",
@@ -3545,13 +3574,15 @@
buildSettings = { buildSettings = {
CLANG_WARN_DOCUMENTATION_COMMENTS = NO; CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
HEADER_SEARCH_PATHS = ( HEADER_SEARCH_PATHS = (
"\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"",
/usr/include/libxml2,
"$(inherited)", "$(inherited)",
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/include\"",
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/include\"",
/usr/include/libxml2,
); );
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(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 = ( OTHER_CFLAGS = (
"-DMPW_SODIUM=1", "-DMPW_SODIUM=1",
@@ -3565,13 +3596,15 @@
buildSettings = { buildSettings = {
CLANG_WARN_DOCUMENTATION_COMMENTS = NO; CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
HEADER_SEARCH_PATHS = ( HEADER_SEARCH_PATHS = (
"\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"",
/usr/include/libxml2,
"$(inherited)", "$(inherited)",
"\"$(PROJECT_DIR)/../lib/libsodium/build-macos~/out/include\"",
"\"$(PROJECT_DIR)/../lib/libjson-c/build-macos~/out/include\"",
/usr/include/libxml2,
); );
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(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 = ( OTHER_CFLAGS = (
"-DMPW_SODIUM=1", "-DMPW_SODIUM=1",

View File

@@ -219,7 +219,7 @@
- (IBAction)sourceButton:(id)sender { - (IBAction)sourceButton:(id)sender {
[[self dismissPopup].navigationController performSegueWithIdentifier:@"web" 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 { - (IBAction)thanksButton:(id)sender {

View File

@@ -9,71 +9,107 @@ plugins {
description = 'Master Password Algorithm Implementation' description = 'Master Password Algorithm Implementation'
artifacts { 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 ) { components.withType( ComponentWithRuntimeFile ) {
if (isOptimized()) { if (isOptimized()) {
from getRuntimeFile() from runtimeFile, {
into standardOperatingSystem( linkTask.get().targetPlatform.get() ) + '/' + into standardOperatingSystem( targetPlatform ) + '/' + standardArchitecture( targetPlatform )
standardArchitecture( linkTask.get().targetPlatform.get() ) }
} }
} }
} }
} }
library { library {
baseName.set( "mpw" ) baseName.set( 'mpw' )
linkage.set( [Linkage.STATIC, Linkage.SHARED] ) linkage.set( [Linkage.SHARED] )
// Reconfigure the toolchain from C++ to C. // Reconfigure the toolchain from C++ to C.
toolChains { toolChains {
withType( VisualCpp ) {
eachPlatform {
cppCompiler.withArguments { addAll( ['/TC', '/MT', '/Ox', '/DMPW_SODIUM=1', '/DSODIUM_STATIC', '/DSODIUM_EXPORT='] ) }
}
}
withType( GccCompatibleToolChain ) { withType( GccCompatibleToolChain ) {
eachPlatform { eachPlatform {
cppCompiler.withArguments { addAll( ["-x", "c", "-std=c11", "-Werror", "-DMPW_SODIUM=1"] ) } 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. // Cross-compile for these host platforms.
// TODO: Cross-compiling, blocked by: https://github.com/gradle/gradle-native/issues/169 - CppLibraryPlugin.java:163 // TODO: Cross-compiling, blocked by: https://github.com/gradle/gradle-native/issues/169 - CppLibraryPlugin.java:163
operatingSystems.set( [objects.named( OperatingSystemFamily, OperatingSystemFamily.WINDOWS ), operatingSystems.set( [objects.named( OperatingSystemFamily, OperatingSystemFamily.LINUX ),
objects.named( OperatingSystemFamily, OperatingSystemFamily.LINUX ), objects.named( OperatingSystemFamily, OperatingSystemFamily.MAC_OS ),
objects.named( OperatingSystemFamily, OperatingSystemFamily.MAC_OS )] ) objects.named( OperatingSystemFamily, OperatingSystemFamily.WINDOWS )] )
components.withType( CppComponent ) {
cppSource.from fileTree( 'src' )
privateHeaders {
// JDK for JNI support.
from files( new File( Jvm.current().javaHome, 'include' ) ) { first().eachDir { from it } }
}
binaries.configureEach { binaries.configureEach {
// Resolve a standard name for the platform. def system = standardOperatingSystem( targetPlatform )
def platform = standardOperatingSystem( targetPlatform )
project.dependencies { project.dependencies {
// Depend on JDK for JNI support.
add( includePathConfiguration.name,
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, add( linkLibraries.name,
fileTree( "../../../lib/libsodium/build-${platform}~/out/lib" ) ) 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) { static String standardOperatingSystem(NativePlatform platform) {
OperatingSystem os = platform.getOperatingSystem() OperatingSystem os = platform.getOperatingSystem()
if (os.isWindows()) { if (os.isWindows())
return OperatingSystemFamily.WINDOWS 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) { static String standardArchitecture(NativePlatform platform) {
Architecture arch = platform.getArchitecture() 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'
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -38,14 +38,16 @@ char *mpw_get_token(const char **in, const char *eol, char *delim) {
time_t mpw_mktime( time_t mpw_mktime(
const char *time) { const char *time) {
// TODO: Support parsing timezone into tm_gmtoff // TODO: Support for parsing non-UTC time strings
struct tm tm = { .tm_isdst = -1 }; struct tm tm = { .tm_isdst = -1 };
if (time && sscanf( time, "%4d-%2d-%2dT%2d:%2d:%2dZ", if (time && sscanf( time, "%4d-%2d-%2dT%2d:%2d:%2dZ",
&tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
&tm.tm_hour, &tm.tm_min, &tm.tm_sec ) == 6) { &tm.tm_hour, &tm.tm_min, &tm.tm_sec ) == 6) {
tm.tm_year -= 1900; // tm_year 0 = rfc3339 year 1900 tm.tm_year -= 1900; // tm_year 0 = rfc3339 year 1900
tm.tm_mon -= 1; // tm_mon 0 = rfc3339 month 1 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; return false;

View File

@@ -206,7 +206,7 @@ static bool mpw_marshal_write_flat(
if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &site->lastUsed ) )) 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", 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, 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_strings( &content, &loginContent, NULL );
} }
mpw_free( &masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
@@ -304,6 +304,7 @@ static bool mpw_marshal_write_json(
if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &site->lastUsed ) )) 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_object_add( json_site, "last_used", json_object_new_string( dateString ) );
if (site->questions_count) {
json_object *json_site_questions = json_object_new_object(); json_object *json_site_questions = json_object_new_object();
json_object_object_add( json_site, "questions", json_site_questions ); json_object_object_add( json_site, "questions", json_site_questions );
for (size_t q = 0; q < site->questions_count; ++q) { for (size_t q = 0; q < site->questions_count; ++q) {
@@ -327,16 +328,19 @@ static bool mpw_marshal_write_json(
json_object_object_add( json_site_question, "answer", json_object_new_string( 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 *json_site_mpw = json_object_new_object();
json_object_object_add( json_site, "_ext_mpw", json_site_mpw );
if (site->url) if (site->url)
json_object_object_add( json_site_mpw, "url", json_object_new_string( 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_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 ); mpw_free( &masterKey, MPMasterKeySize );
json_object_put( json_file ); json_object_put( json_file );
@@ -797,8 +801,9 @@ static MPMarshalledUser *mpw_marshal_read_json(
site->loginContent = mpw_strdup( siteLoginName ); 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 *json_site_questions = mpw_get_json_section( json_site.val, "questions" );
if (json_site_questions) {
json_object_iter json_site_question;
json_object_object_foreachC( json_site_questions, json_site_question ) { json_object_object_foreachC( json_site_questions, json_site_question ) {
MPMarshalledQuestion *question = mpw_marshal_question( site, json_site_question.key ); MPMarshalledQuestion *question = mpw_marshal_question( site, json_site_question.key );
const char *answerContent = mpw_get_json_string( json_site_question.val, "answer", NULL ); const char *answerContent = mpw_get_json_string( json_site_question.val, "answer", NULL );
@@ -817,6 +822,7 @@ static MPMarshalledUser *mpw_marshal_read_json(
} }
} }
} }
}
json_object_put( json_file ); json_object_put( json_file );
*error = (MPMarshalError){ .type = MPMarshalSuccess }; *error = (MPMarshalError){ .type = MPMarshalSuccess };
@@ -871,21 +877,14 @@ const MPMarshalFormat mpw_formatWithName(
if (!formatName || !strlen( formatName )) if (!formatName || !strlen( formatName ))
return MPMarshalFormatNone; return MPMarshalFormatNone;
// Lower-case to standardize it. if (mpw_strncasecmp( mpw_nameForFormat( MPMarshalFormatNone ), formatName, strlen( formatName ) ) == 0)
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)
return MPMarshalFormatNone; return MPMarshalFormatNone;
if (strncmp( mpw_nameForFormat( MPMarshalFormatFlat ), stdFormatName, strlen( stdFormatName ) ) == 0) if (mpw_strncasecmp( mpw_nameForFormat( MPMarshalFormatFlat ), formatName, strlen( formatName ) ) == 0)
return MPMarshalFormatFlat; return MPMarshalFormatFlat;
if (strncmp( mpw_nameForFormat( MPMarshalFormatJSON ), stdFormatName, strlen( stdFormatName ) ) == 0) if (mpw_strncasecmp( mpw_nameForFormat( MPMarshalFormatJSON ), formatName, strlen( formatName ) ) == 0)
return MPMarshalFormatJSON; return MPMarshalFormatJSON;
dbg( "Not a format name: %s", stdFormatName ); dbg( "Not a format name: %s", formatName );
return (MPMarshalFormat)ERR; return (MPMarshalFormat)ERR;
} }

View File

@@ -53,38 +53,31 @@ const MPResultType mpw_typeWithName(const char *typeName) {
return MPResultTypeDeriveKey; 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. // 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; return MPResultTypeTemplateMaximum;
if (strncmp( mpw_nameForType( MPResultTypeTemplateLong ), stdTypeName, strlen( stdTypeName ) ) == 0) if (mpw_strncasecmp( mpw_nameForType( MPResultTypeTemplateLong ), typeName, strlen( typeName ) ) == 0)
return MPResultTypeTemplateLong; return MPResultTypeTemplateLong;
if (strncmp( mpw_nameForType( MPResultTypeTemplateMedium ), stdTypeName, strlen( stdTypeName ) ) == 0) if (mpw_strncasecmp( mpw_nameForType( MPResultTypeTemplateMedium ), typeName, strlen( typeName ) ) == 0)
return MPResultTypeTemplateMedium; return MPResultTypeTemplateMedium;
if (strncmp( mpw_nameForType( MPResultTypeTemplateBasic ), stdTypeName, strlen( stdTypeName ) ) == 0) if (mpw_strncasecmp( mpw_nameForType( MPResultTypeTemplateBasic ), typeName, strlen( typeName ) ) == 0)
return MPResultTypeTemplateBasic; return MPResultTypeTemplateBasic;
if (strncmp( mpw_nameForType( MPResultTypeTemplateShort ), stdTypeName, strlen( stdTypeName ) ) == 0) if (mpw_strncasecmp( mpw_nameForType( MPResultTypeTemplateShort ), typeName, strlen( typeName ) ) == 0)
return MPResultTypeTemplateShort; return MPResultTypeTemplateShort;
if (strncmp( mpw_nameForType( MPResultTypeTemplatePIN ), stdTypeName, strlen( stdTypeName ) ) == 0) if (mpw_strncasecmp( mpw_nameForType( MPResultTypeTemplatePIN ), typeName, strlen( typeName ) ) == 0)
return MPResultTypeTemplatePIN; return MPResultTypeTemplatePIN;
if (strncmp( mpw_nameForType( MPResultTypeTemplateName ), stdTypeName, strlen( stdTypeName ) ) == 0) if (mpw_strncasecmp( mpw_nameForType( MPResultTypeTemplateName ), typeName, strlen( typeName ) ) == 0)
return MPResultTypeTemplateName; return MPResultTypeTemplateName;
if (strncmp( mpw_nameForType( MPResultTypeTemplatePhrase ), stdTypeName, strlen( stdTypeName ) ) == 0) if (mpw_strncasecmp( mpw_nameForType( MPResultTypeTemplatePhrase ), typeName, strlen( typeName ) ) == 0)
return MPResultTypeTemplatePhrase; return MPResultTypeTemplatePhrase;
if (strncmp( mpw_nameForType( MPResultTypeStatefulPersonal ), stdTypeName, strlen( stdTypeName ) ) == 0) if (mpw_strncasecmp( mpw_nameForType( MPResultTypeStatefulPersonal ), typeName, strlen( typeName ) ) == 0)
return MPResultTypeStatefulPersonal; return MPResultTypeStatefulPersonal;
if (strncmp( mpw_nameForType( MPResultTypeStatefulDevice ), stdTypeName, strlen( stdTypeName ) ) == 0) if (mpw_strncasecmp( mpw_nameForType( MPResultTypeStatefulDevice ), typeName, strlen( typeName ) ) == 0)
return MPResultTypeStatefulDevice; return MPResultTypeStatefulDevice;
if (strncmp( mpw_nameForType( MPResultTypeDeriveKey ), stdTypeName, strlen( stdTypeName ) ) == 0) if (mpw_strncasecmp( mpw_nameForType( MPResultTypeDeriveKey ), typeName, strlen( typeName ) ) == 0)
return MPResultTypeDeriveKey; return MPResultTypeDeriveKey;
dbg( "Not a generated type name: %s", stdTypeName ); dbg( "Not a generated type name: %s", typeName );
return (MPResultType)ERR; return (MPResultType)ERR;
} }
@@ -129,35 +122,35 @@ const char **mpw_templatesForType(MPResultType type, size_t *count) {
switch (type) { switch (type) {
case MPResultTypeTemplateMaximum: case MPResultTypeTemplateMaximum:
return mpw_alloc_array( count, const char *, return mpw_strings( count,
"anoxxxxxxxxxxxxxxxxx", "axxxxxxxxxxxxxxxxxno" ); "anoxxxxxxxxxxxxxxxxx", "axxxxxxxxxxxxxxxxxno", NULL );
case MPResultTypeTemplateLong: case MPResultTypeTemplateLong:
return mpw_alloc_array( count, const char *, return mpw_strings( count,
"CvcvnoCvcvCvcv", "CvcvCvcvnoCvcv", "CvcvCvcvCvcvno", "CvcvnoCvcvCvcv", "CvcvCvcvnoCvcv", "CvcvCvcvCvcvno",
"CvccnoCvcvCvcv", "CvccCvcvnoCvcv", "CvccCvcvCvcvno", "CvccnoCvcvCvcv", "CvccCvcvnoCvcv", "CvccCvcvCvcvno",
"CvcvnoCvccCvcv", "CvcvCvccnoCvcv", "CvcvCvccCvcvno", "CvcvnoCvccCvcv", "CvcvCvccnoCvcv", "CvcvCvccCvcvno",
"CvcvnoCvcvCvcc", "CvcvCvcvnoCvcc", "CvcvCvcvCvccno", "CvcvnoCvcvCvcc", "CvcvCvcvnoCvcc", "CvcvCvcvCvccno",
"CvccnoCvccCvcv", "CvccCvccnoCvcv", "CvccCvccCvcvno", "CvccnoCvccCvcv", "CvccCvccnoCvcv", "CvccCvccCvcvno",
"CvcvnoCvccCvcc", "CvcvCvccnoCvcc", "CvcvCvccCvccno", "CvcvnoCvccCvcc", "CvcvCvccnoCvcc", "CvcvCvccCvccno",
"CvccnoCvcvCvcc", "CvccCvcvnoCvcc", "CvccCvcvCvccno" ); "CvccnoCvcvCvcc", "CvccCvcvnoCvcc", "CvccCvcvCvccno", NULL );
case MPResultTypeTemplateMedium: case MPResultTypeTemplateMedium:
return mpw_alloc_array( count, const char *, return mpw_strings( count,
"CvcnoCvc", "CvcCvcno" ); "CvcnoCvc", "CvcCvcno", NULL );
case MPResultTypeTemplateShort: case MPResultTypeTemplateShort:
return mpw_alloc_array( count, const char *, return mpw_strings( count,
"Cvcn" ); "Cvcn", NULL );
case MPResultTypeTemplateBasic: case MPResultTypeTemplateBasic:
return mpw_alloc_array( count, const char *, return mpw_strings( count,
"aaanaaan", "aannaaan", "aaannaaa" ); "aaanaaan", "aannaaan", "aaannaaa", NULL );
case MPResultTypeTemplatePIN: case MPResultTypeTemplatePIN:
return mpw_alloc_array( count, const char *, return mpw_strings( count,
"nnnn" ); "nnnn", NULL );
case MPResultTypeTemplateName: case MPResultTypeTemplateName:
return mpw_alloc_array( count, const char *, return mpw_strings( count,
"cvccvcvcv" ); "cvccvcvcv", NULL );
case MPResultTypeTemplatePhrase: case MPResultTypeTemplatePhrase:
return mpw_alloc_array( count, const char *, return mpw_strings( count,
"cvcc cvc cvccvcv cvc", "cvc cvccvcvcv cvcv", "cv cvccv cvc cvcvccv" ); "cvcc cvc cvccvcv cvc", "cvc cvccvcvcv cvcv", "cv cvccv cvc cvcvccv", NULL );
default: { default: {
dbg( "Unknown generated type: %d", type ); dbg( "Unknown generated type: %d", type );
return NULL; return NULL;
@@ -177,21 +170,14 @@ const char *mpw_templateForType(MPResultType type, uint8_t templateIndex) {
const MPKeyPurpose mpw_purposeWithName(const char *purposeName) { const MPKeyPurpose mpw_purposeWithName(const char *purposeName) {
// Lower-case and trim optionally leading "generated" string from typeName to standardize it. if (mpw_strncasecmp( mpw_nameForPurpose( MPKeyPurposeAuthentication ), purposeName, strlen( purposeName ) ) == 0)
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)
return MPKeyPurposeAuthentication; return MPKeyPurposeAuthentication;
if (strncmp( mpw_nameForPurpose( MPKeyPurposeIdentification ), stdPurposeName, strlen( stdPurposeName ) ) == 0) if (mpw_strncasecmp( mpw_nameForPurpose( MPKeyPurposeIdentification ), purposeName, strlen( purposeName ) ) == 0)
return MPKeyPurposeIdentification; return MPKeyPurposeIdentification;
if (strncmp( mpw_nameForPurpose( MPKeyPurposeRecovery ), stdPurposeName, strlen( stdPurposeName ) ) == 0) if (mpw_strncasecmp( mpw_nameForPurpose( MPKeyPurposeRecovery ), purposeName, strlen( purposeName ) ) == 0)
return MPKeyPurposeRecovery; return MPKeyPurposeRecovery;
dbg( "Not a purpose name: %s", stdPurposeName ); dbg( "Not a purpose name: %s", purposeName );
return (MPKeyPurpose)ERR; return (MPKeyPurpose)ERR;
} }

View File

@@ -62,6 +62,27 @@ void mpw_uint64(const uint64_t number, uint8_t buf[8]) {
buf[7] = (uint8_t)((number >> 0L) & UINT8_MAX); 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) { bool mpw_push_buf(uint8_t **buffer, size_t *bufferSize, const void *pushBuffer, const size_t pushSize) {
if (!buffer || !bufferSize || !pushBuffer || !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; return NULL;
// IV = zero // IV = zero
uint8_t iv[AES_BLOCKLEN]; static uint8_t *iv = NULL;
mpw_zero( iv, sizeof iv ); if (!iv)
iv = calloc( AES_BLOCKLEN, sizeof( uint8_t ) );
// Add PKCS#7 padding // Add PKCS#7 padding
uint32_t aesSize = ((uint32_t)*bufSize + AES_BLOCKLEN - 1) & -AES_BLOCKLEN; // round up to block size. 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. if (encrypt && !(*bufSize % AES_BLOCKLEN)) // add pad block if plain text fits block size.
encrypt += AES_BLOCKLEN; encrypt += AES_BLOCKLEN;
uint8_t aesBuf[aesSize]; uint8_t *aesBuf = malloc( aesSize );
memcpy( aesBuf, buf, *bufSize ); memcpy( aesBuf, buf, *bufSize );
memset( aesBuf + *bufSize, aesSize - *bufSize, aesSize - *bufSize ); memset( aesBuf + *bufSize, aesSize - *bufSize, aesSize - *bufSize );
uint8_t *resultBuf = malloc( aesSize ); 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 ); AES_CBC_encrypt_buffer( resultBuf, aesBuf, aesSize, key, iv );
else else
AES_CBC_decrypt_buffer( resultBuf, aesBuf, aesSize, key, iv ); AES_CBC_decrypt_buffer( resultBuf, aesBuf, aesSize, key, iv );
mpw_zero( aesBuf, aesSize ); mpw_free( &aesBuf, aesSize );
mpw_zero( iv, AES_BLOCKLEN );
// Truncate PKCS#7 padding // Truncate PKCS#7 padding
if (encrypt) if (encrypt)
@@ -496,3 +517,12 @@ char *mpw_strndup(const char *src, size_t max) {
return dst; 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;
}

View File

@@ -33,36 +33,36 @@ extern int mpw_verbosity;
#endif #endif
#ifndef mpw_log #ifndef mpw_log
#define mpw_log(level, ...) ({ \ #define mpw_log(level, format, ...) do { \
if (mpw_verbosity >= level) { \ if (mpw_verbosity >= level) { \
mpw_log_do( level, ##__VA_ARGS__ ); \ mpw_log_do( level, format, ##__VA_ARGS__ ); \
}; }) }; } while (0)
#endif #endif
#ifndef trc #ifndef trc
/** Logging internal state. */ /** Logging internal state. */
#define trc_level 3 #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. */ /** Logging state and events interesting when investigating issues. */
#define dbg_level 2 #define dbg_level 2
#define dbg(...) mpw_log( dbg_level, ##__VA_ARGS__ ) #define dbg(format, ...) mpw_log( dbg_level, format, ##__VA_ARGS__ )
/** User messages. */ /** User messages. */
#define inf_level 1 #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. */ /** Recoverable issues and user suggestions. */
#define wrn_level 0 #define wrn_level 0
#define wrn(...) mpw_log( wrn_level, ##__VA_ARGS__ ) #define wrn(format, ...) mpw_log( wrn_level, format, ##__VA_ARGS__ )
/** Unrecoverable issues. */ /** Unrecoverable issues. */
#define err_level -1 #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. */ /** Issues that lead to abortion. */
#define ftl_level -2 #define ftl_level -2
#define ftl(...) mpw_log( ftl_level, ##__VA_ARGS__ ) #define ftl(format, ...) mpw_log( ftl_level, format, ##__VA_ARGS__ )
#endif #endif
#ifndef min #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]); 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. */ /** 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, ...) ({ \ const char **mpw_strings(
_type stackElements[] = { __VA_ARGS__ }; \ size_t *count, const char *strings, ...);
if (_count) \
*_count = sizeof( stackElements ) / sizeof( _type ); \
_type *allocElements = malloc( sizeof( stackElements ) ); \
memcpy( allocElements, stackElements, sizeof( stackElements ) ); \
allocElements; \
})
/** Push a buffer onto a buffer. reallocs the given buffer and appends the given buffer. */ /** Push a buffer onto a buffer. reallocs the given buffer and appends the given buffer. */
bool mpw_push_buf( 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. /** 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 * 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. * 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 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. * @param deltaSize The amount to increase the buffer's size by.
* @return true if successful, false if reallocation failed. * @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__ ); }) ({ __typeof__(strings) _s = strings; const char *__s = *_s; (void)__s; __mpw_free_strings( (char **)_s, __VA_ARGS__ ); })
bool __mpw_free_strings( bool __mpw_free_strings(
char **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. //// Cryptographic functions.
@@ -207,5 +215,7 @@ const size_t mpw_utf8_strlen(const char *utf8String);
char *mpw_strdup(const char *src); char *mpw_strdup(const char *src);
/** Drop-in for POSIX strndup(3). */ /** Drop-in for POSIX strndup(3). */
char *mpw_strndup(const char *src, size_t max); 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 #endif // _MPW_UTIL_H

View File

@@ -20,11 +20,12 @@ dependencies {
} }
processResources { processResources {
dependsOn task( type: Sync, 'copyResources', { dependsOn task( type: Sync, 'processResources-lib', {
into new File( processResources.outputs.files.singleFile, "lib" )
dependsOn configurations.lib { dependsOn configurations.lib {
files.each { libFile -> files.each { libFile ->
from zipTree( libFile ) from( zipTree( libFile ) )
into new File( processResources.outputs.files.singleFile, "lib" )
} }
} }
} ) } )
@@ -33,7 +34,7 @@ processResources {
compileJava { compileJava {
doLast { doLast {
ant.javah( class: 'com.lyndir.masterpassword.impl.MPAlgorithmV0', 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 ) classpath: files( sourceSets.main.compileClasspath, sourceSets.main.output ).asPath )
} }
} }

View File

@@ -45,8 +45,6 @@ public class MPAlgorithmV0 extends MPAlgorithm {
Native.load( MPAlgorithmV0.class, "mpw" ); Native.load( MPAlgorithmV0.class, "mpw" );
} }
public final Version version = MPAlgorithm.Version.V0;
protected final Logger logger = Logger.get( getClass() ); protected final Logger logger = Logger.get( getClass() );
@Nullable @Nullable

View File

@@ -18,12 +18,16 @@
package com.lyndir.masterpassword.impl; package com.lyndir.masterpassword.impl;
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.io.ByteStreams; import com.google.common.io.ByteStreams;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import java.io.*; import java.io.*;
import java.util.Locale; import java.util.Locale;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/** /**
@@ -45,7 +49,8 @@ public final class Native {
try { try {
System.loadLibrary( name ); System.loadLibrary( name );
return; 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. // Try to find and open a stream to the packaged library resource.
@@ -82,10 +87,26 @@ public final class Native {
@Nonnull @Nonnull
private static String getLibraryResource(final String library) { private static String getLibraryResource(final String library) {
String system = System.getProperty( "os.name" ).toLowerCase( Locale.ROOT ); String system = ifNotNullElse( System.getProperty( "os.name" ), "linux" ).toLowerCase( Locale.ROOT );
String architecture = System.getProperty( "os.arch" ).toLowerCase( Locale.ROOT ); String architecture = ifNotNullElse( System.getProperty( "os.arch" ), "x86" ).toLowerCase( Locale.ROOT );
if ("Mac OS X".equalsIgnoreCase( system ))
// 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"; 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 ); return Joiner.on( RESOURCE_SEPARATOR ).join( "", NATIVES_PATH, system, architecture, library );
} }

View File

@@ -21,8 +21,7 @@ package com.lyndir.masterpassword.gui;
import static com.lyndir.lhunath.opal.system.util.StringUtils.*; import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
import com.google.common.base.Charsets; import com.google.common.base.Charsets;
import com.google.common.io.CharSource; import com.google.common.io.ByteSource;
import com.google.common.io.Resources;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.lhunath.opal.system.util.TypeUtils; import com.lyndir.lhunath.opal.system.util.TypeUtils;
import com.lyndir.masterpassword.gui.view.PasswordFrame; 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.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.net.URI; import java.net.*;
import java.net.URL;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.Optional; import java.util.Optional;
import java.util.jar.*; import java.util.jar.*;
@@ -52,6 +50,9 @@ public class GUI implements UnlockFrame.SignInCallback {
private PasswordFrame<?, ?> passwordFrame; private PasswordFrame<?, ?> passwordFrame;
public static void main(final String... args) { public static void main(final String... args) {
Thread.setDefaultUncaughtExceptionHandler(
(t, e) -> logger.err( e, "Uncaught: %s", e.getLocalizedMessage() ) );
if (Config.get().checkForUpdates()) if (Config.get().checkForUpdates())
checkUpdate(); checkUpdate();
@@ -62,18 +63,24 @@ public class GUI implements UnlockFrame.SignInCallback {
catch (final UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException ignored) { catch (final UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException ignored) {
} }
create().open();
}
private static GUI create() {
try { try {
// AppleGUI adds support for macOS features. // AppleGUI adds support for macOS features.
Optional<Class<GUI>> appleGUI = TypeUtils.loadClass( "com.lyndir.masterpassword.gui.platform.mac.AppleGUI" ); Optional<Class<GUI>> appleGUI = TypeUtils.loadClass( "com.lyndir.masterpassword.gui.platform.mac.AppleGUI" );
if (appleGUI.isPresent()) if (appleGUI.isPresent())
appleGUI.get().getConstructor().newInstance().open(); return appleGUI.get().getConstructor().newInstance();
}
else // No special platform handling. catch (@SuppressWarnings("ErrorNotRethrown") final LinkageError ignored) {
new GUI().open();
} }
catch (final IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) { catch (final IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) {
throw logger.bug( e ); throw logger.bug( e );
} }
// Use platform-independent GUI.
return new GUI();
} }
private static void checkUpdate() { private static void checkUpdate() {
@@ -86,9 +93,17 @@ public class GUI implements UnlockFrame.SignInCallback {
continue; continue;
String manifestRevision = attributes.getValue( Attributes.Name.IMPLEMENTATION_VERSION ); String manifestRevision = attributes.getValue( Attributes.Name.IMPLEMENTATION_VERSION );
String upstreamRevisionURL = "https://masterpassword.app/masterpassword-gui.jar.rev"; String upstreamRevision = new ByteSource() {
CharSource upstream = Resources.asCharSource( URI.create( upstreamRevisionURL ).toURL(), Charsets.UTF_8 ); @Override
String upstreamRevision = upstream.readFirstLine(); 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( if ((manifestRevision != null) && (upstreamRevision != null) && !manifestRevision.equalsIgnoreCase(
upstreamRevision )) { upstreamRevision )) {
logger.inf( "Local Revision: <%s>", manifestRevision ); logger.inf( "Local Revision: <%s>", manifestRevision );

View File

@@ -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.ObjectUtils.*;
import static com.lyndir.lhunath.opal.system.util.StringUtils.*; 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.collect.Maps;
import com.google.common.io.Resources; import com.google.common.io.Resources;
import com.google.common.util.concurrent.JdkFutureAdapters; import com.google.common.util.concurrent.JdkFutureAdapters;
@@ -51,16 +50,18 @@ import org.jetbrains.annotations.NonNls;
public abstract class Res { public abstract class Res {
private static final int AVATAR_COUNT = 19; private static final int AVATAR_COUNT = 19;
private static final Map<Window, ScheduledExecutorService> executorByWindow = new WeakHashMap<>(); 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 Logger logger = Logger.get( Res.class );
private static final Colors colors = new Colors(); private static final Colors colors = new Colors();
public static Future<?> execute(final Window host, final Runnable job) { public static Future<?> job(final Window host, final Runnable job) {
return schedule( host, job, 0, TimeUnit.MILLISECONDS ); return job( host, job, 0, TimeUnit.MILLISECONDS );
} }
public static Future<?> schedule(final Window host, final Runnable job, final long delay, final TimeUnit timeUnit) { public static Future<?> job(final Window host, final Runnable job, final long delay, final TimeUnit timeUnit) {
return getExecutor( host ).schedule( () -> { return jobExecutor( host ).schedule( () -> {
try { try {
job.run(); job.run();
} }
@@ -70,33 +71,29 @@ public abstract class Res {
}, delay, timeUnit ); }, delay, timeUnit );
} }
public static <V> ListenableFuture<V> execute(final Window host, final Callable<V> job) { public static <V> ListenableFuture<V> job(final Window host, final Callable<V> job) {
return schedule( host, job, 0, TimeUnit.MILLISECONDS ); 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) { public static <V> ListenableFuture<V> job(final Window host, final Callable<V> job, final long delay, final TimeUnit timeUnit) {
ScheduledExecutorService executor = getExecutor( host ); ScheduledExecutorService executor = jobExecutor( host );
return JdkFutureAdapters.listenInPoolThread( executor.schedule( () -> { return JdkFutureAdapters.listenInPoolThread( executor.schedule( job::call, delay, timeUnit ), executor );
try {
return job.call();
}
catch (final Throwable t) {
logger.err( t, "Unexpected: %s", t.getLocalizedMessage() );
throw Throwables.propagate( t );
}
}, delay, timeUnit ), executor );
} }
private static ScheduledExecutorService getExecutor(final Window host) { public static Executor uiExecutor(final boolean immediate) {
ScheduledExecutorService executor = executorByWindow.get( host ); return immediate? immediateUiExecutor: laterUiExecutor;
}
public static ScheduledExecutorService jobExecutor(final Window host) {
ScheduledExecutorService executor = jobExecutorByWindow.get( host );
if (executor == null) { if (executor == null) {
executorByWindow.put( host, executor = Executors.newSingleThreadScheduledExecutor() ); jobExecutorByWindow.put( host, executor = Executors.newSingleThreadScheduledExecutor() );
host.addWindowListener( new WindowAdapter() { host.addWindowListener( new WindowAdapter() {
@Override @Override
public void windowClosed(final WindowEvent e) { public void windowClosed(final WindowEvent e) {
ExecutorService executor = executorByWindow.remove( host ); ExecutorService executor = jobExecutorByWindow.remove( host );
if (executor != null) if (executor != null)
executor.shutdownNow(); executor.shutdownNow();
} }
@@ -204,7 +201,7 @@ public abstract class Res {
font = Font.createFont( Font.TRUETYPE_FONT, Resources.getResource( fontResourceName ).openStream() ) ) ); font = Font.createFont( Font.TRUETYPE_FONT, Resources.getResource( fontResourceName ).openStream() ) ) );
} }
catch (final FontFormatException | IOException e) { catch (final FontFormatException | IOException e) {
throw Throwables.propagate( e ); throw logger.bug( e );
} }
return font; return font;

View File

@@ -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 );
}
}
}

View File

@@ -22,6 +22,7 @@ import com.google.common.primitives.UnsignedInteger;
import com.lyndir.masterpassword.MPAlgorithm; import com.lyndir.masterpassword.MPAlgorithm;
import com.lyndir.masterpassword.MPResultType; import com.lyndir.masterpassword.MPResultType;
import com.lyndir.masterpassword.model.impl.MPBasicSite; import com.lyndir.masterpassword.model.impl.MPBasicSite;
import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -44,6 +45,7 @@ public class MPIncognitoSite extends MPBasicSite<MPIncognitoQuestion> {
this.user = user; this.user = user;
} }
@Nonnull
@Override @Override
public MPIncognitoUser getUser() { public MPIncognitoUser getUser() {
return user; return user;

View File

@@ -19,6 +19,7 @@
package com.lyndir.masterpassword.gui.platform.mac; package com.lyndir.masterpassword.gui.platform.mac;
import com.apple.eawt.*; import com.apple.eawt.*;
import com.google.common.base.Preconditions;
import com.lyndir.masterpassword.gui.GUI; import com.lyndir.masterpassword.gui.GUI;
@@ -27,10 +28,17 @@ import com.lyndir.masterpassword.gui.GUI;
*/ */
public class AppleGUI extends GUI { public class AppleGUI extends GUI {
public AppleGUI() { static Application application;
Application application = Application.getApplication(); static {
application.addAppEventListener( new AppForegroundListener() { application = Preconditions.checkNotNull( Application.getApplication(), "Not an Apple Java application." );
}
public AppleGUI() {
application.addAppEventListener( new ApplicationListener() );
}
private class ApplicationListener implements AppForegroundListener, AppReOpenedListener {
@Override @Override
public void appMovedToBackground(final AppEvent.AppForegroundEvent arg0) { public void appMovedToBackground(final AppEvent.AppForegroundEvent arg0) {
@@ -40,12 +48,10 @@ public class AppleGUI extends GUI {
public void appRaisedToForeground(final AppEvent.AppForegroundEvent arg0) { public void appRaisedToForeground(final AppEvent.AppForegroundEvent arg0) {
open(); open();
} }
} );
application.addAppEventListener( new AppReOpenedListener() {
@Override @Override
public void appReOpened(final AppEvent.AppReOpenedEvent arg0) { public void appReOpened(final AppEvent.AppReOpenedEvent arg0) {
open(); open();
} }
} );
} }
} }

View File

@@ -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 );
}
}

View File

@@ -23,11 +23,12 @@ import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.primitives.UnsignedInteger; 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.*;
import com.lyndir.masterpassword.gui.Res; import com.lyndir.masterpassword.gui.Res;
import com.lyndir.masterpassword.gui.util.Components; import com.lyndir.masterpassword.gui.util.*;
import com.lyndir.masterpassword.gui.util.UnsignedIntegerModel;
import com.lyndir.masterpassword.model.MPSite; import com.lyndir.masterpassword.model.MPSite;
import com.lyndir.masterpassword.model.MPUser; import com.lyndir.masterpassword.model.MPUser;
import com.lyndir.masterpassword.model.impl.MPFileSite; import com.lyndir.masterpassword.model.impl.MPFileSite;
@@ -35,7 +36,7 @@ import com.lyndir.masterpassword.model.impl.MPFileUser;
import java.awt.*; import java.awt.*;
import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable; import java.awt.datatransfer.Transferable;
import java.awt.event.*; import java.awt.event.WindowEvent;
import java.util.Collection; import java.util.Collection;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.annotation.Nonnull; 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 { 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") @SuppressWarnings("FieldCanBeLocal")
private final Components.GradientPanel root; private final Components.GradientPanel root;
private final JTextField siteNameField; private final JTextField siteNameField;
@@ -96,30 +99,23 @@ public abstract class PasswordFrame<U extends MPUser<S>, S extends MPSite<?>> ex
siteNameField = Components.textField(), Components.stud(), siteNameField = Components.textField(), Components.stud(),
siteActionButton = Components.button( "Add Site" ) ); siteActionButton = Components.button( "Add Site" ) );
siteNameField.getDocument().addDocumentListener( this ); siteNameField.getDocument().addDocumentListener( this );
siteNameField.addActionListener( new ActionListener() { siteNameField.addActionListener(
@Override e -> Futures.addCallback( updatePassword( true ), new FailableCallback<String>( logger ) {
public void actionPerformed(final ActionEvent e) {
Futures.addCallback( updatePassword( true ), new FutureCallback<String>() {
@Override @Override
public void onSuccess(@Nullable final String sitePassword) { public void onSuccess(@Nullable final String sitePassword) {
if (sitePassword == null)
return;
if (currentSite instanceof MPFileSite)
((MPFileSite) currentSite).use();
Transferable clipboardContents = new StringSelection( sitePassword ); Transferable clipboardContents = new StringSelection( sitePassword );
Toolkit.getDefaultToolkit().getSystemClipboard().setContents( clipboardContents, null ); 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 ) ) );
@Override siteActionButton.addActionListener(
public void onFailure(@Nonnull final Throwable t) { e -> {
}
} );
}
} );
siteActionButton.addActionListener( e -> {
if (currentSite == null) if (currentSite == null)
return; return;
if (currentSite instanceof MPFileSite) if (currentSite instanceof MPFileSite)
@@ -229,10 +225,9 @@ public abstract class PasswordFrame<U extends MPUser<S>, S extends MPSite<?>> ex
site.setCounter( siteCounter ); site.setCounter( siteCounter );
} }
ListenableFuture<String> passwordFuture = Res.execute( this, () -> site.getResult( MPKeyPurpose.Authentication, null, null ) ); ListenableFuture<String> passwordFuture = Res.job( this, () ->
Futures.addCallback( passwordFuture, new FutureCallback<String>() { site.getResult( MPKeyPurpose.Authentication, null, null ) );
@Override
public void onSuccess(@Nullable final String sitePassword) {
SwingUtilities.invokeLater( () -> { SwingUtilities.invokeLater( () -> {
updatingUI = true; updatingUI = true;
currentSite = site; currentSite = site;
@@ -242,21 +237,24 @@ public abstract class PasswordFrame<U extends MPUser<S>, S extends MPSite<?>> ex
else else
siteActionButton.setText( "Add Site" ); siteActionButton.setText( "Add Site" );
resultTypeField.setSelectedItem( currentSite.getResultType() ); resultTypeField.setSelectedItem( currentSite.getResultType() );
siteVersionField.setSelectedItem( currentSite.getAlgorithm() ); siteVersionField.setSelectedItem( currentSite.getAlgorithm().version() );
siteCounterField.setValue( currentSite.getCounter() ); siteCounterField.setValue( currentSite.getCounter() );
siteNameField.setText( currentSite.getName() ); siteNameField.setText( currentSite.getName() );
if (siteNameField.getText().startsWith( siteNameQuery )) if (siteNameField.getText().startsWith( siteNameQuery ))
siteNameField.select( siteNameQuery.length(), siteNameField.getText().length() ); 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 ); passwordField.setText( sitePassword );
tipLabel.setText( "Press [Enter] to copy the password. Then paste it into the password field." );
updatingUI = false; updatingUI = false;
} );
}
@Override
public void onFailure(@Nonnull final Throwable t) {
} }
}, Res.uiExecutor( true ) );
} ); } );
return passwordFuture; return passwordFuture;

View File

@@ -153,7 +153,7 @@ public class UnlockFrame extends JFrame {
boolean checkSignIn() { boolean checkSignIn() {
if (identiconFuture != null) if (identiconFuture != null)
identiconFuture.cancel( false ); identiconFuture.cancel( false );
identiconFuture = Res.schedule( this, () -> SwingUtilities.invokeLater( () -> { identiconFuture = Res.job( this, () -> SwingUtilities.invokeLater( () -> {
String fullName = (user == null)? "": user.getFullName(); String fullName = (user == null)? "": user.getFullName();
char[] masterPassword = authenticationPanel.getMasterPassword(); char[] masterPassword = authenticationPanel.getMasterPassword();
@@ -186,7 +186,7 @@ public class UnlockFrame extends JFrame {
signInButton.setEnabled( false ); signInButton.setEnabled( false );
signInButton.setText( "Signing In..." ); signInButton.setText( "Signing In..." );
Res.execute( this, () -> { Res.job( this, () -> {
try { try {
user.authenticate( authenticationPanel.getMasterPassword() ); user.authenticate( authenticationPanel.getMasterPassword() );

View File

@@ -18,7 +18,8 @@ dependencies {
compileOnly group: 'com.google.auto.value', name: 'auto-value', version: '1.2' compileOnly group: 'com.google.auto.value', name: 'auto-value', version: '1.2'
apt 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' testImplementation group: 'org.testng', name: 'testng', version: '6.8.5'
testCompile group: 'ch.qos.logback', name: 'logback-classic', version: '1.1.2' testImplementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.1.2'
} }
test.useTestNG() test.useTestNG()

View File

@@ -21,6 +21,7 @@ package com.lyndir.masterpassword.model;
import com.google.common.primitives.UnsignedInteger; import com.google.common.primitives.UnsignedInteger;
import com.lyndir.masterpassword.*; import com.lyndir.masterpassword.*;
import java.util.Collection; import java.util.Collection;
import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -31,41 +32,50 @@ public interface MPSite<Q extends MPQuestion> extends Comparable<MPSite<?>> {
// - Meta // - Meta
@Nonnull
String getName(); String getName();
void setName(String name); void setName(String name);
// - Algorithm // - Algorithm
@Nonnull
MPAlgorithm getAlgorithm(); MPAlgorithm getAlgorithm();
void setAlgorithm(MPAlgorithm algorithm); void setAlgorithm(MPAlgorithm algorithm);
@Nonnull
UnsignedInteger getCounter(); UnsignedInteger getCounter();
void setCounter(UnsignedInteger counter); void setCounter(UnsignedInteger counter);
@Nonnull
MPResultType getResultType(); MPResultType getResultType();
void setResultType(MPResultType resultType); void setResultType(MPResultType resultType);
@Nonnull
MPResultType getLoginType(); MPResultType getLoginType();
void setLoginType(@Nullable MPResultType loginType); void setLoginType(@Nullable MPResultType loginType);
@Nonnull
String getResult(MPKeyPurpose keyPurpose, @Nullable String keyContext, @Nullable String state) String getResult(MPKeyPurpose keyPurpose, @Nullable String keyContext, @Nullable String state)
throws MPKeyUnavailableException, MPAlgorithmException; throws MPKeyUnavailableException, MPAlgorithmException;
@Nonnull
String getLogin(@Nullable String state) String getLogin(@Nullable String state)
throws MPKeyUnavailableException, MPAlgorithmException; throws MPKeyUnavailableException, MPAlgorithmException;
// - Relations // - Relations
MPUser<? extends MPSite<?>> getUser(); @Nonnull
MPUser<?> getUser();
void addQuestion(Q question); void addQuestion(Q question);
void deleteQuestion(Q question); void deleteQuestion(Q question);
@Nonnull
Collection<Q> getQuestions(); Collection<Q> getQuestions();
} }

View File

@@ -18,8 +18,7 @@
package com.lyndir.masterpassword.model; package com.lyndir.masterpassword.model;
import com.google.common.collect.ImmutableList; import com.google.common.collect.*;
import com.google.common.collect.Maps;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
@@ -37,7 +36,7 @@ public abstract class MPUserManager<U extends MPUser<?>> {
} }
public Collection<U> getUsers() { public Collection<U> getUsers() {
return ImmutableList.copyOf( usersByName.values() ); return ImmutableSortedSet.copyOf( usersByName.values() );
} }
public U getUserNamed(final String fullName) { public U getUserNamed(final String fullName) {

View File

@@ -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;
}
}
}

View File

@@ -31,7 +31,7 @@ import org.jetbrains.annotations.NotNull;
/** /**
* @author lhunath, 2018-05-14 * @author lhunath, 2018-05-14
*/ */
public abstract class MPBasicQuestion implements MPQuestion { public abstract class MPBasicQuestion extends Changeable implements MPQuestion {
private final String keyword; private final String keyword;
private MPResultType type; private MPResultType type;
@@ -56,6 +56,8 @@ public abstract class MPBasicQuestion implements MPQuestion {
@Override @Override
public void setType(final MPResultType type) { public void setType(final MPResultType type) {
this.type = type; this.type = type;
setChanged();
} }
@Nonnull @Nonnull
@@ -70,6 +72,13 @@ public abstract class MPBasicQuestion implements MPQuestion {
@Override @Override
public abstract MPBasicSite<?> getSite(); public abstract MPBasicSite<?> getSite();
@Override
protected void onChanged() {
super.onChanged();
getSite().setChanged();
}
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hashCode( getKeyword() ); return Objects.hashCode( getKeyword() );

View File

@@ -26,6 +26,7 @@ import com.lyndir.masterpassword.*;
import com.lyndir.masterpassword.model.MPQuestion; import com.lyndir.masterpassword.model.MPQuestion;
import com.lyndir.masterpassword.model.MPSite; import com.lyndir.masterpassword.model.MPSite;
import java.util.*; import java.util.*;
import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -33,7 +34,7 @@ import org.jetbrains.annotations.NotNull;
/** /**
* @author lhunath, 14-12-16 * @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 String name;
private MPAlgorithm algorithm; 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; this.loginType = (loginType == null)? algorithm.mpw_default_login_type(): loginType;
} }
@Nonnull
@Override @Override
public String getName() { public String getName() {
return name; return name;
@@ -64,8 +66,11 @@ public abstract class MPBasicSite<Q extends MPQuestion> implements MPSite<Q> {
@Override @Override
public void setName(final String name) { public void setName(final String name) {
this.name = name; this.name = name;
setChanged();
} }
@Nonnull
@Override @Override
public MPAlgorithm getAlgorithm() { public MPAlgorithm getAlgorithm() {
return algorithm; return algorithm;
@@ -74,8 +79,11 @@ public abstract class MPBasicSite<Q extends MPQuestion> implements MPSite<Q> {
@Override @Override
public void setAlgorithm(final MPAlgorithm algorithm) { public void setAlgorithm(final MPAlgorithm algorithm) {
this.algorithm = algorithm; this.algorithm = algorithm;
setChanged();
} }
@Nonnull
@Override @Override
public UnsignedInteger getCounter() { public UnsignedInteger getCounter() {
return counter; return counter;
@@ -84,8 +92,11 @@ public abstract class MPBasicSite<Q extends MPQuestion> implements MPSite<Q> {
@Override @Override
public void setCounter(final UnsignedInteger counter) { public void setCounter(final UnsignedInteger counter) {
this.counter = counter; this.counter = counter;
setChanged();
} }
@Nonnull
@Override @Override
public MPResultType getResultType() { public MPResultType getResultType() {
return resultType; return resultType;
@@ -94,8 +105,11 @@ public abstract class MPBasicSite<Q extends MPQuestion> implements MPSite<Q> {
@Override @Override
public void setResultType(final MPResultType resultType) { public void setResultType(final MPResultType resultType) {
this.resultType = resultType; this.resultType = resultType;
setChanged();
} }
@Nonnull
@Override @Override
public MPResultType getLoginType() { public MPResultType getLoginType() {
return loginType; return loginType;
@@ -104,8 +118,11 @@ public abstract class MPBasicSite<Q extends MPQuestion> implements MPSite<Q> {
@Override @Override
public void setLoginType(@Nullable final MPResultType loginType) { public void setLoginType(@Nullable final MPResultType loginType) {
this.loginType = ifNotNullElse( loginType, getAlgorithm().mpw_default_login_type() ); this.loginType = ifNotNullElse( loginType, getAlgorithm().mpw_default_login_type() );
setChanged();
} }
@Nonnull
@Override @Override
public String getResult(final MPKeyPurpose keyPurpose, @Nullable final String keyContext, @Nullable final String state) public String getResult(final MPKeyPurpose keyPurpose, @Nullable final String keyContext, @Nullable final String state)
throws MPKeyUnavailableException, MPAlgorithmException { throws MPKeyUnavailableException, MPAlgorithmException {
@@ -131,6 +148,7 @@ public abstract class MPBasicSite<Q extends MPQuestion> implements MPSite<Q> {
keyPurpose, keyContext, type, state ); keyPurpose, keyContext, type, state );
} }
@Nonnull
@Override @Override
public String getLogin(@Nullable final String state) public String getLogin(@Nullable final String state)
throws MPKeyUnavailableException, MPAlgorithmException { throws MPKeyUnavailableException, MPAlgorithmException {
@@ -141,18 +159,34 @@ public abstract class MPBasicSite<Q extends MPQuestion> implements MPSite<Q> {
@Override @Override
public void addQuestion(final Q question) { public void addQuestion(final Q question) {
questions.add( question ); questions.add( question );
setChanged();
} }
@Override @Override
public void deleteQuestion(final Q question) { public void deleteQuestion(final Q question) {
questions.remove( question ); questions.remove( question );
setChanged();
} }
@Nonnull
@Override @Override
public Collection<Q> getQuestions() { public Collection<Q> getQuestions() {
return Collections.unmodifiableCollection( questions ); return Collections.unmodifiableCollection( questions );
} }
@Nonnull
@Override
public abstract MPBasicUser<?> getUser();
@Override
protected void onChanged() {
super.onChanged();
getUser().setChanged();
}
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hashCode( getName() ); return Objects.hashCode( getName() );

View File

@@ -20,7 +20,7 @@ package com.lyndir.masterpassword.model.impl;
import static com.lyndir.lhunath.opal.system.util.StringUtils.*; 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.CodeUtils;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.masterpassword.*; import com.lyndir.masterpassword.*;
@@ -34,7 +34,7 @@ import javax.annotation.Nullable;
/** /**
* @author lhunath, 2014-06-08 * @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() ); protected final Logger logger = Logger.get( getClass() );
@@ -64,6 +64,8 @@ public abstract class MPBasicUser<S extends MPBasicSite<?>> implements MPUser<S>
@Override @Override
public void setAvatar(final int avatar) { public void setAvatar(final int avatar) {
this.avatar = avatar; this.avatar = avatar;
setChanged();
} }
@Nonnull @Nonnull
@@ -81,6 +83,8 @@ public abstract class MPBasicUser<S extends MPBasicSite<?>> implements MPUser<S>
@Override @Override
public void setAlgorithm(final MPAlgorithm algorithm) { public void setAlgorithm(final MPAlgorithm algorithm) {
this.algorithm = algorithm; this.algorithm = algorithm;
setChanged();
} }
@Nullable @Nullable
@@ -136,7 +140,7 @@ public abstract class MPBasicUser<S extends MPBasicSite<?>> implements MPUser<S>
public MPMasterKey getMasterKey() public MPMasterKey getMasterKey()
throws MPKeyUnavailableException { throws MPKeyUnavailableException {
if (masterKey == null) 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; return masterKey;
} }
@@ -144,11 +148,15 @@ public abstract class MPBasicUser<S extends MPBasicSite<?>> implements MPUser<S>
@Override @Override
public void addSite(final S site) { public void addSite(final S site) {
sites.add( site ); sites.add( site );
setChanged();
} }
@Override @Override
public void deleteSite(final S site) { public void deleteSite(final S site) {
sites.remove( site ); sites.remove( site );
setChanged();
} }
@Nonnull @Nonnull
@@ -160,7 +168,7 @@ public abstract class MPBasicUser<S extends MPBasicSite<?>> implements MPUser<S>
@Nonnull @Nonnull
@Override @Override
public Collection<S> findSites(final String query) { public Collection<S> findSites(final String query) {
ImmutableList.Builder<S> results = ImmutableList.builder(); ImmutableSortedSet.Builder<S> results = ImmutableSortedSet.naturalOrder();
for (final S site : getSites()) for (final S site : getSites())
if (site.getName().startsWith( query )) if (site.getName().startsWith( query ))
results.add( site ); results.add( site );

View File

@@ -33,19 +33,24 @@ public class MPFileQuestion extends MPBasicQuestion {
private final MPFileSite site; private final MPFileSite site;
@Nullable @Nullable
private String state; private String answerState;
public MPFileQuestion(final MPFileSite site, final String keyword, 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() ) ); super( keyword, ifNotNullElse( type, site.getAlgorithm().mpw_default_answer_type() ) );
this.site = site; this.site = site;
this.state = state; this.answerState = answerState;
}
@Nullable
public String getAnswerState() {
return answerState;
} }
public String getAnswer() public String getAnswer()
throws MPKeyUnavailableException, MPAlgorithmException { throws MPKeyUnavailableException, MPAlgorithmException {
return getAnswer( state ); return getAnswer( answerState );
} }
public void setAnswer(final MPResultType type, @Nullable final String answer) public void setAnswer(final MPResultType type, @Nullable final String answer)
@@ -53,10 +58,12 @@ public class MPFileQuestion extends MPBasicQuestion {
setType( type ); setType( type );
if (answer == null) if (answer == null)
this.state = null; this.answerState = null;
else else
this.state = getSite().getState( this.answerState = getSite().getState(
MPKeyPurpose.Recovery, getKeyword(), null, getType(), answer ); MPKeyPurpose.Recovery, getKeyword(), null, getType(), answer );
setChanged();
} }
@Nonnull @Nonnull

View File

@@ -20,6 +20,7 @@ package com.lyndir.masterpassword.model.impl;
import com.google.common.primitives.UnsignedInteger; import com.google.common.primitives.UnsignedInteger;
import com.lyndir.masterpassword.*; import com.lyndir.masterpassword.*;
import com.lyndir.masterpassword.model.MPSite;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.joda.time.Instant; import org.joda.time.Instant;
@@ -29,6 +30,7 @@ import org.joda.time.ReadableInstant;
/** /**
* @author lhunath, 14-12-05 * @author lhunath, 14-12-05
*/ */
@SuppressWarnings("ComparableImplementedButEqualsNotOverridden")
public class MPFileSite extends MPBasicSite<MPFileQuestion> { public class MPFileSite extends MPBasicSite<MPFileQuestion> {
private final MPFileUser user; private final MPFileUser user;
@@ -77,6 +79,8 @@ public class MPFileSite extends MPBasicSite<MPFileQuestion> {
public void setUrl(@Nullable final String url) { public void setUrl(@Nullable final String url) {
this.url = url; this.url = url;
setChanged();
} }
public int getUses() { public int getUses() {
@@ -125,6 +129,8 @@ public class MPFileSite extends MPBasicSite<MPFileQuestion> {
else else
this.resultState = getState( this.resultState = getState(
MPKeyPurpose.Authentication, null, getCounter(), getResultType(), password ); MPKeyPurpose.Authentication, null, getCounter(), getResultType(), password );
setChanged();
} }
@Nullable @Nullable
@@ -141,10 +147,22 @@ public class MPFileSite extends MPBasicSite<MPFileQuestion> {
else else
this.loginState = getState( this.loginState = getState(
MPKeyPurpose.Identification, null, null, getLoginType(), loginName ); MPKeyPurpose.Identification, null, null, getLoginType(), loginName );
setChanged();
} }
@Nonnull
@Override @Override
public MPFileUser getUser() { public MPFileUser getUser() {
return user; 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 );
}
} }

View File

@@ -18,7 +18,6 @@
package com.lyndir.masterpassword.model.impl; package com.lyndir.masterpassword.model.impl;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.masterpassword.*; import com.lyndir.masterpassword.*;
import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException; import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException;
import com.lyndir.masterpassword.model.MPUser; import com.lyndir.masterpassword.model.MPUser;
@@ -34,9 +33,6 @@ import org.joda.time.ReadableInstant;
@SuppressWarnings("ComparableImplementedButEqualsNotOverridden") @SuppressWarnings("ComparableImplementedButEqualsNotOverridden")
public class MPFileUser extends MPBasicUser<MPFileSite> { public class MPFileUser extends MPBasicUser<MPFileSite> {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MPFileUser.class );
@Nullable @Nullable
private byte[] keyID; private byte[] keyID;
private MPMarshalFormat format; private MPMarshalFormat format;
@@ -101,6 +97,8 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
public void setFormat(final MPMarshalFormat format) { public void setFormat(final MPMarshalFormat format) {
this.format = format; this.format = format;
setChanged();
} }
public MPMarshaller.ContentMode getContentMode() { public MPMarshaller.ContentMode getContentMode() {
@@ -109,6 +107,8 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
public void setContentMode(final MPMarshaller.ContentMode contentMode) { public void setContentMode(final MPMarshaller.ContentMode contentMode) {
this.contentMode = contentMode; this.contentMode = contentMode;
setChanged();
} }
public MPResultType getDefaultType() { public MPResultType getDefaultType() {
@@ -117,6 +117,8 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
public void setDefaultType(final MPResultType defaultType) { public void setDefaultType(final MPResultType defaultType) {
this.defaultType = defaultType; this.defaultType = defaultType;
setChanged();
} }
public ReadableInstant getLastUsed() { public ReadableInstant getLastUsed() {
@@ -125,6 +127,8 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
public void use() { public void use() {
lastUsed = new Instant(); lastUsed = new Instant();
setChanged();
} }
public void setJSON(final MPJSONFile json) { public void setJSON(final MPJSONFile json) {
@@ -141,8 +145,23 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
throws MPIncorrectMasterPasswordException, MPKeyUnavailableException, MPAlgorithmException { throws MPIncorrectMasterPasswordException, MPKeyUnavailableException, MPAlgorithmException {
super.authenticate( masterKey ); super.authenticate( masterKey );
if (keyID == null) if (keyID == null) {
keyID = masterKey.getKeyID( getAlgorithm() ); 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() void save()
@@ -152,7 +171,7 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
@Override @Override
public int compareTo(final MPUser<?> o) { 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) if (comparison != 0)
return comparison; return comparison;

View File

@@ -18,14 +18,14 @@
package com.lyndir.masterpassword.model.impl; package com.lyndir.masterpassword.model.impl;
import com.fasterxml.jackson.annotation.JsonAnyGetter; import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import java.util.*; import java.util.*;
/** /**
* @author lhunath, 2018-05-14 * @author lhunath, 2018-05-14
*/ */
@JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = MPJSONAnyObject.MPJSONEmptyValue.class)
class MPJSONAnyObject { class MPJSONAnyObject {
@JsonAnySetter @JsonAnySetter
@@ -35,4 +35,21 @@ class MPJSONAnyObject {
public Map<String, Object> getAny() { public Map<String, Object> getAny() {
return Collections.unmodifiableMap( any ); 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;
}
}
} }

View File

@@ -20,7 +20,10 @@ package com.lyndir.masterpassword.model.impl;
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*; 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.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.primitives.UnsignedInteger; import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.CodeUtils; import com.lyndir.lhunath.opal.system.CodeUtils;
@@ -43,12 +46,22 @@ public class MPJSONFile extends MPJSONAnyObject {
protected static final ObjectMapper objectMapper = new ObjectMapper(); protected static final ObjectMapper objectMapper = new ObjectMapper();
static { 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 ); objectMapper.setVisibility( PropertyAccessor.FIELD, JsonAutoDetect.Visibility.NON_PRIVATE );
} }
public MPJSONFile write(final MPFileUser modelUser) public MPJSONFile write(final MPFileUser modelUser)
throws MPKeyUnavailableException, MPAlgorithmException { throws MPKeyUnavailableException, MPAlgorithmException {
// Section: "export" // Section: "export"
if (export == null) if (export == null)
export = new Export(); export = new Export();
@@ -98,38 +111,27 @@ public class MPJSONFile extends MPJSONAnyObject {
site.uses = modelSite.getUses(); site.uses = modelSite.getUses();
site.last_used = MPConstants.dateTimeFormatter.print( modelSite.getLastUsed() ); 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) if (site._ext_mpw == null)
site._ext_mpw = new Site.Ext(); site._ext_mpw = new Site.Ext();
site._ext_mpw.url = modelSite.getUrl(); 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; return this;
@@ -143,6 +145,7 @@ public class MPJSONFile extends MPJSONAnyObject {
(user.default_type != null)? user.default_type: algorithm.mpw_default_result_type(), (user.default_type != null)? user.default_type: algorithm.mpw_default_result_type(),
(user.last_used != null)? MPConstants.dateTimeFormatter.parseDateTime( user.last_used ): new Instant(), (user.last_used != null)? MPConstants.dateTimeFormatter.parseDateTime( user.last_used ): new Instant(),
MPMarshalFormat.JSON, export.redacted? MPMarshaller.ContentMode.PROTECTED: MPMarshaller.ContentMode.VISIBLE ); MPMarshalFormat.JSON, export.redacted? MPMarshaller.ContentMode.PROTECTED: MPMarshaller.ContentMode.VISIBLE );
model.beginChanges();
model.setJSON( this ); model.setJSON( this );
if (masterPassword != null) if (masterPassword != null)
model.authenticate( masterPassword ); model.authenticate( masterPassword );
@@ -167,6 +170,7 @@ public class MPJSONFile extends MPJSONAnyObject {
model.addSite( site ); model.addSite( site );
} }
model.endChanges();
return model; return model;
} }
@@ -193,26 +197,26 @@ public class MPJSONFile extends MPJSONAnyObject {
String full_name; String full_name;
String last_used; String last_used;
@Nullable @Nullable
MPAlgorithm.Version algorithm;
@Nullable
String key_id; String key_id;
@Nullable @Nullable
MPAlgorithm.Version algorithm;
@Nullable
MPResultType default_type; MPResultType default_type;
} }
public static class Site extends MPJSONAnyObject { public static class Site extends MPJSONAnyObject {
@Nullable
MPResultType type;
long counter; long counter;
MPAlgorithm.Version algorithm; MPAlgorithm.Version algorithm;
@Nullable @Nullable
MPResultType type;
@Nullable
String password; String password;
@Nullable @Nullable
MPResultType login_type;
@Nullable
String login_name; String login_name;
@Nullable
MPResultType login_type;
int uses; int uses;
@Nullable @Nullable

View File

@@ -37,7 +37,7 @@ public class MPJSONMarshaller implements MPMarshaller {
throws MPKeyUnavailableException, MPMarshalException, MPAlgorithmException { throws MPKeyUnavailableException, MPMarshalException, MPAlgorithmException {
try { try {
return objectMapper.writeValueAsString( user.getJSON().write( user ) ); return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString( user.getJSON().write( user ) );
} }
catch (final JsonProcessingException e) { catch (final JsonProcessingException e) {
throw new MPMarshalException( "Couldn't compose JSON for: " + user, e ); throw new MPMarshalException( "Couldn't compose JSON for: " + user, e );

View File

@@ -7,13 +7,11 @@ description = 'Master Password Test Suite'
dependencies { dependencies {
implementation group: 'com.lyndir.lhunath.opal', name: 'opal-system', version: '1.7-p2' implementation group: 'com.lyndir.lhunath.opal', name: 'opal-system', version: '1.7-p2'
compile project( ':masterpassword-algorithm' ) implementation project( ':masterpassword-algorithm' )
compile project( ':masterpassword-model' ) implementation project( ':masterpassword-model' )
testCompile group: 'org.testng', name: 'testng', version: '6.8.5' testImplementation group: 'org.testng', name: 'testng', version: '6.8.5'
testCompile group: 'ch.qos.logback', name: 'logback-classic', version: '1.1.2' 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() test.useTestNG()

View File

@@ -12,7 +12,7 @@
<body> <body>
<header> <header>
<p><strong>BETA</strong> - This site may not yet work on your browser.<br> <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> <span id="error"></span>
</header> </header>
<section id="identity" class="active"><form action="#"> <section id="identity" class="active"><form action="#">