diff --git a/README.md b/README.md
index 400222c7d..8872955be 100644
--- a/README.md
+++ b/README.md
@@ -53,9 +53,11 @@ For more information on the security details visit [cryptomator.org](https://cry
```
cd main
-mvn clean install
+mvn clean install -Prelease
```
+An executable jar file will be created inside `main/uber-jar/target`.
+
## Contributing to Cryptomator
Please read our [contribution guide](https://github.com/cryptomator/cryptomator/blob/master/CONTRIBUTING.md), if you would like to report a bug, ask a question or help us with coding.
diff --git a/main/ant-kit/pom.xml b/main/ant-kit/pom.xml
index 607902dba..7292a21ef 100644
--- a/main/ant-kit/pom.xml
+++ b/main/ant-kit/pom.xml
@@ -8,7 +8,7 @@
org.cryptomator
main
- 1.2.2
+ 1.2.3
ant-kit
pom
diff --git a/main/commons-test/pom.xml b/main/commons-test/pom.xml
index d53fdd079..a9bb921f9 100644
--- a/main/commons-test/pom.xml
+++ b/main/commons-test/pom.xml
@@ -10,7 +10,7 @@
org.cryptomator
main
- 1.2.2
+ 1.2.3
commons-test
Cryptomator common test dependencies
diff --git a/main/commons/pom.xml b/main/commons/pom.xml
index cda650dbe..8e3f3101f 100644
--- a/main/commons/pom.xml
+++ b/main/commons/pom.xml
@@ -10,7 +10,7 @@
org.cryptomator
main
- 1.2.2
+ 1.2.3
commons
Cryptomator common
diff --git a/main/filesystem-api/pom.xml b/main/filesystem-api/pom.xml
index 19b43b448..84d3ba083 100644
--- a/main/filesystem-api/pom.xml
+++ b/main/filesystem-api/pom.xml
@@ -9,7 +9,7 @@
org.cryptomator
main
- 1.2.2
+ 1.2.3
filesystem-api
Cryptomator filesystem: API
diff --git a/main/filesystem-charsets/pom.xml b/main/filesystem-charsets/pom.xml
index c37512ce1..127ab648a 100644
--- a/main/filesystem-charsets/pom.xml
+++ b/main/filesystem-charsets/pom.xml
@@ -12,7 +12,7 @@
org.cryptomator
main
- 1.2.2
+ 1.2.3
filesystem-charsets
Cryptomator filesystem: Charset compatibility layer
diff --git a/main/filesystem-charsets/src/main/java/org/cryptomator/filesystem/charsets/NormalizedNameFolder.java b/main/filesystem-charsets/src/main/java/org/cryptomator/filesystem/charsets/NormalizedNameFolder.java
index e2762059b..d03c42d86 100644
--- a/main/filesystem-charsets/src/main/java/org/cryptomator/filesystem/charsets/NormalizedNameFolder.java
+++ b/main/filesystem-charsets/src/main/java/org/cryptomator/filesystem/charsets/NormalizedNameFolder.java
@@ -40,9 +40,9 @@ class NormalizedNameFolder extends DelegatingFolder
org.cryptomator
main
- 1.2.2
+ 1.2.3
filesystem-crypto-integration-tests
Cryptomator filesystem: Encryption layer tests
diff --git a/main/filesystem-crypto/pom.xml b/main/filesystem-crypto/pom.xml
index 7c60a38b8..9cd80702a 100644
--- a/main/filesystem-crypto/pom.xml
+++ b/main/filesystem-crypto/pom.xml
@@ -12,7 +12,7 @@
org.cryptomator
main
- 1.2.2
+ 1.2.3
filesystem-crypto
Cryptomator filesystem: Encryption layer
diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/ConflictResolver.java b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/ConflictResolver.java
index a15c0a6fe..9ef4142bf 100644
--- a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/ConflictResolver.java
+++ b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/ConflictResolver.java
@@ -68,7 +68,7 @@ final class ConflictResolver {
String alternativeCiphertext = nameEncryptor.apply(alternativeCleartext).get();
alternativeFile = folder.file(isDirectory ? DIR_PREFIX + alternativeCiphertext : alternativeCiphertext);
} while (alternativeFile.exists());
- LOG.info("Detected conflict {}:\n{}\n{}", conflictId, canonicalFile, conflictingFile);
+ LOG.debug("Detected conflict {}:\n{}\n{}", conflictId, canonicalFile, conflictingFile);
conflictingFile.moveTo(alternativeFile);
return alternativeFile;
}
diff --git a/main/filesystem-inmemory/pom.xml b/main/filesystem-inmemory/pom.xml
index a640963ca..c52333cc9 100644
--- a/main/filesystem-inmemory/pom.xml
+++ b/main/filesystem-inmemory/pom.xml
@@ -12,7 +12,7 @@
org.cryptomator
main
- 1.2.2
+ 1.2.3
filesystem-inmemory
Cryptomator filesystem: In-memory mock
diff --git a/main/filesystem-invariants-tests/pom.xml b/main/filesystem-invariants-tests/pom.xml
index 442b4dcd0..765eb2595 100644
--- a/main/filesystem-invariants-tests/pom.xml
+++ b/main/filesystem-invariants-tests/pom.xml
@@ -9,7 +9,7 @@
org.cryptomator
main
- 1.2.2
+ 1.2.3
filesystem-invariants-tests
Cryptomator filesystem: Invariants tests
diff --git a/main/filesystem-nameshortening/pom.xml b/main/filesystem-nameshortening/pom.xml
index c2da992b3..95f58e244 100644
--- a/main/filesystem-nameshortening/pom.xml
+++ b/main/filesystem-nameshortening/pom.xml
@@ -12,7 +12,7 @@
org.cryptomator
main
- 1.2.2
+ 1.2.3
filesystem-nameshortening
Cryptomator filesystem: Name shortening layer
diff --git a/main/filesystem-nio/pom.xml b/main/filesystem-nio/pom.xml
index 834da5a2a..7b31518c4 100644
--- a/main/filesystem-nio/pom.xml
+++ b/main/filesystem-nio/pom.xml
@@ -7,7 +7,7 @@
org.cryptomator
main
- 1.2.2
+ 1.2.3
filesystem-nio
Cryptomator filesystem: NIO-based physical layer
diff --git a/main/filesystem-stats/pom.xml b/main/filesystem-stats/pom.xml
index 8d6055807..dd93fccdb 100644
--- a/main/filesystem-stats/pom.xml
+++ b/main/filesystem-stats/pom.xml
@@ -12,7 +12,7 @@
org.cryptomator
main
- 1.2.2
+ 1.2.3
filesystem-stats
Cryptomator filesystem: Throughput statistics
diff --git a/main/frontend-api/pom.xml b/main/frontend-api/pom.xml
index a95764b6e..da7ec0d45 100644
--- a/main/frontend-api/pom.xml
+++ b/main/frontend-api/pom.xml
@@ -12,7 +12,7 @@
org.cryptomator
main
- 1.2.2
+ 1.2.3
frontend-api
Cryptomator frontend: API
diff --git a/main/frontend-webdav/pom.xml b/main/frontend-webdav/pom.xml
index 0182184f3..c788b15e9 100644
--- a/main/frontend-webdav/pom.xml
+++ b/main/frontend-webdav/pom.xml
@@ -12,7 +12,7 @@
org.cryptomator
main
- 1.2.2
+ 1.2.3
frontend-webdav
Cryptomator frontend: WebDAV frontend
diff --git a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/ContextPathBuilder.java b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/ContextPathBuilder.java
index ee036d221..6e9c6a7e8 100644
--- a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/ContextPathBuilder.java
+++ b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/ContextPathBuilder.java
@@ -18,6 +18,10 @@ class ContextPaths {
return format("/%s/%s", id, name);
}
+ public static String removeFrontendId(String path) {
+ return path.replaceAll("/" + FRONTEND_ID_PATTERN + "/", "/[...]/");
+ }
+
public static Optional extractFrontendId(String path) {
Matcher matcher = SERVLET_PATH_WITH_FRONTEND_ID_PATTERN.matcher(path);
if (matcher.matches()) {
diff --git a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/FontendIdHidingServletContextHandler.java b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/FontendIdHidingServletContextHandler.java
new file mode 100644
index 000000000..9b983d9ac
--- /dev/null
+++ b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/FontendIdHidingServletContextHandler.java
@@ -0,0 +1,17 @@
+package org.cryptomator.frontend.webdav;
+
+import org.eclipse.jetty.server.HandlerContainer;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+
+class FontendIdHidingServletContextHandler extends ServletContextHandler {
+
+ public FontendIdHidingServletContextHandler(HandlerContainer parent, String contextPath, int options) {
+ super(parent, contextPath, options);
+ }
+
+ @Override
+ public String toString() {
+ return ContextPaths.removeFrontendId(super.toString());
+ }
+
+}
diff --git a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/WebDavServer.java b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/WebDavServer.java
index cf40f1ce1..81ed65087 100644
--- a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/WebDavServer.java
+++ b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/WebDavServer.java
@@ -8,8 +8,6 @@
*******************************************************************************/
package org.cryptomator.frontend.webdav;
-import static java.lang.String.format;
-
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collection;
@@ -110,7 +108,7 @@ public class WebDavServer implements FrontendFactory {
@Override
public Frontend create(Folder root, FrontendId id, String name) throws FrontendCreationFailedException {
- String contextPath = format("/%s/%s", id, name);
+ String contextPath = ContextPaths.from(id, name);
final URI uri;
try {
uri = new URI("http", null, "localhost", getPort(), contextPath, null, null);
@@ -118,10 +116,10 @@ public class WebDavServer implements FrontendFactory {
throw new IllegalStateException(e);
}
final ServletContextHandler handler = addServlet(root, uri);
- LOG.info("Servlet available under " + uri);
+ LOG.info("Servlet available under " + ContextPaths.removeFrontendId(uri.toString()));
return new WebDavFrontend(webdavMounterProvider, handler, uri);
}
-
+
public void setValidFrontendIds(Collection validFrontendIds) {
tarpit.setValidFrontendIds(validFrontendIds);
}
diff --git a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/WebDavServletContextFactory.java b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/WebDavServletContextFactory.java
index 85915d192..ca6fcbebf 100644
--- a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/WebDavServletContextFactory.java
+++ b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/WebDavServletContextFactory.java
@@ -35,9 +35,10 @@ import org.eclipse.jetty.servlet.ServletHolder;
class WebDavServletContextFactory {
private static final String WILDCARD = "/*";
-
+
@Inject
- public WebDavServletContextFactory() {}
+ public WebDavServletContextFactory() {
+ }
/**
* Creates a new Jetty ServletContextHandler, that can be be added to a servletCollection as follows:
@@ -63,7 +64,7 @@ class WebDavServletContextFactory {
}
};
final String contextPath = StringUtils.removeEnd(contextRoot.getPath(), "/");
- final ServletContextHandler servletContext = new ServletContextHandler(null, contextPath, ServletContextHandler.SESSIONS);
+ final ServletContextHandler servletContext = new FontendIdHidingServletContextHandler(null, contextPath, ServletContextHandler.SESSIONS);
final ServletHolder servletHolder = new ServletHolder(contextPath, new WebDavServlet(contextRoot, root));
servletContext.addServlet(servletHolder, WILDCARD);
servletContext.addFilter(LoopbackFilter.class, WILDCARD, EnumSet.of(DispatcherType.REQUEST));
diff --git a/main/jacoco-report/pom.xml b/main/jacoco-report/pom.xml
index 419454760..6f4f898ae 100644
--- a/main/jacoco-report/pom.xml
+++ b/main/jacoco-report/pom.xml
@@ -5,7 +5,7 @@
org.cryptomator
main
- 1.2.2
+ 1.2.3
jacoco-report
Cryptomator Code Coverage Report
diff --git a/main/keychain/pom.xml b/main/keychain/pom.xml
index fdeb8384d..756e81eeb 100644
--- a/main/keychain/pom.xml
+++ b/main/keychain/pom.xml
@@ -3,7 +3,7 @@
org.cryptomator
main
- 1.2.2
+ 1.2.3
keychain
System Keychain Access
diff --git a/main/pom.xml b/main/pom.xml
index 009875667..f0f365e73 100644
--- a/main/pom.xml
+++ b/main/pom.xml
@@ -6,7 +6,7 @@
4.0.0
org.cryptomator
main
- 1.2.2
+ 1.2.3
pom
Cryptomator
diff --git a/main/uber-jar/pom.xml b/main/uber-jar/pom.xml
index f6222e5cd..4d715f850 100644
--- a/main/uber-jar/pom.xml
+++ b/main/uber-jar/pom.xml
@@ -12,7 +12,7 @@
org.cryptomator
main
- 1.2.2
+ 1.2.3
uber-jar
pom
diff --git a/main/ui/pom.xml b/main/ui/pom.xml
index 368a698d8..06a479090 100644
--- a/main/ui/pom.xml
+++ b/main/ui/pom.xml
@@ -12,7 +12,7 @@
org.cryptomator
main
- 1.2.2
+ 1.2.3
ui
Cryptomator GUI
diff --git a/main/ui/src/main/java/org/cryptomator/ui/Cryptomator.java b/main/ui/src/main/java/org/cryptomator/ui/Cryptomator.java
index 3150ddc4d..1f7e07531 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/Cryptomator.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/Cryptomator.java
@@ -20,6 +20,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import org.apache.commons.lang3.SystemUtils;
+import org.cryptomator.ui.util.ApplicationVersion;
import org.cryptomator.ui.util.SingleInstanceManager;
import org.cryptomator.ui.util.SingleInstanceManager.RemoteInstance;
import org.eclipse.jetty.util.ConcurrentHashSet;
@@ -33,63 +34,62 @@ public class Cryptomator {
public static final CompletableFuture> OPEN_FILE_HANDLER = new CompletableFuture<>();
private static final Logger LOG = LoggerFactory.getLogger(Cryptomator.class);
private static final Set SHUTDOWN_TASKS = new ConcurrentHashSet<>();
- private static final CleanShutdownPerformer CLEAN_SHUTDOWN_PERFORMER = new CleanShutdownPerformer();
public static void main(String[] args) {
- String cryptomatorVersion = Optional.ofNullable(Cryptomator.class.getPackage().getImplementationVersion()).orElse("SNAPSHOT");
- LOG.info("Starting Cryptomator {} on {} {} ({})", cryptomatorVersion, SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH);
+ LOG.info("Starting Cryptomator {} on {} {} ({})", ApplicationVersion.orElse("SNAPSHOT"), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH);
+
if (SystemUtils.IS_OS_MAC_OSX) {
- /*
- * On OSX we're in an awkward position. We need to register a handler in the main thread of this application. However, we can't
- * even pass objects to the application, so we're forced to use a static CompletableFuture for the handler, which actually opens
- * the file in the application.
- *
- * Code taken from https://github.com/axet/desktop/blob/master/src/main/java/com/github/axet/desktop/os/mac/AppleHandlers.java
- */
- try {
- final Class> applicationClass = Class.forName("com.apple.eawt.Application");
- final Class> openFilesHandlerClass = Class.forName("com.apple.eawt.OpenFilesHandler");
- final Method getApplication = applicationClass.getMethod("getApplication");
- final Object application = getApplication.invoke(null);
- final Method setOpenFileHandler = applicationClass.getMethod("setOpenFileHandler", openFilesHandlerClass);
-
- final ClassLoader openFilesHandlerClassLoader = openFilesHandlerClass.getClassLoader();
- final OpenFilesHandlerClassHandler openFilesHandlerHandler = new OpenFilesHandlerClassHandler();
- final Object openFilesHandlerObject = Proxy.newProxyInstance(openFilesHandlerClassLoader, new Class>[] {openFilesHandlerClass}, openFilesHandlerHandler);
-
- setOpenFileHandler.invoke(application, openFilesHandlerObject);
- } catch (ReflectiveOperationException | RuntimeException e) {
- // Since we're trying to call OS-specific code, we'll just have
- // to hope for the best.
- LOG.error("exception adding OSX file open handler", e);
- }
+ addOsxFileOpenHandler();
}
- /*
- * Perform certain things on VM termination.
- */
- Runtime.getRuntime().addShutdownHook(CLEAN_SHUTDOWN_PERFORMER);
+ new CleanShutdownPerformer().registerShutdownHook();
- /*
- * Before starting the application, we check if there is already an instance running on this computer. If so, we send our command
- * line arguments to that instance and quit.
- */
- final Optional remoteInstance = SingleInstanceManager.getRemoteInstance(MainApplication.APPLICATION_KEY);
-
- if (remoteInstance.isPresent()) {
- try (RemoteInstance instance = remoteInstance.get()) {
- LOG.info("An instance of Cryptomator is already running at {}.", instance.getRemotePort());
- for (int i = 0; i < args.length; i++) {
- remoteInstance.get().sendMessage(args[i], 100);
- }
- } catch (Exception e) {
- LOG.error("Error forwarding arguments to remote instance", e);
- }
+ final Optional runningInstance = SingleInstanceManager.getRemoteInstance(MainApplication.APPLICATION_KEY);
+ if (runningInstance.isPresent()) {
+ sendArgsToRunningInstance(args, runningInstance);
} else {
Application.launch(MainApplication.class, args);
}
}
+ private static void addOsxFileOpenHandler() {
+ /*
+ * On OSX we're in an awkward position. We need to register a handler in the main thread of this application. However, we can't
+ * even pass objects to the application, so we're forced to use a static CompletableFuture for the handler, which actually opens
+ * the file in the application.
+ *
+ * Code taken from https://github.com/axet/desktop/blob/master/src/main/java/com/github/axet/desktop/os/mac/AppleHandlers.java
+ */
+ try {
+ final Class> applicationClass = Class.forName("com.apple.eawt.Application");
+ final Class> openFilesHandlerClass = Class.forName("com.apple.eawt.OpenFilesHandler");
+ final Method getApplication = applicationClass.getMethod("getApplication");
+ final Object application = getApplication.invoke(null);
+ final Method setOpenFileHandler = applicationClass.getMethod("setOpenFileHandler", openFilesHandlerClass);
+
+ final ClassLoader openFilesHandlerClassLoader = openFilesHandlerClass.getClassLoader();
+ final OpenFilesHandlerClassHandler openFilesHandlerHandler = new OpenFilesHandlerClassHandler();
+ final Object openFilesHandlerObject = Proxy.newProxyInstance(openFilesHandlerClassLoader, new Class>[] {openFilesHandlerClass}, openFilesHandlerHandler);
+
+ setOpenFileHandler.invoke(application, openFilesHandlerObject);
+ } catch (ReflectiveOperationException | RuntimeException e) {
+ // Since we're trying to call OS-specific code, we'll just have
+ // to hope for the best.
+ LOG.error("exception adding OSX file open handler", e);
+ }
+ }
+
+ private static void sendArgsToRunningInstance(String[] args, final Optional remoteInstance) {
+ try (RemoteInstance instance = remoteInstance.get()) {
+ LOG.info("An instance of Cryptomator is already running at {}.", instance.getRemotePort());
+ for (int i = 0; i < args.length; i++) {
+ remoteInstance.get().sendMessage(args[i], 100);
+ }
+ } catch (Exception e) {
+ LOG.error("Error forwarding arguments to remote instance", e);
+ }
+ }
+
public static void addShutdownTask(Runnable r) {
SHUTDOWN_TASKS.add(r);
}
@@ -111,6 +111,10 @@ public class Cryptomator {
});
SHUTDOWN_TASKS.clear();
}
+
+ public void registerShutdownHook() {
+ Runtime.getRuntime().addShutdownHook(this);
+ }
}
private static void handleOpenFileRequest(File file) {
diff --git a/main/ui/src/main/java/org/cryptomator/ui/CryptomatorComponent.java b/main/ui/src/main/java/org/cryptomator/ui/CryptomatorComponent.java
index e82e006e9..c4360fe56 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/CryptomatorComponent.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/CryptomatorComponent.java
@@ -37,6 +37,8 @@ interface CryptomatorComponent {
ExitUtil exitUtil();
+ DebugMode debugMode();
+
Optional nativeMacFunctions();
}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/DebugMode.java b/main/ui/src/main/java/org/cryptomator/ui/DebugMode.java
new file mode 100644
index 000000000..037e68a42
--- /dev/null
+++ b/main/ui/src/main/java/org/cryptomator/ui/DebugMode.java
@@ -0,0 +1,75 @@
+package org.cryptomator.ui;
+
+import static java.util.Arrays.asList;
+import static org.apache.logging.log4j.LogManager.ROOT_LOGGER_NAME;
+
+import java.util.Collection;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.cryptomator.ui.settings.Settings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class DebugMode {
+
+ private static final Logger LOG = LoggerFactory.getLogger(DebugMode.class);
+
+ private static final Collection LOGGER_UPGRADES = asList( //
+ loggerUpgrade(ROOT_LOGGER_NAME, Level.INFO), //
+ loggerUpgrade("org.cryptomator", Level.TRACE), //
+ loggerUpgrade("org.eclipse.jetty.server.Server", Level.DEBUG) //
+ );
+
+ private final Settings settings;
+
+ @Inject
+ public DebugMode(Settings settings) {
+ this.settings = settings;
+ }
+
+ public void initialize() {
+ if (settings.getDebugMode()) {
+ enable();
+ LOG.debug("Debug mode initialized");
+ }
+ }
+
+ private void enable() {
+ LoggerContext context = (LoggerContext) LogManager.getContext(false);
+ Configuration config = context.getConfiguration();
+ LOGGER_UPGRADES.forEach(loggerUpgrade -> loggerUpgrade.execute(config));
+ context.updateLoggers();
+ }
+
+ private static LoggerUpgrade loggerUpgrade(String loggerName, Level minLevel) {
+ return new LoggerUpgrade(loggerName, minLevel);
+ }
+
+ private static class LoggerUpgrade {
+
+ private final Level level;
+ private final String loggerName;
+
+ public LoggerUpgrade(String loggerName, Level minLevel) {
+ this.loggerName = loggerName;
+ this.level = minLevel;
+ }
+
+ public void execute(Configuration config) {
+ LoggerConfig loggerConfig = config.getLoggerConfig(loggerName);
+ if (loggerConfig.getLevel().isMoreSpecificThan(level)) {
+ loggerConfig.setLevel(level);
+ }
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/main/ui/src/main/java/org/cryptomator/ui/MainApplication.java b/main/ui/src/main/java/org/cryptomator/ui/MainApplication.java
index 11628a65c..e54277d10 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/MainApplication.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/MainApplication.java
@@ -43,18 +43,46 @@ public class MainApplication extends Application {
@Override
public void start(Stage primaryStage) throws IOException {
LOG.info("JavaFX application started");
- final CryptomatorComponent comp;
+
+ CryptomatorComponent comp = createCryptomatorComponent(primaryStage);
+ MainController mainCtrl = comp.mainController();
+ closer = comp.deferredCloser();
+
+ comp.debugMode().initialize();
+
+ setupFXMLClassLoader();
+ setupStylesheets();
+
+ initializeStage(primaryStage, mainCtrl);
+ showWindow(primaryStage);
+
+ registerExitHandler(comp);
+
+ openFilesRequestedDuringStartup(primaryStage, mainCtrl);
+ registerApplicationToProcessOpenFileRequests(primaryStage, comp, mainCtrl);
+ }
+
+ @Override
+ public void stop() {
try {
- comp = DaggerCryptomatorComponent.builder() //
+ closer.close();
+ } catch (ExecutionException e) {
+ LOG.error("Error closing ressources", e);
+ }
+ }
+
+ private CryptomatorComponent createCryptomatorComponent(Stage primaryStage) {
+ try {
+ return 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();
+ }
+ private void setupFXMLClassLoader() {
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
FXMLLoader.setDefaultClassLoader(contextClassLoader);
Platform.runLater(() -> {
@@ -66,35 +94,55 @@ public class MainApplication extends Application {
Thread.currentThread().setContextClassLoader(contextClassLoader);
}
});
+ }
- // Set stylesheets and initialize stage:
+ private void setupStylesheets() {
Font.loadFont(getClass().getResourceAsStream("/css/ionicons.ttf"), 12.0);
chooseNativeStylesheet();
+ }
+
+ private void initializeStage(Stage primaryStage, MainController mainCtrl) {
mainCtrl.initStage(primaryStage);
primaryStage.titleProperty().bind(mainCtrl.windowTitle());
primaryStage.setResizable(false);
if (SystemUtils.IS_OS_WINDOWS) {
primaryStage.getIcons().add(new Image(MainApplication.class.getResourceAsStream("/window_icon.png")));
}
+ }
- // show window and start observing its focus:
+ private void showWindow(Stage primaryStage) {
primaryStage.show();
ActiveWindowStyleSupport.startObservingFocus(primaryStage);
- comp.exitUtil().initExitHandler(this::quit);
+ }
- // open files, if requested during startup:
+ private void registerExitHandler(CryptomatorComponent comp) {
+ comp.exitUtil().initExitHandler(this::quit);
+ }
+
+ private void openFilesRequestedDuringStartup(Stage primaryStage, final MainController mainCtrl) {
for (String arg : getParameters().getUnnamed()) {
handleCommandLineArg(arg, primaryStage, mainCtrl);
}
if (SystemUtils.IS_OS_MAC_OSX) {
Cryptomator.OPEN_FILE_HANDLER.complete(file -> handleCommandLineArg(file.getAbsolutePath(), primaryStage, mainCtrl));
}
+ }
- // register this application instance as primary application, that other instances can send open file requests to:
+ private void registerApplicationToProcessOpenFileRequests(Stage primaryStage, final CryptomatorComponent comp, final MainController mainCtrl) throws IOException {
LocalInstance cryptomatorGuiInstance = closer.closeLater(SingleInstanceManager.startLocalInstance(APPLICATION_KEY, comp.executorService()), LocalInstance::close).get().get();
cryptomatorGuiInstance.registerListener(arg -> handleCommandLineArg(arg, primaryStage, mainCtrl));
}
+ private void chooseNativeStylesheet() {
+ if (SystemUtils.IS_OS_MAC_OSX) {
+ setUserAgentStylesheet(getClass().getResource("/css/mac_theme.css").toString());
+ } else if (SystemUtils.IS_OS_LINUX) {
+ setUserAgentStylesheet(getClass().getResource("/css/linux_theme.css").toString());
+ } else if (SystemUtils.IS_OS_WINDOWS) {
+ setUserAgentStylesheet(getClass().getResource("/css/win_theme.css").toString());
+ }
+ }
+
private void handleCommandLineArg(String arg, Stage primaryStage, MainController mainCtrl) {
// find correct location:
final Path path = FileSystems.getDefault().getPath(arg);
@@ -118,16 +166,6 @@ public class MainApplication extends Application {
});
}
- private void chooseNativeStylesheet() {
- if (SystemUtils.IS_OS_MAC_OSX) {
- setUserAgentStylesheet(getClass().getResource("/css/mac_theme.css").toString());
- } else if (SystemUtils.IS_OS_LINUX) {
- setUserAgentStylesheet(getClass().getResource("/css/linux_theme.css").toString());
- } else if (SystemUtils.IS_OS_WINDOWS) {
- setUserAgentStylesheet(getClass().getResource("/css/win_theme.css").toString());
- }
- }
-
private void quit() {
Platform.runLater(() -> {
stop();
@@ -136,13 +174,4 @@ public class MainApplication extends Application {
});
}
- @Override
- public void stop() {
- try {
- closer.close();
- } catch (ExecutionException e) {
- LOG.error("Error closing ressources", e);
- }
- }
-
}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/SettingsController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/SettingsController.java
index 34ef59b0d..7861a5962 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/controllers/SettingsController.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/SettingsController.java
@@ -59,6 +59,9 @@ public class SettingsController extends LocalizedFXMLViewController {
@FXML
private ChoiceBox prefGvfsScheme;
+ @FXML
+ private CheckBox debugModeCheckbox;
+
@Override
public void initialize() {
checkForUpdatesCheckbox.setDisable(areUpdatesManagedExternally());
@@ -74,11 +77,13 @@ public class SettingsController extends LocalizedFXMLViewController {
prefGvfsScheme.getItems().add("dav");
prefGvfsScheme.getItems().add("webdav");
prefGvfsScheme.setValue(settings.getPreferredGvfsScheme());
+ debugModeCheckbox.setSelected(settings.getDebugMode());
EasyBind.subscribe(checkForUpdatesCheckbox.selectedProperty(), this::checkForUpdateDidChange);
EasyBind.subscribe(portField.textProperty(), this::portDidChange);
EasyBind.subscribe(useIpv6Checkbox.selectedProperty(), this::useIpv6DidChange);
EasyBind.subscribe(prefGvfsScheme.valueProperty(), this::prefGvfsSchemeDidChange);
+ EasyBind.subscribe(debugModeCheckbox.selectedProperty(), this::debugModeDidChange);
}
@Override
@@ -114,6 +119,11 @@ public class SettingsController extends LocalizedFXMLViewController {
settings.save();
}
+ private void debugModeDidChange(Boolean newValue) {
+ settings.setDebugMode(newValue);
+ settings.save();
+ }
+
private void prefGvfsSchemeDidChange(String newValue) {
settings.setPreferredGvfsScheme(newValue);
settings.save();
diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockController.java
index a8d241431..72341c74f 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockController.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockController.java
@@ -30,6 +30,7 @@ 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.cryptomator.ui.util.DialogBuilderUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -40,7 +41,9 @@ import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
+import javafx.scene.control.Alert;
import javafx.scene.control.Button;
+import javafx.scene.control.ButtonType;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.Hyperlink;
@@ -276,6 +279,37 @@ public class UnlockController extends LocalizedFXMLViewController {
}
}
+ // ****************************************
+ // Save password checkbox
+ // ****************************************
+
+ @FXML
+ private void didClickSavePasswordCheckbox(ActionEvent event) {
+ if (!savePassword.isSelected() && hasStoredPassword()) {
+ Alert confirmDialog = DialogBuilderUtil.buildConfirmationDialog( //
+ localization.getString("unlock.savePassword.delete.confirmation.title"), //
+ localization.getString("unlock.savePassword.delete.confirmation.header"), //
+ localization.getString("unlock.savePassword.delete.confirmation.content"), //
+ SystemUtils.IS_OS_MAC_OSX ? ButtonType.CANCEL : ButtonType.OK);
+
+ Optional choice = confirmDialog.showAndWait();
+ if (ButtonType.OK.equals(choice.get())) {
+ keychainAccess.get().deletePassphrase(vault.getId());
+ } else if (ButtonType.CANCEL.equals(choice.get())) {
+ savePassword.setSelected(true);
+ }
+ }
+ }
+
+ private boolean hasStoredPassword() {
+ char[] storedPw = keychainAccess.get().loadPassphrase(vault.getId());
+ boolean hasPw = (storedPw != null);
+ if (storedPw != null) {
+ Arrays.fill(storedPw, ' ');
+ }
+ return hasPw;
+ }
+
// ****************************************
// Unlock button
// ****************************************
diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockedController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockedController.java
index 3d0e58aa4..610260092 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockedController.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockedController.java
@@ -38,6 +38,7 @@ import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
+import javafx.scene.control.MenuItem;
import javafx.scene.control.ToggleButton;
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
@@ -57,15 +58,6 @@ public class UnlockedController extends LocalizedFXMLViewController {
private Optional listener = Optional.empty();
private Timeline ioAnimation;
- @Inject
- public UnlockedController(Localization localization, Provider macWarningsControllerProvider, AsyncTaskService asyncTaskService) {
- super(localization);
- this.macWarningsController = macWarningsControllerProvider.get();
- this.asyncTaskService = asyncTaskService;
-
- macWarningsController.vault.bind(this.vault);
- }
-
@FXML
private Label messageLabel;
@@ -81,11 +73,25 @@ public class UnlockedController extends LocalizedFXMLViewController {
@FXML
private ContextMenu moreOptionsMenu;
+ @FXML
+ private MenuItem revealVaultMenuItem;
+
+ @Inject
+ public UnlockedController(Localization localization, Provider macWarningsControllerProvider, AsyncTaskService asyncTaskService) {
+ super(localization);
+ this.macWarningsController = macWarningsControllerProvider.get();
+ this.asyncTaskService = asyncTaskService;
+
+ macWarningsController.vault.bind(this.vault);
+ }
+
@Override
public void initialize() {
macWarningsController.initStage(macWarningsWindow);
ActiveWindowStyleSupport.startObservingFocus(macWarningsWindow);
+ revealVaultMenuItem.disableProperty().bind(EasyBind.map(vault, vault -> vault != null && !vault.isMounted()));
+
EasyBind.subscribe(vault, this::vaultChanged);
EasyBind.subscribe(moreOptionsMenu.showingProperty(), moreOptionsButton::setSelected);
}
@@ -109,6 +115,11 @@ public class UnlockedController extends LocalizedFXMLViewController {
}
});
+ if (!vault.get().isMounted()) {
+ // TODO Markus Kreusch #393: hyperlink auf FAQ oder sowas?
+ messageLabel.setText(localization.getString("unlocked.label.mountFailed"));
+ }
+
// (re)start throughput statistics:
stopIoSampling();
startIoSampling();
diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/WelcomeController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/WelcomeController.java
index 143a43ef9..e1a59956f 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/controllers/WelcomeController.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/WelcomeController.java
@@ -13,7 +13,6 @@ import java.net.URL;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
-import java.util.Optional;
import javax.inject.Inject;
import javax.inject.Named;
@@ -29,6 +28,7 @@ import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.ui.settings.Localization;
import org.cryptomator.ui.settings.Settings;
+import org.cryptomator.ui.util.ApplicationVersion;
import org.cryptomator.ui.util.AsyncTaskService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -104,7 +104,7 @@ public class WelcomeController extends LocalizedFXMLViewController {
asyncTaskService.asyncTaskOf(() -> {
final HttpClient client = new HttpClient();
final HttpMethod method = new GetMethod("https://cryptomator.org/downloads/latestVersion.json");
- client.getParams().setParameter(HttpClientParams.USER_AGENT, "Cryptomator VersionChecker/" + applicationVersion().orElse("SNAPSHOT"));
+ client.getParams().setParameter(HttpClientParams.USER_AGENT, "Cryptomator VersionChecker/" + ApplicationVersion.orElse("SNAPSHOT"));
client.getParams().setCookiePolicy(CookiePolicy.IGNORE_COOKIES);
client.getParams().setConnectionManagerTimeout(5000);
client.executeMethod(method);
@@ -124,10 +124,6 @@ public class WelcomeController extends LocalizedFXMLViewController {
}).run();
}
- private Optional applicationVersion() {
- return Optional.ofNullable(getClass().getPackage().getImplementationVersion());
- }
-
private void compareVersions(final Map latestVersions) {
final String latestVersion;
if (SystemUtils.IS_OS_MAC_OSX) {
@@ -140,7 +136,7 @@ public class WelcomeController extends LocalizedFXMLViewController {
// no version check possible on unsupported OS
return;
}
- final String currentVersion = applicationVersion().orElse(null);
+ final String currentVersion = ApplicationVersion.orElse(null);
LOG.debug("Current version: {}, lastest version: {}", currentVersion, latestVersion);
if (currentVersion != null && semVerComparator.compare(currentVersion, latestVersion) < 0) {
final String msg = String.format(localization.getString("welcome.newVersionMessage"), latestVersion, currentVersion);
diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java b/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java
index 086fffa90..0d36158fc 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java
@@ -50,6 +50,8 @@ import org.cryptomator.ui.util.DeferredClosable;
import org.cryptomator.ui.util.DeferredCloser;
import org.cryptomator.ui.util.FXThreads;
import org.fxmisc.easybind.EasyBind;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import com.google.common.collect.ImmutableMap;
@@ -65,6 +67,8 @@ import javafx.collections.ObservableList;
public class Vault implements CryptoFileSystemDelegate {
+ private static final Logger LOG = LoggerFactory.getLogger(CryptoFileSystemDelegate.class);
+
public static final String VAULT_FILE_EXTENSION = ".cryptomator";
private final ObjectProperty path;
@@ -72,6 +76,7 @@ public class Vault implements CryptoFileSystemDelegate {
private final CryptoFileSystemFactory cryptoFileSystemFactory;
private final DeferredCloser closer;
private final BooleanProperty unlocked = new SimpleBooleanProperty();
+ private final BooleanProperty mounted = new SimpleBooleanProperty();
private final ObservableList namesOfResourcesWithInvalidMac = FXThreads.observableListOnMainThread(FXCollections.observableArrayList());
private final Set whitelistedResourcesWithInvalidMac = new HashSet<>();
private final AtomicReference nioFileSystem = new AtomicReference<>();
@@ -131,7 +136,8 @@ public class Vault implements CryptoFileSystemDelegate {
}
public synchronized void activateFrontend(FrontendFactory frontendFactory, Settings settings, CharSequence passphrase) throws FrontendCreationFailedException {
- boolean success = false;
+ boolean launchSuccess = false;
+ boolean mountSuccess = false;
try {
FileSystem fs = getNioFileSystem();
FileSystem shorteningFs = shorteningFileSystemFactory.get(fs);
@@ -140,22 +146,32 @@ public class Vault implements CryptoFileSystemDelegate {
StatsFileSystem statsFs = new StatsFileSystem(normalizingFs);
statsFileSystem = Optional.of(statsFs);
Frontend frontend = frontendFactory.create(statsFs, FrontendId.from(id), stripStart(mountName, "/"));
+ launchSuccess = true;
filesystemFrontend = closer.closeLater(frontend);
frontend.mount(getMountParams(settings));
- success = true;
- } catch (UncheckedIOException | CommandFailedException e) {
+ mountSuccess = true;
+ } catch (UncheckedIOException e) {
throw new FrontendCreationFailedException(e);
+ } catch (CommandFailedException e) {
+ LOG.error("Failed to mount vault " + mountName, e);
} finally {
// unlocked is a observable property and should only be changed by the FX application thread
- final boolean finalSuccess = success;
- Platform.runLater(() -> unlocked.set(finalSuccess));
+ boolean finalLaunchSuccess = launchSuccess;
+ boolean finalMountSuccess = mountSuccess;
+ Platform.runLater(() -> {
+ unlocked.set(finalLaunchSuccess);
+ mounted.set(finalMountSuccess);
+ });
}
}
public synchronized void deactivateFrontend() throws Exception {
filesystemFrontend.close();
statsFileSystem = Optional.empty();
- Platform.runLater(() -> unlocked.set(false));
+ Platform.runLater(() -> {
+ mounted.set(false);
+ unlocked.set(false);
+ });
}
private Map> getMountParams(Settings settings) {
@@ -239,10 +255,18 @@ public class Vault implements CryptoFileSystemDelegate {
return unlocked;
}
+ public BooleanProperty mountedProperty() {
+ return mounted;
+ }
+
public boolean isUnlocked() {
return unlocked.get();
}
+ public boolean isMounted() {
+ return mounted.get();
+ }
+
public ObservableList getNamesOfResourcesWithInvalidMac() {
return namesOfResourcesWithInvalidMac;
}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/settings/Settings.java b/main/ui/src/main/java/org/cryptomator/ui/settings/Settings.java
index f9323bf99..e4180f969 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/settings/Settings.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/settings/Settings.java
@@ -28,6 +28,7 @@ public class Settings implements Serializable {
public static final boolean DEFAULT_USE_IPV6 = false;
public static final Integer DEFAULT_NUM_TRAY_NOTIFICATIONS = 3;
public static final String DEFAULT_GVFS_SCHEME = "dav";
+ public static final boolean DEFAULT_DEBUG_MODE = false;
private final Consumer saveCmd;
@@ -49,6 +50,9 @@ public class Settings implements Serializable {
@JsonProperty("preferredGvfsScheme")
private String preferredGvfsScheme;
+ @JsonProperty("debugMode")
+ private Boolean debugMode;
+
/**
* Package-private constructor; use {@link SettingsProvider}.
*/
@@ -125,4 +129,12 @@ public class Settings implements Serializable {
this.preferredGvfsScheme = preferredGvfsScheme;
}
+ public boolean getDebugMode() {
+ return debugMode == null ? DEFAULT_DEBUG_MODE : debugMode;
+ }
+
+ public void setDebugMode(boolean debugMode) {
+ this.debugMode = debugMode;
+ }
+
}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/util/ApplicationVersion.java b/main/ui/src/main/java/org/cryptomator/ui/util/ApplicationVersion.java
new file mode 100644
index 000000000..beaaf46df
--- /dev/null
+++ b/main/ui/src/main/java/org/cryptomator/ui/util/ApplicationVersion.java
@@ -0,0 +1,17 @@
+package org.cryptomator.ui.util;
+
+import java.util.Optional;
+
+import org.cryptomator.ui.Cryptomator;
+
+public class ApplicationVersion {
+
+ public static String orElse(String other) {
+ return get().orElse(other);
+ }
+
+ public static Optional get() {
+ return Optional.ofNullable(Cryptomator.class.getPackage().getImplementationVersion());
+ }
+
+}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/util/SingleInstanceManager.java b/main/ui/src/main/java/org/cryptomator/ui/util/SingleInstanceManager.java
index 9f6c55a65..adcf9bcd6 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/util/SingleInstanceManager.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/util/SingleInstanceManager.java
@@ -85,7 +85,7 @@ public class SingleInstanceManager {
return true;
}
return !buf.hasRemaining();
- } , timeout, 10);
+ }, timeout, 10);
return !buf.hasRemaining();
}
@@ -117,13 +117,14 @@ public class SingleInstanceManager {
final String applicationKey;
final ServerSocketChannel channel;
final Selector selector;
- int port = 0;
+ final int port;
- public LocalInstance(String applicationKey, ServerSocketChannel channel, Selector selector) {
+ public LocalInstance(String applicationKey, ServerSocketChannel channel, Selector selector, int port) {
Objects.requireNonNull(applicationKey);
this.applicationKey = applicationKey;
this.channel = channel;
this.selector = selector;
+ this.port = port;
}
/**
@@ -317,28 +318,27 @@ public class SingleInstanceManager {
*/
public static LocalInstance startLocalInstance(String applicationKey, ExecutorService exec) throws IOException {
final ServerSocketChannel channel = ServerSocketChannel.open();
- channel.configureBlocking(false);
- channel.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0));
+ boolean success = false;
+ try {
+ channel.configureBlocking(false);
+ channel.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0));
- final int port = ((InetSocketAddress) channel.getLocalAddress()).getPort();
- Preferences.userNodeForPackage(Cryptomator.class).putInt(applicationKey, port);
- LOG.debug("InstanceManager bound to port {}", port);
+ final int port = ((InetSocketAddress) channel.getLocalAddress()).getPort();
+ Preferences.userNodeForPackage(Cryptomator.class).putInt(applicationKey, port);
+ LOG.debug("InstanceManager bound to port {}", port);
- Selector selector = Selector.open();
- channel.register(selector, SelectionKey.OP_ACCEPT);
-
- LocalInstance instance = new LocalInstance(applicationKey, channel, selector);
-
- exec.submit(() -> {
- try {
- instance.port = ((InetSocketAddress) channel.getLocalAddress()).getPort();
- } catch (IOException e) {
+ Selector selector = Selector.open();
+ channel.register(selector, SelectionKey.OP_ACCEPT);
+ LocalInstance instance = new LocalInstance(applicationKey, channel, selector, port);
+ exec.submit(instance::selectionLoop);
+ success = true;
+ return instance;
+ } finally {
+ if (!success) {
+ channel.close();
}
- instance.selectionLoop();
- });
-
- return instance;
+ }
}
/**
@@ -368,7 +368,7 @@ public class SingleInstanceManager {
}
}
return !buf.hasRemaining();
- } , timeout, 1);
+ }, timeout, 1);
}
}
}
\ No newline at end of file
diff --git a/main/ui/src/main/resources/fxml/settings.fxml b/main/ui/src/main/resources/fxml/settings.fxml
index 41f770be2..e18995898 100644
--- a/main/ui/src/main/resources/fxml/settings.fxml
+++ b/main/ui/src/main/resources/fxml/settings.fxml
@@ -46,6 +46,10 @@
+
+
+
+
diff --git a/main/ui/src/main/resources/fxml/unlock.fxml b/main/ui/src/main/resources/fxml/unlock.fxml
index 0a8ea2fb8..cd27081da 100644
--- a/main/ui/src/main/resources/fxml/unlock.fxml
+++ b/main/ui/src/main/resources/fxml/unlock.fxml
@@ -69,7 +69,7 @@
-
+
diff --git a/main/ui/src/main/resources/fxml/unlocked.fxml b/main/ui/src/main/resources/fxml/unlocked.fxml
index 05ead1532..81dfddd5f 100644
--- a/main/ui/src/main/resources/fxml/unlocked.fxml
+++ b/main/ui/src/main/resources/fxml/unlocked.fxml
@@ -28,7 +28,7 @@
-