From e78155396d4f1aade4847e407e6de2f1215912f8 Mon Sep 17 00:00:00 2001 From: Martin Beyer Date: Wed, 8 Jul 2020 14:01:10 +0200 Subject: [PATCH 1/7] Implementing #1251 --- .../org/cryptomator/common/Environment.java | 3 ++ .../ui/preferences/AutoStartModule.java | 5 +-- .../ui/preferences/AutoStartStrategy.java | 7 ++++ .../ui/preferences/AutoStartWinStrategy.java | 33 ++++++++++++++++++- 4 files changed, 45 insertions(+), 3 deletions(-) diff --git a/main/commons/src/main/java/org/cryptomator/common/Environment.java b/main/commons/src/main/java/org/cryptomator/common/Environment.java index 890840b7d..54e29df8b 100644 --- a/main/commons/src/main/java/org/cryptomator/common/Environment.java +++ b/main/commons/src/main/java/org/cryptomator/common/Environment.java @@ -40,6 +40,7 @@ public class Environment { LOG.debug("cryptomator.mountPointsDir: {}", System.getProperty("cryptomator.mountPointsDir")); LOG.debug("cryptomator.minPwLength: {}", System.getProperty("cryptomator.minPwLength")); LOG.debug("cryptomator.buildNumber: {}", System.getProperty("cryptomator.buildNumber")); + LOG.debug("cryptomator.binaryPath: {}", System.getProperty("cryptomator.binaryPath")); } public boolean useCustomLogbackConfig() { @@ -74,6 +75,8 @@ public class Environment { return getInt("cryptomator.minPwLength", DEFAULT_MIN_PW_LENGTH); } + public Optional getBinaryPath() { return getPath("cryptomator.binaryPath"); } + private int getInt(String propertyName, int defaultValue) { String value = System.getProperty(propertyName); try { diff --git a/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartModule.java b/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartModule.java index d006d8681..1909b6af2 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartModule.java @@ -3,6 +3,7 @@ package org.cryptomator.ui.preferences; import dagger.Module; import dagger.Provides; import org.apache.commons.lang3.SystemUtils; +import org.cryptomator.common.Environment; import org.cryptomator.jni.MacFunctions; import java.util.Optional; @@ -12,12 +13,12 @@ abstract class AutoStartModule { @Provides @PreferencesScoped - public static Optional provideAutoStartStrategy(Optional macFunctions) { + public static Optional provideAutoStartStrategy(Optional macFunctions, Environment env) { if (SystemUtils.IS_OS_MAC_OSX && macFunctions.isPresent()) { return Optional.of(new AutoStartMacStrategy(macFunctions.get())); } else if (SystemUtils.IS_OS_WINDOWS) { Optional exeName = ProcessHandle.current().info().command(); - return exeName.map(AutoStartWinStrategy::new); + return exeName.map(x -> new AutoStartWinStrategy(x, env)); } else { return Optional.empty(); } diff --git a/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartStrategy.java b/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartStrategy.java index 99b21b4cd..10a431e1f 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartStrategy.java +++ b/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartStrategy.java @@ -21,4 +21,11 @@ public interface AutoStartStrategy { } } + class TogglingAutoStartWithPowershellFailedException extends TogglingAutoStartFailedException { + + public TogglingAutoStartWithPowershellFailedException(String message, Throwable cause) { + super(message, cause); + } + + } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartWinStrategy.java b/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartWinStrategy.java index f4a8b0578..3ffebfc18 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartWinStrategy.java +++ b/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartWinStrategy.java @@ -1,5 +1,6 @@ package org.cryptomator.ui.preferences; +import org.cryptomator.common.Environment; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -15,9 +16,12 @@ class AutoStartWinStrategy implements AutoStartStrategy { private static final String HKCU_AUTOSTART_KEY = "\"HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run\""; private static final String AUTOSTART_VALUE = "Cryptomator"; private final String exePath; + private static final String WINDOWS_START_MENU_FOLDER = "\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs"; + private Environment env; - public AutoStartWinStrategy(String exePath) { + public AutoStartWinStrategy(String exePath, Environment env) { this.exePath = exePath; + this.env = env; } @Override @@ -47,9 +51,11 @@ class AutoStartWinStrategy implements AutoStartStrategy { if (finishedInTime) { LOG.debug("Added {} to registry key {}.", AUTOSTART_VALUE, HKCU_AUTOSTART_KEY); } else { + addShortcutOfAppToAutostartFolder(); throw new TogglingAutoStartFailedException("Adding registry value failed."); } } catch (IOException e) { + addShortcutOfAppToAutostartFolder(); throw new TogglingAutoStartFailedException("Adding registry value failed. " + command, e); } } @@ -66,9 +72,11 @@ class AutoStartWinStrategy implements AutoStartStrategy { if (finishedInTime) { LOG.debug("Removed {} from registry key {}.", AUTOSTART_VALUE, HKCU_AUTOSTART_KEY); } else { + removeShortcutOfAppFromAutostartFolder(); throw new TogglingAutoStartFailedException("Removing registry value failed."); } } catch (IOException e) { + removeShortcutOfAppFromAutostartFolder(); throw new TogglingAutoStartFailedException("Removing registry value failed. " + command, e); } } @@ -88,4 +96,27 @@ class AutoStartWinStrategy implements AutoStartStrategy { return finishedInTime; } + private void addShortcutOfAppToAutostartFolder() throws TogglingAutoStartWithPowershellFailedException{ + String startmenueDirectory = System.getProperty("user.home") + WINDOWS_START_MENU_FOLDER + "\\Cryptomator.lnk"; + String cryptomator = env.getBinaryPath().get().toString(); + String createShortcutCommand = "$s=(New-Object -COM WScript.Shell).CreateShortcut('" + startmenueDirectory + "');$s.TargetPath='" + cryptomator + "';$s.Save();"; + ProcessBuilder shortcutAdd = new ProcessBuilder("cmd", "/c", "Start powershell " + createShortcutCommand); + try { + shortcutAdd.start(); + } catch (IOException e) { + throw new TogglingAutoStartWithPowershellFailedException("Adding shortcut to autostart folder failed.", e); + } + } + + private void removeShortcutOfAppFromAutostartFolder() throws TogglingAutoStartWithPowershellFailedException{ + String startmenueDirectory = System.getProperty("user.home") + WINDOWS_START_MENU_FOLDER + "\\Cryptomator.lnk"; + ProcessBuilder shortcutRemove = new ProcessBuilder("cmd", "/c del \"" + startmenueDirectory + "\""); + try { + shortcutRemove.start(); + } catch (IOException e) { + throw new TogglingAutoStartWithPowershellFailedException("Removing shortcut from autostart folder failed.", e); + } + } + + } From dcaa6e81a3868b72ea4c7a01405797aba017501f Mon Sep 17 00:00:00 2001 From: Martin Beyer Date: Wed, 8 Jul 2020 14:53:15 +0200 Subject: [PATCH 2/7] Improving Exception calling and logging --- .../ui/preferences/AutoStartWinStrategy.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartWinStrategy.java b/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartWinStrategy.java index 3ffebfc18..f863cec61 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartWinStrategy.java +++ b/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartWinStrategy.java @@ -48,9 +48,10 @@ class AutoStartWinStrategy implements AutoStartStrategy { try { Process proc = regAdd.start(); boolean finishedInTime = waitForProcess(proc, 5, TimeUnit.SECONDS); - if (finishedInTime) { + if (finishedInTime && proc.exitValue() == 0) { LOG.debug("Added {} to registry key {}.", AUTOSTART_VALUE, HKCU_AUTOSTART_KEY); } else { + LOG.debug("Registry could not be edited. Error code was {}.", proc.exitValue()); addShortcutOfAppToAutostartFolder(); throw new TogglingAutoStartFailedException("Adding registry value failed."); } @@ -69,9 +70,10 @@ class AutoStartWinStrategy implements AutoStartStrategy { try { Process proc = regRemove.start(); boolean finishedInTime = waitForProcess(proc, 5, TimeUnit.SECONDS); - if (finishedInTime) { + if (finishedInTime && proc.exitValue() == 0) { LOG.debug("Removed {} from registry key {}.", AUTOSTART_VALUE, HKCU_AUTOSTART_KEY); } else { + LOG.debug("Registry could not be edited. Error code was {}.", proc.exitValue()); removeShortcutOfAppFromAutostartFolder(); throw new TogglingAutoStartFailedException("Removing registry value failed."); } @@ -96,7 +98,7 @@ class AutoStartWinStrategy implements AutoStartStrategy { return finishedInTime; } - private void addShortcutOfAppToAutostartFolder() throws TogglingAutoStartWithPowershellFailedException{ + private void addShortcutOfAppToAutostartFolder() throws TogglingAutoStartWithPowershellFailedException { String startmenueDirectory = System.getProperty("user.home") + WINDOWS_START_MENU_FOLDER + "\\Cryptomator.lnk"; String cryptomator = env.getBinaryPath().get().toString(); String createShortcutCommand = "$s=(New-Object -COM WScript.Shell).CreateShortcut('" + startmenueDirectory + "');$s.TargetPath='" + cryptomator + "';$s.Save();"; @@ -108,7 +110,7 @@ class AutoStartWinStrategy implements AutoStartStrategy { } } - private void removeShortcutOfAppFromAutostartFolder() throws TogglingAutoStartWithPowershellFailedException{ + private void removeShortcutOfAppFromAutostartFolder() throws TogglingAutoStartWithPowershellFailedException { String startmenueDirectory = System.getProperty("user.home") + WINDOWS_START_MENU_FOLDER + "\\Cryptomator.lnk"; ProcessBuilder shortcutRemove = new ProcessBuilder("cmd", "/c del \"" + startmenueDirectory + "\""); try { From 526c8328f6192ee41d1540a36033daa1f15a623a Mon Sep 17 00:00:00 2001 From: Martin Beyer Date: Mon, 13 Jul 2020 15:21:24 +0200 Subject: [PATCH 3/7] Refactoring - Removing unnecessary environment variable --- .../java/org/cryptomator/common/Environment.java | 3 --- .../cryptomator/ui/preferences/AutoStartModule.java | 2 +- .../ui/preferences/AutoStartWinStrategy.java | 13 +++++-------- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/main/commons/src/main/java/org/cryptomator/common/Environment.java b/main/commons/src/main/java/org/cryptomator/common/Environment.java index 54e29df8b..890840b7d 100644 --- a/main/commons/src/main/java/org/cryptomator/common/Environment.java +++ b/main/commons/src/main/java/org/cryptomator/common/Environment.java @@ -40,7 +40,6 @@ public class Environment { LOG.debug("cryptomator.mountPointsDir: {}", System.getProperty("cryptomator.mountPointsDir")); LOG.debug("cryptomator.minPwLength: {}", System.getProperty("cryptomator.minPwLength")); LOG.debug("cryptomator.buildNumber: {}", System.getProperty("cryptomator.buildNumber")); - LOG.debug("cryptomator.binaryPath: {}", System.getProperty("cryptomator.binaryPath")); } public boolean useCustomLogbackConfig() { @@ -75,8 +74,6 @@ public class Environment { return getInt("cryptomator.minPwLength", DEFAULT_MIN_PW_LENGTH); } - public Optional getBinaryPath() { return getPath("cryptomator.binaryPath"); } - private int getInt(String propertyName, int defaultValue) { String value = System.getProperty(propertyName); try { diff --git a/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartModule.java b/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartModule.java index 1909b6af2..fa01756ab 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartModule.java @@ -18,7 +18,7 @@ abstract class AutoStartModule { return Optional.of(new AutoStartMacStrategy(macFunctions.get())); } else if (SystemUtils.IS_OS_WINDOWS) { Optional exeName = ProcessHandle.current().info().command(); - return exeName.map(x -> new AutoStartWinStrategy(x, env)); + return exeName.map(AutoStartWinStrategy::new); } else { return Optional.empty(); } diff --git a/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartWinStrategy.java b/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartWinStrategy.java index f863cec61..02e4faa26 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartWinStrategy.java +++ b/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartWinStrategy.java @@ -17,11 +17,9 @@ class AutoStartWinStrategy implements AutoStartStrategy { private static final String AUTOSTART_VALUE = "Cryptomator"; private final String exePath; private static final String WINDOWS_START_MENU_FOLDER = "\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs"; - private Environment env; - public AutoStartWinStrategy(String exePath, Environment env) { + public AutoStartWinStrategy(String exePath) { this.exePath = exePath; - this.env = env; } @Override @@ -99,9 +97,8 @@ class AutoStartWinStrategy implements AutoStartStrategy { } private void addShortcutOfAppToAutostartFolder() throws TogglingAutoStartWithPowershellFailedException { - String startmenueDirectory = System.getProperty("user.home") + WINDOWS_START_MENU_FOLDER + "\\Cryptomator.lnk"; - String cryptomator = env.getBinaryPath().get().toString(); - String createShortcutCommand = "$s=(New-Object -COM WScript.Shell).CreateShortcut('" + startmenueDirectory + "');$s.TargetPath='" + cryptomator + "';$s.Save();"; + String startMenuDirectory = System.getProperty("user.home") + WINDOWS_START_MENU_FOLDER + "\\Cryptomator.lnk"; + String createShortcutCommand = "$s=(New-Object -COM WScript.Shell).CreateShortcut('" + startMenuDirectory + "');$s.TargetPath='" + exePath + "';$s.Save();"; ProcessBuilder shortcutAdd = new ProcessBuilder("cmd", "/c", "Start powershell " + createShortcutCommand); try { shortcutAdd.start(); @@ -111,8 +108,8 @@ class AutoStartWinStrategy implements AutoStartStrategy { } private void removeShortcutOfAppFromAutostartFolder() throws TogglingAutoStartWithPowershellFailedException { - String startmenueDirectory = System.getProperty("user.home") + WINDOWS_START_MENU_FOLDER + "\\Cryptomator.lnk"; - ProcessBuilder shortcutRemove = new ProcessBuilder("cmd", "/c del \"" + startmenueDirectory + "\""); + String startMenuDirectory = System.getProperty("user.home") + WINDOWS_START_MENU_FOLDER + "\\Cryptomator.lnk"; + ProcessBuilder shortcutRemove = new ProcessBuilder("cmd", "/c del \"" + startMenuDirectory + "\""); try { shortcutRemove.start(); } catch (IOException e) { From 0f56d424da63aee65ed9349ea2cd86739601f9ff Mon Sep 17 00:00:00 2001 From: infeo Date: Thu, 20 Aug 2020 12:18:57 +0200 Subject: [PATCH 4/7] Refactored AutoStartStrategy: * prevented on best effort basis inconsistent states * extracted the registry setting as an own strategy (by methods) * refactored the overriden methods to call the strategies (registry or folder) depending on the different variables * removed Powershell specific ToggleException * added documentation --- .../ui/preferences/AutoStartStrategy.java | 7 - .../ui/preferences/AutoStartWinStrategy.java | 181 +++++++++++++----- 2 files changed, 132 insertions(+), 56 deletions(-) diff --git a/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartStrategy.java b/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartStrategy.java index 10a431e1f..99b21b4cd 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartStrategy.java +++ b/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartStrategy.java @@ -21,11 +21,4 @@ public interface AutoStartStrategy { } } - class TogglingAutoStartWithPowershellFailedException extends TogglingAutoStartFailedException { - - public TogglingAutoStartWithPowershellFailedException(String message, Throwable cause) { - super(message, cause); - } - - } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartWinStrategy.java b/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartWinStrategy.java index 02e4faa26..0d2f20536 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartWinStrategy.java +++ b/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartWinStrategy.java @@ -1,87 +1,185 @@ package org.cryptomator.ui.preferences; -import org.cryptomator.common.Environment; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; +/** + * OS specific class to check, en- and disable the auto start on Windows. + *

+ * Two strategies are implemented for this feature, the first uses the registry and the second one the autostart folder. + *

+ * To check if it is enabled at all, both locations are checked. + * To enable it, first the registry is tried and only on failure the autostart folder is used. + * To disable it, first it is determined, which strategies must be used and in the second step those are executed. + * + * @apiNote This class is not thread safe, hence it should be avoided to be used simultaniously by the same threads. + */ class AutoStartWinStrategy implements AutoStartStrategy { private static final Logger LOG = LoggerFactory.getLogger(AutoStartWinStrategy.class); private static final String HKCU_AUTOSTART_KEY = "\"HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run\""; private static final String AUTOSTART_VALUE = "Cryptomator"; + private static final String WINDOWS_START_MENU_ENTRY = "\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Cryptomator.lnk"; + private final String exePath; - private static final String WINDOWS_START_MENU_FOLDER = "\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs"; + + private boolean activatedOverFolder; + private boolean activatedOverRegistry; public AutoStartWinStrategy(String exePath) { this.exePath = exePath; + this.activatedOverFolder = false; + this.activatedOverRegistry = false; } @Override public CompletionStage isAutoStartEnabled() { + return isAutoStartEnabledOverRegistry().thenCombine(isAutoStartEnabledOverFolder(), (bReg, bFolder) -> bReg || bFolder); + } + + private CompletableFuture isAutoStartEnabledOverFolder() { + Path autoStartEntry = Path.of(System.getProperty("user.home") + WINDOWS_START_MENU_ENTRY); + this.activatedOverFolder = Files.exists(autoStartEntry); + return CompletableFuture.completedFuture(activatedOverFolder); + } + + private CompletableFuture isAutoStartEnabledOverRegistry() { ProcessBuilder regQuery = new ProcessBuilder("reg", "query", HKCU_AUTOSTART_KEY, // "/v", AUTOSTART_VALUE); try { Process proc = regQuery.start(); - return proc.onExit().thenApply(p -> p.exitValue() == 0); + return proc.onExit().thenApply(p -> { + this.activatedOverRegistry = p.exitValue() == 0; + return activatedOverRegistry; + }); } catch (IOException e) { - LOG.warn("Failed to query {} from registry key {}", AUTOSTART_VALUE, HKCU_AUTOSTART_KEY); + LOG.debug("Failed to query {} from registry key {}", AUTOSTART_VALUE, HKCU_AUTOSTART_KEY); return CompletableFuture.completedFuture(false); } } @Override public void enableAutoStart() throws TogglingAutoStartFailedException { + try { + enableAutoStartOverRegistry().thenAccept((Void v) -> this.activatedOverRegistry = true).exceptionallyCompose(e -> { + LOG.debug("Falling back to using autostart folder."); + return this.enableAutoStartOverFolder(); + }).thenAccept((Void v) -> this.activatedOverFolder = true).get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new TogglingAutoStartFailedException("Execution of enabling auto start setting was interrupted."); + } catch (ExecutionException e) { + throw new TogglingAutoStartFailedException("Enabling auto start failed both over registry and auto start folder."); + } + } + + private CompletableFuture enableAutoStartOverRegistry() { ProcessBuilder regAdd = new ProcessBuilder("reg", "add", HKCU_AUTOSTART_KEY, // "/v", AUTOSTART_VALUE, // "/t", "REG_SZ", // "/d", "\"" + exePath + "\"", // "/f"); - String command = regAdd.command().stream().collect(Collectors.joining(" ")); try { Process proc = regAdd.start(); - boolean finishedInTime = waitForProcess(proc, 5, TimeUnit.SECONDS); + boolean finishedInTime = waitForProcessOrCancel(proc, 5, TimeUnit.SECONDS); if (finishedInTime && proc.exitValue() == 0) { LOG.debug("Added {} to registry key {}.", AUTOSTART_VALUE, HKCU_AUTOSTART_KEY); + return CompletableFuture.completedFuture(null); } else { - LOG.debug("Registry could not be edited. Error code was {}.", proc.exitValue()); - addShortcutOfAppToAutostartFolder(); - throw new TogglingAutoStartFailedException("Adding registry value failed."); + throw new IOException("Process existed with error code " + proc.exitValue()); } } catch (IOException e) { - addShortcutOfAppToAutostartFolder(); - throw new TogglingAutoStartFailedException("Adding registry value failed. " + command, e); + LOG.debug("Registry could not be edited to set auto start.", e); + return CompletableFuture.failedFuture(new SystemCommandException("Adding registry value failed.")); } } + private CompletableFuture enableAutoStartOverFolder() { + String autoStartFolderEntry = System.getProperty("user.home") + WINDOWS_START_MENU_ENTRY; + String createShortcutCommand = "$s=(New-Object -COM WScript.Shell).CreateShortcut('" + autoStartFolderEntry + "');$s.TargetPath='" + exePath + "';$s.Save();"; + ProcessBuilder shortcutAdd = new ProcessBuilder("cmd", "/c", "Start powershell " + createShortcutCommand); + try { + Process proc = shortcutAdd.start(); + boolean finishedInTime = waitForProcessOrCancel(proc, 5, TimeUnit.SECONDS); + if (finishedInTime && proc.exitValue() == 0) { + LOG.debug("Created file {} for auto start.", autoStartFolderEntry); + return CompletableFuture.completedFuture(null); + } else { + throw new IOException("Process existed with error code " + proc.exitValue()); + } + } catch (IOException e) { + LOG.debug("Adding entry to auto start folder failed.", e); + return CompletableFuture.failedFuture(new SystemCommandException("Adding entry to auto start folder failed.")); + } + } + + @Override public void disableAutoStart() throws TogglingAutoStartFailedException { - ProcessBuilder regRemove = new ProcessBuilder("reg", "delete", HKCU_AUTOSTART_KEY, // - "/v", AUTOSTART_VALUE, // - "/f"); - String command = regRemove.command().stream().collect(Collectors.joining(" ")); - try { - Process proc = regRemove.start(); - boolean finishedInTime = waitForProcess(proc, 5, TimeUnit.SECONDS); - if (finishedInTime && proc.exitValue() == 0) { - LOG.debug("Removed {} from registry key {}.", AUTOSTART_VALUE, HKCU_AUTOSTART_KEY); - } else { - LOG.debug("Registry could not be edited. Error code was {}.", proc.exitValue()); - removeShortcutOfAppFromAutostartFolder(); - throw new TogglingAutoStartFailedException("Removing registry value failed."); - } - } catch (IOException e) { - removeShortcutOfAppFromAutostartFolder(); - throw new TogglingAutoStartFailedException("Removing registry value failed. " + command, e); + if (activatedOverRegistry) { + disableAutoStartOverRegistry().whenComplete((voit, ex) -> { + if (ex == null) { + this.activatedOverRegistry = false; + } + }); + } + + if (activatedOverFolder) { + disableAutoStartOverFolder().whenComplete((voit, ex) -> { + if (ex == null) { + this.activatedOverFolder = false; + } + }); + } + + if (activatedOverRegistry || activatedOverFolder) { + throw new TogglingAutoStartFailedException("Disabling auto start failed both over registry and auto start folder."); } } - private static boolean waitForProcess(Process proc, int timeout, TimeUnit timeUnit) { + public CompletableFuture disableAutoStartOverRegistry() { + ProcessBuilder regRemove = new ProcessBuilder("reg", "delete", HKCU_AUTOSTART_KEY, // + "/v", AUTOSTART_VALUE, // + "/f"); + try { + Process proc = regRemove.start(); + boolean finishedInTime = waitForProcessOrCancel(proc, 5, TimeUnit.SECONDS); + if (finishedInTime && proc.exitValue() == 0) { + LOG.debug("Removed {} from registry key {}.", AUTOSTART_VALUE, HKCU_AUTOSTART_KEY); + return CompletableFuture.completedFuture(null); + } else { + throw new IOException("Process existed with error code " + proc.exitValue()); + } + } catch (IOException e) { + LOG.debug("Registry could not be edited to remove auto start.", e); + return CompletableFuture.failedFuture(new SystemCommandException("Removing registry value failed.")); + } + } + + private CompletableFuture disableAutoStartOverFolder() { + try { + Files.delete(Path.of(WINDOWS_START_MENU_ENTRY)); + LOG.debug("Successfully deleted {}.", WINDOWS_START_MENU_ENTRY); + return CompletableFuture.completedFuture(null); + } catch (NoSuchFileException e) { + //that is also okay + return CompletableFuture.completedFuture(null); + } catch (IOException e) { + LOG.debug("Failed to delete entry from auto start folder.", e); + return CompletableFuture.failedFuture(e); + } + } + + private static boolean waitForProcessOrCancel(Process proc, int timeout, TimeUnit timeUnit) { boolean finishedInTime = false; try { finishedInTime = proc.waitFor(timeout, timeUnit); @@ -96,26 +194,11 @@ class AutoStartWinStrategy implements AutoStartStrategy { return finishedInTime; } - private void addShortcutOfAppToAutostartFolder() throws TogglingAutoStartWithPowershellFailedException { - String startMenuDirectory = System.getProperty("user.home") + WINDOWS_START_MENU_FOLDER + "\\Cryptomator.lnk"; - String createShortcutCommand = "$s=(New-Object -COM WScript.Shell).CreateShortcut('" + startMenuDirectory + "');$s.TargetPath='" + exePath + "';$s.Save();"; - ProcessBuilder shortcutAdd = new ProcessBuilder("cmd", "/c", "Start powershell " + createShortcutCommand); - try { - shortcutAdd.start(); - } catch (IOException e) { - throw new TogglingAutoStartWithPowershellFailedException("Adding shortcut to autostart folder failed.", e); + public class SystemCommandException extends RuntimeException { + + public SystemCommandException(String msg) { + super(msg); } } - private void removeShortcutOfAppFromAutostartFolder() throws TogglingAutoStartWithPowershellFailedException { - String startMenuDirectory = System.getProperty("user.home") + WINDOWS_START_MENU_FOLDER + "\\Cryptomator.lnk"; - ProcessBuilder shortcutRemove = new ProcessBuilder("cmd", "/c del \"" + startMenuDirectory + "\""); - try { - shortcutRemove.start(); - } catch (IOException e) { - throw new TogglingAutoStartWithPowershellFailedException("Removing shortcut from autostart folder failed.", e); - } - } - - } From 921b70ebaa21ffa963164a3d56062336ab18082f Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Tue, 18 Aug 2020 10:36:55 +0200 Subject: [PATCH 5/7] updated webdav dependencies (cherry picked from commit 8c4d35d3db98c190861bfa5420a4f52036d2e510) --- main/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/pom.xml b/main/pom.xml index 5f0d7e5ea..9a55f782a 100644 --- a/main/pom.xml +++ b/main/pom.xml @@ -28,7 +28,7 @@ 2.2.2 1.2.3 1.1.15 - 1.0.11 + 1.0.12 14 From 1d38ee2fcb620abf8b8d3f60133d740d97c4faab Mon Sep 17 00:00:00 2001 From: infeo Date: Thu, 20 Aug 2020 13:49:31 +0200 Subject: [PATCH 6/7] Further improvements: * corrected bad english * improved documentation * restricted visibility of specific exception --- .../ui/preferences/AutoStartWinStrategy.java | 67 ++++++++++--------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartWinStrategy.java b/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartWinStrategy.java index 0d2f20536..3f51af849 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartWinStrategy.java +++ b/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartWinStrategy.java @@ -17,11 +17,14 @@ import java.util.concurrent.TimeUnit; *

* Two strategies are implemented for this feature, the first uses the registry and the second one the autostart folder. *

- * To check if it is enabled at all, both locations are checked. - * To enable it, first the registry is tried and only on failure the autostart folder is used. - * To disable it, first it is determined, which strategies must be used and in the second step those are executed. + * The registry strategy checks/add/removes at the registry key {@value HKCU_AUTOSTART_KEY} an entry for Cryptomator. + * The folder strategy checks/add/removes at the location {@value WINDOWS_START_MENU_ENTRY}. + *

+ * To check if the feature is active, both strategies are applied. + * To enable the feature, first the registry is tried and only on failure the autostart folder is used. + * To disable it, first it is determined by an internal state, which strategies must be used and in the second step those are executed. * - * @apiNote This class is not thread safe, hence it should be avoided to be used simultaniously by the same threads. + * @apiNote This class is not thread safe, hence it should be avoided to call its methods simultaniously by different threads. */ class AutoStartWinStrategy implements AutoStartStrategy { @@ -32,34 +35,34 @@ class AutoStartWinStrategy implements AutoStartStrategy { private final String exePath; - private boolean activatedOverFolder; - private boolean activatedOverRegistry; + private boolean activatedUsingFolder; + private boolean activatedUsingRegistry; public AutoStartWinStrategy(String exePath) { this.exePath = exePath; - this.activatedOverFolder = false; - this.activatedOverRegistry = false; + this.activatedUsingFolder = false; + this.activatedUsingRegistry = false; } @Override public CompletionStage isAutoStartEnabled() { - return isAutoStartEnabledOverRegistry().thenCombine(isAutoStartEnabledOverFolder(), (bReg, bFolder) -> bReg || bFolder); + return isAutoStartEnabledUsingRegistry().thenCombine(isAutoStartEnabledUsingFolder(), (bReg, bFolder) -> bReg || bFolder); } - private CompletableFuture isAutoStartEnabledOverFolder() { + private CompletableFuture isAutoStartEnabledUsingFolder() { Path autoStartEntry = Path.of(System.getProperty("user.home") + WINDOWS_START_MENU_ENTRY); - this.activatedOverFolder = Files.exists(autoStartEntry); - return CompletableFuture.completedFuture(activatedOverFolder); + this.activatedUsingFolder = Files.exists(autoStartEntry); + return CompletableFuture.completedFuture(activatedUsingFolder); } - private CompletableFuture isAutoStartEnabledOverRegistry() { + private CompletableFuture isAutoStartEnabledUsingRegistry() { ProcessBuilder regQuery = new ProcessBuilder("reg", "query", HKCU_AUTOSTART_KEY, // "/v", AUTOSTART_VALUE); try { Process proc = regQuery.start(); return proc.onExit().thenApply(p -> { - this.activatedOverRegistry = p.exitValue() == 0; - return activatedOverRegistry; + this.activatedUsingRegistry = p.exitValue() == 0; + return activatedUsingRegistry; }); } catch (IOException e) { LOG.debug("Failed to query {} from registry key {}", AUTOSTART_VALUE, HKCU_AUTOSTART_KEY); @@ -70,19 +73,19 @@ class AutoStartWinStrategy implements AutoStartStrategy { @Override public void enableAutoStart() throws TogglingAutoStartFailedException { try { - enableAutoStartOverRegistry().thenAccept((Void v) -> this.activatedOverRegistry = true).exceptionallyCompose(e -> { + enableAutoStartUsingRegistry().thenAccept((Void v) -> this.activatedUsingRegistry = true).exceptionallyCompose(e -> { LOG.debug("Falling back to using autostart folder."); - return this.enableAutoStartOverFolder(); - }).thenAccept((Void v) -> this.activatedOverFolder = true).get(); + return this.enableAutoStartUsingFolder(); + }).thenAccept((Void v) -> this.activatedUsingFolder = true).get(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new TogglingAutoStartFailedException("Execution of enabling auto start setting was interrupted."); } catch (ExecutionException e) { - throw new TogglingAutoStartFailedException("Enabling auto start failed both over registry and auto start folder."); + throw new TogglingAutoStartFailedException("Enabling auto start failed both using registry and auto start folder."); } } - private CompletableFuture enableAutoStartOverRegistry() { + private CompletableFuture enableAutoStartUsingRegistry() { ProcessBuilder regAdd = new ProcessBuilder("reg", "add", HKCU_AUTOSTART_KEY, // "/v", AUTOSTART_VALUE, // "/t", "REG_SZ", // @@ -103,7 +106,7 @@ class AutoStartWinStrategy implements AutoStartStrategy { } } - private CompletableFuture enableAutoStartOverFolder() { + private CompletableFuture enableAutoStartUsingFolder() { String autoStartFolderEntry = System.getProperty("user.home") + WINDOWS_START_MENU_ENTRY; String createShortcutCommand = "$s=(New-Object -COM WScript.Shell).CreateShortcut('" + autoStartFolderEntry + "');$s.TargetPath='" + exePath + "';$s.Save();"; ProcessBuilder shortcutAdd = new ProcessBuilder("cmd", "/c", "Start powershell " + createShortcutCommand); @@ -125,28 +128,28 @@ class AutoStartWinStrategy implements AutoStartStrategy { @Override public void disableAutoStart() throws TogglingAutoStartFailedException { - if (activatedOverRegistry) { - disableAutoStartOverRegistry().whenComplete((voit, ex) -> { + if (activatedUsingRegistry) { + disableAutoStartUsingRegistry().whenComplete((voit, ex) -> { if (ex == null) { - this.activatedOverRegistry = false; + this.activatedUsingRegistry = false; } }); } - if (activatedOverFolder) { - disableAutoStartOverFolder().whenComplete((voit, ex) -> { + if (activatedUsingFolder) { + disableAutoStartUsingFolder().whenComplete((voit, ex) -> { if (ex == null) { - this.activatedOverFolder = false; + this.activatedUsingFolder = false; } }); } - if (activatedOverRegistry || activatedOverFolder) { - throw new TogglingAutoStartFailedException("Disabling auto start failed both over registry and auto start folder."); + if (activatedUsingRegistry || activatedUsingFolder) { + throw new TogglingAutoStartFailedException("Disabling auto start failed both using registry and auto start folder."); } } - public CompletableFuture disableAutoStartOverRegistry() { + public CompletableFuture disableAutoStartUsingRegistry() { ProcessBuilder regRemove = new ProcessBuilder("reg", "delete", HKCU_AUTOSTART_KEY, // "/v", AUTOSTART_VALUE, // "/f"); @@ -165,7 +168,7 @@ class AutoStartWinStrategy implements AutoStartStrategy { } } - private CompletableFuture disableAutoStartOverFolder() { + private CompletableFuture disableAutoStartUsingFolder() { try { Files.delete(Path.of(WINDOWS_START_MENU_ENTRY)); LOG.debug("Successfully deleted {}.", WINDOWS_START_MENU_ENTRY); @@ -194,7 +197,7 @@ class AutoStartWinStrategy implements AutoStartStrategy { return finishedInTime; } - public class SystemCommandException extends RuntimeException { + private class SystemCommandException extends RuntimeException { public SystemCommandException(String msg) { super(msg); From a2a990bd0c86fd3531addd09e6a8e15bc2cdee6e Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Wed, 26 Aug 2020 10:25:12 +0200 Subject: [PATCH 7/7] Correct spelling --- .../cryptomator/ui/preferences/AutoStartWinStrategy.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartWinStrategy.java b/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartWinStrategy.java index 3f51af849..438c468bd 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartWinStrategy.java +++ b/main/ui/src/main/java/org/cryptomator/ui/preferences/AutoStartWinStrategy.java @@ -98,7 +98,7 @@ class AutoStartWinStrategy implements AutoStartStrategy { LOG.debug("Added {} to registry key {}.", AUTOSTART_VALUE, HKCU_AUTOSTART_KEY); return CompletableFuture.completedFuture(null); } else { - throw new IOException("Process existed with error code " + proc.exitValue()); + throw new IOException("Process exited with error code " + proc.exitValue()); } } catch (IOException e) { LOG.debug("Registry could not be edited to set auto start.", e); @@ -117,7 +117,7 @@ class AutoStartWinStrategy implements AutoStartStrategy { LOG.debug("Created file {} for auto start.", autoStartFolderEntry); return CompletableFuture.completedFuture(null); } else { - throw new IOException("Process existed with error code " + proc.exitValue()); + throw new IOException("Process exited with error code " + proc.exitValue()); } } catch (IOException e) { LOG.debug("Adding entry to auto start folder failed.", e); @@ -145,7 +145,7 @@ class AutoStartWinStrategy implements AutoStartStrategy { } if (activatedUsingRegistry || activatedUsingFolder) { - throw new TogglingAutoStartFailedException("Disabling auto start failed both using registry and auto start folder."); + throw new TogglingAutoStartFailedException("Disabling auto start failed using registry and/or auto start folder."); } } @@ -160,7 +160,7 @@ class AutoStartWinStrategy implements AutoStartStrategy { LOG.debug("Removed {} from registry key {}.", AUTOSTART_VALUE, HKCU_AUTOSTART_KEY); return CompletableFuture.completedFuture(null); } else { - throw new IOException("Process existed with error code " + proc.exitValue()); + throw new IOException("Process exited with error code " + proc.exitValue()); } } catch (IOException e) { LOG.debug("Registry could not be edited to remove auto start.", e);