mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-08-18 13:16:02 +00:00
Merge branch 'feature/fuse-integration' into develop
This commit is contained in:
@@ -21,6 +21,7 @@ addons:
|
||||
build_command: "mvn -fmain/pom.xml clean test -DskipTests"
|
||||
branch_pattern: release.*
|
||||
install:
|
||||
- curl -o $HOME/.m2/settings.xml https://gist.githubusercontent.com/cryptobot/cf5fbd909c4782aaeeeb7c7f4a1a43da/raw/082261a990b96dcb3e9a3ddb96fc4c2e493f62c0/settings.xml
|
||||
- mvn -fmain/pom.xml clean install -DskipTests dependency:go-offline -Pcoverage,release # "clean install" needed until we can exclude artifacts currently in the reactor, see https://maven.apache.org/plugins/maven-dependency-plugin/go-offline-mojo.html#excludeReactor and https://issues.apache.org/jira/browse/MDEP-568
|
||||
script:
|
||||
- mvn --update-snapshots -fmain/pom.xml clean test jacoco:report verify -Pcoverage
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package org.cryptomator.common.settings;
|
||||
|
||||
public enum NioAdapterImpl {
|
||||
|
||||
WEBDAV,
|
||||
FUSE
|
||||
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
* Copyright (c) 2014, 2017 Sebastian Stenzel
|
||||
* All rights reserved.
|
||||
* This program and the accompanying materials are made available under the terms of the accompanying LICENSE file.
|
||||
*
|
||||
*
|
||||
* Contributors:
|
||||
* Sebastian Stenzel - initial API and implementation
|
||||
******************************************************************************/
|
||||
@@ -30,6 +30,7 @@ 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_NIO_ADAPTER = NioAdapterImpl.WEBDAV.name();
|
||||
|
||||
private final ObservableList<VaultSettings> directories = FXCollections.observableArrayList(VaultSettings::observables);
|
||||
private final BooleanProperty checkForUpdates = new SimpleBooleanProperty(DEFAULT_CHECK_FOR_UDPATES);
|
||||
@@ -37,6 +38,8 @@ 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 Consumer<Settings> saveCmd;
|
||||
|
||||
/**
|
||||
@@ -49,6 +52,7 @@ public class Settings {
|
||||
numTrayNotifications.addListener(this::somethingChanged);
|
||||
preferredGvfsScheme.addListener(this::somethingChanged);
|
||||
debugMode.addListener(this::somethingChanged);
|
||||
nioAdapterImpl.addListener(this::somethingChanged);
|
||||
}
|
||||
|
||||
void setSaveCmd(Consumer<Settings> saveCmd) {
|
||||
@@ -91,4 +95,8 @@ public class Settings {
|
||||
return debugMode;
|
||||
}
|
||||
|
||||
public StringProperty usedNioAdapterImpl() {
|
||||
return nioAdapterImpl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+25
-21
@@ -33,6 +33,7 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
|
||||
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.endObject();
|
||||
}
|
||||
|
||||
@@ -52,27 +53,30 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
|
||||
while (in.hasNext()) {
|
||||
String name = in.nextName();
|
||||
switch (name) {
|
||||
case "directories":
|
||||
settings.getDirectories().addAll(readVaultSettingsArray(in));
|
||||
break;
|
||||
case "checkForUpdatesEnabled":
|
||||
settings.checkForUpdates().set(in.nextBoolean());
|
||||
break;
|
||||
case "port":
|
||||
settings.port().set(in.nextInt());
|
||||
break;
|
||||
case "numTrayNotifications":
|
||||
settings.numTrayNotifications().set(in.nextInt());
|
||||
break;
|
||||
case "preferredGvfsScheme":
|
||||
settings.preferredGvfsScheme().set(in.nextString());
|
||||
break;
|
||||
case "debugMode":
|
||||
settings.debugMode().set(in.nextBoolean());
|
||||
break;
|
||||
default:
|
||||
LOG.warn("Unsupported vault setting found in JSON: " + name);
|
||||
in.skipValue();
|
||||
case "directories":
|
||||
settings.getDirectories().addAll(readVaultSettingsArray(in));
|
||||
break;
|
||||
case "checkForUpdatesEnabled":
|
||||
settings.checkForUpdates().set(in.nextBoolean());
|
||||
break;
|
||||
case "port":
|
||||
settings.port().set(in.nextInt());
|
||||
break;
|
||||
case "numTrayNotifications":
|
||||
settings.numTrayNotifications().set(in.nextInt());
|
||||
break;
|
||||
case "preferredGvfsScheme":
|
||||
settings.preferredGvfsScheme().set(in.nextString());
|
||||
break;
|
||||
case "debugMode":
|
||||
settings.debugMode().set(in.nextBoolean());
|
||||
break;
|
||||
case "nioAdapterImpl":
|
||||
settings.usedNioAdapterImpl().set(in.nextString());
|
||||
break;
|
||||
default:
|
||||
LOG.warn("Unsupported vault setting found in JSON: " + name);
|
||||
in.skipValue();
|
||||
}
|
||||
}
|
||||
in.endObject();
|
||||
|
||||
@@ -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 individualMountPath = new SimpleStringProperty();
|
||||
|
||||
public VaultSettings(String id) {
|
||||
this.id = Objects.requireNonNull(id);
|
||||
@@ -44,7 +45,7 @@ public class VaultSettings {
|
||||
}
|
||||
|
||||
Observable[] observables() {
|
||||
return new Observable[] {path, mountName, winDriveLetter, unlockAfterStartup, mountAfterUnlock, revealAfterMount};
|
||||
return new Observable[]{path, mountName, winDriveLetter, unlockAfterStartup, mountAfterUnlock, revealAfterMount, individualMountPath};
|
||||
}
|
||||
|
||||
private void deriveMountNameFromPath(Path path) {
|
||||
@@ -123,6 +124,10 @@ public class VaultSettings {
|
||||
return revealAfterMount;
|
||||
}
|
||||
|
||||
public StringProperty individualMountPath() {
|
||||
return individualMountPath;
|
||||
}
|
||||
|
||||
/* Hashcode/Equals */
|
||||
|
||||
@Override
|
||||
|
||||
+32
-24
@@ -27,6 +27,9 @@ class VaultSettingsJsonAdapter {
|
||||
out.name("unlockAfterStartup").value(value.unlockAfterStartup().get());
|
||||
out.name("mountAfterUnlock").value(value.mountAfterUnlock().get());
|
||||
out.name("revealAfterMount").value(value.revealAfterMount().get());
|
||||
if(value.individualMountPath().isNotEmpty().get()){
|
||||
out.name("individualMountPath").value(value.individualMountPath().get());
|
||||
}
|
||||
out.endObject();
|
||||
}
|
||||
|
||||
@@ -34,6 +37,7 @@ class VaultSettingsJsonAdapter {
|
||||
String id = null;
|
||||
String path = null;
|
||||
String mountName = null;
|
||||
String individualMountPath = null;
|
||||
String winDriveLetter = null;
|
||||
boolean unlockAfterStartup = VaultSettings.DEFAULT_UNLOCK_AFTER_STARTUP;
|
||||
boolean mountAfterUnlock = VaultSettings.DEFAULT_MOUNT_AFTER_UNLOCK;
|
||||
@@ -43,30 +47,33 @@ class VaultSettingsJsonAdapter {
|
||||
while (in.hasNext()) {
|
||||
String name = in.nextName();
|
||||
switch (name) {
|
||||
case "id":
|
||||
id = in.nextString();
|
||||
break;
|
||||
case "path":
|
||||
path = in.nextString();
|
||||
break;
|
||||
case "mountName":
|
||||
mountName = in.nextString();
|
||||
break;
|
||||
case "winDriveLetter":
|
||||
winDriveLetter = in.nextString();
|
||||
break;
|
||||
case "unlockAfterStartup":
|
||||
unlockAfterStartup = in.nextBoolean();
|
||||
break;
|
||||
case "mountAfterUnlock":
|
||||
mountAfterUnlock = in.nextBoolean();
|
||||
break;
|
||||
case "revealAfterMount":
|
||||
revealAfterMount = in.nextBoolean();
|
||||
break;
|
||||
default:
|
||||
LOG.warn("Unsupported vault setting found in JSON: " + name);
|
||||
in.skipValue();
|
||||
case "id":
|
||||
id = in.nextString();
|
||||
break;
|
||||
case "path":
|
||||
path = in.nextString();
|
||||
break;
|
||||
case "mountName":
|
||||
mountName = in.nextString();
|
||||
break;
|
||||
case "winDriveLetter":
|
||||
winDriveLetter = in.nextString();
|
||||
break;
|
||||
case "unlockAfterStartup":
|
||||
unlockAfterStartup = in.nextBoolean();
|
||||
break;
|
||||
case "mountAfterUnlock":
|
||||
mountAfterUnlock = in.nextBoolean();
|
||||
break;
|
||||
case "revealAfterMount":
|
||||
revealAfterMount = in.nextBoolean();
|
||||
break;
|
||||
case "individualMountPath":
|
||||
individualMountPath = in.nextString();
|
||||
break;
|
||||
default:
|
||||
LOG.warn("Unsupported vault setting found in JSON: " + name);
|
||||
in.skipValue();
|
||||
}
|
||||
}
|
||||
in.endObject();
|
||||
@@ -78,6 +85,7 @@ class VaultSettingsJsonAdapter {
|
||||
vaultSettings.unlockAfterStartup().set(unlockAfterStartup);
|
||||
vaultSettings.mountAfterUnlock().set(mountAfterUnlock);
|
||||
vaultSettings.revealAfterMount().set(revealAfterMount);
|
||||
vaultSettings.individualMountPath().set(individualMountPath);
|
||||
return vaultSettings;
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -22,7 +22,9 @@ public class SettingsJsonAdapterTest {
|
||||
+ "\"checkForUpdatesEnabled\": true,"//
|
||||
+ "\"port\": 8080,"//
|
||||
+ "\"useIpv6\": true,"//
|
||||
+ "\"numTrayNotifications\": 42}";
|
||||
+ "\"numTrayNotifications\": 42,"//
|
||||
+ "\"nioAdapterImpl\": \"webdav\","//
|
||||
+ "\"defaultMountDir\": \"/home/test/crypto\"}";
|
||||
|
||||
Settings settings = adapter.fromJson(json);
|
||||
|
||||
@@ -32,6 +34,7 @@ public class SettingsJsonAdapterTest {
|
||||
// Assert.assertTrue(settings.useIpv6().get()); temporarily ignored
|
||||
Assert.assertEquals(42, settings.numTrayNotifications().get());
|
||||
Assert.assertEquals("dav", settings.preferredGvfsScheme().get());
|
||||
Assert.assertEquals("webdav", settings.usedNioAdapterImpl().get());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-1
@@ -20,7 +20,7 @@ public class VaultSettingsJsonAdapterTest {
|
||||
|
||||
@Test
|
||||
public void testDeserialize() throws IOException {
|
||||
String json = "{\"id\": \"foo\", \"path\": \"/foo/bar\", \"mountName\": \"test\", \"winDriveLetter\": \"X\", \"shouldBeIgnored\": true}";
|
||||
String json = "{\"id\": \"foo\", \"path\": \"/foo/bar\", \"mountName\": \"test\", \"winDriveLetter\": \"X\", \"shouldBeIgnored\": true, \"individualMountPath\": \"/home/test/crypto\"}";
|
||||
JsonReader jsonReader = new JsonReader(new StringReader(json));
|
||||
|
||||
VaultSettings vaultSettings = adapter.read(jsonReader);
|
||||
@@ -28,6 +28,7 @@ public class VaultSettingsJsonAdapterTest {
|
||||
Assert.assertEquals(Paths.get("/foo/bar"), vaultSettings.path().get());
|
||||
Assert.assertEquals("test", vaultSettings.mountName().get());
|
||||
Assert.assertEquals("X", vaultSettings.winDriveLetter().get());
|
||||
Assert.assertEquals("/home/test/crypto", vaultSettings.individualMountPath().get());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+7
-1
@@ -28,7 +28,8 @@
|
||||
<cryptomator.cryptofs.version>1.5.0-SNAPSHOT</cryptomator.cryptofs.version>
|
||||
<cryptomator.webdav.version>1.0.4</cryptomator.webdav.version>
|
||||
<cryptomator.jni.version>2.0.0</cryptomator.jni.version>
|
||||
|
||||
<cryptomator.fuse.version>0.1.1</cryptomator.fuse.version>
|
||||
|
||||
<commons-io.version>2.5</commons-io.version>
|
||||
<commons-lang3.version>3.6</commons-lang3.version>
|
||||
|
||||
@@ -95,6 +96,11 @@
|
||||
<artifactId>cryptofs</artifactId>
|
||||
<version>${cryptomator.cryptofs.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>fuse-nio-adapter</artifactId>
|
||||
<version>${cryptomator.fuse.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>webdav-nio-adapter</artifactId>
|
||||
|
||||
+5
-1
@@ -30,7 +30,11 @@
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>keychain</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>fuse-nio-adapter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- CryptoLib -->
|
||||
<dependency>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Copyright (c) 2014, 2017 Sebastian Stenzel
|
||||
* All rights reserved.
|
||||
* This program and the accompanying materials are made available under the terms of the accompanying LICENSE file.
|
||||
*
|
||||
*
|
||||
* Contributors:
|
||||
* Sebastian Stenzel - initial API and implementation
|
||||
******************************************************************************/
|
||||
@@ -14,9 +14,12 @@ import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
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.common.settings.NioAdapterImpl;
|
||||
|
||||
import com.google.common.base.CharMatcher;
|
||||
import com.google.common.base.Strings;
|
||||
@@ -52,6 +55,15 @@ public class SettingsController implements ViewController {
|
||||
@FXML
|
||||
private CheckBox checkForUpdatesCheckbox;
|
||||
|
||||
@FXML
|
||||
private GridPane webdavVolume;
|
||||
|
||||
@FXML
|
||||
private GridPane fuseVolume;
|
||||
|
||||
@FXML
|
||||
private Label portFieldLabel;
|
||||
|
||||
@FXML
|
||||
private TextField portField;
|
||||
|
||||
@@ -67,6 +79,12 @@ public class SettingsController implements ViewController {
|
||||
@FXML
|
||||
private ChoiceBox<String> prefGvfsScheme;
|
||||
|
||||
@FXML
|
||||
private Label volumeLabel;
|
||||
|
||||
@FXML
|
||||
private ChoiceBox<String> volume;
|
||||
|
||||
@FXML
|
||||
private CheckBox debugModeCheckbox;
|
||||
|
||||
@@ -75,25 +93,57 @@ 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
|
||||
volume.getItems().addAll(getSupportedAdapters());
|
||||
volume.setValue(settings.usedNioAdapterImpl().get());
|
||||
volume.setVisible(true);
|
||||
volume.getSelectionModel().selectedItemProperty().addListener((ObservableValue<? extends String> observable, String oldVal, String newVal) -> changeNioView(newVal));
|
||||
|
||||
|
||||
//WEBDAV
|
||||
webdavVolume.setVisible(settings.usedNioAdapterImpl().getValue().equals(NioAdapterImpl.WEBDAV.name()));
|
||||
webdavVolume.managedProperty().bind(webdavVolume.visibleProperty());
|
||||
prefGvfsScheme.managedProperty().bind(webdavVolume.visibleProperty());
|
||||
prefGvfsSchemeLabel.managedProperty().bind(webdavVolume.visibleProperty());
|
||||
portFieldLabel.managedProperty().bind(webdavVolume.visibleProperty());
|
||||
changePortButton.managedProperty().bind(webdavVolume.visibleProperty());
|
||||
portField.managedProperty().bind(webdavVolume.visibleProperty());
|
||||
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
|
||||
fuseVolume.setVisible(settings.usedNioAdapterImpl().getValue().equals(NioAdapterImpl.FUSE.name()));
|
||||
fuseVolume.managedProperty().bind(fuseVolume.visibleProperty());
|
||||
|
||||
debugModeCheckbox.setSelected(settings.debugMode().get());
|
||||
|
||||
settings.checkForUpdates().bind(checkForUpdatesCheckbox.selectedProperty());
|
||||
settings.preferredGvfsScheme().bind(prefGvfsScheme.valueProperty());
|
||||
settings.usedNioAdapterImpl().bind(volume.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) {
|
||||
fuseVolume.setVisible(newVal.equalsIgnoreCase(NioAdapterImpl.FUSE.name()));
|
||||
webdavVolume.setVisible(newVal.equalsIgnoreCase(NioAdapterImpl.WEBDAV.name()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parent getRoot() {
|
||||
return root;
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
* Copyright (c) 2014, 2017 Sebastian Stenzel
|
||||
* All rights reserved.
|
||||
* This program and the accompanying materials are made available under the terms of the accompanying LICENSE file.
|
||||
*
|
||||
*
|
||||
* Contributors:
|
||||
* Sebastian Stenzel - initial API and implementation
|
||||
******************************************************************************/
|
||||
package org.cryptomator.ui.controllers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Objects;
|
||||
@@ -16,8 +17,12 @@ import java.util.Optional;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.scene.layout.HBox;
|
||||
import org.apache.commons.lang3.CharUtils;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.settings.NioAdapterImpl;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.common.settings.VaultSettings;
|
||||
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
||||
import org.cryptomator.cryptolib.api.UnsupportedVaultFormatException;
|
||||
@@ -72,17 +77,19 @@ public class UnlockController implements ViewController {
|
||||
private final WindowsDriveLetters driveLetters;
|
||||
private final ChangeListener<Character> driveLetterChangeListener = this::winDriveLetterDidChange;
|
||||
private final Optional<KeychainAccess> keychainAccess;
|
||||
private final Settings settings;
|
||||
private Vault vault;
|
||||
private Optional<UnlockListener> listener = Optional.empty();
|
||||
private Subscription vaultSubs = Subscription.EMPTY;
|
||||
|
||||
@Inject
|
||||
public UnlockController(Application app, Localization localization, AsyncTaskService asyncTaskService, WindowsDriveLetters driveLetters, Optional<KeychainAccess> keychainAccess) {
|
||||
public UnlockController(Application app, Localization localization, AsyncTaskService asyncTaskService, WindowsDriveLetters driveLetters, Optional<KeychainAccess> keychainAccess, Settings settings) {
|
||||
this.app = app;
|
||||
this.localization = localization;
|
||||
this.asyncTaskService = asyncTaskService;
|
||||
this.driveLetters = driveLetters;
|
||||
this.keychainAccess = keychainAccess;
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -112,6 +119,18 @@ public class UnlockController implements ViewController {
|
||||
@FXML
|
||||
private ChoiceBox<Character> winDriveLetter;
|
||||
|
||||
@FXML
|
||||
private HBox mountPathBox;
|
||||
|
||||
@FXML
|
||||
private Label mountPathLabel;
|
||||
|
||||
@FXML
|
||||
private TextField mountPath;
|
||||
|
||||
@FXML
|
||||
private Button changeMountPathButton;
|
||||
|
||||
@FXML
|
||||
private ProgressIndicator progressIndicator;
|
||||
|
||||
@@ -140,14 +159,29 @@ public class UnlockController implements ViewController {
|
||||
mountName.textProperty().addListener(this::mountNameDidChange);
|
||||
savePassword.setDisable(!keychainAccess.isPresent());
|
||||
unlockAfterStartup.disableProperty().bind(savePassword.disabledProperty().or(savePassword.selectedProperty().not()));
|
||||
|
||||
mountPathBox.managedProperty().bind(mountPathLabel.visibleProperty());
|
||||
mountPath.managedProperty().bind(mountPathLabel.visibleProperty());
|
||||
changeMountPathButton.managedProperty().bind(mountPathLabel.visibleProperty());
|
||||
|
||||
if (SystemUtils.IS_OS_WINDOWS) {
|
||||
winDriveLetter.setConverter(new WinDriveLetterLabelConverter());
|
||||
mountPathLabel.setVisible(false);
|
||||
mountPathLabel.setManaged(false);
|
||||
//dirty cheat
|
||||
mountPathBox.setMouseTransparent(true);
|
||||
} else {
|
||||
winDriveLetterLabel.setVisible(false);
|
||||
winDriveLetterLabel.setManaged(false);
|
||||
winDriveLetter.setVisible(false);
|
||||
winDriveLetter.setManaged(false);
|
||||
if(settings.usedNioAdapterImpl().isEqualTo(NioAdapterImpl.WEBDAV.name()).get()){
|
||||
mountPathLabel.setVisible(false);
|
||||
mountPathLabel.setManaged(false);
|
||||
}
|
||||
}
|
||||
changeMountPathButton.disableProperty().bind(Bindings.createBooleanBinding(this::isDirVaild, mountPath.textProperty()).not());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -200,14 +234,20 @@ public class UnlockController implements ViewController {
|
||||
Arrays.fill(storedPw, ' ');
|
||||
}
|
||||
}
|
||||
VaultSettings settings = vault.getVaultSettings();
|
||||
unlockAfterStartup.setSelected(savePassword.isSelected() && settings.unlockAfterStartup().get());
|
||||
mountAfterUnlock.setSelected(settings.mountAfterUnlock().get());
|
||||
revealAfterMount.setSelected(settings.revealAfterMount().get());
|
||||
VaultSettings vaultSettings = vault.getVaultSettings();
|
||||
unlockAfterStartup.setSelected(savePassword.isSelected() && vaultSettings.unlockAfterStartup().get());
|
||||
mountAfterUnlock.setSelected(vaultSettings.mountAfterUnlock().get());
|
||||
revealAfterMount.setSelected(vaultSettings.revealAfterMount().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));
|
||||
|
||||
changeMountPathButton.visibleProperty().bind(
|
||||
vaultSettings.individualMountPath().isNotEqualTo(mountPath.textProperty())
|
||||
);
|
||||
mountPath.textProperty().setValue(vaultSettings.individualMountPath().getValueSafe());
|
||||
|
||||
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 +273,28 @@ 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 {
|
||||
return false;
|
||||
}
|
||||
|
||||
} 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();
|
||||
@@ -397,6 +459,7 @@ public class UnlockController implements ViewController {
|
||||
|
||||
@FunctionalInterface
|
||||
interface UnlockListener {
|
||||
|
||||
void didUnlock(Vault vault);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package org.cryptomator.ui.model;
|
||||
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.settings.VaultSettings;
|
||||
import org.cryptomator.cryptofs.CryptoFileSystem;
|
||||
|
||||
import org.cryptomator.frontend.fuse.mount.EnvironmentVariables;
|
||||
import org.cryptomator.frontend.fuse.mount.FuseMount;
|
||||
import org.cryptomator.frontend.fuse.mount.MountFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
@VaultModule.PerVault
|
||||
public class FuseVolume implements Volume {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(FuseVolume.class);
|
||||
private static final String DEFAULT_MOUNTROOTPATH_MAC = System.getProperty("user.home") + "/Library/Application Support/Cryptomator";
|
||||
private static final String DEFAULT_MOUNTROOTPATH_LINUX = System.getProperty("user.home") + "/.Cryptomator";
|
||||
|
||||
private final FuseMount fuseMnt;
|
||||
private final VaultSettings vaultSettings;
|
||||
private final WindowsDriveLetters windowsDriveLetters;
|
||||
|
||||
private CryptoFileSystem cfs;
|
||||
|
||||
@Inject
|
||||
public FuseVolume(VaultSettings vaultSettings, WindowsDriveLetters windowsDriveLetters) {
|
||||
this.vaultSettings = vaultSettings;
|
||||
this.windowsDriveLetters = windowsDriveLetters;
|
||||
this.fuseMnt = MountFactory.createMountObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepare(CryptoFileSystem fs) {
|
||||
this.cfs = fs;
|
||||
if (!(vaultSettings.individualMountPath().isNotNull().get() || SystemUtils.IS_OS_WINDOWS)) {
|
||||
fuseMnt.useExtraMountDir();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mount() throws CommandFailedException {
|
||||
try {
|
||||
EnvironmentVariables envVars = EnvironmentVariables.create()
|
||||
.withMountName(vaultSettings.mountName().getValue() + "_ID-" + vaultSettings.getId())
|
||||
.withMountPath(chooseMountRootPath())
|
||||
.build();
|
||||
fuseMnt.mount(cfs.getPath("/"), envVars);
|
||||
} catch (Exception e) {
|
||||
throw new CommandFailedException("Unable to mount Filesystem", e);
|
||||
}
|
||||
}
|
||||
|
||||
private String chooseMountRootPath() {
|
||||
if (SystemUtils.IS_OS_WINDOWS) {
|
||||
//windows case
|
||||
if (vaultSettings.winDriveLetter().get() != null) {
|
||||
// specific drive letter selected
|
||||
return vaultSettings.winDriveLetter().getValue() + ":\\";
|
||||
} else {
|
||||
// auto assign drive letter selected
|
||||
return windowsDriveLetters.getAvailableDriveLetters().iterator().next() + ":\\";
|
||||
}
|
||||
} else if (vaultSettings.individualMountPath().isNotNull().get()) {
|
||||
//specific path given
|
||||
return vaultSettings.individualMountPath().getValue();
|
||||
}
|
||||
//choose default path
|
||||
return SystemUtils.IS_OS_MAC ? DEFAULT_MOUNTROOTPATH_MAC : DEFAULT_MOUNTROOTPATH_LINUX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reveal() throws CommandFailedException {
|
||||
//fuseMnt.reveal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void unmount() throws CommandFailedException {
|
||||
if (cfs.getStats().pollBytesRead() == 0 && cfs.getStats().pollBytesWritten() == 0) {
|
||||
unmountRaw();
|
||||
} else {
|
||||
throw new CommandFailedException("Pending read or write operations.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void unmountForced() throws CommandFailedException {
|
||||
this.unmountRaw();
|
||||
}
|
||||
|
||||
private synchronized void unmountRaw() throws CommandFailedException {
|
||||
try {
|
||||
fuseMnt.unmount();
|
||||
} catch (org.cryptomator.frontend.fuse.mount.CommandFailedException e) {
|
||||
throw new CommandFailedException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
try {
|
||||
fuseMnt.cleanUp();
|
||||
} catch (org.cryptomator.frontend.fuse.mount.CommandFailedException e) {
|
||||
LOG.warn(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMountUri() {
|
||||
return Paths.get(fuseMnt.getMountPath()).toUri().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: chang this to a real implementation
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean isSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsForcedUnmount() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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,17 +28,7 @@ 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 javafx.application.Platform;
|
||||
import javafx.beans.Observable;
|
||||
@@ -53,6 +36,12 @@ 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 +53,20 @@ public class Vault {
|
||||
|
||||
private final Settings settings;
|
||||
private final VaultSettings vaultSettings;
|
||||
private final WebDavServer server;
|
||||
private final AtomicReference<CryptoFileSystem> cryptoFileSystem = new AtomicReference<>();
|
||||
private final ObjectProperty<State> state = new SimpleObjectProperty<State>(State.LOCKED);
|
||||
|
||||
private WebDavServletController servlet;
|
||||
private Mount mount;
|
||||
private Volume volume;
|
||||
|
||||
public enum State {
|
||||
LOCKED, UNLOCKED, MOUNTING, MOUNTED, UNMOUNTING
|
||||
};
|
||||
}
|
||||
|
||||
@Inject
|
||||
Vault(Settings settings, VaultSettings vaultSettings, WebDavServer server) {
|
||||
Vault(Settings settings, VaultSettings vaultSettings, Volume volume) {
|
||||
this.settings = settings;
|
||||
this.vaultSettings = vaultSettings;
|
||||
this.server = server;
|
||||
this.volume = volume;
|
||||
}
|
||||
|
||||
// ******************************************************************************
|
||||
@@ -111,80 +98,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);
|
||||
volume.prepare(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
|
||||
volume.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<Mount, ? extends UnmountOperation> 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 && volume.supportsForcedUnmount()) {
|
||||
volume.unmountForced();
|
||||
} else {
|
||||
volume.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 {
|
||||
volume.stop();
|
||||
CryptoFileSystem fs = cryptoFileSystem.getAndSet(null);
|
||||
if (fs != null) {
|
||||
fs.close();
|
||||
@@ -201,7 +156,7 @@ public class Vault {
|
||||
try {
|
||||
unmount();
|
||||
} catch (CommandFailedException e) {
|
||||
if (supportsForcedUnmount()) {
|
||||
if (volume.supportsForcedUnmount()) {
|
||||
try {
|
||||
unmountForced();
|
||||
} catch (CommandFailedException e1) {
|
||||
@@ -219,9 +174,7 @@ public class Vault {
|
||||
}
|
||||
|
||||
public void reveal() throws CommandFailedException {
|
||||
if (mount != null) {
|
||||
mount.reveal();
|
||||
}
|
||||
volume.reveal();
|
||||
}
|
||||
|
||||
// ******************************************************************************
|
||||
@@ -243,17 +196,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 +257,14 @@ public class Vault {
|
||||
return vaultSettings.mountName().get();
|
||||
}
|
||||
|
||||
public StringProperty getMountPathProperty() {
|
||||
return vaultSettings.individualMountPath();
|
||||
}
|
||||
|
||||
public void setMountPath(String mountPath) {
|
||||
vaultSettings.individualMountPath().set(mountPath);
|
||||
}
|
||||
|
||||
public void setMountName(String mountName) throws IllegalArgumentException {
|
||||
if (StringUtils.isBlank(mountName)) {
|
||||
throw new IllegalArgumentException("mount name is empty");
|
||||
@@ -332,6 +289,10 @@ public class Vault {
|
||||
}
|
||||
}
|
||||
|
||||
public String getFilesystemRootUrl() {
|
||||
return volume.getMountUri();
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return vaultSettings.getId();
|
||||
}
|
||||
@@ -355,4 +316,7 @@ public class Vault {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean supportsForcedUnmount() {
|
||||
return volume.supportsForcedUnmount();
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,8 @@ import java.util.Objects;
|
||||
|
||||
import javax.inject.Scope;
|
||||
|
||||
import org.cryptomator.common.settings.NioAdapterImpl;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.common.settings.VaultSettings;
|
||||
|
||||
import dagger.Module;
|
||||
@@ -36,6 +38,22 @@ public class VaultModule {
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface PerVault {
|
||||
|
||||
}
|
||||
|
||||
@Provides
|
||||
@PerVault
|
||||
public Volume provideNioAdpater(Settings settings, WebDavVolume webDavVolume, FuseVolume fuseVolume) {
|
||||
NioAdapterImpl impl = NioAdapterImpl.valueOf(settings.usedNioAdapterImpl().get());
|
||||
switch (impl) {
|
||||
case WEBDAV:
|
||||
return webDavVolume;
|
||||
case FUSE:
|
||||
return fuseVolume;
|
||||
default:
|
||||
//this should not happen!
|
||||
throw new IllegalStateException("Unsupported NioAdapter: " + settings.usedNioAdapterImpl().get());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package org.cryptomator.ui.model;
|
||||
|
||||
import org.cryptomator.cryptofs.CryptoFileSystem;
|
||||
|
||||
/**
|
||||
* Takes a Volume and usess it to mount an unlocked vault
|
||||
*/
|
||||
public interface Volume {
|
||||
|
||||
void prepare(CryptoFileSystem fs);
|
||||
|
||||
void mount() throws CommandFailedException;
|
||||
|
||||
default void reveal() throws CommandFailedException {
|
||||
throw new CommandFailedException("Not implemented.");
|
||||
}
|
||||
|
||||
void unmount() throws CommandFailedException;
|
||||
|
||||
default void unmountForced() throws CommandFailedException {
|
||||
throw new CommandFailedException("Operation not supported.");
|
||||
}
|
||||
|
||||
void stop();
|
||||
|
||||
String getMountUri();
|
||||
|
||||
default boolean isSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
default boolean supportsForcedUnmount() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
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;
|
||||
|
||||
@VaultModule.PerVault
|
||||
public class WebDavVolume implements Volume {
|
||||
|
||||
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 WebDavVolume(WebDavServer server, VaultSettings vaultSettings, Settings settings) {
|
||||
this.server = server;
|
||||
this.vaultSettings = vaultSettings;
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepare(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 void reveal() throws CommandFailedException {
|
||||
try {
|
||||
mount.reveal();
|
||||
} 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 getMountUri() {
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -35,22 +35,32 @@
|
||||
<!-- Row 0 -->
|
||||
<Label GridPane.rowIndex="0" GridPane.columnIndex="0" text="%settings.checkForUpdates.label" cacheShape="true" cache="true" />
|
||||
<CheckBox GridPane.rowIndex="0" GridPane.columnIndex="1" fx:id="checkForUpdatesCheckbox" cacheShape="true" cache="true" />
|
||||
|
||||
|
||||
<!-- Row 1 -->
|
||||
<Label GridPane.rowIndex="1" GridPane.columnIndex="0" text="%settings.port.label" cacheShape="true" cache="true" />
|
||||
<HBox GridPane.rowIndex="1" GridPane.columnIndex="1" spacing="6.0">
|
||||
<TextField fx:id="portField" cacheShape="true" cache="true" promptText="%settings.port.prompt" />
|
||||
<Button text="%settings.port.apply" fx:id="changePortButton" onAction="#changePort"/>
|
||||
</HBox>
|
||||
|
||||
<Label GridPane.rowIndex="1" GridPane.columnIndex="0" text="%settings.debugMode.label" cacheShape="true" cache="true" />
|
||||
<CheckBox GridPane.rowIndex="1" GridPane.columnIndex="1" fx:id="debugModeCheckbox" cacheShape="true" cache="true" />
|
||||
|
||||
<!-- Row 2 -->
|
||||
<Label GridPane.rowIndex="2" GridPane.columnIndex="0" fx:id="prefGvfsSchemeLabel" text="%settings.prefGvfsScheme.label" cacheShape="true" cache="true" />
|
||||
<ChoiceBox GridPane.rowIndex="2" GridPane.columnIndex="1" fx:id="prefGvfsScheme" GridPane.hgrow="ALWAYS" maxWidth="Infinity" cacheShape="true" cache="true" />
|
||||
|
||||
<!-- Row 3 -->
|
||||
<Label GridPane.rowIndex="3" GridPane.columnIndex="0" text="%settings.debugMode.label" cacheShape="true" cache="true" />
|
||||
<CheckBox GridPane.rowIndex="3" GridPane.columnIndex="1" fx:id="debugModeCheckbox" cacheShape="true" cache="true" />
|
||||
|
||||
<Label fx:id="volumeLabel" GridPane.rowIndex="2" GridPane.columnIndex="0" text="%settings.volume.label" cacheShape="true" cache="true" />
|
||||
<ChoiceBox GridPane.rowIndex="2" GridPane.columnIndex="1" fx:id="volume" cacheShape="true" cache="true" />
|
||||
|
||||
<!-- Row 3 Alt 1-->
|
||||
<GridPane fx:id="webdavVolume" vgap="12.0" hgap="12.0" GridPane.rowIndex="3" GridPane.columnIndex="0" GridPane.columnSpan="2" visible="true" cacheShape="true" cache="true">
|
||||
<Label fx:id="portFieldLabel" GridPane.rowIndex="3" GridPane.columnIndex="0" text="%settings.webdav.port.label" cacheShape="true" cache="true" />
|
||||
<HBox GridPane.rowIndex="3" GridPane.columnIndex="1" spacing="6.0">
|
||||
<TextField fx:id="portField" cacheShape="true" cache="true" promptText="%settings.webdav.port.prompt" />
|
||||
<Button text="%settings.webdav.port.apply" fx:id="changePortButton" onAction="#changePort"/>
|
||||
</HBox>
|
||||
|
||||
<!-- Row 4 -->
|
||||
<Label GridPane.rowIndex="4" GridPane.columnIndex="0" fx:id="prefGvfsSchemeLabel" text="%settings.webdav.prefGvfsScheme.label" cacheShape="true" cache="true" />
|
||||
<ChoiceBox GridPane.rowIndex="4" GridPane.columnIndex="1" fx:id="prefGvfsScheme" GridPane.hgrow="ALWAYS" maxWidth="Infinity" cacheShape="true" cache="true" />
|
||||
</GridPane>
|
||||
|
||||
<!-- Row 3 Alt 2-->
|
||||
<GridPane fx:id="fuseVolume" vgap="12.0" hgap="12.0" GridPane.rowIndex="3" GridPane.columnIndex="0" GridPane.columnSpan="2" visible="false" cacheShape="true" cache="true">
|
||||
</GridPane>
|
||||
|
||||
</children>
|
||||
</GridPane>
|
||||
<Label VBox.vgrow="NEVER" text="%settings.requiresRestartLabel" alignment="CENTER" cacheShape="true" cache="true" />
|
||||
|
||||
@@ -83,9 +83,17 @@
|
||||
<!-- Row 3.5 -->
|
||||
<CheckBox GridPane.rowIndex="5" GridPane.columnIndex="0" GridPane.columnSpan="2" fx:id="revealAfterMount" text="%unlock.label.revealAfterMount" cacheShape="true" cache="true" />
|
||||
|
||||
<!-- Row 3.6 -->
|
||||
<!-- 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.6 Alt2 -->
|
||||
<Label GridPane.rowIndex="6" GridPane.columnIndex="0" fx:id="mountPathLabel" text="%unlock.label.mountPath" cacheShape="true" cache="true" />
|
||||
|
||||
<HBox GridPane.rowIndex="6" GridPane.columnIndex="1" fx:id="mountPathBox" spacing="6.0">
|
||||
<TextField GridPane.rowIndex="6" GridPane.columnIndex="1" fx:id="mountPath" cacheShape="true" cache="true" />
|
||||
<Button text="%unlock.label.mountPathButton" fx:id="changeMountPathButton" onAction="#didClickchangeMountPathButton"/>
|
||||
</HBox>
|
||||
</GridPane>
|
||||
|
||||
<!-- Row 4 -->
|
||||
|
||||
@@ -67,6 +67,8 @@ unlock.label.mountName=Drive Name
|
||||
unlock.label.unlockAfterStartup=Auto-Unlock on Start (Experimental)
|
||||
unlock.label.revealAfterMount=Reveal Drive
|
||||
unlock.label.winDriveLetter=Drive Letter
|
||||
unlock.label.mountPath=Mount Path
|
||||
unlock.label.mountPathButton=Apply
|
||||
unlock.label.downloadsPageLink=All Cryptomator versions
|
||||
unlock.label.advancedHeading=Advanced Options
|
||||
unlock.button.unlock=Unlock Vault
|
||||
@@ -97,7 +99,7 @@ unlocked.button.lock=Lock Vault
|
||||
unlocked.moreOptions.mount=Mount Drive
|
||||
unlocked.moreOptions.unmount=Eject Drive
|
||||
unlocked.moreOptions.reveal=Reveal Drive
|
||||
unlocked.moreOptions.copyUrl=Copy WebDAV URL
|
||||
unlocked.moreOptions.copyUrl=Copy Filesystem URL
|
||||
unlocked.label.mountFailed=Connecting drive failed
|
||||
unlocked.label.revealFailed=Command failed
|
||||
unlocked.label.unmountFailed=Ejecting drive failed
|
||||
@@ -111,12 +113,15 @@ unlocked.lock.force.confirmation.content=This may be because other programs are
|
||||
# settings.fxml
|
||||
settings.version.label=Version %s
|
||||
settings.checkForUpdates.label=Check for Updates
|
||||
settings.port.label=WebDAV Port
|
||||
settings.port.prompt=0 = Choose automatically
|
||||
settings.port.apply=Apply
|
||||
settings.prefGvfsScheme.label=WebDAV Scheme
|
||||
settings.webdav.port.label=WebDAV Port
|
||||
settings.webdav.port.prompt=0 = Choose automatically
|
||||
settings.webdav.port.apply=Apply
|
||||
settings.webdav.prefGvfsScheme.label=WebDAV Scheme
|
||||
settings.debugMode.label=Debug Mode *
|
||||
settings.requiresRestartLabel=* Cryptomator needs to restart
|
||||
settings.volume.label= Mount-Methode *
|
||||
settings.volume.webdav=WebDAV
|
||||
settings.volume.fuse=FUSE
|
||||
|
||||
# tray icon
|
||||
tray.menu.open=Open
|
||||
|
||||
Reference in New Issue
Block a user