diff --git a/src/main/java/org/cryptomator/frontend/webdav/mount/FallbackMounter.java b/src/main/java/org/cryptomator/frontend/webdav/mount/FallbackMounter.java index d72139e..1538b5d 100644 --- a/src/main/java/org/cryptomator/frontend/webdav/mount/FallbackMounter.java +++ b/src/main/java/org/cryptomator/frontend/webdav/mount/FallbackMounter.java @@ -20,7 +20,7 @@ class FallbackMounter implements MounterStrategy { @Override public Mount mount(URI uri, Map mountParams) throws CommandFailedException { - LOG.warn("Attempted to mount {}, but no applicable strategy has been found for your system. Try using the URI with a WebDAV client of your choice.", uri); + LOG.warn("No applicable strategy has been found for your system. Please use a WebDAV client of your choice to mount: {}", uri); throw new CommandFailedException("No mounting strategy found."); } diff --git a/src/main/java/org/cryptomator/frontend/webdav/mount/Mounter.java b/src/main/java/org/cryptomator/frontend/webdav/mount/Mounter.java index b31a71f..9160eff 100644 --- a/src/main/java/org/cryptomator/frontend/webdav/mount/Mounter.java +++ b/src/main/java/org/cryptomator/frontend/webdav/mount/Mounter.java @@ -17,7 +17,9 @@ public interface Mounter { Mount mount(URI uri, Map mountParams) throws CommandFailedException; public enum MountParam { - MOUNT_NAME, WIN_DRIVE_LETTER, PREFERRED_GVFS_SCHEME; + @Deprecated MOUNT_NAME, // + WIN_DRIVE_LETTER, // + PREFERRED_GVFS_SCHEME; } /** diff --git a/src/main/java/org/cryptomator/frontend/webdav/mount/WindowsMounter.java b/src/main/java/org/cryptomator/frontend/webdav/mount/WindowsMounter.java index 9395314..dc8baf1 100644 --- a/src/main/java/org/cryptomator/frontend/webdav/mount/WindowsMounter.java +++ b/src/main/java/org/cryptomator/frontend/webdav/mount/WindowsMounter.java @@ -1,16 +1,32 @@ package org.cryptomator.frontend.webdav.mount; +import java.io.IOException; import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.HashSet; import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.inject.Inject; import javax.inject.Singleton; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.SystemUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @Singleton class WindowsMounter implements MounterStrategy { + private static final Logger LOG = LoggerFactory.getLogger(WindowsMounter.class); + private static final Pattern WIN_MOUNT_DRIVELETTER_PATTERN = Pattern.compile("\\s*([A-Z]:)\\s*"); + private static final Pattern REG_QUERY_PROXY_OVERRIDES_PATTERN = Pattern.compile("\\s*ProxyOverride\\s+REG_SZ\\s+(.*)\\s*"); + private static final String AUTOASSIGN_DRRIVE_LETTER = "*"; + @Inject WindowsMounter() { } @@ -22,8 +38,103 @@ public boolean isApplicable() { @Override public Mount mount(URI uri, Map mountParams) throws CommandFailedException { - // TODO Auto-generated method stub - throw new CommandFailedException("not yet implemented."); + try { + tuneProxyConfig(uri); + String preferredDriveLetter = mountParams.getOrDefault(MountParam.WIN_DRIVE_LETTER, AUTOASSIGN_DRRIVE_LETTER); + String uncPath = "\\\\localhost@" + uri.getPort() + "\\DavWWWRoot" + uri.getRawPath().replace('/', '\\'); + ProcessBuilder mount = new ProcessBuilder("net", "use", preferredDriveLetter, uncPath); + Process mountProcess = mount.start(); + String stdout = ProcessUtil.toString(mountProcess.getInputStream(), StandardCharsets.UTF_8); + ProcessUtil.waitFor(mountProcess, 1, TimeUnit.SECONDS); + ProcessUtil.assertExitValue(mountProcess, 0); + String driveLetter = AUTOASSIGN_DRRIVE_LETTER.equals(preferredDriveLetter) ? getDriveLetter(stdout) : preferredDriveLetter; + LOG.debug("Mounted {} on drive {}", uncPath, driveLetter); + return new MountImpl(driveLetter); + } catch (IOException e) { + throw new CommandFailedException(e); + } + } + + private String getDriveLetter(String result) throws CommandFailedException { + final Matcher matcher = WIN_MOUNT_DRIVELETTER_PATTERN.matcher(result); + if (matcher.find()) { + return matcher.group(1); + } else { + throw new CommandFailedException("Failed to get a drive letter from net use output."); + } + } + + /** + * @param uri The URI for which to tune the registry settings + * @throws CommandFailedException If registry access fails + * @deprecated TODO overheadhunter: check if this is really necessary. + */ + @Deprecated + private void tuneProxyConfig(URI uri) throws CommandFailedException { + try { + // get existing value for ProxyOverride key from reqistry: + ProcessBuilder regQuery = new ProcessBuilder("reg", "query", "\"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\"", "/v", "ProxyOverride"); + Process regQueryProcess = regQuery.start(); + ProcessUtil.waitFor(regQueryProcess, 1, TimeUnit.SECONDS); + String regQueryResult = ProcessUtil.toString(regQueryProcess.getInputStream(), StandardCharsets.UTF_8); + + // determine new value for ProxyOverride key: + Set overrides = new HashSet<>(); + Matcher matcher = REG_QUERY_PROXY_OVERRIDES_PATTERN.matcher(regQueryResult); + if (regQueryProcess.exitValue() == 0 && matcher.find()) { + String originalOverrides = matcher.group(1); + LOG.debug("Original Registry value for ProxyOverride is: {}", originalOverrides); + Arrays.stream(StringUtils.split(originalOverrides, ';')).forEach(overrides::add); + } + overrides.removeIf(s -> s.startsWith("localhost:")); + overrides.add(""); + overrides.add("localhost"); + overrides.add("localhost:" + uri.getPort()); + + // set new value: + String adjustedOverrides = StringUtils.join(overrides, ';'); + ProcessBuilder regAdd = new ProcessBuilder("reg", "add", "\"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\"", "/v", "ProxyOverride", "/d", "\"" + adjustedOverrides + "\"", "/f"); + LOG.debug("Setting Registry value for ProxyOverride to: {}", adjustedOverrides); + Process regAddProcess = regAdd.start(); + ProcessUtil.waitFor(regAddProcess, 1, TimeUnit.SECONDS); + ProcessUtil.assertExitValue(regAddProcess, 0); + } catch (IOException e) { + throw new CommandFailedException(e); + } + + } + + private static class MountImpl implements Mount { + + private final ProcessBuilder unmountCommand; + private final ProcessBuilder revealCommand; + + public MountImpl(String driveLetter) { + this.unmountCommand = new ProcessBuilder("net", "use", driveLetter, "/delete", "/no"); + this.revealCommand = new ProcessBuilder("explorer.exe", "/select," + driveLetter); + } + + @Override + public void unmount() throws CommandFailedException { + try { + Process proc = unmountCommand.start(); + ProcessUtil.waitFor(proc, 1, TimeUnit.SECONDS); + ProcessUtil.assertExitValue(proc, 0); + } catch (IOException e) { + throw new CommandFailedException(e); + } + } + + @Override + public void reveal() throws CommandFailedException { + try { + Process proc = revealCommand.start(); + ProcessUtil.waitFor(proc, 2, TimeUnit.SECONDS); + } catch (IOException e) { + throw new CommandFailedException(e); + } + } + } } diff --git a/src/test/java/org/cryptomator/frontend/webdav/MirroringTest.java b/src/test/java/org/cryptomator/frontend/webdav/MirroringTest.java index 8bf4405..a41776c 100644 --- a/src/test/java/org/cryptomator/frontend/webdav/MirroringTest.java +++ b/src/test/java/org/cryptomator/frontend/webdav/MirroringTest.java @@ -12,20 +12,35 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; import java.util.Scanner; +import org.cryptomator.frontend.webdav.mount.Mounter.CommandFailedException; +import org.cryptomator.frontend.webdav.mount.Mounter.Mount; +import org.cryptomator.frontend.webdav.mount.Mounter.MountParam; +import org.cryptomator.frontend.webdav.servlet.WebDavServletController; + public class MirroringTest { - public static void main(String[] args) throws IOException { + public static void main(String[] args) throws IOException, CommandFailedException { try (Scanner scanner = new Scanner(System.in)) { System.out.println("Enter path to the directory you want to be accessible via WebDAV:"); Path p = Paths.get(scanner.nextLine()); if (Files.isDirectory(p)) { WebDavServer server = WebDavServer.create("localhost", 8080); server.start(); - server.createWebDavServlet(p, "test").start(); + WebDavServletController servlet = server.createWebDavServlet(p, "test"); + servlet.start(); + + Map mountOptions = new HashMap<>(); + mountOptions.put(MountParam.WIN_DRIVE_LETTER, "X:"); + Mount mount = servlet.mount(mountOptions); + mount.reveal(); + System.out.println("Enter anything to stop the server..."); System.in.read(); + mount.unmount(); server.stop(); } else { System.out.println("Invalid directory.");