diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPConstant.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPConstant.java new file mode 100644 index 00000000..25a027fa --- /dev/null +++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPConstant.java @@ -0,0 +1,74 @@ +package com.lyndir.masterpassword; + +import com.google.common.base.Charsets; +import com.lyndir.lhunath.opal.system.MessageAuthenticationDigests; +import com.lyndir.lhunath.opal.system.MessageDigests; +import java.nio.ByteOrder; +import java.nio.charset.Charset; + + +/** + * @author lhunath, 2016-10-29 + */ +public class MPConstant { + + /* Environment */ + + /** + * mpw: default user name if one is not provided. + */ + public static final String env_userName = "MP_USERNAME"; + /** + * mpw: default site type if one is not provided. + * + * @see MPSiteType#forOption(String) + */ + public static final String env_siteType = "MP_SITETYPE"; + /** + * mpw: default site counter value if one is not provided. + */ + public static final String env_siteCounter = "MP_SITECOUNTER"; + /** + * mpw: default path to look for run configuration files if the platform default is not desired. + */ + public static final String env_rcDir = "MP_RCDIR"; + /** + * mpw: permit automatic update checks. + */ + public static final String env_checkUpdates = "MP_CHECKUPDATES"; + + /* Algorithm */ + + /** + * scrypt: CPU cost parameter. + */ + public static final int scrypt_N = 32768; + /** + * scrypt: Memory cost parameter. + */ + public static final int scrypt_r = 8; + /** + * scrypt: Parallelization parameter. + */ + public static final int scrypt_p = 2; + /** + * mpw: Master key size (byte). + */ + public static final int mpw_dkLen = 64; + /** + * mpw: Input character encoding. + */ + public static final Charset mpw_charset = Charsets.UTF_8; + /** + * mpw: Platform-agnostic byte order. + */ + public static final ByteOrder mpw_byteOrder = ByteOrder.BIG_ENDIAN; + /** + * mpw: Site digest. + */ + public static final MessageAuthenticationDigests mpw_digest = MessageAuthenticationDigests.HmacSHA256; + /** + * mpw: Key ID hash. + */ + public static final MessageDigests mpw_hash = MessageDigests.SHA256; +} diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV0.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV0.java index b84872ab..3805efd2 100644 --- a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV0.java +++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV0.java @@ -1,6 +1,5 @@ package com.lyndir.masterpassword; -import com.google.common.base.Charsets; import com.google.common.base.Preconditions; import com.google.common.primitives.Bytes; import com.google.common.primitives.UnsignedInteger; @@ -8,7 +7,6 @@ import com.lambdaworks.crypto.SCrypt; import com.lyndir.lhunath.opal.system.*; import com.lyndir.lhunath.opal.system.logging.Logger; import java.nio.*; -import java.nio.charset.Charset; import java.security.GeneralSecurityException; import java.util.Arrays; import javax.annotation.Nonnull; @@ -25,19 +23,11 @@ import javax.annotation.Nullable; */ public class MasterKeyV0 extends MasterKey { + private static final int MP_intLen = 32; + @SuppressWarnings("UnusedDeclaration") private static final Logger logger = Logger.get( MasterKeyV0.class ); - protected final int MP_N = 32768; - protected final int MP_r = 8; - protected final int MP_p = 2; - protected final int MP_dkLen = 64; - protected final int MP_intLen = 32; - protected final Charset MP_charset = Charsets.UTF_8; - protected final ByteOrder MP_byteOrder = ByteOrder.BIG_ENDIAN; - protected final MessageDigests MP_hash = MessageDigests.SHA256; - protected final MessageAuthenticationDigests MP_mac = MessageAuthenticationDigests.HmacSHA256; - public MasterKeyV0(final String fullName) { super( fullName ); } @@ -52,15 +42,15 @@ public class MasterKeyV0 extends MasterKey { @Override protected byte[] deriveKey(final char[] masterPassword) { String fullName = getFullName(); - byte[] fullNameBytes = fullName.getBytes( MP_charset ); + byte[] fullNameBytes = fullName.getBytes( MPConstant.mpw_charset ); byte[] fullNameLengthBytes = bytesForInt( fullName.length() ); String mpKeyScope = MPSiteVariant.Password.getScope(); - byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MP_charset ), fullNameLengthBytes, fullNameBytes ); + byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MPConstant.mpw_charset ), fullNameLengthBytes, fullNameBytes ); logger.trc( "key scope: %s", mpKeyScope ); logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) ); - ByteBuffer mpBytesBuf = MP_charset.encode( CharBuffer.wrap( masterPassword ) ); + ByteBuffer mpBytesBuf = MPConstant.mpw_charset.encode( CharBuffer.wrap( masterPassword ) ); byte[] mpBytes = new byte[mpBytesBuf.remaining()]; mpBytesBuf.get( mpBytes, 0, mpBytes.length ); Arrays.fill( mpBytesBuf.array(), (byte) 0 ); @@ -71,9 +61,9 @@ public class MasterKeyV0 extends MasterKey { protected byte[] scrypt(final byte[] masterKeySalt, final byte[] mpBytes) { try { if (isAllowNative()) - return SCrypt.scrypt( mpBytes, masterKeySalt, MP_N, MP_r, MP_p, MP_dkLen ); + return SCrypt.scrypt( mpBytes, masterKeySalt, MPConstant.scrypt_N, MPConstant.scrypt_r, MPConstant.scrypt_p, MPConstant.mpw_dkLen ); else - return SCrypt.scryptJ( mpBytes, masterKeySalt, MP_N, MP_r, MP_p, MP_dkLen ); + return SCrypt.scryptJ( mpBytes, masterKeySalt, MPConstant.scrypt_N, MPConstant.scrypt_r, MPConstant.scrypt_p, MPConstant.mpw_dkLen ); } catch (GeneralSecurityException e) { logger.bug( e ); @@ -99,22 +89,22 @@ public class MasterKeyV0 extends MasterKey { siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (300 * 1000)) * 300 ); String siteScope = siteVariant.getScope(); - byte[] siteNameBytes = siteName.getBytes( MP_charset ); + byte[] siteNameBytes = siteName.getBytes( MPConstant.mpw_charset ); byte[] siteNameLengthBytes = bytesForInt( siteName.length() ); byte[] siteCounterBytes = bytesForInt( siteCounter ); - byte[] siteContextBytes = siteContext == null || siteContext.isEmpty()? null: siteContext.getBytes( MP_charset ); + byte[] siteContextBytes = siteContext == null || siteContext.isEmpty()? null: siteContext.getBytes( MPConstant.mpw_charset ); byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length ); logger.trc( "site scope: %s, context: %s", siteScope, siteContextBytes == null? "": siteContext ); logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ), siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ), siteContextBytes == null? "(null)": siteContext ); - byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes ); + byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MPConstant.mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes ); if (siteContextBytes != null) sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes ); logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) ); - byte[] sitePasswordSeedBytes = MP_mac.of( getKey(), sitePasswordInfo ); + byte[] sitePasswordSeedBytes = MPConstant.mpw_digest.of( getKey(), sitePasswordInfo ); int[] sitePasswordSeed = new int[sitePasswordSeedBytes.length]; for (int i = 0; i < sitePasswordSeedBytes.length; ++i) { ByteBuffer buf = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( ByteOrder.BIG_ENDIAN ); @@ -146,16 +136,16 @@ public class MasterKeyV0 extends MasterKey { @Override protected byte[] bytesForInt(final int number) { - return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MP_byteOrder ).putInt( number ).array(); + return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MPConstant.mpw_byteOrder ).putInt( number ).array(); } @Override protected byte[] bytesForInt(@Nonnull final UnsignedInteger number) { - return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MP_byteOrder ).putInt( number.intValue() ).array(); + return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MPConstant.mpw_byteOrder ).putInt( number.intValue() ).array(); } @Override protected byte[] idForBytes(final byte[] bytes) { - return MP_hash.of( bytes ); + return MPConstant.mpw_hash.of( bytes ); } } diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV1.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV1.java index d58dcf4e..9e68cb52 100644 --- a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV1.java +++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV1.java @@ -46,22 +46,22 @@ public class MasterKeyV1 extends MasterKeyV0 { siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (300 * 1000)) * 300 ); String siteScope = siteVariant.getScope(); - byte[] siteNameBytes = siteName.getBytes( MP_charset ); + byte[] siteNameBytes = siteName.getBytes( MPConstant.mpw_charset ); byte[] siteNameLengthBytes = bytesForInt( siteName.length() ); byte[] siteCounterBytes = bytesForInt( siteCounter ); - byte[] siteContextBytes = siteContext == null || siteContext.isEmpty()? null: siteContext.getBytes( MP_charset ); + byte[] siteContextBytes = siteContext == null || siteContext.isEmpty()? null: siteContext.getBytes( MPConstant.mpw_charset ); byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length ); logger.trc( "site scope: %s, context: %s", siteScope, siteContextBytes == null? "": siteContext ); logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ), siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ), siteContextBytes == null? "(null)": siteContext ); - byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes ); + byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MPConstant.mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes ); if (siteContextBytes != null) sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes ); logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) ); - byte[] sitePasswordSeed = MP_mac.of( getKey(), sitePasswordInfo ); + byte[] sitePasswordSeed = MPConstant.mpw_digest.of( getKey(), sitePasswordInfo ); logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) ); Preconditions.checkState( sitePasswordSeed.length > 0 ); diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV2.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV2.java index e4019d52..5761d34f 100644 --- a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV2.java +++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV2.java @@ -45,22 +45,22 @@ public class MasterKeyV2 extends MasterKeyV1 { siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (300 * 1000)) * 300 ); String siteScope = siteVariant.getScope(); - byte[] siteNameBytes = siteName.getBytes( MP_charset ); + byte[] siteNameBytes = siteName.getBytes( MPConstant.mpw_charset ); byte[] siteNameLengthBytes = bytesForInt( siteNameBytes.length ); byte[] siteCounterBytes = bytesForInt( siteCounter ); - byte[] siteContextBytes = siteContext == null || siteContext.isEmpty()? null: siteContext.getBytes( MP_charset ); + byte[] siteContextBytes = siteContext == null || siteContext.isEmpty()? null: siteContext.getBytes( MPConstant.mpw_charset ); byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length ); logger.trc( "site scope: %s, context: %s", siteScope, siteContextBytes == null? "": siteContext ); logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ), siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ), siteContextBytes == null? "(null)": siteContext ); - byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes ); + byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MPConstant.mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes ); if (siteContextBytes != null) sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes ); logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) ); - byte[] sitePasswordSeed = MP_mac.of( getKey(), sitePasswordInfo ); + byte[] sitePasswordSeed = MPConstant.mpw_digest.of( getKey(), sitePasswordInfo ); logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) ); Preconditions.checkState( sitePasswordSeed.length > 0 ); diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV3.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV3.java index bc620a5d..e148c08c 100644 --- a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV3.java +++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV3.java @@ -1,12 +1,10 @@ package com.lyndir.masterpassword; import com.google.common.primitives.Bytes; -import com.lambdaworks.crypto.SCrypt; import com.lyndir.lhunath.opal.system.CodeUtils; import com.lyndir.lhunath.opal.system.logging.Logger; import java.nio.ByteBuffer; import java.nio.CharBuffer; -import java.security.GeneralSecurityException; import java.util.Arrays; import javax.annotation.Nullable; @@ -35,15 +33,15 @@ public class MasterKeyV3 extends MasterKeyV2 { @Nullable @Override protected byte[] deriveKey(final char[] masterPassword) { - byte[] fullNameBytes = getFullName().getBytes( MP_charset ); + byte[] fullNameBytes = getFullName().getBytes( MPConstant.mpw_charset ); byte[] fullNameLengthBytes = bytesForInt( fullNameBytes.length ); String mpKeyScope = MPSiteVariant.Password.getScope(); - byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MP_charset ), fullNameLengthBytes, fullNameBytes ); + byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MPConstant.mpw_charset ), fullNameLengthBytes, fullNameBytes ); logger.trc( "key scope: %s", mpKeyScope ); logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) ); - ByteBuffer mpBytesBuf = MP_charset.encode( CharBuffer.wrap( masterPassword ) ); + ByteBuffer mpBytesBuf = MPConstant.mpw_charset.encode( CharBuffer.wrap( masterPassword ) ); byte[] mpBytes = new byte[mpBytesBuf.remaining()]; mpBytesBuf.get( mpBytes, 0, mpBytes.length ); Arrays.fill( mpBytesBuf.array(), (byte) 0 ); diff --git a/MasterPassword/Java/masterpassword-cli/src/main/java/com/lyndir/masterpassword/CLI.java b/MasterPassword/Java/masterpassword-cli/src/main/java/com/lyndir/masterpassword/CLI.java index de42d2f0..c58b1b2d 100644 --- a/MasterPassword/Java/masterpassword-cli/src/main/java/com/lyndir/masterpassword/CLI.java +++ b/MasterPassword/Java/masterpassword-cli/src/main/java/com/lyndir/masterpassword/CLI.java @@ -37,21 +37,17 @@ import java.util.Map; */ public class CLI { - private static final String ENV_USERNAME = "MP_USERNAME"; - private static final String ENV_SITETYPE = "MP_SITETYPE"; - private static final String ENV_SITECOUNTER = "MP_SITECOUNTER"; - public static void main(final String[] args) throws IOException { // Read information from the environment. char[] masterPassword; String siteName = null, context = null; - String userName = System.getenv( ENV_USERNAME ); - String siteTypeName = ifNotNullElse( System.getenv( ENV_SITETYPE ), "" ); + String userName = System.getenv( MPConstant.env_userName ); + String siteTypeName = ifNotNullElse( System.getenv( MPConstant.env_siteType ), "" ); MPSiteType siteType = siteTypeName.isEmpty()? MPSiteType.GeneratedLong: MPSiteType.forOption( siteTypeName ); MPSiteVariant variant = MPSiteVariant.Password; - String siteCounterName = ifNotNullElse( System.getenv( ENV_SITECOUNTER ), "" ); + String siteCounterName = ifNotNullElse( System.getenv( MPConstant.env_siteCounter ), "" ); UnsignedInteger siteCounter = siteCounterName.isEmpty()? UnsignedInteger.valueOf( 1 ): UnsignedInteger.valueOf( siteCounterName ); // Parse information from option arguments. @@ -102,9 +98,9 @@ public class CLI { System.out.println(); System.out.format( "Usage: mpw [-u name] [-t type] [-c counter] site\n\n" ); System.out.format( " -u name Specify the full name of the user.\n" ); - System.out.format( " Defaults to %s in env.\n\n", ENV_USERNAME ); + System.out.format( " Defaults to %s in env.\n\n", MPConstant.env_userName ); System.out.format( " -t type Specify the password's template.\n" ); - System.out.format( " Defaults to %s in env or 'long' for password, 'name' for login.\n", ENV_SITETYPE ); + System.out.format( " Defaults to %s in env or 'long' for password, 'name' for login.\n", MPConstant.env_siteType ); int optionsLength = 0; Map typeMap = Maps.newLinkedHashMap(); @@ -122,7 +118,7 @@ public class CLI { System.out.println(); System.out.format( " -c counter The value of the counter.\n" ); - System.out.format( " Defaults to %s in env or '1'.\n\n", ENV_SITECOUNTER ); + System.out.format( " Defaults to %s in env or '1'.\n\n", MPConstant.env_siteCounter ); System.out.format( " -v variant The kind of content to generate.\n" ); System.out.format( " Defaults to 'password'.\n" ); diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/Config.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/Config.java index f3f7cc85..8f25a05d 100644 --- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/Config.java +++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/Config.java @@ -1,6 +1,7 @@ package com.lyndir.masterpassword.gui; import com.lyndir.lhunath.opal.system.util.ConversionUtils; +import com.lyndir.masterpassword.MPConstant; /** @@ -15,6 +16,6 @@ public class Config { } public boolean checkForUpdates() { - return ConversionUtils.toBoolean( System.getProperty( "mp.update.check" ) ).or( true ); + return ConversionUtils.toBoolean( System.getenv( MPConstant.env_checkUpdates ) ).or( true ); } } diff --git a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPUserFileManager.java b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPUserFileManager.java index df0d0873..42904e5b 100644 --- a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPUserFileManager.java +++ b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPUserFileManager.java @@ -1,31 +1,32 @@ package com.lyndir.masterpassword.model; +import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*; + import com.google.common.base.*; import com.google.common.collect.*; import com.google.common.io.CharSink; import com.lyndir.lhunath.opal.system.logging.Logger; +import com.lyndir.masterpassword.MPConstant; import java.io.*; -import java.util.SortedSet; import javax.annotation.Nullable; /** + * Manages user data stored in user-specific {@code .mpsites} files under {@code .mpw.d}. * @author lhunath, 14-12-07 */ public class MPUserFileManager extends MPUserManager { @SuppressWarnings("UnusedDeclaration") private static final Logger logger = Logger.get( MPUserFileManager.class ); - private static final File mpwd = new File( System.getProperty( "user.home" ), ".mpw.d" ); private static final MPUserFileManager instance; static { - File mpwrc = new File( System.getProperty( "user.home" ), ".mpwrc" ); - if (mpwrc.exists() && !mpwd.exists()) - if (!mpwrc.renameTo( mpwd )) - logger.err( "Couldn't migrate: %s -> %s", mpwrc, mpwd ); - - instance = create( mpwd ); + String rcDir = System.getenv( MPConstant.env_rcDir ); + if (rcDir != null) + instance = create( new File( rcDir ) ); + else + instance = create( new File( ifNotNullElseNullable( System.getProperty( "user.home" ), System.getenv( "HOME" ) ), ".mpw.d" ) ); } private final File userFilesDirectory; @@ -51,12 +52,7 @@ public class MPUserFileManager extends MPUserManager { return ImmutableList.of(); } - return FluentIterable.from( ImmutableList.copyOf( userFilesDirectory.listFiles( new FilenameFilter() { - @Override - public boolean accept(final File dir, final String name) { - return name.endsWith( ".mpsites" ); - } - } ) ) ).transform( new Function() { + return FluentIterable.from( listUserFiles( userFilesDirectory ) ).transform( new Function() { @Nullable @Override public MPUser apply(@Nullable final File file) { @@ -71,6 +67,15 @@ public class MPUserFileManager extends MPUserManager { } ).filter( Predicates.notNull() ); } + private static ImmutableList listUserFiles(final File userFilesDirectory) { + return ImmutableList.copyOf( ifNotNullElse( userFilesDirectory.listFiles( new FilenameFilter() { + @Override + public boolean accept(final File dir, final String name) { + return name.endsWith( ".mpsites" ); + } + } ), new File[0] ) ); + } + @Override public void addUser(final MPUser user) { super.addUser( user ); @@ -103,12 +108,7 @@ public class MPUserFileManager extends MPUserManager { } // Remove deleted users. - for (File userFile : userFilesDirectory.listFiles( new FilenameFilter() { - @Override - public boolean accept(final File dir, final String name) { - return name.endsWith( ".mpsites" ); - } - } )) + for (File userFile : listUserFiles( userFilesDirectory )) if (getUserNamed( userFile.getName().replaceFirst( "\\.mpsites$", "" ) ) == null) if (!userFile.delete()) logger.err( "Couldn't delete file: %s", userFile ); @@ -118,6 +118,6 @@ public class MPUserFileManager extends MPUserManager { * @return The location on the file system where the user models are stored. */ public File getPath() { - return mpwd; + return userFilesDirectory; } }