From: AsamK Date: Sun, 18 Nov 2018 18:51:21 +0000 (+0100) Subject: Extract utils methods X-Git-Tag: v0.6.1~11 X-Git-Url: https://git.nmode.ca/signal-cli/commitdiff_plain/184354ffb71ea643b62c01c8406402ea4f492ac1?ds=sidebyside Extract utils methods --- diff --git a/src/main/java/org/asamk/signal/Main.java b/src/main/java/org/asamk/signal/Main.java index 810f1deb..70a02738 100644 --- a/src/main/java/org/asamk/signal/Main.java +++ b/src/main/java/org/asamk/signal/Main.java @@ -70,8 +70,8 @@ import java.util.concurrent.TimeoutException; public class Main { - public static final String SIGNAL_BUSNAME = "org.asamk.Signal"; - public static final String SIGNAL_OBJECTPATH = "/org/asamk/Signal"; + private static final String SIGNAL_BUSNAME = "org.asamk.Signal"; + private static final String SIGNAL_OBJECTPATH = "/org/asamk/Signal"; public static void main(String[] args) { // Workaround for BKS truststore @@ -286,15 +286,12 @@ public class Main { } catch (IOException e) { e.printStackTrace(); return 3; - } catch (InvalidKeyException e) { + } catch (InvalidKeyException | URISyntaxException e) { e.printStackTrace(); return 2; } catch (AssertionError e) { handleAssertionError(e); return 1; - } catch (URISyntaxException e) { - e.printStackTrace(); - return 2; } break; case "listDevices": @@ -528,9 +525,9 @@ public class Main { if (groupName == null) { groupName = ""; } - List groupMembers = ns.getList("member"); + List groupMembers = ns.getList("member"); if (groupMembers == null) { - groupMembers = new ArrayList(); + groupMembers = new ArrayList<>(); } String groupAvatar = ns.getString("avatar"); if (groupAvatar == null) { @@ -943,7 +940,7 @@ public class Main { final Manager m; - public ReceiveMessageHandler(Manager m) { + ReceiveMessageHandler(Manager m) { this.m = m; } @@ -1207,7 +1204,7 @@ public class Main { final DBusConnection conn; - public DbusReceiveMessageHandler(Manager m, DBusConnection conn) { + DbusReceiveMessageHandler(Manager m, DBusConnection conn) { super(m); this.conn = conn; } @@ -1225,7 +1222,7 @@ public class Main { final Manager m; final ObjectMapper jsonProcessor; - public JsonReceiveMessageHandler(Manager m) { + JsonReceiveMessageHandler(Manager m) { this.m = m; this.jsonProcessor = new ObjectMapper(); jsonProcessor.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); // disable autodetect @@ -1256,7 +1253,7 @@ public class Main { final DBusConnection conn; - public JsonDbusReceiveMessageHandler(Manager m, DBusConnection conn) { + JsonDbusReceiveMessageHandler(Manager m, DBusConnection conn) { super(m); this.conn = conn; } diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index e8860ef8..1b703e42 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -16,7 +16,6 @@ */ package org.asamk.signal.manager; -import org.apache.http.util.TextUtils; import org.asamk.Signal; import org.asamk.signal.*; import org.asamk.signal.storage.SignalAccount; @@ -28,13 +27,10 @@ import org.asamk.signal.storage.threads.ThreadInfo; import org.asamk.signal.util.IOUtils; import org.asamk.signal.util.Util; import org.signal.libsignal.metadata.*; -import org.signal.libsignal.metadata.certificate.CertificateValidator; import org.whispersystems.libsignal.*; import org.whispersystems.libsignal.ecc.Curve; import org.whispersystems.libsignal.ecc.ECKeyPair; import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.whispersystems.libsignal.fingerprint.Fingerprint; -import org.whispersystems.libsignal.fingerprint.NumericFingerprintGenerator; import org.whispersystems.libsignal.state.PreKeyRecord; import org.whispersystems.libsignal.state.SignedPreKeyRecord; import org.whispersystems.libsignal.util.KeyHelper; @@ -57,7 +53,6 @@ import org.whispersystems.signalservice.api.push.exceptions.EncapsulatedExceptio import org.whispersystems.signalservice.api.push.exceptions.NetworkFailureException; import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException; import org.whispersystems.signalservice.api.util.InvalidNumberException; -import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; import org.whispersystems.signalservice.api.util.SleepTimer; import org.whispersystems.signalservice.api.util.UptimeSleepTimer; import org.whispersystems.signalservice.internal.push.SignalServiceProtos; @@ -65,8 +60,6 @@ import org.whispersystems.signalservice.internal.util.Base64; import java.io.*; import java.net.URI; -import java.net.URISyntaxException; -import java.net.URLEncoder; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; @@ -99,43 +92,6 @@ public class Manager implements Signal { } - private static List getSignalServiceAttachments(List attachments) throws AttachmentInvalidException { - List SignalServiceAttachments = null; - if (attachments != null) { - SignalServiceAttachments = new ArrayList<>(attachments.size()); - for (String attachment : attachments) { - try { - SignalServiceAttachments.add(createAttachment(new File(attachment))); - } catch (IOException e) { - throw new AttachmentInvalidException(attachment, e); - } - } - } - return SignalServiceAttachments; - } - - private static SignalServiceAttachmentStream createAttachment(File attachmentFile) throws IOException { - InputStream attachmentStream = new FileInputStream(attachmentFile); - final long attachmentSize = attachmentFile.length(); - String mime = Files.probeContentType(attachmentFile.toPath()); - if (mime == null) { - mime = "application/octet-stream"; - } - // TODO mabybe add a parameter to set the voiceNote, preview, width, height and caption option - Optional preview = Optional.absent(); - Optional caption = Optional.absent(); - return new SignalServiceAttachmentStream(attachmentStream, mime, attachmentSize, Optional.of(attachmentFile.getName()), false, preview, 0, 0, caption, null); - } - - private static CertificateValidator getCertificateValidator() { - try { - ECPublicKey unidentifiedSenderTrustRoot = Curve.decodePoint(Base64.decode(BaseConfig.UNIDENTIFIED_SENDER_TRUST_ROOT), 0); - return new CertificateValidator(unidentifiedSenderTrustRoot); - } catch (InvalidKeyException | IOException e) { - throw new AssertionError(e); - } - } - public String getUsername() { return username; } @@ -163,7 +119,7 @@ public class Manager implements Signal { } public boolean userHasKeys() { - return account.getSignalProtocolStore() != null; + return account != null && account.getSignalProtocolStore() != null; } public void init() throws IOException { @@ -253,7 +209,7 @@ public class Manager implements Signal { accountManager.setGcmId(Optional.absent()); } - public URI getDeviceLinkUri() throws TimeoutException, IOException { + public String getDeviceLinkUri() throws TimeoutException, IOException { if (account == null) { createNewIdentity(); } @@ -261,12 +217,7 @@ public class Manager implements Signal { accountManager = new SignalServiceAccountManager(BaseConfig.serviceConfiguration, username, account.getPassword(), BaseConfig.USER_AGENT, timer); String uuid = accountManager.getNewDeviceUuid(); - try { - return new URI("tsdevice:/?uuid=" + URLEncoder.encode(uuid, "utf-8") + "&pub_key=" + URLEncoder.encode(Base64.encodeBytesWithoutPadding(getIdentity().serialize()), "utf-8")); - } catch (URISyntaxException e) { - // Shouldn't happen - return null; - } + return Utils.createDeviceLinkUri(new Utils.DeviceLinkInfo(uuid, getIdentity().getPublicKey())); } public void finishDeviceLink(String deviceName) throws IOException, InvalidKeyException, TimeoutException, UserAlreadyExists { @@ -290,6 +241,8 @@ public class Manager implements Signal { requestSyncGroups(); requestSyncContacts(); + requestSyncBlocked(); + requestSyncConfiguration(); account.save(); } @@ -305,17 +258,9 @@ public class Manager implements Signal { } public void addDeviceLink(URI linkUri) throws IOException, InvalidKeyException { - Map query = Util.getQueryMap(linkUri.getRawQuery()); - String deviceIdentifier = query.get("uuid"); - String publicKeyEncoded = query.get("pub_key"); + Utils.DeviceLinkInfo info = Utils.parseDeviceLinkUri(linkUri); - if (TextUtils.isEmpty(deviceIdentifier) || TextUtils.isEmpty(publicKeyEncoded)) { - throw new RuntimeException("Invalid device link uri"); - } - - ECPublicKey deviceKey = Curve.decodePoint(Base64.decode(publicKeyEncoded), 0); - - addDevice(deviceIdentifier, deviceKey); + addDevice(info.deviceIdentifier, info.deviceKey); } private void addDevice(String deviceIdentifier, ECPublicKey deviceKey) throws IOException, InvalidKeyException { @@ -362,6 +307,7 @@ public class Manager implements Signal { public void verifyAccount(String verificationCode, String pin) throws IOException { verificationCode = verificationCode.replace("-", ""); account.setSignalingKey(KeyUtils.createSignalingKey()); + // TODO make unrestricted unidentified access configurable accountManager.verifyAccountWithCode(verificationCode, account.getSignalingKey(), account.getSignalProtocolStore().getLocalRegistrationId(), true, pin, getSelfUnidentifiedAccessKey(), false); //accountManager.setGcmId(Optional.of(GoogleCloudMessaging.getInstance(this).register(REGISTRATION_ID))); @@ -379,6 +325,7 @@ public class Manager implements Signal { } else { account.setRegistrationLockPin(null); } + account.save(); } private void refreshPreKeys() throws IOException { @@ -395,7 +342,7 @@ public class Manager implements Signal { return Optional.absent(); } - return Optional.of(createAttachment(file)); + return Optional.of(Utils.createAttachment(file)); } private Optional createContactAvatarAttachment(String number) throws IOException { @@ -404,7 +351,7 @@ public class Manager implements Signal { return Optional.absent(); } - return Optional.of(createAttachment(file)); + return Optional.of(Utils.createAttachment(file)); } private GroupInfo getGroupForSending(byte[] groupId) throws GroupNotFoundException, NotAGroupMemberException { @@ -430,7 +377,7 @@ public class Manager implements Signal { throws IOException, EncapsulatedExceptions, GroupNotFoundException, AttachmentInvalidException { final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder().withBody(messageText); if (attachments != null) { - messageBuilder.withAttachments(getSignalServiceAttachments(attachments)); + messageBuilder.withAttachments(Utils.getSignalServiceAttachments(attachments)); } if (groupId != null) { SignalServiceGroup group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.DELIVER) @@ -484,7 +431,7 @@ public class Manager implements Signal { Set newMembers = new HashSet<>(); for (String member : members) { try { - member = canonicalizeNumber(member); + member = Utils.canonicalizeNumber(member, username); } catch (InvalidNumberException e) { System.err.println("Failed to add member \"" + member + "\" to group: " + e.getMessage()); System.err.println("Aborting…"); @@ -552,7 +499,7 @@ public class Manager implements Signal { File aFile = getGroupAvatarFile(g.groupId); if (aFile.exists()) { try { - group.withAvatar(createAttachment(aFile)); + group.withAvatar(Utils.createAttachment(aFile)); } catch (IOException e) { throw new AttachmentInvalidException(aFile.toString(), e); } @@ -593,7 +540,7 @@ public class Manager implements Signal { throws IOException, EncapsulatedExceptions, AttachmentInvalidException { final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder().withBody(messageText); if (attachments != null) { - messageBuilder.withAttachments(getSignalServiceAttachments(attachments)); + messageBuilder.withAttachments(Utils.getSignalServiceAttachments(attachments)); } sendMessageLegacy(messageBuilder, recipients); } @@ -796,8 +743,11 @@ public class Manager implements Signal { private List sendMessage(SignalServiceDataMessage.Builder messageBuilder, Collection recipients) throws IOException { - Set recipientsTS = getSignalServiceAddresses(recipients); - if (recipientsTS == null) return Collections.emptyList(); + Set recipientsTS = Utils.getSignalServiceAddresses(recipients, username); + if (recipientsTS == null) { + account.save(); + return Collections.emptyList(); + } SignalServiceDataMessage message = null; try { @@ -849,23 +799,8 @@ public class Manager implements Signal { } } - private Set getSignalServiceAddresses(Collection recipients) { - Set recipientsTS = new HashSet<>(recipients.size()); - for (String recipient : recipients) { - try { - recipientsTS.add(getPushAddress(recipient)); - } catch (InvalidNumberException e) { - System.err.println("Failed to add recipient \"" + recipient + "\": " + e.getMessage()); - System.err.println("Aborting sending."); - account.save(); - return null; - } - } - return recipientsTS; - } - private SignalServiceContent decryptMessage(SignalServiceEnvelope envelope) throws InvalidMetadataMessageException, ProtocolInvalidMessageException, ProtocolDuplicateMessageException, ProtocolLegacyMessageException, ProtocolInvalidKeyIdException, InvalidMetadataVersionException, ProtocolInvalidVersionException, ProtocolNoSessionException, ProtocolInvalidKeyException, ProtocolUntrustedIdentityException, SelfSendException { - SignalServiceCipher cipher = new SignalServiceCipher(new SignalServiceAddress(username), account.getSignalProtocolStore(), getCertificateValidator()); + SignalServiceCipher cipher = new SignalServiceCipher(new SignalServiceAddress(username), account.getSignalProtocolStore(), Utils.getCertificateValidator()); try { return cipher.decrypt(envelope); } catch (ProtocolUntrustedIdentityException e) { @@ -978,6 +913,9 @@ public class Manager implements Signal { } } if (message.getProfileKey().isPresent() && message.getProfileKey().get().length == 32) { + if (source.equals(username)) { + this.account.setProfileKey(message.getProfileKey().get()); + } ContactInfo contact = account.getContactStore().getContact(source); if (contact == null) { contact = new ContactInfo(); @@ -1003,7 +941,7 @@ public class Manager implements Signal { } SignalServiceEnvelope envelope; try { - envelope = loadEnvelope(fileEntry); + envelope = Utils.loadEnvelope(fileEntry); if (envelope == null) { continue; } @@ -1054,7 +992,7 @@ public class Manager implements Signal { // store message on disk, before acknowledging receipt to the server try { File cacheFile = getMessageCacheFile(envelope.getSource(), now, envelope.getTimestamp()); - storeEnvelope(envelope, cacheFile); + Utils.storeEnvelope(envelope, cacheFile); } catch (IOException e) { System.err.println("Failed to store encrypted message in disk cache, ignoring: " + e.getMessage()); } @@ -1242,73 +1180,6 @@ public class Manager implements Signal { } } - private SignalServiceEnvelope loadEnvelope(File file) throws IOException { - try (FileInputStream f = new FileInputStream(file)) { - DataInputStream in = new DataInputStream(f); - int version = in.readInt(); - if (version > 2) { - return null; - } - int type = in.readInt(); - String source = in.readUTF(); - int sourceDevice = in.readInt(); - if (version == 1) { - // read legacy relay field - in.readUTF(); - } - long timestamp = in.readLong(); - byte[] content = null; - int contentLen = in.readInt(); - if (contentLen > 0) { - content = new byte[contentLen]; - in.readFully(content); - } - byte[] legacyMessage = null; - int legacyMessageLen = in.readInt(); - if (legacyMessageLen > 0) { - legacyMessage = new byte[legacyMessageLen]; - in.readFully(legacyMessage); - } - long serverTimestamp = 0; - String uuid = null; - if (version == 2) { - serverTimestamp = in.readLong(); - uuid = in.readUTF(); - if ("".equals(uuid)) { - uuid = null; - } - } - return new SignalServiceEnvelope(type, source, sourceDevice, timestamp, legacyMessage, content, serverTimestamp, uuid); - } - } - - private void storeEnvelope(SignalServiceEnvelope envelope, File file) throws IOException { - try (FileOutputStream f = new FileOutputStream(file)) { - try (DataOutputStream out = new DataOutputStream(f)) { - out.writeInt(2); // version - out.writeInt(envelope.getType()); - out.writeUTF(envelope.getSource()); - out.writeInt(envelope.getSourceDevice()); - out.writeLong(envelope.getTimestamp()); - if (envelope.hasContent()) { - out.writeInt(envelope.getContent().length); - out.write(envelope.getContent()); - } else { - out.writeInt(0); - } - if (envelope.hasLegacyMessage()) { - out.writeInt(envelope.getLegacyMessage().length); - out.write(envelope.getLegacyMessage()); - } else { - out.writeInt(0); - } - out.writeLong(envelope.getServerTimestamp()); - String uuid = envelope.getUuid(); - out.writeUTF(uuid == null ? "" : uuid); - } - } - } - private File getContactAvatarFile(String number) { return new File(avatarsPath, "contact-" + number); } @@ -1320,7 +1191,7 @@ public class Manager implements Signal { return retrieveAttachment(pointer, getContactAvatarFile(number), false); } else { SignalServiceAttachmentStream stream = attachment.asStream(); - return retrieveAttachment(stream, getContactAvatarFile(number)); + return Utils.retrieveAttachment(stream, getContactAvatarFile(number)); } } @@ -1335,7 +1206,7 @@ public class Manager implements Signal { return retrieveAttachment(pointer, getGroupAvatarFile(groupId), false); } else { SignalServiceAttachmentStream stream = attachment.asStream(); - return retrieveAttachment(stream, getGroupAvatarFile(groupId)); + return Utils.retrieveAttachment(stream, getGroupAvatarFile(groupId)); } } @@ -1348,23 +1219,6 @@ public class Manager implements Signal { return retrieveAttachment(pointer, getAttachmentFile(pointer.getId()), true); } - private File retrieveAttachment(SignalServiceAttachmentStream stream, File outputFile) throws IOException { - InputStream input = stream.getInputStream(); - - try (OutputStream output = new FileOutputStream(outputFile)) { - byte[] buffer = new byte[4096]; - int read; - - while ((read = input.read(buffer)) != -1) { - output.write(buffer, 0, read); - } - } catch (FileNotFoundException e) { - e.printStackTrace(); - return null; - } - return outputFile; - } - private File retrieveAttachment(SignalServiceAttachmentPointer pointer, File outputFile, boolean storePreview) throws IOException, InvalidMessageException { if (storePreview && pointer.getPreview().isPresent()) { File previewFile = new File(outputFile + ".preview"); @@ -1407,16 +1261,6 @@ public class Manager implements Signal { return messageReceiver.retrieveAttachment(pointer, tmpFile, BaseConfig.MAX_ATTACHMENT_SIZE); } - private String canonicalizeNumber(String number) throws InvalidNumberException { - String localNumber = username; - return PhoneNumberFormatter.formatNumber(number, localNumber); - } - - private SignalServiceAddress getPushAddress(String number) throws InvalidNumberException { - String e164number = canonicalizeNumber(number); - return new SignalServiceAddress(e164number); - } - @Override public boolean isRemote() { return false; @@ -1618,8 +1462,7 @@ public class Manager implements Signal { } public String computeSafetyNumber(String theirUsername, IdentityKey theirIdentityKey) { - Fingerprint fingerprint = new NumericFingerprintGenerator(5200).createFor(username, getIdentity(), theirUsername, theirIdentityKey); - return fingerprint.getDisplayableFingerprint().getDisplayText(); + return Utils.computeSafetyNumber(username, getIdentity(), theirUsername, theirIdentityKey); } public interface ReceiveMessageHandler { diff --git a/src/main/java/org/asamk/signal/manager/Utils.java b/src/main/java/org/asamk/signal/manager/Utils.java new file mode 100644 index 00000000..f47dc1ce --- /dev/null +++ b/src/main/java/org/asamk/signal/manager/Utils.java @@ -0,0 +1,234 @@ +package org.asamk.signal.manager; + +import org.apache.http.util.TextUtils; +import org.asamk.signal.AttachmentInvalidException; +import org.signal.libsignal.metadata.certificate.CertificateValidator; +import org.whispersystems.libsignal.IdentityKey; +import org.whispersystems.libsignal.InvalidKeyException; +import org.whispersystems.libsignal.ecc.Curve; +import org.whispersystems.libsignal.ecc.ECPublicKey; +import org.whispersystems.libsignal.fingerprint.Fingerprint; +import org.whispersystems.libsignal.fingerprint.NumericFingerprintGenerator; +import org.whispersystems.libsignal.util.guava.Optional; +import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; +import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream; +import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; +import org.whispersystems.signalservice.api.push.SignalServiceAddress; +import org.whispersystems.signalservice.api.util.InvalidNumberException; +import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; +import org.whispersystems.signalservice.internal.util.Base64; + +import java.io.*; +import java.net.URI; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.file.Files; +import java.util.*; + +class Utils { + + static List getSignalServiceAttachments(List attachments) throws AttachmentInvalidException { + List SignalServiceAttachments = null; + if (attachments != null) { + SignalServiceAttachments = new ArrayList<>(attachments.size()); + for (String attachment : attachments) { + try { + SignalServiceAttachments.add(createAttachment(new File(attachment))); + } catch (IOException e) { + throw new AttachmentInvalidException(attachment, e); + } + } + } + return SignalServiceAttachments; + } + + static SignalServiceAttachmentStream createAttachment(File attachmentFile) throws IOException { + InputStream attachmentStream = new FileInputStream(attachmentFile); + final long attachmentSize = attachmentFile.length(); + String mime = Files.probeContentType(attachmentFile.toPath()); + if (mime == null) { + mime = "application/octet-stream"; + } + // TODO mabybe add a parameter to set the voiceNote, preview, width, height and caption option + Optional preview = Optional.absent(); + Optional caption = Optional.absent(); + return new SignalServiceAttachmentStream(attachmentStream, mime, attachmentSize, Optional.of(attachmentFile.getName()), false, preview, 0, 0, caption, null); + } + + static CertificateValidator getCertificateValidator() { + try { + ECPublicKey unidentifiedSenderTrustRoot = Curve.decodePoint(Base64.decode(BaseConfig.UNIDENTIFIED_SENDER_TRUST_ROOT), 0); + return new CertificateValidator(unidentifiedSenderTrustRoot); + } catch (InvalidKeyException | IOException e) { + throw new AssertionError(e); + } + } + + private static Map getQueryMap(String query) { + String[] params = query.split("&"); + Map map = new HashMap<>(); + for (String param : params) { + String name = null; + final String[] paramParts = param.split("="); + try { + name = URLDecoder.decode(paramParts[0], "utf-8"); + } catch (UnsupportedEncodingException e) { + // Impossible + } + String value = null; + try { + value = URLDecoder.decode(paramParts[1], "utf-8"); + } catch (UnsupportedEncodingException e) { + // Impossible + } + map.put(name, value); + } + return map; + } + + static String createDeviceLinkUri(DeviceLinkInfo info) { + try { + return "tsdevice:/?uuid=" + URLEncoder.encode(info.deviceIdentifier, "utf-8") + "&pub_key=" + URLEncoder.encode(Base64.encodeBytesWithoutPadding(info.deviceKey.serialize()), "utf-8"); + } catch (UnsupportedEncodingException e) { + // Shouldn't happen + return null; + } + } + + static DeviceLinkInfo parseDeviceLinkUri(URI linkUri) throws IOException, InvalidKeyException { + Map query = getQueryMap(linkUri.getRawQuery()); + String deviceIdentifier = query.get("uuid"); + String publicKeyEncoded = query.get("pub_key"); + + if (TextUtils.isEmpty(deviceIdentifier) || TextUtils.isEmpty(publicKeyEncoded)) { + throw new RuntimeException("Invalid device link uri"); + } + + ECPublicKey deviceKey = Curve.decodePoint(Base64.decode(publicKeyEncoded), 0); + + return new DeviceLinkInfo(deviceIdentifier, deviceKey); + } + + static Set getSignalServiceAddresses(Collection recipients, String localNumber) { + Set recipientsTS = new HashSet<>(recipients.size()); + for (String recipient : recipients) { + try { + recipientsTS.add(getPushAddress(recipient, localNumber)); + } catch (InvalidNumberException e) { + System.err.println("Failed to add recipient \"" + recipient + "\": " + e.getMessage()); + System.err.println("Aborting sending."); + return null; + } + } + return recipientsTS; + } + + static String canonicalizeNumber(String number, String localNumber) throws InvalidNumberException { + return PhoneNumberFormatter.formatNumber(number, localNumber); + } + + private static SignalServiceAddress getPushAddress(String number, String localNumber) throws InvalidNumberException { + String e164number = canonicalizeNumber(number, localNumber); + return new SignalServiceAddress(e164number); + } + + static SignalServiceEnvelope loadEnvelope(File file) throws IOException { + try (FileInputStream f = new FileInputStream(file)) { + DataInputStream in = new DataInputStream(f); + int version = in.readInt(); + if (version > 2) { + return null; + } + int type = in.readInt(); + String source = in.readUTF(); + int sourceDevice = in.readInt(); + if (version == 1) { + // read legacy relay field + in.readUTF(); + } + long timestamp = in.readLong(); + byte[] content = null; + int contentLen = in.readInt(); + if (contentLen > 0) { + content = new byte[contentLen]; + in.readFully(content); + } + byte[] legacyMessage = null; + int legacyMessageLen = in.readInt(); + if (legacyMessageLen > 0) { + legacyMessage = new byte[legacyMessageLen]; + in.readFully(legacyMessage); + } + long serverTimestamp = 0; + String uuid = null; + if (version == 2) { + serverTimestamp = in.readLong(); + uuid = in.readUTF(); + if ("".equals(uuid)) { + uuid = null; + } + } + return new SignalServiceEnvelope(type, source, sourceDevice, timestamp, legacyMessage, content, serverTimestamp, uuid); + } + } + + static void storeEnvelope(SignalServiceEnvelope envelope, File file) throws IOException { + try (FileOutputStream f = new FileOutputStream(file)) { + try (DataOutputStream out = new DataOutputStream(f)) { + out.writeInt(2); // version + out.writeInt(envelope.getType()); + out.writeUTF(envelope.getSource()); + out.writeInt(envelope.getSourceDevice()); + out.writeLong(envelope.getTimestamp()); + if (envelope.hasContent()) { + out.writeInt(envelope.getContent().length); + out.write(envelope.getContent()); + } else { + out.writeInt(0); + } + if (envelope.hasLegacyMessage()) { + out.writeInt(envelope.getLegacyMessage().length); + out.write(envelope.getLegacyMessage()); + } else { + out.writeInt(0); + } + out.writeLong(envelope.getServerTimestamp()); + String uuid = envelope.getUuid(); + out.writeUTF(uuid == null ? "" : uuid); + } + } + } + + static File retrieveAttachment(SignalServiceAttachmentStream stream, File outputFile) throws IOException { + InputStream input = stream.getInputStream(); + + try (OutputStream output = new FileOutputStream(outputFile)) { + byte[] buffer = new byte[4096]; + int read; + + while ((read = input.read(buffer)) != -1) { + output.write(buffer, 0, read); + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + return null; + } + return outputFile; + } + + static String computeSafetyNumber(String ownUsername, IdentityKey ownIdentityKey, String theirUsername, IdentityKey theirIdentityKey) { + Fingerprint fingerprint = new NumericFingerprintGenerator(5200).createFor(ownUsername, ownIdentityKey, theirUsername, theirIdentityKey); + return fingerprint.getDisplayableFingerprint().getDisplayText(); + } + + static class DeviceLinkInfo { + + String deviceIdentifier; + ECPublicKey deviceKey; + + DeviceLinkInfo(final String deviceIdentifier, final ECPublicKey deviceKey) { + this.deviceIdentifier = deviceIdentifier; + this.deviceKey = deviceKey; + } + } +} diff --git a/src/main/java/org/asamk/signal/util/Util.java b/src/main/java/org/asamk/signal/util/Util.java index 93a595d1..5a1dcdda 100644 --- a/src/main/java/org/asamk/signal/util/Util.java +++ b/src/main/java/org/asamk/signal/util/Util.java @@ -3,10 +3,6 @@ package org.asamk.signal.util; import com.fasterxml.jackson.databind.JsonNode; import java.io.InvalidObjectException; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.util.HashMap; -import java.util.Map; public class Util { @@ -23,28 +19,6 @@ public class Util { return f.toString(); } - public static Map getQueryMap(String query) { - String[] params = query.split("&"); - Map map = new HashMap<>(); - for (String param : params) { - String name = null; - final String[] paramParts = param.split("="); - try { - name = URLDecoder.decode(paramParts[0], "utf-8"); - } catch (UnsupportedEncodingException e) { - // Impossible - } - String value = null; - try { - value = URLDecoder.decode(paramParts[1], "utf-8"); - } catch (UnsupportedEncodingException e) { - // Impossible - } - map.put(name, value); - } - return map; - } - public static String join(CharSequence separator, Iterable list) { StringBuilder buf = new StringBuilder(); for (CharSequence str : list) {