diff --git a/.gitignore b/.gitignore index 7051b0cd0..52f9a6ccd 100644 --- a/.gitignore +++ b/.gitignore @@ -9,15 +9,13 @@ .settings .project .classpath + +# Maven # target/ -test-output/ -# IntelliJ Settings Files # -.idea/ -out/ -.idea_modules/ -*.iws -*.iml - -# Temporary file created by test launcher -main/launcher/.ipcPort.tmp +# IntelliJ Settings Files (https://intellij-support.jetbrains.com/hc/en-us/articles/206544839-How-to-manage-projects-under-Version-Control-Systems) # +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/dictionaries +.idea/**/libraries/ +*.iml \ No newline at end of file diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 000000000..f9944b4aa --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +Cryptomator \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 000000000..d6cb2c866 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,51 @@ + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 000000000..79ee123c2 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 000000000..eb6e630bd --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 000000000..74a53dde5 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 000000000..146ab09b7 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 000000000..fd8e48573 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 000000000..94a25f7f4 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index b7789ec3d..00a1351d5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,13 @@ language: java sudo: false jdk: -- oraclejdk10 +- oraclejdk9 cache: directories: - $HOME/.m2 env: global: - - secure: "lV9OwUbHMrMpLUH1CY+Z4puLDdFXytudyPlG1eGRsesdpuG6KM3uQVz6uAtf6lrU8DRbMM/T7ML+PmvQ4UoPPYLdLxESLLBat2qUPOIVBOhTSlCc7I0DmGy04CSvkeMy8dPaQC0ukgNiR7zwoNzfcpGRN/U9S8tziDruuHoZSrg=" # BINTRAY_API_KEY + - secure: "HftEaabMmWn5GwKFKksUkOcelc3Mn7xazwAEy+4d4gL1+F8VhID/6DCK7nas+afUymWnxTano8Rv4Ci5MWryNkNkTH+FUPWmF3xWezc3hajSyS7RB92IZ8VPetl4Fo8UI1WwM5apDEaugalPxkIf8a7N+lpG5X/Gpumwzo3Be3w=" # BINTRAY_API_KEY - secure: "oWFgRTVP6lyTa7qVxlvkpm20MtVc3BtmsNXQJS6bfg2A0o/iCQMNx7OD59BaafCLGRKvCcJVESiC8FlSylVMS7CDSyYu0gg70NUiIuHp4NBM5inFWYCy/PdQsCTzr5uvNG+rMFQpMFRaCV0FrfM3tLondcVkhsHL68l93Xoexx4=" # CODACY_PROJECT_TOKEN - secure: "zJxgytA2Ks5Xzv+7kUaUq+EBFNQw9Qec63lcMJVuXVWczjL16nKW1EzzV515ag+OWL46z3lEPForDhufw0VtFnNmaX68jkO0mp01eLrHApc1llN2Y/U8GBXfNNazN4+Kom4H+z/AO+wJr8EsKMMUczCdQ3APgd9uVI0hzXw/Z3M=" # GITHUB_API_KEY addons: diff --git a/main/ant-kit/.gitignore b/main/ant-kit/.gitignore deleted file mode 100644 index b83d22266..000000000 --- a/main/ant-kit/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/target/ diff --git a/main/ant-kit/pom.xml b/main/ant-kit/pom.xml index acd09e3f5..090e5142c 100644 --- a/main/ant-kit/pom.xml +++ b/main/ant-kit/pom.xml @@ -4,7 +4,7 @@ org.cryptomator main - 1.4.0-SNAPSHOT + 1.5.0-SNAPSHOT ant-kit pom diff --git a/main/commons/pom.xml b/main/commons/pom.xml index 80d96e00c..723568fa6 100644 --- a/main/commons/pom.xml +++ b/main/commons/pom.xml @@ -4,7 +4,7 @@ org.cryptomator main - 1.4.0-SNAPSHOT + 1.5.0-SNAPSHOT commons Cryptomator Commons 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 087c9b200..5c13b66bc 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 @@ -5,16 +5,6 @@ *******************************************************************************/ package org.cryptomator.common.settings; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.nio.file.Path; -import java.util.Base64; -import java.util.Objects; -import java.util.UUID; - -import org.apache.commons.lang3.StringUtils; -import org.fxmisc.easybind.EasyBind; - import javafx.beans.Observable; import javafx.beans.property.BooleanProperty; import javafx.beans.property.ObjectProperty; @@ -22,7 +12,20 @@ import javafx.beans.property.SimpleBooleanProperty; 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.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.Base64; +import java.util.Objects; +import java.util.UUID; + +/** + * The settings specific to a single vault. + * TODO: Change the name of individualMountPath and its derivatives to customMountPath + */ public class VaultSettings { public static final boolean DEFAULT_UNLOCK_AFTER_STARTUP = false; 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 96d983d9b..996c9a358 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 @@ -5,14 +5,13 @@ *******************************************************************************/ package org.cryptomator.common.settings; -import java.io.IOException; -import java.nio.file.Paths; - +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.nio.file.Paths; class VaultSettingsJsonAdapter { @@ -27,8 +26,7 @@ class VaultSettingsJsonAdapter { out.name("unlockAfterStartup").value(value.unlockAfterStartup().get()); out.name("revealAfterMount").value(value.revealAfterMount().get()); out.name("usesIndividualMountPath").value(value.usesIndividualMountPath().get()); - //TODO: should this always be written? ( because it could contain metadata, which the user does not want to save!) - out.name("individualMountPath").value(value.individualMountPath().get()); + out.name("individualMountPath").value(value.individualMountPath().get()); //TODO: should this always be written? ( because it could contain metadata, which the user may not want to save!) out.name("usesReadOnlyMode").value(value.usesReadOnlyMode().get()); out.endObject(); } diff --git a/main/commons/src/main/java/org/cryptomator/common/settings/VolumeImpl.java b/main/commons/src/main/java/org/cryptomator/common/settings/VolumeImpl.java index 634f528f4..e475ff0d9 100644 --- a/main/commons/src/main/java/org/cryptomator/common/settings/VolumeImpl.java +++ b/main/commons/src/main/java/org/cryptomator/common/settings/VolumeImpl.java @@ -5,7 +5,7 @@ import java.util.Arrays; public enum VolumeImpl { WEBDAV("WebDAV"), FUSE("FUSE"), - DOKANY("DOKANY"); + DOKANY("Dokany"); private String displayName; diff --git a/main/keychain/pom.xml b/main/keychain/pom.xml index 1d2ae0e0a..8ae41d506 100644 --- a/main/keychain/pom.xml +++ b/main/keychain/pom.xml @@ -4,7 +4,7 @@ org.cryptomator main - 1.4.0-SNAPSHOT + 1.5.0-SNAPSHOT keychain System Keychain Access diff --git a/main/launcher/pom.xml b/main/launcher/pom.xml index bb55d145b..aef980622 100644 --- a/main/launcher/pom.xml +++ b/main/launcher/pom.xml @@ -4,7 +4,7 @@ org.cryptomator main - 1.4.0-SNAPSHOT + 1.5.0-SNAPSHOT launcher Cryptomator Launcher diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/MainApplication.java b/main/launcher/src/main/java/org/cryptomator/launcher/MainApplication.java index 8ee283c36..c62f624c4 100644 --- a/main/launcher/src/main/java/org/cryptomator/launcher/MainApplication.java +++ b/main/launcher/src/main/java/org/cryptomator/launcher/MainApplication.java @@ -5,15 +5,12 @@ *******************************************************************************/ package org.cryptomator.launcher; +import javafx.application.Application; +import javafx.stage.Stage; import org.cryptomator.ui.controllers.MainController; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javafx.application.Application; -import javafx.application.Platform; -import javafx.fxml.FXMLLoader; -import javafx.stage.Stage; - public class MainApplication extends Application { private static final Logger LOG = LoggerFactory.getLogger(MainApplication.class); diff --git a/main/pom.xml b/main/pom.xml index 7ab33380a..d50465fc2 100644 --- a/main/pom.xml +++ b/main/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.cryptomator main - 1.4.0-SNAPSHOT + 1.5.0-SNAPSHOT pom Cryptomator @@ -24,20 +24,20 @@ UTF-8 - 1.2.0 - 1.5.2 + 1.2.1 + 1.6.2 2.0.0 - 0.1.5 - 0.1.2 - 1.0.4 + 1.0.3 + 1.1.1 + 1.0.5 - 2.5 - 3.6 + 2.6 + 3.8.1 1.0.3 - 25.1-jre - 2.16 + 27.0-jre + 2.20 2.8.5 1.7.25 @@ -45,7 +45,7 @@ 4.12 4.12.1 - 2.19.0 + 2.23.0 1.3 @@ -288,7 +288,7 @@ org.jacoco jacoco-maven-plugin - 0.8.1 + 0.8.2 prepare-agent @@ -315,15 +315,9 @@ maven-compiler-plugin - 3.7.0 + 3.8.0 - 10 - 10 - 10 - - --add-modules - jdk.incubator.httpclient - + 9 com.google.dagger diff --git a/main/uber-jar/pom.xml b/main/uber-jar/pom.xml index e0fb9e634..fa28bfada 100644 --- a/main/uber-jar/pom.xml +++ b/main/uber-jar/pom.xml @@ -4,7 +4,7 @@ org.cryptomator main - 1.4.0-SNAPSHOT + 1.5.0-SNAPSHOT uber-jar Single über jar with all dependencies diff --git a/main/ui/pom.xml b/main/ui/pom.xml index 2421329e6..a613c2b01 100644 --- a/main/ui/pom.xml +++ b/main/ui/pom.xml @@ -4,7 +4,7 @@ org.cryptomator main - 1.4.0-SNAPSHOT + 1.5.0-SNAPSHOT ui Cryptomator GUI diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java index fc5019162..b1a823178 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java @@ -2,7 +2,7 @@ * Copyright (c) 2014, 2017 Sebastian Stenzel * All rights reserved. * This program and the accompanying materials are made available under the terms of the accompanying LICENSE file. - * + * * Contributors: * Sebastian Stenzel - initial API and implementation * Jean-Noël Charon - confirmation dialog on vault removal @@ -110,7 +110,7 @@ public class MainController implements ViewController { @Inject public MainController(@Named("mainWindow") Stage mainWindow, ExecutorService executorService, @Named("fileOpenRequests") BlockingQueue fileOpenRequests, ExitUtil exitUtil, Localization localization, - VaultFactory vaultFactoy, ViewControllerLoader viewControllerLoader, UpgradeStrategies upgradeStrategies, VaultList vaults, AutoUnlocker autoUnlocker) { + VaultFactory vaultFactoy, ViewControllerLoader viewControllerLoader, UpgradeStrategies upgradeStrategies, VaultList vaults, AutoUnlocker autoUnlocker) { this.mainWindow = mainWindow; this.executorService = executorService; this.fileOpenRequests = fileOpenRequests; @@ -222,7 +222,7 @@ public class MainController implements ViewController { ButtonType forceShutdownButtonType = new ButtonType(localization.getString("main.gracefulShutdown.button.forceShutdown")); Alert gracefulShutdownDialog = DialogBuilderUtil.buildGracefulShutdownDialog( localization.getString("main.gracefulShutdown.dialog.title"), localization.getString("main.gracefulShutdown.dialog.header"), localization.getString("main.gracefulShutdown.dialog.content"), - forceShutdownButtonType, forceShutdownButtonType, tryAgainButtonType); + forceShutdownButtonType, ButtonType.CANCEL, forceShutdownButtonType, tryAgainButtonType); Optional choice = gracefulShutdownDialog.showAndWait(); choice.ifPresent(btnType -> { @@ -230,8 +230,12 @@ public class MainController implements ViewController { gracefulShutdown(); } else if (forceShutdownButtonType.equals(btnType)) { Platform.runLater(Platform::exit); + } else { + return; } }); + } else { + Platform.runLater(Platform::exit); } } @@ -329,7 +333,7 @@ public class MainController implements ViewController { /** * adds the given directory or selects it if it is already in the list of directories. - * + * * @param path to a vault directory or masterkey file */ public void addVault(final Path path, boolean select) { @@ -430,7 +434,13 @@ public class MainController implements ViewController { } private void didPressKeyOnRoot(KeyEvent event) { - if ((event.isMetaDown() || event.isControlDown()) && event.getCode().isDigitKey()) { + boolean triggered; + if (SystemUtils.IS_OS_MAC) { + triggered = event.isMetaDown(); + } else { + triggered = event.isControlDown() && !event.isAltDown(); + } + if (triggered && event.getCode().isDigitKey()) { int digit = Integer.valueOf(event.getText()); switch (digit) { case 0: { diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/SettingsController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/SettingsController.java index 958dceb5b..8c1454404 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/SettingsController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/SettingsController.java @@ -8,17 +8,13 @@ ******************************************************************************/ package org.cryptomator.ui.controllers; -import java.util.Optional; - -import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; - import com.google.common.base.CharMatcher; import com.google.common.base.Strings; +import javafx.beans.Observable; import javafx.beans.binding.Bindings; import javafx.event.ActionEvent; import javafx.fxml.FXML; +import javafx.scene.Group; import javafx.scene.Parent; import javafx.scene.control.Button; import javafx.scene.control.CheckBox; @@ -26,15 +22,19 @@ import javafx.scene.control.ChoiceBox; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.input.KeyEvent; -import javafx.scene.layout.GridPane; import javafx.scene.layout.VBox; import javafx.util.StringConverter; import org.apache.commons.lang3.SystemUtils; -import org.cryptomator.common.settings.VolumeImpl; import org.cryptomator.common.settings.Settings; +import org.cryptomator.common.settings.VolumeImpl; import org.cryptomator.ui.l10n.Localization; import org.cryptomator.ui.model.Volume; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import java.util.Optional; + @Singleton public class SettingsController implements ViewController { @@ -49,16 +49,13 @@ public class SettingsController implements ViewController { this.localization = localization; this.settings = settings; this.applicationVersion = applicationVersion; + this.webdavSettings = new Group(); } @FXML private CheckBox checkForUpdatesCheckbox; - @FXML - private GridPane webdavVolume; - - @FXML - private GridPane fuseVolume; + private Group webdavSettings; @FXML private Label portFieldLabel; @@ -97,15 +94,18 @@ public class SettingsController implements ViewController { volume.getItems().addAll(Volume.getCurrentSupportedAdapters()); volume.setValue(settings.preferredVolumeImpl().get()); volume.setConverter(new NioAdapterImplStringConverter()); + volume.valueProperty().addListener(this::setVisibilityGvfsElements); //WEBDAV - webdavVolume.visibleProperty().bind(volume.valueProperty().isEqualTo(VolumeImpl.WEBDAV)); - webdavVolume.managedProperty().bind(webdavVolume.visibleProperty()); - prefGvfsScheme.managedProperty().bind(webdavVolume.visibleProperty()); - prefGvfsSchemeLabel.managedProperty().bind(webdavVolume.visibleProperty()); - portFieldLabel.managedProperty().bind(webdavVolume.visibleProperty()); - changePortButton.managedProperty().bind(webdavVolume.visibleProperty()); - portField.managedProperty().bind(webdavVolume.visibleProperty()); + webdavSettings.visibleProperty().bind(volume.valueProperty().isEqualTo(VolumeImpl.WEBDAV)); + webdavSettings.managedProperty().bind(webdavSettings.visibleProperty()); + prefGvfsScheme.managedProperty().bind(webdavSettings.visibleProperty()); + prefGvfsSchemeLabel.managedProperty().bind(webdavSettings.visibleProperty()); + portFieldLabel.managedProperty().bind(webdavSettings.visibleProperty()); + portFieldLabel.visibleProperty().bind(webdavSettings.visibleProperty()); + changePortButton.managedProperty().bind(webdavSettings.visibleProperty()); + portField.managedProperty().bind(webdavSettings.visibleProperty()); + portField.visibleProperty().bind(webdavSettings.visibleProperty()); portField.setText(String.valueOf(settings.port().intValue())); portField.addEventFilter(KeyEvent.KEY_TYPED, this::filterNumericKeyEvents); changePortButton.visibleProperty().bind(settings.port().asString().isNotEqualTo(portField.textProperty())); @@ -116,10 +116,6 @@ public class SettingsController implements ViewController { prefGvfsSchemeLabel.setVisible(SystemUtils.IS_OS_LINUX); prefGvfsScheme.setVisible(SystemUtils.IS_OS_LINUX); - //FUSE - fuseVolume.visibleProperty().bind(volume.valueProperty().isEqualTo(VolumeImpl.FUSE)); - fuseVolume.managedProperty().bind(fuseVolume.visibleProperty()); - debugModeCheckbox.setSelected(settings.debugMode().get()); settings.checkForUpdates().bind(checkForUpdatesCheckbox.selectedProperty()); @@ -163,6 +159,11 @@ public class SettingsController implements ViewController { } } + private void setVisibilityGvfsElements(Observable obs, Object oldValue, Object newValue) { + prefGvfsSchemeLabel.setVisible(SystemUtils.IS_OS_LINUX && ((VolumeImpl) newValue).getDisplayName().equals("WebDAV")); + prefGvfsScheme.setVisible(SystemUtils.IS_OS_LINUX && ((VolumeImpl) newValue).getDisplayName().equals("WebDAV")); + } + private boolean areUpdatesManagedExternally() { return Boolean.parseBoolean(System.getProperty("cryptomator.updatesManagedExternally", "false")); } diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockController.java index 1b01bcef4..c2c50bdc5 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockController.java @@ -8,13 +8,6 @@ ******************************************************************************/ package org.cryptomator.ui.controllers; -import javax.inject.Inject; -import java.util.Arrays; -import java.util.Comparator; -import java.util.Objects; -import java.util.Optional; -import java.util.concurrent.ExecutorService; - import com.google.common.base.CharMatcher; import com.google.common.base.Strings; import javafx.application.Application; @@ -33,8 +26,12 @@ import javafx.scene.control.Label; import javafx.scene.control.ProgressIndicator; import javafx.scene.control.TextField; import javafx.scene.input.KeyEvent; +import javafx.scene.input.MouseEvent; import javafx.scene.layout.GridPane; +import javafx.scene.layout.HBox; import javafx.scene.text.Text; +import javafx.stage.DirectoryChooser; +import javafx.stage.Stage; import javafx.util.StringConverter; import org.apache.commons.lang3.CharUtils; import org.apache.commons.lang3.SystemUtils; @@ -45,7 +42,6 @@ import org.cryptomator.cryptolib.api.InvalidPassphraseException; import org.cryptomator.cryptolib.api.UnsupportedVaultFormatException; import org.cryptomator.frontend.webdav.ServerLifecycleException; import org.cryptomator.keychain.KeychainAccess; -import org.cryptomator.ui.model.InvalidSettingsException; import org.cryptomator.ui.controls.SecPasswordField; import org.cryptomator.ui.l10n.Localization; import org.cryptomator.ui.model.Vault; @@ -57,6 +53,17 @@ import org.fxmisc.easybind.Subscription; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.inject.Inject; +import javax.inject.Named; +import java.io.File; +import java.nio.file.DirectoryNotEmptyException; +import java.nio.file.NotDirectoryException; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ExecutorService; + public class UnlockController implements ViewController { private static final Logger LOG = LoggerFactory.getLogger(UnlockController.class); @@ -67,6 +74,7 @@ public class UnlockController implements ViewController { .precomputed(); private final Application app; + private final Stage mainWindow; private final Localization localization; private final WindowsDriveLetters driveLetters; private final ChangeListener driveLetterChangeListener = this::winDriveLetterDidChange; @@ -78,8 +86,9 @@ public class UnlockController implements ViewController { private Subscription vaultSubs = Subscription.EMPTY; @Inject - public UnlockController(Application app, Localization localization, WindowsDriveLetters driveLetters, Optional keychainAccess, Settings settings, ExecutorService executor) { + public UnlockController(Application app, @Named("mainWindow") Stage mainWindow, Localization localization, WindowsDriveLetters driveLetters, Optional keychainAccess, Settings settings, ExecutorService executor) { this.app = app; + this.mainWindow = mainWindow; this.localization = localization; this.driveLetters = driveLetters; this.keychainAccess = keychainAccess; @@ -115,13 +124,13 @@ public class UnlockController implements ViewController { private ChoiceBox winDriveLetter; @FXML - private CheckBox useOwnMountPath; + private CheckBox useCustomMountPoint; @FXML - private Label mountPathLabel; + private HBox customMountPoint; @FXML - private TextField mountPath; + private Label customMountPointField; @FXML private ProgressIndicator progressIndicator; @@ -153,27 +162,29 @@ public class UnlockController implements ViewController { savePassword.setDisable(!keychainAccess.isPresent()); unlockAfterStartup.disableProperty().bind(savePassword.disabledProperty().or(savePassword.selectedProperty().not())); - mountPathLabel.visibleProperty().bind(useOwnMountPath.selectedProperty()); - mountPath.visibleProperty().bind(useOwnMountPath.selectedProperty()); - mountPath.managedProperty().bind(useOwnMountPath.selectedProperty()); - mountPath.textProperty().addListener(this::mountPathDidChange); + customMountPoint.visibleProperty().bind(useCustomMountPoint.selectedProperty()); + customMountPoint.managedProperty().bind(useCustomMountPoint.selectedProperty()); + winDriveLetter.setConverter(new WinDriveLetterLabelConverter()); - if (SystemUtils.IS_OS_WINDOWS) { - winDriveLetter.setConverter(new WinDriveLetterLabelConverter()); - useOwnMountPath.setVisible(false); - useOwnMountPath.setManaged(false); - mountPathLabel.setManaged(false); - //dirty cheat - mountPath.setMouseTransparent(true); - } else { + if (!SystemUtils.IS_OS_WINDOWS) { winDriveLetterLabel.setVisible(false); winDriveLetterLabel.setManaged(false); winDriveLetter.setVisible(false); winDriveLetter.setManaged(false); - if (VolumeImpl.WEBDAV.equals(settings.preferredVolumeImpl().get())) { - useOwnMountPath.setVisible(false); - useOwnMountPath.setManaged(false); - mountPathLabel.setManaged(false); + } + + if (VolumeImpl.WEBDAV.equals(settings.preferredVolumeImpl().get())) { + useCustomMountPoint.setVisible(false); + useCustomMountPoint.setManaged(false); + customMountPoint.setVisible(false); + customMountPoint.setManaged(false); + } else { + useCustomMountPoint.setVisible(true); + if (SystemUtils.IS_OS_WINDOWS) { + winDriveLetter.visibleProperty().bind(useCustomMountPoint.selectedProperty().not()); + winDriveLetter.managedProperty().bind(useCustomMountPoint.selectedProperty().not()); + winDriveLetterLabel.visibleProperty().bind(useCustomMountPoint.selectedProperty().not()); + winDriveLetterLabel.managedProperty().bind(useCustomMountPoint.selectedProperty().not()); } } } @@ -213,13 +224,11 @@ public class UnlockController implements ViewController { winDriveLetter.getItems().addAll(driveLetters.getAvailableDriveLetters()); winDriveLetter.getItems().sort(new WinDriveLetterComparator()); winDriveLetter.valueProperty().addListener(driveLetterChangeListener); + chooseSelectedDriveLetter(); } downloadsPageLink.setVisible(false); messageText.setText(null); mountName.setText(vault.getMountName()); - if (SystemUtils.IS_OS_WINDOWS) { - chooseSelectedDriveLetter(); - } savePassword.setSelected(false); // auto-fill pw from keychain: if (keychainAccess.isPresent()) { @@ -234,16 +243,16 @@ public class UnlockController implements ViewController { VaultSettings vaultSettings = vault.getVaultSettings(); unlockAfterStartup.setSelected(savePassword.isSelected() && vaultSettings.unlockAfterStartup().get()); revealAfterMount.setSelected(vaultSettings.revealAfterMount().get()); - useOwnMountPath.setSelected(vaultSettings.usesIndividualMountPath().get()); useReadOnlyMode.setSelected(vaultSettings.usesReadOnlyMode().get()); + if (!settings.preferredVolumeImpl().get().equals(VolumeImpl.WEBDAV)) { + useCustomMountPoint.setSelected(vaultSettings.usesIndividualMountPath().get()); + customMountPointField.textProperty().setValue(vaultSettings.individualMountPath().getValueSafe()); + } vaultSubs = vaultSubs.and(EasyBind.subscribe(unlockAfterStartup.selectedProperty(), vaultSettings.unlockAfterStartup()::set)); vaultSubs = vaultSubs.and(EasyBind.subscribe(revealAfterMount.selectedProperty(), vaultSettings.revealAfterMount()::set)); - vaultSubs = vaultSubs.and(EasyBind.subscribe(useOwnMountPath.selectedProperty(), vaultSettings.usesIndividualMountPath()::set)); + vaultSubs = vaultSubs.and(EasyBind.subscribe(useCustomMountPoint.selectedProperty(), vaultSettings.usesIndividualMountPath()::set)); vaultSubs = vaultSubs.and(EasyBind.subscribe(useReadOnlyMode.selectedProperty(), vaultSettings.usesReadOnlyMode()::set)); - - mountPath.textProperty().setValue(vaultSettings.individualMountPath().getValueSafe()); - } // **************************************** @@ -285,8 +294,13 @@ public class UnlockController implements ViewController { } } - private void mountPathDidChange(ObservableValue property, String oldValue, String newValue) { - vault.setIndividualMountPath(newValue); + public void didClickChooseCustomMountPoint(ActionEvent actionEvent) { + DirectoryChooser dirChooser = new DirectoryChooser(); + File file = dirChooser.showDialog(mainWindow); + if (file != null) { + customMountPointField.setText(file.toString()); + vault.setCustomMountPath(file.toString()); + } } /** @@ -299,7 +313,7 @@ public class UnlockController implements ViewController { if (letter == null) { return localization.getString("unlock.choicebox.winDriveLetter.auto"); } else { - return Character.toString(letter) + ":"; + return letter + ":"; } } @@ -393,6 +407,7 @@ public class UnlockController implements ViewController { CharSequence password = passwordField.getCharacters(); Tasks.create(() -> { + messageText.setText(localization.getString("unlock.pendingMessage.unlocking")); vault.unlock(password); if (keychainAccess.isPresent() && savePassword.isSelected()) { keychainAccess.get().storePassphrase(vault.getId(), password); @@ -401,10 +416,6 @@ public class UnlockController implements ViewController { messageText.setText(null); downloadsPageLink.setVisible(false); listener.ifPresent(lstnr -> lstnr.didUnlock(vault)); - }).onError(InvalidSettingsException.class, e -> { - messageText.setText(localization.getString("unlock.errorMessage.invalidMountPath")); - advancedOptions.setVisible(true); - mountPath.setStyle("-fx-border-color: red;"); }).onError(InvalidPassphraseException.class, e -> { messageText.setText(localization.getString("unlock.errorMessage.wrongPassword")); passwordField.selectAll(); @@ -420,10 +431,17 @@ public class UnlockController implements ViewController { } else if (e.getDetectedVersion() == Integer.MAX_VALUE) { messageText.setText(localization.getString("unlock.errorMessage.unauthenticVersionMac")); } - }).onError(ServerLifecycleException.class, e -> { - LOG.error("Unlock failed for technical reasons.", e); - messageText.setText(localization.getString("unlock.errorMessage.unlockFailed")); - }).onError(Exception.class, e -> { + }).onError(NotDirectoryException.class, e -> { + LOG.error("Unlock failed. Mount point not a directory: {}", e.getMessage()); + advancedOptions.setVisible(true); + messageText.setText(null); + showUnlockFailedErrorDialog("unlock.failedDialog.content.mountPathNonExisting"); + }).onError(DirectoryNotEmptyException.class, e -> { + LOG.error("Unlock failed. Mount point not empty: {}", e.getMessage()); + advancedOptions.setVisible(true); + messageText.setText(null); + showUnlockFailedErrorDialog("unlock.failedDialog.content.mountPathNotEmpty"); + }).onError(Exception.class, e -> { // including RuntimeExceptions LOG.error("Unlock failed for technical reasons.", e); messageText.setText(localization.getString("unlock.errorMessage.unlockFailed")); }).andFinally(() -> { @@ -432,12 +450,17 @@ public class UnlockController implements ViewController { } advancedOptions.setDisable(false); progressIndicator.setVisible(false); - if (advancedOptions.isVisible()) { //dirty programming, but otherwise the focus is wrong - mountPath.requestFocus(); - } }).runOnce(executor); } + private void showUnlockFailedErrorDialog(String localizableContentKey) { + String title = localization.getString("unlock.failedDialog.title"); + String header = localization.getString("unlock.failedDialog.header"); + String content = localization.getString(localizableContentKey); + Alert alert = DialogBuilderUtil.buildErrorDialog(title, header, content, ButtonType.OK); + alert.show(); + } + /* callback */ public void setListener(UnlockListener listener) { diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/DokanyVolume.java b/main/ui/src/main/java/org/cryptomator/ui/model/DokanyVolume.java index 35aff5bcb..26a4b7993 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/DokanyVolume.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/DokanyVolume.java @@ -1,14 +1,16 @@ package org.cryptomator.ui.model; -import javax.inject.Inject; -import java.util.Set; -import java.util.concurrent.ExecutorService; - -import com.google.common.collect.Sets; +import com.google.common.base.Strings; import org.cryptomator.common.settings.VaultSettings; import org.cryptomator.cryptofs.CryptoFileSystem; import org.cryptomator.frontend.dokany.Mount; import org.cryptomator.frontend.dokany.MountFactory; +import org.cryptomator.frontend.dokany.MountFailedException; + +import javax.inject.Inject; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.concurrent.ExecutorService; public class DokanyVolume implements Volume { @@ -32,25 +34,31 @@ public class DokanyVolume implements Volume { return DokanyVolume.isSupportedStatic(); } - //TODO: Drive letter 'A' as mount point is invalid in dokany. maybe we should do already here something against it @Override public void mount(CryptoFileSystem fs) throws VolumeException { - char driveLetter; - if (!vaultSettings.winDriveLetter().getValueSafe().equals("")) { - driveLetter = vaultSettings.winDriveLetter().get().charAt(0); + Path mountPath = Paths.get(getMountPathString()); + String mountName = vaultSettings.mountName().get(); + try { + this.mount = mountFactory.mount(fs.getPath("/"), mountPath, mountName, FS_TYPE_NAME); + } catch (MountFailedException e) { + throw new VolumeException("Unable to mount Filesystem", e); + } + } + + private String getMountPathString() throws VolumeException { + if (vaultSettings.usesIndividualMountPath().get()) { + return vaultSettings.individualMountPath().get(); + } else if (!Strings.isNullOrEmpty(vaultSettings.winDriveLetter().get())) { + return vaultSettings.winDriveLetter().get().charAt(0) + ":\\"; } else { //auto assign drive letter if (!windowsDriveLetters.getAvailableDriveLetters().isEmpty()) { - //this is a temporary fix for 'A' being an invalid drive letter - Set availableLettersWithoutA = Sets.difference(windowsDriveLetters.getAvailableDriveLetters(), Set.of('A')); - driveLetter = availableLettersWithoutA.iterator().next(); -// driveLetter = windowsDriveLetters.getAvailableDriveLetters().iterator().next(); + return windowsDriveLetters.getAvailableDriveLetters().iterator().next() + ":\\"; } else { throw new VolumeException("No free drive letter available."); } } - String mountName = vaultSettings.mountName().get(); - this.mount = mountFactory.mount(fs.getPath("/"), driveLetter, mountName, FS_TYPE_NAME); + } @Override diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/FuseVolume.java b/main/ui/src/main/java/org/cryptomator/ui/model/FuseVolume.java index ec63da443..972b9ce06 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/FuseVolume.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/FuseVolume.java @@ -1,14 +1,5 @@ package org.cryptomator.ui.model; -import java.io.IOException; -import java.nio.file.DirectoryNotEmptyException; -import java.nio.file.DirectoryStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; - -import javax.inject.Inject; - import org.apache.commons.lang3.SystemUtils; import org.cryptomator.common.settings.VaultSettings; import org.cryptomator.cryptofs.CryptoFileSystem; @@ -20,65 +11,84 @@ import org.cryptomator.frontend.fuse.mount.Mount; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.inject.Inject; +import java.io.IOException; +import java.nio.file.DirectoryNotEmptyException; +import java.nio.file.DirectoryStream; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; +import java.nio.file.NotDirectoryException; +import java.nio.file.Path; +import java.nio.file.Paths; + public class FuseVolume implements Volume { private static final Logger LOG = LoggerFactory.getLogger(FuseVolume.class); - /** - * TODO: dont use fixed Strings and rather set them in some system environment variables in the cryptomator installer and load those! - */ + // TODO: dont use fixed Strings and rather set them in some system environment variables in the cryptomator installer and load those! private static final String DEFAULT_MOUNTROOTPATH_MAC = System.getProperty("user.home") + "/Library/Application Support/Cryptomator"; private static final String DEFAULT_MOUNTROOTPATH_LINUX = System.getProperty("user.home") + "/.Cryptomator"; + private static final int MAX_TMPMOUNTPOINT_CREATION_RETRIES = 10; private final VaultSettings vaultSettings; private Mount fuseMnt; - private Path mountPath; - private boolean extraDirCreated; + private Path mountPoint; + private boolean createdTemporaryMountPoint; @Inject public FuseVolume(VaultSettings vaultSettings) { this.vaultSettings = vaultSettings; - this.extraDirCreated = false; + this.createdTemporaryMountPoint = false; } @Override public void mount(CryptoFileSystem fs) throws IOException, FuseNotSupportedException, VolumeException { - String mountPath; if (vaultSettings.usesIndividualMountPath().get()) { - //specific path given - mountPath = vaultSettings.individualMountPath().get(); + Path customMountPoint = Paths.get(vaultSettings.individualMountPath().get()); + checkProvidedMountPoint(customMountPoint); + this.mountPoint = customMountPoint; + LOG.debug("Successfully checked custom mount point: {}", mountPoint); } else { - //choose default path & create extra directory - mountPath = createDirIfNotExist(SystemUtils.IS_OS_MAC ? DEFAULT_MOUNTROOTPATH_MAC : DEFAULT_MOUNTROOTPATH_LINUX, vaultSettings.mountName().get()); - extraDirCreated = true; + this.mountPoint = createTemporaryMountPoint(); + createdTemporaryMountPoint = true; + LOG.debug("Successfully created mount point: {}", mountPoint); } - this.mountPath = Paths.get(mountPath).toAbsolutePath(); mount(fs.getPath("/")); } - private String createDirIfNotExist(String prefix, String dirName) throws IOException { - Path p = Paths.get(prefix, dirName + vaultSettings.getId()); - if (Files.isDirectory(p)) { - try (DirectoryStream emptyCheck = Files.newDirectoryStream(p)) { - if (emptyCheck.iterator().hasNext()) { - throw new DirectoryNotEmptyException("Mount point is not empty."); - } else { - LOG.info("Directory already exists and is empty. Using it as mount point."); - return p.toString(); - } - } - } else { - Files.createDirectory(p); - return p.toString(); + private void checkProvidedMountPoint(Path mountPoint) throws IOException { + if (!Files.isDirectory(mountPoint)) { + throw new NotDirectoryException(mountPoint.toString()); } + try (DirectoryStream ds = Files.newDirectoryStream(mountPoint)) { + if (ds.iterator().hasNext()) { + throw new DirectoryNotEmptyException(mountPoint.toString()); + } + } + } + + private Path createTemporaryMountPoint() throws IOException { + Path parent = Paths.get(SystemUtils.IS_OS_MAC ? DEFAULT_MOUNTROOTPATH_MAC : DEFAULT_MOUNTROOTPATH_LINUX); + String basename = vaultSettings.getId(); + for (int i = 0; i < MAX_TMPMOUNTPOINT_CREATION_RETRIES; i++) { + try { + Path mountPath = parent.resolve(basename + "_" + i); + Files.createDirectory(mountPath); + return mountPath; + } catch (FileAlreadyExistsException e) { + continue; + } + } + LOG.error("Failed to create mount path at {}/{}_x. Giving up after {} attempts.", parent, basename, MAX_TMPMOUNTPOINT_CREATION_RETRIES); + throw new FileAlreadyExistsException(parent.toString() + "/" + basename); } private void mount(Path root) throws VolumeException { try { - EnvironmentVariables envVars = EnvironmentVariables.create() - .withMountName(vaultSettings.mountName().getValue()) - .withMountPath(mountPath) + EnvironmentVariables envVars = EnvironmentVariables.create() // + .withMountName(vaultSettings.mountName().getValue()) // + .withMountPath(mountPoint) // .build(); this.fuseMnt = FuseMountFactory.getMounter().mount(root, envVars); } catch (CommandFailedException e) { @@ -91,7 +101,7 @@ public class FuseVolume implements Volume { try { fuseMnt.revealInFileManager(); } catch (CommandFailedException e) { - LOG.info("Revealing the vault in file manger failed: " + e.getMessage()); + LOG.debug("Revealing the vault in file manger failed: " + e.getMessage()); throw new VolumeException(e); } } @@ -103,15 +113,16 @@ public class FuseVolume implements Volume { } catch (CommandFailedException e) { throw new VolumeException(e); } - cleanup(); + deleteTemporaryMountPoint(); } - private void cleanup() { - if (extraDirCreated) { + private void deleteTemporaryMountPoint() { + if (createdTemporaryMountPoint) { try { - Files.delete(mountPath); + Files.delete(mountPoint); + LOG.debug("Successfully deleted mount point: {}", mountPoint); } catch (IOException e) { - LOG.warn("Could not delete mounting directory:" + e.getMessage()); + LOG.warn("Could not delete mount point: {}", e.getMessage()); } } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/InvalidSettingsException.java b/main/ui/src/main/java/org/cryptomator/ui/model/InvalidSettingsException.java deleted file mode 100644 index ed9b39040..000000000 --- a/main/ui/src/main/java/org/cryptomator/ui/model/InvalidSettingsException.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.cryptomator.ui.model; - -public class InvalidSettingsException extends RuntimeException { - -} diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3to4.java b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3to4.java index 3c9e6aa2e..cb0981d52 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3to4.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3to4.java @@ -5,8 +5,8 @@ *******************************************************************************/ package org.cryptomator.ui.model; -import static java.nio.charset.StandardCharsets.UTF_8; - +import javax.inject.Inject; +import javax.inject.Singleton; import java.io.IOException; import java.nio.file.FileVisitOption; import java.nio.file.FileVisitResult; @@ -19,11 +19,7 @@ import java.util.EnumSet; import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.inject.Inject; -import javax.inject.Singleton; - -import org.apache.commons.codec.binary.Base32; -import org.apache.commons.codec.binary.BaseNCodec; +import com.google.common.io.BaseEncoding; import org.apache.commons.lang3.StringUtils; import org.cryptomator.cryptolib.Cryptors; import org.cryptomator.cryptolib.api.Cryptor; @@ -32,6 +28,8 @@ import org.cryptomator.ui.l10n.Localization; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static java.nio.charset.StandardCharsets.UTF_8; + /** * Contains the collective knowledge of all creatures who were alive during the development of vault format 3. * This class uses no external classes from the crypto or shortening layer by purpose, so we don't need legacy code inside these. @@ -50,7 +48,7 @@ class UpgradeVersion3to4 extends UpgradeStrategy { private static final String NEW_FOLDER_PREFIX = "0"; private final MessageDigest sha1 = MessageDigestSupplier.SHA1.get(); - private final BaseNCodec base32 = new Base32(); + private final BaseEncoding base32 = BaseEncoding.base32(); @Inject public UpgradeVersion3to4(Localization localization) { @@ -155,7 +153,7 @@ class UpgradeVersion3to4 extends UpgradeStrategy { String oldLongName = new String(Files.readAllBytes(oldMetadataFile), UTF_8); if (oldLongName.endsWith(OLD_FOLDER_SUFFIX)) { String newLongName = NEW_FOLDER_PREFIX + StringUtils.removeEnd(oldLongName, OLD_FOLDER_SUFFIX); - String newCanonicalBase32 = base32.encodeAsString(sha1.digest(newLongName.getBytes(UTF_8))); + String newCanonicalBase32 = base32.encode(sha1.digest(newLongName.getBytes(UTF_8))); String newCanonicalName = newCanonicalBase32 + LONG_FILENAME_EXT; Path newMetadataFile = metadataDir.resolve(newCanonicalName.substring(0, 2)).resolve(newCanonicalName.substring(2, 4)).resolve(newCanonicalName); String newName = newCanonicalBase32 + oldNameSuffix + LONG_FILENAME_EXT; diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java b/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java index cf0e6d7cc..78a2a51e4 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java @@ -8,19 +8,6 @@ *******************************************************************************/ package org.cryptomator.ui.model; -import java.io.IOException; -import java.nio.file.FileAlreadyExistsException; -import java.nio.file.Files; -import java.nio.file.NoSuchFileException; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Objects; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Predicate; - -import javax.inject.Inject; -import javax.inject.Provider; - import javafx.application.Platform; import javafx.beans.Observable; import javafx.beans.binding.Binding; @@ -42,6 +29,19 @@ import org.fxmisc.easybind.EasyBind; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.inject.Inject; +import javax.inject.Provider; +import java.io.IOException; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.NotDirectoryException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Predicate; + @PerVault public class Vault { @@ -98,11 +98,11 @@ public class Vault { CryptoFileSystemProvider.changePassphrase(getPath(), MASTERKEY_FILENAME, oldPassphrase, newPassphrase); } - public synchronized void unlock(CharSequence passphrase) throws InvalidSettingsException, CryptoException, IOException, Volume.VolumeException { + public synchronized void unlock(CharSequence passphrase) throws CryptoException, IOException, Volume.VolumeException { Platform.runLater(() -> state.set(State.PROCESSING)); try { - if (vaultSettings.usesIndividualMountPath().and(vaultSettings.individualMountPath().isEmpty()).get()) { - throw new InvalidSettingsException(); + if (vaultSettings.usesIndividualMountPath().get() && vaultSettings.individualMountPath().get().isEmpty()) { + throw new NotDirectoryException(""); } CryptoFileSystem fs = getCryptoFileSystem(passphrase); volume = volumeProvider.get(); @@ -241,11 +241,11 @@ public class Vault { return vaultSettings.mountName().get(); } - public String getIndividualMountPath() { + public String getCustomMountPath() { return vaultSettings.individualMountPath().getValueSafe(); } - public void setIndividualMountPath(String mountPath) { + public void setCustomMountPath(String mountPath) { vaultSettings.individualMountPath().set(mountPath); } diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/WindowsDriveLetters.java b/main/ui/src/main/java/org/cryptomator/ui/model/WindowsDriveLetters.java index 41b6799ba..4c2c3111e 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/WindowsDriveLetters.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/WindowsDriveLetters.java @@ -22,11 +22,11 @@ import org.apache.commons.lang3.SystemUtils; @Singleton public final class WindowsDriveLetters { - private static final Set A_TO_Z; + private static final Set D_TO_Z; static { - try (IntStream stream = IntStream.rangeClosed('A', 'Z')) { - A_TO_Z = stream.mapToObj(i -> (char) i).collect(Collectors.toSet()); + try (IntStream stream = IntStream.rangeClosed('D', 'Z')) { + D_TO_Z = stream.mapToObj(i -> (char) i).collect(Collectors.toSet()); } } @@ -45,7 +45,7 @@ public final class WindowsDriveLetters { public Set getAvailableDriveLetters() { Set occupiedDriveLetters = getOccupiedDriveLetters(); Predicate isOccupiedDriveLetter = occupiedDriveLetters::contains; - return A_TO_Z.stream().filter(isOccupiedDriveLetter.negate()).collect(Collectors.toSet()); + return D_TO_Z.stream().filter(isOccupiedDriveLetter.negate()).collect(Collectors.toSet()); } } diff --git a/main/ui/src/main/resources/fxml/settings.fxml b/main/ui/src/main/resources/fxml/settings.fxml index 794181cf5..efa95f19c 100644 --- a/main/ui/src/main/resources/fxml/settings.fxml +++ b/main/ui/src/main/resources/fxml/settings.fxml @@ -7,7 +7,6 @@ Contributors: Sebastian Stenzel - initial API and implementation --> - @@ -27,8 +26,8 @@ - - + + @@ -45,21 +44,15 @@ - -