Ver código fonte

Create SPI for cloud location presets

Armin Schrenk 2 anos atrás
pai
commit
5b6d09308b

+ 16 - 0
src/main/java/module-info.java

@@ -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;
 }

+ 27 - 0
src/main/java/org/cryptomator/common/locationpresets/DropboxMacLocationPresetsProvider.java

@@ -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));
+	}
+}

+ 27 - 0
src/main/java/org/cryptomator/common/locationpresets/DropboxWindowsLocationPresetsProvider.java

@@ -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));
+	}
+}

+ 27 - 0
src/main/java/org/cryptomator/common/locationpresets/GoogleDriveMacLocationPresetsProvider.java

@@ -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));
+	}
+}

+ 27 - 0
src/main/java/org/cryptomator/common/locationpresets/GoogleDriveWindowsLocationPresetsProvider.java

@@ -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));
+	}
+}

+ 26 - 0
src/main/java/org/cryptomator/common/locationpresets/ICloudMacLocationPresetsProvider.java

@@ -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));
+	}
+}

+ 26 - 0
src/main/java/org/cryptomator/common/locationpresets/ICloudWindowsLocationPresetsProvider.java

@@ -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));
+	}
+}

+ 9 - 0
src/main/java/org/cryptomator/common/locationpresets/LocationPreset.java

@@ -0,0 +1,9 @@
+package org.cryptomator.common.locationpresets;
+
+import java.nio.file.Path;
+
+public record LocationPreset(String name, Path path) {
+
+
+
+}

+ 20 - 0
src/main/java/org/cryptomator/common/locationpresets/LocationPresetsProvider.java

@@ -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);
+		}
+	}
+
+}

+ 28 - 0
src/main/java/org/cryptomator/common/locationpresets/MegaLocationPresetsProvider.java

@@ -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));
+	}
+}

+ 27 - 0
src/main/java/org/cryptomator/common/locationpresets/OneDriveMacLocationPresetsProvider.java

@@ -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));
+	}
+}

+ 107 - 0
src/main/java/org/cryptomator/common/locationpresets/OneDriveWindowsLocationPresetsProvider.java

@@ -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;
+		}
+
+	}
+
+
+}

+ 29 - 0
src/main/java/org/cryptomator/common/locationpresets/PCloudLocationPresetsProvider.java

@@ -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));
+	}
+}