From 05c7028e1e00fda0da9470fb211ab10af6755b33 Mon Sep 17 00:00:00 2001 From: crschnick Date: Sat, 25 May 2024 19:30:21 +0000 Subject: [PATCH] Rework registry handling --- .../io/xpipe/app/core/check/AppAvCheck.java | 6 +- .../xpipe/app/prefs/ExternalEditorType.java | 4 +- .../xpipe/app/terminal/TabbyTerminalType.java | 4 +- .../xpipe/app/terminal/WezTerminalType.java | 2 +- .../io/xpipe/app/util/WindowsRegistry.java | 265 +++++++++++++----- 5 files changed, 196 insertions(+), 85 deletions(-) diff --git a/app/src/main/java/io/xpipe/app/core/check/AppAvCheck.java b/app/src/main/java/io/xpipe/app/core/check/AppAvCheck.java index 8aa86af5b..4ac332233 100644 --- a/app/src/main/java/io/xpipe/app/core/check/AppAvCheck.java +++ b/app/src/main/java/io/xpipe/app/core/check/AppAvCheck.java @@ -81,7 +81,7 @@ public class AppAvCheck { @Override public boolean isActive() { - return WindowsRegistry.exists( + return WindowsRegistry.local().valueExists( WindowsRegistry.HKEY_LOCAL_MACHINE, "SOFTWARE\\Bitdefender", "InstallDir"); } }, @@ -93,7 +93,7 @@ public class AppAvCheck { @Override public boolean isActive() { - return WindowsRegistry.exists(WindowsRegistry.HKEY_LOCAL_MACHINE, "SOFTWARE\\Malwarebytes", "id"); + return WindowsRegistry.local().valueExists(WindowsRegistry.HKEY_LOCAL_MACHINE, "SOFTWARE\\Malwarebytes", "id"); } }, MCAFEE("McAfee") { @@ -104,7 +104,7 @@ public class AppAvCheck { @Override public boolean isActive() { - return WindowsRegistry.exists(WindowsRegistry.HKEY_LOCAL_MACHINE, "SOFTWARE\\McAfee", "mi"); + return WindowsRegistry.local().valueExists(WindowsRegistry.HKEY_LOCAL_MACHINE, "SOFTWARE\\McAfee", "mi"); } }; diff --git a/app/src/main/java/io/xpipe/app/prefs/ExternalEditorType.java b/app/src/main/java/io/xpipe/app/prefs/ExternalEditorType.java index b8065e70b..67adca97c 100644 --- a/app/src/main/java/io/xpipe/app/prefs/ExternalEditorType.java +++ b/app/src/main/java/io/xpipe/app/prefs/ExternalEditorType.java @@ -64,11 +64,11 @@ public interface ExternalEditorType extends PrefsChoiceValue { @Override protected Optional determineInstallation() { - var found = WindowsRegistry.readString(WindowsRegistry.HKEY_LOCAL_MACHINE, "SOFTWARE\\Notepad++", null); + var found = WindowsRegistry.local().readString(WindowsRegistry.HKEY_LOCAL_MACHINE, "SOFTWARE\\Notepad++", null); // Check 32 bit install if (found.isEmpty()) { - found = WindowsRegistry.readString( + found = WindowsRegistry.local().readString( WindowsRegistry.HKEY_LOCAL_MACHINE, "WOW6432Node\\SOFTWARE\\Notepad++", null); } return found.map(p -> p + "\\notepad++.exe").map(Path::of); diff --git a/app/src/main/java/io/xpipe/app/terminal/TabbyTerminalType.java b/app/src/main/java/io/xpipe/app/terminal/TabbyTerminalType.java index 9cb36268d..c73eb7c40 100644 --- a/app/src/main/java/io/xpipe/app/terminal/TabbyTerminalType.java +++ b/app/src/main/java/io/xpipe/app/terminal/TabbyTerminalType.java @@ -85,7 +85,7 @@ public interface TabbyTerminalType extends ExternalTerminalType { @Override protected Optional determineInstallation() { - var perUser = WindowsRegistry.readString( + var perUser = WindowsRegistry.local().readString( WindowsRegistry.HKEY_CURRENT_USER, "SOFTWARE\\71445fac-d6ef-5436-9da7-5a323762d7f5", "InstallLocation") @@ -95,7 +95,7 @@ public interface TabbyTerminalType extends ExternalTerminalType { return perUser; } - var systemWide = WindowsRegistry.readString( + var systemWide = WindowsRegistry.local().readString( WindowsRegistry.HKEY_LOCAL_MACHINE, "SOFTWARE\\71445fac-d6ef-5436-9da7-5a323762d7f5", "InstallLocation") diff --git a/app/src/main/java/io/xpipe/app/terminal/WezTerminalType.java b/app/src/main/java/io/xpipe/app/terminal/WezTerminalType.java index 55e8ef343..feb3547a3 100644 --- a/app/src/main/java/io/xpipe/app/terminal/WezTerminalType.java +++ b/app/src/main/java/io/xpipe/app/terminal/WezTerminalType.java @@ -52,7 +52,7 @@ public interface WezTerminalType extends ExternalTerminalType { @Override protected Optional determineInstallation() { Optional launcherDir; - launcherDir = WindowsRegistry.readString( + launcherDir = WindowsRegistry.local().readString( WindowsRegistry.HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{BCF6F0DA-5B9A-408D-8562-F680AE6E1EAF}_is1", "InstallLocation") diff --git a/app/src/main/java/io/xpipe/app/util/WindowsRegistry.java b/app/src/main/java/io/xpipe/app/util/WindowsRegistry.java index 40aee29a3..9dd09720c 100644 --- a/app/src/main/java/io/xpipe/app/util/WindowsRegistry.java +++ b/app/src/main/java/io/xpipe/app/util/WindowsRegistry.java @@ -6,103 +6,214 @@ import io.xpipe.core.process.ShellControl; import com.sun.jna.platform.win32.Advapi32Util; import com.sun.jna.platform.win32.WinReg; +import lombok.Value; import java.util.Optional; -public class WindowsRegistry { +public abstract class WindowsRegistry { + + @Value + public static class Key { + int hkey; + String key; + } + + public static WindowsRegistry.Local local() { + return new Local(); + } + + public static WindowsRegistry ofShell(ShellControl shellControl) { + return shellControl.isLocal() ? local() : new Remote(shellControl); + } public static final int HKEY_CURRENT_USER = 0x80000001; public static final int HKEY_LOCAL_MACHINE = 0x80000002; - public static boolean exists(int hkey, String key, String valueName) { - // This can fail even with errors in case the jna native library extraction fails - try { - return Advapi32Util.registryValueExists( - hkey == HKEY_LOCAL_MACHINE ? WinReg.HKEY_LOCAL_MACHINE : WinReg.HKEY_CURRENT_USER, key, valueName); - } catch (Throwable t) { - ErrorEvent.fromThrowable(t).handle(); - return false; - } - } + public abstract boolean keyExists(int hkey, String key) throws Exception; - public static Optional readString(int hkey, String key) { + public abstract boolean valueExists(int hkey, String key, String valueName) throws Exception; + + public abstract Optional readString(int hkey, String key, String valueName) throws Exception; + + public Optional readString(int hkey, String key) throws Exception { return readString(hkey, key, null); } - public static Optional readString(int hkey, String key, String valueName) { - // This can fail even with errors in case the jna native library extraction fails - try { - if (!Advapi32Util.registryValueExists( - hkey == HKEY_LOCAL_MACHINE ? WinReg.HKEY_LOCAL_MACHINE : WinReg.HKEY_CURRENT_USER, - key, - valueName)) { + public abstract Optional findValuesRecursive(int hkey, String key, String valueName) throws Exception; + + public abstract Optional findKeyForEqualMatchRecursive(int hkey, String key, String match) throws Exception; + + + public static class Local extends WindowsRegistry { + + private WinReg.HKEY hkey(int hkey) { + return hkey == HKEY_LOCAL_MACHINE ? WinReg.HKEY_LOCAL_MACHINE : WinReg.HKEY_CURRENT_USER; + } + + @Override + public boolean keyExists(int hkey, String key) throws Exception { + // This can fail even with errors in case the jna native library extraction or loading fails + try { + return Advapi32Util.registryKeyExists(hkey(hkey), key); + } catch (Throwable t) { + ErrorEvent.fromThrowable(t).handle(); + return false; + } + } + + @Override + public boolean valueExists(int hkey, String key, String valueName) { + // This can fail even with errors in case the jna native library extraction or loading fails + try { + return Advapi32Util.registryValueExists(hkey(hkey), key, valueName); + } catch (Throwable t) { + ErrorEvent.fromThrowable(t).handle(); + return false; + } + } + + @Override + public Optional readString(int hkey, String key, String valueName) { + // This can fail even with errors in case the jna native library extraction or loading fails + try { + if (!Advapi32Util.registryValueExists( + hkey(hkey), + key, + valueName)) { + return Optional.empty(); + } + + return Optional.ofNullable(Advapi32Util.registryGetStringValue(hkey(hkey), key, valueName)); + } catch (Throwable t) { + ErrorEvent.fromThrowable(t).handle(); return Optional.empty(); } + } + + @Override + public Optional findValuesRecursive(int hkey, String key, String valueName) throws Exception { + try (var sc = LocalShell.getShell().start()) { + return new Remote(sc).findValuesRecursive(hkey, key, valueName); + } + } + + @Override + public Optional findKeyForEqualMatchRecursive(int hkey, String key, String match) throws Exception { + try (var sc = LocalShell.getShell().start()) { + return new Remote(sc).findKeyForEqualMatchRecursive(hkey, key, match); + } + } + } + + + public static class Remote extends WindowsRegistry { + + private final ShellControl shellControl; + + public Remote(ShellControl shellControl) {this.shellControl = shellControl;} + + private String hkey(int hkey) { + return hkey == HKEY_LOCAL_MACHINE ? "HKEY_LOCAL_MACHINE" : "HKEY_CURRENT_USER"; + } + + @Override + public boolean keyExists(int hkey, String key) throws Exception { + var command = CommandBuilder.of() + .add("reg", "query") + .addQuoted(hkey(hkey) + "\\" + key) + .add("/ve"); + try (var c = shellControl.command(command).start()) { + return c.discardAndCheckExit(); + } + } + + @Override + public boolean valueExists(int hkey, String key, String valueName) throws Exception { + var command = CommandBuilder.of() + .add("reg", "query") + .addQuoted(hkey(hkey) + "\\" + key) + .add("/v") + .addQuoted(valueName); + try (var c = shellControl.command(command).start()) { + return c.discardAndCheckExit(); + } + } + + @Override + public Optional readString(int hkey, String key, String valueName) throws Exception { + var command = CommandBuilder.of() + .add("reg", "query") + .addQuoted(hkey(hkey) + "\\" + key) + .add("/v") + .addQuoted(valueName); + + String output; + try (var c = shellControl.command(command).start()) { + output = c.readStdoutDiscardErr(); + if (c.getExitCode() != 0) { + return Optional.empty(); + } + } + + // Output has the following format: + // \n\n\n\t\t + if (output.contains("\t")) { + String[] parsed = output.split("\t"); + return Optional.of(parsed[parsed.length - 1]); + } + + if (output.contains(" ")) { + String[] parsed = output.split(" "); + return Optional.of(parsed[parsed.length - 1]); + } - return Optional.ofNullable(Advapi32Util.registryGetStringValue( - hkey == HKEY_LOCAL_MACHINE ? WinReg.HKEY_LOCAL_MACHINE : WinReg.HKEY_CURRENT_USER, key, valueName)); - } catch (Throwable t) { - ErrorEvent.fromThrowable(t).handle(); return Optional.empty(); } - } - public static boolean remoteKeyExists(ShellControl shellControl, int hkey, String key) throws Exception { - var command = CommandBuilder.of() - .add("reg", "query") - .addQuoted((hkey == HKEY_LOCAL_MACHINE ? "HKEY_LOCAL_MACHINE" : "HKEY_CURRENT_USER") + "\\" + key) - .add("/ve"); - try (var c = shellControl.command(command).start()) { - return c.discardAndCheckExit(); - } - } - - public static Optional findRemoteValuesRecursive( - ShellControl shellControl, int hkey, String key, String valueName) throws Exception { - var command = CommandBuilder.of() - .add("reg", "query") - .addQuoted((hkey == HKEY_LOCAL_MACHINE ? "HKEY_LOCAL_MACHINE" : "HKEY_CURRENT_USER") + "\\" + key) - .add("/v") - .addQuoted(valueName) - .add("/s"); - try (var c = shellControl.command(command).start()) { - var output = c.readStdoutDiscardErr(); - if (c.getExitCode() != 0) { - return Optional.empty(); - } else { - return Optional.of(output); - } - } - } - - public static Optional readRemoteString(ShellControl shellControl, int hkey, String key, String valueName) - throws Exception { - var command = CommandBuilder.of() - .add("reg", "query") - .addQuoted((hkey == HKEY_LOCAL_MACHINE ? "HKEY_LOCAL_MACHINE" : "HKEY_CURRENT_USER") + "\\" + key) - .add("/v") - .addQuoted(valueName); - - String output; - try (var c = shellControl.command(command).start()) { - output = c.readStdoutDiscardErr(); - if (c.getExitCode() != 0) { - return Optional.empty(); + @Override + public Optional findValuesRecursive(int hkey, String key, String valueName) throws Exception { + var command = CommandBuilder.of() + .add("reg", "query") + .addQuoted(hkey(hkey) + "\\" + key) + .add("/v") + .addQuoted(valueName) + .add("/s"); + try (var c = shellControl.command(command).start()) { + var output = c.readStdoutDiscardErr(); + if (c.getExitCode() != 0) { + return Optional.empty(); + } else { + return Optional.of(output); + } } } - // Output has the following format: - // \n\n\n\t\t - if (output.contains("\t")) { - String[] parsed = output.split("\t"); - return Optional.of(parsed[parsed.length - 1]); + @Override + public Optional findKeyForEqualMatchRecursive(int hkey, String key, String match) throws Exception { + var command = CommandBuilder.of() + .add("reg", "query") + .addQuoted(hkey(hkey) + "\\" + key) + .add("/f") + .addQuoted(match) + .add("/s") + .add("/e") + .add("/d"); + try (var c = shellControl.command(command).start()) { + var output = c.readStdoutDiscardErr(); + if (c.getExitCode() != 0) { + return Optional.empty(); + } else { + return output.lines().findFirst().flatMap(s -> { + if (s.startsWith("HKEY_CURRENT_USER\\")) { + return Optional.of(new Key(HKEY_CURRENT_USER, s.replace("HKEY_CURRENT_USER\\", ""))); + } + if (s.startsWith("HKEY_LOCAL_MACHINE\\")) { + return Optional.of(new Key(HKEY_LOCAL_MACHINE, s.replace("HKEY_LOCAL_MACHINE\\", ""))); + } + return Optional.empty(); + }); + } + } } - - if (output.contains(" ")) { - String[] parsed = output.split(" "); - return Optional.of(parsed[parsed.length - 1]); - } - - return Optional.empty(); } }