Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@

<!-- runtime dependencies -->

<api.version>1.3.1</api.version>
<api.version>1.4.0-beta2</api.version>
<secret-service.version>2.0.1-alpha</secret-service.version>
<kdewallet.version>1.4.0</kdewallet.version>
<slf4j.version>2.0.13</slf4j.version>
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
import org.cryptomator.integrations.quickaccess.QuickAccessService;
import org.cryptomator.integrations.revealpath.RevealPathService;
import org.cryptomator.integrations.tray.TrayMenuController;
import org.cryptomator.linux.keychain.KDEWalletKeychainAccess;
import org.cryptomator.linux.keychain.SecretServiceKeychainAccess;
import org.cryptomator.linux.quickaccess.NautilusBookmarks;
import org.cryptomator.linux.revealpath.DBusSendRevealPathService;
import org.cryptomator.linux.tray.AppindicatorTrayMenuController;

Expand All @@ -17,6 +19,7 @@
provides KeychainAccessProvider with SecretServiceKeychainAccess, KDEWalletKeychainAccess;
provides RevealPathService with DBusSendRevealPathService;
provides TrayMenuController with AppindicatorTrayMenuController;
provides QuickAccessService with NautilusBookmarks;

opens org.cryptomator.linux.tray to org.cryptomator.integrations.api;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package org.cryptomator.linux.quickaccess;

import org.cryptomator.integrations.common.CheckAvailability;
import org.cryptomator.integrations.common.DisplayName;
import org.cryptomator.integrations.common.OperatingSystem;
import org.cryptomator.integrations.common.Priority;
import org.cryptomator.integrations.quickaccess.QuickAccessService;
import org.cryptomator.integrations.quickaccess.QuickAccessServiceException;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

@Priority(100)
@CheckAvailability
Comment thread
infeo marked this conversation as resolved.
@OperatingSystem(OperatingSystem.Value.LINUX)
@DisplayName("GNOME Nautilus Bookmarks")
public class NautilusBookmarks implements QuickAccessService {

private static final int MAX_FILE_SIZE = 4096;
private static final Path BOOKMARKS_FILE = Path.of(System.getProperty("user.home"), ".config/gtk-3.0/bookmarks");
private static final Path TMP_FILE = BOOKMARKS_FILE.resolveSibling("bookmarks.cryptomator.tmp");
private static final Lock BOOKMARKS_LOCK = new ReentrantReadWriteLock().writeLock();

@Override
public QuickAccessService.QuickAccessEntry add(Path target, String displayName) throws QuickAccessServiceException {
String entryLine = "file://" + target.toAbsolutePath() + " " + displayName;
try {
BOOKMARKS_LOCK.lock();
if (Files.size(BOOKMARKS_FILE) > MAX_FILE_SIZE) {
throw new IOException("File %s exceeds size of %d bytes".formatted(BOOKMARKS_FILE, MAX_FILE_SIZE));
}
//by reading all lines, we ensure that each line is terminated with EOL
var entries = Files.readAllLines(BOOKMARKS_FILE, StandardCharsets.UTF_8);
entries.add(entryLine);
Files.write(TMP_FILE, entries, StandardCharsets.UTF_8, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
Files.move(TMP_FILE, BOOKMARKS_FILE, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
return new NautilusQuickAccessEntry(entryLine);
} catch (IOException e) {
throw new QuickAccessServiceException("Adding entry to Nautilus bookmarks file failed.", e);
} finally {
BOOKMARKS_LOCK.unlock();
}
}
Comment thread
infeo marked this conversation as resolved.

static class NautilusQuickAccessEntry implements QuickAccessEntry {

private final String line;
private volatile boolean isRemoved = false;

NautilusQuickAccessEntry(String line) {
this.line = line;
}

@Override
public void remove() throws QuickAccessServiceException {
try {
BOOKMARKS_LOCK.lock();
if (isRemoved) {
return;
}
if (Files.size(BOOKMARKS_FILE) > MAX_FILE_SIZE) {
throw new IOException("File %s exceeds size of %d bytes".formatted(BOOKMARKS_FILE, MAX_FILE_SIZE));
}
var entries = Files.readAllLines(BOOKMARKS_FILE);
if (entries.remove(line)) {
Files.write(TMP_FILE, entries, StandardCharsets.UTF_8, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
Files.move(TMP_FILE, BOOKMARKS_FILE, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
}
isRemoved = true;
} catch (IOException e) {
throw new QuickAccessServiceException("Removing entry from Nautilus bookmarks file failed", e);
} finally {
BOOKMARKS_LOCK.unlock();
}
}
}

@CheckAvailability
public static boolean isSupported() {
try {
var nautilusExistsProc = new ProcessBuilder().command("test", "`command -v nautilus`").start();
Comment thread
infeo marked this conversation as resolved.
if (nautilusExistsProc.waitFor(5000, TimeUnit.MILLISECONDS)) {
return nautilusExistsProc.exitValue() == 0;
}
} catch (IOException | InterruptedException e) {
//NO-OP
}
return false;
Comment thread
infeo marked this conversation as resolved.
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.cryptomator.linux.quickaccess.NautilusSidebarService
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.cryptomator.linux.quickaccess;

import org.cryptomator.integrations.quickaccess.QuickAccessServiceException;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import java.nio.file.Path;
import java.time.Duration;

public class NautilusBookmarksIT {

@Test
@DisplayName("Adds for 20s an entryto the Nautilus sidebar")
@Disabled
public void testSidebarIntegration(@TempDir Path tmpdir) throws QuickAccessServiceException, InterruptedException {
var entry = new NautilusBookmarks().add(tmpdir, "integrations-linux");
Thread.sleep(Duration.ofSeconds(20));
entry.remove();
}
}