Various fixes

This commit is contained in:
Christopher Schnick
2022-10-18 01:37:21 +02:00
parent 9e1363c5df
commit a2238be4cd
22 changed files with 323 additions and 190 deletions

View File

@@ -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());
}
}

View File

@@ -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;

View File

@@ -45,7 +45,6 @@ public abstract class DataSource<DS extends DataStore> extends JacksonizedValue
throw new AssertionError(ex);
}
}
public void test() throws Exception {
store.validate();
}

View File

@@ -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()) {

View File

@@ -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() {

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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);