Skip to content

Commit 203f9c4

Browse files
committed
for isSupported() replace "whch" usage by dbus-send:
* checking known file manager object paths * compatible with dbus impls returning empty nodes * flatpak compatible
1 parent 9afe012 commit 203f9c4

File tree

1 file changed

+69
-10
lines changed

1 file changed

+69
-10
lines changed

src/main/java/org/cryptomator/linux/revealpath/DBusFileMangerRevealPath.java

Lines changed: 69 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@
1111
import java.nio.file.Path;
1212
import java.nio.file.attribute.BasicFileAttributes;
1313
import java.util.Arrays;
14+
import java.util.List;
15+
import java.util.Objects;
1416
import java.util.concurrent.CountDownLatch;
1517
import java.util.concurrent.TimeUnit;
1618
import java.util.stream.Collectors;
1719

1820
public class DBusFileMangerRevealPath implements RevealPathService {
1921

22+
private static final String[] FILEMANAGER_OBJECT_PATHS = {"/org/gnome/Nautilus", "/org/kde/dolphin", "/xfce/Thunar"};
2023
private static final String FOR_FOLDERS = "org.freedesktop.FileManager1.ShowFolders";
2124
private static final String FOR_FILES = "org.freedesktop.FileManager1.ShowItems";
2225
private static final int TIMEOUT_THRESHOLD = 5000;
@@ -56,24 +59,80 @@ public void reveal(Path path) throws RevealFailedException {
5659

5760
@Override
5861
public boolean isSupported() {
59-
CountDownLatch waitBarrier = new CountDownLatch(3);
60-
ProcessBuilder builderExistsDbusSend = new ProcessBuilder().command("which", "dbus-send");
61-
ProcessBuilder builderExistsNautilus = new ProcessBuilder().command("which", "nautilus");
62-
ProcessBuilder builderExistsDolphin = new ProcessBuilder().command("which", "dolphin");
62+
CountDownLatch waitBarrier = new CountDownLatch(FILEMANAGER_OBJECT_PATHS.length + 1);
63+
ProcessBuilder dbusSendExistBuilder = new ProcessBuilder().command("test", " `command -v dbus-send`");
64+
List<ProcessBuilder> fileManagerExistBuilders = Arrays.stream(FILEMANAGER_OBJECT_PATHS)
65+
.map(DBusFileMangerRevealPath::createDbusObjectCheck).toList();
66+
6367
try {
64-
var existsDbusSend = builderExistsDbusSend.start();
68+
var existsDbusSend = dbusSendExistBuilder.start();
6569
existsDbusSend.onExit().thenRun(waitBarrier::countDown);
66-
var existsNautilus = builderExistsNautilus.start();
67-
existsNautilus.onExit().thenRun(waitBarrier::countDown);
68-
var existsDolphin = builderExistsDolphin.start();
69-
existsDolphin.onExit().thenRun(waitBarrier::countDown);
70+
//TODO: process process-output in paralell
71+
List<Process> fileManagerChecks = fileManagerExistBuilders.stream().map(builder -> {
72+
try {
73+
return builder.start();
74+
} catch (IOException e) {
75+
waitBarrier.countDown(); //to prevent blocking
76+
return null;
77+
}
78+
}).filter(Objects::nonNull).toList();
79+
80+
fileManagerChecks.forEach(process -> process.onExit().thenRun(waitBarrier::countDown));
7081
if (waitBarrier.await(TIMEOUT_THRESHOLD, TimeUnit.MILLISECONDS)) {
71-
return existsDbusSend.exitValue() == 0 && (existsNautilus.exitValue() == 0 | existsDolphin.exitValue() == 0);
82+
var zeroExitfileManagerChecks = fileManagerChecks.stream().filter(p -> p.exitValue() == 0).toList();
83+
if (existsDbusSend.exitValue() == 0 && zeroExitfileManagerChecks.size() != 0) {
84+
return parseOutputsForActualDBusObject(zeroExitfileManagerChecks);
85+
}
7286
}
7387
} catch (IOException | InterruptedException e) {
7488
//NO-OP
7589
}
7690
return false;
7791
}
7892

93+
/**
94+
* Parses process stdout to see if dbus-send answer is just an empty node.
95+
* <p>
96+
* Some dbus-send implementations return on calling methods on not-existing objects an empty node
97+
* <pre>
98+
* &lt;node&gt;
99+
* &lt;/node&gt;
100+
* </pre>
101+
* instead of throwing an error.
102+
* <p>
103+
* Regarding parsing, see the dbus spec <a href="https://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format">on the introsepction format</a>.
104+
*
105+
* @param dbusChecks List of dbus-send processes with zero-exitcode
106+
* @return if one dbus-send output contains actual content
107+
* @throws IOException if the Inputer reader on the process output cannot be created
108+
*/
109+
private boolean parseOutputsForActualDBusObject(List<Process> dbusChecks) throws IOException {
110+
for (var check : dbusChecks) {
111+
try (var reader = check.inputReader(StandardCharsets.UTF_8)) {
112+
boolean passedInitialNode = false;
113+
var line = reader.readLine();
114+
while (line != null) {
115+
if (passedInitialNode) {
116+
return !line.equals("</node>");
117+
}
118+
passedInitialNode = line.startsWith("<node"); //root node might also contain name, hence no closing bracket
119+
line = reader.readLine();
120+
}
121+
}
122+
}
123+
return false;
124+
}
125+
126+
private static ProcessBuilder createDbusObjectCheck(String dbusObjectPath) {
127+
return new ProcessBuilder().command(
128+
"dbus-send",
129+
"--session",
130+
"--print-reply",
131+
"--reply-timeout=" + TIMEOUT_THRESHOLD,
132+
"--dest=org.freedesktop.FileManager1",
133+
"--type=method_call",
134+
dbusObjectPath,
135+
"org.freedesktop.DBus.Introspectable.Introspect"
136+
);
137+
}
79138
}

0 commit comments

Comments
 (0)