mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-04-21 10:06:55 -04:00
Create SPI for cloud location presets
This commit is contained in:
@@ -1,4 +1,15 @@
|
||||
import ch.qos.logback.classic.spi.Configurator;
|
||||
import org.cryptomator.common.locationpresets.DropboxMacLocationPresetsProvider;
|
||||
import org.cryptomator.common.locationpresets.DropboxWindowsLocationPresetsProvider;
|
||||
import org.cryptomator.common.locationpresets.GoogleDriveMacLocationPresetsProvider;
|
||||
import org.cryptomator.common.locationpresets.GoogleDriveWindowsLocationPresetsProvider;
|
||||
import org.cryptomator.common.locationpresets.ICloudMacLocationPresetsProvider;
|
||||
import org.cryptomator.common.locationpresets.ICloudWindowsLocationPresetsProvider;
|
||||
import org.cryptomator.common.locationpresets.LocationPresetsProvider;
|
||||
import org.cryptomator.common.locationpresets.MegaLocationPresetsProvider;
|
||||
import org.cryptomator.common.locationpresets.OneDriveMacLocationPresetsProvider;
|
||||
import org.cryptomator.common.locationpresets.OneDriveWindowsLocationPresetsProvider;
|
||||
import org.cryptomator.common.locationpresets.PCloudLocationPresetsProvider;
|
||||
import org.cryptomator.integrations.tray.TrayMenuController;
|
||||
import org.cryptomator.logging.LogbackConfiguratorFactory;
|
||||
import org.cryptomator.ui.traymenu.AwtTrayMenuController;
|
||||
@@ -39,4 +50,9 @@ open module org.cryptomator.desktop {
|
||||
|
||||
provides TrayMenuController with AwtTrayMenuController;
|
||||
provides Configurator with LogbackConfiguratorFactory;
|
||||
provides LocationPresetsProvider with DropboxMacLocationPresetsProvider, //
|
||||
DropboxWindowsLocationPresetsProvider, ICloudMacLocationPresetsProvider, //
|
||||
ICloudWindowsLocationPresetsProvider, GoogleDriveWindowsLocationPresetsProvider, //
|
||||
GoogleDriveMacLocationPresetsProvider, PCloudLocationPresetsProvider, //
|
||||
MegaLocationPresetsProvider, OneDriveMacLocationPresetsProvider, OneDriveWindowsLocationPresetsProvider;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.cryptomator.common.locationpresets;
|
||||
|
||||
import org.cryptomator.integrations.common.CheckAvailability;
|
||||
import org.cryptomator.integrations.common.OperatingSystem;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC;
|
||||
|
||||
@OperatingSystem(MAC)
|
||||
public final class DropboxMacLocationPresetsProvider implements LocationPresetsProvider {
|
||||
|
||||
private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/Library/CloudStorage/Dropbox");
|
||||
|
||||
|
||||
@CheckAvailability
|
||||
public static boolean isPresent() {
|
||||
return Files.isDirectory(LOCATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<LocationPreset> getLocations() {
|
||||
return Stream.of(new LocationPreset("Dropbox", LOCATION));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.cryptomator.common.locationpresets;
|
||||
|
||||
import org.cryptomator.integrations.common.CheckAvailability;
|
||||
import org.cryptomator.integrations.common.OperatingSystem;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS;
|
||||
|
||||
@OperatingSystem(WINDOWS)
|
||||
public final class DropboxWindowsLocationPresetsProvider implements LocationPresetsProvider {
|
||||
|
||||
private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/Dropbox");
|
||||
|
||||
|
||||
@CheckAvailability
|
||||
public static boolean isPresent() {
|
||||
return Files.isDirectory(LOCATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<LocationPreset> getLocations() {
|
||||
return Stream.of(new LocationPreset("Dropbox", LOCATION));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.cryptomator.common.locationpresets;
|
||||
|
||||
import org.cryptomator.integrations.common.CheckAvailability;
|
||||
import org.cryptomator.integrations.common.OperatingSystem;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC;
|
||||
|
||||
@OperatingSystem(MAC)
|
||||
public final class GoogleDriveMacLocationPresetsProvider implements LocationPresetsProvider {
|
||||
|
||||
private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/Google Drive");
|
||||
|
||||
|
||||
@CheckAvailability
|
||||
public static boolean isPresent() {
|
||||
return Files.isDirectory(LOCATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<LocationPreset> getLocations() {
|
||||
return Stream.of(new LocationPreset("Google Drive", LOCATION));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.cryptomator.common.locationpresets;
|
||||
|
||||
import org.cryptomator.integrations.common.CheckAvailability;
|
||||
import org.cryptomator.integrations.common.OperatingSystem;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS;
|
||||
|
||||
@OperatingSystem(WINDOWS)
|
||||
public final class GoogleDriveWindowsLocationPresetsProvider implements LocationPresetsProvider {
|
||||
|
||||
private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/Google Drive/My Drive");
|
||||
|
||||
|
||||
@CheckAvailability
|
||||
public static boolean isPresent() {
|
||||
return Files.isDirectory(LOCATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<LocationPreset> getLocations() {
|
||||
return Stream.of(new LocationPreset("Google Drive", LOCATION));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.cryptomator.common.locationpresets;
|
||||
|
||||
import org.cryptomator.integrations.common.CheckAvailability;
|
||||
import org.cryptomator.integrations.common.OperatingSystem;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC;
|
||||
|
||||
@OperatingSystem(MAC)
|
||||
public final class ICloudMacLocationPresetsProvider implements LocationPresetsProvider {
|
||||
|
||||
private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/Library/Mobile Documents/com~apple~CloudDocs");
|
||||
|
||||
@CheckAvailability
|
||||
public static boolean isPresent() {
|
||||
return Files.isDirectory(LOCATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<LocationPreset> getLocations() {
|
||||
return Stream.of(new LocationPreset("iCloud Drive", LOCATION));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.cryptomator.common.locationpresets;
|
||||
|
||||
import org.cryptomator.integrations.common.CheckAvailability;
|
||||
import org.cryptomator.integrations.common.OperatingSystem;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS;
|
||||
|
||||
@OperatingSystem(WINDOWS)
|
||||
public final class ICloudWindowsLocationPresetsProvider implements LocationPresetsProvider {
|
||||
|
||||
private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/iCloudDrive");
|
||||
|
||||
@CheckAvailability
|
||||
public static boolean isPresent() {
|
||||
return Files.isDirectory(LOCATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<LocationPreset> getLocations() {
|
||||
return Stream.of(new LocationPreset("iCloud Drive", LOCATION));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.cryptomator.common.locationpresets;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public record LocationPreset(String name, Path path) {
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.cryptomator.common.locationpresets;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public interface LocationPresetsProvider {
|
||||
|
||||
String USER_HOME = System.getProperty("user.home");
|
||||
|
||||
Stream<LocationPreset> getLocations();
|
||||
|
||||
static Path resolveLocation(String p) {
|
||||
if (p.startsWith("~/")) {
|
||||
return Path.of(USER_HOME, p.substring(2));
|
||||
} else {
|
||||
return Path.of(p);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.cryptomator.common.locationpresets;
|
||||
|
||||
import org.cryptomator.integrations.common.CheckAvailability;
|
||||
import org.cryptomator.integrations.common.OperatingSystem;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC;
|
||||
import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS;
|
||||
|
||||
@OperatingSystem(WINDOWS)
|
||||
@OperatingSystem(MAC)
|
||||
public final class MegaLocationPresetsProvider implements LocationPresetsProvider {
|
||||
|
||||
private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/MEGA");
|
||||
|
||||
@CheckAvailability
|
||||
public static boolean isPresent() {
|
||||
return Files.isDirectory(LOCATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<LocationPreset> getLocations() {
|
||||
return Stream.of(new LocationPreset("MEGA", LOCATION));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.cryptomator.common.locationpresets;
|
||||
|
||||
import org.cryptomator.integrations.common.CheckAvailability;
|
||||
import org.cryptomator.integrations.common.OperatingSystem;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC;
|
||||
|
||||
@OperatingSystem(MAC)
|
||||
public final class OneDriveMacLocationPresetsProvider implements LocationPresetsProvider {
|
||||
|
||||
|
||||
private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/OneDrive");
|
||||
|
||||
@CheckAvailability
|
||||
public static boolean isPresent() {
|
||||
return Files.isDirectory(LOCATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<LocationPreset> getLocations() {
|
||||
return Stream.of(new LocationPreset("OneDrive", LOCATION));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package org.cryptomator.common.locationpresets;
|
||||
|
||||
import org.cryptomator.integrations.common.OperatingSystem;
|
||||
import org.jetbrains.annotations.Blocking;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS;
|
||||
|
||||
@OperatingSystem(WINDOWS)
|
||||
public final class OneDriveWindowsLocationPresetsProvider implements LocationPresetsProvider {
|
||||
|
||||
private static final String REGSTR_TOKEN = "REG_SZ";
|
||||
private static final String REG_ONEDRIVE_ACCOUNTS = "HKEY_CURRENT_USER\\Software\\Microsoft\\OneDrive\\Accounts\\";
|
||||
|
||||
@Override
|
||||
public Stream<LocationPreset> getLocations() {
|
||||
try {
|
||||
|
||||
var accounts = queryRegistry(REG_ONEDRIVE_ACCOUNTS, List.of(), l -> l.startsWith(REG_ONEDRIVE_ACCOUNTS)).toList();
|
||||
var cloudLocations = new ArrayList<LocationPreset>();
|
||||
for (var account : accounts) {
|
||||
var path = queryRegistry(REG_ONEDRIVE_ACCOUNTS + account, List.of("/v", "UserFolder"), l -> l.contains("UserFolder")).map(result -> result.substring(result.indexOf(REGSTR_TOKEN) + REGSTR_TOKEN.length()).trim()) //
|
||||
.map(Path::of) //
|
||||
.findFirst().orElseThrow();
|
||||
var name = "OneDrive"; //we assume personal oneDrive account by default
|
||||
if (!account.equals("Personal")) {
|
||||
name = queryRegistry(REG_ONEDRIVE_ACCOUNTS + account, List.of("/v", "DisplayName"), l -> l.contains("DisplayName")).map(result -> result.substring(result.indexOf(REGSTR_TOKEN) + REGSTR_TOKEN.length()).trim()) //
|
||||
.map("OneDrive - "::concat).findFirst().orElseThrow();
|
||||
}
|
||||
cloudLocations.add(new LocationPreset(name, path));
|
||||
}
|
||||
return cloudLocations.stream();
|
||||
} catch (RuntimeException e) {
|
||||
return Stream.of();
|
||||
}
|
||||
}
|
||||
|
||||
private Stream<String> queryRegistry(String keyname, List<String> moreArgs, Predicate<String> outputFilter) {
|
||||
var args = new ArrayList<String>();
|
||||
args.add("reg");
|
||||
args.add("query");
|
||||
args.add(keyname);
|
||||
args.addAll(moreArgs);
|
||||
try {
|
||||
ProcessBuilder command = new ProcessBuilder(args);
|
||||
Process p = command.start();
|
||||
waitForSuccess(p, 3, "`reg query`");
|
||||
return p.inputReader(StandardCharsets.UTF_8).lines().filter(outputFilter);
|
||||
} catch (TimeoutException | IOException | CommandFailedException e) {
|
||||
throw new RuntimeException("FAIL");
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new RuntimeException("FAIL");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Waits {@code timeoutSeconds} seconds for {@code process} to finish with exit code {@code 0}.
|
||||
*
|
||||
* @param process The process to wait for
|
||||
* @param timeoutSeconds How long to wait (in seconds)
|
||||
* @param cmdDescription A short description of the process used to generate log and exception messages
|
||||
* @throws TimeoutException Thrown when the process doesn't finish in time
|
||||
* @throws InterruptedException Thrown when the thread is interrupted while waiting for the process to finish
|
||||
* @throws CommandFailedException Thrown when the process exit code is non-zero
|
||||
*/
|
||||
@Blocking
|
||||
static void waitForSuccess(Process process, int timeoutSeconds, String cmdDescription) throws TimeoutException, InterruptedException, CommandFailedException {
|
||||
boolean exited = process.waitFor(timeoutSeconds, TimeUnit.SECONDS);
|
||||
if (!exited) {
|
||||
throw new TimeoutException(cmdDescription + " timed out after " + timeoutSeconds + "s");
|
||||
}
|
||||
if (process.exitValue() != 0) {
|
||||
@SuppressWarnings("resource") var stdout = process.inputReader(StandardCharsets.UTF_8).lines().collect(Collectors.joining("\n"));
|
||||
@SuppressWarnings("resource") var stderr = process.errorReader(StandardCharsets.UTF_8).lines().collect(Collectors.joining("\n"));
|
||||
throw new CommandFailedException(cmdDescription, process.exitValue(), stdout, stderr);
|
||||
}
|
||||
}
|
||||
|
||||
static class CommandFailedException extends Exception {
|
||||
|
||||
int exitCode;
|
||||
String stdout;
|
||||
String stderr;
|
||||
|
||||
private CommandFailedException(String cmdDescription, int exitCode, String stdout, String stderr) {
|
||||
super(cmdDescription + " returned with non-zero exit code " + exitCode);
|
||||
this.exitCode = exitCode;
|
||||
this.stdout = stdout;
|
||||
this.stderr = stderr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.cryptomator.common.locationpresets;
|
||||
|
||||
import org.cryptomator.integrations.common.CheckAvailability;
|
||||
import org.cryptomator.integrations.common.OperatingSystem;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC;
|
||||
import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS;
|
||||
|
||||
@OperatingSystem(WINDOWS)
|
||||
@OperatingSystem(MAC)
|
||||
public final class PCloudLocationPresetsProvider implements LocationPresetsProvider {
|
||||
|
||||
|
||||
private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/pCloudDrive");
|
||||
|
||||
@CheckAvailability
|
||||
public static boolean isPresent() {
|
||||
return Files.isDirectory(LOCATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<LocationPreset> getLocations() {
|
||||
return Stream.of(new LocationPreset("pCloud", LOCATION));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user