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 7035fe88c..600d9e36b 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/MainApplication.java +++ b/main/ui/src/main/java/org/cryptomator/ui/MainApplication.java @@ -43,7 +43,6 @@ public class MainApplication extends Application { private static final Logger LOG = LoggerFactory.getLogger(MainApplication.class); - private final CleanShutdownPerformer cleanShutdownPerformer = new CleanShutdownPerformer(); private final ExecutorService executorService; private final ControllerFactory controllerFactory; private final DeferredCloser closer; @@ -65,6 +64,7 @@ public class MainApplication extends Application { this.executorService = executorService; this.controllerFactory = controllerFactory; this.closer = closer; + Cryptomator.addShutdownTask(closer::close); appRef.set(this); } @@ -82,8 +82,6 @@ public class MainApplication extends Application { } }); - Runtime.getRuntime().addShutdownHook(cleanShutdownPerformer); - chooseNativeStylesheet(); final ResourceBundle rb = ResourceBundle.getBundle("localization"); final FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/main.fxml"), rb); @@ -162,18 +160,6 @@ public class MainApplication extends Application { @Override public void stop() { closer.close(); - try { - Runtime.getRuntime().removeShutdownHook(cleanShutdownPerformer); - } catch (Exception e) { - - } - } - - private class CleanShutdownPerformer extends Thread { - @Override - public void run() { - closer.close(); - } } /** diff --git a/main/ui/src/main/java/org/cryptomator/ui/util/DeferredCloser.java b/main/ui/src/main/java/org/cryptomator/ui/util/DeferredCloser.java index aaeae93c5..56414ad93 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/util/DeferredCloser.java +++ b/main/ui/src/main/java/org/cryptomator/ui/util/DeferredCloser.java @@ -8,6 +8,7 @@ ******************************************************************************/ package org.cryptomator.ui.util; +import java.util.Iterator; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -91,11 +92,13 @@ public class DeferredCloser implements AutoCloseable { } /** - * Closes all added objects which have not been closed before. + * Closes all added objects which have not been closed before and releases references. */ public void close() { - for (ManagedResource closableProvider : cleanups.values()) { + for (Iterator> iterator = cleanups.values().iterator(); iterator.hasNext();) { + final ManagedResource closableProvider = iterator.next(); closableProvider.close(); + iterator.remove(); } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/util/command/CommandResult.java b/main/ui/src/main/java/org/cryptomator/ui/util/command/CommandResult.java index f069a4f42..466fab057 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/util/command/CommandResult.java +++ b/main/ui/src/main/java/org/cryptomator/ui/util/command/CommandResult.java @@ -14,6 +14,7 @@ import static java.lang.String.format; import java.io.IOException; import org.apache.commons.io.IOUtils; +import org.apache.logging.log4j.util.Strings; import org.cryptomator.ui.util.mount.CommandFailedException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -77,7 +78,15 @@ public final class CommandResult { private void logDebugInfo() { if (LOG.isDebugEnabled()) { - LOG.debug("Command execution finished. Exit code: {}\n" + "Output:\n" + "{}\n" + "Error:\n" + "{}\n", process.exitValue(), stdout, stderr); + if (Strings.isEmpty(stderr) && Strings.isEmpty(stdout)) { + LOG.debug("Command execution finished. Exit code: {}", process.exitValue()); + } else if (Strings.isEmpty(stderr)) { + LOG.debug("Command execution finished. Exit code: {}\nOutput: {}", process.exitValue(), stdout); + } else if (Strings.isEmpty(stdout)) { + LOG.debug("Command execution finished. Exit code: {}\nError: {}", process.exitValue(), stderr); + } else { + LOG.debug("Command execution finished. Exit code: {}\n Output: {}\nError: {}", process.exitValue(), stdout, stderr); + } } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/util/mount/WindowsWebDavMounter.java b/main/ui/src/main/java/org/cryptomator/ui/util/mount/WindowsWebDavMounter.java index eed6fae4f..84a98e48b 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/util/mount/WindowsWebDavMounter.java +++ b/main/ui/src/main/java/org/cryptomator/ui/util/mount/WindowsWebDavMounter.java @@ -13,7 +13,7 @@ import static org.cryptomator.ui.util.command.Script.fromLines; import java.net.URI; import java.nio.file.FileSystems; -import java.nio.file.Files; +import java.nio.file.Path; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -26,13 +26,11 @@ import org.cryptomator.ui.util.command.Script; * A {@link WebDavMounterStrategy} utilizing the "net use" command. *

* Tested on Windows 7 but should also work on Windows 8. - * - * @author Markus Kreusch */ final class WindowsWebDavMounter implements WebDavMounterStrategy { private static final Pattern WIN_MOUNT_DRIVELETTER_PATTERN = Pattern.compile("\\s*([A-Z]:)\\s*"); - private static final int MAX_MOUNT_ATTEMPTS = 12; + private static final int MAX_MOUNT_ATTEMPTS = 5; @Override public boolean shouldWork() { @@ -41,59 +39,71 @@ final class WindowsWebDavMounter implements WebDavMounterStrategy { @Override public void warmUp(int serverPort) { - try { - final Script proxyBypassCmd = fromLines("reg add \"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\" /v \"ProxyOverride\" /d \";0--1.ipv6-literal.net;0--1.ipv6-literal.net:%PORT%\" /f"); - proxyBypassCmd.addEnv("PORT", String.valueOf(serverPort)); - proxyBypassCmd.execute(); - final Script mountCmd = fromLines("net use * http://0--1.ipv6-literal.net:%PORT%/bill-gates-mom-uses-goto /persistent:no"); - mountCmd.addEnv("PORT", String.valueOf(serverPort)); - mountCmd.execute(); - } catch (CommandFailedException e) { - // will most certainly throw an exception, because this is a fake WebDav path. But now windows has some DNS things cached :) - } +// try { +// final Script mountScript = fromLines("net use * \\\\localhost@%DAV_PORT%\\DavWWWRoot\\bill-gates-mom-uses-goto /persistent:no"); +// mountScript.addEnv("DAV_PORT", String.valueOf(serverPort)); +// mountScript.execute(1, TimeUnit.SECONDS); +// } catch (CommandFailedException e) { +// // will most certainly throw an exception, because this is a fake WebDav path. But now windows has some DNS things cached :) +// } } @Override public WebDavMount mount(URI uri, String name) throws CommandFailedException { - final Script proxyBypassCmd = fromLines("reg add \"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\" /v \"ProxyOverride\" /d \";0--1.ipv6-literal.net;0--1.ipv6-literal.net:%PORT%\" /f"); - proxyBypassCmd.addEnv("PORT", String.valueOf(uri.getPort())); - final Script mountScript = fromLines("net use * http://0--1.ipv6-literal.net:%PORT%%DAV_PATH% /persistent:no"); - mountScript.addEnv("PORT", String.valueOf(uri.getPort())).addEnv("DAV_PATH", uri.getRawPath()); - String driveLetter = null; - // The ugliness of the following 20 lines is solely windows' fault. Deal with it. - for (int i = 0; i < MAX_MOUNT_ATTEMPTS; i++) { - try { - proxyBypassCmd.execute(); - final CommandResult mountResult = mountScript.execute(5, TimeUnit.SECONDS); - driveLetter = getDriveLetter(mountResult.getStdOut()); - break; - } catch (CommandFailedException ex) { - if (i == MAX_MOUNT_ATTEMPTS - 1) { - throw ex; - } else { - try { - // retry after 2.5s - Thread.sleep(2500); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - } + CommandResult mountResult; + try { + final Script mountScript = fromLines("net use * \\\\0--1.ipv6-literal.net@%DAV_PORT%\\DavWWWRoot%DAV_UNC_PATH% /persistent:no"); + mountScript.addEnv("DAV_PORT", String.valueOf(uri.getPort())).addEnv("DAV_UNC_PATH", uri.getRawPath().replace('/', '\\')); + mountResult = mountScript.execute(5, TimeUnit.SECONDS); + } catch (CommandFailedException ex) { + final Script mountScript = fromLines("net use * \\\\0--1.ipv6-literal.net@%DAV_PORT%\\DavWWWRoot%DAV_UNC_PATH% /persistent:no"); + mountScript.addEnv("DAV_PORT", String.valueOf(uri.getPort())).addEnv("DAV_UNC_PATH", uri.getRawPath().replace('/', '\\')); + final Script proxyBypassScript = fromLines("reg add \"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\" /v \"ProxyOverride\" /d \";0--1.ipv6-literal.net;0--1.ipv6-literal.net:%DAV_PORT%\" /f"); + proxyBypassScript.addEnv("DAV_PORT", String.valueOf(uri.getPort())); + mountResult = bypassProxyAndRetryMount(mountScript, proxyBypassScript); } + + final String driveLetter = getDriveLetter(mountResult.getStdOut()); final Script openExplorerScript = fromLines("start explorer.exe " + driveLetter); openExplorerScript.execute(); final Script unmountScript = fromLines("net use " + driveLetter + " /delete").addEnv("DRIVE_LETTER", driveLetter); - final String finalDriveLetter = driveLetter; return new AbstractWebDavMount() { @Override public void unmount() throws CommandFailedException { // only attempt unmount if user didn't unmount manually: - if (Files.exists(FileSystems.getDefault().getPath(finalDriveLetter))) { + if (isVolumeMounted(driveLetter)) { unmountScript.execute(); } } }; } + + private boolean isVolumeMounted(String driveLetter) { + for (Path path : FileSystems.getDefault().getRootDirectories()) { + if (path.toString().startsWith(driveLetter)) { + return true; + } + } + return false; + } + + private CommandResult bypassProxyAndRetryMount(Script mountScript, Script proxyBypassScript) throws CommandFailedException { + CommandFailedException latestException = null; + for (int i = 0; i < MAX_MOUNT_ATTEMPTS; i++) { + try { + // wait a moment before next attempt + Thread.sleep(5000); + proxyBypassScript.execute(); + return mountScript.execute(5, TimeUnit.SECONDS); + } catch (CommandFailedException ex) { + latestException = ex; + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + throw new CommandFailedException(ex); + } + } + throw latestException; + } private String getDriveLetter(String result) throws CommandFailedException { final Matcher matcher = WIN_MOUNT_DRIVELETTER_PATTERN.matcher(result);