From: AsamK Date: Tue, 29 Dec 2020 21:48:39 +0000 (+0100) Subject: Cleanup utils X-Git-Tag: v0.7.2~4 X-Git-Url: https://git.nmode.ca/signal-cli/commitdiff_plain/bbdd6a89102f200f284a01a41ac2809c0759ae50 Cleanup utils --- diff --git a/src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java b/src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java new file mode 100644 index 00000000..5b9fbe28 --- /dev/null +++ b/src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java @@ -0,0 +1,60 @@ +package org.asamk.signal.manager; + +import org.whispersystems.libsignal.InvalidKeyException; +import org.whispersystems.libsignal.ecc.Curve; +import org.whispersystems.libsignal.ecc.ECPublicKey; +import org.whispersystems.util.Base64; + +import java.io.IOException; +import java.net.URI; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +import static org.whispersystems.signalservice.internal.util.Util.isEmpty; + +public class DeviceLinkInfo { + + final String deviceIdentifier; + final ECPublicKey deviceKey; + + public 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 (isEmpty(deviceIdentifier) || isEmpty(publicKeyEncoded)) { + throw new RuntimeException("Invalid device link uri"); + } + + ECPublicKey deviceKey = Curve.decodePoint(Base64.decode(publicKeyEncoded), 0); + + return new DeviceLinkInfo(deviceIdentifier, deviceKey); + } + + private static Map getQueryMap(String query) { + String[] params = query.split("&"); + Map map = new HashMap<>(); + for (String param : params) { + final String[] paramParts = param.split("="); + String name = URLDecoder.decode(paramParts[0], StandardCharsets.UTF_8); + String value = URLDecoder.decode(paramParts[1], StandardCharsets.UTF_8); + map.put(name, value); + } + return map; + } + + public DeviceLinkInfo(final String deviceIdentifier, final ECPublicKey deviceKey) { + this.deviceIdentifier = deviceIdentifier; + this.deviceKey = deviceKey; + } + + public String createDeviceLinkUri() { + return "tsdevice:/?uuid=" + + URLEncoder.encode(deviceIdentifier, StandardCharsets.UTF_8) + + "&pub_key=" + + URLEncoder.encode(Base64.encodeBytesWithoutPadding(deviceKey.serialize()), StandardCharsets.UTF_8); + } +} diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index e00f44ac..5ed8fc4e 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -37,8 +37,11 @@ import org.asamk.signal.manager.storage.profiles.SignalProfile; import org.asamk.signal.manager.storage.profiles.SignalProfileEntry; import org.asamk.signal.manager.storage.protocol.IdentityInfo; import org.asamk.signal.manager.storage.stickers.Sticker; -import org.asamk.signal.util.IOUtils; -import org.asamk.signal.util.Util; +import org.asamk.signal.manager.util.AttachmentUtils; +import org.asamk.signal.manager.util.IOUtils; +import org.asamk.signal.manager.util.KeyUtils; +import org.asamk.signal.manager.util.MessageCacheUtils; +import org.asamk.signal.manager.util.Utils; import org.signal.libsignal.metadata.InvalidMetadataMessageException; import org.signal.libsignal.metadata.InvalidMetadataVersionException; import org.signal.libsignal.metadata.ProtocolDuplicateMessageException; @@ -50,6 +53,7 @@ import org.signal.libsignal.metadata.ProtocolLegacyMessageException; import org.signal.libsignal.metadata.ProtocolNoSessionException; import org.signal.libsignal.metadata.ProtocolUntrustedIdentityException; import org.signal.libsignal.metadata.SelfSendException; +import org.signal.libsignal.metadata.certificate.CertificateValidator; import org.signal.storageservice.protos.groups.GroupChange; import org.signal.storageservice.protos.groups.local.DecryptedGroup; import org.signal.storageservice.protos.groups.local.DecryptedGroupJoinInfo; @@ -125,6 +129,7 @@ import org.whispersystems.signalservice.api.push.ContactTokenDetails; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.MissingConfigurationException; 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.StreamDetails; import org.whispersystems.signalservice.api.util.UptimeSleepTimer; @@ -185,6 +190,7 @@ public class Manager implements Closeable { final static Logger logger = LoggerFactory.getLogger(Manager.class); private final SleepTimer timer = new UptimeSleepTimer(); + private final CertificateValidator certificateValidator = new CertificateValidator(ServiceConfig.getUnidentifiedSenderTrustRoot()); private final SignalServiceConfiguration serviceConfiguration; private final String userAgent; @@ -419,7 +425,7 @@ public class Manager implements Closeable { } public void addDeviceLink(URI linkUri) throws IOException, InvalidKeyException { - Utils.DeviceLinkInfo info = Utils.parseDeviceLinkUri(linkUri); + DeviceLinkInfo info = DeviceLinkInfo.parseDeviceLinkUri(linkUri); addDevice(info.deviceIdentifier, info.deviceKey); } @@ -696,7 +702,7 @@ public class Manager implements Closeable { return Optional.absent(); } - return Optional.of(Utils.createAttachment(file)); + return Optional.of(AttachmentUtils.createAttachment(file)); } private Optional createContactAvatarAttachment(String number) throws IOException { @@ -705,7 +711,7 @@ public class Manager implements Closeable { return Optional.absent(); } - return Optional.of(Utils.createAttachment(file)); + return Optional.of(AttachmentUtils.createAttachment(file)); } private GroupInfo getGroupForSending(GroupId groupId) throws GroupNotFoundException, NotAGroupMemberException { @@ -751,7 +757,7 @@ public class Manager implements Closeable { final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder() .withBody(messageText); if (attachments != null) { - messageBuilder.withAttachments(Utils.getSignalServiceAttachments(attachments)); + messageBuilder.withAttachments(AttachmentUtils.getSignalServiceAttachments(attachments)); } return sendGroupMessage(messageBuilder, groupId); @@ -928,7 +934,7 @@ public class Manager implements Closeable { newE164Members.remove(contact.getNumber()); } throw new IOException("Failed to add members " - + Util.join(", ", newE164Members) + + String.join(", ", newE164Members) + " to group: Not registered on Signal"); } @@ -971,7 +977,7 @@ public class Manager implements Closeable { File aFile = getGroupAvatarFile(g.getGroupId()); if (aFile.exists()) { try { - group.withAvatar(Utils.createAttachment(aFile)); + group.withAvatar(AttachmentUtils.createAttachment(aFile)); } catch (IOException e) { throw new AttachmentInvalidException(aFile.toString(), e); } @@ -1022,7 +1028,7 @@ public class Manager implements Closeable { final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder() .withBody(messageText); if (attachments != null) { - List attachmentStreams = Utils.getSignalServiceAttachments(attachments); + List attachmentStreams = AttachmentUtils.getSignalServiceAttachments(attachments); // Upload attachments here, so we only upload once even for multiple recipients SignalServiceMessageSender messageSender = createMessageSender(); @@ -1510,7 +1516,7 @@ public class Manager implements Closeable { private SignalServiceContent decryptMessage(SignalServiceEnvelope envelope) throws InvalidMetadataMessageException, ProtocolInvalidMessageException, ProtocolDuplicateMessageException, ProtocolLegacyMessageException, ProtocolInvalidKeyIdException, InvalidMetadataVersionException, ProtocolInvalidVersionException, ProtocolNoSessionException, ProtocolInvalidKeyException, SelfSendException, UnsupportedDataMessageException, org.whispersystems.libsignal.UntrustedIdentityException { SignalServiceCipher cipher = new SignalServiceCipher(account.getSelfAddress(), account.getSignalProtocolStore(), - Utils.getCertificateValidator()); + certificateValidator); try { return cipher.decrypt(envelope); } catch (ProtocolUntrustedIdentityException e) { @@ -1820,7 +1826,7 @@ public class Manager implements Closeable { ) { SignalServiceEnvelope envelope; try { - envelope = Utils.loadEnvelope(fileEntry); + envelope = MessageCacheUtils.loadEnvelope(fileEntry); if (envelope == null) { return; } @@ -1887,7 +1893,7 @@ public class Manager implements Closeable { try { String source = envelope1.getSourceE164().isPresent() ? envelope1.getSourceE164().get() : ""; File cacheFile = getMessageCacheFile(source, now, envelope1.getTimestamp()); - Utils.storeEnvelope(envelope1, cacheFile); + MessageCacheUtils.storeEnvelope(envelope1, cacheFile); } catch (IOException e) { logger.warn("Failed to store encrypted message in disk cache, ignoring: {}", e.getMessage()); } @@ -2240,7 +2246,7 @@ public class Manager implements Closeable { return retrieveAttachment(pointer, getContactAvatarFile(number), false); } else { SignalServiceAttachmentStream stream = attachment.asStream(); - return Utils.retrieveAttachment(stream, getContactAvatarFile(number)); + return AttachmentUtils.retrieveAttachment(stream, getContactAvatarFile(number)); } } @@ -2257,7 +2263,7 @@ public class Manager implements Closeable { return retrieveAttachment(pointer, getGroupAvatarFile(groupId), false); } else { SignalServiceAttachmentStream stream = attachment.asStream(); - return Utils.retrieveAttachment(stream, getGroupAvatarFile(groupId)); + return AttachmentUtils.retrieveAttachment(stream, getGroupAvatarFile(groupId)); } } @@ -2509,7 +2515,7 @@ public class Manager implements Closeable { } public ContactInfo getContact(String number) { - return account.getContactStore().getContact(Util.getSignalServiceAddressFromIdentifier(number)); + return account.getContactStore().getContact(Utils.getSignalServiceAddressFromIdentifier(number)); } public GroupInfo getGroup(GroupId groupId) { @@ -2613,7 +2619,8 @@ public class Manager implements Closeable { public String computeSafetyNumber( SignalServiceAddress theirAddress, IdentityKey theirIdentityKey ) { - return Utils.computeSafetyNumber(account.getSelfAddress(), + return Utils.computeSafetyNumber(ServiceConfig.capabilities.isUuid(), + account.getSelfAddress(), getIdentityKeyPair().getPublicKey(), theirAddress, theirIdentityKey); @@ -2626,12 +2633,12 @@ public class Manager implements Closeable { public SignalServiceAddress canonicalizeAndResolveSignalServiceAddress(String identifier) throws InvalidNumberException { String canonicalizedNumber = UuidUtil.isUuid(identifier) ? identifier - : Util.canonicalizeNumber(identifier, account.getUsername()); + : PhoneNumberFormatter.formatNumber(identifier, account.getUsername()); return resolveSignalServiceAddress(canonicalizedNumber); } public SignalServiceAddress resolveSignalServiceAddress(String identifier) { - SignalServiceAddress address = Util.getSignalServiceAddressFromIdentifier(identifier); + SignalServiceAddress address = Utils.getSignalServiceAddressFromIdentifier(identifier); return resolveSignalServiceAddress(address); } diff --git a/src/main/java/org/asamk/signal/manager/ProvisioningManager.java b/src/main/java/org/asamk/signal/manager/ProvisioningManager.java index 95e92c7a..8b3f0eb4 100644 --- a/src/main/java/org/asamk/signal/manager/ProvisioningManager.java +++ b/src/main/java/org/asamk/signal/manager/ProvisioningManager.java @@ -17,6 +17,7 @@ package org.asamk.signal.manager; import org.asamk.signal.manager.storage.SignalAccount; +import org.asamk.signal.manager.util.KeyUtils; import org.signal.zkgroup.InvalidInputException; import org.signal.zkgroup.profiles.ProfileKey; import org.whispersystems.libsignal.IdentityKeyPair; @@ -71,8 +72,7 @@ public class ProvisioningManager { public String getDeviceLinkUri() throws TimeoutException, IOException { String deviceUuid = accountManager.getNewDeviceUuid(); - return Utils.createDeviceLinkUri(new Utils.DeviceLinkInfo(deviceUuid, - identityKey.getPublicKey().getPublicKey())); + return new DeviceLinkInfo(deviceUuid, identityKey.getPublicKey().getPublicKey()).createDeviceLinkUri(); } public String finishDeviceLink(String deviceName) throws IOException, InvalidKeyException, TimeoutException, UserAlreadyExists { diff --git a/src/main/java/org/asamk/signal/manager/ServiceConfig.java b/src/main/java/org/asamk/signal/manager/ServiceConfig.java index 353670ae..939d5b5b 100644 --- a/src/main/java/org/asamk/signal/manager/ServiceConfig.java +++ b/src/main/java/org/asamk/signal/manager/ServiceConfig.java @@ -1,6 +1,9 @@ package org.asamk.signal.manager; import org.signal.zkgroup.ServerPublicParams; +import org.whispersystems.libsignal.InvalidKeyException; +import org.whispersystems.libsignal.ecc.Curve; +import org.whispersystems.libsignal.ecc.ECPublicKey; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.account.AccountAttributes; import org.whispersystems.signalservice.api.push.TrustStore; @@ -106,6 +109,14 @@ public class ServiceConfig { } } + static ECPublicKey getUnidentifiedSenderTrustRoot() { + try { + return Curve.decodePoint(Base64.decode(UNIDENTIFIED_SENDER_TRUST_ROOT), 0); + } catch (InvalidKeyException | IOException e) { + throw new AssertionError(e); + } + } + private static Map makeSignalCdnUrlMapFor( SignalCdnUrl[] cdn0Urls, SignalCdnUrl[] cdn2Urls ) { diff --git a/src/main/java/org/asamk/signal/manager/Utils.java b/src/main/java/org/asamk/signal/manager/Utils.java deleted file mode 100644 index 0a815ea9..00000000 --- a/src/main/java/org/asamk/signal/manager/Utils.java +++ /dev/null @@ -1,304 +0,0 @@ -package org.asamk.signal.manager; - -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.StreamDetails; -import org.whispersystems.signalservice.api.util.UuidUtil; -import org.whispersystems.signalservice.internal.push.http.ResumableUploadSpec; -import org.whispersystems.util.Base64; - -import java.io.BufferedInputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -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; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import static org.whispersystems.signalservice.internal.util.Util.isEmpty; - -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 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))) { - mime = URLConnection.guessContentTypeFromStream(bufferedStream); - } - } - if (mime == null) { - return defaultMimeType; - } - return mime; - } - - static SignalServiceAttachmentStream createAttachment(File attachmentFile) throws IOException { - InputStream attachmentStream = new FileInputStream(attachmentFile); - final long attachmentSize = attachmentFile.length(); - 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, - false, - preview, - 0, - 0, - uploadTimestamp, - caption, - blurHash, - null, - null, - resumableUploadSpec); - } - - static StreamDetails createStreamDetailsFromFile(File file) throws IOException { - InputStream stream = new FileInputStream(file); - final long size = file.length(); - String mime = Files.probeContentType(file.toPath()); - if (mime == null) { - mime = "application/octet-stream"; - } - return new StreamDetails(stream, mime, size); - } - - static CertificateValidator getCertificateValidator() { - try { - ECPublicKey unidentifiedSenderTrustRoot = Curve.decodePoint(Base64.decode(ServiceConfig.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) { - final String[] paramParts = param.split("="); - 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) { - 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 { - Map query = getQueryMap(linkUri.getRawQuery()); - String deviceIdentifier = query.get("uuid"); - String publicKeyEncoded = query.get("pub_key"); - - if (isEmpty(deviceIdentifier) || isEmpty(publicKeyEncoded)) { - throw new RuntimeException("Invalid device link uri"); - } - - ECPublicKey deviceKey = Curve.decodePoint(Base64.decode(publicKeyEncoded), 0); - - return new DeviceLinkInfo(deviceIdentifier, deviceKey); - } - - static SignalServiceEnvelope loadEnvelope(File file) throws IOException { - try (FileInputStream f = new FileInputStream(file)) { - DataInputStream in = new DataInputStream(f); - int version = in.readInt(); - if (version > 4) { - return null; - } - int type = in.readInt(); - String source = in.readUTF(); - UUID sourceUuid = null; - if (version >= 3) { - sourceUuid = UuidUtil.parseOrNull(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 serverReceivedTimestamp = 0; - String uuid = null; - 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, - 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(4); // version - out.writeInt(envelope.getType()); - out.writeUTF(envelope.getSourceE164().isPresent() ? envelope.getSourceE164().get() : ""); - out.writeUTF(envelope.getSourceUuid().isPresent() ? envelope.getSourceUuid().get() : ""); - 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.getServerReceivedTimestamp()); - String uuid = envelope.getUuid(); - out.writeUTF(uuid == null ? "" : uuid); - out.writeLong(envelope.getServerDeliveredTimestamp()); - } - } - } - - 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( - SignalServiceAddress ownAddress, - IdentityKey ownIdentityKey, - SignalServiceAddress theirAddress, - IdentityKey theirIdentityKey - ) { - int version; - byte[] ownId; - byte[] theirId; - - if (ServiceConfig.capabilities.isUuid() && ownAddress.getUuid().isPresent() && theirAddress.getUuid() - .isPresent()) { - // Version 2: UUID user - version = 2; - ownId = UuidUtil.toByteArray(ownAddress.getUuid().get()); - theirId = UuidUtil.toByteArray(theirAddress.getUuid().get()); - } else { - // Version 1: E164 user - version = 1; - if (!ownAddress.getNumber().isPresent() || !theirAddress.getNumber().isPresent()) { - return "INVALID ID"; - } - ownId = ownAddress.getNumber().get().getBytes(); - theirId = theirAddress.getNumber().get().getBytes(); - } - - Fingerprint fingerprint = new NumericFingerprintGenerator(5200).createFor(version, - ownId, - ownIdentityKey, - theirId, - theirIdentityKey); - return fingerprint.getDisplayableFingerprint().getDisplayText(); - } - - static class DeviceLinkInfo { - - final String deviceIdentifier; - final ECPublicKey deviceKey; - - DeviceLinkInfo(final String deviceIdentifier, final ECPublicKey deviceKey) { - this.deviceIdentifier = deviceIdentifier; - this.deviceKey = deviceKey; - } - } -} diff --git a/src/main/java/org/asamk/signal/manager/groups/GroupIdV1.java b/src/main/java/org/asamk/signal/manager/groups/GroupIdV1.java index d865356e..237a34b6 100644 --- a/src/main/java/org/asamk/signal/manager/groups/GroupIdV1.java +++ b/src/main/java/org/asamk/signal/manager/groups/GroupIdV1.java @@ -1,6 +1,6 @@ package org.asamk.signal.manager.groups; -import static org.asamk.signal.manager.KeyUtils.getSecretBytes; +import static org.asamk.signal.manager.util.KeyUtils.getSecretBytes; public class GroupIdV1 extends GroupId { diff --git a/src/main/java/org/asamk/signal/manager/groups/GroupLinkPassword.java b/src/main/java/org/asamk/signal/manager/groups/GroupLinkPassword.java index 41be672a..7edc7afb 100644 --- a/src/main/java/org/asamk/signal/manager/groups/GroupLinkPassword.java +++ b/src/main/java/org/asamk/signal/manager/groups/GroupLinkPassword.java @@ -1,6 +1,6 @@ package org.asamk.signal.manager.groups; -import org.asamk.signal.manager.KeyUtils; +import org.asamk.signal.manager.util.KeyUtils; import java.util.Arrays; diff --git a/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java b/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java index 394eba57..8a2320e0 100644 --- a/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java +++ b/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java @@ -7,7 +7,7 @@ import org.asamk.signal.manager.groups.GroupLinkPassword; import org.asamk.signal.manager.groups.GroupUtils; import org.asamk.signal.manager.storage.groups.GroupInfoV2; import org.asamk.signal.manager.storage.profiles.SignalProfile; -import org.asamk.signal.util.IOUtils; +import org.asamk.signal.manager.util.IOUtils; import org.signal.storageservice.protos.groups.AccessControl; import org.signal.storageservice.protos.groups.GroupChange; import org.signal.storageservice.protos.groups.Member; diff --git a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java index c3573209..c787471f 100644 --- a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java +++ b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java @@ -25,8 +25,8 @@ import org.asamk.signal.manager.storage.protocol.SignalServiceAddressResolver; import org.asamk.signal.manager.storage.stickers.StickerStore; import org.asamk.signal.manager.storage.threads.LegacyJsonThreadStore; import org.asamk.signal.manager.storage.threads.ThreadInfo; -import org.asamk.signal.util.IOUtils; -import org.asamk.signal.util.Util; +import org.asamk.signal.manager.util.IOUtils; +import org.asamk.signal.manager.util.Utils; import org.signal.zkgroup.InvalidInputException; import org.signal.zkgroup.profiles.ProfileKey; import org.slf4j.Logger; @@ -211,28 +211,28 @@ public class SignalAccount implements Closeable { deviceId = node.asInt(); } if (rootNode.has("isMultiDevice")) { - isMultiDevice = Util.getNotNullNode(rootNode, "isMultiDevice").asBoolean(); + isMultiDevice = Utils.getNotNullNode(rootNode, "isMultiDevice").asBoolean(); } - username = Util.getNotNullNode(rootNode, "username").asText(); - password = Util.getNotNullNode(rootNode, "password").asText(); + username = Utils.getNotNullNode(rootNode, "username").asText(); + password = Utils.getNotNullNode(rootNode, "password").asText(); JsonNode pinNode = rootNode.get("registrationLockPin"); registrationLockPin = pinNode == null || pinNode.isNull() ? null : pinNode.asText(); if (rootNode.has("signalingKey")) { - signalingKey = Util.getNotNullNode(rootNode, "signalingKey").asText(); + signalingKey = Utils.getNotNullNode(rootNode, "signalingKey").asText(); } if (rootNode.has("preKeyIdOffset")) { - preKeyIdOffset = Util.getNotNullNode(rootNode, "preKeyIdOffset").asInt(0); + preKeyIdOffset = Utils.getNotNullNode(rootNode, "preKeyIdOffset").asInt(0); } else { preKeyIdOffset = 0; } if (rootNode.has("nextSignedPreKeyId")) { - nextSignedPreKeyId = Util.getNotNullNode(rootNode, "nextSignedPreKeyId").asInt(); + nextSignedPreKeyId = Utils.getNotNullNode(rootNode, "nextSignedPreKeyId").asInt(); } else { nextSignedPreKeyId = 0; } if (rootNode.has("profileKey")) { try { - profileKey = new ProfileKey(Base64.decode(Util.getNotNullNode(rootNode, "profileKey").asText())); + profileKey = new ProfileKey(Base64.decode(Utils.getNotNullNode(rootNode, "profileKey").asText())); } catch (InvalidInputException e) { throw new IOException( "Config file contains an invalid profileKey, needs to be base64 encoded array of 32 bytes", @@ -240,9 +240,9 @@ public class SignalAccount implements Closeable { } } - signalProtocolStore = jsonProcessor.convertValue(Util.getNotNullNode(rootNode, "axolotlStore"), + signalProtocolStore = jsonProcessor.convertValue(Utils.getNotNullNode(rootNode, "axolotlStore"), JsonSignalProtocolStore.class); - registered = Util.getNotNullNode(rootNode, "registered").asBoolean(); + registered = Utils.getNotNullNode(rootNode, "registered").asBoolean(); JsonNode groupStoreNode = rootNode.get("groupStore"); if (groupStoreNode != null) { groupStore = jsonProcessor.convertValue(groupStoreNode, JsonGroupStore.class); diff --git a/src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java b/src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java index 2b4dbcf5..fdcd28a3 100644 --- a/src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java @@ -16,8 +16,8 @@ import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupIdV1; import org.asamk.signal.manager.groups.GroupIdV2; import org.asamk.signal.manager.groups.GroupUtils; +import org.asamk.signal.manager.util.IOUtils; import org.asamk.signal.util.Hex; -import org.asamk.signal.util.IOUtils; import org.signal.storageservice.protos.groups.local.DecryptedGroup; import org.signal.zkgroup.InvalidInputException; import org.signal.zkgroup.groups.GroupMasterKey; diff --git a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java index 517b384e..5bc1c11f 100644 --- a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java @@ -9,7 +9,7 @@ import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import org.asamk.signal.manager.TrustLevel; -import org.asamk.signal.util.Util; +import org.asamk.signal.manager.util.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.libsignal.IdentityKey; @@ -51,7 +51,7 @@ public class JsonIdentityKeyStore implements IdentityKeyStore { if (resolver != null) { return resolver.resolveSignalServiceAddress(identifier); } else { - return Util.getSignalServiceAddressFromIdentifier(identifier); + return Utils.getSignalServiceAddressFromIdentifier(identifier); } } @@ -213,7 +213,7 @@ public class JsonIdentityKeyStore implements IdentityKeyStore { UUID uuid = trustedKey.hasNonNull("uuid") ? UuidUtil.parseOrNull(trustedKey.get("uuid") .asText()) : null; final SignalServiceAddress serviceAddress = uuid == null - ? Util.getSignalServiceAddressFromIdentifier(trustedKeyName) + ? Utils.getSignalServiceAddressFromIdentifier(trustedKeyName) : new SignalServiceAddress(uuid, trustedKeyName); try { IdentityKey id = new IdentityKey(Base64.decode(trustedKey.get("identityKey").asText()), 0); diff --git a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java index f55aff14..6e300214 100644 --- a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java @@ -8,7 +8,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; -import org.asamk.signal.util.Util; +import org.asamk.signal.manager.util.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.libsignal.SignalProtocolAddress; @@ -43,7 +43,7 @@ class JsonSessionStore implements SessionStore { if (resolver != null) { return resolver.resolveSignalServiceAddress(identifier); } else { - return Util.getSignalServiceAddressFromIdentifier(identifier); + return Utils.getSignalServiceAddressFromIdentifier(identifier); } } @@ -147,7 +147,7 @@ class JsonSessionStore implements SessionStore { UUID uuid = session.hasNonNull("uuid") ? UuidUtil.parseOrNull(session.get("uuid").asText()) : null; final SignalServiceAddress serviceAddress = uuid == null - ? Util.getSignalServiceAddressFromIdentifier(sessionName) + ? Utils.getSignalServiceAddressFromIdentifier(sessionName) : new SignalServiceAddress(uuid, sessionName); final int deviceId = session.get("deviceId").asInt(); final String record = session.get("record").asText(); diff --git a/src/main/java/org/asamk/signal/manager/util/AttachmentUtils.java b/src/main/java/org/asamk/signal/manager/util/AttachmentUtils.java new file mode 100644 index 00000000..b9a97073 --- /dev/null +++ b/src/main/java/org/asamk/signal/manager/util/AttachmentUtils.java @@ -0,0 +1,79 @@ +package org.asamk.signal.manager.util; + +import org.asamk.signal.manager.AttachmentInvalidException; +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.internal.push.http.ResumableUploadSpec; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +public class AttachmentUtils { + + public 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; + } + + public static SignalServiceAttachmentStream createAttachment(File attachmentFile) throws IOException { + InputStream attachmentStream = new FileInputStream(attachmentFile); + final long attachmentSize = attachmentFile.length(); + final String mime = Utils.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, + false, + preview, + 0, + 0, + uploadTimestamp, + caption, + blurHash, + null, + null, + resumableUploadSpec); + } + + public 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; + } +} diff --git a/src/main/java/org/asamk/signal/manager/util/IOUtils.java b/src/main/java/org/asamk/signal/manager/util/IOUtils.java new file mode 100644 index 00000000..06f8aa22 --- /dev/null +++ b/src/main/java/org/asamk/signal/manager/util/IOUtils.java @@ -0,0 +1,72 @@ +package org.asamk.signal.manager.util; + +import org.whispersystems.signalservice.internal.util.Util; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.PosixFilePermissions; +import java.util.EnumSet; +import java.util.Set; + +import static java.nio.file.attribute.PosixFilePermission.OWNER_EXECUTE; +import static java.nio.file.attribute.PosixFilePermission.OWNER_READ; +import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE; + +public class IOUtils { + + public static File createTempFile() throws IOException { + return File.createTempFile("signal_tmp_", ".tmp"); + } + + public static byte[] readFully(InputStream in) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Util.copy(in, baos); + return baos.toByteArray(); + } + + public static void createPrivateDirectories(File file) throws IOException { + if (file.exists()) { + return; + } + + final Path path = file.toPath(); + try { + Set perms = EnumSet.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE); + Files.createDirectories(path, PosixFilePermissions.asFileAttribute(perms)); + } catch (UnsupportedOperationException e) { + Files.createDirectories(path); + } + } + + public static void createPrivateFile(File path) throws IOException { + final Path file = path.toPath(); + try { + Set perms = EnumSet.of(OWNER_READ, OWNER_WRITE); + Files.createFile(file, PosixFilePermissions.asFileAttribute(perms)); + } catch (UnsupportedOperationException e) { + Files.createFile(file); + } + } + + public static void copyStreamToFile(InputStream input, File outputFile) throws IOException { + copyStreamToFile(input, outputFile, 8192); + } + + public static void copyStreamToFile(InputStream input, File outputFile, int bufferSize) throws IOException { + try (OutputStream output = new FileOutputStream(outputFile)) { + byte[] buffer = new byte[bufferSize]; + int read; + + while ((read = input.read(buffer)) != -1) { + output.write(buffer, 0, read); + } + } + } +} diff --git a/src/main/java/org/asamk/signal/manager/KeyUtils.java b/src/main/java/org/asamk/signal/manager/util/KeyUtils.java similarity index 79% rename from src/main/java/org/asamk/signal/manager/KeyUtils.java rename to src/main/java/org/asamk/signal/manager/util/KeyUtils.java index 6ac093db..2b4bc371 100644 --- a/src/main/java/org/asamk/signal/manager/KeyUtils.java +++ b/src/main/java/org/asamk/signal/manager/util/KeyUtils.java @@ -1,4 +1,4 @@ -package org.asamk.signal.manager; +package org.asamk.signal.manager.util; import org.asamk.signal.util.RandomUtils; import org.signal.zkgroup.InvalidInputException; @@ -10,11 +10,11 @@ public class KeyUtils { private KeyUtils() { } - static String createSignalingKey() { + public static String createSignalingKey() { return getSecret(52); } - static ProfileKey createProfileKey() { + public static ProfileKey createProfileKey() { try { return new ProfileKey(getSecretBytes(32)); } catch (InvalidInputException e) { @@ -22,11 +22,11 @@ public class KeyUtils { } } - static String createPassword() { + public static String createPassword() { return getSecret(18); } - static byte[] createStickerUploadKey() { + public static byte[] createStickerUploadKey() { return getSecretBytes(32); } diff --git a/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java b/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java new file mode 100644 index 00000000..8661c10b --- /dev/null +++ b/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java @@ -0,0 +1,105 @@ +package org.asamk.signal.manager.util; + +import org.whispersystems.libsignal.util.guava.Optional; +import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; +import org.whispersystems.signalservice.api.push.SignalServiceAddress; +import org.whispersystems.signalservice.api.util.UuidUtil; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.UUID; + +public class MessageCacheUtils { + + public static SignalServiceEnvelope loadEnvelope(File file) throws IOException { + try (FileInputStream f = new FileInputStream(file)) { + DataInputStream in = new DataInputStream(f); + int version = in.readInt(); + if (version > 4) { + return null; + } + int type = in.readInt(); + String source = in.readUTF(); + UUID sourceUuid = null; + if (version >= 3) { + sourceUuid = UuidUtil.parseOrNull(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 serverReceivedTimestamp = 0; + String uuid = null; + 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, + serverReceivedTimestamp, + serverDeliveredTimestamp, + uuid); + } + } + + public static void storeEnvelope(SignalServiceEnvelope envelope, File file) throws IOException { + try (FileOutputStream f = new FileOutputStream(file)) { + try (DataOutputStream out = new DataOutputStream(f)) { + out.writeInt(4); // version + out.writeInt(envelope.getType()); + out.writeUTF(envelope.getSourceE164().isPresent() ? envelope.getSourceE164().get() : ""); + out.writeUTF(envelope.getSourceUuid().isPresent() ? envelope.getSourceUuid().get() : ""); + 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.getServerReceivedTimestamp()); + String uuid = envelope.getUuid(); + out.writeUTF(uuid == null ? "" : uuid); + out.writeLong(envelope.getServerDeliveredTimestamp()); + } + } + } +} diff --git a/src/main/java/org/asamk/signal/manager/util/Utils.java b/src/main/java/org/asamk/signal/manager/util/Utils.java new file mode 100644 index 00000000..e68b5ce3 --- /dev/null +++ b/src/main/java/org/asamk/signal/manager/util/Utils.java @@ -0,0 +1,97 @@ +package org.asamk.signal.manager.util; + +import com.fasterxml.jackson.databind.JsonNode; + +import org.whispersystems.libsignal.IdentityKey; +import org.whispersystems.libsignal.fingerprint.Fingerprint; +import org.whispersystems.libsignal.fingerprint.NumericFingerprintGenerator; +import org.whispersystems.signalservice.api.push.SignalServiceAddress; +import org.whispersystems.signalservice.api.util.StreamDetails; +import org.whispersystems.signalservice.api.util.UuidUtil; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InvalidObjectException; +import java.net.URLConnection; +import java.nio.file.Files; + +public class Utils { + + public 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))) { + mime = URLConnection.guessContentTypeFromStream(bufferedStream); + } + } + if (mime == null) { + return defaultMimeType; + } + return mime; + } + + public static StreamDetails createStreamDetailsFromFile(File file) throws IOException { + InputStream stream = new FileInputStream(file); + final long size = file.length(); + String mime = Files.probeContentType(file.toPath()); + if (mime == null) { + mime = "application/octet-stream"; + } + return new StreamDetails(stream, mime, size); + } + + public static String computeSafetyNumber( + boolean isUuidCapable, + SignalServiceAddress ownAddress, + IdentityKey ownIdentityKey, + SignalServiceAddress theirAddress, + IdentityKey theirIdentityKey + ) { + int version; + byte[] ownId; + byte[] theirId; + + if (isUuidCapable && ownAddress.getUuid().isPresent() && theirAddress.getUuid().isPresent()) { + // Version 2: UUID user + version = 2; + ownId = UuidUtil.toByteArray(ownAddress.getUuid().get()); + theirId = UuidUtil.toByteArray(theirAddress.getUuid().get()); + } else { + // Version 1: E164 user + version = 1; + if (!ownAddress.getNumber().isPresent() || !theirAddress.getNumber().isPresent()) { + return "INVALID ID"; + } + ownId = ownAddress.getNumber().get().getBytes(); + theirId = theirAddress.getNumber().get().getBytes(); + } + + Fingerprint fingerprint = new NumericFingerprintGenerator(5200).createFor(version, + ownId, + ownIdentityKey, + theirId, + theirIdentityKey); + return fingerprint.getDisplayableFingerprint().getDisplayText(); + } + + public static SignalServiceAddress getSignalServiceAddressFromIdentifier(final String identifier) { + if (UuidUtil.isUuid(identifier)) { + return new SignalServiceAddress(UuidUtil.parseOrNull(identifier), null); + } else { + return new SignalServiceAddress(null, identifier); + } + } + + public static JsonNode getNotNullNode(JsonNode parent, String name) throws InvalidObjectException { + JsonNode node = parent.get(name); + if (node == null) { + throw new InvalidObjectException(String.format("Incorrect file format: expected parameter %s not found ", + name)); + } + + return node; + } +} diff --git a/src/main/java/org/asamk/signal/util/IOUtils.java b/src/main/java/org/asamk/signal/util/IOUtils.java index 59727a9a..766d1905 100644 --- a/src/main/java/org/asamk/signal/util/IOUtils.java +++ b/src/main/java/org/asamk/signal/util/IOUtils.java @@ -1,35 +1,16 @@ package org.asamk.signal.util; -import org.whispersystems.signalservice.internal.util.Util; - -import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import java.io.StringWriter; import java.nio.charset.Charset; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.attribute.PosixFilePermission; -import java.nio.file.attribute.PosixFilePermissions; -import java.util.EnumSet; -import java.util.Set; - -import static java.nio.file.attribute.PosixFilePermission.OWNER_EXECUTE; -import static java.nio.file.attribute.PosixFilePermission.OWNER_READ; -import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE; public class IOUtils { private IOUtils() { } - public static File createTempFile() throws IOException { - return File.createTempFile("signal_tmp_", ".tmp"); - } - public static String readAll(InputStream in, Charset charset) throws IOException { StringWriter output = new StringWriter(); byte[] buffer = new byte[4096]; @@ -40,36 +21,6 @@ public class IOUtils { return output.toString(); } - public static byte[] readFully(InputStream in) throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - Util.copy(in, baos); - return baos.toByteArray(); - } - - public static void createPrivateDirectories(File file) throws IOException { - if (file.exists()) { - return; - } - - final Path path = file.toPath(); - try { - Set perms = EnumSet.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE); - Files.createDirectories(path, PosixFilePermissions.asFileAttribute(perms)); - } catch (UnsupportedOperationException e) { - Files.createDirectories(path); - } - } - - public static void createPrivateFile(File path) throws IOException { - final Path file = path.toPath(); - try { - Set perms = EnumSet.of(OWNER_READ, OWNER_WRITE); - Files.createFile(file, PosixFilePermissions.asFileAttribute(perms)); - } catch (UnsupportedOperationException e) { - Files.createFile(file); - } - } - public static File getDataHomeDir() { String dataHome = System.getenv("XDG_DATA_HOME"); if (dataHome != null) { @@ -78,19 +29,4 @@ public class IOUtils { return new File(new File(System.getProperty("user.home"), ".local"), "share"); } - - public static void copyStreamToFile(InputStream input, File outputFile) throws IOException { - copyStreamToFile(input, outputFile, 8192); - } - - public static void copyStreamToFile(InputStream input, File outputFile, int bufferSize) throws IOException { - try (OutputStream output = new FileOutputStream(outputFile)) { - byte[] buffer = new byte[bufferSize]; - int read; - - while ((read = input.read(buffer)) != -1) { - output.write(buffer, 0, read); - } - } - } } diff --git a/src/main/java/org/asamk/signal/util/Util.java b/src/main/java/org/asamk/signal/util/Util.java index 79a6587b..92bfae7b 100644 --- a/src/main/java/org/asamk/signal/util/Util.java +++ b/src/main/java/org/asamk/signal/util/Util.java @@ -1,15 +1,7 @@ package org.asamk.signal.util; -import com.fasterxml.jackson.databind.JsonNode; - import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupIdFormatException; -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.api.util.UuidUtil; - -import java.io.InvalidObjectException; public class Util { @@ -26,41 +18,7 @@ public class Util { return f.toString(); } - public static String join(CharSequence separator, Iterable list) { - StringBuilder buf = new StringBuilder(); - for (CharSequence str : list) { - if (buf.length() > 0) { - buf.append(separator); - } - buf.append(str); - } - - return buf.toString(); - } - - public static JsonNode getNotNullNode(JsonNode parent, String name) throws InvalidObjectException { - JsonNode node = parent.get(name); - if (node == null) { - throw new InvalidObjectException(String.format("Incorrect file format: expected parameter %s not found ", - name)); - } - - return node; - } - public static GroupId decodeGroupId(String groupId) throws GroupIdFormatException { return GroupId.fromBase64(groupId); } - - public static String canonicalizeNumber(String number, String localNumber) throws InvalidNumberException { - return PhoneNumberFormatter.formatNumber(number, localNumber); - } - - public static SignalServiceAddress getSignalServiceAddressFromIdentifier(final String identifier) { - if (UuidUtil.isUuid(identifier)) { - return new SignalServiceAddress(UuidUtil.parseOrNull(identifier), null); - } else { - return new SignalServiceAddress(null, identifier); - } - } }