Selaa lähdekoodia

Removed "Mount after unlock" option. Mounting/unmounting no longer visible to the user, but merged with unlocking/locking.

Sebastian Stenzel 7 vuotta sitten
vanhempi
commit
d53af61b58

+ 1 - 7
main/commons/src/main/java/org/cryptomator/common/settings/VaultSettings.java

@@ -26,7 +26,6 @@ import javafx.beans.property.StringProperty;
 public class VaultSettings {
 
 	public static final boolean DEFAULT_UNLOCK_AFTER_STARTUP = false;
-	public static final boolean DEFAULT_MOUNT_AFTER_UNLOCK = true;
 	public static final boolean DEFAULT_REAVEAL_AFTER_MOUNT = true;
 	public static final boolean DEFAULT_USES_INDIVIDUAL_MOUNTPATH = false;
 
@@ -35,7 +34,6 @@ public class VaultSettings {
 	private final StringProperty mountName = new SimpleStringProperty();
 	private final StringProperty winDriveLetter = new SimpleStringProperty();
 	private final BooleanProperty unlockAfterStartup = new SimpleBooleanProperty(DEFAULT_UNLOCK_AFTER_STARTUP);
-	private final BooleanProperty mountAfterUnlock = new SimpleBooleanProperty(DEFAULT_MOUNT_AFTER_UNLOCK);
 	private final BooleanProperty revealAfterMount = new SimpleBooleanProperty(DEFAULT_REAVEAL_AFTER_MOUNT);
 	private final BooleanProperty usesIndividualMountPath = new SimpleBooleanProperty(DEFAULT_USES_INDIVIDUAL_MOUNTPATH);
 	private final StringProperty individualMountPath = new SimpleStringProperty();
@@ -47,7 +45,7 @@ public class VaultSettings {
 	}
 
 	Observable[] observables() {
-		return new Observable[]{path, mountName, winDriveLetter, unlockAfterStartup, mountAfterUnlock, revealAfterMount, usesIndividualMountPath, individualMountPath};
+		return new Observable[]{path, mountName, winDriveLetter, unlockAfterStartup, revealAfterMount, usesIndividualMountPath, individualMountPath};
 	}
 
 	private void deriveMountNameFromPath(Path path) {
@@ -118,10 +116,6 @@ public class VaultSettings {
 		return unlockAfterStartup;
 	}
 
-	public BooleanProperty mountAfterUnlock() {
-		return mountAfterUnlock;
-	}
-
 	public BooleanProperty revealAfterMount() {
 		return revealAfterMount;
 	}

+ 0 - 6
main/commons/src/main/java/org/cryptomator/common/settings/VaultSettingsJsonAdapter.java

@@ -25,7 +25,6 @@ class VaultSettingsJsonAdapter {
 		out.name("mountName").value(value.mountName().get());
 		out.name("winDriveLetter").value(value.winDriveLetter().get());
 		out.name("unlockAfterStartup").value(value.unlockAfterStartup().get());
-		out.name("mountAfterUnlock").value(value.mountAfterUnlock().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!)
@@ -40,7 +39,6 @@ class VaultSettingsJsonAdapter {
 		String individualMountPath = null;
 		String winDriveLetter = null;
 		boolean unlockAfterStartup = VaultSettings.DEFAULT_UNLOCK_AFTER_STARTUP;
-		boolean mountAfterUnlock = VaultSettings.DEFAULT_MOUNT_AFTER_UNLOCK;
 		boolean revealAfterMount = VaultSettings.DEFAULT_REAVEAL_AFTER_MOUNT;
 		boolean usesIndividualMountPath = VaultSettings.DEFAULT_USES_INDIVIDUAL_MOUNTPATH;
 
@@ -63,9 +61,6 @@ class VaultSettingsJsonAdapter {
 				case "unlockAfterStartup":
 					unlockAfterStartup = in.nextBoolean();
 					break;
-				case "mountAfterUnlock":
-					mountAfterUnlock = in.nextBoolean();
-					break;
 				case "revealAfterMount":
 					revealAfterMount = in.nextBoolean();
 					break;
@@ -87,7 +82,6 @@ class VaultSettingsJsonAdapter {
 		vaultSettings.path().set(Paths.get(path));
 		vaultSettings.winDriveLetter().set(winDriveLetter);
 		vaultSettings.unlockAfterStartup().set(unlockAfterStartup);
-		vaultSettings.mountAfterUnlock().set(mountAfterUnlock);
 		vaultSettings.revealAfterMount().set(revealAfterMount);
 		vaultSettings.usesIndividualMountPath().set(usesIndividualMountPath);
 		vaultSettings.individualMountPath().set(individualMountPath);

+ 0 - 7
main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockController.java

@@ -107,9 +107,6 @@ public class UnlockController implements ViewController {
 	@FXML
 	private CheckBox savePassword;
 
-	@FXML
-	private CheckBox mountAfterUnlock;
-
 	@FXML
 	private TextField mountName;
 
@@ -159,8 +156,6 @@ public class UnlockController implements ViewController {
 	public void initialize() {
 		advancedOptions.managedProperty().bind(advancedOptions.visibleProperty());
 		unlockButton.disableProperty().bind(passwordField.textProperty().isEmpty());
-		mountName.disableProperty().bind(mountAfterUnlock.selectedProperty().not());
-		revealAfterMount.disableProperty().bind(mountAfterUnlock.selectedProperty().not());
 		mountName.addEventFilter(KeyEvent.KEY_TYPED, this::filterAlphanumericKeyEvents);
 		mountName.textProperty().addListener(this::mountNameDidChange);
 		savePassword.setDisable(!keychainAccess.isPresent());
@@ -250,12 +245,10 @@ public class UnlockController implements ViewController {
 		}
 		VaultSettings vaultSettings = vault.getVaultSettings();
 		unlockAfterStartup.setSelected(savePassword.isSelected() && vaultSettings.unlockAfterStartup().get());
-		mountAfterUnlock.setSelected(vaultSettings.mountAfterUnlock().get());
 		revealAfterMount.setSelected(vaultSettings.revealAfterMount().get());
 		useOwnMountPath.setSelected(vaultSettings.usesIndividualMountPath().get());
 
 		vaultSubs = vaultSubs.and(EasyBind.subscribe(unlockAfterStartup.selectedProperty(), vaultSettings.unlockAfterStartup()::set));
-		vaultSubs = vaultSubs.and(EasyBind.subscribe(mountAfterUnlock.selectedProperty(), vaultSettings.mountAfterUnlock()::set));
 		vaultSubs = vaultSubs.and(EasyBind.subscribe(revealAfterMount.selectedProperty(), vaultSettings.revealAfterMount()::set));
 		vaultSubs = vaultSubs.and(EasyBind.subscribe(useOwnMountPath.selectedProperty(), vaultSettings.usesIndividualMountPath()::set));
 

+ 27 - 75
main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockedController.java

@@ -83,15 +83,6 @@ public class UnlockedController implements ViewController {
 	@FXML
 	private ContextMenu moreOptionsMenu;
 
-	@FXML
-	private MenuItem mountVaultMenuItem;
-
-	@FXML
-	private MenuItem unmountVaultMenuItem;
-
-	@FXML
-	private MenuItem revealVaultMenuItem;
-
 	@FXML
 	private VBox root;
 
@@ -103,10 +94,6 @@ public class UnlockedController implements ViewController {
 
 	@Override
 	public void initialize() {
-		mountVaultMenuItem.disableProperty().bind(vaultState.isEqualTo(Vault.State.UNLOCKED).not()); // enable when unlocked
-		unmountVaultMenuItem.disableProperty().bind(vaultState.isEqualTo(Vault.State.MOUNTED).not()); // enable when mounted
-		revealVaultMenuItem.disableProperty().bind(vaultState.isEqualTo(Vault.State.MOUNTED).not()); // enable when mounted
-
 		EasyBind.subscribe(vault, this::vaultChanged);
 		EasyBind.subscribe(moreOptionsMenu.showingProperty(), moreOptionsButton::setSelected);
 	}
@@ -121,10 +108,6 @@ public class UnlockedController implements ViewController {
 			return;
 		}
 
-		if (newVault.getState() == Vault.State.UNLOCKED && newVault.getVaultSettings().mountAfterUnlock().get()) {
-			mountVault(newVault);
-		}
-
 		// (re)start throughput statistics:
 		stopIoSampling();
 		startIoSampling();
@@ -132,57 +115,16 @@ public class UnlockedController implements ViewController {
 
 	@FXML
 	private void didClickLockVault(ActionEvent event) {
-		regularUnmountVault(this::lockVault);
+		regularLockVault(this::lockVaultSucceeded);
 	}
 
-	private void lockVault() {
-		try {
-			vault.get().lock();
-		} catch (ServerLifecycleException | IOException e) {
-			LOG.error("Lock failed", e);
-		}
+	private void lockVaultSucceeded() {
 		listener.ifPresent(listener -> listener.didLock(this));
 	}
 
-	@FXML
-	private void didClickMoreOptions(ActionEvent event) {
-		if (moreOptionsMenu.isShowing()) {
-			moreOptionsMenu.hide();
-		} else {
-			moreOptionsMenu.setAnchorLocation(AnchorLocation.CONTENT_TOP_RIGHT);
-			moreOptionsMenu.show(moreOptionsButton, Side.BOTTOM, moreOptionsButton.getWidth(), 0.0);
-		}
-	}
-
-	@FXML
-	public void didClickMountVault(ActionEvent event) {
-		mountVault(vault.get());
-	}
-
-	private void mountVault(Vault vault) {
-		asyncTaskService.asyncTaskOf(() -> {
-			vault.mount();
-		}).onSuccess(() -> {
-			LOG.trace("Mount succeeded.");
-			messageLabel.setText(null);
-			if (vault.getVaultSettings().revealAfterMount().get()) {
-				revealVault(vault);
-			}
-		}).onError(CommandFailedException.class, e -> {
-			LOG.error("Mount failed.", e);
-			// TODO Markus Kreusch #393: hyperlink auf FAQ oder sowas?
-			messageLabel.setText(localization.getString("unlocked.label.mountFailed"));
-		}).run();
-	}
-
-	@FXML
-	public void didClickUnmountVault(ActionEvent event) {
-		regularUnmountVault(Runnables.doNothing());
-	}
-
-	private void regularUnmountVault(Runnable onSuccess) {
+	private void regularLockVault(Runnable onSuccess) {
 		asyncTaskService.asyncTaskOf(() -> {
-			vault.get().unmount();
+			vault.get().lock(false);
 		}).onSuccess(() -> {
 			LOG.trace("Regular unmount succeeded.");
 			onSuccess.run();
@@ -191,18 +133,6 @@ public class UnlockedController implements ViewController {
 		}).run();
 	}
 
-	private void forcedUnmountVault(Runnable onSuccess) {
-		asyncTaskService.asyncTaskOf(() -> {
-			vault.get().unmountForced();
-		}).onSuccess(() -> {
-			LOG.trace("Forced unmount succeeded.");
-			onSuccess.run();
-		}).onError(Exception.class, e -> {
-			LOG.error("Forced unmount failed.", e);
-			messageLabel.setText(localization.getString("unlocked.label.unmountFailed"));
-		}).run();
-	}
-
 	private void onRegularUnmountVaultFailed(Exception e, Runnable onSuccess) {
 		if (vault.get().supportsForcedUnmount()) {
 			LOG.trace("Regular unmount failed.", e);
@@ -214,7 +144,7 @@ public class UnlockedController implements ViewController {
 
 			Optional<ButtonType> choice = confirmDialog.showAndWait();
 			if (ButtonType.YES.equals(choice.get())) {
-				forcedUnmountVault(onSuccess);
+				forcedLockVault(onSuccess);
 			} else {
 				LOG.trace("Unmount cancelled.", e);
 			}
@@ -224,6 +154,28 @@ public class UnlockedController implements ViewController {
 		}
 	}
 
+	private void forcedLockVault(Runnable onSuccess) {
+		asyncTaskService.asyncTaskOf(() -> {
+			vault.get().lock(true);
+		}).onSuccess(() -> {
+			LOG.trace("Forced unmount succeeded.");
+			onSuccess.run();
+		}).onError(Exception.class, e -> {
+			LOG.error("Forced unmount failed.", e);
+			messageLabel.setText(localization.getString("unlocked.label.unmountFailed"));
+		}).run();
+	}
+
+	@FXML
+	private void didClickMoreOptions(ActionEvent event) {
+		if (moreOptionsMenu.isShowing()) {
+			moreOptionsMenu.hide();
+		} else {
+			moreOptionsMenu.setAnchorLocation(AnchorLocation.CONTENT_TOP_RIGHT);
+			moreOptionsMenu.show(moreOptionsButton, Side.BOTTOM, moreOptionsButton.getWidth(), 0.0);
+		}
+	}
+
 	@FXML
 	private void didClickRevealVault(ActionEvent event) {
 		revealVault(vault.get());

+ 2 - 6
main/ui/src/main/java/org/cryptomator/ui/controls/DirectoryListCell.java

@@ -68,9 +68,7 @@ public class DirectoryListCell extends DraggableListCell<Vault> {
 		}
 		switch (state) {
 		case UNLOCKED:
-		case MOUNTED:
-		case MOUNTING:
-		case UNMOUNTING:
+		case PROCESSING:
 			return "\uf09c";
 		case LOCKED:
 		default:
@@ -84,9 +82,7 @@ public class DirectoryListCell extends DraggableListCell<Vault> {
 		}
 		switch (state) {
 		case UNLOCKED:
-		case MOUNTED:
-		case MOUNTING:
-		case UNMOUNTING:
+		case PROCESSING:
 			return UNLOCKED_ICON_COLOR;
 		case LOCKED:
 		default:

+ 2 - 14
main/ui/src/main/java/org/cryptomator/ui/model/AutoUnlocker.java

@@ -73,26 +73,14 @@ public class AutoUnlocker {
 		}
 		try {
 			vault.unlock(CharBuffer.wrap(storedPw));
-			mountSilently(vault);
-		} catch (IOException | CryptoException e) {
+			revealSilently(vault);
+		} catch (IOException | CryptoException | CommandFailedException e) {
 			LOG.error("Auto unlock failed.", e);
 		} finally {
 			Arrays.fill(storedPw, ' ');
 		}
 	}
 
-	private void mountSilently(Vault unlockedVault) {
-		if (!unlockedVault.getVaultSettings().mountAfterUnlock().get()) {
-			return;
-		}
-		try {
-			unlockedVault.mount();
-			revealSilently(unlockedVault);
-		} catch (CommandFailedException e) {
-			LOG.error("Auto unlock succeded, but mounting the drive failed.", e);
-		}
-	}
-
 	private void revealSilently(Vault mountedVault) {
 		if (!mountedVault.getVaultSettings().revealAfterMount().get()) {
 			return;

+ 8 - 34
main/ui/src/main/java/org/cryptomator/ui/model/FuseVolume.java

@@ -31,7 +31,6 @@ public class FuseVolume implements Volume {
 	private static final String DEFAULT_MOUNTROOTPATH_LINUX = System.getProperty("user.home") + "/.Cryptomator";
 
 	private final VaultSettings vaultSettings;
-	private final WindowsDriveLetters windowsDriveLetters;
 
 	private Mount fuseMnt;
 	private CryptoFileSystem cfs;
@@ -39,26 +38,16 @@ public class FuseVolume implements Volume {
 	private boolean extraDirCreated;
 
 	@Inject
-	public FuseVolume(VaultSettings vaultSettings, WindowsDriveLetters windowsDriveLetters) {
+	public FuseVolume(VaultSettings vaultSettings) {
 		this.vaultSettings = vaultSettings;
-		this.windowsDriveLetters = windowsDriveLetters;
 		this.extraDirCreated = false;
 	}
 
 	@Override
-	public void prepare(CryptoFileSystem fs) throws IOException, FuseNotSupportedException {
+	public void mount(CryptoFileSystem fs) throws IOException, FuseNotSupportedException, CommandFailedException {
 		this.cfs = fs;
 		String mountPath;
-		if (SystemUtils.IS_OS_WINDOWS) {
-			//windows case
-			if (vaultSettings.winDriveLetter().get() != null) {
-				// specific drive letter selected
-				mountPath = vaultSettings.winDriveLetter().get() + ":\\";
-			} else {
-				// auto assign drive letter
-				mountPath = windowsDriveLetters.getAvailableDriveLetters().iterator().next() + ":\\";
-			}
-		} else if (vaultSettings.usesIndividualMountPath().get()) {
+		if (vaultSettings.usesIndividualMountPath().get()) {
 			//specific path given
 			mountPath = vaultSettings.individualMountPath().get();
 		} else {
@@ -67,6 +56,7 @@ public class FuseVolume implements Volume {
 			extraDirCreated = true;
 		}
 		this.mountPath = Paths.get(mountPath).toAbsolutePath();
+		mount();
 	}
 
 	private String createDirIfNotExist(String prefix, String dirName) throws IOException {
@@ -87,8 +77,7 @@ public class FuseVolume implements Volume {
 		}
 	}
 
-	@Override
-	public void mount() throws CommandFailedException {
+	private void mount() throws CommandFailedException {
 		try {
 			EnvironmentVariables envVars = EnvironmentVariables.create()
 					.withMountName(vaultSettings.mountName().getValue())
@@ -117,15 +106,10 @@ public class FuseVolume implements Volume {
 		} catch (org.cryptomator.frontend.fuse.mount.CommandFailedException e) {
 			throw new CommandFailedException(e);
 		}
+		cleanup();
 	}
 
-	@Override
-	public synchronized void unmountForced() throws CommandFailedException {
-		unmount();
-	}
-
-	@Override
-	public void stop() {
+	private void cleanup() {
 		if (extraDirCreated) {
 			try {
 				Files.delete(mountPath);
@@ -135,19 +119,9 @@ public class FuseVolume implements Volume {
 		}
 	}
 
-	@Override
-	public String getMountUri() {
-		return "";
-	}
-
 	@Override
 	public boolean isSupported() {
-		return FuseMountFactory.isFuseSupported();
-	}
-
-	@Override
-	public boolean supportsForcedUnmount() {
-		return false;
+		return (SystemUtils.IS_OS_MAC_OSX || SystemUtils.IS_OS_LINUX) && FuseMountFactory.isFuseSupported();
 	}
 
 }

+ 26 - 54
main/ui/src/main/java/org/cryptomator/ui/model/Vault.java

@@ -20,16 +20,6 @@ import java.util.function.Predicate;
 
 import javax.inject.Inject;
 
-import org.cryptomator.common.LazyInitializer;
-import org.cryptomator.common.settings.Settings;
-import org.cryptomator.common.settings.VaultSettings;
-import org.cryptomator.cryptofs.CryptoFileSystem;
-import org.cryptomator.cryptofs.CryptoFileSystemProperties;
-import org.cryptomator.cryptofs.CryptoFileSystemProvider;
-import org.cryptomator.cryptolib.api.CryptoException;
-import org.cryptomator.cryptolib.api.InvalidPassphraseException;
-import org.cryptomator.ui.model.VaultModule.PerVault;
-
 import javafx.application.Platform;
 import javafx.beans.Observable;
 import javafx.beans.binding.Binding;
@@ -39,6 +29,15 @@ import javafx.beans.property.SimpleObjectProperty;
 import javafx.beans.property.StringProperty;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.SystemUtils;
+import org.cryptomator.common.LazyInitializer;
+import org.cryptomator.common.settings.Settings;
+import org.cryptomator.common.settings.VaultSettings;
+import org.cryptomator.cryptofs.CryptoFileSystem;
+import org.cryptomator.cryptofs.CryptoFileSystemProperties;
+import org.cryptomator.cryptofs.CryptoFileSystemProvider;
+import org.cryptomator.cryptolib.api.CryptoException;
+import org.cryptomator.cryptolib.api.InvalidPassphraseException;
+import org.cryptomator.ui.model.VaultModule.PerVault;
 import org.fxmisc.easybind.EasyBind;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -59,7 +58,7 @@ public class Vault {
 	private Volume volume;
 
 	public enum State {
-		LOCKED, UNLOCKED, MOUNTING, MOUNTED, UNMOUNTING
+		LOCKED, PROCESSING, UNLOCKED
 	}
 
 	@Inject
@@ -98,51 +97,33 @@ public class Vault {
 		CryptoFileSystemProvider.changePassphrase(getPath(), MASTERKEY_FILENAME, oldPassphrase, newPassphrase);
 	}
 
-	public synchronized void unlock(CharSequence passphrase) throws CryptoException, IOException {
-		CryptoFileSystem fs = getCryptoFileSystem(passphrase);
-		volume.prepare(fs);
+	public synchronized void unlock(CharSequence passphrase) throws CryptoException, IOException, CommandFailedException {
 		Platform.runLater(() -> {
-			state.set(State.UNLOCKED);
+			state.set(State.PROCESSING);
 		});
-	}
-
-	public synchronized void mount() throws CommandFailedException {
-		Platform.runLater(() -> {
-			state.set(State.MOUNTING);
-		});
-		volume.mount();
+		CryptoFileSystem fs = getCryptoFileSystem(passphrase);
+		volume.mount(fs);
 		Platform.runLater(() -> {
-			state.set(State.MOUNTED);
+			state.set(State.UNLOCKED);
 		});
 	}
 
-	public synchronized void unmountForced() throws CommandFailedException {
-		unmount(true);
-	}
-
-	public synchronized void unmount() throws CommandFailedException {
-		unmount(false);
-	}
-
-	private synchronized void unmount(boolean forced) throws CommandFailedException {
+	public synchronized void lock(boolean forced) throws CommandFailedException {
 		Platform.runLater(() -> {
-			state.set(State.UNMOUNTING);
+			state.set(State.PROCESSING);
 		});
 		if (forced && volume.supportsForcedUnmount()) {
 			volume.unmountForced();
 		} else {
 			volume.unmount();
 		}
-		Platform.runLater(() -> {
-			state.set(State.UNLOCKED);
-		});
-	}
-
-	public synchronized void lock() throws IOException {
-		volume.stop();
 		CryptoFileSystem fs = cryptoFileSystem.getAndSet(null);
 		if (fs != null) {
-			fs.close();
+			try {
+				fs.close();
+			} catch (IOException e) {
+				LOG.error("Error closing file system.", e);
+			}
 		}
 		Platform.runLater(() -> {
 			state.set(State.LOCKED);
@@ -154,23 +135,18 @@ public class Vault {
 	 */
 	public void prepareForShutdown() {
 		try {
-			unmount();
+			lock(false);
 		} catch (CommandFailedException e) {
 			if (volume.supportsForcedUnmount()) {
 				try {
-					unmountForced();
+					lock(true);
 				} catch (CommandFailedException e1) {
-					LOG.warn("Failed to force unmount vault.");
+					LOG.warn("Failed to force lock vault.", e1);
 				}
 			} else {
-				LOG.warn("Failed to gracefully unmount vault.");
+				LOG.warn("Failed to gracefully lock vault.", e);
 			}
 		}
-		try {
-			lock();
-		} catch (Exception e) {
-			LOG.warn("Failed to lock vault.");
-		}
 	}
 
 	public void reveal() throws CommandFailedException {
@@ -289,10 +265,6 @@ public class Vault {
 		}
 	}
 
-	public String getFilesystemRootUrl() {
-		return volume.getMountUri();
-	}
-
 	public String getId() {
 		return vaultSettings.getId();
 	}

+ 16 - 13
main/ui/src/main/java/org/cryptomator/ui/model/Volume.java

@@ -9,9 +9,18 @@ import java.io.IOException;
  */
 public interface Volume {
 
-	void prepare(CryptoFileSystem fs) throws IOException;
-
-	void mount() throws CommandFailedException;
+	/**
+	 * Checks in constant time whether this volume type is supported on the system running Cryptomator.
+	 * @return true if this volume can be mounted
+	 */
+	boolean isSupported();
+
+	/**
+	 *
+	 * @param fs
+	 * @throws IOException
+	 */
+	void mount(CryptoFileSystem fs) throws IOException, CommandFailedException;
 
 	default void reveal() throws CommandFailedException {
 		throw new CommandFailedException("Not implemented.");
@@ -19,20 +28,14 @@ public interface Volume {
 
 	void unmount() throws CommandFailedException;
 
-	default void unmountForced() throws CommandFailedException {
-		throw new CommandFailedException("Operation not supported.");
-	}
-
-	void stop();
-
-	String getMountUri();
+	// optional forced unmounting:
 
-	default boolean isSupported() {
+	default boolean supportsForcedUnmount() {
 		return false;
 	}
 
-	default boolean supportsForcedUnmount() {
-		return false;
+	default void unmountForced() throws CommandFailedException {
+		throw new CommandFailedException("Operation not supported.");
 	}
 
 }

+ 13 - 16
main/ui/src/main/java/org/cryptomator/ui/model/WebDavVolume.java

@@ -36,7 +36,7 @@ public class WebDavVolume implements Volume {
 	}
 
 	@Override
-	public void prepare(CryptoFileSystem fs) {
+	public void mount(CryptoFileSystem fs) throws CommandFailedException {
 		if (server == null) {
 			server = serverProvider.get();
 		}
@@ -45,10 +45,10 @@ public class WebDavVolume implements Volume {
 		}
 		servlet = server.createWebDavServlet(fs.getPath("/"), vaultSettings.getId() + "/" + vaultSettings.mountName().get());
 		servlet.start();
+		mount();
 	}
 
-	@Override
-	public void mount() throws CommandFailedException {
+	private void mount() throws CommandFailedException {
 		if (servlet == null) {
 			throw new IllegalStateException("Mounting requires unlocked WebDAV servlet.");
 		}
@@ -82,11 +82,17 @@ public class WebDavVolume implements Volume {
 		} catch (Mounter.CommandFailedException e) {
 			throw new CommandFailedException(e);
 		}
+		cleanup();
 	}
 
 	@Override
-	public synchronized void unmountForced() {
-		mount.forced();
+	public synchronized void unmountForced() throws CommandFailedException {
+		try {
+			mount.forced().orElseThrow(IllegalStateException::new).unmount();
+		} catch (Mounter.CommandFailedException e) {
+			throw new CommandFailedException(e);
+		}
+		cleanup();
 	}
 
 	private String getLocalhostAliasOrNull() {
@@ -102,28 +108,19 @@ public class WebDavVolume implements Volume {
 		}
 	}
 
-	@Override
-	public void stop() {
+	private void cleanup() {
 		if (servlet != null) {
 			servlet.stop();
 		}
 
 	}
 
-	public synchronized String getMountUri() {
-		return servlet.getServletRootUri().toString() + "/";
-	}
-
-	/**
-	 * TODO: what to check wether it is implemented?
-	 *
-	 * @return
-	 */
 	@Override
 	public boolean isSupported() {
 		return true;
 	}
 
+	@Override
 	public boolean supportsForcedUnmount() {
 		return mount != null && mount.forced().isPresent();
 	}

+ 12 - 15
main/ui/src/main/resources/fxml/unlock.fxml

@@ -75,28 +75,25 @@
 			
 			<!-- Row 3.2 -->
 			<CheckBox GridPane.rowIndex="2" GridPane.columnIndex="0" GridPane.columnSpan="2" fx:id="unlockAfterStartup" text="%unlock.label.unlockAfterStartup" cacheShape="true" cache="true" />
-			
+
 			<!-- Row 3.3 -->
-			<CheckBox GridPane.rowIndex="3" GridPane.columnIndex="0" GridPane.columnSpan="2" fx:id="mountAfterUnlock" text="%unlock.label.mountAfterUnlock" cacheShape="true" cache="true" />
+			<Label GridPane.rowIndex="3" GridPane.columnIndex="0" text="%unlock.label.mountName"  cacheShape="true" cache="true" />
+			<TextField GridPane.rowIndex="3" GridPane.columnIndex="1" fx:id="mountName" GridPane.hgrow="ALWAYS" maxWidth="Infinity" cacheShape="true" cache="true" />
 			
 			<!-- Row 3.4 -->
-			<Label GridPane.rowIndex="4" GridPane.columnIndex="0" text="%unlock.label.mountName"  cacheShape="true" cache="true" />
-			<TextField GridPane.rowIndex="4" GridPane.columnIndex="1" fx:id="mountName" GridPane.hgrow="ALWAYS" maxWidth="Infinity" cacheShape="true" cache="true" />
-			
-			<!-- Row 3.5 -->
-			<CheckBox GridPane.rowIndex="5" GridPane.columnIndex="0" GridPane.columnSpan="2" fx:id="revealAfterMount" text="%unlock.label.revealAfterMount" cacheShape="true" cache="true" />
+			<CheckBox GridPane.rowIndex="4" GridPane.columnIndex="0" GridPane.columnSpan="2" fx:id="revealAfterMount" text="%unlock.label.revealAfterMount" cacheShape="true" cache="true" />
 			
-			<!-- Row 3.6 Alt1 -->
-			<Label GridPane.rowIndex="6" GridPane.columnIndex="0" fx:id="winDriveLetterLabel" text="%unlock.label.winDriveLetter" cacheShape="true" cache="true" />
-			<ChoiceBox GridPane.rowIndex="6" GridPane.columnIndex="1" fx:id="winDriveLetter" GridPane.hgrow="ALWAYS" maxWidth="Infinity" cacheShape="true" cache="true" />
+			<!-- Row 3.5 Alt1 -->
+			<Label GridPane.rowIndex="5" GridPane.columnIndex="0" fx:id="winDriveLetterLabel" text="%unlock.label.winDriveLetter" cacheShape="true" cache="true" />
+			<ChoiceBox GridPane.rowIndex="5" GridPane.columnIndex="1" fx:id="winDriveLetter" GridPane.hgrow="ALWAYS" maxWidth="Infinity" cacheShape="true" cache="true" />
 
-			<!-- Row 3.6 Alt2 -->
-			<CheckBox GridPane.rowIndex="6" GridPane.columnIndex="0" fx:id="useOwnMountPath" text="%unlock.label.useOwnMountPath" cacheShape="true" cache="true" />
+			<!-- Row 3.5 Alt2 -->
+			<CheckBox GridPane.rowIndex="5" GridPane.columnIndex="0" fx:id="useOwnMountPath" text="%unlock.label.useOwnMountPath" cacheShape="true" cache="true" />
 
-			<Label GridPane.rowIndex="7" GridPane.columnIndex="0" fx:id="mountPathLabel" text="%unlock.label.mountPath" cacheShape="true" cache="true" />
+			<Label GridPane.rowIndex="6" GridPane.columnIndex="0" fx:id="mountPathLabel" text="%unlock.label.mountPath" cacheShape="true" cache="true" />
 
-			<HBox GridPane.rowIndex="7" GridPane.columnIndex="1" fx:id="mountPathBox" spacing="6.0">
-				<TextField GridPane.rowIndex="7" GridPane.columnIndex="1" fx:id="mountPath"  cacheShape="true" cache="true" />
+			<HBox GridPane.rowIndex="6" GridPane.columnIndex="1" fx:id="mountPathBox" spacing="6.0">
+				<TextField fx:id="mountPath"  cacheShape="true" cache="true" />
 				<Button text="%unlock.label.mountPathButton" fx:id="changeMountPathButton" onAction="#didClickchangeMountPathButton"/>
 			</HBox>
 

+ 0 - 6
main/ui/src/main/resources/fxml/unlocked.fxml

@@ -28,12 +28,6 @@
 	<fx:define>
 		<ContextMenu fx:id="moreOptionsMenu">
 			<items>
-				<MenuItem fx:id="mountVaultMenuItem" text="%unlocked.moreOptions.mount" onAction="#didClickMountVault">
-					<graphic><Label text="&#xf139;" styleClass="ionicons"/></graphic>
-				</MenuItem>
-				<MenuItem fx:id="unmountVaultMenuItem" text="%unlocked.moreOptions.unmount" onAction="#didClickUnmountVault">
-					<graphic><Label text="&#xf131;" styleClass="ionicons"/></graphic>
-				</MenuItem>
 				<MenuItem fx:id="revealVaultMenuItem" text="%unlocked.moreOptions.reveal" onAction="#didClickRevealVault">
 					<graphic><Label text="&#xf133;" styleClass="ionicons"/></graphic>
 				</MenuItem>

+ 0 - 3
main/ui/src/main/resources/localization/en.txt

@@ -62,7 +62,6 @@ upgrade.version5toX.msg=This vault needs to be migrated to a newer format.\nPlea
 # unlock.fxml
 unlock.label.password=Password
 unlock.label.savePassword=Save Password
-unlock.label.mountAfterUnlock=Mount Drive
 unlock.label.mountName=Drive Name
 unlock.label.unlockAfterStartup=Auto-Unlock on Start (Experimental)
 unlock.label.revealAfterMount=Reveal Drive
@@ -100,8 +99,6 @@ changePassword.errorMessage.decryptionFailed=Decryption failed
 
 # unlocked.fxml
 unlocked.button.lock=Lock Vault
-unlocked.moreOptions.mount=Mount Drive
-unlocked.moreOptions.unmount=Eject Drive
 unlocked.moreOptions.reveal=Reveal Drive
 unlocked.label.mountFailed=Connecting drive failed
 unlocked.label.revealFailed=Command failed