|
| 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 | +} |
0 commit comments