X-Git-Url: https://git.nmode.ca/signal-cli/blobdiff_plain/8163a42d3afa269242537227be2a0fe87633ea1f..591c0fe8a3744608575a6dcb1f6f4f9f818948d2:/src/main/java/org/asamk/signal/manager/Utils.java diff --git a/src/main/java/org/asamk/signal/manager/Utils.java b/src/main/java/org/asamk/signal/manager/Utils.java index a5b37b05..0a815ea9 100644 --- a/src/main/java/org/asamk/signal/manager/Utils.java +++ b/src/main/java/org/asamk/signal/manager/Utils.java @@ -1,6 +1,5 @@ package org.asamk.signal.manager; -import org.asamk.signal.AttachmentInvalidException; import org.signal.libsignal.metadata.certificate.CertificateValidator; import org.whispersystems.libsignal.IdentityKey; import org.whispersystems.libsignal.InvalidKeyException; @@ -28,11 +27,11 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URLConnection; import java.net.URLDecoder; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.ArrayList; import java.util.HashMap; @@ -59,7 +58,7 @@ class Utils { return signalServiceAttachments; } - private static String getFileMimeType(File file) throws IOException { + static String getFileMimeType(File file, String defaultMimeType) throws IOException { String mime = Files.probeContentType(file.toPath()); if (mime == null) { try (InputStream bufferedStream = new BufferedInputStream(new FileInputStream(file))) { @@ -67,7 +66,7 @@ class Utils { } } if (mime == null) { - mime = "application/octet-stream"; + return defaultMimeType; } return mime; } @@ -75,14 +74,28 @@ class Utils { static SignalServiceAttachmentStream createAttachment(File attachmentFile) throws IOException { InputStream attachmentStream = new FileInputStream(attachmentFile); final long attachmentSize = attachmentFile.length(); - final String mime = getFileMimeType(attachmentFile); - // TODO mabybe add a parameter to set the voiceNote, preview, width, height and caption option + final String mime = getFileMimeType(attachmentFile, "application/octet-stream"); + // TODO mabybe add a parameter to set the voiceNote, borderless, preview, width, height and caption option final long uploadTimestamp = System.currentTimeMillis(); Optional preview = Optional.absent(); Optional caption = Optional.absent(); Optional blurHash = Optional.absent(); final Optional resumableUploadSpec = Optional.absent(); - return new SignalServiceAttachmentStream(attachmentStream, mime, attachmentSize, Optional.of(attachmentFile.getName()), false, preview, 0, 0, uploadTimestamp, caption, blurHash, null, null, resumableUploadSpec); + return new SignalServiceAttachmentStream(attachmentStream, + mime, + attachmentSize, + Optional.of(attachmentFile.getName()), + false, + false, + preview, + 0, + 0, + uploadTimestamp, + caption, + blurHash, + null, + null, + resumableUploadSpec); } static StreamDetails createStreamDetailsFromFile(File file) throws IOException { @@ -97,7 +110,8 @@ class Utils { static CertificateValidator getCertificateValidator() { try { - ECPublicKey unidentifiedSenderTrustRoot = Curve.decodePoint(Base64.decode(ServiceConfig.UNIDENTIFIED_SENDER_TRUST_ROOT), 0); + ECPublicKey unidentifiedSenderTrustRoot = Curve.decodePoint(Base64.decode(ServiceConfig.UNIDENTIFIED_SENDER_TRUST_ROOT), + 0); return new CertificateValidator(unidentifiedSenderTrustRoot); } catch (InvalidKeyException | IOException e) { throw new AssertionError(e); @@ -108,31 +122,20 @@ class Utils { 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 - } + String name = URLDecoder.decode(paramParts[0], StandardCharsets.UTF_8); + String value = URLDecoder.decode(paramParts[1], StandardCharsets.UTF_8); 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; - } + return "tsdevice:/?uuid=" + + URLEncoder.encode(info.deviceIdentifier, StandardCharsets.UTF_8) + + "&pub_key=" + + URLEncoder.encode(Base64.encodeBytesWithoutPadding(info.deviceKey.serialize()), + StandardCharsets.UTF_8); } static DeviceLinkInfo parseDeviceLinkUri(URI linkUri) throws IOException, InvalidKeyException { @@ -153,7 +156,7 @@ class Utils { try (FileInputStream f = new FileInputStream(file)) { DataInputStream in = new DataInputStream(f); int version = in.readInt(); - if (version > 3) { + if (version > 4) { return null; } int type = in.readInt(); @@ -180,26 +183,38 @@ class Utils { legacyMessage = new byte[legacyMessageLen]; in.readFully(legacyMessage); } - long serverTimestamp = 0; + long serverReceivedTimestamp = 0; String uuid = null; - if (version == 2) { - serverTimestamp = in.readLong(); + if (version >= 2) { + serverReceivedTimestamp = in.readLong(); uuid = in.readUTF(); if ("".equals(uuid)) { uuid = null; } } + long serverDeliveredTimestamp = 0; + if (version >= 4) { + serverDeliveredTimestamp = in.readLong(); + } Optional addressOptional = sourceUuid == null && source.isEmpty() ? Optional.absent() : Optional.of(new SignalServiceAddress(sourceUuid, source)); - return new SignalServiceEnvelope(type, addressOptional, sourceDevice, timestamp, legacyMessage, content, serverTimestamp, uuid); + return new SignalServiceEnvelope(type, + addressOptional, + sourceDevice, + timestamp, + legacyMessage, + content, + serverReceivedTimestamp, + serverDeliveredTimestamp, + uuid); } } static void storeEnvelope(SignalServiceEnvelope envelope, File file) throws IOException { try (FileOutputStream f = new FileOutputStream(file)) { try (DataOutputStream out = new DataOutputStream(f)) { - out.writeInt(3); // version + out.writeInt(4); // version out.writeInt(envelope.getType()); out.writeUTF(envelope.getSourceE164().isPresent() ? envelope.getSourceE164().get() : ""); out.writeUTF(envelope.getSourceUuid().isPresent() ? envelope.getSourceUuid().get() : ""); @@ -217,9 +232,10 @@ class Utils { } else { out.writeInt(0); } - out.writeLong(envelope.getServerTimestamp()); + out.writeLong(envelope.getServerReceivedTimestamp()); String uuid = envelope.getUuid(); out.writeUTF(uuid == null ? "" : uuid); + out.writeLong(envelope.getServerDeliveredTimestamp()); } } } @@ -241,13 +257,18 @@ class Utils { return outputFile; } - static String computeSafetyNumber(SignalServiceAddress ownAddress, IdentityKey ownIdentityKey, SignalServiceAddress theirAddress, IdentityKey theirIdentityKey) { + static String computeSafetyNumber( + SignalServiceAddress ownAddress, + IdentityKey ownIdentityKey, + SignalServiceAddress theirAddress, + IdentityKey theirIdentityKey + ) { int version; byte[] ownId; byte[] theirId; - if (ServiceConfig.capabilities.isUuid() - && ownAddress.getUuid().isPresent() && theirAddress.getUuid().isPresent()) { + if (ServiceConfig.capabilities.isUuid() && ownAddress.getUuid().isPresent() && theirAddress.getUuid() + .isPresent()) { // Version 2: UUID user version = 2; ownId = UuidUtil.toByteArray(ownAddress.getUuid().get()); @@ -262,7 +283,11 @@ class Utils { theirId = theirAddress.getNumber().get().getBytes(); } - Fingerprint fingerprint = new NumericFingerprintGenerator(5200).createFor(version, ownId, ownIdentityKey, theirId, theirIdentityKey); + Fingerprint fingerprint = new NumericFingerprintGenerator(5200).createFor(version, + ownId, + ownIdentityKey, + theirId, + theirIdentityKey); return fingerprint.getDisplayableFingerprint().getDisplayText(); }