mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-05-02 14:43:09 +01:00
Add read support for binary service ids.
This commit is contained in:
committed by
jeffrey-signal
parent
bf4aa9cae9
commit
f16405fabf
@@ -184,6 +184,7 @@ public class SignalServiceMessageSender {
|
||||
private final Scheduler scheduler;
|
||||
private final long maxEnvelopeSize;
|
||||
private final BooleanSupplier useRestFallback;
|
||||
private final boolean useBinaryId;
|
||||
|
||||
public SignalServiceMessageSender(PushServiceSocket pushServiceSocket,
|
||||
SignalServiceDataStore store,
|
||||
@@ -194,7 +195,8 @@ public class SignalServiceMessageSender {
|
||||
Optional<EventListener> eventListener,
|
||||
ExecutorService executor,
|
||||
long maxEnvelopeSize,
|
||||
BooleanSupplier useRestFallback)
|
||||
BooleanSupplier useRestFallback,
|
||||
boolean useBinaryId)
|
||||
{
|
||||
CredentialsProvider credentialsProvider = pushServiceSocket.getCredentialsProvider();
|
||||
|
||||
@@ -212,6 +214,7 @@ public class SignalServiceMessageSender {
|
||||
this.scheduler = Schedulers.from(executor, false, false);
|
||||
this.keysApi = keysApi;
|
||||
this.useRestFallback = useRestFallback;
|
||||
this.useBinaryId = useBinaryId;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1057,6 +1060,7 @@ public class SignalServiceMessageSender {
|
||||
.id(message.getQuote().get().getId())
|
||||
.text(message.getQuote().get().getText())
|
||||
.authorAci(message.getQuote().get().getAuthor().toString())
|
||||
.authorAciBinary(useBinaryId ? message.getQuote().get().getAuthor().toByteString() : null)
|
||||
.type(message.getQuote().get().getType().getProtoType());
|
||||
|
||||
List<SignalServiceDataMessage.Mention> mentions = message.getQuote().get().getMentions();
|
||||
@@ -1164,7 +1168,8 @@ public class SignalServiceMessageSender {
|
||||
.emoji(message.getReaction().get().getEmoji())
|
||||
.remove(message.getReaction().get().isRemove())
|
||||
.targetSentTimestamp(message.getReaction().get().getTargetSentTimestamp())
|
||||
.targetAuthorAci(message.getReaction().get().getTargetAuthor().toString());
|
||||
.targetAuthorAci(message.getReaction().get().getTargetAuthor().toString())
|
||||
.targetAuthorAciBinary(useBinaryId ? message.getReaction().get().getTargetAuthor().toByteString() : null);
|
||||
|
||||
builder.reaction(reactionBuilder.build());
|
||||
builder.requiredProtocolVersion(Math.max(DataMessage.ProtocolVersion.REACTIONS.getValue(), builder.requiredProtocolVersion));
|
||||
@@ -1209,6 +1214,7 @@ public class SignalServiceMessageSender {
|
||||
|
||||
builder.storyContext(new DataMessage.StoryContext.Builder()
|
||||
.authorAci(storyContext.getAuthorServiceId().toString())
|
||||
.authorAciBinary(useBinaryId ? storyContext.getAuthorServiceId().toByteString() : null)
|
||||
.sentTimestamp(storyContext.getSentTimestamp())
|
||||
.build());
|
||||
}
|
||||
@@ -1399,6 +1405,7 @@ public class SignalServiceMessageSender {
|
||||
|
||||
unidentifiedDeliveryStatuses.add(new SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder()
|
||||
.destinationServiceId(result.getAddress().getServiceId().toString())
|
||||
.destinationServiceIdBinary(useBinaryId ? result.getAddress().getServiceId().toByteString() : null)
|
||||
.unidentified(false)
|
||||
.destinationPniIdentityKey(identity)
|
||||
.build());
|
||||
@@ -1408,6 +1415,7 @@ public class SignalServiceMessageSender {
|
||||
|
||||
if (recipient.isPresent()) {
|
||||
sentMessage.destinationServiceId(recipient.get().getServiceId().toString());
|
||||
sentMessage.destinationServiceIdBinary(useBinaryId ? recipient.get().getServiceId().toByteString() : null);
|
||||
if (recipient.get().getNumber().isPresent()) {
|
||||
sentMessage.destinationE164(recipient.get().getNumber().get());
|
||||
}
|
||||
@@ -1447,6 +1455,7 @@ public class SignalServiceMessageSender {
|
||||
return new SyncMessage.Sent.StoryMessageRecipient.Builder()
|
||||
.distributionListIds(storyMessageRecipient.getDistributionListIds())
|
||||
.destinationServiceId(storyMessageRecipient.getSignalServiceAddress().getIdentifier())
|
||||
.destinationServiceIdBinary(useBinaryId ? storyMessageRecipient.getSignalServiceAddress().getServiceId().toByteString() : null)
|
||||
.isAllowedToReply(storyMessageRecipient.isAllowedToReply())
|
||||
.build();
|
||||
}
|
||||
@@ -1460,6 +1469,7 @@ public class SignalServiceMessageSender {
|
||||
.map(readMessage -> new SyncMessage.Read.Builder()
|
||||
.timestamp(readMessage.getTimestamp())
|
||||
.senderAci(readMessage.getSenderAci().toString())
|
||||
.senderAciBinary(useBinaryId ? readMessage.getSenderAci().toByteString() : null)
|
||||
.build())
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
@@ -1476,6 +1486,7 @@ public class SignalServiceMessageSender {
|
||||
.map(readMessage -> new SyncMessage.Viewed.Builder()
|
||||
.timestamp(readMessage.getTimestamp())
|
||||
.senderAci(readMessage.getSender().toString())
|
||||
.senderAciBinary(useBinaryId ? readMessage.getSender().toByteString() : null)
|
||||
.build())
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
@@ -1490,6 +1501,7 @@ public class SignalServiceMessageSender {
|
||||
builder.viewOnceOpen(new SyncMessage.ViewOnceOpen.Builder()
|
||||
.timestamp(readMessage.getTimestamp())
|
||||
.senderAci(readMessage.getSender().toString())
|
||||
.senderAciBinary(useBinaryId ? readMessage.getSender().toByteString() : null)
|
||||
.build());
|
||||
|
||||
return container.syncMessage(builder.build()).build();
|
||||
@@ -1501,6 +1513,7 @@ public class SignalServiceMessageSender {
|
||||
SyncMessage.Blocked.Builder blockedMessage = new SyncMessage.Blocked.Builder();
|
||||
|
||||
blockedMessage.acis(blocked.individuals.stream().filter(a -> a.getAci() != null).map(a -> a.getAci().toString()).collect(Collectors.toList()));
|
||||
blockedMessage.acisBinary(useBinaryId ? blocked.individuals.stream().filter(a -> a.getAci() != null).map(a -> a.getAci().toByteString()).collect(Collectors.toList()) : Collections.emptyList());
|
||||
blockedMessage.numbers(blocked.individuals.stream().filter(a -> a.getE164() != null).map(a -> a.getE164()).collect(Collectors.toList()));
|
||||
blockedMessage.groupIds(blocked.groupIds.stream().map(ByteString::of).collect(Collectors.toList()));
|
||||
|
||||
@@ -1600,6 +1613,7 @@ public class SignalServiceMessageSender {
|
||||
|
||||
if (message.getPerson().isPresent()) {
|
||||
responseMessage.threadAci(message.getPerson().get().toString());
|
||||
responseMessage.threadAciBinary(useBinaryId ? message.getPerson().get().toByteString() : null);
|
||||
}
|
||||
|
||||
switch (message.getType()) {
|
||||
@@ -1697,6 +1711,7 @@ public class SignalServiceMessageSender {
|
||||
verifiedMessageBuilder.nullMessage(ByteString.of(nullMessage));
|
||||
verifiedMessageBuilder.identityKey(ByteString.of(verifiedMessage.getIdentityKey().serialize()));
|
||||
verifiedMessageBuilder.destinationAci(verifiedMessage.getDestination().getServiceId().toString());
|
||||
verifiedMessageBuilder.destinationAciBinary(useBinaryId ? verifiedMessage.getDestination().getServiceId().toByteString() : null);
|
||||
|
||||
|
||||
switch (verifiedMessage.getVerified()) {
|
||||
|
||||
@@ -49,6 +49,7 @@ import org.whispersystems.signalservice.api.push.DistributionId;
|
||||
import org.whispersystems.signalservice.api.push.ServiceId;
|
||||
import org.whispersystems.signalservice.api.push.ServiceId.ACI;
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||
import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||
import org.whispersystems.signalservice.internal.push.Content;
|
||||
import org.whispersystems.signalservice.internal.push.Envelope;
|
||||
import org.whispersystems.signalservice.internal.push.OutgoingPushMessage;
|
||||
@@ -171,32 +172,36 @@ public class SignalServiceCipher {
|
||||
ProtocolInvalidKeyIdException, ProtocolNoSessionException,
|
||||
SelfSendException, InvalidMessageStructureException
|
||||
{
|
||||
ServiceId sourceServiceId = ServiceId.parseOrNull(envelope.sourceServiceId, envelope.sourceServiceIdBinary);
|
||||
try {
|
||||
ServiceId destinationServiceId = ServiceId.parseOrNull(envelope.destinationServiceId, envelope.destinationServiceIdBinary);
|
||||
String destinationStr = (destinationServiceId != null) ? destinationServiceId.toString() : "";
|
||||
String serverGuid = UuidUtil.getStringUUID(envelope.serverGuid, envelope.serverGuidBinary);
|
||||
|
||||
byte[] paddedMessage;
|
||||
SignalServiceMetadata metadata;
|
||||
|
||||
if (envelope.sourceServiceId == null && envelope.type != Envelope.Type.UNIDENTIFIED_SENDER) {
|
||||
if (sourceServiceId == null && envelope.type != Envelope.Type.UNIDENTIFIED_SENDER) {
|
||||
throw new InvalidMessageStructureException("Non-UD envelope is missing a UUID!");
|
||||
}
|
||||
|
||||
if (envelope.type == Envelope.Type.PREKEY_BUNDLE) {
|
||||
SignalProtocolAddress sourceAddress = new SignalProtocolAddress(envelope.sourceServiceId, envelope.sourceDevice);
|
||||
SignalProtocolAddress sourceAddress = new SignalProtocolAddress(sourceServiceId.toString(), envelope.sourceDevice);
|
||||
SignalSessionCipher sessionCipher = new SignalSessionCipher(sessionLock, new SessionCipher(signalProtocolStore, sourceAddress));
|
||||
|
||||
paddedMessage = sessionCipher.decrypt(new PreKeySignalMessage(envelope.content.toByteArray()));
|
||||
metadata = new SignalServiceMetadata(getSourceAddress(envelope), envelope.sourceDevice, envelope.timestamp, envelope.serverTimestamp, serverDeliveredTimestamp, false, envelope.serverGuid, Optional.empty(), envelope.destinationServiceId);
|
||||
metadata = new SignalServiceMetadata(getSourceAddress(envelope), envelope.sourceDevice, envelope.timestamp, envelope.serverTimestamp, serverDeliveredTimestamp, false, serverGuid, Optional.empty(), destinationStr);
|
||||
|
||||
signalProtocolStore.clearSenderKeySharedWith(Collections.singleton(sourceAddress));
|
||||
} else if (envelope.type == Envelope.Type.CIPHERTEXT) {
|
||||
SignalProtocolAddress sourceAddress = new SignalProtocolAddress(envelope.sourceServiceId, envelope.sourceDevice);
|
||||
SignalProtocolAddress sourceAddress = new SignalProtocolAddress(sourceServiceId.toString(), envelope.sourceDevice);
|
||||
SignalSessionCipher sessionCipher = new SignalSessionCipher(sessionLock, new SessionCipher(signalProtocolStore, sourceAddress));
|
||||
|
||||
paddedMessage = sessionCipher.decrypt(new SignalMessage(envelope.content.toByteArray()));
|
||||
metadata = new SignalServiceMetadata(getSourceAddress(envelope), envelope.sourceDevice, envelope.timestamp, envelope.serverTimestamp, serverDeliveredTimestamp, false, envelope.serverGuid, Optional.empty(), envelope.destinationServiceId);
|
||||
metadata = new SignalServiceMetadata(getSourceAddress(envelope), envelope.sourceDevice, envelope.timestamp, envelope.serverTimestamp, serverDeliveredTimestamp, false, serverGuid, Optional.empty(), destinationStr);
|
||||
} else if (envelope.type == Envelope.Type.PLAINTEXT_CONTENT) {
|
||||
paddedMessage = new PlaintextContent(envelope.content.toByteArray()).getBody();
|
||||
metadata = new SignalServiceMetadata(getSourceAddress(envelope), envelope.sourceDevice, envelope.timestamp, envelope.serverTimestamp, serverDeliveredTimestamp, false, envelope.serverGuid, Optional.empty(), envelope.destinationServiceId);
|
||||
metadata = new SignalServiceMetadata(getSourceAddress(envelope), envelope.sourceDevice, envelope.timestamp, envelope.serverTimestamp, serverDeliveredTimestamp, false, serverGuid, Optional.empty(), destinationStr);
|
||||
} else if (envelope.type == Envelope.Type.UNIDENTIFIED_SENDER) {
|
||||
SignalSealedSessionCipher sealedSessionCipher = new SignalSealedSessionCipher(sessionLock, new SealedSessionCipher(signalProtocolStore, localAddress.getServiceId().getRawUuid(), localAddress.getNumber().orElse(null), localDeviceId));
|
||||
DecryptionResult result = sealedSessionCipher.decrypt(certificateValidator, envelope.content.toByteArray(), envelope.serverTimestamp);
|
||||
@@ -204,7 +209,7 @@ public class SignalServiceCipher {
|
||||
Optional<byte[]> groupId = result.getGroupId();
|
||||
boolean needsReceipt = true;
|
||||
|
||||
if (envelope.sourceServiceId != null) {
|
||||
if (sourceServiceId != null) {
|
||||
Log.w(TAG, "[" + envelope.timestamp + "] Received a UD-encrypted message sent over an identified channel. Marking as needsReceipt=false");
|
||||
needsReceipt = false;
|
||||
}
|
||||
@@ -214,7 +219,7 @@ public class SignalServiceCipher {
|
||||
}
|
||||
|
||||
paddedMessage = result.getPaddedMessage();
|
||||
metadata = new SignalServiceMetadata(resultAddress, result.getDeviceId(), envelope.timestamp, envelope.serverTimestamp, serverDeliveredTimestamp, needsReceipt, envelope.serverGuid, groupId, envelope.destinationServiceId);
|
||||
metadata = new SignalServiceMetadata(resultAddress, result.getDeviceId(), envelope.timestamp, envelope.serverTimestamp, serverDeliveredTimestamp, needsReceipt, serverGuid, groupId, destinationStr);
|
||||
} else {
|
||||
throw new InvalidMetadataMessageException("Unknown type: " + envelope.type);
|
||||
}
|
||||
@@ -224,26 +229,26 @@ public class SignalServiceCipher {
|
||||
|
||||
return new Plaintext(metadata, data);
|
||||
} catch (DuplicateMessageException e) {
|
||||
throw new ProtocolDuplicateMessageException(e, envelope.sourceServiceId, envelope.sourceDevice);
|
||||
throw new ProtocolDuplicateMessageException(e, sourceServiceId.toString(), envelope.sourceDevice);
|
||||
} catch (LegacyMessageException e) {
|
||||
throw new ProtocolLegacyMessageException(e, envelope.sourceServiceId, envelope.sourceDevice);
|
||||
throw new ProtocolLegacyMessageException(e, sourceServiceId.toString(), envelope.sourceDevice);
|
||||
} catch (InvalidMessageException e) {
|
||||
throw new ProtocolInvalidMessageException(e, envelope.sourceServiceId, envelope.sourceDevice);
|
||||
throw new ProtocolInvalidMessageException(e, sourceServiceId.toString(), envelope.sourceDevice);
|
||||
} catch (InvalidKeyIdException e) {
|
||||
throw new ProtocolInvalidKeyIdException(e, envelope.sourceServiceId, envelope.sourceDevice);
|
||||
throw new ProtocolInvalidKeyIdException(e, sourceServiceId.toString(), envelope.sourceDevice);
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new ProtocolInvalidKeyException(e, envelope.sourceServiceId, envelope.sourceDevice);
|
||||
throw new ProtocolInvalidKeyException(e, sourceServiceId.toString(), envelope.sourceDevice);
|
||||
} catch (UntrustedIdentityException e) {
|
||||
throw new ProtocolUntrustedIdentityException(e, envelope.sourceServiceId, envelope.sourceDevice);
|
||||
throw new ProtocolUntrustedIdentityException(e, sourceServiceId.toString(), envelope.sourceDevice);
|
||||
} catch (InvalidVersionException e) {
|
||||
throw new ProtocolInvalidVersionException(e, envelope.sourceServiceId, envelope.sourceDevice);
|
||||
throw new ProtocolInvalidVersionException(e, sourceServiceId.toString(), envelope.sourceDevice);
|
||||
} catch (NoSessionException e) {
|
||||
throw new ProtocolNoSessionException(e, envelope.sourceServiceId, envelope.sourceDevice);
|
||||
throw new ProtocolNoSessionException(e, sourceServiceId.toString(), envelope.sourceDevice);
|
||||
}
|
||||
}
|
||||
|
||||
private static SignalServiceAddress getSourceAddress(Envelope envelope) {
|
||||
return new SignalServiceAddress(ServiceId.parseOrNull(envelope.sourceServiceId));
|
||||
return new SignalServiceAddress(ServiceId.parseOrNull(envelope.sourceServiceId, envelope.sourceServiceIdBinary));
|
||||
}
|
||||
|
||||
private static class Plaintext {
|
||||
|
||||
@@ -19,6 +19,7 @@ import org.whispersystems.signalservice.internal.push.ReceiptMessage
|
||||
import org.whispersystems.signalservice.internal.push.StoryMessage
|
||||
import org.whispersystems.signalservice.internal.push.SyncMessage
|
||||
import org.whispersystems.signalservice.internal.push.TypingMessage
|
||||
import org.whispersystems.signalservice.internal.util.Util
|
||||
|
||||
/**
|
||||
* Validates an [Envelope] and its decrypted [Content] so that we know the message can be processed safely
|
||||
@@ -36,7 +37,8 @@ object EnvelopeContentValidator {
|
||||
validatePlaintextContent(content)?.let { return it }
|
||||
}
|
||||
|
||||
if (envelope.sourceServiceId != null && envelope.sourceServiceId.isInvalidServiceId()) {
|
||||
val sourceServiceId = ServiceId.parseOrNull(envelope.sourceServiceId, envelope.sourceServiceIdBinary)
|
||||
if (Util.anyNotNull(envelope.sourceServiceId, envelope.sourceServiceIdBinary) && sourceServiceId.isNullOrInvalidServiceId()) {
|
||||
return Result.Invalid("Envelope had an invalid sourceServiceId!")
|
||||
}
|
||||
|
||||
@@ -83,7 +85,7 @@ object EnvelopeContentValidator {
|
||||
return Result.Invalid("[DataMessage] Timestamps don't match! envelope: ${envelope.timestamp}, content: ${dataMessage.timestamp}")
|
||||
}
|
||||
|
||||
if (dataMessage.quote != null && dataMessage.quote.authorAci.isNullOrInvalidAci()) {
|
||||
if (dataMessage.quote != null && ACI.parseOrNull(dataMessage.quote.authorAci, dataMessage.quote.authorAciBinary).isNullOrInvalidServiceId()) {
|
||||
return Result.Invalid("[DataMessage] Invalid ACI on quote!")
|
||||
}
|
||||
|
||||
@@ -95,7 +97,7 @@ object EnvelopeContentValidator {
|
||||
return Result.Invalid("[DataMessage] Invalid AttachmentPointer on DataMessage.previewList.image!")
|
||||
}
|
||||
|
||||
if (dataMessage.bodyRanges.any { it.mentionAci != null && it.mentionAci.isNullOrInvalidAci() }) {
|
||||
if (dataMessage.bodyRanges.any { Util.anyNotNull(it.mentionAci, it.mentionAciBinary) && ACI.parseOrNull(it.mentionAci, it.mentionAciBinary).isNullOrInvalidServiceId() }) {
|
||||
return Result.Invalid("[DataMessage] Invalid ACI on body range!")
|
||||
}
|
||||
|
||||
@@ -108,7 +110,7 @@ object EnvelopeContentValidator {
|
||||
return Result.Invalid("[DataMessage] Missing timestamp on DataMessage.reaction!")
|
||||
}
|
||||
|
||||
if (dataMessage.reaction.targetAuthorAci.isNullOrInvalidAci()) {
|
||||
if (ACI.parseOrNull(dataMessage.reaction.targetAuthorAci, dataMessage.reaction.targetAuthorAciBinary).isNullOrInvalidServiceId()) {
|
||||
return Result.Invalid("[DataMessage] Invalid ACI on DataMessage.reaction!")
|
||||
}
|
||||
}
|
||||
@@ -117,7 +119,7 @@ object EnvelopeContentValidator {
|
||||
return Result.Invalid("[DataMessage] Missing timestamp on DataMessage.delete!")
|
||||
}
|
||||
|
||||
if (dataMessage.storyContext != null && dataMessage.storyContext.authorAci.isNullOrInvalidAci()) {
|
||||
if (dataMessage.storyContext != null && ACI.parseOrNull(dataMessage.storyContext.authorAci, dataMessage.storyContext.authorAciBinary).isNullOrInvalidServiceId()) {
|
||||
return Result.Invalid("[DataMessage] Invalid ACI on DataMessage.storyContext!")
|
||||
}
|
||||
|
||||
@@ -166,14 +168,14 @@ object EnvelopeContentValidator {
|
||||
|
||||
private fun validateSyncMessage(envelope: Envelope, syncMessage: SyncMessage, localAci: ACI): Result {
|
||||
// Source serviceId was already determined to be a valid serviceId in general
|
||||
val sourceServiceId = ServiceId.parseOrThrow(envelope.sourceServiceId!!)
|
||||
val sourceServiceId = ServiceId.parseOrThrow(envelope.sourceServiceId, envelope.sourceServiceIdBinary)
|
||||
|
||||
if (sourceServiceId != localAci) {
|
||||
return Result.Invalid("[SyncMessage] Source was not our own account!")
|
||||
}
|
||||
|
||||
if (syncMessage.sent != null) {
|
||||
val validAddress = syncMessage.sent.destinationServiceId.isValidServiceId()
|
||||
val validAddress = ServiceId.parseOrNull(syncMessage.sent.destinationServiceId, syncMessage.sent.destinationServiceIdBinary) != null
|
||||
val hasDataGroup = syncMessage.sent.message?.groupV2 != null
|
||||
val hasStoryGroup = syncMessage.sent.storyMessage?.group != null
|
||||
val hasStoryManifest = syncMessage.sent.storyMessageRecipients.isNotEmpty()
|
||||
@@ -196,7 +198,7 @@ object EnvelopeContentValidator {
|
||||
}
|
||||
|
||||
for (status in syncMessage.sent.unidentifiedStatus) {
|
||||
if (status.destinationServiceId.isNullOrInvalidServiceId()) {
|
||||
if (ServiceId.parseOrNull(status.destinationServiceId, status.destinationServiceIdBinary).isNullOrInvalidServiceId()) {
|
||||
return Result.Invalid("[SyncMessage] Invalid ServiceId in SyncMessage.sent.unidentifiedStatusList!")
|
||||
}
|
||||
}
|
||||
@@ -214,19 +216,19 @@ object EnvelopeContentValidator {
|
||||
}
|
||||
}
|
||||
|
||||
if (syncMessage.read.any { it.senderAci.isNullOrInvalidAci() }) {
|
||||
if (syncMessage.read.any { ACI.parseOrNull(it.senderAci, it.senderAciBinary).isNullOrInvalidServiceId() }) {
|
||||
return Result.Invalid("[SyncMessage] Invalid ACI in SyncMessage.readList!")
|
||||
}
|
||||
|
||||
if (syncMessage.viewed.any { it.senderAci.isNullOrInvalidAci() }) {
|
||||
if (syncMessage.viewed.any { ACI.parseOrNull(it.senderAci, it.senderAciBinary).isNullOrInvalidServiceId() }) {
|
||||
return Result.Invalid("[SyncMessage] Invalid ACI in SyncMessage.viewList!")
|
||||
}
|
||||
|
||||
if (syncMessage.viewOnceOpen != null && syncMessage.viewOnceOpen.senderAci.isNullOrInvalidAci()) {
|
||||
if (syncMessage.viewOnceOpen != null && ACI.parseOrNull(syncMessage.viewOnceOpen.senderAci, syncMessage.viewOnceOpen.senderAciBinary).isNullOrInvalidServiceId()) {
|
||||
return Result.Invalid("[SyncMessage] Invalid ACI in SyncMessage.viewOnceOpen!")
|
||||
}
|
||||
|
||||
if (syncMessage.verified != null && syncMessage.verified.destinationAci.isNullOrInvalidAci()) {
|
||||
if (syncMessage.verified != null && ACI.parseOrNull(syncMessage.verified.destinationAci, syncMessage.verified.destinationAciBinary).isNullOrInvalidServiceId()) {
|
||||
return Result.Invalid("[SyncMessage] Invalid ACI in SyncMessage.verified!")
|
||||
}
|
||||
|
||||
@@ -234,11 +236,11 @@ object EnvelopeContentValidator {
|
||||
return Result.Invalid("[SyncMessage] Missing packId in stickerPackOperationList!")
|
||||
}
|
||||
|
||||
if (syncMessage.blocked != null && syncMessage.blocked.acis.any { it.isNullOrInvalidAci() }) {
|
||||
if (syncMessage.blocked != null && syncMessage.blocked.acis.any { it.isNullOrInvalidAci() } && syncMessage.blocked.acisBinary.any { it.isNullOrInvalidAci() }) {
|
||||
return Result.Invalid("[SyncMessage] Invalid ACI in SyncMessage.blocked!")
|
||||
}
|
||||
|
||||
if (syncMessage.messageRequestResponse != null && syncMessage.messageRequestResponse.groupId == null && syncMessage.messageRequestResponse.threadAci.isNullOrInvalidAci()) {
|
||||
if (syncMessage.messageRequestResponse != null && syncMessage.messageRequestResponse.groupId == null && ACI.parseOrNull(syncMessage.messageRequestResponse.threadAci, syncMessage.messageRequestResponse.threadAciBinary).isNullOrInvalidServiceId()) {
|
||||
return Result.Invalid("[SyncMessage] Invalid ACI in SyncMessage.messageRequestResponse!")
|
||||
}
|
||||
|
||||
@@ -329,7 +331,7 @@ object EnvelopeContentValidator {
|
||||
return Result.Invalid("[EditMessage] Invalid AttachmentPointer on DataMessage.previewList.image!")
|
||||
}
|
||||
|
||||
if (dataMessage.bodyRanges.any { it.mentionAci != null && it.mentionAci.isNullOrInvalidAci() }) {
|
||||
if (dataMessage.bodyRanges.any { Util.anyNotNull(it.mentionAci, it.mentionAciBinary) && ACI.parseOrNull(it.mentionAci, it.mentionAciBinary).isNullOrInvalidServiceId() }) {
|
||||
return Result.Invalid("[EditMessage] Invalid UUID on body range!")
|
||||
}
|
||||
|
||||
@@ -362,11 +364,6 @@ object EnvelopeContentValidator {
|
||||
return parsed == null || parsed.isUnknown
|
||||
}
|
||||
|
||||
private fun String.isInvalidServiceId(): Boolean {
|
||||
val parsed = ServiceId.parseOrNull(this)
|
||||
return parsed == null || parsed.isUnknown
|
||||
}
|
||||
|
||||
private fun String?.isNullOrInvalidAci(): Boolean {
|
||||
val parsed = ACI.parseOrNull(this)
|
||||
return parsed == null || parsed.isUnknown
|
||||
@@ -382,6 +379,10 @@ object EnvelopeContentValidator {
|
||||
return parsed == null || parsed.isUnknown
|
||||
}
|
||||
|
||||
private fun ServiceId?.isNullOrInvalidServiceId(): Boolean {
|
||||
return this == null || this.isUnknown
|
||||
}
|
||||
|
||||
private fun Content?.meetsStoryFlagCriteria(): Boolean {
|
||||
return when {
|
||||
this == null -> false
|
||||
|
||||
@@ -16,6 +16,7 @@ import org.signal.libsignal.zkgroup.profiles.ProfileKey;
|
||||
import org.whispersystems.signalservice.api.push.ServiceId;
|
||||
import org.whispersystems.signalservice.api.push.ServiceId.ACI;
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||
import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||
import org.whispersystems.signalservice.internal.push.ContactDetails;
|
||||
import org.whispersystems.signalservice.internal.util.Util;
|
||||
|
||||
@@ -42,11 +43,11 @@ public class DeviceContactsInputStream extends ChunkedInputStream {
|
||||
|
||||
ContactDetails details = ContactDetails.ADAPTER.decode(detailsSerialized);
|
||||
|
||||
if (!SignalServiceAddress.isValidAddress(details.aci, details.number)) {
|
||||
if (ACI.parseOrNull(details.aci, details.aciBinary) == null) {
|
||||
throw new IOException("Missing contact address!");
|
||||
}
|
||||
|
||||
Optional<ACI> aci = Optional.ofNullable(ACI.parseOrNull(details.aci));
|
||||
Optional<ACI> aci = Optional.ofNullable(ACI.parseOrNull(details.aci, details.aciBinary));
|
||||
Optional<String> e164 = Optional.ofNullable(details.number);
|
||||
Optional<String> name = Optional.ofNullable(details.name);
|
||||
Optional<DeviceContactAvatar> avatar = Optional.empty();
|
||||
|
||||
@@ -16,8 +16,11 @@ import okio.ByteString;
|
||||
|
||||
public class DeviceContactsOutputStream extends ChunkedOutputStream {
|
||||
|
||||
public DeviceContactsOutputStream(OutputStream out) {
|
||||
private final boolean useBinaryId;
|
||||
|
||||
public DeviceContactsOutputStream(OutputStream out, boolean useBinaryId) {
|
||||
super(out);
|
||||
this.useBinaryId = useBinaryId;
|
||||
}
|
||||
|
||||
public void write(DeviceContact contact) throws IOException {
|
||||
@@ -40,6 +43,7 @@ public class DeviceContactsOutputStream extends ChunkedOutputStream {
|
||||
|
||||
if (contact.getAci().isPresent()) {
|
||||
contactDetails.aci(contact.getAci().get().toString());
|
||||
contactDetails.aciBinary(useBinaryId ? contact.getAci().get().toByteString() : null);
|
||||
}
|
||||
|
||||
if (contact.getE164().isPresent()) {
|
||||
|
||||
@@ -86,7 +86,7 @@ sealed class ServiceId(val libSignalServiceId: LibSignalServiceId) {
|
||||
/** Parses a ServiceId serialized as a string. Crashes if the ServiceId is invalid. */
|
||||
@JvmStatic
|
||||
@Throws(IllegalArgumentException::class)
|
||||
fun parseOrThrow(raw: String): ServiceId = parseOrNull(raw) ?: throw IllegalArgumentException("Invalid ServiceId!")
|
||||
fun parseOrThrow(raw: String?): ServiceId = parseOrNull(raw) ?: throw IllegalArgumentException("Invalid ServiceId!")
|
||||
|
||||
/** Parses a ServiceId serialized as a byte array. Crashes if the ServiceId is invalid. */
|
||||
@JvmStatic
|
||||
@@ -104,6 +104,19 @@ sealed class ServiceId(val libSignalServiceId: LibSignalServiceId) {
|
||||
fun parseOrUnknown(bytes: ByteString): ServiceId {
|
||||
return parseOrNull(bytes) ?: ACI.UNKNOWN
|
||||
}
|
||||
|
||||
/** Parses a ServiceId serialized as either a byteString or string, with preference to the byteString if available. Returns null if invalid. */
|
||||
@JvmStatic
|
||||
fun parseOrNull(raw: String?, bytes: ByteString?): ServiceId? {
|
||||
return parseOrNull(bytes) ?: parseOrNull(raw)
|
||||
}
|
||||
|
||||
/** Parses a ServiceId serialized as either a byteString or string, with preference to the byteString if available. Throws if invalid. */
|
||||
@JvmStatic
|
||||
@Throws(IllegalArgumentException::class)
|
||||
fun parseOrThrow(raw: String?, bytes: ByteString?): ServiceId {
|
||||
return parseOrNull(bytes) ?: parseOrThrow(raw)
|
||||
}
|
||||
}
|
||||
|
||||
val rawUuid: UUID = libSignalServiceId.rawUUID
|
||||
@@ -144,7 +157,7 @@ sealed class ServiceId(val libSignalServiceId: LibSignalServiceId) {
|
||||
fun parseOrNull(raw: ByteArray?): ACI? = ServiceId.parseOrNull(raw).let { if (it is ACI) it else null }
|
||||
|
||||
@JvmStatic
|
||||
fun parseOrNull(bytes: ByteString): ACI? = parseOrNull(bytes.toByteArray())
|
||||
fun parseOrNull(bytes: ByteString?): ACI? = parseOrNull(bytes?.toByteArray())
|
||||
|
||||
@JvmStatic
|
||||
@Throws(IllegalArgumentException::class)
|
||||
@@ -163,6 +176,19 @@ sealed class ServiceId(val libSignalServiceId: LibSignalServiceId) {
|
||||
|
||||
@JvmStatic
|
||||
fun parseOrUnknown(raw: String?): ACI = parseOrNull(raw) ?: UNKNOWN
|
||||
|
||||
/** Parses either a byteString or string as an ACI, with preference to the byteString if available. Returns null if invalid or missing. */
|
||||
@JvmStatic
|
||||
fun parseOrNull(raw: String?, bytes: ByteString?): ACI? {
|
||||
return parseOrNull(bytes) ?: parseOrNull(raw)
|
||||
}
|
||||
|
||||
/** Parses either a byteString or string as an ACI, with preference to the byteString if available. Throws if invalid or missing. */
|
||||
@JvmStatic
|
||||
@Throws(IllegalArgumentException::class)
|
||||
fun parseOrThrow(raw: String?, bytes: ByteString?): ACI {
|
||||
return parseOrNull(bytes) ?: parseOrThrow(raw)
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String = super.toString()
|
||||
@@ -212,7 +238,7 @@ sealed class ServiceId(val libSignalServiceId: LibSignalServiceId) {
|
||||
|
||||
/** Parses a [ByteString] as a PNI, regardless if the `PNI:` prefix is present or not. Only use this if you are certain that what you're reading is a PNI. */
|
||||
@JvmStatic
|
||||
fun parseOrNull(bytes: ByteString): PNI? = parseOrNull(bytes.toByteArray())
|
||||
fun parseOrNull(bytes: ByteString?): PNI? = parseOrNull(bytes?.toByteArray())
|
||||
|
||||
/** Parses a string as a PNI, regardless if the `PNI:` prefix is present or not. Only use this if you are certain that what you're reading is a PNI. */
|
||||
@JvmStatic
|
||||
@@ -231,6 +257,24 @@ sealed class ServiceId(val libSignalServiceId: LibSignalServiceId) {
|
||||
|
||||
/** Parses a string as a PNI, expecting that the value has a `PNI:` prefix. If it does not have the prefix (or is otherwise invalid), this will return null. */
|
||||
fun parsePrefixedOrNull(raw: String?): PNI? = ServiceId.parseOrNull(raw).let { if (it is PNI) it else null }
|
||||
|
||||
/** Parses either a byteString or string as a PNI, with preference to the byteString. Expecting that the value has a `PNI:` prefix. If it does not have the prefix (or is otherwise invalid), this will return null. */
|
||||
fun parsePrefixedOrNull(raw: String?, bytes: ByteString?): PNI? {
|
||||
return parseOrNull(bytes).let { if (it is PNI) it else null } ?: parsePrefixedOrNull(raw)
|
||||
}
|
||||
|
||||
/** Parses either a byteString or string as a PNI, with preference to the byteString. Only use this if you are certain what you're reading is a PNI. Returns null if invalid. */
|
||||
@JvmStatic
|
||||
fun parseOrNull(raw: String?, bytes: ByteString?): PNI? {
|
||||
return parseOrNull(bytes) ?: parseOrNull(raw)
|
||||
}
|
||||
|
||||
/** Parses either a byteString or string as a PNI, with preference to the byteString. Only use this if you are certain what you're reading is a PNI. Throws if missing or invalid. */
|
||||
@JvmStatic
|
||||
@Throws(IllegalArgumentException::class)
|
||||
fun parseOrThrow(raw: String?, bytes: ByteString?): PNI {
|
||||
return parseOrNull(bytes) ?: parseOrThrow(raw)
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String = super.toString()
|
||||
|
||||
@@ -8,7 +8,6 @@ package org.whispersystems.signalservice.api.storage
|
||||
import okio.ByteString
|
||||
import okio.ByteString.Companion.toByteString
|
||||
import org.signal.core.util.isNotEmpty
|
||||
import org.signal.core.util.isNotNullOrBlank
|
||||
import org.whispersystems.signalservice.api.payments.PaymentsConstants
|
||||
import org.whispersystems.signalservice.api.push.ServiceId
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
||||
@@ -58,6 +57,6 @@ fun AccountRecord.Builder.safeSetBackupsSubscriber(subscriberId: ByteString, iap
|
||||
}
|
||||
|
||||
fun AccountRecord.PinnedConversation.Contact.toSignalServiceAddress(): SignalServiceAddress {
|
||||
val serviceId = ServiceId.parseOrNull(this.serviceId)
|
||||
val serviceId = ServiceId.parseOrNull(this.serviceId, this.serviceIdBinary)
|
||||
return SignalServiceAddress(serviceId, this.e164)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import org.whispersystems.signalservice.api.push.ServiceId
|
||||
import org.whispersystems.signalservice.internal.storage.protos.ContactRecord
|
||||
|
||||
val ContactRecord.signalAci: ServiceId.ACI?
|
||||
get() = ServiceId.ACI.parseOrNull(this.aci)
|
||||
get() = ServiceId.ACI.parseOrNull(this.aci, this.aciBinary)
|
||||
|
||||
val ContactRecord.signalPni: ServiceId.PNI?
|
||||
get() = ServiceId.PNI.parseOrNull(this.pni)
|
||||
get() = ServiceId.PNI.parseOrNull(this.pni, this.pniBinary)
|
||||
|
||||
@@ -11,7 +11,10 @@ import org.whispersystems.signalservice.internal.storage.protos.StoryDistributio
|
||||
|
||||
val StoryDistributionListRecord.recipientServiceAddresses: List<SignalServiceAddress>
|
||||
get() {
|
||||
return this.recipientServiceIds
|
||||
.mapNotNull { ServiceId.parseOrNull(it) }
|
||||
.map { SignalServiceAddress(it) }
|
||||
val serviceIds = if (this.recipientServiceIdsBinary.isNotEmpty()) {
|
||||
this.recipientServiceIdsBinary.mapNotNull { ServiceId.parseOrNull(it) }
|
||||
} else {
|
||||
this.recipientServiceIds.mapNotNull { ServiceId.parseOrNull(it) }
|
||||
}
|
||||
return serviceIds.map { SignalServiceAddress(it) }
|
||||
}
|
||||
|
||||
@@ -78,6 +78,11 @@ public final class UuidUtil {
|
||||
return parseOrNull(bytes.toByteArray());
|
||||
}
|
||||
|
||||
public static @Nullable String getStringUUID(@Nullable String stringId, @Nullable ByteString bytes) {
|
||||
UUID uuid = parseOrNull(bytes);
|
||||
return (uuid != null) ? uuid.toString() : stringId;
|
||||
}
|
||||
|
||||
public static UUID fromByteStringOrUnknown(ByteString bytes) {
|
||||
UUID uuid = fromByteStringOrNull(bytes);
|
||||
return uuid != null ? uuid : UNKNOWN_UUID;
|
||||
@@ -88,7 +93,7 @@ public final class UuidUtil {
|
||||
}
|
||||
|
||||
public static UUID parseOrNull(ByteString byteString) {
|
||||
return parseOrNull(byteString.toByteArray());
|
||||
return byteString != null ? parseOrNull(byteString.toByteArray()): null;
|
||||
}
|
||||
|
||||
public static List<UUID> fromByteStrings(Collection<ByteString> byteStringCollection) {
|
||||
|
||||
@@ -164,4 +164,22 @@ public class Util {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean anyNotNull(Object... values) {
|
||||
for (Object value : values) {
|
||||
if (value != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean allAreNull(Object... values) {
|
||||
for (Object value : values) {
|
||||
if (value != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,11 @@ message Envelope {
|
||||
optional bool story = 16; // indicates that the content is a story.
|
||||
optional bytes report_spam_token = 17; // token sent when reporting spam
|
||||
reserved 18; // internal server use
|
||||
// next: 19
|
||||
optional bytes sourceServiceIdBinary = 19; // service ID binary (i.e. 16 byte UUID for ACI, 1 byte prefix + 16 byte UUID for PNI)
|
||||
optional bytes destinationServiceIdBinary = 20; // service ID binary (i.e. 16 byte UUID for ACI, 1 byte prefix + 16 byte UUID for PNI)
|
||||
optional bytes serverGuidBinary = 21; // 16-byte UUID
|
||||
optional bytes updatedPniBinary = 22; // 16-byte UUID
|
||||
// next: 22
|
||||
}
|
||||
|
||||
message Content {
|
||||
@@ -193,6 +197,7 @@ message DataMessage {
|
||||
repeated QuotedAttachment attachments = 4;
|
||||
repeated BodyRange bodyRanges = 6;
|
||||
optional Type type = 7;
|
||||
optional bytes authorAciBinary = 8; // 16-byte UUID
|
||||
}
|
||||
|
||||
message Contact {
|
||||
@@ -277,6 +282,7 @@ message DataMessage {
|
||||
reserved /* targetAuthorE164 */ 3;
|
||||
optional string targetAuthorAci = 4;
|
||||
optional uint64 targetSentTimestamp = 5;
|
||||
optional bytes targetAuthorAciBinary = 6; // 16-byte UUID
|
||||
}
|
||||
|
||||
message Delete {
|
||||
@@ -290,6 +296,7 @@ message DataMessage {
|
||||
message StoryContext {
|
||||
optional string authorAci = 1;
|
||||
optional uint64 sentTimestamp = 2;
|
||||
optional bytes authorAciBinary = 3; // 16-byte UUID
|
||||
}
|
||||
|
||||
enum ProtocolVersion {
|
||||
@@ -442,6 +449,7 @@ message Verified {
|
||||
optional bytes identityKey = 2;
|
||||
optional State state = 3;
|
||||
optional bytes nullMessage = 4;
|
||||
optional bytes destinationAciBinary = 6; // 16-byte UUID
|
||||
}
|
||||
|
||||
message SyncMessage {
|
||||
@@ -452,6 +460,7 @@ message SyncMessage {
|
||||
optional bool unidentified = 2;
|
||||
reserved /*destinationPni */ 4;
|
||||
optional bytes destinationPniIdentityKey = 5; // Only set for PNI destinations
|
||||
optional bytes destinationServiceIdBinary = 6; // service ID binary (i.e. 16 byte UUID for ACI, 1 byte prefix + 16 byte UUID for PNI)
|
||||
}
|
||||
|
||||
message StoryMessageRecipient {
|
||||
@@ -459,6 +468,7 @@ message SyncMessage {
|
||||
repeated string distributionListIds = 2;
|
||||
optional bool isAllowedToReply = 3;
|
||||
reserved /*destinationPni */ 4;
|
||||
optional bytes destinationServiceIdBinary = 5; // service ID binary (i.e. 16 byte UUID for ACI, 1 byte prefix + 16 byte UUID for PNI)
|
||||
}
|
||||
|
||||
optional string destinationE164 = 1;
|
||||
@@ -472,7 +482,8 @@ message SyncMessage {
|
||||
repeated StoryMessageRecipient storyMessageRecipients = 9;
|
||||
optional EditMessage editMessage = 10;
|
||||
reserved /*destinationPni */ 11;
|
||||
// Next ID: 12
|
||||
optional bytes destinationServiceIdBinary = 12; // service ID binary (i.e. 16 byte UUID for ACI, 1 byte prefix + 16 byte UUID for PNI)
|
||||
// Next ID: 13
|
||||
}
|
||||
|
||||
message Contacts {
|
||||
@@ -484,6 +495,7 @@ message SyncMessage {
|
||||
repeated string numbers = 1;
|
||||
repeated string acis = 3;
|
||||
repeated bytes groupIds = 2;
|
||||
repeated bytes acisBinary = 4; // 16-byte UUID
|
||||
}
|
||||
|
||||
message Request {
|
||||
@@ -504,12 +516,14 @@ message SyncMessage {
|
||||
reserved /*senderE164*/ 1;
|
||||
optional string senderAci = 3;
|
||||
optional uint64 timestamp = 2;
|
||||
optional bytes senderAciBinary = 4; // 16-byte UUID
|
||||
}
|
||||
|
||||
message Viewed {
|
||||
reserved /*senderE164*/ 1;
|
||||
optional string senderAci = 3;
|
||||
optional uint64 timestamp = 2;
|
||||
optional bytes senderAciBinary = 4; // 16-byte UUID
|
||||
}
|
||||
|
||||
message Configuration {
|
||||
@@ -536,6 +550,7 @@ message SyncMessage {
|
||||
reserved /*senderE164*/ 1;
|
||||
optional string senderAci = 3;
|
||||
optional uint64 timestamp = 2;
|
||||
optional bytes senderAciBinary = 4; // 16-byte UUID
|
||||
}
|
||||
|
||||
message FetchLatest {
|
||||
@@ -576,6 +591,7 @@ message SyncMessage {
|
||||
optional string threadAci = 2;
|
||||
optional bytes groupId = 3;
|
||||
optional Type type = 4;
|
||||
optional bytes threadAciBinary = 5; // 16-byte UUID
|
||||
}
|
||||
|
||||
message OutgoingPayment {
|
||||
@@ -823,6 +839,7 @@ message ContactDetails {
|
||||
|
||||
optional string number = 1;
|
||||
optional string aci = 9;
|
||||
optional bytes aciBinary = 13; // 16-byte UUID
|
||||
optional string name = 2;
|
||||
optional Avatar avatar = 3;
|
||||
reserved /* color */ 4;
|
||||
@@ -833,7 +850,7 @@ message ContactDetails {
|
||||
optional uint32 expireTimerVersion = 12;
|
||||
optional uint32 inboxPosition = 10;
|
||||
reserved /* archived */ 11;
|
||||
// NEXT ID: 13
|
||||
// NEXT ID: 14
|
||||
}
|
||||
|
||||
message PaymentAddress {
|
||||
@@ -880,6 +897,7 @@ message BodyRange {
|
||||
oneof associatedValue {
|
||||
string mentionAci = 3;
|
||||
Style style = 4;
|
||||
bytes mentionAciBinary = 5; // 16-byte UUID
|
||||
}
|
||||
}
|
||||
|
||||
@@ -887,6 +905,7 @@ message AddressableMessage {
|
||||
oneof author {
|
||||
string authorServiceId = 1;
|
||||
string authorE164 = 2;
|
||||
bytes authorServiceIdBinary = 4; // service ID binary (i.e. 16 byte UUID for ACI, 1 byte prefix + 16 byte UUID for PNI)
|
||||
}
|
||||
optional uint64 sentTimestamp = 3;
|
||||
}
|
||||
@@ -896,5 +915,6 @@ message ConversationIdentifier {
|
||||
string threadServiceId = 1;
|
||||
bytes threadGroupId = 2;
|
||||
string threadE164 = 3;
|
||||
bytes threadServiceIdBinary = 4; // service ID binary (i.e. 16 byte UUID for ACI, 1 byte prefix + 16 byte UUID for PNI)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,7 +140,9 @@ message ContactRecord {
|
||||
Name nickname = 22;
|
||||
string note = 23;
|
||||
optional AvatarColor avatarColor = 24;
|
||||
// Next ID: 25
|
||||
bytes aciBinary = 25; // 16-byte UUID
|
||||
bytes pniBinary = 26; // 16-byte UUID
|
||||
// Next ID: 27
|
||||
}
|
||||
|
||||
message GroupV1Record {
|
||||
@@ -187,8 +189,9 @@ message AccountRecord {
|
||||
|
||||
message PinnedConversation {
|
||||
message Contact {
|
||||
string serviceId = 1;
|
||||
string e164 = 2;
|
||||
string serviceId = 1;
|
||||
string e164 = 2;
|
||||
bytes serviceIdBinary = 3; // service ID binary (i.e. 16 byte UUID for ACI, 1 byte prefix + 16 byte UUID for PNI)
|
||||
}
|
||||
|
||||
oneof identifier {
|
||||
@@ -294,12 +297,13 @@ message AccountRecord {
|
||||
}
|
||||
|
||||
message StoryDistributionListRecord {
|
||||
bytes identifier = 1;
|
||||
string name = 2;
|
||||
repeated string recipientServiceIds = 3;
|
||||
uint64 deletedAtTimestamp = 4;
|
||||
bool allowsReplies = 5;
|
||||
bool isBlockList = 6;
|
||||
bytes identifier = 1;
|
||||
string name = 2;
|
||||
repeated string recipientServiceIds = 3;
|
||||
uint64 deletedAtTimestamp = 4;
|
||||
bool allowsReplies = 5;
|
||||
bool isBlockList = 6;
|
||||
repeated bytes recipientServiceIdsBinary = 7; // service ID binary (i.e. 16 byte UUID for ACI, 1 byte prefix + 16 byte UUID for PNI)
|
||||
}
|
||||
|
||||
message CallLinkRecord {
|
||||
@@ -311,8 +315,9 @@ message CallLinkRecord {
|
||||
|
||||
message Recipient {
|
||||
message Contact {
|
||||
string serviceId = 1;
|
||||
string e164 = 2;
|
||||
string serviceId = 1;
|
||||
string e164 = 2;
|
||||
bytes serviceIdBinary = 3; // service ID binary (i.e. 16 byte UUID for ACI, 1 byte prefix + 16 byte UUID for PNI)
|
||||
}
|
||||
|
||||
oneof identifier {
|
||||
|
||||
Reference in New Issue
Block a user