X-Git-Url: https://git.nmode.ca/signal-cli/blobdiff_plain/b2efef4d8c3238b413fe9fe758bd6a5c428498a2..e03c48e0ae58c3d37e03dc3eb0e89d44b07d40f9:/src/main/java/org/asamk/signal/util/IOUtils.java diff --git a/src/main/java/org/asamk/signal/util/IOUtils.java b/src/main/java/org/asamk/signal/util/IOUtils.java index 434669de..9f220946 100644 --- a/src/main/java/org/asamk/signal/util/IOUtils.java +++ b/src/main/java/org/asamk/signal/util/IOUtils.java @@ -1,16 +1,32 @@ package org.asamk.signal.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.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; +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; @@ -18,16 +34,19 @@ import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE; 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)); @@ -35,13 +54,12 @@ public class IOUtils { return output.toString(); } - public static void createPrivateDirectories(String directoryPath) throws IOException { - final File file = new File(directoryPath); + public static void createPrivateDirectories(File file) throws IOException { if (file.exists()) { return; } - final Path path = file.toPath(); + final var path = file.toPath(); try { Set perms = EnumSet.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE); Files.createDirectories(path, PosixFilePermissions.asFileAttribute(perms)); @@ -50,22 +68,93 @@ public class IOUtils { } } - public static void createPrivateFile(String path) throws IOException { - final Path file = new File(path).toPath(); + public static File getDataHomeDir() { + 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 File getRuntimeDir() { + var runtimeDir = System.getenv("XDG_RUNTIME_DIR"); + if (runtimeDir != null) { + return new File(runtimeDir); + } + + logger.debug("XDG_RUNTIME_DIR not set, falling back to temp dir"); + return new File(System.getProperty("java.io.tmpdir")); + } + + public static Supplier 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 { - Set perms = EnumSet.of(OWNER_READ, OWNER_WRITE); - Files.createFile(file, PosixFilePermissions.asFileAttribute(perms)); - } catch (UnsupportedOperationException e) { - Files.createFile(file); + 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 String getDataHomeDir() { - String dataHome = System.getenv("XDG_DATA_HOME"); - if (dataHome != null) { - return dataHome; + 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; + } - return System.getProperty("user.home") + "/.local/share"; + 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(); + } } }