2
0

Compare commits

..

16 Commits

Author SHA1 Message Date
Maarten Billemont
c26281e3b7 2.7-java-4 2018-08-02 12:20:45 -04:00
Maarten Billemont
f0b1f0c9e0 Build for older glibc. 2018-08-02 12:19:49 -04:00
Maarten Billemont
9682efc7c9 Release masterpassword-gui-2.7.3 2018-08-02 01:44:50 -04:00
Maarten Billemont
1264cad377 2.7-java-3 2018-08-02 01:37:37 -04:00
Maarten Billemont
d185a0af14 Add mpw native binary for windows 32-bit. 2018-08-02 01:37:10 -04:00
Maarten Billemont
4275a6cc61 Fix build on Windows. 2018-08-02 01:32:55 -04:00
Maarten Billemont
c94ff429e8 Switch linux build of libmpw to debian for glibc instead of musl libc. 2018-08-01 20:13:42 -04:00
Maarten Billemont
00744cb264 Statically link the mpw library. 2018-08-01 14:20:47 -04:00
Maarten Billemont
7202fe6d1d Bump site for release of masterpassword-gui-2.7.2.jar 2018-07-31 15:35:57 -04:00
Maarten Billemont
63b4d9cd2e 2.7-java-2 2018-07-31 15:32:13 -04:00
Maarten Billemont
36a7c7f423 Clean up iconifying on copy. 2018-07-31 15:31:47 -04:00
Maarten Billemont
c2c4fb18bf Help improvements. 2018-07-31 15:16:33 -04:00
Maarten Billemont
3fc8acba70 Global hotkey, iconifying and application activation, help text. 2018-07-31 14:55:19 -04:00
Maarten Billemont
f5c0c4d787 Fix offsetting local time back to UTC. 2018-07-31 12:44:49 -04:00
Maarten Billemont
86775f1c75 Standardize epoch time calculation. 2018-07-31 09:27:41 -04:00
Maarten Billemont
2bb190f49a Bump site for masterpassword-gui-2.7.1 release. 2018-07-29 15:38:54 -04:00
33 changed files with 255 additions and 73 deletions

View File

@@ -1,9 +1,12 @@
FROM alpine FROM debian:stable-slim
# For i386 # For i386
#FROM i386/alpine #FROM i386/debian:stable-slim
#ENTRYPOINT ["linux32", "--"] #ENTRYPOINT ["linux32", "--"]
RUN apk add --no-cache git libtool automake autoconf make g++ bash openjdk8 # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=863199
RUN mkdir -p /usr/share/man/man1
RUN apt-get update && apt-get install -y default-jdk-headless git-core bash libtool automake autoconf make g++
RUN git clone --depth=3 $(: --shallow-submodules) --recurse-submodules https://gitlab.com/MasterPassword/MasterPassword.git /mpw RUN git clone --depth=3 $(: --shallow-submodules) --recurse-submodules https://gitlab.com/MasterPassword/MasterPassword.git /mpw
RUN cd /mpw/gradle && ./gradlew -i clean build RUN cd /mpw/gradle && ./gradlew -i clean build

View File

@@ -2,7 +2,7 @@ allprojects {
apply plugin: 'findbugs' apply plugin: 'findbugs'
group = 'com.lyndir.masterpassword' group = 'com.lyndir.masterpassword'
version = '2.7.1' version = '2.7.4'
tasks.withType( JavaCompile ) { tasks.withType( JavaCompile ) {
options.encoding = 'UTF-8' options.encoding = 'UTF-8'

View File

@@ -32,8 +32,15 @@ PATH+=:/usr/local/bin
needs() { _needs "$@"; } needs() { _needs "$@"; }
_needs() { _needs() {
local failed=0 local failed=0
for tool; do for spec; do
hash "$tool" || { echo >&2 "Missing: $tool. Please install this tool."; (( failed++ )); } IFS=: read pkg tools <<< "$spec"
IFS=, read -a tools <<< "${tools:-$pkg}"
for tool in "${tools[@]}"; do
hash "$tool" && continue 2
done
echo >&2 "Missing: $pkg. Please install this package."
(( failed++ ))
done done
return $failed return $failed
@@ -51,7 +58,7 @@ _initialize() {
# #
# Check if all tools needed for the default implementations are available. # Check if all tools needed for the default implementations are available.
# #
# By default, this will check for `libtool` (for libtoolize), `automake` (for aclocal) and `autoconf` (for autoreconf). # By default, this will check for `libtool` (for libtoolize), `automake` (for aclocal), `autoconf` (for autoreconf) and make.
initialize_needs() { _initialize_needs "$@"; } initialize_needs() { _initialize_needs "$@"; }
_initialize_needs() { _initialize_needs() {
if [[ $platform = windows ]]; then if [[ $platform = windows ]]; then
@@ -59,7 +66,7 @@ _initialize_needs() {
export VSINSTALLDIR="${VSINSTALLDIR:-$(cd "$(cygpath -F 0x002a)/Microsoft Visual Studio"/*/*/Common7/.. && pwd)}" 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; } [[ -e "$VSINSTALLDIR/Common7/Tools/VsMSBuildCmd.bat" ]] || { echo >&2 "Missing: msbuild. Please install 'Build Tools for Visual Studio'."; return 1; }
else else
needs libtool automake autoconf needs libtool:libtoolize,glibtoolize automake autoconf make
fi fi
} }

View File

@@ -1,4 +1,13 @@
#!/usr/bin/env bash #!/usr/bin/env bash
source "${BASH_SOURCE%/*}/build_lib" source "${BASH_SOURCE%/*}/build_lib"
finalize_merge() {
local prefix=$1 platform=$2; shift 2
local archs=( "$@" )
cp -a "src/libsodium/include" "$prefix/out"
_finalize_merge "$prefix" "$platform" "${archs[@]}"
}
build libsodium windows build libsodium windows

View File

@@ -35,7 +35,7 @@ library {
} }
withType( GccCompatibleToolChain ) { withType( GccCompatibleToolChain ) {
eachPlatform { eachPlatform {
cppCompiler.withArguments { addAll( ['-x', 'c', '-O3', '-std=c11', '-Werror', '-DMPW_SODIUM=1'] ) } cppCompiler.withArguments { addAll( ['-x', 'c', '-O3', '-Werror', '-DMPW_SODIUM=1'] ) }
} }
} }
} }
@@ -69,14 +69,14 @@ library {
} }
// libjson-c // libjson-c
archive.dependsOn project.tasks.maybeCreate( "build_libjson-c-${system}", Exec ).configure { /*archive.dependsOn project.tasks.maybeCreate( "build_libjson-c-${system}", Exec ).configure {
commandLine 'bash', "$rootDir/../lib/bin/build_libjson-c-${system}" commandLine 'bash', "$rootDir/../lib/bin/build_libjson-c-${system}"
privateHeaders.from "$rootDir/../lib/libjson-c/build-${system}~/out/include" privateHeaders.from "$rootDir/../lib/libjson-c/build-${system}~/out/include"
add( linkLibraries.name, fileTree( "$rootDir/../lib/libjson-c/build-${system}~/out/lib" ) ) add( linkLibraries.name, fileTree( "$rootDir/../lib/libjson-c/build-${system}~/out/lib" ) )
} }
clean.dependsOn project.tasks.maybeCreate( "clean_libjson-c-${system}", Exec ).configure { clean.dependsOn project.tasks.maybeCreate( "clean_libjson-c-${system}", Exec ).configure {
commandLine 'bash', "$rootDir/../lib/bin/build_libjson-c-${system}", 'clean' commandLine 'bash', "$rootDir/../lib/bin/build_libjson-c-${system}", 'clean'
} }*/
} }
} }
} }

Binary file not shown.

View File

@@ -7,7 +7,7 @@
// TODO: We may need to zero the jbytes safely. // TODO: We may need to zero the jbytes safely.
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env; JNIEnv* env;
if ((*vm)->GetEnv( vm, (void **)&env, JNI_VERSION_1_6 ) != JNI_OK) if ((*vm)->GetEnv( vm, (void **)&env, JNI_VERSION_1_6 ) != JNI_OK)
return -1; return -1;

View File

@@ -35,10 +35,10 @@ char *mpw_get_token(const char **in, const char *eol, char *delim) {
return token; return token;
} }
time_t mpw_mktime( time_t mpw_timegm(const char *time) {
const char *time) {
// TODO: Support for parsing non-UTC time strings // TODO: Support for parsing non-UTC time strings
// Parse time as a UTC timestamp, into a tm.
struct tm tm = { .tm_isdst = -1 }; struct tm tm = { .tm_isdst = -1 };
if (time && sscanf( time, "%4d-%2d-%2dT%2d:%2d:%2dZ", if (time && sscanf( time, "%4d-%2d-%2dT%2d:%2d:%2dZ",
&tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
@@ -46,8 +46,10 @@ time_t mpw_mktime(
tm.tm_year -= 1900; // tm_year 0 = rfc3339 year 1900 tm.tm_year -= 1900; // tm_year 0 = rfc3339 year 1900
tm.tm_mon -= 1; // tm_mon 0 = rfc3339 month 1 tm.tm_mon -= 1; // tm_mon 0 = rfc3339 month 1
// mktime converts tm to local, setting tm_gmtoff; use it to offset the result back to UTC. // mktime interprets tm as being local, we need to offset back to UTC (timegm/tm_gmtoff are non-standard).
return mktime( &tm ) + tm.tm_gmtoff; time_t local_time = mktime( &tm ), local_dst = tm.tm_isdst > 0? 3600: 0;
time_t gmtoff = local_time + local_dst - mktime( gmtime( &local_time ) );
return local_time + gmtoff;
} }
return false; return false;

View File

@@ -34,7 +34,7 @@
char *mpw_get_token( char *mpw_get_token(
const char **in, const char *eol, char *delim); const char **in, const char *eol, char *delim);
/** Convert an RFC 3339 time string into epoch time. */ /** Convert an RFC 3339 time string into epoch time. */
time_t mpw_mktime( time_t mpw_timegm(
const char *time); const char *time);
/// JSON parsing. /// JSON parsing.

View File

@@ -407,7 +407,7 @@ static void mpw_marshal_read_flat_info(
if (strcmp( headerName, "Passwords" ) == 0) if (strcmp( headerName, "Passwords" ) == 0)
info->redacted = strcmp( headerValue, "VISIBLE" ) != 0; info->redacted = strcmp( headerValue, "VISIBLE" ) != 0;
if (strcmp( headerName, "Date" ) == 0) if (strcmp( headerName, "Date" ) == 0)
info->date = mpw_mktime( headerValue ); info->date = mpw_timegm( headerValue );
mpw_free_strings( &headerName, &headerValue, NULL ); mpw_free_strings( &headerName, &headerValue, NULL );
continue; continue;
@@ -580,7 +580,7 @@ static MPMarshalledUser *mpw_marshal_read_flat(
return NULL; return NULL;
} }
MPAlgorithmVersion siteAlgorithm = (MPAlgorithmVersion)value; MPAlgorithmVersion siteAlgorithm = (MPAlgorithmVersion)value;
time_t siteLastUsed = mpw_mktime( str_lastUsed ); time_t siteLastUsed = mpw_timegm( str_lastUsed );
if (!siteLastUsed) { if (!siteLastUsed) {
*error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site last used: %s: %s", siteName, str_lastUsed ) }; *error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site last used: %s: %s", siteName, str_lastUsed ) };
return NULL; return NULL;
@@ -650,7 +650,7 @@ static void mpw_marshal_read_json_info(
if (fileFormat < 1) if (fileFormat < 1)
return; return;
info->redacted = mpw_get_json_boolean( json_file, "export.redacted", true ); info->redacted = mpw_get_json_boolean( json_file, "export.redacted", true );
info->date = mpw_mktime( mpw_get_json_string( json_file, "export.date", NULL ) ); info->date = mpw_timegm( mpw_get_json_string( json_file, "export.date", NULL ) );
// Section: "user" // Section: "user"
info->algorithm = (MPAlgorithmVersion)mpw_get_json_int( json_file, "user.algorithm", MPAlgorithmVersionCurrent ); info->algorithm = (MPAlgorithmVersion)mpw_get_json_int( json_file, "user.algorithm", MPAlgorithmVersionCurrent );
@@ -707,7 +707,7 @@ static MPMarshalledUser *mpw_marshal_read_json(
*error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user default type: %u", defaultType ) }; *error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user default type: %u", defaultType ) };
return NULL; return NULL;
} }
time_t lastUsed = mpw_mktime( str_lastUsed ); time_t lastUsed = mpw_timegm( str_lastUsed );
if (!lastUsed) { if (!lastUsed) {
*error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user last used: %s", str_lastUsed ) }; *error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user last used: %s", str_lastUsed ) };
return NULL; return NULL;
@@ -760,7 +760,7 @@ static MPMarshalledUser *mpw_marshal_read_json(
MPResultType siteLoginType = (MPResultType)mpw_get_json_int( json_site.val, "login_type", MPResultTypeTemplateName ); MPResultType siteLoginType = (MPResultType)mpw_get_json_int( json_site.val, "login_type", MPResultTypeTemplateName );
unsigned int siteUses = (unsigned int)mpw_get_json_int( json_site.val, "uses", 0 ); unsigned int siteUses = (unsigned int)mpw_get_json_int( json_site.val, "uses", 0 );
str_lastUsed = mpw_get_json_string( json_site.val, "last_used", NULL ); str_lastUsed = mpw_get_json_string( json_site.val, "last_used", NULL );
time_t siteLastUsed = mpw_mktime( str_lastUsed ); time_t siteLastUsed = mpw_timegm( str_lastUsed );
if (!siteLastUsed) { if (!siteLastUsed) {
*error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site last used: %s: %s", siteName, str_lastUsed ) }; *error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site last used: %s: %s", siteName, str_lastUsed ) };
return NULL; return NULL;

View File

@@ -19,6 +19,14 @@ public final class Utilities {
return consumer.apply( value ); return consumer.apply( value );
} }
@Nonnull
public static <T> T ifNotNullElse(@Nullable final T value, @Nonnull final T nullValue) {
if (value == null)
return nullValue;
return value;
}
@Nonnull @Nonnull
public static <T, R> R ifNotNullElse(@Nullable final T value, final Function<T, R> consumer, @Nonnull final R nullValue) { public static <T, R> R ifNotNullElse(@Nullable final T value, final Function<T, R> consumer, @Nonnull final R nullValue) {
if (value == null) if (value == null)

View File

@@ -11,6 +11,7 @@ dependencies {
implementation group: 'com.lyndir.lhunath.opal', name: 'opal-system', version: '1.7-p2' implementation group: 'com.lyndir.lhunath.opal', name: 'opal-system', version: '1.7-p2'
implementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.1.2' implementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.1.2'
implementation group: 'com.yuvimasory', name: 'orange-extensions', version: '1.3.0' implementation group: 'com.yuvimasory', name: 'orange-extensions', version: '1.3.0'
implementation group: 'com.github.tulskiy', name: 'jkeymaster', version: '1.2'
compile project( ':masterpassword-model' ) compile project( ':masterpassword-model' )
} }

View File

@@ -19,7 +19,7 @@
package com.lyndir.masterpassword.gui; package com.lyndir.masterpassword.gui;
import com.lyndir.lhunath.opal.system.util.ConversionUtils; import com.lyndir.lhunath.opal.system.util.ConversionUtils;
import com.lyndir.masterpassword.model.MPConstants; import com.lyndir.masterpassword.model.MPModelConstants;
/** /**
@@ -35,6 +35,6 @@ public class Config {
} }
public boolean checkForUpdates() { public boolean checkForUpdates() {
return ConversionUtils.toBoolean( System.getenv( MPConstants.env_checkUpdates ) ).orElse( true ); return ConversionUtils.toBoolean( System.getenv( MPModelConstants.env_checkUpdates ) ).orElse( true );
} }
} }

View File

@@ -4,6 +4,8 @@ import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.masterpassword.gui.util.Platform; import com.lyndir.masterpassword.gui.util.Platform;
import com.lyndir.masterpassword.gui.util.Res; import com.lyndir.masterpassword.gui.util.Res;
import com.lyndir.masterpassword.gui.view.MasterPasswordFrame; import com.lyndir.masterpassword.gui.view.MasterPasswordFrame;
import com.tulskiy.keymaster.common.Provider;
import java.awt.*;
/** /**
@@ -18,9 +20,17 @@ public class GUI {
public GUI() { public GUI() {
Platform.get().installAppForegroundHandler( this::open ); Platform.get().installAppForegroundHandler( this::open );
Platform.get().installAppReopenHandler( this::open ); Platform.get().installAppReopenHandler( this::open );
Provider.getCurrentProvider( true ).register( MPGuiConstants.ui_hotkey, hotKey -> open() );
} }
public void open() { public void open() {
Res.ui( () -> frame.setVisible( true ) ); Res.ui( () -> {
frame.setAlwaysOnTop( true );
frame.setVisible( true );
frame.setExtendedState( Frame.NORMAL );
frame.setAlwaysOnTop( false );
Platform.get().requestForeground();
} );
} }
} }

View File

@@ -0,0 +1,14 @@
package com.lyndir.masterpassword.gui;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import javax.swing.*;
/**
* @author lhunath, 2018-07-31
*/
public final class MPGuiConstants {
public static final KeyStroke ui_hotkey = KeyStroke.getKeyStroke( KeyEvent.VK_P, InputEvent.CTRL_DOWN_MASK | InputEvent.META_DOWN_MASK );
}

View File

@@ -24,6 +24,7 @@ import com.google.common.base.Charsets;
import com.google.common.io.ByteSource; import com.google.common.io.ByteSource;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.lhunath.opal.system.util.ObjectUtils; import com.lyndir.lhunath.opal.system.util.ObjectUtils;
import com.lyndir.masterpassword.gui.util.Components;
import com.lyndir.masterpassword.model.MPUser; import com.lyndir.masterpassword.model.MPUser;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@@ -72,9 +73,14 @@ public final class MasterPassword {
listener.onUserSelected( activeUser ); listener.onUserSelected( activeUser );
} }
private static void checkUpdate() { @Nullable
public String version() {
return MasterPassword.class.getPackage().getImplementationVersion();
}
public void checkUpdate() {
try { try {
String implementationVersion = MasterPassword.class.getPackage().getImplementationVersion(); String implementationVersion = version();
String latestVersion = new ByteSource() { String latestVersion = new ByteSource() {
@Override @Override
public InputStream openStream() public InputStream openStream()
@@ -86,16 +92,14 @@ public final class MasterPassword {
} }
}.asCharSource( Charsets.UTF_8 ).readFirstLine(); }.asCharSource( Charsets.UTF_8 ).readFirstLine();
if ((implementationVersion != null) && (latestVersion != null) && if ((implementationVersion != null) && !implementationVersion.equalsIgnoreCase( latestVersion )) {
!implementationVersion.equalsIgnoreCase( latestVersion )) {
logger.inf( "Implementation: <%s>", implementationVersion ); logger.inf( "Implementation: <%s>", implementationVersion );
logger.inf( "Latest : <%s>", latestVersion ); logger.inf( "Latest : <%s>", latestVersion );
logger.wrn( "You are not running the current official version. Please update from:%n%s", logger.wrn( "You are not running the current official version. Please update from:%n%s",
"https://masterpassword.app/masterpassword-gui.jar" ); "https://masterpassword.app/masterpassword-gui.jar" );
JOptionPane.showMessageDialog( null, JOptionPane.showMessageDialog( null, Components.linkLabel( strf(
strf( "A new version of Master Password is available.%n " "A new version of Master Password is available."
+ "Please download the latest version from %s", + "<p>Please download the latest version from <a href='https://masterpassword.app'>https://masterpassword.app</a>." ) ),
"https://masterpassword.app" ),
"Update Available", JOptionPane.INFORMATION_MESSAGE ); "Update Available", JOptionPane.INFORMATION_MESSAGE );
} }
} }
@@ -105,7 +109,7 @@ public final class MasterPassword {
} }
public static void main(final String... args) { public static void main(final String... args) {
// Thread.setDefaultUncaughtExceptionHandler( //Thread.setDefaultUncaughtExceptionHandler(
// (t, e) -> logger.bug( e, "Uncaught: %s", e.getLocalizedMessage() ) ); // (t, e) -> logger.bug( e, "Uncaught: %s", e.getLocalizedMessage() ) );
// Try and set the system look & feel, if available. // Try and set the system look & feel, if available.
@@ -115,15 +119,16 @@ public final class MasterPassword {
catch (final UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException ignored) { catch (final UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException ignored) {
} }
// Check online to see if this version has been superseded.
if (Config.get().checkForUpdates())
checkUpdate();
// Create a platform-specific GUI and open it. // Create a platform-specific GUI and open it.
new GUI().open(); new GUI().open();
// Check online to see if this version has been superseded.
if (Config.get().checkForUpdates())
get().checkUpdate();
} }
public interface Listener { public interface Listener {
void onUserSelected(@Nullable MPUser<?> user); void onUserSelected(@Nullable MPUser<?> user);
} }
} }

View File

@@ -18,10 +18,12 @@
package com.lyndir.masterpassword.gui.util; package com.lyndir.masterpassword.gui.util;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.awt.*; import java.awt.*;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.io.File; import java.io.File;
import java.net.URISyntaxException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.function.Consumer; import java.util.function.Consumer;
@@ -30,9 +32,9 @@ import javax.annotation.Nullable;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.Border; import javax.swing.border.Border;
import javax.swing.border.CompoundBorder; import javax.swing.border.CompoundBorder;
import javax.swing.event.DocumentEvent; import javax.swing.event.*;
import javax.swing.event.DocumentListener;
import javax.swing.text.DefaultFormatterFactory; import javax.swing.text.DefaultFormatterFactory;
import org.jetbrains.annotations.NonNls;
/** /**
@@ -41,6 +43,8 @@ import javax.swing.text.DefaultFormatterFactory;
@SuppressWarnings({ "SerializableStoresNonSerializable", "serial" }) @SuppressWarnings({ "SerializableStoresNonSerializable", "serial" })
public abstract class Components { public abstract class Components {
private static final Logger logger = Logger.get( Components.class );
public static final float TEXT_SIZE_HEADING = 19f; public static final float TEXT_SIZE_HEADING = 19f;
public static final float TEXT_SIZE_CONTROL = 13f; public static final float TEXT_SIZE_CONTROL = 13f;
public static final int SIZE_MARGIN = 12; public static final int SIZE_MARGIN = 12;
@@ -100,11 +104,11 @@ public abstract class Components {
showDialog( dialog ); showDialog( dialog );
Object selectedValue = pane.getValue(); Object selectedValue = pane.getValue();
if(selectedValue == null) if (selectedValue == null)
return JOptionPane.CLOSED_OPTION; return JOptionPane.CLOSED_OPTION;
Object[] options = pane.getOptions(); Object[] options = pane.getOptions();
if(options == null) if (options == null)
return (selectedValue instanceof Integer)? (Integer) selectedValue: JOptionPane.CLOSED_OPTION; return (selectedValue instanceof Integer)? (Integer) selectedValue: JOptionPane.CLOSED_OPTION;
int option = Arrays.binarySearch( options, selectedValue ); int option = Arrays.binarySearch( options, selectedValue );
@@ -337,7 +341,7 @@ public abstract class Components {
BorderFactory.createEmptyBorder( 4, 4, 4, 4 ) ); BorderFactory.createEmptyBorder( 4, 4, 4, 4 ) );
DefaultFormatterFactory formatterFactory = new DefaultFormatterFactory(); DefaultFormatterFactory formatterFactory = new DefaultFormatterFactory();
if (model instanceof UnsignedIntegerModel) if (model instanceof UnsignedIntegerModel)
formatterFactory.setDefaultFormatter( ((UnsignedIntegerModel)model).getFormatter() ); formatterFactory.setDefaultFormatter( ((UnsignedIntegerModel) model).getFormatter() );
((DefaultEditor) getEditor()).getTextField().setFormatterFactory( formatterFactory ); ((DefaultEditor) getEditor()).getTextField().setFormatterFactory( formatterFactory );
((DefaultEditor) getEditor()).getTextField().setBorder( editorBorder ); ((DefaultEditor) getEditor()).getTextField().setBorder( editorBorder );
setAlignmentX( LEFT_ALIGNMENT ); setAlignmentX( LEFT_ALIGNMENT );
@@ -474,6 +478,24 @@ public abstract class Components {
}; };
} }
public static JEditorPane linkLabel(@NonNls final String html) {
return new JEditorPane( "text/html", "<html><body style='width:640;font-family:sans-serif'>" + html ) {
{
setOpaque( false );
setEditable( false );
addHyperlinkListener( event -> {
if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED)
try {
Platform.get().open( event.getURL().toURI() );
}
catch (final URISyntaxException e) {
logger.err( e, "After triggering hyperlink: %s", event );
}
} );
}
};
}
public static class GradientPanel extends JPanel { public static class GradientPanel extends JPanel {
@Nullable @Nullable

View File

@@ -138,6 +138,10 @@ public abstract class Res {
return icon( "media/icon_import.png" ); return icon( "media/icon_import.png" );
} }
public Icon help() {
return icon( "media/icon_help.png" );
}
public Icon export() { public Icon export() {
return icon( "media/icon_export.png" ); return icon( "media/icon_export.png" );
} }

View File

@@ -3,9 +3,9 @@ package com.lyndir.masterpassword.gui.util.platform;
import com.apple.eawt.*; import com.apple.eawt.*;
import com.apple.eio.FileManager; import com.apple.eio.FileManager;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.base.Throwables; import com.lyndir.lhunath.opal.system.logging.Logger;
import java.io.File; import java.io.*;
import java.io.FileNotFoundException; import java.net.URI;
/** /**
@@ -13,7 +13,8 @@ import java.io.FileNotFoundException;
*/ */
public class ApplePlatform implements IPlatform { public class ApplePlatform implements IPlatform {
static Application application = Preconditions.checkNotNull( private static final Logger logger = Logger.get( ApplePlatform.class );
private static final Application application = Preconditions.checkNotNull(
Application.getApplication(), "Not an Apple Java application." ); Application.getApplication(), "Not an Apple Java application." );
@Override @Override
@@ -37,12 +38,31 @@ public class ApplePlatform implements IPlatform {
return true; return true;
} }
@Override
public boolean requestForeground() {
application.requestForeground( true );
return true;
}
@Override @Override
public boolean show(final File file) { public boolean show(final File file) {
try { try {
return FileManager.revealInFinder( file ); return FileManager.revealInFinder( file );
} }
catch (final FileNotFoundException ignored) { catch (final FileNotFoundException e) {
logger.err( e, "While showing: %s", file );
return false;
}
}
@Override
public boolean open(final URI url) {
try {
FileManager.openURL( url.toString() );
return true;
}
catch (final IOException e) {
logger.err( e, "While opening: %s", url );
return false; return false;
} }
} }

View File

@@ -1,6 +1,7 @@
package com.lyndir.masterpassword.gui.util.platform; package com.lyndir.masterpassword.gui.util.platform;
import java.io.File; import java.io.File;
import java.net.URI;
/** /**
@@ -18,8 +19,18 @@ public class BasePlatform implements IPlatform {
return false; return false;
} }
@Override
public boolean requestForeground() {
return false;
}
@Override @Override
public boolean show(final File file) { public boolean show(final File file) {
return false; return false;
} }
@Override
public boolean open(final URI url) {
return false;
}
} }

View File

@@ -1,6 +1,8 @@
package com.lyndir.masterpassword.gui.util.platform; package com.lyndir.masterpassword.gui.util.platform;
import java.io.File; import java.io.File;
import java.net.URI;
import java.net.URL;
/** /**
@@ -12,5 +14,9 @@ public interface IPlatform {
boolean installAppReopenHandler(Runnable handler); boolean installAppReopenHandler(Runnable handler);
boolean requestForeground();
boolean show(File file); boolean show(File file);
boolean open(URI url);
} }

View File

@@ -1,8 +1,11 @@
package com.lyndir.masterpassword.gui.util.platform; package com.lyndir.masterpassword.gui.util.platform;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.awt.*; import java.awt.*;
import java.awt.desktop.*; import java.awt.desktop.*;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.net.URI;
/** /**
@@ -11,9 +14,12 @@ import java.io.File;
@SuppressWarnings("Since15") @SuppressWarnings("Since15")
public class JDK9Platform implements IPlatform { public class JDK9Platform implements IPlatform {
private static final Logger logger = Logger.get( JDK9Platform.class );
private static final Desktop desktop = Desktop.getDesktop();
@Override @Override
public boolean installAppForegroundHandler(final Runnable handler) { public boolean installAppForegroundHandler(final Runnable handler) {
Desktop.getDesktop().addAppEventListener( new AppForegroundListener() { desktop.addAppEventListener( new AppForegroundListener() {
@Override @Override
public void appRaisedToForeground(final AppForegroundEvent e) { public void appRaisedToForeground(final AppForegroundEvent e) {
handler.run(); handler.run();
@@ -28,7 +34,13 @@ public class JDK9Platform implements IPlatform {
@Override @Override
public boolean installAppReopenHandler(final Runnable handler) { public boolean installAppReopenHandler(final Runnable handler) {
Desktop.getDesktop().addAppEventListener( (AppReopenedListener) e -> handler.run() ); desktop.addAppEventListener( (AppReopenedListener) e -> handler.run() );
return true;
}
@Override
public boolean requestForeground() {
desktop.requestForeground( true );
return true; return true;
} }
@@ -37,7 +49,19 @@ public class JDK9Platform implements IPlatform {
if (!file.exists()) if (!file.exists())
return false; return false;
Desktop.getDesktop().browseFileDirectory( file ); desktop.browseFileDirectory( file );
return true; return true;
} }
@Override
public boolean open(final URI url) {
try {
desktop.browse( url );
return true;
}
catch (final IOException e) {
logger.err( e, "While opening: %s", url );
return false;
}
}
} }

View File

@@ -8,6 +8,7 @@ import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.lhunath.opal.system.util.ObjectUtils; import com.lyndir.lhunath.opal.system.util.ObjectUtils;
import com.lyndir.masterpassword.*; import com.lyndir.masterpassword.*;
import com.lyndir.masterpassword.gui.MPGuiConstants;
import com.lyndir.masterpassword.gui.MasterPassword; import com.lyndir.masterpassword.gui.MasterPassword;
import com.lyndir.masterpassword.gui.model.MPIncognitoUser; import com.lyndir.masterpassword.gui.model.MPIncognitoUser;
import com.lyndir.masterpassword.gui.model.MPNewSite; import com.lyndir.masterpassword.gui.model.MPNewSite;
@@ -49,6 +50,8 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
"Add a new user to Master Password." ); "Add a new user to Master Password." );
private final JButton importButton = Components.button( Res.icons().import_(), event -> importUser(), private final JButton importButton = Components.button( Res.icons().import_(), event -> importUser(),
"Import a user from a backup file into Master Password." ); "Import a user from a backup file into Master Password." );
private final JButton helpButton = Components.button( Res.icons().help(), event -> showHelp(),
"Show information on how to use Master Password." );
private final JPanel userToolbar = Components.panel( BoxLayout.PAGE_AXIS ); private final JPanel userToolbar = Components.panel( BoxLayout.PAGE_AXIS );
private final JPanel siteToolbar = Components.panel( BoxLayout.PAGE_AXIS ); private final JPanel siteToolbar = Components.panel( BoxLayout.PAGE_AXIS );
@@ -208,7 +211,7 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
this, strf( "<html>Couldn't read import file:<br><pre>%s</pre></html>.", e.getLocalizedMessage() ), this, strf( "<html>Couldn't read import file:<br><pre>%s</pre></html>.", e.getLocalizedMessage() ),
"Import Failed", JOptionPane.ERROR_MESSAGE ); "Import Failed", JOptionPane.ERROR_MESSAGE );
} }
catch (MPMarshalException e) { catch (final MPMarshalException e) {
logger.err( e, "While parsing user import file." ); logger.err( e, "While parsing user import file." );
JOptionPane.showMessageDialog( JOptionPane.showMessageDialog(
this, strf( "<html>Couldn't parse import file:<br><pre>%s</pre></html>.", e.getLocalizedMessage() ), this, strf( "<html>Couldn't parse import file:<br><pre>%s</pre></html>.", e.getLocalizedMessage() ),
@@ -216,6 +219,32 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
} }
} }
private void showHelp() {
JOptionPane.showMessageDialog( this, Components.linkLabel( strf(
"<h1>Master Password - v%s</h1>"
+ "<p>The primary goal of this application is to provide a reliable security solution that also "
+ "makes you independent from your computer. If you lose access to this computer or your data, "
+ "the application can regenerate all your secrets from scratch on any new device.</p>"
+ "<h2>Opening Master Password</h2>"
+ "<p>To use Master Password, simply open the application on your computer. "
+ "Once running, you can bring up the user interface at any time by pressing the keys "
+ "<strong><code>%s+%s</code></strong>."
+ "<h2>Persistence</h2>"
+ "<p>Though at the core, Master Password does not require the use of any form of data "
+ "storage, the application does remember the names of the sites you've used in the past to "
+ "make it easier for you to use them again in the future. All user information is saved in "
+ "files on your computer at the following location:<br><pre>%s</pre></p>"
+ "<p>You can read, modify, backup or place new files in this location as you see fit. "
+ "Some people even configure this location to be synced between their different computers "
+ "using services such as those provided by SpiderOak or Dropbox.</p>"
+ "<hr><p><a href='https://masterpassword.app'>https://masterpassword.app</a> — by Maarten Billemont</p>",
MasterPassword.get().version(),
InputEvent.getModifiersExText( MPGuiConstants.ui_hotkey.getModifiers() ),
KeyEvent.getKeyText( MPGuiConstants.ui_hotkey.getKeyCode() ),
MPFileUserManager.get().getPath().getAbsolutePath() ) ),
"About Master Password", JOptionPane.INFORMATION_MESSAGE );
}
private enum ContentMode { private enum ContentMode {
NO_USER, NO_USER,
AUTHENTICATE, AUTHENTICATE,
@@ -239,6 +268,8 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
userToolbar.add( addButton ); userToolbar.add( addButton );
userToolbar.add( importButton ); userToolbar.add( importButton );
userToolbar.add( Box.createGlue() );
userToolbar.add( helpButton );
add( Box.createGlue() ); add( Box.createGlue() );
add( Components.heading( "Select a user to proceed." ) ); add( Components.heading( "Select a user to proceed." ) );
@@ -275,6 +306,8 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
userToolbar.add( exportButton ); userToolbar.add( exportButton );
userToolbar.add( deleteButton ); userToolbar.add( deleteButton );
userToolbar.add( resetButton ); userToolbar.add( resetButton );
userToolbar.add( Box.createGlue() );
userToolbar.add( helpButton );
add( Components.heading( user.getFullName(), SwingConstants.CENTER ) ); add( Components.heading( user.getFullName(), SwingConstants.CENTER ) );
add( Components.strut() ); add( Components.strut() );
@@ -461,6 +494,8 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
userToolbar.add( addButton ); userToolbar.add( addButton );
userToolbar.add( userButton ); userToolbar.add( userButton );
userToolbar.add( logoutButton ); userToolbar.add( logoutButton );
userToolbar.add( Box.createGlue() );
userToolbar.add( helpButton );
siteToolbar.add( settingsButton ); siteToolbar.add( settingsButton );
siteToolbar.add( questionsButton ); siteToolbar.add( questionsButton );
@@ -615,8 +650,8 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
Res.ui( () -> { Res.ui( () -> {
Window window = SwingUtilities.windowForComponent( UserContentPanel.this ); Window window = SwingUtilities.windowForComponent( UserContentPanel.this );
if (window != null) if (window instanceof Frame)
window.dispatchEvent( new WindowEvent( window, WindowEvent.WINDOW_CLOSING ) ); ((Frame) window).setExtendedState( Frame.ICONIFIED );
} ); } );
} ); } );
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -25,7 +25,7 @@ import org.joda.time.format.ISODateTimeFormat;
/** /**
* @author lhunath, 2016-10-29 * @author lhunath, 2016-10-29
*/ */
public final class MPConstants { public final class MPModelConstants {
/* Environment */ /* Environment */
@@ -33,6 +33,7 @@ public final class MPConstants {
* mpw: default path to look for run configuration files if the platform default is not desired. * mpw: default path to look for run configuration files if the platform default is not desired.
*/ */
public static final String env_rcDir = "MPW_RCDIR"; public static final String env_rcDir = "MPW_RCDIR";
/** /**
* mpw: permit automatic update checks. * mpw: permit automatic update checks.
*/ */

View File

@@ -22,7 +22,7 @@ import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.ImmutableSortedSet;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.masterpassword.model.MPConstants; import com.lyndir.masterpassword.model.MPModelConstants;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
@@ -42,7 +42,7 @@ public class MPFileUserManager {
private static final MPFileUserManager instance; private static final MPFileUserManager instance;
static { static {
String rcDir = System.getenv( MPConstants.env_rcDir ); String rcDir = System.getenv( MPModelConstants.env_rcDir );
if (rcDir != null) if (rcDir != null)
instance = create( new File( rcDir ) ); instance = create( new File( rcDir ) );

View File

@@ -25,7 +25,7 @@ import com.google.common.base.Charsets;
import com.google.common.io.CharSink; import com.google.common.io.CharSink;
import com.lyndir.masterpassword.MPAlgorithmException; import com.lyndir.masterpassword.MPAlgorithmException;
import com.lyndir.masterpassword.MPKeyUnavailableException; import com.lyndir.masterpassword.MPKeyUnavailableException;
import com.lyndir.masterpassword.model.MPConstants; import com.lyndir.masterpassword.model.MPModelConstants;
import java.io.*; import java.io.*;
import org.joda.time.Instant; import org.joda.time.Instant;
@@ -50,7 +50,7 @@ public class MPFlatMarshaller implements MPMarshaller {
content.append( "# \n" ); content.append( "# \n" );
content.append( "##\n" ); content.append( "##\n" );
content.append( "# Format: " ).append( FORMAT ).append( '\n' ); content.append( "# Format: " ).append( FORMAT ).append( '\n' );
content.append( "# Date: " ).append( MPConstants.dateTimeFormatter.print( new Instant() ) ).append( '\n' ); content.append( "# Date: " ).append( MPModelConstants.dateTimeFormatter.print( new Instant() ) ).append( '\n' );
content.append( "# User Name: " ).append( user.getFullName() ).append( '\n' ); content.append( "# User Name: " ).append( user.getFullName() ).append( '\n' );
content.append( "# Full Name: " ).append( user.getFullName() ).append( '\n' ); content.append( "# Full Name: " ).append( user.getFullName() ).append( '\n' );
content.append( "# Avatar: " ).append( user.getAvatar() ).append( '\n' ); content.append( "# Avatar: " ).append( user.getAvatar() ).append( '\n' );
@@ -72,7 +72,7 @@ public class MPFlatMarshaller implements MPMarshaller {
} }
content.append( strf( "%s %8d %8s %25s\t%25s\t%s\n", // content.append( strf( "%s %8d %8s %25s\t%25s\t%s\n", //
MPConstants.dateTimeFormatter.print( site.getLastUsed() ), // lastUsed MPModelConstants.dateTimeFormatter.print( site.getLastUsed() ), // lastUsed
site.getUses(), // uses site.getUses(), // uses
strf( "%d:%d:%d", // strf( "%d:%d:%d", //
site.getResultType().getType(), // type site.getResultType().getType(), // type

View File

@@ -25,7 +25,7 @@ import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.lhunath.opal.system.util.ConversionUtils; import com.lyndir.lhunath.opal.system.util.ConversionUtils;
import com.lyndir.masterpassword.*; import com.lyndir.masterpassword.*;
import com.lyndir.masterpassword.model.MPConstants; import com.lyndir.masterpassword.model.MPModelConstants;
import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException; import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException;
import java.io.*; import java.io.*;
import java.util.regex.Matcher; import java.util.regex.Matcher;
@@ -157,7 +157,7 @@ public class MPFlatUnmarshaller implements MPUnmarshaller {
MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ), MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
clearContent? null: siteMatcher.group( 6 ), clearContent? null: siteMatcher.group( 6 ),
null, null, null, ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ), null, null, null, ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ),
MPConstants.dateTimeFormatter.parseDateTime( siteMatcher.group( 1 ) ).toInstant() ); MPModelConstants.dateTimeFormatter.parseDateTime( siteMatcher.group( 1 ) ).toInstant() );
if (clearContent) if (clearContent)
site.setSitePassword( site.getResultType(), siteMatcher.group( 6 ) ); site.setSitePassword( site.getResultType(), siteMatcher.group( 6 ) );
break; break;
@@ -171,7 +171,7 @@ public class MPFlatUnmarshaller implements MPUnmarshaller {
clearContent? null: siteMatcher.group( 8 ), clearContent? null: siteMatcher.group( 8 ),
MPResultType.GeneratedName, clearContent? null: siteMatcher.group( 6 ), null, MPResultType.GeneratedName, clearContent? null: siteMatcher.group( 6 ), null,
ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ), ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ),
MPConstants.dateTimeFormatter.parseDateTime( siteMatcher.group( 1 ) ).toInstant() ); MPModelConstants.dateTimeFormatter.parseDateTime( siteMatcher.group( 1 ) ).toInstant() );
if (clearContent) { if (clearContent) {
site.setSitePassword( site.getResultType(), siteMatcher.group( 8 ) ); site.setSitePassword( site.getResultType(), siteMatcher.group( 8 ) );
site.setLoginName( MPResultType.StoredPersonal, siteMatcher.group( 6 ) ); site.setLoginName( MPResultType.StoredPersonal, siteMatcher.group( 6 ) );

View File

@@ -28,7 +28,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.primitives.UnsignedInteger; import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.CodeUtils; import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.masterpassword.*; import com.lyndir.masterpassword.*;
import com.lyndir.masterpassword.model.MPConstants; import com.lyndir.masterpassword.model.MPModelConstants;
import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException; import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.File; import java.io.File;
@@ -70,13 +70,13 @@ public class MPJSONFile extends MPJSONAnyObject {
export = new Export(); export = new Export();
export.format = 1; export.format = 1;
export.redacted = modelUser.getContentMode().isRedacted(); export.redacted = modelUser.getContentMode().isRedacted();
export.date = MPConstants.dateTimeFormatter.print( new Instant() ); export.date = MPModelConstants.dateTimeFormatter.print( new Instant() );
// Section: "user" // Section: "user"
user = new User(); user = new User();
user.avatar = modelUser.getAvatar(); user.avatar = modelUser.getAvatar();
user.full_name = modelUser.getFullName(); user.full_name = modelUser.getFullName();
user.last_used = MPConstants.dateTimeFormatter.print( modelUser.getLastUsed() ); user.last_used = MPModelConstants.dateTimeFormatter.print( modelUser.getLastUsed() );
user.key_id = modelUser.exportKeyID(); user.key_id = modelUser.exportKeyID();
user.algorithm = modelUser.getAlgorithm().version(); user.algorithm = modelUser.getAlgorithm().version();
user.default_type = modelUser.getDefaultType(); user.default_type = modelUser.getDefaultType();
@@ -111,7 +111,7 @@ public class MPJSONFile extends MPJSONAnyObject {
site.login_type = modelSite.getLoginType(); site.login_type = modelSite.getLoginType();
site.uses = modelSite.getUses(); site.uses = modelSite.getUses();
site.last_used = MPConstants.dateTimeFormatter.print( modelSite.getLastUsed() ); site.last_used = MPModelConstants.dateTimeFormatter.print( modelSite.getLastUsed() );
site.questions = new LinkedHashMap<>(); site.questions = new LinkedHashMap<>();
for (final MPFileQuestion question : modelSite.getQuestions()) for (final MPFileQuestion question : modelSite.getQuestions())
@@ -141,7 +141,7 @@ public class MPJSONFile extends MPJSONAnyObject {
return new MPFileUser( return new MPFileUser(
user.full_name, CodeUtils.decodeHex( user.key_id ), algorithm, user.avatar, user.full_name, CodeUtils.decodeHex( user.key_id ), algorithm, user.avatar,
(user.default_type != null)? user.default_type: algorithm.mpw_default_result_type(), (user.default_type != null)? user.default_type: algorithm.mpw_default_result_type(),
(user.last_used != null)? MPConstants.dateTimeFormatter.parseDateTime( user.last_used ): new Instant(), (user.last_used != null)? MPModelConstants.dateTimeFormatter.parseDateTime( user.last_used ): new Instant(),
export.redacted? MPMarshaller.ContentMode.PROTECTED: MPMarshaller.ContentMode.VISIBLE, export.redacted? MPMarshaller.ContentMode.PROTECTED: MPMarshaller.ContentMode.VISIBLE,
MPMarshalFormat.JSON, file.getParentFile() MPMarshalFormat.JSON, file.getParentFile()
); );
@@ -157,7 +157,7 @@ public class MPJSONFile extends MPJSONAnyObject {
fileSite.type, export.redacted? fileSite.password: null, fileSite.type, export.redacted? fileSite.password: null,
fileSite.login_type, export.redacted? fileSite.login_name: null, fileSite.login_type, export.redacted? fileSite.login_name: null,
(fileSite._ext_mpw != null)? fileSite._ext_mpw.url: null, fileSite.uses, (fileSite._ext_mpw != null)? fileSite._ext_mpw.url: null, fileSite.uses,
(fileSite.last_used != null)? MPConstants.dateTimeFormatter.parseDateTime( fileSite.last_used ): new Instant() ); (fileSite.last_used != null)? MPModelConstants.dateTimeFormatter.parseDateTime( fileSite.last_used ): new Instant() );
if (!export.redacted) { if (!export.redacted) {
if (fileSite.password != null) if (fileSite.password != null)