1 package org
.asamk
.signal
.manager
.util
;
3 import org
.asamk
.signal
.manager
.api
.Pair
;
4 import org
.signal
.libsignal
.protocol
.IdentityKey
;
5 import org
.signal
.libsignal
.protocol
.fingerprint
.Fingerprint
;
6 import org
.signal
.libsignal
.protocol
.fingerprint
.NumericFingerprintGenerator
;
7 import org
.slf4j
.Logger
;
8 import org
.slf4j
.LoggerFactory
;
9 import org
.whispersystems
.signalservice
.api
.NetworkResult
;
10 import org
.whispersystems
.signalservice
.api
.push
.ServiceId
;
11 import org
.whispersystems
.signalservice
.api
.util
.StreamDetails
;
13 import java
.io
.ByteArrayInputStream
;
15 import java
.io
.FileInputStream
;
16 import java
.io
.IOException
;
17 import java
.io
.InputStream
;
18 import java
.net
.Proxy
;
19 import java
.net
.ProxySelector
;
21 import java
.net
.URISyntaxException
;
22 import java
.net
.URLDecoder
;
23 import java
.nio
.charset
.StandardCharsets
;
24 import java
.util
.HashMap
;
25 import java
.util
.List
;
26 import java
.util
.Locale
;
28 import java
.util
.Optional
;
29 import java
.util
.Spliterator
;
30 import java
.util
.Spliterators
;
31 import java
.util
.function
.BiFunction
;
32 import java
.util
.function
.Consumer
;
33 import java
.util
.function
.Function
;
34 import java
.util
.stream
.Collectors
;
35 import java
.util
.stream
.Stream
;
36 import java
.util
.stream
.StreamSupport
;
38 import okio
.ByteString
;
42 private static final Logger logger
= LoggerFactory
.getLogger(Utils
.class);
44 public static Pair
<StreamDetails
, Optional
<String
>> createStreamDetailsFromDataURI(final String dataURI
) {
45 final DataURI uri
= DataURI
.of(dataURI
);
47 return new Pair
<>(new StreamDetails(new ByteArrayInputStream(uri
.data()), uri
.mediaType(), uri
.data().length
),
48 Optional
.ofNullable(uri
.parameter().get("filename")));
51 public static StreamDetails
createStreamDetailsFromFile(final File file
) throws IOException
{
52 final InputStream stream
= new FileInputStream(file
);
53 final var size
= file
.length();
54 final var mime
= MimeUtils
.getFileMimeType(file
).orElse(MimeUtils
.OCTET_STREAM
);
55 return new StreamDetails(stream
, mime
, size
);
58 public static Pair
<StreamDetails
, Optional
<String
>> createStreamDetails(final String value
) throws IOException
{
60 return createStreamDetailsFromDataURI(value
);
61 } catch (final IllegalArgumentException e
) {
62 final File f
= new File(value
);
64 return new Pair
<>(createStreamDetailsFromFile(f
), Optional
.of(f
.getName()));
68 public static Fingerprint
computeSafetyNumberForNumber(
70 IdentityKey ownIdentityKey
,
72 IdentityKey theirIdentityKey
74 // Version 1: E164 user
75 final var version
= 1;
76 final var ownId
= ownNumber
.getBytes(StandardCharsets
.UTF_8
);
77 final var theirId
= theirNumber
.getBytes(StandardCharsets
.UTF_8
);
79 return getFingerprint(version
, ownId
, ownIdentityKey
, theirId
, theirIdentityKey
);
82 public static Fingerprint
computeSafetyNumberForUuid(
83 ServiceId ownServiceId
,
84 IdentityKey ownIdentityKey
,
85 ServiceId theirServiceId
,
86 IdentityKey theirIdentityKey
88 // Version 2: UUID user
89 final var version
= 2;
90 final var ownId
= ownServiceId
.toByteArray();
91 final var theirId
= theirServiceId
.toByteArray();
93 return getFingerprint(version
, ownId
, ownIdentityKey
, theirId
, theirIdentityKey
);
96 private static Fingerprint
getFingerprint(
99 final IdentityKey ownIdentityKey
,
100 final byte[] theirId
,
101 final IdentityKey theirIdentityKey
103 return new NumericFingerprintGenerator(5200).createFor(version
,
110 public static Locale
getDefaultLocale(Locale fallback
) {
111 final var locale
= Locale
.getDefault();
112 if (locale
== null) {
113 logger
.debug("No default locale found, using fallback: {}", fallback
);
116 final var localeString
= locale
.getLanguage() + "-" + locale
.getCountry();
118 Locale
.LanguageRange
.parse(localeString
);
119 } catch (IllegalArgumentException e
) {
120 logger
.debug("Invalid locale '{}', using fallback: {}", locale
, fallback
);
124 logger
.trace("Using default locale: {} ({})", locale
, localeString
);
128 public static <L
, R
, T
> Stream
<T
> zip(Stream
<L
> leftStream
, Stream
<R
> rightStream
, BiFunction
<L
, R
, T
> combiner
) {
129 Spliterator
<L
> lefts
= leftStream
.spliterator();
130 Spliterator
<R
> rights
= rightStream
.spliterator();
131 return StreamSupport
.stream(new Spliterators
.AbstractSpliterator
<>(Long
.min(lefts
.estimateSize(),
132 rights
.estimateSize()), lefts
.characteristics() & rights
.characteristics()) {
134 public boolean tryAdvance(Consumer
<?
super T
> action
) {
135 return lefts
.tryAdvance(left
-> rights
.tryAdvance(right
-> action
.accept(combiner
.apply(left
, right
))));
137 }, leftStream
.isParallel() || rightStream
.isParallel());
140 public static <OK
, NK
, V
> Map
<NK
, V
> mapKeys(Map
<OK
, V
> map
, Function
<OK
, NK
> keyMapper
) {
141 return map
.entrySet().stream().collect(Collectors
.toMap(e
-> keyMapper
.apply(e
.getKey()), Map
.Entry
::getValue
));
144 public static Map
<String
, String
> getQueryMap(String query
) {
145 var params
= query
.split("&");
146 var map
= new HashMap
<String
, String
>();
147 for (var param
: params
) {
148 final var paramParts
= param
.split("=");
149 var name
= URLDecoder
.decode(paramParts
[0], StandardCharsets
.UTF_8
);
150 var value
= paramParts
.length
== 1 ?
null : URLDecoder
.decode(paramParts
[1], StandardCharsets
.UTF_8
);
151 map
.put(name
, value
);
156 public static <T
> T
handleResponseException(final NetworkResult
<T
> response
) throws IOException
{
157 final var throwableOptional
= response
.getCause();
158 if (throwableOptional
!= null) {
159 if (throwableOptional
instanceof IOException ioException
) {
162 throw new IOException(throwableOptional
);
165 return response
.successOrThrow();
168 public static ByteString
firstNonEmpty(ByteString
... strings
) {
169 for (final var s
: strings
) {
174 return ByteString
.EMPTY
;
178 public static <T
> List
<T
> firstNonEmpty(List
<T
>... values
) {
179 for (final var s
: values
) {
187 public static String
firstNonEmpty(String
... strings
) {
188 for (final var s
: strings
) {
197 public static <T
> T
firstNonNull(T
... values
) {
198 for (final var v
: values
) {
206 public static String
nullIfEmpty(String string
) {
207 return string
== null || string
.isEmpty() ?
null : string
;
210 public static Proxy
getHttpsProxy() {
213 uri
= new URI("https://example");
214 } catch (URISyntaxException e
) {
215 throw new RuntimeException(e
);
217 final var proxies
= ProxySelector
.getDefault().select(uri
);
218 if (proxies
.isEmpty()) {
219 return Proxy
.NO_PROXY
;
221 return proxies
.getFirst();