mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-04-19 00:56:52 -04:00
Merge branch 'windows-unc-path-mounter'
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user