package org.asamk.signal.util;
-import org.whispersystems.signalservice.internal.util.Util;
+import org.asamk.signal.commands.exceptions.IOErrorException;
+import org.asamk.signal.commands.exceptions.UserErrorException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
-import java.io.ByteArrayOutputStream;
+import java.io.BufferedReader;
import java.io.File;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.OutputStream;
+import java.io.Reader;
import java.io.StringWriter;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.StandardProtocolFamily;
+import java.net.UnixDomainSocketAddress;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.file.Files;
-import java.nio.file.Path;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.EnumSet;
import java.util.Set;
+import java.util.function.Supplier;
+
+import jdk.net.ExtendedSocketOptions;
+import jdk.net.UnixDomainPrincipal;
import static java.nio.file.attribute.PosixFilePermission.OWNER_EXECUTE;
import static java.nio.file.attribute.PosixFilePermission.OWNER_READ;
public class IOUtils {
+ private final static Logger logger = LoggerFactory.getLogger(IOUtils.class);
+
private IOUtils() {
}
- public static File createTempFile() throws IOException {
- return File.createTempFile("signal_tmp_", ".tmp");
+ public static Charset getConsoleCharset() {
+ final var console = System.console();
+ return console == null ? Charset.defaultCharset() : console.charset();
}
public static String readAll(InputStream in, Charset charset) throws IOException {
- StringWriter output = new StringWriter();
- byte[] buffer = new byte[4096];
+ var output = new StringWriter();
+ var buffer = new byte[4096];
int n;
while (-1 != (n = in.read(buffer))) {
output.write(new String(buffer, 0, n, charset));
return output.toString();
}
- public static byte[] readFully(InputStream in) throws IOException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- Util.copy(in, baos);
- return baos.toByteArray();
- }
-
public static void createPrivateDirectories(File file) throws IOException {
if (file.exists()) {
return;
}
- final Path path = file.toPath();
+ final var path = file.toPath();
try {
Set<PosixFilePermission> perms = EnumSet.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE);
Files.createDirectories(path, PosixFilePermissions.asFileAttribute(perms));
}
}
- public static void createPrivateFile(File path) throws IOException {
- final Path file = path.toPath();
- try {
- Set<PosixFilePermission> perms = EnumSet.of(OWNER_READ, OWNER_WRITE);
- Files.createFile(file, PosixFilePermissions.asFileAttribute(perms));
- } catch (UnsupportedOperationException e) {
- Files.createFile(file);
- }
- }
-
public static File getDataHomeDir() {
- String dataHome = System.getenv("XDG_DATA_HOME");
+ var dataHome = System.getenv("XDG_DATA_HOME");
if (dataHome != null) {
return new File(dataHome);
}
+ logger.debug("XDG_DATA_HOME not set, falling back to home dir");
return new File(new File(System.getProperty("user.home"), ".local"), "share");
}
- public static void copyStreamToFile(InputStream input, File outputFile) throws IOException {
- copyStreamToFile(input, outputFile, 8192);
- }
+ public static File getRuntimeDir() {
+ var runtimeDir = System.getenv("XDG_RUNTIME_DIR");
+ if (runtimeDir != null) {
+ return new File(runtimeDir);
+ }
- public static void copyStreamToFile(InputStream input, File outputFile, int bufferSize) throws IOException {
- try (OutputStream output = new FileOutputStream(outputFile)) {
- byte[] buffer = new byte[bufferSize];
- int read;
+ logger.debug("XDG_RUNTIME_DIR not set, falling back to temp dir");
+ return new File(System.getProperty("java.io.tmpdir"));
+ }
- while ((read = input.read(buffer)) != -1) {
- output.write(buffer, 0, read);
+ public static Supplier<String> getLineSupplier(final Reader reader) {
+ final var bufferedReader = new BufferedReader(reader);
+ return () -> {
+ try {
+ return bufferedReader.readLine();
+ } catch (IOException e) {
+ logger.error("Error occurred while reading line", e);
+ return null;
}
+ };
+ }
+
+ public static InetSocketAddress parseInetSocketAddress(final String tcpAddress) throws UserErrorException {
+ final var colonIndex = tcpAddress.lastIndexOf(':');
+ if (colonIndex < 0) {
+ throw new UserErrorException("Invalid tcp bind address (expected host:port): " + tcpAddress);
+ }
+ final var host = tcpAddress.substring(0, colonIndex);
+ final var portString = tcpAddress.substring(colonIndex + 1);
+
+ final int port;
+ try {
+ port = Integer.parseInt(portString);
+ } catch (NumberFormatException e) {
+ throw new UserErrorException("Invalid tcp port: " + portString, e);
+ }
+ final var socketAddress = new InetSocketAddress(host, port);
+ if (socketAddress.isUnresolved()) {
+ throw new UserErrorException("Invalid tcp bind address, invalid host: " + host);
+ }
+ return socketAddress;
+ }
+
+ public static UnixDomainPrincipal getUnixDomainPrincipal(final SocketChannel channel) throws IOException {
+ UnixDomainPrincipal principal = null;
+ try {
+ principal = channel.getOption(ExtendedSocketOptions.SO_PEERCRED);
+ } catch (UnsupportedOperationException ignored) {
+ }
+ return principal;
+ }
+
+ public static ServerSocketChannel bindSocket(final SocketAddress address) throws IOErrorException {
+ final ServerSocketChannel serverChannel;
+ try {
+ preBind(address);
+ serverChannel = address instanceof UnixDomainSocketAddress
+ ? ServerSocketChannel.open(StandardProtocolFamily.UNIX)
+ : ServerSocketChannel.open();
+ serverChannel.bind(address);
+ logger.info("Listening on socket: " + address);
+ postBind(address);
+ } catch (IOException e) {
+ throw new IOErrorException("Failed to bind socket " + address + ": " + e.getMessage(), e);
+ }
+ return serverChannel;
+ }
+
+ private static void preBind(SocketAddress address) throws IOException {
+ if (address instanceof UnixDomainSocketAddress usa) {
+ createPrivateDirectories(usa.getPath().toFile().getParentFile());
+ }
+ }
+
+ private static void postBind(SocketAddress address) {
+ if (address instanceof UnixDomainSocketAddress usa) {
+ usa.getPath().toFile().deleteOnExit();
}
}
}