Use new compact representations in protobufs
Co-authored-by: trevor-signal <131492920+trevor-signal@users.noreply.github.com>
This commit is contained in:
parent
157496f822
commit
8251720444
64 changed files with 1000 additions and 459 deletions
|
@ -222,7 +222,7 @@
|
|||
"@indutny/parallel-prettier": "3.0.0",
|
||||
"@indutny/rezip-electron": "2.0.1",
|
||||
"@napi-rs/canvas": "0.1.61",
|
||||
"@signalapp/mock-server": "12.0.0",
|
||||
"@signalapp/mock-server": "13.0.0",
|
||||
"@storybook/addon-a11y": "8.4.4",
|
||||
"@storybook/addon-actions": "8.4.4",
|
||||
"@storybook/addon-controls": "8.4.4",
|
||||
|
|
10
pnpm-lock.yaml
generated
10
pnpm-lock.yaml
generated
|
@ -430,8 +430,8 @@ importers:
|
|||
specifier: 0.1.61
|
||||
version: 0.1.61
|
||||
'@signalapp/mock-server':
|
||||
specifier: 12.0.0
|
||||
version: 12.0.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
|
||||
specifier: 13.0.0
|
||||
version: 13.0.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
|
||||
'@storybook/addon-a11y':
|
||||
specifier: 8.4.4
|
||||
version: 8.4.4(storybook@8.4.4(bufferutil@4.0.9)(prettier@3.3.3)(utf-8-validate@5.0.10))
|
||||
|
@ -2770,8 +2770,8 @@ packages:
|
|||
'@signalapp/libsignal-client@0.74.1':
|
||||
resolution: {integrity: sha512-PEJou0yrBvxaAGg7JjONlRNM/t3PCBuY96wu7W6+57e38/7Mibo9kAMfE5B8DgVv+DUNMW9AgJhx5McCoIXYew==}
|
||||
|
||||
'@signalapp/mock-server@12.0.0':
|
||||
resolution: {integrity: sha512-5Ebu2c3/BViNsZ4yId8zfHyazMGUmsSfjMXXXFwNn7IYw0M0l/u+FFiR8SJdFnLoBbcxHG+KC3P+QqPdn91FIQ==}
|
||||
'@signalapp/mock-server@13.0.0':
|
||||
resolution: {integrity: sha512-2HsUu8CDtf6g4yt34hDO7tfWVqZ4lPdXLAPM8NElKr/OjO1KiUGXljZFfxNIC5UDySVHaC2//ROcRE+qrhqCyw==}
|
||||
|
||||
'@signalapp/parchment-cjs@3.0.1':
|
||||
resolution: {integrity: sha512-hSBMQ1M7wE4GcC8ZeNtvpJF+DAJg3eIRRf1SiHS3I3Algav/sgJJNm6HIYm6muHuK7IJmuEjkL3ILSXgmu0RfQ==}
|
||||
|
@ -12466,7 +12466,7 @@ snapshots:
|
|||
type-fest: 4.26.1
|
||||
uuid: 11.0.2
|
||||
|
||||
'@signalapp/mock-server@12.0.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)':
|
||||
'@signalapp/mock-server@13.0.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)':
|
||||
dependencies:
|
||||
'@indutny/parallel-prettier': 3.0.0(prettier@3.3.3)
|
||||
'@signalapp/libsignal-client': 0.60.2
|
||||
|
|
|
@ -1,41 +1,56 @@
|
|||
// Copyright 2014 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
/*
|
||||
* Copyright 2020 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
syntax = "proto2";
|
||||
|
||||
package signalservice;
|
||||
|
||||
message ProvisioningUuid {
|
||||
optional string uuid = 1;
|
||||
}
|
||||
option java_package = "org.whispersystems.signalservice.internal.push";
|
||||
option java_outer_classname = "ProvisioningProtos";
|
||||
|
||||
// An opaque address sent by the server when clients first open a provisioning
|
||||
// WebSocket
|
||||
message ProvisioningAddress {
|
||||
|
||||
// The opaque provisioning address for the active provisioning WebSocket
|
||||
// session; clients should not attempt to interpret or modify the contents
|
||||
// of the address string
|
||||
optional string address = 1;
|
||||
}
|
||||
|
||||
message ProvisionEnvelope {
|
||||
optional bytes publicKey = 1;
|
||||
optional bytes body = 2; // Encrypted ProvisionMessage
|
||||
optional bytes body = 2; // Encrypted ProvisionMessage
|
||||
}
|
||||
|
||||
message ProvisionMessage {
|
||||
optional bytes aciIdentityKeyPublic = 1;
|
||||
optional bytes aciIdentityKeyPrivate = 2;
|
||||
optional bytes pniIdentityKeyPublic = 11;
|
||||
optional bytes pniIdentityKeyPrivate = 12;
|
||||
optional string aci = 8;
|
||||
optional string pni = 10;
|
||||
optional string number = 3;
|
||||
optional string provisioningCode = 4;
|
||||
optional string userAgent = 5;
|
||||
optional bytes profileKey = 6;
|
||||
optional bool readReceipts = 7;
|
||||
optional uint32 ProvisioningVersion = 9;
|
||||
optional bytes masterKey = 13;
|
||||
optional bytes ephemeralBackupKey = 14; // 32 bytes
|
||||
optional string accountEntropyPool = 15;
|
||||
optional bytes mediaRootBackupKey = 16; // 32-bytes
|
||||
optional bytes aciIdentityKeyPublic = 1;
|
||||
optional bytes aciIdentityKeyPrivate = 2;
|
||||
optional bytes pniIdentityKeyPublic = 11;
|
||||
optional bytes pniIdentityKeyPrivate = 12;
|
||||
optional string aci = 8;
|
||||
optional string pni = 10;
|
||||
optional string number = 3;
|
||||
optional string provisioningCode = 4;
|
||||
optional string userAgent = 5;
|
||||
optional bytes profileKey = 6;
|
||||
optional bool readReceipts = 7;
|
||||
optional uint32 provisioningVersion = 9;
|
||||
optional bytes masterKey = 13; // Deprecated, but required by linked devices
|
||||
optional bytes ephemeralBackupKey = 14; // 32 bytes
|
||||
optional string accountEntropyPool = 15;
|
||||
optional bytes mediaRootBackupKey = 16; // 32-bytes
|
||||
optional bytes aciBinary = 17; // 16-byte UUID
|
||||
optional bytes pniBinary = 18; // 16-byte UUID
|
||||
// NEXT ID: 19
|
||||
}
|
||||
|
||||
enum ProvisioningVersion {
|
||||
option allow_alias = true;
|
||||
|
||||
INITIAL = 0;
|
||||
INITIAL = 0;
|
||||
TABLET_SUPPORT = 1;
|
||||
CURRENT = 1;
|
||||
CURRENT = 1;
|
||||
}
|
||||
|
|
41
protos/Migrations.proto
Normal file
41
protos/Migrations.proto
Normal file
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2025 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
syntax = "proto3";
|
||||
|
||||
package migrations;
|
||||
|
||||
// Snapshot made at 9f22445e9
|
||||
|
||||
message Envelope {
|
||||
// Our parser does not handle reserved in enums: DESKTOP-1569
|
||||
enum Type {
|
||||
UNKNOWN = 0;
|
||||
CIPHERTEXT = 1; // content => (version byte | SignalMessage{Content})
|
||||
// reserved 2;
|
||||
// reserved "KEY_EXCHANGE";
|
||||
PREKEY_BUNDLE = 3; // content => (version byte | PreKeySignalMessage{Content})
|
||||
SERVER_DELIVERY_RECEIPT = 5; // legacyMessage => [] AND content => []
|
||||
UNIDENTIFIED_SENDER = 6; // legacyMessage => [] AND content => ((version byte | UnidentifiedSenderMessage) OR (version byte | Multi-Recipient Sealed Sender Format))
|
||||
SENDERKEY_MESSAGE = 7; // legacyMessage => [] AND content => (version byte | SenderKeyMessage)
|
||||
PLAINTEXT_CONTENT = 8; // legacyMessage => [] AND content => (marker byte | Content)
|
||||
}
|
||||
|
||||
optional Type type = 1;
|
||||
reserved 2; // formerly optional string sourceE164 = 2;
|
||||
optional string sourceServiceId = 11;
|
||||
optional uint32 sourceDevice = 7;
|
||||
optional string destinationServiceId = 13;
|
||||
reserved 3; // formerly optional string relay = 3;
|
||||
optional uint64 timestamp = 5;
|
||||
reserved 6; // formerly optional bytes legacyMessage = 6; // Contains an encrypted DataMessage; this field could have been set historically for type 1 or 3 messages; no longer in use
|
||||
optional bytes content = 8; // Contains an encrypted Content
|
||||
optional string serverGuid = 9;
|
||||
optional uint64 serverTimestamp = 10;
|
||||
optional bool ephemeral = 12; // indicates that the message should not be persisted if the recipient is offline
|
||||
optional bool urgent = 14 [default = true]; // indicates that the content is considered timely by the sender; defaults to true so senders have to opt-out to say something isn't time critical
|
||||
optional string updatedPni = 15; // for number-change synchronization messages, provides the new server-assigned phone number identifier associated with the changed number
|
||||
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
|
||||
}
|
|
@ -3,32 +3,89 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
syntax = "proto2";
|
||||
|
||||
package signalservice;
|
||||
|
||||
option java_package = "org.whispersystems.signalservice.internal.push";
|
||||
option java_outer_classname = "SignalServiceProtos";
|
||||
|
||||
message Envelope {
|
||||
// Our parser does not handle reserved in enums: DESKTOP-1569
|
||||
enum Type {
|
||||
UNKNOWN = 0;
|
||||
CIPHERTEXT = 1; // content => (version byte | SignalMessage{Content})
|
||||
// reserved 2;
|
||||
// reserved "KEY_EXCHANGE";
|
||||
PREKEY_BUNDLE = 3; // content => (version byte | PreKeySignalMessage{Content})
|
||||
SERVER_DELIVERY_RECEIPT = 5; // legacyMessage => [] AND content => []
|
||||
UNIDENTIFIED_SENDER = 6; // legacyMessage => [] AND content => ((version byte | UnidentifiedSenderMessage) OR (version byte | Multi-Recipient Sealed Sender Format))
|
||||
SENDERKEY_MESSAGE = 7; // legacyMessage => [] AND content => (version byte | SenderKeyMessage)
|
||||
PLAINTEXT_CONTENT = 8; // legacyMessage => [] AND content => (marker byte | Content)
|
||||
|
||||
/**
|
||||
* A double-ratchet message represents a "normal," "unsealed-sender" message
|
||||
* encrypted using the Double Ratchet within an established Signal session.
|
||||
* Double-ratchet messages include sender information in the plaintext
|
||||
* portion of the `Envelope`.
|
||||
*/
|
||||
DOUBLE_RATCHET = 1; // content => (version byte | SignalMessage{Content})
|
||||
|
||||
reserved 2;
|
||||
reserved "KEY_EXCHANGE";
|
||||
|
||||
/**
|
||||
* A prekey message begins a new Signal session. The `content` of a prekey
|
||||
* message is a superset of a double-ratchet message's `content` and
|
||||
* contains the sender's identity public key and information identifying the
|
||||
* pre-keys used in the message's ciphertext. Like double-ratchet messages,
|
||||
* prekey messages contain sender information in the plaintext portion of
|
||||
* the `Envelope`.
|
||||
*/
|
||||
PREKEY_MESSAGE = 3; // content => (version byte | PreKeySignalMessage{Content})
|
||||
|
||||
/**
|
||||
* Server delivery receipts are generated by the server when
|
||||
* "unsealed-sender" messages are delivered to and acknowledged by the
|
||||
* destination device. Server delivery receipts identify the sender in the
|
||||
* plaintext portion of the `Envelope` and have no `content`. Note that
|
||||
* receipts for sealed-sender messages are generated by clients as
|
||||
* `UNIDENTIFIED_SENDER` messages.
|
||||
*
|
||||
* Note that, with server delivery receipts, the "client timestamp" on
|
||||
* the envelope refers to the timestamp of the original message (i.e. the
|
||||
* message the server just delivered) and not to the time of delivery. The
|
||||
* "server timestamp" refers to the time of delivery.
|
||||
*/
|
||||
SERVER_DELIVERY_RECEIPT = 5; // content => []
|
||||
|
||||
/**
|
||||
* An unidentified sender message represents a message with no sender
|
||||
* information in the plaintext portion of the `Envelope`. Unidentified
|
||||
* sender messages always contain an additional `subtype` in their
|
||||
* `content`. They may or may not be part of an existing Signal session
|
||||
* (i.e. an unidentified sender message may have a "prekey message"
|
||||
* subtype or may indicate an encryption error).
|
||||
*/
|
||||
UNIDENTIFIED_SENDER = 6; // content => ((version byte | UnidentifiedSenderMessage) OR (version byte | Multi-Recipient Sealed Sender Format))
|
||||
|
||||
reserved 7;
|
||||
reserved "SENDERKEY_MESSAGE";
|
||||
|
||||
/**
|
||||
* A plaintext message is used solely to convey encryption error receipts
|
||||
* and never contains encrypted message content. Encryption error receipts
|
||||
* must be delivered in plaintext because, encryption/decryption of a prior
|
||||
* message failed and there is no reason to believe that
|
||||
* encryption/decryption of subsequent messages with the same key material
|
||||
* would succeed.
|
||||
*
|
||||
* Critically, plaintext messages never have "real" message content
|
||||
* generated by users. Plaintext messages include sender information.
|
||||
*/
|
||||
PLAINTEXT_CONTENT = 8; // content => (marker byte | Content)
|
||||
|
||||
// next: 9
|
||||
}
|
||||
|
||||
optional Type type = 1;
|
||||
reserved 2; // formerly optional string sourceE164 = 2;
|
||||
optional string sourceServiceId = 11;
|
||||
optional uint32 sourceDevice = 7;
|
||||
optional uint32 sourceDeviceId = 7;
|
||||
optional string destinationServiceId = 13;
|
||||
reserved 3; // formerly optional string relay = 3;
|
||||
optional uint64 timestamp = 5;
|
||||
optional uint64 clientTimestamp = 5;
|
||||
reserved 6; // formerly optional bytes legacyMessage = 6; // Contains an encrypted DataMessage; this field could have been set historically for type 1 or 3 messages; no longer in use
|
||||
optional bytes content = 8; // Contains an encrypted Content
|
||||
optional string serverGuid = 9;
|
||||
|
@ -39,7 +96,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 {
|
||||
|
@ -191,6 +252,7 @@ message DataMessage {
|
|||
repeated QuotedAttachment attachments = 4;
|
||||
repeated BodyRange bodyRanges = 6;
|
||||
optional Type type = 7;
|
||||
optional bytes authorAciBinary = 8; // 16-byte UUID
|
||||
}
|
||||
|
||||
message Contact {
|
||||
|
@ -275,6 +337,7 @@ message DataMessage {
|
|||
reserved /* targetAuthorE164 */ 3;
|
||||
optional string targetAuthorAci = 4;
|
||||
optional uint64 targetSentTimestamp = 5;
|
||||
optional bytes targetAuthorAciBinary = 6; // 16-byte UUID
|
||||
}
|
||||
|
||||
message Delete {
|
||||
|
@ -288,6 +351,7 @@ message DataMessage {
|
|||
message StoryContext {
|
||||
optional string authorAci = 1;
|
||||
optional uint64 sentTimestamp = 2;
|
||||
optional bytes authorAciBinary = 3; // 16-byte UUID
|
||||
}
|
||||
|
||||
enum ProtocolVersion {
|
||||
|
@ -419,6 +483,7 @@ message Verified {
|
|||
optional bytes identityKey = 2;
|
||||
optional State state = 3;
|
||||
optional bytes nullMessage = 4;
|
||||
optional bytes destinationAciBinary = 6; // 16-byte UUID
|
||||
}
|
||||
|
||||
message SyncMessage {
|
||||
|
@ -429,6 +494,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 {
|
||||
|
@ -436,6 +502,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;
|
||||
|
@ -449,7 +516,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 {
|
||||
|
@ -461,6 +529,7 @@ message SyncMessage {
|
|||
repeated string numbers = 1;
|
||||
repeated string acis = 3;
|
||||
repeated bytes groupIds = 2;
|
||||
repeated bytes acisBinary = 4; // 16-byte UUID
|
||||
}
|
||||
|
||||
message Request {
|
||||
|
@ -481,12 +550,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 {
|
||||
|
@ -513,6 +584,7 @@ message SyncMessage {
|
|||
reserved /*senderE164*/ 1;
|
||||
optional string senderAci = 3;
|
||||
optional uint64 timestamp = 2;
|
||||
optional bytes senderAciBinary = 4; // 16-byte UUID
|
||||
}
|
||||
|
||||
message FetchLatest {
|
||||
|
@ -553,6 +625,25 @@ message SyncMessage {
|
|||
optional string threadAci = 2;
|
||||
optional bytes groupId = 3;
|
||||
optional Type type = 4;
|
||||
optional bytes threadAciBinary = 5; // 16-byte UUID
|
||||
}
|
||||
|
||||
message OutgoingPayment {
|
||||
message MobileCoin {
|
||||
optional bytes recipientAddress = 1;
|
||||
optional uint64 amountPicoMob = 2;
|
||||
optional uint64 feePicoMob = 3;
|
||||
optional bytes receipt = 4;
|
||||
optional uint64 ledgerBlockTimestamp = 5;
|
||||
optional uint64 ledgerBlockIndex = 6;
|
||||
repeated bytes spentKeyImages = 7;
|
||||
repeated bytes outputPublicKeys = 8;
|
||||
}
|
||||
optional string recipientServiceId = 1;
|
||||
optional string note = 2;
|
||||
oneof attachment_identifier {
|
||||
MobileCoin mobileCoin = 3;
|
||||
}
|
||||
}
|
||||
|
||||
message PniChangeNumber {
|
||||
|
@ -719,6 +810,7 @@ message SyncMessage {
|
|||
optional FetchLatest fetchLatest = 12;
|
||||
optional Keys keys = 13;
|
||||
optional MessageRequestResponse messageRequestResponse = 14;
|
||||
optional OutgoingPayment outgoingPayment = 15;
|
||||
repeated Viewed viewed = 16;
|
||||
reserved /*pniIdentity*/ 17;
|
||||
optional PniChangeNumber pniChangeNumber = 18;
|
||||
|
@ -732,11 +824,10 @@ message SyncMessage {
|
|||
}
|
||||
|
||||
message AttachmentPointer {
|
||||
// Our parser does not handle reserved in enums: DESKTOP-1569
|
||||
enum Flags {
|
||||
VOICE_MESSAGE = 1;
|
||||
BORDERLESS = 2;
|
||||
// reserved 4;
|
||||
reserved 4;
|
||||
GIF = 8;
|
||||
}
|
||||
|
||||
|
@ -781,6 +872,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;
|
||||
|
@ -791,7 +883,18 @@ message ContactDetails {
|
|||
optional uint32 expireTimerVersion = 12;
|
||||
optional uint32 inboxPosition = 10;
|
||||
reserved /* archived */ 11;
|
||||
// NEXT ID: 13
|
||||
// NEXT ID: 14
|
||||
}
|
||||
|
||||
message PaymentAddress {
|
||||
message MobileCoin {
|
||||
optional bytes publicAddress = 1;
|
||||
optional bytes signature = 2;
|
||||
}
|
||||
|
||||
oneof Address {
|
||||
MobileCoin mobileCoin = 1;
|
||||
}
|
||||
}
|
||||
|
||||
message DecryptionErrorMessage {
|
||||
|
@ -827,6 +930,7 @@ message BodyRange {
|
|||
oneof associatedValue {
|
||||
string mentionAci = 3;
|
||||
Style style = 4;
|
||||
bytes mentionAciBinary = 5; // 16-byte UUID
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -834,6 +938,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;
|
||||
}
|
||||
|
@ -843,5 +948,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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -142,7 +142,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 {
|
||||
|
@ -174,6 +176,11 @@ message GroupV2Record {
|
|||
optional AvatarColor avatarColor = 11;
|
||||
}
|
||||
|
||||
message Payments {
|
||||
bool enabled = 1;
|
||||
bytes entropy = 2;
|
||||
}
|
||||
|
||||
message AccountRecord {
|
||||
|
||||
enum PhoneNumberSharingMode {
|
||||
|
@ -186,6 +193,7 @@ message AccountRecord {
|
|||
message Contact {
|
||||
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 {
|
||||
|
@ -301,6 +309,7 @@ message StoryDistributionListRecord {
|
|||
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 StickerPackRecord {
|
||||
|
@ -335,6 +344,7 @@ message Recipient {
|
|||
message Contact {
|
||||
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 {
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
signal as Signal,
|
||||
signalbackups as Backups,
|
||||
signalservice as SignalService,
|
||||
migrations as Migrations,
|
||||
} from './compiled';
|
||||
|
||||
export { Backups, SignalService, Signal };
|
||||
export { Backups, SignalService, Signal, Migrations };
|
||||
|
|
|
@ -10,7 +10,6 @@ import {
|
|||
parseContactsV2,
|
||||
type ContactDetailsWithAvatar,
|
||||
} from '../textsecure/ContactsParser';
|
||||
import { normalizeAci } from '../util/normalizeAci';
|
||||
import * as Conversation from '../types/Conversation';
|
||||
import * as Errors from '../types/errors';
|
||||
import type { ValidateConversationType } from '../model-types.d';
|
||||
|
@ -152,7 +151,7 @@ async function doContactSync({
|
|||
for (const details of contacts) {
|
||||
const partialConversation: ValidateConversationType = {
|
||||
e164: details.number,
|
||||
serviceId: normalizeAci(details.aci, 'doContactSync'),
|
||||
serviceId: details.aci,
|
||||
type: 'private',
|
||||
};
|
||||
|
||||
|
@ -167,7 +166,7 @@ async function doContactSync({
|
|||
|
||||
const { conversation } = window.ConversationController.maybeMergeContacts({
|
||||
e164: details.number,
|
||||
aci: normalizeAci(details.aci, 'contactSync.aci'),
|
||||
aci: details.aci,
|
||||
reason: logId,
|
||||
});
|
||||
|
||||
|
|
|
@ -81,6 +81,7 @@ import {
|
|||
callLinkFromRecord,
|
||||
getRoomIdFromRootKeyString,
|
||||
} from '../util/callLinksRingrtc';
|
||||
import { fromPniUuidBytesOrUntaggedString } from '../util/ServiceId';
|
||||
import { callLinkRefreshJobQueue } from '../jobs/callLinkRefreshJobQueue';
|
||||
|
||||
const log = createLogger('storage');
|
||||
|
@ -1800,11 +1801,16 @@ async function processRemoteRecords(
|
|||
return true;
|
||||
}
|
||||
|
||||
if (!contact.e164 || !contact.pni) {
|
||||
const pni = fromPniUuidBytesOrUntaggedString(
|
||||
contact.pniBinary,
|
||||
contact.pni,
|
||||
'splitPNIContacts'
|
||||
);
|
||||
if (!contact.e164 || !pni) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const localAci = window.ConversationController.get(contact.pni)?.getAci();
|
||||
const localAci = window.ConversationController.get(pni)?.getAci();
|
||||
if (!localAci) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -45,14 +45,10 @@ import { normalizeStoryDistributionId } from '../types/StoryDistributionId';
|
|||
import type { StoryDistributionIdString } from '../types/StoryDistributionId';
|
||||
import type { ServiceIdString } from '../types/ServiceId';
|
||||
import {
|
||||
normalizeServiceId,
|
||||
normalizePni,
|
||||
ServiceIdKind,
|
||||
isUntaggedPniString,
|
||||
normalizeServiceId,
|
||||
toUntaggedPni,
|
||||
toTaggedPni,
|
||||
} from '../types/ServiceId';
|
||||
import { normalizeAci } from '../util/normalizeAci';
|
||||
import { isAciString } from '../util/isAciString';
|
||||
import * as Stickers from '../types/Stickers';
|
||||
import type {
|
||||
|
@ -84,6 +80,15 @@ import {
|
|||
generateBackupsSubscriberData,
|
||||
saveBackupsSubscriberData,
|
||||
} from '../util/backupSubscriptionData';
|
||||
import {
|
||||
toAciObject,
|
||||
toPniObject,
|
||||
toServiceIdObject,
|
||||
fromServiceIdBinaryOrString,
|
||||
fromAciUuidBytesOrString,
|
||||
fromPniUuidBytesOrUntaggedString,
|
||||
} from '../util/ServiceId';
|
||||
import { isProtoBinaryEncodingEnabled } from '../util/isProtoBinaryEncodingEnabled';
|
||||
import { getLinkPreviewSetting } from '../types/LinkPreview';
|
||||
import {
|
||||
getReadReceiptSetting,
|
||||
|
@ -228,7 +233,11 @@ export async function toContactRecord(
|
|||
const contactRecord = new Proto.ContactRecord();
|
||||
const aci = conversation.getAci();
|
||||
if (aci) {
|
||||
contactRecord.aci = aci;
|
||||
if (isProtoBinaryEncodingEnabled()) {
|
||||
contactRecord.aciBinary = toAciObject(aci).getRawUuidBytes();
|
||||
} else {
|
||||
contactRecord.aci = aci;
|
||||
}
|
||||
}
|
||||
const e164 = conversation.get('e164');
|
||||
if (e164) {
|
||||
|
@ -241,7 +250,11 @@ export async function toContactRecord(
|
|||
}
|
||||
const pni = conversation.getPni();
|
||||
if (pni) {
|
||||
contactRecord.pni = toUntaggedPni(pni);
|
||||
if (isProtoBinaryEncodingEnabled()) {
|
||||
contactRecord.pniBinary = toPniObject(pni).getRawUuidBytes();
|
||||
} else {
|
||||
contactRecord.pni = toUntaggedPni(pni);
|
||||
}
|
||||
}
|
||||
contactRecord.pniSignatureVerified =
|
||||
conversation.get('pniSignatureVerified') ?? false;
|
||||
|
@ -411,9 +424,19 @@ export function toAccountRecord(
|
|||
new Proto.AccountRecord.PinnedConversation();
|
||||
|
||||
if (pinnedConversation.get('type') === 'private') {
|
||||
const serviceId = pinnedConversation.getServiceId();
|
||||
pinnedConversationRecord.identifier = 'contact';
|
||||
pinnedConversationRecord.contact = {
|
||||
serviceId: pinnedConversation.getServiceId(),
|
||||
...(isProtoBinaryEncodingEnabled()
|
||||
? {
|
||||
serviceIdBinary:
|
||||
serviceId == null
|
||||
? null
|
||||
: toServiceIdObject(serviceId).getServiceIdBinary(),
|
||||
}
|
||||
: {
|
||||
serviceId,
|
||||
}),
|
||||
e164: pinnedConversation.get('e164'),
|
||||
};
|
||||
} else if (isGroupV1(pinnedConversation.attributes)) {
|
||||
|
@ -621,8 +644,16 @@ export function toStoryDistributionListRecord(
|
|||
storyDistributionListRecord.isBlockList = Boolean(
|
||||
storyDistributionList.isBlockList
|
||||
);
|
||||
storyDistributionListRecord.recipientServiceIds =
|
||||
storyDistributionList.members;
|
||||
|
||||
if (isProtoBinaryEncodingEnabled()) {
|
||||
storyDistributionListRecord.recipientServiceIdsBinary =
|
||||
storyDistributionList.members.map(serviceId => {
|
||||
return toServiceIdObject(serviceId).getServiceIdBinary();
|
||||
});
|
||||
} else {
|
||||
storyDistributionListRecord.recipientServiceIds =
|
||||
storyDistributionList.members;
|
||||
}
|
||||
|
||||
if (storyDistributionList.storageUnknownFields) {
|
||||
storyDistributionListRecord.$unknownFields = [
|
||||
|
@ -1108,17 +1139,16 @@ export async function mergeContactRecord(
|
|||
const contactRecord = {
|
||||
...originalContactRecord,
|
||||
|
||||
aci: originalContactRecord.aci
|
||||
? normalizeAci(originalContactRecord.aci, 'ContactRecord.aci')
|
||||
: undefined,
|
||||
pni:
|
||||
originalContactRecord.pni &&
|
||||
isUntaggedPniString(originalContactRecord.pni)
|
||||
? normalizePni(
|
||||
toTaggedPni(originalContactRecord.pni),
|
||||
'ContactRecord.pni'
|
||||
)
|
||||
: undefined,
|
||||
aci: fromAciUuidBytesOrString(
|
||||
originalContactRecord.aciBinary,
|
||||
originalContactRecord.aci,
|
||||
'ContactRecord.aci'
|
||||
),
|
||||
pni: fromPniUuidBytesOrUntaggedString(
|
||||
originalContactRecord.pniBinary,
|
||||
originalContactRecord.pni,
|
||||
'ContactRecord.pni'
|
||||
),
|
||||
};
|
||||
|
||||
const e164 = dropNull(contactRecord.e164);
|
||||
|
@ -1481,19 +1511,22 @@ export async function mergeAccountRecord(
|
|||
let convo: ConversationModel | undefined;
|
||||
|
||||
if (contact) {
|
||||
if (!contact.serviceId && !contact.e164) {
|
||||
if (
|
||||
!contact.serviceId &&
|
||||
!Bytes.isNotEmpty(contact.serviceIdBinary) &&
|
||||
!contact.e164
|
||||
) {
|
||||
log.error(
|
||||
'storageService.mergeAccountRecord: No serviceId or e164 on contact'
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
convo = window.ConversationController.lookupOrCreate({
|
||||
serviceId: contact.serviceId
|
||||
? normalizeServiceId(
|
||||
contact.serviceId,
|
||||
'AccountRecord.pin.serviceId'
|
||||
)
|
||||
: undefined,
|
||||
serviceId: fromServiceIdBinaryOrString(
|
||||
contact.serviceIdBinary,
|
||||
contact.serviceId,
|
||||
'AccountRecord.pin.serviceId'
|
||||
),
|
||||
e164: contact.e164,
|
||||
reason: 'storageService.mergeAccountRecord',
|
||||
});
|
||||
|
@ -1751,9 +1784,20 @@ export async function mergeStoryDistributionListRecord(
|
|||
storyDistributionListRecord
|
||||
);
|
||||
|
||||
const remoteListMembers: Array<ServiceIdString> = (
|
||||
storyDistributionListRecord.recipientServiceIds || []
|
||||
).map(id => normalizeServiceId(id, 'mergeStoryDistributionListRecord'));
|
||||
let remoteListMembers: Array<ServiceIdString>;
|
||||
|
||||
if (storyDistributionListRecord.recipientServiceIdsBinary?.length) {
|
||||
remoteListMembers =
|
||||
storyDistributionListRecord.recipientServiceIdsBinary.map(id =>
|
||||
fromServiceIdBinaryOrString(id, undefined, 'unused')
|
||||
);
|
||||
} else if (storyDistributionListRecord.recipientServiceIds?.length) {
|
||||
remoteListMembers = storyDistributionListRecord.recipientServiceIds.map(
|
||||
id => normalizeServiceId(id, 'mergeStoryDistributionListRecord')
|
||||
);
|
||||
} else {
|
||||
remoteListMembers = [];
|
||||
}
|
||||
|
||||
if (storyDistributionListRecord.$unknownFields) {
|
||||
details.push('adding unknown fields');
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
toTaggedPni,
|
||||
isUntaggedPniString,
|
||||
} from '../../types/ServiceId';
|
||||
import { SignalService as Proto } from '../../protobuf';
|
||||
import { Migrations as Proto } from '../../protobuf';
|
||||
import { sql } from '../util';
|
||||
import type { WritableDB } from '../Interface';
|
||||
import { getOurUuid } from './41-uuid-keys';
|
||||
|
|
|
@ -13,9 +13,11 @@ import type { ProcessedAttachment } from '../textsecure/Types.d';
|
|||
import { SignalService as Proto } from '../protobuf';
|
||||
import { IMAGE_GIF, IMAGE_JPEG, LONG_MESSAGE } from '../types/MIME';
|
||||
import { generateAci } from '../types/ServiceId';
|
||||
import { toAciObject } from '../util/ServiceId';
|
||||
import { uuidToBytes } from '../util/uuidToBytes';
|
||||
|
||||
const ACI_1 = generateAci();
|
||||
const ACI_BINARY_1 = toAciObject(ACI_1).getRawUuidBytes();
|
||||
const FLAGS = Proto.DataMessage.Flags;
|
||||
|
||||
const TIMESTAMP = Date.now();
|
||||
|
@ -205,7 +207,7 @@ describe('processDataMessage', () => {
|
|||
const out = check({
|
||||
quote: {
|
||||
id: Long.fromNumber(1),
|
||||
authorAci: ACI_1,
|
||||
authorAciBinary: ACI_BINARY_1,
|
||||
text: 'text',
|
||||
attachments: [
|
||||
{
|
||||
|
@ -298,7 +300,7 @@ describe('processDataMessage', () => {
|
|||
check({
|
||||
reaction: {
|
||||
emoji: '😎',
|
||||
targetAuthorAci: ACI_1,
|
||||
targetAuthorAciBinary: ACI_BINARY_1,
|
||||
targetSentTimestamp: Long.fromNumber(TIMESTAMP),
|
||||
},
|
||||
}).reaction,
|
||||
|
@ -315,7 +317,7 @@ describe('processDataMessage', () => {
|
|||
reaction: {
|
||||
emoji: '😎',
|
||||
remove: true,
|
||||
targetAuthorAci: ACI_1,
|
||||
targetAuthorAciBinary: ACI_BINARY_1,
|
||||
targetSentTimestamp: Long.fromNumber(TIMESTAMP),
|
||||
},
|
||||
}).reaction,
|
||||
|
|
|
@ -13,6 +13,7 @@ import { createLogger } from '../logging/log';
|
|||
import * as Bytes from '../Bytes';
|
||||
import * as Errors from '../types/errors';
|
||||
import { APPLICATION_OCTET_STREAM } from '../types/MIME';
|
||||
import { type AciString, generateAci } from '../types/ServiceId';
|
||||
import { SignalService as Proto } from '../protobuf';
|
||||
import {
|
||||
ParseContactsTransform,
|
||||
|
@ -21,12 +22,15 @@ import {
|
|||
import type { ContactDetailsWithAvatar } from '../textsecure/ContactsParser';
|
||||
import { createTempDir, deleteTempDir } from '../updater/common';
|
||||
import { strictAssert } from '../util/assert';
|
||||
import { toAciObject } from '../util/ServiceId';
|
||||
import { generateKeys, encryptAttachmentV2ToDisk } from '../AttachmentCrypto';
|
||||
|
||||
const log = createLogger('ContactsParser_test');
|
||||
|
||||
const { Writer } = protobuf;
|
||||
|
||||
const DEFAULT_ACI = generateAci();
|
||||
|
||||
describe('ContactsParser', () => {
|
||||
let tempDir: string;
|
||||
|
||||
|
@ -130,9 +134,9 @@ describe('ContactsParser', () => {
|
|||
try {
|
||||
const avatarBuffer = generateAvatar();
|
||||
const bytes = Bytes.concatenate([
|
||||
generatePrefixedContact(avatarBuffer, 'invalid'),
|
||||
generatePrefixedContact(avatarBuffer, null),
|
||||
avatarBuffer,
|
||||
generatePrefixedContact(undefined, 'invalid'),
|
||||
generatePrefixedContact(undefined, null),
|
||||
getTestBuffer(),
|
||||
]);
|
||||
|
||||
|
@ -216,12 +220,12 @@ function getTestBuffer(): Uint8Array {
|
|||
|
||||
function generatePrefixedContact(
|
||||
avatarBuffer: Uint8Array | undefined,
|
||||
aci = '7198E1BD-1293-452A-A098-F982FF201902'
|
||||
aci: AciString | null = DEFAULT_ACI
|
||||
) {
|
||||
const contactInfoBuffer = Proto.ContactDetails.encode({
|
||||
name: 'Zero Cool',
|
||||
number: '+10000000000',
|
||||
aci,
|
||||
aciBinary: aci == null ? null : toAciObject(aci).getRawUuidBytes(),
|
||||
avatar: avatarBuffer
|
||||
? { contentType: 'image/jpeg', length: avatarBuffer.length }
|
||||
: undefined,
|
||||
|
@ -239,7 +243,7 @@ async function verifyContact(
|
|||
): Promise<void> {
|
||||
assert.strictEqual(contact.name, 'Zero Cool');
|
||||
assert.strictEqual(contact.number, '+10000000000');
|
||||
assert.strictEqual(contact.aci, '7198e1bd-1293-452a-a098-f982ff201902');
|
||||
assert.strictEqual(contact.aci, DEFAULT_ACI);
|
||||
|
||||
if (avatarIsMissing) {
|
||||
return;
|
||||
|
|
|
@ -11,6 +11,7 @@ import { IncomingWebSocketRequestLegacy } from '../textsecure/WebsocketResources
|
|||
import type { DecryptionErrorEvent } from '../textsecure/messageReceiverEvents';
|
||||
import { generateAci } from '../types/ServiceId';
|
||||
import type { AciString } from '../types/ServiceId';
|
||||
import { toAciObject } from '../util/ServiceId';
|
||||
import { SignalService as Proto } from '../protobuf';
|
||||
import * as Crypto from '../Crypto';
|
||||
import { toBase64 } from '../Bytes';
|
||||
|
@ -47,10 +48,10 @@ describe('MessageReceiver', () => {
|
|||
});
|
||||
|
||||
const body = Proto.Envelope.encode({
|
||||
type: Proto.Envelope.Type.CIPHERTEXT,
|
||||
sourceServiceId: someAci,
|
||||
sourceDevice: deviceId,
|
||||
timestamp: Long.fromNumber(Date.now()),
|
||||
type: Proto.Envelope.Type.DOUBLE_RATCHET,
|
||||
sourceServiceIdBinary: toAciObject(someAci).getRawUuidBytes(),
|
||||
sourceDeviceId: deviceId,
|
||||
clientTimestamp: Long.fromNumber(Date.now()),
|
||||
content: Crypto.getRandomBytes(200),
|
||||
}).finish();
|
||||
|
||||
|
|
|
@ -106,7 +106,7 @@ describe('backups', function (this: Mocha.Suite) {
|
|||
identifier: uuidToBytes(MY_STORY_ID),
|
||||
isBlockList: true,
|
||||
name: MY_STORY_ID,
|
||||
recipientServiceIds: [pinned.device.aci],
|
||||
recipientServiceIdsBinary: [pinned.device.aciBinary],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -119,7 +119,7 @@ describe('backups', function (this: Mocha.Suite) {
|
|||
identifier: uuidToBytes(DISTRIBUTION1),
|
||||
isBlockList: false,
|
||||
name: 'friend',
|
||||
recipientServiceIds: [friend.device.aci],
|
||||
recipientServiceIdsBinary: [friend.device.aciBinary],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -262,14 +262,14 @@ describe('backups', function (this: Mocha.Suite) {
|
|||
async (window, snapshot) => {
|
||||
const leftPane = window.locator('#LeftPane');
|
||||
const pinnedElem = leftPane.locator(
|
||||
`[data-testid="${pinned.toContact().aci}"] >> "cat photo"`
|
||||
`[data-testid="${pinned.device.aci}"] >> "cat photo"`
|
||||
);
|
||||
|
||||
debug('Waiting for messages to pinned contact to come through');
|
||||
await pinnedElem.click();
|
||||
|
||||
const contactElem = leftPane.locator(
|
||||
`[data-testid="${friend.toContact().aci}"] >> "respond 4"`
|
||||
`[data-testid="${friend.device.aci}"] >> "respond 4"`
|
||||
);
|
||||
|
||||
debug('Waiting for messages to regular contact to come through');
|
||||
|
|
|
@ -62,9 +62,7 @@ Bootstrap.benchmark(async (bootstrap: Bootstrap): Promise<void> => {
|
|||
|
||||
const openConvo = async (contact: PrimaryDevice): Promise<void> => {
|
||||
debug('opening conversation', contact.profileName);
|
||||
const item = leftPane.locator(
|
||||
`[data-testid="${contact.toContact().aci}"]`
|
||||
);
|
||||
const item = leftPane.locator(`[data-testid="${contact.device.aci}"]`);
|
||||
|
||||
await item.click();
|
||||
};
|
||||
|
|
|
@ -126,39 +126,49 @@ Bootstrap.benchmark(async (bootstrap: Bootstrap): Promise<void> => {
|
|||
}
|
||||
debug('encrypted');
|
||||
|
||||
await Promise.all(messages.map(message => server.send(desktop, message)));
|
||||
debug('sending first message');
|
||||
{
|
||||
const firstMessage = messages.shift();
|
||||
if (firstMessage != null) {
|
||||
await server.send(desktop, firstMessage);
|
||||
}
|
||||
}
|
||||
|
||||
const window = await app.getWindow();
|
||||
|
||||
debug('waiting for conversation');
|
||||
{
|
||||
const leftPane = window.locator('#LeftPane');
|
||||
|
||||
// Left pane should show either the message preview or
|
||||
// "You were added to the group".
|
||||
await leftPane
|
||||
.locator(
|
||||
`.module-conversation-list__item--contact-or-conversation[data-testid="${group.id}"]`
|
||||
)
|
||||
.waitFor();
|
||||
}
|
||||
|
||||
debug('sending the rest of messages');
|
||||
await Promise.all(messages.map(message => server.send(desktop, message)));
|
||||
|
||||
debug('opening conversation');
|
||||
{
|
||||
const leftPane = window.locator('#LeftPane');
|
||||
|
||||
const item = leftPane
|
||||
await leftPane
|
||||
.locator(
|
||||
`.module-conversation-list__item--contact-or-conversation[data-testid="${group.id}"]`
|
||||
`.module-conversation-list__item--contact-or-conversation[data-testid="${group.id}"]` +
|
||||
` >> text=${LAST_MESSAGE}`
|
||||
)
|
||||
.first();
|
||||
|
||||
// Wait for unread indicator to give desktop time to process messages without
|
||||
// the timeline open
|
||||
await item
|
||||
.locator(
|
||||
'.module-conversation-list__item--contact-or-conversation__content'
|
||||
)
|
||||
.locator(
|
||||
'.module-conversation-list__item--contact-or-conversation__unread-indicator'
|
||||
)
|
||||
.first()
|
||||
.waitFor();
|
||||
|
||||
await item.click();
|
||||
.click();
|
||||
}
|
||||
|
||||
debug('scrolling to bottom of timeline');
|
||||
await window
|
||||
.locator('.module-timeline__messages__at-bottom-detector')
|
||||
.scrollIntoViewIfNeeded();
|
||||
.locator('.ScrollDownButton')
|
||||
.or(window.locator(`.module-message >> text="${LAST_MESSAGE}"`))
|
||||
.click({ timeout: MINUTE });
|
||||
|
||||
debug('finding message in timeline');
|
||||
{
|
||||
|
|
|
@ -68,7 +68,7 @@ Bootstrap.benchmark(async (bootstrap: Bootstrap): Promise<void> => {
|
|||
{
|
||||
const leftPane = window.locator('#LeftPane');
|
||||
const item = leftPane.locator(
|
||||
`[data-testid="${first.toContact().aci}"] >> text=${LAST_MESSAGE}`
|
||||
`[data-testid="${first.device.aci}"] >> text=${LAST_MESSAGE}`
|
||||
);
|
||||
await item.click();
|
||||
}
|
||||
|
|
|
@ -49,9 +49,7 @@ Bootstrap.benchmark(async (bootstrap: Bootstrap): Promise<void> => {
|
|||
|
||||
const leftPane = window.locator('#LeftPane');
|
||||
|
||||
const item = leftPane.locator(
|
||||
`[data-testid="${lastContact?.toContact().aci}"]`
|
||||
);
|
||||
const item = leftPane.locator(`[data-testid="${lastContact?.device.aci}"]`);
|
||||
await item.waitFor();
|
||||
|
||||
const duration = Date.now() - start;
|
||||
|
|
|
@ -129,11 +129,11 @@ function maybeWrapInSyncMessage({
|
|||
? {
|
||||
syncMessage: {
|
||||
sent: {
|
||||
destinationServiceId: getDevice(to).aci,
|
||||
destinationServiceIdBinary: getDevice(to).aciBinary,
|
||||
message: dataMessage,
|
||||
timestamp: dataMessage.timestamp,
|
||||
unidentifiedStatus: (sentTo ?? [to]).map(contact => ({
|
||||
destinationServiceId: getDevice(contact).aci,
|
||||
destinationServiceIdBinary: getDevice(contact).aciBinary,
|
||||
destination: getDevice(contact).number,
|
||||
})),
|
||||
},
|
||||
|
@ -222,7 +222,7 @@ export function sendReaction({
|
|||
timestamp: Long.fromNumber(reactionTimestamp),
|
||||
reaction: {
|
||||
emoji,
|
||||
targetAuthorAci: getDevice(targetAuthor).aci,
|
||||
targetAuthorAciBinary: getDevice(targetAuthor).aciRawUuid,
|
||||
targetSentTimestamp: Long.fromNumber(targetMessageTimestamp),
|
||||
},
|
||||
},
|
||||
|
|
|
@ -119,13 +119,13 @@ describe('attachment backfill', function (this: Mocha.Suite) {
|
|||
return entry.syncMessage.attachmentBackfillRequest != null;
|
||||
});
|
||||
|
||||
assert.strictEqual(
|
||||
request?.targetConversation?.threadServiceId,
|
||||
unknownContact.device.aci
|
||||
assert.deepEqual(
|
||||
request?.targetConversation?.threadServiceIdBinary,
|
||||
unknownContact.device.aciBinary
|
||||
);
|
||||
assert.strictEqual(
|
||||
request?.targetMessage?.authorServiceId,
|
||||
unknownContact.device.aci
|
||||
assert.deepEqual(
|
||||
request?.targetMessage?.authorServiceIdBinary,
|
||||
unknownContact.device.aciBinary
|
||||
);
|
||||
assert.strictEqual(
|
||||
request?.targetMessage?.sentTimestamp?.toNumber(),
|
||||
|
@ -302,13 +302,13 @@ describe('attachment backfill', function (this: Mocha.Suite) {
|
|||
return entry.syncMessage.attachmentBackfillRequest != null;
|
||||
});
|
||||
|
||||
assert.strictEqual(
|
||||
request?.targetConversation?.threadServiceId,
|
||||
unknownContact.device.aci
|
||||
assert.deepEqual(
|
||||
request?.targetConversation?.threadServiceIdBinary,
|
||||
unknownContact.device.aciBinary
|
||||
);
|
||||
assert.strictEqual(
|
||||
request?.targetMessage?.authorServiceId,
|
||||
unknownContact.device.aci
|
||||
assert.deepEqual(
|
||||
request?.targetMessage?.authorServiceIdBinary,
|
||||
unknownContact.device.aciBinary
|
||||
);
|
||||
assert.strictEqual(
|
||||
request?.targetMessage?.sentTimestamp?.toNumber(),
|
||||
|
@ -380,13 +380,13 @@ describe('attachment backfill', function (this: Mocha.Suite) {
|
|||
return entry.syncMessage.attachmentBackfillRequest != null;
|
||||
});
|
||||
|
||||
assert.strictEqual(
|
||||
request?.targetConversation?.threadServiceId,
|
||||
unknownContact.device.aci
|
||||
assert.deepEqual(
|
||||
request?.targetConversation?.threadServiceIdBinary,
|
||||
unknownContact.device.aciBinary
|
||||
);
|
||||
assert.strictEqual(
|
||||
request?.targetMessage?.authorServiceId,
|
||||
unknownContact.device.aci
|
||||
assert.deepEqual(
|
||||
request?.targetMessage?.authorServiceIdBinary,
|
||||
unknownContact.device.aciBinary
|
||||
);
|
||||
assert.strictEqual(
|
||||
request?.targetMessage?.sentTimestamp?.toNumber(),
|
||||
|
@ -439,7 +439,7 @@ describe('attachment backfill', function (this: Mocha.Suite) {
|
|||
desktop,
|
||||
quote: {
|
||||
id: Long.fromNumber(bootstrap.getTimestamp()),
|
||||
authorAci: unknownContact.device.aci,
|
||||
authorAciBinary: unknownContact.device.aciRawUuid,
|
||||
text: 'quote text',
|
||||
attachments: [
|
||||
{
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { Proto } from '@signalapp/mock-server';
|
||||
import { Aci } from '@signalapp/libsignal-client';
|
||||
import { assert } from 'chai';
|
||||
import createDebug from 'debug';
|
||||
import Long from 'long';
|
||||
|
@ -23,6 +24,7 @@ import { sleep } from '../../util/sleep';
|
|||
export const debug = createDebug('mock:test:edit');
|
||||
|
||||
const ACI_1 = generateAci();
|
||||
const ACI_1_BINARY = Aci.parseFromServiceIdString(ACI_1).getRawUuidBytes();
|
||||
const UNPROCESSED_ATTACHMENT: Proto.IAttachmentPointer = {
|
||||
cdnId: Long.fromNumber(123),
|
||||
key: new Uint8Array([1, 2, 3]),
|
||||
|
@ -57,7 +59,7 @@ function createMessageWithQuote(body: string): Proto.IDataMessage {
|
|||
body,
|
||||
quote: {
|
||||
id: Long.fromNumber(1),
|
||||
authorAci: ACI_1,
|
||||
authorAciBinary: ACI_1_BINARY,
|
||||
text: 'text',
|
||||
attachments: [
|
||||
{
|
||||
|
@ -516,7 +518,6 @@ describe('editing', function (this: Mocha.Suite) {
|
|||
const { contacts, desktop } = bootstrap;
|
||||
|
||||
const [friend] = contacts;
|
||||
const contact = friend.toContact();
|
||||
|
||||
const page = await app.getWindow();
|
||||
|
||||
|
@ -567,7 +568,7 @@ describe('editing', function (this: Mocha.Suite) {
|
|||
debug("getting friend's conversationId");
|
||||
const conversationId = await page.evaluate(
|
||||
serviceId => window.SignalCI?.getConversationId(serviceId),
|
||||
contact.aci
|
||||
friend.device.aci
|
||||
);
|
||||
debug(`got friend's conversationId: ${conversationId}`);
|
||||
strictAssert(conversationId, 'conversationId exists');
|
||||
|
|
|
@ -70,7 +70,6 @@ describe('messaging/expireTimerVersion', function (this: Mocha.Suite) {
|
|||
identifier: uuidToBytes(MY_STORY_ID),
|
||||
isBlockList: true,
|
||||
name: MY_STORY_ID,
|
||||
recipientServiceIds: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -180,11 +179,11 @@ describe('messaging/expireTimerVersion', function (this: Mocha.Suite) {
|
|||
const sendSync = async () => {
|
||||
debug('Send a sync message');
|
||||
const timestamp = bootstrap.getTimestamp();
|
||||
const destinationServiceId = stranger.device.aci;
|
||||
const destinationServiceIdBinary = stranger.device.aciBinary;
|
||||
const content = {
|
||||
syncMessage: {
|
||||
sent: {
|
||||
destinationServiceId,
|
||||
destinationServiceIdBinary,
|
||||
timestamp: Long.fromNumber(timestamp),
|
||||
message: {
|
||||
body: 'request',
|
||||
|
@ -194,7 +193,7 @@ describe('messaging/expireTimerVersion', function (this: Mocha.Suite) {
|
|||
},
|
||||
unidentifiedStatus: [
|
||||
{
|
||||
destinationServiceId,
|
||||
destinationServiceIdBinary,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
@ -102,14 +102,14 @@ describe('readSync', function (this: Mocha.Suite) {
|
|||
Long.fromNumber(timestamp)
|
||||
);
|
||||
|
||||
const senderAci = friend.device.aci;
|
||||
const senderAciBinary = friend.device.aciRawUuid;
|
||||
|
||||
await phone.sendRaw(
|
||||
desktop,
|
||||
{
|
||||
syncMessage: {
|
||||
read: longTimestamps.map(timestamp => ({
|
||||
senderAci,
|
||||
senderAciBinary,
|
||||
timestamp,
|
||||
})),
|
||||
},
|
||||
|
|
|
@ -55,7 +55,7 @@ describe('safety number', function (this: Mocha.Suite) {
|
|||
identifier: uuidToBytes(MY_STORY_ID),
|
||||
isBlockList: false,
|
||||
name: MY_STORY_ID,
|
||||
recipientServiceIds: [alice.device.aci],
|
||||
recipientServiceIdsBinary: [alice.device.aciBinary],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -68,7 +68,7 @@ describe('sendSync', function (this: Mocha.Suite) {
|
|||
timestamp: Long.fromNumber(timestamp),
|
||||
message: originalDataMessage,
|
||||
unidentifiedStatus: members.map(member => ({
|
||||
destinationServiceId: member.device.aci,
|
||||
destinationServiceIdBinary: member.device.aciBinary,
|
||||
destination: member.device.number,
|
||||
})),
|
||||
},
|
||||
|
|
|
@ -51,7 +51,6 @@ describe('story/messaging', function (this: Mocha.Suite) {
|
|||
identifier: uuidToBytes(MY_STORY_ID),
|
||||
isBlockList: false,
|
||||
name: MY_STORY_ID,
|
||||
recipientServiceIds: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -65,7 +64,7 @@ describe('story/messaging', function (this: Mocha.Suite) {
|
|||
identifier: uuidToBytes(DISTRIBUTION1),
|
||||
isBlockList: false,
|
||||
name: 'first',
|
||||
recipientServiceIds: [first.device.aci],
|
||||
recipientServiceIdsBinary: [first.device.aciBinary],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -77,7 +76,7 @@ describe('story/messaging', function (this: Mocha.Suite) {
|
|||
identifier: uuidToBytes(DISTRIBUTION2),
|
||||
isBlockList: false,
|
||||
name: 'second',
|
||||
recipientServiceIds: [second.device.aci],
|
||||
recipientServiceIdsBinary: [second.device.aciBinary],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -148,12 +147,12 @@ describe('story/messaging', function (this: Mocha.Suite) {
|
|||
},
|
||||
storyMessageRecipients: [
|
||||
{
|
||||
destinationServiceId: first.device.aci,
|
||||
destinationServiceIdBinary: first.device.aciBinary,
|
||||
distributionListIds: [DISTRIBUTION1],
|
||||
isAllowedToReply: true,
|
||||
},
|
||||
{
|
||||
destinationServiceId: second.device.aci,
|
||||
destinationServiceIdBinary: second.device.aciBinary,
|
||||
distributionListIds: [DISTRIBUTION2],
|
||||
isAllowedToReply: true,
|
||||
},
|
||||
|
@ -171,7 +170,7 @@ describe('story/messaging', function (this: Mocha.Suite) {
|
|||
dataMessage: {
|
||||
body: 'first reply',
|
||||
storyContext: {
|
||||
authorAci: phone.device.aci,
|
||||
authorAciBinary: phone.device.aciRawUuid,
|
||||
sentTimestamp: Long.fromNumber(sentAt),
|
||||
},
|
||||
timestamp: Long.fromNumber(sentAt + 1),
|
||||
|
@ -185,7 +184,7 @@ describe('story/messaging', function (this: Mocha.Suite) {
|
|||
dataMessage: {
|
||||
body: 'second reply',
|
||||
storyContext: {
|
||||
authorAci: phone.device.aci,
|
||||
authorAciBinary: phone.device.aciRawUuid,
|
||||
sentTimestamp: Long.fromNumber(sentAt),
|
||||
},
|
||||
timestamp: Long.fromNumber(sentAt + 2),
|
||||
|
@ -245,7 +244,7 @@ describe('story/messaging', function (this: Mocha.Suite) {
|
|||
dataMessage: {
|
||||
body: 'first reply',
|
||||
storyContext: {
|
||||
authorAci: desktop.aci,
|
||||
authorAciBinary: desktop.aciRawUuid,
|
||||
sentTimestamp: Long.fromNumber(sentAt),
|
||||
},
|
||||
groupV2: {
|
||||
|
|
|
@ -98,7 +98,7 @@ describe('unknown contacts', function (this: Mocha.Suite) {
|
|||
syncMessage: {
|
||||
messageRequestResponse: {
|
||||
type: Proto.SyncMessage.MessageRequestResponse.Type.ACCEPT,
|
||||
threadAci: unknownContact.device.aci,
|
||||
threadAciBinary: unknownContact.device.aciRawUuid,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -64,7 +64,7 @@ describe('Libsignal-net', function (this: Mocha.Suite) {
|
|||
{
|
||||
const leftPane = window.locator('#LeftPane');
|
||||
const item = leftPane
|
||||
.getByTestId(contact.toContact().aci)
|
||||
.getByTestId(contact.device.aci)
|
||||
.getByText('incoming message');
|
||||
await item.click();
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ describe('pnp/calling', function (this: Mocha.Suite) {
|
|||
});
|
||||
|
||||
debug('Open conversation with a known contact');
|
||||
await leftPane.locator(`[data-testid="${alice.toContact().aci}"]`).click();
|
||||
await leftPane.locator(`[data-testid="${alice.device.aci}"]`).click();
|
||||
|
||||
debug('Accept conversation from a known contact');
|
||||
await acceptConversation(window);
|
||||
|
|
|
@ -61,7 +61,7 @@ describe('pnp/change number', function (this: Mocha.Suite) {
|
|||
]);
|
||||
|
||||
debug('opening conversation with the first contact');
|
||||
await leftPane.locator(`[data-testid="${first.toContact().aci}"]`).click();
|
||||
await leftPane.locator(`[data-testid="${first.device.aci}"]`).click();
|
||||
|
||||
debug('done');
|
||||
});
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { timingSafeEqual } from 'node:crypto';
|
||||
import { assert } from 'chai';
|
||||
import { ServiceIdKind, Proto, StorageState } from '@signalapp/mock-server';
|
||||
import type { PrimaryDevice } from '@signalapp/mock-server';
|
||||
|
@ -10,7 +11,6 @@ import Long from 'long';
|
|||
import * as durations from '../../util/durations';
|
||||
import { uuidToBytes } from '../../util/uuidToBytes';
|
||||
import { generateConfigMatrix } from '../../util/generateConfigMatrix';
|
||||
import { toUntaggedPni } from '../../types/ServiceId';
|
||||
import { MY_STORY_ID } from '../../types/Stories';
|
||||
import { Bootstrap } from '../bootstrap';
|
||||
import type { App } from '../bootstrap';
|
||||
|
@ -87,7 +87,6 @@ describe('pnp/merge', function (this: Mocha.Suite) {
|
|||
identifier: uuidToBytes(MY_STORY_ID),
|
||||
isBlockList: true,
|
||||
name: MY_STORY_ID,
|
||||
recipientServiceIds: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -282,7 +281,7 @@ describe('pnp/merge', function (this: Mocha.Suite) {
|
|||
let state = await phone.expectStorageState('consistency check');
|
||||
|
||||
state = state.updateContact(pniContact, {
|
||||
pni: undefined,
|
||||
pniBinary: undefined,
|
||||
e164: undefined,
|
||||
unregisteredAtTimestamp: Long.fromNumber(bootstrap.getTimestamp()),
|
||||
});
|
||||
|
@ -403,25 +402,34 @@ describe('pnp/merge', function (this: Mocha.Suite) {
|
|||
throw new Error('Invalid record');
|
||||
}
|
||||
|
||||
const { aci, e164, pni } = contact;
|
||||
if (aci === pniContact.device.aci) {
|
||||
const { aciBinary, e164, pniBinary } = contact;
|
||||
if (
|
||||
aciBinary?.length &&
|
||||
timingSafeEqual(aciBinary, pniContact.device.aciRawUuid)
|
||||
) {
|
||||
aciContacts += 1;
|
||||
assert.strictEqual(pni, '');
|
||||
assert.strictEqual(pniBinary?.length, 0);
|
||||
assert.strictEqual(e164, '');
|
||||
} else if (pni === toUntaggedPni(pniContact.device.pni)) {
|
||||
} else if (
|
||||
pniBinary?.length &&
|
||||
timingSafeEqual(pniBinary, pniContact.device.pniRawUuid)
|
||||
) {
|
||||
pniContacts += 1;
|
||||
assert.strictEqual(aci, '');
|
||||
assert.strictEqual(aciBinary?.length, 0);
|
||||
assert.strictEqual(e164, pniContact.device.number);
|
||||
}
|
||||
}
|
||||
assert.strictEqual(aciContacts, 1);
|
||||
assert.strictEqual(pniContacts, 1);
|
||||
|
||||
assert.strictEqual(
|
||||
removed[0].contact?.pni,
|
||||
toUntaggedPni(pniContact.device.pni)
|
||||
assert.deepEqual(
|
||||
removed[0].contact?.pniBinary,
|
||||
pniContact.device.pniRawUuid
|
||||
);
|
||||
assert.deepEqual(
|
||||
removed[0].contact?.aciBinary,
|
||||
pniContact.device.aciRawUuid
|
||||
);
|
||||
assert.strictEqual(removed[0].contact?.aci, pniContact.device.aci);
|
||||
|
||||
// Pin PNI so that it appears in the left pane
|
||||
const updated = newState.pin(pniContact, ServiceIdKind.PNI);
|
||||
|
@ -556,12 +564,12 @@ describe('pnp/merge', function (this: Mocha.Suite) {
|
|||
for (const key of ['aci' as const, 'pni' as const]) {
|
||||
debug(`Send a ${key} sync message`);
|
||||
const timestamp = bootstrap.getTimestamp();
|
||||
const destinationServiceId = pniContact.device[key];
|
||||
const destinationServiceIdBinary = pniContact.device[`${key}Binary`];
|
||||
const destination = key === 'pni' ? pniContact.device.number : undefined;
|
||||
const content = {
|
||||
syncMessage: {
|
||||
sent: {
|
||||
destinationServiceId,
|
||||
destinationServiceIdBinary,
|
||||
destination,
|
||||
timestamp: Long.fromNumber(timestamp),
|
||||
message: {
|
||||
|
@ -572,7 +580,7 @@ describe('pnp/merge', function (this: Mocha.Suite) {
|
|||
},
|
||||
unidentifiedStatus: [
|
||||
{
|
||||
destinationServiceId,
|
||||
destinationServiceIdBinary,
|
||||
destination,
|
||||
},
|
||||
],
|
||||
|
|
|
@ -9,7 +9,6 @@ import createDebug from 'debug';
|
|||
import * as durations from '../../util/durations';
|
||||
import { uuidToBytes } from '../../util/uuidToBytes';
|
||||
import { MY_STORY_ID } from '../../types/Stories';
|
||||
import { toUntaggedPni } from '../../types/ServiceId';
|
||||
import { Bootstrap } from '../bootstrap';
|
||||
import type { App } from '../bootstrap';
|
||||
import {
|
||||
|
@ -73,7 +72,6 @@ describe('pnp/phone discovery', function (this: Mocha.Suite) {
|
|||
identifier: uuidToBytes(MY_STORY_ID),
|
||||
isBlockList: true,
|
||||
name: MY_STORY_ID,
|
||||
recipientServiceIds: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -119,7 +117,7 @@ describe('pnp/phone discovery', function (this: Mocha.Suite) {
|
|||
whitelisted: true,
|
||||
identityKey: pniContact.publicKey.serialize(),
|
||||
profileKey: pniContact.profileKey.serialize(),
|
||||
pni: toUntaggedPni(pniContact.device.pni),
|
||||
pniBinary: pniContact.device.pniRawUuid,
|
||||
})
|
||||
);
|
||||
await phone.sendFetchStorage({
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { timingSafeEqual } from 'node:crypto';
|
||||
import { assert } from 'chai';
|
||||
import { ServiceIdKind, StorageState, Proto } from '@signalapp/mock-server';
|
||||
import type { PrimaryDevice } from '@signalapp/mock-server';
|
||||
import createDebug from 'debug';
|
||||
|
||||
import * as durations from '../../util/durations';
|
||||
import { generatePni, toUntaggedPni } from '../../types/ServiceId';
|
||||
import { generatePni } from '../../types/ServiceId';
|
||||
import { toPniObject } from '../../util/ServiceId';
|
||||
import { Bootstrap } from '../bootstrap';
|
||||
import type { App } from '../bootstrap';
|
||||
import {
|
||||
|
@ -54,7 +56,7 @@ describe('pnp/PNI Change', function (this: Mocha.Suite) {
|
|||
whitelisted: true,
|
||||
e164: contactA.device.number,
|
||||
identityKey: contactA.getPublicKey(ServiceIdKind.PNI).serialize(),
|
||||
pni: toUntaggedPni(contactA.device.pni),
|
||||
pniBinary: contactA.device.pniRawUuid,
|
||||
givenName: 'ContactA',
|
||||
},
|
||||
ServiceIdKind.PNI
|
||||
|
@ -135,17 +137,21 @@ describe('pnp/PNI Change', function (this: Mocha.Suite) {
|
|||
const state = await phone.expectStorageState('consistency check');
|
||||
const updated = await phone.setStorageState(
|
||||
state
|
||||
.removeRecord(
|
||||
item =>
|
||||
item.record.contact?.pni === toUntaggedPni(contactA.device.pni)
|
||||
)
|
||||
.removeRecord(item => {
|
||||
return item.record.contact?.pniBinary?.length
|
||||
? timingSafeEqual(
|
||||
item.record.contact.pniBinary,
|
||||
contactA.device.pniRawUuid
|
||||
)
|
||||
: false;
|
||||
})
|
||||
.addContact(
|
||||
contactA,
|
||||
{
|
||||
identityState: Proto.ContactRecord.IdentityState.DEFAULT,
|
||||
whitelisted: true,
|
||||
e164: contactA.device.number,
|
||||
pni: toUntaggedPni(updatedPni),
|
||||
pniBinary: toPniObject(updatedPni).getRawUuidBytes(),
|
||||
identityKey: contactA.getPublicKey(ServiceIdKind.PNI).serialize(),
|
||||
},
|
||||
ServiceIdKind.PNI
|
||||
|
@ -232,17 +238,21 @@ describe('pnp/PNI Change', function (this: Mocha.Suite) {
|
|||
const state = await phone.expectStorageState('consistency check');
|
||||
const updated = await phone.setStorageState(
|
||||
state
|
||||
.removeRecord(
|
||||
item =>
|
||||
item.record.contact?.pni === toUntaggedPni(contactA.device.pni)
|
||||
)
|
||||
.removeRecord(item => {
|
||||
return item.record.contact?.pniBinary?.length
|
||||
? timingSafeEqual(
|
||||
item.record.contact.pniBinary,
|
||||
contactA.device.pniRawUuid
|
||||
)
|
||||
: false;
|
||||
})
|
||||
.addContact(
|
||||
contactB,
|
||||
{
|
||||
identityState: Proto.ContactRecord.IdentityState.DEFAULT,
|
||||
whitelisted: true,
|
||||
e164: contactA.device.number,
|
||||
pni: toUntaggedPni(contactB.device.pni),
|
||||
pniBinary: contactB.device.pniRawUuid,
|
||||
|
||||
// Key change - different identity key
|
||||
identityKey: contactB.publicKey.serialize(),
|
||||
|
@ -334,17 +344,21 @@ describe('pnp/PNI Change', function (this: Mocha.Suite) {
|
|||
const state = await phone.expectStorageState('consistency check');
|
||||
const updated = await phone.setStorageState(
|
||||
state
|
||||
.removeRecord(
|
||||
item =>
|
||||
item.record.contact?.pni === toUntaggedPni(contactA.device.pni)
|
||||
)
|
||||
.removeRecord(item => {
|
||||
return item.record.contact?.pniBinary?.length
|
||||
? timingSafeEqual(
|
||||
item.record.contact.pniBinary,
|
||||
contactA.device.pniRawUuid
|
||||
)
|
||||
: false;
|
||||
})
|
||||
.addContact(
|
||||
contactB,
|
||||
{
|
||||
identityState: Proto.ContactRecord.IdentityState.DEFAULT,
|
||||
whitelisted: true,
|
||||
e164: contactA.device.number,
|
||||
pni: toUntaggedPni(contactB.device.pni),
|
||||
pniBinary: contactB.device.pniRawUuid,
|
||||
|
||||
// Note: No identityKey key provided here!
|
||||
},
|
||||
|
@ -465,17 +479,21 @@ describe('pnp/PNI Change', function (this: Mocha.Suite) {
|
|||
const state = await phone.expectStorageState('consistency check');
|
||||
const updated = await phone.setStorageState(
|
||||
state
|
||||
.removeRecord(
|
||||
item =>
|
||||
item.record.contact?.pni === toUntaggedPni(contactA.device.pni)
|
||||
)
|
||||
.removeRecord(item => {
|
||||
return item.record.contact?.pniBinary?.length
|
||||
? timingSafeEqual(
|
||||
item.record.contact.pniBinary,
|
||||
contactA.device.pniRawUuid
|
||||
)
|
||||
: false;
|
||||
})
|
||||
.addContact(
|
||||
contactB,
|
||||
{
|
||||
identityState: Proto.ContactRecord.IdentityState.DEFAULT,
|
||||
whitelisted: true,
|
||||
e164: contactA.device.number,
|
||||
pni: toUntaggedPni(contactB.device.pni),
|
||||
pniBinary: contactB.device.pniRawUuid,
|
||||
|
||||
// Note: No identityKey key provided here!
|
||||
},
|
||||
|
@ -497,17 +515,21 @@ describe('pnp/PNI Change', function (this: Mocha.Suite) {
|
|||
const state = await phone.expectStorageState('consistency check');
|
||||
const updated = await phone.setStorageState(
|
||||
state
|
||||
.removeRecord(
|
||||
item =>
|
||||
item.record.contact?.pni === toUntaggedPni(contactB.device.pni)
|
||||
)
|
||||
.removeRecord(item => {
|
||||
return item.record.contact?.pniBinary?.length
|
||||
? timingSafeEqual(
|
||||
item.record.contact.pniBinary,
|
||||
contactB.device.pniRawUuid
|
||||
)
|
||||
: false;
|
||||
})
|
||||
.addContact(
|
||||
contactB,
|
||||
{
|
||||
identityState: Proto.ContactRecord.IdentityState.DEFAULT,
|
||||
whitelisted: true,
|
||||
e164: contactA.device.number,
|
||||
pni: toUntaggedPni(contactA.device.pni),
|
||||
pniBinary: contactA.device.pniRawUuid,
|
||||
},
|
||||
ServiceIdKind.PNI
|
||||
)
|
||||
|
|
|
@ -15,7 +15,6 @@ import createDebug from 'debug';
|
|||
import * as durations from '../../util/durations';
|
||||
import { uuidToBytes } from '../../util/uuidToBytes';
|
||||
import { MY_STORY_ID } from '../../types/Stories';
|
||||
import { isUntaggedPniString, toTaggedPni } from '../../types/ServiceId';
|
||||
import { Bootstrap } from '../bootstrap';
|
||||
import type { App } from '../bootstrap';
|
||||
import {
|
||||
|
@ -61,7 +60,6 @@ describe('pnp/PNI Signature', function (this: Mocha.Suite) {
|
|||
identifier: uuidToBytes(MY_STORY_ID),
|
||||
isBlockList: true,
|
||||
name: MY_STORY_ID,
|
||||
recipientServiceIds: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -132,9 +130,7 @@ describe('pnp/PNI Signature', function (this: Mocha.Suite) {
|
|||
});
|
||||
|
||||
debug('Open conversation with the stranger');
|
||||
await leftPane
|
||||
.locator(`[data-testid="${stranger.toContact().aci}"]`)
|
||||
.click();
|
||||
await leftPane.locator(`[data-testid="${stranger.device.aci}"]`).click();
|
||||
|
||||
debug('Accept conversation from a stranger');
|
||||
await acceptConversation(window);
|
||||
|
@ -259,7 +255,7 @@ describe('pnp/PNI Signature', function (this: Mocha.Suite) {
|
|||
|
||||
debug('Send a PNI sync message');
|
||||
const timestamp = bootstrap.getTimestamp();
|
||||
const destinationServiceId = stranger.device.pni;
|
||||
const destinationServiceIdBinary = stranger.device.pniBinary;
|
||||
const destinationE164 = stranger.device.number;
|
||||
const destinationPniIdentityKey = await stranger.device.getIdentityKey(
|
||||
ServiceIdKind.PNI
|
||||
|
@ -271,13 +267,13 @@ describe('pnp/PNI Signature', function (this: Mocha.Suite) {
|
|||
const content = {
|
||||
syncMessage: {
|
||||
sent: {
|
||||
destinationServiceId,
|
||||
destinationServiceIdBinary,
|
||||
destinationE164,
|
||||
timestamp: Long.fromNumber(timestamp),
|
||||
message: originalDataMessage,
|
||||
unidentifiedStatus: [
|
||||
{
|
||||
destinationServiceId,
|
||||
destinationServiceIdBinary,
|
||||
destinationPniIdentityKey: destinationPniIdentityKey.serialize(),
|
||||
},
|
||||
],
|
||||
|
@ -367,9 +363,7 @@ describe('pnp/PNI Signature', function (this: Mocha.Suite) {
|
|||
});
|
||||
|
||||
debug('Wait for merge to happen');
|
||||
await leftPane
|
||||
.locator(`[data-testid="${stranger.toContact().aci}"]`)
|
||||
.waitFor();
|
||||
await leftPane.locator(`[data-testid="${stranger.device.aci}"]`).waitFor();
|
||||
|
||||
{
|
||||
debug('Wait for composition input to clear');
|
||||
|
@ -409,13 +403,8 @@ describe('pnp/PNI Signature', function (this: Mocha.Suite) {
|
|||
);
|
||||
assert(aciRecord, 'ACI Contact must be in storage service');
|
||||
|
||||
assert.strictEqual(aciRecord?.aci, stranger.device.aci);
|
||||
assert.strictEqual(
|
||||
aciRecord?.pni &&
|
||||
isUntaggedPniString(aciRecord?.pni) &&
|
||||
toTaggedPni(aciRecord?.pni),
|
||||
stranger.device.pni
|
||||
);
|
||||
assert.deepEqual(aciRecord?.aciBinary, stranger.device.aciRawUuid);
|
||||
assert.deepEqual(aciRecord?.pniBinary, stranger.device.pniRawUuid);
|
||||
assert.strictEqual(aciRecord?.pniSignatureVerified, true);
|
||||
|
||||
// Two outgoing, one incoming
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
import createDebug from 'debug';
|
||||
|
||||
import * as durations from '../../util/durations';
|
||||
import { generatePni, toUntaggedPni } from '../../types/ServiceId';
|
||||
import { generatePni } from '../../types/ServiceId';
|
||||
import { Bootstrap } from '../bootstrap';
|
||||
import type { App } from '../bootstrap';
|
||||
|
||||
|
@ -93,7 +93,7 @@ describe('pnp/PNI DecryptionError unlink', function (this: Mocha.Suite) {
|
|||
},
|
||||
{
|
||||
timestamp: bootstrap.getTimestamp(),
|
||||
updatedPni: toUntaggedPni(generatePni()),
|
||||
updatedPni: generatePni(),
|
||||
}
|
||||
)
|
||||
);
|
||||
|
@ -107,7 +107,7 @@ describe('pnp/PNI DecryptionError unlink', function (this: Mocha.Suite) {
|
|||
},
|
||||
{
|
||||
timestamp: bootstrap.getTimestamp(),
|
||||
updatedPni: toUntaggedPni(desktop.pni),
|
||||
updatedPni: desktop.pni,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
|
|
@ -73,7 +73,6 @@ describe('pnp/send gv2 invite', function (this: Mocha.Suite) {
|
|||
identifier: uuidToBytes(MY_STORY_ID),
|
||||
isBlockList: true,
|
||||
name: MY_STORY_ID,
|
||||
recipientServiceIds: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -67,7 +67,6 @@ describe('pnp/username', function (this: Mocha.Suite) {
|
|||
identifier: uuidToBytes(MY_STORY_ID),
|
||||
isBlockList: true,
|
||||
name: MY_STORY_ID,
|
||||
recipientServiceIds: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -146,10 +145,16 @@ describe('pnp/username', function (this: Mocha.Suite) {
|
|||
'only one record must be removed'
|
||||
);
|
||||
|
||||
assert.strictEqual(added[0].contact?.aci, usernameContact.device.aci);
|
||||
assert.deepEqual(
|
||||
added[0].contact?.aciBinary,
|
||||
usernameContact.device.aciRawUuid
|
||||
);
|
||||
assert.strictEqual(added[0].contact?.username, '');
|
||||
|
||||
assert.strictEqual(removed[0].contact?.aci, usernameContact.device.aci);
|
||||
assert.deepEqual(
|
||||
removed[0].contact?.aciBinary,
|
||||
usernameContact.device.aciRawUuid
|
||||
);
|
||||
assert.strictEqual(removed[0].contact?.username, USERNAME);
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,6 @@ describe('story/no-sender-key', function (this: Mocha.Suite) {
|
|||
identifier: uuidToBytes(MY_STORY_ID),
|
||||
isBlockList: true,
|
||||
name: MY_STORY_ID,
|
||||
recipientServiceIds: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -9,7 +9,6 @@ import * as durations from '../../util/durations';
|
|||
import { Bootstrap } from '../bootstrap';
|
||||
import type { App } from '../bootstrap';
|
||||
import { ReceiptType } from '../../types/Receipt';
|
||||
import { toUntaggedPni } from '../../types/ServiceId';
|
||||
import {
|
||||
acceptConversation,
|
||||
typeIntoInput,
|
||||
|
@ -57,7 +56,7 @@ describe('challenge/receipts', function (this: Mocha.Suite) {
|
|||
whitelisted: true,
|
||||
e164: contact.device.number,
|
||||
identityKey: contact.getPublicKey(ServiceIdKind.PNI).serialize(),
|
||||
pni: toUntaggedPni(contact.device.pni),
|
||||
pniBinary: contact.device.pniRawUuid,
|
||||
givenName: 'Jamie',
|
||||
},
|
||||
ServiceIdKind.PNI
|
||||
|
@ -68,7 +67,7 @@ describe('challenge/receipts', function (this: Mocha.Suite) {
|
|||
whitelisted: true,
|
||||
e164: contactB.device.number,
|
||||
identityKey: contactB.getPublicKey(ServiceIdKind.PNI).serialize(),
|
||||
pni: toUntaggedPni(contactB.device.pni),
|
||||
pniBinary: contactB.device.pniRawUuid,
|
||||
givenName: 'Kim',
|
||||
},
|
||||
ServiceIdKind.PNI
|
||||
|
@ -111,10 +110,8 @@ describe('challenge/receipts', function (this: Mocha.Suite) {
|
|||
const window = await app.getWindow();
|
||||
const leftPane = window.locator('#LeftPane');
|
||||
|
||||
debug(`Opening conversation with contact (${contact.toContact().aci})`);
|
||||
await leftPane
|
||||
.locator(`[data-testid="${contact.toContact().aci}"]`)
|
||||
.click();
|
||||
debug(`Opening conversation with contact (${contact.device.aci})`);
|
||||
await leftPane.locator(`[data-testid="${contact.device.aci}"]`).click();
|
||||
|
||||
debug('Accept conversation from contact - does not trigger captcha!');
|
||||
await acceptConversation(window);
|
||||
|
@ -172,10 +169,8 @@ describe('challenge/receipts', function (this: Mocha.Suite) {
|
|||
timestamp: timestampA,
|
||||
});
|
||||
|
||||
debug(`Opening conversation with ContactA (${contact.toContact().aci})`);
|
||||
await leftPane
|
||||
.locator(`[data-testid="${contact.toContact().aci}"]`)
|
||||
.click();
|
||||
debug(`Opening conversation with ContactA (${contact.device.aci})`);
|
||||
await leftPane.locator(`[data-testid="${contact.device.aci}"]`).click();
|
||||
|
||||
debug('Accept conversation from ContactA - does not trigger captcha!');
|
||||
await acceptConversation(window);
|
||||
|
@ -186,10 +181,8 @@ describe('challenge/receipts', function (this: Mocha.Suite) {
|
|||
timestamp: timestampB,
|
||||
});
|
||||
|
||||
debug(`Opening conversation with ContactB (${contact.toContact().aci})`);
|
||||
await leftPane
|
||||
.locator(`[data-testid="${contactB.toContact().aci}"]`)
|
||||
.click();
|
||||
debug(`Opening conversation with ContactB (${contact.device.aci})`);
|
||||
await leftPane.locator(`[data-testid="${contactB.device.aci}"]`).click();
|
||||
|
||||
debug('Accept conversation from ContactB - does not trigger captcha!');
|
||||
await acceptConversation(window);
|
||||
|
@ -273,10 +266,8 @@ describe('challenge/receipts', function (this: Mocha.Suite) {
|
|||
const window = await app.getWindow();
|
||||
const leftPane = window.locator('#LeftPane');
|
||||
|
||||
debug(`Opening conversation with contact (${contact.toContact().aci})`);
|
||||
await leftPane
|
||||
.locator(`[data-testid="${contact.toContact().aci}"]`)
|
||||
.click();
|
||||
debug(`Opening conversation with contact (${contact.device.aci})`);
|
||||
await leftPane.locator(`[data-testid="${contact.device.aci}"]`).click();
|
||||
|
||||
debug('Accept conversation from contact - does not trigger captcha!');
|
||||
await acceptConversation(window);
|
||||
|
@ -342,10 +333,8 @@ describe('challenge/receipts', function (this: Mocha.Suite) {
|
|||
timestamp,
|
||||
});
|
||||
|
||||
debug(`Opening conversation with Contact B (${contactB.toContact().aci})`);
|
||||
await leftPane
|
||||
.locator(`[data-testid="${contactB.toContact().aci}"]`)
|
||||
.click();
|
||||
debug(`Opening conversation with Contact B (${contactB.device.aci})`);
|
||||
await leftPane.locator(`[data-testid="${contactB.device.aci}"]`).click();
|
||||
|
||||
debug('Accept conversation from Contact B - does not trigger captcha!');
|
||||
await acceptConversation(window);
|
||||
|
|
|
@ -57,7 +57,7 @@ describe('routing', function (this: Mocha.Suite) {
|
|||
await page.locator('#LeftPane').waitFor();
|
||||
const token = await page.evaluate(
|
||||
serviceId => window.SignalCI?.createNotificationToken(serviceId),
|
||||
friend.toContact().aci
|
||||
friend.device.aci
|
||||
);
|
||||
strictAssert(typeof token === 'string', 'token must be returned');
|
||||
const conversationUrl = showConversationRoute.toAppUrl({
|
||||
|
|
|
@ -49,7 +49,7 @@ describe('storage service', function (this: Mocha.Suite) {
|
|||
});
|
||||
|
||||
await leftPane
|
||||
.locator(`[data-testid="${firstContact.toContact().aci}"]`)
|
||||
.locator(`[data-testid="${firstContact.device.aci}"]`)
|
||||
.waitFor({ state: 'hidden' });
|
||||
|
||||
await leftPane
|
||||
|
@ -74,7 +74,7 @@ describe('storage service', function (this: Mocha.Suite) {
|
|||
});
|
||||
|
||||
await leftPane
|
||||
.locator(`[data-testid="${firstContact.toContact().aci}"]`)
|
||||
.locator(`[data-testid="${firstContact.device.aci}"]`)
|
||||
.waitFor();
|
||||
|
||||
await leftPane
|
||||
|
@ -89,7 +89,7 @@ describe('storage service', function (this: Mocha.Suite) {
|
|||
const state = await phone.expectStorageState('consistency check');
|
||||
|
||||
await leftPane
|
||||
.locator(`[data-testid="${firstContact.toContact().aci}"]`)
|
||||
.locator(`[data-testid="${firstContact.device.aci}"]`)
|
||||
.click();
|
||||
|
||||
const moreButton = conversationStack.locator(
|
||||
|
|
|
@ -41,7 +41,6 @@ describe('storage service', function (this: Mocha.Suite) {
|
|||
identifier: uuidToBytes(MY_STORY_ID),
|
||||
isBlockList: true,
|
||||
name: MY_STORY_ID,
|
||||
recipientServiceIds: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -101,7 +101,6 @@ export async function initStorage(
|
|||
identifier: uuidToBytes(MY_STORY_ID),
|
||||
isBlockList: true,
|
||||
name: MY_STORY_ID,
|
||||
recipientServiceIds: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -6,6 +6,7 @@ import { Proto } from '@signalapp/mock-server';
|
|||
|
||||
import * as durations from '../../util/durations';
|
||||
import { generateAci } from '../../types/ServiceId';
|
||||
import { toAciObject } from '../../util/ServiceId';
|
||||
import { MAX_READ_KEYS } from '../../services/storageConstants';
|
||||
import type { App, Bootstrap } from './fixtures';
|
||||
import { initStorage, debug } from './fixtures';
|
||||
|
@ -45,7 +46,7 @@ describe('storage service', function (this: Mocha.Suite) {
|
|||
|
||||
debug('wait for first contact to be pinned in the left pane');
|
||||
await leftPane
|
||||
.locator(`[data-testid="${firstContact.toContact().aci}"]`)
|
||||
.locator(`[data-testid="${firstContact.device.aci}"]`)
|
||||
.waitFor();
|
||||
|
||||
{
|
||||
|
@ -57,7 +58,7 @@ describe('storage service', function (this: Mocha.Suite) {
|
|||
type: IdentifierType.CONTACT,
|
||||
record: {
|
||||
contact: {
|
||||
aci: generateAci(),
|
||||
aciBinary: toAciObject(generateAci()).getRawUuidBytes(),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -76,7 +77,7 @@ describe('storage service', function (this: Mocha.Suite) {
|
|||
|
||||
debug('wait for last contact to be pinned in the left pane');
|
||||
await leftPane
|
||||
.locator(`[data-testid="${lastContact.toContact().aci}"]`)
|
||||
.locator(`[data-testid="${lastContact.device.aci}"]`)
|
||||
.waitFor({ timeout: durations.MINUTE });
|
||||
|
||||
debug('Verifying the final manifest version');
|
||||
|
|
|
@ -56,10 +56,8 @@ describe('storage service', function (this: Mocha.Suite) {
|
|||
const leftPane = window.locator('#LeftPane');
|
||||
|
||||
debug('Opening conversation with a stranger');
|
||||
debug(stranger.toContact().aci);
|
||||
await leftPane
|
||||
.locator(`[data-testid="${stranger.toContact().aci}"]`)
|
||||
.click();
|
||||
debug(stranger.device.aci);
|
||||
await leftPane.locator(`[data-testid="${stranger.device.aci}"]`).click();
|
||||
|
||||
debug("Verify that we stored stranger's profile key");
|
||||
const postMessageState = await phone.waitForStorageState({
|
||||
|
|
|
@ -108,7 +108,7 @@ describe('storage service', function (this: Mocha.Suite) {
|
|||
|
||||
debug('pinning contact=%d', i);
|
||||
const convo = leftPane.locator(
|
||||
`[data-testid="${contact.toContact().aci}"]`
|
||||
`[data-testid="${contact.device.aci}"]`
|
||||
);
|
||||
await convo.click();
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ describe('storage service', function (this: Mocha.Suite) {
|
|||
);
|
||||
|
||||
await leftPane
|
||||
.locator(`[data-testid="${firstContact.toContact().aci}"]`)
|
||||
.locator(`[data-testid="${firstContact.device.aci}"]`)
|
||||
.click();
|
||||
|
||||
{
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
import { assert } from 'chai';
|
||||
|
||||
import { type WritableDB } from '../../sql/Interface';
|
||||
import { SignalService as Proto } from '../../protobuf';
|
||||
import { Migrations as Proto } from '../../protobuf';
|
||||
import { generateAci } from '../../types/ServiceId';
|
||||
import { createDB, updateToVersion, insertData, getTableData } from './helpers';
|
||||
|
||||
|
|
|
@ -3,16 +3,16 @@
|
|||
|
||||
import { Transform } from 'stream';
|
||||
|
||||
import { createLogger } from '../logging/log';
|
||||
import { SignalService as Proto } from '../protobuf';
|
||||
import protobuf from '../protobuf/wrap';
|
||||
import { normalizeAci } from '../util/normalizeAci';
|
||||
import { isAciString } from '../util/isAciString';
|
||||
import { DurationInSeconds } from '../util/durations';
|
||||
import { createLogger } from '../logging/log';
|
||||
import type { ContactAvatarType } from '../types/Avatar';
|
||||
import type { AttachmentType } from '../types/Attachment';
|
||||
import type { AciString } from '../types/ServiceId';
|
||||
import { computeHash } from '../Crypto';
|
||||
import { dropNull } from '../util/dropNull';
|
||||
import { fromAciUuidBytesOrString } from '../util/ServiceId';
|
||||
import { decryptAttachmentV2ToSink } from '../AttachmentCrypto';
|
||||
|
||||
import Avatar = Proto.ContactDetails.IAvatar;
|
||||
|
@ -30,8 +30,9 @@ type OptionalFields = {
|
|||
|
||||
type MessageWithAvatar<Message extends OptionalFields> = Omit<
|
||||
Message,
|
||||
'avatar' | 'toJSON'
|
||||
'avatar' | 'toJSON' | 'aci' | 'aciBinary'
|
||||
> & {
|
||||
aci: AciString;
|
||||
avatar?: ContactAvatarType;
|
||||
expireTimer?: DurationInSeconds;
|
||||
expireTimerVersion: number | null;
|
||||
|
@ -193,7 +194,7 @@ export class ParseContactsTransform extends Transform {
|
|||
}
|
||||
|
||||
function prepareContact(
|
||||
proto: Proto.ContactDetails,
|
||||
{ aci: rawAci, aciBinary, ...proto }: Proto.ContactDetails,
|
||||
avatar?: ContactAvatarType
|
||||
): ContactDetailsWithAvatar | undefined {
|
||||
const expireTimer =
|
||||
|
@ -201,15 +202,13 @@ function prepareContact(
|
|||
? DurationInSeconds.fromSeconds(proto.expireTimer)
|
||||
: undefined;
|
||||
|
||||
// We reject incoming contacts with invalid aci information
|
||||
if (proto.aci && !isAciString(proto.aci)) {
|
||||
log.warn('ParseContactsTransform: Dropping contact with invalid aci');
|
||||
const aci = fromAciUuidBytesOrString(aciBinary, rawAci, 'ContactBuffer.aci');
|
||||
|
||||
if (aci == null) {
|
||||
log.warn('ParseContactsTransform: Dropping contact with invalid aci');
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const aci = proto.aci ? normalizeAci(proto.aci, 'ContactBuffer.aci') : null;
|
||||
|
||||
const result = {
|
||||
...proto,
|
||||
expireTimer,
|
||||
|
|
|
@ -54,7 +54,7 @@ import { DurationInSeconds } from '../util/durations';
|
|||
import { Address } from '../types/Address';
|
||||
import { QualifiedAddress } from '../types/QualifiedAddress';
|
||||
import { normalizeStoryDistributionId } from '../types/StoryDistributionId';
|
||||
import type { ServiceIdString } from '../types/ServiceId';
|
||||
import type { ServiceIdString, AciString } from '../types/ServiceId';
|
||||
import {
|
||||
fromPniObject,
|
||||
isPniString,
|
||||
|
@ -159,6 +159,12 @@ import { checkOurPniIdentityKey } from '../util/checkOurPniIdentityKey';
|
|||
import { CallLinkUpdateSyncType } from '../types/CallLink';
|
||||
import { bytesToUuid } from '../util/uuidToBytes';
|
||||
import { isBodyTooLong } from '../util/longAttachment';
|
||||
import {
|
||||
fromServiceIdBinaryOrString,
|
||||
fromAciUuidBytes,
|
||||
fromAciUuidBytesOrString,
|
||||
fromPniUuidBytesOrUntaggedString,
|
||||
} from '../util/ServiceId';
|
||||
|
||||
const log = createLogger('MessageReceiver');
|
||||
|
||||
|
@ -415,27 +421,24 @@ export default class MessageReceiver
|
|||
// Proto.Envelope fields
|
||||
type: decoded.type ?? Proto.Envelope.Type.UNKNOWN,
|
||||
source: undefined,
|
||||
sourceServiceId: decoded.sourceServiceId
|
||||
? normalizeServiceId(
|
||||
decoded.sourceServiceId,
|
||||
'MessageReceiver.handleRequest.sourceServiceId'
|
||||
)
|
||||
: undefined,
|
||||
sourceDevice: decoded.sourceDevice ?? 1,
|
||||
destinationServiceId: decoded.destinationServiceId
|
||||
? normalizeServiceId(
|
||||
decoded.destinationServiceId,
|
||||
'MessageReceiver.handleRequest.destinationServiceId'
|
||||
)
|
||||
: ourAci,
|
||||
updatedPni:
|
||||
decoded.updatedPni && isUntaggedPniString(decoded.updatedPni)
|
||||
? normalizePni(
|
||||
toTaggedPni(decoded.updatedPni),
|
||||
'MessageReceiver.handleRequest.updatedPni'
|
||||
)
|
||||
: undefined,
|
||||
timestamp: decoded.timestamp?.toNumber() ?? 0,
|
||||
sourceServiceId: fromServiceIdBinaryOrString(
|
||||
decoded.sourceServiceIdBinary,
|
||||
decoded.sourceServiceId,
|
||||
'MessageReceiver.handleRequest.sourceServiceId'
|
||||
),
|
||||
sourceDevice: decoded.sourceDeviceId ?? 1,
|
||||
destinationServiceId:
|
||||
fromServiceIdBinaryOrString(
|
||||
decoded.destinationServiceIdBinary,
|
||||
decoded.destinationServiceId,
|
||||
'MessageReceiver.handleRequest.destinationServiceId'
|
||||
) || ourAci,
|
||||
updatedPni: fromPniUuidBytesOrUntaggedString(
|
||||
decoded.updatedPniBinary,
|
||||
decoded.updatedPni,
|
||||
'MessageReceiver.handleRequest.updatedPni'
|
||||
),
|
||||
timestamp: decoded.clientTimestamp?.toNumber() ?? 0,
|
||||
content,
|
||||
serverGuid: decoded.serverGuid ?? getGuid(),
|
||||
serverTimestamp,
|
||||
|
@ -1828,7 +1831,7 @@ export default class MessageReceiver
|
|||
|
||||
if (
|
||||
serviceIdKind === ServiceIdKind.PNI &&
|
||||
envelope.type !== envelopeTypeEnum.PREKEY_BUNDLE
|
||||
envelope.type !== envelopeTypeEnum.PREKEY_MESSAGE
|
||||
) {
|
||||
log.warn(`innerDecrypt(${logId}): non-PreKey envelope on PNI`);
|
||||
return undefined;
|
||||
|
@ -1850,7 +1853,7 @@ export default class MessageReceiver
|
|||
wasEncrypted: false,
|
||||
};
|
||||
}
|
||||
if (envelope.type === envelopeTypeEnum.CIPHERTEXT) {
|
||||
if (envelope.type === envelopeTypeEnum.DOUBLE_RATCHET) {
|
||||
log.info(`decrypt/${logId}: ciphertext message`);
|
||||
if (!identifier) {
|
||||
throw new Error(
|
||||
|
@ -1879,7 +1882,7 @@ export default class MessageReceiver
|
|||
);
|
||||
return { plaintext, wasEncrypted: true };
|
||||
}
|
||||
if (envelope.type === envelopeTypeEnum.PREKEY_BUNDLE) {
|
||||
if (envelope.type === envelopeTypeEnum.PREKEY_MESSAGE) {
|
||||
log.info(`decrypt/${logId}: prekey message`);
|
||||
if (!identifier) {
|
||||
throw new Error(
|
||||
|
@ -3180,9 +3183,11 @@ export default class MessageReceiver
|
|||
|
||||
const ev = new ViewOnceOpenSyncEvent(
|
||||
{
|
||||
sourceAci: sync.senderAci
|
||||
? normalizeAci(sync.senderAci, 'handleViewOnceOpen.senderUuid')
|
||||
: undefined,
|
||||
sourceAci: fromAciUuidBytesOrString(
|
||||
sync.senderAciBinary,
|
||||
sync.senderAci,
|
||||
'handleViewOnceOpen.senderUuid'
|
||||
),
|
||||
timestamp: sync.timestamp?.toNumber(),
|
||||
},
|
||||
this.#removeFromCache.bind(this, envelope)
|
||||
|
@ -3216,12 +3221,11 @@ export default class MessageReceiver
|
|||
const ev = new MessageRequestResponseEvent(
|
||||
{
|
||||
envelopeId: envelope.id,
|
||||
threadAci: sync.threadAci
|
||||
? normalizeAci(
|
||||
sync.threadAci,
|
||||
'handleMessageRequestResponse.threadUuid'
|
||||
)
|
||||
: undefined,
|
||||
threadAci: fromAciUuidBytesOrString(
|
||||
sync.threadAciBinary,
|
||||
sync.threadAci,
|
||||
'handleMessageRequestResponse.threadUuid'
|
||||
),
|
||||
messageRequestResponseType: sync.type,
|
||||
groupV2Id: groupV2IdString,
|
||||
},
|
||||
|
@ -3357,16 +3361,20 @@ export default class MessageReceiver
|
|||
|
||||
logUnexpectedUrgentValue(envelope, 'readSync');
|
||||
|
||||
const reads = read.map(
|
||||
({ timestamp, senderAci }): ReadSyncEventData => ({
|
||||
const reads = read.map((data): ReadSyncEventData => {
|
||||
const { timestamp, senderAci: rawSenderAci, senderAciBinary } = data;
|
||||
|
||||
return {
|
||||
envelopeId: envelope.id,
|
||||
envelopeTimestamp: envelope.timestamp,
|
||||
timestamp: timestamp?.toNumber(),
|
||||
senderAci: senderAci
|
||||
? normalizeAci(senderAci, 'handleRead.senderAci')
|
||||
: undefined,
|
||||
})
|
||||
);
|
||||
senderAci: fromAciUuidBytesOrString(
|
||||
senderAciBinary,
|
||||
rawSenderAci,
|
||||
'handleRead.senderAci'
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
await this.#dispatchAndWait(
|
||||
logId,
|
||||
|
@ -3388,14 +3396,18 @@ export default class MessageReceiver
|
|||
|
||||
logUnexpectedUrgentValue(envelope, 'viewSync');
|
||||
|
||||
const views = viewed.map(
|
||||
({ timestamp, senderAci }): ViewSyncEventData => ({
|
||||
const views = viewed.map((data): ViewSyncEventData => {
|
||||
const { timestamp, senderAci: rawSenderAci, senderAciBinary } = data;
|
||||
|
||||
return {
|
||||
timestamp: timestamp?.toNumber(),
|
||||
senderAci: senderAci
|
||||
? normalizeAci(senderAci, 'handleViewed.senderAci')
|
||||
: undefined,
|
||||
})
|
||||
);
|
||||
senderAci: fromAciUuidBytesOrString(
|
||||
senderAciBinary,
|
||||
rawSenderAci,
|
||||
'handleViewed.senderAci'
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
await this.#dispatchAndWait(
|
||||
logId,
|
||||
|
@ -3879,21 +3891,40 @@ export default class MessageReceiver
|
|||
log.info(`${logId}: New e164 unblocks:`, removed);
|
||||
await this.#storage.put('blocked', blocked.numbers);
|
||||
}
|
||||
if (blocked.acis) {
|
||||
if (blocked.acisBinary?.length || blocked.acis?.length) {
|
||||
const previous = this.#storage.get('blocked-uuids', []);
|
||||
const acis = blocked.acis
|
||||
.map((aci, index) => {
|
||||
try {
|
||||
return normalizeAci(aci, `handleBlocked.acis.${index}`);
|
||||
} catch (error) {
|
||||
log.warn(
|
||||
`${logId}: ACI ${aci} was malformed`,
|
||||
Errors.toLogFormat(error)
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
})
|
||||
.filter(isNotNil);
|
||||
let acis: Array<AciString>;
|
||||
if (blocked.acisBinary?.length) {
|
||||
acis = blocked.acisBinary
|
||||
.map((aciBinary, index) => {
|
||||
try {
|
||||
return fromAciUuidBytes(aciBinary);
|
||||
} catch (error) {
|
||||
log.warn(
|
||||
`${logId}: ACI ${index} was malformed`,
|
||||
Errors.toLogFormat(error)
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
})
|
||||
.filter(isNotNil);
|
||||
} else if (blocked.acis?.length) {
|
||||
acis = blocked.acis
|
||||
.map((aci, index) => {
|
||||
try {
|
||||
return normalizeAci(aci, `handleBlocked.acis.${index}`);
|
||||
} catch (error) {
|
||||
log.warn(
|
||||
`${logId}: ACI ${aci} was malformed`,
|
||||
Errors.toLogFormat(error)
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
})
|
||||
.filter(isNotNil);
|
||||
} else {
|
||||
throw new Error('No blocked acis');
|
||||
}
|
||||
|
||||
const { added, removed } = diffArraysAsSets(previous, acis);
|
||||
if (added.length) {
|
||||
|
@ -4004,13 +4035,13 @@ export default class MessageReceiver
|
|||
function envelopeTypeToCiphertextType(type: number | undefined): number {
|
||||
const { Type } = Proto.Envelope;
|
||||
|
||||
if (type === Type.CIPHERTEXT) {
|
||||
if (type === Type.DOUBLE_RATCHET) {
|
||||
return CiphertextMessageType.Whisper;
|
||||
}
|
||||
if (type === Type.PLAINTEXT_CONTENT) {
|
||||
return CiphertextMessageType.Plaintext;
|
||||
}
|
||||
if (type === Type.PREKEY_BUNDLE) {
|
||||
if (type === Type.PREKEY_MESSAGE) {
|
||||
return CiphertextMessageType.PreKey;
|
||||
}
|
||||
if (type === Type.SERVER_DELIVERY_RECEIPT) {
|
||||
|
@ -4042,25 +4073,26 @@ function processAddressableMessage(
|
|||
return undefined;
|
||||
}
|
||||
|
||||
const { authorServiceId } = target;
|
||||
const { authorServiceId: rawAuthorServiceId, authorServiceIdBinary } = target;
|
||||
|
||||
const authorServiceId = fromServiceIdBinaryOrString(
|
||||
authorServiceIdBinary,
|
||||
rawAuthorServiceId,
|
||||
logId
|
||||
);
|
||||
|
||||
if (authorServiceId) {
|
||||
if (isAciString(authorServiceId)) {
|
||||
return {
|
||||
type: 'aci' as const,
|
||||
authorAci: normalizeAci(
|
||||
authorServiceId,
|
||||
`${logId}/processAddressableMessage/aci`
|
||||
),
|
||||
authorAci: authorServiceId,
|
||||
sentAt,
|
||||
};
|
||||
}
|
||||
if (isPniString(authorServiceId)) {
|
||||
return {
|
||||
type: 'pni' as const,
|
||||
authorPni: normalizePni(
|
||||
authorServiceId,
|
||||
`${logId}/processAddressableMessage/pni`
|
||||
),
|
||||
authorPni: authorServiceId,
|
||||
sentAt,
|
||||
};
|
||||
}
|
||||
|
@ -4087,19 +4119,30 @@ function processConversationIdentifier(
|
|||
target: Proto.IConversationIdentifier,
|
||||
logId: string
|
||||
): ConversationIdentifier | undefined {
|
||||
const { threadServiceId, threadGroupId, threadE164 } = target;
|
||||
const {
|
||||
threadServiceId: rawThreadServiceId,
|
||||
threadServiceIdBinary,
|
||||
threadGroupId,
|
||||
threadE164,
|
||||
} = target;
|
||||
|
||||
const threadServiceId = fromServiceIdBinaryOrString(
|
||||
threadServiceIdBinary,
|
||||
rawThreadServiceId,
|
||||
logId
|
||||
);
|
||||
|
||||
if (threadServiceId) {
|
||||
if (isAciString(threadServiceId)) {
|
||||
return {
|
||||
type: 'aci' as const,
|
||||
aci: normalizeAci(threadServiceId, `${logId}/aci`),
|
||||
aci: threadServiceId,
|
||||
};
|
||||
}
|
||||
if (isPniString(threadServiceId)) {
|
||||
return {
|
||||
type: 'pni' as const,
|
||||
pni: normalizePni(threadServiceId, `${logId}/pni`),
|
||||
pni: threadServiceId,
|
||||
};
|
||||
}
|
||||
log.error(
|
||||
|
|
|
@ -72,10 +72,10 @@ type OutgoingMessageOptionsType = SendOptionsType & {
|
|||
|
||||
function ciphertextMessageTypeToEnvelopeType(type: number) {
|
||||
if (type === CiphertextMessageType.PreKey) {
|
||||
return Proto.Envelope.Type.PREKEY_BUNDLE;
|
||||
return Proto.Envelope.Type.PREKEY_MESSAGE;
|
||||
}
|
||||
if (type === CiphertextMessageType.Whisper) {
|
||||
return Proto.Envelope.Type.CIPHERTEXT;
|
||||
return Proto.Envelope.Type.DOUBLE_RATCHET;
|
||||
}
|
||||
if (type === CiphertextMessageType.Plaintext) {
|
||||
return Proto.Envelope.Type.PLAINTEXT_CONTENT;
|
||||
|
|
|
@ -6,18 +6,12 @@ import pTimeout, { TimeoutError as PTimeoutError } from 'p-timeout';
|
|||
import { createLogger } from '../logging/log';
|
||||
import * as Errors from '../types/errors';
|
||||
import { MAX_DEVICE_NAME_LENGTH } from '../types/InstallScreen';
|
||||
import {
|
||||
isUntaggedPniString,
|
||||
normalizePni,
|
||||
toTaggedPni,
|
||||
} from '../types/ServiceId';
|
||||
import { strictAssert } from '../util/assert';
|
||||
import { BackOff, FIBONACCI_TIMEOUTS } from '../util/BackOff';
|
||||
import { SECOND } from '../util/durations';
|
||||
import { explodePromise } from '../util/explodePromise';
|
||||
import { drop } from '../util/drop';
|
||||
import { isLinkAndSyncEnabled } from '../util/isLinkAndSyncEnabled';
|
||||
import { normalizeAci } from '../util/normalizeAci';
|
||||
import { normalizeDeviceName } from '../util/normalizeDeviceName';
|
||||
import { linkDeviceRoute } from '../util/signalRoutes';
|
||||
import { sleep } from '../util/sleep';
|
||||
|
@ -156,10 +150,10 @@ export class Provisioner {
|
|||
provisioningCode,
|
||||
aciKeyPair,
|
||||
pniKeyPair,
|
||||
aci,
|
||||
aci: ourAci,
|
||||
profileKey,
|
||||
masterKey,
|
||||
untaggedPni,
|
||||
pni: ourPni,
|
||||
userAgent,
|
||||
readReceipts,
|
||||
ephemeralBackupKey,
|
||||
|
@ -171,7 +165,6 @@ export class Provisioner {
|
|||
strictAssert(provisioningCode, 'prepareLinkData: missing provisioningCode');
|
||||
strictAssert(aciKeyPair, 'prepareLinkData: missing aciKeyPair');
|
||||
strictAssert(pniKeyPair, 'prepareLinkData: missing pniKeyPair');
|
||||
strictAssert(aci, 'prepareLinkData: missing aci');
|
||||
strictAssert(
|
||||
Bytes.isNotEmpty(profileKey),
|
||||
'prepareLinkData: missing profileKey'
|
||||
|
@ -180,16 +173,6 @@ export class Provisioner {
|
|||
Bytes.isNotEmpty(masterKey) || accountEntropyPool,
|
||||
'prepareLinkData: missing masterKey or accountEntropyPool'
|
||||
);
|
||||
strictAssert(
|
||||
isUntaggedPniString(untaggedPni),
|
||||
'prepareLinkData: invalid untaggedPni'
|
||||
);
|
||||
|
||||
const ourAci = normalizeAci(aci, 'provisionMessage.aci');
|
||||
const ourPni = normalizePni(
|
||||
toTaggedPni(untaggedPni),
|
||||
'provisionMessage.pni'
|
||||
);
|
||||
|
||||
return {
|
||||
type: AccountType.Linked,
|
||||
|
@ -363,11 +346,14 @@ export class Provisioner {
|
|||
'Provisioner.connect: duplicate uuid'
|
||||
);
|
||||
|
||||
const proto = Proto.ProvisioningUuid.decode(body);
|
||||
strictAssert(proto.uuid, 'Provisioner.connect: expected a UUID');
|
||||
const proto = Proto.ProvisioningAddress.decode(body);
|
||||
strictAssert(
|
||||
proto.address,
|
||||
'Provisioner.connect: expected a UUID'
|
||||
);
|
||||
|
||||
state = SocketState.WaitingForEnvelope;
|
||||
uuidPromise.resolve(proto.uuid);
|
||||
uuidPromise.resolve(proto.address);
|
||||
request.respond(200, 'OK');
|
||||
} else if (requestType === ServerRequestType.ProvisioningMessage) {
|
||||
strictAssert(
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
/* eslint-disable max-classes-per-file */
|
||||
|
||||
import * as client from '@signalapp/libsignal-client';
|
||||
import { PublicKey, Aci, Pni } from '@signalapp/libsignal-client';
|
||||
import type { KeyPairType } from './Types.d';
|
||||
import * as Bytes from '../Bytes';
|
||||
import {
|
||||
|
@ -15,13 +15,23 @@ import { calculateAgreement, createKeyPair, generateKeyPair } from '../Curve';
|
|||
import { SignalService as Proto } from '../protobuf';
|
||||
import { strictAssert } from '../util/assert';
|
||||
import { dropNull } from '../util/dropNull';
|
||||
import { normalizeAci } from '../util/normalizeAci';
|
||||
import {
|
||||
type AciString,
|
||||
type PniString,
|
||||
normalizePni,
|
||||
toTaggedPni,
|
||||
isUntaggedPniString,
|
||||
fromAciObject,
|
||||
fromPniObject,
|
||||
} from '../types/ServiceId';
|
||||
|
||||
export type ProvisionDecryptResult = Readonly<{
|
||||
aciKeyPair: KeyPairType;
|
||||
pniKeyPair?: KeyPairType;
|
||||
number?: string;
|
||||
aci?: string;
|
||||
untaggedPni?: string;
|
||||
aci: AciString;
|
||||
pni: PniString;
|
||||
provisioningCode?: string;
|
||||
userAgent?: string;
|
||||
readReceipts?: boolean;
|
||||
|
@ -57,7 +67,7 @@ class ProvisioningCipherInner {
|
|||
}
|
||||
|
||||
const ecRes = calculateAgreement(
|
||||
client.PublicKey.deserialize(Buffer.from(masterEphemeral)),
|
||||
PublicKey.deserialize(Buffer.from(masterEphemeral)),
|
||||
this.keyPair.privateKey
|
||||
);
|
||||
const keys = deriveSecrets(
|
||||
|
@ -78,16 +88,36 @@ class ProvisioningCipherInner {
|
|||
? createKeyPair(pniPrivKey)
|
||||
: undefined;
|
||||
|
||||
const { aci, pni } = provisionMessage;
|
||||
strictAssert(aci, 'Missing aci in provisioning message');
|
||||
strictAssert(pni, 'Missing pni in provisioning message');
|
||||
const {
|
||||
aci: rawAci,
|
||||
pni: rawUntaggedPni,
|
||||
aciBinary,
|
||||
pniBinary,
|
||||
} = provisionMessage;
|
||||
|
||||
let aci: AciString;
|
||||
let pni: PniString;
|
||||
if (Bytes.isNotEmpty(aciBinary) && Bytes.isNotEmpty(pniBinary)) {
|
||||
aci = fromAciObject(Aci.fromUuidBytes(aciBinary));
|
||||
pni = fromPniObject(Pni.fromUuidBytes(pniBinary));
|
||||
} else if (rawAci && rawUntaggedPni) {
|
||||
strictAssert(
|
||||
isUntaggedPniString(rawUntaggedPni),
|
||||
'ProvisioningCipher: invalid untaggedPni'
|
||||
);
|
||||
|
||||
aci = normalizeAci(rawAci, 'provisionMessage.aci');
|
||||
pni = normalizePni(toTaggedPni(rawUntaggedPni), 'provisionMessage.pni');
|
||||
} else {
|
||||
throw new Error('Missing aci/pni in provisioning message');
|
||||
}
|
||||
|
||||
return {
|
||||
aciKeyPair,
|
||||
pniKeyPair,
|
||||
number: dropNull(provisionMessage.number),
|
||||
aci,
|
||||
untaggedPni: pni,
|
||||
pni,
|
||||
provisioningCode: dropNull(provisionMessage.provisioningCode),
|
||||
userAgent: dropNull(provisionMessage.userAgent),
|
||||
readReceipts: provisionMessage.readReceipts ?? false,
|
||||
|
@ -107,7 +137,7 @@ class ProvisioningCipherInner {
|
|||
};
|
||||
}
|
||||
|
||||
getPublicKey(): client.PublicKey {
|
||||
getPublicKey(): PublicKey {
|
||||
if (!this.keyPair) {
|
||||
this.keyPair = generateKeyPair();
|
||||
}
|
||||
|
@ -132,5 +162,5 @@ export default class ProvisioningCipher {
|
|||
provisionEnvelope: Proto.ProvisionEnvelope
|
||||
) => ProvisionDecryptResult;
|
||||
|
||||
getPublicKey: () => client.PublicKey;
|
||||
getPublicKey: () => PublicKey;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import PQueue from 'p-queue';
|
|||
import pMap from 'p-map';
|
||||
import type { PlaintextContent } from '@signalapp/libsignal-client';
|
||||
import {
|
||||
Pni,
|
||||
ProtocolAddress,
|
||||
SenderKeyDistributionMessage,
|
||||
} from '@signalapp/libsignal-client';
|
||||
|
@ -33,6 +32,7 @@ import {
|
|||
serviceIdSchema,
|
||||
isPniString,
|
||||
} from '../types/ServiceId';
|
||||
import { toAciObject, toPniObject, toServiceIdObject } from '../util/ServiceId';
|
||||
import type {
|
||||
ChallengeType,
|
||||
GetGroupLogOptionsType,
|
||||
|
@ -100,6 +100,7 @@ import {
|
|||
getProtoForCallHistory,
|
||||
} from '../util/callDisposition';
|
||||
import { MAX_MESSAGE_COUNT } from '../util/deleteForMe.types';
|
||||
import { isProtoBinaryEncodingEnabled } from '../util/isProtoBinaryEncodingEnabled';
|
||||
import type { GroupSendToken } from '../types/GroupSendEndorsements';
|
||||
|
||||
const log = createLogger('SendMessage');
|
||||
|
@ -415,6 +416,11 @@ class Message {
|
|||
proto.reaction.emoji = this.reaction.emoji || null;
|
||||
proto.reaction.remove = this.reaction.remove || false;
|
||||
proto.reaction.targetAuthorAci = this.reaction.targetAuthorAci || null;
|
||||
if (isProtoBinaryEncodingEnabled()) {
|
||||
proto.reaction.targetAuthorAciBinary = this.reaction.targetAuthorAci
|
||||
? toAciObject(this.reaction.targetAuthorAci).getRawUuidBytes()
|
||||
: null;
|
||||
}
|
||||
proto.reaction.targetSentTimestamp =
|
||||
this.reaction.targetTimestamp === undefined
|
||||
? null
|
||||
|
@ -520,6 +526,11 @@ class Message {
|
|||
quote.id =
|
||||
this.quote.id === undefined ? null : Long.fromNumber(this.quote.id);
|
||||
quote.authorAci = this.quote.authorAci || null;
|
||||
if (isProtoBinaryEncodingEnabled()) {
|
||||
quote.authorAciBinary = this.quote.authorAci
|
||||
? toAciObject(this.quote.authorAci).getRawUuidBytes()
|
||||
: null;
|
||||
}
|
||||
quote.text = this.quote.text || null;
|
||||
quote.attachments = this.quote.attachments.slice() || [];
|
||||
const bodyRanges = this.quote.bodyRanges || [];
|
||||
|
@ -529,6 +540,11 @@ class Message {
|
|||
bodyRange.length = range.length;
|
||||
if (BodyRange.isMention(range)) {
|
||||
bodyRange.mentionAci = range.mentionAci;
|
||||
if (isProtoBinaryEncodingEnabled()) {
|
||||
bodyRange.mentionAciBinary = toAciObject(
|
||||
range.mentionAci
|
||||
).getRawUuidBytes();
|
||||
}
|
||||
} else if (BodyRange.isFormatting(range)) {
|
||||
bodyRange.style = range.style;
|
||||
} else {
|
||||
|
@ -599,6 +615,11 @@ class Message {
|
|||
const storyContext = new StoryContext();
|
||||
if (this.storyContext.authorAci) {
|
||||
storyContext.authorAci = this.storyContext.authorAci;
|
||||
if (isProtoBinaryEncodingEnabled()) {
|
||||
storyContext.authorAciBinary = toAciObject(
|
||||
this.storyContext.authorAci
|
||||
).getRawUuidBytes();
|
||||
}
|
||||
}
|
||||
storyContext.sentTimestamp = Long.fromNumber(this.storyContext.timestamp);
|
||||
|
||||
|
@ -637,9 +658,7 @@ function addPniSignatureMessageToProto({
|
|||
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
proto.pniSignatureMessage = {
|
||||
pni: Pni.parseFromServiceIdString(
|
||||
pniSignatureMessage.pni
|
||||
).getRawUuidBytes(),
|
||||
pni: toPniObject(pniSignatureMessage.pni).getRawUuidBytes(),
|
||||
signature: pniSignatureMessage.signature,
|
||||
};
|
||||
}
|
||||
|
@ -1300,6 +1319,10 @@ export default class MessageSender {
|
|||
}
|
||||
if (destinationServiceId) {
|
||||
sentMessage.destinationServiceId = destinationServiceId;
|
||||
if (isProtoBinaryEncodingEnabled()) {
|
||||
sentMessage.destinationServiceIdBinary =
|
||||
toServiceIdObject(destinationServiceId).getServiceIdBinary();
|
||||
}
|
||||
}
|
||||
if (expirationStartTimestamp) {
|
||||
sentMessage.expirationStartTimestamp = Long.fromNumber(
|
||||
|
@ -1330,6 +1353,10 @@ export default class MessageSender {
|
|||
const serviceId = conv.getServiceId();
|
||||
if (serviceId) {
|
||||
status.destinationServiceId = serviceId;
|
||||
if (isProtoBinaryEncodingEnabled()) {
|
||||
status.destinationServiceIdBinary =
|
||||
toServiceIdObject(serviceId).getServiceIdBinary();
|
||||
}
|
||||
}
|
||||
if (isPniString(serviceId)) {
|
||||
const pniIdentityKey =
|
||||
|
@ -1801,7 +1828,11 @@ export default class MessageSender {
|
|||
const syncMessage = MessageSender.createSyncMessage();
|
||||
|
||||
const viewOnceOpen = new Proto.SyncMessage.ViewOnceOpen();
|
||||
viewOnceOpen.senderAci = senderAci;
|
||||
if (isProtoBinaryEncodingEnabled()) {
|
||||
viewOnceOpen.senderAciBinary = toAciObject(senderAci).getRawUuidBytes();
|
||||
} else {
|
||||
viewOnceOpen.senderAci = senderAci;
|
||||
}
|
||||
viewOnceOpen.timestamp = Long.fromNumber(timestamp);
|
||||
syncMessage.viewOnceOpen = viewOnceOpen;
|
||||
|
||||
|
@ -1823,7 +1854,7 @@ export default class MessageSender {
|
|||
static getBlockSync(
|
||||
options: Readonly<{
|
||||
e164s: Array<string>;
|
||||
acis: Array<string>;
|
||||
acis: Array<AciString>;
|
||||
groupIds: Array<Uint8Array>;
|
||||
}>
|
||||
): SingleProtoJobData {
|
||||
|
@ -1833,7 +1864,13 @@ export default class MessageSender {
|
|||
|
||||
const blocked = new Proto.SyncMessage.Blocked();
|
||||
blocked.numbers = options.e164s;
|
||||
blocked.acis = options.acis;
|
||||
if (isProtoBinaryEncodingEnabled()) {
|
||||
blocked.acisBinary = options.acis.map(aci =>
|
||||
toAciObject(aci).getRawUuidBytes()
|
||||
);
|
||||
} else {
|
||||
blocked.acis = options.acis;
|
||||
}
|
||||
blocked.groupIds = options.groupIds;
|
||||
syncMessage.blocked = blocked;
|
||||
|
||||
|
@ -1867,7 +1904,13 @@ export default class MessageSender {
|
|||
|
||||
const response = new Proto.SyncMessage.MessageRequestResponse();
|
||||
if (options.threadAci !== undefined) {
|
||||
response.threadAci = options.threadAci;
|
||||
if (isProtoBinaryEncodingEnabled()) {
|
||||
response.threadAciBinary = toAciObject(
|
||||
options.threadAci
|
||||
).getRawUuidBytes();
|
||||
} else {
|
||||
response.threadAci = options.threadAci;
|
||||
}
|
||||
}
|
||||
if (options.groupId) {
|
||||
response.groupId = options.groupId;
|
||||
|
@ -1950,7 +1993,12 @@ export default class MessageSender {
|
|||
const verified = new Proto.Verified();
|
||||
verified.state = state;
|
||||
if (destinationAci) {
|
||||
verified.destinationAci = destinationAci;
|
||||
if (isProtoBinaryEncodingEnabled()) {
|
||||
verified.destinationAciBinary =
|
||||
toAciObject(destinationAci).getRawUuidBytes();
|
||||
} else {
|
||||
verified.destinationAci = destinationAci;
|
||||
}
|
||||
}
|
||||
verified.identityKey = identityKey;
|
||||
verified.nullMessage = padding;
|
||||
|
@ -2501,11 +2549,23 @@ function toAddressableMessage(message: AddressableMessage) {
|
|||
targetMessage.sentTimestamp = Long.fromNumber(message.sentAt);
|
||||
|
||||
if (message.type === 'aci') {
|
||||
targetMessage.authorServiceId = message.authorAci;
|
||||
if (isProtoBinaryEncodingEnabled()) {
|
||||
targetMessage.authorServiceIdBinary = toAciObject(
|
||||
message.authorAci
|
||||
).getServiceIdBinary();
|
||||
} else {
|
||||
targetMessage.authorServiceId = message.authorAci;
|
||||
}
|
||||
} else if (message.type === 'e164') {
|
||||
targetMessage.authorE164 = message.authorE164;
|
||||
} else if (message.type === 'pni') {
|
||||
targetMessage.authorServiceId = message.authorPni;
|
||||
if (isProtoBinaryEncodingEnabled()) {
|
||||
targetMessage.authorServiceIdBinary = toPniObject(
|
||||
message.authorPni
|
||||
).getServiceIdBinary();
|
||||
} else {
|
||||
targetMessage.authorServiceId = message.authorPni;
|
||||
}
|
||||
} else {
|
||||
throw missingCaseError(message);
|
||||
}
|
||||
|
@ -2517,9 +2577,21 @@ function toConversationIdentifier(conversation: ConversationIdentifier) {
|
|||
const targetConversation = new Proto.ConversationIdentifier();
|
||||
|
||||
if (conversation.type === 'aci') {
|
||||
targetConversation.threadServiceId = conversation.aci;
|
||||
if (isProtoBinaryEncodingEnabled()) {
|
||||
targetConversation.threadServiceIdBinary = toAciObject(
|
||||
conversation.aci
|
||||
).getServiceIdBinary();
|
||||
} else {
|
||||
targetConversation.threadServiceId = conversation.aci;
|
||||
}
|
||||
} else if (conversation.type === 'pni') {
|
||||
targetConversation.threadServiceId = conversation.pni;
|
||||
if (isProtoBinaryEncodingEnabled()) {
|
||||
targetConversation.threadServiceIdBinary = toPniObject(
|
||||
conversation.pni
|
||||
).getServiceIdBinary();
|
||||
} else {
|
||||
targetConversation.threadServiceId = conversation.pni;
|
||||
}
|
||||
} else if (conversation.type === 'group') {
|
||||
targetConversation.threadGroupId = Bytes.fromBase64(conversation.groupId);
|
||||
} else if (conversation.type === 'e164') {
|
||||
|
|
7
ts/textsecure/Types.d.ts
vendored
7
ts/textsecure/Types.d.ts
vendored
|
@ -189,8 +189,6 @@ export type ProcessedBodyRange = RawBodyRange;
|
|||
|
||||
export type ProcessedGroupCallUpdate = Proto.DataMessage.IGroupCallUpdate;
|
||||
|
||||
export type ProcessedStoryContext = Proto.DataMessage.IStoryContext;
|
||||
|
||||
export type ProcessedGiftBadge = {
|
||||
expiration: number;
|
||||
id: string | undefined;
|
||||
|
@ -199,6 +197,11 @@ export type ProcessedGiftBadge = {
|
|||
state: GiftBadgeStates;
|
||||
};
|
||||
|
||||
export type ProcessedStoryContext = {
|
||||
authorAci: AciString | undefined;
|
||||
sentTimestamp: number;
|
||||
};
|
||||
|
||||
export type ProcessedDataMessage = {
|
||||
body?: string;
|
||||
bodyAttachment?: ProcessedAttachment;
|
||||
|
|
|
@ -7,6 +7,8 @@ import { isNumber } from 'lodash';
|
|||
|
||||
import { assertDev, strictAssert } from '../util/assert';
|
||||
import { dropNull, shallowDropNull } from '../util/dropNull';
|
||||
import { fromAciUuidBytesOrString } from '../util/ServiceId';
|
||||
import { getTimestampFromLong } from '../util/timestampLongUtils';
|
||||
import { SignalService as Proto } from '../protobuf';
|
||||
import { deriveGroupFields } from '../groups';
|
||||
import * as Bytes from '../Bytes';
|
||||
|
@ -22,6 +24,7 @@ import type {
|
|||
ProcessedReaction,
|
||||
ProcessedDelete,
|
||||
ProcessedGiftBadge,
|
||||
ProcessedStoryContext,
|
||||
} from './Types.d';
|
||||
import { GiftBadgeStates } from '../components/conversation/Message';
|
||||
import { APPLICATION_OCTET_STREAM, stringToMIMEType } from '../types/MIME';
|
||||
|
@ -29,8 +32,6 @@ import { SECOND, DurationInSeconds } from '../util/durations';
|
|||
import type { AnyPaymentEvent } from '../types/Payment';
|
||||
import { PaymentEventKind } from '../types/Payment';
|
||||
import { filterAndClean } from '../types/BodyRange';
|
||||
import { isAciString } from '../util/isAciString';
|
||||
import { normalizeAci } from '../util/normalizeAci';
|
||||
import { bytesToUuid } from '../util/uuidToBytes';
|
||||
import { createName } from '../util/attachmentPath';
|
||||
import { partitionBodyAndNormalAttachments } from '../types/Attachment';
|
||||
|
@ -168,14 +169,16 @@ export function processQuote(
|
|||
return undefined;
|
||||
}
|
||||
|
||||
const { authorAci } = quote;
|
||||
if (!isAciString(authorAci)) {
|
||||
throw new Error('quote.authorAci is not an ACI string');
|
||||
}
|
||||
const { authorAci: rawAuthorAci, authorAciBinary } = quote;
|
||||
const authorAci = fromAciUuidBytesOrString(
|
||||
authorAciBinary,
|
||||
rawAuthorAci,
|
||||
'Quote.authorAci'
|
||||
);
|
||||
|
||||
return {
|
||||
id: quote.id?.toNumber(),
|
||||
authorAci: normalizeAci(authorAci, 'Quote.authorAci'),
|
||||
authorAci,
|
||||
text: dropNull(quote.text),
|
||||
attachments: (quote.attachments ?? []).slice(0, 1).map(attachment => {
|
||||
return {
|
||||
|
@ -191,6 +194,30 @@ export function processQuote(
|
|||
};
|
||||
}
|
||||
|
||||
export function processStoryContext(
|
||||
storyContext?: Proto.DataMessage.IStoryContext | null
|
||||
): ProcessedStoryContext | undefined {
|
||||
if (!storyContext) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const {
|
||||
authorAci: rawAuthorAci,
|
||||
authorAciBinary,
|
||||
sentTimestamp,
|
||||
} = storyContext;
|
||||
const authorAci = fromAciUuidBytesOrString(
|
||||
authorAciBinary,
|
||||
rawAuthorAci,
|
||||
'StoryContext.authorAci'
|
||||
);
|
||||
|
||||
return {
|
||||
authorAci,
|
||||
sentTimestamp: getTimestampFromLong(sentTimestamp),
|
||||
};
|
||||
}
|
||||
|
||||
export function processContact(
|
||||
contact?: ReadonlyArray<Proto.DataMessage.IContact> | null
|
||||
): ReadonlyArray<ProcessedContact> | undefined {
|
||||
|
@ -266,15 +293,18 @@ export function processReaction(
|
|||
return undefined;
|
||||
}
|
||||
|
||||
const { targetAuthorAci } = reaction;
|
||||
if (!isAciString(targetAuthorAci)) {
|
||||
throw new Error('reaction.targetAuthorAci is not an ACI string');
|
||||
}
|
||||
const { targetAuthorAci: rawTargetAuthorAci, targetAuthorAciBinary } =
|
||||
reaction;
|
||||
const targetAuthorAci = fromAciUuidBytesOrString(
|
||||
targetAuthorAciBinary,
|
||||
rawTargetAuthorAci,
|
||||
'Reaction.targetAuthorAci'
|
||||
);
|
||||
|
||||
return {
|
||||
emoji: dropNull(reaction.emoji),
|
||||
remove: Boolean(reaction.remove),
|
||||
targetAuthorAci: normalizeAci(targetAuthorAci, 'Reaction.targetAuthorAci'),
|
||||
targetAuthorAci,
|
||||
targetTimestamp: reaction.targetSentTimestamp?.toNumber(),
|
||||
};
|
||||
}
|
||||
|
@ -380,7 +410,7 @@ export function processDataMessage(
|
|||
delete: processDelete(message.delete),
|
||||
bodyRanges: filterAndClean(message.bodyRanges),
|
||||
groupCallUpdate: dropNull(message.groupCallUpdate),
|
||||
storyContext: dropNull(message.storyContext),
|
||||
storyContext: processStoryContext(message.storyContext),
|
||||
giftBadge: processGiftBadge(message.giftBadge),
|
||||
};
|
||||
|
||||
|
|
|
@ -3,11 +3,12 @@
|
|||
|
||||
import type { SignalService as Proto } from '../protobuf';
|
||||
import type { ServiceIdString } from '../types/ServiceId';
|
||||
import { normalizeServiceId } from '../types/ServiceId';
|
||||
import { fromServiceIdBinaryOrString } from '../util/ServiceId';
|
||||
import type { ProcessedSent, ProcessedSyncMessage } from './Types.d';
|
||||
|
||||
type ProtoServiceId = Readonly<{
|
||||
destinationServiceId?: string | null;
|
||||
destinationServiceIdBinary?: Uint8Array | null;
|
||||
}>;
|
||||
|
||||
function processProtoWithDestinationServiceId<Input extends ProtoServiceId>(
|
||||
|
@ -15,14 +16,20 @@ function processProtoWithDestinationServiceId<Input extends ProtoServiceId>(
|
|||
): Omit<Input, keyof ProtoServiceId> & {
|
||||
destinationServiceId?: ServiceIdString;
|
||||
} {
|
||||
const { destinationServiceId, ...remaining } = input;
|
||||
const {
|
||||
destinationServiceId: rawDestinationServiceId,
|
||||
destinationServiceIdBinary,
|
||||
...remaining
|
||||
} = input;
|
||||
|
||||
return {
|
||||
...remaining,
|
||||
|
||||
destinationServiceId: destinationServiceId
|
||||
? normalizeServiceId(destinationServiceId, 'processSyncMessage')
|
||||
: undefined,
|
||||
destinationServiceId: fromServiceIdBinaryOrString(
|
||||
destinationServiceIdBinary,
|
||||
rawDestinationServiceId,
|
||||
'processSyncMessage'
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -34,7 +41,8 @@ function processSent(
|
|||
}
|
||||
|
||||
const {
|
||||
destinationServiceId,
|
||||
destinationServiceId: rawDestinationServiceId,
|
||||
destinationServiceIdBinary,
|
||||
unidentifiedStatus,
|
||||
storyMessageRecipients,
|
||||
...remaining
|
||||
|
@ -43,9 +51,11 @@ function processSent(
|
|||
return {
|
||||
...remaining,
|
||||
|
||||
destinationServiceId: destinationServiceId
|
||||
? normalizeServiceId(destinationServiceId, 'processSent')
|
||||
: undefined,
|
||||
destinationServiceId: fromServiceIdBinaryOrString(
|
||||
destinationServiceIdBinary,
|
||||
rawDestinationServiceId,
|
||||
'processSent'
|
||||
),
|
||||
unidentifiedStatus: unidentifiedStatus
|
||||
? unidentifiedStatus.map(processProtoWithDestinationServiceId)
|
||||
: undefined,
|
||||
|
|
|
@ -3,6 +3,17 @@
|
|||
|
||||
import { Aci, Pni, ServiceId } from '@signalapp/libsignal-client';
|
||||
import type { AciString, PniString, ServiceIdString } from '../types/ServiceId';
|
||||
import {
|
||||
normalizeServiceId,
|
||||
normalizePni,
|
||||
isUntaggedPniString,
|
||||
toTaggedPni,
|
||||
fromServiceIdObject,
|
||||
fromAciObject,
|
||||
fromPniObject,
|
||||
} from '../types/ServiceId';
|
||||
import * as Bytes from '../Bytes';
|
||||
import { normalizeAci } from './normalizeAci';
|
||||
|
||||
export function toServiceIdObject(serviceId: ServiceIdString): ServiceId {
|
||||
return ServiceId.parseFromServiceIdString(serviceId);
|
||||
|
@ -15,3 +26,86 @@ export function toAciObject(aci: AciString): Aci {
|
|||
export function toPniObject(pni: PniString): Pni {
|
||||
return Pni.parseFromServiceIdString(pni);
|
||||
}
|
||||
|
||||
export function fromServiceIdBinaryOrString(
|
||||
bytes: Uint8Array,
|
||||
fallback: string | undefined | null,
|
||||
context: string
|
||||
): ServiceIdString;
|
||||
|
||||
export function fromServiceIdBinaryOrString(
|
||||
bytes: Uint8Array | undefined | null,
|
||||
fallback: string | undefined | null,
|
||||
context: string
|
||||
): ServiceIdString | undefined;
|
||||
|
||||
export function fromServiceIdBinaryOrString(
|
||||
bytes: Uint8Array | undefined | null,
|
||||
fallback: string | undefined | null,
|
||||
context: string
|
||||
): ServiceIdString | undefined {
|
||||
if (Bytes.isNotEmpty(bytes)) {
|
||||
return fromServiceIdObject(
|
||||
ServiceId.parseFromServiceIdBinary(Buffer.from(bytes))
|
||||
);
|
||||
}
|
||||
if (fallback) {
|
||||
return normalizeServiceId(fallback, context);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function fromAciUuidBytes(bytes: Uint8Array): AciString;
|
||||
|
||||
export function fromAciUuidBytes(
|
||||
bytes: Uint8Array | undefined | null
|
||||
): AciString | undefined;
|
||||
|
||||
export function fromAciUuidBytes(
|
||||
bytes: Uint8Array | undefined | null
|
||||
): AciString | undefined {
|
||||
if (Bytes.isNotEmpty(bytes)) {
|
||||
return fromAciObject(Aci.fromUuidBytes(Buffer.from(bytes)));
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function fromAciUuidBytesOrString(
|
||||
bytes: Uint8Array,
|
||||
fallback: string | undefined | null,
|
||||
context: string
|
||||
): AciString;
|
||||
|
||||
export function fromAciUuidBytesOrString(
|
||||
bytes: Uint8Array | undefined | null,
|
||||
fallback: string | undefined | null,
|
||||
context: string
|
||||
): AciString | undefined;
|
||||
|
||||
export function fromAciUuidBytesOrString(
|
||||
bytes: Uint8Array | undefined | null,
|
||||
fallback: string | undefined | null,
|
||||
context: string
|
||||
): AciString | undefined {
|
||||
if (Bytes.isNotEmpty(bytes)) {
|
||||
return fromAciUuidBytes(bytes);
|
||||
}
|
||||
if (fallback) {
|
||||
return normalizeAci(fallback, context);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function fromPniUuidBytesOrUntaggedString(
|
||||
bytes: Uint8Array | undefined | null,
|
||||
fallback: string | undefined | null,
|
||||
context: string
|
||||
): PniString | undefined {
|
||||
if (Bytes.isNotEmpty(bytes)) {
|
||||
return fromPniObject(Pni.fromUuidBytes(Buffer.from(bytes)));
|
||||
}
|
||||
if (fallback && isUntaggedPniString(fallback)) {
|
||||
return normalizePni(toTaggedPni(fallback), context);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
import * as Bytes from '../Bytes';
|
||||
|
||||
import { SignalService as Proto } from '../protobuf';
|
||||
import { fromServiceIdBinaryOrString } from './ServiceId';
|
||||
|
||||
import PinnedConversation = Proto.AccountRecord.IPinnedConversation;
|
||||
|
||||
|
@ -22,10 +23,24 @@ export function arePinnedConversationsEqual(
|
|||
localPinnedConversation;
|
||||
|
||||
if (contact) {
|
||||
return (
|
||||
remotePinnedConversation.contact &&
|
||||
contact.serviceId === remotePinnedConversation.contact.serviceId
|
||||
const { contact: remoteContact } = remotePinnedConversation;
|
||||
if (!remoteContact) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const serviceId = fromServiceIdBinaryOrString(
|
||||
contact.serviceIdBinary,
|
||||
contact.serviceId,
|
||||
`arePinnedConversationsEqual(${index}).local`
|
||||
);
|
||||
|
||||
const remoteServiceId = fromServiceIdBinaryOrString(
|
||||
remoteContact.serviceIdBinary,
|
||||
remoteContact.serviceId,
|
||||
`arePinnedConversationsEqual(${index}).remote`
|
||||
);
|
||||
|
||||
return serviceId === remoteServiceId;
|
||||
}
|
||||
|
||||
if (groupMasterKey && groupMasterKey.length) {
|
||||
|
|
|
@ -31,7 +31,7 @@ export function checkFirstEnvelope(incoming: IncomingWebSocketRequest): void {
|
|||
}
|
||||
|
||||
const decoded = Proto.Envelope.decode(plaintext);
|
||||
const newEnvelopeTimestamp = decoded.timestamp?.toNumber();
|
||||
const newEnvelopeTimestamp = decoded.clientTimestamp?.toNumber();
|
||||
if (!isNumber(newEnvelopeTimestamp)) {
|
||||
log.warn('timestamp is not a number!');
|
||||
return;
|
||||
|
|
|
@ -5,33 +5,32 @@ import type {
|
|||
ReadonlyMessageAttributesType,
|
||||
MessageAttributesType,
|
||||
} from '../model-types.d';
|
||||
import type { SignalService as Proto } from '../protobuf';
|
||||
import type { AciString } from '../types/ServiceId';
|
||||
import { type AciString } from '../types/ServiceId';
|
||||
import { type ProcessedStoryContext } from '../textsecure/Types.d';
|
||||
import { DataReader } from '../sql/Client';
|
||||
import { createLogger } from '../logging/log';
|
||||
import { normalizeAci } from './normalizeAci';
|
||||
import { getAuthorId } from '../messages/helpers';
|
||||
import { getTimestampFromLong } from './timestampLongUtils';
|
||||
|
||||
const log = createLogger('findStoryMessage');
|
||||
|
||||
export async function findStoryMessages(
|
||||
conversationId: string,
|
||||
storyContext?: Proto.DataMessage.IStoryContext
|
||||
storyContext?: ProcessedStoryContext
|
||||
): Promise<Array<MessageAttributesType>> {
|
||||
if (!storyContext) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const { authorAci: rawAuthorAci, sentTimestamp } = storyContext;
|
||||
const { authorAci, sentTimestamp: sentAt } = storyContext;
|
||||
|
||||
if (!rawAuthorAci || !sentTimestamp) {
|
||||
if (!sentAt) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const authorAci = normalizeAci(rawAuthorAci, 'findStoryMessage');
|
||||
if (authorAci == null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const sentAt = getTimestampFromLong(sentTimestamp);
|
||||
const ourConversationId =
|
||||
window.ConversationController.getOurConversationIdOrThrow();
|
||||
|
||||
|
|
13
ts/util/isProtoBinaryEncodingEnabled.ts
Normal file
13
ts/util/isProtoBinaryEncodingEnabled.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
// Copyright 2025 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { isTestOrMockEnvironment } from '../environment';
|
||||
|
||||
export function isProtoBinaryEncodingEnabled(): boolean {
|
||||
if (isTestOrMockEnvironment()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: https://signalmessenger.atlassian.net/browse/DESKTOP-8938
|
||||
return false;
|
||||
}
|
|
@ -4,7 +4,6 @@
|
|||
import { isEqual } from 'lodash';
|
||||
import { DataReader } from '../sql/Client';
|
||||
import type { StoryRecipientUpdateEvent } from '../textsecure/messageReceiverEvents';
|
||||
import { normalizeServiceId } from '../types/ServiceId';
|
||||
import { normalizeStoryDistributionId } from '../types/StoryDistributionId';
|
||||
import { createLogger } from '../logging/log';
|
||||
import { SendStatus } from '../messages/MessageSendState';
|
||||
|
@ -13,6 +12,7 @@ import { isStory } from '../state/selectors/message';
|
|||
import { queueUpdateMessage } from './messageBatcher';
|
||||
import { isMe } from './whatTypeOfConversation';
|
||||
import { drop } from './drop';
|
||||
import { fromServiceIdBinaryOrString } from './ServiceId';
|
||||
import { handleDeleteForEveryone } from './deleteForEveryone';
|
||||
import { MessageModel } from '../models/messages';
|
||||
|
||||
|
@ -61,15 +61,22 @@ export async function onStoryRecipientUpdate(
|
|||
Set<string>
|
||||
>();
|
||||
data.storyMessageRecipients.forEach(item => {
|
||||
const { destinationServiceId: recipientServiceId } = item;
|
||||
const {
|
||||
destinationServiceId: rawDestinationServiceId,
|
||||
destinationServiceIdBinary,
|
||||
} = item;
|
||||
|
||||
if (!recipientServiceId) {
|
||||
const recipientServiceId = fromServiceIdBinaryOrString(
|
||||
destinationServiceIdBinary,
|
||||
rawDestinationServiceId,
|
||||
`${logId}.recipientServiceId`
|
||||
);
|
||||
|
||||
if (recipientServiceId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const convo = window.ConversationController.get(
|
||||
normalizeServiceId(recipientServiceId, `${logId}.recipientServiceId`)
|
||||
);
|
||||
const convo = window.ConversationController.get(recipientServiceId);
|
||||
|
||||
if (!convo || !item.distributionListIds) {
|
||||
return;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue