]> nmode's Git Repositories - signal-cli/blobdiff - src/main/java/org/asamk/signal/util/IOUtils.java
Use console charset for reading/writing to stdin/out
[signal-cli] / src / main / java / org / asamk / signal / util / IOUtils.java
index 59727a9a773314a9cd81e164d1cdaa2f1f85346d..9f220946cd09dcaa3b68c57e095adfc1a727e843 100644 (file)
@@ -1,21 +1,32 @@
 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;
@@ -23,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));
@@ -40,18 +54,12 @@ public class IOUtils {
         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));
@@ -60,37 +68,93 @@ public class IOUtils {
         }
     }
 
-    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();
         }
     }
 }