Refactor shells

This commit is contained in:
Christopher Schnick 2023-01-04 05:23:57 +01:00
parent ac16967efd
commit 5dcb994f9b
17 changed files with 267 additions and 221 deletions

View file

@ -1,7 +1,6 @@
package io.xpipe.core.impl;
import io.xpipe.core.process.ShellProcessControl;
import io.xpipe.core.process.ShellTypes;
import java.util.ServiceLoader;

View file

@ -14,6 +14,11 @@ public class OutputStreamStore implements StreamDataStore {
this.out = out;
}
@Override
public boolean isContentExclusivelyAccessible() {
return true;
}
@Override
public DataFlow getFlow() {
return DataFlow.OUTPUT;

View file

@ -7,6 +7,8 @@ import java.util.function.Consumer;
public interface CommandProcessControl extends ProcessControl {
@Override
public CommandProcessControl sensitive();
CommandProcessControl complex();
default InputStream startExternalStdout() throws Exception {
@ -64,9 +66,7 @@ public interface CommandProcessControl extends ProcessControl {
String readOnlyStdout() throws Exception;
public default void discardOrThrow() throws Exception {
readOrThrow();
}
public void discardOrThrow() throws Exception;
void accumulateStdout(Consumer<String> con);

View file

@ -8,6 +8,8 @@ import java.nio.charset.Charset;
public interface ProcessControl extends Closeable, AutoCloseable {
ProcessControl sensitive();
String prepareTerminalOpen() throws Exception;
void closeStdin() throws IOException;

View file

@ -5,6 +5,7 @@ import io.xpipe.core.charsetter.NewLine;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@JsonTypeInfo(
@ -15,15 +16,11 @@ public interface ShellType {
String getScriptFileEnding();
default String commandWithVariable(String key, String value, String command) {
return joinCommands(getSetVariableCommand(key, value), command);
}
String addInlineVariablesToCommand(Map<String, String> variables, String command);
String getPauseCommand();
String createInitFileContent(String command);
String getTerminalFileOpenCommand(String file);
String prepareScriptContent(String content);
default String flatten(List<String> command) {
return command.stream()
@ -35,12 +32,6 @@ public interface ShellType {
.collect(Collectors.joining(" "));
}
default String joinCommands(String... s) {
return String.join(getConcatenationOperator(), s);
}
void elevate(ShellProcessControl control, String command, String displayCommand) throws Exception;
default String getExitCommand() {
return "exit";
}
@ -61,11 +52,11 @@ public interface ShellType {
return getEchoCommand(s, false);
}
String getSetVariableCommand(String variable, String value);
String getEchoCommand(String s, boolean toErrorStream);
String queryShellProcessId(ShellProcessControl control) throws Exception;
String getSetVariableCommand(String variableName, String value);
default String getPrintVariableCommand(String name) {
return getPrintVariableCommand("", name);
@ -79,19 +70,21 @@ public interface ShellType {
List<String> executeCommandListWithShell(String cmd);
List<String> createMkdirsCommand(String dirs);
List<String> getMkdirsCommand(String dirs);
String createFileReadCommand(String file);
String getFileReadCommand(String file);
String createFileWriteCommand(String file);
String getStreamFileWriteCommand(String file);
String createFileDeleteCommand(String file);
String getSimpleFileWriteCommand(String content, String file);
String createFileExistsCommand(String file);
String getFileDeleteCommand(String file);
String createFileTouchCommand(String file);
String getFileExistsCommand(String file);
String createWhichCommand(String executable);
String getFileTouchCommand(String file);
String getWhichCommand(String executable);
Charset determineCharset(ShellProcessControl control) throws Exception;
@ -103,5 +96,5 @@ public interface ShellType {
String getExecutable();
boolean echoesInput();
boolean doesRepeatInput();
}

View file

@ -6,11 +6,11 @@ import lombok.EqualsAndHashCode;
import lombok.Value;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
public class ShellTypes {
@ -45,6 +45,16 @@ public class ShellTypes {
@Value
public static class Cmd implements ShellType {
@Override
public String addInlineVariablesToCommand(Map<String, String> variables, String command) {
var content = "";
for (Map.Entry<String, String> e : variables.entrySet()) {
content += ("set \"" + e.getKey() + "=" + e.getValue().replaceAll("\"", "^$0") + "\"");
content += getConcatenationOperator();
}
return content + command;
}
@Override
public String getSetVariableCommand(String variableName, String value) {
return ("set \"" + variableName + "=" + value.replaceAll("\"", "^$0") + "\"");
@ -66,19 +76,6 @@ public class ShellTypes {
+ "\"\r\necho %echov%\r\n@echo on\n(goto) 2>nul & del \"%~f0\"");
}
@Override
public String queryShellProcessId(ShellProcessControl control) throws IOException {
control.writeLine("powershell (Get-WmiObject Win32_Process -Filter ProcessId=$PID).ParentProcessId");
var r = new BufferedReader(new InputStreamReader(control.getStdout(), StandardCharsets.US_ASCII));
// Read echo of command
r.readLine();
// Read actual output
var line = r.readLine();
r.readLine();
return line;
}
@Override
public String getConcatenationOperator() {
return "&";
@ -89,11 +86,6 @@ public class ShellTypes {
return "echo.";
}
@Override
public String getTerminalFileOpenCommand(String file) {
return String.format("%s %s \"%s\"", getExecutable(), "/C", file);
}
public String escapeStringValue(String input) {
return input.replaceAll("[&^|<>\"]", "^$0");
}
@ -109,20 +101,8 @@ public class ShellTypes {
}
@Override
public String createInitFileContent(String command) {
return "@echo off\n" + command;
}
@Override
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);
public String prepareScriptContent(String content) {
return "@echo off\n" + content;
}
@Override
@ -151,37 +131,42 @@ public class ShellTypes {
}
@Override
public List<String> createMkdirsCommand(String dirs) {
public List<String> getMkdirsCommand(String dirs) {
return List.of("(", "if", "not", "exist", dirs, "mkdir", dirs, ")");
}
@Override
public String createFileReadCommand(String file) {
public String getFileReadCommand(String file) {
return "type \"" + file + "\"";
}
@Override
public String createFileWriteCommand(String file) {
public String getStreamFileWriteCommand(String file) {
return "findstr \"^\" > \"" + file + "\"";
}
@Override
public String createFileDeleteCommand(String file) {
public String getSimpleFileWriteCommand(String content, String file) {
return "echo " + content + " > \"" + file + "\"";
}
@Override
public String getFileDeleteCommand(String file) {
return "rd /s /q \"" + file + "\"";
}
@Override
public String createFileExistsCommand(String file) {
public String getFileExistsCommand(String file) {
return String.format("dir /a \"%s\"", file);
}
@Override
public String createFileTouchCommand(String file) {
public String getFileTouchCommand(String file) {
return "COPY NUL \"" + file + "\"";
}
@Override
public String createWhichCommand(String executable) {
public String getWhichCommand(String executable) {
return "where \"" + executable + "\"";
}
@ -218,7 +203,7 @@ public class ShellTypes {
}
@Override
public boolean echoesInput() {
public boolean doesRepeatInput() {
return true;
}
}
@ -228,7 +213,22 @@ public class ShellTypes {
public static class PowerShell implements ShellType {
@Override
public String createFileTouchCommand(String file) {
public String addInlineVariablesToCommand(Map<String, String> variables, String command) {
var content = "";
for (Map.Entry<String, String> e : variables.entrySet()) {
content += "$env:" + e.getKey() + " = \"" + escapeStringValue(e.getValue()) + "\"";
content += getConcatenationOperator();
}
return content + command;
}
@Override
public String getSimpleFileWriteCommand(String content, String file) {
return "echo \"" + content + "\" | Out-File \"" + file + "\"";
}
@Override
public String getFileTouchCommand(String file) {
return "$error_count=$error.Count; Out-File -FilePath \"" + file + "\"; $LASTEXITCODE=$error.Count - $error_count";
}
@ -257,18 +257,6 @@ public class ShellTypes {
return List.of("powershell", "-Command", cmd);
}
@Override
public String queryShellProcessId(ShellProcessControl control) throws IOException {
control.writeLine("echo $PID");
var r = new BufferedReader(new InputStreamReader(control.getStdout(), StandardCharsets.US_ASCII));
// Read echo of command
r.readLine();
// Read actual output
var line = r.readLine();
return line;
}
@Override
public String getConcatenationOperator() {
return ";";
@ -280,7 +268,7 @@ public class ShellTypes {
}
@Override
public boolean echoesInput() {
public boolean doesRepeatInput() {
return true;
}
@ -295,33 +283,14 @@ public class ShellTypes {
}
@Override
public String createInitFileContent(String command) {
return command;
}
@Override
public String getTerminalFileOpenCommand(String file) {
return String.format("%s -ExecutionPolicy Bypass -File \"%s\"", getExecutable(), file);
public String prepareScriptContent(String content) {
return content;
}
public String escapeStringValue(String input) {
return input.replaceAll("[\"]", "`$0");
}
@Override
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);
}
@Override
public String getExitCodeVariable() {
return "LASTEXITCODE";
@ -347,32 +316,32 @@ public class ShellTypes {
}
@Override
public String createFileReadCommand(String file) {
public String getFileReadCommand(String file) {
return "cmd /c type \"" + file + "\"";
}
@Override
public String createFileWriteCommand(String file) {
public String getStreamFileWriteCommand(String file) {
return "cmd /c 'findstr \"^\" > \"" + file + "\"'";
}
@Override
public List<String> createMkdirsCommand(String dirs) {
public List<String> getMkdirsCommand(String dirs) {
return List.of("cmd", "/c", "mkdir", dirs);
}
@Override
public String createFileDeleteCommand(String file) {
public String getFileDeleteCommand(String file) {
return "rm /path \"" + file + "\" -force";
}
@Override
public String createFileExistsCommand(String file) {
public String getFileExistsCommand(String file) {
return String.format("cmd /c dir /a \"%s\"", file);
}
@Override
public String createWhichCommand(String executable) {
public String getWhichCommand(String executable) {
return "$LASTEXITCODE=(1 - (Get-Command -erroraction \"silentlycontinue\" \"" + executable + "\").Length)";
}
@ -415,7 +384,12 @@ public class ShellTypes {
public abstract static class PosixBase implements ShellType {
@Override
public String createFileTouchCommand(String file) {
public String getSimpleFileWriteCommand(String content, String file) {
return "echo \"" + content + "\" > \"" + file + "\"";
}
@Override
public String getFileTouchCommand(String file) {
return "touch \"" + file + "\"";
}
@ -429,12 +403,12 @@ public class ShellTypes {
}
@Override
public String createFileDeleteCommand(String file) {
public String getFileDeleteCommand(String file) {
return "rm -rf \"" + file + "\"";
}
@Override
public String createWhichCommand(String executable) {
public String getWhichCommand(String executable) {
return "which \"" + executable + "\"";
}
@ -449,20 +423,25 @@ public class ShellTypes {
}
@Override
public String commandWithVariable(String key, String value, String command) {
return getSetVariableCommand(key, value) + " " + command;
public String addInlineVariablesToCommand(Map<String, String> variables, String command) {
var content = "";
for (Map.Entry<String, String> e : variables.entrySet()) {
content += e.getKey() + "=\"" + e.getValue() + "\"";
content += getConcatenationOperator();
}
return content + command;
}
@Override
public String getPauseCommand() {
return "bash -c read -rsp \"Press any key to continue...\n\" -n 1 key";
return "bash -c read -rsp \"Press any key to continue...\" -n 1 key";
}
public abstract String getName();
@Override
public String createInitFileContent(String command) {
return command;
public String prepareScriptContent(String content) {
return content;
}
@Override
@ -480,23 +459,6 @@ public class ShellTypes {
return ";";
}
@Override
public String getTerminalFileOpenCommand(String file) {
return String.format("%s -i -c \"%s\"", getExecutable(), file);
}
@Override
public void elevate(ShellProcessControl control, String command, String displayCommand) throws Exception {
if (control.getElevationPassword() == null) {
control.executeCommand("SUDO_ASKPASS=/bin/false sudo -n -p \"\" -S -- " + command);
return;
}
// For sudo to always query for a password by using the -k switch
control.executeCommand("sudo -p \"\" -k -S -- " + command);
control.writeLine(control.getElevationPassword().getSecretValue());
}
@Override
public String getExitCodeVariable() {
return "?";
@ -508,18 +470,8 @@ public class ShellTypes {
}
@Override
public String queryShellProcessId(ShellProcessControl control) throws Exception {
try (CommandProcessControl c = control.command("echo $$").start()) {
var out = c.readOnlyStdout();
var matcher = Pattern.compile("\\d+$").matcher(out);
matcher.find();
return matcher.group(0);
}
}
@Override
public String getSetVariableCommand(String variableName, String value) {
return variableName + "=\"" + value + "\"";
public String getSetVariableCommand(String variable, String value) {
return "export " + variable + "=\"" + value + "\"";
}
@Override
@ -533,22 +485,22 @@ public class ShellTypes {
}
@Override
public List<String> createMkdirsCommand(String dirs) {
public List<String> getMkdirsCommand(String dirs) {
return List.of("mkdir", "-p", dirs);
}
@Override
public String createFileReadCommand(String file) {
public String getFileReadCommand(String file) {
return "cat \"" + file + "\"";
}
@Override
public String createFileWriteCommand(String file) {
public String getStreamFileWriteCommand(String file) {
return "cat > \"" + file + "\"";
}
@Override
public String createFileExistsCommand(String file) {
public String getFileExistsCommand(String file) {
return String.format("test -f \"%s\" || test -d \"%s\"", file, file);
}
@ -563,7 +515,7 @@ public class ShellTypes {
}
@Override
public boolean echoesInput() {
public boolean doesRepeatInput() {
return false;
}
}
@ -573,6 +525,11 @@ public class ShellTypes {
@EqualsAndHashCode(callSuper = false)
public static class Sh extends PosixBase {
@Override
public String getStreamFileWriteCommand(String file) {
throw new UnsupportedOperationException();
}
@Override
public String getExecutable() {
return "/bin/sh";

View file

@ -12,21 +12,21 @@ public interface MachineStore extends FileSystemStore, ShellStore {
@Override
public default InputStream openInput(String file) throws Exception {
return create().command(proc -> proc.getShellType()
.createFileReadCommand(proc.getOsType().normalizeFileName(file)))
.getFileReadCommand(proc.getOsType().normalizeFileName(file)))
.startExternalStdout();
}
@Override
public default OutputStream openOutput(String file) throws Exception {
return create().command(proc -> proc.getShellType()
.createFileWriteCommand(proc.getOsType().normalizeFileName(file)))
.getStreamFileWriteCommand(proc.getOsType().normalizeFileName(file)))
.startExternalStdin();
}
@Override
public default boolean exists(String file) throws Exception {
try (var pc = create().command(proc -> proc.getShellType()
.createFileExistsCommand(proc.getOsType().normalizeFileName(file)))
.getFileExistsCommand(proc.getOsType().normalizeFileName(file)))
.start()) {
return pc.discardAndCheckExit();
}
@ -36,7 +36,7 @@ public interface MachineStore extends FileSystemStore, ShellStore {
public default boolean mkdirs(String file) throws Exception {
try (var pc = create().command(proc -> proc.getShellType()
.flatten(proc.getShellType()
.createMkdirsCommand(proc.getOsType().normalizeFileName(file))))
.getMkdirsCommand(proc.getOsType().normalizeFileName(file))))
.start()) {
return pc.discardAndCheckExit();
}

View file

@ -105,6 +105,6 @@ public class Deobfuscator {
}
var t = ShellTypes.getPlatformDefault();
return LocalProcess.executeSimpleBooleanCommand(t.createWhichCommand("retrace." + t.getScriptFileEnding()));
return LocalProcess.executeSimpleBooleanCommand(t.getWhichCommand("retrace." + t.getScriptFileEnding()));
}
}

View file

@ -0,0 +1,53 @@
package io.xpipe.core.util;
import io.xpipe.core.process.OsType;
import lombok.Value;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.UUID;
@Value
public class XPipeSession {
boolean isNewSystemSession;
/**
* Unique identifier that resets on every X-Pipe restart.
*/
UUID sessionId;
/**
* Unique identifier that resets on every X-Pipe update.
*/
UUID buildSessionId;
/**
* Unique identifier that resets on system restarts.
*/
UUID systemSessionId;
private static XPipeSession INSTANCE;
public static void init(UUID buildSessionId) throws Exception {
var sessionFile = XPipeTempDirectory.getLocal().resolve("xpipe_session");
var isNew = !Files.exists(sessionFile);
var systemSessionId = isNew ? UUID.randomUUID() : UUID.fromString(Files.readString(sessionFile));
if (OsType.getLocal().equals(OsType.WINDOWS)) {
var pf = Path.of("C:\\pagefile.sys");
BasicFileAttributes attr = Files.readAttributes(pf, BasicFileAttributes.class);
var timeUuid = UUID.nameUUIDFromBytes(attr.creationTime().toInstant().toString().getBytes());
isNew = isNew && timeUuid.equals(systemSessionId);
systemSessionId = timeUuid;
}
Files.writeString(sessionFile, systemSessionId.toString());
INSTANCE = new XPipeSession(isNew, UUID.randomUUID(), buildSessionId, systemSessionId);
}
public static XPipeSession get() {
return INSTANCE;
}
}

View file

@ -20,8 +20,8 @@ public class XPipeTempDirectory {
var base = proc.getOsType().getTempDirectory(proc);
var dir = FileNames.join(base, "xpipe");
if (!proc.executeBooleanSimpleCommand(proc.getShellType().createFileExistsCommand(dir))) {
proc.executeSimpleCommand(proc.getShellType().flatten(proc.getShellType().createMkdirsCommand(dir)), "Unable to access or create temporary directory " + dir);
if (!proc.executeBooleanSimpleCommand(proc.getShellType().getFileExistsCommand(dir))) {
proc.executeSimpleCommand(proc.getShellType().flatten(proc.getShellType().getMkdirsCommand(dir)), "Unable to access or create temporary directory " + dir);
if (proc.getOsType().equals(OsType.LINUX) || proc.getOsType().equals(OsType.MAC)) {
proc.executeSimpleCommand("(chmod -f 777 \"" + dir + "\"");
@ -33,7 +33,7 @@ public class XPipeTempDirectory {
public static void clear(ShellProcessControl proc) throws Exception {
var dir = get(proc);
if (!proc.executeBooleanSimpleCommand(proc.getShellType().createFileDeleteCommand(dir))) {
if (!proc.executeBooleanSimpleCommand(proc.getShellType().getFileDeleteCommand(dir))) {
throw new IOException("Unable to delete temporary directory " + dir);
}
}

View file

@ -7,7 +7,7 @@ import java.io.IOException;
public class ApplicationHelper {
public static boolean isInPath(ShellProcessControl processControl, String executable) throws Exception {
return processControl.executeBooleanSimpleCommand(processControl.getShellType().createWhichCommand(executable));
return processControl.executeBooleanSimpleCommand(processControl.getShellType().getWhichCommand(executable));
}
public static void checkSupport(ShellProcessControl processControl, String executable, String displayName) throws Exception {

View file

@ -4,10 +4,13 @@ import io.xpipe.api.DataSource;
import io.xpipe.beacon.BeaconDaemonController;
import io.xpipe.core.store.DataStore;
import io.xpipe.core.util.JacksonMapper;
import io.xpipe.core.util.XPipeSession;
import io.xpipe.extension.XPipeServiceProviders;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import java.util.UUID;
public class DaemonExtensionTest extends ExtensionTest {
public static DataSource getSource(String type, DataStore store) {
@ -26,6 +29,7 @@ public class DaemonExtensionTest extends ExtensionTest {
public static void setup() throws Exception {
JacksonMapper.initModularized(ModuleLayer.boot());
XPipeServiceProviders.load(ModuleLayer.boot());
XPipeSession.init(UUID.randomUUID());
BeaconDaemonController.start();
}

View file

@ -1,52 +0,0 @@
package io.xpipe.extension.util;
import io.xpipe.core.impl.FileNames;
import io.xpipe.core.process.ShellProcessControl;
import io.xpipe.core.store.ShellStore;
import io.xpipe.core.util.XPipeTempDirectory;
import lombok.SneakyThrows;
import java.util.Objects;
public class ExecScriptHelper {
public static int getConnectionHash(String command) {
return Math.abs(Objects.hash(command));
}
@SneakyThrows
public static String createLocalExecScript(String content) {
try (var l = ShellStore.local().create().start()) {
return createExecScript(l, content);
}
}
@SneakyThrows
public static String createExecScript(ShellProcessControl processControl, String content) {
var fileName = "exec-" + getConnectionHash(content);
content = processControl.getShellType().createInitFileContent(content);
var temp = XPipeTempDirectory.get(processControl);
var file = FileNames.join(
temp, fileName + "." + processControl.getShellType().getScriptFileEnding());
if (processControl.executeBooleanSimpleCommand(processControl.getShellType().createFileExistsCommand(file))) {
return file;
}
try (var c = processControl.command(processControl.getShellType()
.joinCommands(
processControl.getShellType().createFileWriteCommand(file),
processControl.getShellType().getMakeExecutableCommand(file)
))
.start()) {
c.discardOut();
c.discardErr();
c.getStdin().write(content.getBytes(processControl.getCharset()));
c.closeStdin();
}
processControl.restart();
return file;
}
}

View file

@ -1,14 +1,18 @@
package io.xpipe.extension.util;
import io.xpipe.core.util.JacksonMapper;
import io.xpipe.core.util.XPipeSession;
import io.xpipe.extension.XPipeServiceProviders;
import org.junit.jupiter.api.BeforeAll;
import java.util.UUID;
public class LocalExtensionTest extends ExtensionTest {
@BeforeAll
public static void setup() throws Exception {
JacksonMapper.initModularized(ModuleLayer.boot());
XPipeServiceProviders.load(ModuleLayer.boot());
XPipeSession.init(UUID.randomUUID());
}
}

View file

@ -0,0 +1,80 @@
package io.xpipe.extension.util;
import io.xpipe.core.impl.FileNames;
import io.xpipe.core.process.ShellProcessControl;
import io.xpipe.core.process.ShellType;
import io.xpipe.core.store.ShellStore;
import io.xpipe.core.util.SecretValue;
import io.xpipe.core.util.XPipeSession;
import io.xpipe.core.util.XPipeTempDirectory;
import io.xpipe.extension.event.TrackEvent;
import lombok.SneakyThrows;
import java.util.Objects;
public class ScriptHelper {
public static int getConnectionHash(String command) {
return Math.abs(Objects.hash(command, XPipeSession.get().getSystemSessionId()));
}
@SneakyThrows
public static String createLocalExecScript(String content) {
try (var l = ShellStore.local().create().start()) {
return createExecScript(l, content, false);
}
}
@SneakyThrows
public static String createExecScript(ShellProcessControl processControl, String content, boolean restart) {
var fileName = "exec-" + getConnectionHash(content);
ShellType type = processControl.getShellType();
var temp = XPipeTempDirectory.get(processControl);
var file = FileNames.join(temp, fileName + "." + type.getScriptFileEnding());
return createExecScript(processControl, file, content, restart);
}
@SneakyThrows
private static String createExecScript(ShellProcessControl processControl, String file, String content, boolean restart) {
ShellType type = processControl.getShellType();
content = type.prepareScriptContent(content);
if (processControl.executeBooleanSimpleCommand(type.getFileExistsCommand(file))) {
return file;
}
TrackEvent.withTrace("proc", "Writing exec script")
.tag("file", file)
.tag("content", content)
.handle();
processControl.executeSimpleCommand(type.getFileTouchCommand(file), "Failed to create script " + file);
processControl.executeSimpleCommand(type.getMakeExecutableCommand(file), "Failed to make script " + file + " executable");
if (!content.contains("\n")) {
processControl.executeSimpleCommand(type.getSimpleFileWriteCommand(content, file));
return file;
}
try (var c = processControl.command(type.getStreamFileWriteCommand(file)).start()) {
c.discardOut();
c.discardErr();
c.getStdin().write(content.getBytes(processControl.getCharset()));
c.closeStdin();
}
if (restart) {
processControl.restart();
}
return file;
}
@SneakyThrows
public static String createAskPassScript(SecretValue pass, ShellProcessControl parent, ShellType type, boolean restart) {
var content = type.getScriptEchoCommand(pass.getSecretValue());
var temp = XPipeTempDirectory.get(parent);
var file = FileNames.join(temp, "askpass-" + getConnectionHash(content) + "." + type.getScriptFileEnding());
return createExecScript(parent,file, content, restart);
}
}

View file

@ -3,33 +3,34 @@ package io.xpipe.extension.util;
import io.xpipe.core.impl.LocalStore;
import io.xpipe.core.store.DataStore;
import io.xpipe.core.store.ShellStore;
import io.xpipe.core.util.ValidationException;
import io.xpipe.extension.I18n;
import java.util.function.Predicate;
public class Validators {
public static void nonNull(Object object, String name) {
public static void nonNull(Object object, String name) throws ValidationException {
if (object == null) {
throw new IllegalArgumentException(I18n.get("extension.null", name));
throw new ValidationException(I18n.get("extension.mustNotBeEmpty", name));
}
}
public static void notEmpty(String string, String name) {
public static void notEmpty(String string, String name) throws ValidationException {
if (string.trim().length() == 0) {
throw new IllegalArgumentException(I18n.get("extension.empty", name));
throw new ValidationException(I18n.get("extension.mustNotBeEmpty", name));
}
}
public static void namedStoreExists(DataStore store, String name) {
public static void namedStoreExists(DataStore store, String name) throws ValidationException {
if (!XPipeDaemon.getInstance().getNamedStores().contains(store) && !(store instanceof LocalStore)) {
throw new IllegalArgumentException(I18n.get("extension.missingStore", name));
throw new ValidationException(I18n.get("extension.missingStore", name));
}
}
public static void hostFeature(ShellStore host, Predicate<ShellStore> predicate, String name) {
public static void hostFeature(ShellStore host, Predicate<ShellStore> predicate, String name) throws ValidationException {
if (!predicate.test(host)) {
throw new IllegalArgumentException(I18n.get("extension.hostFeatureUnsupported", name));
throw new ValidationException(I18n.get("extension.hostFeatureUnsupported", name));
}
}
}

View file

@ -5,7 +5,7 @@ lf=LF (Linux)
none=None
common=Common
other=Other
nullPointer=Null Pointer: $MSG$
nullPointer=Null Pointer
mustNotBeEmpty=$NAME$ must not be empty
null=$VALUE$ must be not null
hostFeatureUnsupported=Host does not support the feature $FEATURE$