3
3
import java .io .IOException ;
4
4
import java .net .URI ;
5
5
import java .nio .charset .StandardCharsets ;
6
+ import java .nio .file .Files ;
7
+ import java .nio .file .Path ;
8
+ import java .nio .file .Paths ;
6
9
import java .util .concurrent .TimeUnit ;
10
+ import java .util .regex .Matcher ;
11
+ import java .util .regex .Pattern ;
7
12
8
13
import org .slf4j .Logger ;
9
14
import org .slf4j .LoggerFactory ;
@@ -17,6 +22,7 @@ class MacAppleScriptMounter implements MounterStrategy {
17
22
private static final Logger LOG = LoggerFactory .getLogger (MacAppleScriptMounter .class );
18
23
private static final boolean IS_OS_MACOSX = System .getProperty ("os.name" ).contains ("Mac OS X" );
19
24
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)"
20
26
21
27
@ Override
22
28
public boolean isApplicable () {
@@ -30,58 +36,59 @@ public boolean isApplicable() {
30
36
@ Override
31
37
public Mount mount (URI uri , MountParams mountParams ) throws CommandFailedException {
32
38
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 ());
46
41
ProcessBuilder mount = new ProcessBuilder ("/usr/bin/osascript" , "-e" , mountAppleScript );
47
42
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 );
50
51
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 );
53
61
}
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 );
63
62
} catch (IOException e ) {
64
63
throw new CommandFailedException (e );
65
64
}
66
65
}
67
66
68
67
private static class MountImpl implements Mount {
69
68
69
+ private final Path mountPath ;
70
70
private final ProcessBuilder revealCommand ;
71
71
private final ProcessBuilder unmountCommand ;
72
72
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 + "\" " );
80
77
}
81
78
82
79
@ Override
83
80
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
+ }
84
86
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
+ }
85
92
}
86
93
87
94
@ Override
0 commit comments