Skip to content

Commit 0c6662e

Browse files
authored
Merge pull request #16 from cryptomator/feature/reveal-path-service
Feature: RevealPathService via dbus freedesktop FileManger interface
2 parents 80f7770 + d35878e commit 0c6662e

File tree

4 files changed

+120
-1
lines changed

4 files changed

+120
-1
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
<project.jdk.version>17</project.jdk.version>
4040

4141
<!-- runtime dependencies -->
42-
<api.version>1.1.0</api.version>
42+
<api.version>1.2.0-beta4</api.version>
4343
<secret-service.version>1.7.0</secret-service.version>
4444
<kdewallet.version>1.2.6</kdewallet.version>
4545
<guava.version>31.1-jre</guava.version>
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package org.cryptomator.linux.revealpath;
2+
3+
import org.cryptomator.integrations.revealpath.RevealFailedException;
4+
import org.cryptomator.integrations.revealpath.RevealPathService;
5+
6+
import java.io.IOException;
7+
import java.net.URLEncoder;
8+
import java.nio.charset.StandardCharsets;
9+
import java.nio.file.Files;
10+
import java.nio.file.LinkOption;
11+
import java.nio.file.Path;
12+
import java.nio.file.attribute.BasicFileAttributes;
13+
import java.util.Arrays;
14+
import java.util.concurrent.CountDownLatch;
15+
import java.util.concurrent.TimeUnit;
16+
import java.util.stream.Collectors;
17+
18+
public class DBusFileMangerRevealPath implements RevealPathService {
19+
20+
private static final String FOR_FOLDERS = "org.freedesktop.FileManager1.ShowFolders";
21+
private static final String FOR_FILES = "org.freedesktop.FileManager1.ShowItems";
22+
private static final int TIMEOUT_THRESHOLD=5000;
23+
24+
@Override
25+
public void reveal(Path path) throws RevealFailedException {
26+
try {
27+
var attrs = Files.readAttributes(path, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
28+
var uriPath = Arrays.stream(path.toUri().getPath().split("/")).map(s -> URLEncoder.encode(s, StandardCharsets.UTF_8).replace("+", "%20")).collect(Collectors.joining("/"));
29+
ProcessBuilder pb = new ProcessBuilder().command("dbus-send",
30+
"--print-reply",
31+
"--reply-timeout="+TIMEOUT_THRESHOLD,
32+
"--dest=org.freedesktop.FileManager1",
33+
"--type=method_call",
34+
"/org/freedesktop/FileManager1",
35+
attrs.isDirectory() ? FOR_FOLDERS : FOR_FILES,
36+
String.format("array:string:file://%s", uriPath),
37+
"string:\"\""
38+
);
39+
var process = pb.start();
40+
if (process.waitFor(TIMEOUT_THRESHOLD, TimeUnit.MILLISECONDS)) {
41+
int exitValue = process.exitValue();
42+
if (exitValue != 0) {
43+
throw new RevealFailedException("dbus-send returned with code" + exitValue);
44+
}
45+
}
46+
} catch (IOException e) {
47+
throw new RevealFailedException(e);
48+
} catch (InterruptedException e) {
49+
Thread.currentThread().interrupt();
50+
throw new RevealFailedException(e);
51+
}
52+
}
53+
54+
@Override
55+
public boolean isSupported() {
56+
CountDownLatch waitBarrier = new CountDownLatch(3);
57+
ProcessBuilder builderExistsDbusSend = new ProcessBuilder().command("which", "dbus-send");
58+
ProcessBuilder builderExistsNautilus = new ProcessBuilder().command("which", "nautilus");
59+
ProcessBuilder builderExistsDolphin = new ProcessBuilder().command("which", "dolphin");
60+
try {
61+
var existsDbusSend = builderExistsDbusSend.start();
62+
existsDbusSend.onExit().thenRun(waitBarrier::countDown);
63+
var existsNautilus = builderExistsNautilus.start();
64+
existsNautilus.onExit().thenRun(waitBarrier::countDown);
65+
var existsDolphin = builderExistsDolphin.start();
66+
existsDolphin.onExit().thenRun(waitBarrier::countDown);
67+
if (waitBarrier.await(TIMEOUT_THRESHOLD, TimeUnit.MILLISECONDS)) {
68+
return existsDbusSend.exitValue() == 0 && (existsNautilus.exitValue() == 0 | existsDolphin.exitValue() == 0);
69+
}
70+
} catch (IOException | InterruptedException e) {
71+
//NO-OP
72+
}
73+
return false;
74+
}
75+
76+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
org.cryptomator.linux.revealpath.DBusFileMangerRevealPath
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package org.cryptomator.linux.keychain;
2+
3+
import org.cryptomator.integrations.revealpath.RevealFailedException;
4+
import org.cryptomator.linux.revealpath.DBusFileMangerRevealPath;
5+
import org.junit.jupiter.api.Assertions;
6+
import org.junit.jupiter.api.Assumptions;
7+
import org.junit.jupiter.api.Disabled;
8+
import org.junit.jupiter.api.Test;
9+
import org.junit.jupiter.api.condition.EnabledOnOs;
10+
import org.junit.jupiter.api.condition.OS;
11+
import org.junit.jupiter.api.io.TempDir;
12+
13+
import java.nio.file.Path;
14+
15+
@EnabledOnOs(OS.LINUX)
16+
@Disabled
17+
public class DbusFileManagerRevealPathTest {
18+
19+
@TempDir Path tmpDir;
20+
DBusFileMangerRevealPath inTest = new DBusFileMangerRevealPath();
21+
22+
@Test
23+
public void testIsSupported() {
24+
Assertions.assertDoesNotThrow(() -> inTest.isSupported());
25+
}
26+
27+
@Test
28+
public void testRevealSuccess() {
29+
DBusFileMangerRevealPath revealPathService = new DBusFileMangerRevealPath();
30+
Assumptions.assumeTrue(revealPathService.isSupported());
31+
32+
Assertions.assertDoesNotThrow(() -> revealPathService.reveal(tmpDir));
33+
}
34+
35+
@Test
36+
public void testRevealFail() {
37+
DBusFileMangerRevealPath revealPathService = new DBusFileMangerRevealPath();
38+
Assumptions.assumeTrue(revealPathService.isSupported());
39+
40+
Assertions.assertThrows(RevealFailedException.class, () -> revealPathService.reveal(tmpDir.resolve("foobar")));
41+
}
42+
}

0 commit comments

Comments
 (0)