mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-14 08:41:28 +00:00
Merge branch 'webdav-mounting'
This commit is contained in:
@@ -2,20 +2,21 @@ package org.cryptomator.ui.model;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.cryptomator.crypto.Cryptor;
|
||||
import org.cryptomator.crypto.SamplingDecorator;
|
||||
import org.cryptomator.crypto.aes256.Aes256Cryptor;
|
||||
import org.cryptomator.ui.MainApplication;
|
||||
import org.cryptomator.ui.util.MasterKeyFilter;
|
||||
import org.cryptomator.ui.util.WebDavMounter;
|
||||
import org.cryptomator.ui.util.WebDavMounter.CommandFailedException;
|
||||
import org.cryptomator.ui.util.webdav.CommandFailedException;
|
||||
import org.cryptomator.ui.util.webdav.WebDavMount;
|
||||
import org.cryptomator.ui.util.webdav.WebDavMounter;
|
||||
import org.cryptomator.webdav.WebDAVServer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -35,7 +36,7 @@ public class Directory implements Serializable {
|
||||
private final ObjectProperty<Boolean> unlocked = new SimpleObjectProperty<Boolean>(this, "unlocked", Boolean.FALSE);
|
||||
private final Path path;
|
||||
// private boolean unlocked;
|
||||
private String unmountCommand;
|
||||
private WebDavMount webDavMount;
|
||||
private final Runnable shutdownTask = new ShutdownTask();
|
||||
|
||||
public Directory(final Path path) {
|
||||
@@ -69,7 +70,8 @@ public class Directory implements Serializable {
|
||||
|
||||
public boolean mount() {
|
||||
try {
|
||||
unmountCommand = WebDavMounter.mount(server.getPort());
|
||||
URI shareUri = URI.create(String.format("dav://localhost:%d", server.getPort()));
|
||||
webDavMount = WebDavMounter.mount(shareUri);
|
||||
return true;
|
||||
} catch (CommandFailedException e) {
|
||||
LOG.warn("mount failed", e);
|
||||
@@ -79,9 +81,9 @@ public class Directory implements Serializable {
|
||||
|
||||
public boolean unmount() {
|
||||
try {
|
||||
if (StringUtils.isNotEmpty(unmountCommand)) {
|
||||
WebDavMounter.unmount(unmountCommand);
|
||||
unmountCommand = null;
|
||||
if (webDavMount != null) {
|
||||
webDavMount.unmount();
|
||||
webDavMount = null;
|
||||
}
|
||||
return true;
|
||||
} catch (CommandFailedException e) {
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 Sebastian Stenzel
|
||||
* This file is licensed under the terms of the MIT license.
|
||||
* See the LICENSE.txt file for more info.
|
||||
*
|
||||
* Contributors:
|
||||
* Sebastian Stenzel - initial API and implementation
|
||||
******************************************************************************/
|
||||
package org.cryptomator.ui.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public final class WebDavMounter {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(WebDavMounter.class);
|
||||
private static final int CMD_DEFAULT_TIMEOUT = 3;
|
||||
private static final Pattern WIN_MOUNT_DRIVELETTER_PATTERN = Pattern.compile("\\s*[A-Z]:\\s*");
|
||||
|
||||
private WebDavMounter() {
|
||||
throw new IllegalStateException("not instantiable.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Unmount Command
|
||||
*/
|
||||
public static synchronized String mount(int localPort) throws CommandFailedException {
|
||||
if (SystemUtils.IS_OS_MAC_OSX) {
|
||||
exec("mkdir /Volumes/Cryptomator" + localPort, CMD_DEFAULT_TIMEOUT);
|
||||
exec("mount_webdav -S -v Cryptomator localhost:" + localPort + " /Volumes/Cryptomator" + localPort, CMD_DEFAULT_TIMEOUT);
|
||||
exec("open /Volumes/Cryptomator" + localPort, CMD_DEFAULT_TIMEOUT);
|
||||
return "umount /Volumes/Cryptomator" + localPort;
|
||||
} else if (SystemUtils.IS_OS_WINDOWS) {
|
||||
final String result = exec("net use * http://127.0.0.1:" + localPort + " /persistent:no", CMD_DEFAULT_TIMEOUT);
|
||||
final Matcher matcher = WIN_MOUNT_DRIVELETTER_PATTERN.matcher(result);
|
||||
if (matcher.find()) {
|
||||
final String driveLetter = matcher.group();
|
||||
return "net use " + driveLetter + " /delete";
|
||||
}
|
||||
} else if (SystemUtils.IS_OS_LINUX) {
|
||||
// TODO check result of "which gvfs-mount" first and choose a good strategy. also refactor this class ;-)
|
||||
exec("gvfs-mount dav://localhost:" + localPort, CMD_DEFAULT_TIMEOUT);
|
||||
exec("xdg-open dav://localhost:" + localPort, CMD_DEFAULT_TIMEOUT);
|
||||
return "gvfs-mount -u dav://localhost:" + localPort;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void unmount(String command) throws CommandFailedException {
|
||||
if (command != null) {
|
||||
exec(command, CMD_DEFAULT_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
private static String exec(String cmd, int timoutSeconds) throws CommandFailedException {
|
||||
try {
|
||||
final Process proc;
|
||||
if (SystemUtils.IS_OS_WINDOWS) {
|
||||
proc = Runtime.getRuntime().exec(new String[] {"cmd", "/C", cmd});
|
||||
} else {
|
||||
proc = Runtime.getRuntime().exec(new String[] {"/bin/sh", "-c", cmd});
|
||||
}
|
||||
if (!proc.waitFor(timoutSeconds, TimeUnit.SECONDS)) {
|
||||
proc.destroy();
|
||||
throw new CommandFailedException("Timeout executing command " + cmd);
|
||||
}
|
||||
if (proc.exitValue() != 0) {
|
||||
throw new CommandFailedException(IOUtils.toString(proc.getErrorStream()));
|
||||
}
|
||||
return IOUtils.toString(proc.getInputStream());
|
||||
} catch (IOException | InterruptedException | IllegalThreadStateException e) {
|
||||
LOG.error("Command execution failed.", e);
|
||||
throw new CommandFailedException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class CommandFailedException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 5784853630182321479L;
|
||||
|
||||
private CommandFailedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
private CommandFailedException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 Markus Kreusch
|
||||
* This file is licensed under the terms of the MIT license.
|
||||
* See the LICENSE.txt file for more info.
|
||||
*
|
||||
* Contributors:
|
||||
* Markus Kreusch
|
||||
******************************************************************************/
|
||||
package org.cryptomator.ui.util.command;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
final class AsyncLineWriter extends Thread {
|
||||
|
||||
private final String[] lines;
|
||||
private final OutputStream output;
|
||||
|
||||
private IOException exception;
|
||||
|
||||
public AsyncLineWriter(String[] lines, OutputStream output) {
|
||||
this.lines = lines;
|
||||
this.output = output;
|
||||
start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try (OutputStream outputToBeClosed = output) {
|
||||
for (String line : lines) {
|
||||
output.write(line.getBytes());
|
||||
output.write("\n".getBytes());
|
||||
output.flush();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
exception = e;
|
||||
}
|
||||
}
|
||||
|
||||
public void assertOk() throws IOException {
|
||||
try {
|
||||
join();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
if (exception != null) {
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 Markus Kreusch
|
||||
* This file is licensed under the terms of the MIT license.
|
||||
* See the LICENSE.txt file for more info.
|
||||
*
|
||||
* Contributors:
|
||||
* Markus Kreusch
|
||||
******************************************************************************/
|
||||
package org.cryptomator.ui.util.command;
|
||||
|
||||
import static org.apache.commons.io.IOUtils.copy;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
final class AsyncStreamCopier extends Thread {
|
||||
|
||||
private final InputStream input;
|
||||
private final OutputStream output;
|
||||
|
||||
private IOException exception;
|
||||
|
||||
public AsyncStreamCopier(InputStream input, OutputStream output) {
|
||||
this.input = input;
|
||||
this.output = output;
|
||||
start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try (InputStream inputToBeClosed = input;
|
||||
OutputStream outputToBeClosed = output) {
|
||||
copy(input, output);
|
||||
} catch (IOException e) {
|
||||
exception = e;
|
||||
}
|
||||
}
|
||||
|
||||
public void assertOk() throws IOException {
|
||||
try {
|
||||
join();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
if (exception != null) {
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 Markus Kreusch
|
||||
* This file is licensed under the terms of the MIT license.
|
||||
* See the LICENSE.txt file for more info.
|
||||
*
|
||||
* Contributors:
|
||||
* Markus Kreusch
|
||||
******************************************************************************/
|
||||
package org.cryptomator.ui.util.command;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.cryptomator.ui.util.webdav.CommandFailedException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class CommandResult {
|
||||
|
||||
private static final int DEFAULT_TIMEOUT_MILLISECONDS = 10000;
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CommandResult.class);
|
||||
|
||||
private final ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
private final ByteArrayOutputStream error = new ByteArrayOutputStream();
|
||||
private final Process process;
|
||||
|
||||
private final AsyncStreamCopier processOutputCopier;
|
||||
private final AsyncStreamCopier processErrorCopier;
|
||||
|
||||
private boolean finished;
|
||||
|
||||
public CommandResult(Process process, String[] lines) {
|
||||
this.process = process;
|
||||
new AsyncLineWriter(lines, process.getOutputStream());
|
||||
processOutputCopier = new AsyncStreamCopier(process.getInputStream(), output);
|
||||
processErrorCopier = new AsyncStreamCopier(process.getErrorStream(), error);
|
||||
}
|
||||
|
||||
public String getOutput() throws CommandFailedException {
|
||||
return getOutput(DEFAULT_TIMEOUT_MILLISECONDS, TimeUnit.MICROSECONDS);
|
||||
}
|
||||
|
||||
public String getError() throws CommandFailedException {
|
||||
return getError(DEFAULT_TIMEOUT_MILLISECONDS, TimeUnit.MICROSECONDS);
|
||||
}
|
||||
|
||||
public String getOutput(long timeout, TimeUnit unit) throws CommandFailedException {
|
||||
waitAndAssertOk(timeout, unit);
|
||||
return new String(output.toByteArray());
|
||||
}
|
||||
|
||||
public String getError(long timeout, TimeUnit unit) throws CommandFailedException {
|
||||
waitAndAssertOk(timeout, unit);
|
||||
return new String(error.toByteArray());
|
||||
}
|
||||
|
||||
public int getExitValue(long timeout, TimeUnit unit) throws CommandFailedException {
|
||||
waitAndAssertOk(timeout, unit);
|
||||
return process.exitValue();
|
||||
}
|
||||
|
||||
private void waitAndAssertOk(long timeout, TimeUnit unit) throws CommandFailedException {
|
||||
if (finished) return;
|
||||
try {
|
||||
if (!process.waitFor(timeout, unit)) {
|
||||
throw new CommandFailedException("Waiting time elapsed before command execution finished");
|
||||
}
|
||||
processOutputCopier.assertOk();
|
||||
processErrorCopier.assertOk();
|
||||
finished = true;
|
||||
logDebugInfo();
|
||||
} catch (IOException | InterruptedException e) {
|
||||
throw new CommandFailedException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void logDebugInfo() {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Command execution finished. Exit code: {}\n"
|
||||
+ "Output:\n"
|
||||
+ "{}\n"
|
||||
+ "Error:\n"
|
||||
+ "{}\n",
|
||||
process.exitValue(),
|
||||
new String(output.toByteArray()),
|
||||
new String(error.toByteArray()));
|
||||
}
|
||||
}
|
||||
|
||||
public void assertOk() throws CommandFailedException {
|
||||
assertOk(DEFAULT_TIMEOUT_MILLISECONDS, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public void assertOk(long timeout, TimeUnit unit) throws CommandFailedException {
|
||||
int exitValue = getExitValue(timeout, unit);
|
||||
if (exitValue != 0) {
|
||||
throw new CommandFailedException(format(
|
||||
"Command execution failed. Exit code: %d\n"
|
||||
+ "# Output:\n"
|
||||
+ "%s\n"
|
||||
+ "# Error:\n"
|
||||
+ "%s",
|
||||
exitValue,
|
||||
new String(output.toByteArray()),
|
||||
new String(error.toByteArray())));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 Markus Kreusch
|
||||
* This file is licensed under the terms of the MIT license.
|
||||
* See the LICENSE.txt file for more info.
|
||||
*
|
||||
* Contributors:
|
||||
* Markus Kreusch
|
||||
******************************************************************************/
|
||||
package org.cryptomator.ui.util.command;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.apache.commons.lang3.SystemUtils.IS_OS_UNIX;
|
||||
import static org.apache.commons.lang3.SystemUtils.IS_OS_WINDOWS;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.ui.util.webdav.CommandFailedException;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Runs commands using a system compatible CLI.
|
||||
* <p>
|
||||
* To detect the system type {@link SystemUtils} is used. The following CLIs are
|
||||
* used by default:
|
||||
* <ul>
|
||||
* <li><i>{@link #WINDOWS_DEFAULT_CLI}</i> if {@link SystemUtils#IS_OS_WINDOWS}
|
||||
* <li><i>{@link #UNIX_DEFAULT_CLI}</i> if {@link SystemUtils#IS_OS_UNIX}
|
||||
* </ul>
|
||||
* <p>
|
||||
* If the path to the executables differs from the default or the system can not
|
||||
* be detected the Java system property {@value #CLI_EXECUTABLE_PROPERTY} can be
|
||||
* set to define it.
|
||||
* <p>
|
||||
* If a CLI executable can not be determined using these methods operation of
|
||||
* {@link CommandRunner} will fail with {@link IllegalStateException}s.
|
||||
*
|
||||
* @author Markus Kreusch
|
||||
*/
|
||||
class CommandRunner {
|
||||
|
||||
public static final String CLI_EXECUTABLE_PROPERTY = "cryptomator.cli";
|
||||
|
||||
public static final String WINDOWS_DEFAULT_CLI[] = {"cmd"};
|
||||
public static final String UNIX_DEFAULT_CLI[] = {"/bin/sh", "-e"};
|
||||
|
||||
static CommandResult execute(Script script) throws CommandFailedException {
|
||||
ProcessBuilder builder = new ProcessBuilder(determineCli());
|
||||
builder.environment().clear();
|
||||
builder.environment().putAll(script.environment());
|
||||
try {
|
||||
return run(builder.start(), script.getLines());
|
||||
} catch (IOException e) {
|
||||
throw new CommandFailedException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static CommandResult run(Process process, String[] lines) {
|
||||
return new CommandResult(process, lines);
|
||||
}
|
||||
|
||||
private static String[] determineCli() {
|
||||
final String cliFromProperty = System.getProperty(CLI_EXECUTABLE_PROPERTY);
|
||||
if (cliFromProperty != null) {
|
||||
return cliFromProperty.split("");
|
||||
} else if (IS_OS_WINDOWS) {
|
||||
return WINDOWS_DEFAULT_CLI;
|
||||
} else if (IS_OS_UNIX) {
|
||||
return UNIX_DEFAULT_CLI;
|
||||
} else {
|
||||
throw new IllegalStateException(format(
|
||||
"Failed to determine cli to use. Set Java system property %s to the executable.",
|
||||
CLI_EXECUTABLE_PROPERTY));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 Markus Kreusch
|
||||
* This file is licensed under the terms of the MIT license.
|
||||
* See the LICENSE.txt file for more info.
|
||||
*
|
||||
* Contributors:
|
||||
* Markus Kreusch
|
||||
******************************************************************************/
|
||||
package org.cryptomator.ui.util.command;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.cryptomator.ui.util.webdav.CommandFailedException;
|
||||
|
||||
public final class Script {
|
||||
|
||||
public static Script fromLines(String ... commands) {
|
||||
return new Script(commands);
|
||||
}
|
||||
|
||||
private final String[] lines;
|
||||
private final Map<String,String> environment = new HashMap<>();
|
||||
|
||||
private Script(String[] lines) {
|
||||
this.lines = lines;
|
||||
setEnv(System.getenv());
|
||||
}
|
||||
|
||||
public String[] getLines() {
|
||||
return lines;
|
||||
}
|
||||
|
||||
public CommandResult execute() throws CommandFailedException {
|
||||
return CommandRunner.execute(this);
|
||||
}
|
||||
|
||||
Map<String,String> environment() {
|
||||
return environment;
|
||||
}
|
||||
|
||||
public Script setEnv(Map<String,String> environment) {
|
||||
this.environment.clear();
|
||||
addEnv(environment);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Script addEnv(Map<String,String> environment) {
|
||||
this.environment.putAll(environment);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Script addEnv(String name, String value) {
|
||||
environment.put(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 Sebastian Stenzel
|
||||
* This file is licensed under the terms of the MIT license.
|
||||
* See the LICENSE.txt file for more info.
|
||||
*
|
||||
* Contributors:
|
||||
* Sebastian Stenzel - initial API and implementation
|
||||
* Markus Kreusch - Refactored WebDavMounter to use strategy pattern
|
||||
******************************************************************************/
|
||||
package org.cryptomator.ui.util.webdav;
|
||||
|
||||
public class CommandFailedException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 5784853630182321479L;
|
||||
|
||||
public CommandFailedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CommandFailedException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 Markus Kreusch
|
||||
* This file is licensed under the terms of the MIT license.
|
||||
* See the LICENSE.txt file for more info.
|
||||
*
|
||||
* Contributors:
|
||||
* Markus Kreusch - Refactored WebDavMounter to use strategy pattern
|
||||
******************************************************************************/
|
||||
package org.cryptomator.ui.util.webdav;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* A WebDavMounter acting as fallback if no other mounter works.
|
||||
*
|
||||
* @author Markus Kreusch
|
||||
*/
|
||||
final class FallbackWebDavMounter implements WebDavMounterStrategy {
|
||||
|
||||
@Override
|
||||
public boolean shouldWork() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebDavMount mount(URI uri) {
|
||||
displayMountInstructions();
|
||||
return new WebDavMount() {
|
||||
@Override
|
||||
public void unmount() {
|
||||
displayUnmountInstructions();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void displayMountInstructions() {
|
||||
// TODO display message to user pointing to cryptomator.org/mounting#mount which describes what to do
|
||||
}
|
||||
|
||||
private void displayUnmountInstructions() {
|
||||
// TODO display message to user pointing to cryptomator.org/mounting#unmount which describes what to do
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 Sebastian Stenzel, Markus Kreusch
|
||||
* This file is licensed under the terms of the MIT license.
|
||||
* See the LICENSE.txt file for more info.
|
||||
*
|
||||
* Contributors:
|
||||
* Sebastian Stenzel - initial API and implementation
|
||||
* Markus Kreusch - Refactored WebDavMounter to use strategy pattern
|
||||
******************************************************************************/
|
||||
package org.cryptomator.ui.util.webdav;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.ui.util.command.Script;
|
||||
|
||||
final class LinuxGvfsWebDavMounter implements WebDavMounterStrategy {
|
||||
|
||||
@Override
|
||||
public boolean shouldWork() {
|
||||
if (SystemUtils.IS_OS_LINUX) {
|
||||
final Script checkScripts = Script.fromLines("which gvfs-mount xdg-open");
|
||||
try {
|
||||
checkScripts.execute().assertOk();
|
||||
return true;
|
||||
} catch (CommandFailedException e) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebDavMount mount(final URI uri) throws CommandFailedException {
|
||||
final Script mountScript = Script.fromLines(
|
||||
"set -x",
|
||||
"gvfs-mount \"$URI\"",
|
||||
"xdg-open \"$URI\"")
|
||||
.addEnv("URI", uri.toString());
|
||||
final Script unmountScript = Script.fromLines(
|
||||
"set -x",
|
||||
"gvfs-mount -u \"$URI\"")
|
||||
.addEnv("URI", uri.toString());
|
||||
mountScript.execute().assertOk();
|
||||
return new WebDavMount() {
|
||||
@Override
|
||||
public void unmount() throws CommandFailedException {
|
||||
unmountScript.execute().assertOk();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 Sebastian Stenzel, Markus Kreusch
|
||||
* This file is licensed under the terms of the MIT license.
|
||||
* See the LICENSE.txt file for more info.
|
||||
*
|
||||
* Contributors:
|
||||
* Sebastian Stenzel - initial API and implementation
|
||||
* Markus Kreusch - Refactored WebDavMounter to use strategy pattern
|
||||
******************************************************************************/
|
||||
package org.cryptomator.ui.util.webdav;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.ui.util.command.Script;
|
||||
|
||||
final class MacOsXWebDavMounter implements WebDavMounterStrategy {
|
||||
|
||||
@Override
|
||||
public boolean shouldWork() {
|
||||
return SystemUtils.IS_OS_MAC_OSX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebDavMount mount(URI uri) throws CommandFailedException {
|
||||
final String path = "/Volumes/Cryptomator" + uri.getPort();
|
||||
final Script mountScript = Script.fromLines(
|
||||
"set -x",
|
||||
"mkdir \"$MOUNT_PATH\"",
|
||||
"mount_webdav -S -v Cryptomator \"$URI\" \"$MOUNT_PATH\"",
|
||||
"open \"$MOUNT_PATH\"")
|
||||
.addEnv("URI", uri.toString())
|
||||
.addEnv("MOUNT_PATH", path);
|
||||
final Script unmountScript = Script.fromLines(
|
||||
"set -x",
|
||||
"unmount $MOUNT_PATH")
|
||||
.addEnv("MOUNT_PATH", path);
|
||||
mountScript.execute().assertOk();
|
||||
return new WebDavMount() {
|
||||
@Override
|
||||
public void unmount() throws CommandFailedException {
|
||||
unmountScript.execute().assertOk();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 Markus Kreusch
|
||||
* This file is licensed under the terms of the MIT license.
|
||||
* See the LICENSE.txt file for more info.
|
||||
*
|
||||
* Contributors:
|
||||
* Markus Kreusch - Refactored WebDavMounter to use strategy pattern
|
||||
******************************************************************************/
|
||||
package org.cryptomator.ui.util.webdav;
|
||||
|
||||
|
||||
/**
|
||||
* A mounted webdav share.
|
||||
*
|
||||
* @author Markus Kreusch
|
||||
*/
|
||||
public interface WebDavMount {
|
||||
|
||||
/**
|
||||
* Unmounts this {@code WebDavMount}.
|
||||
*
|
||||
* @throws CommandFailedException if the unmount operation fails
|
||||
*/
|
||||
void unmount() throws CommandFailedException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 Sebastian Stenzel
|
||||
* This file is licensed under the terms of the MIT license.
|
||||
* See the LICENSE.txt file for more info.
|
||||
*
|
||||
* Contributors:
|
||||
* Sebastian Stenzel - initial API and implementation
|
||||
* Markus Kreusch - Refactored to use strategy pattern
|
||||
******************************************************************************/
|
||||
package org.cryptomator.ui.util.webdav;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public final class WebDavMounter {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(WebDavMounter.class);
|
||||
|
||||
private static final WebDavMounterStrategy[] STRATEGIES = {
|
||||
new WindowsWebDavMounter(),
|
||||
new MacOsXWebDavMounter(),
|
||||
new LinuxGvfsWebDavMounter()
|
||||
};
|
||||
|
||||
private static volatile WebDavMounterStrategy choosenStrategy;
|
||||
|
||||
/**
|
||||
* Tries to mount a given webdav share.
|
||||
*
|
||||
* @param uri
|
||||
* the {@link URI} of the webdav share
|
||||
* @return a {@link WebDavMount} representing the mounted share
|
||||
* @throws CommandFailedException if the mount operation fails
|
||||
*/
|
||||
public static WebDavMount mount(URI uri) throws CommandFailedException {
|
||||
return chooseStrategy().mount(uri);
|
||||
}
|
||||
|
||||
private static WebDavMounterStrategy chooseStrategy() {
|
||||
if (choosenStrategy == null) {
|
||||
choosenStrategy = getStrategyWhichShouldWork();
|
||||
}
|
||||
return choosenStrategy;
|
||||
}
|
||||
|
||||
private static WebDavMounterStrategy getStrategyWhichShouldWork() {
|
||||
for (WebDavMounterStrategy strategy : STRATEGIES) {
|
||||
if (strategy.shouldWork()) {
|
||||
LOG.info("Using {}", strategy.getClass().getSimpleName());
|
||||
return strategy;
|
||||
}
|
||||
}
|
||||
return new FallbackWebDavMounter();
|
||||
}
|
||||
|
||||
private WebDavMounter() {
|
||||
throw new IllegalStateException("Class is not instantiable.");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 Markus Kreusch
|
||||
* This file is licensed under the terms of the MIT license.
|
||||
* See the LICENSE.txt file for more info.
|
||||
*
|
||||
* Contributors:
|
||||
* Markus Kreusch - Refactored WebDavMounter to use strategy pattern
|
||||
******************************************************************************/
|
||||
package org.cryptomator.ui.util.webdav;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* A strategy able to mount a webdav share and display it to the user.
|
||||
*
|
||||
* @author Markus Kreusch
|
||||
*/
|
||||
interface WebDavMounterStrategy {
|
||||
|
||||
/**
|
||||
* @return {@code false} if this {@code WebDavMounterStrategy} can not work
|
||||
* on the local machine, {@code true} if it could work
|
||||
*/
|
||||
boolean shouldWork();
|
||||
|
||||
/**
|
||||
* Tries to mount a given webdav share.
|
||||
*
|
||||
* @param uri
|
||||
* the {@link URI} of the webdav share
|
||||
* @return a {@link WebDavMount} representing the mounted share
|
||||
* @throws CommandFailedException if the mount operation fails
|
||||
*/
|
||||
WebDavMount mount(URI uri) throws CommandFailedException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 Sebastian Stenzel, Markus Kreusch
|
||||
* This file is licensed under the terms of the MIT license.
|
||||
* See the LICENSE.txt file for more info.
|
||||
*
|
||||
* Contributors:
|
||||
* Sebastian Stenzel - initial API and implementation
|
||||
* Markus Kreusch - Refactored WebDavMounter to use strategy pattern
|
||||
******************************************************************************/
|
||||
package org.cryptomator.ui.util.webdav;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.cryptomator.ui.util.command.Script.fromLines;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.ui.util.command.CommandResult;
|
||||
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("Laufwerk\\s*([A-Z]:)\\s*ist");
|
||||
|
||||
@Override
|
||||
public boolean shouldWork() {
|
||||
return SystemUtils.IS_OS_WINDOWS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebDavMount mount(URI uri) throws CommandFailedException {
|
||||
final Script mountScript = fromLines(
|
||||
"net use * %URI% /persistent:no",
|
||||
"if %errorLevel% neq 0 exit %errorLevel%")
|
||||
.addEnv("URI", toHttpUri(uri));
|
||||
final CommandResult mountResult = mountScript.execute();
|
||||
mountResult.assertOk();
|
||||
final String driveLetter = getDriveLetter(mountResult.getOutput());
|
||||
final Script unmountScript = fromLines(
|
||||
"net use "+driveLetter+" /delete",
|
||||
"if %errorLevel% neq 0 exit %errorLevel%")
|
||||
.addEnv("DRIVE_LETTER", driveLetter);
|
||||
return new WebDavMount() {
|
||||
@Override
|
||||
public void unmount() throws CommandFailedException {
|
||||
unmountScript.execute().assertOk();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
private String toHttpUri(URI uri) {
|
||||
if ("http".equals(uri.getScheme()) || "https".equals(uri.getScheme())) {
|
||||
return uri.toString();
|
||||
} else if ("dav".equals(uri.getScheme())) {
|
||||
return replaceScheme(uri, "http").toString();
|
||||
} else if ("davs".equals(uri.getScheme())) {
|
||||
return replaceScheme(uri, "https").toString();
|
||||
} else {
|
||||
throw new IllegalStateException(format("No webdav uri %s", uri));
|
||||
}
|
||||
}
|
||||
|
||||
private URI replaceScheme(URI uri, String scheme) {
|
||||
try {
|
||||
return new URI(scheme,
|
||||
uri.getUserInfo(),
|
||||
uri.getHost(),
|
||||
uri.getPort(),
|
||||
uri.getPath(),
|
||||
uri.getQuery(),
|
||||
uri.getFragment());
|
||||
} catch (URISyntaxException e) {
|
||||
throw new IllegalStateException("Building an URI with replaced scheme failed");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user