Merge branch 'release/1.2.0'

# Conflicts:
#	main/ant-kit/pom.xml
#	main/commons-test/pom.xml
#	main/commons/pom.xml
#	main/filesystem-api/pom.xml
#	main/filesystem-charsets/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/jacoco-report/pom.xml
#	main/pom.xml
#	main/uber-jar/pom.xml
#	main/ui/pom.xml
This commit is contained in:
Sebastian Stenzel
2016-09-19 15:10:43 +02:00
120 changed files with 1890 additions and 643 deletions
@@ -8,10 +8,12 @@
*******************************************************************************/
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;
@@ -34,4 +36,7 @@ interface CryptomatorComponent {
Localization localization();
ExitUtil exitUtil();
}
Optional<MacFunctions> nativeMacFunctions();
}
@@ -8,6 +8,8 @@
*******************************************************************************/
package org.cryptomator.ui;
import static java.util.stream.Collectors.toList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -16,10 +18,16 @@ 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;
@@ -31,9 +39,10 @@ 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})
@Module(includes = {CryptoEngineModule.class, CommonsModule.class, WebDavModule.class, KeychainModule.class, JniModule.class, CryptoLibModule.class})
class CryptomatorModule {
private static final Logger LOG = LoggerFactory.getLogger(CryptomatorModule.class);
@@ -93,10 +102,17 @@ class CryptomatorModule {
@Provides
@Singleton
FrontendFactory provideFrontendFactory(DeferredCloser closer, WebDavServer webDavServer, Settings settings) {
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()));
}
}
@@ -21,6 +21,7 @@ import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
@@ -32,6 +33,9 @@ import javax.script.ScriptException;
import javax.swing.SwingUtilities;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.jni.JniException;
import org.cryptomator.jni.MacApplicationUiState;
import org.cryptomator.jni.MacFunctions;
import org.cryptomator.ui.settings.Localization;
import org.cryptomator.ui.settings.Settings;
import org.slf4j.Logger;
@@ -48,12 +52,14 @@ class ExitUtil {
private final Stage mainWindow;
private final Localization localization;
private final Settings settings;
private final Optional<MacFunctions> macFunctions;
@Inject
public ExitUtil(@Named("mainWindow") Stage mainWindow, Localization localization, Settings settings) {
public ExitUtil(@Named("mainWindow") Stage mainWindow, Localization localization, Settings settings, Optional<MacFunctions> macFunctions) {
this.mainWindow = mainWindow;
this.localization = localization;
this.settings = settings;
this.macFunctions = macFunctions;
}
public void initExitHandler(Runnable exitCommand) {
@@ -88,6 +94,7 @@ class ExitUtil {
if (Platform.isImplicitExit()) {
exitCommand.run();
} else {
macFunctions.map(MacFunctions::uiState).ifPresent(JniException.ignore(MacApplicationUiState::transformToAgentApplication));
mainWindow.close();
this.showTrayNotification(trayIcon);
}
@@ -189,6 +196,7 @@ class ExitUtil {
private void restoreFromTray(ActionEvent event) {
Platform.runLater(() -> {
macFunctions.map(MacFunctions::uiState).ifPresent(JniException.ignore(MacApplicationUiState::transformToForegroundApplication));
mainWindow.show();
mainWindow.requestFocus();
});
@@ -12,9 +12,12 @@ import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.concurrent.ExecutionException;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.cryptolib.common.SecureRandomModule;
import org.cryptomator.ui.controllers.MainController;
import org.cryptomator.ui.util.ActiveWindowStyleSupport;
import org.cryptomator.ui.util.DeferredCloser;
@@ -40,7 +43,15 @@ public class MainApplication extends Application {
@Override
public void start(Stage primaryStage) throws IOException {
LOG.info("JavaFX application started");
final CryptomatorComponent comp = DaggerCryptomatorComponent.builder().cryptomatorModule(new CryptomatorModule(this, primaryStage)).build();
final CryptomatorComponent comp;
try {
comp = DaggerCryptomatorComponent.builder() //
.cryptomatorModule(new CryptomatorModule(this, primaryStage)) //
.secureRandomModule(new SecureRandomModule(SecureRandom.getInstanceStrong())) //
.build();
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("Every implementation of the Java platform is required to support at least one strong SecureRandom implementation.", e);
}
final MainController mainCtrl = comp.mainController();
closer = comp.deferredCloser();
@@ -12,6 +12,7 @@ package org.cryptomator.ui.controllers;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URL;
import java.util.Objects;
import java.util.Optional;
import javax.inject.Inject;
@@ -31,9 +32,7 @@ import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
@@ -49,9 +48,9 @@ public class ChangePasswordController extends LocalizedFXMLViewController {
private final Application app;
private final PasswordStrengthUtil strengthRater;
final ObjectProperty<Vault> vault = new SimpleObjectProperty<>();
private Optional<ChangePasswordListener> listener = Optional.empty();
private final IntegerProperty passwordStrength = new SimpleIntegerProperty(); // 0-4
private Optional<ChangePasswordListener> listener = Optional.empty();
private Vault vault;
@Inject
public ChangePasswordController(Application app, PasswordStrengthUtil strengthRater, Localization localization) {
@@ -101,7 +100,6 @@ public class ChangePasswordController extends LocalizedFXMLViewController {
BooleanBinding oldPasswordIsEmpty = oldPasswordField.textProperty().isEmpty();
BooleanBinding newPasswordIsEmpty = newPasswordField.textProperty().isEmpty();
BooleanBinding passwordsDiffer = newPasswordField.textProperty().isNotEqualTo(retypePasswordField.textProperty());
EasyBind.subscribe(vault, this::vaultDidChange);
changePasswordButton.disableProperty().bind(oldPasswordIsEmpty.or(newPasswordIsEmpty.or(passwordsDiffer)));
passwordStrength.bind(EasyBind.map(newPasswordField.textProperty(), strengthRater::computeRate));
@@ -118,13 +116,15 @@ public class ChangePasswordController extends LocalizedFXMLViewController {
return getClass().getResource("/fxml/change_password.fxml");
}
private void vaultDidChange(Vault newVault) {
oldPasswordField.clear();
newPasswordField.clear();
retypePasswordField.clear();
void setVault(Vault vault) {
this.vault = Objects.requireNonNull(vault);
oldPasswordField.swipe();
newPasswordField.swipe();
retypePasswordField.swipe();
// trigger "default" change to refresh key bindings:
changePasswordButton.setDefaultButton(false);
changePasswordButton.setDefaultButton(true);
messageText.setText(null);
}
// ****************************************
@@ -144,8 +144,8 @@ public class ChangePasswordController extends LocalizedFXMLViewController {
private void didClickChangePasswordButton(ActionEvent event) {
downloadsPageLink.setVisible(false);
try {
vault.get().changePassphrase(oldPasswordField.getCharacters(), newPasswordField.getCharacters());
messageText.setText(localization.getString("changePassword.infoMessage.success"));
vault.changePassphrase(oldPasswordField.getCharacters(), newPasswordField.getCharacters());
messageText.setText(null);
listener.ifPresent(this::invokeListenerLater);
} catch (InvalidPassphraseException e) {
messageText.setText(localization.getString("changePassword.errorMessage.wrongPassword"));
@@ -10,9 +10,10 @@
package org.cryptomator.ui.controllers;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URL;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.FileAlreadyExistsException;
import java.util.Objects;
import java.util.Optional;
import javax.inject.Inject;
@@ -29,9 +30,7 @@ import org.slf4j.LoggerFactory;
import javafx.application.Platform;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
@@ -44,9 +43,9 @@ public class InitializeController extends LocalizedFXMLViewController {
private static final Logger LOG = LoggerFactory.getLogger(InitializeController.class);
private final PasswordStrengthUtil strengthRater;
final ObjectProperty<Vault> vault = new SimpleObjectProperty<>();
private Optional<InitializationListener> listener = Optional.empty();
private final IntegerProperty passwordStrength = new SimpleIntegerProperty(); // 0-4
private Optional<InitializationListener> listener = Optional.empty();
private Vault vault;
@Inject
public InitializeController(Localization localization, PasswordStrengthUtil strengthRater) {
@@ -88,7 +87,6 @@ public class InitializeController extends LocalizedFXMLViewController {
public void initialize() {
BooleanBinding passwordIsEmpty = passwordField.textProperty().isEmpty();
BooleanBinding passwordsDiffer = passwordField.textProperty().isNotEqualTo(retypePasswordField.textProperty());
EasyBind.subscribe(vault, this::vaultDidChange);
okButton.disableProperty().bind(passwordIsEmpty.or(passwordsDiffer));
passwordStrength.bind(EasyBind.map(passwordField.textProperty(), strengthRater::computeRate));
@@ -105,9 +103,10 @@ public class InitializeController extends LocalizedFXMLViewController {
return getClass().getResource("/fxml/initialize.fxml");
}
private void vaultDidChange(Vault newVault) {
passwordField.clear();
retypePasswordField.clear();
void setVault(Vault vault) {
this.vault = Objects.requireNonNull(vault);
passwordField.swipe();
retypePasswordField.swipe();
// trigger "default" change to refresh key bindings:
okButton.setDefaultButton(false);
okButton.setDefaultButton(true);
@@ -121,11 +120,13 @@ public class InitializeController extends LocalizedFXMLViewController {
protected void initializeVault(ActionEvent event) {
final CharSequence passphrase = passwordField.getCharacters();
try {
vault.get().create(passphrase);
vault.create(passphrase);
listener.ifPresent(this::invokeListenerLater);
} catch (FileAlreadyExistsException ex) {
messageLabel.setText(localization.getString("initialize.messageLabel.alreadyInitialized"));
} catch (UncheckedIOException | IOException ex) {
} catch (DirectoryNotEmptyException ex) {
messageLabel.setText(localization.getString("initialize.messageLabel.notEmpty"));
} catch (IOException ex) {
LOG.error("I/O Exception", ex);
messageLabel.setText(localization.getString("initialize.messageLabel.initializationFailed"));
} finally {
@@ -29,6 +29,7 @@ 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.settings.Localization;
import org.cryptomator.ui.settings.Settings;
import org.cryptomator.ui.util.DialogBuilderUtil;
@@ -44,9 +45,6 @@ import javafx.beans.binding.BooleanBinding;
import javafx.beans.binding.BooleanExpression;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.geometry.Side;
@@ -81,7 +79,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 ObservableList<Vault> vaults;
private final Vaults 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));
@@ -92,7 +90,8 @@ public class MainController extends LocalizedFXMLViewController {
@Inject
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) {
Provider<UnlockedController> unlockedControllerProvider, Lazy<ChangePasswordController> changePasswordController, Lazy<SettingsController> settingsController, Lazy<UpgradeStrategies> upgradeStrategies,
Vaults vaults) {
super(localization);
this.mainWindow = mainWindow;
this.vaultFactoy = vaultFactoy;
@@ -105,10 +104,7 @@ public class MainController extends LocalizedFXMLViewController {
this.changePasswordController = changePasswordController;
this.settingsController = settingsController;
this.upgradeStrategies = upgradeStrategies;
this.vaults = FXCollections.observableList(settings.getDirectories());
this.vaults.addListener((Change<? extends Vault> c) -> {
settings.save();
});
this.vaults = vaults;
// derived bindings:
this.isShowingSettings = activeController.isEqualTo(settingsController.get());
@@ -319,7 +315,8 @@ public class MainController extends LocalizedFXMLViewController {
private void showInitializeView() {
final InitializeController ctrl = initializeController.get();
ctrl.vault.bind(selectedVault);
ctrl.loadFxml();
ctrl.setVault(selectedVault.get());
ctrl.setListener(this::didInitialize);
activeController.set(ctrl);
}
@@ -330,7 +327,8 @@ public class MainController extends LocalizedFXMLViewController {
private void showUpgradeView() {
final UpgradeController ctrl = upgradeController.get();
ctrl.vault.bind(selectedVault);
ctrl.loadFxml();
ctrl.setVault(selectedVault.get());
ctrl.setListener(this::didUpgrade);
activeController.set(ctrl);
}
@@ -341,7 +339,8 @@ public class MainController extends LocalizedFXMLViewController {
private void showUnlockView() {
final UnlockController ctrl = unlockController.get();
ctrl.vault.bind(selectedVault);
ctrl.loadFxml();
ctrl.setVault(selectedVault.get());
ctrl.setListener(this::didUnlock);
activeController.set(ctrl);
}
@@ -357,6 +356,7 @@ public class MainController extends LocalizedFXMLViewController {
final UnlockedController ctrl = unlockedVaults.computeIfAbsent(vault, k -> {
return unlockedControllerProvider.get();
});
ctrl.loadFxml();
ctrl.setVault(vault);
ctrl.setListener(this::didLock);
activeController.set(ctrl);
@@ -372,7 +372,8 @@ public class MainController extends LocalizedFXMLViewController {
private void showChangePasswordView() {
final ChangePasswordController ctrl = changePasswordController.get();
ctrl.vault.bind(selectedVault);
ctrl.loadFxml();
ctrl.setVault(selectedVault.get());
ctrl.setListener(this::didChangePassword);
activeController.set(ctrl);
}
@@ -9,7 +9,9 @@
package org.cryptomator.ui.controllers;
import java.net.URL;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Objects;
import java.util.Optional;
import javax.inject.Inject;
@@ -22,25 +24,24 @@ 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.keychain.KeychainAccess;
import org.cryptomator.ui.controls.SecPasswordField;
import org.cryptomator.ui.model.Vault;
import org.cryptomator.ui.settings.Localization;
import org.cryptomator.ui.settings.Settings;
import org.cryptomator.ui.util.AsyncTaskService;
import org.fxmisc.easybind.EasyBind;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import dagger.Lazy;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.Hyperlink;
import javafx.scene.control.Label;
@@ -61,22 +62,34 @@ public class UnlockController extends LocalizedFXMLViewController {
private final Settings settings;
private final WindowsDriveLetters driveLetters;
private final ChangeListener<Character> driveLetterChangeListener = this::winDriveLetterDidChange;
final ObjectProperty<Vault> vault = new SimpleObjectProperty<>();
private final Optional<KeychainAccess> keychainAccess;
private Vault vault;
private Optional<UnlockListener> listener = Optional.empty();
@Inject
public UnlockController(Application app, Localization localization, AsyncTaskService asyncTaskService, Lazy<FrontendFactory> frontendFactory, Settings settings, WindowsDriveLetters driveLetters) {
public UnlockController(Application app, Localization localization, AsyncTaskService asyncTaskService, Lazy<FrontendFactory> frontendFactory, 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;
}
@FXML
private SecPasswordField passwordField;
@FXML
private Button advancedOptionsButton;
@FXML
private Button unlockButton;
@FXML
private CheckBox savePassword;
@FXML
private TextField mountName;
@@ -86,12 +99,6 @@ public class UnlockController extends LocalizedFXMLViewController {
@FXML
private ChoiceBox<Character> winDriveLetter;
@FXML
private Button advancedOptionsButton;
@FXML
private Button unlockButton;
@FXML
private ProgressIndicator progressIndicator;
@@ -107,8 +114,10 @@ public class UnlockController extends LocalizedFXMLViewController {
@Override
public void initialize() {
advancedOptions.managedProperty().bind(advancedOptions.visibleProperty());
unlockButton.disableProperty().bind(passwordField.textProperty().isEmpty());
mountName.addEventFilter(KeyEvent.KEY_TYPED, this::filterAlphanumericKeyEvents);
mountName.textProperty().addListener(this::mountNameDidChange);
savePassword.setDisable(!keychainAccess.isPresent());
if (SystemUtils.IS_OS_WINDOWS) {
winDriveLetter.setConverter(new WinDriveLetterLabelConverter());
} else {
@@ -117,9 +126,6 @@ public class UnlockController extends LocalizedFXMLViewController {
winDriveLetter.setVisible(false);
winDriveLetter.setManaged(false);
}
unlockButton.disableProperty().bind(passwordField.textProperty().isEmpty());
EasyBind.subscribe(vault, this::vaultDidChange);
}
@Override
@@ -127,11 +133,15 @@ public class UnlockController extends LocalizedFXMLViewController {
return getClass().getResource("/fxml/unlock.fxml");
}
private void vaultDidChange(Vault newVault) {
if (newVault == null) {
void setVault(Vault vault) {
// trigger "default" change to refresh key bindings:
unlockButton.setDefaultButton(false);
unlockButton.setDefaultButton(true);
if (vault.equals(this.vault)) {
return;
}
passwordField.clear();
this.vault = Objects.requireNonNull(vault);
passwordField.swipe();
advancedOptions.setVisible(false);
advancedOptionsButton.setText(localization.getString("unlock.button.advancedOptions.show"));
progressIndicator.setVisible(false);
@@ -145,13 +155,21 @@ public class UnlockController extends LocalizedFXMLViewController {
}
downloadsPageLink.setVisible(false);
messageText.setText(null);
mountName.setText(newVault.getMountName());
mountName.setText(vault.getMountName());
if (SystemUtils.IS_OS_WINDOWS) {
chooseSelectedDriveLetter();
}
// trigger "default" change to refresh key bindings:
unlockButton.setDefaultButton(false);
unlockButton.setDefaultButton(true);
savePassword.setSelected(false);
// auto-fill pw from keychain:
if (keychainAccess.isPresent()) {
char[] storedPw = keychainAccess.get().loadPassphrase(vault.getId());
if (storedPw != null) {
savePassword.setSelected(true);
passwordField.setText(new String(storedPw));
passwordField.selectRange(storedPw.length, storedPw.length);
Arrays.fill(storedPw, ' ');
}
}
}
// ****************************************
@@ -160,7 +178,7 @@ public class UnlockController extends LocalizedFXMLViewController {
@FXML
public void didClickDownloadsLink(ActionEvent event) {
app.getHostServices().showDocument("https://cryptomator.org/downloads/");
app.getHostServices().showDocument("https://cryptomator.org/downloads/#allVersions");
}
// ****************************************
@@ -188,14 +206,11 @@ public class UnlockController extends LocalizedFXMLViewController {
}
private void mountNameDidChange(ObservableValue<? extends String> property, String oldValue, String newValue) {
if (vault.get() == null) {
return;
}
// newValue is guaranteed to be a-z0-9_, see #filterAlphanumericKeyEvents
if (newValue.isEmpty()) {
mountName.setText(vault.get().getMountName());
mountName.setText(vault.getMountName());
} else {
vault.get().setMountName(newValue);
vault.setMountName(newValue);
}
}
@@ -242,20 +257,17 @@ public class UnlockController extends LocalizedFXMLViewController {
}
private void winDriveLetterDidChange(ObservableValue<? extends Character> property, Character oldValue, Character newValue) {
if (vault.get() == null) {
return;
}
vault.get().setWinDriveLetter(newValue);
vault.setWinDriveLetter(newValue);
settings.save();
}
private void chooseSelectedDriveLetter() {
assert SystemUtils.IS_OS_WINDOWS;
// if the vault prefers a drive letter, that is currently occupied, this is our last chance to reset this:
if (driveLetters.getOccupiedDriveLetters().contains(vault.get().getWinDriveLetter())) {
vault.get().setWinDriveLetter(null);
if (driveLetters.getOccupiedDriveLetters().contains(vault.getWinDriveLetter())) {
vault.setWinDriveLetter(null);
}
final Character letter = vault.get().getWinDriveLetter();
final Character letter = vault.getWinDriveLetter();
if (letter == null) {
// first option is known to be 'auto-assign' due to #WinDriveLetterComparator.
this.winDriveLetter.getSelectionModel().selectFirst();
@@ -275,10 +287,10 @@ public class UnlockController extends LocalizedFXMLViewController {
progressIndicator.setVisible(true);
downloadsPageLink.setVisible(false);
CharSequence password = passwordField.getCharacters();
asyncTaskService.asyncTaskOf(() -> this.unlock(vault.get(), password)).run();
asyncTaskService.asyncTaskOf(() -> this.unlock(password)).run();
}
private void unlock(Vault vault, CharSequence password) {
private void unlock(CharSequence password) {
try {
vault.activateFrontend(frontendFactory.get(), settings, password);
vault.reveal();
@@ -286,18 +298,28 @@ public class UnlockController extends LocalizedFXMLViewController {
messageText.setText(null);
listener.ifPresent(lstnr -> lstnr.didUnlock(vault));
});
if (keychainAccess.isPresent() && savePassword.isSelected()) {
keychainAccess.get().storePassphrase(vault.getId(), password);
} else {
passwordField.swipe();
}
} catch (InvalidPassphraseException e) {
Platform.runLater(() -> {
messageText.setText(localization.getString("unlock.errorMessage.wrongPassword"));
passwordField.selectAll();
passwordField.requestFocus();
});
} catch (UnsupportedVaultFormatException e) {
Platform.runLater(() -> {
downloadsPageLink.setVisible(true);
if (e.isVaultOlderThanSoftware()) {
// whitespace after localized text used as separator between text and hyperlink
messageText.setText(localization.getString("unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware") + " ");
downloadsPageLink.setVisible(true);
} else if (e.isSoftwareOlderThanVault()) {
messageText.setText(localization.getString("unlock.errorMessage.unsupportedVersion.softwareOlderThanVault") + " ");
downloadsPageLink.setVisible(true);
} else if (e.getDetectedVersion() == Integer.MAX_VALUE) {
messageText.setText(localization.getString("unlock.errorMessage.unauthenticVersionMac"));
}
});
} catch (FrontendCreationFailedException | CommandFailedException e) {
@@ -307,7 +329,6 @@ public class UnlockController extends LocalizedFXMLViewController {
});
} finally {
Platform.runLater(() -> {
passwordField.swipe();
mountName.setDisable(false);
advancedOptionsButton.setDisable(false);
progressIndicator.setVisible(false);
@@ -15,21 +15,23 @@ import org.cryptomator.ui.settings.Localization;
import org.cryptomator.ui.util.AsyncTaskService;
import org.fxmisc.easybind.EasyBind;
import javafx.beans.binding.BooleanExpression;
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.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
public class UpgradeController extends LocalizedFXMLViewController {
final ObjectProperty<Vault> vault = new SimpleObjectProperty<>();
final ObjectProperty<Optional<UpgradeStrategy>> strategy = new SimpleObjectProperty<>();
private final ObjectProperty<Optional<UpgradeStrategy>> strategy = new SimpleObjectProperty<>();
private final UpgradeStrategies strategies;
private final AsyncTaskService asyncTaskService;
private Optional<UpgradeListener> listener = Optional.empty();
private Vault vault;
@Inject
public UpgradeController(Localization localization, UpgradeStrategies strategies, AsyncTaskService asyncTaskService) {
@@ -44,6 +46,9 @@ public class UpgradeController extends LocalizedFXMLViewController {
@FXML
private SecPasswordField passwordField;
@FXML
private CheckBox confirmationCheckbox;
@FXML
private Button upgradeButton;
@@ -59,9 +64,9 @@ public class UpgradeController extends LocalizedFXMLViewController {
return instruction.map(this::upgradeNotification).orElse("");
}).orElse(""));
upgradeButton.disableProperty().bind(passwordField.textProperty().isEmpty().or(passwordField.disabledProperty()));
EasyBind.subscribe(vault, this::vaultDidChange);
BooleanExpression passwordProvided = passwordField.textProperty().isNotEmpty().and(passwordField.disabledProperty().not());
BooleanExpression syncFinished = confirmationCheckbox.selectedProperty();
upgradeButton.disableProperty().bind(passwordProvided.not().or(syncFinished.not()));
}
@Override
@@ -69,9 +74,10 @@ public class UpgradeController extends LocalizedFXMLViewController {
return getClass().getResource("/fxml/upgrade.fxml");
}
private void vaultDidChange(Vault newVault) {
void setVault(Vault vault) {
this.vault = Objects.requireNonNull(vault);
errorLabel.setText(null);
strategy.set(strategies.getUpgradeStrategy(newVault));
strategy.set(strategies.getUpgradeStrategy(vault));
// trigger "default" change to refresh key bindings:
upgradeButton.setDefaultButton(false);
upgradeButton.setDefaultButton(true);
@@ -82,7 +88,7 @@ public class UpgradeController extends LocalizedFXMLViewController {
// ****************************************
private String upgradeNotification(UpgradeStrategy instruction) {
return instruction.getNotification(vault.get());
return instruction.getNotification(vault);
}
// ****************************************
@@ -95,15 +101,14 @@ public class UpgradeController extends LocalizedFXMLViewController {
}
private void upgrade(UpgradeStrategy instruction) {
Vault v = Objects.requireNonNull(vault.getValue());
passwordField.setDisable(true);
progressIndicator.setVisible(true);
asyncTaskService //
.asyncTaskOf(() -> {
if (!instruction.isApplicable(v)) {
throw new IllegalStateException("No ugprade needed for " + v.path().getValue());
if (!instruction.isApplicable(vault)) {
throw new IllegalStateException("No ugprade needed for " + vault.path().getValue());
}
instruction.upgrade(v, passwordField.getCharacters());
instruction.upgrade(vault, passwordField.getCharacters());
}) //
.onSuccess(this::showNextUpgrade) //
.onError(UpgradeFailedException.class, e -> {
@@ -118,7 +123,7 @@ public class UpgradeController extends LocalizedFXMLViewController {
private void showNextUpgrade() {
errorLabel.setText(null);
Optional<UpgradeStrategy> nextStrategy = strategies.getUpgradeStrategy(vault.getValue());
Optional<UpgradeStrategy> nextStrategy = strategies.getUpgradeStrategy(vault);
if (nextStrategy.isPresent()) {
strategy.set(nextStrategy);
} else {
@@ -19,6 +19,7 @@ import java.util.regex.Pattern;
import org.apache.commons.lang3.SystemUtils;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender;
import org.apache.logging.log4j.core.appender.FileManager;
import org.apache.logging.log4j.core.config.plugins.Plugin;
@@ -26,10 +27,11 @@ import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.layout.PatternLayout;
import org.apache.logging.log4j.core.util.Booleans;
import org.apache.logging.log4j.util.Strings;
/**
* A preconfigured FileAppender only relying on a configurable system property, e.g. <code>-DlogPath=/var/log/cryptomator.log</code>.<br/>
* A preconfigured FileAppender only relying on a configurable system property, e.g. <code>-Dcryptomator.logPath=/var/log/cryptomator.log</code>.<br/>
* Other than the normal {@link org.apache.logging.log4j.core.appender.FileAppender} paths can be resolved relative to the users home directory.
*/
@Plugin(name = "ConfigurableFile", category = "Core", elementType = "appender", printObject = true)
@@ -37,7 +39,6 @@ public class ConfigurableFileAppender extends AbstractOutputStreamAppender<FileM
private static final long serialVersionUID = -6548221568069606389L;
private static final int DEFAULT_BUFFER_SIZE = 8192;
private static final String DEFAULT_FILE_NAME = "cryptomator.log";
private static final Pattern DRIVE_LETTER_WITH_PRECEEDING_SLASH = Pattern.compile("^/[A-Z]:", Pattern.CASE_INSENSITIVE);
protected ConfigurableFileAppender(String name, Layout<? extends Serializable> layout, Filter filter, FileManager manager) {
@@ -46,9 +47,8 @@ public class ConfigurableFileAppender extends AbstractOutputStreamAppender<FileM
}
@PluginFactory
public static ConfigurableFileAppender createAppender(@PluginAttribute("name") final String name, @PluginAttribute("pathPropertyName") final String pathPropertyName,
public static AbstractAppender createAppender(@PluginAttribute("name") final String name, @PluginAttribute("pathPropertyName") final String pathPropertyName, @PluginAttribute("append") final String append,
@PluginElement("Layout") Layout<? extends Serializable> layout) {
if (name == null) {
LOGGER.error("No name provided for HomeDirectoryAwareFileAppender");
return null;
@@ -59,41 +59,16 @@ public class ConfigurableFileAppender extends AbstractOutputStreamAppender<FileM
return null;
}
String fileName = System.getProperty(pathPropertyName);
final String fileName = System.getProperty(pathPropertyName);
if (Strings.isEmpty(fileName)) {
fileName = DEFAULT_FILE_NAME;
LOGGER.warn("No log file location provided in system property \"" + pathPropertyName + "\"");
return null;
}
final Path filePath;
if (fileName.startsWith("~/")) {
// home-dir-relative Path:
final Path userHome = FileSystems.getDefault().getPath(SystemUtils.USER_HOME);
filePath = userHome.resolve(fileName.substring(2));
} else if (fileName.startsWith("/")) {
// absolute Path:
filePath = FileSystems.getDefault().getPath(fileName);
} else if (SystemUtils.IS_OS_WINDOWS && fileName.startsWith("%appdata%/")) {
final String appdata = System.getenv("APPDATA");
final Path appdataPath = appdata != null ? FileSystems.getDefault().getPath(appdata) : FileSystems.getDefault().getPath(SystemUtils.USER_HOME);
filePath = appdataPath.resolve(fileName.substring(10));
} else {
// relative Path:
try {
String jarFileLocation = ConfigurableFileAppender.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath();
if (SystemUtils.IS_OS_WINDOWS && DRIVE_LETTER_WITH_PRECEEDING_SLASH.matcher(jarFileLocation).find()) {
// on windows we need to remove a preceeding slash from "/C:/foo/bar":
jarFileLocation = jarFileLocation.substring(1);
}
final Path workingDir = FileSystems.getDefault().getPath(jarFileLocation).getParent();
filePath = workingDir.resolve(fileName);
} catch (URISyntaxException e) {
LOGGER.error("Unable to resolve working directory ", e);
return null;
}
}
if (layout == null) {
layout = PatternLayout.createDefaultLayout();
final Path filePath = parsePath(fileName);
if (filePath == null) {
LOGGER.warn("Invalid path \"" + fileName + "\"");
return null;
}
if (!Files.exists(filePath.getParent())) {
@@ -105,8 +80,38 @@ public class ConfigurableFileAppender extends AbstractOutputStreamAppender<FileM
}
}
final FileManager manager = FileManager.getFileManager(filePath.toString(), false, false, true, null, layout, DEFAULT_BUFFER_SIZE);
final boolean shouldAppend = Booleans.parseBoolean(append, true);
if (layout == null) {
layout = PatternLayout.createDefaultLayout();
}
final FileManager manager = FileManager.getFileManager(filePath.toString(), shouldAppend, false, true, null, layout, DEFAULT_BUFFER_SIZE);
return new ConfigurableFileAppender(name, layout, null, manager);
}
private static Path parsePath(String path) {
if (path.startsWith("~/")) {
// home-dir-relative Path:
final Path userHome = FileSystems.getDefault().getPath(SystemUtils.USER_HOME);
return userHome.resolve(path.substring(2));
} else if (path.startsWith("/")) {
// absolute Path:
return FileSystems.getDefault().getPath(path);
} else {
// relative Path:
try {
String jarFileLocation = ConfigurableFileAppender.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath();
if (SystemUtils.IS_OS_WINDOWS && DRIVE_LETTER_WITH_PRECEEDING_SLASH.matcher(jarFileLocation).find()) {
// on windows we need to remove a preceeding slash from "/C:/foo/bar":
jarFileLocation = jarFileLocation.substring(1);
}
final Path workingDir = FileSystems.getDefault().getPath(jarFileLocation).getParent();
return workingDir.resolve(path);
} catch (URISyntaxException e) {
LOGGER.error("Unable to resolve working directory ", e);
return null;
}
}
}
}
@@ -14,8 +14,8 @@ public class UpgradeStrategies {
private final Collection<UpgradeStrategy> strategies;
@Inject
public UpgradeStrategies(UpgradeVersion3DropBundleExtension upgrader1, UpgradeVersion3to4 upgrader2) {
strategies = Collections.unmodifiableList(Arrays.asList(upgrader1, upgrader2));
public UpgradeStrategies(UpgradeVersion3DropBundleExtension upgrader1, UpgradeVersion3to4 upgrader2, UpgradeVersion4to5 upgrader3) {
strategies = Collections.unmodifiableList(Arrays.asList(upgrader1, upgrader2, upgrader3));
}
public Optional<UpgradeStrategy> getUpgradeStrategy(Vault vault) {
@@ -6,11 +6,11 @@ import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import javax.inject.Provider;
import org.cryptomator.crypto.engine.Cryptor;
import org.cryptomator.crypto.engine.InvalidPassphraseException;
import org.cryptomator.crypto.engine.UnsupportedVaultFormatException;
import org.cryptomator.cryptolib.api.Cryptor;
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;
@@ -20,12 +20,16 @@ public abstract class UpgradeStrategy {
private static final Logger LOG = LoggerFactory.getLogger(UpgradeStrategy.class);
protected final Provider<Cryptor> cryptorProvider;
protected final CryptorProvider cryptorProvider;
protected final Localization localization;
protected final int vaultVersionBeforeUpgrade;
protected final int vaultVersionAfterUpgrade;
UpgradeStrategy(Provider<Cryptor> cryptorProvider, Localization localization) {
UpgradeStrategy(CryptorProvider cryptorProvider, Localization localization, int vaultVersionBeforeUpgrade, int vaultVersionAfterUpgrade) {
this.cryptorProvider = cryptorProvider;
this.localization = localization;
this.vaultVersionBeforeUpgrade = vaultVersionBeforeUpgrade;
this.vaultVersionAfterUpgrade = vaultVersionAfterUpgrade;
}
/**
@@ -37,27 +41,37 @@ 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 {
final Cryptor cryptor = cryptorProvider.get();
Cryptor cryptor = null;
try {
final Path masterkeyFile = vault.path().getValue().resolve(Constants.MASTERKEY_FILENAME);
final byte[] masterkeyFileContents = Files.readAllBytes(masterkeyFile);
cryptor.readKeysFromMasterkeyFile(masterkeyFileContents, passphrase);
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);
Files.copy(masterkeyFile, masterkeyBackupFile, StandardCopyOption.REPLACE_EXISTING);
// do stuff:
upgrade(vault, cryptor);
// write updated masterkey file:
final byte[] upgradedMasterkeyFileContents = cryptor.writeKeysToMasterkeyFile(passphrase);
final Path masterkeyFileAfterUpgrading = vault.path().getValue().resolve(Constants.MASTERKEY_FILENAME); // path may have changed
Files.write(masterkeyFileAfterUpgrading, upgradedMasterkeyFileContents, StandardOpenOption.TRUNCATE_EXISTING);
final byte[] upgradedMasterkeyFileContents = cryptor.writeKeysToMasterkeyFile(passphrase, vaultVersionAfterUpgrade).serialize();
final Path masterkeyFileAfterUpgrade = vault.path().getValue().resolve(Constants.MASTERKEY_FILENAME); // path may have changed
Files.write(masterkeyFileAfterUpgrade, upgradedMasterkeyFileContents, StandardOpenOption.TRUNCATE_EXISTING);
} catch (InvalidPassphraseException e) {
throw new UpgradeFailedException(localization.getString("unlock.errorMessage.wrongPassword"));
} catch (IOException | UnsupportedVaultFormatException e) {
} catch (UnsupportedVaultFormatException e) {
if (e.getDetectedVersion() == Integer.MAX_VALUE) {
LOG.warn("Version MAC authentication error in vault {}", vault.path().get());
throw new UpgradeFailedException(localization.getString("unlock.errorMessage.unauthenticVersionMac"));
} else {
LOG.warn("Upgrade failed.", e);
throw new UpgradeFailedException("Upgrade failed. Details in log message.");
}
} catch (IOException e) {
LOG.warn("Upgrade failed.", e);
throw new UpgradeFailedException("Upgrade failed. Details in log message.");
} finally {
cryptor.destroy();
if (cryptor != null) {
cryptor.destroy();
}
}
}
@@ -68,7 +82,21 @@ 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 abstract boolean isApplicable(Vault vault);
public boolean isApplicable(Vault vault) {
final Path masterkeyFile = vault.path().getValue().resolve(Constants.MASTERKEY_FILENAME);
try {
if (Files.isRegularFile(masterkeyFile)) {
byte[] masterkeyFileContents = Files.readAllBytes(masterkeyFile);
return KeyFile.parse(masterkeyFileContents).getVersion() == vaultVersionBeforeUpgrade;
} else {
LOG.warn("Not a file: {}", masterkeyFile);
return false;
}
} catch (IOException e) {
LOG.warn("Could not determine, whether upgrade is applicable.", e);
return false;
}
}
/**
* Thrown when data migration failed.
@@ -1,19 +1,17 @@
package org.cryptomator.ui.model;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import org.apache.commons.lang3.StringUtils;
import org.cryptomator.crypto.engine.Cryptor;
import org.cryptomator.crypto.engine.InvalidPassphraseException;
import org.cryptomator.crypto.engine.UnsupportedVaultFormatException;
import org.cryptomator.filesystem.crypto.Constants;
import org.cryptomator.cryptolib.api.CryptoLibVersion;
import org.cryptomator.cryptolib.api.CryptoLibVersion.Version;
import org.cryptomator.cryptolib.api.Cryptor;
import org.cryptomator.cryptolib.api.CryptorProvider;
import org.cryptomator.ui.settings.Localization;
import org.cryptomator.ui.settings.Settings;
import org.slf4j.Logger;
@@ -28,8 +26,8 @@ class UpgradeVersion3DropBundleExtension extends UpgradeStrategy {
private final Settings settings;
@Inject
public UpgradeVersion3DropBundleExtension(Provider<Cryptor> cryptorProvider, Localization localization, Settings settings) {
super(cryptorProvider, localization);
public UpgradeVersion3DropBundleExtension(@CryptoLibVersion(Version.ONE) CryptorProvider version1CryptorProvider, Localization localization, Settings settings) {
super(version1CryptorProvider, localization, 3, 3);
this.settings = settings;
}
@@ -42,25 +40,6 @@ class UpgradeVersion3DropBundleExtension extends UpgradeStrategy {
return String.format(fmt, oldVaultName, newVaultName);
}
@Override
public void upgrade(Vault vault, CharSequence passphrase) throws UpgradeFailedException {
final Cryptor cryptor = cryptorProvider.get();
try {
final Path masterkeyFile = vault.path().getValue().resolve(Constants.MASTERKEY_FILENAME);
final byte[] masterkeyFileContents = Files.readAllBytes(masterkeyFile);
cryptor.readKeysFromMasterkeyFile(masterkeyFileContents, passphrase);
upgrade(vault, cryptor);
// don't write new masterkey. this is a special case, as we were stupid and didn't increase the vault version with this upgrade...
} catch (InvalidPassphraseException e) {
throw new UpgradeFailedException(localization.getString("unlock.errorMessage.wrongPassword"));
} catch (IOException | UnsupportedVaultFormatException e) {
LOG.warn("Upgrade failed.", e);
throw new UpgradeFailedException("Upgrade failed. Details in log message.");
} finally {
cryptor.destroy();
}
}
@Override
protected void upgrade(Vault vault, Cryptor cryptor) throws UpgradeFailedException {
Path path = vault.path().getValue();
@@ -73,6 +52,7 @@ class UpgradeVersion3DropBundleExtension extends UpgradeStrategy {
throw new UpgradeFailedException(msg);
} else {
try {
LOG.info("Renaming {} to {}", path, newPath.getFileName());
Files.move(path, path.resolveSibling(newVaultName));
Platform.runLater(() -> {
vault.setPath(newPath);
@@ -89,19 +69,7 @@ class UpgradeVersion3DropBundleExtension extends UpgradeStrategy {
public boolean isApplicable(Vault vault) {
Path vaultPath = vault.path().getValue();
if (vaultPath.toString().endsWith(Vault.VAULT_FILE_EXTENSION)) {
final Path masterkeyFile = vaultPath.resolve(Constants.MASTERKEY_FILENAME);
try {
if (Files.isRegularFile(masterkeyFile)) {
final String keyContents = new String(Files.readAllBytes(masterkeyFile), StandardCharsets.UTF_8);
return keyContents.contains("\"version\":3") || keyContents.contains("\"version\": 3");
} else {
LOG.warn("Not a file: {}", masterkeyFile);
return false;
}
} catch (IOException e) {
LOG.warn("Could not determine, whether upgrade is applicable.", e);
return false;
}
return super.isApplicable(vault);
} else {
return false;
}
@@ -9,19 +9,20 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import org.apache.commons.codec.binary.Base32;
import org.apache.commons.codec.binary.BaseNCodec;
import org.apache.commons.lang3.StringUtils;
import org.cryptomator.crypto.engine.Cryptor;
import org.cryptomator.filesystem.crypto.Constants;
import org.cryptomator.cryptolib.api.CryptoLibVersion;
import org.cryptomator.cryptolib.api.CryptoLibVersion.Version;
import org.cryptomator.cryptolib.api.Cryptor;
import org.cryptomator.cryptolib.api.CryptorProvider;
import org.cryptomator.cryptolib.common.MessageDigestSupplier;
import org.cryptomator.ui.settings.Localization;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -40,17 +41,12 @@ class UpgradeVersion3to4 extends UpgradeStrategy {
private static final String OLD_FOLDER_SUFFIX = "_";
private static final String NEW_FOLDER_PREFIX = "0";
private final MessageDigest sha1;
private final MessageDigest sha1 = MessageDigestSupplier.SHA1.get();
private final BaseNCodec base32 = new Base32();
@Inject
public UpgradeVersion3to4(Provider<Cryptor> cryptorProvider, Localization localization) {
super(cryptorProvider, localization);
try {
sha1 = MessageDigest.getInstance("SHA-1");
} catch (NoSuchAlgorithmException e) {
throw new AssertionError("SHA-1 exists in every JVM");
}
public UpgradeVersion3to4(@CryptoLibVersion(Version.ONE) CryptorProvider version1CryptorProvider, Localization localization) {
super(version1CryptorProvider, localization, 3, 4);
}
@Override
@@ -144,21 +140,4 @@ class UpgradeVersion3to4 extends UpgradeStrategy {
}
}
@Override
public boolean isApplicable(Vault vault) {
final Path masterkeyFile = vault.path().getValue().resolve(Constants.MASTERKEY_FILENAME);
try {
if (Files.isRegularFile(masterkeyFile)) {
final String keyContents = new String(Files.readAllBytes(masterkeyFile), UTF_8);
return keyContents.contains("\"version\":3") || keyContents.contains("\"version\": 3");
} else {
LOG.warn("Not a file: {}", masterkeyFile);
return false;
}
} catch (IOException e) {
LOG.warn("Could not determine, whether upgrade is applicable.", e);
return false;
}
}
}
@@ -0,0 +1,130 @@
package org.cryptomator.ui.model;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.regex.Pattern;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.cryptomator.cryptolib.Cryptors;
import org.cryptomator.cryptolib.api.CryptoLibVersion;
import org.cryptomator.cryptolib.api.CryptoLibVersion.Version;
import org.cryptomator.cryptolib.api.Cryptor;
import org.cryptomator.cryptolib.api.CryptorProvider;
import org.cryptomator.cryptolib.api.FileHeader;
import org.cryptomator.ui.settings.Localization;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Contains the collective knowledge of all creatures who were alive during the development of vault format 3.
* This class uses no external classes from the crypto or shortening layer by purpose, so we don't need legacy code inside these.
*/
@Singleton
class UpgradeVersion4to5 extends UpgradeStrategy {
private static final Logger LOG = LoggerFactory.getLogger(UpgradeVersion4to5.class);
private static final Pattern BASE32_PATTERN = Pattern.compile("^([A-Z2-7]{8})*[A-Z2-7=]{8}");
@Inject
public UpgradeVersion4to5(@CryptoLibVersion(Version.ONE) CryptorProvider version1CryptorProvider, Localization localization) {
super(version1CryptorProvider, localization, 4, 5);
}
@Override
public String getNotification(Vault vault) {
return localization.getString("upgrade.version3to4.msg");
}
@Override
protected void upgrade(Vault vault, Cryptor cryptor) throws UpgradeFailedException {
Path dataDir = vault.path().get().resolve("d");
if (!Files.isDirectory(dataDir)) {
return; // empty vault. no migration needed.
}
try {
Files.walkFileTree(dataDir, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (BASE32_PATTERN.matcher(file.getFileName().toString()).find() && attrs.size() > cryptor.fileHeaderCryptor().headerSize()) {
migrate(file, attrs, cryptor);
}
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
LOG.error("Migration failed.", e);
throw new UpgradeFailedException(localization.getString("upgrade.version3to4.err.io"));
}
LOG.info("Migration finished.");
}
private void migrate(Path file, BasicFileAttributes attrs, Cryptor cryptor) throws IOException {
try (FileChannel ch = FileChannel.open(file, StandardOpenOption.READ, StandardOpenOption.WRITE)) {
// read header:
ByteBuffer headerBuf = ByteBuffer.allocate(cryptor.fileHeaderCryptor().headerSize());
ch.read(headerBuf);
headerBuf.flip();
FileHeader header = cryptor.fileHeaderCryptor().decryptHeader(headerBuf);
long cleartextSize = header.getFilesize();
if (cleartextSize < 0) {
LOG.info("Skipping already migrated file {}.", file);
return;
} else if (cleartextSize > attrs.size()) {
LOG.warn("Skipping file {} with invalid file size {}/{}", file, cleartextSize, attrs.size());
return;
}
int headerSize = cryptor.fileHeaderCryptor().headerSize();
int ciphertextChunkSize = cryptor.fileContentCryptor().ciphertextChunkSize();
int cleartextChunkSize = cryptor.fileContentCryptor().cleartextChunkSize();
long newCiphertextSize = Cryptors.ciphertextSize(cleartextSize, cryptor);
long newEOF = headerSize + newCiphertextSize;
long newFullChunks = newCiphertextSize / ciphertextChunkSize; // int-truncation
long newAdditionalCiphertextBytes = newCiphertextSize % ciphertextChunkSize;
if (newAdditionalCiphertextBytes == 0) {
// (new) last block is already correct. just truncate:
LOG.info("Migrating {} of cleartext size {}: Truncating to new ciphertext size: {}", file, cleartextSize, newEOF);
ch.truncate(newEOF);
} else {
// last block may contain padding and needs to be re-encrypted:
long lastChunkIdx = newFullChunks;
LOG.info("Migrating {} of cleartext size {}: Re-encrypting chunk {}. New ciphertext size: {}", file, cleartextSize, lastChunkIdx, newEOF);
long beginOfLastChunk = headerSize + lastChunkIdx * ciphertextChunkSize;
assert beginOfLastChunk < newEOF;
int lastCleartextChunkLength = (int) (cleartextSize % cleartextChunkSize);
assert lastCleartextChunkLength < cleartextChunkSize;
assert lastCleartextChunkLength > 0;
ch.position(beginOfLastChunk);
ByteBuffer lastCiphertextChunk = ByteBuffer.allocate(ciphertextChunkSize);
int read = ch.read(lastCiphertextChunk);
if (read != -1) {
lastCiphertextChunk.flip();
ByteBuffer lastCleartextChunk = cryptor.fileContentCryptor().decryptChunk(lastCiphertextChunk, lastChunkIdx, header, true);
lastCleartextChunk.position(0).limit(lastCleartextChunkLength);
assert lastCleartextChunk.remaining() == lastCleartextChunkLength;
ByteBuffer newLastChunkCiphertext = cryptor.fileContentCryptor().encryptChunk(lastCleartextChunk, lastChunkIdx, header);
ch.truncate(beginOfLastChunk);
ch.write(newLastChunkCiphertext);
} else {
LOG.error("Reached EOF at position {}/{}", beginOfLastChunk, newEOF);
return; // must exit method before changing header!
}
}
header.setFilesize(-1l);
ByteBuffer newHeaderBuf = cryptor.fileHeaderCryptor().encryptHeader(header);
ch.position(0);
ch.write(newHeaderBuf);
}
}
}
@@ -12,6 +12,7 @@ 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.Files;
import java.nio.file.Path;
@@ -30,6 +31,7 @@ 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;
@@ -109,8 +111,10 @@ public class Vault implements CryptoFileSystemDelegate {
public void create(CharSequence passphrase) throws IOException {
try {
FileSystem fs = getNioFileSystem();
if (fs.children().count() > 0) {
throw new FileAlreadyExistsException(null, null, "Vault location not empty.");
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) {
@@ -164,7 +168,7 @@ public class Vault implements CryptoFileSystemDelegate {
);
}
public void reveal() throws CommandFailedException {
public synchronized void reveal() throws CommandFailedException {
Optionals.ifPresent(filesystemFrontend.get(), Frontend::reveal);
}
@@ -186,7 +190,7 @@ public class Vault implements CryptoFileSystemDelegate {
// Getter/Setter
// *******************************************************************************/
public String getWebDavUrl() {
public synchronized String getWebDavUrl() {
return filesystemFrontend.get().map(Frontend::getWebDavUrl).orElseThrow(IllegalStateException::new);
}
@@ -0,0 +1,177 @@
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();
}
}
@@ -39,7 +39,7 @@ public class Localization extends ResourceBundle {
Objects.requireNonNull(in);
Reader reader = new InputStreamReader(in, StandardCharsets.UTF_8);
this.fallback = new PropertyResourceBundle(reader);
LOG.info("Loaded localization from {}", LOCALIZATION_FILE);
LOG.info("Loaded localization from bundle:{}", LOCALIZATION_FILE);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
@@ -17,6 +17,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
@@ -28,6 +29,7 @@ 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.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -38,23 +40,17 @@ import com.fasterxml.jackson.databind.ObjectMapper;
public class SettingsProvider implements Provider<Settings> {
private static final Logger LOG = LoggerFactory.getLogger(SettingsProvider.class);
private static final Path SETTINGS_DIR;
private static final String SETTINGS_FILE = "settings.json";
private static final Path DEFAULT_SETTINGS_PATH;
private static final long SAVE_DELAY_MS = 1000;
static {
final String appdata = System.getenv("APPDATA");
final FileSystem fs = FileSystems.getDefault();
if (SystemUtils.IS_OS_WINDOWS && appdata != null) {
SETTINGS_DIR = fs.getPath(appdata, "Cryptomator");
} else if (SystemUtils.IS_OS_WINDOWS && appdata == null) {
SETTINGS_DIR = fs.getPath(SystemUtils.USER_HOME, ".Cryptomator");
if (SystemUtils.IS_OS_WINDOWS) {
DEFAULT_SETTINGS_PATH = fs.getPath(SystemUtils.USER_HOME, "AppData/Roaming/Cryptomator/settings.json");
} else if (SystemUtils.IS_OS_MAC_OSX) {
SETTINGS_DIR = fs.getPath(SystemUtils.USER_HOME, "Library/Application Support/Cryptomator");
DEFAULT_SETTINGS_PATH = fs.getPath(SystemUtils.USER_HOME, "Library/Application Support/Cryptomator/settings.json");
} else {
// (os.contains("solaris") || os.contains("sunos") || os.contains("linux") || os.contains("unix"))
SETTINGS_DIR = fs.getPath(SystemUtils.USER_HOME, ".Cryptomator");
DEFAULT_SETTINGS_PATH = fs.getPath(SystemUtils.USER_HOME, ".Cryptomator/settings.json");
}
}
@@ -68,11 +64,15 @@ public class SettingsProvider implements Provider<Settings> {
}
private Path getSettingsPath() throws IOException {
String settingsPathProperty = System.getProperty("cryptomator.settingsPath");
if (settingsPathProperty == null) {
return SETTINGS_DIR.resolve(SETTINGS_FILE);
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);
}
private String replaceHomeDir(String path) {
if (path.startsWith("~/")) {
return SystemUtils.USER_HOME + path.substring(1);
} else {
return FileSystems.getDefault().getPath(settingsPathProperty);
return path;
}
}
@@ -96,7 +96,7 @@ public class SettingsProvider implements Provider<Settings> {
}
ScheduledFuture<?> saveCmd = saveScheduler.schedule(() -> {
this.save(settings);
} , SAVE_DELAY_MS, TimeUnit.MILLISECONDS);
}, SAVE_DELAY_MS, TimeUnit.MILLISECONDS);
ScheduledFuture<?> previousSaveCmd = scheduledSaveCmd.getAndSet(saveCmd);
if (previousSaveCmd != null) {
previousSaveCmd.cancel(false);
@@ -305,17 +305,17 @@
****************************************************************************/
.check-box {
-fx-label-padding: 0 0 0 3;
-fx-label-padding: 0 0 0 6px;
-fx-text-fill: COLOR_TEXT;
}
.check-box > .box {
-fx-padding: 3;
-fx-padding: 3px;
-fx-background-color: COLOR_BORDER_DARK, #FFF;
-fx-background-insets: 0, 1;
}
.check-box > .box > .mark {
-fx-background-color: transparent;
-fx-padding: 4;
-fx-padding: 4px;
-fx-shape: "M-1,4, L-1,5.5 L3.5,8.5 L9,0 L9,-1 L7,-1 L3,6 L1,4 Z";
}
.check-box:selected > .box {
+1 -1
View File
@@ -386,7 +386,7 @@
******************************************************************************/
.check-box {
-fx-label-padding: 0 0 0 3px;
-fx-label-padding: 0 0 0 6px;
-fx-text-fill: COLOR_TEXT;
}
.check-box > .box {
+1 -1
View File
@@ -354,7 +354,7 @@
******************************************************************************/
.check-box {
-fx-label-padding: 0 0 0 3px;
-fx-label-padding: 0 0 0 6px;
-fx-text-fill: COLOR_TEXT;
}
.check-box > .box {
+8 -4
View File
@@ -68,12 +68,16 @@
</HBox>
<!-- Row 3.1 -->
<Label text="%unlock.label.mountName" GridPane.rowIndex="1" GridPane.columnIndex="0" cacheShape="true" cache="true" />
<TextField fx:id="mountName" GridPane.rowIndex="1" GridPane.columnIndex="1" GridPane.hgrow="ALWAYS" maxWidth="Infinity" cacheShape="true" cache="true" />
<Label GridPane.rowIndex="1" GridPane.columnIndex="0" text="%unlock.label.savePassword" cacheShape="true" cache="true" />
<CheckBox GridPane.rowIndex="1" GridPane.columnIndex="1" fx:id="savePassword" cacheShape="true" cache="true" />
<!-- Row 3.2 -->
<Label fx:id="winDriveLetterLabel" text="%unlock.label.winDriveLetter" GridPane.rowIndex="2" GridPane.columnIndex="0" cacheShape="true" cache="true" />
<ChoiceBox fx:id="winDriveLetter" GridPane.rowIndex="2" GridPane.columnIndex="1" GridPane.hgrow="ALWAYS" maxWidth="Infinity" cacheShape="true" cache="true" />
<Label GridPane.rowIndex="2" GridPane.columnIndex="0" text="%unlock.label.mountName" cacheShape="true" cache="true" />
<TextField GridPane.rowIndex="2" GridPane.columnIndex="1" fx:id="mountName" GridPane.hgrow="ALWAYS" maxWidth="Infinity" cacheShape="true" cache="true" />
<!-- Row 3.3 -->
<Label GridPane.rowIndex="3" GridPane.columnIndex="0" fx:id="winDriveLetterLabel" text="%unlock.label.winDriveLetter" cacheShape="true" cache="true" />
<ChoiceBox GridPane.rowIndex="3" GridPane.columnIndex="1" fx:id="winDriveLetter" GridPane.hgrow="ALWAYS" maxWidth="Infinity" cacheShape="true" cache="true" />
</GridPane>
<!-- Row 4 -->
+9 -5
View File
@@ -17,6 +17,8 @@
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.CheckBox?>
<GridPane vgap="12.0" hgap="12.0" prefWidth="400.0" xmlns:fx="http://javafx.com/fxml" cacheShape="true" cache="true">
<padding>
@@ -34,15 +36,17 @@
<Pane prefHeight="12.0" GridPane.rowIndex="1" GridPane.columnIndex="0" GridPane.columnSpan="2" cacheShape="true" cache="true">
</Pane>
<Label text="%unlock.label.password" cacheShape="true" cache="true" GridPane.rowIndex="2" GridPane.columnIndex="0" />
<SecPasswordField fx:id="passwordField" cacheShape="true" cache="true" GridPane.rowIndex="2" GridPane.columnIndex="1" />
<Label text="%unlock.label.password" GridPane.rowIndex="2" GridPane.columnIndex="0" cacheShape="true" cache="true" />
<SecPasswordField fx:id="passwordField" GridPane.rowIndex="2" GridPane.columnIndex="1" cacheShape="true" cache="true" />
<CheckBox fx:id="confirmationCheckbox" text="%upgrade.confirmation.label" wrapText="true" GridPane.rowIndex="3" GridPane.columnIndex="0" GridPane.columnSpan="2" cacheShape="true" cache="true" />
<HBox alignment="CENTER_RIGHT" GridPane.hgrow="ALWAYS" GridPane.rowIndex="3" GridPane.columnIndex="0" GridPane.columnSpan="2" cacheShape="true" cache="true">
<HBox alignment="CENTER_RIGHT" GridPane.hgrow="ALWAYS" GridPane.rowIndex="4" GridPane.columnIndex="0" GridPane.columnSpan="2" cacheShape="true" cache="true">
<Button fx:id="upgradeButton" text="%upgrade.button" prefWidth="150.0" onAction="#didClickUpgradeButton" cacheShape="true" cache="true" />
</HBox>
<ProgressIndicator progress="-1" fx:id="progressIndicator" visible="false" cacheShape="true" cache="true" cacheHint="SPEED" GridPane.rowIndex="4" GridPane.columnIndex="0" GridPane.columnSpan="2" />
<ProgressIndicator progress="-1" fx:id="progressIndicator" visible="false" GridPane.rowIndex="5" GridPane.columnIndex="0" GridPane.columnSpan="2" cacheShape="true" cache="true" cacheHint="SPEED" />
<Label fx:id="errorLabel" wrapText="true" GridPane.rowIndex="5" GridPane.columnIndex="0" GridPane.columnSpan="2" cacheShape="true" cache="true" />
<Label fx:id="errorLabel" wrapText="true" GridPane.rowIndex="6" GridPane.columnIndex="0" GridPane.columnSpan="2" cacheShape="true" cache="true" />
</children>
</GridPane>
@@ -82,4 +82,10 @@ main.directoryList.remove.confirmation.title = Tresor entfernen
main.directoryList.remove.confirmation.header = Möchten Sie diesen Tresor wirklich entfernen?
main.directoryList.remove.confirmation.content = Dieser Tresor wird nur aus der Liste entfernt. Um den Tresor unwiderruflich zu löschen, entfernen Sie bitte die Dateien aus Ihrem Dateisystem.
upgrade.version3to4.msg = Dieser Tresor muss auf ein neueres Format aktualisiert werden.\nVerschlüsselte Ordnernamen werden dabei aktualisiert.\nStellen Sie bitte sicher, dass derzeit keine Synchronisation stattfindet.
upgrade.version3to4.err.io = Migration aufgrund eines I/O-Fehlers fehlgeschlagen.\nWeitere Informationen in der Log-Datei.
upgrade.version3to4.err.io = Migration aufgrund eines I/O-Fehlers fehlgeschlagen.\nWeitere Informationen in der Log-Datei.
settings.prefGvfsScheme.label = WebDAV Schema
# upgrade.fxml
upgrade.confirmation.label = Ja, die Synchronisation ist abgeschlossen
initialize.messageLabel.notEmpty = Tresor ist nicht leer
unlock.label.savePassword = Passwort speichern
unlock.errorMessage.unauthenticVersionMac = Versions-MAC konnte nicht authentifiziert werden.
@@ -26,6 +26,7 @@ initialize.label.password=Password
initialize.label.retypePassword=Retype password
initialize.button.ok=Create vault
initialize.messageLabel.alreadyInitialized=Vault already initialized
initialize.messageLabel.notEmpty=Vault not empty
initialize.messageLabel.initializationFailed=Could not initialize vault. See log file for details.
initialize.messageLabel.passwordStrength.0=Very weak
initialize.messageLabel.passwordStrength.1=Weak
@@ -38,6 +39,7 @@ initialize.label.doNotForget=IMPORTANT: If you forget your password, there is no
notfound.label=Vault couldn't be found. Has it been moved?
# upgrade.fxml
upgrade.confirmation.label=Yes, I've made sure that synchronization has finished
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.
@@ -48,6 +50,7 @@ upgrade.version3to4.err.io=Migration failed due to an I/O Exception. See log fil
# unlock.fxml
unlock.label.password=Password
unlock.label.savePassword=Save password
unlock.label.mountName=Drive name
unlock.label.winDriveLetter=Drive letter
unlock.label.downloadsPageLink=All Cryptomator versions
@@ -60,6 +63,7 @@ unlock.errorMessage.wrongPassword=Wrong password
unlock.errorMessage.mountingFailed=Mounting failed. See log file for details.
unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware=Unsupported vault. This vault has been created with an older version of Cryptomator.
unlock.errorMessage.unsupportedVersion.softwareOlderThanVault=Unsupported vault. This vault has been created with a newer version of Cryptomator.
unlock.errorMessage.unauthenticVersionMac=Could not authenticate version MAC.
unlock.messageLabel.startServerFailed=Starting WebDAV server failed.
# change_password.fxml
@@ -70,7 +74,6 @@ changePassword.label.downloadsPageLink=All Cryptomator versions
changePassword.button.change=Change password
changePassword.errorMessage.wrongPassword=Wrong password
changePassword.errorMessage.decryptionFailed=Decryption failed
changePassword.infoMessage.success=Password changed
# unlocked.fxml
unlocked.button.lock=Lock vault
@@ -14,7 +14,7 @@ initialize.label.password = Contraseña
initialize.label.retypePassword = Reintroduzca contraseña
initialize.button.ok = Crear caja fuerte
initialize.messageLabel.alreadyInitialized = Caja fuerte ya está inicializado
initialize.messageLabel.initializationFailed = No se pudo inicializar la caja fuerte. Ver archivo de registro para detalles.
initialize.messageLabel.initializationFailed = No se pudo inicializar la caja fuerte. Ver archivo de registro para detalles.
# notfound.fxml
notfound.label = No se pudo encontrar la caja fuerte. Se movió a otro lugar?
# upgrade.fxml
@@ -83,4 +83,10 @@ main.directoryList.remove.confirmation.title = Borrar caja fuerte
main.directoryList.remove.confirmation.header = ¿Quieres de verdad borrar este caja fuerte?
main.directoryList.remove.confirmation.content = La caja fuerte solo se borra de la lista. Para eliminarla permanente por favor elimina los datos de tú sistema de archivos.
upgrade.version3to4.msg = Este caja fuerte se debe migrar a un formato más reciente.\nLos nombres de las carpetas cifradas se actualizan.\nPor favor asegurarse de que la sincronización ha terminado antes de seguir.
upgrade.version3to4.err.io = Migración ha fallado por causa de una excepción I/O. Para detalles revisa el archivo de registro.
upgrade.version3to4.err.io = Migración ha fallado por causa de una excepción I/O. Para detalles revisa el archivo de registro.
settings.prefGvfsScheme.label = WebDAV scheme
# upgrade.fxml
upgrade.confirmation.label = Yes, I've made sure that synchronization has finished
initialize.messageLabel.notEmpty = Vault not empty
unlock.label.savePassword = Save password
unlock.errorMessage.unauthenticVersionMac = Could not authenticate version MAC.
@@ -78,10 +78,16 @@ initialize.messageLabel.passwordStrength.0 = Très faible
initialize.messageLabel.passwordStrength.1 = Faible
initialize.messageLabel.passwordStrength.2 = Raisonnable
initialize.messageLabel.passwordStrength.3 = Fort
initialize.messageLabel.passwordStrength.4 = Très fort\n
initialize.messageLabel.passwordStrength.4 = Très fort
initialize.label.doNotForget = ATTENTION \: Si vous oubliez votre mot de passe, il n'y aura aucun moyen de récupérer vos données.
main.directoryList.remove.confirmation.title = Retirer un coffre
main.directoryList.remove.confirmation.header = Voulez-vous vraiment retirer ce coffre ?
main.directoryList.remove.confirmation.content = Le coffre sera simplement retiré de la liste. Pour le supprimer complètement, supprimez les fichiers depuis votre système de fichiers.
upgrade.version3to4.msg = Ce coffre doit être converti dans un nouveau format. Les noms de dossiers chiffrés seront mis à jour.\nMerci de vous assurer que la procédure de synchronisation est terminée avant de continuer.
upgrade.version3to4.err.io = La migration a échoué à cause d'une erreur d'entrée/sortie. Référez-vous au fichier log pour plus de détails.
upgrade.version3to4.err.io = La migration a échoué à cause d'une erreur d'entrée/sortie. Référez-vous au fichier log pour plus de détails.
settings.prefGvfsScheme.label = Schéma d'URI WebDAV
# upgrade.fxml
upgrade.confirmation.label = Oui, je suis certain que la synchronisation est finie
initialize.messageLabel.notEmpty = Vault not empty
unlock.label.savePassword = Save password
unlock.errorMessage.unauthenticVersionMac = Could not authenticate version MAC.
@@ -79,4 +79,12 @@ initialize.messageLabel.passwordStrength.4 = Nagyon erős
initialize.label.doNotForget = FONTOS\: Ha elfelejted a jelszavadat, akkor nincs lehetőség az adataid visszaállítására.
main.directoryList.remove.confirmation.title = Széf eltávolítása
main.directoryList.remove.confirmation.header = Tényleg törölni akarod ezt a széfet?
main.directoryList.remove.confirmation.content = A széf csak a listából lesz eltávolítva. Végleges törléshez kérlek töröld a merevlemezen tárolt fájlokat.
main.directoryList.remove.confirmation.content = A széf csak a listából lesz eltávolítva. Végleges törléshez kérlek töröld a merevlemezen tárolt fájlokat.
upgrade.version3to4.msg = Ennek a széfnek egy újabb formátumra való migrációja szükséges. A titkosított könyvtárnevek frissítve lesznek. Kérlek, győződj meg a szinkronizáció befejeztéről mielőtt továbblépnél.
upgrade.version3to4.err.io = Migráció meghíusúlt egy I/O kivétel miatt. Kérlek nézd meg a naplófájlt a további részletekért.
settings.prefGvfsScheme.label = WebDAV séma
# upgrade.fxml
upgrade.confirmation.label = Yes, I've made sure that synchronization has finished
initialize.messageLabel.notEmpty = Vault not empty
unlock.label.savePassword = Save password
unlock.errorMessage.unauthenticVersionMac = Could not authenticate version MAC.
+14 -6
View File
@@ -18,7 +18,7 @@ initialize.messageLabel.initializationFailed = Non è possibile inizializzare il
notfound.label = Il vault non può essere trovato. E' stato rimosso?
# upgrade.fxml
upgrade.button = Aggiorna vault
upgrade.version3dropBundleExtension.msg = Questo vault deve essere migrato ad un nuovo formato.\n"%1$s" verrà rinominato in "%2$s".\nPer favore verifica che la sincronizzazione sia finita prima di procedere\n
upgrade.version3dropBundleExtension.msg = Questo vault deve essere migrato ad un nuovo formato.\n"%1$s" verrà rinominato in "%2$s".\nPer favore verifica che la sincronizzazione sia finita prima di procedere
upgrade.version3dropBundleExtension.err.alreadyExists = Migrazione automatica fallita.\n"%s" esiste già.
# unlock.fxml
unlock.label.password = Password
@@ -45,14 +45,14 @@ changePassword.errorMessage.wrongPassword = Password errata
changePassword.errorMessage.decryptionFailed = Decriptaggio fallito
changePassword.infoMessage.success = Password cambiata
# unlocked.fxml
unlocked.button.lock = Blocca vault
unlocked.button.lock = Blocca vault
unlocked.moreOptions.reveal = Apri il disco
unlocked.moreOptions.copyUrl = Copia url WebDAV
unlocked.label.revealFailed = Comando fallito
unlocked.label.unmountFailed = Espulsione disco fallita
unlocked.label.statsEncrypted = criptato
unlocked.label.statsDecrypted = decriptato
unlocked.ioGraph.yAxis.label = Volume dati (MiB/s)
unlocked.ioGraph.yAxis.label = Volume dati (MiB/s)
# mac_warnings.fxml
macWarnings.windowTitle = Pericolo - File corroto in %s
macWarnings.message = Cryptomator ha individuato potenziali pericolose corruzioni nei seguenti file\:
@@ -69,8 +69,8 @@ settings.requiresRestartLabel = * Cryptomator deve essere riavviato
tray.menu.open = Apri
tray.menu.quit = Chiudi
tray.infoMsg.title = Ancora in esecuzione
tray.infoMsg.msg = Cryptomator è ancora in esecuzione. Chiudilo utilizzando l'icona nel menù di stato.
tray.infoMsg.msg.osx = Cryptomator è ancora in esecuzione. Chiudilo utilizzando l'icona nella barra del menù.
tray.infoMsg.msg = Cryptomator è ancora in esecuzione. Chiudilo utilizzando l'icona nel menù di stato.
tray.infoMsg.msg.osx = Cryptomator è ancora in esecuzione. Chiudilo utilizzando l'icona nella barra del menù.
initialize.messageLabel.passwordStrength.0 = Molto debole
initialize.messageLabel.passwordStrength.1 = Debole
initialize.messageLabel.passwordStrength.2 = Buona
@@ -79,4 +79,12 @@ initialize.messageLabel.passwordStrength.4 = Molto sicura
initialize.label.doNotForget = IMPORTANTE\: Se dimentichi la password, non c'è modo di recuperare i tuoi dati.
main.directoryList.remove.confirmation.title = Rimuovi vault
main.directoryList.remove.confirmation.header = Vuoi davvero rimuovere questo vault?
main.directoryList.remove.confirmation.content = Il vault sarà rimosso solo dalla lista. Per eliminarlo definitivamente, elimina per favore i file dal tuo hard disk.
main.directoryList.remove.confirmation.content = Il vault sarà rimosso solo dalla lista. Per eliminarlo definitivamente, elimina per favore i file dal tuo hard disk.
upgrade.version3to4.msg = This vault needs to be migrated to a newer format.\nEncrypted folder names will be updated.\nPlease make sure synchronization has finished before proceeding.
upgrade.version3to4.err.io = Migration failed due to an I/O Exception. See log file for details.
settings.prefGvfsScheme.label = WebDAV scheme
# upgrade.fxml
upgrade.confirmation.label = Yes, I've made sure that synchronization has finished
initialize.messageLabel.notEmpty = Vault not empty
unlock.label.savePassword = Save password
unlock.errorMessage.unauthenticVersionMac = Could not authenticate version MAC.
@@ -81,4 +81,10 @@ main.directoryList.remove.confirmation.title = 보관함 삭제
main.directoryList.remove.confirmation.header = 정말 이 보관함을 삭제하시겠습니까?
main.directoryList.remove.confirmation.content = 보관함이 목록에서만 제거되었습니다. 데이터를 완전히 제거하시려면, 여러분의 파일시스템이서 제거해 주시기 바랍니다.
upgrade.version3to4.msg = 이 보관함은 새로운 형식으로 바뀔 필요가 있습니다. 암호화된 폴더 이름이 업데이트 될 것입니다. 진행하기 전에 동기화가 완료되었는지 확인하기 바랍니다.
upgrade.version3to4.err.io = 입출력 예외 문제로 마이그레이션이 실패하였습니다. 자세한 사항은 로그 파일을 확인하세요.
upgrade.version3to4.err.io = 입출력 예외 문제로 마이그레이션이 실패하였습니다. 자세한 사항은 로그 파일을 확인하세요.
settings.prefGvfsScheme.label = WebDAV sceme
# upgrade.fxml
upgrade.confirmation.label = 네. 동기화가 완료되었음을 확인하였습니다.
initialize.messageLabel.notEmpty = Vault not empty
unlock.label.savePassword = Save password
unlock.errorMessage.unauthenticVersionMac = Could not authenticate version MAC.
@@ -0,0 +1,91 @@
app.name = Cryptomator
# main.fxml
main.emptyListInstructions = Noklikšķiniet šeit, lai pievienotu glabātuvi
main.directoryList.contextMenu.remove = Noņemt no saraksta
main.directoryList.contextMenu.changePassword = Mainīt paroli
main.addDirectory.contextMenu.new = Izveidot jaunu glabātuvi
main.addDirectory.contextMenu.open = Atvērt esošu glabātuvi
# welcome.fxml
welcome.checkForUpdates.label.currentlyChecking = Pārbauda atjauninājumus...
welcome.newVersionMessage = Lejupielādei pieejama versija %1$s.\nŠī ir %2$s.
# initialize.fxml
initialize.label.password = Parole
initialize.label.retypePassword = Atkārto paroli
initialize.button.ok = Izveidot glabātuvi
initialize.messageLabel.alreadyInitialized = Glabātuve jau ir inicializēta
initialize.messageLabel.initializationFailed = Nevarēja inicializēt glabātuvi. Sīkākai informācijai skati žurnālu.
# notfound.fxml
notfound.label = Nevar atrast glabātuvi. Vai tā ir pārvietota?
# upgrade.fxml
upgrade.button = Atjaunināt glabātuvi
upgrade.version3dropBundleExtension.msg = Šī glabātuve ir jāmigrē uz jaunākas versijas formātu.\n"%1$s" tiks pārsaukta par "%2$s".\nPirms palaišanas pārliecinies, ka ir beigusies sinhronizācija.
upgrade.version3dropBundleExtension.err.alreadyExists = Automātiskā migrācija neizdevās.\n"%s" jau eksistē.
# unlock.fxml
unlock.label.password = Parole
unlock.label.mountName = Diska nosaukums
unlock.label.winDriveLetter = Diska burts
unlock.label.downloadsPageLink = Visas Cryptomator versijas
unlock.label.advancedHeading = Papildus opcijas
unlock.button.unlock = Atslēgt glabātuvi
unlock.button.advancedOptions.show = Vairāk opcijas
unlock.button.advancedOptions.hide = Mazāk opcijas
unlock.choicebox.winDriveLetter.auto = Piešķirt automātiski
unlock.errorMessage.wrongPassword = Nepareiza parole
unlock.errorMessage.mountingFailed = Montēšana neizdevās. Sīkākai informācijai skati žurnālu.
unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware = Neatbastīta glabātuve. Šī glabātuve ir veidota ar vecāku Cryptomator versiju.
unlock.errorMessage.unsupportedVersion.softwareOlderThanVault = Neatbastīta glabātuve. Šī glabātuve ir veidota ar jaunāku Cryptomator versiju.
unlock.messageLabel.startServerFailed = WebDAV servera palaišana neizdevās.
# change_password.fxml
changePassword.label.oldPassword = Vecā parole
changePassword.label.newPassword = Jaunā parole
changePassword.label.retypePassword = Atkārto paroli
changePassword.label.downloadsPageLink = Visas Cryptomator versijas
changePassword.button.change = Mainīt paroli
changePassword.errorMessage.wrongPassword = Nepareiza parole
changePassword.errorMessage.decryptionFailed = Atšifrēšana neizdevās
changePassword.infoMessage.success = Parole nomainīta
# unlocked.fxml
unlocked.button.lock = Aizslēgt glabātuvi
unlocked.moreOptions.reveal = Atklāt disku
unlocked.moreOptions.copyUrl = Kopēt WebDAV saiti
unlocked.label.revealFailed = Komunikācija neizdevās
unlocked.label.unmountFailed = Diska izgrūšana neizdevās
unlocked.label.statsEncrypted = šifrēts
unlocked.label.statsDecrypted = atšifrēts
unlocked.ioGraph.yAxis.label = Caurlaidība (MiB/s)
# mac_warnings.fxml
macWarnings.windowTitle = Bīstami - %s satur bojātu datni
macWarnings.message = Cryptomator konstatēja potenciāli ļaunprātīgu bojājumu sekojošām datnēm\:
macWarnings.moreInformationButton = Skatīt vairāk
macWarnings.whitelistButton = Atšifrēt iezīmēto tāpat
# settings.fxml
settings.version.label = Versija %s
settings.checkForUpdates.label = Meklēt atjauninājumus
settings.port.label = WebDAV Ports *
settings.port.prompt = 0 \= automātiski
settings.useipv6.label = Izmantot IPv6
settings.requiresRestartLabel = * Cryptomator nepieciešams restarts
# tray icon
tray.menu.open = Atvērt
tray.menu.quit = Iziet
tray.infoMsg.title = Joprojām darbojās
# Fuzzy
tray.infoMsg.msg = Cryptomator joprojām darbojās. Iziet var no ikonas.
tray.infoMsg.msg.osx = Cryptomator is still alive. joprojām darbojās. Iziet var no izvēlnes ikonas.
initialize.messageLabel.passwordStrength.0 = Ļoti vāja
initialize.messageLabel.passwordStrength.1 = Vāja
initialize.messageLabel.passwordStrength.2 = Vidēja
initialize.messageLabel.passwordStrength.3 = Stipra
initialize.messageLabel.passwordStrength.4 = Ļoti stipra
initialize.label.doNotForget = SVARĪGI\: Ja tu aizmirsti paroli, nav ceļa datu atjaunošanai
main.directoryList.remove.confirmation.title = Noņemt glabātuvi
main.directoryList.remove.confirmation.header = Vai tiešām vēlie noņemt šo glabātuvi?
main.directoryList.remove.confirmation.content = Glabātuve tiks noņemta tikai no saraksta. Lai to dzēstu pilnībā, lūdzu nodzēs to no datņu sistēmas.
upgrade.version3to4.msg = Šī glabātuve ir jāmigrē uz jaunāku formāta versiju. Sifrētie mapju nosaukumi tiks atjaunināti. Pirms palaišanas lūdzu pārliecinies, ka ir pabeigusies sinhronizācija.
upgrade.version3to4.err.io = Migrācija neizdevās dēļ I/O izņēmuma. Sīkākai informācijai skati žurnālu.
settings.prefGvfsScheme.label = WebDAV shēma
# upgrade.fxml
upgrade.confirmation.label = Jā, esmu pārliecinājies, ka sinhronizācija ir pabeigta.
initialize.messageLabel.notEmpty = Vault not empty
unlock.label.savePassword = Save password
unlock.errorMessage.unauthenticVersionMac = Could not authenticate version MAC.
@@ -81,4 +81,10 @@ main.directoryList.remove.confirmation.title = Verwijder Kluis
main.directoryList.remove.confirmation.header = Weet je zeker dat je deze kluis wilt verwijderen?
main.directoryList.remove.confirmation.content = De kluis zal alleen van de lijst worden verwijdert. Verwijder de bestanden van het bestandssysteem voor permanente verwijdering.
upgrade.version3to4.msg = Deze kluis dient gemigreerd te worden naar een nieuwer type. \nVersleutelde mapnamen zullen worden geüpdatet. \nZorg ervoor dat de synchronisatie voltooid is alvorens door te gaan.
upgrade.version3to4.err.io = Migratie mislukt door een I/O Exception. Zie logbestand voor details.
upgrade.version3to4.err.io = Migratie mislukt door een I/O Exception. Zie logbestand voor details.
settings.prefGvfsScheme.label = WebDAV scheme
# upgrade.fxml
upgrade.confirmation.label = Yes, I've made sure that synchronization has finished
initialize.messageLabel.notEmpty = Vault not empty
unlock.label.savePassword = Save password
unlock.errorMessage.unauthenticVersionMac = Could not authenticate version MAC.
@@ -0,0 +1,90 @@
app.name = Cryptomator
# main.fxml
main.emptyListInstructions = Kliknij tutaj aby dodać kryptę
main.directoryList.contextMenu.remove = Usuń z listy
main.directoryList.contextMenu.changePassword = Zmień hasło
main.addDirectory.contextMenu.new = Stwórz nową kryptę
main.addDirectory.contextMenu.open = Otwórz istniejącą kryptę
# welcome.fxml
welcome.checkForUpdates.label.currentlyChecking = Sprawdź aktualizację
welcome.newVersionMessage = Wersja %1$s może być pobrana.\nObecna %2$s.
# initialize.fxml
initialize.label.password = Hasło
initialize.label.retypePassword = Ponów hasło
initialize.button.ok = Stwórz kryptę
initialize.messageLabel.alreadyInitialized = Krypta aktualnie załadowana
initialize.messageLabel.initializationFailed = Nie można załadować krypty. Sprawdź logi
# notfound.fxml
notfound.label = Krypta nie znaleziona. Przeniesiona?
# upgrade.fxml
upgrade.button = Aktualizuj kryptę
upgrade.version3dropBundleExtension.msg = Ta krypta musi zostać przeniesiona do nowego formatu\n"%1$s" zamienione na "%2$s".\nProszę synchronizować przed skończeniem procesu
upgrade.version3dropBundleExtension.err.alreadyExists = Błąd automatycznej migracji.\n"%s" już istnieje
# unlock.fxml
unlock.label.password = Hasło
unlock.label.mountName = Nazwa dysku
unlock.label.winDriveLetter = Litera dysku
unlock.label.downloadsPageLink = Wszystkie wersje Cryptomatora
unlock.label.advancedHeading = Zaawansowane opcje
unlock.button.unlock = Odblokuj kryptę
unlock.button.advancedOptions.show = Więcej opcji
unlock.button.advancedOptions.hide = Mniej opcji
unlock.choicebox.winDriveLetter.auto = Przydzielone automatycznie
unlock.errorMessage.wrongPassword = Złe hasło
unlock.errorMessage.mountingFailed = Błąd zamontowania. Sprawdź logi
unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware = Niewspierana krypta, utworzona przez starszą wersje Cryptomatora
unlock.errorMessage.unsupportedVersion.softwareOlderThanVault = Niewspierana krypta, utworzona przez nowszą wersje Cryptomatora
unlock.messageLabel.startServerFailed = Błąd startu serwera WebDAV
# change_password.fxml
changePassword.label.oldPassword = Stare hasło
changePassword.label.newPassword = Nowe hasło
changePassword.label.retypePassword = Powtórz hasło
changePassword.label.downloadsPageLink = Wszystkie wersje Cryptomatora
changePassword.button.change = Zmień hasło
changePassword.errorMessage.wrongPassword = Złe hasło
changePassword.errorMessage.decryptionFailed = Błąd deszyfracji
changePassword.infoMessage.success = Hasło zmienione
# unlocked.fxml
unlocked.button.lock = Zablokuj kryptę
unlocked.moreOptions.reveal = Zwolnij napęd
unlocked.moreOptions.copyUrl = Skopiuj url WebDAV
unlocked.label.revealFailed = Zła komenda
unlocked.label.unmountFailed = Błąd wysuwania napędu
unlocked.label.statsEncrypted = Zaszyfrowany
unlocked.label.statsDecrypted = Odszyfrowany
unlocked.ioGraph.yAxis.label = Wydajność (MiB/s)
# mac_warnings.fxml
macWarnings.windowTitle = Uwaga - Plik uszkodzony %s
macWarnings.message = Wykryto zagrożenie w plikach
macWarnings.moreInformationButton = Czytaj więcej
macWarnings.whitelistButton = Odszyfruj wszystko
# settings.fxml
settings.version.label = Wersja %s
settings.checkForUpdates.label = Sprawdź aktualizacje
settings.port.label = WebDAV port *
settings.port.prompt = 0 \= Automatycznie
settings.useipv6.label = Użyj IPv6
settings.requiresRestartLabel = * Cryptomator wymaga restartu
# tray icon
tray.menu.open = Otwórz
tray.menu.quit = Wyjdź
tray.infoMsg.title = Nadal działa
tray.infoMsg.msg = Cryptomator działa nadal. Wyjdź z ikony tray
tray.infoMsg.msg.osx = Cryptomator działa nadal. Wyjdź z menu programu
initialize.messageLabel.passwordStrength.0 = Bardzo słabe
initialize.messageLabel.passwordStrength.1 = Słabe
initialize.messageLabel.passwordStrength.2 = W porządku
initialize.messageLabel.passwordStrength.3 = Mocne
initialize.messageLabel.passwordStrength.4 = Bardzo mocne
initialize.label.doNotForget = Ważne\! Jeśli zapomnisz hasło stracisz dostęp do danych
main.directoryList.remove.confirmation.title = Usuń kryptę
main.directoryList.remove.confirmation.header = Czy na pewno chcesz osunąć kryptę?
main.directoryList.remove.confirmation.content = Ta krypta zostanie usunięta z listy. Ostatecznie usuń dane z dysku.
upgrade.version3to4.msg = Krypta wymaga migracji do nowszej wersji. Nazwa folderu zaszyfrowanego zostanie zaktualizowana. Synchronizuj dane przed procesem.
upgrade.version3to4.err.io = Błąd migracji I/O Exception. Sprawdź logi
settings.prefGvfsScheme.label = Schemat WebDAV
# upgrade.fxml
upgrade.confirmation.label = Tak, jestem pewien, że zakończyłem synchronizacje
initialize.messageLabel.notEmpty = Vault not empty
unlock.label.savePassword = Save password
unlock.errorMessage.unauthenticVersionMac = Could not authenticate version MAC.
+73 -17
View File
@@ -1,34 +1,90 @@
# Copyright (c) 2016 The Cryptomator Contributors
# This file is licensed under the terms of the MIT license.
# See the LICENSE.txt file for more info.
#
# Contributors:
# Will
# André Di Biasi
app.name = Cryptomator
# main.fxml
main.emptyListInstructions = Clique aqui para adicionar uma caixa-forte
main.directoryList.contextMenu.remove = Remover da lista
main.directoryList.contextMenu.changePassword = Alterar senha
main.addDirectory.contextMenu.new = Criar nova caixa-forte
main.addDirectory.contextMenu.open = Abrir caixa-forte existente
# welcome.fxml
welcome.checkForUpdates.label.currentlyChecking = Procurando atualizações...
welcome.checkForUpdates.label.currentlyChecking = Buscando por atualizações...
welcome.newVersionMessage = Versão %1$s pode ser abaixada. Esta é a versão %2$s.
# initialize.fxml
initialize.label.password = Senha
initialize.label.retypePassword = Digite a senha novamente
initialize.label.retypePassword = Redigitar senha
initialize.button.ok = Criar caixa-forte
initialize.messageLabel.alreadyInitialized = Caixa-forte já inicializada
initialize.messageLabel.initializationFailed = Não pôde inicializar a caixa-forte. Ver arquivo de log para mais detalhes.
# notfound.fxml
notfound.label = A caixa-forte não pode ser encontrada. Ela foi movida?
# upgrade.fxml
upgrade.button = Atualizar caixa-forte
upgrade.version3dropBundleExtension.msg = Esta caixa-forte tem que ser a migrada a um novo formato. "%1$s" será renomeada para "%2$s". Por favor assegure-se de que a sincronização foi finalizada antes de proceder com a migração.
upgrade.version3dropBundleExtension.err.alreadyExists = A migração automática falhou. "%s" já existe.
# unlock.fxml
unlock.label.password = Senha
unlock.label.mountName = Nome do drive
unlock.label.winDriveLetter = Letra do drive
unlock.label.downloadsPageLink = Todas as versões do Cryptomator
unlock.label.advancedHeading = Opções avançadas
unlock.button.unlock = Abrir caixa-forte
unlock.button.advancedOptions.show = Mais opções
unlock.button.advancedOptions.hide = Menos opções
unlock.choicebox.winDriveLetter.auto = Atribuir automaticamente
unlock.errorMessage.wrongPassword = Senha incorreta
unlock.errorMessage.mountingFailed = Montagem falhou. Veja o arquivo de log para detalhes
unlock.messageLabel.startServerFailed = Inicialização do servidor WEBDAV falhou
unlock.errorMessage.wrongPassword = Senha errada
unlock.errorMessage.mountingFailed = Falha ao montar. Ver arquivo de log para mais detalhes.
unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware = Caixa-forte não suportada. Esta caixa-forte foi criada por uma versão antiga do Cryptomator.
unlock.errorMessage.unsupportedVersion.softwareOlderThanVault = Caixa-forte não suportada. Esta caixa-forte foi criada por uma versão mais nova do Cryptomator.
unlock.messageLabel.startServerFailed = Falha ao iniciar servidor WebDAV.
# change_password.fxml
changePassword.label.oldPassword = Senha antiga
changePassword.label.oldPassword = Senha anterior
changePassword.label.newPassword = Nova senha
changePassword.label.retypePassword = Digite a senha novamente
changePassword.label.retypePassword = Re-digitar senha
changePassword.label.downloadsPageLink = Todas as versões do Cryptomator
changePassword.button.change = Alterar senha
changePassword.errorMessage.wrongPassword = Senha incorreta
changePassword.errorMessage.decryptionFailed = Falha ao desencriptar
changePassword.infoMessage.success = Senha alterada
unlocked.label.revealFailed = Falha no comando
unlocked.label.unmountFailed = Falha ao ejetar o drive
# unlocked.fxml
unlocked.button.lock = Fechar caixa-forte
unlocked.moreOptions.reveal = Mostrar drive
unlocked.moreOptions.copyUrl = Copiar URL WebDAV
unlocked.label.revealFailed = Falha ao mostrar drive
unlocked.label.unmountFailed = Falha ao ejetar drive
unlocked.label.statsEncrypted = encriptado
unlocked.label.statsDecrypted = desencriptado
unlocked.ioGraph.yAxis.label = Taxa de transferência (MiB/s)
# mac_warnings.fxml
macWarnings.windowTitle = Perigo - Arquivo corrompido na %s
macWarnings.message = Cryptomator detectou potenciais corrupções maliciosas nos seguintes arquivos\:
macWarnings.moreInformationButton = Saiba mais
macWarnings.whitelistButton = Desencriptar selecionado de qualquer modo
# settings.fxml
settings.version.label = Versão %s
settings.checkForUpdates.label = Verificar por atualizações
settings.port.label = Port WebDAV *
settings.port.prompt = 0 \= Escolher automaticamente
settings.useipv6.label = Usar IPv6 literal
settings.requiresRestartLabel = * Cryptomator necessita ser reiniciado
# tray icon
tray.menu.open = Abrir
tray.menu.quit = Sair
tray.infoMsg.title = Ainda em execução
tray.infoMsg.msg = Cryptomator ainda está ativo. Finalize-o a partir do ícone da bandeja.
tray.infoMsg.msg.osx = Cryptomator ainda está ativo. Finalize-o a partir do ícone da barra de menu.
initialize.messageLabel.passwordStrength.0 = Muito fraca
initialize.messageLabel.passwordStrength.1 = Fraca
initialize.messageLabel.passwordStrength.2 = Razoável
initialize.messageLabel.passwordStrength.3 = Forte
initialize.messageLabel.passwordStrength.4 = Muito forte
initialize.label.doNotForget = IMPORTANTE\: se você esquecer a sua senha, não haverá como recuperar os seus dados.
main.directoryList.remove.confirmation.title = Remover caixa-forte
main.directoryList.remove.confirmation.header = Você realmente quer remover esta caixa-forte?
main.directoryList.remove.confirmation.content = A caixa-forte somente será removida da lista. Para apagá-la permanentemente, por favor apagar os arquivos do seu sistema de arquivos.
upgrade.version3to4.msg = Esta caixa-forte tem que ser migrada a um novo formato. Os nomes das pastas encriptadas serão atualizados. Por favor assegure-se de que a sincronização foi finalizada antes de proceder com a migração.
upgrade.version3to4.err.io = Falha na migração devido a um erro de E/S. Ver arquivo de log para mais detalhes.
settings.prefGvfsScheme.label = WebDAV scheme
# upgrade.fxml
upgrade.confirmation.label = Yes, I've made sure that synchronization has finished
initialize.messageLabel.notEmpty = Vault not empty
unlock.label.savePassword = Save password
unlock.errorMessage.unauthenticVersionMac = Could not authenticate version MAC.
@@ -83,4 +83,10 @@ main.directoryList.remove.confirmation.title = Удаление хранилищ
main.directoryList.remove.confirmation.header = Вы действительно хотите удалить это хранилище?
main.directoryList.remove.confirmation.content = Хранилище будет удалено только из списка. Чтобы стереть его окончательно, удалите файлы из файловой системы.
upgrade.version3to4.msg = Это хранилище требуется преобразовать в новый формат. Зашифрованные имена папок будут обновлены. Прежде чем продолжить, выполните синхронизацию.
upgrade.version3to4.err.io = Преобразование не выполнено из-за ошибки ввода-вывода. См. подробности в файле-отчёте.
upgrade.version3to4.err.io = Преобразование не выполнено из-за ошибки ввода-вывода. См. подробности в файле-отчёте.
settings.prefGvfsScheme.label = Схема WebDAV
# upgrade.fxml
upgrade.confirmation.label = Да, синхронизация точно завершена
initialize.messageLabel.notEmpty = Vault not empty
unlock.label.savePassword = Save password
unlock.errorMessage.unauthenticVersionMac = Could not authenticate version MAC.
+19 -2
View File
@@ -22,7 +22,7 @@ notfound.label = Trezor nemohol byť nenájdený. Bol presunutý?
# upgrade.fxml
upgrade.button = Upgradnúť trezor
upgrade.version3dropBundleExtension.msg = Tento trezor musí byť premigrovaný na nový formát. "%1$s" bude premenovaný na "%2$s". Prosím, uistite sa že je dokončená synchronizácia skôr než budete pokračovať.
upgrade.version3dropBundleExtension.err.alreadyExists = Automatická migrácia zlyhala. "%s" už existuje.
upgrade.version3dropBundleExtension.err.alreadyExists = Automatická migrácia zlyhala. "%s" už existuje.
# unlock.fxml
unlock.label.password = Heslo
unlock.label.mountName = Názov jednotky
@@ -73,4 +73,21 @@ tray.menu.open = Otvoriť
tray.menu.quit = Vypnúť
tray.infoMsg.title = Stále beží
tray.infoMsg.msg = Cryptomator je stále spustený. Vypnite ho pomocou ikony v systémovej lište.
tray.infoMsg.msg.osx = Cryptomator je stále sputený. Ukončite ho pomocou ikony v menu.
tray.infoMsg.msg.osx = Cryptomator je stále sputený. Ukončite ho pomocou ikony v menu.
initialize.messageLabel.passwordStrength.0 = Very weak
initialize.messageLabel.passwordStrength.1 = Weak
initialize.messageLabel.passwordStrength.2 = Fair
initialize.messageLabel.passwordStrength.3 = Strong
initialize.messageLabel.passwordStrength.4 = Very strong
initialize.label.doNotForget = IMPORTANT\: If you forget your password, there is no way to recover your data.
main.directoryList.remove.confirmation.title = Remove Vault
main.directoryList.remove.confirmation.header = Do you really want to remove this vault?
main.directoryList.remove.confirmation.content = The vault will only be removed from the list. To permanently delete it, please delete the files from your filesystem.
upgrade.version3to4.msg = This vault needs to be migrated to a newer format.\nEncrypted folder names will be updated.\nPlease make sure synchronization has finished before proceeding.
upgrade.version3to4.err.io = Migration failed due to an I/O Exception. See log file for details.
settings.prefGvfsScheme.label = WebDAV scheme
# upgrade.fxml
upgrade.confirmation.label = Yes, I've made sure that synchronization has finished
initialize.messageLabel.notEmpty = Vault not empty
unlock.label.savePassword = Save password
unlock.errorMessage.unauthenticVersionMac = Could not authenticate version MAC.
@@ -81,4 +81,10 @@ main.directoryList.remove.confirmation.title = Kasayı Sil
main.directoryList.remove.confirmation.header = Kasayı silmek istediğinize emin misiniz ?
main.directoryList.remove.confirmation.content = Kasa yalnızca listeden silinecek. Tamamen silmek için dosya sisteminizden dosyaları elle siliniz.
upgrade.version3to4.msg = Bu kasanın yeni formata geçirilmesi gerekmekte. Şifreli klasör isimleri güncellenecek. Devam etmeden önce senkronizasyonun bittiğine emin olun.
upgrade.version3to4.err.io = Format değiştirme işlemi I/O Hatası dolayısı ile başarısız oldu. Detaylar için log dosyasına bakın
upgrade.version3to4.err.io = Format değiştirme işlemi I/O Hatası dolayısı ile başarısız oldu. Detaylar için log dosyasına bakın
settings.prefGvfsScheme.label = WebDAV scheme
# upgrade.fxml
upgrade.confirmation.label = Yes, I've made sure that synchronization has finished
initialize.messageLabel.notEmpty = Vault not empty
unlock.label.savePassword = Save password
unlock.errorMessage.unauthenticVersionMac = Could not authenticate version MAC.
@@ -0,0 +1,90 @@
app.name = Cryptomator
# main.fxml
main.emptyListInstructions = Натисніть тут, щоб додати сховище
main.directoryList.contextMenu.remove = Видалити зі списку
main.directoryList.contextMenu.changePassword = Змінити пароль
main.addDirectory.contextMenu.new = Створити нове сховище
main.addDirectory.contextMenu.open = Відкрити існуюче сховище
# welcome.fxml
welcome.checkForUpdates.label.currentlyChecking = Перевірка оновлень...
welcome.newVersionMessage = Доступна версія %1$s.\nВаша версія %2$s.
# initialize.fxml
initialize.label.password = Пароль
initialize.label.retypePassword = Повторіть пароль
initialize.button.ok = Створити сховище
initialize.messageLabel.alreadyInitialized = Сховище вже ініціалізовано
initialize.messageLabel.initializationFailed = Неможливо ініціалізувати сховище. Дивіться деталі в файлі-звіті.
# notfound.fxml
notfound.label = Сховище не знайдене. Воно було переміщене?
# upgrade.fxml
upgrade.button = Оновити сховище
upgrade.version3dropBundleExtension.msg = Це сховище необхідно перетворити в новіший формат.\n"%1$s" буде перейменовано в "%2$s".\nПереконайтеся, що синхронізація завершена, перш ніж продовжити.
upgrade.version3dropBundleExtension.err.alreadyExists = Автоматичне перетворення не виконане.\n"%s" вже існує.
# unlock.fxml
unlock.label.password = Пароль
unlock.label.mountName = Ім'я диска
unlock.label.winDriveLetter = Літера диска
unlock.label.downloadsPageLink = Всі версії Cryptomator
unlock.label.advancedHeading = Додаткові налаштування
unlock.button.unlock = Розблокувати сховище
unlock.button.advancedOptions.show = Більше налаштувань
unlock.button.advancedOptions.hide = Менше налаштувань
unlock.choicebox.winDriveLetter.auto = Призначити автоматично
unlock.errorMessage.wrongPassword = Пароль невірний
unlock.errorMessage.mountingFailed = Монтування невдале. Дивіться деталі в файлі-звіті.
unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware = Сховище не підтримується. Воно було створене в старішій версії Cryptomator.
unlock.errorMessage.unsupportedVersion.softwareOlderThanVault = Сховище не підтримується. Воно було створене в новішій версії Cryptomator.
unlock.messageLabel.startServerFailed = Помилка запуску сервера WebDAV.
# change_password.fxml
changePassword.label.oldPassword = Старий пароль
changePassword.label.newPassword = Новий пароль
changePassword.label.retypePassword = Повторіть пароль
changePassword.label.downloadsPageLink = Всі версії Cryptomator
changePassword.button.change = Змінити пароль
changePassword.errorMessage.wrongPassword = Пароль невірний
changePassword.errorMessage.decryptionFailed = Розшифрування невдале
changePassword.infoMessage.success = Пароль змінений
# unlocked.fxml
unlocked.button.lock = Заблокувати сховище
unlocked.moreOptions.reveal = Відкрити накопичувач
unlocked.moreOptions.copyUrl = Копіювати URL-адресу WebDAV
unlocked.label.revealFailed = Команда невдала
unlocked.label.unmountFailed = Вилучення диска невдале
unlocked.label.statsEncrypted = зашифровано
unlocked.label.statsDecrypted = розшифровано
unlocked.ioGraph.yAxis.label = Пропускна спроможність (МіБ/с)
# mac_warnings.fxml
macWarnings.windowTitle = Небезпека - Пошкоджений файл в %s
macWarnings.message = Cryptomator виявив потенційно небезпечні пошкодження в наступних файлах\:
macWarnings.moreInformationButton = Докладніше
macWarnings.whitelistButton = Розшифрувати обрані все одно
# settings.fxml
settings.version.label = Версія %s
settings.checkForUpdates.label = Перевірити оновлення
settings.port.label = Порт WebDAV *
settings.port.prompt = 0 \= Обрати автоматично
settings.useipv6.label = Використовувати IPv6
settings.requiresRestartLabel = * Необхідне перезавантаження Cryptomator
# tray icon
tray.menu.open = Відкрити
tray.menu.quit = Вихід
tray.infoMsg.title = Ще працює
tray.infoMsg.msg = Cryptomator все ще працює. Вийдіть за допомогою значка в області сповіщень.
tray.infoMsg.msg.osx = Cryptomator все ще працює. Вийдіть за допомогою значка в рядку меню.
initialize.messageLabel.passwordStrength.0 = Дуже слабкий
initialize.messageLabel.passwordStrength.1 = Слабкий
initialize.messageLabel.passwordStrength.2 = Прийнятний
initialize.messageLabel.passwordStrength.3 = Сильний
initialize.messageLabel.passwordStrength.4 = Дуже сильний
initialize.label.doNotForget = ВАЖЛИВО\: Якщо ви забудете свій пароль, відновити дані буде неможливо.
main.directoryList.remove.confirmation.title = Видалити сховище
main.directoryList.remove.confirmation.header = Ви дійсно хочете видалити це сховище?
main.directoryList.remove.confirmation.content = Сховище буде видалене тільки зі списку. Щоб стерти його остаточно, видаліть файли із файлової системи.
upgrade.version3to4.msg = Це сховище необхідно перетворити в новий формат. Зашифровані імена папок будуть оновлені. Перш ніж продовжити, виконайте синхронізацію.
upgrade.version3to4.err.io = Перетворення невдале через помилку введення-виведення. Дивіться деталі в файлі-звіті.
settings.prefGvfsScheme.label = WebDAV scheme
# upgrade.fxml
upgrade.confirmation.label = Yes, I've made sure that synchronization has finished
initialize.messageLabel.notEmpty = Vault not empty
unlock.label.savePassword = Save password
unlock.errorMessage.unauthenticVersionMac = Could not authenticate version MAC.
+13 -7
View File
@@ -10,7 +10,7 @@
<Configuration status="WARN" packages="org.cryptomator.ui.logging">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<Console name="StdOut" target="SYSTEM_OUT">
<PatternLayout pattern="%16d %-5p [%c{1}:%L] %m%n" />
<ThresholdFilter level="WARN" onMatch="DENY" onMismatch="ACCEPT" />
</Console>
@@ -18,21 +18,27 @@
<PatternLayout pattern="%16d %-5p [%c{1}:%L] %m%n" />
<ThresholdFilter level="WARN" onMatch="ACCEPT" onMismatch="DENY" />
</Console>
<!-- <File fileName="${sys:logPath}" ...> not feasible for paths like ~/foo/bar -->
<ConfigurableFile name="File" pathPropertyName="cryptomator.logPath">
<ConfigurableFile name="DefaultLog" pathPropertyName="cryptomator.logPath" append="false">
<PatternLayout pattern="%16d %-5p [%c{1}:%L] %m%n" />
</ConfigurableFile>
<ConfigurableFile name="UpgradeLog" pathPropertyName="cryptomator.upgradeLogPath" append="true">
<PatternLayout pattern="%16d %-5p [%c{1}:%L] %m%n" />
</ConfigurableFile>
</Appenders>
<Loggers>
<!-- show our own debug messages: -->
<!-- package-specific config: -->
<Logger name="org.cryptomator" level="DEBUG" />
<Logger name="org.eclipse.jetty.server.Server" level="DEBUG" />
<!-- mute dependencies: -->
<Logger name="org.cryptomator.ui.model" level="INFO">
<AppenderRef ref="UpgradeLog" />
</Logger>
<!-- defaults: -->
<Root level="INFO">
<AppenderRef ref="Console" />
<AppenderRef ref="StdOut" />
<AppenderRef ref="StdErr" />
<AppenderRef ref="File" />
<AppenderRef ref="DefaultLog" />
</Root>
</Loggers>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 369 B

After

Width:  |  Height:  |  Size: 350 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 783 B

After

Width:  |  Height:  |  Size: 692 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 362 B

After

Width:  |  Height:  |  Size: 359 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 792 B

After

Width:  |  Height:  |  Size: 707 B

@@ -31,7 +31,7 @@ public class LocalizationTest {
private static final Logger LOG = LoggerFactory.getLogger(LocalizationTest.class);
private static final String RESOURCE_FOLDER_PATH = "/localization/";
private static final String REF_FILE_NAME = "en.txt";
private static final String[] LANG_FILE_NAMES = {"de.txt", "es.txt", "fr.txt", "hu.txt", "it.txt", "kr.txt", "nl.txt", "pt.txt", "ru.txt", "sk.txt", "tr.txt"};
private static final String[] LANG_FILE_NAMES = {"de.txt", "es.txt", "fr.txt", "hu.txt", "it.txt", "kr.txt", "lv.txt", "nl.txt", "pl.txt", "pt.txt", "ru.txt", "sk.txt", "tr.txt", "uk.txt"};
/*
* @see Formatter