mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-09-19 22:44:32 +00:00
call me THE DESTROYER!!!
first compile-clean but totally fubar version
This commit is contained in:
+8
-28
@@ -24,35 +24,11 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>filesystem-api</artifactId>
|
||||
<artifactId>cryptofs</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>filesystem-nio</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>filesystem-nameshortening</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>filesystem-crypto</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>filesystem-charsets</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>filesystem-stats</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>frontend-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>frontend-webdav</artifactId>
|
||||
<artifactId>webdav-nio-adapter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
@@ -77,8 +53,8 @@
|
||||
|
||||
<!-- JSON -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Guava -->
|
||||
@@ -96,6 +72,10 @@
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-collections4</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-httpclient</groupId>
|
||||
<artifactId>commons-httpclient</artifactId>
|
||||
|
||||
@@ -15,15 +15,15 @@ import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.ui.util.ApplicationVersion;
|
||||
import org.cryptomator.ui.util.SingleInstanceManager;
|
||||
import org.cryptomator.ui.util.SingleInstanceManager.RemoteInstance;
|
||||
import org.eclipse.jetty.util.ConcurrentHashSet;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -33,7 +33,7 @@ public class Cryptomator {
|
||||
|
||||
public static final CompletableFuture<Consumer<File>> OPEN_FILE_HANDLER = new CompletableFuture<>();
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Cryptomator.class);
|
||||
private static final Set<Runnable> SHUTDOWN_TASKS = new ConcurrentHashSet<>();
|
||||
private static final ConcurrentMap<Runnable, Boolean> SHUTDOWN_TASKS = new ConcurrentHashMap<>();
|
||||
|
||||
public static void main(String[] args) {
|
||||
LOG.info("Starting Cryptomator {} on {} {} ({})", ApplicationVersion.orElse("SNAPSHOT"), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH);
|
||||
@@ -91,7 +91,7 @@ public class Cryptomator {
|
||||
}
|
||||
|
||||
public static void addShutdownTask(Runnable r) {
|
||||
SHUTDOWN_TASKS.add(r);
|
||||
SHUTDOWN_TASKS.put(r, Boolean.TRUE);
|
||||
}
|
||||
|
||||
public static void removeShutdownTask(Runnable r) {
|
||||
@@ -102,7 +102,7 @@ public class Cryptomator {
|
||||
@Override
|
||||
public void run() {
|
||||
LOG.debug("Shutting down");
|
||||
SHUTDOWN_TASKS.forEach(r -> {
|
||||
SHUTDOWN_TASKS.keySet().forEach(r -> {
|
||||
try {
|
||||
r.run();
|
||||
} catch (RuntimeException e) {
|
||||
|
||||
@@ -8,24 +8,20 @@
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.ui;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.cryptomator.jni.MacFunctions;
|
||||
import org.cryptomator.ui.controllers.MainController;
|
||||
import org.cryptomator.ui.settings.Localization;
|
||||
import org.cryptomator.ui.util.AsyncTaskService;
|
||||
import org.cryptomator.ui.model.VaultComponent;
|
||||
import org.cryptomator.ui.model.VaultModule;
|
||||
import org.cryptomator.ui.util.DeferredCloser;
|
||||
|
||||
import dagger.Component;
|
||||
|
||||
@Singleton
|
||||
@Component(modules = CryptomatorModule.class)
|
||||
interface CryptomatorComponent {
|
||||
|
||||
AsyncTaskService asyncTaskService();
|
||||
public interface CryptomatorComponent {
|
||||
|
||||
ExecutorService executorService();
|
||||
|
||||
@@ -33,12 +29,10 @@ interface CryptomatorComponent {
|
||||
|
||||
MainController mainController();
|
||||
|
||||
Localization localization();
|
||||
|
||||
ExitUtil exitUtil();
|
||||
|
||||
DebugMode debugMode();
|
||||
|
||||
Optional<MacFunctions> nativeMacFunctions();
|
||||
VaultComponent newVaultComponent(VaultModule vaultModule);
|
||||
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.ui;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
@@ -17,32 +15,22 @@ import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.cryptomator.common.CommonsModule;
|
||||
import org.cryptomator.crypto.engine.impl.CryptoEngineModule;
|
||||
import org.cryptomator.cryptolib.CryptoLibModule;
|
||||
import org.cryptomator.frontend.FrontendFactory;
|
||||
import org.cryptomator.frontend.FrontendId;
|
||||
import org.cryptomator.frontend.webdav.WebDavModule;
|
||||
import org.cryptomator.frontend.webdav.WebDavServer;
|
||||
import org.cryptomator.jni.JniModule;
|
||||
import org.cryptomator.keychain.KeychainModule;
|
||||
import org.cryptomator.ui.model.Vault;
|
||||
import org.cryptomator.ui.model.VaultObjectMapperProvider;
|
||||
import org.cryptomator.ui.model.Vaults;
|
||||
import org.cryptomator.ui.settings.Settings;
|
||||
import org.cryptomator.ui.settings.SettingsProvider;
|
||||
import org.cryptomator.ui.util.DeferredCloser;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import javafx.application.Application;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
@Module(includes = {CryptoEngineModule.class, CommonsModule.class, WebDavModule.class, KeychainModule.class, JniModule.class, CryptoLibModule.class})
|
||||
@Module(includes = {CommonsModule.class, KeychainModule.class, JniModule.class, CryptoLibModule.class})
|
||||
class CryptomatorModule {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CryptomatorModule.class);
|
||||
@@ -81,13 +69,6 @@ class CryptomatorModule {
|
||||
return closer;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("VaultJsonMapper")
|
||||
ObjectMapper provideVaultObjectMapper(VaultObjectMapperProvider vaultObjectMapperProvider) {
|
||||
return vaultObjectMapperProvider.get();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
Settings provideSettings(SettingsProvider settingsProvider) {
|
||||
@@ -102,17 +83,8 @@ class CryptomatorModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
FrontendFactory provideFrontendFactory(DeferredCloser closer, WebDavServer webDavServer, Vaults vaults, Settings settings) {
|
||||
vaults.addListener((Observable o) -> setValidFrontendIds(webDavServer, vaults));
|
||||
setValidFrontendIds(webDavServer, vaults);
|
||||
webDavServer.setPort(settings.getPort());
|
||||
webDavServer.start();
|
||||
return closer.closeLater(webDavServer, WebDavServer::stop).get().orElseThrow(IllegalStateException::new);
|
||||
}
|
||||
|
||||
private void setValidFrontendIds(WebDavServer webDavServer, Vaults vaults) {
|
||||
webDavServer.setValidFrontendIds(vaults.stream() //
|
||||
.map(Vault::getId).map(FrontendId::from).collect(toList()));
|
||||
WebDavServer provideWebDavServer(Settings settings) {
|
||||
return WebDavServer.create("localhost", settings.getPort());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.cryptomator.crypto.engine.InvalidPassphraseException;
|
||||
import org.cryptomator.crypto.engine.UnsupportedVaultFormatException;
|
||||
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
||||
import org.cryptomator.cryptolib.api.UnsupportedVaultFormatException;
|
||||
import org.cryptomator.ui.controls.SecPasswordField;
|
||||
import org.cryptomator.ui.model.Vault;
|
||||
import org.cryptomator.ui.settings.Localization;
|
||||
|
||||
@@ -29,9 +29,10 @@ import org.cryptomator.ui.controls.DirectoryListCell;
|
||||
import org.cryptomator.ui.model.UpgradeStrategies;
|
||||
import org.cryptomator.ui.model.Vault;
|
||||
import org.cryptomator.ui.model.VaultFactory;
|
||||
import org.cryptomator.ui.model.Vaults;
|
||||
import org.cryptomator.ui.model.VaultList;
|
||||
import org.cryptomator.ui.settings.Localization;
|
||||
import org.cryptomator.ui.settings.Settings;
|
||||
import org.cryptomator.ui.settings.VaultSettings;
|
||||
import org.cryptomator.ui.util.DialogBuilderUtil;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
import org.slf4j.Logger;
|
||||
@@ -79,7 +80,7 @@ public class MainController extends LocalizedFXMLViewController {
|
||||
private final Lazy<SettingsController> settingsController;
|
||||
private final Lazy<UpgradeStrategies> upgradeStrategies;
|
||||
private final ObjectProperty<AbstractFXMLViewController> activeController = new SimpleObjectProperty<>();
|
||||
private final Vaults vaults;
|
||||
private final VaultList vaults;
|
||||
private final ObjectProperty<Vault> selectedVault = new SimpleObjectProperty<>();
|
||||
private final BooleanExpression isSelectedVaultUnlocked = BooleanBinding.booleanExpression(EasyBind.select(selectedVault).selectObject(Vault::unlockedProperty).orElse(false));
|
||||
private final BooleanExpression isSelectedVaultValid = BooleanBinding.booleanExpression(EasyBind.monadic(selectedVault).map(Vault::isValidVaultDirectory).orElse(false));
|
||||
@@ -91,7 +92,7 @@ public class MainController extends LocalizedFXMLViewController {
|
||||
public MainController(@Named("mainWindow") Stage mainWindow, Localization localization, Settings settings, VaultFactory vaultFactoy, Lazy<WelcomeController> welcomeController,
|
||||
Lazy<InitializeController> initializeController, Lazy<NotFoundController> notFoundController, Lazy<UpgradeController> upgradeController, Lazy<UnlockController> unlockController,
|
||||
Provider<UnlockedController> unlockedControllerProvider, Lazy<ChangePasswordController> changePasswordController, Lazy<SettingsController> settingsController, Lazy<UpgradeStrategies> upgradeStrategies,
|
||||
Vaults vaults) {
|
||||
VaultList vaults) {
|
||||
super(localization);
|
||||
this.mainWindow = mainWindow;
|
||||
this.vaultFactoy = vaultFactoy;
|
||||
@@ -230,7 +231,9 @@ public class MainController extends LocalizedFXMLViewController {
|
||||
return;
|
||||
}
|
||||
|
||||
final Vault vault = vaultFactoy.createVault(vaultPath);
|
||||
final VaultSettings vaultSettings = VaultSettings.withRandomId();
|
||||
vaultSettings.setPath(vaultPath);
|
||||
final Vault vault = vaultFactoy.get(vaultSettings);
|
||||
if (!vaults.contains(vault)) {
|
||||
vaults.add(vault);
|
||||
}
|
||||
|
||||
@@ -18,15 +18,13 @@ import javax.inject.Inject;
|
||||
|
||||
import org.apache.commons.lang3.CharUtils;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.crypto.engine.InvalidPassphraseException;
|
||||
import org.cryptomator.crypto.engine.UnsupportedVaultFormatException;
|
||||
import org.cryptomator.frontend.CommandFailedException;
|
||||
import org.cryptomator.frontend.FrontendCreationFailedException;
|
||||
import org.cryptomator.frontend.FrontendFactory;
|
||||
import org.cryptomator.frontend.webdav.mount.WindowsDriveLetters;
|
||||
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
||||
import org.cryptomator.cryptolib.api.UnsupportedVaultFormatException;
|
||||
import org.cryptomator.frontend.webdav.ServerLifecycleException;
|
||||
import org.cryptomator.keychain.KeychainAccess;
|
||||
import org.cryptomator.ui.controls.SecPasswordField;
|
||||
import org.cryptomator.ui.model.Vault;
|
||||
import org.cryptomator.ui.model.WindowsDriveLetters;
|
||||
import org.cryptomator.ui.settings.Localization;
|
||||
import org.cryptomator.ui.settings.Settings;
|
||||
import org.cryptomator.ui.util.AsyncTaskService;
|
||||
@@ -34,7 +32,6 @@ import org.cryptomator.ui.util.DialogBuilderUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import dagger.Lazy;
|
||||
import javafx.application.Application;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
@@ -61,7 +58,6 @@ public class UnlockController extends LocalizedFXMLViewController {
|
||||
|
||||
private final Application app;
|
||||
private final AsyncTaskService asyncTaskService;
|
||||
private final Lazy<FrontendFactory> frontendFactory;
|
||||
private final Settings settings;
|
||||
private final WindowsDriveLetters driveLetters;
|
||||
private final ChangeListener<Character> driveLetterChangeListener = this::winDriveLetterDidChange;
|
||||
@@ -70,12 +66,10 @@ public class UnlockController extends LocalizedFXMLViewController {
|
||||
private Optional<UnlockListener> listener = Optional.empty();
|
||||
|
||||
@Inject
|
||||
public UnlockController(Application app, Localization localization, AsyncTaskService asyncTaskService, Lazy<FrontendFactory> frontendFactory, Settings settings, WindowsDriveLetters driveLetters,
|
||||
Optional<KeychainAccess> keychainAccess) {
|
||||
public UnlockController(Application app, Localization localization, AsyncTaskService asyncTaskService, Settings settings, WindowsDriveLetters driveLetters, Optional<KeychainAccess> keychainAccess) {
|
||||
super(localization);
|
||||
this.app = app;
|
||||
this.asyncTaskService = asyncTaskService;
|
||||
this.frontendFactory = frontendFactory;
|
||||
this.settings = settings;
|
||||
this.driveLetters = driveLetters;
|
||||
this.keychainAccess = keychainAccess;
|
||||
@@ -326,7 +320,7 @@ public class UnlockController extends LocalizedFXMLViewController {
|
||||
|
||||
private void unlock(CharSequence password) {
|
||||
try {
|
||||
vault.activateFrontend(frontendFactory.get(), settings, password);
|
||||
vault.activateFrontend(password);
|
||||
vault.reveal();
|
||||
Platform.runLater(() -> {
|
||||
messageText.setText(null);
|
||||
@@ -356,8 +350,8 @@ public class UnlockController extends LocalizedFXMLViewController {
|
||||
messageText.setText(localization.getString("unlock.errorMessage.unauthenticVersionMac"));
|
||||
}
|
||||
});
|
||||
} catch (FrontendCreationFailedException | CommandFailedException e) {
|
||||
LOG.error("Decryption failed for technical reasons.", e);
|
||||
} catch (ServerLifecycleException e) {
|
||||
LOG.error("Unlock failed for technical reasons.", e);
|
||||
Platform.runLater(() -> {
|
||||
messageText.setText(localization.getString("unlock.errorMessage.mountingFailed"));
|
||||
});
|
||||
|
||||
@@ -14,7 +14,6 @@ import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
|
||||
import org.cryptomator.frontend.CommandFailedException;
|
||||
import org.cryptomator.ui.model.Vault;
|
||||
import org.cryptomator.ui.settings.Localization;
|
||||
import org.cryptomator.ui.util.ActiveWindowStyleSupport;
|
||||
@@ -107,13 +106,16 @@ public class UnlockedController extends LocalizedFXMLViewController {
|
||||
}
|
||||
|
||||
// listen to MAC warnings as long as this vault is unlocked:
|
||||
final ListChangeListener<String> macWarningsListener = this::macWarningsDidChange;
|
||||
newVault.getNamesOfResourcesWithInvalidMac().addListener(macWarningsListener);
|
||||
newVault.unlockedProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (Boolean.FALSE.equals(newValue)) {
|
||||
newVault.getNamesOfResourcesWithInvalidMac().removeListener(macWarningsListener);
|
||||
}
|
||||
});
|
||||
// TODO overheadhunter: reimplement eventually
|
||||
/*
|
||||
* final ListChangeListener<String> macWarningsListener = this::macWarningsDidChange;
|
||||
* newVault.getNamesOfResourcesWithInvalidMac().addListener(macWarningsListener);
|
||||
* newVault.unlockedProperty().addListener((observable, oldValue, newValue) -> {
|
||||
* if (Boolean.FALSE.equals(newValue)) {
|
||||
* newVault.getNamesOfResourcesWithInvalidMac().removeListener(macWarningsListener);
|
||||
* }
|
||||
* });
|
||||
*/
|
||||
|
||||
if (!vault.get().isMounted()) {
|
||||
// TODO Markus Kreusch #393: hyperlink auf FAQ oder sowas?
|
||||
@@ -150,7 +152,8 @@ public class UnlockedController extends LocalizedFXMLViewController {
|
||||
private void didClickRevealVault(ActionEvent event) {
|
||||
asyncTaskService.asyncTaskOf(() -> {
|
||||
vault.get().reveal();
|
||||
}).onError(CommandFailedException.class, () -> {
|
||||
}).onError(RuntimeException.class, () -> {
|
||||
// TODO overheadhunter catch more specific exception type thrown by reveal()
|
||||
messageLabel.setText(localization.getString("unlocked.label.revealFailed"));
|
||||
}).run();
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ public class UpgradeController extends LocalizedFXMLViewController {
|
||||
asyncTaskService //
|
||||
.asyncTaskOf(() -> {
|
||||
if (!instruction.isApplicable(vault)) {
|
||||
throw new IllegalStateException("No ugprade needed for " + vault.path().getValue());
|
||||
throw new IllegalStateException("No ugprade needed for " + vault.getPath());
|
||||
}
|
||||
instruction.upgrade(vault, passwordField.getCharacters());
|
||||
}) //
|
||||
|
||||
@@ -9,9 +9,11 @@
|
||||
package org.cryptomator.ui.controllers;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -24,7 +26,6 @@ import org.apache.commons.httpclient.HttpStatus;
|
||||
import org.apache.commons.httpclient.cookie.CookiePolicy;
|
||||
import org.apache.commons.httpclient.methods.GetMethod;
|
||||
import org.apache.commons.httpclient.params.HttpClientParams;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.ui.settings.Localization;
|
||||
import org.cryptomator.ui.settings.Settings;
|
||||
@@ -33,8 +34,9 @@ import org.cryptomator.ui.util.AsyncTaskService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import javafx.application.Application;
|
||||
import javafx.application.Platform;
|
||||
@@ -110,10 +112,10 @@ public class WelcomeController extends LocalizedFXMLViewController {
|
||||
client.executeMethod(method);
|
||||
final InputStream responseBodyStream = method.getResponseBodyAsStream();
|
||||
if (method.getStatusCode() == HttpStatus.SC_OK && responseBodyStream != null) {
|
||||
final byte[] responseData = IOUtils.toByteArray(responseBodyStream);
|
||||
final ObjectMapper mapper = new ObjectMapper();
|
||||
final Map<String, String> map = mapper.readValue(responseData, new TypeReference<HashMap<String, String>>() {
|
||||
});
|
||||
Gson gson = new GsonBuilder().setLenient().create();
|
||||
Reader utf8Reader = new InputStreamReader(responseBodyStream, StandardCharsets.UTF_8);
|
||||
Map<String, String> map = gson.fromJson(utf8Reader, new TypeToken<Map<String, String>>() {
|
||||
}.getType());
|
||||
if (map != null) {
|
||||
this.compareVersions(map);
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public class DirectoryListCell extends DraggableListCell<Vault> {
|
||||
statusIndicator.fillProperty().bind(EasyBind.monadic(itemProperty()).flatMap(Vault::unlockedProperty).filter(Boolean.TRUE::equals).map(unlocked -> GREEN_FILL).orElse(RED_FILL));
|
||||
statusIndicator.strokeProperty().bind(EasyBind.monadic(itemProperty()).flatMap(Vault::unlockedProperty).filter(Boolean.TRUE::equals).map(unlocked -> GREEN_STROKE).orElse(RED_STROKE));
|
||||
|
||||
tooltipProperty().bind(EasyBind.monadic(itemProperty()).flatMap(Vault::path).map(p -> new Tooltip(p.toString())));
|
||||
tooltipProperty().bind(EasyBind.monadic(itemProperty()).flatMap(Vault::displayablePath).map(p -> new Tooltip(p.toString())));
|
||||
contextMenuProperty().bind(EasyBind.monadic(itemProperty()).flatMap(Vault::unlockedProperty).map(unlocked -> {
|
||||
return unlocked ? null : vaultContextMenu;
|
||||
}));
|
||||
|
||||
@@ -11,7 +11,6 @@ import org.cryptomator.cryptolib.api.CryptorProvider;
|
||||
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
||||
import org.cryptomator.cryptolib.api.KeyFile;
|
||||
import org.cryptomator.cryptolib.api.UnsupportedVaultFormatException;
|
||||
import org.cryptomator.filesystem.crypto.Constants;
|
||||
import org.cryptomator.ui.settings.Localization;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -19,6 +18,8 @@ import org.slf4j.LoggerFactory;
|
||||
public abstract class UpgradeStrategy {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(UpgradeStrategy.class);
|
||||
private static final String MASTERKEY_FILENAME = "masterkey.cryptomator";
|
||||
private static final String MASTERKEY_BACKUP_FILENAME = "masterkey.cryptomator.bkup";
|
||||
|
||||
protected final CryptorProvider cryptorProvider;
|
||||
protected final Localization localization;
|
||||
@@ -46,28 +47,28 @@ public abstract class UpgradeStrategy {
|
||||
* Upgrades a vault. Might take a moment, should be run in a background thread.
|
||||
*/
|
||||
public void upgrade(Vault vault, CharSequence passphrase) throws UpgradeFailedException {
|
||||
LOG.info("Upgrading {} from {} to {}.", vault.path().getValue(), vaultVersionBeforeUpgrade, vaultVersionAfterUpgrade);
|
||||
LOG.info("Upgrading {} from {} to {}.", vault.getPath(), vaultVersionBeforeUpgrade, vaultVersionAfterUpgrade);
|
||||
Cryptor cryptor = null;
|
||||
try {
|
||||
final Path masterkeyFile = vault.path().getValue().resolve(Constants.MASTERKEY_FILENAME);
|
||||
final Path masterkeyFile = vault.getPath().resolve(MASTERKEY_FILENAME);
|
||||
final byte[] masterkeyFileContents = Files.readAllBytes(masterkeyFile);
|
||||
cryptor = cryptorProvider.createFromKeyFile(KeyFile.parse(masterkeyFileContents), passphrase, vaultVersionBeforeUpgrade);
|
||||
// create backup, as soon as we know the password was correct:
|
||||
final Path masterkeyBackupFile = vault.path().getValue().resolve(Constants.MASTERKEY_BACKUP_FILENAME);
|
||||
final Path masterkeyBackupFile = vault.getPath().resolve(MASTERKEY_BACKUP_FILENAME);
|
||||
Files.copy(masterkeyFile, masterkeyBackupFile, StandardCopyOption.REPLACE_EXISTING);
|
||||
LOG.info("Backuped masterkey.");
|
||||
// do stuff:
|
||||
upgrade(vault, cryptor);
|
||||
// write updated masterkey file:
|
||||
final byte[] upgradedMasterkeyFileContents = cryptor.writeKeysToMasterkeyFile(passphrase, vaultVersionAfterUpgrade).serialize();
|
||||
final Path masterkeyFileAfterUpgrade = vault.path().getValue().resolve(Constants.MASTERKEY_FILENAME); // path may have changed
|
||||
final Path masterkeyFileAfterUpgrade = vault.getPath().resolve(MASTERKEY_FILENAME); // path may have changed
|
||||
Files.write(masterkeyFileAfterUpgrade, upgradedMasterkeyFileContents, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
LOG.info("Updated masterkey.");
|
||||
} catch (InvalidPassphraseException e) {
|
||||
throw new UpgradeFailedException(localization.getString("unlock.errorMessage.wrongPassword"));
|
||||
} catch (UnsupportedVaultFormatException e) {
|
||||
if (e.getDetectedVersion() == Integer.MAX_VALUE) {
|
||||
LOG.warn("Version MAC authentication error in vault {}", vault.path().get());
|
||||
LOG.warn("Version MAC authentication error in vault {}", vault.getPath());
|
||||
throw new UpgradeFailedException(localization.getString("unlock.errorMessage.unauthenticVersionMac"));
|
||||
} else {
|
||||
LOG.warn("Upgrade failed.", e);
|
||||
@@ -91,7 +92,7 @@ public abstract class UpgradeStrategy {
|
||||
* @return <code>true</code> if and only if the vault can be migrated to a newer version without the risk of data losses.
|
||||
*/
|
||||
public boolean isApplicable(Vault vault) {
|
||||
final Path masterkeyFile = vault.path().getValue().resolve(Constants.MASTERKEY_FILENAME);
|
||||
final Path masterkeyFile = vault.getPath().resolve(MASTERKEY_FILENAME);
|
||||
try {
|
||||
if (Files.isRegularFile(masterkeyFile)) {
|
||||
byte[] masterkeyFileContents = Files.readAllBytes(masterkeyFile);
|
||||
|
||||
+4
-4
@@ -39,7 +39,7 @@ class UpgradeVersion3DropBundleExtension extends UpgradeStrategy {
|
||||
@Override
|
||||
public String getMessage(Vault vault) {
|
||||
String fmt = localization.getString("upgrade.version3dropBundleExtension.msg");
|
||||
Path path = vault.path().getValue();
|
||||
Path path = vault.getPath();
|
||||
String oldVaultName = path.getFileName().toString();
|
||||
String newVaultName = StringUtils.removeEnd(oldVaultName, Vault.VAULT_FILE_EXTENSION);
|
||||
return String.format(fmt, oldVaultName, newVaultName);
|
||||
@@ -47,7 +47,7 @@ class UpgradeVersion3DropBundleExtension extends UpgradeStrategy {
|
||||
|
||||
@Override
|
||||
protected void upgrade(Vault vault, Cryptor cryptor) throws UpgradeFailedException {
|
||||
Path path = vault.path().getValue();
|
||||
Path path = vault.getPath();
|
||||
String oldVaultName = path.getFileName().toString();
|
||||
String newVaultName = StringUtils.removeEnd(oldVaultName, Vault.VAULT_FILE_EXTENSION);
|
||||
Path newPath = path.resolveSibling(newVaultName);
|
||||
@@ -60,7 +60,7 @@ class UpgradeVersion3DropBundleExtension extends UpgradeStrategy {
|
||||
LOG.info("Renaming {} to {}", path, newPath.getFileName());
|
||||
Files.move(path, path.resolveSibling(newVaultName));
|
||||
Platform.runLater(() -> {
|
||||
vault.setPath(newPath);
|
||||
vault.getVaultSettings().setPath(newPath);
|
||||
settings.save();
|
||||
});
|
||||
} catch (IOException e) {
|
||||
@@ -72,7 +72,7 @@ class UpgradeVersion3DropBundleExtension extends UpgradeStrategy {
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(Vault vault) {
|
||||
Path vaultPath = vault.path().getValue();
|
||||
Path vaultPath = vault.getPath();
|
||||
if (vaultPath.toString().endsWith(Vault.VAULT_FILE_EXTENSION)) {
|
||||
return super.isApplicable(vault);
|
||||
} else {
|
||||
|
||||
@@ -61,8 +61,8 @@ class UpgradeVersion3to4 extends UpgradeStrategy {
|
||||
|
||||
@Override
|
||||
protected void upgrade(Vault vault, Cryptor cryptor) throws UpgradeFailedException {
|
||||
Path dataDir = vault.path().get().resolve("d");
|
||||
Path metadataDir = vault.path().get().resolve("m");
|
||||
Path dataDir = vault.getPath().resolve("d");
|
||||
Path metadataDir = vault.getPath().resolve("m");
|
||||
if (!Files.isDirectory(dataDir)) {
|
||||
return; // empty vault. no migration needed.
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ class UpgradeVersion4to5 extends UpgradeStrategy {
|
||||
|
||||
@Override
|
||||
protected void upgrade(Vault vault, Cryptor cryptor) throws UpgradeFailedException {
|
||||
Path dataDir = vault.path().get().resolve("d");
|
||||
Path dataDir = vault.getPath().resolve("d");
|
||||
if (!Files.isDirectory(dataDir)) {
|
||||
return; // empty vault. no migration needed.
|
||||
}
|
||||
@@ -76,6 +76,7 @@ class UpgradeVersion4to5 extends UpgradeStrategy {
|
||||
LOG.info("Migration finished.");
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void migrate(Path file, BasicFileAttributes attrs, Cryptor cryptor) throws IOException {
|
||||
LOG.info("Starting migration of {}...", file);
|
||||
try (FileChannel ch = FileChannel.open(file, StandardOpenOption.READ, StandardOpenOption.WRITE)) {
|
||||
|
||||
@@ -8,96 +8,61 @@
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.ui.model;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.stripStart;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.DirectoryNotEmptyException;
|
||||
import java.nio.file.FileAlreadyExistsException;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.Normalizer;
|
||||
import java.text.Normalizer.Form;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.apache.commons.lang3.CharUtils;
|
||||
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.crypto.engine.InvalidPassphraseException;
|
||||
import org.cryptomator.filesystem.File;
|
||||
import org.cryptomator.filesystem.FileSystem;
|
||||
import org.cryptomator.filesystem.charsets.NormalizedNameFileSystem;
|
||||
import org.cryptomator.filesystem.crypto.CryptoFileSystemDelegate;
|
||||
import org.cryptomator.filesystem.crypto.CryptoFileSystemFactory;
|
||||
import org.cryptomator.filesystem.nio.NioFileSystem;
|
||||
import org.cryptomator.filesystem.shortening.ShorteningFileSystemFactory;
|
||||
import org.cryptomator.filesystem.stats.StatsFileSystem;
|
||||
import org.cryptomator.frontend.CommandFailedException;
|
||||
import org.cryptomator.frontend.Frontend;
|
||||
import org.cryptomator.frontend.Frontend.MountParam;
|
||||
import org.cryptomator.frontend.FrontendCreationFailedException;
|
||||
import org.cryptomator.frontend.FrontendFactory;
|
||||
import org.cryptomator.frontend.FrontendId;
|
||||
import org.cryptomator.ui.settings.Settings;
|
||||
import org.cryptomator.ui.util.DeferredClosable;
|
||||
import org.cryptomator.cryptofs.CryptoFileSystem;
|
||||
import org.cryptomator.cryptofs.CryptoFileSystemProperties;
|
||||
import org.cryptomator.cryptofs.CryptoFileSystemProvider;
|
||||
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
||||
import org.cryptomator.frontend.webdav.WebDavServer;
|
||||
import org.cryptomator.ui.model.VaultModule.PerVault;
|
||||
import org.cryptomator.ui.settings.VaultSettings;
|
||||
import org.cryptomator.ui.util.DeferredCloser;
|
||||
import org.cryptomator.ui.util.FXThreads;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Binding;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
public class Vault implements CryptoFileSystemDelegate {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CryptoFileSystemDelegate.class);
|
||||
@PerVault
|
||||
public class Vault {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Vault.class);
|
||||
public static final String VAULT_FILE_EXTENSION = ".cryptomator";
|
||||
|
||||
private final ObjectProperty<Path> path;
|
||||
private final ShorteningFileSystemFactory shorteningFileSystemFactory;
|
||||
private final CryptoFileSystemFactory cryptoFileSystemFactory;
|
||||
private final VaultSettings vaultSettings;
|
||||
private final WebDavServer server;
|
||||
private final DeferredCloser closer;
|
||||
|
||||
private final BooleanProperty unlocked = new SimpleBooleanProperty();
|
||||
private final BooleanProperty mounted = new SimpleBooleanProperty();
|
||||
private final ObservableList<String> namesOfResourcesWithInvalidMac = FXThreads.observableListOnMainThread(FXCollections.observableArrayList());
|
||||
private final Set<String> whitelistedResourcesWithInvalidMac = new HashSet<>();
|
||||
private final AtomicReference<FileSystem> nioFileSystem = new AtomicReference<>();
|
||||
private final String id;
|
||||
|
||||
private String mountName;
|
||||
private Character winDriveLetter;
|
||||
private Optional<StatsFileSystem> statsFileSystem = Optional.empty();
|
||||
private DeferredClosable<Frontend> filesystemFrontend = DeferredClosable.empty();
|
||||
private final AtomicReference<CryptoFileSystem> cryptoFileSystem = new AtomicReference<>();
|
||||
|
||||
/**
|
||||
* Package private constructor, use {@link VaultFactory}.
|
||||
*
|
||||
* @param string
|
||||
*/
|
||||
Vault(String id, Path vaultDirectoryPath, ShorteningFileSystemFactory shorteningFileSystemFactory, CryptoFileSystemFactory cryptoFileSystemFactory, DeferredCloser closer) {
|
||||
this.path = new SimpleObjectProperty<Path>(vaultDirectoryPath);
|
||||
this.shorteningFileSystemFactory = shorteningFileSystemFactory;
|
||||
this.cryptoFileSystemFactory = cryptoFileSystemFactory;
|
||||
@Inject
|
||||
Vault(VaultSettings vaultSettings, WebDavServer server, DeferredCloser closer) {
|
||||
this.vaultSettings = vaultSettings;
|
||||
this.server = server;
|
||||
this.closer = closer;
|
||||
this.id = id;
|
||||
try {
|
||||
setMountName(name().getValue());
|
||||
} catch (IllegalArgumentException e) {
|
||||
@@ -105,129 +70,92 @@ public class Vault implements CryptoFileSystemDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
private FileSystem getNioFileSystem() {
|
||||
return LazyInitializer.initializeLazily(nioFileSystem, () -> NioFileSystem.rootedAt(path.getValue()));
|
||||
}
|
||||
|
||||
// ******************************************************************************
|
||||
// Commands
|
||||
// ********************************************************************************/
|
||||
|
||||
private CryptoFileSystem getCryptoFileSystem(CharSequence passphrase) throws IOException {
|
||||
return LazyInitializer.initializeLazily(cryptoFileSystem, () -> createCryptoFileSystem(passphrase), IOException.class);
|
||||
}
|
||||
|
||||
private CryptoFileSystem createCryptoFileSystem(CharSequence passphrase) throws IOException {
|
||||
CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties().withPassphrase(passphrase).build();
|
||||
CryptoFileSystem fs = CryptoFileSystemProvider.newFileSystem(getPath(), fsProps);
|
||||
closer.closeLater(fs);
|
||||
return fs;
|
||||
}
|
||||
|
||||
public void create(CharSequence passphrase) throws IOException {
|
||||
try {
|
||||
FileSystem fs = getNioFileSystem();
|
||||
if (fs.files().map(File::name).filter(s -> s.endsWith(VAULT_FILE_EXTENSION)).count() > 0) {
|
||||
throw new FileAlreadyExistsException("masterkey.cryptomator", null, "Vault location not empty.");
|
||||
} else if (fs.folders().count() > 0) {
|
||||
throw new DirectoryNotEmptyException(fs.toString());
|
||||
}
|
||||
cryptoFileSystemFactory.initializeNew(fs, passphrase);
|
||||
} catch (UncheckedIOException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
getCryptoFileSystem(passphrase);
|
||||
// TODO and now?
|
||||
}
|
||||
|
||||
public void changePassphrase(CharSequence oldPassphrase, CharSequence newPassphrase) throws IOException, InvalidPassphraseException {
|
||||
try {
|
||||
cryptoFileSystemFactory.changePassphrase(getNioFileSystem(), oldPassphrase, newPassphrase);
|
||||
} catch (UncheckedIOException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
// TODO implement
|
||||
}
|
||||
|
||||
public synchronized void activateFrontend(FrontendFactory frontendFactory, Settings settings, CharSequence passphrase) throws FrontendCreationFailedException {
|
||||
public synchronized void activateFrontend(CharSequence passphrase) {
|
||||
boolean launchSuccess = false;
|
||||
boolean mountSuccess = false;
|
||||
try {
|
||||
FileSystem fs = getNioFileSystem();
|
||||
FileSystem shorteningFs = shorteningFileSystemFactory.get(fs);
|
||||
FileSystem cryptoFs = cryptoFileSystemFactory.unlockExisting(shorteningFs, passphrase, this);
|
||||
FileSystem normalizingFs = new NormalizedNameFileSystem(cryptoFs, SystemUtils.IS_OS_MAC_OSX ? Form.NFD : Form.NFC);
|
||||
StatsFileSystem statsFs = new StatsFileSystem(normalizingFs);
|
||||
statsFileSystem = Optional.of(statsFs);
|
||||
Frontend frontend = frontendFactory.create(statsFs, FrontendId.from(id), stripStart(mountName, "/"));
|
||||
launchSuccess = true;
|
||||
filesystemFrontend = closer.closeLater(frontend);
|
||||
frontend.mount(getMountParams(settings));
|
||||
mountSuccess = true;
|
||||
} catch (UncheckedIOException e) {
|
||||
throw new FrontendCreationFailedException(e);
|
||||
} catch (CommandFailedException e) {
|
||||
LOG.error("Failed to mount vault " + mountName, e);
|
||||
FileSystem fs = getCryptoFileSystem(passphrase);
|
||||
if (!server.isRunning()) {
|
||||
server.start();
|
||||
}
|
||||
server.startWebDavServlet(fs.getPath("/"), vaultSettings.getId());
|
||||
} catch (IOException e) {
|
||||
LOG.error("Unable to provide frontend", e);
|
||||
} finally {
|
||||
// unlocked is a observable property and should only be changed by the FX application thread
|
||||
boolean finalLaunchSuccess = launchSuccess;
|
||||
boolean finalMountSuccess = mountSuccess;
|
||||
Platform.runLater(() -> {
|
||||
unlocked.set(finalLaunchSuccess);
|
||||
mounted.set(finalMountSuccess);
|
||||
unlocked.set(launchSuccess);
|
||||
mounted.set(mountSuccess);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void deactivateFrontend() throws Exception {
|
||||
filesystemFrontend.close();
|
||||
statsFileSystem = Optional.empty();
|
||||
CryptoFileSystem fs = cryptoFileSystem.getAndSet(null);
|
||||
if (fs != null) {
|
||||
fs.close();
|
||||
}
|
||||
// TODO overheadhunter remove servlet from server
|
||||
Platform.runLater(() -> {
|
||||
mounted.set(false);
|
||||
unlocked.set(false);
|
||||
});
|
||||
}
|
||||
|
||||
private Map<MountParam, Optional<String>> getMountParams(Settings settings) {
|
||||
String hostname = SystemUtils.IS_OS_WINDOWS && settings.shouldUseIpv6() ? "0--1.ipv6-literal.net" : "localhost";
|
||||
return ImmutableMap.of( //
|
||||
MountParam.MOUNT_NAME, Optional.ofNullable(mountName), //
|
||||
MountParam.WIN_DRIVE_LETTER, Optional.ofNullable(CharUtils.toString(winDriveLetter)), //
|
||||
MountParam.HOSTNAME, Optional.of(hostname), //
|
||||
MountParam.PREFERRED_GVFS_SCHEME, Optional.ofNullable(settings.getPreferredGvfsScheme()) //
|
||||
);
|
||||
}
|
||||
|
||||
public synchronized void reveal() throws CommandFailedException {
|
||||
Optionals.ifPresent(filesystemFrontend.get(), Frontend::reveal);
|
||||
}
|
||||
|
||||
// ******************************************************************************
|
||||
// Delegate methods
|
||||
// ********************************************************************************/
|
||||
|
||||
@Override
|
||||
public void authenticationFailed(String cleartextPath) {
|
||||
namesOfResourcesWithInvalidMac.add(cleartextPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldSkipAuthentication(String cleartextPath) {
|
||||
return whitelistedResourcesWithInvalidMac.contains(cleartextPath);
|
||||
public synchronized void reveal() {
|
||||
// TODO implement
|
||||
}
|
||||
|
||||
// ******************************************************************************
|
||||
// Getter/Setter
|
||||
// *******************************************************************************/
|
||||
|
||||
public VaultSettings getVaultSettings() {
|
||||
return vaultSettings;
|
||||
}
|
||||
|
||||
public synchronized String getWebDavUrl() {
|
||||
return filesystemFrontend.get().map(Frontend::getWebDavUrl).orElseThrow(IllegalStateException::new);
|
||||
// TODO implement
|
||||
return "http://localhost/not/implemented";
|
||||
}
|
||||
|
||||
void setPath(Path path) {
|
||||
this.path.set(path);
|
||||
this.nioFileSystem.set(null);
|
||||
}
|
||||
|
||||
public ReadOnlyObjectProperty<Path> path() {
|
||||
return path;
|
||||
public Path getPath() {
|
||||
return vaultSettings.pathProperty().getValue();
|
||||
}
|
||||
|
||||
public Binding<String> displayablePath() {
|
||||
Path homeDir = Paths.get(SystemUtils.USER_HOME);
|
||||
return EasyBind.map(path, p -> {
|
||||
return EasyBind.map(vaultSettings.pathProperty(), p -> {
|
||||
if (p.startsWith(homeDir)) {
|
||||
Path relativePath = homeDir.relativize(p);
|
||||
String homePrefix = SystemUtils.IS_OS_WINDOWS ? "~\\" : "~/";
|
||||
return homePrefix + relativePath.toString();
|
||||
} else {
|
||||
return path.getValue().toString();
|
||||
return p.toString();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -236,16 +164,16 @@ public class Vault implements CryptoFileSystemDelegate {
|
||||
* @return Directory name without preceeding path components and file extension
|
||||
*/
|
||||
public Binding<String> name() {
|
||||
return EasyBind.map(path, p -> p.getFileName().toString());
|
||||
return EasyBind.map(vaultSettings.pathProperty(), p -> p.getFileName().toString());
|
||||
}
|
||||
|
||||
public boolean doesVaultDirectoryExist() {
|
||||
return Files.isDirectory(path.getValue());
|
||||
return Files.isDirectory(getPath());
|
||||
}
|
||||
|
||||
public boolean isValidVaultDirectory() {
|
||||
try {
|
||||
return doesVaultDirectoryExist() && cryptoFileSystemFactory.isValidVaultStructure(getNioFileSystem());
|
||||
return doesVaultDirectoryExist(); // TODO: && cryptoFileSystemFactory.isValidVaultStructure(getNioFileSystem());
|
||||
} catch (UncheckedIOException e) {
|
||||
return false;
|
||||
}
|
||||
@@ -268,19 +196,23 @@ public class Vault implements CryptoFileSystemDelegate {
|
||||
}
|
||||
|
||||
public ObservableList<String> getNamesOfResourcesWithInvalidMac() {
|
||||
return namesOfResourcesWithInvalidMac;
|
||||
// TODO overheadhunter implement.
|
||||
return null;
|
||||
}
|
||||
|
||||
public Set<String> getWhitelistedResourcesWithInvalidMac() {
|
||||
return whitelistedResourcesWithInvalidMac;
|
||||
// TODO overheadhunter implement.
|
||||
return null;
|
||||
}
|
||||
|
||||
public long pollBytesRead() {
|
||||
return statsFileSystem.map(StatsFileSystem::getThenResetBytesRead).orElse(0l);
|
||||
// TODO overheadhunter implement.
|
||||
return 0l;
|
||||
}
|
||||
|
||||
public long pollBytesWritten() {
|
||||
return statsFileSystem.map(StatsFileSystem::getThenResetBytesWritten).orElse(0l);
|
||||
// TODO overheadhunter implement.
|
||||
return 0l;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -310,7 +242,7 @@ public class Vault implements CryptoFileSystemDelegate {
|
||||
}
|
||||
|
||||
public String getMountName() {
|
||||
return mountName;
|
||||
return vaultSettings.getMountName();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -320,23 +252,32 @@ public class Vault implements CryptoFileSystemDelegate {
|
||||
* @throws IllegalArgumentException if the name is empty after normalization
|
||||
*/
|
||||
public void setMountName(String mountName) throws IllegalArgumentException {
|
||||
mountName = normalize(mountName);
|
||||
if (StringUtils.isEmpty(mountName)) {
|
||||
String normalized = normalize(mountName);
|
||||
if (StringUtils.isEmpty(normalized)) {
|
||||
throw new IllegalArgumentException("mount name is empty");
|
||||
} else {
|
||||
vaultSettings.setMountName(normalized);
|
||||
}
|
||||
this.mountName = mountName;
|
||||
}
|
||||
|
||||
public Character getWinDriveLetter() {
|
||||
return winDriveLetter;
|
||||
if (vaultSettings.getWinDriveLetter() == null) {
|
||||
return null;
|
||||
} else {
|
||||
return vaultSettings.getWinDriveLetter().charAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
public void setWinDriveLetter(Character winDriveLetter) {
|
||||
this.winDriveLetter = winDriveLetter;
|
||||
if (winDriveLetter == null) {
|
||||
vaultSettings.setWinDriveLetter(null);
|
||||
} else {
|
||||
vaultSettings.setWinDriveLetter(String.valueOf(winDriveLetter));
|
||||
}
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
return vaultSettings.getId();
|
||||
}
|
||||
|
||||
// ******************************************************************************
|
||||
@@ -345,14 +286,14 @@ public class Vault implements CryptoFileSystemDelegate {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return path.getValue().hashCode();
|
||||
return Objects.hash(vaultSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof Vault) {
|
||||
if (obj instanceof Vault && obj.getClass().equals(this.getClass())) {
|
||||
final Vault other = (Vault) obj;
|
||||
return this.path.getValue().equals(other.path.getValue());
|
||||
return Objects.equals(this.vaultSettings, other.vaultSettings);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.cryptomator.ui.model;
|
||||
|
||||
import org.cryptomator.ui.model.VaultModule.PerVault;
|
||||
|
||||
import dagger.Subcomponent;
|
||||
|
||||
@PerVault
|
||||
@Subcomponent(modules = {VaultModule.class})
|
||||
public interface VaultComponent {
|
||||
|
||||
Vault vault();
|
||||
|
||||
}
|
||||
@@ -8,36 +8,34 @@
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.ui.model;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.cryptomator.filesystem.crypto.CryptoFileSystemFactory;
|
||||
import org.cryptomator.filesystem.shortening.ShorteningFileSystemFactory;
|
||||
import org.cryptomator.frontend.FrontendId;
|
||||
import org.cryptomator.ui.util.DeferredCloser;
|
||||
import org.cryptomator.ui.CryptomatorComponent;
|
||||
import org.cryptomator.ui.settings.VaultSettings;
|
||||
|
||||
@Singleton
|
||||
public class VaultFactory {
|
||||
|
||||
private final ShorteningFileSystemFactory shorteningFileSystemFactory;
|
||||
private final CryptoFileSystemFactory cryptoFileSystemFactory;
|
||||
private final DeferredCloser closer;
|
||||
private final CryptomatorComponent cryptomatorComponent;
|
||||
private final ConcurrentMap<VaultSettings, Vault> vaults = new ConcurrentHashMap<>();
|
||||
|
||||
@Inject
|
||||
public VaultFactory(ShorteningFileSystemFactory shorteningFileSystemFactory, CryptoFileSystemFactory cryptoFileSystemFactory, DeferredCloser closer) {
|
||||
this.shorteningFileSystemFactory = shorteningFileSystemFactory;
|
||||
this.cryptoFileSystemFactory = cryptoFileSystemFactory;
|
||||
this.closer = closer;
|
||||
public VaultFactory(CryptomatorComponent cryptomatorComponent) {
|
||||
this.cryptomatorComponent = cryptomatorComponent;
|
||||
}
|
||||
|
||||
public Vault createVault(String id, Path path) {
|
||||
return new Vault(id, path, shorteningFileSystemFactory, cryptoFileSystemFactory, closer);
|
||||
public Vault get(VaultSettings vaultSettings) {
|
||||
return vaults.computeIfAbsent(vaultSettings, this::create);
|
||||
}
|
||||
|
||||
public Vault createVault(Path path) {
|
||||
return createVault(FrontendId.generate().toString(), path);
|
||||
private Vault create(VaultSettings vaultSettings) {
|
||||
VaultModule module = new VaultModule(vaultSettings);
|
||||
VaultComponent comp = cryptomatorComponent.newVaultComponent(module);
|
||||
return comp.vault();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
package org.cryptomator.ui.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.cryptomator.ui.settings.Settings;
|
||||
import org.cryptomator.ui.settings.VaultSettings;
|
||||
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener.Change;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.transformation.TransformationList;
|
||||
|
||||
@Singleton
|
||||
public class VaultList extends TransformationList<Vault, VaultSettings> {
|
||||
|
||||
private final VaultFactory vaultFactory;
|
||||
private final ObservableList<VaultSettings> source;
|
||||
|
||||
@Inject
|
||||
public VaultList(Settings settings, VaultFactory vaultFactory) {
|
||||
this(FXCollections.observableList(settings.getDirectories()), settings, vaultFactory);
|
||||
}
|
||||
|
||||
private VaultList(ObservableList<VaultSettings> source, Settings settings, VaultFactory vaultFactory) {
|
||||
super(source);
|
||||
this.source = source;
|
||||
this.vaultFactory = vaultFactory;
|
||||
addListener((Change<? extends Vault> change) -> settings.save());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSourceIndex(int index) {
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vault get(int index) {
|
||||
VaultSettings s = source.get(index);
|
||||
return vaultFactory.get(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, Vault element) {
|
||||
source.add(index, element.getVaultSettings());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vault remove(int index) {
|
||||
VaultSettings s = source.remove(index);
|
||||
return vaultFactory.get(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return getSource().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sourceChanged(Change<? extends VaultSettings> c) {
|
||||
this.fireChange(new VaultListChange(c));
|
||||
}
|
||||
|
||||
private class VaultListChange extends Change<Vault> {
|
||||
|
||||
private final Change<? extends VaultSettings> delegate;
|
||||
|
||||
public VaultListChange(Change<? extends VaultSettings> delegate) {
|
||||
super(VaultList.this);
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean next() {
|
||||
return delegate.next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
delegate.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFrom() {
|
||||
return delegate.getFrom();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTo() {
|
||||
return delegate.getTo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Vault> getRemoved() {
|
||||
List<Vault> removed = new ArrayList<>();
|
||||
for (VaultSettings s : delegate.getRemoved()) {
|
||||
removed.add(vaultFactory.get(s));
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int[] getPermutation() {
|
||||
if (delegate.wasPermutated()) {
|
||||
int len = getTo() - getFrom();
|
||||
int[] permutations = new int[len];
|
||||
for (int i = 0; i < len; i++) {
|
||||
permutations[i] = getPermutation(i);
|
||||
}
|
||||
return permutations;
|
||||
} else {
|
||||
return new int[0];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package org.cryptomator.ui.model;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.inject.Scope;
|
||||
|
||||
import org.cryptomator.ui.settings.VaultSettings;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
@Module
|
||||
public class VaultModule {
|
||||
|
||||
private final VaultSettings vaultSettings;
|
||||
|
||||
public VaultModule(VaultSettings vaultSettings) {
|
||||
this.vaultSettings = Objects.requireNonNull(vaultSettings);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@PerVault
|
||||
public VaultSettings provideVaultSettings() {
|
||||
return vaultSettings;
|
||||
}
|
||||
|
||||
@Scope
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface PerVault {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 Sebastian Stenzel and others.
|
||||
* This file is licensed under the terms of the MIT license.
|
||||
* See the LICENSE.txt file for more info.
|
||||
*
|
||||
* Contributors:
|
||||
* Sebastian Stenzel - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.ui.model;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.apache.commons.lang3.CharUtils;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
|
||||
@Singleton
|
||||
public class VaultObjectMapperProvider implements Provider<ObjectMapper> {
|
||||
|
||||
private final VaultFactory vaultFactoy;
|
||||
|
||||
@Inject
|
||||
public VaultObjectMapperProvider(final VaultFactory vaultFactoy) {
|
||||
this.vaultFactoy = vaultFactoy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectMapper get() {
|
||||
final ObjectMapper om = new ObjectMapper();
|
||||
final SimpleModule module = new SimpleModule("VaultJsonMapper");
|
||||
module.addSerializer(Vault.class, new VaultSerializer());
|
||||
module.addDeserializer(Vault.class, new VaultDeserializer());
|
||||
om.registerModule(module);
|
||||
return om;
|
||||
}
|
||||
|
||||
private static class VaultSerializer extends JsonSerializer<Vault> {
|
||||
|
||||
@Override
|
||||
public void serialize(Vault value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
|
||||
jgen.writeStartObject();
|
||||
jgen.writeStringField("path", value.path().getValue().toString());
|
||||
jgen.writeStringField("mountName", value.getMountName());
|
||||
jgen.writeStringField("id", value.getId());
|
||||
final Character winDriveLetter = value.getWinDriveLetter();
|
||||
if (winDriveLetter != null) {
|
||||
jgen.writeStringField("winDriveLetter", Character.toString(winDriveLetter));
|
||||
}
|
||||
jgen.writeEndObject();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class VaultDeserializer extends JsonDeserializer<Vault> {
|
||||
|
||||
@Override
|
||||
public Vault deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
|
||||
final JsonNode node = jp.readValueAsTree();
|
||||
if (node == null || !node.has("path")) {
|
||||
throw new InvalidFormatException("Node is null or doesn't contain a path.", node, Vault.class);
|
||||
}
|
||||
final String pathStr = node.get("path").asText();
|
||||
final Path path = FileSystems.getDefault().getPath(pathStr);
|
||||
final Vault vault;
|
||||
if (node.has("id")) {
|
||||
vault = vaultFactoy.createVault(node.get("id").asText(), path);
|
||||
} else {
|
||||
vault = vaultFactoy.createVault(path);
|
||||
}
|
||||
if (node.has("mountName")) {
|
||||
vault.setMountName(node.get("mountName").asText());
|
||||
}
|
||||
if (node.has("winDriveLetter")) {
|
||||
vault.setWinDriveLetter(CharUtils.toCharacterObject(node.get("winDriveLetter").asText()));
|
||||
}
|
||||
return vault;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
package org.cryptomator.ui.model;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.cryptomator.ui.settings.Settings;
|
||||
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ListChangeListener.Change;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
@Singleton
|
||||
public class Vaults implements ObservableList<Vault> {
|
||||
|
||||
private final ObservableList<Vault> delegate;
|
||||
|
||||
@Inject
|
||||
public Vaults(Settings settings) {
|
||||
this.delegate = FXCollections.observableList(settings.getDirectories());
|
||||
addListener((Change<? extends Vault> change) -> settings.save());
|
||||
}
|
||||
|
||||
public void addListener(ListChangeListener<? super Vault> listener) {
|
||||
delegate.addListener(listener);
|
||||
}
|
||||
|
||||
public void removeListener(ListChangeListener<? super Vault> listener) {
|
||||
delegate.removeListener(listener);
|
||||
}
|
||||
|
||||
public void addListener(InvalidationListener listener) {
|
||||
delegate.addListener(listener);
|
||||
}
|
||||
|
||||
public boolean addAll(Vault... elements) {
|
||||
return delegate.addAll(elements);
|
||||
}
|
||||
|
||||
public boolean setAll(Vault... elements) {
|
||||
return delegate.setAll(elements);
|
||||
}
|
||||
|
||||
public boolean setAll(Collection<? extends Vault> col) {
|
||||
return delegate.setAll(col);
|
||||
}
|
||||
|
||||
public boolean removeAll(Vault... elements) {
|
||||
return delegate.removeAll(elements);
|
||||
}
|
||||
|
||||
public void removeListener(InvalidationListener listener) {
|
||||
delegate.removeListener(listener);
|
||||
}
|
||||
|
||||
public boolean retainAll(Vault... elements) {
|
||||
return delegate.retainAll(elements);
|
||||
}
|
||||
|
||||
public void remove(int from, int to) {
|
||||
delegate.remove(from, to);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return delegate.size();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return delegate.isEmpty();
|
||||
}
|
||||
|
||||
public boolean contains(Object o) {
|
||||
return delegate.contains(o);
|
||||
}
|
||||
|
||||
public Iterator<Vault> iterator() {
|
||||
return delegate.iterator();
|
||||
}
|
||||
|
||||
public Object[] toArray() {
|
||||
return delegate.toArray();
|
||||
}
|
||||
|
||||
public <T> T[] toArray(T[] a) {
|
||||
return delegate.toArray(a);
|
||||
}
|
||||
|
||||
public boolean add(Vault e) {
|
||||
return delegate.add(e);
|
||||
}
|
||||
|
||||
public boolean remove(Object o) {
|
||||
return delegate.remove(o);
|
||||
}
|
||||
|
||||
public boolean containsAll(Collection<?> c) {
|
||||
return delegate.containsAll(c);
|
||||
}
|
||||
|
||||
public boolean addAll(Collection<? extends Vault> c) {
|
||||
return delegate.addAll(c);
|
||||
}
|
||||
|
||||
public boolean addAll(int index, Collection<? extends Vault> c) {
|
||||
return delegate.addAll(index, c);
|
||||
}
|
||||
|
||||
public boolean removeAll(Collection<?> c) {
|
||||
return delegate.removeAll(c);
|
||||
}
|
||||
|
||||
public boolean retainAll(Collection<?> c) {
|
||||
return delegate.retainAll(c);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
delegate.clear();
|
||||
}
|
||||
|
||||
public Vault get(int index) {
|
||||
return delegate.get(index);
|
||||
}
|
||||
|
||||
public Vault set(int index, Vault element) {
|
||||
return delegate.set(index, element);
|
||||
}
|
||||
|
||||
public void add(int index, Vault element) {
|
||||
delegate.add(index, element);
|
||||
}
|
||||
|
||||
public Vault remove(int index) {
|
||||
return delegate.remove(index);
|
||||
}
|
||||
|
||||
public int indexOf(Object o) {
|
||||
return delegate.indexOf(o);
|
||||
}
|
||||
|
||||
public int lastIndexOf(Object o) {
|
||||
return delegate.lastIndexOf(o);
|
||||
}
|
||||
|
||||
public ListIterator<Vault> listIterator() {
|
||||
return delegate.listIterator();
|
||||
}
|
||||
|
||||
public ListIterator<Vault> listIterator(int index) {
|
||||
return delegate.listIterator(index);
|
||||
}
|
||||
|
||||
public List<Vault> subList(int fromIndex, int toIndex) {
|
||||
return delegate.subList(fromIndex, toIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) return true;
|
||||
if (obj == null || getClass() != obj.getClass()) return false;
|
||||
return internalEquals((Vaults)obj);
|
||||
}
|
||||
|
||||
private boolean internalEquals(Vaults other) {
|
||||
return delegate.equals(other.delegate);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return delegate.hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package org.cryptomator.ui.model;
|
||||
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.apache.commons.lang3.CharUtils;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
|
||||
@Singleton
|
||||
public final class WindowsDriveLetters {
|
||||
|
||||
private static final Set<Character> A_TO_Z;
|
||||
|
||||
static {
|
||||
try (IntStream stream = IntStream.rangeClosed('A', 'Z')) {
|
||||
A_TO_Z = stream.mapToObj(i -> (char) i).collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
||||
@Inject
|
||||
public WindowsDriveLetters() {
|
||||
}
|
||||
|
||||
public Set<Character> getOccupiedDriveLetters() {
|
||||
if (!SystemUtils.IS_OS_WINDOWS) {
|
||||
throw new UnsupportedOperationException("This method is only defined for Windows file systems");
|
||||
}
|
||||
Iterable<Path> rootDirs = FileSystems.getDefault().getRootDirectories();
|
||||
return StreamSupport.stream(rootDirs.spliterator(), false).map(Path::toString).map(CharUtils::toChar).map(Character::toUpperCase).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public Set<Character> getAvailableDriveLetters() {
|
||||
Set<Character> occupiedDriveLetters = getOccupiedDriveLetters();
|
||||
Predicate<Character> isOccupiedDriveLetter = occupiedDriveLetters::contains;
|
||||
return A_TO_Z.stream().filter(isOccupiedDriveLetter.negate()).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -65,8 +65,8 @@ public class Localization extends ResourceBundle {
|
||||
}
|
||||
|
||||
// returns null if no resource for given path
|
||||
private ResourceBundle loadLocalizationFile(String resourcePath) throws IOException {
|
||||
try (InputStream in = getClass().getResourceAsStream(resourcePath)) {
|
||||
private static ResourceBundle loadLocalizationFile(String resourcePath) throws IOException {
|
||||
try (InputStream in = Localization.class.getResourceAsStream(resourcePath)) {
|
||||
if (in != null) {
|
||||
Reader reader = new InputStreamReader(in, StandardCharsets.UTF_8);
|
||||
return new PropertyResourceBundle(reader);
|
||||
|
||||
@@ -8,20 +8,15 @@
|
||||
******************************************************************************/
|
||||
package org.cryptomator.ui.settings;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.cryptomator.ui.model.Vault;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
public class Settings {
|
||||
|
||||
@JsonPropertyOrder(value = {"directories", "checkForUpdatesEnabled", "port", "useIpv6", "numTrayNotifications", "preferredGvfsScheme"})
|
||||
public class Settings implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 7609959894417878744L;
|
||||
public static final int MIN_PORT = 1024;
|
||||
public static final int MAX_PORT = 65535;
|
||||
public static final int DEFAULT_PORT = 42427;
|
||||
@@ -32,25 +27,32 @@ public class Settings implements Serializable {
|
||||
|
||||
private final Consumer<Settings> saveCmd;
|
||||
|
||||
@JsonProperty("directories")
|
||||
private List<Vault> directories;
|
||||
@Expose
|
||||
@SerializedName("directories")
|
||||
private List<VaultSettings> directories;
|
||||
|
||||
@JsonProperty("checkForUpdatesEnabled")
|
||||
@Expose
|
||||
@SerializedName("checkForUpdatesEnabled")
|
||||
private Boolean checkForUpdatesEnabled;
|
||||
|
||||
@JsonProperty("port")
|
||||
@Expose
|
||||
@SerializedName("port")
|
||||
private Integer port;
|
||||
|
||||
@JsonProperty("useIpv6")
|
||||
@Expose
|
||||
@SerializedName("useIpv6")
|
||||
private Boolean useIpv6;
|
||||
|
||||
@JsonProperty("numTrayNotifications")
|
||||
@Expose
|
||||
@SerializedName("numTrayNotifications")
|
||||
private Integer numTrayNotifications;
|
||||
|
||||
@JsonProperty("preferredGvfsScheme")
|
||||
@Expose
|
||||
@SerializedName("preferredGvfsScheme")
|
||||
private String preferredGvfsScheme;
|
||||
|
||||
@JsonProperty("debugMode")
|
||||
@Expose
|
||||
@SerializedName("debugMode")
|
||||
private Boolean debugMode;
|
||||
|
||||
/**
|
||||
@@ -61,19 +63,21 @@ public class Settings implements Serializable {
|
||||
}
|
||||
|
||||
public void save() {
|
||||
saveCmd.accept(this);
|
||||
if (saveCmd != null) {
|
||||
saveCmd.accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
/* Getter/Setter */
|
||||
|
||||
public List<Vault> getDirectories() {
|
||||
public List<VaultSettings> getDirectories() {
|
||||
if (directories == null) {
|
||||
directories = new ArrayList<>();
|
||||
}
|
||||
return directories;
|
||||
}
|
||||
|
||||
public void setDirectories(List<Vault> directories) {
|
||||
public void setDirectories(List<VaultSettings> directories) {
|
||||
this.directories = directories;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.cryptomator.ui.settings;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.google.gson.InstanceCreator;
|
||||
|
||||
class SettingsInstanceCreator implements InstanceCreator<Settings> {
|
||||
|
||||
private final Consumer<Settings> saveCmd;
|
||||
|
||||
public SettingsInstanceCreator(Consumer<Settings> saveCmd) {
|
||||
this.saveCmd = saveCmd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Settings createInstance(Type type) {
|
||||
return new Settings(saveCmd);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,7 +10,12 @@ package org.cryptomator.ui.settings;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
@@ -25,16 +30,17 @@ import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Provider;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.LazyInitializer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
@Singleton
|
||||
public class SettingsProvider implements Provider<Settings> {
|
||||
@@ -54,16 +60,21 @@ public class SettingsProvider implements Provider<Settings> {
|
||||
}
|
||||
}
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
private final ScheduledExecutorService saveScheduler = Executors.newSingleThreadScheduledExecutor();
|
||||
private final AtomicReference<ScheduledFuture<?>> scheduledSaveCmd = new AtomicReference<>();
|
||||
private final AtomicReference<Settings> settings = new AtomicReference<>();
|
||||
private final SettingsInstanceCreator settingsInstanceCreator = new SettingsInstanceCreator(this::scheduleSave);
|
||||
private final Gson gson;
|
||||
|
||||
@Inject
|
||||
public SettingsProvider(@Named("VaultJsonMapper") ObjectMapper objectMapper) {
|
||||
this.objectMapper = objectMapper;
|
||||
public SettingsProvider() {
|
||||
GsonBuilder gsonBuilder = new GsonBuilder() //
|
||||
.setPrettyPrinting().setLenient().disableHtmlEscaping().excludeFieldsWithoutExposeAnnotation();
|
||||
gsonBuilder.registerTypeAdapter(Settings.class, settingsInstanceCreator);
|
||||
this.gson = gsonBuilder.create();
|
||||
}
|
||||
|
||||
private Path getSettingsPath() throws IOException {
|
||||
private Path getSettingsPath() {
|
||||
final String settingsPathProperty = System.getProperty("cryptomator.settingsPath");
|
||||
return Optional.ofNullable(settingsPathProperty).filter(StringUtils::isNotBlank).map(this::replaceHomeDir).map(FileSystems.getDefault()::getPath).orElse(DEFAULT_SETTINGS_PATH);
|
||||
}
|
||||
@@ -78,14 +89,19 @@ public class SettingsProvider implements Provider<Settings> {
|
||||
|
||||
@Override
|
||||
public Settings get() {
|
||||
final Settings settings = new Settings(this::scheduleSave);
|
||||
try {
|
||||
final Path settingsPath = getSettingsPath();
|
||||
final InputStream in = Files.newInputStream(settingsPath, StandardOpenOption.READ);
|
||||
objectMapper.readerForUpdating(settings).readValue(in);
|
||||
return LazyInitializer.initializeLazily(settings, this::load);
|
||||
}
|
||||
|
||||
private Settings load() {
|
||||
Settings settings;
|
||||
final Path settingsPath = getSettingsPath();
|
||||
try (InputStream in = Files.newInputStream(settingsPath, StandardOpenOption.READ); //
|
||||
Reader reader = new InputStreamReader(in, StandardCharsets.UTF_8)) {
|
||||
settings = gson.fromJson(reader, Settings.class);
|
||||
LOG.info("Settings loaded from " + settingsPath);
|
||||
} catch (IOException e) {
|
||||
LOG.info("Failed to load settings, creating new one.");
|
||||
settings = settingsInstanceCreator.createInstance(Settings.class);
|
||||
}
|
||||
return settings;
|
||||
}
|
||||
@@ -105,12 +121,14 @@ public class SettingsProvider implements Provider<Settings> {
|
||||
|
||||
private void save(Settings settings) {
|
||||
Objects.requireNonNull(settings);
|
||||
final Path settingsPath = getSettingsPath();
|
||||
try {
|
||||
final Path settingsPath = getSettingsPath();
|
||||
Files.createDirectories(settingsPath.getParent());
|
||||
final OutputStream out = Files.newOutputStream(settingsPath, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE);
|
||||
objectMapper.writeValue(out, settings);
|
||||
LOG.info("Settings saved to " + settingsPath);
|
||||
try (OutputStream out = Files.newOutputStream(settingsPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); //
|
||||
Writer writer = new OutputStreamWriter(out, StandardCharsets.UTF_8)) {
|
||||
gson.toJson(settings, writer);
|
||||
LOG.info("Settings saved to " + settingsPath);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.error("Failed to save settings.", e);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
package org.cryptomator.ui.settings;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Base64;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.google.gson.annotations.JsonAdapter;
|
||||
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
|
||||
@JsonAdapter(VaultSettingsJsonAdapter.class)
|
||||
public class VaultSettings {
|
||||
|
||||
private final String id;
|
||||
private final Property<Path> path = new SimpleObjectProperty<>();
|
||||
private final Property<String> mountName = new SimpleStringProperty();
|
||||
private final Property<String> winDriveLetter = new SimpleStringProperty();
|
||||
|
||||
public VaultSettings(String id) {
|
||||
this.id = Objects.requireNonNull(id);
|
||||
}
|
||||
|
||||
public static VaultSettings withRandomId() {
|
||||
return new VaultSettings(generateId());
|
||||
}
|
||||
|
||||
private static String generateId() {
|
||||
return asBase64String(nineBytesFrom(UUID.randomUUID()));
|
||||
}
|
||||
|
||||
private static String asBase64String(byte[] bytes) {
|
||||
byte[] base64Bytes = Base64.getUrlEncoder().encode(bytes);
|
||||
return new String(base64Bytes, StandardCharsets.US_ASCII);
|
||||
}
|
||||
|
||||
private static byte[] nineBytesFrom(UUID uuid) {
|
||||
ByteBuffer uuidBuffer = ByteBuffer.allocate(9);
|
||||
uuidBuffer.putLong(uuid.getMostSignificantBits());
|
||||
uuidBuffer.put((byte) (uuid.getLeastSignificantBits() & 0xFF));
|
||||
uuidBuffer.flip();
|
||||
return uuidBuffer.array();
|
||||
}
|
||||
|
||||
/* Getter/Setter */
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Property<Path> pathProperty() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public Path getPath() {
|
||||
return path.getValue();
|
||||
}
|
||||
|
||||
public void setPath(Path path) {
|
||||
this.path.setValue(path);
|
||||
}
|
||||
|
||||
public Property<String> mountNameProperty() {
|
||||
return mountName;
|
||||
}
|
||||
|
||||
public String getMountName() {
|
||||
return mountName.getValue();
|
||||
}
|
||||
|
||||
public void setMountName(String mountName) {
|
||||
this.mountName.setValue(mountName);
|
||||
}
|
||||
|
||||
public Property<String> winDriveLetterProperty() {
|
||||
return mountName;
|
||||
}
|
||||
|
||||
public String getWinDriveLetter() {
|
||||
return winDriveLetter.getValue();
|
||||
}
|
||||
|
||||
public void setWinDriveLetter(String winDriveLetter) {
|
||||
this.winDriveLetter.setValue(winDriveLetter);
|
||||
}
|
||||
|
||||
/* Hashcode/Equals */
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof VaultSettings && obj.getClass().equals(this.getClass())) {
|
||||
VaultSettings other = (VaultSettings) obj;
|
||||
return Objects.equals(this.id, other.id);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package org.cryptomator.ui.settings;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
class VaultSettingsJsonAdapter extends TypeAdapter<VaultSettings> {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(VaultSettingsJsonAdapter.class);
|
||||
|
||||
@Override
|
||||
public void write(JsonWriter out, VaultSettings value) throws IOException {
|
||||
out.beginObject();
|
||||
out.name("id").value(value.getId());
|
||||
out.name("path").value(value.getPath().toString());
|
||||
out.name("mountName").value(value.getMountName());
|
||||
out.name("winDriveLetter").value(value.getWinDriveLetter());
|
||||
out.endObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaultSettings read(JsonReader in) throws IOException {
|
||||
String id = null;
|
||||
String path = null;
|
||||
String mountName = null;
|
||||
String winDriveLetter = null;
|
||||
|
||||
in.beginObject();
|
||||
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;
|
||||
default:
|
||||
LOG.warn("Unsupported vault setting found in JSON: " + name);
|
||||
in.skipValue();
|
||||
}
|
||||
}
|
||||
in.endObject();
|
||||
|
||||
VaultSettings settings = (id == null) ? VaultSettings.withRandomId() : new VaultSettings(id);
|
||||
settings.setPath(Paths.get(path));
|
||||
settings.setMountName(mountName);
|
||||
settings.setWinDriveLetter(winDriveLetter);
|
||||
return settings;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user