]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/util/Utils.java
Update libsignal-service-java
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / util / Utils.java
1 package org.asamk.signal.manager.util;
2
3 import org.asamk.signal.manager.api.Pair;
4 import org.asamk.signal.manager.storage.recipients.RecipientAddress;
5 import org.signal.libsignal.protocol.IdentityKey;
6 import org.signal.libsignal.protocol.fingerprint.Fingerprint;
7 import org.signal.libsignal.protocol.fingerprint.NumericFingerprintGenerator;
8 import org.slf4j.Logger;
9 import org.slf4j.LoggerFactory;
10 import org.whispersystems.signalservice.api.util.StreamDetails;
11 import org.whispersystems.signalservice.internal.ServiceResponse;
12
13 import java.io.ByteArrayInputStream;
14 import java.io.File;
15 import java.io.FileInputStream;
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.net.URLDecoder;
19 import java.nio.charset.StandardCharsets;
20 import java.util.HashMap;
21 import java.util.Locale;
22 import java.util.Map;
23 import java.util.Optional;
24 import java.util.Spliterator;
25 import java.util.Spliterators;
26 import java.util.function.BiFunction;
27 import java.util.function.Consumer;
28 import java.util.stream.Stream;
29 import java.util.stream.StreamSupport;
30
31 public class Utils {
32
33 private final static Logger logger = LoggerFactory.getLogger(Utils.class);
34
35 public static Pair<StreamDetails, Optional<String>> createStreamDetailsFromDataURI(final String dataURI) {
36 final DataURI uri = DataURI.of(dataURI);
37
38 return new Pair<>(new StreamDetails(new ByteArrayInputStream(uri.data()), uri.mediaType(), uri.data().length),
39 Optional.ofNullable(uri.parameter().get("filename")));
40 }
41
42 public static StreamDetails createStreamDetailsFromFile(final File file) throws IOException {
43 final InputStream stream = new FileInputStream(file);
44 final var size = file.length();
45 final var mime = MimeUtils.getFileMimeType(file).orElse(MimeUtils.OCTET_STREAM);
46 return new StreamDetails(stream, mime, size);
47 }
48
49 public static Pair<StreamDetails, Optional<String>> createStreamDetails(final String value) throws IOException {
50 try {
51 return createStreamDetailsFromDataURI(value);
52 } catch (final IllegalArgumentException e) {
53 final File f = new File(value);
54
55 return new Pair<>(createStreamDetailsFromFile(f), Optional.of(f.getName()));
56 }
57 }
58
59 public static Fingerprint computeSafetyNumber(
60 boolean isUuidCapable,
61 RecipientAddress ownAddress,
62 IdentityKey ownIdentityKey,
63 RecipientAddress theirAddress,
64 IdentityKey theirIdentityKey
65 ) {
66 int version;
67 byte[] ownId;
68 byte[] theirId;
69
70 if (!isUuidCapable && ownAddress.number().isPresent() && theirAddress.number().isPresent()) {
71 // Version 1: E164 user
72 version = 1;
73 ownId = ownAddress.number().get().getBytes();
74 theirId = theirAddress.number().get().getBytes();
75 } else if (isUuidCapable && theirAddress.serviceId().isPresent()) {
76 // Version 2: UUID user
77 version = 2;
78 ownId = ownAddress.getServiceId().toByteArray();
79 theirId = theirAddress.getServiceId().toByteArray();
80 } else {
81 return null;
82 }
83
84 return new NumericFingerprintGenerator(5200).createFor(version,
85 ownId,
86 ownIdentityKey,
87 theirId,
88 theirIdentityKey);
89 }
90
91 public static Locale getDefaultLocale(Locale fallback) {
92 final var locale = Locale.getDefault();
93 if (locale == null) {
94 logger.debug("No default locale found, using fallback: {}", fallback);
95 return fallback;
96 }
97 final var localeString = locale.getLanguage() + "-" + locale.getCountry();
98 try {
99 Locale.LanguageRange.parse(localeString);
100 } catch (IllegalArgumentException e) {
101 logger.debug("Invalid locale '{}', using fallback: {}", locale, fallback);
102 return fallback;
103 }
104
105 logger.trace("Using default locale: {} ({})", locale, localeString);
106 return locale;
107 }
108
109 public static <L, R, T> Stream<T> zip(Stream<L> leftStream, Stream<R> rightStream, BiFunction<L, R, T> combiner) {
110 Spliterator<L> lefts = leftStream.spliterator();
111 Spliterator<R> rights = rightStream.spliterator();
112 return StreamSupport.stream(new Spliterators.AbstractSpliterator<>(Long.min(lefts.estimateSize(),
113 rights.estimateSize()), lefts.characteristics() & rights.characteristics()) {
114 @Override
115 public boolean tryAdvance(Consumer<? super T> action) {
116 return lefts.tryAdvance(left -> rights.tryAdvance(right -> action.accept(combiner.apply(left, right))));
117 }
118 }, leftStream.isParallel() || rightStream.isParallel());
119 }
120
121 public static Map<String, String> getQueryMap(String query) {
122 var params = query.split("&");
123 var map = new HashMap<String, String>();
124 for (var param : params) {
125 final var paramParts = param.split("=");
126 var name = URLDecoder.decode(paramParts[0], StandardCharsets.UTF_8);
127 var value = paramParts.length == 1 ? null : URLDecoder.decode(paramParts[1], StandardCharsets.UTF_8);
128 map.put(name, value);
129 }
130 return map;
131 }
132
133 public static <T> T handleResponseException(final ServiceResponse<T> response) throws IOException {
134 final var throwableOptional = response.getExecutionError().or(response::getApplicationError);
135 if (throwableOptional.isPresent()) {
136 if (throwableOptional.get() instanceof IOException) {
137 throw (IOException) throwableOptional.get();
138 } else {
139 throw new IOException(throwableOptional.get());
140 }
141 }
142 return response.getResult().orElse(null);
143 }
144 }