mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-14 08:41:28 +00:00
- fixed clean unmounting
- fixed correct subprocess status codes (not using status code of parent shell)
This commit is contained in:
@@ -22,8 +22,10 @@ import javafx.beans.value.ObservableValue;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ProgressIndicator;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@@ -50,6 +52,9 @@ public class UnlockController implements Initializable {
|
||||
|
||||
@FXML
|
||||
private SecPasswordField passwordField;
|
||||
|
||||
@FXML
|
||||
private ProgressIndicator progressIndicator;
|
||||
|
||||
@FXML
|
||||
private Label messageLabel;
|
||||
@@ -83,6 +88,7 @@ public class UnlockController implements Initializable {
|
||||
final CharSequence password = passwordField.getCharacters();
|
||||
InputStream masterKeyInputStream = null;
|
||||
try {
|
||||
progressIndicator.setVisible(true);
|
||||
masterKeyInputStream = Files.newInputStream(masterKeyPath, StandardOpenOption.READ);
|
||||
directory.getCryptor().decryptMasterKey(masterKeyInputStream, password);
|
||||
if (!directory.startServer()) {
|
||||
@@ -91,16 +97,16 @@ public class UnlockController implements Initializable {
|
||||
return;
|
||||
}
|
||||
directory.setUnlocked(true);
|
||||
directory.mount();
|
||||
if (listener != null) {
|
||||
listener.didUnlock(this);
|
||||
}
|
||||
directory.mountAsync(this::didUnlockAndMount);
|
||||
} catch (DecryptFailedException | IOException ex) {
|
||||
progressIndicator.setVisible(false);
|
||||
messageLabel.setText(rb.getString("unlock.errorMessage.decryptionFailed"));
|
||||
LOG.error("Decryption failed for technical reasons.", ex);
|
||||
} catch (WrongPasswordException e) {
|
||||
progressIndicator.setVisible(false);
|
||||
messageLabel.setText(rb.getString("unlock.errorMessage.wrongPassword"));
|
||||
} catch (UnsupportedKeyLengthException ex) {
|
||||
progressIndicator.setVisible(false);
|
||||
messageLabel.setText(rb.getString("unlock.errorMessage.unsupportedKeyLengthInstallJCE"));
|
||||
LOG.warn("Unsupported Key-Length. Please install Oracle Java Cryptography Extension (JCE).", ex);
|
||||
} finally {
|
||||
@@ -127,6 +133,16 @@ public class UnlockController implements Initializable {
|
||||
LOG.trace("Invalid path: " + directory.getPath(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private Void didUnlockAndMount(boolean mountSuccess) {
|
||||
Platform.runLater(() -> {
|
||||
progressIndicator.setVisible(false);
|
||||
if (listener != null) {
|
||||
listener.didUnlock(this);
|
||||
}
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
/* Getter/Setter */
|
||||
|
||||
|
||||
@@ -4,9 +4,13 @@ import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.util.Callback;
|
||||
|
||||
import org.cryptomator.crypto.Cryptor;
|
||||
import org.cryptomator.crypto.SamplingDecorator;
|
||||
@@ -67,7 +71,21 @@ public class Directory implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean mount() {
|
||||
public void mountAsync(Callback<Boolean, Void> callback) {
|
||||
final FutureTask<Boolean> mountTask = new FutureTask<>(this::mount);
|
||||
final Executor exec = Executors.newSingleThreadExecutor();
|
||||
exec.execute(mountTask);
|
||||
exec.execute(() -> {
|
||||
try {
|
||||
final Boolean result = mountTask.get();
|
||||
callback.call(result);
|
||||
} catch (Exception e) {
|
||||
callback.call(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean mount() {
|
||||
try {
|
||||
webDavMount = WebDavMounter.mount(server.getPort());
|
||||
return true;
|
||||
|
||||
@@ -20,8 +20,6 @@ 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();
|
||||
@@ -41,20 +39,16 @@ public class CommandResult {
|
||||
}
|
||||
|
||||
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);
|
||||
if (!finished) {
|
||||
throw new IllegalStateException("Command not yet finished.");
|
||||
}
|
||||
return new String(output.toByteArray());
|
||||
}
|
||||
|
||||
public String getError(long timeout, TimeUnit unit) throws CommandFailedException {
|
||||
waitAndAssertOk(timeout, unit);
|
||||
public String getError() throws CommandFailedException {
|
||||
if (!finished) {
|
||||
throw new IllegalStateException("Command not yet finished.");
|
||||
}
|
||||
return new String(error.toByteArray());
|
||||
}
|
||||
|
||||
@@ -90,10 +84,6 @@ public class CommandResult {
|
||||
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);
|
||||
|
||||
@@ -13,7 +13,10 @@ 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 java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.ui.util.mount.CommandFailedException;
|
||||
|
||||
@@ -41,15 +44,26 @@ 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"};
|
||||
|
||||
public static final String WINDOWS_DEFAULT_CLI[] = {"cmd", "/C"};
|
||||
public static final String UNIX_DEFAULT_CLI[] = {"/bin/sh", "-c"};
|
||||
|
||||
/**
|
||||
* Executes all lines in the given script in the specified order. Stops as soon as the first command fails.
|
||||
* @param script Script containing command lines and environment variables.
|
||||
* @return Result of the last command, if it exited successfully.
|
||||
* @throws CommandFailedException If one of the command lines in the given script fails.
|
||||
*/
|
||||
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());
|
||||
final List<String> env = script.environment().entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.toList());
|
||||
CommandResult result = null;
|
||||
for (final String line : script.getLines()) {
|
||||
final String[] cmds = ArrayUtils.add(determineCli(), line);
|
||||
final Process proc = Runtime.getRuntime().exec(cmds, env.toArray(new String[0]));
|
||||
result = run(proc, script.getLines());
|
||||
result.assertOk(script.getTimeout(), script.getTimeoutUnit());
|
||||
}
|
||||
return result;
|
||||
} catch (IOException e) {
|
||||
throw new CommandFailedException(e);
|
||||
}
|
||||
|
||||
@@ -10,17 +10,22 @@ package org.cryptomator.ui.util.command;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.cryptomator.ui.util.mount.CommandFailedException;
|
||||
|
||||
public final class Script {
|
||||
|
||||
private static final int DEFAULT_TIMEOUT_MILLISECONDS = 3000;
|
||||
|
||||
public static Script fromLines(String ... commands) {
|
||||
return new Script(commands);
|
||||
}
|
||||
|
||||
private final String[] lines;
|
||||
private final Map<String,String> environment = new HashMap<>();
|
||||
private long timeout = DEFAULT_TIMEOUT_MILLISECONDS;
|
||||
private TimeUnit timeoutUnit = TimeUnit.MILLISECONDS;
|
||||
|
||||
private Script(String[] lines) {
|
||||
this.lines = lines;
|
||||
@@ -54,5 +59,21 @@ public final class Script {
|
||||
environment.put(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public long getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
public void setTimeout(long timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
public TimeUnit getTimeoutUnit() {
|
||||
return timeoutUnit;
|
||||
}
|
||||
|
||||
public void setTimeoutUnit(TimeUnit timeoutUnit) {
|
||||
this.timeoutUnit = timeoutUnit;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ final class LinuxGvfsWebDavMounter implements WebDavMounterStrategy {
|
||||
if (SystemUtils.IS_OS_LINUX) {
|
||||
final Script checkScripts = Script.fromLines("which gvfs-mount xdg-open");
|
||||
try {
|
||||
checkScripts.execute().assertOk();
|
||||
checkScripts.execute();
|
||||
return true;
|
||||
} catch (CommandFailedException e) {
|
||||
return false;
|
||||
@@ -40,11 +40,11 @@ final class LinuxGvfsWebDavMounter implements WebDavMounterStrategy {
|
||||
"set -x",
|
||||
"gvfs-mount -u \"dav://[::1]:$PORT\"")
|
||||
.addEnv("URI", String.valueOf(localPort));
|
||||
mountScript.execute().assertOk();
|
||||
mountScript.execute();
|
||||
return new WebDavMount() {
|
||||
@Override
|
||||
public void unmount() throws CommandFailedException {
|
||||
unmountScript.execute().assertOk();
|
||||
unmountScript.execute();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -23,21 +23,19 @@ final class MacOsXWebDavMounter implements WebDavMounterStrategy {
|
||||
public WebDavMount mount(int localPort) throws CommandFailedException {
|
||||
final String path = "/Volumes/Cryptomator" + localPort;
|
||||
final Script mountScript = Script.fromLines(
|
||||
"set -x",
|
||||
"mkdir \"$MOUNT_PATH\"",
|
||||
"mount_webdav -S -v Cryptomator \"[::1]:$PORT\" \"$MOUNT_PATH\"",
|
||||
"open \"$MOUNT_PATH\"")
|
||||
.addEnv("PORT", String.valueOf(localPort))
|
||||
.addEnv("MOUNT_PATH", path);
|
||||
final Script unmountScript = Script.fromLines(
|
||||
"set -x",
|
||||
"umount $MOUNT_PATH")
|
||||
.addEnv("MOUNT_PATH", path);
|
||||
mountScript.execute().assertOk();
|
||||
mountScript.execute();
|
||||
return new WebDavMount() {
|
||||
@Override
|
||||
public void unmount() throws CommandFailedException {
|
||||
unmountScript.execute().assertOk();
|
||||
unmountScript.execute();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.cryptomator.ui.util.mount;
|
||||
|
||||
import static org.cryptomator.ui.util.command.Script.fromLines;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -36,21 +37,18 @@ final class WindowsWebDavMounter implements WebDavMounterStrategy {
|
||||
|
||||
@Override
|
||||
public WebDavMount mount(int localPort) throws CommandFailedException {
|
||||
final Script mountScript = fromLines(
|
||||
"net use * http://0--1.ipv6-literal.net:%PORT% /persistent:no",
|
||||
"if %errorLevel% neq 0 exit %errorLevel%")
|
||||
final Script mountScript = fromLines("net use * http://0--1.ipv6-literal.net:%PORT% /persistent:no")
|
||||
.addEnv("PORT", String.valueOf(localPort));
|
||||
mountScript.setTimeout(30);
|
||||
mountScript.setTimeoutUnit(TimeUnit.SECONDS);
|
||||
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%")
|
||||
final Script unmountScript = fromLines("net use "+driveLetter+" /delete")
|
||||
.addEnv("DRIVE_LETTER", driveLetter);
|
||||
return new WebDavMount() {
|
||||
@Override
|
||||
public void unmount() throws CommandFailedException {
|
||||
unmountScript.execute().assertOk();
|
||||
unmountScript.execute();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<?import java.lang.String?>
|
||||
<?import org.cryptomator.ui.controls.SecPasswordField?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.ProgressIndicator?>
|
||||
|
||||
|
||||
<GridPane vgap="12.0" hgap="12.0" prefWidth="400.0" fx:controller="org.cryptomator.ui.UnlockController" xmlns:fx="http://javafx.com/fxml">
|
||||
@@ -39,6 +40,9 @@
|
||||
<!-- Row 2 -->
|
||||
<Button text="%unlock.button.unlock" defaultButton="true" GridPane.rowIndex="2" GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.halignment="RIGHT" prefWidth="150.0" onAction="#didClickUnlockButton" focusTraversable="false"/>
|
||||
|
||||
<!-- Row 3 -->
|
||||
<ProgressIndicator progress="-1" fx:id="progressIndicator" GridPane.rowIndex="3" GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.halignment="CENTER" visible="false"/>
|
||||
|
||||
<!-- Row 5 -->
|
||||
<Label fx:id="messageLabel" GridPane.rowIndex="5" GridPane.columnIndex="0" GridPane.columnSpan="2" />
|
||||
</children>
|
||||
|
||||
Reference in New Issue
Block a user