From 1e7478a89f2f0460c92705857f4411c21f385b29 Mon Sep 17 00:00:00 2001 From: infeo Date: Sat, 3 Feb 2018 16:43:33 +0100 Subject: [PATCH 01/23] commit of the nioAdapter Interface, implementation enum and the commandFailedException --- .../ui/model/CommandFailedException.java | 17 ++++++++++ .../org/cryptomator/ui/model/NioAdapter.java | 31 +++++++++++++++++++ .../cryptomator/ui/model/NioAdapterImpl.java | 10 ++++++ 3 files changed, 58 insertions(+) create mode 100644 main/ui/src/main/java/org/cryptomator/ui/model/CommandFailedException.java create mode 100644 main/ui/src/main/java/org/cryptomator/ui/model/NioAdapter.java create mode 100644 main/ui/src/main/java/org/cryptomator/ui/model/NioAdapterImpl.java diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/CommandFailedException.java b/main/ui/src/main/java/org/cryptomator/ui/model/CommandFailedException.java new file mode 100644 index 000000000..cfac40ab0 --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/model/CommandFailedException.java @@ -0,0 +1,17 @@ +package org.cryptomator.ui.model; + +public class CommandFailedException extends Exception { + + public CommandFailedException(String message) { + super(message); + } + + public CommandFailedException(Throwable cause) { + super(cause); + } + + public CommandFailedException(String message, Throwable cause){ + super(message,cause); + } + +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/NioAdapter.java b/main/ui/src/main/java/org/cryptomator/ui/model/NioAdapter.java new file mode 100644 index 000000000..9cda2c462 --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/model/NioAdapter.java @@ -0,0 +1,31 @@ +package org.cryptomator.ui.model; + +import org.cryptomator.cryptofs.CryptoFileSystem; + +import java.util.HashMap; + + +public interface NioAdapter { + + void unlock(CryptoFileSystem fs); + + void mount() throws CommandFailedException; + + void unmount() throws CommandFailedException; + + void stop(); + + String getFilesystemRootUrl(); + + default boolean isSupported() { + return false; + } + + default boolean supportsForcedUnmount() { + return false; + } + + default void unmountForced() throws CommandFailedException { + throw new CommandFailedException("Operation not supported"); + } +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/NioAdapterImpl.java b/main/ui/src/main/java/org/cryptomator/ui/model/NioAdapterImpl.java new file mode 100644 index 000000000..6b4cedcdd --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/model/NioAdapterImpl.java @@ -0,0 +1,10 @@ +package org.cryptomator.ui.model; + +import java.util.ArrayList; + +public enum NioAdapterImpl { + + WEBDAV, + FUSE + +} From fa10a92fa46835b5c272bb16bf3fdb3debd63b77 Mon Sep 17 00:00:00 2001 From: infeo Date: Sat, 3 Feb 2018 16:44:07 +0100 Subject: [PATCH 02/23] commit of the webDavNioAdapter --- .../ui/model/WebDavNioAdapter.java | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 main/ui/src/main/java/org/cryptomator/ui/model/WebDavNioAdapter.java diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/WebDavNioAdapter.java b/main/ui/src/main/java/org/cryptomator/ui/model/WebDavNioAdapter.java new file mode 100644 index 000000000..7dc805ed7 --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/model/WebDavNioAdapter.java @@ -0,0 +1,115 @@ +package org.cryptomator.ui.model; + + +import org.cryptomator.common.settings.Settings; +import org.cryptomator.common.settings.VaultSettings; +import org.cryptomator.cryptofs.CryptoFileSystem; +import org.cryptomator.frontend.webdav.WebDavServer; +import org.cryptomator.frontend.webdav.mount.MountParams; +import org.cryptomator.frontend.webdav.mount.Mounter; +import org.cryptomator.frontend.webdav.servlet.WebDavServletController; + +import javax.inject.Inject; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.function.Function; + +@VaultModule.PerVault +public class WebDavNioAdapter implements NioAdapter { + + private static final String LOCALHOST_ALIAS = "cryptomator-vault"; + + private final WebDavServer server; + private final VaultSettings vaultSettings; + private final Settings settings; + + private WebDavServletController servlet; + private Mounter.Mount mount; + + @Inject + public WebDavNioAdapter(WebDavServer server, VaultSettings vaultSettings, Settings settings){ + this.server = server; + this.vaultSettings = vaultSettings; + this.settings = settings; + } + + @Override + public void unlock(CryptoFileSystem fs){ + if(!server.isRunning()){ + server.start(); + } + servlet = server.createWebDavServlet(fs.getPath("/"), vaultSettings.getId()+ "/" + vaultSettings.mountName().get()); + servlet.start(); + } + + @Override + public void mount() throws CommandFailedException { + if (servlet == null) { + throw new IllegalStateException("Mounting requires unlocked WebDAV servlet."); + } + MountParams mountParams = MountParams.create() // + .withWindowsDriveLetter(vaultSettings.winDriveLetter().get()) // + .withPreferredGvfsScheme(settings.preferredGvfsScheme().get())// + .withWebdavHostname(getLocalhostAliasOrNull()) // + .build(); + try { + this.mount = servlet.mount(mountParams); // might block this thread for a while + } catch (Mounter.CommandFailedException e) { + e.printStackTrace(); + throw new CommandFailedException(e); + } + } + + @Override + public synchronized void unmount() throws CommandFailedException { + try { + mount.unmount(); + } + catch (Mounter.CommandFailedException e){ + throw new CommandFailedException(e); + } + } + + @Override + public synchronized void unmountForced(){ + mount.forced(); + } + + private String getLocalhostAliasOrNull() { + try { + InetAddress alias = InetAddress.getByName(LOCALHOST_ALIAS); + if (alias.getHostAddress().equals("127.0.0.1")) { + return LOCALHOST_ALIAS; + } else { + return null; + } + } catch (UnknownHostException e) { + return null; + } + } + + @Override + public void stop() { + if(servlet != null){ + servlet.stop(); + } + + } + + public synchronized String getFilesystemRootUrl() { + return servlet.getServletRootUri().toString(); + } + + /** + * TODO: what to check wether it is implemented? + * @return + */ + @Override + public boolean isSupported() { + return true; + } + + public boolean supportsForcedUnmount() { + return mount != null && mount.forced().isPresent(); + } +} From 2914af5f7ba2f3de3486fc54c0d0d271636ec189 Mon Sep 17 00:00:00 2001 From: infeo Date: Sat, 3 Feb 2018 17:02:05 +0100 Subject: [PATCH 03/23] reformatting & removing unused imports --- .../ui/model/CommandFailedException.java | 4 ++-- .../org/cryptomator/ui/model/NioAdapter.java | 10 ++++------ .../cryptomator/ui/model/WebDavNioAdapter.java | 16 +++++++--------- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/CommandFailedException.java b/main/ui/src/main/java/org/cryptomator/ui/model/CommandFailedException.java index cfac40ab0..77d04f845 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/CommandFailedException.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/CommandFailedException.java @@ -10,8 +10,8 @@ public class CommandFailedException extends Exception { super(cause); } - public CommandFailedException(String message, Throwable cause){ - super(message,cause); + public CommandFailedException(String message, Throwable cause) { + super(message, cause); } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/NioAdapter.java b/main/ui/src/main/java/org/cryptomator/ui/model/NioAdapter.java index 9cda2c462..4452e082e 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/NioAdapter.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/NioAdapter.java @@ -2,9 +2,6 @@ package org.cryptomator.ui.model; import org.cryptomator.cryptofs.CryptoFileSystem; -import java.util.HashMap; - - public interface NioAdapter { void unlock(CryptoFileSystem fs); @@ -13,6 +10,10 @@ public interface NioAdapter { void unmount() throws CommandFailedException; + default void unmountForced() throws CommandFailedException { + throw new CommandFailedException("Operation not supported"); + } + void stop(); String getFilesystemRootUrl(); @@ -25,7 +26,4 @@ public interface NioAdapter { return false; } - default void unmountForced() throws CommandFailedException { - throw new CommandFailedException("Operation not supported"); - } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/WebDavNioAdapter.java b/main/ui/src/main/java/org/cryptomator/ui/model/WebDavNioAdapter.java index 7dc805ed7..97ab3de90 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/WebDavNioAdapter.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/WebDavNioAdapter.java @@ -12,7 +12,6 @@ import org.cryptomator.frontend.webdav.servlet.WebDavServletController; import javax.inject.Inject; import java.net.InetAddress; import java.net.UnknownHostException; -import java.util.function.Function; @VaultModule.PerVault public class WebDavNioAdapter implements NioAdapter { @@ -27,18 +26,18 @@ public class WebDavNioAdapter implements NioAdapter { private Mounter.Mount mount; @Inject - public WebDavNioAdapter(WebDavServer server, VaultSettings vaultSettings, Settings settings){ + public WebDavNioAdapter(WebDavServer server, VaultSettings vaultSettings, Settings settings) { this.server = server; this.vaultSettings = vaultSettings; this.settings = settings; } @Override - public void unlock(CryptoFileSystem fs){ - if(!server.isRunning()){ + public void unlock(CryptoFileSystem fs) { + if (!server.isRunning()) { server.start(); } - servlet = server.createWebDavServlet(fs.getPath("/"), vaultSettings.getId()+ "/" + vaultSettings.mountName().get()); + servlet = server.createWebDavServlet(fs.getPath("/"), vaultSettings.getId() + "/" + vaultSettings.mountName().get()); servlet.start(); } @@ -64,14 +63,13 @@ public class WebDavNioAdapter implements NioAdapter { public synchronized void unmount() throws CommandFailedException { try { mount.unmount(); - } - catch (Mounter.CommandFailedException e){ + } catch (Mounter.CommandFailedException e) { throw new CommandFailedException(e); } } @Override - public synchronized void unmountForced(){ + public synchronized void unmountForced() { mount.forced(); } @@ -90,7 +88,7 @@ public class WebDavNioAdapter implements NioAdapter { @Override public void stop() { - if(servlet != null){ + if (servlet != null) { servlet.stop(); } From 39d1d9c5618d421125b08f93aff83a3d27cbdb44 Mon Sep 17 00:00:00 2001 From: infeo Date: Sat, 3 Feb 2018 17:15:51 +0100 Subject: [PATCH 04/23] adapted vault class to nioAdapter Interface (and some other classes) --- .../ui/controllers/UnlockedController.java | 4 +- .../java/org/cryptomator/ui/model/Vault.java | 132 +++++++----------- .../org/cryptomator/ui/model/VaultModule.java | 17 +++ 3 files changed, 73 insertions(+), 80 deletions(-) diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockedController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockedController.java index fdafb99c4..d1f0e0417 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockedController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockedController.java @@ -244,8 +244,8 @@ public class UnlockedController implements ViewController { @FXML private void didClickCopyUrl(ActionEvent event) { ClipboardContent clipboardContent = new ClipboardContent(); - clipboardContent.putUrl(vault.get().getWebDavUrl()); - clipboardContent.putString(vault.get().getWebDavUrl()); + clipboardContent.putUrl(vault.get().getFilesystemRootUrl()); + clipboardContent.putString(vault.get().getFilesystemRootUrl()); Clipboard.getSystemClipboard().setContent(clipboardContent); } 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 2b57d14ab..428e09d8c 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 @@ -9,25 +9,18 @@ package org.cryptomator.ui.model; import java.io.IOException; -import java.net.InetAddress; -import java.net.UnknownHostException; import java.nio.file.FileAlreadyExistsException; -import java.nio.file.FileSystem; 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.Function; import java.util.function.Predicate; import javax.inject.Inject; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.SystemUtils; import org.cryptomator.common.LazyInitializer; -import org.cryptomator.common.Optionals; import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.VaultSettings; import org.cryptomator.cryptofs.CryptoFileSystem; @@ -35,24 +28,21 @@ 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.frontend.webdav.ServerLifecycleException; -import org.cryptomator.frontend.webdav.WebDavServer; -import org.cryptomator.frontend.webdav.mount.MountParams; -import org.cryptomator.frontend.webdav.mount.Mounter.CommandFailedException; -import org.cryptomator.frontend.webdav.mount.Mounter.Mount; -import org.cryptomator.frontend.webdav.mount.Mounter.UnmountOperation; -import org.cryptomator.frontend.webdav.servlet.WebDavServletController; import org.cryptomator.ui.model.VaultModule.PerVault; -import org.fxmisc.easybind.EasyBind; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import com.google.common.base.Strings; import javafx.application.Platform; import javafx.beans.Observable; import javafx.beans.binding.Binding; import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyObjectProperty; import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.property.StringProperty; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; +import org.fxmisc.easybind.EasyBind; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @PerVault public class Vault { @@ -64,22 +54,24 @@ public class Vault { private final Settings settings; private final VaultSettings vaultSettings; - private final WebDavServer server; private final AtomicReference cryptoFileSystem = new AtomicReference<>(); private final ObjectProperty state = new SimpleObjectProperty(State.LOCKED); - private WebDavServletController servlet; - private Mount mount; + private NioAdapter nioAdapter; public enum State { LOCKED, UNLOCKED, MOUNTING, MOUNTED, UNMOUNTING - }; + } @Inject - Vault(Settings settings, VaultSettings vaultSettings, WebDavServer server) { + Vault(Settings settings, VaultSettings vaultSettings, NioAdapter nioAdapter) { this.settings = settings; this.vaultSettings = vaultSettings; - this.server = server; + this.nioAdapter = nioAdapter; + + if (Strings.isNullOrEmpty(vaultSettings.mountPath().get())) { + vaultSettings.mountPath().set(settings.defaultMountDir().get() + "/" + vaultSettings.mountName().get()); + } } // ****************************************************************************** @@ -111,80 +103,48 @@ public class Vault { CryptoFileSystemProvider.changePassphrase(getPath(), MASTERKEY_FILENAME, oldPassphrase, newPassphrase); } - public synchronized void unlock(CharSequence passphrase) throws ServerLifecycleException, CryptoException, IOException { - FileSystem fs = getCryptoFileSystem(passphrase); - if (!server.isRunning()) { - server.start(); - } - servlet = server.createWebDavServlet(fs.getPath("/"), vaultSettings.getId() + "/" + vaultSettings.mountName().get()); - servlet.start(); + public synchronized void unlock(CharSequence passphrase) throws CryptoException, IOException { + CryptoFileSystem fs = getCryptoFileSystem(passphrase); + nioAdapter.unlock(fs); Platform.runLater(() -> { state.set(State.UNLOCKED); }); } public synchronized void mount() throws CommandFailedException { - if (servlet == null) { - throw new IllegalStateException("Mounting requires unlocked WebDAV servlet."); - } - - MountParams mountParams = MountParams.create() // - .withWindowsDriveLetter(vaultSettings.winDriveLetter().get()) // - .withPreferredGvfsScheme(settings.preferredGvfsScheme().get()) // - .withWebdavHostname(getLocalhostAliasOrNull()) // - .build(); - Platform.runLater(() -> { state.set(State.MOUNTING); }); - mount = servlet.mount(mountParams); // might block this thread for a while + nioAdapter.mount(); Platform.runLater(() -> { state.set(State.MOUNTED); }); } - private String getLocalhostAliasOrNull() { - try { - InetAddress alias = InetAddress.getByName(LOCALHOST_ALIAS); - if (alias.getHostAddress().equals("127.0.0.1")) { - return LOCALHOST_ALIAS; - } else { - return null; - } - } catch (UnknownHostException e) { - return null; - } + public synchronized void unmountForced() throws CommandFailedException { + unmount(true); } public synchronized void unmount() throws CommandFailedException { - unmount(Function.identity()); + unmount(false); } - public synchronized void unmountForced() throws CommandFailedException { - unmount(Optionals.unwrap(Mount::forced)); - } - - private synchronized void unmount(Function unmountOperationChooser) throws CommandFailedException { + private synchronized void unmount(boolean forced) throws CommandFailedException { Platform.runLater(() -> { state.set(State.UNMOUNTING); }); - if (mount != null) { - unmountOperationChooser.apply(mount).unmount(); - mount = null; + if (forced && nioAdapter.supportsForcedUnmount()) { + nioAdapter.unmountForced(); + } else { + nioAdapter.unmount(); } Platform.runLater(() -> { state.set(State.UNLOCKED); }); } - public boolean supportsForcedUnmount() { - return mount != null && mount.forced().isPresent(); - } - - public synchronized void lock() throws ServerLifecycleException, IOException { - if (servlet != null) { - servlet.stop(); - } + public synchronized void lock() throws IOException { + nioAdapter.stop(); CryptoFileSystem fs = cryptoFileSystem.getAndSet(null); if (fs != null) { fs.close(); @@ -201,7 +161,7 @@ public class Vault { try { unmount(); } catch (CommandFailedException e) { - if (supportsForcedUnmount()) { + if (nioAdapter.supportsForcedUnmount()) { try { unmountForced(); } catch (CommandFailedException e1) { @@ -218,10 +178,11 @@ public class Vault { } } + /** + * TODO: implement it again + */ public void reveal() throws CommandFailedException { - if (mount != null) { - mount.reveal(); - } + } // ****************************************************************************** @@ -243,17 +204,13 @@ public class Vault { } public Observable[] observables() { - return new Observable[] {state}; + return new Observable[]{state}; } public VaultSettings getVaultSettings() { return vaultSettings; } - public synchronized String getWebDavUrl() { - return servlet.getServletRootUri().toString(); - } - public Path getPath() { return vaultSettings.path().getValue(); } @@ -308,6 +265,18 @@ public class Vault { return vaultSettings.mountName().get(); } + public StringProperty getMountPathProperty() { + return vaultSettings.mountPath(); + } + + public void setMountPath(String mountPath) { + if (mountPath.isEmpty()) { + vaultSettings.mountPath().set(settings.defaultMountDir().get()); + } else { + vaultSettings.mountPath().set(mountPath); + } + } + public void setMountName(String mountName) throws IllegalArgumentException { if (StringUtils.isBlank(mountName)) { throw new IllegalArgumentException("mount name is empty"); @@ -332,6 +301,10 @@ public class Vault { } } + public String getFilesystemRootUrl() { + return nioAdapter.getFilesystemRootUrl(); + } + public String getId() { return vaultSettings.getId(); } @@ -355,4 +328,7 @@ public class Vault { } } + public boolean supportsForcedUnmount() { + return nioAdapter.supportsForcedUnmount(); + } } \ No newline at end of file diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/VaultModule.java b/main/ui/src/main/java/org/cryptomator/ui/model/VaultModule.java index c8a622233..1e076c2da 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/VaultModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/VaultModule.java @@ -12,10 +12,12 @@ import java.util.Objects; import javax.inject.Scope; +import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.VaultSettings; import dagger.Module; import dagger.Provides; +import sun.reflect.generics.reflectiveObjects.NotImplementedException; @Module public class VaultModule { @@ -36,6 +38,21 @@ public class VaultModule { @Documented @Retention(RetentionPolicy.RUNTIME) @interface PerVault { + } + @Provides + @PerVault + public NioAdapter provideNioAdpater(Settings settings, WebDavNioAdapter webDavNioAdapter) { + NioAdapterImpl impl = NioAdapterImpl.valueOf(settings.usedNioAdapterImpl().get()); + switch (impl) { + case WEBDAV: + return webDavNioAdapter; + case FUSE: + throw new NotImplementedException(); + default: + //this should not happen! + throw new IllegalStateException("Unsupported NioAdapter: " + settings.usedNioAdapterImpl().get()); + } + } } From d170e87c1b6c1931222cb1db8da7a3c8a61d5c9c Mon Sep 17 00:00:00 2001 From: infeo Date: Sat, 3 Feb 2018 17:26:59 +0100 Subject: [PATCH 05/23] integration of the fuseNioAdapter, including all the gui (controller & fxml) and settings wiring. english localization fitted and enriched for fuse --- .../cryptomator/common/settings/Settings.java | 14 ++ .../common/settings/SettingsJsonAdapter.java | 8 + .../common/settings/VaultSettings.java | 5 + .../settings/VaultSettingsJsonAdapter.java | 4 + main/pom.xml | 8 +- main/ui/pom.xml | 6 +- .../ui/controllers/SettingsController.java | 98 +++++++- .../ui/controllers/UnlockController.java | 54 +++++ .../cryptomator/ui/model/AutoUnlocker.java | 1 - .../cryptomator/ui/model/FuseNioAdapter.java | 225 ++++++++++++++++++ .../org/cryptomator/ui/model/VaultModule.java | 4 +- main/ui/src/main/resources/fxml/settings.fxml | 43 ++-- main/ui/src/main/resources/fxml/unlock.fxml | 10 +- .../ui/src/main/resources/localization/en.txt | 17 +- 14 files changed, 469 insertions(+), 28 deletions(-) create mode 100644 main/ui/src/main/java/org/cryptomator/ui/model/FuseNioAdapter.java diff --git a/main/commons/src/main/java/org/cryptomator/common/settings/Settings.java b/main/commons/src/main/java/org/cryptomator/common/settings/Settings.java index a2721f4d3..34492ce2d 100644 --- a/main/commons/src/main/java/org/cryptomator/common/settings/Settings.java +++ b/main/commons/src/main/java/org/cryptomator/common/settings/Settings.java @@ -30,6 +30,8 @@ public class Settings { public static final int DEFAULT_NUM_TRAY_NOTIFICATIONS = 3; public static final String DEFAULT_GVFS_SCHEME = "dav"; public static final boolean DEFAULT_DEBUG_MODE = false; + public static final String DEFAULT_DEFAULT_MOUNT_DIR = System.getProperty("user.home"); + public static final String DEFAULT_NIO_ADAPTER = "WEBDAV"; private final ObservableList directories = FXCollections.observableArrayList(VaultSettings::observables); private final BooleanProperty checkForUpdates = new SimpleBooleanProperty(DEFAULT_CHECK_FOR_UDPATES); @@ -37,6 +39,9 @@ public class Settings { private final IntegerProperty numTrayNotifications = new SimpleIntegerProperty(DEFAULT_NUM_TRAY_NOTIFICATIONS); private final StringProperty preferredGvfsScheme = new SimpleStringProperty(DEFAULT_GVFS_SCHEME); private final BooleanProperty debugMode = new SimpleBooleanProperty(DEFAULT_DEBUG_MODE); + private final StringProperty nioAdapterImpl = new SimpleStringProperty(DEFAULT_NIO_ADAPTER); + private final StringProperty defaultMountDir = new SimpleStringProperty(DEFAULT_DEFAULT_MOUNT_DIR); + private Consumer saveCmd; /** @@ -49,6 +54,8 @@ public class Settings { numTrayNotifications.addListener(this::somethingChanged); preferredGvfsScheme.addListener(this::somethingChanged); debugMode.addListener(this::somethingChanged); + nioAdapterImpl.addListener(this::somethingChanged); + defaultMountDir.addListener(this::somethingChanged); } void setSaveCmd(Consumer saveCmd) { @@ -91,4 +98,11 @@ public class Settings { return debugMode; } + public StringProperty usedNioAdapterImpl() { + return nioAdapterImpl; + } + + public StringProperty defaultMountDir() { + return defaultMountDir; + } } diff --git a/main/commons/src/main/java/org/cryptomator/common/settings/SettingsJsonAdapter.java b/main/commons/src/main/java/org/cryptomator/common/settings/SettingsJsonAdapter.java index e474fcf7c..0a2a13c3f 100644 --- a/main/commons/src/main/java/org/cryptomator/common/settings/SettingsJsonAdapter.java +++ b/main/commons/src/main/java/org/cryptomator/common/settings/SettingsJsonAdapter.java @@ -33,6 +33,8 @@ public class SettingsJsonAdapter extends TypeAdapter { out.name("numTrayNotifications").value(value.numTrayNotifications().get()); out.name("preferredGvfsScheme").value(value.preferredGvfsScheme().get()); out.name("debugMode").value(value.debugMode().get()); + out.name("nioAdapterImpl").value(value.usedNioAdapterImpl().get()); + out.name("defaultMountDir").value(value.defaultMountDir().get()); out.endObject(); } @@ -70,6 +72,12 @@ public class SettingsJsonAdapter extends TypeAdapter { case "debugMode": settings.debugMode().set(in.nextBoolean()); break; + case "nioAdapterImpl": + settings.usedNioAdapterImpl().set(in.nextString()); + break; + case "defaultMountDir": + settings.defaultMountDir().set(in.nextString()); + break; default: LOG.warn("Unsupported vault setting found in JSON: " + name); in.skipValue(); 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 8ee82146f..bcaae5587 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 @@ -36,6 +36,7 @@ public class VaultSettings { 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 StringProperty mountPath = new SimpleStringProperty(Settings.DEFAULT_DEFAULT_MOUNT_DIR); public VaultSettings(String id) { this.id = Objects.requireNonNull(id); @@ -123,6 +124,10 @@ public class VaultSettings { return revealAfterMount; } + public StringProperty mountPath() { + return mountPath; + } + /* Hashcode/Equals */ @Override 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 d1de6231e..e171ea178 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 @@ -34,6 +34,7 @@ class VaultSettingsJsonAdapter { String id = null; String path = null; String mountName = null; + String mountPath = null; String winDriveLetter = null; boolean unlockAfterStartup = VaultSettings.DEFAULT_UNLOCK_AFTER_STARTUP; boolean mountAfterUnlock = VaultSettings.DEFAULT_MOUNT_AFTER_UNLOCK; @@ -64,6 +65,8 @@ class VaultSettingsJsonAdapter { case "revealAfterMount": revealAfterMount = in.nextBoolean(); break; + case "mountPath": + mountPath = in.nextString(); default: LOG.warn("Unsupported vault setting found in JSON: " + name); in.skipValue(); @@ -78,6 +81,7 @@ class VaultSettingsJsonAdapter { vaultSettings.unlockAfterStartup().set(unlockAfterStartup); vaultSettings.mountAfterUnlock().set(mountAfterUnlock); vaultSettings.revealAfterMount().set(revealAfterMount); + vaultSettings.mountPath().set(mountPath); return vaultSettings; } diff --git a/main/pom.xml b/main/pom.xml index 6c939e273..e5738b56c 100644 --- a/main/pom.xml +++ b/main/pom.xml @@ -28,7 +28,8 @@ 1.4.5 1.0.3 1.0.2 - + 0.1.1-SNAPSHOT + 2.5 3.6 4.5.3 @@ -96,6 +97,11 @@ cryptofs ${cryptomator.cryptofs.version} + + org.cryptomator + fuse-nio-adapter + ${cryptomator.fuse.version} + org.cryptomator webdav-nio-adapter diff --git a/main/ui/pom.xml b/main/ui/pom.xml index c5a0980c9..7172387ab 100644 --- a/main/ui/pom.xml +++ b/main/ui/pom.xml @@ -30,7 +30,11 @@ org.cryptomator keychain - + + org.cryptomator + fuse-nio-adapter + + org.cryptomator 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 20ac7cac6..1e01db321 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,15 +8,22 @@ ******************************************************************************/ package org.cryptomator.ui.controllers; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Optional; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; +import javafx.beans.Observable; +import javafx.beans.value.ObservableValue; +import javafx.scene.layout.GridPane; import org.apache.commons.lang3.SystemUtils; import org.cryptomator.common.settings.Settings; import org.cryptomator.ui.l10n.Localization; +import org.cryptomator.ui.model.NioAdapterImpl; import com.google.common.base.CharMatcher; import com.google.common.base.Strings; @@ -52,6 +59,15 @@ public class SettingsController implements ViewController { @FXML private CheckBox checkForUpdatesCheckbox; + @FXML + private GridPane webdavNioAdapter; + + @FXML + private GridPane fuseNioAdapter; + + @FXML + private Label portFieldLabel; + @FXML private TextField portField; @@ -64,9 +80,24 @@ public class SettingsController implements ViewController { @FXML private Label prefGvfsSchemeLabel; + @FXML + private Label defaultMountDirLabel; + + @FXML + private TextField defaultMountDir; + + @FXML + private Button changeDefaultMountDirButton; + @FXML private ChoiceBox prefGvfsScheme; + @FXML + private Label nioAdapterLabel; + + @FXML + private ChoiceBox nioAdapter; + @FXML private CheckBox debugModeCheckbox; @@ -75,25 +106,69 @@ public class SettingsController implements ViewController { @Override public void initialize() { + versionLabel.setText(String.format(localization.getString("settings.version.label"), applicationVersion.orElse("SNAPSHOT"))); checkForUpdatesCheckbox.setDisable(areUpdatesManagedExternally()); checkForUpdatesCheckbox.setSelected(settings.checkForUpdates().get() && !areUpdatesManagedExternally()); + + //NIOADAPTER + nioAdapter.getItems().addAll(getSupportedAdapters()); + nioAdapter.setValue(settings.usedNioAdapterImpl().get()); + nioAdapter.setVisible(true); + nioAdapter.getSelectionModel().selectedItemProperty().addListener( (ObservableValue observable, String oldVal, String newVal) -> changeNioView(newVal) ); + + + //WEBDAV + webdavNioAdapter.managedProperty().bind(webdavNioAdapter.visibleProperty()); + prefGvfsScheme.managedProperty().bind(webdavNioAdapter.visibleProperty()); + prefGvfsSchemeLabel.managedProperty().bind(webdavNioAdapter.visibleProperty()); + portFieldLabel.managedProperty().bind(webdavNioAdapter.visibleProperty()); + changePortButton.managedProperty().bind(webdavNioAdapter.visibleProperty()); + portField.managedProperty().bind(webdavNioAdapter.visibleProperty()); portField.setText(String.valueOf(settings.port().intValue())); portField.addEventFilter(KeyEvent.KEY_TYPED, this::filterNumericKeyEvents); changePortButton.visibleProperty().bind(settings.port().asString().isNotEqualTo(portField.textProperty())); changePortButton.disableProperty().bind(Bindings.createBooleanBinding(this::isPortValid, portField.textProperty()).not()); - versionLabel.setText(String.format(localization.getString("settings.version.label"), applicationVersion.orElse("SNAPSHOT"))); - prefGvfsSchemeLabel.setVisible(SystemUtils.IS_OS_LINUX); - prefGvfsScheme.setVisible(SystemUtils.IS_OS_LINUX); prefGvfsScheme.getItems().add("dav"); prefGvfsScheme.getItems().add("webdav"); prefGvfsScheme.setValue(settings.preferredGvfsScheme().get()); + prefGvfsSchemeLabel.setVisible(SystemUtils.IS_OS_LINUX); + prefGvfsScheme.setVisible(SystemUtils.IS_OS_LINUX); + + //FUSE + fuseNioAdapter.managedProperty().bind(fuseNioAdapter.visibleProperty()); + defaultMountDirLabel.managedProperty().bind(fuseNioAdapter.visibleProperty()); + defaultMountDir.managedProperty().bind(fuseNioAdapter.visibleProperty()); + defaultMountDirLabel.setVisible(SystemUtils.IS_OS_LINUX); + defaultMountDir.setVisible(SystemUtils.IS_OS_LINUX); + defaultMountDir.setText(String.valueOf(settings.defaultMountDir().get())); + changeDefaultMountDirButton.setVisible(false); + changeDefaultMountDirButton.visibleProperty().bind( + Bindings.createBooleanBinding( + () -> fuseNioAdapter.visibleProperty().get() && settings.defaultMountDir().isNotEqualTo(Strings.nullToEmpty(defaultMountDir.getText())).get() , + fuseNioAdapter.visibleProperty(), + settings.defaultMountDir().isNotEqualTo(defaultMountDir.textProperty()) + ) + ); + changeDefaultMountDirButton.disableProperty().bind(Bindings.createBooleanBinding(this::isDirValid, defaultMountDir.textProperty()).not()); + debugModeCheckbox.setSelected(settings.debugMode().get()); settings.checkForUpdates().bind(checkForUpdatesCheckbox.selectedProperty()); settings.preferredGvfsScheme().bind(prefGvfsScheme.valueProperty()); + settings.usedNioAdapterImpl().bind(nioAdapter.valueProperty()); settings.debugMode().bind(debugModeCheckbox.selectedProperty()); } + //TODO: how to implement this? + private String [] getSupportedAdapters() { + return new String[]{NioAdapterImpl.FUSE.name(), NioAdapterImpl.WEBDAV.name()}; + } + + private void changeNioView(String newVal) { + fuseNioAdapter.setVisible(newVal.equalsIgnoreCase("FUSE")); + webdavNioAdapter.setVisible(newVal.equalsIgnoreCase("WEBDAV")); + } + @Override public Parent getRoot() { return root; @@ -110,6 +185,23 @@ public class SettingsController implements ViewController { } } + @FXML + private void changeDefaultMountDir(ActionEvent event){ + assert isDirValid() : "Error. Not a valid Directory. Does and exist and do you have the needed Rights?"; + settings.defaultMountDir().set(defaultMountDir.getText()); + } + + private boolean isDirValid(){ + if(SystemUtils.IS_OS_WINDOWS){ + //this should never ever happen! + return false; + } + else{ + Path path = Paths.get(defaultMountDir.getText()); + return Files.isDirectory(path) && Files.isReadable(path) && Files.isWritable(path) && Files.isExecutable(path); + } + } + private boolean isPortValid() { try { int port = Integer.parseInt(portField.getText()); 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 6d30a7e2f..c1b2aac68 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,7 +8,9 @@ ******************************************************************************/ package org.cryptomator.ui.controllers; +import java.io.File; import java.io.IOException; +import java.nio.file.*; import java.util.Arrays; import java.util.Comparator; import java.util.Objects; @@ -16,6 +18,8 @@ import java.util.Optional; import javax.inject.Inject; +import javafx.beans.Observable; +import javafx.beans.binding.Bindings; import org.apache.commons.lang3.CharUtils; import org.apache.commons.lang3.SystemUtils; import org.cryptomator.common.settings.VaultSettings; @@ -112,6 +116,15 @@ public class UnlockController implements ViewController { @FXML private ChoiceBox winDriveLetter; + @FXML + private Label mountPathLabel; + + @FXML + private TextField mountPath; + + @FXML + private Button changeMountPathButton; + @FXML private ProgressIndicator progressIndicator; @@ -142,12 +155,26 @@ public class UnlockController implements ViewController { unlockAfterStartup.disableProperty().bind(savePassword.disabledProperty().or(savePassword.selectedProperty().not())); if (SystemUtils.IS_OS_WINDOWS) { winDriveLetter.setConverter(new WinDriveLetterLabelConverter()); + mountPathLabel.setVisible(false); + mountPathLabel.setManaged(false); + mountPath.setVisible(false); + mountPath.setManaged(false); + changeMountPathButton.setVisible(false); + changeMountPathButton.setManaged(false); } else { winDriveLetterLabel.setVisible(false); winDriveLetterLabel.setManaged(false); winDriveLetter.setVisible(false); winDriveLetter.setManaged(false); } + changeMountPathButton.disableProperty().bind(Bindings.createBooleanBinding(this::isDirVaild, mountPath.textProperty()).not()); + changeMountPathButton.visibleProperty().bind( + Bindings.createBooleanBinding( + ()-> mountPathLabel.isVisible() && mountPath.textProperty().isEmpty().not().get(), + mountPathLabel.visibleProperty(), + mountPath.textProperty().isEmpty().not() + ) + ); } @Override @@ -208,6 +235,7 @@ public class UnlockController implements ViewController { vaultSubs = vaultSubs.and(EasyBind.subscribe(unlockAfterStartup.selectedProperty(), settings.unlockAfterStartup()::set)); vaultSubs = vaultSubs.and(EasyBind.subscribe(mountAfterUnlock.selectedProperty(), settings.mountAfterUnlock()::set)); vaultSubs = vaultSubs.and(EasyBind.subscribe(revealAfterMount.selectedProperty(), settings.revealAfterMount()::set)); + } // **************************************** @@ -233,6 +261,32 @@ public class UnlockController implements ViewController { } } + @FXML + private void didClickchangeMountPathButton(ActionEvent event){ + assert isDirVaild(); + vault.setMountPath(mountPath.getText()); + } + + private boolean isDirVaild(){ + try{ + if(!mountPath.textProperty().isEmpty().get()){ + Path p = Paths.get(mountPath.textProperty().get()); + return Files.isDirectory(p) && Files.isReadable(p) && Files.isWritable(p) && Files.isExecutable(p); + } + else{ + //default path will be taken + return true; + } + + } + catch (InvalidPathException e){ + LOG.info("Invalid path"); + return false; + } + } + + + private void filterAlphanumericKeyEvents(KeyEvent t) { if (!Strings.isNullOrEmpty(t.getCharacter()) && !ALPHA_NUMERIC_MATCHER.matchesAllOf(t.getCharacter())) { t.consume(); diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/AutoUnlocker.java b/main/ui/src/main/java/org/cryptomator/ui/model/AutoUnlocker.java index 3d5ac4201..ba982c872 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/AutoUnlocker.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/AutoUnlocker.java @@ -18,7 +18,6 @@ import javax.inject.Inject; import javax.inject.Singleton; import org.cryptomator.cryptolib.api.CryptoException; -import org.cryptomator.frontend.webdav.mount.Mounter.CommandFailedException; import org.cryptomator.keychain.KeychainAccess; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/FuseNioAdapter.java b/main/ui/src/main/java/org/cryptomator/ui/model/FuseNioAdapter.java new file mode 100644 index 000000000..6abd25955 --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/model/FuseNioAdapter.java @@ -0,0 +1,225 @@ +package org.cryptomator.ui.model; + +import org.cryptomator.common.settings.Settings; +import org.cryptomator.common.settings.VaultSettings; +import org.cryptomator.cryptofs.CryptoFileSystem; +import org.cryptomator.frontend.fuse.AdapterFactory; + +import org.apache.commons.lang3.SystemUtils; +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.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Scanner; +import java.util.concurrent.TimeUnit; + +@VaultModule.PerVault +public class FuseNioAdapter implements NioAdapter { + + private static final Logger LOG = LoggerFactory.getLogger(FuseNioAdapter.class); + private static final String AUTOASSIGN_DRRIVE_LETTER = "*"; + + private enum OS { + WINDOWS, + LINUX, + MAC; + + public static OS getCurrentOS() { + if (SystemUtils.IS_OS_WINDOWS) { + return WINDOWS; + } else if (SystemUtils.IS_OS_MAC) { + return MAC; + } else { + return LINUX; + } + } + + } + + + private final VaultSettings vaultSettings; + private final Settings settings; + private final WindowsDriveLetters windowsDriveLetters; + private final OS os = OS.getCurrentOS(); + private org.cryptomator.frontend.fuse.FuseNioAdapter ffs; + private String mountNameAndId; + private String mountURL; + private CryptoFileSystem cfs; + + @Inject + public FuseNioAdapter(VaultSettings vaultSettings, Settings settings, WindowsDriveLetters windowsDriveLetters) { + this.vaultSettings = vaultSettings; + this.settings = settings; + this.windowsDriveLetters = windowsDriveLetters; + } + + @Override + public void unlock(CryptoFileSystem fs) { + this.cfs = fs; + ffs = AdapterFactory.createReadWriteAdapter(fs.getPath("/")); + } + + /** + * TODO: should createTempDirectory() be used instead of createDirectory()? + * + * @throws CommandFailedException + */ + @Override + public void mount() throws CommandFailedException { + ArrayList mountOptions = new ArrayList<>(8); + mountOptions.add(("-oatomic_o_trunc")); + Path path; + try { + switch (os) { + case MAC: + path = Paths.get(vaultSettings.mountPath().get() + vaultSettings.mountName().get()); + createVaultDirIfNotExist(path); + mountOptions.add("-ouid=" + getUIdOrGID("uid")); + mountOptions.add("-ogid=" + getUIdOrGID("gid")); + mountOptions.add("-ovolname=" + vaultSettings.mountName().get()); + mountOptions.add("-oauto_xattr"); + break; + case WINDOWS: + if (vaultSettings.winDriveLetter().get().equals(AUTOASSIGN_DRRIVE_LETTER)) { + if (!windowsDriveLetters.getAvailableDriveLetters().isEmpty()) { + path = Paths.get(windowsDriveLetters.getAvailableDriveLetters().iterator().next() + ":\\"); + } else { + throw new CommandFailedException("No free drive letter to mount."); + } + } else { + path = Paths.get(vaultSettings.winDriveLetter().get() + ":\\"); + } + mountOptions.add("-ouid=-1"); + mountOptions.add("-ogid=-1"); + mountOptions.add("-ovolname=" + vaultSettings.mountName().get()); + mountOptions.add("-oFileInfoTimeout=-1"); + break; + case LINUX: + path = Paths.get(vaultSettings.mountPath().get() + vaultSettings.mountName().get()); + createVaultDirIfNotExist(path); + mountOptions.add("-ouid=" + getUIdOrGID("uid")); + mountOptions.add("-ogid=" + getUIdOrGID("gid")); + mountOptions.add("-oauto_unmount"); + mountOptions.add("-ofsname=CryptoFs"); + break; + default: + throw new IllegalStateException("Not Supported OS."); + } + ffs.mount(path, false, false, mountOptions.toArray(new String[mountOptions.size()])); + mountURL = path.toAbsolutePath().toUri().toURL().toString(); + } catch (Exception e) { + throw new CommandFailedException("Unable to mount Filesystem", e); + } + } + + private void createVaultDirIfNotExist(Path p) throws IOException { + try { + if (Files.exists(p)) { + if (Files.isDirectory(p)) { + if (Files.newDirectoryStream(p).iterator().hasNext()) { + return; + } else { + throw new DirectoryNotEmptyException("Directory not empty."); + } + } + } else { + Files.createDirectory(p); + } + } catch (IOException e) { + throw e; + } + } + + private String getUIdOrGID(String idtype) throws IOException { + String id; + String parameter; + switch (idtype) { + case "uid": + parameter = "-u"; + break; + case "gid": + parameter = "-g"; + break; + default: + throw new IllegalArgumentException("Unkown ID type"); + } + Process getId = new ProcessBuilder("sh", "-c", "id " + parameter).start(); + Scanner s = new Scanner(getId.getInputStream()).useDelimiter("\\A"); + try { + getId.waitFor(1000, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + id = s.nextLine(); + return id; + } + + @Override + public synchronized void unmount() throws CommandFailedException { + if (!(cfs.getStats().pollBytesRead() > 0 || cfs.getStats().pollBytesWritten() > 0)) { + unmountForced(); + } else { + throw new CommandFailedException("Pending read or write operations."); + } + } + + @Override + public synchronized void unmountForced() throws CommandFailedException { + ffs.umount(); + } + + @Override + public void stop() { + switch (os) { + case WINDOWS: + return; + case MAC: + case LINUX: + try { + Files.deleteIfExists(Paths.get(vaultSettings.mountPath().get() + vaultSettings.mountName().get())); + } catch (IOException e) { + LOG.warn("Could not delete mount directory of vault " + vaultSettings.mountName()); + e.printStackTrace(); + } + return; + default: + return; + } + + } + + @Override + public String getFilesystemRootUrl() { + return mountURL; + } + + /** + * TODO: what should i check here? + */ + @Override + public boolean isSupported() { + switch (os) { + case LINUX: + return true; + case WINDOWS: + break; + case MAC: + break; + default: + return false; + } + return true; + } + + @Override + public boolean supportsForcedUnmount() { + return true; + } + +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/VaultModule.java b/main/ui/src/main/java/org/cryptomator/ui/model/VaultModule.java index 1e076c2da..d456d4f1f 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/VaultModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/VaultModule.java @@ -43,13 +43,13 @@ public class VaultModule { @Provides @PerVault - public NioAdapter provideNioAdpater(Settings settings, WebDavNioAdapter webDavNioAdapter) { + public NioAdapter provideNioAdpater(Settings settings, WebDavNioAdapter webDavNioAdapter, FuseNioAdapter fuseNioAdapter) { NioAdapterImpl impl = NioAdapterImpl.valueOf(settings.usedNioAdapterImpl().get()); switch (impl) { case WEBDAV: return webDavNioAdapter; case FUSE: - throw new NotImplementedException(); + return fuseNioAdapter; default: //this should not happen! throw new IllegalStateException("Unsupported NioAdapter: " + settings.usedNioAdapterImpl().get()); diff --git a/main/ui/src/main/resources/fxml/settings.fxml b/main/ui/src/main/resources/fxml/settings.fxml index d3f9c7c12..f778ba090 100644 --- a/main/ui/src/main/resources/fxml/settings.fxml +++ b/main/ui/src/main/resources/fxml/settings.fxml @@ -35,22 +35,37 @@