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
script:
- "( brew bundle )"
- "( ./lib/bin/build_libsodium-macos )"
- "( ./lib/bin/build_libsodium-macos clean && ./lib/bin/build_libsodium-macos )"
- "( ./lib/bin/build_libjson-c-macos clean && ./lib/bin/build_libjson-c-macos )"
- "( cd ./platform-independent/c/cli && ./clean && targets=all ./build && ./mpw-tests && ./mpw-cli-tests )"
- "( cd ./gradle && ./gradlew --info clean test )"
- "( cd ./gradle && ./gradlew --stacktrace clean test )"
- "( xcodebuild -workspace platform-darwin/MasterPassword.xcworkspace -configuration 'Test' -scheme 'MasterPassword iOS' -sdk iphonesimulator clean build )"
- "( xcodebuild -workspace platform-darwin/MasterPassword.xcworkspace -configuration 'Test' -scheme 'MasterPassword macOS' clean build )"
tags:

View File

@@ -1,5 +1,6 @@
brew "libsodium"
brew "json-c"
brew "libtool"
brew "automake"
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.
Please don't hesitate to [get in touch](#support), we're more than happy to answer all your Master Password questions. Any problems or suggestions can be reported [as GitHub issues](https://github.com/Lyndir/MasterPassword/issues).
Please don't hesitate to [get in touch](#support), we're more than happy to answer all your Master Password questions. Any problems or suggestions can be reported [as GitLab issues](https://gitlab.com/MasterPassword/MasterPassword/issues/).
@@ -187,7 +187,7 @@ Note that in order to build the Android application, you will need to have the A
Go into the `platform-independent/cli-c` directory and run `./build`. The native command-line client will then be built.
For detailed instructions, see [the native CLI instructions](platform-independent/cli-c/README.md).
For detailed instructions, see [the native CLI instructions](platform-independent/c/README.md).
## Support

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"?>
<project version="4">
<component name="FrameworkDetectionExcludesConfiguration">
<type id="jpa" />
<type id="web" />
</component>
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/../../opal/pom.xml" />
<option value="$PROJECT_DIR$/../../pom.xml" />
</list>
</option>
</component>
<component name="NullableNotNullManager">
<option name="myDefaultNullable" value="javax.annotation.Nullable" />
<option name="myDefaultNotNull" value="javax.annotation.Nonnull" />
@@ -37,9 +25,9 @@
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/classes" />
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ThriftCompiler">
<compilers />
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

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

View File

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

View File

@@ -1,29 +1,27 @@
rootProject.name = 'masterpassword'
def local = new Properties();
try {
local.load(file('local.properties').newDataInputStream())
} catch (FileNotFoundException ignored) {
}
def local = new Properties()
def localFile = file( 'local.properties' )
localFile.exists() && local.load( localFile.newDataInputStream() )
include 'masterpassword-core'
project(':masterpassword-core').projectDir = new File( '../platform-independent/c/core' )
project( ':masterpassword-core' ).projectDir = new File( '../platform-independent/c/core' )
include 'masterpassword-algorithm'
project(':masterpassword-algorithm').projectDir = new File( '../platform-independent/java/algorithm' )
project( ':masterpassword-algorithm' ).projectDir = new File( '../platform-independent/java/algorithm' )
include 'masterpassword-model'
project(':masterpassword-model').projectDir = new File( '../platform-independent/java/model' )
project( ':masterpassword-model' ).projectDir = new File( '../platform-independent/java/model' )
include 'masterpassword-tests'
project(':masterpassword-tests').projectDir = new File( '../platform-independent/java/tests' )
project( ':masterpassword-tests' ).projectDir = new File( '../platform-independent/java/tests' )
include 'masterpassword-gui'
project(':masterpassword-gui').projectDir = new File( '../platform-independent/java/gui' )
project( ':masterpassword-gui' ).projectDir = new File( '../platform-independent/java/gui' )
if (local.containsKey('sdk.dir')) {
if (local.containsKey( 'sdk.dir' ) && file( local.getProperty( 'sdk.dir' ) ).exists()) {
include 'masterpassword-android'
project(':masterpassword-android').projectDir = new File( '../platform-android' )
project( ':masterpassword-android' ).projectDir = new File( '../platform-android' )
} else {
logger.warn( "Skipping masterpassword-android since sdk.dir is not defined in local.properties." )
}

View File

@@ -2,7 +2,7 @@
#
# Your build script should simply source this script, optionally override any build hooks and then invoke `build`.
# The build product should be available under `build-<platform>~/out`, under the library path.
#
#
# Hook lifecycle:
# - build
# - initialize
@@ -50,10 +50,16 @@ _initialize() {
#
# Check if all tools needed for the default implementations are available.
#
# By default, this will check for `automake` (for aclocal) and `autoconf` (for autoreconf).
# By default, this will check for `libtool` (for libtoolize), `automake` (for aclocal) and `autoconf` (for autoreconf).
initialize_needs() { _initialize_needs "$@"; }
_initialize_needs() {
needs automake autoconf
if [[ $platform = windows ]]; then
needs cmd
export VSINSTALLDIR="${VSINSTALLDIR:-$(cd "$(cygpath -F 0x002a)/Microsoft Visual Studio"/*/*/Common7/.. && pwd)}"
[[ -e "$VSINSTALLDIR/Common7/Tools/VsMSBuildCmd.bat" ]] || { echo >&2 "Missing: msbuild. Please install 'Build Tools for Visual Studio'."; return 1; }
else
needs libtool automake autoconf
fi
}
# clean <prefix> <platform>
@@ -63,9 +69,15 @@ _initialize_needs() {
# By default, this will wipe the prefix, run `make distclean` and `git clean -fdx`.
clean() { _clean "$@"; }
_clean() {
if [[ $platform = windows ]]; then
printf '"%%VSINSTALLDIR%%\Common7\Tools\VsMSBuildCmd.bat" && msbuild /t:Clean' > .clean.bat
cmd //c .clean.bat
rm -f .clean.bat
elif [[ -e Makefile ]] && make -s distclean; then :
elif [[ -e .git ]] && git clean -fdx; then :
fi
rm -rf "$prefix"
[[ ! -e Makefile ]] || make -s distclean
[[ ! -e .git ]] || git clean -fdx
}
# prepare <prefix> <platform> [ <arch> ... ]
@@ -91,7 +103,7 @@ _prepare_clean() {
local prefix=$1 platform=$2; shift 2
rm -rf "$prefix"
install -d "$prefix"
install -d "$prefix/out"
}
# prepare_config <prefix> <platform> [ <arch> ... ]
@@ -103,7 +115,14 @@ prepare_config() { _prepare_config "$@"; }
_prepare_config() {
local prefix=$1 platform=$2; shift 2
[[ -e configure ]] || autoreconf --verbose --install --symlink 2> >(sed 's/^\([^:]*\):[0-9]\{1,\}: /\1: /')
[[ -e "$prefix/out/.prepared" ]] && return
if [[ $platform = windows ]]; then :
else
autoreconf --verbose --install --force 2> >(sed 's/^\([^:]*\):[0-9]\{1,\}: /\1: /')
fi
touch "$prefix/out/.prepared"
}
# target <prefix> <platform> <arch>
@@ -127,19 +146,27 @@ target_prepare() { _target_prepare "$@"; }
_target_prepare() {
local prefix=$1 platform=$2 arch=$3; shift 3
[[ ! -e Makefile ]] || make -s clean
if [[ $platform = windows ]]; then :
else
[[ ! -e Makefile ]] || make -s clean
fi
}
# target_configure <prefix> <platform> <arch> [ <args> ... ]
#
# Configure the library for building the target.
#
# By default, this will run `./configure --host=<host> --prefix=<prefix>/<arch> --disable-shared <args>`.
# By default, this will run `./configure --host=<host> --prefix=<prefix>/<arch> <args>`.
# By default, some platform-specific arguments will be passed in as well as
# --enable-pic --disable-pie to ensure the resulting library can be linked again.
target_configure() { _target_configure "$@"; }
_target_configure() {
local prefix=$1 platform=$2 arch=$3; shift 3
case "$platform" in
'windows')
return
;;
'android')
host=( "$SDKROOT"/*-android* ) host=${host##*/}
@@ -153,7 +180,7 @@ _target_configure() {
;;
esac
./configure ${host:+--host="$host"} --prefix="$prefix/$arch" "$@"
./configure ${host:+--host="$host"} --enable-pic --disable-pie --prefix="$prefix/$arch" "$@"
}
# target_build <prefix> <platform> <arch>
@@ -164,10 +191,16 @@ _target_configure() {
target_build() { _target_build "$@"; }
_target_build() {
local prefix=$1 platform=$2 arch=$3; shift 3
#make -j3 check
cores=$(getconf NPROCESSORS_ONLN 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null ||:)
make -j"${cores:-3}" install
if [[ $platform = windows ]]; then
# I cannot for the life of me figure out how to pass this command directly into cmd.
printf '"%%VSINSTALLDIR%%\Common7\Tools\VsMSBuildCmd.bat" && msbuild /t:Rebuild /p:Configuration=ReleaseDLL;Platform=%s;OutDir=%s' "$arch" "$(cygpath -w "${prefix##$PWD/}/$arch/")" > .build.bat
cmd //c .build.bat
rm -f .build.bat
else
local cores=$(getconf NPROCESSORS_ONLN 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null ||:)
make -j"${cores:-3}" install
fi
}
# finalize <prefix> <platform> [ <arch> ... ]
@@ -188,21 +221,34 @@ _finalize() {
finalize_merge() { _finalize_merge "$@"; }
_finalize_merge() {
local prefix=$1 platform=$2; shift 2
local archs=( "$@" )
mv -f -- "$prefix/$1/include" "$prefix/out/"
[[ -e "$prefix/$archs/include" ]] && mv -f -- "$prefix/$archs/include" "$prefix/out/"
mkdir -p "$prefix/out/lib"
install -d "$prefix/out/lib"
case "$platform" in
'linux')
for arch in "${archs[@]}"; do
install -d "$prefix/out/lib/$arch"
install -p "$prefix/$arch/lib/"*.a "$prefix/out/lib/$arch/"
done
;;
'windows')
for arch in "${archs[@]}"; do
install -d "$prefix/out/lib/$arch"
install -p "$prefix/$arch/"*.lib "$prefix/out/lib/$arch/"
done
;;
'macos'|'ios')
for lib in "$prefix/$1/lib/"*; do
for lib in "$prefix/$archs/lib/"*; do
if lipo -info "$lib" >/dev/null 2>&1; then
local lib=("${lib##*/}") libs=("${@/#/$prefix/}") libs=("${libs[@]/%//lib/$lib}")
local lib=("${lib##*/}") libs=("${archs[@]/#/$prefix/}") libs=("${libs[@]/%//lib/$lib}")
lipo -create "${libs[@]}" -output "$prefix/out/lib/$lib"
fi
done
;;
'android')
for arch; do
for arch in "${archs[@]}"; do
local abi=$arch
case "$arch" in
'arm') abi='armeabi-v7a' ;;
@@ -247,6 +293,8 @@ _build() {
'macos') archs=( 'x86_64' ) ;;
'ios') archs=( 'i386' 'x86_64' 'armv7' 'armv7s' 'arm64' ) ;;
'android') archs=( 'arm' 'arm64' 'x86' 'x86_64' ) ;;
'windows') archs=( 'Win32' 'x64' ) ;;
*) archs=( 'i386' 'x86_64' ) ;;
esac
fi
@@ -270,6 +318,8 @@ _build() {
# Set up a base environment for the platform.
case "$platform" in
'windows')
;;
'macos')
SDKROOT="$(xcrun --show-sdk-path --sdk macosx)"
export PATH="$(xcrun --show-sdk-platform-path --sdk macosx)/usr/bin:$PATH"
@@ -319,4 +369,3 @@ _build() {
finalize "$prefix" "$platform" "${archs[@]}"
}

8
lib/bin/build_libjson-c-linux Executable file
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 )
set_target_properties( sodium PROPERTIES IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../lib/libsodium/build-android~/out/lib/${ANDROID_ABI}/libsodium.so" )
target_include_directories( mpw PRIVATE "${PROJECT_SOURCE_DIR}/../lib/libsodium/build-android~/out" )
target_include_directories( mpw PRIVATE "${PROJECT_SOURCE_DIR}/../lib/libsodium/build-android~/out/include" )
target_compile_definitions( mpw PRIVATE -DMPW_SODIUM=1 )
target_link_libraries( mpw PRIVATE sodium )
add_library( json-c SHARED IMPORTED )
set_target_properties( json-c PROPERTIES IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../lib/libjson-c/build-android~/out/lib/${ANDROID_ABI}/libjson-c.so" )
target_include_directories( mpw PRIVATE "${PROJECT_SOURCE_DIR}/../lib/libjson-c/build-android~/out" )
target_include_directories( mpw PRIVATE "${PROJECT_SOURCE_DIR}/../lib/libjson-c/build-android~/out/include" )
target_compile_definitions( mpw PRIVATE -DMPW_JSON=1 )
target_link_libraries( mpw PRIVATE json-c )

View File

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

View File

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

View File

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

View File

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

View File

@@ -219,7 +219,7 @@
- (IBAction)sourceButton:(id)sender {
[[self dismissPopup].navigationController performSegueWithIdentifier:@"web" sender:
[NSURL URLWithString:@"https://github.com/Lyndir/MasterPassword/"]];
[NSURL URLWithString:@"https://gitlab.com/MasterPassword/MasterPassword/"]];
}
- (IBAction)thanksButton:(id)sender {

View File

@@ -9,71 +9,107 @@ plugins {
description = 'Master Password Algorithm Implementation'
artifacts {
'default' task( type: Zip, "archive" ) {
'default' task( type: Zip, 'archive' ) {
// TODO: exclude lib files that are produced by the build.
from 'lib'
components.withType( ComponentWithRuntimeFile ) {
if (isOptimized()) {
from getRuntimeFile()
into standardOperatingSystem( linkTask.get().targetPlatform.get() ) + '/' +
standardArchitecture( linkTask.get().targetPlatform.get() )
from runtimeFile, {
into standardOperatingSystem( targetPlatform ) + '/' + standardArchitecture( targetPlatform )
}
}
}
}
}
library {
baseName.set( "mpw" )
linkage.set( [Linkage.STATIC, Linkage.SHARED] )
baseName.set( 'mpw' )
linkage.set( [Linkage.SHARED] )
// Reconfigure the toolchain from C++ to C.
toolChains {
withType( GccCompatibleToolChain ) {
withType( VisualCpp ) {
eachPlatform {
cppCompiler.withArguments { addAll( ["-x", "c", "-std=c11", "-Werror", "-DMPW_SODIUM=1"] ) }
cppCompiler.withArguments { addAll( ['/TC', '/MT', '/Ox', '/DMPW_SODIUM=1', '/DSODIUM_STATIC', '/DSODIUM_EXPORT='] ) }
}
}
withType( GccCompatibleToolChain ) {
eachPlatform {
cppCompiler.withArguments { addAll( ['-x', 'c', '-O3', '-std=c11', '-Werror', '-DMPW_SODIUM=1'] ) }
}
}
}
components.withType( CppComponent ) {
cppSource.from fileTree( dir: "src", include: "**/*.c" )
}
// Cross-compile for these host platforms.
// TODO: Cross-compiling, blocked by: https://github.com/gradle/gradle-native/issues/169 - CppLibraryPlugin.java:163
operatingSystems.set( [objects.named( OperatingSystemFamily, OperatingSystemFamily.WINDOWS ),
objects.named( OperatingSystemFamily, OperatingSystemFamily.LINUX ),
objects.named( OperatingSystemFamily, OperatingSystemFamily.MAC_OS )] )
operatingSystems.set( [objects.named( OperatingSystemFamily, OperatingSystemFamily.LINUX ),
objects.named( OperatingSystemFamily, OperatingSystemFamily.MAC_OS ),
objects.named( OperatingSystemFamily, OperatingSystemFamily.WINDOWS )] )
binaries.configureEach {
// Resolve a standard name for the platform.
def platform = standardOperatingSystem( targetPlatform )
components.withType( CppComponent ) {
cppSource.from fileTree( 'src' )
project.dependencies {
// Depend on JDK for JNI support.
add( includePathConfiguration.name,
files( new File( Jvm.current().javaHome, "include" ) ) { first().eachDir { from it } } )
privateHeaders {
// JDK for JNI support.
from files( new File( Jvm.current().javaHome, 'include' ) ) { first().eachDir { from it } }
}
// Depend on libsodium from `lib`; run `lib/bin/build_libsodium-${platform}` first.
add( includePathConfiguration.name,
files( "../../../lib/libsodium/build-${platform}~/out/include" ) )
add( linkLibraries.name,
fileTree( "../../../lib/libsodium/build-${platform}~/out/lib" ) )
binaries.configureEach {
def system = standardOperatingSystem( targetPlatform )
project.dependencies {
add( linkLibraries.name,
fileTree( "$rootDir/../lib/libsodium/build-${system}~/out/lib" ) )
}
archive.dependsOn project.tasks.maybeCreate( "build_libsodium-${system}", Exec.class ).configure {
commandLine 'bash', "$rootDir/../lib/bin/build_libsodium-${system}"
privateHeaders.from "$rootDir/../lib/libsodium/build-${system}~/out/include"
}
clean.dependsOn project.tasks.maybeCreate( "clean_libsodium-${system}", Exec.class ).configure {
commandLine 'bash', "$rootDir/../lib/bin/build_libsodium-${system}", 'clean'
}
archive.dependsOn project.tasks.maybeCreate( "build_libjson-c-${system}", Exec.class ).configure {
commandLine 'bash', "$rootDir/../lib/bin/build_libjson-c-${system}"
privateHeaders.from "$rootDir/../lib/libjson-c/build-${system}~/out/include"
}
clean.dependsOn project.tasks.maybeCreate( "clean_libjson-c-${system}", Exec.class ).configure {
commandLine 'bash', "$rootDir/../lib/bin/build_libjson-c-${system}", 'clean'
}
}
}
}
static String standardOperatingSystem(NativePlatform platform) {
OperatingSystem os = platform.getOperatingSystem()
if (os.isWindows()) {
if (os.isWindows())
return OperatingSystemFamily.WINDOWS
} else if (os.isLinux()) {
return OperatingSystemFamily.LINUX
} else if (os.isMacOsX()) {
return OperatingSystemFamily.MAC_OS
}
return os.name.toLowerCase()
else if (os.isMacOsX())
return OperatingSystemFamily.MAC_OS
else if (os.isLinux())
return OperatingSystemFamily.LINUX
// Other systems will need to use a Linux compatibility layer.
return OperatingSystemFamily.LINUX
}
static String standardArchitecture(NativePlatform platform) {
Architecture arch = platform.getArchitecture()
return arch.name.toLowerCase().replaceAll( "-", "_" )
if (arch.isArm())
return 'arm'
else if (arch.name.toLowerCase( Locale.ROOT ).startsWith( 'arm' ))
return 'arm64'
else if (arch.isAmd64())
return 'x86_64'
else if (arch.isI386())
return 'x86'
// Other systems will need to be compatible with the x86 architecture.
return 'x86'
}

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(
const char *time) {
// TODO: Support parsing timezone into tm_gmtoff
// TODO: Support for parsing non-UTC time strings
struct tm tm = { .tm_isdst = -1 };
if (time && sscanf( time, "%4d-%2d-%2dT%2d:%2d:%2dZ",
&tm.tm_year, &tm.tm_mon, &tm.tm_mday,
&tm.tm_hour, &tm.tm_min, &tm.tm_sec ) == 6) {
tm.tm_year -= 1900; // tm_year 0 = rfc3339 year 1900
tm.tm_mon -= 1; // tm_mon 0 = rfc3339 month 1
return mktime( &tm );
// mktime converts tm to local, setting tm_gmtoff; use it to offset the result back to UTC.
return mktime( &tm ) + tm.tm_gmtoff;
}
return false;

View File

@@ -206,7 +206,7 @@ static bool mpw_marshal_write_flat(
if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &site->lastUsed ) ))
mpw_string_pushf( out, "%s %8ld %lu:%lu:%lu %25s\t%25s\t%s\n",
dateString, (long)site->uses, (long)site->type, (long)site->algorithm, (long)site->counter,
loginContent?: "", site->name, content?: "" );
loginContent? loginContent: "", site->name, content? content: "" );
mpw_free_strings( &content, &loginContent, NULL );
}
mpw_free( &masterKey, MPMasterKeySize );
@@ -304,39 +304,43 @@ static bool mpw_marshal_write_json(
if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &site->lastUsed ) ))
json_object_object_add( json_site, "last_used", json_object_new_string( dateString ) );
json_object *json_site_questions = json_object_new_object();
json_object_object_add( json_site, "questions", json_site_questions );
for (size_t q = 0; q < site->questions_count; ++q) {
MPMarshalledQuestion *question = &site->questions[q];
if (!question->keyword)
continue;
if (site->questions_count) {
json_object *json_site_questions = json_object_new_object();
json_object_object_add( json_site, "questions", json_site_questions );
for (size_t q = 0; q < site->questions_count; ++q) {
MPMarshalledQuestion *question = &site->questions[q];
if (!question->keyword)
continue;
json_object *json_site_question = json_object_new_object();
json_object_object_add( json_site_questions, question->keyword, json_site_question );
json_object_object_add( json_site_question, "type", json_object_new_int( (int32_t)question->type ) );
json_object *json_site_question = json_object_new_object();
json_object_object_add( json_site_questions, question->keyword, json_site_question );
json_object_object_add( json_site_question, "type", json_object_new_int( (int32_t)question->type ) );
if (!user->redacted) {
// Clear Text
const char *answerContent = mpw_siteResult( masterKey, site->name, MPCounterValueInitial,
MPKeyPurposeRecovery, question->keyword, question->type, question->content, site->algorithm );
json_object_object_add( json_site_question, "answer", json_object_new_string( answerContent ) );
}
else {
// Redacted
if (site->type & MPSiteFeatureExportContent && question->content && strlen( question->content ))
json_object_object_add( json_site_question, "answer", json_object_new_string( question->content ) );
if (!user->redacted) {
// Clear Text
const char *answerContent = mpw_siteResult( masterKey, site->name, MPCounterValueInitial,
MPKeyPurposeRecovery, question->keyword, question->type, question->content, site->algorithm );
json_object_object_add( json_site_question, "answer", json_object_new_string( answerContent ) );
}
else {
// Redacted
if (site->type & MPSiteFeatureExportContent && question->content && strlen( question->content ))
json_object_object_add( json_site_question, "answer", json_object_new_string( question->content ) );
}
}
}
json_object *json_site_mpw = json_object_new_object();
json_object_object_add( json_site, "_ext_mpw", json_site_mpw );
if (site->url)
json_object_object_add( json_site_mpw, "url", json_object_new_string( site->url ) );
if (json_object_object_length( json_site_mpw ))
json_object_object_add( json_site, "_ext_mpw", json_site_mpw );
mpw_free_strings( &content, &loginContent, NULL );
}
mpw_string_pushf( out, "%s\n", json_object_to_json_string_ext( json_file, JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED ) );
mpw_string_pushf( out, "%s\n", json_object_to_json_string_ext( json_file,
JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_NOSLASHESCAPE ) );
mpw_free( &masterKey, MPMasterKeySize );
json_object_put( json_file );
@@ -797,23 +801,25 @@ static MPMarshalledUser *mpw_marshal_read_json(
site->loginContent = mpw_strdup( siteLoginName );
}
json_object_iter json_site_question;
json_object *json_site_questions = mpw_get_json_section( json_site.val, "questions" );
json_object_object_foreachC( json_site_questions, json_site_question ) {
MPMarshalledQuestion *question = mpw_marshal_question( site, json_site_question.key );
const char *answerContent = mpw_get_json_string( json_site_question.val, "answer", NULL );
question->type = (MPResultType)mpw_get_json_int( json_site_question.val, "type", MPResultTypeTemplatePhrase );
if (json_site_questions) {
json_object_iter json_site_question;
json_object_object_foreachC( json_site_questions, json_site_question ) {
MPMarshalledQuestion *question = mpw_marshal_question( site, json_site_question.key );
const char *answerContent = mpw_get_json_string( json_site_question.val, "answer", NULL );
question->type = (MPResultType)mpw_get_json_int( json_site_question.val, "type", MPResultTypeTemplatePhrase );
if (!user->redacted) {
// Clear Text
if (answerContent && strlen( answerContent ))
question->content = mpw_siteState( masterKey, site->name, MPCounterValueInitial,
MPKeyPurposeRecovery, question->keyword, question->type, answerContent, site->algorithm );
}
else {
// Redacted
if (answerContent && strlen( answerContent ))
question->content = mpw_strdup( answerContent );
if (!user->redacted) {
// Clear Text
if (answerContent && strlen( answerContent ))
question->content = mpw_siteState( masterKey, site->name, MPCounterValueInitial,
MPKeyPurposeRecovery, question->keyword, question->type, answerContent, site->algorithm );
}
else {
// Redacted
if (answerContent && strlen( answerContent ))
question->content = mpw_strdup( answerContent );
}
}
}
}
@@ -871,21 +877,14 @@ const MPMarshalFormat mpw_formatWithName(
if (!formatName || !strlen( formatName ))
return MPMarshalFormatNone;
// Lower-case to standardize it.
size_t stdFormatNameSize = strlen( formatName );
char stdFormatName[stdFormatNameSize + 1];
for (size_t c = 0; c < stdFormatNameSize; ++c)
stdFormatName[c] = (char)tolower( formatName[c] );
stdFormatName[stdFormatNameSize] = '\0';
if (strncmp( mpw_nameForFormat( MPMarshalFormatNone ), stdFormatName, strlen( stdFormatName ) ) == 0)
if (mpw_strncasecmp( mpw_nameForFormat( MPMarshalFormatNone ), formatName, strlen( formatName ) ) == 0)
return MPMarshalFormatNone;
if (strncmp( mpw_nameForFormat( MPMarshalFormatFlat ), stdFormatName, strlen( stdFormatName ) ) == 0)
if (mpw_strncasecmp( mpw_nameForFormat( MPMarshalFormatFlat ), formatName, strlen( formatName ) ) == 0)
return MPMarshalFormatFlat;
if (strncmp( mpw_nameForFormat( MPMarshalFormatJSON ), stdFormatName, strlen( stdFormatName ) ) == 0)
if (mpw_strncasecmp( mpw_nameForFormat( MPMarshalFormatJSON ), formatName, strlen( formatName ) ) == 0)
return MPMarshalFormatJSON;
dbg( "Not a format name: %s", stdFormatName );
dbg( "Not a format name: %s", formatName );
return (MPMarshalFormat)ERR;
}

View File

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

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);
}
const char **mpw_strings(size_t *count, const char *strings, ...) {
va_list args;
va_start( args, strings );
const char **array = NULL;
size_t size = 0;
for (const char *string = strings; string; (string = va_arg( args, const char * ))) {
size_t cursor = size / sizeof( *array );
if (!mpw_realloc( &array, &size, sizeof( string ) )) {
mpw_free( &array, size );
*count = 0;
return NULL;
}
array[cursor] = string;
}
va_end( args );
*count = size / sizeof( *array );
return array;
}
bool mpw_push_buf(uint8_t **buffer, size_t *bufferSize, const void *pushBuffer, const size_t pushSize) {
if (!buffer || !bufferSize || !pushBuffer || !pushSize)
@@ -275,14 +296,15 @@ static uint8_t const *mpw_aes(bool encrypt, const uint8_t *key, const size_t key
return NULL;
// IV = zero
uint8_t iv[AES_BLOCKLEN];
mpw_zero( iv, sizeof iv );
static uint8_t *iv = NULL;
if (!iv)
iv = calloc( AES_BLOCKLEN, sizeof( uint8_t ) );
// Add PKCS#7 padding
uint32_t aesSize = ((uint32_t)*bufSize + AES_BLOCKLEN - 1) & -AES_BLOCKLEN; // round up to block size.
if (encrypt && !(*bufSize % AES_BLOCKLEN)) // add pad block if plain text fits block size.
encrypt += AES_BLOCKLEN;
uint8_t aesBuf[aesSize];
uint8_t *aesBuf = malloc( aesSize );
memcpy( aesBuf, buf, *bufSize );
memset( aesBuf + *bufSize, aesSize - *bufSize, aesSize - *bufSize );
uint8_t *resultBuf = malloc( aesSize );
@@ -291,8 +313,7 @@ static uint8_t const *mpw_aes(bool encrypt, const uint8_t *key, const size_t key
AES_CBC_encrypt_buffer( resultBuf, aesBuf, aesSize, key, iv );
else
AES_CBC_decrypt_buffer( resultBuf, aesBuf, aesSize, key, iv );
mpw_zero( aesBuf, aesSize );
mpw_zero( iv, AES_BLOCKLEN );
mpw_free( &aesBuf, aesSize );
// Truncate PKCS#7 padding
if (encrypt)
@@ -496,3 +517,12 @@ char *mpw_strndup(const char *src, size_t max) {
return dst;
}
int mpw_strncasecmp(const char *s1, const char *s2, size_t max) {
int cmp = 0;
for (; !cmp && max-- > 0 && s1 && s2; ++s1, ++s2)
cmp = tolower( *(unsigned char *)s1 ) - tolower( *(unsigned char *)s2 );
return cmp;
}

View File

@@ -33,36 +33,36 @@ extern int mpw_verbosity;
#endif
#ifndef mpw_log
#define mpw_log(level, ...) ({ \
#define mpw_log(level, format, ...) do { \
if (mpw_verbosity >= level) { \
mpw_log_do( level, ##__VA_ARGS__ ); \
}; })
mpw_log_do( level, format, ##__VA_ARGS__ ); \
}; } while (0)
#endif
#ifndef trc
/** Logging internal state. */
#define trc_level 3
#define trc(...) mpw_log( trc_level, ##__VA_ARGS__ )
#define trc(format, ...) mpw_log( trc_level, format, ##__VA_ARGS__ )
/** Logging state and events interesting when investigating issues. */
#define dbg_level 2
#define dbg(...) mpw_log( dbg_level, ##__VA_ARGS__ )
#define dbg(format, ...) mpw_log( dbg_level, format, ##__VA_ARGS__ )
/** User messages. */
#define inf_level 1
#define inf(...) mpw_log( inf_level, ##__VA_ARGS__ )
#define inf(format, ...) mpw_log( inf_level, format, ##__VA_ARGS__ )
/** Recoverable issues and user suggestions. */
#define wrn_level 0
#define wrn(...) mpw_log( wrn_level, ##__VA_ARGS__ )
#define wrn(format, ...) mpw_log( wrn_level, format, ##__VA_ARGS__ )
/** Unrecoverable issues. */
#define err_level -1
#define err(...) mpw_log( err_level, ##__VA_ARGS__ )
#define err(format, ...) mpw_log( err_level, format, ##__VA_ARGS__ )
/** Issues that lead to abortion. */
#define ftl_level -2
#define ftl(...) mpw_log( ftl_level, ##__VA_ARGS__ )
#define ftl(format, ...) mpw_log( ftl_level, format, ##__VA_ARGS__ )
#endif
#ifndef min
@@ -98,14 +98,8 @@ void mpw_uint32(const uint32_t number, uint8_t buf[4]);
void mpw_uint64(const uint64_t number, uint8_t buf[8]);
/** Allocate a new array of _type, assign its element count to _count if not NULL and populate it with the varargs. */
#define mpw_alloc_array(_count, _type, ...) ({ \
_type stackElements[] = { __VA_ARGS__ }; \
if (_count) \
*_count = sizeof( stackElements ) / sizeof( _type ); \
_type *allocElements = malloc( sizeof( stackElements ) ); \
memcpy( allocElements, stackElements, sizeof( stackElements ) ); \
allocElements; \
})
const char **mpw_strings(
size_t *count, const char *strings, ...);
/** Push a buffer onto a buffer. reallocs the given buffer and appends the given buffer. */
bool mpw_push_buf(
@@ -124,9 +118,9 @@ bool mpw_push_int(
/** Reallocate the given buffer from the given size by adding the delta size.
* On success, the buffer size pointer will be updated to the buffer's new size
* and the buffer pointer may be updated to a new memory address.
* On failure, the buffer and pointers will remain unaffected.
* On failure, the pointers will remain unaffected.
* @param buffer A pointer to the buffer to reallocate.
* @param bufferSize A pointer to the buffer's actual size.
* @param bufferSize A pointer to the buffer's current size.
* @param deltaSize The amount to increase the buffer's size by.
* @return true if successful, false if reallocation failed.
*/
@@ -150,6 +144,20 @@ bool __mpw_free_string(
({ __typeof__(strings) _s = strings; const char *__s = *_s; (void)__s; __mpw_free_strings( (char **)_s, __VA_ARGS__ ); })
bool __mpw_free_strings(
char **strings, ...);
#ifdef _MSC_VER
#undef mpw_realloc
#define mpw_realloc(buffer, bufferSize, deltaSize) \
__mpw_realloc( (const void **)buffer, bufferSize, deltaSize )
#undef mpw_free
#define mpw_free(buffer, bufferSize) \
__mpw_free( (void **)buffer, bufferSize )
#undef mpw_free_string
#define mpw_free_string(string) \
__mpw_free_string( (char **)string )
#undef mpw_free_strings
#define mpw_free_strings(strings, ...) \
__mpw_free_strings( (char **)strings, __VA_ARGS__ )
#endif
//// Cryptographic functions.
@@ -207,5 +215,7 @@ const size_t mpw_utf8_strlen(const char *utf8String);
char *mpw_strdup(const char *src);
/** Drop-in for POSIX strndup(3). */
char *mpw_strndup(const char *src, size_t max);
/** Drop-in for POSIX strncasecmp(3). */
int mpw_strncasecmp(const char *s1, const char *s2, size_t max);
#endif // _MPW_UTIL_H

View File

@@ -20,11 +20,12 @@ dependencies {
}
processResources {
dependsOn task( type: Sync, 'copyResources', {
dependsOn task( type: Sync, 'processResources-lib', {
into new File( processResources.outputs.files.singleFile, "lib" )
dependsOn configurations.lib {
files.each { libFile ->
from zipTree( libFile )
into new File( processResources.outputs.files.singleFile, "lib" )
from( zipTree( libFile ) )
}
}
} )
@@ -33,7 +34,7 @@ processResources {
compileJava {
doLast {
ant.javah( class: 'com.lyndir.masterpassword.impl.MPAlgorithmV0',
outputFile: '../../c/core/src/mpw-jni.h',
outputFile: new File( project( ':masterpassword-core' ).projectDir, 'src/mpw-jni.h' ),
classpath: files( sourceSets.main.compileClasspath, sourceSets.main.output ).asPath )
}
}

View File

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

View File

@@ -18,12 +18,16 @@
package com.lyndir.masterpassword.impl;
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.io.ByteStreams;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.io.*;
import java.util.Locale;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
@@ -45,7 +49,8 @@ public final class Native {
try {
System.loadLibrary( name );
return;
} catch (@SuppressWarnings("ErrorNotRethrown") final UnsatisfiedLinkError ignored) {
}
catch (@SuppressWarnings("ErrorNotRethrown") final UnsatisfiedLinkError ignored) {
}
// Try to find and open a stream to the packaged library resource.
@@ -82,10 +87,26 @@ public final class Native {
@Nonnull
private static String getLibraryResource(final String library) {
String system = System.getProperty( "os.name" ).toLowerCase( Locale.ROOT );
String architecture = System.getProperty( "os.arch" ).toLowerCase( Locale.ROOT );
if ("Mac OS X".equalsIgnoreCase( system ))
String system = ifNotNullElse( System.getProperty( "os.name" ), "linux" ).toLowerCase( Locale.ROOT );
String architecture = ifNotNullElse( System.getProperty( "os.arch" ), "x86" ).toLowerCase( Locale.ROOT );
// Standardize system naming in accordance with masterpassword-core.
if (system.contains( "windows" ))
system = "windows";
else if (system.contains( "mac os x" ) || system.contains( "darwin" ) || system.contains( "osx" ))
system = "macos";
else
system = "linux";
// Standardize architecture naming in accordance with masterpassword-core.
if (ImmutableList.of( "arm", "arm-v7", "armv7", "arm32" ).contains( architecture ))
architecture = "arm";
else if (architecture.startsWith( "arm" ))
architecture = "arm64";
else if (ImmutableList.of( "x86_64", "amd64", "x64", "x86-64" ).contains( architecture ))
architecture = "x86_64";
else
architecture = "x86";
return Joiner.on( RESOURCE_SEPARATOR ).join( "", NATIVES_PATH, system, architecture, library );
}

View File

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

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

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

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
*/
public abstract class MPBasicQuestion implements MPQuestion {
public abstract class MPBasicQuestion extends Changeable implements MPQuestion {
private final String keyword;
private MPResultType type;
@@ -56,6 +56,8 @@ public abstract class MPBasicQuestion implements MPQuestion {
@Override
public void setType(final MPResultType type) {
this.type = type;
setChanged();
}
@Nonnull
@@ -70,6 +72,13 @@ public abstract class MPBasicQuestion implements MPQuestion {
@Override
public abstract MPBasicSite<?> getSite();
@Override
protected void onChanged() {
super.onChanged();
getSite().setChanged();
}
@Override
public int hashCode() {
return Objects.hashCode( getKeyword() );

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -18,14 +18,14 @@
package com.lyndir.masterpassword.model.impl;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.*;
import java.util.*;
/**
* @author lhunath, 2018-05-14
*/
@JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = MPJSONAnyObject.MPJSONEmptyValue.class)
class MPJSONAnyObject {
@JsonAnySetter
@@ -35,4 +35,21 @@ class MPJSONAnyObject {
public Map<String, Object> getAny() {
return Collections.unmodifiableMap( any );
}
@SuppressWarnings("EqualsAndHashcode")
public static class MPJSONEmptyValue {
@Override
@SuppressWarnings({ "ChainOfInstanceofChecks", "Contract" })
public boolean equals(final Object obj) {
if (obj instanceof Collection<?>)
return ((Collection<?>) obj).isEmpty();
if (obj instanceof Map<?, ?>)
return ((Map<?, ?>) obj).isEmpty();
if (obj instanceof MPJSONFile.Site.Ext)
return ((MPJSONAnyObject) obj).any.isEmpty();
return obj == null;
}
}
}

View File

@@ -20,7 +20,10 @@ package com.lyndir.masterpassword.model.impl;
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.core.util.Separators;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.CodeUtils;
@@ -37,18 +40,28 @@ import org.joda.time.Instant;
/**
* @author lhunath, 2018-04-27
*/
@SuppressFBWarnings( "URF_UNREAD_FIELD" )
@SuppressFBWarnings("URF_UNREAD_FIELD")
public class MPJSONFile extends MPJSONAnyObject {
protected static final ObjectMapper objectMapper = new ObjectMapper();
static {
objectMapper.setSerializationInclusion( JsonInclude.Include.NON_EMPTY );
objectMapper.setDefaultPrettyPrinter( new DefaultPrettyPrinter() {
private static final long serialVersionUID = 1;
@Override
public DefaultPrettyPrinter withSeparators(final Separators separators) {
super.withSeparators( separators );
_objectFieldValueSeparatorWithSpaces = separators.getObjectFieldValueSeparator() + " ";
return this;
}
} );
objectMapper.setVisibility( PropertyAccessor.FIELD, JsonAutoDetect.Visibility.NON_PRIVATE );
}
public MPJSONFile write(final MPFileUser modelUser)
throws MPKeyUnavailableException, MPAlgorithmException {
// Section: "export"
if (export == null)
export = new Export();
@@ -98,38 +111,27 @@ public class MPJSONFile extends MPJSONAnyObject {
site.uses = modelSite.getUses();
site.last_used = MPConstants.dateTimeFormatter.print( modelSite.getLastUsed() );
if (site.questions == null)
site.questions = new LinkedHashMap<>();
for (final MPFileQuestion question : modelSite.getQuestions())
site.questions.put( question.getKeyword(), new Site.Question() {
{
type = question.getType();
if (!export.redacted) {
// Clear Text
answer = question.getAnswer();
} else {
// Redacted
if (question.getType().supportsTypeFeature( MPSiteFeature.ExportContent ))
answer = question.getAnswerState();
}
}
} );
if (site._ext_mpw == null)
site._ext_mpw = new Site.Ext();
site._ext_mpw.url = modelSite.getUrl();
if (site.questions == null)
site.questions = new LinkedHashMap<>();
// for (size_t q = 0; q < site.questions_count; ++q) {
// MPMarshalledQuestion *question = &site.questions[q];
// if (!question.keyword)
// continue;
//
// json_object *json_site_question = json_object_new_object();
// json_object_object_add( json_site_questions, question.keyword, json_site_question );
// json_object_object_add( json_site_question, "type = question.type;
//
// if (!user.redacted) {
// // Clear Text
// const char *answerContent = mpw_siteResult( masterKey, site.name, MPCounterValueInitial,
// MPKeyPurposeRecovery, question.keyword, question.type, question.content, site.algorithm );
// json_object_object_add( json_site_question, "answer = answerContent;
// }
// else {
// // Redacted
// if (site.type & MPSiteFeatureExportContent && question.content && strlen( question.content ))
// json_object_object_add( json_site_question, "answer = question.content;
// }
// }
// json_object *json_site_mpw = json_object_new_object();
// fileSite._ext_mpw = json_site_mpw;
// if (site.url)
// json_object_object_add( json_site_mpw, "url", site.url );
}
return this;
@@ -143,6 +145,7 @@ public class MPJSONFile extends MPJSONAnyObject {
(user.default_type != null)? user.default_type: algorithm.mpw_default_result_type(),
(user.last_used != null)? MPConstants.dateTimeFormatter.parseDateTime( user.last_used ): new Instant(),
MPMarshalFormat.JSON, export.redacted? MPMarshaller.ContentMode.PROTECTED: MPMarshaller.ContentMode.VISIBLE );
model.beginChanges();
model.setJSON( this );
if (masterPassword != null)
model.authenticate( masterPassword );
@@ -167,6 +170,7 @@ public class MPJSONFile extends MPJSONAnyObject {
model.addSite( site );
}
model.endChanges();
return model;
}
@@ -193,26 +197,26 @@ public class MPJSONFile extends MPJSONAnyObject {
String full_name;
String last_used;
@Nullable
MPAlgorithm.Version algorithm;
@Nullable
String key_id;
@Nullable
MPAlgorithm.Version algorithm;
@Nullable
MPResultType default_type;
}
public static class Site extends MPJSONAnyObject {
@Nullable
MPResultType type;
long counter;
MPAlgorithm.Version algorithm;
@Nullable
MPResultType type;
@Nullable
String password;
@Nullable
MPResultType login_type;
@Nullable
String login_name;
@Nullable
MPResultType login_type;
int uses;
@Nullable

View File

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

View File

@@ -7,13 +7,11 @@ description = 'Master Password Test Suite'
dependencies {
implementation group: 'com.lyndir.lhunath.opal', name: 'opal-system', version: '1.7-p2'
compile project( ':masterpassword-algorithm' )
compile project( ':masterpassword-model' )
implementation project( ':masterpassword-algorithm' )
implementation project( ':masterpassword-model' )
testCompile group: 'org.testng', name: 'testng', version: '6.8.5'
testCompile group: 'ch.qos.logback', name: 'logback-classic', version: '1.1.2'
testImplementation group: 'org.testng', name: 'testng', version: '6.8.5'
testImplementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.1.2'
}
//tasks.withType(Test) {
// systemProperty "java.library.path", "/Users/lhunath/Documents/workspace/lyndir/MasterPassword/core/java/algorithm/build/libs/mpw/shared"
//}
test.useTestNG()

View File

@@ -12,7 +12,7 @@
<body>
<header>
<p><strong>BETA</strong> - This site may not yet work on your browser.<br>
Passwords are generated locally, your master password is not sent to any server. See <a href="https://github.com/Lyndir/MasterPassword/tree/master/platform-independent/web-js">the source</a>.</p>
Passwords are generated locally, your master password is not sent to any server. See <a href="https://gitlab.com/MasterPassword/MasterPassword/tree/master/platform-independent/web">the source</a>.</p>
<span id="error"></span>
</header>
<section id="identity" class="active"><form action="#">