Skip to content

Commit 59a1240

Browse files
More robust MacAppleScriptMounter
1 parent 3aeb91d commit 59a1240

File tree

2 files changed

+41
-34
lines changed

2 files changed

+41
-34
lines changed

src/main/java/org/cryptomator/frontend/webdav/mount/MacAppleScriptMounter.java

Lines changed: 40 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@
33
import java.io.IOException;
44
import java.net.URI;
55
import java.nio.charset.StandardCharsets;
6+
import java.nio.file.Files;
7+
import java.nio.file.Path;
8+
import java.nio.file.Paths;
69
import java.util.concurrent.TimeUnit;
10+
import java.util.regex.Matcher;
11+
import java.util.regex.Pattern;
712

813
import org.slf4j.Logger;
914
import org.slf4j.LoggerFactory;
@@ -17,6 +22,7 @@ class MacAppleScriptMounter implements MounterStrategy {
1722
private static final Logger LOG = LoggerFactory.getLogger(MacAppleScriptMounter.class);
1823
private static final boolean IS_OS_MACOSX = System.getProperty("os.name").contains("Mac OS X");
1924
private static final String[] OS_VERSION = Iterables.toArray(Splitter.on('.').splitToList(System.getProperty("os.version")), String.class);
25+
private static final Pattern MOUNT_PATTERN = Pattern.compile(".* on (\\S+) \\(.*\\)"); // catches mount point in "http://host:port/foo/ on /Volumes/foo (webdav, nodev, noexec, nosuid)"
2026

2127
@Override
2228
public boolean isApplicable() {
@@ -30,58 +36,59 @@ public boolean isApplicable() {
3036
@Override
3137
public Mount mount(URI uri, MountParams mountParams) throws CommandFailedException {
3238
try {
33-
ProcessBuilder storeCredentials = new ProcessBuilder("security", "add-internet-password", //
34-
"-a", "anonymous", //
35-
"-s", "localhost", //
36-
"-P", String.valueOf(uri.getPort()), //
37-
"-r", "http", //
38-
"-D", "Cryptomator WebDAV Access", //
39-
"-T", "/System/Library/CoreServices/NetAuthAgent.app/Contents/MacOS/NetAuthSysAgent");
40-
ProcessUtil.startAndWaitFor(storeCredentials, 10, TimeUnit.SECONDS);
41-
} catch (CommandFailedException e) {
42-
LOG.warn("Unable to store credentials for WebDAV access: {}", e.getMessage());
43-
}
44-
try {
45-
String mountAppleScript = String.format("mount volume \"%s\"", uri.toASCIIString());
39+
// mount:
40+
String mountAppleScript = String.format("mount volume \"%s\" as user name \"anonymous\" with password \"\"", uri.toASCIIString());
4641
ProcessBuilder mount = new ProcessBuilder("/usr/bin/osascript", "-e", mountAppleScript);
4742
Process mountProcess = mount.start();
48-
String stdout = ProcessUtil.toString(mountProcess.getInputStream(), StandardCharsets.UTF_8);
49-
ProcessUtil.waitFor(mountProcess, 30, TimeUnit.SECONDS);
43+
ProcessUtil.waitFor(mountProcess, 60, TimeUnit.SECONDS); // huge timeout since the user might need to confirm connecting via http
44+
ProcessUtil.assertExitValue(mountProcess, 0);
45+
46+
// verify mounted:
47+
ProcessBuilder verifyMount = new ProcessBuilder("/bin/sh", "-c", "mount | grep \"" + uri.toASCIIString() + "\"");
48+
Process verifyProcess = verifyMount.start();
49+
String stdout = ProcessUtil.toString(verifyProcess.getInputStream(), StandardCharsets.UTF_8);
50+
ProcessUtil.waitFor(verifyProcess, 10, TimeUnit.SECONDS);
5051
ProcessUtil.assertExitValue(mountProcess, 0);
51-
if (!stdout.startsWith("file ")) {
52-
throw new CommandFailedException("Unexpected mount result: " + stdout);
52+
53+
// determine mount point:
54+
Matcher mountPointMatcher = MOUNT_PATTERN.matcher(stdout);
55+
if (mountPointMatcher.find()) {
56+
String mountPoint = mountPointMatcher.group(1);
57+
LOG.debug("Mounted {} on {}.", uri.toASCIIString(), mountPoint);
58+
return new MountImpl(Paths.get(mountPoint));
59+
} else {
60+
throw new CommandFailedException("Mount succeeded, but failed to determine mount point in string: " + stdout);
5361
}
54-
assert stdout.startsWith("file ");
55-
String volumeIdentifier = CharMatcher.whitespace().trimFrom(stdout.substring(5)); // remove preceeding "file "
56-
String waitAppleScript1 = String.format("tell application \"Finder\" to repeat while not (\"%s\" exists)", volumeIdentifier);
57-
String waitAppleScript2 = "delay 0.1";
58-
String waitAppleScript3 = "end repeat";
59-
ProcessBuilder wait = new ProcessBuilder("/usr/bin/osascript", "-e", waitAppleScript1, "-e", waitAppleScript2, "-e", waitAppleScript3);
60-
ProcessUtil.assertExitValue(ProcessUtil.startAndWaitFor(wait, 30, TimeUnit.SECONDS), 0);
61-
LOG.debug("Mounted {}.", uri.toASCIIString());
62-
return new MountImpl(volumeIdentifier);
6362
} catch (IOException e) {
6463
throw new CommandFailedException(e);
6564
}
6665
}
6766

6867
private static class MountImpl implements Mount {
6968

69+
private final Path mountPath;
7070
private final ProcessBuilder revealCommand;
7171
private final ProcessBuilder unmountCommand;
7272

73-
private MountImpl(String volumeIdentifier) {
74-
String openAppleScript = String.format("tell application \"Finder\" to open \"%s\"", volumeIdentifier);
75-
String activateAppleScript = String.format("tell application \"Finder\" to activate \"%s\"", volumeIdentifier);
76-
String ejectAppleScript = String.format("tell application \"Finder\" to if \"%s\" exists then eject \"%s\"", volumeIdentifier, volumeIdentifier);
77-
78-
this.revealCommand = new ProcessBuilder("/usr/bin/osascript", "-e", openAppleScript, "-e", activateAppleScript);
79-
this.unmountCommand = new ProcessBuilder("/usr/bin/osascript", "-e", ejectAppleScript);
73+
private MountImpl(Path mountPath) {
74+
this.mountPath = mountPath;
75+
this.revealCommand = new ProcessBuilder("open", mountPath.toString());
76+
this.unmountCommand = new ProcessBuilder("sh", "-c", "diskutil umount \"" + mountPath + "\"");
8077
}
8178

8279
@Override
8380
public void unmount() throws CommandFailedException {
81+
if (!Files.isDirectory(mountPath)) {
82+
// unmounting a mounted drive will delete the associated mountpoint (at least under OS X 10.11)
83+
LOG.debug("Volume already unmounted.");
84+
return;
85+
}
8486
ProcessUtil.assertExitValue(ProcessUtil.startAndWaitFor(unmountCommand, 10, TimeUnit.SECONDS), 0);
87+
try {
88+
Files.deleteIfExists(mountPath);
89+
} catch (IOException e) {
90+
LOG.warn("Could not delete {} due to exception: {}", mountPath, e.getMessage());
91+
}
8592
}
8693

8794
@Override

src/test/java/org/cryptomator/frontend/webdav/MirroringTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public static void main(String[] args) throws IOException, CommandFailedExceptio
3535
MountParams mountParams = MountParams.create() //
3636
.withWindowsDriveLetter("X:") //
3737
.withPreferredGvfsScheme("dav") //
38-
.withWebdavHostname("cryptomator-vault") //
38+
// .withWebdavHostname("cryptomator-vault") // uncomment only if hostname is set in /etc/hosts!
3939
.build();
4040
Mount mount = servlet.mount(mountParams);
4141
mount.reveal();

0 commit comments

Comments
 (0)