diff --git a/src/main/java/org/cryptomator/common/mount/IllegalMountPointException.java b/src/main/java/org/cryptomator/common/mount/IllegalMountPointException.java new file mode 100644 index 000000000..5fdb1d91c --- /dev/null +++ b/src/main/java/org/cryptomator/common/mount/IllegalMountPointException.java @@ -0,0 +1,9 @@ +package org.cryptomator.common.mount; + +public class IllegalMountPointException extends IllegalArgumentException { + + public IllegalMountPointException(String msg) { + super(msg); + } + +} diff --git a/src/main/java/org/cryptomator/common/mount/MountPointNotExistsException.java b/src/main/java/org/cryptomator/common/mount/MountPointNotExistsException.java new file mode 100644 index 000000000..e90523bc2 --- /dev/null +++ b/src/main/java/org/cryptomator/common/mount/MountPointNotExistsException.java @@ -0,0 +1,8 @@ +package org.cryptomator.common.mount; + +public class MountPointNotExistsException extends IllegalMountPointException { + + public MountPointNotExistsException(String msg) { + super(msg); + } +} diff --git a/src/main/java/org/cryptomator/common/mount/MountPointNotSupportedException.java b/src/main/java/org/cryptomator/common/mount/MountPointNotSupportedException.java new file mode 100644 index 000000000..e321f23b1 --- /dev/null +++ b/src/main/java/org/cryptomator/common/mount/MountPointNotSupportedException.java @@ -0,0 +1,8 @@ +package org.cryptomator.common.mount; + +public class MountPointNotSupportedException extends IllegalMountPointException { + + public MountPointNotSupportedException(String msg) { + super(msg); + } +} diff --git a/src/main/java/org/cryptomator/common/vaults/Vault.java b/src/main/java/org/cryptomator/common/vaults/Vault.java index 547be30f5..adbe3e636 100644 --- a/src/main/java/org/cryptomator/common/vaults/Vault.java +++ b/src/main/java/org/cryptomator/common/vaults/Vault.java @@ -13,6 +13,9 @@ import org.apache.commons.lang3.SystemUtils; import org.cryptomator.common.Constants; import org.cryptomator.common.Environment; import org.cryptomator.common.mount.ActualMountService; +import org.cryptomator.common.mount.IllegalMountPointException; +import org.cryptomator.common.mount.MountPointNotExistsException; +import org.cryptomator.common.mount.MountPointNotSupportedException; import org.cryptomator.common.mount.WindowsDriveLetters; import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.VaultSettings; @@ -175,22 +178,45 @@ public class Vault { var userChosenMountPoint = vaultSettings.getMountPoint(); var defaultMountPointBase = env.getMountPointsDir().orElseThrow(); + var canMountToDriveLetter = mountService.hasCapability(MOUNT_AS_DRIVE_LETTER); + var canMountToParent = mountService.hasCapability(MOUNT_WITHIN_EXISTING_PARENT); + var canMountToDir = mountService.hasCapability(MOUNT_TO_EXISTING_DIR); if (userChosenMountPoint == null) { if (mountService.hasCapability(MOUNT_TO_SYSTEM_CHOSEN_PATH)) { // no need to set a mount point - } else if (mountService.hasCapability(MOUNT_AS_DRIVE_LETTER)) { + } else if (canMountToDriveLetter) { builder.setMountpoint(windowsDriveLetters.getFirstDesiredAvailable().orElseThrow()); - } else if (mountService.hasCapability(MOUNT_WITHIN_EXISTING_PARENT)) { + } else if (canMountToParent) { Files.createDirectories(defaultMountPointBase); builder.setMountpoint(defaultMountPointBase); - } else if (mountService.hasCapability(MOUNT_TO_EXISTING_DIR)) { + } else if (canMountToDir) { var mountPoint = defaultMountPointBase.resolve(vaultSettings.mountName().get()); Files.createDirectories(mountPoint); builder.setMountpoint(mountPoint); } - } else if (mountService.hasCapability(MOUNT_TO_EXISTING_DIR) || mountService.hasCapability(MOUNT_WITHIN_EXISTING_PARENT) || mountService.hasCapability(MOUNT_AS_DRIVE_LETTER)) { + } else { // TODO: move the mount point away in case of MOUNT_WITHIN_EXISTING_PARENT? - builder.setMountpoint(userChosenMountPoint); + try { + builder.setMountpoint(userChosenMountPoint); + } catch (IllegalArgumentException e) { + //TODO: move code elsewhere + var mpIsDriveLetter = userChosenMountPoint.toString().matches("[A-Z]:\\\\"); + var configNotSupported = (!canMountToDriveLetter && mpIsDriveLetter) || (!canMountToDir && !mpIsDriveLetter) || (!canMountToParent && !mpIsDriveLetter); + if(configNotSupported) { + throw new MountPointNotSupportedException(e.getMessage()); + } else if (canMountToDir && !canMountToParent && !Files.exists(userChosenMountPoint)) { + //mountpoint must exist + throw new MountPointNotExistsException(e.getMessage()); + } else { + throw new IllegalMountPointException(e.getMessage()); + } + /* + //TODO: + if (!canMountToDir && canMountToParent && !Files.notExists(userChosenMountPoint)) { + //parent must exist, mountpoint must not exist + } + */ + } } return builder; diff --git a/src/main/java/org/cryptomator/ui/unlock/UnlockInvalidMountPointController.java b/src/main/java/org/cryptomator/ui/unlock/UnlockInvalidMountPointController.java index 928953c9e..aeb4a9166 100644 --- a/src/main/java/org/cryptomator/ui/unlock/UnlockInvalidMountPointController.java +++ b/src/main/java/org/cryptomator/ui/unlock/UnlockInvalidMountPointController.java @@ -1,11 +1,18 @@ package org.cryptomator.ui.unlock; +import org.cryptomator.common.mount.MountPointNotExistsException; +import org.cryptomator.common.mount.MountPointNotSupportedException; import org.cryptomator.common.vaults.Vault; import org.cryptomator.ui.common.FxController; +import org.cryptomator.ui.controls.FormattedLabel; +import org.cryptomator.ui.fxapp.FxApplicationWindows; +import org.cryptomator.ui.preferences.SelectedPreferencesTab; import javax.inject.Inject; import javafx.fxml.FXML; import javafx.stage.Stage; +import java.util.ResourceBundle; +import java.util.concurrent.atomic.AtomicReference; //At the current point in time only the CustomMountPointChooser may cause this window to be shown. @UnlockScoped @@ -13,11 +20,32 @@ public class UnlockInvalidMountPointController implements FxController { private final Stage window; private final Vault vault; + private final AtomicReference unlockException; + private final FxApplicationWindows appWindows; + private final ResourceBundle resourceBundle; + + public FormattedLabel dialogDescription; @Inject - UnlockInvalidMountPointController(@UnlockWindow Stage window, @UnlockWindow Vault vault) { + UnlockInvalidMountPointController(@UnlockWindow Stage window, @UnlockWindow Vault vault, @UnlockWindow AtomicReference unlockException, FxApplicationWindows appWindows, ResourceBundle resourceBundle) { this.window = window; this.vault = vault; + this.unlockException = unlockException; + this.appWindows = appWindows; + this.resourceBundle = resourceBundle; + } + + @FXML + public void initialize() { + var e = unlockException.get(); + String translationKey = "unlock.error.customPath.description.generic"; + if (e instanceof MountPointNotSupportedException) { + translationKey = "unlock.error.customPath.description.notSupported"; + } else if (e instanceof MountPointNotExistsException) { + translationKey = "unlock.error.customPath.description.notExists"; + } + dialogDescription.setFormat(resourceBundle.getString(translationKey)); + dialogDescription.setArg1(e.getMessage()); } @FXML @@ -25,4 +53,10 @@ public class UnlockInvalidMountPointController implements FxController { window.close(); } + @FXML + public void closeAndOpenPreferences() { + appWindows.showPreferencesWindow(SelectedPreferencesTab.VOLUME); + window.close(); + } + } \ No newline at end of file diff --git a/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java b/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java index b14286698..b59fa272d 100644 --- a/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java +++ b/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java @@ -23,6 +23,7 @@ import javafx.stage.Modality; import javafx.stage.Stage; import java.util.Map; import java.util.ResourceBundle; +import java.util.concurrent.atomic.AtomicReference; @Module(subcomponents = {KeyLoadingComponent.class}) abstract class UnlockModule { @@ -57,6 +58,13 @@ abstract class UnlockModule { return compBuilder.vault(vault).window(window).build().keyloadingStrategy(); } + @Provides + @UnlockWindow + @UnlockScoped + static AtomicReference unlockException() { + return new AtomicReference<>(); + } + @Provides @FxmlScene(FxmlFile.UNLOCK_SUCCESS) @UnlockScoped diff --git a/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java b/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java index 9c47aa281..985f1b471 100644 --- a/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java +++ b/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java @@ -2,6 +2,7 @@ package org.cryptomator.ui.unlock; import com.google.common.base.Throwables; import dagger.Lazy; +import org.cryptomator.common.mount.IllegalMountPointException; import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.VaultState; import org.cryptomator.cryptolib.api.CryptoException; @@ -20,6 +21,7 @@ import javafx.concurrent.Task; import javafx.scene.Scene; import javafx.stage.Stage; import java.io.IOException; +import java.util.concurrent.atomic.AtomicReference; /** * A multi-step task that consists of background activities as well as user interaction. @@ -38,9 +40,10 @@ public class UnlockWorkflow extends Task { private final Lazy invalidMountPointScene; private final FxApplicationWindows appWindows; private final KeyLoadingStrategy keyLoadingStrategy; + private final AtomicReference unlockFailedException; @Inject - UnlockWorkflow(@UnlockWindow Stage window, @UnlockWindow Vault vault, VaultService vaultService, @FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy successScene, @FxmlScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT) Lazy invalidMountPointScene, FxApplicationWindows appWindows, @UnlockWindow KeyLoadingStrategy keyLoadingStrategy) { + UnlockWorkflow(@UnlockWindow Stage window, @UnlockWindow Vault vault, VaultService vaultService, @FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy successScene, @FxmlScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT) Lazy invalidMountPointScene, FxApplicationWindows appWindows, @UnlockWindow KeyLoadingStrategy keyLoadingStrategy, @UnlockWindow AtomicReference unlockFailedException) { this.window = window; this.vault = vault; this.vaultService = vaultService; @@ -48,6 +51,7 @@ public class UnlockWorkflow extends Task { this.invalidMountPointScene = invalidMountPointScene; this.appWindows = appWindows; this.keyLoadingStrategy = keyLoadingStrategy; + this.unlockFailedException = unlockFailedException; } @Override @@ -67,13 +71,15 @@ public class UnlockWorkflow extends Task { } catch (Exception e) { Throwables.propagateIfPossible(e, IOException.class); Throwables.propagateIfPossible(e, CryptoException.class); + Throwables.propagateIfPossible(e, IllegalMountPointException.class); Throwables.propagateIfPossible(e, MountFailedException.class); throw new IllegalStateException("unexpected exception type", e); } } - private void showInvalidMountPointScene() { + private void handleIllegalMountPointError(IllegalMountPointException impe) { Platform.runLater(() -> { + unlockFailedException.set(impe); window.setScene(invalidMountPointScene.get()); window.show(); }); @@ -107,7 +113,11 @@ public class UnlockWorkflow extends Task { protected void failed() { LOG.info("Unlock of '{}' failed.", vault.getDisplayName()); Throwable throwable = super.getException(); - handleGenericError(throwable); + if(throwable instanceof IllegalMountPointException impe) { + handleIllegalMountPointError(impe); + } else { + handleGenericError(throwable); + } vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.LOCKED); } diff --git a/src/main/resources/fxml/unlock_invalid_mount_point.fxml b/src/main/resources/fxml/unlock_invalid_mount_point.fxml index d6be54dac..1b52f568c 100644 --- a/src/main/resources/fxml/unlock_invalid_mount_point.fxml +++ b/src/main/resources/fxml/unlock_invalid_mount_point.fxml @@ -34,16 +34,19 @@ -