Implement more features for shells

This commit is contained in:
Christopher Schnick 2022-11-20 19:27:41 +01:00
parent 85389d26f9
commit 07be7eb7d6
17 changed files with 327 additions and 280 deletions

View file

@ -130,7 +130,7 @@ public abstract class Charsetter {
public NewLine inferNewLine(byte[] content) {
Map<NewLine, Integer> count = new HashMap<>();
for (var nl : NewLine.values()) {
var nlBytes = nl.getNewLine().getBytes(StandardCharsets.UTF_8);
var nlBytes = nl.getNewLineString().getBytes(StandardCharsets.UTF_8);
count.put(nl, count(content, nlBytes));
}

View file

@ -20,7 +20,7 @@ public enum NewLine {
public static NewLine platform() {
return Arrays.stream(values())
.filter(n -> n.getNewLine().equals(System.getProperty("line.separator")))
.filter(n -> n.getNewLineString().equals(System.getProperty("line.separator")))
.findFirst()
.orElseThrow();
}
@ -32,7 +32,7 @@ public enum NewLine {
.orElseThrow();
}
public String getNewLine() {
public String getNewLineString() {
return newLine;
}

View file

@ -14,6 +14,6 @@ public class TextWriteConnection extends StreamWriteConnection implements io.xpi
@Override
public void writeLine(String line) throws Exception {
writer.write(line);
writer.write(source.getNewLine().getNewLine());
writer.write(source.getNewLine().getNewLineString());
}
}

View file

@ -2,8 +2,6 @@ package io.xpipe.core.store;
import java.io.*;
import java.nio.charset.Charset;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
public interface CommandProcessControl extends ProcessControl {
@ -45,7 +43,7 @@ public interface CommandProcessControl extends ProcessControl {
CommandProcessControl start() throws Exception;
@Override
CommandProcessControl exitTimeout(int timeout);
CommandProcessControl exitTimeout(Integer timeout);
String readOnlyStdout() throws Exception;
@ -53,6 +51,8 @@ public interface CommandProcessControl extends ProcessControl {
readOrThrow();
}
public String readOrThrow() throws Exception;
public default boolean startAndCheckExit() {
try (var pc = start()) {
return pc.discardAndCheckExit();
@ -70,52 +70,6 @@ public interface CommandProcessControl extends ProcessControl {
}
}
public default Optional<String> readStderrIfPresent() throws Exception {
discardOut();
var bytes = getStderr().readAllBytes();
var string = new String(bytes, getCharset());
var ec = waitFor();
return ec ? Optional.of(string) : Optional.empty();
}
public default String readOrThrow() throws Exception {
AtomicReference<String> readError = new AtomicReference<>("");
var errorThread = new Thread(() -> {
try {
readError.set(new String(getStderr().readAllBytes(), getCharset()));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
errorThread.setDaemon(true);
errorThread.start();
AtomicReference<String> read = new AtomicReference<>("");
var t = new Thread(() -> {
try {
read.set(readLine());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
t.setDaemon(true);
t.start();
var ec = waitFor();
if (!ec) {
throw new ProcessOutputException("Command timed out");
}
var exitCode = getExitCode();
if (exitCode == 0 && !(read.get().isEmpty() && !readError.get().isEmpty())) {
return read.get().trim();
} else {
throw new ProcessOutputException(
"Command returned with " + ec + ": " + readError.get().trim());
}
}
Thread discardOut();
Thread discardErr();

View file

@ -1,19 +1,26 @@
package io.xpipe.core.store;
import io.xpipe.core.util.SupportedOs;
import java.io.InputStream;
import java.io.OutputStream;
public interface MachineStore extends FileSystemStore, ShellStore {
@Override
default void validate() throws Exception {
try (ShellProcessControl pc = create().start()) {
}
}
public default boolean isLocal() {
return false;
}
public default String queryMachineName() throws Exception {
try (CommandProcessControl pc = create().commandListFunction(shellProcessControl ->
shellProcessControl.getShellType().getOperatingSystemNameCommand())
.start()) {
return pc.readOrThrow().trim();
try (var pc = create().start()) {
var operatingSystem = SupportedOs.determine(pc);
return operatingSystem.determineOperatingSystemName(pc);
}
}

View file

@ -1,10 +1,10 @@
package io.xpipe.core.store;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.UUID;
public interface ProcessControl extends AutoCloseable {
@ -12,41 +12,15 @@ public interface ProcessControl extends AutoCloseable {
ShellType getShellType();
String readResultLine(String input, boolean captureOutput) throws IOException;
void writeLine(String line) throws IOException;
void writeLine(String line, boolean captureOutput) throws IOException;
void typeLine(String line);
public default String readOutput() throws IOException {
var id = UUID.randomUUID();
writeLine("echo " + id, false);
String lines = "";
while (true) {
var newLine = readLine();
if (newLine.contains(id.toString())) {
if (getShellType().echoesInput()) {
readLine();
}
break;
}
lines = lines + newLine + "\n";
}
return lines;
}
@Override
void close() throws IOException;
void kill() throws Exception;
String readLine() throws IOException;
void kill() throws IOException;
ProcessControl exitTimeout(int timeout);
ProcessControl exitTimeout(Integer timeout);
ProcessControl start() throws Exception;
@ -59,4 +33,7 @@ public interface ProcessControl extends AutoCloseable {
InputStream getStderr();
Charset getCharset();
BufferedReader getStdoutReader();
BufferedReader getStderrReader();
}

View file

@ -0,0 +1,51 @@
package io.xpipe.core.store;
import lombok.SneakyThrows;
import java.io.BufferedReader;
import java.io.StringReader;
import java.util.LinkedHashMap;
import java.util.Map;
public class PropertiesFormatsParser {
@SneakyThrows
public static Map<String, String> parse(String text, String split) {
var map = new LinkedHashMap<String, String>();
var reader = new BufferedReader(new StringReader(text));
String line;
String currentKey = null;
String currentValue = "";
while ((line = reader.readLine()) != null) {
if (line.startsWith("\s") || line.startsWith("\t")) {
currentValue += line;
continue;
}
if (!line.contains(split)) {
continue;
}
var keyName = line.substring(0, line.indexOf(split)).strip();
var value = line.substring(line.indexOf(split) + 1).strip();
if (value.startsWith("\"") && value.endsWith("\"")) {
value = value.substring(1, value.length() - 1);
}
if (currentKey != null) {
map.put(currentKey, currentValue);
}
currentKey = keyName;
currentValue = value;
}
if (currentKey != null) {
map.put(currentKey, currentValue);
}
return map;
}
}

View file

@ -6,15 +6,20 @@ import lombok.NonNull;
import java.io.IOException;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public interface ShellProcessControl extends ProcessControl {
ShellProcessControl elevation(SecretValue value);
ShellProcessControl elevated(Predicate<ShellProcessControl> elevationFunction);
ShellProcessControl elevation(SecretValue value);
ShellProcessControl startTimeout(Integer timeout);
SecretValue getElevationPassword();
default ShellProcessControl shell(@NonNull ShellType type){
default ShellProcessControl shell(@NonNull ShellType type) {
return shell(type.openCommand());
}
@ -23,15 +28,15 @@ public interface ShellProcessControl extends ProcessControl {
command.stream().map(s -> s.contains(" ") ? "\"" + s + "\"" : s).collect(Collectors.joining(" ")));
}
default ShellProcessControl shell(@NonNull String command){
default ShellProcessControl shell(@NonNull String command) {
return shell(processControl -> command);
}
ShellProcessControl shell(@NonNull Function<ProcessControl, String> command);
ShellProcessControl shell(@NonNull Function<ShellProcessControl, String> command);
void executeCommand(String command) throws IOException;
void executeCommand(String command) throws Exception;
default void executeCommand(List<String> command) throws IOException {
default void executeCommand(List<String> command) throws Exception {
executeCommand(
command.stream().map(s -> s.contains(" ") ? "\"" + s + "\"" : s).collect(Collectors.joining(" ")));
}
@ -40,8 +45,9 @@ public interface ShellProcessControl extends ProcessControl {
ShellProcessControl start() throws Exception;
default CommandProcessControl commandListFunction(Function<ShellProcessControl, List<String>> command) {
return commandFunction(shellProcessControl ->
command.apply(shellProcessControl).stream().map(s -> s.contains(" ") ? "\"" + s + "\"" : s).collect(Collectors.joining(" ")));
return commandFunction(shellProcessControl -> command.apply(shellProcessControl).stream()
.map(s -> s.contains(" ") ? "\"" + s + "\"" : s)
.collect(Collectors.joining(" ")));
}
CommandProcessControl commandFunction(Function<ShellProcessControl, String> command);
@ -54,5 +60,4 @@ public interface ShellProcessControl extends ProcessControl {
}
void exit() throws IOException;
}

View file

@ -8,6 +8,10 @@ public interface ShellStore extends DataStore {
return new LocalStore();
}
static boolean isLocal(ShellStore s) {
return s instanceof LocalStore;
}
ShellProcessControl create();
public default ShellType determineType() throws Exception {

View file

@ -3,20 +3,13 @@ package io.xpipe.core.store;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import io.xpipe.core.charsetter.NewLine;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List;
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
property = "type"
)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
public interface ShellType {
void elevate(ShellProcessControl control, String command, String displayCommand) throws IOException;
default void init(ProcessControl proc) throws IOException {
}
void elevate(ShellProcessControl control, String command, String displayCommand) throws Exception;
default String getExitCommand() {
return "exit";
@ -28,9 +21,14 @@ public interface ShellType {
return ";";
}
String getEchoCommand(String s, boolean newLine);
default String getAndConcatenationOperator() {
return "&&";
}
String getEchoCommand(String s, boolean toErrorStream);
List<String> openCommand();
String switchTo(String cmd);
List<String> createMkdirsCommand(String dirs);
@ -41,7 +39,7 @@ public interface ShellType {
List<String> createFileExistsCommand(String file);
Charset determineCharset(ProcessControl control) throws Exception;
Charset determineCharset(ShellProcessControl control) throws Exception;
NewLine getNewLine();
@ -49,7 +47,5 @@ public interface ShellType {
String getDisplayName();
List<String> getOperatingSystemNameCommand();
boolean echoesInput();
}

View file

@ -4,7 +4,8 @@ import com.fasterxml.jackson.annotation.JsonTypeName;
import io.xpipe.core.charsetter.NewLine;
import lombok.Value;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.List;
@ -16,42 +17,6 @@ public class ShellTypes {
public static final ShellType CMD = new Cmd();
public static final ShellType SH = new Sh();
public static ShellType determine(ProcessControl proc) throws Exception {
proc.writeLine("echo -NoEnumerate \"a\"", false);
String line;
while (true) {
line = proc.readLine();
if (line.equals("-NoEnumerate a")) {
return SH;
}
if (line.contains("echo -NoEnumerate \"a\"")) {
break;
}
}
var o = proc.readLine();
if (o.equals("a")) {
return POWERSHELL;
} else if (o.equals("-NoEnumerate \"a\"")) {
return CMD;
} else {
return SH;
}
}
public static ShellType[] getAvailable(ShellStore store) throws Exception {
try (ProcessControl proc = store.create().start()) {
var type = determine(proc);
if (type == SH) {
return getLinuxShells();
} else {
return getWindowsShells();
}
}
}
public static ShellType getRecommendedDefault() {
if (System.getProperty("os.name").startsWith("Windows")) {
return POWERSHELL;
@ -81,8 +46,8 @@ public class ShellTypes {
public static class Cmd implements ShellType {
@Override
public String getEchoCommand(String s, boolean newLine) {
return newLine ? "echo " + s : "echo | set /p dummyName=" + s;
public String getEchoCommand(String s, boolean toErrorStream) {
return toErrorStream ? "(echo " + s + ")1>&2" : "echo " + s;
}
@Override
@ -91,27 +56,20 @@ public class ShellTypes {
}
@Override
public void elevate(ShellProcessControl control, String command, String displayCommand) throws IOException {
control.executeCommand("net session >NUL 2>NUL");
control.executeCommand("echo %errorLevel%");
var exitCode = Integer.parseInt(control.readLine());
if (exitCode != 0) {
throw new IllegalStateException("The command \"" + displayCommand + "\" requires elevation.");
public void elevate(ShellProcessControl control, String command, String displayCommand) throws Exception {
try (CommandProcessControl c = control.command("net session >NUL 2>NUL")) {
var exitCode = c.getExitCode();
if (exitCode != 0) {
throw new IllegalStateException("The command \"" + displayCommand + "\" requires elevation.");
}
}
control.executeCommand(command);
}
@Override
public void init(ProcessControl proc) throws IOException {
proc.readLine();
proc.readLine();
proc.readLine();
}
@Override
public String getExitCodeVariable() {
return "%errorlevel%";
return "!errorlevel!";
}
@Override
@ -121,12 +79,12 @@ public class ShellTypes {
@Override
public List<String> openCommand() {
return List.of("cmd");
return List.of("cmd", "/V:on");
}
@Override
public String switchTo(String cmd) {
return "cmd.exe /c " + cmd;
return "cmd.exe /V:on /c " + cmd;
}
@Override
@ -150,9 +108,18 @@ public class ShellTypes {
}
@Override
public Charset determineCharset(ProcessControl control) throws Exception {
var output = control.readResultLine("chcp", true);
var matcher = Pattern.compile("\\d+").matcher(output);
public Charset determineCharset(ShellProcessControl control) throws Exception {
control.writeLine("chcp");
var r = new BufferedReader(new InputStreamReader(control.getStdout(), StandardCharsets.US_ASCII));
// Read echo of command
r.readLine();
// Read actual output
var line = r.readLine();
// Read additional empty line
r.readLine();
var matcher = Pattern.compile("\\d+").matcher(line);
matcher.find();
return Charset.forName("ibm" + matcher.group());
}
@ -167,11 +134,6 @@ public class ShellTypes {
return "cmd";
}
@Override
public List<String> getOperatingSystemNameCommand() {
return List.of("Get-ComputerInfo");
}
@Override
public boolean echoesInput() {
return true;
@ -188,11 +150,13 @@ public class ShellTypes {
}
@Override
public void elevate(ShellProcessControl control, String command, String displayCommand) throws IOException {
control.executeCommand("([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)");
var exitCode = Integer.parseInt(control.readLine());
if (exitCode != 0) {
throw new IllegalStateException("The command \"" + displayCommand + "\" requires elevation.");
public void elevate(ShellProcessControl control, String command, String displayCommand) throws Exception {
try (CommandProcessControl c = control.command(
"([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)")
.start()) {
if (c.startAndCheckExit()) {
throw new IllegalStateException("The command \"" + displayCommand + "\" requires elevation.");
}
}
control.executeCommand(command);
@ -204,8 +168,8 @@ public class ShellTypes {
}
@Override
public String getEchoCommand(String s, boolean newLine) {
return newLine ? "echo " + s : String.format("Write-Host \"%s\" -NoNewLine", s);
public String getEchoCommand(String s, boolean toErrorStream) {
return String.format("%s \"%s\"", toErrorStream ? "Write-Error" : "Write-Output", s);
}
@Override
@ -220,7 +184,7 @@ public class ShellTypes {
@Override
public List<String> createMkdirsCommand(String dirs) {
return List.of("New-Item", "-Path", "D:\\temp\\Test Folder", "-ItemType", "Directory");
return List.of("New-Item", "-Path", dirs, "-ItemType", "Directory");
}
@Override
@ -239,11 +203,13 @@ public class ShellTypes {
}
@Override
public Charset determineCharset(ProcessControl control) throws Exception {
var output = control.readResultLine("chcp", true);
var matcher = Pattern.compile("\\d+").matcher(output);
matcher.find();
return Charset.forName("ibm" + matcher.group());
public Charset determineCharset(ShellProcessControl control) throws Exception {
try (CommandProcessControl c = control.command("chcp").start()) {
var output = c.readOrThrow().strip();
var matcher = Pattern.compile("\\d+").matcher(output);
matcher.find();
return Charset.forName("ibm" + matcher.group());
}
}
@Override
@ -260,11 +226,6 @@ public class ShellTypes {
public String getDisplayName() {
return "PowerShell";
}
@Override
public List<String> getOperatingSystemNameCommand() {
return List.of("systeminfo", "|", "findstr", "/B", "/C:\"OS Name\"");
}
}
@JsonTypeName("sh")
@ -272,12 +233,24 @@ public class ShellTypes {
public static class Sh implements ShellType {
@Override
public void elevate(ShellProcessControl control, String command, String displayCommand) throws IOException {
if (control.getElevationPassword().getSecretValue() == null) {
throw new IllegalStateException("No password for sudo has been set");
public String getExitCommand() {
return "exit 0";
}
@Override
public String getConcatenationOperator() {
return ";";
}
@Override
public void elevate(ShellProcessControl control, String command, String displayCommand) throws Exception {
if (control.getElevationPassword() == null) {
control.executeCommand("SUDO_ASKPASS=/bin/false; sudo -p \"\" -S " + command);
return;
}
control.executeCommand("sudo -S " + switchTo(command));
control.executeCommand("sudo -p \"\" -S " + command);
// Thread.sleep(200);
control.writeLine(control.getElevationPassword().getSecretValue());
}
@ -287,13 +260,13 @@ public class ShellTypes {
}
@Override
public String getEchoCommand(String s, boolean newLine) {
return newLine ? "echo " + s : "echo -n " + s;
public String getEchoCommand(String s, boolean toErrorStream) {
return "echo " + s + (toErrorStream ? " 1>&2" : "");
}
@Override
public List<String> openCommand() {
return List.of("sh");
return List.of("sh", "-i", "-l");
}
@Override
@ -322,7 +295,7 @@ public class ShellTypes {
}
@Override
public Charset determineCharset(ProcessControl st) throws Exception {
public Charset determineCharset(ShellProcessControl st) throws Exception {
return StandardCharsets.UTF_8;
}
@ -341,11 +314,6 @@ public class ShellTypes {
return "/bin/sh";
}
@Override
public List<String> getOperatingSystemNameCommand() {
return List.of("uname", "-o");
}
@Override
public boolean echoesInput() {
return false;

View file

@ -0,0 +1,153 @@
package io.xpipe.core.util;
import io.xpipe.core.store.*;
import lombok.SneakyThrows;
import java.nio.file.Path;
import java.util.Map;
import java.util.UUID;
public interface SupportedOs {
Windows WINDOWS = new Windows();
Linux LINUX = new Linux();
Mac MAC = new Mac();
static SupportedOs determine(ShellProcessControl pc) throws Exception {
try (CommandProcessControl c = pc.command(pc.getShellType().createFileExistsCommand("C:\\pagefile.sys")).start()) {
if (c.discardAndCheckExit()) {
return WINDOWS;
}
}
return LINUX;
}
Map<String, String> getProperties(ShellProcessControl pc) throws Exception;
String determineOperatingSystemName(ShellProcessControl pc) throws Exception;
@SneakyThrows
public static SupportedOs getLocal() {
try (ShellProcessControl pc = ShellStore.local().create().start()) {
return determine(pc);
}
}
Path getBaseInstallPath();
UUID getSystemUUID(ShellProcessControl pc) throws Exception;
static class Windows implements SupportedOs {
@Override
public Map<String, String> getProperties(ShellProcessControl pc) throws Exception {
try (CommandProcessControl c =
pc.shell(ShellTypes.CMD).command("systeminfo").start()) {
var text = c.readOrThrow();
return PropertiesFormatsParser.parse(text, ":");
}
}
@Override
public String determineOperatingSystemName(ShellProcessControl pc) throws Exception {
var properties = getProperties(pc);
return properties.get("OS Name") + " "
+ properties.get("OS Version").split(" ")[0];
}
@Override
public Path getBaseInstallPath() {
return Path.of(System.getenv("LOCALAPPDATA"), "X-Pipe");
}
@Override
public UUID getSystemUUID(ShellProcessControl pc) throws Exception {
try (CommandProcessControl c = pc.command(
"reg query \"Computer\\\\HKEY_LOCAL_MACHINE\\\\SOFTWARE\\\\Microsoft\\\\Cryptography\" /v MachineGuid")) {
var output = c.readOnlyStdout();
return null;
}
}
}
static class Linux implements SupportedOs {
@Override
public Map<String, String> getProperties(ShellProcessControl pc) throws Exception {
return null;
}
@Override
public String determineOperatingSystemName(ShellProcessControl pc) throws Exception {
try (CommandProcessControl c =
pc.shell(ShellTypes.SH).command("lsb_release -a").start()) {
var text = c.readOnlyStdout();
if (c.getExitCode() == 0) {
return PropertiesFormatsParser.parse(text, ":").getOrDefault("Description", null);
}
}
try (CommandProcessControl c =
pc.shell(ShellTypes.SH).command("cat /etc/*release").start()) {
var text = c.readOnlyStdout();
if (c.getExitCode() == 0) {
return PropertiesFormatsParser.parse(text, "=").getOrDefault("PRETTY_NAME", null);
}
}
String type = "Unknown";
try (CommandProcessControl c =
pc.shell(ShellTypes.SH).command("uname -o").start()) {
var text = c.readOnlyStdout();
if (c.getExitCode() == 0) {
type = text.strip();
}
}
String version = "?";
try (CommandProcessControl c =
pc.shell(ShellTypes.SH).command("uname -r").start()) {
var text = c.readOnlyStdout();
if (c.getExitCode() == 0) {
version = text.strip();
}
}
return type + " " + version;
}
@Override
public Path getBaseInstallPath() {
return Path.of("/opt/xpipe");
}
@Override
public UUID getSystemUUID(ShellProcessControl pc) throws Exception {
return null;
}
}
static class Mac implements SupportedOs {
@Override
public Map<String, String> getProperties(ShellProcessControl pc) throws Exception {
return null;
}
@Override
public String determineOperatingSystemName(ShellProcessControl pc) throws Exception {
return null;
}
@Override
public Path getBaseInstallPath() {
return Path.of(System.getProperty("user.home"), "Application Support", "X-Pipe");
}
@Override
public UUID getSystemUUID(ShellProcessControl pc) throws Exception {
return null;
}
}
}

View file

@ -4,6 +4,7 @@ import io.xpipe.fxcomps.Comp;
import io.xpipe.fxcomps.CompStructure;
import io.xpipe.fxcomps.SimpleCompStructure;
import io.xpipe.fxcomps.util.PlatformThread;
import io.xpipe.fxcomps.util.SimpleChangeListener;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleStringProperty;
import javafx.event.EventHandler;
@ -26,7 +27,9 @@ public class TextFieldComp extends Comp<CompStructure<TextField>> {
this.currentValue = new SimpleStringProperty(value.getValue());
this.lazy = lazy;
if (!lazy) {
value.bind(currentValue);
SimpleChangeListener.apply(currentValue, val -> {
value.setValue(val);
});
}
}

View file

@ -5,7 +5,7 @@ import java.util.ServiceLoader;
public abstract class EventHandler {
private static final EventHandler DEFAULT = new EventHandler() {
public static final EventHandler DEFAULT = new EventHandler() {
@Override
public List<TrackEvent> snapshotEvents() {
return List.of();

View file

@ -35,6 +35,10 @@ public class TrackEvent {
return builder().type("info").message(message);
}
public static TrackEventBuilder withWarn(String category, String message) {
return builder().category(category).type("warn").message(message);
}
public static TrackEventBuilder withWarn(String message) {
return builder().type("warn").message(message);
}

View file

@ -183,7 +183,7 @@ public class DynamicOptionsBuilder {
public DynamicOptionsBuilder addComp(ObservableValue<String> name, Comp<?> comp, Property<?> prop) {
entries.add(new DynamicOptionsComp.Entry(name, comp));
props.add(prop);
if (prop != null) props.add(prop);
return this;
}

View file

@ -1,75 +0,0 @@
package io.xpipe.extension.util;
import org.apache.commons.lang3.SystemUtils;
import java.nio.file.Path;
import java.util.UUID;
public interface SupportedOs {
Windows WINDOWS = new Windows();
Linux LINUX = new Linux();
Mac MAC = new Mac();
public static SupportedOs get() {
if (SystemUtils.IS_OS_WINDOWS) {
return WINDOWS;
} else if (SystemUtils.IS_OS_LINUX) {
return LINUX;
} else if (SystemUtils.IS_OS_MAC) {
return MAC;
} else {
throw new UnsupportedOperationException("Unsupported operating system");
}
}
Path getBaseInstallPath();
UUID getSystemUUID();
static class Windows implements SupportedOs {
@Override
public Path getBaseInstallPath() {
return Path.of(System.getenv("LOCALAPPDATA"), "X-Pipe");
}
@Override
public UUID getSystemUUID() {
var s = WindowsRegistry.readRegistry(
"Computer\\HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography", "MachineGuid")
.orElse(null);
if (s == null) {
return null;
}
return UUID.fromString(s);
}
}
static class Linux implements SupportedOs {
@Override
public Path getBaseInstallPath() {
return Path.of("/opt/xpipe");
}
@Override
public UUID getSystemUUID() {
return null;
}
}
static class Mac implements SupportedOs {
@Override
public Path getBaseInstallPath() {
return Path.of(System.getProperty("user.home"), "Application Support", "X-Pipe");
}
@Override
public UUID getSystemUUID() {
return null;
}
}
}