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 Charset
getConsoleCharset() {
43 final var console
= System
.console();
44 return console
== null ? Charset
.defaultCharset() : console
.charset();
47 public static String
readAll(InputStream
in, Charset charset
) throws IOException
{
48 var output
= new StringWriter();
49 var buffer
= new byte[4096];
51 while (-1 != (n
= in.read(buffer
))) {
52 output
.write(new String(buffer
, 0, n
, charset
));
54 return output
.toString();
57 public static void createPrivateDirectories(File file
) throws IOException
{
62 final var path
= file
.toPath();
64 Set
<PosixFilePermission
> perms
= EnumSet
.of(OWNER_READ
, OWNER_WRITE
, OWNER_EXECUTE
);
65 Files
.createDirectories(path
, PosixFilePermissions
.asFileAttribute(perms
));
66 } catch (UnsupportedOperationException e
) {
67 Files
.createDirectories(path
);
71 public static File
getDataHomeDir() {
72 var dataHome
= System
.getenv("XDG_DATA_HOME");
73 if (dataHome
!= null) {
74 return new File(dataHome
);
77 logger
.debug("XDG_DATA_HOME not set, falling back to home dir");
78 return new File(new File(System
.getProperty("user.home"), ".local"), "share");
81 public static File
getRuntimeDir() {
82 var runtimeDir
= System
.getenv("XDG_RUNTIME_DIR");
83 if (runtimeDir
!= null) {
84 return new File(runtimeDir
);
87 logger
.debug("XDG_RUNTIME_DIR not set, falling back to temp dir");
88 return new File(System
.getProperty("java.io.tmpdir"));
91 public static Supplier
<String
> getLineSupplier(final Reader reader
) {
92 final var bufferedReader
= new BufferedReader(reader
);
95 return bufferedReader
.readLine();
96 } catch (IOException e
) {
97 logger
.error("Error occurred while reading line", e
);
103 public static InetSocketAddress
parseInetSocketAddress(final String tcpAddress
) throws UserErrorException
{
104 final var colonIndex
= tcpAddress
.lastIndexOf(':');
105 if (colonIndex
< 0) {
106 throw new UserErrorException("Invalid tcp bind address (expected host:port): " + tcpAddress
);
108 final var host
= tcpAddress
.substring(0, colonIndex
);
109 final var portString
= tcpAddress
.substring(colonIndex
+ 1);
113 port
= Integer
.parseInt(portString
);
114 } catch (NumberFormatException e
) {
115 throw new UserErrorException("Invalid tcp port: " + portString
, e
);
117 final var socketAddress
= new InetSocketAddress(host
, port
);
118 if (socketAddress
.isUnresolved()) {
119 throw new UserErrorException("Invalid tcp bind address, invalid host: " + host
);
121 return socketAddress
;
124 public static UnixDomainPrincipal
getUnixDomainPrincipal(final SocketChannel channel
) throws IOException
{
125 UnixDomainPrincipal principal
= null;
127 principal
= channel
.getOption(ExtendedSocketOptions
.SO_PEERCRED
);
128 } catch (UnsupportedOperationException ignored
) {
133 public static ServerSocketChannel
bindSocket(final SocketAddress address
) throws IOErrorException
{
134 final ServerSocketChannel serverChannel
;
137 serverChannel
= address
instanceof UnixDomainSocketAddress
138 ? ServerSocketChannel
.open(StandardProtocolFamily
.UNIX
)
139 : ServerSocketChannel
.open();
140 serverChannel
.bind(address
);
141 logger
.info("Listening on socket: " + address
);
143 } catch (IOException e
) {
144 throw new IOErrorException("Failed to bind socket " + address
+ ": " + e
.getMessage(), e
);
146 return serverChannel
;
149 private static void preBind(SocketAddress address
) throws IOException
{
150 if (address
instanceof UnixDomainSocketAddress usa
) {
151 createPrivateDirectories(usa
.getPath().toFile().getParentFile());
155 private static void postBind(SocketAddress address
) {
156 if (address
instanceof UnixDomainSocketAddress usa
) {
157 usa
.getPath().toFile().deleteOnExit();