mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-17 10:11:27 +00:00
Merge branch 'master' into patches-1.0.x
# Conflicts: # main/commons-test/pom.xml # main/commons/pom.xml # main/filesystem-api/pom.xml # main/filesystem-crypto-integration-tests/pom.xml # main/filesystem-crypto/pom.xml # main/filesystem-inmemory/pom.xml # main/filesystem-invariants-tests/pom.xml # main/filesystem-nameshortening/pom.xml # main/filesystem-nio/pom.xml # main/filesystem-stats/pom.xml # main/frontend-api/pom.xml # main/frontend-webdav/pom.xml # main/pom.xml # main/uber-jar/pom.xml # main/ui/pom.xml
This commit is contained in:
@@ -27,7 +27,12 @@ public class CryptoEngineModule {
|
||||
@Provides
|
||||
public SecureRandom provideSecureRandom() {
|
||||
try {
|
||||
return SecureRandom.getInstanceStrong();
|
||||
// https://tersesystems.com/2015/12/17/the-right-way-to-use-securerandom/
|
||||
final SecureRandom nativeRandom = SecureRandom.getInstanceStrong();
|
||||
byte[] seed = nativeRandom.generateSeed(55); // NIST SP800-90A suggests 440 bits for SHA1 seed
|
||||
SecureRandom sha1Random = SecureRandom.getInstance("SHA1PRNG");
|
||||
sha1Random.setSeed(seed);
|
||||
return sha1Random;
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException("No strong PRNGs available.", e);
|
||||
}
|
||||
|
||||
@@ -29,5 +29,5 @@ interface CryptomatorComponent {
|
||||
|
||||
Localization localization();
|
||||
|
||||
TrayIconUtil trayIconUtil();
|
||||
ExitUtil exitUtil();
|
||||
}
|
||||
@@ -38,22 +38,41 @@ import javafx.application.Platform;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
@Singleton
|
||||
class TrayIconUtil {
|
||||
class ExitUtil {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(TrayIconUtil.class);
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ExitUtil.class);
|
||||
|
||||
private final Stage mainWindow;
|
||||
private final Localization localization;
|
||||
private final Settings settings;
|
||||
|
||||
@Inject
|
||||
public TrayIconUtil(@Named("mainWindow") Stage mainWindow, Localization localization, Settings settings) {
|
||||
public ExitUtil(@Named("mainWindow") Stage mainWindow, Localization localization, Settings settings) {
|
||||
this.mainWindow = mainWindow;
|
||||
this.localization = localization;
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
public void initTrayIcon(Runnable exitCommand) {
|
||||
public void initExitHandler(Runnable exitCommand) {
|
||||
if (SystemUtils.IS_OS_LINUX) {
|
||||
initMinimizeExitHandler(exitCommand);
|
||||
} else {
|
||||
initTrayIconExitHandler(exitCommand);
|
||||
}
|
||||
}
|
||||
|
||||
private void initMinimizeExitHandler(Runnable exitCommand) {
|
||||
mainWindow.setOnCloseRequest(e -> {
|
||||
if (Platform.isImplicitExit()) {
|
||||
exitCommand.run();
|
||||
} else {
|
||||
mainWindow.setIconified(true);
|
||||
e.consume();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void initTrayIconExitHandler(Runnable exitCommand) {
|
||||
final TrayIcon trayIcon = createTrayIcon(exitCommand);
|
||||
try {
|
||||
SystemTray.getSystemTray().add(trayIcon);
|
||||
@@ -67,7 +67,7 @@ public class MainApplication extends Application {
|
||||
// show window and start observing its focus:
|
||||
primaryStage.show();
|
||||
ActiveWindowStyleSupport.startObservingFocus(primaryStage);
|
||||
comp.trayIconUtil().initTrayIcon(this::quit);
|
||||
comp.exitUtil().initExitHandler(this::quit);
|
||||
|
||||
// open files, if requested during startup:
|
||||
for (String arg : getParameters().getUnnamed()) {
|
||||
|
||||
@@ -12,7 +12,6 @@ import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.URL;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
@@ -37,19 +36,18 @@ import javafx.scene.control.Hyperlink;
|
||||
import javafx.scene.text.Text;
|
||||
|
||||
@Singleton
|
||||
public class ChangePasswordController extends AbstractFXMLViewController {
|
||||
public class ChangePasswordController extends LocalizedFXMLViewController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ChangePasswordController.class);
|
||||
|
||||
private final Application app;
|
||||
private final Localization localization;
|
||||
final ObjectProperty<Vault> vault = new SimpleObjectProperty<>();
|
||||
private Optional<ChangePasswordListener> listener = Optional.empty();
|
||||
|
||||
@Inject
|
||||
public ChangePasswordController(Application app, Localization localization) {
|
||||
super(localization);
|
||||
this.app = app;
|
||||
this.localization = localization;
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -83,11 +81,6 @@ public class ChangePasswordController extends AbstractFXMLViewController {
|
||||
return getClass().getResource("/fxml/change_password.fxml");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ResourceBundle getFxmlResourceBundle() {
|
||||
return localization;
|
||||
}
|
||||
|
||||
// ****************************************
|
||||
// Downloads link
|
||||
// ****************************************
|
||||
@@ -143,13 +136,13 @@ public class ChangePasswordController extends AbstractFXMLViewController {
|
||||
|
||||
private void invokeListenerLater(ChangePasswordListener listener) {
|
||||
Platform.runLater(() -> {
|
||||
listener.didChangePassword(this);
|
||||
listener.didChangePassword();
|
||||
});
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface ChangePasswordListener {
|
||||
void didChangePassword(ChangePasswordController ctrl);
|
||||
void didChangePassword();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import java.io.UncheckedIOException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.FileAlreadyExistsException;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
@@ -34,17 +33,16 @@ import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
|
||||
@Singleton
|
||||
public class InitializeController extends AbstractFXMLViewController {
|
||||
public class InitializeController extends LocalizedFXMLViewController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(InitializeController.class);
|
||||
|
||||
private final Localization localization;
|
||||
final ObjectProperty<Vault> vault = new SimpleObjectProperty<>();
|
||||
private Optional<InitializationListener> listener = Optional.empty();
|
||||
|
||||
@Inject
|
||||
public InitializeController(Localization localization) {
|
||||
this.localization = localization;
|
||||
super(localization);
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -71,11 +69,6 @@ public class InitializeController extends AbstractFXMLViewController {
|
||||
return getClass().getResource("/fxml/initialize.fxml");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ResourceBundle getFxmlResourceBundle() {
|
||||
return localization;
|
||||
}
|
||||
|
||||
// ****************************************
|
||||
// OK button
|
||||
// ****************************************
|
||||
@@ -111,13 +104,13 @@ public class InitializeController extends AbstractFXMLViewController {
|
||||
|
||||
private void invokeListenerLater(InitializationListener listener) {
|
||||
Platform.runLater(() -> {
|
||||
listener.didInitialize(this);
|
||||
listener.didInitialize();
|
||||
});
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface InitializationListener {
|
||||
void didInitialize(InitializeController ctrl);
|
||||
void didInitialize();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.cryptomator.ui.controllers;
|
||||
|
||||
import org.cryptomator.ui.settings.Localization;
|
||||
|
||||
abstract class LocalizedFXMLViewController extends AbstractFXMLViewController {
|
||||
|
||||
protected final Localization localization;
|
||||
|
||||
public LocalizedFXMLViewController(Localization localization) {
|
||||
this.localization = localization;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Localization getFxmlResourceBundle() {
|
||||
return localization;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,7 +9,6 @@
|
||||
package org.cryptomator.ui.controllers;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -39,10 +38,9 @@ import javafx.scene.control.cell.CheckBoxListCell;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
public class MacWarningsController extends AbstractFXMLViewController {
|
||||
public class MacWarningsController extends LocalizedFXMLViewController {
|
||||
|
||||
private final Application application;
|
||||
private final Localization localization;
|
||||
private final ObservableList<Warning> warnings = FXCollections.observableArrayList();
|
||||
private final ListChangeListener<String> unauthenticatedResourcesChangeListener = this::unauthenticatedResourcesDidChange;
|
||||
private final ChangeListener<Boolean> stageVisibilityChangeListener = this::windowVisibilityDidChange;
|
||||
@@ -51,8 +49,8 @@ public class MacWarningsController extends AbstractFXMLViewController {
|
||||
|
||||
@Inject
|
||||
public MacWarningsController(Application application, Localization localization) {
|
||||
super(localization);
|
||||
this.application = application;
|
||||
this.localization = localization;
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -85,11 +83,6 @@ public class MacWarningsController extends AbstractFXMLViewController {
|
||||
return getClass().getResource("/fxml/mac_warnings.fxml");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ResourceBundle getFxmlResourceBundle() {
|
||||
return localization;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initStage(Stage stage) {
|
||||
super.initStage(stage);
|
||||
@@ -130,7 +123,7 @@ public class MacWarningsController extends AbstractFXMLViewController {
|
||||
|
||||
private void windowVisibilityDidChange(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
|
||||
if (Boolean.TRUE.equals(newValue)) {
|
||||
stage.setTitle(String.format(localization.getString("macWarnings.windowTitle"), vault.get().getName()));
|
||||
stage.setTitle(String.format(localization.getString("macWarnings.windowTitle"), vault.get().name().getValue()));
|
||||
warnings.addAll(vault.get().getNamesOfResourcesWithInvalidMac().stream().map(Warning::new).collect(Collectors.toList()));
|
||||
vault.get().getNamesOfResourcesWithInvalidMac().addListener(this.unauthenticatedResourcesChangeListener);
|
||||
} else {
|
||||
|
||||
@@ -16,7 +16,6 @@ import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
@@ -57,15 +56,16 @@ import javafx.stage.FileChooser;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
@Singleton
|
||||
public class MainController extends AbstractFXMLViewController {
|
||||
public class MainController extends LocalizedFXMLViewController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MainController.class);
|
||||
|
||||
private final Stage mainWindow;
|
||||
private final Localization localization;
|
||||
private final VaultFactory vaultFactoy;
|
||||
private final Lazy<WelcomeController> welcomeController;
|
||||
private final Lazy<InitializeController> initializeController;
|
||||
private final Lazy<NotFoundController> notFoundController;
|
||||
private final Lazy<UpgradeController> upgradeController;
|
||||
private final Lazy<UnlockController> unlockController;
|
||||
private final Provider<UnlockedController> unlockedControllerProvider;
|
||||
private final Lazy<ChangePasswordController> changePasswordController;
|
||||
@@ -73,20 +73,22 @@ public class MainController extends AbstractFXMLViewController {
|
||||
private final ObjectProperty<AbstractFXMLViewController> activeController = new SimpleObjectProperty<>();
|
||||
private final ObservableList<Vault> vaults;
|
||||
private final ObjectProperty<Vault> selectedVault = new SimpleObjectProperty<>();
|
||||
private final MonadicBinding<Boolean> isSelectedVaultUnlocked = EasyBind.select(selectedVault).selectObject(Vault::unlockedProperty);
|
||||
private final MonadicBinding<Boolean> isSelectedVaultUnlocked = EasyBind.select(selectedVault).selectObject(Vault::unlockedProperty);;
|
||||
private final Binding<Boolean> canEditSelectedVault = EasyBind.combine(selectedVault.isNull(), isSelectedVaultUnlocked.orElse(false), Boolean::logicalOr);
|
||||
private final BooleanBinding isShowingSettings;
|
||||
private final Map<Vault, UnlockedController> unlockedVaults = new HashMap<>();
|
||||
|
||||
@Inject
|
||||
public MainController(@Named("mainWindow") Stage mainWindow, Localization localization, Settings settings, VaultFactory vaultFactoy, Lazy<WelcomeController> welcomeController,
|
||||
Lazy<InitializeController> initializeController, Lazy<UnlockController> unlockController, Provider<UnlockedController> unlockedControllerProvider, Lazy<ChangePasswordController> changePasswordController,
|
||||
Lazy<SettingsController> settingsController) {
|
||||
Lazy<InitializeController> initializeController, Lazy<NotFoundController> notFoundController, Lazy<UpgradeController> upgradeController, Lazy<UnlockController> unlockController,
|
||||
Provider<UnlockedController> unlockedControllerProvider, Lazy<ChangePasswordController> changePasswordController, Lazy<SettingsController> settingsController) {
|
||||
super(localization);
|
||||
this.mainWindow = mainWindow;
|
||||
this.localization = localization;
|
||||
this.vaultFactoy = vaultFactoy;
|
||||
this.welcomeController = welcomeController;
|
||||
this.initializeController = initializeController;
|
||||
this.notFoundController = notFoundController;
|
||||
this.upgradeController = upgradeController;
|
||||
this.unlockController = unlockController;
|
||||
this.unlockedControllerProvider = unlockedControllerProvider;
|
||||
this.changePasswordController = changePasswordController;
|
||||
@@ -133,9 +135,8 @@ public class MainController extends AbstractFXMLViewController {
|
||||
removeVaultButton.disableProperty().bind(canEditSelectedVault);
|
||||
emptyListInstructions.visibleProperty().bind(Bindings.isEmpty(vaults));
|
||||
|
||||
EasyBind.subscribe(activeController, this::activeControllerDidChange);
|
||||
EasyBind.subscribe(selectedVault, this::selectedVaultDidChange);
|
||||
EasyBind.subscribe(isSelectedVaultUnlocked, this::selectedVaultUnlockedDidChange);
|
||||
EasyBind.subscribe(activeController, this::activeControllerDidChange);
|
||||
EasyBind.subscribe(isShowingSettings, settingsButton::setSelected);
|
||||
EasyBind.subscribe(addVaultContextMenu.showingProperty(), addVaultButton::setSelected);
|
||||
}
|
||||
@@ -145,11 +146,6 @@ public class MainController extends AbstractFXMLViewController {
|
||||
return getClass().getResource("/fxml/main.fxml");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ResourceBundle getFxmlResourceBundle() {
|
||||
return localization;
|
||||
}
|
||||
|
||||
private ListCell<Vault> createDirecoryListCell(ListView<Vault> param) {
|
||||
final DirectoryListCell cell = new DirectoryListCell();
|
||||
cell.setVaultContextMenu(vaultListCellContextMenu);
|
||||
@@ -172,19 +168,12 @@ public class MainController extends AbstractFXMLViewController {
|
||||
@FXML
|
||||
private void didClickCreateNewVault(ActionEvent event) {
|
||||
final FileChooser fileChooser = new FileChooser();
|
||||
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Cryptomator vault", "*" + Vault.VAULT_FILE_EXTENSION));
|
||||
final File file = fileChooser.showSaveDialog(mainWindow);
|
||||
if (file == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final Path vaultDir;
|
||||
// enforce .cryptomator file extension:
|
||||
if (!file.getName().endsWith(Vault.VAULT_FILE_EXTENSION)) {
|
||||
vaultDir = file.toPath().resolveSibling(file.getName() + Vault.VAULT_FILE_EXTENSION);
|
||||
} else {
|
||||
vaultDir = file.toPath();
|
||||
}
|
||||
final Path vaultDir = file.toPath();
|
||||
if (!Files.exists(vaultDir)) {
|
||||
Files.createDirectory(vaultDir);
|
||||
}
|
||||
@@ -272,6 +261,10 @@ public class MainController extends AbstractFXMLViewController {
|
||||
}
|
||||
if (newValue.isUnlocked()) {
|
||||
this.showUnlockedView(newValue);
|
||||
} else if (!newValue.doesVaultDirectoryExist()) {
|
||||
this.showNotFoundView();
|
||||
} else if (newValue.isValidVaultDirectory() && newValue.needsUpgrade()) {
|
||||
this.showUpgradeView();
|
||||
} else if (newValue.isValidVaultDirectory()) {
|
||||
this.showUnlockView();
|
||||
} else {
|
||||
@@ -279,29 +272,23 @@ public class MainController extends AbstractFXMLViewController {
|
||||
}
|
||||
}
|
||||
|
||||
private void selectedVaultUnlockedDidChange(Boolean unlocked) {
|
||||
if (unlocked == null) {
|
||||
// no vault selected -> no-op
|
||||
} else if (unlocked) {
|
||||
Platform.setImplicitExit(false);
|
||||
this.showUnlockedView(selectedVault.get());
|
||||
} else {
|
||||
this.showUnlockView();
|
||||
}
|
||||
}
|
||||
|
||||
// ****************************************
|
||||
// Public Bindings
|
||||
// ****************************************
|
||||
|
||||
public Binding<String> windowTitle() {
|
||||
return EasyBind.monadic(selectedVault).map(Vault::getName).orElse(localization.getString("app.name"));
|
||||
return EasyBind.monadic(selectedVault).flatMap(Vault::name).orElse(localization.getString("app.name"));
|
||||
}
|
||||
|
||||
// ****************************************
|
||||
// Subcontroller for right panel
|
||||
// ****************************************
|
||||
|
||||
private void showNotFoundView() {
|
||||
final NotFoundController ctrl = notFoundController.get();
|
||||
activeController.set(ctrl);
|
||||
}
|
||||
|
||||
private void showInitializeView() {
|
||||
final InitializeController ctrl = initializeController.get();
|
||||
ctrl.vault.bind(selectedVault);
|
||||
@@ -309,16 +296,35 @@ public class MainController extends AbstractFXMLViewController {
|
||||
activeController.set(ctrl);
|
||||
}
|
||||
|
||||
public void didInitialize(InitializeController ctrl) {
|
||||
public void didInitialize() {
|
||||
showUnlockView();
|
||||
}
|
||||
|
||||
private void showUpgradeView() {
|
||||
final UpgradeController ctrl = upgradeController.get();
|
||||
ctrl.vault.bind(selectedVault);
|
||||
ctrl.setListener(this::didUpgrade);
|
||||
activeController.set(ctrl);
|
||||
}
|
||||
|
||||
public void didUpgrade() {
|
||||
showUnlockView();
|
||||
}
|
||||
|
||||
private void showUnlockView() {
|
||||
final UnlockController ctrl = unlockController.get();
|
||||
ctrl.vault.bind(selectedVault);
|
||||
ctrl.setListener(this::didUnlock);
|
||||
activeController.set(ctrl);
|
||||
}
|
||||
|
||||
public void didUnlock(Vault vault) {
|
||||
Platform.setImplicitExit(false);
|
||||
if (vault.equals(selectedVault.getValue())) {
|
||||
this.showUnlockedView(vault);
|
||||
}
|
||||
}
|
||||
|
||||
private void showUnlockedView(Vault vault) {
|
||||
final UnlockedController ctrl = unlockedVaults.computeIfAbsent(vault, k -> {
|
||||
return unlockedControllerProvider.get();
|
||||
@@ -343,7 +349,7 @@ public class MainController extends AbstractFXMLViewController {
|
||||
activeController.set(ctrl);
|
||||
}
|
||||
|
||||
public void didChangePassword(ChangePasswordController ctrl) {
|
||||
public void didChangePassword() {
|
||||
showUnlockView();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.cryptomator.ui.controllers;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.cryptomator.ui.settings.Localization;
|
||||
|
||||
@Singleton
|
||||
public class NotFoundController extends LocalizedFXMLViewController {
|
||||
|
||||
@Inject
|
||||
public NotFoundController(Localization localization) {
|
||||
super(localization);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected URL getFxmlResourceUrl() {
|
||||
return getClass().getResource("/fxml/notfound.fxml");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,7 +10,6 @@ package org.cryptomator.ui.controllers;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
@@ -28,14 +27,13 @@ import javafx.scene.control.TextField;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
|
||||
@Singleton
|
||||
public class SettingsController extends AbstractFXMLViewController {
|
||||
public class SettingsController extends LocalizedFXMLViewController {
|
||||
|
||||
private final Localization localization;
|
||||
private final Settings settings;
|
||||
|
||||
@Inject
|
||||
public SettingsController(Localization localization, Settings settings) {
|
||||
this.localization = localization;
|
||||
super(localization);
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
@@ -44,7 +42,7 @@ public class SettingsController extends AbstractFXMLViewController {
|
||||
|
||||
@FXML
|
||||
private TextField portField;
|
||||
|
||||
|
||||
@FXML
|
||||
private CheckBox useIpv6Checkbox;
|
||||
|
||||
@@ -71,11 +69,6 @@ public class SettingsController extends AbstractFXMLViewController {
|
||||
return getClass().getResource("/fxml/settings.fxml");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ResourceBundle getFxmlResourceBundle() {
|
||||
return localization;
|
||||
}
|
||||
|
||||
private Optional<String> applicationVersion() {
|
||||
return Optional.ofNullable(getClass().getPackage().getImplementationVersion());
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ package org.cryptomator.ui.controllers;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Comparator;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -51,23 +51,23 @@ import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.text.Text;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
public class UnlockController extends AbstractFXMLViewController {
|
||||
public class UnlockController extends LocalizedFXMLViewController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(UnlockController.class);
|
||||
|
||||
private final Application app;
|
||||
private final Localization localization;
|
||||
private final ExecutorService exec;
|
||||
private final Lazy<FrontendFactory> frontendFactory;
|
||||
private final Settings settings;
|
||||
private final WindowsDriveLetters driveLetters;
|
||||
private final ChangeListener<Character> driveLetterChangeListener = this::winDriveLetterDidChange;
|
||||
final ObjectProperty<Vault> vault = new SimpleObjectProperty<>();
|
||||
private Optional<UnlockListener> listener = Optional.empty();
|
||||
|
||||
@Inject
|
||||
public UnlockController(Application app, Localization localization, ExecutorService exec, Lazy<FrontendFactory> frontendFactory, Settings settings, WindowsDriveLetters driveLetters) {
|
||||
super(localization);
|
||||
this.app = app;
|
||||
this.localization = localization;
|
||||
this.exec = exec;
|
||||
this.frontendFactory = frontendFactory;
|
||||
this.settings = settings;
|
||||
@@ -127,11 +127,6 @@ public class UnlockController extends AbstractFXMLViewController {
|
||||
return getClass().getResource("/fxml/unlock.fxml");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ResourceBundle getFxmlResourceBundle() {
|
||||
return localization;
|
||||
}
|
||||
|
||||
private void vaultChanged(Vault newVault) {
|
||||
if (newVault == null) {
|
||||
return;
|
||||
@@ -276,21 +271,24 @@ public class UnlockController extends AbstractFXMLViewController {
|
||||
progressIndicator.setVisible(true);
|
||||
downloadsPageLink.setVisible(false);
|
||||
CharSequence password = passwordField.getCharacters();
|
||||
exec.submit(() -> this.unlock(password));
|
||||
exec.submit(() -> this.unlock(vault.get(), password));
|
||||
|
||||
}
|
||||
|
||||
private void unlock(CharSequence password) {
|
||||
private void unlock(Vault vault, CharSequence password) {
|
||||
try {
|
||||
vault.get().activateFrontend(frontendFactory.get(), settings, password);
|
||||
vault.get().reveal();
|
||||
vault.activateFrontend(frontendFactory.get(), settings, password);
|
||||
vault.reveal();
|
||||
Platform.runLater(() -> {
|
||||
messageText.setText(null);
|
||||
listener.ifPresent(lstnr -> lstnr.didUnlock(vault));
|
||||
});
|
||||
} catch (InvalidPassphraseException e) {
|
||||
Platform.runLater(() -> {
|
||||
messageText.setText(localization.getString("unlock.errorMessage.wrongPassword"));
|
||||
passwordField.requestFocus();
|
||||
});
|
||||
} catch (UnsupportedVaultFormatException e) {
|
||||
LOG.warn("Unable to unlock vault: " + e.getMessage());
|
||||
Platform.runLater(() -> {
|
||||
downloadsPageLink.setVisible(true);
|
||||
if (e.isVaultOlderThanSoftware()) {
|
||||
@@ -314,4 +312,15 @@ public class UnlockController extends AbstractFXMLViewController {
|
||||
}
|
||||
}
|
||||
|
||||
/* callback */
|
||||
|
||||
public void setListener(UnlockListener listener) {
|
||||
this.listener = Optional.ofNullable(listener);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface UnlockListener {
|
||||
void didUnlock(Vault vault);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ package org.cryptomator.ui.controllers;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -46,12 +45,11 @@ import javafx.stage.PopupWindow.AnchorLocation;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.util.Duration;
|
||||
|
||||
public class UnlockedController extends AbstractFXMLViewController {
|
||||
public class UnlockedController extends LocalizedFXMLViewController {
|
||||
|
||||
private static final int IO_SAMPLING_STEPS = 100;
|
||||
private static final double IO_SAMPLING_INTERVAL = 0.25;
|
||||
|
||||
private final Localization localization;
|
||||
private final Stage macWarningsWindow = new Stage();
|
||||
private final MacWarningsController macWarningsController;
|
||||
private final ExecutorService exec;
|
||||
@@ -61,7 +59,7 @@ public class UnlockedController extends AbstractFXMLViewController {
|
||||
|
||||
@Inject
|
||||
public UnlockedController(Localization localization, Provider<MacWarningsController> macWarningsControllerProvider, ExecutorService exec) {
|
||||
this.localization = localization;
|
||||
super(localization);
|
||||
this.macWarningsController = macWarningsControllerProvider.get();
|
||||
this.exec = exec;
|
||||
|
||||
@@ -97,11 +95,6 @@ public class UnlockedController extends AbstractFXMLViewController {
|
||||
return getClass().getResource("/fxml/unlocked.fxml");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ResourceBundle getFxmlResourceBundle() {
|
||||
return localization;
|
||||
}
|
||||
|
||||
private void vaultChanged(Vault newVault) {
|
||||
if (newVault == null) {
|
||||
return;
|
||||
@@ -261,10 +254,6 @@ public class UnlockedController extends AbstractFXMLViewController {
|
||||
|
||||
/* callback */
|
||||
|
||||
public LockListener getListener() {
|
||||
return listener.orElse(null);
|
||||
}
|
||||
|
||||
public void setListener(LockListener listener) {
|
||||
this.listener = Optional.ofNullable(listener);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
package org.cryptomator.ui.controllers;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.cryptomator.ui.model.UpgradeInstruction;
|
||||
import org.cryptomator.ui.model.UpgradeInstruction.UpgradeFailedException;
|
||||
import org.cryptomator.ui.model.Vault;
|
||||
import org.cryptomator.ui.settings.Localization;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Binding;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ProgressIndicator;
|
||||
|
||||
public class UpgradeController extends LocalizedFXMLViewController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(UpgradeController.class);
|
||||
|
||||
final ObjectProperty<Vault> vault = new SimpleObjectProperty<>();
|
||||
private final ExecutorService exec;
|
||||
private final Binding<Optional<UpgradeInstruction>> upgradeInstruction = EasyBind.monadic(vault).map(Vault::availableUpgrade);
|
||||
private Optional<UpgradeListener> listener = Optional.empty();
|
||||
|
||||
@Inject
|
||||
public UpgradeController(Localization localization, ExecutorService exec) {
|
||||
super(localization);
|
||||
this.exec = exec;
|
||||
}
|
||||
|
||||
@FXML
|
||||
private Label upgradeLabel;
|
||||
|
||||
@FXML
|
||||
private Button upgradeButton;
|
||||
|
||||
@FXML
|
||||
private ProgressIndicator progressIndicator;
|
||||
|
||||
@FXML
|
||||
private Label errorLabel;
|
||||
|
||||
@Override
|
||||
protected void initialize() {
|
||||
upgradeLabel.textProperty().bind(EasyBind.monadic(upgradeInstruction).map(instruction -> {
|
||||
return instruction.map(this::upgradeNotification).orElse("");
|
||||
}).orElse(""));
|
||||
|
||||
EasyBind.subscribe(vault, this::vaultChanged);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected URL getFxmlResourceUrl() {
|
||||
return getClass().getResource("/fxml/upgrade.fxml");
|
||||
}
|
||||
|
||||
private void vaultChanged(Vault newVault) {
|
||||
errorLabel.setText(null);
|
||||
}
|
||||
|
||||
// ****************************************
|
||||
// Upgrade label
|
||||
// ****************************************
|
||||
|
||||
private String upgradeNotification(UpgradeInstruction instruction) {
|
||||
return instruction.getNotification(vault.get(), localization);
|
||||
}
|
||||
|
||||
// ****************************************
|
||||
// Upgrade button
|
||||
// ****************************************
|
||||
|
||||
@FXML
|
||||
private void didClickUpgradeButton(ActionEvent event) {
|
||||
upgradeInstruction.getValue().ifPresent(this::upgrade);
|
||||
}
|
||||
|
||||
private void upgrade(UpgradeInstruction instruction) {
|
||||
Vault v = vault.getValue();
|
||||
Objects.requireNonNull(v);
|
||||
progressIndicator.setVisible(true);
|
||||
upgradeButton.setDisable(true);
|
||||
exec.submit(() -> {
|
||||
if (!instruction.isApplicable(v)) {
|
||||
LOG.error("No upgrade needed for " + v.path().getValue());
|
||||
throw new IllegalStateException("No ugprade needed for " + v.path().getValue());
|
||||
}
|
||||
try {
|
||||
instruction.upgrade(v, localization);
|
||||
Platform.runLater(() -> {
|
||||
progressIndicator.setVisible(false);
|
||||
upgradeButton.setDisable(false);
|
||||
listener.ifPresent(UpgradeListener::didUpgrade);
|
||||
});
|
||||
} catch (UpgradeFailedException e) {
|
||||
Platform.runLater(() -> {
|
||||
errorLabel.setText(e.getLocalizedMessage());
|
||||
progressIndicator.setVisible(false);
|
||||
upgradeButton.setDisable(false);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* callback */
|
||||
|
||||
public void setListener(UpgradeListener listener) {
|
||||
this.listener = Optional.ofNullable(listener);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface UpgradeListener {
|
||||
void didUpgrade();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,7 +15,6 @@ import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -48,20 +47,19 @@ import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ProgressIndicator;
|
||||
|
||||
@Singleton
|
||||
public class WelcomeController extends AbstractFXMLViewController {
|
||||
public class WelcomeController extends LocalizedFXMLViewController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(WelcomeController.class);
|
||||
|
||||
private final Application app;
|
||||
private final Localization localization;
|
||||
private final Settings settings;
|
||||
private final Comparator<String> semVerComparator;
|
||||
private final ExecutorService executor;
|
||||
|
||||
@Inject
|
||||
public WelcomeController(Application app, Localization localization, Settings settings, @Named("SemVer") Comparator<String> semVerComparator, ExecutorService executor) {
|
||||
super(localization);
|
||||
this.app = app;
|
||||
this.localization = localization;
|
||||
this.settings = settings;
|
||||
this.semVerComparator = semVerComparator;
|
||||
this.executor = executor;
|
||||
@@ -93,11 +91,6 @@ public class WelcomeController extends AbstractFXMLViewController {
|
||||
return getClass().getResource("/fxml/welcome.fxml");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ResourceBundle getFxmlResourceBundle() {
|
||||
return localization;
|
||||
}
|
||||
|
||||
// ****************************************
|
||||
// Check for updates
|
||||
// ****************************************
|
||||
|
||||
@@ -9,9 +9,8 @@
|
||||
package org.cryptomator.ui.controls;
|
||||
|
||||
import org.cryptomator.ui.model.Vault;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.ContentDisplay;
|
||||
import javafx.scene.control.ContextMenu;
|
||||
@@ -21,15 +20,18 @@ import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.paint.Paint;
|
||||
import javafx.scene.shape.Circle;
|
||||
|
||||
public class DirectoryListCell extends DraggableListCell<Vault>implements ChangeListener<Boolean> {
|
||||
public class DirectoryListCell extends DraggableListCell<Vault> {
|
||||
|
||||
// fill: #FD4943, stroke: #E1443F
|
||||
private static final Color RED_FILL = Color.rgb(253, 73, 67);
|
||||
private static final Color RED_STROKE = Color.rgb(225, 68, 63);
|
||||
|
||||
// fill: #FFBF2F, stroke: #E4AC36
|
||||
// private static final Color YELLOW_FILL = Color.rgb(255, 191, 47);
|
||||
// private static final Color YELLOW_STROKE = Color.rgb(228, 172, 54);
|
||||
|
||||
// fill: #28CA40, stroke: #30B740
|
||||
private static final Color GREEN_FILL = Color.rgb(40, 202, 64);
|
||||
private static final Color GREEN_STROKE = Color.rgb(48, 183, 64);
|
||||
@@ -46,58 +48,38 @@ public class DirectoryListCell extends DraggableListCell<Vault>implements Change
|
||||
hbox.setPrefWidth(1);
|
||||
vbox.setFillWidth(true);
|
||||
|
||||
nameText.textProperty().bind(EasyBind.monadic(itemProperty()).flatMap(Vault::name));
|
||||
nameText.textFillProperty().bind(this.textFillProperty());
|
||||
nameText.fontProperty().bind(this.fontProperty());
|
||||
|
||||
pathText.textProperty().bind(EasyBind.monadic(itemProperty()).flatMap(Vault::displayablePath));
|
||||
pathText.setTextOverrun(OverrunStyle.ELLIPSIS);
|
||||
pathText.getStyleClass().add("detail-label");
|
||||
|
||||
statusIndicator.fillProperty().bind(EasyBind.monadic(itemProperty()).flatMap(Vault::unlockedProperty).map(unlocked -> {
|
||||
return unlocked ? GREEN_FILL : RED_FILL;
|
||||
}));
|
||||
|
||||
statusIndicator.strokeProperty().bind(EasyBind.monadic(itemProperty()).flatMap(Vault::unlockedProperty).map(unlocked -> {
|
||||
return unlocked ? GREEN_STROKE : RED_STROKE;
|
||||
}));
|
||||
|
||||
tooltipProperty().bind(EasyBind.monadic(itemProperty()).flatMap(Vault::path).map(p -> new Tooltip(p.toString())));
|
||||
contextMenuProperty().bind(EasyBind.monadic(itemProperty()).flatMap(Vault::unlockedProperty).map(unlocked -> {
|
||||
return unlocked ? null : vaultContextMenu;
|
||||
}));
|
||||
|
||||
setGraphic(hbox);
|
||||
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateItem(Vault item, boolean empty) {
|
||||
final Vault oldItem = super.getItem();
|
||||
if (oldItem != null) {
|
||||
oldItem.unlockedProperty().removeListener(this);
|
||||
}
|
||||
super.updateItem(item, empty);
|
||||
if (item == null) {
|
||||
nameText.setText(null);
|
||||
pathText.setText(null);
|
||||
setTooltip(null);
|
||||
setContextMenu(null);
|
||||
statusIndicator.setVisible(false);
|
||||
} else {
|
||||
nameText.setText(item.getName());
|
||||
pathText.setText(item.getDisplayablePath());
|
||||
setTooltip(new Tooltip(item.getPath().toString()));
|
||||
statusIndicator.setVisible(true);
|
||||
item.unlockedProperty().addListener(this);
|
||||
updateStatusIndicator();
|
||||
updateContextMenu();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
|
||||
updateStatusIndicator();
|
||||
updateContextMenu();
|
||||
}
|
||||
|
||||
private void updateStatusIndicator() {
|
||||
final Paint fillColor = getItem().isUnlocked() ? GREEN_FILL : RED_FILL;
|
||||
final Paint strokeColor = getItem().isUnlocked() ? GREEN_STROKE : RED_STROKE;
|
||||
statusIndicator.setFill(fillColor);
|
||||
statusIndicator.setStroke(strokeColor);
|
||||
}
|
||||
|
||||
private void updateContextMenu() {
|
||||
if (getItem().isUnlocked()) {
|
||||
this.setContextMenu(null);
|
||||
} else {
|
||||
this.setContextMenu(vaultContextMenu);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.cryptomator.ui.model;
|
||||
|
||||
import org.cryptomator.ui.settings.Localization;
|
||||
|
||||
public interface UpgradeInstruction {
|
||||
|
||||
static UpgradeInstruction[] AVAILABLE_INSTRUCTIONS = {new UpgradeVersion3DropBundleExtension()};
|
||||
|
||||
/**
|
||||
* @return Localized string to display to the user when an upgrade is needed.
|
||||
*/
|
||||
String getNotification(Vault vault, Localization localization);
|
||||
|
||||
/**
|
||||
* Upgrades a vault. Might take a moment, should be run in a background thread.
|
||||
*/
|
||||
void upgrade(Vault vault, Localization localization) throws UpgradeFailedException;
|
||||
|
||||
/**
|
||||
* Determines in O(1), if an upgrade can be applied to a vault.
|
||||
*
|
||||
* @return <code>true</code> if and only if the vault can be migrated to a newer version without the risk of data losses.
|
||||
*/
|
||||
boolean isApplicable(Vault vault);
|
||||
|
||||
/**
|
||||
* Thrown when data migration failed.
|
||||
*/
|
||||
public class UpgradeFailedException extends Exception {
|
||||
|
||||
UpgradeFailedException() {
|
||||
}
|
||||
|
||||
UpgradeFailedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package org.cryptomator.ui.model;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.cryptomator.ui.settings.Localization;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javafx.application.Platform;
|
||||
|
||||
class UpgradeVersion3DropBundleExtension implements UpgradeInstruction {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(UpgradeVersion3DropBundleExtension.class);
|
||||
|
||||
@Override
|
||||
public String getNotification(Vault vault, Localization localization) {
|
||||
String fmt = localization.getString("upgrade.version3dropBundleExtension.msg");
|
||||
Path path = vault.path().getValue();
|
||||
String oldVaultName = path.getFileName().toString();
|
||||
String newVaultName = StringUtils.removeEnd(oldVaultName, Vault.VAULT_FILE_EXTENSION);
|
||||
return String.format(fmt, oldVaultName, newVaultName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void upgrade(Vault vault, Localization localization) throws UpgradeFailedException {
|
||||
Path path = vault.path().getValue();
|
||||
String oldVaultName = path.getFileName().toString();
|
||||
String newVaultName = StringUtils.removeEnd(oldVaultName, Vault.VAULT_FILE_EXTENSION);
|
||||
Path newPath = path.resolveSibling(newVaultName);
|
||||
if (Files.exists(newPath)) {
|
||||
String fmt = localization.getString("upgrade.version3dropBundleExtension.err.alreadyExists");
|
||||
String msg = String.format(fmt, newPath);
|
||||
throw new UpgradeFailedException(msg);
|
||||
} else {
|
||||
try {
|
||||
Files.move(path, path.resolveSibling(newVaultName));
|
||||
Platform.runLater(() -> {
|
||||
vault.setPath(newPath);
|
||||
});
|
||||
} catch (IOException e) {
|
||||
LOG.error("Vault migration failed", e);
|
||||
throw new UpgradeFailedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(Vault vault) {
|
||||
return vault.path().getValue().getFileName().toString().endsWith(Vault.VAULT_FILE_EXTENSION);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,10 +11,12 @@ package org.cryptomator.ui.model;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.FileAlreadyExistsException;
|
||||
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.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -42,12 +44,17 @@ import org.cryptomator.ui.settings.Settings;
|
||||
import org.cryptomator.ui.util.DeferredClosable;
|
||||
import org.cryptomator.ui.util.DeferredCloser;
|
||||
import org.cryptomator.ui.util.FXThreads;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
|
||||
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;
|
||||
|
||||
@@ -55,7 +62,7 @@ public class Vault implements CryptoFileSystemDelegate {
|
||||
|
||||
public static final String VAULT_FILE_EXTENSION = ".cryptomator";
|
||||
|
||||
private final Path path;
|
||||
private final ObjectProperty<Path> path;
|
||||
private final DeferredCloser closer;
|
||||
private final ShorteningFileSystemFactory shorteningFileSystemFactory;
|
||||
private final CryptoFileSystemFactory cryptoFileSystemFactory;
|
||||
@@ -73,20 +80,20 @@ public class Vault implements CryptoFileSystemDelegate {
|
||||
* Package private constructor, use {@link VaultFactory}.
|
||||
*/
|
||||
Vault(Path vaultDirectoryPath, ShorteningFileSystemFactory shorteningFileSystemFactory, CryptoFileSystemFactory cryptoFileSystemFactory, DeferredCloser closer) {
|
||||
this.path = vaultDirectoryPath;
|
||||
this.path = new SimpleObjectProperty<Path>(vaultDirectoryPath);
|
||||
this.closer = closer;
|
||||
this.shorteningFileSystemFactory = shorteningFileSystemFactory;
|
||||
this.cryptoFileSystemFactory = cryptoFileSystemFactory;
|
||||
|
||||
try {
|
||||
setMountName(getName());
|
||||
setMountName(name().getValue());
|
||||
} catch (IllegalArgumentException e) {
|
||||
// mount name needs to be set by the user explicitly later
|
||||
}
|
||||
}
|
||||
|
||||
private FileSystem getNioFileSystem() {
|
||||
return LazyInitializer.initializeLazily(nioFileSystem, () -> NioFileSystem.rootedAt(path));
|
||||
return LazyInitializer.initializeLazily(nioFileSystem, () -> NioFileSystem.rootedAt(path.getValue()));
|
||||
}
|
||||
|
||||
// ******************************************************************************
|
||||
@@ -158,6 +165,16 @@ public class Vault implements CryptoFileSystemDelegate {
|
||||
Optionals.ifPresent(filesystemFrontend.get(), Frontend::unmount);
|
||||
}
|
||||
|
||||
public boolean needsUpgrade() {
|
||||
return availableUpgrade().isPresent();
|
||||
}
|
||||
|
||||
public Optional<UpgradeInstruction> availableUpgrade() {
|
||||
return Arrays.stream(UpgradeInstruction.AVAILABLE_INSTRUCTIONS).filter(instruction -> {
|
||||
return instruction.isApplicable(this);
|
||||
}).findAny();
|
||||
}
|
||||
|
||||
// ******************************************************************************
|
||||
// Delegate methods
|
||||
// ********************************************************************************/
|
||||
@@ -180,31 +197,42 @@ public class Vault implements CryptoFileSystemDelegate {
|
||||
return filesystemFrontend.get().map(Frontend::getWebDavUrl).orElseThrow(IllegalStateException::new);
|
||||
}
|
||||
|
||||
public Path getPath() {
|
||||
void setPath(Path path) {
|
||||
this.path.set(path);
|
||||
this.nioFileSystem.set(null);
|
||||
}
|
||||
|
||||
public ReadOnlyObjectProperty<Path> path() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public String getDisplayablePath() {
|
||||
public Binding<String> displayablePath() {
|
||||
Path homeDir = Paths.get(SystemUtils.USER_HOME);
|
||||
if (path.startsWith(homeDir)) {
|
||||
Path relativePath = homeDir.relativize(path);
|
||||
String homePrefix = SystemUtils.IS_OS_WINDOWS ? "~\\" : "~/";
|
||||
return homePrefix + relativePath.toString();
|
||||
} else {
|
||||
return path.toString();
|
||||
}
|
||||
return EasyBind.map(path, p -> {
|
||||
if (p.startsWith(homeDir)) {
|
||||
Path relativePath = homeDir.relativize(p);
|
||||
String homePrefix = SystemUtils.IS_OS_WINDOWS ? "~\\" : "~/";
|
||||
return homePrefix + relativePath.toString();
|
||||
} else {
|
||||
return path.toString();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Directory name without preceeding path components and file extension
|
||||
*/
|
||||
public String getName() {
|
||||
return StringUtils.removeEnd(path.getFileName().toString(), VAULT_FILE_EXTENSION);
|
||||
public Binding<String> name() {
|
||||
return EasyBind.map(path, p -> p.getFileName().toString());
|
||||
}
|
||||
|
||||
public boolean doesVaultDirectoryExist() {
|
||||
return Files.isDirectory(path.getValue());
|
||||
}
|
||||
|
||||
public boolean isValidVaultDirectory() {
|
||||
try {
|
||||
return cryptoFileSystemFactory.isValidVaultStructure(getNioFileSystem());
|
||||
return doesVaultDirectoryExist() && cryptoFileSystemFactory.isValidVaultStructure(getNioFileSystem());
|
||||
} catch (UncheckedIOException e) {
|
||||
return false;
|
||||
}
|
||||
@@ -292,17 +320,17 @@ public class Vault implements CryptoFileSystemDelegate {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return path.hashCode();
|
||||
return path.getValue().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof Vault) {
|
||||
final Vault other = (Vault) obj;
|
||||
return this.path.equals(other.path);
|
||||
return this.path.getValue().equals(other.path.getValue());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,7 @@ public class VaultObjectMapperProvider implements Provider<ObjectMapper> {
|
||||
@Override
|
||||
public void serialize(Vault value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
|
||||
jgen.writeStartObject();
|
||||
jgen.writeStringField("path", value.getPath().toString());
|
||||
jgen.writeStringField("path", value.path().getValue().toString());
|
||||
jgen.writeStringField("mountName", value.getMountName());
|
||||
final Character winDriveLetter = value.getWinDriveLetter();
|
||||
if (winDriveLetter != null) {
|
||||
|
||||
@@ -77,7 +77,6 @@ public class SettingsProvider implements Provider<Settings> {
|
||||
final Path settingsPath = getSettingsPath();
|
||||
final InputStream in = Files.newInputStream(settingsPath, StandardOpenOption.READ);
|
||||
settings = objectMapper.readValue(in, Settings.class);
|
||||
settings.getDirectories().removeIf(v -> !v.isValidVaultDirectory());
|
||||
} catch (IOException e) {
|
||||
LOG.warn("Failed to load settings, creating new one.");
|
||||
settings = new Settings();
|
||||
|
||||
20
main/ui/src/main/resources/fxml/notfound.fxml
Normal file
20
main/ui/src/main/resources/fxml/notfound.fxml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright (c) 2014 Sebastian Stenzel
|
||||
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
|
||||
-->
|
||||
<?import java.lang.String?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
|
||||
<VBox prefWidth="400.0" prefHeight="400.0" spacing="24.0" alignment="CENTER" xmlns:fx="http://javafx.com/fxml" cacheShape="true" cache="true">
|
||||
|
||||
<Label text="%notfound.label" textAlignment="CENTER" wrapText="true" cacheShape="true" cache="true"/>
|
||||
|
||||
</VBox>
|
||||
|
||||
|
||||
27
main/ui/src/main/resources/fxml/upgrade.fxml
Normal file
27
main/ui/src/main/resources/fxml/upgrade.fxml
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright (c) 2014 Sebastian Stenzel
|
||||
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
|
||||
-->
|
||||
<?import java.lang.String?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.ProgressIndicator?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
|
||||
<VBox prefWidth="400.0" prefHeight="400.0" spacing="24.0" alignment="CENTER" xmlns:fx="http://javafx.com/fxml" cacheShape="true" cache="true">
|
||||
|
||||
<Label fx:id="upgradeLabel" textAlignment="CENTER" wrapText="true"/>
|
||||
|
||||
<Button fx:id="upgradeButton" text="%upgrade.button" prefWidth="150.0" onAction="#didClickUpgradeButton" cacheShape="true" cache="true" />
|
||||
|
||||
<ProgressIndicator progress="-1" fx:id="progressIndicator" visible="false" cacheShape="true" cache="true" cacheHint="SPEED" />
|
||||
|
||||
<Label fx:id="errorLabel" textAlignment="CENTER" wrapText="true"/>
|
||||
</VBox>
|
||||
|
||||
|
||||
@@ -27,6 +27,15 @@ initialize.button.ok=Create vault
|
||||
initialize.messageLabel.alreadyInitialized=Vault already initialized
|
||||
initialize.messageLabel.initializationFailed=Could not initialize vault. See logfile for details.
|
||||
|
||||
# notfound.fxml
|
||||
notfound.label=Vault couldn't be found. Has it been moved?
|
||||
|
||||
# upgrade.fxml
|
||||
upgrade.button=Upgrade vault
|
||||
|
||||
upgrade.version3dropBundleExtension.msg=This vault needs to be migrated to a newer format.\n"%1$s" will be renamed to "%2$s".\nPlease make sure synchronization has finished before proceeding.
|
||||
upgrade.version3dropBundleExtension.err.alreadyExists=Automatic migration failed.\n"%s" already exists.
|
||||
|
||||
# unlock.fxml
|
||||
unlock.label.password=Password
|
||||
unlock.label.mountName=Drive name
|
||||
|
||||
@@ -27,6 +27,15 @@ initialize.button.ok=Tresor erstellen
|
||||
initialize.messageLabel.alreadyInitialized=Tresor bereits vorhanden
|
||||
initialize.messageLabel.initializationFailed=Fehler beim Initialisieren. Details in der Log-Datei.
|
||||
|
||||
# notfound.fxml
|
||||
notfound.label=Tresor konnte nicht gefunden werden.\nWurde er verschoben?
|
||||
|
||||
# upgrade.fxml
|
||||
upgrade.button=Tresor aktualisieren
|
||||
|
||||
upgrade.version3dropBundleExtension.msg=Dieser Tresor muss auf ein neueres Format aktualisiert werden.\n"%1$s" wird in "%2$s" umbenannt.\nStellen Sie bitte sicher, dass derzeit keine Synchronisation stattfindet.
|
||||
upgrade.version3dropBundleExtension.err.alreadyExists=Migration fehlgeschlagen.\n"%s" existiert bereits.
|
||||
|
||||
# unlock.fxml
|
||||
unlock.label.password=Passwort
|
||||
unlock.label.mountName=Laufwerksname
|
||||
|
||||
Reference in New Issue
Block a user