mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-04-21 01:56:55 -04:00
Merge branch 'develop' into feature/hub
# Conflicts: # dist/mac/dmg/build.sh # dist/win/build.ps1 # pom.xml
This commit is contained in:
92
src/main/java/org/cryptomator/common/CatchingExecutors.java
Normal file
92
src/main/java/org/cryptomator/common/CatchingExecutors.java
Normal file
@@ -0,0 +1,92 @@
|
||||
package org.cryptomator.common;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.concurrent.Task;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
//Inspired by: https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/ThreadPoolExecutor.html#afterExecute(java.lang.Runnable,java.lang.Throwable)
|
||||
public final class CatchingExecutors {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CatchingExecutors.class);
|
||||
|
||||
private CatchingExecutors() { /* NO-OP */ }
|
||||
|
||||
public static class CatchingScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor {
|
||||
|
||||
public CatchingScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory) {
|
||||
super(corePoolSize, threadFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterExecute(Runnable runnable, Throwable throwable) {
|
||||
super.afterExecute(runnable, throwable);
|
||||
afterExecuteInternal(runnable, throwable);
|
||||
}
|
||||
}
|
||||
|
||||
public static class CatchingThreadPoolExecutor extends ThreadPoolExecutor {
|
||||
|
||||
public CatchingThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
|
||||
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterExecute(Runnable runnable, Throwable throwable) {
|
||||
super.afterExecute(runnable, throwable);
|
||||
afterExecuteInternal(runnable, throwable);
|
||||
}
|
||||
}
|
||||
|
||||
private static void afterExecuteInternal(Runnable runnable, Throwable throwable) {
|
||||
if (throwable != null) {
|
||||
callHandler(Thread.currentThread(), throwable);
|
||||
} else if (runnable instanceof Task<?> t) {
|
||||
afterExecuteTask(t);
|
||||
} else if (runnable instanceof Future<?> f) {
|
||||
afterExecuteFuture(f);
|
||||
}
|
||||
//Errors in this method are delegated to the UncaughtExceptionHandler of the current thread
|
||||
}
|
||||
|
||||
private static void callHandler(Thread thread, Throwable throwable) {
|
||||
Objects.requireNonNullElseGet(thread.getUncaughtExceptionHandler(), CatchingExecutors::fallbackHandler).uncaughtException(thread, throwable);
|
||||
}
|
||||
|
||||
private static Thread.UncaughtExceptionHandler fallbackHandler() {
|
||||
return (thread, throwable) -> LOG.error("FALLBACK: Uncaught exception in " + thread.getName(), throwable);
|
||||
}
|
||||
|
||||
private static void afterExecuteTask(Task<?> task) {
|
||||
var caller = Thread.currentThread();
|
||||
Platform.runLater(() -> {
|
||||
if (task.getOnFailed() == null) {
|
||||
callHandler(caller, task.getException());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void afterExecuteFuture(Future<?> future) {
|
||||
assert future.isDone();
|
||||
try {
|
||||
future.get();
|
||||
} catch (CancellationException ce) {
|
||||
callHandler(Thread.currentThread(), ce);
|
||||
} catch (ExecutionException ee) {
|
||||
callHandler(Thread.currentThread(), ee.getCause());
|
||||
} catch (InterruptedException ie) {
|
||||
//Ignore/Reset
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,9 +12,7 @@ import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.keychain.KeychainModule;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.common.settings.SettingsProvider;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultComponent;
|
||||
import org.cryptomator.common.vaults.VaultListManager;
|
||||
import org.cryptomator.common.vaults.VaultListModule;
|
||||
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
|
||||
import org.cryptomator.frontend.webdav.WebDavServer;
|
||||
@@ -25,16 +23,13 @@ import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import javafx.beans.binding.Binding;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.collections.ObservableList;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Comparator;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@@ -93,7 +88,7 @@ public abstract class CommonsModule {
|
||||
@Singleton
|
||||
static ScheduledExecutorService provideScheduledExecutorService(ShutdownHook shutdownHook) {
|
||||
final AtomicInteger threadNumber = new AtomicInteger(1);
|
||||
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(NUM_SCHEDULER_THREADS, r -> {
|
||||
ScheduledExecutorService executorService = new CatchingExecutors.CatchingScheduledThreadPoolExecutor(NUM_SCHEDULER_THREADS, r -> {
|
||||
String name = String.format("App Scheduled Executor %02d", threadNumber.getAndIncrement());
|
||||
Thread t = new Thread(r);
|
||||
t.setName(name);
|
||||
@@ -110,7 +105,7 @@ public abstract class CommonsModule {
|
||||
@Singleton
|
||||
static ExecutorService provideExecutorService(ShutdownHook shutdownHook) {
|
||||
final AtomicInteger threadNumber = new AtomicInteger(1);
|
||||
ExecutorService executorService = new ThreadPoolExecutor(NUM_CORE_BG_THREADS, Integer.MAX_VALUE, BG_THREAD_KEEPALIVE_SECONDS, TimeUnit.SECONDS, new SynchronousQueue<>(), r -> {
|
||||
ExecutorService executorService = new CatchingExecutors.CatchingThreadPoolExecutor(NUM_CORE_BG_THREADS, Integer.MAX_VALUE, BG_THREAD_KEEPALIVE_SECONDS, TimeUnit.SECONDS, new SynchronousQueue<>(), r -> {
|
||||
String name = String.format("App Background Thread %03d", threadNumber.getAndIncrement());
|
||||
Thread t = new Thread(r);
|
||||
t.setName(name);
|
||||
|
||||
@@ -6,6 +6,7 @@ public interface Constants {
|
||||
String MASTERKEY_BACKUP_SUFFIX = ".bkup";
|
||||
String VAULTCONFIG_FILENAME = "vault.cryptomator";
|
||||
String CRYPTOMATOR_FILENAME_EXT = ".cryptomator";
|
||||
String CRYPTOMATOR_FILENAME_GLOB = "*.cryptomator";
|
||||
byte[] PEPPER = new byte[0];
|
||||
|
||||
}
|
||||
|
||||
64
src/main/java/org/cryptomator/common/LocationPreset.java
Normal file
64
src/main/java/org/cryptomator/common/LocationPreset.java
Normal file
@@ -0,0 +1,64 @@
|
||||
package org.cryptomator.common;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Enum of common cloud providers and their default local storage location path.
|
||||
*/
|
||||
public enum LocationPreset {
|
||||
|
||||
DROPBOX("Dropbox", "~/Dropbox"),
|
||||
ICLOUDDRIVE("iCloud Drive", "~/Library/Mobile Documents/com~apple~CloudDocs", "~/iCloudDrive"),
|
||||
GDRIVE("Google Drive", "~/Google Drive/My Drive", "~/Google Drive"),
|
||||
MEGA("MEGA", "~/MEGA"),
|
||||
ONEDRIVE("OneDrive", "~/OneDrive"),
|
||||
PCLOUD("pCloud", "~/pCloudDrive"),
|
||||
|
||||
LOCAL("local");
|
||||
|
||||
private final String name;
|
||||
private final List<Path> candidates;
|
||||
|
||||
LocationPreset(String name, String... candidates) {
|
||||
this.name = name;
|
||||
this.candidates = Arrays.stream(candidates).map(UserHome::resolve).map(Path::of).toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for this LocationPreset if any of the associated paths exist.
|
||||
*
|
||||
* @return the first existing path or null, if none exists.
|
||||
*/
|
||||
public Path existingPath() {
|
||||
return candidates.stream().filter(Files::isDirectory).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getDisplayName();
|
||||
}
|
||||
|
||||
//this contruct is needed, since static members are initialized after every enum member is initialized
|
||||
//TODO: refactor this to normal class and use this also in different parts of the project
|
||||
private static class UserHome {
|
||||
|
||||
private static final String USER_HOME = System.getProperty("user.home");
|
||||
|
||||
private static String resolve(String path) {
|
||||
if (path.startsWith("~/")) {
|
||||
return UserHome.USER_HOME + path.substring(1);
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ public class Settings {
|
||||
public static final int DEFAULT_NUM_TRAY_NOTIFICATIONS = 3;
|
||||
public static final WebDavUrlScheme DEFAULT_GVFS_SCHEME = WebDavUrlScheme.DAV;
|
||||
public static final boolean DEFAULT_DEBUG_MODE = false;
|
||||
public static final VolumeImpl DEFAULT_PREFERRED_VOLUME_IMPL = SystemUtils.IS_OS_WINDOWS ? VolumeImpl.DOKANY : VolumeImpl.FUSE;
|
||||
public static final VolumeImpl DEFAULT_PREFERRED_VOLUME_IMPL = VolumeImpl.FUSE;
|
||||
public static final UiTheme DEFAULT_THEME = UiTheme.LIGHT;
|
||||
@Deprecated // to be changed to "whatever is available" eventually
|
||||
public static final String DEFAULT_KEYCHAIN_PROVIDER = SystemUtils.IS_OS_WINDOWS ? "org.cryptomator.windows.keychain.WindowsProtectedKeychainAccess" : SystemUtils.IS_OS_MAC ? "org.cryptomator.macos.keychain.MacSystemKeychainAccess" : "org.cryptomator.linux.keychain.SecretServiceKeychainAccess";
|
||||
|
||||
@@ -10,6 +10,7 @@ package org.cryptomator.common.vaults;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.Constants;
|
||||
import org.cryptomator.common.mountpoint.InvalidMountPointException;
|
||||
import org.cryptomator.common.settings.VaultSettings;
|
||||
import org.cryptomator.common.vaults.Volume.VolumeException;
|
||||
@@ -125,6 +126,7 @@ public class Vault {
|
||||
.withKeyLoader(keyLoader) //
|
||||
.withFlags(flags) //
|
||||
.withMaxCleartextNameLength(vaultSettings.maxCleartextFilenameLength().get()) //
|
||||
.withVaultConfigFilename(Constants.VAULTCONFIG_FILENAME) //
|
||||
.build();
|
||||
return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps);
|
||||
}
|
||||
|
||||
@@ -19,22 +19,13 @@ public interface VaultComponent {
|
||||
|
||||
Vault vault();
|
||||
|
||||
@Subcomponent.Builder
|
||||
interface Builder {
|
||||
@Subcomponent.Factory
|
||||
interface Factory {
|
||||
|
||||
@BindsInstance
|
||||
Builder vaultSettings(VaultSettings vaultSettings);
|
||||
VaultComponent create(@BindsInstance VaultSettings vaultSettings, //
|
||||
@BindsInstance VaultConfigCache configCache, //
|
||||
@BindsInstance VaultState.Value vaultState, //
|
||||
@BindsInstance @Nullable @Named("lastKnownException") Exception initialErrorCause);
|
||||
|
||||
@BindsInstance
|
||||
Builder vaultConfigCache(VaultConfigCache configCache);
|
||||
|
||||
@BindsInstance
|
||||
Builder initialVaultState(VaultState.Value vaultState);
|
||||
|
||||
@BindsInstance
|
||||
Builder initialErrorCause(@Nullable @Named("lastKnownException") Exception initialErrorCause);
|
||||
|
||||
VaultComponent build();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -38,15 +38,15 @@ public class VaultListManager {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(VaultListManager.class);
|
||||
|
||||
private final AutoLocker autoLocker;
|
||||
private final VaultComponent.Builder vaultComponentBuilder;
|
||||
private final VaultComponent.Factory vaultComponentFactory;
|
||||
private final ObservableList<Vault> vaultList;
|
||||
private final String defaultVaultName;
|
||||
|
||||
@Inject
|
||||
public VaultListManager(ObservableList<Vault> vaultList, AutoLocker autoLocker, VaultComponent.Builder vaultComponentBuilder, ResourceBundle resourceBundle, Settings settings) {
|
||||
public VaultListManager(ObservableList<Vault> vaultList, AutoLocker autoLocker, VaultComponent.Factory vaultComponentFactory, ResourceBundle resourceBundle, Settings settings) {
|
||||
this.vaultList = vaultList;
|
||||
this.autoLocker = autoLocker;
|
||||
this.vaultComponentBuilder = vaultComponentBuilder;
|
||||
this.vaultComponentFactory = vaultComponentFactory;
|
||||
this.defaultVaultName = resourceBundle.getString("defaults.vault.vaultName");
|
||||
|
||||
addAll(settings.getDirectories());
|
||||
@@ -93,21 +93,17 @@ public class VaultListManager {
|
||||
}
|
||||
|
||||
private Vault create(VaultSettings vaultSettings) {
|
||||
VaultComponent.Builder compBuilder = vaultComponentBuilder.vaultSettings(vaultSettings);
|
||||
var wrapper = new VaultConfigCache(vaultSettings);
|
||||
try {
|
||||
VaultState.Value vaultState = determineVaultState(vaultSettings.path().get());
|
||||
VaultConfigCache wrapper = new VaultConfigCache(vaultSettings);
|
||||
compBuilder.vaultConfigCache(wrapper); //first set the wrapper in the builder, THEN try to load config
|
||||
var vaultState = determineVaultState(vaultSettings.path().get());
|
||||
if (vaultState == LOCKED) { //for legacy reasons: pre v8 vault do not have a config, but they are in the NEEDS_MIGRATION state
|
||||
wrapper.reloadConfig();
|
||||
}
|
||||
compBuilder.initialVaultState(vaultState);
|
||||
return vaultComponentFactory.create(vaultSettings, wrapper, vaultState, null).vault();
|
||||
} catch (IOException e) {
|
||||
LOG.warn("Failed to determine vault state for " + vaultSettings.path().get(), e);
|
||||
compBuilder.initialVaultState(ERROR);
|
||||
compBuilder.initialErrorCause(e);
|
||||
return vaultComponentFactory.create(vaultSettings, wrapper, ERROR, e).vault();
|
||||
}
|
||||
return compBuilder.build().vault();
|
||||
}
|
||||
|
||||
public static VaultState.Value redetermineVaultState(Vault vault) {
|
||||
|
||||
@@ -83,7 +83,7 @@ public class VaultState extends ObservableValueBase<VaultState.Value> implements
|
||||
if (success) {
|
||||
fireValueChangedEvent();
|
||||
} else {
|
||||
LOG.debug("Failed transiting into state {}: Expected state was not{}.", fromState, toState);
|
||||
LOG.debug("Failed transiting into state {}: Expected state was not {}.", fromState, toState);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ public class SupportedLanguages {
|
||||
// these are BCP 47 language codes, not ISO. Note the "-" instead of the "_":
|
||||
public static final List<String> LANGUAGAE_TAGS = List.of("en", "ar", "bn", "bs", "ca", "cs", "de", "el", "es", "fil", "fr", "gl", "he", //
|
||||
"hi", "hr", "hu", "id", "it", "ja", "ko", "lv", "mk", "nb", "nl", "nn", "no", "pa", "pl", "pt", "pt-BR", "ro", "ru", "sk", "sr", //
|
||||
"sr-Latn", "sv", "ta", "te", "th", "tr", "uk", "zh", "zh-HK", "zh-TW");
|
||||
"sr-Latn", "sv", "sw", "ta", "te", "th", "tr", "uk", "zh", "zh-HK", "zh-TW");
|
||||
|
||||
@Nullable
|
||||
private final String preferredLanguage;
|
||||
|
||||
@@ -25,6 +25,8 @@ import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import static org.cryptomator.common.Constants.CRYPTOMATOR_FILENAME_GLOB;
|
||||
|
||||
@AddVaultWizardScoped
|
||||
public class ChooseExistingVaultController implements FxController {
|
||||
|
||||
@@ -73,7 +75,7 @@ public class ChooseExistingVaultController implements FxController {
|
||||
public void chooseFileAndNext() {
|
||||
FileChooser fileChooser = new FileChooser();
|
||||
fileChooser.setTitle(resourceBundle.getString("addvaultwizard.existing.filePickerTitle"));
|
||||
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Cryptomator Vault", "*.cryptomator"));
|
||||
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(resourceBundle.getString("addvaultwizard.existing.filePickerMimeDesc"), CRYPTOMATOR_FILENAME_GLOB));
|
||||
File masterkeyFile = fileChooser.showOpenDialog(window);
|
||||
if (masterkeyFile != null) {
|
||||
vaultPath.setValue(masterkeyFile.toPath().toAbsolutePath().getParent());
|
||||
|
||||
@@ -46,7 +46,7 @@ public class CreateNewVaultLocationController implements FxController {
|
||||
private final Stage window;
|
||||
private final Lazy<Scene> chooseNameScene;
|
||||
private final Lazy<Scene> choosePasswordScene;
|
||||
private final LocationPresets locationPresets;
|
||||
private final ObservedLocationPresets locationPresets;
|
||||
private final ObjectProperty<Path> vaultPath;
|
||||
private final StringProperty vaultName;
|
||||
private final ResourceBundle resourceBundle;
|
||||
@@ -71,7 +71,7 @@ public class CreateNewVaultLocationController implements FxController {
|
||||
public FontAwesome5IconView badLocation;
|
||||
|
||||
@Inject
|
||||
CreateNewVaultLocationController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_NAME) Lazy<Scene> chooseNameScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_PASSWORD) Lazy<Scene> choosePasswordScene, LocationPresets locationPresets, ObjectProperty<Path> vaultPath, @Named("vaultName") StringProperty vaultName, ResourceBundle resourceBundle) {
|
||||
CreateNewVaultLocationController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_NAME) Lazy<Scene> chooseNameScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_PASSWORD) Lazy<Scene> choosePasswordScene, ObservedLocationPresets locationPresets, ObjectProperty<Path> vaultPath, @Named("vaultName") StringProperty vaultName, ResourceBundle resourceBundle) {
|
||||
this.window = window;
|
||||
this.chooseNameScene = chooseNameScene;
|
||||
this.choosePasswordScene = choosePasswordScene;
|
||||
@@ -197,7 +197,7 @@ public class CreateNewVaultLocationController implements FxController {
|
||||
return validVaultPath.get();
|
||||
}
|
||||
|
||||
public LocationPresets getLocationPresets() {
|
||||
public ObservedLocationPresets getObservedLocationPresets() {
|
||||
return locationPresets;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,15 @@
|
||||
package org.cryptomator.ui.addvaultwizard;
|
||||
|
||||
import org.cryptomator.common.LocationPreset;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.beans.binding.BooleanBinding;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
@AddVaultWizardScoped
|
||||
public class LocationPresets {
|
||||
|
||||
private static final String USER_HOME = System.getProperty("user.home");
|
||||
private static final String[] ICLOUDDRIVE_LOCATIONS = {"~/Library/Mobile Documents/iCloud~com~setolabs~Cryptomator/Documents", "~/iCloudDrive/iCloud~com~setolabs~Cryptomator"};
|
||||
private static final String[] DROPBOX_LOCATIONS = {"~/Dropbox"};
|
||||
private static final String[] GDRIVE_LOCATIONS = {"~/Google Drive/My Drive", "~/Google Drive"};
|
||||
private static final String[] ONEDRIVE_LOCATIONS = {"~/OneDrive"};
|
||||
private static final String[] MEGA_LOCATIONS = {"~/MEGA"};
|
||||
private static final String[] PCLOUD_LOCATIONS = {"~/pCloudDrive"};
|
||||
public class ObservedLocationPresets {
|
||||
|
||||
private final ReadOnlyObjectProperty<Path> iclouddriveLocation;
|
||||
private final ReadOnlyObjectProperty<Path> dropboxLocation;
|
||||
@@ -33,13 +25,13 @@ public class LocationPresets {
|
||||
private final BooleanBinding foundPcloud;
|
||||
|
||||
@Inject
|
||||
public LocationPresets() {
|
||||
this.iclouddriveLocation = new SimpleObjectProperty<>(existingWritablePath(ICLOUDDRIVE_LOCATIONS));
|
||||
this.dropboxLocation = new SimpleObjectProperty<>(existingWritablePath(DROPBOX_LOCATIONS));
|
||||
this.gdriveLocation = new SimpleObjectProperty<>(existingWritablePath(GDRIVE_LOCATIONS));
|
||||
this.onedriveLocation = new SimpleObjectProperty<>(existingWritablePath(ONEDRIVE_LOCATIONS));
|
||||
this.megaLocation = new SimpleObjectProperty<>(existingWritablePath(MEGA_LOCATIONS));
|
||||
this.pcloudLocation = new SimpleObjectProperty<>(existingWritablePath(PCLOUD_LOCATIONS));
|
||||
public ObservedLocationPresets() {
|
||||
this.iclouddriveLocation = new SimpleObjectProperty<>(LocationPreset.ICLOUDDRIVE.existingPath());
|
||||
this.dropboxLocation = new SimpleObjectProperty<>(LocationPreset.DROPBOX.existingPath());
|
||||
this.gdriveLocation = new SimpleObjectProperty<>(LocationPreset.GDRIVE.existingPath());
|
||||
this.onedriveLocation = new SimpleObjectProperty<>(LocationPreset.ONEDRIVE.existingPath());
|
||||
this.megaLocation = new SimpleObjectProperty<>(LocationPreset.MEGA.existingPath());
|
||||
this.pcloudLocation = new SimpleObjectProperty<>(LocationPreset.PCLOUD.existingPath());
|
||||
this.foundIclouddrive = iclouddriveLocation.isNotNull();
|
||||
this.foundDropbox = dropboxLocation.isNotNull();
|
||||
this.foundGdrive = gdriveLocation.isNotNull();
|
||||
@@ -48,24 +40,6 @@ public class LocationPresets {
|
||||
this.foundPcloud = pcloudLocation.isNotNull();
|
||||
}
|
||||
|
||||
private static Path existingWritablePath(String... candidates) {
|
||||
for (String candidate : candidates) {
|
||||
Path path = Paths.get(resolveHomePath(candidate));
|
||||
if (Files.isDirectory(path)) {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String resolveHomePath(String path) {
|
||||
if (path.startsWith("~/")) {
|
||||
return USER_HOME + path.substring(1);
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
/* Observables */
|
||||
|
||||
public ReadOnlyObjectProperty<Path> iclouddriveLocationProperty() {
|
||||
@@ -43,7 +43,9 @@ public abstract class UpdateCheckerModule {
|
||||
@FxApplicationScoped
|
||||
static Optional<HttpClient> provideHttpClient() {
|
||||
try {
|
||||
return Optional.of(HttpClient.newHttpClient());
|
||||
return Optional.of(HttpClient.newBuilder() //
|
||||
.followRedirects(HttpClient.Redirect.NORMAL) // from version 1.6.11 onwards, Cryptomator can follow redirects, in case this URL ever changes
|
||||
.build());
|
||||
} catch (UncheckedIOException e) {
|
||||
LOG.error("HttpClient for update check cannot be created.", e);
|
||||
return Optional.empty();
|
||||
|
||||
@@ -15,6 +15,8 @@ import java.nio.file.Path;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static org.cryptomator.common.Constants.CRYPTOMATOR_FILENAME_GLOB;
|
||||
|
||||
@ChooseMasterkeyFileScoped
|
||||
public class ChooseMasterkeyFileController implements FxController {
|
||||
|
||||
@@ -46,7 +48,7 @@ public class ChooseMasterkeyFileController implements FxController {
|
||||
LOG.trace("proceed()");
|
||||
FileChooser fileChooser = new FileChooser();
|
||||
fileChooser.setTitle(resourceBundle.getString("unlock.chooseMasterkey.filePickerTitle"));
|
||||
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Cryptomator Masterkey", "*.cryptomator"));
|
||||
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(resourceBundle.getString("unlock.chooseMasterkey.filePickerMimeDesc"), CRYPTOMATOR_FILENAME_GLOB));
|
||||
File masterkeyFile = fileChooser.showOpenDialog(window);
|
||||
if (masterkeyFile != null) {
|
||||
LOG.debug("Chose masterkey file: {}", masterkeyFile);
|
||||
|
||||
@@ -21,13 +21,13 @@ public class VaultDetailLockedController implements FxController {
|
||||
|
||||
private final ReadOnlyObjectProperty<Vault> vault;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final VaultOptionsComponent.Builder vaultOptionsWindow;
|
||||
private final VaultOptionsComponent.Factory vaultOptionsWindow;
|
||||
private final KeychainManager keychain;
|
||||
private final Stage mainWindow;
|
||||
private final BooleanExpression passwordSaved;
|
||||
|
||||
@Inject
|
||||
VaultDetailLockedController(ObjectProperty<Vault> vault, FxApplicationWindows appWindows, VaultOptionsComponent.Builder vaultOptionsWindow, KeychainManager keychain, @MainWindow Stage mainWindow) {
|
||||
VaultDetailLockedController(ObjectProperty<Vault> vault, FxApplicationWindows appWindows, VaultOptionsComponent.Factory vaultOptionsWindow, KeychainManager keychain, @MainWindow Stage mainWindow) {
|
||||
this.vault = vault;
|
||||
this.appWindows = appWindows;
|
||||
this.vaultOptionsWindow = vaultOptionsWindow;
|
||||
@@ -47,12 +47,12 @@ public class VaultDetailLockedController implements FxController {
|
||||
|
||||
@FXML
|
||||
public void showVaultOptions() {
|
||||
vaultOptionsWindow.vault(vault.get()).build().showVaultOptionsWindow(SelectedVaultOptionsTab.ANY);
|
||||
vaultOptionsWindow.create(vault.get()).showVaultOptionsWindow(SelectedVaultOptionsTab.ANY);
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void showKeyVaultOptions() {
|
||||
vaultOptionsWindow.vault(vault.get()).build().showVaultOptionsWindow(SelectedVaultOptionsTab.KEY);
|
||||
vaultOptionsWindow.create(vault.get()).showVaultOptionsWindow(SelectedVaultOptionsTab.KEY);
|
||||
}
|
||||
|
||||
/* Getter/Setter */
|
||||
|
||||
@@ -13,6 +13,8 @@ import javafx.stage.Stage;
|
||||
import java.io.File;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import static org.cryptomator.common.Constants.CRYPTOMATOR_FILENAME_GLOB;
|
||||
|
||||
@MainWindowScoped
|
||||
public class VaultDetailMissingVaultController implements FxController {
|
||||
|
||||
@@ -45,7 +47,7 @@ public class VaultDetailMissingVaultController implements FxController {
|
||||
// copied from ChooseExistingVaultController class
|
||||
FileChooser fileChooser = new FileChooser();
|
||||
fileChooser.setTitle(resourceBundle.getString("addvaultwizard.existing.filePickerTitle"));
|
||||
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Cryptomator Masterkey", "*.cryptomator"));
|
||||
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(resourceBundle.getString("addvaultwizard.existing.filePickerMimeDesc"), CRYPTOMATOR_FILENAME_GLOB));
|
||||
File masterkeyFile = fileChooser.showOpenDialog(window);
|
||||
if (masterkeyFile != null) {
|
||||
vault.get().getVaultSettings().path().setValue(masterkeyFile.toPath().toAbsolutePath().getParent());
|
||||
|
||||
@@ -31,7 +31,7 @@ public class VaultListContextMenuController implements FxController {
|
||||
private final VaultService vaultService;
|
||||
private final KeychainManager keychain;
|
||||
private final RemoveVaultComponent.Builder removeVault;
|
||||
private final VaultOptionsComponent.Builder vaultOptionsWindow;
|
||||
private final VaultOptionsComponent.Factory vaultOptionsWindow;
|
||||
private final OptionalBinding<VaultState.Value> selectedVaultState;
|
||||
private final Binding<Boolean> selectedVaultPassphraseStored;
|
||||
private final Binding<Boolean> selectedVaultRemovable;
|
||||
@@ -39,7 +39,7 @@ public class VaultListContextMenuController implements FxController {
|
||||
private final Binding<Boolean> selectedVaultLockable;
|
||||
|
||||
@Inject
|
||||
VaultListContextMenuController(ObjectProperty<Vault> selectedVault, @MainWindow Stage mainWindow, FxApplicationWindows appWindows, VaultService vaultService, KeychainManager keychain, RemoveVaultComponent.Builder removeVault, VaultOptionsComponent.Builder vaultOptionsWindow) {
|
||||
VaultListContextMenuController(ObjectProperty<Vault> selectedVault, @MainWindow Stage mainWindow, FxApplicationWindows appWindows, VaultService vaultService, KeychainManager keychain, RemoveVaultComponent.Builder removeVault, VaultOptionsComponent.Factory vaultOptionsWindow) {
|
||||
this.selectedVault = EasyBind.wrapNullable(selectedVault);
|
||||
this.mainWindow = mainWindow;
|
||||
this.appWindows = appWindows;
|
||||
@@ -69,7 +69,7 @@ public class VaultListContextMenuController implements FxController {
|
||||
@FXML
|
||||
public void didClickShowVaultOptions() {
|
||||
selectedVault.ifValuePresent(v -> {
|
||||
vaultOptionsWindow.vault(v).build().showVaultOptionsWindow(SelectedVaultOptionsTab.ANY);
|
||||
vaultOptionsWindow.create(v).showVaultOptionsWindow(SelectedVaultOptionsTab.ANY);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -48,11 +48,6 @@ public class VolumePreferencesController implements FxController {
|
||||
webDavPortField.setText(String.valueOf(settings.port().get()));
|
||||
changeWebDavPortButton.visibleProperty().bind(settings.port().asString().isNotEqualTo(webDavPortField.textProperty()));
|
||||
changeWebDavPortButton.disableProperty().bind(Bindings.createBooleanBinding(this::validateWebDavPort, webDavPortField.textProperty()).not());
|
||||
webDavPortField.focusedProperty().addListener((observableValue, wasFocused, isFocused) -> {
|
||||
if(!isFocused) {
|
||||
webDavPortField.setText(String.valueOf(settings.port().get()));
|
||||
}
|
||||
});
|
||||
|
||||
webDavUrlSchemeChoiceBox.getItems().addAll(WebDavUrlScheme.values());
|
||||
webDavUrlSchemeChoiceBox.valueProperty().bindBidirectional(settings.preferredGvfsScheme());
|
||||
|
||||
@@ -36,13 +36,10 @@ public interface VaultOptionsComponent {
|
||||
stage.requestFocus();
|
||||
}
|
||||
|
||||
@Subcomponent.Builder
|
||||
interface Builder {
|
||||
@Subcomponent.Factory
|
||||
interface Factory {
|
||||
|
||||
@BindsInstance
|
||||
Builder vault(@VaultOptionsWindow Vault vault);
|
||||
|
||||
VaultOptionsComponent build();
|
||||
VaultOptionsComponent create(@BindsInstance @VaultOptionsWindow Vault vault);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user