mirror of
https://github.com/xpipe-io/xpipe.git
synced 2026-04-26 09:28:26 -04:00
Various fixes
This commit is contained in:
@@ -68,7 +68,7 @@ public abstract class Charsetter {
|
||||
if (charset.hasByteOrderMark()) {
|
||||
var bom = stream.readNBytes(charset.getByteOrderMark().length);
|
||||
if (bom.length != 0 && !Arrays.equals(bom, charset.getByteOrderMark())) {
|
||||
throw new IllegalStateException("Invalid charset: " + toString());
|
||||
throw new IllegalStateException("Charset does not match: " + charset.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,31 +16,45 @@ public class StreamCharset {
|
||||
public static final StreamCharset UTF8 = new StreamCharset(StandardCharsets.UTF_8, null);
|
||||
public static final StreamCharset UTF8_BOM =
|
||||
new StreamCharset(StandardCharsets.UTF_8, new byte[] {(byte) 0xEF, (byte) 0xBB, (byte) 0xBF});
|
||||
public static final StreamCharset UTF16 = new StreamCharset(StandardCharsets.UTF_16, null);
|
||||
public static final StreamCharset UTF16_BOM =
|
||||
new StreamCharset(StandardCharsets.UTF_16, new byte[] {(byte) 0xFE, (byte) 0xFF});
|
||||
public static final StreamCharset UTF16_BE = new StreamCharset(StandardCharsets.UTF_16BE, null);
|
||||
public static final StreamCharset UTF16_BE_BOM =
|
||||
new StreamCharset(StandardCharsets.UTF_16BE, new byte[] {(byte) 0xFE, (byte) 0xFF});
|
||||
public static final StreamCharset UTF16_LE = new StreamCharset(StandardCharsets.UTF_16LE, null);
|
||||
public static final StreamCharset UTF16_LE_BOM =
|
||||
new StreamCharset(StandardCharsets.UTF_16LE, new byte[] {(byte) 0xFF, (byte) 0xFE});
|
||||
public static final StreamCharset UTF32 = new StreamCharset(Charset.forName("utf-32"), null);
|
||||
public static final StreamCharset UTF32_BOM =
|
||||
new StreamCharset(Charset.forName("utf-32"), new byte[] {0x00, 0x00, (byte) 0xFE, (byte) 0xFF});
|
||||
|
||||
public static final StreamCharset UTF32_LE = new StreamCharset(Charset.forName("utf-32le"), null);
|
||||
public static final StreamCharset UTF32_LE_BOM =
|
||||
new StreamCharset(Charset.forName("utf-32le"), new byte[] {0x00, 0x00, (byte) 0xFE, (byte) 0xFF});
|
||||
public static final StreamCharset UTF32_BE = new StreamCharset(Charset.forName("utf-32be"), null);
|
||||
public static final StreamCharset UTF32_BE_BOM =
|
||||
new StreamCharset(Charset.forName("utf-32be"), new byte[] {(byte) 0xFF, (byte) 0xFE, 0x00, 0x00, });
|
||||
|
||||
public static final List<StreamCharset> COMMON = List.of(
|
||||
UTF8,
|
||||
UTF8_BOM,
|
||||
UTF16,
|
||||
UTF16_BOM,
|
||||
UTF16_BE,
|
||||
UTF16_BE_BOM,
|
||||
UTF16_LE,
|
||||
UTF16_LE_BOM,
|
||||
UTF32,
|
||||
UTF32_BOM,
|
||||
new StreamCharset(StandardCharsets.US_ASCII, null),
|
||||
new StreamCharset(StandardCharsets.ISO_8859_1, null),
|
||||
new StreamCharset(Charset.forName("Windows-1251"), null),
|
||||
new StreamCharset(Charset.forName("Windows-1252"), null));
|
||||
public static final List<StreamCharset> RARE = Charset.availableCharsets().values().stream()
|
||||
.filter(charset -> COMMON.stream().noneMatch(c -> c.getCharset().equals(charset)))
|
||||
.map(charset -> new StreamCharset(charset, null))
|
||||
private static final List<StreamCharset> RARE_KNOWN = List.of(UTF32_LE, UTF32_LE_BOM, UTF32_BE, UTF32_BE_BOM);
|
||||
public static final List<StreamCharset> RARE = Stream.concat(
|
||||
RARE_KNOWN.stream(),
|
||||
Charset.availableCharsets().values().stream()
|
||||
.filter(charset -> !charset.equals(StandardCharsets.UTF_16)
|
||||
&& !charset.equals(Charset.forName("utf-32"))
|
||||
&& !charset.displayName().startsWith("x-")
|
||||
&& !charset.displayName().startsWith("X-")
|
||||
&& !charset.displayName().endsWith("-BOM")
|
||||
&& COMMON.stream()
|
||||
.noneMatch(c -> c.getCharset().equals(charset))
|
||||
&& RARE_KNOWN.stream()
|
||||
.noneMatch(c -> c.getCharset().equals(charset)))
|
||||
.map(charset -> new StreamCharset(charset, null)))
|
||||
.toList();
|
||||
Charset charset;
|
||||
byte[] byteOrderMark;
|
||||
|
||||
@@ -45,7 +45,6 @@ public abstract class DataSource<DS extends DataStore> extends JacksonizedValue
|
||||
throw new AssertionError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void test() throws Exception {
|
||||
store.validate();
|
||||
}
|
||||
|
||||
@@ -51,14 +51,14 @@ public class LocalStore extends JacksonizedValue implements MachineFileStore, St
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessControl prepareCommand(List<SecretValue> input, List<String> cmd, Integer timeout) {
|
||||
return new LocalProcessControl(input, cmd, getEffectiveTimeOut(timeout));
|
||||
public ProcessControl prepareCommand(List<SecretValue> input, List<String> cmd, Integer timeout, Charset charset) {
|
||||
return new LocalProcessControl(input, cmd, getEffectiveTimeOut(timeout), charset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessControl preparePrivilegedCommand(List<SecretValue> input, List<String> cmd, Integer timeOut)
|
||||
public ProcessControl preparePrivilegedCommand(List<SecretValue> input, List<String> cmd, Integer timeOut, Charset charset)
|
||||
throws Exception {
|
||||
return new LocalProcessControl(input, cmd, getEffectiveTimeOut(timeOut));
|
||||
return new LocalProcessControl(input, cmd, getEffectiveTimeOut(timeOut), charset);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -71,14 +71,15 @@ public class LocalStore extends JacksonizedValue implements MachineFileStore, St
|
||||
private final List<SecretValue> input;
|
||||
private final Integer timeout;
|
||||
private final List<String> command;
|
||||
private Charset charset;
|
||||
private final Charset charset;
|
||||
|
||||
private Process process;
|
||||
|
||||
LocalProcessControl(List<SecretValue> input, List<String> cmd, Integer timeout) {
|
||||
LocalProcessControl(List<SecretValue> input, List<String> cmd, Integer timeout, Charset charset) {
|
||||
this.input = input;
|
||||
this.timeout = timeout;
|
||||
this.command = cmd;
|
||||
this.charset = charset;
|
||||
}
|
||||
|
||||
private InputStream createInputStream() {
|
||||
@@ -93,7 +94,6 @@ public class LocalStore extends JacksonizedValue implements MachineFileStore, St
|
||||
var l = type.switchTo(command);
|
||||
var builder = new ProcessBuilder(l);
|
||||
process = builder.start();
|
||||
charset = type.determineCharset(LocalStore.this);
|
||||
|
||||
var t = new Thread(() -> {
|
||||
try (var inputStream = createInputStream()) {
|
||||
|
||||
@@ -10,22 +10,54 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public abstract class ProcessControl {
|
||||
|
||||
public String readOutOnly() throws Exception {
|
||||
start();
|
||||
var errT = discardErr();
|
||||
var string = new String(getStdout().readAllBytes(), getCharset());
|
||||
waitFor();
|
||||
return string.trim();
|
||||
public String executeAndReadStdout() throws Exception {
|
||||
var pc = this;
|
||||
pc.start();
|
||||
pc.discardErr();
|
||||
var bytes = pc.getStdout().readAllBytes();
|
||||
var string = new String(bytes, pc.getCharset());
|
||||
return string;
|
||||
}
|
||||
|
||||
public Optional<String> readErrOnly() throws Exception {
|
||||
start();
|
||||
var outT = discardOut();
|
||||
public void executeOrThrow() throws Exception {
|
||||
var pc = this;
|
||||
pc.start();
|
||||
pc.discardOut();
|
||||
pc.discardErr();
|
||||
pc.waitFor();
|
||||
}
|
||||
|
||||
public Optional<String> executeAndReadStderrIfPresent() throws Exception {
|
||||
var pc = this;
|
||||
pc.start();
|
||||
pc.discardOut();
|
||||
var bytes = pc.getStderr().readAllBytes();
|
||||
var string = new String(bytes, pc.getCharset());
|
||||
var ec = pc.waitFor();
|
||||
return ec != 0 ? Optional.of(string) : Optional.empty();
|
||||
}
|
||||
|
||||
public String executeAndReadStdoutOrThrow()
|
||||
throws Exception {
|
||||
var pc = this;
|
||||
pc.start();
|
||||
|
||||
AtomicReference<String> readError = new AtomicReference<>();
|
||||
var errorThread = new Thread(() -> {
|
||||
try {
|
||||
|
||||
readError.set(new String(pc.getStderr().readAllBytes(), pc.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(new String(getStderr().readAllBytes(), getCharset()));
|
||||
read.set(new String(pc.getStdout().readAllBytes(), pc.getCharset()));
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
@@ -33,8 +65,17 @@ public abstract class ProcessControl {
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
|
||||
var ec = waitFor();
|
||||
return ec != 0 ? Optional.of(read.get().trim()) : Optional.empty();
|
||||
var ec = pc.waitFor();
|
||||
if (ec == -1) {
|
||||
throw new ProcessOutputException("Command timed out");
|
||||
}
|
||||
|
||||
if (ec == 0 && !(read.get().isEmpty() && !readError.get().isEmpty())) {
|
||||
return read.get().trim();
|
||||
} else {
|
||||
throw new ProcessOutputException(
|
||||
"Command returned with " + ec + ": " + readError.get().trim());
|
||||
}
|
||||
}
|
||||
|
||||
public Thread discardOut() {
|
||||
|
||||
@@ -2,11 +2,8 @@ package io.xpipe.core.store;
|
||||
|
||||
import io.xpipe.core.util.SecretValue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public interface ShellStore extends DataStore {
|
||||
|
||||
@@ -18,78 +15,6 @@ public interface ShellStore extends DataStore {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
public default String executeAndRead(List<String> cmd, Integer timeout) throws Exception {
|
||||
var pc = prepareCommand(List.of(), cmd, getEffectiveTimeOut(timeout));
|
||||
pc.start();
|
||||
pc.discardErr();
|
||||
var string = new String(pc.getStdout().readAllBytes(), pc.getCharset());
|
||||
return string;
|
||||
}
|
||||
|
||||
public default String executeAndCheckOut(List<SecretValue> in, List<String> cmd, Integer timeout)
|
||||
throws ProcessOutputException, Exception {
|
||||
var pc = prepareCommand(in, cmd, getEffectiveTimeOut(timeout));
|
||||
pc.start();
|
||||
|
||||
AtomicReference<String> readError = new AtomicReference<>();
|
||||
var errorThread = new Thread(() -> {
|
||||
try {
|
||||
|
||||
readError.set(new String(pc.getStderr().readAllBytes(), pc.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(new String(pc.getStdout().readAllBytes(), pc.getCharset()));
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
});
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
|
||||
var ec = pc.waitFor();
|
||||
if (ec == -1) {
|
||||
throw new ProcessOutputException("Command timed out");
|
||||
}
|
||||
|
||||
if (ec == 0 && !(read.get().isEmpty() && !readError.get().isEmpty())) {
|
||||
return read.get().trim();
|
||||
} else {
|
||||
throw new ProcessOutputException(
|
||||
"Command returned with " + ec + ": " + readError.get().trim());
|
||||
}
|
||||
}
|
||||
|
||||
public default Optional<String> executeAndCheckErr(List<SecretValue> in, List<String> cmd) throws Exception {
|
||||
var pc = prepareCommand(in, cmd, getTimeout());
|
||||
pc.start();
|
||||
var outT = pc.discardOut();
|
||||
|
||||
AtomicReference<String> read = new AtomicReference<>();
|
||||
var t = new Thread(() -> {
|
||||
try {
|
||||
read.set(new String(pc.getStderr().readAllBytes(), pc.getCharset()));
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
});
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
|
||||
outT.join();
|
||||
t.join();
|
||||
|
||||
var ec = pc.waitFor();
|
||||
return ec != 0 ? Optional.of(read.get()) : Optional.empty();
|
||||
}
|
||||
|
||||
public default Integer getEffectiveTimeOut(Integer timeout) {
|
||||
if (this.getTimeout() == null) {
|
||||
return timeout;
|
||||
@@ -100,18 +25,18 @@ public interface ShellStore extends DataStore {
|
||||
return Math.min(getTimeout(), timeout);
|
||||
}
|
||||
|
||||
public default ProcessControl prepareCommand(List<String> cmd, Integer timeout) throws Exception {
|
||||
return prepareCommand(List.of(), cmd, timeout);
|
||||
public default ProcessControl prepareCommand(List<String> cmd, Integer timeout, Charset charset) throws Exception {
|
||||
return prepareCommand(List.of(), cmd, timeout, charset);
|
||||
}
|
||||
|
||||
public abstract ProcessControl prepareCommand(List<SecretValue> input, List<String> cmd, Integer timeout)
|
||||
public abstract ProcessControl prepareCommand(List<SecretValue> input, List<String> cmd, Integer timeout, Charset charset)
|
||||
throws Exception;
|
||||
|
||||
public default ProcessControl preparePrivilegedCommand(List<String> cmd, Integer timeout) throws Exception {
|
||||
return preparePrivilegedCommand(List.of(), cmd, timeout);
|
||||
public default ProcessControl preparePrivilegedCommand(List<String> cmd, Integer timeout, Charset charset) throws Exception {
|
||||
return preparePrivilegedCommand(List.of(), cmd, timeout, charset);
|
||||
}
|
||||
|
||||
public default ProcessControl preparePrivilegedCommand(List<SecretValue> input, List<String> cmd, Integer timeout)
|
||||
public default ProcessControl preparePrivilegedCommand(List<SecretValue> input, List<String> cmd, Integer timeout, Charset charset)
|
||||
throws Exception {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@@ -18,11 +18,18 @@ public class ShellTypes {
|
||||
public static final StandardShellStore.ShellType SH = new Sh();
|
||||
|
||||
public static StandardShellStore.ShellType determine(ShellStore store) throws Exception {
|
||||
var o = store.executeAndCheckOut(List.of(), List.of("echo", "$0"), null).strip();
|
||||
var o = store.prepareCommand(List.of(), List.of("echo", "$0"), null, StandardCharsets.US_ASCII)
|
||||
.executeAndReadStdoutOrThrow()
|
||||
.strip();
|
||||
if (!o.equals("$0")) {
|
||||
return SH;
|
||||
} else {
|
||||
o = store.executeAndCheckOut(List.of(), List.of("(dir 2>&1 *`|echo CMD);&<# rem #>echo PowerShell"), null)
|
||||
o = store.prepareCommand(
|
||||
List.of(),
|
||||
List.of("(dir 2>&1 *`|echo CMD);&<# rem #>echo PowerShell"),
|
||||
null,
|
||||
StandardCharsets.UTF_16LE)
|
||||
.executeAndReadStdoutOrThrow()
|
||||
.trim();
|
||||
if (o.equals("PowerShell")) {
|
||||
return POWERSHELL;
|
||||
@@ -33,7 +40,8 @@ public class ShellTypes {
|
||||
}
|
||||
|
||||
public static StandardShellStore.ShellType[] getAvailable(ShellStore store) throws Exception {
|
||||
var o = store.executeAndCheckOut(List.of(), List.of("echo", "$0"), null);
|
||||
var o = store.prepareCommand(List.of(), List.of("echo", "$0"), null, StandardCharsets.US_ASCII)
|
||||
.executeAndReadStdoutOrThrow();
|
||||
if (!o.trim().equals("$0")) {
|
||||
return getLinuxShells();
|
||||
} else {
|
||||
@@ -43,7 +51,7 @@ public class ShellTypes {
|
||||
|
||||
public static StandardShellStore.ShellType getDefault() {
|
||||
if (System.getProperty("os.name").startsWith("Windows")) {
|
||||
return CMD;
|
||||
return POWERSHELL;
|
||||
} else {
|
||||
return SH;
|
||||
}
|
||||
@@ -71,18 +79,20 @@ public class ShellTypes {
|
||||
var l = new ArrayList<>(cmd);
|
||||
l.add(0, "cmd.exe");
|
||||
l.add(1, "/c");
|
||||
l.add(2, "@chcp 65001");
|
||||
l.add(3, ">");
|
||||
l.add(4, "nul");
|
||||
l.add(5, "&&");
|
||||
l.add(2, "@chcp");
|
||||
l.add(3, "65001");
|
||||
l.add(4, ">");
|
||||
l.add(5, "nul");
|
||||
l.add(6, "&&");
|
||||
return l;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessControl prepareElevatedCommand(
|
||||
ShellStore st, List<SecretValue> in, List<String> cmd, Integer timeout, String pw) throws Exception {
|
||||
ShellStore st, List<SecretValue> in, List<String> cmd, Integer timeout, String pw, Charset charset)
|
||||
throws Exception {
|
||||
var l = List.of("net", "session", ";", "if", "%errorLevel%", "!=", "0");
|
||||
return st.prepareCommand(List.of(), l, timeout);
|
||||
return st.prepareCommand(List.of(), l, timeout, charset);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -184,12 +194,13 @@ public class ShellTypes {
|
||||
|
||||
@Override
|
||||
public ProcessControl prepareElevatedCommand(
|
||||
ShellStore st, List<SecretValue> in, List<String> cmd, Integer timeout, String pw) throws Exception {
|
||||
ShellStore st, List<SecretValue> in, List<String> cmd, Integer timeout, String pw, Charset charset)
|
||||
throws Exception {
|
||||
var l = new ArrayList<>(cmd);
|
||||
l.add(0, "sudo");
|
||||
l.add(1, "-S");
|
||||
var pws = new ByteArrayInputStream(pw.getBytes(determineCharset(st)));
|
||||
return st.prepareCommand(List.of(SecretValue.createForSecretValue(pw)), l, timeout);
|
||||
return st.prepareCommand(List.of(SecretValue.createForSecretValue(pw)), l, timeout, charset);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -22,7 +22,12 @@ public interface StandardShellStore extends MachineFileStore, ShellStore {
|
||||
public abstract ShellType determineType() throws Exception;
|
||||
|
||||
public default String querySystemName() throws Exception {
|
||||
var result = executeAndCheckOut(List.of(), determineType().getOperatingSystemNameCommand(), getTimeout());
|
||||
var result = prepareCommand(
|
||||
List.of(),
|
||||
determineType().getOperatingSystemNameCommand(),
|
||||
getTimeout(),
|
||||
determineType().determineCharset(this))
|
||||
.executeAndReadStdoutOrThrow();
|
||||
return result.strip();
|
||||
}
|
||||
|
||||
@@ -30,7 +35,7 @@ public interface StandardShellStore extends MachineFileStore, ShellStore {
|
||||
public default InputStream openInput(String file) throws Exception {
|
||||
var type = determineType();
|
||||
var cmd = type.createFileReadCommand(file);
|
||||
var p = prepareCommand(List.of(), cmd, null);
|
||||
var p = prepareCommand(List.of(), cmd, null, type.determineCharset(this));
|
||||
p.start();
|
||||
return p.getStdout();
|
||||
}
|
||||
@@ -49,7 +54,7 @@ public interface StandardShellStore extends MachineFileStore, ShellStore {
|
||||
public default boolean exists(String file) throws Exception {
|
||||
var type = determineType();
|
||||
var cmd = type.createFileExistsCommand(file);
|
||||
var p = prepareCommand(List.of(), cmd, null);
|
||||
var p = prepareCommand(List.of(), cmd, null, type.determineCharset(this));
|
||||
p.start();
|
||||
return p.waitFor() == 0;
|
||||
}
|
||||
@@ -63,8 +68,9 @@ public interface StandardShellStore extends MachineFileStore, ShellStore {
|
||||
List<String> switchTo(List<String> cmd);
|
||||
|
||||
default ProcessControl prepareElevatedCommand(
|
||||
ShellStore st, List<SecretValue> in, List<String> cmd, Integer timeout, String pw) throws Exception {
|
||||
return st.prepareCommand(in, cmd, timeout);
|
||||
ShellStore st, List<SecretValue> in, List<String> cmd, Integer timeout, String pw, Charset charset)
|
||||
throws Exception {
|
||||
return st.prepareCommand(in, cmd, timeout, charset);
|
||||
}
|
||||
|
||||
List<String> createFileReadCommand(String file);
|
||||
|
||||
Reference in New Issue
Block a user