Parcourir la source

Handle illegelArgumentException when setting custom mountpoint

Armin Schrenk il y a 2 ans
Parent
commit
6395f17736

+ 9 - 0
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);
+	}
+
+}

+ 8 - 0
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);
+	}
+}

+ 8 - 0
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);
+	}
+}

+ 31 - 5
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;

+ 35 - 1
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<Throwable> 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<Throwable> 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();
+	}
+
 }

+ 8 - 0
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<Throwable> unlockException() {
+		return new AtomicReference<>();
+	}
+
 	@Provides
 	@FxmlScene(FxmlFile.UNLOCK_SUCCESS)
 	@UnlockScoped

+ 13 - 3
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<Boolean> {
 	private final Lazy<Scene> invalidMountPointScene;
 	private final FxApplicationWindows appWindows;
 	private final KeyLoadingStrategy keyLoadingStrategy;
+	private final AtomicReference<Throwable> unlockFailedException;
 
 	@Inject
-	UnlockWorkflow(@UnlockWindow Stage window, @UnlockWindow Vault vault, VaultService vaultService, @FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy<Scene> successScene, @FxmlScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT) Lazy<Scene> invalidMountPointScene, FxApplicationWindows appWindows, @UnlockWindow KeyLoadingStrategy keyLoadingStrategy) {
+	UnlockWorkflow(@UnlockWindow Stage window, @UnlockWindow Vault vault, VaultService vaultService, @FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy<Scene> successScene, @FxmlScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT) Lazy<Scene> invalidMountPointScene, FxApplicationWindows appWindows, @UnlockWindow KeyLoadingStrategy keyLoadingStrategy, @UnlockWindow AtomicReference<Throwable> unlockFailedException) {
 		this.window = window;
 		this.vault = vault;
 		this.vaultService = vaultService;
@@ -48,6 +51,7 @@ public class UnlockWorkflow extends Task<Boolean> {
 		this.invalidMountPointScene = invalidMountPointScene;
 		this.appWindows = appWindows;
 		this.keyLoadingStrategy = keyLoadingStrategy;
+		this.unlockFailedException = unlockFailedException;
 	}
 
 	@Override
@@ -67,13 +71,15 @@ public class UnlockWorkflow extends Task<Boolean> {
 		} 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<Boolean> {
 	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);
 	}
 

+ 5 - 2
src/main/resources/fxml/unlock_invalid_mount_point.fxml

@@ -34,16 +34,19 @@
 			</StackPane>
 		</Group>
 		<VBox HBox.hgrow="ALWAYS">
-			<Label styleClass="label-large" text="%unlock.error.message" wrapText="true" textAlignment="LEFT">
+			<Label styleClass="label-large" text="%unlock.error.customPath.message" wrapText="true" textAlignment="LEFT">
 				<padding>
 					<Insets bottom="6" top="6"/>
 				</padding>
 			</Label>
 
+			<FormattedLabel fx:id="dialogDescription" wrapText="true" textAlignment="LEFT"/>
+
 			<Region VBox.vgrow="ALWAYS" minHeight="18"/>
-			<ButtonBar buttonMinWidth="120" buttonOrder="+C">
+			<ButtonBar buttonMinWidth="120" buttonOrder="+CI">
 				<buttons>
 					<Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#close"/>
+					<Button text="%hub.noKeychain.openBtn" ButtonBar.buttonData="FINISH" defaultButton="true" onAction="#closeAndOpenPreferences"/>
 				</buttons>
 			</ButtonBar>
 		</VBox>

+ 4 - 5
src/main/resources/i18n/strings.properties

@@ -124,11 +124,10 @@ unlock.success.description=Content in vault "%s" is now accessible over its moun
 unlock.success.rememberChoice=Remember my choice, don't ask again
 unlock.success.revealBtn=Reveal Drive
 ## Failure
-unlock.error.message=Unable to unlock vault
-### Invalid Mount Point
-unlock.error.invalidMountPoint.notExisting=Mount point "%s" is not a directory, not empty or does not exist.
-unlock.error.invalidMountPoint.existing=Mount point "%s" already exists or parent folder is missing.
-unlock.error.invalidMountPoint.driveLetterOccupied=Drive Letter "%s" is already in use.
+unlock.error.customPath.message=Unable to mount vault to custom path
+unlock.error.customPath.description.notSupported=If you wish to keep using the custom path, please go to the preferences and select a volume type that supports it. Otherwise, go to the vault options and choose a supported mount point.
+unlock.error.customPath.description.notExists=The custom mount path does not exist. Either create it in your local filesystem or change it in the vault options.
+unlock.error.customPath.description.generic=You have selected a custom mount path for this vault, but using it failed with the message: %s.
 ## Hub
 hub.noKeychain.message=Unable to access device key
 hub.noKeychain.description=In order to unlock Hub vaults, a device key is required, which is secured using a keychain. To proceed, enable “%s” and select a keychain in the preferences.