]> nmode's Git Repositories - signal-cli/blob - src/main/java/org/asamk/signal/manager/Utils.java
Fix inspections
[signal-cli] / src / main / java / org / asamk / signal / manager / Utils.java
1 package org.asamk.signal.manager;
2
3 import org.apache.http.util.TextUtils;
4 import org.asamk.signal.AttachmentInvalidException;
5 import org.signal.libsignal.metadata.certificate.CertificateValidator;
6 import org.whispersystems.libsignal.IdentityKey;
7 import org.whispersystems.libsignal.InvalidKeyException;
8 import org.whispersystems.libsignal.ecc.Curve;
9 import org.whispersystems.libsignal.ecc.ECPublicKey;
10 import org.whispersystems.libsignal.fingerprint.Fingerprint;
11 import org.whispersystems.libsignal.fingerprint.NumericFingerprintGenerator;
12 import org.whispersystems.libsignal.util.guava.Optional;
13 import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
14 import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream;
15 import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
16 import org.whispersystems.signalservice.api.push.SignalServiceAddress;
17 import org.whispersystems.signalservice.api.util.InvalidNumberException;
18 import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
19 import org.whispersystems.signalservice.internal.util.Base64;
20
21 import java.io.*;
22 import java.net.URI;
23 import java.net.URLDecoder;
24 import java.net.URLEncoder;
25 import java.nio.file.Files;
26 import java.util.*;
27
28 class Utils {
29
30 static List<SignalServiceAttachment> getSignalServiceAttachments(List<String> attachments) throws AttachmentInvalidException {
31 List<SignalServiceAttachment> SignalServiceAttachments = null;
32 if (attachments != null) {
33 SignalServiceAttachments = new ArrayList<>(attachments.size());
34 for (String attachment : attachments) {
35 try {
36 SignalServiceAttachments.add(createAttachment(new File(attachment)));
37 } catch (IOException e) {
38 throw new AttachmentInvalidException(attachment, e);
39 }
40 }
41 }
42 return SignalServiceAttachments;
43 }
44
45 static SignalServiceAttachmentStream createAttachment(File attachmentFile) throws IOException {
46 InputStream attachmentStream = new FileInputStream(attachmentFile);
47 final long attachmentSize = attachmentFile.length();
48 String mime = Files.probeContentType(attachmentFile.toPath());
49 if (mime == null) {
50 mime = "application/octet-stream";
51 }
52 // TODO mabybe add a parameter to set the voiceNote, preview, width, height and caption option
53 Optional<byte[]> preview = Optional.absent();
54 Optional<String> caption = Optional.absent();
55 return new SignalServiceAttachmentStream(attachmentStream, mime, attachmentSize, Optional.of(attachmentFile.getName()), false, preview, 0, 0, caption, null);
56 }
57
58 static CertificateValidator getCertificateValidator() {
59 try {
60 ECPublicKey unidentifiedSenderTrustRoot = Curve.decodePoint(Base64.decode(BaseConfig.UNIDENTIFIED_SENDER_TRUST_ROOT), 0);
61 return new CertificateValidator(unidentifiedSenderTrustRoot);
62 } catch (InvalidKeyException | IOException e) {
63 throw new AssertionError(e);
64 }
65 }
66
67 private static Map<String, String> getQueryMap(String query) {
68 String[] params = query.split("&");
69 Map<String, String> map = new HashMap<>();
70 for (String param : params) {
71 String name = null;
72 final String[] paramParts = param.split("=");
73 try {
74 name = URLDecoder.decode(paramParts[0], "utf-8");
75 } catch (UnsupportedEncodingException e) {
76 // Impossible
77 }
78 String value = null;
79 try {
80 value = URLDecoder.decode(paramParts[1], "utf-8");
81 } catch (UnsupportedEncodingException e) {
82 // Impossible
83 }
84 map.put(name, value);
85 }
86 return map;
87 }
88
89 static String createDeviceLinkUri(DeviceLinkInfo info) {
90 try {
91 return "tsdevice:/?uuid=" + URLEncoder.encode(info.deviceIdentifier, "utf-8") + "&pub_key=" + URLEncoder.encode(Base64.encodeBytesWithoutPadding(info.deviceKey.serialize()), "utf-8");
92 } catch (UnsupportedEncodingException e) {
93 // Shouldn't happen
94 return null;
95 }
96 }
97
98 static DeviceLinkInfo parseDeviceLinkUri(URI linkUri) throws IOException, InvalidKeyException {
99 Map<String, String> query = getQueryMap(linkUri.getRawQuery());
100 String deviceIdentifier = query.get("uuid");
101 String publicKeyEncoded = query.get("pub_key");
102
103 if (TextUtils.isEmpty(deviceIdentifier) || TextUtils.isEmpty(publicKeyEncoded)) {
104 throw new RuntimeException("Invalid device link uri");
105 }
106
107 ECPublicKey deviceKey = Curve.decodePoint(Base64.decode(publicKeyEncoded), 0);
108
109 return new DeviceLinkInfo(deviceIdentifier, deviceKey);
110 }
111
112 static Set<SignalServiceAddress> getSignalServiceAddresses(Collection<String> recipients, String localNumber) {
113 Set<SignalServiceAddress> recipientsTS = new HashSet<>(recipients.size());
114 for (String recipient : recipients) {
115 try {
116 recipientsTS.add(getPushAddress(recipient, localNumber));
117 } catch (InvalidNumberException e) {
118 System.err.println("Failed to add recipient \"" + recipient + "\": " + e.getMessage());
119 System.err.println("Aborting sending.");
120 return null;
121 }
122 }
123 return recipientsTS;
124 }
125
126 static String canonicalizeNumber(String number, String localNumber) throws InvalidNumberException {
127 return PhoneNumberFormatter.formatNumber(number, localNumber);
128 }
129
130 private static SignalServiceAddress getPushAddress(String number, String localNumber) throws InvalidNumberException {
131 String e164number = canonicalizeNumber(number, localNumber);
132 return new SignalServiceAddress(e164number);
133 }
134
135 static SignalServiceEnvelope loadEnvelope(File file) throws IOException {
136 try (FileInputStream f = new FileInputStream(file)) {
137 DataInputStream in = new DataInputStream(f);
138 int version = in.readInt();
139 if (version > 2) {
140 return null;
141 }
142 int type = in.readInt();
143 String source = in.readUTF();
144 int sourceDevice = in.readInt();
145 if (version == 1) {
146 // read legacy relay field
147 in.readUTF();
148 }
149 long timestamp = in.readLong();
150 byte[] content = null;
151 int contentLen = in.readInt();
152 if (contentLen > 0) {
153 content = new byte[contentLen];
154 in.readFully(content);
155 }
156 byte[] legacyMessage = null;
157 int legacyMessageLen = in.readInt();
158 if (legacyMessageLen > 0) {
159 legacyMessage = new byte[legacyMessageLen];
160 in.readFully(legacyMessage);
161 }
162 long serverTimestamp = 0;
163 String uuid = null;
164 if (version == 2) {
165 serverTimestamp = in.readLong();
166 uuid = in.readUTF();
167 if ("".equals(uuid)) {
168 uuid = null;
169 }
170 }
171 return new SignalServiceEnvelope(type, source, sourceDevice, timestamp, legacyMessage, content, serverTimestamp, uuid);
172 }
173 }
174
175 static void storeEnvelope(SignalServiceEnvelope envelope, File file) throws IOException {
176 try (FileOutputStream f = new FileOutputStream(file)) {
177 try (DataOutputStream out = new DataOutputStream(f)) {
178 out.writeInt(2); // version
179 out.writeInt(envelope.getType());
180 out.writeUTF(envelope.getSource());
181 out.writeInt(envelope.getSourceDevice());
182 out.writeLong(envelope.getTimestamp());
183 if (envelope.hasContent()) {
184 out.writeInt(envelope.getContent().length);
185 out.write(envelope.getContent());
186 } else {
187 out.writeInt(0);
188 }
189 if (envelope.hasLegacyMessage()) {
190 out.writeInt(envelope.getLegacyMessage().length);
191 out.write(envelope.getLegacyMessage());
192 } else {
193 out.writeInt(0);
194 }
195 out.writeLong(envelope.getServerTimestamp());
196 String uuid = envelope.getUuid();
197 out.writeUTF(uuid == null ? "" : uuid);
198 }
199 }
200 }
201
202 static File retrieveAttachment(SignalServiceAttachmentStream stream, File outputFile) throws IOException {
203 InputStream input = stream.getInputStream();
204
205 try (OutputStream output = new FileOutputStream(outputFile)) {
206 byte[] buffer = new byte[4096];
207 int read;
208
209 while ((read = input.read(buffer)) != -1) {
210 output.write(buffer, 0, read);
211 }
212 } catch (FileNotFoundException e) {
213 e.printStackTrace();
214 return null;
215 }
216 return outputFile;
217 }
218
219 static String computeSafetyNumber(String ownUsername, IdentityKey ownIdentityKey, String theirUsername, IdentityKey theirIdentityKey) {
220 Fingerprint fingerprint = new NumericFingerprintGenerator(5200).createFor(ownUsername, ownIdentityKey, theirUsername, theirIdentityKey);
221 return fingerprint.getDisplayableFingerprint().getDisplayText();
222 }
223
224 static class DeviceLinkInfo {
225
226 final String deviceIdentifier;
227 final ECPublicKey deviceKey;
228
229 DeviceLinkInfo(final String deviceIdentifier, final ECPublicKey deviceKey) {
230 this.deviceIdentifier = deviceIdentifier;
231 this.deviceKey = deviceKey;
232 }
233 }
234 }