2
0

Warning fixes and spotbug configuration tweaks.

This commit is contained in:
Maarten Billemont 2019-09-24 13:06:40 -04:00
parent 36692ac10d
commit fd1014926c
13 changed files with 134 additions and 100 deletions

View File

@ -1,3 +1,6 @@
import com.github.spotbugs.SpotBugsTask
buildscript { buildscript {
repositories { repositories {
google() google()
@ -27,14 +30,23 @@ subprojects {
} }
dependencies { dependencies {
spotbugsPlugins group: 'com.h3xstream.findsecbugs', name: 'findsecbugs-plugin', version: '1.9.0' spotbugsPlugins group: 'com.h3xstream.findsecbugs', name: 'findsecbugs-plugin', version: '1.9.0'
//spotbugsPlugins group: 'com.mebigfatguy.sb-contrib', name: 'sb-contrib', version: '7.4.6'
}
spotbugs {
effort 'max'
showProgress true
} }
tasks.withType( JavaCompile ) { tasks.withType( JavaCompile ) {
options.encoding = 'UTF-8' options.encoding = 'UTF-8'
sourceCompatibility = '1.8' sourceCompatibility = '1.8'
targetCompatibility = '1.8' targetCompatibility = '1.8'
options.compilerArgs << '-Xlint:unchecked'
if (it.name != JavaPlugin.COMPILE_JAVA_TASK_NAME) {
options.compilerArgs << '-Xlint:deprecation'
}
} }
tasks.withType( com.github.spotbugs.SpotBugsTask ) { tasks.withType( SpotBugsTask ) {
reports { reports {
xml.enabled = false xml.enabled = false
html.enabled = true html.enabled = true

View File

@ -24,6 +24,7 @@ import com.google.common.base.Charsets;
import com.google.common.primitives.UnsignedBytes; import com.google.common.primitives.UnsignedBytes;
import com.lyndir.lhunath.opal.system.MessageAuthenticationDigests; import com.lyndir.lhunath.opal.system.MessageAuthenticationDigests;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.nio.*; import java.nio.*;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.Arrays; import java.util.Arrays;
@ -57,6 +58,7 @@ public class MPIdenticon {
this( fullName, masterPassword.toCharArray() ); this( fullName, masterPassword.toCharArray() );
} }
@SuppressFBWarnings("CLI_CONSTANT_LIST_INDEX")
@SuppressWarnings("MethodCanBeVariableArityMethod") @SuppressWarnings("MethodCanBeVariableArityMethod")
public MPIdenticon(final String fullName, final char[] masterPassword) { public MPIdenticon(final String fullName, final char[] masterPassword) {
this.fullName = fullName; this.fullName = fullName;

View File

@ -60,17 +60,14 @@ public class MPAlgorithmV0 extends MPAlgorithm {
ByteBuffer masterPasswordBuffer = ByteBuffer.wrap( masterPasswordBytes ); ByteBuffer masterPasswordBuffer = ByteBuffer.wrap( masterPasswordBytes );
CoderResult result = encoder.encode( CharBuffer.wrap( masterPassword ), masterPasswordBuffer, true ); CoderResult result = encoder.encode( CharBuffer.wrap( masterPassword ), masterPasswordBuffer, true );
if (!result.isUnderflow()) if (result.isError())
result.throwException(); throw new IllegalStateException( result.toString() );
result = encoder.flush( masterPasswordBuffer ); result = encoder.flush( masterPasswordBuffer );
if (!result.isUnderflow()) if (result.isError())
result.throwException(); throw new IllegalStateException( result.toString() );
return _masterKey( fullName, masterPasswordBytes, version().toInt() ); return _masterKey( fullName, masterPasswordBytes, version().toInt() );
} }
catch (final CharacterCodingException e) {
throw new IllegalStateException( e );
}
finally { finally {
Arrays.fill( masterPasswordBytes, (byte) 0 ); Arrays.fill( masterPasswordBytes, (byte) 0 );
} }

View File

@ -43,7 +43,7 @@ public final class Native {
private static final char EXTENSION_SEPARATOR = '.'; private static final char EXTENSION_SEPARATOR = '.';
private static final String NATIVES_PATH = "lib"; private static final String NATIVES_PATH = "lib";
@SuppressFBWarnings("PATH_TRAVERSAL_IN") @SuppressFBWarnings({"PATH_TRAVERSAL_IN", "IOI_USE_OF_FILE_STREAM_CONSTRUCTORS", "EXS_EXCEPTION_SOFTENING_RETURN_FALSE"})
@SuppressWarnings({ "HardcodedFileSeparator", "LoadLibraryWithNonConstantString" }) @SuppressWarnings({ "HardcodedFileSeparator", "LoadLibraryWithNonConstantString" })
public static boolean load(final Class<?> context, final String name) { public static boolean load(final Class<?> context, final String name) {
@ -89,7 +89,7 @@ public final class Native {
return true; return true;
} }
catch (@SuppressWarnings("ErrorNotRethrown") final IOException | UnsatisfiedLinkError e) { catch (@SuppressWarnings("ErrorNotRethrown") final IOException | UnsatisfiedLinkError e) {
logger.dbg( e, "Couldn't load library: %s", libraryResource ); logger.wrn( e, "Couldn't load library: %s", libraryResource );
if (libraryFile != null) if (libraryFile != null)
if (libraryFile.exists() && !libraryFile.delete()) if (libraryFile.exists() && !libraryFile.delete())

View File

@ -19,6 +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.gui.util.State;
import com.lyndir.masterpassword.model.MPConfig; import com.lyndir.masterpassword.model.MPConfig;
import com.lyndir.masterpassword.model.MPModelConstants; import com.lyndir.masterpassword.model.MPModelConstants;
@ -43,7 +44,7 @@ public class MPGuiConfig extends MPConfig {
public void setCheckForUpdates(final boolean checkForUpdates) { public void setCheckForUpdates(final boolean checkForUpdates) {
this.checkForUpdates = checkForUpdates; this.checkForUpdates = checkForUpdates;
MasterPassword.get().updateCheck(); State.get().updateCheck();
setChanged(); setChanged();
} }

View File

@ -50,40 +50,14 @@ public final class MasterPassword {
private static final MasterPassword instance = new MasterPassword(); private static final MasterPassword instance = new MasterPassword();
private final Provider keyMaster = Provider.getCurrentProvider( true ); private final Provider keyMaster = Provider.getCurrentProvider( true );
private final Collection<Listener> listeners = new CopyOnWriteArraySet<>();
@Nullable @Nullable
private MasterPasswordFrame frame; private MasterPasswordFrame frame;
@Nullable
private MPUser<?> activeUser;
public static MasterPassword get() { public static MasterPassword get() {
return instance; return instance;
} }
public void addListener(final Listener listener) {
if (listeners.add( listener ))
listener.onUserSelected( activeUser );
}
public void removeListener(final Listener listener) {
listeners.remove( listener );
}
public void activateUser(final MPUser<?> user) {
if (ObjectUtils.equals( activeUser, user ))
return;
activeUser = user;
for (final Listener listener : listeners)
listener.onUserSelected( activeUser );
}
@Nullable
public String version() {
return MasterPassword.class.getPackage().getImplementationVersion();
}
public void open() { public void open() {
Res.ui( () -> { Res.ui( () -> {
if (frame == null) if (frame == null)
@ -110,43 +84,10 @@ public final class MasterPassword {
// Create and open the UI. // Create and open the UI.
get().open(); get().open();
// UI features.
get().updateResidence(); get().updateResidence();
get().updateCheck();
}
public void updateCheck() { // Background.
if (!MPGuiConfig.get().checkForUpdates()) State.get().updateCheck();
return;
try {
String implementationVersion = version();
String latestVersion = 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 ((implementationVersion != null) && !implementationVersion.equalsIgnoreCase( latestVersion )) {
logger.inf( "Implementation: <%s>", implementationVersion );
logger.inf( "Latest : <%s>", latestVersion );
logger.wrn( "You are not running the current official version. Please update from:%n%s",
"https://masterpassword.app/masterpassword-gui.jar" );
JOptionPane.showMessageDialog( null, Components.linkLabel( strf(
"A new version of Master Password is available."
+ "<p>Please download the latest version from <a href='https://masterpassword.app'>https://masterpassword.app</a>." ) ),
"Update Available", JOptionPane.INFORMATION_MESSAGE );
}
}
catch (final IOException e) {
logger.wrn( e, "Couldn't check for version update." );
}
} }
public void updateResidence() { public void updateResidence() {
@ -154,10 +95,4 @@ public final class MasterPassword {
Platform.get().installAppReopenHandler( get()::open ); Platform.get().installAppReopenHandler( get()::open );
keyMaster.register( MPGuiConstants.ui_hotkey, hotKey -> get().open() ); keyMaster.register( MPGuiConstants.ui_hotkey, hotKey -> get().open() );
} }
@SuppressWarnings("InterfaceMayBeAnnotatedFunctional")
public interface Listener {
void onUserSelected(@Nullable MPUser<?> user);
}
} }

View File

@ -22,7 +22,6 @@ import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
import com.lyndir.masterpassword.MPResultType; import com.lyndir.masterpassword.MPResultType;
import com.lyndir.masterpassword.model.impl.MPBasicQuestion; import com.lyndir.masterpassword.model.impl.MPBasicQuestion;
import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;

View File

@ -145,17 +145,15 @@ public abstract class Components {
return ((selectedFiles != null) && (selectedFiles.length > 0))? selectedFiles[0]: null; return ((selectedFiles != null) && (selectedFiles.length > 0))? selectedFiles[0]: null;
} }
public static JDialog showDialog(@Nullable final Component owner, @Nullable final String title, final Container content) { public static void showDialog(@Nullable final Component owner, @Nullable final String title, final Container content) {
JDialog dialog = new JDialog( (owner != null)? SwingUtilities.windowForComponent( owner ): null, JDialog dialog = new JDialog( (owner != null)? SwingUtilities.windowForComponent( owner ): null,
title, Dialog.ModalityType.DOCUMENT_MODAL ); title, Dialog.ModalityType.DOCUMENT_MODAL );
dialog.setMinimumSize( new Dimension( 320, 0 ) ); dialog.setMinimumSize( new Dimension( 320, 0 ) );
dialog.setLocationRelativeTo( owner ); dialog.setLocationRelativeTo( owner );
dialog.setContentPane( content ); dialog.setContentPane( content );
return showDialog( dialog );
} }
private static JDialog showDialog(final JDialog dialog) { private static void showDialog(final JDialog dialog) {
// OpenJDK does not correctly implement this setting in native code. // OpenJDK does not correctly implement this setting in native code.
dialog.getRootPane().putClientProperty( "apple.awt.documentModalSheet", Boolean.TRUE ); dialog.getRootPane().putClientProperty( "apple.awt.documentModalSheet", Boolean.TRUE );
dialog.getRootPane().putClientProperty( "Window.style", "small" ); dialog.getRootPane().putClientProperty( "Window.style", "small" );
@ -163,8 +161,6 @@ public abstract class Components {
dialog.setLocationByPlatform( true ); dialog.setLocationByPlatform( true );
dialog.setVisible( true ); dialog.setVisible( true );
return dialog;
} }
public static JTextField textField() { public static JTextField textField() {

View File

@ -226,7 +226,7 @@ public abstract class Res {
return get( Font.PLAIN, size ); return get( Font.PLAIN, size );
} }
Font get(final int style, final int size) { synchronized Font get(final int style, final int size) {
if (!registered) if (!registered)
register(); register();
@ -234,7 +234,7 @@ public abstract class Res {
} }
@SuppressFBWarnings("URLCONNECTION_SSRF_FD") @SuppressFBWarnings("URLCONNECTION_SSRF_FD")
private void register() { private synchronized void register() {
try { try {
Font font = Font.createFont( Font.TRUETYPE_FONT, Resources.getResource( resourceName ).openStream() ); Font font = Font.createFont( Font.TRUETYPE_FONT, Resources.getResource( resourceName ).openStream() );
GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont( font ); GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont( font );

View File

@ -0,0 +1,94 @@
package com.lyndir.masterpassword.gui.util;
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
import com.google.common.base.Charsets;
import com.google.common.io.ByteSource;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.lhunath.opal.system.util.ObjectUtils;
import com.lyndir.masterpassword.gui.MPGuiConfig;
import com.lyndir.masterpassword.model.MPUser;
import java.io.IOException;
import java.io.InputStream;
import java.net.*;
import java.util.Collection;
import java.util.concurrent.CopyOnWriteArraySet;
import javax.annotation.Nullable;
import javax.swing.*;
public class State {
private static final Logger logger = Logger.get( State.class );
private static final State instance = new State();
private final Collection<Listener> listeners = new CopyOnWriteArraySet<>();
@Nullable
private MPUser<?> activeUser;
public static State get() {
return instance;
}
public void addListener(final Listener listener) {
if (listeners.add( listener ))
listener.onUserSelected( activeUser );
}
public void removeListener(final Listener listener) {
listeners.remove( listener );
}
public void activateUser(final MPUser<?> user) {
if (ObjectUtils.equals( activeUser, user ))
return;
activeUser = user;
for (final Listener listener : listeners)
listener.onUserSelected( activeUser );
}
@Nullable
public String version() {
return State.class.getPackage().getImplementationVersion();
}
public void updateCheck() {
if (!MPGuiConfig.get().checkForUpdates())
return;
try {
String implementationVersion = version();
String latestVersion = 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 ((implementationVersion != null) && !implementationVersion.equalsIgnoreCase( latestVersion )) {
logger.inf( "Implementation: <%s>", implementationVersion );
logger.inf( "Latest : <%s>", latestVersion );
logger.wrn( "You are not running the current official version. Please update from:%n%s",
"https://masterpassword.app/masterpassword-gui.jar" );
JOptionPane.showMessageDialog( null, Components.linkLabel( strf(
"A new version of Master Password is available."
+ "<p>Please download the latest version from <a href='https://masterpassword.app'>https://masterpassword.app</a>." ) ),
"Update Available", JOptionPane.INFORMATION_MESSAGE );
}
}
catch (final IOException e) {
logger.wrn( e, "Couldn't check for version update." );
}
}
@SuppressWarnings("InterfaceMayBeAnnotatedFunctional")
public interface Listener {
void onUserSelected(@Nullable MPUser<?> user);
}
}

View File

@ -124,15 +124,13 @@ public class UnsignedIntegerModel extends SpinnerNumberModel implements Selectab
return new JFormattedTextField.AbstractFormatter() { return new JFormattedTextField.AbstractFormatter() {
@Override @Override
@Nullable @Nullable
public Object stringToValue(@Nullable final String text) public Object stringToValue(@Nullable final String text) {
throws ParseException {
return (text != null)? UnsignedInteger.valueOf( text ): null; return (text != null)? UnsignedInteger.valueOf( text ): null;
} }
@Override @Override
@Nullable @Nullable
public String valueToString(final Object value) public String valueToString(final Object value) {
throws ParseException {
return (value != null)? value.toString(): null; return (value != null)? value.toString(): null;
} }
}; };

View File

@ -17,13 +17,13 @@ import javax.swing.*;
* @author lhunath, 2018-07-14 * @author lhunath, 2018-07-14
*/ */
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class FilesPanel extends JPanel implements MPFileUserManager.Listener, MasterPassword.Listener { public class FilesPanel extends JPanel implements MPFileUserManager.Listener, State.Listener {
private final JButton avatarButton = Components.button( Res.icons().avatar( 0 ), event -> setAvatar(), private final JButton avatarButton = Components.button( Res.icons().avatar( 0 ), event -> setAvatar(),
"Click to change the user's avatar." ); "Click to change the user's avatar." );
private final CollectionListModel<MPUser<?>> usersModel = private final CollectionListModel<MPUser<?>> usersModel =
new CollectionListModel<MPUser<?>>( MPFileUserManager.get().getFiles() ).selection( MasterPassword.get()::activateUser ); new CollectionListModel<MPUser<?>>( MPFileUserManager.get().getFiles() ).selection( State.get()::activateUser );
protected FilesPanel() { protected FilesPanel() {
setOpaque( false ); setOpaque( false );
@ -45,7 +45,7 @@ public class FilesPanel extends JPanel implements MPFileUserManager.Listener, Ma
add( Components.comboBox( usersModel, user -> ifNotNull( user, MPUser::getFullName ) ) ); add( Components.comboBox( usersModel, user -> ifNotNull( user, MPUser::getFullName ) ) );
MPFileUserManager.get().addListener( this ); MPFileUserManager.get().addListener( this );
MasterPassword.get().addListener( this ); State.get().addListener( this );
} }
private void setAvatar() { private void setAvatar() {

View File

@ -41,7 +41,7 @@ import javax.swing.text.PlainDocument;
* @author lhunath, 2018-07-14 * @author lhunath, 2018-07-14
*/ */
@SuppressWarnings("SerializableStoresNonSerializable") @SuppressWarnings("SerializableStoresNonSerializable")
public class UserContentPanel extends JPanel implements MasterPassword.Listener, MPUser.Listener { public class UserContentPanel extends JPanel implements State.Listener, MPUser.Listener {
private static final Random random = new SecureRandom(); private static final Random random = new SecureRandom();
private static final int SIZE_RESULT = 48; private static final int SIZE_RESULT = 48;
@ -72,7 +72,7 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
setBorder( Components.marginBorder() ); setBorder( Components.marginBorder() );
showUser( null ); showUser( null );
MasterPassword.get().addListener( this ); State.get().addListener( this );
} }
protected JComponent getUserToolbar() { protected JComponent getUserToolbar() {
@ -155,9 +155,9 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
return; return;
if (incognitoField.isSelected()) if (incognitoField.isSelected())
MasterPassword.get().activateUser( new MPIncognitoUser( fullName ) ); State.get().activateUser( new MPIncognitoUser( fullName ) );
else else
MasterPassword.get().activateUser( MPFileUserManager.get().add( fullName ) ); State.get().activateUser( MPFileUserManager.get().add( fullName ) );
} }
private void importUser() { private void importUser() {
@ -200,7 +200,7 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
Res.format( importUser.getLastUsed() ) ) ))) Res.format( importUser.getLastUsed() ) ) )))
return; return;
MasterPassword.get().activateUser( MPFileUserManager.get().add( importUser ) ); State.get().activateUser( MPFileUserManager.get().add( importUser ) );
} }
catch (final MPIncorrectMasterPasswordException | MPAlgorithmException e) { catch (final MPIncorrectMasterPasswordException | MPAlgorithmException e) {
JOptionPane.showMessageDialog( JOptionPane.showMessageDialog(
@ -242,7 +242,7 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
+ "Some people even configure this location to be synced between their different computers " + "Some people even configure this location to be synced between their different computers "
+ "using services such as those provided by SpiderOak or Dropbox.</p>" + "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>", + "<hr><p><a href='https://masterpassword.app'>https://masterpassword.app</a> — by Maarten Billemont</p>",
MasterPassword.get().version(), State.get().version(),
InputEvent.getModifiersExText( MPGuiConstants.ui_hotkey.getModifiers() ), InputEvent.getModifiersExText( MPGuiConstants.ui_hotkey.getModifiers() ),
KeyEvent.getKeyText( MPGuiConstants.ui_hotkey.getKeyCode() ), KeyEvent.getKeyText( MPGuiConstants.ui_hotkey.getKeyCode() ),
MPFileUserManager.get().getPath().getAbsolutePath() ) ), MPFileUserManager.get().getPath().getAbsolutePath() ) ),