Merge branch 'windows-unc-path-mounter'

This commit is contained in:
Sebastian Stenzel
2015-05-30 20:39:23 +02:00
4 changed files with 66 additions and 58 deletions

View File

@@ -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();
}
}
/**

View File

@@ -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<ManagedResource<?>> iterator = cleanups.values().iterator(); iterator.hasNext();) {
final ManagedResource<?> closableProvider = iterator.next();
closableProvider.close();
iterator.remove();
}
}

View File

@@ -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);
}
}
}

View File

@@ -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.
* <p>
* 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 \"<local>;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 \"<local>;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 \"<local>;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);