From 15204a2c841e5347dd15d07774441845f25d2877 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Thu, 17 Aug 2023 14:43:42 -0400 Subject: [PATCH] Remove SignalServiceContent. --- .../PushProcessMessageQueueJobMigration.java | 27 +- .../securesms/mms/IncomingMediaMessage.kt | 26 - .../securesms/util/GroupUtil.java | 35 - .../api/messages/SignalServiceContent.java | 1618 ----------------- 4 files changed, 18 insertions(+), 1688 deletions(-) delete mode 100644 libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceContent.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/migrations/PushProcessMessageQueueJobMigration.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/migrations/PushProcessMessageQueueJobMigration.java index b7e7f361d8..1720e64b7c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/migrations/PushProcessMessageQueueJobMigration.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/migrations/PushProcessMessageQueueJobMigration.java @@ -5,18 +5,23 @@ import android.content.Context; import androidx.annotation.NonNull; import org.signal.core.util.logging.Log; +import org.signal.libsignal.zkgroup.InvalidInputException; +import org.signal.libsignal.zkgroup.groups.GroupMasterKey; import org.thoughtcrime.securesms.groups.GroupId; import org.thoughtcrime.securesms.jobmanager.JsonJobData; import org.thoughtcrime.securesms.jobmanager.JobMigration; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.util.Base64; -import org.whispersystems.signalservice.api.messages.SignalServiceContent; +import org.whispersystems.signalservice.api.push.ServiceId; +import org.whispersystems.signalservice.api.push.SignalServiceAddress; +import org.whispersystems.signalservice.internal.serialize.protos.SignalServiceContentProto; import java.io.IOException; +import java.util.Optional; /** - * We changed the format of the queue key for {@link org.thoughtcrime.securesms.jobs.PushProcessMessageJob} + * We changed the format of the queue key for legacy PushProcessMessageJob * to have the recipient ID in it, so this migrates existing jobs to be in that format. */ public class PushProcessMessageQueueJobMigration extends JobMigration { @@ -36,7 +41,7 @@ public class PushProcessMessageQueueJobMigration extends JobMigration { Log.i(TAG, "Found a PushProcessMessageJob to migrate."); try { return migratePushProcessMessageJob(context, jobData); - } catch (IOException e) { + } catch (IOException | InvalidInputException e) { Log.w(TAG, "Failed to migrate message job.", e); return jobData; } @@ -44,24 +49,28 @@ public class PushProcessMessageQueueJobMigration extends JobMigration { return jobData; } - private static @NonNull JobData migratePushProcessMessageJob(@NonNull Context context, @NonNull JobData jobData) throws IOException { + private static @NonNull JobData migratePushProcessMessageJob(@NonNull Context context, @NonNull JobData jobData) throws IOException, InvalidInputException { JsonJobData data = JsonJobData.deserialize(jobData.getData()); String suffix = ""; if (data.getInt("message_state") == 0) { - SignalServiceContent content = SignalServiceContent.deserialize(Base64.decode(data.getString("message_content"))); + SignalServiceContentProto proto = SignalServiceContentProto.parseFrom(Base64.decode(data.getString("message_content"))); - if (content != null && content.getDataMessage().isPresent() && content.getDataMessage().get().getGroupContext().isPresent()) { + if (proto != null && proto.hasContent() && proto.getContent().hasDataMessage() && proto.getContent().getDataMessage().hasGroupV2()) { Log.i(TAG, "Migrating a group message."); - GroupId groupId = GroupId.v2(content.getDataMessage().get().getGroupContext().get().getMasterKey()); + GroupId groupId = GroupId.v2(new GroupMasterKey(proto.getContent().getDataMessage().getGroupV2().getMasterKey().toByteArray())); Recipient recipient = Recipient.externalGroupExact(groupId); suffix = recipient.getId().toQueueKey(); - } else if (content != null) { + } else if (proto != null && proto.hasMetadata() && proto.getMetadata().hasAddress()) { Log.i(TAG, "Migrating an individual message."); - suffix = RecipientId.from(content.getSender()).toQueueKey(); + ServiceId senderServiceId = ServiceId.parseOrThrow(proto.getMetadata().getAddress().getUuid()); + String senderE164 = proto.getMetadata().getAddress().getE164(); + SignalServiceAddress sender = new SignalServiceAddress(senderServiceId, Optional.ofNullable(senderE164)); + + suffix = RecipientId.from(sender).toQueueKey(); } } else { Log.i(TAG, "Migrating an exception message."); diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/IncomingMediaMessage.kt b/app/src/main/java/org/thoughtcrime/securesms/mms/IncomingMediaMessage.kt index 388fe51d9d..f915f0c4aa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/IncomingMediaMessage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/IncomingMediaMessage.kt @@ -12,10 +12,8 @@ import org.thoughtcrime.securesms.groups.GroupId import org.thoughtcrime.securesms.linkpreview.LinkPreview import org.thoughtcrime.securesms.recipients.RecipientId import org.whispersystems.signalservice.api.messages.SignalServiceAttachment -import org.whispersystems.signalservice.api.messages.SignalServiceContent import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2 import java.util.Optional -import java.util.UUID class IncomingMediaMessage( val from: RecipientId?, @@ -144,28 +142,4 @@ class IncomingMediaMessage( isPaymentsActivated = paymentsActivated, messageRanges = messageRanges ) - - companion object { - @JvmStatic - fun createIncomingPaymentNotification( - from: RecipientId, - content: SignalServiceContent, - receivedTime: Long, - expiresIn: Long, - paymentUuid: UUID - ): IncomingMediaMessage { - return IncomingMediaMessage( - from = from, - body = paymentUuid.toString(), - sentTimeMillis = content.timestamp, - serverTimeMillis = content.serverReceivedTimestamp, - receivedTimeMillis = receivedTime, - expiresIn = expiresIn, - isUnidentified = content.isNeedsReceipt, - serverGuid = content.serverUuid, - isPushMessage = true, - isPaymentsNotification = true - ) - } - } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/GroupUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/GroupUtil.java index ee6c1a1a2d..e569bac54d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/GroupUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/GroupUtil.java @@ -19,14 +19,12 @@ import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil; import org.thoughtcrime.securesms.mms.MessageGroupContext; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; -import org.whispersystems.signalservice.api.messages.SignalServiceContent; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2; import org.whispersystems.signalservice.internal.push.SignalServiceProtos; import java.io.IOException; import java.util.List; -import java.util.Optional; public final class GroupUtil { @@ -35,28 +33,6 @@ public final class GroupUtil { private static final String TAG = Log.tag(GroupUtil.class); - /** - * @return The group context present on the content if one exists, otherwise null. - */ - public static @Nullable SignalServiceGroupV2 getGroupContextIfPresent(@Nullable SignalServiceContent content) { - if (content == null) { - return null; - } else if (content.getDataMessage().isPresent() && content.getDataMessage().get().getGroupContext().isPresent()) { - return content.getDataMessage().get().getGroupContext().get(); - } else if (content.getSyncMessage().isPresent() && - content.getSyncMessage().get().getSent().isPresent() && - content.getSyncMessage().get().getSent().get().getDataMessage().isPresent() && - content.getSyncMessage().get().getSent().get().getDataMessage().get().getGroupContext().isPresent()) - { - return content.getSyncMessage().get().getSent().get().getDataMessage().get().getGroupContext().get(); - } else if (content.getStoryMessage().isPresent() && content.getStoryMessage().get().getGroupContext().isPresent()) { - return content.getStoryMessage().get().getGroupContext().get(); - } else { - return null; - } - } - - public static @Nullable SignalServiceProtos.GroupContextV2 getGroupContextIfPresent(@NonNull SignalServiceProtos.Content content) { if (content.hasDataMessage() && SignalServiceProtoUtil.INSTANCE.getHasGroupContext(content.getDataMessage())) { return content.getDataMessage().getGroupV2(); @@ -73,17 +49,6 @@ public final class GroupUtil { } } - /** - * Result may be a v1 or v2 GroupId. - */ - public static @NonNull Optional idFromGroupContext(@NonNull Optional groupContext) { - if (groupContext.isPresent()) { - return Optional.of(GroupId.v2(groupContext.get().getMasterKey())); - } else { - return Optional.empty(); - } - } - public static @NonNull GroupMasterKey requireMasterKey(@NonNull byte[] masterKey) { try { return new GroupMasterKey(masterKey); diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceContent.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceContent.java deleted file mode 100644 index 8a9bd9f2ce..0000000000 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceContent.java +++ /dev/null @@ -1,1618 +0,0 @@ -/* - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ - -package org.whispersystems.signalservice.api.messages; - -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; - -import org.signal.libsignal.metadata.ProtocolInvalidKeyException; -import org.signal.libsignal.metadata.ProtocolInvalidMessageException; -import org.signal.libsignal.protocol.IdentityKey; -import org.signal.libsignal.protocol.InvalidKeyException; -import org.signal.libsignal.protocol.InvalidMessageException; -import org.signal.libsignal.protocol.InvalidVersionException; -import org.signal.libsignal.protocol.LegacyMessageException; -import org.signal.libsignal.protocol.logging.Log; -import org.signal.libsignal.protocol.message.DecryptionErrorMessage; -import org.signal.libsignal.protocol.message.SenderKeyDistributionMessage; -import org.signal.libsignal.zkgroup.InvalidInputException; -import org.signal.libsignal.zkgroup.groups.GroupMasterKey; -import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialPresentation; -import org.whispersystems.signalservice.api.InvalidMessageStructureException; -import org.whispersystems.signalservice.api.messages.calls.AnswerMessage; -import org.whispersystems.signalservice.api.messages.calls.BusyMessage; -import org.whispersystems.signalservice.api.messages.calls.HangupMessage; -import org.whispersystems.signalservice.api.messages.calls.IceUpdateMessage; -import org.whispersystems.signalservice.api.messages.calls.OfferMessage; -import org.whispersystems.signalservice.api.messages.calls.OpaqueMessage; -import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage; -import org.whispersystems.signalservice.api.messages.multidevice.BlockedListMessage; -import org.whispersystems.signalservice.api.messages.multidevice.ConfigurationMessage; -import org.whispersystems.signalservice.api.messages.multidevice.ContactsMessage; -import org.whispersystems.signalservice.api.messages.multidevice.KeysMessage; -import org.whispersystems.signalservice.api.messages.multidevice.MessageRequestResponseMessage; -import org.whispersystems.signalservice.api.messages.multidevice.OutgoingPaymentMessage; -import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage; -import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage; -import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage; -import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; -import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOperationMessage; -import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage; -import org.whispersystems.signalservice.api.messages.multidevice.ViewOnceOpenMessage; -import org.whispersystems.signalservice.api.messages.multidevice.ViewedMessage; -import org.whispersystems.signalservice.api.messages.shared.SharedContact; -import org.whispersystems.signalservice.api.payments.Money; -import org.whispersystems.signalservice.api.push.ServiceId.ACI; -import org.whispersystems.signalservice.api.push.ServiceId.PNI; -import org.whispersystems.signalservice.api.push.ServiceId; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.signalservice.api.storage.StorageKey; -import org.whispersystems.signalservice.api.util.AttachmentPointerUtil; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos; -import org.whispersystems.signalservice.internal.push.UnsupportedDataMessageException; -import org.whispersystems.signalservice.internal.push.UnsupportedDataMessageProtocolVersionException; -import org.whispersystems.signalservice.internal.serialize.SignalServiceAddressProtobufSerializer; -import org.whispersystems.signalservice.internal.serialize.SignalServiceMetadataProtobufSerializer; -import org.whispersystems.signalservice.internal.serialize.protos.SignalServiceContentProto; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -import javax.annotation.Nullable; - -@SuppressWarnings("OptionalIsPresent") -public final class SignalServiceContent { - - private static final String TAG = SignalServiceContent.class.getSimpleName(); - - private final SignalServiceAddress sender; - private final int senderDevice; - private final long timestamp; - private final long serverReceivedTimestamp; - private final long serverDeliveredTimestamp; - private final boolean needsReceipt; - private final SignalServiceContentProto serializedState; - private final String serverUuid; - private final Optional groupId; - private final String destinationUuid; - - private final Optional message; - private final Optional synchronizeMessage; - private final Optional callMessage; - private final Optional readMessage; - private final Optional typingMessage; - private final Optional senderKeyDistributionMessage; - private final Optional decryptionErrorMessage; - private final Optional storyMessage; - private final Optional pniSignatureMessage; - private final Optional editMessage; - - private SignalServiceContent(SignalServiceDataMessage message, - Optional senderKeyDistributionMessage, - Optional pniSignatureMessage, - SignalServiceAddress sender, - int senderDevice, - long timestamp, - long serverReceivedTimestamp, - long serverDeliveredTimestamp, - boolean needsReceipt, - String serverUuid, - Optional groupId, - String destinationUuid, - SignalServiceContentProto serializedState) - { - this.sender = sender; - this.senderDevice = senderDevice; - this.timestamp = timestamp; - this.serverReceivedTimestamp = serverReceivedTimestamp; - this.serverDeliveredTimestamp = serverDeliveredTimestamp; - this.needsReceipt = needsReceipt; - this.serverUuid = serverUuid; - this.groupId = groupId; - this.destinationUuid = destinationUuid; - this.serializedState = serializedState; - - this.message = Optional.ofNullable(message); - this.synchronizeMessage = Optional.empty(); - this.callMessage = Optional.empty(); - this.readMessage = Optional.empty(); - this.typingMessage = Optional.empty(); - this.senderKeyDistributionMessage = senderKeyDistributionMessage; - this.decryptionErrorMessage = Optional.empty(); - this.storyMessage = Optional.empty(); - this.pniSignatureMessage = pniSignatureMessage; - this.editMessage = Optional.empty(); - } - - private SignalServiceContent(SignalServiceSyncMessage synchronizeMessage, - Optional senderKeyDistributionMessage, - Optional pniSignatureMessage, - SignalServiceAddress sender, - int senderDevice, - long timestamp, - long serverReceivedTimestamp, - long serverDeliveredTimestamp, - boolean needsReceipt, - String serverUuid, - Optional groupId, - String destinationUuid, - SignalServiceContentProto serializedState) - { - this.sender = sender; - this.senderDevice = senderDevice; - this.timestamp = timestamp; - this.serverReceivedTimestamp = serverReceivedTimestamp; - this.serverDeliveredTimestamp = serverDeliveredTimestamp; - this.needsReceipt = needsReceipt; - this.serverUuid = serverUuid; - this.groupId = groupId; - this.destinationUuid = destinationUuid; - this.serializedState = serializedState; - - this.message = Optional.empty(); - this.synchronizeMessage = Optional.ofNullable(synchronizeMessage); - this.callMessage = Optional.empty(); - this.readMessage = Optional.empty(); - this.typingMessage = Optional.empty(); - this.senderKeyDistributionMessage = senderKeyDistributionMessage; - this.decryptionErrorMessage = Optional.empty(); - this.storyMessage = Optional.empty(); - this.pniSignatureMessage = pniSignatureMessage; - this.editMessage = Optional.empty(); - } - - private SignalServiceContent(SignalServiceCallMessage callMessage, - Optional senderKeyDistributionMessage, - Optional pniSignatureMessage, - SignalServiceAddress sender, - int senderDevice, - long timestamp, - long serverReceivedTimestamp, - long serverDeliveredTimestamp, - boolean needsReceipt, - String serverUuid, - Optional groupId, - String destinationUuid, - SignalServiceContentProto serializedState) - { - this.sender = sender; - this.senderDevice = senderDevice; - this.timestamp = timestamp; - this.serverReceivedTimestamp = serverReceivedTimestamp; - this.serverDeliveredTimestamp = serverDeliveredTimestamp; - this.needsReceipt = needsReceipt; - this.serverUuid = serverUuid; - this.groupId = groupId; - this.destinationUuid = destinationUuid; - this.serializedState = serializedState; - - this.message = Optional.empty(); - this.synchronizeMessage = Optional.empty(); - this.callMessage = Optional.of(callMessage); - this.readMessage = Optional.empty(); - this.typingMessage = Optional.empty(); - this.senderKeyDistributionMessage = senderKeyDistributionMessage; - this.decryptionErrorMessage = Optional.empty(); - this.storyMessage = Optional.empty(); - this.pniSignatureMessage = pniSignatureMessage; - this.editMessage = Optional.empty(); - } - - private SignalServiceContent(SignalServiceReceiptMessage receiptMessage, - Optional senderKeyDistributionMessage, - Optional pniSignatureMessage, - SignalServiceAddress sender, - int senderDevice, - long timestamp, - long serverReceivedTimestamp, - long serverDeliveredTimestamp, - boolean needsReceipt, - String serverUuid, - Optional groupId, - String destinationUuid, - SignalServiceContentProto serializedState) - { - this.sender = sender; - this.senderDevice = senderDevice; - this.timestamp = timestamp; - this.serverReceivedTimestamp = serverReceivedTimestamp; - this.serverDeliveredTimestamp = serverDeliveredTimestamp; - this.needsReceipt = needsReceipt; - this.serverUuid = serverUuid; - this.groupId = groupId; - this.destinationUuid = destinationUuid; - this.serializedState = serializedState; - - this.message = Optional.empty(); - this.synchronizeMessage = Optional.empty(); - this.callMessage = Optional.empty(); - this.readMessage = Optional.of(receiptMessage); - this.typingMessage = Optional.empty(); - this.senderKeyDistributionMessage = senderKeyDistributionMessage; - this.decryptionErrorMessage = Optional.empty(); - this.storyMessage = Optional.empty(); - this.pniSignatureMessage = pniSignatureMessage; - this.editMessage = Optional.empty(); - } - - private SignalServiceContent(DecryptionErrorMessage errorMessage, - Optional senderKeyDistributionMessage, - Optional pniSignatureMessage, - SignalServiceAddress sender, - int senderDevice, - long timestamp, - long serverReceivedTimestamp, - long serverDeliveredTimestamp, - boolean needsReceipt, - String serverUuid, - Optional groupId, - String destinationUuid, - SignalServiceContentProto serializedState) - { - this.sender = sender; - this.senderDevice = senderDevice; - this.timestamp = timestamp; - this.serverReceivedTimestamp = serverReceivedTimestamp; - this.serverDeliveredTimestamp = serverDeliveredTimestamp; - this.needsReceipt = needsReceipt; - this.serverUuid = serverUuid; - this.groupId = groupId; - this.destinationUuid = destinationUuid; - this.serializedState = serializedState; - - this.message = Optional.empty(); - this.synchronizeMessage = Optional.empty(); - this.callMessage = Optional.empty(); - this.readMessage = Optional.empty(); - this.typingMessage = Optional.empty(); - this.senderKeyDistributionMessage = senderKeyDistributionMessage; - this.decryptionErrorMessage = Optional.of(errorMessage); - this.storyMessage = Optional.empty(); - this.pniSignatureMessage = pniSignatureMessage; - this.editMessage = Optional.empty(); - } - - private SignalServiceContent(SignalServiceTypingMessage typingMessage, - Optional senderKeyDistributionMessage, - Optional pniSignatureMessage, - SignalServiceAddress sender, - int senderDevice, - long timestamp, - long serverReceivedTimestamp, - long serverDeliveredTimestamp, - boolean needsReceipt, - String serverUuid, - Optional groupId, - String destinationUuid, - SignalServiceContentProto serializedState) - { - this.sender = sender; - this.senderDevice = senderDevice; - this.timestamp = timestamp; - this.serverReceivedTimestamp = serverReceivedTimestamp; - this.serverDeliveredTimestamp = serverDeliveredTimestamp; - this.needsReceipt = needsReceipt; - this.serverUuid = serverUuid; - this.groupId = groupId; - this.destinationUuid = destinationUuid; - this.serializedState = serializedState; - - this.message = Optional.empty(); - this.synchronizeMessage = Optional.empty(); - this.callMessage = Optional.empty(); - this.readMessage = Optional.empty(); - this.typingMessage = Optional.of(typingMessage); - this.senderKeyDistributionMessage = senderKeyDistributionMessage; - this.decryptionErrorMessage = Optional.empty(); - this.storyMessage = Optional.empty(); - this.pniSignatureMessage = pniSignatureMessage; - this.editMessage = Optional.empty(); - } - - private SignalServiceContent(SenderKeyDistributionMessage senderKeyDistributionMessage, - Optional pniSignatureMessage, - SignalServiceAddress sender, - int senderDevice, - long timestamp, - long serverReceivedTimestamp, - long serverDeliveredTimestamp, - boolean needsReceipt, - String serverUuid, - Optional groupId, - String destinationUuid, - SignalServiceContentProto serializedState) - { - this.sender = sender; - this.senderDevice = senderDevice; - this.timestamp = timestamp; - this.serverReceivedTimestamp = serverReceivedTimestamp; - this.serverDeliveredTimestamp = serverDeliveredTimestamp; - this.needsReceipt = needsReceipt; - this.serverUuid = serverUuid; - this.groupId = groupId; - this.destinationUuid = destinationUuid; - this.serializedState = serializedState; - - this.message = Optional.empty(); - this.synchronizeMessage = Optional.empty(); - this.callMessage = Optional.empty(); - this.readMessage = Optional.empty(); - this.typingMessage = Optional.empty(); - this.senderKeyDistributionMessage = Optional.of(senderKeyDistributionMessage); - this.decryptionErrorMessage = Optional.empty(); - this.storyMessage = Optional.empty(); - this.pniSignatureMessage = pniSignatureMessage; - this.editMessage = Optional.empty(); - } - - private SignalServiceContent(SignalServicePniSignatureMessage pniSignatureMessage, - Optional senderKeyDistributionMessage, - SignalServiceAddress sender, - int senderDevice, - long timestamp, - long serverReceivedTimestamp, - long serverDeliveredTimestamp, - boolean needsReceipt, - String serverUuid, - Optional groupId, - String destinationUuid, - SignalServiceContentProto serializedState) - { - this.sender = sender; - this.senderDevice = senderDevice; - this.timestamp = timestamp; - this.serverReceivedTimestamp = serverReceivedTimestamp; - this.serverDeliveredTimestamp = serverDeliveredTimestamp; - this.needsReceipt = needsReceipt; - this.serverUuid = serverUuid; - this.groupId = groupId; - this.destinationUuid = destinationUuid; - this.serializedState = serializedState; - - this.message = Optional.empty(); - this.synchronizeMessage = Optional.empty(); - this.callMessage = Optional.empty(); - this.readMessage = Optional.empty(); - this.typingMessage = Optional.empty(); - this.senderKeyDistributionMessage = senderKeyDistributionMessage; - this.decryptionErrorMessage = Optional.empty(); - this.storyMessage = Optional.empty(); - this.pniSignatureMessage = Optional.of(pniSignatureMessage); - this.editMessage = Optional.empty(); - } - - private SignalServiceContent(SignalServiceStoryMessage storyMessage, - Optional senderKeyDistributionMessage, - Optional pniSignatureMessage, - SignalServiceAddress sender, - int senderDevice, - long timestamp, - long serverReceivedTimestamp, - long serverDeliveredTimestamp, - boolean needsReceipt, - String serverUuid, - Optional groupId, - String destinationUuid, - SignalServiceContentProto serializedState) - { - this.sender = sender; - this.senderDevice = senderDevice; - this.timestamp = timestamp; - this.serverReceivedTimestamp = serverReceivedTimestamp; - this.serverDeliveredTimestamp = serverDeliveredTimestamp; - this.needsReceipt = needsReceipt; - this.serverUuid = serverUuid; - this.groupId = groupId; - this.destinationUuid = destinationUuid; - this.serializedState = serializedState; - - this.message = Optional.empty(); - this.synchronizeMessage = Optional.empty(); - this.callMessage = Optional.empty(); - this.readMessage = Optional.empty(); - this.typingMessage = Optional.empty(); - this.senderKeyDistributionMessage = senderKeyDistributionMessage; - this.decryptionErrorMessage = Optional.empty(); - this.storyMessage = Optional.of(storyMessage); - this.pniSignatureMessage = pniSignatureMessage; - this.editMessage = Optional.empty(); - } - - private SignalServiceContent(SignalServiceEditMessage editMessage, - Optional senderKeyDistributionMessage, - Optional pniSignatureMessage, - SignalServiceAddress sender, - int senderDevice, - long timestamp, - long serverReceivedTimestamp, - long serverDeliveredTimestamp, - boolean needsReceipt, - String serverUuid, - Optional groupId, - String destinationUuid, - SignalServiceContentProto serializedState) - { - this.sender = sender; - this.senderDevice = senderDevice; - this.timestamp = timestamp; - this.serverReceivedTimestamp = serverReceivedTimestamp; - this.serverDeliveredTimestamp = serverDeliveredTimestamp; - this.needsReceipt = needsReceipt; - this.serverUuid = serverUuid; - this.groupId = groupId; - this.destinationUuid = destinationUuid; - this.serializedState = serializedState; - - this.message = Optional.empty(); - this.synchronizeMessage = Optional.empty(); - this.callMessage = Optional.empty(); - this.readMessage = Optional.empty(); - this.typingMessage = Optional.empty(); - this.senderKeyDistributionMessage = senderKeyDistributionMessage; - this.decryptionErrorMessage = Optional.empty(); - this.storyMessage = Optional.empty(); - this.pniSignatureMessage = pniSignatureMessage; - this.editMessage = Optional.of(editMessage); - } - - public Optional getDataMessage() { - return message; - } - - public Optional getSyncMessage() { - return synchronizeMessage; - } - - public Optional getCallMessage() { - return callMessage; - } - - public Optional getReceiptMessage() { - return readMessage; - } - - public Optional getTypingMessage() { - return typingMessage; - } - - public Optional getStoryMessage() { - return storyMessage; - } - - public Optional getSenderKeyDistributionMessage() { - return senderKeyDistributionMessage; - } - - public Optional getDecryptionErrorMessage() { - return decryptionErrorMessage; - } - - public Optional getPniSignatureMessage() { - return pniSignatureMessage; - } - - public Optional getEditMessage() { - return editMessage; - } - - public SignalServiceAddress getSender() { - return sender; - } - - public int getSenderDevice() { - return senderDevice; - } - - public long getTimestamp() { - return timestamp; - } - - public long getServerReceivedTimestamp() { - return serverReceivedTimestamp; - } - - public long getServerDeliveredTimestamp() { - return serverDeliveredTimestamp; - } - - public boolean isNeedsReceipt() { - return needsReceipt; - } - - public String getServerUuid() { - return serverUuid; - } - - public Optional getGroupId() { - return groupId; - } - - public String getDestinationServiceId() { - return destinationUuid; - } - - public byte[] serialize() { - return serializedState.toByteArray(); - } - - public static @Nullable SignalServiceContent deserialize(byte[] data) { - try { - if (data == null) return null; - - SignalServiceContentProto signalServiceContentProto = SignalServiceContentProto.parseFrom(data); - - return createFromProto(signalServiceContentProto); - } catch (InvalidProtocolBufferException | ProtocolInvalidMessageException | ProtocolInvalidKeyException | UnsupportedDataMessageException | InvalidMessageStructureException e) { - // We do not expect any of these exceptions if this byte[] has come from serialize. - throw new AssertionError(e); - } - } - - /** - * Takes internal protobuf serialization format and processes it into a {@link SignalServiceContent}. - */ - public static @Nullable SignalServiceContent createFromProto(SignalServiceContentProto serviceContentProto) - throws ProtocolInvalidMessageException, ProtocolInvalidKeyException, UnsupportedDataMessageException, InvalidMessageStructureException - { - SignalServiceMetadata metadata = SignalServiceMetadataProtobufSerializer.fromProtobuf(serviceContentProto.getMetadata()); - SignalServiceAddress localAddress = SignalServiceAddressProtobufSerializer.fromProtobuf(serviceContentProto.getLocalAddress()); - - if (serviceContentProto.getDataCase() == SignalServiceContentProto.DataCase.LEGACYDATAMESSAGE) { - throw new InvalidMessageStructureException("Legacy message!"); - } else if (serviceContentProto.getDataCase() == SignalServiceContentProto.DataCase.CONTENT) { - SignalServiceProtos.Content message = serviceContentProto.getContent(); - Optional senderKeyDistributionMessage = Optional.empty(); - - if (message.hasSenderKeyDistributionMessage()) { - try { - senderKeyDistributionMessage = Optional.of(new SenderKeyDistributionMessage(message.getSenderKeyDistributionMessage().toByteArray())); - } catch (LegacyMessageException | InvalidMessageException | InvalidVersionException | InvalidKeyException e) { - Log.w(TAG, "Failed to parse SenderKeyDistributionMessage!", e); - } - } - - Optional pniSignatureMessage = Optional.empty(); - - if (message.hasPniSignatureMessage()) { - PNI pni = PNI.parseOrNull(message.getPniSignatureMessage().getPni().toByteArray()); - if (pni != null) { - pniSignatureMessage = Optional.of(new SignalServicePniSignatureMessage(pni, message.getPniSignatureMessage().getSignature().toByteArray())); - } else { - Log.w(TAG, "Invalid PNI on PNI signature message! Ignoring."); - } - } - - if (message.hasDataMessage()) { - return new SignalServiceContent(createSignalServiceDataMessage(metadata, message.getDataMessage()), - senderKeyDistributionMessage, - pniSignatureMessage, - metadata.getSender(), - metadata.getSenderDevice(), - metadata.getTimestamp(), - metadata.getServerReceivedTimestamp(), - metadata.getServerDeliveredTimestamp(), - metadata.isNeedsReceipt(), - metadata.getServerGuid(), - metadata.getGroupId(), - metadata.getDestinationUuid(), - serviceContentProto); - } else if (message.hasSyncMessage() && localAddress.matches(metadata.getSender())) { - return new SignalServiceContent(createSynchronizeMessage(metadata, message.getSyncMessage()), - senderKeyDistributionMessage, - pniSignatureMessage, - metadata.getSender(), - metadata.getSenderDevice(), - metadata.getTimestamp(), - metadata.getServerReceivedTimestamp(), - metadata.getServerDeliveredTimestamp(), - metadata.isNeedsReceipt(), - metadata.getServerGuid(), - metadata.getGroupId(), - metadata.getDestinationUuid(), - serviceContentProto); - } else if (message.hasCallMessage()) { - return new SignalServiceContent(createCallMessage(message.getCallMessage()), - senderKeyDistributionMessage, - pniSignatureMessage, - metadata.getSender(), - metadata.getSenderDevice(), - metadata.getTimestamp(), - metadata.getServerReceivedTimestamp(), - metadata.getServerDeliveredTimestamp(), - metadata.isNeedsReceipt(), - metadata.getServerGuid(), - metadata.getGroupId(), - metadata.getDestinationUuid(), - serviceContentProto); - } else if (message.hasReceiptMessage()) { - return new SignalServiceContent(createReceiptMessage(metadata, message.getReceiptMessage()), - senderKeyDistributionMessage, - pniSignatureMessage, - metadata.getSender(), - metadata.getSenderDevice(), - metadata.getTimestamp(), - metadata.getServerReceivedTimestamp(), - metadata.getServerDeliveredTimestamp(), - metadata.isNeedsReceipt(), - metadata.getServerGuid(), - metadata.getGroupId(), - metadata.getDestinationUuid(), - serviceContentProto); - } else if (message.hasTypingMessage()) { - return new SignalServiceContent(createTypingMessage(metadata, message.getTypingMessage()), - senderKeyDistributionMessage, - pniSignatureMessage, - metadata.getSender(), - metadata.getSenderDevice(), - metadata.getTimestamp(), - metadata.getServerReceivedTimestamp(), - metadata.getServerDeliveredTimestamp(), - false, - metadata.getServerGuid(), - metadata.getGroupId(), - metadata.getDestinationUuid(), - serviceContentProto); - } else if (message.hasDecryptionErrorMessage()) { - return new SignalServiceContent(createDecryptionErrorMessage(metadata, message.getDecryptionErrorMessage()), - senderKeyDistributionMessage, - pniSignatureMessage, - metadata.getSender(), - metadata.getSenderDevice(), - metadata.getTimestamp(), - metadata.getServerReceivedTimestamp(), - metadata.getServerDeliveredTimestamp(), - metadata.isNeedsReceipt(), - metadata.getServerGuid(), - metadata.getGroupId(), - metadata.getDestinationUuid(), - serviceContentProto); - } else if (message.hasStoryMessage()) { - return new SignalServiceContent(createStoryMessage(message.getStoryMessage()), - senderKeyDistributionMessage, - pniSignatureMessage, - metadata.getSender(), - metadata.getSenderDevice(), - metadata.getTimestamp(), - metadata.getServerReceivedTimestamp(), - metadata.getServerDeliveredTimestamp(), - false, - metadata.getServerGuid(), - metadata.getGroupId(), - metadata.getDestinationUuid(), - serviceContentProto); - } else if (pniSignatureMessage.isPresent()) { - return new SignalServiceContent(pniSignatureMessage.get(), - senderKeyDistributionMessage, - metadata.getSender(), - metadata.getSenderDevice(), - metadata.getTimestamp(), - metadata.getServerReceivedTimestamp(), - metadata.getServerDeliveredTimestamp(), - false, - metadata.getServerGuid(), - metadata.getGroupId(), - metadata.getDestinationUuid(), - serviceContentProto); - } else if (message.hasEditMessage()) { - return new SignalServiceContent(createEditMessage(metadata, message.getEditMessage()), - senderKeyDistributionMessage, - pniSignatureMessage, - metadata.getSender(), - metadata.getSenderDevice(), - metadata.getTimestamp(), - metadata.getServerReceivedTimestamp(), - metadata.getServerDeliveredTimestamp(), - false, - metadata.getServerGuid(), - metadata.getGroupId(), - metadata.getDestinationUuid(), - serviceContentProto); - } else if (senderKeyDistributionMessage.isPresent()) { - // IMPORTANT: This block should always be last, since you can pair SKDM's with other content - return new SignalServiceContent(senderKeyDistributionMessage.get(), - pniSignatureMessage, - metadata.getSender(), - metadata.getSenderDevice(), - metadata.getTimestamp(), - metadata.getServerReceivedTimestamp(), - metadata.getServerDeliveredTimestamp(), - false, - metadata.getServerGuid(), - metadata.getGroupId(), - metadata.getDestinationUuid(), - serviceContentProto); - } - } - - return null; - } - - private static SignalServiceDataMessage createSignalServiceDataMessage(SignalServiceMetadata metadata, - SignalServiceProtos.DataMessage content) - throws UnsupportedDataMessageException, InvalidMessageStructureException - { - SignalServiceGroupV2 groupInfoV2 = createGroupV2Info(content); - Optional groupContext = Optional.ofNullable(groupInfoV2); - - List attachments = new LinkedList<>(); - boolean endSession = ((content.getFlags() & SignalServiceProtos.DataMessage.Flags.END_SESSION_VALUE) != 0); - boolean expirationUpdate = ((content.getFlags() & SignalServiceProtos.DataMessage.Flags.EXPIRATION_TIMER_UPDATE_VALUE) != 0); - boolean profileKeyUpdate = ((content.getFlags() & SignalServiceProtos.DataMessage.Flags.PROFILE_KEY_UPDATE_VALUE) != 0); - boolean isGroupV2 = groupInfoV2 != null; - SignalServiceDataMessage.Quote quote = createQuote(content, isGroupV2); - List sharedContacts = createSharedContacts(content); - List previews = createPreviews(content); - List mentions = createMentions(content.getBodyRangesList(), content.getBody(), isGroupV2); - SignalServiceDataMessage.Sticker sticker = createSticker(content); - SignalServiceDataMessage.Reaction reaction = createReaction(content); - SignalServiceDataMessage.RemoteDelete remoteDelete = createRemoteDelete(content); - SignalServiceDataMessage.GroupCallUpdate groupCallUpdate = createGroupCallUpdate(content); - SignalServiceDataMessage.StoryContext storyContext = createStoryContext(content); - SignalServiceDataMessage.GiftBadge giftBadge = createGiftBadge(content); - List bodyRanges = createBodyRanges(content.getBodyRangesList(), content.getBody()); - - if (content.getRequiredProtocolVersion() > SignalServiceProtos.DataMessage.ProtocolVersion.CURRENT_VALUE) { - throw new UnsupportedDataMessageProtocolVersionException(SignalServiceProtos.DataMessage.ProtocolVersion.CURRENT_VALUE, - content.getRequiredProtocolVersion(), - metadata.getSender().getIdentifier(), - metadata.getSenderDevice(), - groupContext); - } - - SignalServiceDataMessage.Payment payment = createPayment(content); - - if (content.getRequiredProtocolVersion() > SignalServiceProtos.DataMessage.ProtocolVersion.CURRENT.getNumber()) { - throw new UnsupportedDataMessageProtocolVersionException(SignalServiceProtos.DataMessage.ProtocolVersion.CURRENT.getNumber(), - content.getRequiredProtocolVersion(), - metadata.getSender().getIdentifier(), - metadata.getSenderDevice(), - groupContext); - } - - for (SignalServiceProtos.AttachmentPointer pointer : content.getAttachmentsList()) { - attachments.add(createAttachmentPointer(pointer)); - } - - if (content.hasTimestamp() && content.getTimestamp() != metadata.getTimestamp()) { - throw new InvalidMessageStructureException("Timestamps don't match: " + content.getTimestamp() + " vs " + metadata.getTimestamp(), - metadata.getSender().getIdentifier(), - metadata.getSenderDevice()); - } - - return SignalServiceDataMessage.newBuilder() - .withTimestamp(metadata.getTimestamp()) - .asGroupMessage(groupInfoV2) - .withAttachments(attachments) - .withBody(content.hasBody() ? content.getBody() : null) - .asEndSessionMessage(endSession) - .withExpiration(content.getExpireTimer()) - .asExpirationUpdate(expirationUpdate) - .withProfileKey(content.hasProfileKey() ? content.getProfileKey().toByteArray() : null) - .asProfileKeyUpdate(profileKeyUpdate) - .withQuote(quote) - .withSharedContacts(sharedContacts) - .withPreviews(previews) - .withMentions(mentions) - .withSticker(sticker) - .withViewOnce(content.getIsViewOnce()) - .withReaction(reaction) - .withRemoteDelete(remoteDelete) - .withGroupCallUpdate(groupCallUpdate) - .withPayment(payment) - .withStoryContext(storyContext) - .withGiftBadge(giftBadge) - .withBodyRanges(bodyRanges) - .build(); - } - - private static SignalServiceSyncMessage createSynchronizeMessage(SignalServiceMetadata metadata, - SignalServiceProtos.SyncMessage content) - throws ProtocolInvalidKeyException, UnsupportedDataMessageException, InvalidMessageStructureException - { - if (content.hasSent()) { - Map unidentifiedStatuses = new HashMap<>(); - SignalServiceProtos.SyncMessage.Sent sentContent = content.getSent(); - Optional dataMessage = sentContent.hasMessage() ? Optional.of(createSignalServiceDataMessage(metadata, sentContent.getMessage())) : Optional.empty(); - Optional storyMessage = sentContent.hasStoryMessage() ? Optional.of(createStoryMessage(sentContent.getStoryMessage())) : Optional.empty(); - Optional editMessage = sentContent.hasEditMessage() ? Optional.of(createEditMessage(metadata, sentContent.getEditMessage())) : Optional.empty(); - Optional address = SignalServiceAddress.isValidAddress(sentContent.getDestinationServiceId()) - ? Optional.of(new SignalServiceAddress(ServiceId.parseOrThrow(sentContent.getDestinationServiceId()), sentContent.getDestinationE164())) - : Optional.empty(); - Set recipientManifest = sentContent.getStoryMessageRecipientsList() - .stream() - .map(SignalServiceContent::createSignalServiceStoryMessageRecipient) - .collect(Collectors.toSet()); - - if (!address.isPresent() && - !dataMessage.flatMap(SignalServiceDataMessage::getGroupContext).isPresent() && - !storyMessage.flatMap(SignalServiceStoryMessage::getGroupContext).isPresent() && - recipientManifest.isEmpty()) - { - throw new InvalidMessageStructureException("SyncMessage missing destination, group ID, and recipient manifest!"); - } - - for (SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus status : sentContent.getUnidentifiedStatusList()) { - if (SignalServiceAddress.isValidAddress(status.getDestinationServiceId(), null)) { - unidentifiedStatuses.put(ServiceId.parseOrNull(status.getDestinationServiceId()), status.getUnidentified()); - } else { - Log.w(TAG, "Encountered an invalid UnidentifiedDeliveryStatus in a SentTranscript! Ignoring."); - } - } - - return SignalServiceSyncMessage.forSentTranscript(new SentTranscriptMessage(address, - sentContent.getTimestamp(), - dataMessage, - sentContent.getExpirationStartTimestamp(), - unidentifiedStatuses, - sentContent.getIsRecipientUpdate(), - storyMessage, - recipientManifest, - editMessage)); - } - - if (content.hasRequest()) { - return SignalServiceSyncMessage.forRequest(new RequestMessage(content.getRequest())); - } - - if (content.getReadList().size() > 0) { - List readMessages = new LinkedList<>(); - - for (SignalServiceProtos.SyncMessage.Read read : content.getReadList()) { - ACI aci = ACI.parseOrNull(read.getSenderAci()); - if (aci != null) { - readMessages.add(new ReadMessage(aci, read.getTimestamp())); - } else { - Log.w(TAG, "Encountered an invalid ReadMessage! Ignoring."); - } - } - - return SignalServiceSyncMessage.forRead(readMessages); - } - - if (content.getViewedList().size() > 0) { - List viewedMessages = new LinkedList<>(); - - for (SignalServiceProtos.SyncMessage.Viewed viewed : content.getViewedList()) { - ACI aci = ACI.parseOrNull(viewed.getSenderAci()); - if (aci != null) { - viewedMessages.add(new ViewedMessage(aci, viewed.getTimestamp())); - } else { - Log.w(TAG, "Encountered an invalid ReadMessage! Ignoring."); - } - } - - return SignalServiceSyncMessage.forViewed(viewedMessages); - } - - if (content.hasViewOnceOpen()) { - ACI aci = ACI.parseOrNull(content.getViewOnceOpen().getSenderAci()); - if (aci != null) { - ViewOnceOpenMessage timerRead = new ViewOnceOpenMessage(aci, content.getViewOnceOpen().getTimestamp()); - return SignalServiceSyncMessage.forViewOnceOpen(timerRead); - } else { - throw new InvalidMessageStructureException("ViewOnceOpen message has no sender!"); - } - } - - if (content.hasVerified()) { - if (SignalServiceAddress.isValidAddress(content.getVerified().getDestinationAci())) { - try { - SignalServiceProtos.Verified verified = content.getVerified(); - SignalServiceAddress destination = new SignalServiceAddress(ServiceId.parseOrThrow(verified.getDestinationAci())); - IdentityKey identityKey = new IdentityKey(verified.getIdentityKey().toByteArray(), 0); - - VerifiedMessage.VerifiedState verifiedState; - - if (verified.getState() == SignalServiceProtos.Verified.State.DEFAULT) { - verifiedState = VerifiedMessage.VerifiedState.DEFAULT; - } else if (verified.getState() == SignalServiceProtos.Verified.State.VERIFIED) { - verifiedState = VerifiedMessage.VerifiedState.VERIFIED; - } else if (verified.getState() == SignalServiceProtos.Verified.State.UNVERIFIED) { - verifiedState = VerifiedMessage.VerifiedState.UNVERIFIED; - } else { - throw new InvalidMessageStructureException("Unknown state: " + verified.getState().getNumber(), - metadata.getSender().getIdentifier(), - metadata.getSenderDevice()); - } - - return SignalServiceSyncMessage.forVerified(new VerifiedMessage(destination, identityKey, verifiedState, System.currentTimeMillis())); - } catch (InvalidKeyException e) { - throw new ProtocolInvalidKeyException(e, metadata.getSender().getIdentifier(), metadata.getSenderDevice()); - } - } else { - throw new InvalidMessageStructureException("Verified message has no sender!"); - } - } - - if (content.getStickerPackOperationList().size() > 0) { - List operations = new LinkedList<>(); - - for (SignalServiceProtos.SyncMessage.StickerPackOperation operation : content.getStickerPackOperationList()) { - byte[] packId = operation.hasPackId() ? operation.getPackId().toByteArray() : null; - byte[] packKey = operation.hasPackKey() ? operation.getPackKey().toByteArray() : null; - StickerPackOperationMessage.Type type = null; - - if (operation.hasType()) { - switch (operation.getType()) { - case INSTALL: type = StickerPackOperationMessage.Type.INSTALL; break; - case REMOVE: type = StickerPackOperationMessage.Type.REMOVE; break; - } - } - operations.add(new StickerPackOperationMessage(packId, packKey, type)); - } - - return SignalServiceSyncMessage.forStickerPackOperations(operations); - } - - if (content.hasBlocked()) { - List numbers = content.getBlocked().getNumbersList(); - List uuids = content.getBlocked().getAcisList(); - List addresses = new ArrayList<>(numbers.size() + uuids.size()); - List groupIds = new ArrayList<>(content.getBlocked().getGroupIdsList().size()); - - for (String uuid : uuids) { - Optional address = SignalServiceAddress.fromRaw(uuid, null); - if (address.isPresent()) { - addresses.add(address.get()); - } - } - - for (ByteString groupId : content.getBlocked().getGroupIdsList()) { - groupIds.add(groupId.toByteArray()); - } - - return SignalServiceSyncMessage.forBlocked(new BlockedListMessage(addresses, groupIds)); - } - - if (content.hasConfiguration()) { - Boolean readReceipts = content.getConfiguration().hasReadReceipts() ? content.getConfiguration().getReadReceipts() : null; - Boolean unidentifiedDeliveryIndicators = content.getConfiguration().hasUnidentifiedDeliveryIndicators() ? content.getConfiguration().getUnidentifiedDeliveryIndicators() : null; - Boolean typingIndicators = content.getConfiguration().hasTypingIndicators() ? content.getConfiguration().getTypingIndicators() : null; - Boolean linkPreviews = content.getConfiguration().hasLinkPreviews() ? content.getConfiguration().getLinkPreviews() : null; - - return SignalServiceSyncMessage.forConfiguration(new ConfigurationMessage(Optional.ofNullable(readReceipts), - Optional.ofNullable(unidentifiedDeliveryIndicators), - Optional.ofNullable(typingIndicators), - Optional.ofNullable(linkPreviews))); - } - - if (content.hasFetchLatest() && content.getFetchLatest().hasType()) { - switch (content.getFetchLatest().getType()) { - case LOCAL_PROFILE: return SignalServiceSyncMessage.forFetchLatest(SignalServiceSyncMessage.FetchType.LOCAL_PROFILE); - case STORAGE_MANIFEST: return SignalServiceSyncMessage.forFetchLatest(SignalServiceSyncMessage.FetchType.STORAGE_MANIFEST); - case SUBSCRIPTION_STATUS: return SignalServiceSyncMessage.forFetchLatest(SignalServiceSyncMessage.FetchType.SUBSCRIPTION_STATUS); - } - } - - if (content.hasMessageRequestResponse()) { - MessageRequestResponseMessage.Type type; - - switch (content.getMessageRequestResponse().getType()) { - case ACCEPT: - type = MessageRequestResponseMessage.Type.ACCEPT; - break; - case DELETE: - type = MessageRequestResponseMessage.Type.DELETE; - break; - case BLOCK: - type = MessageRequestResponseMessage.Type.BLOCK; - break; - case BLOCK_AND_DELETE: - type = MessageRequestResponseMessage.Type.BLOCK_AND_DELETE; - break; - default: - type = MessageRequestResponseMessage.Type.UNKNOWN; - break; - } - - MessageRequestResponseMessage responseMessage; - - if (content.getMessageRequestResponse().hasGroupId()) { - responseMessage = MessageRequestResponseMessage.forGroup(content.getMessageRequestResponse().getGroupId().toByteArray(), type); - } else { - ACI aci = ACI.parseOrNull(content.getMessageRequestResponse().getThreadAci()); - if (aci != null) { - responseMessage = MessageRequestResponseMessage.forIndividual(aci, type); - } else { - throw new InvalidMessageStructureException("Message request response has an invalid thread identifier!"); - } - } - - return SignalServiceSyncMessage.forMessageRequestResponse(responseMessage); - } - - if (content.hasOutgoingPayment()) { - SignalServiceProtos.SyncMessage.OutgoingPayment outgoingPayment = content.getOutgoingPayment(); - switch (outgoingPayment.getPaymentDetailCase()) { - case MOBILECOIN: { - SignalServiceProtos.SyncMessage.OutgoingPayment.MobileCoin mobileCoin = outgoingPayment.getMobileCoin(); - Money.MobileCoin amount = Money.picoMobileCoin(mobileCoin.getAmountPicoMob()); - Money.MobileCoin fee = Money.picoMobileCoin(mobileCoin.getFeePicoMob()); - ByteString address = mobileCoin.getRecipientAddress(); - Optional recipient = Optional.ofNullable(ServiceId.parseOrNull(outgoingPayment.getRecipientServiceId())); - - return SignalServiceSyncMessage.forOutgoingPayment(new OutgoingPaymentMessage(recipient, - amount, - fee, - mobileCoin.getReceipt(), - mobileCoin.getLedgerBlockIndex(), - mobileCoin.getLedgerBlockTimestamp(), - address.isEmpty() ? Optional.empty() : Optional.of(address.toByteArray()), - Optional.of(outgoingPayment.getNote()), - mobileCoin.getOutputPublicKeysList(), - mobileCoin.getSpentKeyImagesList())); - } - default: - return SignalServiceSyncMessage.empty(); - } - } - - if (content.hasKeys() && content.getKeys().hasStorageService()) { - byte[] storageKey = content.getKeys().getStorageService().toByteArray(); - - return SignalServiceSyncMessage.forKeys(new KeysMessage(Optional.of(new StorageKey(storageKey)))); - } - - if (content.hasContacts()) { - return SignalServiceSyncMessage.forContacts(new ContactsMessage(createAttachmentPointer(content.getContacts().getBlob()), content.getContacts().getComplete())); - } - - if (content.hasCallEvent()) { - return SignalServiceSyncMessage.forCallEvent(content.getCallEvent()); - } - - return SignalServiceSyncMessage.empty(); - } - - private static SignalServiceStoryMessageRecipient createSignalServiceStoryMessageRecipient(SignalServiceProtos.SyncMessage.Sent.StoryMessageRecipient storyMessageRecipient) { - return new SignalServiceStoryMessageRecipient( - new SignalServiceAddress(ServiceId.parseOrThrow(storyMessageRecipient.getDestinationServiceId())), - storyMessageRecipient.getDistributionListIdsList(), - storyMessageRecipient.getIsAllowedToReply() - ); - } - - private static SignalServiceCallMessage createCallMessage(SignalServiceProtos.CallMessage content) { - boolean isMultiRing = content.getMultiRing(); - Integer destinationDeviceId = content.hasDestinationDeviceId() ? content.getDestinationDeviceId() : null; - - if (content.hasOffer()) { - SignalServiceProtos.CallMessage.Offer offerContent = content.getOffer(); - return SignalServiceCallMessage.forOffer(new OfferMessage(offerContent.getId(), offerContent.hasSdp() ? offerContent.getSdp() : null, OfferMessage.Type.fromProto(offerContent.getType()), offerContent.hasOpaque() ? offerContent.getOpaque().toByteArray() : null), isMultiRing, destinationDeviceId); - } else if (content.hasAnswer()) { - SignalServiceProtos.CallMessage.Answer answerContent = content.getAnswer(); - return SignalServiceCallMessage.forAnswer(new AnswerMessage(answerContent.getId(), answerContent.hasSdp() ? answerContent.getSdp() : null, answerContent.hasOpaque() ? answerContent.getOpaque().toByteArray() : null), isMultiRing, destinationDeviceId); - } else if (content.getIceUpdateCount() > 0) { - List iceUpdates = new LinkedList<>(); - - for (SignalServiceProtos.CallMessage.IceUpdate iceUpdate : content.getIceUpdateList()) { - iceUpdates.add(new IceUpdateMessage(iceUpdate.getId(), iceUpdate.hasOpaque() ? iceUpdate.getOpaque().toByteArray() : null, iceUpdate.hasSdp() ? iceUpdate.getSdp() : null)); - } - - return SignalServiceCallMessage.forIceUpdates(iceUpdates, isMultiRing, destinationDeviceId); - } else if (content.hasLegacyHangup()) { - SignalServiceProtos.CallMessage.Hangup hangup = content.getLegacyHangup(); - return SignalServiceCallMessage.forHangup(new HangupMessage(hangup.getId(), HangupMessage.Type.fromProto(hangup.getType()), hangup.getDeviceId(), content.hasLegacyHangup()), isMultiRing, destinationDeviceId); - } else if (content.hasHangup()) { - SignalServiceProtos.CallMessage.Hangup hangup = content.getHangup(); - return SignalServiceCallMessage.forHangup(new HangupMessage(hangup.getId(), HangupMessage.Type.fromProto(hangup.getType()), hangup.getDeviceId(), content.hasLegacyHangup()), isMultiRing, destinationDeviceId); - } else if (content.hasBusy()) { - SignalServiceProtos.CallMessage.Busy busy = content.getBusy(); - return SignalServiceCallMessage.forBusy(new BusyMessage(busy.getId()), isMultiRing, destinationDeviceId); - } else if (content.hasOpaque()) { - SignalServiceProtos.CallMessage.Opaque opaque = content.getOpaque(); - return SignalServiceCallMessage.forOpaque(new OpaqueMessage(opaque.getData().toByteArray(), null), isMultiRing, destinationDeviceId); - } - - return SignalServiceCallMessage.empty(); - } - - private static SignalServiceReceiptMessage createReceiptMessage(SignalServiceMetadata metadata, SignalServiceProtos.ReceiptMessage content) { - SignalServiceReceiptMessage.Type type; - - if (content.getType() == SignalServiceProtos.ReceiptMessage.Type.DELIVERY) type = SignalServiceReceiptMessage.Type.DELIVERY; - else if (content.getType() == SignalServiceProtos.ReceiptMessage.Type.READ) type = SignalServiceReceiptMessage.Type.READ; - else if (content.getType() == SignalServiceProtos.ReceiptMessage.Type.VIEWED) type = SignalServiceReceiptMessage.Type.VIEWED; - else type = SignalServiceReceiptMessage.Type.UNKNOWN; - - return new SignalServiceReceiptMessage(type, content.getTimestampList(), metadata.getTimestamp()); - } - - private static DecryptionErrorMessage createDecryptionErrorMessage(SignalServiceMetadata metadata, ByteString content) throws InvalidMessageStructureException { - try { - return new DecryptionErrorMessage(content.toByteArray()); - } catch (InvalidMessageException e) { - throw new InvalidMessageStructureException(e, metadata.getSender().getIdentifier(), metadata.getSenderDevice()); - } - } - - private static SignalServiceTypingMessage createTypingMessage(SignalServiceMetadata metadata, SignalServiceProtos.TypingMessage content) throws InvalidMessageStructureException { - SignalServiceTypingMessage.Action action; - - if (content.getAction() == SignalServiceProtos.TypingMessage.Action.STARTED) action = SignalServiceTypingMessage.Action.STARTED; - else if (content.getAction() == SignalServiceProtos.TypingMessage.Action.STOPPED) action = SignalServiceTypingMessage.Action.STOPPED; - else action = SignalServiceTypingMessage.Action.UNKNOWN; - - if (content.hasTimestamp() && content.getTimestamp() != metadata.getTimestamp()) { - throw new InvalidMessageStructureException("Timestamps don't match: " + content.getTimestamp() + " vs " + metadata.getTimestamp(), - metadata.getSender().getIdentifier(), - metadata.getSenderDevice()); - } - - return new SignalServiceTypingMessage(action, content.getTimestamp(), - content.hasGroupId() ? Optional.of(content.getGroupId().toByteArray()) : - Optional.empty()); - } - - private static SignalServiceStoryMessage createStoryMessage(SignalServiceProtos.StoryMessage content) throws InvalidMessageStructureException { - byte[] profileKey = content.hasProfileKey() ? content.getProfileKey().toByteArray() : null; - - if (content.hasFileAttachment()) { - return SignalServiceStoryMessage.forFileAttachment(profileKey, - createGroupV2Info(content), - createAttachmentPointer(content.getFileAttachment()), - content.getAllowsReplies(), - content.getBodyRangesList()); - } else { - return SignalServiceStoryMessage.forTextAttachment(profileKey, - createGroupV2Info(content), - createTextAttachment(content.getTextAttachment()), - content.getAllowsReplies(), - content.getBodyRangesList()); - } - } - - private static SignalServiceEditMessage createEditMessage(SignalServiceMetadata metadata, SignalServiceProtos.EditMessage content) throws InvalidMessageStructureException, UnsupportedDataMessageException { - if (content.hasDataMessage() && content.getTargetSentTimestamp() != 0) { - return new SignalServiceEditMessage(content.getTargetSentTimestamp(), createSignalServiceDataMessage(metadata, content.getDataMessage())); - } else { - throw new InvalidMessageStructureException("Missing data message or timestamp from edit message."); - } - } - - private static @Nullable SignalServiceDataMessage.Quote createQuote(SignalServiceProtos.DataMessage content, boolean isGroupV2) - throws InvalidMessageStructureException - { - if (!content.hasQuote()) return null; - - List attachments = new LinkedList<>(); - - for (SignalServiceProtos.DataMessage.Quote.QuotedAttachment attachment : content.getQuote().getAttachmentsList()) { - attachments.add(new SignalServiceDataMessage.Quote.QuotedAttachment(attachment.getContentType(), - attachment.getFileName(), - attachment.hasThumbnail() ? createAttachmentPointer(attachment.getThumbnail()) : null)); - } - - ACI author = ACI.parseOrNull(content.getQuote().getAuthorAci()); - if (author != null) { - return new SignalServiceDataMessage.Quote(content.getQuote().getId(), - author, - content.getQuote().getText(), - attachments, - createMentions(content.getQuote().getBodyRangesList(), content.getQuote().getText(), isGroupV2), - SignalServiceDataMessage.Quote.Type.fromProto(content.getQuote().getType()), - createBodyRanges(content.getQuote().getBodyRangesList(), content.getQuote().getText())); - } else { - Log.w(TAG, "Quote was missing an author! Returning null."); - return null; - } - } - - private static @Nullable List createPreviews(SignalServiceProtos.DataMessage content) throws InvalidMessageStructureException { - if (content.getPreviewCount() <= 0) return null; - - List results = new LinkedList<>(); - - for (SignalServiceProtos.Preview preview : content.getPreviewList()) { - results.add(createPreview(preview)); - } - - return results; - } - - private static SignalServicePreview createPreview(SignalServiceProtos.Preview preview) throws InvalidMessageStructureException { - SignalServiceAttachment attachment = null; - - if (preview.hasImage()) { - attachment = createAttachmentPointer(preview.getImage()); - } - - return new SignalServicePreview(preview.getUrl(), - preview.getTitle(), - preview.getDescription(), - preview.getDate(), - Optional.ofNullable(attachment)); - } - - private static @Nullable List createMentions(List bodyRanges, String body, boolean isGroupV2) - throws InvalidMessageStructureException - { - if (bodyRanges == null || bodyRanges.isEmpty() || body == null) { - return null; - } - - List mentions = new LinkedList<>(); - - for (SignalServiceProtos.BodyRange bodyRange : bodyRanges) { - if (bodyRange.hasMentionAci()) { - try { - mentions.add(new SignalServiceDataMessage.Mention(ServiceId.parseOrThrow(bodyRange.getMentionAci()), bodyRange.getStart(), bodyRange.getLength())); - } catch (IllegalArgumentException e) { - throw new InvalidMessageStructureException("Invalid body range!"); - } - } - } - - if (mentions.size() > 0 && !isGroupV2) { - Log.w(TAG, "Mentions received in non-GV2 message"); - } - - return mentions; - } - - private static @Nullable List createBodyRanges(List bodyRanges, String body) { - if (bodyRanges == null || bodyRanges.isEmpty() || body == null) { - return null; - } - - List ranges = new LinkedList<>(); - - for (SignalServiceProtos.BodyRange bodyRange : bodyRanges) { - if (bodyRange.hasStyle()) { - ranges.add(bodyRange); - } - } - - return ranges; - } - - private static @Nullable SignalServiceDataMessage.Sticker createSticker(SignalServiceProtos.DataMessage content) throws InvalidMessageStructureException { - if (!content.hasSticker() || - !content.getSticker().hasPackId() || - !content.getSticker().hasPackKey() || - !content.getSticker().hasStickerId() || - !content.getSticker().hasData()) - { - return null; - } - - SignalServiceProtos.DataMessage.Sticker sticker = content.getSticker(); - - return new SignalServiceDataMessage.Sticker(sticker.getPackId().toByteArray(), - sticker.getPackKey().toByteArray(), - sticker.getStickerId(), - sticker.getEmoji(), - createAttachmentPointer(sticker.getData())); - } - - private static @Nullable SignalServiceDataMessage.Reaction createReaction(SignalServiceProtos.DataMessage content) { - if (!content.hasReaction() || - !content.getReaction().hasEmoji() || - !content.getReaction().hasTargetAuthorAci() || - !content.getReaction().hasTargetSentTimestamp()) - { - return null; - } - - SignalServiceProtos.DataMessage.Reaction reaction = content.getReaction(); - ACI aci = ACI.parseOrNull(reaction.getTargetAuthorAci()); - - if (aci == null) { - Log.w(TAG, "Cannot parse author UUID on reaction"); - return null; - } - - return new SignalServiceDataMessage.Reaction(reaction.getEmoji(), - reaction.getRemove(), - aci, - reaction.getTargetSentTimestamp()); - } - - private static @Nullable SignalServiceDataMessage.RemoteDelete createRemoteDelete(SignalServiceProtos.DataMessage content) { - if (!content.hasDelete() || !content.getDelete().hasTargetSentTimestamp()) { - return null; - } - - SignalServiceProtos.DataMessage.Delete delete = content.getDelete(); - - return new SignalServiceDataMessage.RemoteDelete(delete.getTargetSentTimestamp()); - } - - private static @Nullable SignalServiceDataMessage.GroupCallUpdate createGroupCallUpdate(SignalServiceProtos.DataMessage content) { - if (!content.hasGroupCallUpdate()) { - return null; - } - - SignalServiceProtos.DataMessage.GroupCallUpdate groupCallUpdate = content.getGroupCallUpdate(); - - return new SignalServiceDataMessage.GroupCallUpdate(groupCallUpdate.getEraId()); - } - - private static @Nullable SignalServiceDataMessage.Payment createPayment(SignalServiceProtos.DataMessage content) throws InvalidMessageStructureException { - if (!content.hasPayment()) { - return null; - } - - SignalServiceProtos.DataMessage.Payment payment = content.getPayment(); - - switch (payment.getItemCase()) { - case NOTIFICATION: - return new SignalServiceDataMessage.Payment(createPaymentNotification(payment), null); - case ACTIVATION: - return new SignalServiceDataMessage.Payment(null, createPaymentActivation(payment)); - default: - throw new InvalidMessageStructureException("Unknown payment item"); - } - } - - private static @Nullable SignalServiceDataMessage.StoryContext createStoryContext(SignalServiceProtos.DataMessage content) throws InvalidMessageStructureException { - if (!content.hasStoryContext()) { - return null; - } - - ACI aci = ACI.parseOrNull(content.getStoryContext().getAuthorAci()); - - if (aci == null) { - throw new InvalidMessageStructureException("Invalid author ACI!"); - } - - return new SignalServiceDataMessage.StoryContext(aci, content.getStoryContext().getSentTimestamp()); - } - - private static @Nullable SignalServiceDataMessage.GiftBadge createGiftBadge(SignalServiceProtos.DataMessage content) throws InvalidMessageStructureException { - if (!content.hasGiftBadge()) { - return null; - } - - if (!content.getGiftBadge().hasReceiptCredentialPresentation()) { - throw new InvalidMessageStructureException("GiftBadge does not contain a receipt credential presentation!"); - } - - try { - ReceiptCredentialPresentation receiptCredentialPresentation = new ReceiptCredentialPresentation(content.getGiftBadge().getReceiptCredentialPresentation().toByteArray()); - return new SignalServiceDataMessage.GiftBadge(receiptCredentialPresentation); - } catch (InvalidInputException invalidInputException) { - throw new InvalidMessageStructureException(invalidInputException); - } - } - - private static SignalServiceDataMessage.PaymentNotification createPaymentNotification(SignalServiceProtos.DataMessage.Payment content) - throws InvalidMessageStructureException - { - if (!content.hasNotification() || - content.getNotification().getTransactionCase() != SignalServiceProtos.DataMessage.Payment.Notification.TransactionCase.MOBILECOIN) - { - throw new InvalidMessageStructureException("Badly-formatted payment notification!"); - } - - SignalServiceProtos.DataMessage.Payment.Notification payment = content.getNotification(); - - return new SignalServiceDataMessage.PaymentNotification(payment.getMobileCoin().getReceipt().toByteArray(), payment.getNote()); - } - - private static SignalServiceDataMessage.PaymentActivation createPaymentActivation(SignalServiceProtos.DataMessage.Payment content) - throws InvalidMessageStructureException - { - if (!content.hasActivation() || - content.getItemCase() != SignalServiceProtos.DataMessage.Payment.ItemCase.ACTIVATION) - { - throw new InvalidMessageStructureException("Badly-formatted payment activation!"); - } - - SignalServiceProtos.DataMessage.Payment.Activation payment = content.getActivation(); - - return new SignalServiceDataMessage.PaymentActivation(payment.getType()); - } - - public static @Nullable List createSharedContacts(SignalServiceProtos.DataMessage content) throws InvalidMessageStructureException { - if (content.getContactCount() <= 0) return null; - - List results = new LinkedList<>(); - - for (SignalServiceProtos.DataMessage.Contact contact : content.getContactList()) { - SharedContact.Builder builder = SharedContact.newBuilder() - .setName(SharedContact.Name.newBuilder() - .setDisplay(contact.getName().getDisplayName()) - .setFamily(contact.getName().getFamilyName()) - .setGiven(contact.getName().getGivenName()) - .setMiddle(contact.getName().getMiddleName()) - .setPrefix(contact.getName().getPrefix()) - .setSuffix(contact.getName().getSuffix()) - .build()); - - if (contact.getAddressCount() > 0) { - for (SignalServiceProtos.DataMessage.Contact.PostalAddress address : contact.getAddressList()) { - SharedContact.PostalAddress.Type type = SharedContact.PostalAddress.Type.HOME; - - switch (address.getType()) { - case WORK: type = SharedContact.PostalAddress.Type.WORK; break; - case HOME: type = SharedContact.PostalAddress.Type.HOME; break; - case CUSTOM: type = SharedContact.PostalAddress.Type.CUSTOM; break; - } - - builder.withAddress(SharedContact.PostalAddress.newBuilder() - .setCity(address.getCity()) - .setCountry(address.getCountry()) - .setLabel(address.getLabel()) - .setNeighborhood(address.getNeighborhood()) - .setPobox(address.getPobox()) - .setPostcode(address.getPostcode()) - .setRegion(address.getRegion()) - .setStreet(address.getStreet()) - .setType(type) - .build()); - } - } - - if (contact.getNumberCount() > 0) { - for (SignalServiceProtos.DataMessage.Contact.Phone phone : contact.getNumberList()) { - SharedContact.Phone.Type type = SharedContact.Phone.Type.HOME; - - switch (phone.getType()) { - case HOME: type = SharedContact.Phone.Type.HOME; break; - case WORK: type = SharedContact.Phone.Type.WORK; break; - case MOBILE: type = SharedContact.Phone.Type.MOBILE; break; - case CUSTOM: type = SharedContact.Phone.Type.CUSTOM; break; - } - - builder.withPhone(SharedContact.Phone.newBuilder() - .setLabel(phone.getLabel()) - .setType(type) - .setValue(phone.getValue()) - .build()); - } - } - - if (contact.getEmailCount() > 0) { - for (SignalServiceProtos.DataMessage.Contact.Email email : contact.getEmailList()) { - SharedContact.Email.Type type = SharedContact.Email.Type.HOME; - - switch (email.getType()) { - case HOME: type = SharedContact.Email.Type.HOME; break; - case WORK: type = SharedContact.Email.Type.WORK; break; - case MOBILE: type = SharedContact.Email.Type.MOBILE; break; - case CUSTOM: type = SharedContact.Email.Type.CUSTOM; break; - } - - builder.withEmail(SharedContact.Email.newBuilder() - .setLabel(email.getLabel()) - .setType(type) - .setValue(email.getValue()) - .build()); - } - } - - if (contact.hasAvatar()) { - builder.setAvatar(SharedContact.Avatar.newBuilder() - .withAttachment(createAttachmentPointer(contact.getAvatar().getAvatar())) - .withProfileFlag(contact.getAvatar().getIsProfile()) - .build()); - } - - if (contact.hasOrganization()) { - builder.withOrganization(contact.getOrganization()); - } - - results.add(builder.build()); - } - - return results; - } - - private static SignalServiceAttachmentPointer createAttachmentPointer(SignalServiceProtos.AttachmentPointer pointer) throws InvalidMessageStructureException { - return AttachmentPointerUtil.createSignalAttachmentPointer(pointer); - } - - private static SignalServiceTextAttachment createTextAttachment(SignalServiceProtos.TextAttachment attachment) throws InvalidMessageStructureException { - SignalServiceTextAttachment.Style style = null; - if (attachment.hasTextStyle()) { - switch (attachment.getTextStyle()) { - case DEFAULT: - style = SignalServiceTextAttachment.Style.DEFAULT; - break; - case REGULAR: - style = SignalServiceTextAttachment.Style.REGULAR; - break; - case BOLD: - style = SignalServiceTextAttachment.Style.BOLD; - break; - case SERIF: - style = SignalServiceTextAttachment.Style.SERIF; - break; - case SCRIPT: - style = SignalServiceTextAttachment.Style.SCRIPT; - break; - case CONDENSED: - style = SignalServiceTextAttachment.Style.CONDENSED; - break; - } - } - - Optional text = Optional.ofNullable(attachment.hasText() ? attachment.getText() : null); - Optional textForegroundColor = Optional.ofNullable(attachment.hasTextForegroundColor() ? attachment.getTextForegroundColor() : null); - Optional textBackgroundColor = Optional.ofNullable(attachment.hasTextBackgroundColor() ? attachment.getTextBackgroundColor() : null); - Optional preview = Optional.ofNullable(attachment.hasPreview() ? createPreview(attachment.getPreview()) : null); - - if (attachment.hasGradient()) { - SignalServiceProtos.TextAttachment.Gradient attachmentGradient = attachment.getGradient(); - - Integer startColor = attachmentGradient.hasStartColor() ? attachmentGradient.getStartColor() : null; - Integer endColor = attachmentGradient.hasEndColor() ? attachmentGradient.getEndColor() : null; - Integer angle = attachmentGradient.hasAngle() ? attachmentGradient.getAngle() : null; - List colors; - List positions; - - if (attachmentGradient.getColorsCount() > 0 && attachmentGradient.getColorsCount() == attachmentGradient.getPositionsCount()) { - colors = new ArrayList<>(attachmentGradient.getColorsList()); - positions = new ArrayList<>(attachmentGradient.getPositionsList()); - } else if (startColor != null && endColor != null) { - colors = Arrays.asList(startColor, endColor); - positions = Arrays.asList(0f, 1f); - } else { - colors = Collections.emptyList(); - positions = Collections.emptyList(); - } - - SignalServiceTextAttachment.Gradient gradient = new SignalServiceTextAttachment.Gradient(Optional.ofNullable(angle), - colors, - positions); - - return SignalServiceTextAttachment.forGradientBackground(text, Optional.ofNullable(style), textForegroundColor, textBackgroundColor, preview, gradient); - } else { - return SignalServiceTextAttachment.forSolidBackground(text, Optional.ofNullable(style), textForegroundColor, textBackgroundColor, preview, attachment.getColor()); - } - } - - private static @Nullable SignalServiceGroupV2 createGroupV2Info(SignalServiceProtos.StoryMessage storyMessage) throws InvalidMessageStructureException { - if (!storyMessage.hasGroup()) { - return null; - } - return createGroupV2Info(storyMessage.getGroup()); - } - - private static @Nullable SignalServiceGroupV2 createGroupV2Info(SignalServiceProtos.DataMessage dataMessage) throws InvalidMessageStructureException { - if (!dataMessage.hasGroupV2()) { - return null; - } - return createGroupV2Info(dataMessage.getGroupV2()); - } - - private static @Nullable SignalServiceGroupV2 createGroupV2Info(SignalServiceProtos.GroupContextV2 groupV2) throws InvalidMessageStructureException { - if (groupV2 == null) { - return null; - } - - if (!groupV2.hasMasterKey()) { - throw new InvalidMessageStructureException("No GV2 master key on message"); - } - if (!groupV2.hasRevision()) { - throw new InvalidMessageStructureException("No GV2 revision on message"); - } - - SignalServiceGroupV2.Builder builder; - try { - builder = SignalServiceGroupV2.newBuilder(new GroupMasterKey(groupV2.getMasterKey().toByteArray())) - .withRevision(groupV2.getRevision()); - } catch (InvalidInputException e) { - throw new InvalidMessageStructureException("Invalid GV2 input!"); - } - - if (groupV2.hasGroupChange() && !groupV2.getGroupChange().isEmpty()) { - builder.withSignedGroupChange(groupV2.getGroupChange().toByteArray()); - } - - return builder.build(); - } -}