diff --git a/main/buildkit/pom.xml b/main/buildkit/pom.xml index c86927d33..705138204 100644 --- a/main/buildkit/pom.xml +++ b/main/buildkit/pom.xml @@ -67,7 +67,7 @@ ${project.build.directory}/libs linux,mac,win - dbus-java,secret-service,hkdf,java-utils + dbus-java,secret-service,kdewallet,hkdf,java-utils @@ -83,14 +83,14 @@ - copy-linux-secret-service + copy-linux-system-keychain-access prepare-package copy-dependencies ${project.build.directory}/linux-libs - dbus-java,secret-service,hkdf,java-utils + dbus-java,secret-service,kdewallet,hkdf,java-utils diff --git a/main/commons/pom.xml b/main/commons/pom.xml index 54e711559..a6d00c2db 100644 --- a/main/commons/pom.xml +++ b/main/commons/pom.xml @@ -44,7 +44,7 @@ - org.fxmisc.easybind + com.tobiasdiez easybind diff --git a/main/commons/src/main/java/org/cryptomator/common/CommonsModule.java b/main/commons/src/main/java/org/cryptomator/common/CommonsModule.java index bac0114da..93e0d3d44 100644 --- a/main/commons/src/main/java/org/cryptomator/common/CommonsModule.java +++ b/main/commons/src/main/java/org/cryptomator/common/CommonsModule.java @@ -5,6 +5,7 @@ *******************************************************************************/ package org.cryptomator.common; +import com.tobiasdiez.easybind.EasyBind; import dagger.Module; import dagger.Provides; import javafx.beans.binding.Binding; @@ -17,7 +18,6 @@ import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.VaultComponent; import org.cryptomator.common.vaults.VaultListManager; import org.cryptomator.frontend.webdav.WebDavServer; -import org.fxmisc.easybind.EasyBind; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/main/commons/src/main/java/org/cryptomator/common/settings/VaultSettings.java b/main/commons/src/main/java/org/cryptomator/common/settings/VaultSettings.java index baf63ce1f..2a90b969b 100644 --- a/main/commons/src/main/java/org/cryptomator/common/settings/VaultSettings.java +++ b/main/commons/src/main/java/org/cryptomator/common/settings/VaultSettings.java @@ -7,7 +7,10 @@ package org.cryptomator.common.settings; import com.google.common.base.Strings; import com.google.common.io.BaseEncoding; +import com.tobiasdiez.easybind.EasyBind; import javafx.beans.Observable; +import javafx.beans.binding.Bindings; +import javafx.beans.binding.StringBinding; import javafx.beans.property.BooleanProperty; import javafx.beans.property.IntegerProperty; import javafx.beans.property.ObjectProperty; @@ -17,7 +20,6 @@ import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import org.apache.commons.lang3.StringUtils; -import org.fxmisc.easybind.EasyBind; import java.nio.file.Path; import java.util.Objects; @@ -34,7 +36,6 @@ public class VaultSettings { public static final boolean DEFAULT_USES_INDIVIDUAL_MOUNTPATH = false; public static final boolean DEFAULT_USES_READONLY_MODE = false; public static final String DEFAULT_MOUNT_FLAGS = ""; - public static final String DEFAULT_MOUNT_NAME = "Vault"; public static final int DEFAULT_FILENAME_LENGTH_LIMIT = -1; public static final WhenUnlocked DEFAULT_ACTION_AFTER_UNLOCK = WhenUnlocked.ASK; @@ -42,7 +43,7 @@ public class VaultSettings { private final String id; private final ObjectProperty path = new SimpleObjectProperty(); - private final StringProperty mountName = new SimpleStringProperty(); + private final StringProperty displayName = new SimpleStringProperty(); private final StringProperty winDriveLetter = new SimpleStringProperty(); private final BooleanProperty unlockAfterStartup = new SimpleBooleanProperty(DEFAULT_UNLOCK_AFTER_STARTUP); private final BooleanProperty revealAfterMount = new SimpleBooleanProperty(DEFAULT_REAVEAL_AFTER_MOUNT); @@ -53,30 +54,15 @@ public class VaultSettings { private final IntegerProperty filenameLengthLimit = new SimpleIntegerProperty(DEFAULT_FILENAME_LENGTH_LIMIT); private final ObjectProperty actionAfterUnlock = new SimpleObjectProperty<>(DEFAULT_ACTION_AFTER_UNLOCK); + private final StringBinding mountName; + public VaultSettings(String id) { this.id = Objects.requireNonNull(id); - - EasyBind.subscribe(path, this::deriveMountNameFromPathOrUseDefault); + this.mountName = Bindings.createStringBinding(this::normalizeDisplayName, displayName); } Observable[] observables() { - return new Observable[]{path, mountName, winDriveLetter, unlockAfterStartup, revealAfterMount, useCustomMountPath, customMountPath, usesReadOnlyMode, mountFlags, filenameLengthLimit, actionAfterUnlock}; - } - - private void deriveMountNameFromPathOrUseDefault(Path newPath) { - final boolean mountNameSet = !StringUtils.isBlank(mountName.get()); - final boolean dirnameExists = (newPath != null) && (newPath.getFileName() != null); - - if (!mountNameSet && dirnameExists) { - mountName.set(normalizeMountName(newPath.getFileName().toString())); - } else if (!mountNameSet && !dirnameExists) { - mountName.set(DEFAULT_MOUNT_NAME + id); - } else if (mountNameSet && dirnameExists) { - if (mountName.get().equals(DEFAULT_MOUNT_NAME + id)) { - //this is okay, since this function is only executed if the path changes (aka, the vault is created or added) - mountName.set(newPath.getFileName().toString()); - } - } + return new Observable[]{path, displayName, winDriveLetter, unlockAfterStartup, revealAfterMount, useCustomMountPath, customMountPath, usesReadOnlyMode, mountFlags, filenameLengthLimit, actionAfterUnlock}; } public static VaultSettings withRandomId() { @@ -89,8 +75,9 @@ public class VaultSettings { return BaseEncoding.base64Url().encode(randomBytes); } - public static String normalizeMountName(String mountName) { - String normalizedMountName = StringUtils.stripAccents(mountName); + //visible for testing + String normalizeDisplayName() { + String normalizedMountName = StringUtils.stripAccents(displayName.get()); StringBuilder builder = new StringBuilder(); for (char c : normalizedMountName.toCharArray()) { if (Character.isWhitespace(c)) { @@ -118,7 +105,11 @@ public class VaultSettings { return path; } - public StringProperty mountName() { + public StringProperty displayName() { + return displayName; + } + + public StringBinding mountName() { return mountName; } diff --git a/main/commons/src/main/java/org/cryptomator/common/settings/VaultSettingsJsonAdapter.java b/main/commons/src/main/java/org/cryptomator/common/settings/VaultSettingsJsonAdapter.java index d136ffaff..04a352a49 100644 --- a/main/commons/src/main/java/org/cryptomator/common/settings/VaultSettingsJsonAdapter.java +++ b/main/commons/src/main/java/org/cryptomator/common/settings/VaultSettingsJsonAdapter.java @@ -21,7 +21,7 @@ class VaultSettingsJsonAdapter { out.beginObject(); out.name("id").value(value.getId()); out.name("path").value(value.path().get().toString()); - out.name("mountName").value(value.mountName().get()); + out.name("displayName").value(value.displayName().get()); out.name("winDriveLetter").value(value.winDriveLetter().get()); out.name("unlockAfterStartup").value(value.unlockAfterStartup().get()); out.name("revealAfterMount").value(value.revealAfterMount().get()); @@ -37,7 +37,8 @@ class VaultSettingsJsonAdapter { public VaultSettings read(JsonReader in) throws IOException { String id = null; String path = null; - String mountName = null; + String mountName = null; //see https://github.com/cryptomator/cryptomator/pull/1318 + String displayName = null; String customMountPath = null; String winDriveLetter = null; boolean unlockAfterStartup = VaultSettings.DEFAULT_UNLOCK_AFTER_STARTUP; @@ -54,7 +55,8 @@ class VaultSettingsJsonAdapter { switch (name) { case "id" -> id = in.nextString(); case "path" -> path = in.nextString(); - case "mountName" -> mountName = in.nextString(); + case "mountName" -> mountName = in.nextString(); //see https://github.com/cryptomator/cryptomator/pull/1318 + case "displayName" -> displayName = in.nextString(); case "winDriveLetter" -> winDriveLetter = in.nextString(); case "unlockAfterStartup" -> unlockAfterStartup = in.nextBoolean(); case "revealAfterMount" -> revealAfterMount = in.nextBoolean(); @@ -73,7 +75,11 @@ class VaultSettingsJsonAdapter { in.endObject(); VaultSettings vaultSettings = (id == null) ? VaultSettings.withRandomId() : new VaultSettings(id); - vaultSettings.mountName().set(mountName); + if (displayName != null) { //see https://github.com/cryptomator/cryptomator/pull/1318 + vaultSettings.displayName().set(displayName); + } else { + vaultSettings.displayName().set(mountName); + } vaultSettings.path().set(Paths.get(path)); vaultSettings.winDriveLetter().set(winDriveLetter); vaultSettings.unlockAfterStartup().set(unlockAfterStartup); diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/DokanyVolume.java b/main/commons/src/main/java/org/cryptomator/common/vaults/DokanyVolume.java index e65b3f0f9..20b1e69d3 100644 --- a/main/commons/src/main/java/org/cryptomator/common/vaults/DokanyVolume.java +++ b/main/commons/src/main/java/org/cryptomator/common/vaults/DokanyVolume.java @@ -36,9 +36,9 @@ public class DokanyVolume extends AbstractVolume { @Override public void mount(CryptoFileSystem fs, String mountFlags) throws InvalidMountPointException, VolumeException { this.mountPoint = determineMountPoint(); - String mountName = vaultSettings.mountName().get(); + String mountName = vaultSettings.displayName().get(); try { - this.mount = mountFactory.mount(fs.getPath("/"), mountPoint, mountName, FS_TYPE_NAME, mountFlags.strip()); + this.mount = mountFactory.mount(fs.getPath("/"), mountPoint, vaultSettings.mountName().get(), FS_TYPE_NAME, mountFlags.strip()); } catch (MountFailedException e) { if (vaultSettings.getCustomMountPath().isPresent()) { LOG.warn("Failed to mount vault into {}. Is this directory currently accessed by another process (e.g. Windows Explorer)?", mountPoint); diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/Vault.java b/main/commons/src/main/java/org/cryptomator/common/vaults/Vault.java index 34433db0e..d63ab0330 100644 --- a/main/commons/src/main/java/org/cryptomator/common/vaults/Vault.java +++ b/main/commons/src/main/java/org/cryptomator/common/vaults/Vault.java @@ -57,7 +57,7 @@ public class Vault { private final ObjectProperty state; private final ObjectProperty lastKnownException; private final VaultStats stats; - private final StringBinding displayableName; + private final StringBinding displayName; private final StringBinding displayablePath; private final BooleanBinding locked; private final BooleanBinding processing; @@ -79,7 +79,7 @@ public class Vault { this.state = state; this.lastKnownException = lastKnownException; this.stats = stats; - this.displayableName = Bindings.createStringBinding(this::getDisplayableName, vaultSettings.path()); + this.displayName = Bindings.createStringBinding(this::getDisplayName, vaultSettings.displayName()); this.displayablePath = Bindings.createStringBinding(this::getDisplayablePath, vaultSettings.path()); this.locked = Bindings.createBooleanBinding(this::isLocked, state); this.processing = Bindings.createBooleanBinding(this::isProcessing, state); @@ -223,13 +223,12 @@ public class Vault { return state.get() == VaultState.ERROR; } - public StringBinding displayableNameProperty() { - return displayableName; + public StringBinding displayNameProperty() { + return displayName; } - public String getDisplayableName() { - Path p = vaultSettings.path().get(); - return p.getFileName().toString(); + public String getDisplayName() { + return vaultSettings.displayName().get(); } public StringBinding accessPointProperty() { diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultListManager.java b/main/commons/src/main/java/org/cryptomator/common/vaults/VaultListManager.java index 75a48c6fc..a670acab5 100644 --- a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultListManager.java +++ b/main/commons/src/main/java/org/cryptomator/common/vaults/VaultListManager.java @@ -20,11 +20,11 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.inject.Singleton; import java.io.IOException; -import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.util.Collection; import java.util.Optional; +import java.util.ResourceBundle; import java.util.stream.Collectors; import static org.cryptomator.common.Constants.MASTERKEY_FILENAME; @@ -36,10 +36,12 @@ public class VaultListManager { private final VaultComponent.Builder vaultComponentBuilder; private final ObservableList vaultList; + private final String defaultVaultName; @Inject - public VaultListManager(VaultComponent.Builder vaultComponentBuilder, Settings settings) { + public VaultListManager(VaultComponent.Builder vaultComponentBuilder, ResourceBundle resourceBundle, Settings settings) { this.vaultComponentBuilder = vaultComponentBuilder; + this.defaultVaultName = resourceBundle.getString("defaults.vault.vaultName"); this.vaultList = FXCollections.observableArrayList(Vault::observables); addAll(settings.getDirectories()); @@ -59,14 +61,23 @@ public class VaultListManager { if (alreadyExistingVault.isPresent()) { return alreadyExistingVault.get(); } else { - VaultSettings vaultSettings = VaultSettings.withRandomId(); - vaultSettings.path().set(normalizedPathToVault); - Vault newVault = create(vaultSettings); + Vault newVault = create(newVaultSettings(normalizedPathToVault)); vaultList.add(newVault); return newVault; } } + private VaultSettings newVaultSettings(Path path) { + VaultSettings vaultSettings = VaultSettings.withRandomId(); + vaultSettings.path().set(path); + if (path.getFileName() != null) { + vaultSettings.displayName().set(path.getFileName().toString()); + } else { + vaultSettings.displayName().set(defaultVaultName); + } + return vaultSettings; + } + private void addAll(Collection vaultSettings) { Collection vaults = vaultSettings.stream().map(this::create).collect(Collectors.toList()); vaultList.addAll(vaults); @@ -92,7 +103,7 @@ public class VaultListManager { } return compBuilder.build().vault(); } - + public static VaultState redetermineVaultState(Vault vault) { VaultState previousState = vault.getState(); return switch (previousState) { diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultModule.java b/main/commons/src/main/java/org/cryptomator/common/vaults/VaultModule.java index caf347602..28aa7527a 100644 --- a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultModule.java +++ b/main/commons/src/main/java/org/cryptomator/common/vaults/VaultModule.java @@ -12,9 +12,7 @@ import javafx.beans.binding.StringBinding; import javafx.beans.property.BooleanProperty; import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyBooleanProperty; -import javafx.beans.property.ReadOnlyStringProperty; import javafx.beans.property.SimpleObjectProperty; -import javafx.beans.property.StringProperty; import org.apache.commons.lang3.SystemUtils; import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.VaultSettings; @@ -77,7 +75,7 @@ public class VaultModule { @DefaultMountFlags public StringBinding provideDefaultMountFlags(Settings settings, VaultSettings vaultSettings) { ObjectProperty preferredVolumeImpl = settings.preferredVolumeImpl(); - StringProperty mountName = vaultSettings.mountName(); + StringBinding mountName = vaultSettings.mountName(); BooleanProperty readOnly = vaultSettings.usesReadOnlyMode(); return Bindings.createStringBinding(() -> { @@ -97,7 +95,7 @@ public class VaultModule { } // see: https://github.com/osxfuse/osxfuse/wiki/Mount-options - private String getMacFuseDefaultMountFlags(ReadOnlyStringProperty mountName, ReadOnlyBooleanProperty readOnly) { + private String getMacFuseDefaultMountFlags(StringBinding mountName, ReadOnlyBooleanProperty readOnly) { assert SystemUtils.IS_OS_MAC_OSX; StringBuilder flags = new StringBuilder(); if (readOnly.get()) { diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/WebDavVolume.java b/main/commons/src/main/java/org/cryptomator/common/vaults/WebDavVolume.java index ecdb31d88..ddc611649 100644 --- a/main/commons/src/main/java/org/cryptomator/common/vaults/WebDavVolume.java +++ b/main/commons/src/main/java/org/cryptomator/common/vaults/WebDavVolume.java @@ -97,7 +97,7 @@ public class WebDavVolume implements Volume { @Override public Optional getMountPoint() { - return Optional.empty(); + return Optional.empty(); //TODO } @Override diff --git a/main/commons/src/test/java/org/cryptomator/common/settings/SettingsTest.java b/main/commons/src/test/java/org/cryptomator/common/settings/SettingsTest.java index 708578763..33d384cf8 100644 --- a/main/commons/src/test/java/org/cryptomator/common/settings/SettingsTest.java +++ b/main/commons/src/test/java/org/cryptomator/common/settings/SettingsTest.java @@ -29,7 +29,7 @@ public class SettingsTest { Mockito.verify(changeListener, Mockito.times(2)).accept(settings); // third change (to property of list item): - vaultSettings.mountName().set("asd"); + vaultSettings.displayName().set("asd"); Mockito.verify(changeListener, Mockito.times(3)).accept(settings); } diff --git a/main/commons/src/test/java/org/cryptomator/common/settings/VaultSettingsJsonAdapterTest.java b/main/commons/src/test/java/org/cryptomator/common/settings/VaultSettingsJsonAdapterTest.java index 237b00904..a0faee551 100644 --- a/main/commons/src/test/java/org/cryptomator/common/settings/VaultSettingsJsonAdapterTest.java +++ b/main/commons/src/test/java/org/cryptomator/common/settings/VaultSettingsJsonAdapterTest.java @@ -23,13 +23,13 @@ public class VaultSettingsJsonAdapterTest { @Test public void testDeserialize() throws IOException { - String json = "{\"id\": \"foo\", \"path\": \"/foo/bar\", \"mountName\": \"test\", \"winDriveLetter\": \"X\", \"shouldBeIgnored\": true, \"individualMountPath\": \"/home/test/crypto\", \"mountFlags\":\"--foo --bar\"}"; + String json = "{\"id\": \"foo\", \"path\": \"/foo/bar\", \"displayName\": \"test\", \"winDriveLetter\": \"X\", \"shouldBeIgnored\": true, \"individualMountPath\": \"/home/test/crypto\", \"mountFlags\":\"--foo --bar\"}"; JsonReader jsonReader = new JsonReader(new StringReader(json)); VaultSettings vaultSettings = adapter.read(jsonReader); Assertions.assertEquals("foo", vaultSettings.getId()); Assertions.assertEquals(Paths.get("/foo/bar"), vaultSettings.path().get()); - Assertions.assertEquals("test", vaultSettings.mountName().get()); + Assertions.assertEquals("test", vaultSettings.displayName().get()); Assertions.assertEquals("X", vaultSettings.winDriveLetter().get()); Assertions.assertEquals("/home/test/crypto", vaultSettings.customMountPath().get()); Assertions.assertEquals("--foo --bar", vaultSettings.mountFlags().get()); @@ -41,7 +41,7 @@ public class VaultSettingsJsonAdapterTest { public void testSerialize() throws IOException { VaultSettings vaultSettings = new VaultSettings("test"); vaultSettings.path().set(Paths.get("/foo/bar")); - vaultSettings.mountName().set("mountyMcMountFace"); + vaultSettings.displayName().set("mountyMcMountFace"); vaultSettings.mountFlags().set("--foo --bar"); StringWriter buf = new StringWriter(); @@ -55,7 +55,7 @@ public class VaultSettingsJsonAdapterTest { } else { MatcherAssert.assertThat(result, CoreMatchers.containsString("\"path\":\"/foo/bar\"")); } - MatcherAssert.assertThat(result, CoreMatchers.containsString("\"mountName\":\"mountyMcMountFace\"")); + MatcherAssert.assertThat(result, CoreMatchers.containsString("\"displayName\":\"mountyMcMountFace\"")); MatcherAssert.assertThat(result, CoreMatchers.containsString("\"mountFlags\":\"--foo --bar\"")); } diff --git a/main/commons/src/test/java/org/cryptomator/common/settings/VaultSettingsTest.java b/main/commons/src/test/java/org/cryptomator/common/settings/VaultSettingsTest.java index 042ff9896..8ec6dc681 100644 --- a/main/commons/src/test/java/org/cryptomator/common/settings/VaultSettingsTest.java +++ b/main/commons/src/test/java/org/cryptomator/common/settings/VaultSettingsTest.java @@ -8,19 +8,19 @@ *******************************************************************************/ package org.cryptomator.common.settings; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import static org.junit.jupiter.api.Assertions.assertEquals; public class VaultSettingsTest { - @Test - public void testNormalize() throws Exception { - assertEquals("_", VaultSettings.normalizeMountName(" ")); - assertEquals("a", VaultSettings.normalizeMountName("ä")); - assertEquals("C", VaultSettings.normalizeMountName("Ĉ")); - assertEquals("_", VaultSettings.normalizeMountName(":")); - assertEquals("_", VaultSettings.normalizeMountName("汉语")); + @ParameterizedTest + @CsvSource({"a a,a_a", "ä,a", "Ĉ,C", ":,_", "汉语,_"}) + public void testNormalize(String test, String expected) { + VaultSettings settings = new VaultSettings("id"); + settings.displayName().setValue(test); + assertEquals(expected, settings.normalizeDisplayName()); } } diff --git a/main/commons/src/test/java/org/cryptomator/common/vaults/VaultModuleTest.java b/main/commons/src/test/java/org/cryptomator/common/vaults/VaultModuleTest.java index 1eb9a9d2f..e4922963b 100644 --- a/main/commons/src/test/java/org/cryptomator/common/vaults/VaultModuleTest.java +++ b/main/commons/src/test/java/org/cryptomator/common/vaults/VaultModuleTest.java @@ -1,9 +1,9 @@ package org.cryptomator.common.vaults; +import javafx.beans.binding.Bindings; import javafx.beans.binding.StringBinding; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleObjectProperty; -import javafx.beans.property.SimpleStringProperty; import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.VaultSettings; import org.cryptomator.common.settings.VolumeImpl; @@ -28,7 +28,7 @@ public class VaultModuleTest { @BeforeEach public void setup(@TempDir Path tmpDir) { - Mockito.when(vaultSettings.mountName()).thenReturn(new SimpleStringProperty("TEST")); + Mockito.when(vaultSettings.mountName()).thenReturn(Bindings.createStringBinding(() -> "TEST")); Mockito.when(vaultSettings.usesReadOnlyMode()).thenReturn(new SimpleBooleanProperty(true)); System.setProperty("user.home", tmpDir.toString()); } diff --git a/main/keychain/pom.xml b/main/keychain/pom.xml index fd2b26c3a..3eaa5c2e7 100644 --- a/main/keychain/pom.xml +++ b/main/keychain/pom.xml @@ -24,7 +24,7 @@ org.openjfx javafx-graphics - + org.apache.commons @@ -53,6 +53,12 @@ secret-service + + + org.purejava + kdewallet + + org.slf4j diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/KeychainModule.java b/main/keychain/src/main/java/org/cryptomator/keychain/KeychainModule.java index 1db94fec6..a6054be2d 100644 --- a/main/keychain/src/main/java/org/cryptomator/keychain/KeychainModule.java +++ b/main/keychain/src/main/java/org/cryptomator/keychain/KeychainModule.java @@ -28,7 +28,7 @@ public abstract class KeychainModule { @Binds @IntoSet - abstract KeychainAccessStrategy bindLinuxSecretServiceKeychainAccess(LinuxSecretServiceKeychainAccess keychainAccessStrategy); + abstract KeychainAccessStrategy bindLinuxSystemKeychainAccess(LinuxSystemKeychainAccess keychainAccessStrategy); @Provides @Singleton diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/LinuxKDEWalletKeychainAccessImpl.java b/main/keychain/src/main/java/org/cryptomator/keychain/LinuxKDEWalletKeychainAccessImpl.java new file mode 100644 index 000000000..120bef435 --- /dev/null +++ b/main/keychain/src/main/java/org/cryptomator/keychain/LinuxKDEWalletKeychainAccessImpl.java @@ -0,0 +1,121 @@ +package org.cryptomator.keychain; + +import org.freedesktop.dbus.connections.impl.DBusConnection; +import org.freedesktop.dbus.exceptions.DBusException; +import org.kde.KWallet; +import org.kde.Static; +import org.purejava.KDEWallet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class LinuxKDEWalletKeychainAccessImpl implements KeychainAccessStrategy { + + private final Logger log = LoggerFactory.getLogger(LinuxKDEWalletKeychainAccessImpl.class); + + private final String FOLDER_NAME = "Cryptomator"; + private final String APP_NAME = "Cryptomator"; + private DBusConnection connection; + private KDEWallet wallet; + private int handle = -1; + + public LinuxKDEWalletKeychainAccessImpl() { + try { + connection = DBusConnection.getConnection(DBusConnection.DBusBusType.SESSION); + } catch (DBusException e) { + e.printStackTrace(); + } + } + + @Override + public boolean isSupported() { + try { + wallet = new KDEWallet(connection); + return wallet.isEnabled(); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + @Override + public void storePassphrase(String key, CharSequence passphrase) throws KeychainAccessException { + try { + if (walletIsOpen() && + !(wallet.hasEntry(handle, FOLDER_NAME, key, APP_NAME) + && wallet.entryType(handle, FOLDER_NAME, key, APP_NAME) == 1) + && wallet.writePassword(handle, FOLDER_NAME, key, passphrase.toString(), APP_NAME) == 0) { + log.debug("Passphrase successfully stored."); + } else { + log.debug("Passphrase was not stored."); + } + } catch (Exception e) { + log.error(e.toString(), e.getCause()); + throw new KeychainAccessException(e); + } + } + + @Override + public char[] loadPassphrase(String key) throws KeychainAccessException { + String password = ""; + try { + if (walletIsOpen()) { + password = wallet.readPassword(handle, FOLDER_NAME, key, APP_NAME); + log.debug("loadPassphrase: wallet is open."); + } else { + log.debug("loadPassphrase: wallet is closed."); + } + return (password.equals("")) ? null : password.toCharArray(); + } catch (Exception e) { + throw new KeychainAccessException(e); + } + } + + @Override + public void deletePassphrase(String key) throws KeychainAccessException { + try { + if (walletIsOpen() + && wallet.hasEntry(handle, FOLDER_NAME, key, APP_NAME) + && wallet.entryType(handle, FOLDER_NAME, key, APP_NAME) == 1 + && wallet.removeEntry(handle, FOLDER_NAME, key, APP_NAME) == 0) { + log.debug("Passphrase successfully deleted."); + } else { + log.debug("Passphrase was not deleted."); + } + } catch (Exception e) { + throw new KeychainAccessException(e); + } + } + + @Override + public void changePassphrase(String key, CharSequence passphrase) throws KeychainAccessException { + try { + if (walletIsOpen() + && wallet.hasEntry(handle, FOLDER_NAME, key, APP_NAME) + && wallet.entryType(handle, FOLDER_NAME, key, APP_NAME) == 1 + && wallet.writePassword(handle, FOLDER_NAME, key, passphrase.toString(), APP_NAME) == 0) { + log.debug("Passphrase successfully changed."); + } else { + log.debug("Passphrase could not be changed."); + } + } catch (Exception e) { + throw new KeychainAccessException(e); + } + } + + private boolean walletIsOpen() throws KeychainAccessException { + try { + if (wallet.isOpen(Static.DEFAULT_WALLET)) { + // This is needed due to KeechainManager loading the passphase directly + if (handle == -1) handle = wallet.open(Static.DEFAULT_WALLET, 0, APP_NAME); + return true; + } + wallet.openAsync(Static.DEFAULT_WALLET, 0, APP_NAME, false); + wallet.getSignalHandler().await(KWallet.walletAsyncOpened.class, Static.ObjectPaths.KWALLETD5, () -> null); + handle = wallet.getSignalHandler().getLastHandledSignal(KWallet.walletAsyncOpened.class, Static.ObjectPaths.KWALLETD5).handle; + log.debug("Wallet successfully initialized."); + return handle != -1; + } catch (Exception e) { + throw new KeychainAccessException(e); + } + } +} diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/LinuxSecretServiceKeychainAccess.java b/main/keychain/src/main/java/org/cryptomator/keychain/LinuxSystemKeychainAccess.java similarity index 66% rename from main/keychain/src/main/java/org/cryptomator/keychain/LinuxSecretServiceKeychainAccess.java rename to main/keychain/src/main/java/org/cryptomator/keychain/LinuxSystemKeychainAccess.java index f11bdbd45..e75d387cb 100644 --- a/main/keychain/src/main/java/org/cryptomator/keychain/LinuxSecretServiceKeychainAccess.java +++ b/main/keychain/src/main/java/org/cryptomator/keychain/LinuxSystemKeychainAccess.java @@ -7,24 +7,28 @@ import javax.inject.Singleton; import java.util.Optional; /** - * A facade to LinuxSecretServiceKeychainAccessImpl that doesn't depend on libraries that are unavailable on Mac and Windows. + * A facade to LinuxSecretServiceKeychainAccessImpl and LinuxKDEWalletKeychainAccessImpl + * that depend on libraries that are unavailable on Mac and Windows. */ @Singleton -public class LinuxSecretServiceKeychainAccess implements KeychainAccessStrategy { +public class LinuxSystemKeychainAccess implements KeychainAccessStrategy { - // the actual implementation is hidden in this delegate object which is loaded via reflection, + // the actual implementation is hidden in this delegate objects which are loaded via reflection, // as it depends on libraries that aren't necessarily available: private final Optional delegate; @Inject - public LinuxSecretServiceKeychainAccess() { - this.delegate = constructGnomeKeyringKeychainAccess(); + public LinuxSystemKeychainAccess() { + this.delegate = constructKeychainAccess(); } - private static Optional constructGnomeKeyringKeychainAccess() { - try { + private static Optional constructKeychainAccess() { + try { // is gnome-keyring or kwallet installed? Class clazz = Class.forName("org.cryptomator.keychain.LinuxSecretServiceKeychainAccessImpl"); KeychainAccessStrategy instance = (KeychainAccessStrategy) clazz.getDeclaredConstructor().newInstance(); + if (instance.isSupported()) return Optional.of(instance); + clazz = Class.forName("org.cryptomator.keychain.LinuxKDEWalletKeychainAccessImpl"); + instance = (KeychainAccessStrategy) clazz.getDeclaredConstructor().newInstance(); return Optional.of(instance); } catch (Exception e) { return Optional.empty(); diff --git a/main/pom.xml b/main/pom.xml index 0e9dd8a26..8702b89d9 100644 --- a/main/pom.xml +++ b/main/pom.xml @@ -34,8 +34,9 @@ 14 3.11 1.1.0 + 1.0.1 3.10.3 - 1.0.3 + 2.1.0 29.0-jre 2.22 2.8.6 @@ -168,6 +169,11 @@ secret-service ${secret-service.version} + + org.purejava + kdewallet + ${kdewallet.version} + @@ -178,7 +184,7 @@ - org.fxmisc.easybind + com.tobiasdiez easybind ${easybind.version} diff --git a/main/ui/pom.xml b/main/ui/pom.xml index c27fbb1aa..0991215cc 100644 --- a/main/ui/pom.xml +++ b/main/ui/pom.xml @@ -35,7 +35,7 @@ - org.fxmisc.easybind + com.tobiasdiez easybind diff --git a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultLocationController.java b/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultLocationController.java index 5b4549db1..c1f224529 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultLocationController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultLocationController.java @@ -1,5 +1,6 @@ package org.cryptomator.ui.addvaultwizard; +import com.tobiasdiez.easybind.EasyBind; import dagger.Lazy; import javafx.beans.binding.Bindings; import javafx.beans.binding.BooleanBinding; @@ -83,11 +84,11 @@ public class CreateNewVaultLocationController implements FxController { public void initialize() { predefinedLocationToggler.selectedToggleProperty().addListener(this::togglePredefinedLocation); usePresetPath.bind(predefinedLocationToggler.selectedToggleProperty().isNotEqualTo(customRadioButton)); - vaultPath.addListener(this::vaultPathDidChange); + EasyBind.subscribe(vaultPath, this::vaultPathDidChange); } - private void vaultPathDidChange(@SuppressWarnings("unused") ObservableValue observable, @SuppressWarnings("unused") Path oldValue, Path newValue) { - if (!Files.notExists(newValue)) { + private void vaultPathDidChange(Path newValue) { + if ( newValue != null && !Files.notExists(newValue)) { warningText.set(resourceBundle.getString("addvaultwizard.new.fileAlreadyExists")); } else { warningText.set(null); diff --git a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java b/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java index 2c2f3885a..87152f7b5 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java @@ -32,7 +32,6 @@ import javax.inject.Named; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.channels.WritableByteChannel; -import java.nio.file.FileAlreadyExistsException; import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.NoSuchFileException; diff --git a/main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordController.java b/main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordController.java index 2d9998fb1..d357c8451 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordController.java @@ -68,7 +68,7 @@ public class ChangePasswordController implements FxController { public void finish() { try { CryptoFileSystemProvider.changePassphrase(vault.getPath(), MASTERKEY_FILENAME, oldPasswordField.getCharacters(), newPassword.get()); - LOG.info("Successfully changed password for {}", vault.getDisplayableName()); + LOG.info("Successfully changed password for {}", vault.getDisplayName()); window.close(); updatePasswordInSystemkeychain(); } catch (IOException e) { @@ -85,7 +85,7 @@ public class ChangePasswordController implements FxController { if (keychain.isPresent()) { try { keychain.get().changePassphrase(vault.getId(), CharBuffer.wrap(newPassword.get())); - LOG.info("Successfully updated password in system keychain for {}", vault.getDisplayableName()); + LOG.info("Successfully updated password in system keychain for {}", vault.getDisplayName()); } catch (KeychainAccessException e) { LOG.error("Failed to update password in system keychain.", e); } diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/NewPasswordController.java b/main/ui/src/main/java/org/cryptomator/ui/common/NewPasswordController.java index 24f3ac8db..310a2379d 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/common/NewPasswordController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/common/NewPasswordController.java @@ -1,5 +1,6 @@ package org.cryptomator.ui.common; +import com.tobiasdiez.easybind.EasyBind; import javafx.beans.Observable; import javafx.beans.binding.Bindings; import javafx.beans.binding.BooleanBinding; @@ -10,7 +11,6 @@ import javafx.fxml.FXML; import javafx.scene.control.Label; import org.cryptomator.ui.controls.FontAwesome5IconView; import org.cryptomator.ui.controls.NiceSecurePasswordField; -import org.fxmisc.easybind.EasyBind; import java.util.ResourceBundle; @@ -24,10 +24,12 @@ public class NewPasswordController implements FxController { public NiceSecurePasswordField passwordField; public NiceSecurePasswordField reenterField; public Label passwordStrengthLabel; + public FontAwesome5IconView passwordStrengthCheckmark; + public FontAwesome5IconView passwordStrengthWarning; + public FontAwesome5IconView passwordStrengthCross; public Label passwordMatchLabel; - public FontAwesome5IconView checkmark; - public FontAwesome5IconView warning; - public FontAwesome5IconView cross; + public FontAwesome5IconView passwordMatchCheckmark; + public FontAwesome5IconView passwordMatchCross; public NewPasswordController(ResourceBundle resourceBundle, PasswordStrengthUtil strengthRater, ObjectProperty password) { this.resourceBundle = resourceBundle; @@ -45,7 +47,7 @@ public class NewPasswordController implements FxController { BooleanBinding passwordsMatch = Bindings.createBooleanBinding(this::hasSamePasswordInBothFields, passwordField.textProperty(), reenterField.textProperty()); BooleanBinding reenterFieldNotEmpty = reenterField.textProperty().isNotEmpty(); passwordMatchLabel.visibleProperty().bind(reenterFieldNotEmpty); - passwordMatchLabel.graphicProperty().bind(Bindings.when(passwordsMatch.and(reenterFieldNotEmpty)).then(checkmark).otherwise(cross)); + passwordMatchLabel.graphicProperty().bind(Bindings.when(passwordsMatch.and(reenterFieldNotEmpty)).then(passwordMatchCheckmark).otherwise(passwordMatchCross)); passwordMatchLabel.textProperty().bind(Bindings.when(passwordsMatch.and(reenterFieldNotEmpty)).then(resourceBundle.getString("newPassword.passwordsMatch")).otherwise(resourceBundle.getString("newPassword.passwordsDoNotMatch"))); passwordField.textProperty().addListener(this::passwordsDidChange); @@ -56,11 +58,11 @@ public class NewPasswordController implements FxController { if (passwordField.getCharacters().length() == 0) { return null; } else if (passwordStrength.intValue() <= -1) { - return cross; + return passwordStrengthCross; } else if (passwordStrength.intValue() < 3) { - return warning; + return passwordStrengthWarning; } else { - return checkmark; + return passwordStrengthCheckmark; } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/VaultService.java b/main/ui/src/main/java/org/cryptomator/ui/common/VaultService.java index 3e09e1fad..8aadd8c98 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/common/VaultService.java +++ b/main/ui/src/main/java/org/cryptomator/ui/common/VaultService.java @@ -44,8 +44,8 @@ public class VaultService { */ public Task createRevealTask(Vault vault) { Task task = new RevealVaultTask(vault); - task.setOnSucceeded(evt -> LOG.info("Revealed {}", vault.getDisplayableName())); - task.setOnFailed(evt -> LOG.error("Failed to reveal " + vault.getDisplayableName(), evt.getSource().getException())); + task.setOnSucceeded(evt -> LOG.info("Revealed {}", vault.getDisplayName())); + task.setOnFailed(evt -> LOG.error("Failed to reveal " + vault.getDisplayName(), evt.getSource().getException())); return task; } @@ -68,8 +68,8 @@ public class VaultService { */ public Task createLockTask(Vault vault, boolean forced) { Task task = new LockVaultTask(vault, forced); - task.setOnSucceeded(evt -> LOG.info("Locked {}", vault.getDisplayableName())); - task.setOnFailed(evt -> LOG.error("Failed to lock " + vault.getDisplayableName(), evt.getSource().getException())); + task.setOnSucceeded(evt -> LOG.info("Locked {}", vault.getDisplayName())); + task.setOnFailed(evt -> LOG.error("Failed to lock " + vault.getDisplayName(), evt.getSource().getException())); return task; } @@ -94,7 +94,7 @@ public class VaultService { List> lockTasks = vaults.stream().map(v -> new LockVaultTask(v, forced)).collect(Collectors.toUnmodifiableList()); lockTasks.forEach(executorService::execute); Task> task = new WaitForTasksTask(lockTasks); - String vaultNames = vaults.stream().map(Vault::getDisplayableName).collect(Collectors.joining(", ")); + String vaultNames = vaults.stream().map(Vault::getDisplayName).collect(Collectors.joining(", ")); task.setOnSucceeded(evt -> LOG.info("Locked {}", vaultNames)); task.setOnFailed(evt -> LOG.error("Failed to lock vaults " + vaultNames, evt.getSource().getException())); return task; diff --git a/main/ui/src/main/java/org/cryptomator/ui/controls/DraggableListCell.java b/main/ui/src/main/java/org/cryptomator/ui/controls/DraggableListCell.java index 62f87c1f4..ca78bcac6 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controls/DraggableListCell.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controls/DraggableListCell.java @@ -8,6 +8,7 @@ *******************************************************************************/ package org.cryptomator.ui.controls; +import com.tobiasdiez.easybind.EasyBind; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.scene.SnapshotParameters; @@ -18,7 +19,6 @@ import javafx.scene.input.DragEvent; import javafx.scene.input.Dragboard; import javafx.scene.input.MouseEvent; import javafx.scene.input.TransferMode; -import org.fxmisc.easybind.EasyBind; import java.util.List; diff --git a/main/ui/src/main/java/org/cryptomator/ui/controls/PasswordStrengthIndicator.java b/main/ui/src/main/java/org/cryptomator/ui/controls/PasswordStrengthIndicator.java index a1619968b..156da3b0b 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controls/PasswordStrengthIndicator.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controls/PasswordStrengthIndicator.java @@ -1,12 +1,12 @@ package org.cryptomator.ui.controls; +import com.tobiasdiez.easybind.EasyBind; import javafx.beans.binding.BooleanBinding; import javafx.beans.property.IntegerProperty; import javafx.beans.property.SimpleIntegerProperty; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.scene.layout.Region; -import org.fxmisc.easybind.EasyBind; public class PasswordStrengthIndicator extends HBox { diff --git a/main/ui/src/main/java/org/cryptomator/ui/forgetPassword/ForgetPasswordController.java b/main/ui/src/main/java/org/cryptomator/ui/forgetPassword/ForgetPasswordController.java index ea1e0530e..008668126 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/forgetPassword/ForgetPasswordController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/forgetPassword/ForgetPasswordController.java @@ -41,7 +41,7 @@ public class ForgetPasswordController implements FxController { if (keychain.isPresent()) { try { keychain.get().deletePassphrase(vault.getId()); - LOG.debug("Forgot password for vault {}.", vault.getDisplayableName()); + LOG.debug("Forgot password for vault {}.", vault.getDisplayName()); confirmedResult.setValue(true); } catch (KeychainAccessException e) { LOG.error("Failed to remove entry from system keychain.", e); diff --git a/main/ui/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java b/main/ui/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java index 4d80a3451..8402bddf2 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java +++ b/main/ui/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java @@ -1,5 +1,6 @@ package org.cryptomator.ui.fxapp; +import com.tobiasdiez.easybind.EasyBind; import dagger.Lazy; import javafx.application.Application; import javafx.application.Platform; @@ -64,7 +65,7 @@ public class FxApplication extends Application { LOG.trace("FxApplication.start()"); Platform.setImplicitExit(false); - hasVisibleStages.addListener(this::hasVisibleStagesChanged); + EasyBind.subscribe(hasVisibleStages, this::hasVisibleStagesChanged); settings.theme().addListener(this::themeChanged); loadSelectedStyleSheet(settings.theme().get()); @@ -75,7 +76,7 @@ public class FxApplication extends Application { throw new UnsupportedOperationException("Use start() instead."); } - private void hasVisibleStagesChanged(@SuppressWarnings("unused") ObservableValue observableValue, @SuppressWarnings("unused") boolean oldValue, boolean newValue) { + private void hasVisibleStagesChanged(boolean newValue) { if (newValue) { macFunctions.map(MacFunctions::uiState).ifPresent(MacApplicationUiState::transformToForegroundApplication); } else { @@ -100,7 +101,7 @@ public class FxApplication extends Application { public void startUnlockWorkflow(Vault vault, Optional owner) { Platform.runLater(() -> { unlockWindowBuilderProvider.get().vault(vault).owner(owner).build().startUnlockWorkflow(); - LOG.debug("Showing UnlockWindow for {}", vault.getDisplayableName()); + LOG.debug("Showing UnlockWindow for {}", vault.getDisplayName()); }); } diff --git a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailController.java b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailController.java index 36a746039..e384a67bc 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailController.java @@ -1,5 +1,6 @@ package org.cryptomator.ui.mainwindow; +import com.tobiasdiez.easybind.EasyBind; import javafx.beans.binding.Binding; import javafx.beans.binding.BooleanBinding; import javafx.beans.property.ObjectProperty; @@ -10,7 +11,6 @@ import org.cryptomator.common.vaults.VaultState; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.controls.FontAwesome5Icon; import org.cryptomator.ui.fxapp.FxApplication; -import org.fxmisc.easybind.EasyBind; import javax.inject.Inject; @@ -26,17 +26,23 @@ public class VaultDetailController implements FxController { VaultDetailController(ObjectProperty vault, FxApplication application) { this.vault = vault; this.application = application; - this.glyph = EasyBind.select(vault).selectObject(Vault::stateProperty).map(this::getGlyphForVaultState).orElse(FontAwesome5Icon.EXCLAMATION_TRIANGLE); + this.glyph = EasyBind.select(vault) // + .selectObject(Vault::stateProperty) // + .map(this::getGlyphForVaultState); this.anyVaultSelected = vault.isNotNull(); } private FontAwesome5Icon getGlyphForVaultState(VaultState state) { - return switch (state) { - case LOCKED -> FontAwesome5Icon.LOCK; - case PROCESSING -> FontAwesome5Icon.SPINNER; - case UNLOCKED -> FontAwesome5Icon.LOCK_OPEN; - case NEEDS_MIGRATION, MISSING, ERROR -> FontAwesome5Icon.EXCLAMATION_TRIANGLE; - }; + if (state != null) { + return switch (state) { + case LOCKED -> FontAwesome5Icon.LOCK; + case PROCESSING -> FontAwesome5Icon.SPINNER; + case UNLOCKED -> FontAwesome5Icon.LOCK_OPEN; + case NEEDS_MIGRATION, MISSING, ERROR -> FontAwesome5Icon.EXCLAMATION_TRIANGLE; + }; + } else { + return FontAwesome5Icon.EXCLAMATION_TRIANGLE; + } } @FXML diff --git a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailLockedController.java b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailLockedController.java index 55720521b..f615d51ca 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailLockedController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailLockedController.java @@ -1,5 +1,6 @@ package org.cryptomator.ui.mainwindow; +import com.tobiasdiez.easybind.EasyBind; import javafx.beans.binding.BooleanExpression; import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyObjectProperty; @@ -11,7 +12,6 @@ import org.cryptomator.keychain.KeychainManager; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.fxapp.FxApplication; import org.cryptomator.ui.vaultoptions.VaultOptionsComponent; -import org.fxmisc.easybind.EasyBind; import javax.inject.Inject; import java.util.Optional; diff --git a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnknownErrorController.java b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnknownErrorController.java index 4c23ba4ae..b975174f8 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnknownErrorController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnknownErrorController.java @@ -1,10 +1,10 @@ package org.cryptomator.ui.mainwindow; +import com.tobiasdiez.easybind.EasyBind; import javafx.beans.binding.Binding; import javafx.beans.property.ObjectProperty; import org.cryptomator.common.vaults.Vault; import org.cryptomator.ui.common.FxController; -import org.fxmisc.easybind.EasyBind; import javax.inject.Inject; import java.io.ByteArrayOutputStream; @@ -18,14 +18,20 @@ public class VaultDetailUnknownErrorController implements FxController { @Inject public VaultDetailUnknownErrorController(ObjectProperty vault) { - this.stackTrace = EasyBind.select(vault).selectObject(Vault::lastKnownExceptionProperty).map(this::provideStackTrace).orElse(""); + this.stackTrace = EasyBind.select(vault) // + .selectObject(Vault::lastKnownExceptionProperty) // + .map(this::provideStackTrace); } private String provideStackTrace(Throwable cause) { // TODO deduplicate ErrorModule.java - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - cause.printStackTrace(new PrintStream(baos)); - return baos.toString(StandardCharsets.UTF_8); + if (cause != null) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + cause.printStackTrace(new PrintStream(baos)); + return baos.toString(StandardCharsets.UTF_8); + } else { + return ""; + } } /* Getter/Setter */ diff --git a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultListCellController.java b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultListCellController.java index 6703ec674..999afaaf1 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultListCellController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultListCellController.java @@ -1,5 +1,6 @@ package org.cryptomator.ui.mainwindow; +import com.tobiasdiez.easybind.EasyBind; import javafx.beans.binding.Binding; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; @@ -7,7 +8,6 @@ import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.VaultState; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.controls.FontAwesome5Icon; -import org.fxmisc.easybind.EasyBind; import javax.inject.Inject; @@ -19,16 +19,22 @@ public class VaultListCellController implements FxController { @Inject VaultListCellController() { - this.glyph = EasyBind.select(vault).selectObject(Vault::stateProperty).map(this::getGlyphForVaultState).orElse(FontAwesome5Icon.EXCLAMATION_TRIANGLE); + this.glyph = EasyBind.select(vault) // + .selectObject(Vault::stateProperty) // + .map(this::getGlyphForVaultState); } private FontAwesome5Icon getGlyphForVaultState(VaultState state) { - return switch (state) { - case LOCKED -> FontAwesome5Icon.LOCK; - case PROCESSING -> FontAwesome5Icon.SPINNER; - case UNLOCKED -> FontAwesome5Icon.LOCK_OPEN; - case NEEDS_MIGRATION, MISSING, ERROR -> FontAwesome5Icon.EXCLAMATION_TRIANGLE; - }; + if(state != null){ + return switch (state) { + case LOCKED -> FontAwesome5Icon.LOCK; + case PROCESSING -> FontAwesome5Icon.SPINNER; + case UNLOCKED -> FontAwesome5Icon.LOCK_OPEN; + case NEEDS_MIGRATION, MISSING, ERROR -> FontAwesome5Icon.EXCLAMATION_TRIANGLE; + }; + } else { + return FontAwesome5Icon.EXCLAMATION_TRIANGLE; + } } /* Getter/Setter */ diff --git a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java index c0a6c7720..d1aecb19f 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java @@ -1,16 +1,15 @@ package org.cryptomator.ui.mainwindow; +import com.tobiasdiez.easybind.EasyBind; import javafx.beans.binding.Bindings; import javafx.beans.binding.BooleanBinding; import javafx.beans.property.ObjectProperty; -import javafx.beans.value.ObservableValue; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.scene.control.ListView; import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.VaultListManager; -import org.cryptomator.common.vaults.VaultState; import org.cryptomator.ui.addvaultwizard.AddVaultWizardComponent; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.removevault.RemoveVaultComponent; @@ -18,7 +17,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; -import java.io.IOException; @MainWindowScoped public class VaultListController implements FxController { @@ -43,7 +41,7 @@ public class VaultListController implements FxController { this.removeVault = removeVault; this.noVaultSelected = selectedVault.isNull(); this.emptyVaultList = Bindings.isEmpty(vaults); - selectedVault.addListener(this::selectedVaultDidChange); + EasyBind.subscribe(selectedVault, this::selectedVaultDidChange); } public void initialize() { @@ -60,11 +58,10 @@ public class VaultListController implements FxController { }); } - private void selectedVaultDidChange(@SuppressWarnings("unused") ObservableValue observableValue, @SuppressWarnings("unused") Vault oldValue, Vault newValue) { - if (newValue == null) { - return; + private void selectedVaultDidChange(Vault newValue) { + if (newValue != null) { + VaultListManager.redetermineVaultState(newValue); } - VaultListManager.redetermineVaultState(newValue); } @FXML diff --git a/main/ui/src/main/java/org/cryptomator/ui/migration/MigrationRunController.java b/main/ui/src/main/java/org/cryptomator/ui/migration/MigrationRunController.java index 3123d4cb2..5233d979b 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/migration/MigrationRunController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/migration/MigrationRunController.java @@ -116,10 +116,10 @@ public class MigrationRunController implements FxController { return migrators.needsMigration(vault.getPath(), MASTERKEY_FILENAME); }).onSuccess(needsAnotherMigration -> { if (needsAnotherMigration) { - LOG.info("Migration of '{}' succeeded, but another migration is required.", vault.getDisplayableName()); + LOG.info("Migration of '{}' succeeded, but another migration is required.", vault.getDisplayName()); vault.setState(VaultState.NEEDS_MIGRATION); } else { - LOG.info("Migration of '{}' succeeded.", vault.getDisplayableName()); + LOG.info("Migration of '{}' succeeded.", vault.getDisplayName()); vault.setState(VaultState.LOCKED); passwordField.wipe(); window.setScene(successScene.get()); diff --git a/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartModule.java b/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartModule.java index d006d8681..fa01756ab 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartModule.java @@ -3,6 +3,7 @@ package org.cryptomator.ui.preferences; import dagger.Module; import dagger.Provides; import org.apache.commons.lang3.SystemUtils; +import org.cryptomator.common.Environment; import org.cryptomator.jni.MacFunctions; import java.util.Optional; @@ -12,7 +13,7 @@ abstract class AutoStartModule { @Provides @PreferencesScoped - public static Optional provideAutoStartStrategy(Optional macFunctions) { + public static Optional provideAutoStartStrategy(Optional macFunctions, Environment env) { if (SystemUtils.IS_OS_MAC_OSX && macFunctions.isPresent()) { return Optional.of(new AutoStartMacStrategy(macFunctions.get())); } else if (SystemUtils.IS_OS_WINDOWS) { diff --git a/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartWinStrategy.java b/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartWinStrategy.java index f4a8b0578..438c468bd 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartWinStrategy.java +++ b/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartWinStrategy.java @@ -4,76 +4,185 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; +/** + * OS specific class to check, en- and disable the auto start on Windows. + *

+ * Two strategies are implemented for this feature, the first uses the registry and the second one the autostart folder. + *

+ * The registry strategy checks/add/removes at the registry key {@value HKCU_AUTOSTART_KEY} an entry for Cryptomator. + * The folder strategy checks/add/removes at the location {@value WINDOWS_START_MENU_ENTRY}. + *

+ * To check if the feature is active, both strategies are applied. + * To enable the feature, first the registry is tried and only on failure the autostart folder is used. + * To disable it, first it is determined by an internal state, which strategies must be used and in the second step those are executed. + * + * @apiNote This class is not thread safe, hence it should be avoided to call its methods simultaniously by different threads. + */ class AutoStartWinStrategy implements AutoStartStrategy { private static final Logger LOG = LoggerFactory.getLogger(AutoStartWinStrategy.class); private static final String HKCU_AUTOSTART_KEY = "\"HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run\""; private static final String AUTOSTART_VALUE = "Cryptomator"; + private static final String WINDOWS_START_MENU_ENTRY = "\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Cryptomator.lnk"; + private final String exePath; + private boolean activatedUsingFolder; + private boolean activatedUsingRegistry; + public AutoStartWinStrategy(String exePath) { this.exePath = exePath; + this.activatedUsingFolder = false; + this.activatedUsingRegistry = false; } @Override public CompletionStage isAutoStartEnabled() { + return isAutoStartEnabledUsingRegistry().thenCombine(isAutoStartEnabledUsingFolder(), (bReg, bFolder) -> bReg || bFolder); + } + + private CompletableFuture isAutoStartEnabledUsingFolder() { + Path autoStartEntry = Path.of(System.getProperty("user.home") + WINDOWS_START_MENU_ENTRY); + this.activatedUsingFolder = Files.exists(autoStartEntry); + return CompletableFuture.completedFuture(activatedUsingFolder); + } + + private CompletableFuture isAutoStartEnabledUsingRegistry() { ProcessBuilder regQuery = new ProcessBuilder("reg", "query", HKCU_AUTOSTART_KEY, // "/v", AUTOSTART_VALUE); try { Process proc = regQuery.start(); - return proc.onExit().thenApply(p -> p.exitValue() == 0); + return proc.onExit().thenApply(p -> { + this.activatedUsingRegistry = p.exitValue() == 0; + return activatedUsingRegistry; + }); } catch (IOException e) { - LOG.warn("Failed to query {} from registry key {}", AUTOSTART_VALUE, HKCU_AUTOSTART_KEY); + LOG.debug("Failed to query {} from registry key {}", AUTOSTART_VALUE, HKCU_AUTOSTART_KEY); return CompletableFuture.completedFuture(false); } } @Override public void enableAutoStart() throws TogglingAutoStartFailedException { + try { + enableAutoStartUsingRegistry().thenAccept((Void v) -> this.activatedUsingRegistry = true).exceptionallyCompose(e -> { + LOG.debug("Falling back to using autostart folder."); + return this.enableAutoStartUsingFolder(); + }).thenAccept((Void v) -> this.activatedUsingFolder = true).get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new TogglingAutoStartFailedException("Execution of enabling auto start setting was interrupted."); + } catch (ExecutionException e) { + throw new TogglingAutoStartFailedException("Enabling auto start failed both using registry and auto start folder."); + } + } + + private CompletableFuture enableAutoStartUsingRegistry() { ProcessBuilder regAdd = new ProcessBuilder("reg", "add", HKCU_AUTOSTART_KEY, // "/v", AUTOSTART_VALUE, // "/t", "REG_SZ", // "/d", "\"" + exePath + "\"", // "/f"); - String command = regAdd.command().stream().collect(Collectors.joining(" ")); try { Process proc = regAdd.start(); - boolean finishedInTime = waitForProcess(proc, 5, TimeUnit.SECONDS); - if (finishedInTime) { + boolean finishedInTime = waitForProcessOrCancel(proc, 5, TimeUnit.SECONDS); + if (finishedInTime && proc.exitValue() == 0) { LOG.debug("Added {} to registry key {}.", AUTOSTART_VALUE, HKCU_AUTOSTART_KEY); + return CompletableFuture.completedFuture(null); } else { - throw new TogglingAutoStartFailedException("Adding registry value failed."); + throw new IOException("Process exited with error code " + proc.exitValue()); } } catch (IOException e) { - throw new TogglingAutoStartFailedException("Adding registry value failed. " + command, e); + LOG.debug("Registry could not be edited to set auto start.", e); + return CompletableFuture.failedFuture(new SystemCommandException("Adding registry value failed.")); } } + private CompletableFuture enableAutoStartUsingFolder() { + String autoStartFolderEntry = System.getProperty("user.home") + WINDOWS_START_MENU_ENTRY; + String createShortcutCommand = "$s=(New-Object -COM WScript.Shell).CreateShortcut('" + autoStartFolderEntry + "');$s.TargetPath='" + exePath + "';$s.Save();"; + ProcessBuilder shortcutAdd = new ProcessBuilder("cmd", "/c", "Start powershell " + createShortcutCommand); + try { + Process proc = shortcutAdd.start(); + boolean finishedInTime = waitForProcessOrCancel(proc, 5, TimeUnit.SECONDS); + if (finishedInTime && proc.exitValue() == 0) { + LOG.debug("Created file {} for auto start.", autoStartFolderEntry); + return CompletableFuture.completedFuture(null); + } else { + throw new IOException("Process exited with error code " + proc.exitValue()); + } + } catch (IOException e) { + LOG.debug("Adding entry to auto start folder failed.", e); + return CompletableFuture.failedFuture(new SystemCommandException("Adding entry to auto start folder failed.")); + } + } + + @Override public void disableAutoStart() throws TogglingAutoStartFailedException { - ProcessBuilder regRemove = new ProcessBuilder("reg", "delete", HKCU_AUTOSTART_KEY, // - "/v", AUTOSTART_VALUE, // - "/f"); - String command = regRemove.command().stream().collect(Collectors.joining(" ")); - try { - Process proc = regRemove.start(); - boolean finishedInTime = waitForProcess(proc, 5, TimeUnit.SECONDS); - if (finishedInTime) { - LOG.debug("Removed {} from registry key {}.", AUTOSTART_VALUE, HKCU_AUTOSTART_KEY); - } else { - throw new TogglingAutoStartFailedException("Removing registry value failed."); - } - } catch (IOException e) { - throw new TogglingAutoStartFailedException("Removing registry value failed. " + command, e); + if (activatedUsingRegistry) { + disableAutoStartUsingRegistry().whenComplete((voit, ex) -> { + if (ex == null) { + this.activatedUsingRegistry = false; + } + }); + } + + if (activatedUsingFolder) { + disableAutoStartUsingFolder().whenComplete((voit, ex) -> { + if (ex == null) { + this.activatedUsingFolder = false; + } + }); + } + + if (activatedUsingRegistry || activatedUsingFolder) { + throw new TogglingAutoStartFailedException("Disabling auto start failed using registry and/or auto start folder."); } } - private static boolean waitForProcess(Process proc, int timeout, TimeUnit timeUnit) { + public CompletableFuture disableAutoStartUsingRegistry() { + ProcessBuilder regRemove = new ProcessBuilder("reg", "delete", HKCU_AUTOSTART_KEY, // + "/v", AUTOSTART_VALUE, // + "/f"); + try { + Process proc = regRemove.start(); + boolean finishedInTime = waitForProcessOrCancel(proc, 5, TimeUnit.SECONDS); + if (finishedInTime && proc.exitValue() == 0) { + LOG.debug("Removed {} from registry key {}.", AUTOSTART_VALUE, HKCU_AUTOSTART_KEY); + return CompletableFuture.completedFuture(null); + } else { + throw new IOException("Process exited with error code " + proc.exitValue()); + } + } catch (IOException e) { + LOG.debug("Registry could not be edited to remove auto start.", e); + return CompletableFuture.failedFuture(new SystemCommandException("Removing registry value failed.")); + } + } + + private CompletableFuture disableAutoStartUsingFolder() { + try { + Files.delete(Path.of(WINDOWS_START_MENU_ENTRY)); + LOG.debug("Successfully deleted {}.", WINDOWS_START_MENU_ENTRY); + return CompletableFuture.completedFuture(null); + } catch (NoSuchFileException e) { + //that is also okay + return CompletableFuture.completedFuture(null); + } catch (IOException e) { + LOG.debug("Failed to delete entry from auto start folder.", e); + return CompletableFuture.failedFuture(e); + } + } + + private static boolean waitForProcessOrCancel(Process proc, int timeout, TimeUnit timeUnit) { boolean finishedInTime = false; try { finishedInTime = proc.waitFor(timeout, timeUnit); @@ -88,4 +197,11 @@ class AutoStartWinStrategy implements AutoStartStrategy { return finishedInTime; } + private class SystemCommandException extends RuntimeException { + + public SystemCommandException(String msg) { + super(msg); + } + } + } diff --git a/main/ui/src/main/java/org/cryptomator/ui/quit/QuitController.java b/main/ui/src/main/java/org/cryptomator/ui/quit/QuitController.java index 66dd13ade..7255115ea 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/quit/QuitController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/quit/QuitController.java @@ -53,7 +53,7 @@ public class QuitController implements FxController { Task> lockAllTask = vaultService.createLockAllTask(unlockedVaults, false); lockAllTask.setOnSucceeded(evt -> { - LOG.info("Locked {}", lockAllTask.getValue().stream().map(Vault::getDisplayableName).collect(Collectors.joining(", "))); + LOG.info("Locked {}", lockAllTask.getValue().stream().map(Vault::getDisplayName).collect(Collectors.joining(", "))); if (unlockedVaults.isEmpty()) { window.close(); response.performQuit(); diff --git a/main/ui/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyModule.java b/main/ui/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyModule.java index 48e81f3a5..9ad3d73ad 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyModule.java @@ -9,7 +9,6 @@ import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.scene.Scene; -import javafx.scene.image.Image; import javafx.stage.Modality; import javafx.stage.Stage; import org.cryptomator.common.vaults.Vault; @@ -25,7 +24,6 @@ import org.cryptomator.ui.common.StageFactory; import javax.inject.Named; import javax.inject.Provider; -import java.util.List; import java.util.Map; import java.util.ResourceBundle; @@ -107,7 +105,7 @@ abstract class RecoveryKeyModule { @IntoMap @FxControllerKey(RecoveryKeyDisplayController.class) static FxController provideRecoveryKeyDisplayController(@RecoveryKeyWindow Stage window, @RecoveryKeyWindow Vault vault, @RecoveryKeyWindow StringProperty recoveryKey, ResourceBundle localization) { - return new RecoveryKeyDisplayController(window, vault.getDisplayableName(), recoveryKey.get(), localization); + return new RecoveryKeyDisplayController(window, vault.getDisplayName(), recoveryKey.get(), localization); } @Binds diff --git a/main/ui/src/main/java/org/cryptomator/ui/removevault/RemoveVaultController.java b/main/ui/src/main/java/org/cryptomator/ui/removevault/RemoveVaultController.java index e2d5ec024..836ed519f 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/removevault/RemoveVaultController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/removevault/RemoveVaultController.java @@ -34,7 +34,7 @@ public class RemoveVaultController implements FxController { @FXML public void finish() { vaults.remove(vault); - LOG.debug("Removing vault {}.", vault.getDisplayableName()); + LOG.debug("Removing vault {}.", vault.getDisplayName()); window.close(); } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java b/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java index af12beebe..1610c6862 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java @@ -80,7 +80,7 @@ class TrayMenuController { } private Menu buildSubmenu(Vault vault) { - Menu submenu = new Menu(vault.getDisplayableName()); + Menu submenu = new Menu(vault.getDisplayName()); if (vault.isLocked()) { MenuItem unlockItem = new MenuItem(resourceBundle.getString("traymenu.vault.unlock")); diff --git a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java index 7310a369c..537eba9a8 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java @@ -77,7 +77,7 @@ public class UnlockController implements FxController { this.unlockButtonContentDisplay = Bindings.createObjectBinding(this::getUnlockButtonContentDisplay, passwordEntryLock.awaitingInteraction()); this.userInteractionDisabled = passwordEntryLock.awaitingInteraction().not(); this.unlockButtonDisabled = new SimpleBooleanProperty(); - this.vaultName = WeakBindings.bindString(vault.displayableNameProperty()); + this.vaultName = WeakBindings.bindString(vault.displayNameProperty()); this.window.setOnCloseRequest(windowEvent -> cancel()); } diff --git a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java index e07583c84..bef612dc1 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java @@ -82,7 +82,7 @@ abstract class UnlockModule { @UnlockScoped static Stage provideStage(StageFactory factory, @UnlockWindow Vault vault, @Named("unlockWindowOwner") Optional owner) { Stage stage = factory.create(); - stage.setTitle(vault.getDisplayableName()); + stage.setTitle(vault.getDisplayName()); stage.setResizable(false); if (owner.isPresent()) { stage.initOwner(owner.get()); diff --git a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java index d216fc12b..ea692f783 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java +++ b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java @@ -129,7 +129,7 @@ public class UnlockWorkflow extends Task { } private void handleSuccess() { - LOG.info("Unlock of '{}' succeeded.", vault.getDisplayableName()); + LOG.info("Unlock of '{}' succeeded.", vault.getDisplayName()); if (savePassword.get()) { savePasswordToSystemkeychain(); } diff --git a/main/ui/src/main/java/org/cryptomator/ui/vaultoptions/GeneralVaultOptionsController.java b/main/ui/src/main/java/org/cryptomator/ui/vaultoptions/GeneralVaultOptionsController.java index ddb6e0553..9849b0513 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/vaultoptions/GeneralVaultOptionsController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/vaultoptions/GeneralVaultOptionsController.java @@ -3,6 +3,7 @@ package org.cryptomator.ui.vaultoptions; import javafx.fxml.FXML; import javafx.scene.control.CheckBox; import javafx.scene.control.ChoiceBox; +import javafx.scene.control.TextField; import javafx.util.StringConverter; import org.cryptomator.common.settings.UiTheme; import org.cryptomator.common.settings.WhenUnlocked; @@ -18,6 +19,7 @@ public class GeneralVaultOptionsController implements FxController { private final Vault vault; private final ResourceBundle resourceBundle; + public TextField vaultName; public CheckBox unlockOnStartupCheckbox; public ChoiceBox actionAfterUnlockChoiceBox; @@ -29,6 +31,7 @@ public class GeneralVaultOptionsController implements FxController { @FXML public void initialize() { + vaultName.textProperty().bindBidirectional(vault.getVaultSettings().displayName()); unlockOnStartupCheckbox.selectedProperty().bindBidirectional(vault.getVaultSettings().unlockAfterStartup()); actionAfterUnlockChoiceBox.getItems().addAll(WhenUnlocked.values()); actionAfterUnlockChoiceBox.valueProperty().bindBidirectional(vault.getVaultSettings().actionAfterUnlock()); diff --git a/main/ui/src/main/java/org/cryptomator/ui/vaultoptions/MountOptionsController.java b/main/ui/src/main/java/org/cryptomator/ui/vaultoptions/MountOptionsController.java index 975f40bf3..df3cf7039 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/vaultoptions/MountOptionsController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/vaultoptions/MountOptionsController.java @@ -43,7 +43,6 @@ public class MountOptionsController implements FxController { private final BooleanBinding webDavAndWindows; private final WindowsDriveLetters windowsDriveLetters; private final ResourceBundle resourceBundle; - public TextField driveName; public CheckBox readOnlyCheckbox; public CheckBox customMountFlagsCheckbox; public TextField mountFlags; @@ -70,7 +69,6 @@ public class MountOptionsController implements FxController { @FXML public void initialize() { - driveName.textProperty().bindBidirectional(vault.getVaultSettings().mountName()); // readonly: readOnlyCheckbox.selectedProperty().bindBidirectional(vault.getVaultSettings().usesReadOnlyMode()); diff --git a/main/ui/src/main/java/org/cryptomator/ui/vaultoptions/VaultOptionsModule.java b/main/ui/src/main/java/org/cryptomator/ui/vaultoptions/VaultOptionsModule.java index d0aa49281..f39af7e34 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/vaultoptions/VaultOptionsModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/vaultoptions/VaultOptionsModule.java @@ -5,7 +5,6 @@ import dagger.Module; import dagger.Provides; import dagger.multibindings.IntoMap; import javafx.scene.Scene; -import javafx.scene.image.Image; import javafx.stage.Modality; import javafx.stage.Stage; import org.cryptomator.common.vaults.Vault; @@ -20,9 +19,7 @@ import org.cryptomator.ui.common.StageFactory; import org.cryptomator.ui.mainwindow.MainWindow; import org.cryptomator.ui.recoverykey.RecoveryKeyComponent; -import javax.inject.Named; import javax.inject.Provider; -import java.util.List; import java.util.Map; import java.util.ResourceBundle; @@ -41,7 +38,7 @@ abstract class VaultOptionsModule { @VaultOptionsScoped static Stage provideStage(StageFactory factory, @MainWindow Stage owner, @VaultOptionsWindow Vault vault) { Stage stage = factory.create(); - stage.setTitle(vault.getDisplayableName()); + stage.setTitle(vault.getDisplayName()); stage.setResizable(true); stage.setMinWidth(400); stage.setMinHeight(300); diff --git a/main/ui/src/main/resources/fxml/addvault_success.fxml b/main/ui/src/main/resources/fxml/addvault_success.fxml index 6bb4808c5..fd0309450 100644 --- a/main/ui/src/main/resources/fxml/addvault_success.fxml +++ b/main/ui/src/main/resources/fxml/addvault_success.fxml @@ -30,7 +30,7 @@ - + diff --git a/main/ui/src/main/resources/fxml/changepassword.fxml b/main/ui/src/main/resources/fxml/changepassword.fxml index 07fe7cbc4..18c1b9383 100644 --- a/main/ui/src/main/resources/fxml/changepassword.fxml +++ b/main/ui/src/main/resources/fxml/changepassword.fxml @@ -19,7 +19,7 @@ - + diff --git a/main/ui/src/main/resources/fxml/migration_run.fxml b/main/ui/src/main/resources/fxml/migration_run.fxml index 03be9a140..f70f27ce3 100644 --- a/main/ui/src/main/resources/fxml/migration_run.fxml +++ b/main/ui/src/main/resources/fxml/migration_run.fxml @@ -21,7 +21,7 @@ - + diff --git a/main/ui/src/main/resources/fxml/migration_start.fxml b/main/ui/src/main/resources/fxml/migration_start.fxml index c568ae9cb..c836a9393 100644 --- a/main/ui/src/main/resources/fxml/migration_start.fxml +++ b/main/ui/src/main/resources/fxml/migration_start.fxml @@ -28,7 +28,7 @@ - + diff --git a/main/ui/src/main/resources/fxml/migration_success.fxml b/main/ui/src/main/resources/fxml/migration_success.fxml index 59e997550..9fd081757 100644 --- a/main/ui/src/main/resources/fxml/migration_success.fxml +++ b/main/ui/src/main/resources/fxml/migration_success.fxml @@ -25,7 +25,7 @@ - + diff --git a/main/ui/src/main/resources/fxml/new_password.fxml b/main/ui/src/main/resources/fxml/new_password.fxml index 7593a1ccd..c406b8c70 100644 --- a/main/ui/src/main/resources/fxml/new_password.fxml +++ b/main/ui/src/main/resources/fxml/new_password.fxml @@ -12,9 +12,11 @@ spacing="6" alignment="CENTER_LEFT"> - - - + + + + +