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