1 package org
.asamk
.signal
.util
;
3 import org
.asamk
.signal
.commands
.exceptions
.IOErrorException
;
4 import org
.asamk
.signal
.commands
.exceptions
.UserErrorException
;
5 import org
.slf4j
.Logger
;
6 import org
.slf4j
.LoggerFactory
;
8 import java
.io
.BufferedReader
;
10 import java
.io
.IOException
;
11 import java
.io
.InputStream
;
12 import java
.io
.Reader
;
13 import java
.io
.StringWriter
;
14 import java
.net
.InetSocketAddress
;
15 import java
.net
.SocketAddress
;
16 import java
.net
.StandardProtocolFamily
;
17 import java
.net
.UnixDomainSocketAddress
;
18 import java
.nio
.channels
.ServerSocketChannel
;
19 import java
.nio
.channels
.SocketChannel
;
20 import java
.nio
.charset
.Charset
;
21 import java
.nio
.file
.Files
;
22 import java
.nio
.file
.attribute
.PosixFilePermission
;
23 import java
.nio
.file
.attribute
.PosixFilePermissions
;
24 import java
.util
.EnumSet
;
26 import java
.util
.function
.Supplier
;
28 import jdk
.net
.ExtendedSocketOptions
;
29 import jdk
.net
.UnixDomainPrincipal
;
31 import static java
.nio
.file
.attribute
.PosixFilePermission
.OWNER_EXECUTE
;
32 import static java
.nio
.file
.attribute
.PosixFilePermission
.OWNER_READ
;
33 import static java
.nio
.file
.attribute
.PosixFilePermission
.OWNER_WRITE
;
35 public class IOUtils
{
37 private final static Logger logger
= LoggerFactory
.getLogger(IOUtils
.class);
42 public static String
readAll(InputStream
in, Charset charset
) throws IOException
{
43 var output
= new StringWriter();
44 var buffer
= new byte[4096];
46 while (-1 != (n
= in.read(buffer
))) {
47 output
.write(new String(buffer
, 0, n
, charset
));
49 return output
.toString();
52 public static void createPrivateDirectories(File file
) throws IOException
{
57 final var path
= file
.toPath();
59 Set
<PosixFilePermission
> perms
= EnumSet
.of(OWNER_READ
, OWNER_WRITE
, OWNER_EXECUTE
);
60 Files
.createDirectories(path
, PosixFilePermissions
.asFileAttribute(perms
));
61 } catch (UnsupportedOperationException e
) {
62 Files
.createDirectories(path
);
66 public static File
getDataHomeDir() {
67 var dataHome
= System
.getenv("XDG_DATA_HOME");
68 if (dataHome
!= null) {
69 return new File(dataHome
);
72 logger
.debug("XDG_DATA_HOME not set, falling back to home dir");
73 return new File(new File(System
.getProperty("user.home"), ".local"), "share");
76 public static File
getRuntimeDir() {
77 var runtimeDir
= System
.getenv("XDG_RUNTIME_DIR");
78 if (runtimeDir
!= null) {
79 return new File(runtimeDir
);
82 logger
.debug("XDG_RUNTIME_DIR not set, falling back to temp dir");
83 return new File(System
.getProperty("java.io.tmpdir"));
86 public static Supplier
<String
> getLineSupplier(final Reader reader
) {
87 final var bufferedReader
= new BufferedReader(reader
);
90 return bufferedReader
.readLine();
91 } catch (IOException e
) {
92 logger
.error("Error occurred while reading line", e
);
98 public static InetSocketAddress
parseInetSocketAddress(final String tcpAddress
) throws UserErrorException
{
99 final var colonIndex
= tcpAddress
.lastIndexOf(':');
100 if (colonIndex
< 0) {
101 throw new UserErrorException("Invalid tcp bind address (expected host:port): " + tcpAddress
);
103 final var host
= tcpAddress
.substring(0, colonIndex
);
104 final var portString
= tcpAddress
.substring(colonIndex
+ 1);
108 port
= Integer
.parseInt(portString
);
109 } catch (NumberFormatException e
) {
110 throw new UserErrorException("Invalid tcp port: " + portString
, e
);
112 final var socketAddress
= new InetSocketAddress(host
, port
);
113 if (socketAddress
.isUnresolved()) {
114 throw new UserErrorException("Invalid tcp bind address, invalid host: " + host
);
116 return socketAddress
;
119 public static UnixDomainPrincipal
getUnixDomainPrincipal(final SocketChannel channel
) throws IOException
{
120 UnixDomainPrincipal principal
= null;
122 principal
= channel
.getOption(ExtendedSocketOptions
.SO_PEERCRED
);
123 } catch (UnsupportedOperationException ignored
) {
128 public static ServerSocketChannel
bindSocket(final SocketAddress address
) throws IOErrorException
{
129 final ServerSocketChannel serverChannel
;
132 serverChannel
= address
instanceof UnixDomainSocketAddress
133 ? ServerSocketChannel
.open(StandardProtocolFamily
.UNIX
)
134 : ServerSocketChannel
.open();
135 serverChannel
.bind(address
);
136 logger
.info("Listening on socket: " + address
);
138 } catch (IOException e
) {
139 throw new IOErrorException("Failed to bind socket " + address
+ ": " + e
.getMessage(), e
);
141 return serverChannel
;
144 private static void preBind(SocketAddress address
) throws IOException
{
145 if (address
instanceof UnixDomainSocketAddress usa
) {
146 createPrivateDirectories(usa
.getPath().toFile().getParentFile());
150 private static void postBind(SocketAddress address
) {
151 if (address
instanceof UnixDomainSocketAddress usa
) {
152 usa
.getPath().toFile().deleteOnExit();