More protobufjs migration

This commit is contained in:
Fedor Indutny 2021-07-09 12:36:10 -07:00 committed by GitHub
parent cf06e6638e
commit ddbbe3a6b1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
70 changed files with 3967 additions and 3369 deletions

View file

@ -1,101 +1,160 @@
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable max-classes-per-file */
import { ByteBufferClass } from '../window.d';
import { AttachmentType } from './SendMessage';
import { Reader } from 'protobufjs';
type ProtobufConstructorType = {
decode: (data: ArrayBuffer) => ProtobufType;
import { SignalService as Proto } from '../protobuf';
import { normalizeUuid } from '../util/normalizeUuid';
import { typedArrayToArrayBuffer } from '../Crypto';
import Avatar = Proto.ContactDetails.IAvatar;
type OptionalAvatar = { avatar?: Avatar | null };
type DecoderBase<Message extends OptionalAvatar> = {
decodeDelimited(reader: Reader): Message | undefined;
};
type ProtobufType = {
avatar?: PackedAttachmentType;
profileKey?: any;
uuid?: string;
members: Array<string>;
export type MessageWithAvatar<Message extends OptionalAvatar> = Omit<
Message,
'avatar'
> & {
avatar?: (Avatar & { data: ArrayBuffer }) | null;
};
export type PackedAttachmentType = AttachmentType & {
length: number;
};
export type ModifiedGroupDetails = MessageWithAvatar<Proto.GroupDetails>;
export class ProtoParser {
buffer: ByteBufferClass;
export type ModifiedContactDetails = MessageWithAvatar<Proto.ContactDetails>;
protobuf: ProtobufConstructorType;
// TODO: remove once we move away from ArrayBuffers
const FIXMEU8 = Uint8Array;
constructor(arrayBuffer: ArrayBuffer, protobuf: ProtobufConstructorType) {
this.protobuf = protobuf;
this.buffer = new window.dcodeIO.ByteBuffer();
this.buffer.append(arrayBuffer);
this.buffer.offset = 0;
this.buffer.limit = arrayBuffer.byteLength;
class ParserBase<
Message extends OptionalAvatar,
Decoder extends DecoderBase<Message>
> {
protected readonly reader: Reader;
constructor(arrayBuffer: ArrayBuffer, private readonly decoder: Decoder) {
this.reader = new Reader(new FIXMEU8(arrayBuffer));
}
next(): ProtobufType | undefined | null {
protected decodeDelimited(): MessageWithAvatar<Message> | undefined {
if (this.reader.pos === this.reader.len) {
return undefined; // eof
}
try {
if (this.buffer.limit === this.buffer.offset) {
return undefined; // eof
}
const len = this.buffer.readVarint32();
const nextBuffer = this.buffer
.slice(this.buffer.offset, this.buffer.offset + len)
.toArrayBuffer();
const proto = this.decoder.decodeDelimited(this.reader);
const proto = this.protobuf.decode(nextBuffer);
this.buffer.skip(len);
if (proto.avatar) {
const attachmentLen = proto.avatar.length;
proto.avatar.data = this.buffer
.slice(this.buffer.offset, this.buffer.offset + attachmentLen)
.toArrayBuffer();
this.buffer.skip(attachmentLen);
if (!proto) {
return undefined;
}
if (proto.profileKey) {
proto.profileKey = proto.profileKey.toArrayBuffer();
if (!proto.avatar) {
return {
...proto,
avatar: null,
};
}
if (proto.uuid) {
window.normalizeUuids(
proto,
['uuid'],
'ProtoParser::next (proto.uuid)'
);
}
const attachmentLen = proto.avatar.length ?? 0;
const avatarData = this.reader.buf.slice(
this.reader.pos,
this.reader.pos + attachmentLen
);
this.reader.skip(attachmentLen);
if (proto.members) {
window.normalizeUuids(
proto,
proto.members.map((_member, i) => `members.${i}.uuid`),
'ProtoParser::next (proto.members)'
);
}
return {
...proto,
return proto;
avatar: {
...proto.avatar,
data: typedArrayToArrayBuffer(avatarData),
},
};
} catch (error) {
window.log.error(
'ProtoParser.next error:',
error && error.stack ? error.stack : error
);
return undefined;
}
}
}
export class GroupBuffer extends ParserBase<
Proto.GroupDetails,
typeof Proto.GroupDetails
> {
constructor(arrayBuffer: ArrayBuffer) {
super(arrayBuffer, Proto.GroupDetails);
}
public next(): ModifiedGroupDetails | undefined {
const proto = this.decodeDelimited();
if (!proto) {
return undefined;
}
return null;
if (!proto.members) {
return proto;
}
return {
...proto,
members: proto.members.map((member, i) => {
if (!member.uuid) {
return member;
}
return {
...member,
uuid: normalizeUuid(member.uuid, `GroupBuffer.member[${i}].uuid`),
};
}),
};
}
}
export class GroupBuffer extends ProtoParser {
export class ContactBuffer extends ParserBase<
Proto.ContactDetails,
typeof Proto.ContactDetails
> {
constructor(arrayBuffer: ArrayBuffer) {
super(arrayBuffer, window.textsecure.protobuf.GroupDetails as any);
super(arrayBuffer, Proto.ContactDetails);
}
}
export class ContactBuffer extends ProtoParser {
constructor(arrayBuffer: ArrayBuffer) {
super(arrayBuffer, window.textsecure.protobuf.ContactDetails as any);
public next(): ModifiedContactDetails | undefined {
const proto = this.decodeDelimited();
if (!proto) {
return undefined;
}
if (!proto.uuid) {
return proto;
}
const { verified } = proto;
return {
...proto,
verified:
verified && verified.destinationUuid
? {
...verified,
destinationUuid: normalizeUuid(
verified.destinationUuid,
'ContactBuffer.verified.destinationUuid'
),
}
: verified,
uuid: normalizeUuid(proto.uuid, 'ContactBuffer.uuid'),
};
}
}

View file

@ -184,7 +184,7 @@ const Crypto = {
async decryptAttachment(
encryptedBin: ArrayBuffer,
keys: ArrayBuffer,
theirDigest: ArrayBuffer
theirDigest?: ArrayBuffer
): Promise<ArrayBuffer> {
if (keys.byteLength !== 64) {
throw new Error('Got invalid length attachment keys');

View file

@ -12,8 +12,10 @@
* https://developer.mozilla.org/en-US/docs/Web/API/EventTarget
*/
export type EventHandler = (event: any) => unknown;
export default class EventTarget {
listeners?: { [type: string]: Array<Function> };
listeners?: { [type: string]: Array<EventHandler> };
dispatchEvent(ev: Event): Array<unknown> {
if (!(ev instanceof Event)) {
@ -36,7 +38,7 @@ export default class EventTarget {
return results;
}
addEventListener(eventName: string, callback: Function): void {
addEventListener(eventName: string, callback: EventHandler): void {
if (typeof eventName !== 'string') {
throw new Error('First argument expects a string');
}
@ -54,7 +56,7 @@ export default class EventTarget {
this.listeners[eventName] = listeners;
}
removeEventListener(eventName: string, callback: Function): void {
removeEventListener(eventName: string, callback: EventHandler): void {
if (typeof eventName !== 'string') {
throw new Error('First argument expects a string');
}

File diff suppressed because it is too large Load diff

View file

@ -23,7 +23,6 @@ import {
} from '@signalapp/signal-client';
import { WebAPIType } from './WebAPI';
import { ContentClass, DataMessageClass } from '../textsecure.d';
import {
CallbackResultType,
SendMetadataType,
@ -42,6 +41,7 @@ import { Sessions, IdentityKeys } from '../LibSignalStores';
import { typedArrayToArrayBuffer as toArrayBuffer } from '../Crypto';
import { updateConversationsWithUuidLookup } from '../updateConversationsWithUuidLookup';
import { getKeysForIdentifier } from './getKeysForIdentifier';
import { SignalService as Proto } from '../protobuf';
export const enum SenderCertificateMode {
WithE164,
@ -72,13 +72,13 @@ type OutgoingMessageOptionsType = SendOptionsType & {
function ciphertextMessageTypeToEnvelopeType(type: number) {
if (type === CiphertextMessageType.PreKey) {
return window.textsecure.protobuf.Envelope.Type.PREKEY_BUNDLE;
return Proto.Envelope.Type.PREKEY_BUNDLE;
}
if (type === CiphertextMessageType.Whisper) {
return window.textsecure.protobuf.Envelope.Type.CIPHERTEXT;
return Proto.Envelope.Type.CIPHERTEXT;
}
if (type === CiphertextMessageType.Plaintext) {
return window.textsecure.protobuf.Envelope.Type.PLAINTEXT_CONTENT;
return Proto.Envelope.Type.PLAINTEXT_CONTENT;
}
throw new Error(
`ciphertextMessageTypeToEnvelopeType: Unrecognized type ${type}`
@ -96,11 +96,11 @@ function getPaddedMessageLength(messageLength: number): number {
return messagePartCount * 160;
}
export function padMessage(messageBuffer: ArrayBuffer): Uint8Array {
export function padMessage(messageBuffer: Uint8Array): Uint8Array {
const plaintext = new Uint8Array(
getPaddedMessageLength(messageBuffer.byteLength + 1) - 1
);
plaintext.set(new Uint8Array(messageBuffer));
plaintext.set(messageBuffer);
plaintext[messageBuffer.byteLength] = 0x80;
return plaintext;
@ -113,7 +113,7 @@ export default class OutgoingMessage {
identifiers: Array<string>;
message: ContentClass | PlaintextContent;
message: Proto.Content | PlaintextContent;
callback: (result: CallbackResultType) => void;
@ -141,14 +141,14 @@ export default class OutgoingMessage {
server: WebAPIType,
timestamp: number,
identifiers: Array<string>,
message: ContentClass | DataMessageClass | PlaintextContent,
message: Proto.Content | Proto.DataMessage | PlaintextContent,
contentHint: number,
groupId: string | undefined,
callback: (result: CallbackResultType) => void,
options: OutgoingMessageOptionsType = {}
) {
if (message instanceof window.textsecure.protobuf.DataMessage) {
const content = new window.textsecure.protobuf.Content();
if (message instanceof Proto.DataMessage) {
const content = new Proto.Content();
content.dataMessage = message;
// eslint-disable-next-line no-param-reassign
this.message = content;
@ -304,8 +304,8 @@ export default class OutgoingMessage {
if (!this.plaintext) {
const { message } = this;
if (message instanceof window.textsecure.protobuf.Content) {
this.plaintext = padMessage(message.toArrayBuffer());
if (message instanceof Proto.Content) {
this.plaintext = padMessage(Proto.Content.encode(message).finish());
} else {
this.plaintext = message.serialize();
}
@ -324,7 +324,7 @@ export default class OutgoingMessage {
}): Promise<CiphertextMessage> {
const { message } = this;
if (message instanceof window.textsecure.protobuf.Content) {
if (message instanceof Proto.Content) {
return signalEncrypt(
Buffer.from(this.getPlaintext()),
protocolAddress,
@ -421,8 +421,7 @@ export default class OutgoingMessage {
);
return {
type:
window.textsecure.protobuf.Envelope.Type.UNIDENTIFIED_SENDER,
type: Proto.Envelope.Type.UNIDENTIFIED_SENDER,
destinationDeviceId,
destinationRegistrationId,
content: buffer.toString('base64'),

View file

@ -14,7 +14,8 @@ import {
} from '../Crypto';
import { calculateAgreement, createKeyPair, generateKeyPair } from '../Curve';
import { SignalService as Proto } from '../protobuf';
import { assert } from '../util/assert';
import { strictAssert } from '../util/assert';
import { normalizeUuid } from '../util/normalizeUuid';
// TODO: remove once we move away from ArrayBuffers
const FIXMEU8 = Uint8Array;
@ -35,7 +36,7 @@ class ProvisioningCipherInner {
async decrypt(
provisionEnvelope: Proto.ProvisionEnvelope
): Promise<ProvisionDecryptResult> {
assert(
strictAssert(
provisionEnvelope.publicKey && provisionEnvelope.body,
'Missing required fields in ProvisionEnvelope'
);
@ -79,19 +80,17 @@ class ProvisioningCipherInner {
new FIXMEU8(plaintext)
);
const privKey = provisionMessage.identityKeyPrivate;
assert(privKey, 'Missing identityKeyPrivate in ProvisionMessage');
strictAssert(privKey, 'Missing identityKeyPrivate in ProvisionMessage');
const keyPair = createKeyPair(typedArrayToArrayBuffer(privKey));
window.normalizeUuids(
provisionMessage,
['uuid'],
'ProvisioningCipher.decrypt'
);
const { uuid } = provisionMessage;
strictAssert(uuid, 'Missing uuid in provisioning message');
const ret: ProvisionDecryptResult = {
identityKeyPair: keyPair,
number: provisionMessage.number,
uuid: provisionMessage.uuid,
uuid: normalizeUuid(uuid, 'ProvisionMessage.uuid'),
provisioningCode: provisionMessage.provisioningCode,
userAgent: provisionMessage.userAgent,
readReceipts: provisionMessage.readReceipts,

View file

@ -30,22 +30,16 @@ import {
import createTaskWithTimeout from './TaskWithTimeout';
import OutgoingMessage, { SerializedCertificateType } from './OutgoingMessage';
import Crypto from './Crypto';
import * as Bytes from '../Bytes';
import {
base64ToArrayBuffer,
concatenateBytes,
getRandomBytes,
getZeroes,
hexToArrayBuffer,
typedArrayToArrayBuffer,
} from '../Crypto';
import {
AttachmentPointerClass,
CallingMessageClass,
ContentClass,
DataMessageClass,
StorageServiceCallOptionsType,
StorageServiceCredentials,
SyncMessageClass,
} from '../textsecure.d';
import { MessageError, SignedPreKeyRotationError } from './Errors';
import { BodyRangesType } from '../types/Util';
@ -56,18 +50,6 @@ import {
import { concat } from '../util/iterables';
import { SignalService as Proto } from '../protobuf';
function stringToArrayBuffer(str: string): ArrayBuffer {
if (typeof str !== 'string') {
throw new Error('Passed non-string to stringToArrayBuffer');
}
const res = new ArrayBuffer(str.length);
const uint = new Uint8Array(res);
for (let i = 0; i < str.length; i += 1) {
uint[i] = str.charCodeAt(i);
}
return res;
}
export type SendMetadataType = {
[identifier: string]: {
accessKey: string;
@ -101,7 +83,7 @@ type PreviewType = {
type QuoteAttachmentType = {
thumbnail?: AttachmentType;
attachmentPointer?: AttachmentPointerClass;
attachmentPointer?: Proto.IAttachmentPointer;
};
export type GroupV2InfoType = {
@ -130,7 +112,7 @@ export type AttachmentType = {
height: number;
caption: string;
attachmentPointer?: AttachmentPointerClass;
attachmentPointer?: Proto.IAttachmentPointer;
blurHash?: string;
};
@ -174,6 +156,9 @@ export type GroupSendOptionsType = {
groupCallUpdate?: GroupCallUpdateType;
};
// TODO: remove once we move away from ArrayBuffers
const FIXMEU8 = Uint8Array;
class Message {
attachments: Array<any>;
@ -219,7 +204,7 @@ class Message {
dataMessage: any;
attachmentPointers?: Array<any>;
attachmentPointers: Array<Proto.IAttachmentPointer> = [];
deletedForEveryoneTimestamp?: number;
@ -301,17 +286,14 @@ class Message {
}
isEndSession() {
return (
(this.flags || 0) &
window.textsecure.protobuf.DataMessage.Flags.END_SESSION
);
return (this.flags || 0) & Proto.DataMessage.Flags.END_SESSION;
}
toProto(): DataMessageClass {
if (this.dataMessage instanceof window.textsecure.protobuf.DataMessage) {
toProto(): Proto.DataMessage {
if (this.dataMessage instanceof Proto.DataMessage) {
return this.dataMessage;
}
const proto = new window.textsecure.protobuf.DataMessage();
const proto = new Proto.DataMessage();
proto.timestamp = this.timestamp;
proto.attachments = this.attachmentPointers;
@ -330,19 +312,19 @@ class Message {
proto.flags = this.flags;
}
if (this.groupV2) {
proto.groupV2 = new window.textsecure.protobuf.GroupContextV2();
proto.groupV2 = new Proto.GroupContextV2();
proto.groupV2.masterKey = this.groupV2.masterKey;
proto.groupV2.revision = this.groupV2.revision;
proto.groupV2.groupChange = this.groupV2.groupChange || null;
} else if (this.group) {
proto.group = new window.textsecure.protobuf.GroupContext();
proto.group.id = stringToArrayBuffer(this.group.id);
proto.group = new Proto.GroupContext();
proto.group.id = Bytes.fromString(this.group.id);
proto.group.type = this.group.type;
}
if (this.sticker) {
proto.sticker = new window.textsecure.protobuf.DataMessage.Sticker();
proto.sticker.packId = hexToArrayBuffer(this.sticker.packId);
proto.sticker.packKey = base64ToArrayBuffer(this.sticker.packKey);
proto.sticker = new Proto.DataMessage.Sticker();
proto.sticker.packId = Bytes.fromHex(this.sticker.packId);
proto.sticker.packKey = Bytes.fromBase64(this.sticker.packKey);
proto.sticker.stickerId = this.sticker.stickerId;
if (this.sticker.attachmentPointer) {
@ -350,7 +332,7 @@ class Message {
}
}
if (this.reaction) {
proto.reaction = new window.textsecure.protobuf.DataMessage.Reaction();
proto.reaction = new Proto.DataMessage.Reaction();
proto.reaction.emoji = this.reaction.emoji || null;
proto.reaction.remove = this.reaction.remove || false;
proto.reaction.targetAuthorUuid = this.reaction.targetAuthorUuid || null;
@ -359,7 +341,7 @@ class Message {
if (Array.isArray(this.preview)) {
proto.preview = this.preview.map(preview => {
const item = new window.textsecure.protobuf.DataMessage.Preview();
const item = new Proto.DataMessage.Preview();
item.title = preview.title;
item.url = preview.url;
item.description = preview.description || null;
@ -369,8 +351,8 @@ class Message {
});
}
if (this.quote) {
const { QuotedAttachment } = window.textsecure.protobuf.DataMessage.Quote;
const { BodyRange, Quote } = window.textsecure.protobuf.DataMessage;
const { QuotedAttachment } = Proto.DataMessage.Quote;
const { BodyRange, Quote } = Proto.DataMessage;
proto.quote = new Quote();
const { quote } = proto;
@ -396,24 +378,26 @@ class Message {
const bodyRange = new BodyRange();
bodyRange.start = range.start;
bodyRange.length = range.length;
bodyRange.mentionUuid = range.mentionUuid;
if (range.mentionUuid !== undefined) {
bodyRange.mentionUuid = range.mentionUuid;
}
return bodyRange;
});
if (
quote.bodyRanges.length &&
(!proto.requiredProtocolVersion ||
proto.requiredProtocolVersion <
window.textsecure.protobuf.DataMessage.ProtocolVersion.MENTIONS)
Proto.DataMessage.ProtocolVersion.MENTIONS)
) {
proto.requiredProtocolVersion =
window.textsecure.protobuf.DataMessage.ProtocolVersion.MENTIONS;
Proto.DataMessage.ProtocolVersion.MENTIONS;
}
}
if (this.expireTimer) {
proto.expireTimer = this.expireTimer;
}
if (this.profileKey) {
proto.profileKey = this.profileKey;
proto.profileKey = new FIXMEU8(this.profileKey);
}
if (this.deletedForEveryoneTimestamp) {
proto.delete = {
@ -422,7 +406,7 @@ class Message {
}
if (this.mentions) {
proto.requiredProtocolVersion =
window.textsecure.protobuf.DataMessage.ProtocolVersion.MENTIONS;
Proto.DataMessage.ProtocolVersion.MENTIONS;
proto.bodyRanges = this.mentions.map(
({ start, length, mentionUuid }) => ({
start,
@ -433,7 +417,7 @@ class Message {
}
if (this.groupCallUpdate) {
const { GroupCallUpdate } = window.textsecure.protobuf.DataMessage;
const { GroupCallUpdate } = Proto.DataMessage;
const groupCallUpdate = new GroupCallUpdate();
groupCallUpdate.eraId = this.groupCallUpdate.eraId;
@ -446,7 +430,9 @@ class Message {
}
toArrayBuffer() {
return this.toProto().toArrayBuffer();
return typedArrayToArrayBuffer(
Proto.DataMessage.encode(this.toProto()).finish()
);
}
}
@ -492,13 +478,13 @@ export default class MessageSender {
);
}
getRandomPadding(): ArrayBuffer {
getRandomPadding(): Uint8Array {
// Generate a random int from 1 and 512
const buffer = getRandomBytes(2);
const paddingLength = (new Uint16Array(buffer)[0] & 0x1ff) + 1;
// Generate a random padding buffer of the chosen size
return getRandomBytes(paddingLength);
return new FIXMEU8(getRandomBytes(paddingLength));
}
getPaddedAttachment(data: ArrayBuffer): ArrayBuffer {
@ -511,10 +497,11 @@ export default class MessageSender {
async makeAttachmentPointer(
attachment: AttachmentType
): Promise<AttachmentPointerClass | undefined> {
if (typeof attachment !== 'object' || attachment == null) {
return Promise.resolve(undefined);
}
): Promise<Proto.IAttachmentPointer> {
assert(
typeof attachment === 'object' && attachment !== null,
'Got null attachment in `makeAttachmentPointer`'
);
const { data, size } = attachment;
if (!(data instanceof ArrayBuffer) && !ArrayBuffer.isView(data)) {
@ -535,12 +522,12 @@ export default class MessageSender {
const result = await Crypto.encryptAttachment(padded, key, iv);
const id = await this.server.putAttachment(result.ciphertext);
const proto = new window.textsecure.protobuf.AttachmentPointer();
const proto = new Proto.AttachmentPointer();
proto.cdnId = id;
proto.contentType = attachment.contentType;
proto.key = key;
proto.key = new FIXMEU8(key);
proto.size = attachment.size;
proto.digest = result.digest;
proto.digest = new FIXMEU8(result.digest);
if (attachment.fileName) {
proto.fileName = attachment.fileName;
@ -657,11 +644,11 @@ export default class MessageSender {
return message.toArrayBuffer();
}
async getContentMessage(options: MessageOptionsType): Promise<ContentClass> {
async getContentMessage(options: MessageOptionsType): Promise<Proto.Content> {
const message = await this.getHydratedMessage(options);
const dataMessage = message.toProto();
const contentMessage = new window.textsecure.protobuf.Content();
const contentMessage = new Proto.Content();
contentMessage.dataMessage = dataMessage;
return contentMessage;
@ -685,8 +672,8 @@ export default class MessageSender {
groupMembers: Array<string>;
isTyping: boolean;
timestamp?: number;
}): ContentClass {
const ACTION_ENUM = window.textsecure.protobuf.TypingMessage.Action;
}): Proto.Content {
const ACTION_ENUM = Proto.TypingMessage.Action;
const { recipientId, groupId, isTyping, timestamp } = options;
if (!recipientId && !groupId) {
@ -698,12 +685,14 @@ export default class MessageSender {
const finalTimestamp = timestamp || Date.now();
const action = isTyping ? ACTION_ENUM.STARTED : ACTION_ENUM.STOPPED;
const typingMessage = new window.textsecure.protobuf.TypingMessage();
typingMessage.groupId = groupId || null;
const typingMessage = new Proto.TypingMessage();
if (groupId) {
typingMessage.groupId = new FIXMEU8(groupId);
}
typingMessage.action = action;
typingMessage.timestamp = finalTimestamp;
const contentMessage = new window.textsecure.protobuf.Content();
const contentMessage = new Proto.Content();
contentMessage.typingMessage = typingMessage;
return contentMessage;
@ -767,7 +756,7 @@ export default class MessageSender {
group: groupV1
? {
id: groupV1.id,
type: window.textsecure.protobuf.GroupContext.Type.DELIVER,
type: Proto.GroupContext.Type.DELIVER,
}
: undefined,
mentions,
@ -781,8 +770,8 @@ export default class MessageSender {
};
}
createSyncMessage(): SyncMessageClass {
const syncMessage = new window.textsecure.protobuf.SyncMessage();
createSyncMessage(): Proto.SyncMessage {
const syncMessage = new Proto.SyncMessage();
syncMessage.padding = this.getRandomPadding();
@ -843,7 +832,7 @@ export default class MessageSender {
}: {
timestamp: number;
recipients: Array<string>;
proto: ContentClass | DataMessageClass | PlaintextContent;
proto: Proto.Content | Proto.DataMessage | PlaintextContent;
contentHint: number;
groupId: string | undefined;
callback: (result: CallbackResultType) => void;
@ -885,7 +874,7 @@ export default class MessageSender {
}: {
timestamp: number;
recipients: Array<string>;
proto: ContentClass | DataMessageClass | PlaintextContent;
proto: Proto.Content | Proto.DataMessage | PlaintextContent;
contentHint: number;
groupId: string | undefined;
options?: SendOptionsType;
@ -920,7 +909,7 @@ export default class MessageSender {
options,
}: {
identifier: string | undefined;
proto: DataMessageClass | ContentClass | PlaintextContent;
proto: Proto.DataMessage | Proto.Content | PlaintextContent;
timestamp: number;
contentHint: number;
options?: SendOptionsType;
@ -1030,10 +1019,10 @@ export default class MessageSender {
return Promise.resolve();
}
const dataMessage = window.textsecure.protobuf.DataMessage.decode(
encodedDataMessage
const dataMessage = Proto.DataMessage.decode(
new FIXMEU8(encodedDataMessage)
);
const sentMessage = new window.textsecure.protobuf.SyncMessage.Sent();
const sentMessage = new Proto.SyncMessage.Sent();
sentMessage.timestamp = timestamp;
sentMessage.message = dataMessage;
if (destination) {
@ -1063,13 +1052,17 @@ export default class MessageSender {
// number we sent to.
if (sentTo && sentTo.length) {
sentMessage.unidentifiedStatus = sentTo.map(identifier => {
const status = new window.textsecure.protobuf.SyncMessage.Sent.UnidentifiedDeliveryStatus();
const status = new Proto.SyncMessage.Sent.UnidentifiedDeliveryStatus();
const conv = window.ConversationController.get(identifier);
if (conv && conv.get('e164')) {
status.destination = conv.get('e164');
}
if (conv && conv.get('uuid')) {
status.destinationUuid = conv.get('uuid');
if (conv) {
const e164 = conv.get('e164');
if (e164) {
status.destination = e164;
}
const uuid = conv.get('uuid');
if (uuid) {
status.destinationUuid = uuid;
}
}
status.unidentified = Boolean(unidentifiedLookup[identifier]);
return status;
@ -1078,12 +1071,10 @@ export default class MessageSender {
const syncMessage = this.createSyncMessage();
syncMessage.sent = sentMessage;
const contentMessage = new window.textsecure.protobuf.Content();
const contentMessage = new Proto.Content();
contentMessage.syncMessage = syncMessage;
const {
ContentHint,
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
return this.sendIndividualProto({
identifier: myUuid || myNumber,
@ -1101,17 +1092,14 @@ export default class MessageSender {
const myUuid = window.textsecure.storage.user.getUuid();
const myDevice = window.textsecure.storage.user.getDeviceId();
if (myDevice !== 1) {
const request = new window.textsecure.protobuf.SyncMessage.Request();
request.type =
window.textsecure.protobuf.SyncMessage.Request.Type.BLOCKED;
const request = new Proto.SyncMessage.Request();
request.type = Proto.SyncMessage.Request.Type.BLOCKED;
const syncMessage = this.createSyncMessage();
syncMessage.request = request;
const contentMessage = new window.textsecure.protobuf.Content();
const contentMessage = new Proto.Content();
contentMessage.syncMessage = syncMessage;
const {
ContentHint,
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
return this.sendIndividualProto({
identifier: myUuid || myNumber,
@ -1132,17 +1120,14 @@ export default class MessageSender {
const myUuid = window.textsecure.storage.user.getUuid();
const myDevice = window.textsecure.storage.user.getDeviceId();
if (myDevice !== 1) {
const request = new window.textsecure.protobuf.SyncMessage.Request();
request.type =
window.textsecure.protobuf.SyncMessage.Request.Type.CONFIGURATION;
const request = new Proto.SyncMessage.Request();
request.type = Proto.SyncMessage.Request.Type.CONFIGURATION;
const syncMessage = this.createSyncMessage();
syncMessage.request = request;
const contentMessage = new window.textsecure.protobuf.Content();
const contentMessage = new Proto.Content();
contentMessage.syncMessage = syncMessage;
const {
ContentHint,
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
return this.sendIndividualProto({
identifier: myUuid || myNumber,
@ -1163,16 +1148,14 @@ export default class MessageSender {
const myUuid = window.textsecure.storage.user.getUuid();
const myDevice = window.textsecure.storage.user.getDeviceId();
if (myDevice !== 1) {
const request = new window.textsecure.protobuf.SyncMessage.Request();
request.type = window.textsecure.protobuf.SyncMessage.Request.Type.GROUPS;
const request = new Proto.SyncMessage.Request();
request.type = Proto.SyncMessage.Request.Type.GROUPS;
const syncMessage = this.createSyncMessage();
syncMessage.request = request;
const contentMessage = new window.textsecure.protobuf.Content();
const contentMessage = new Proto.Content();
contentMessage.syncMessage = syncMessage;
const {
ContentHint,
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
return this.sendIndividualProto({
identifier: myUuid || myNumber,
@ -1194,17 +1177,14 @@ export default class MessageSender {
const myDevice = window.textsecure.storage.user.getDeviceId();
if (myDevice !== 1) {
const request = new window.textsecure.protobuf.SyncMessage.Request();
request.type =
window.textsecure.protobuf.SyncMessage.Request.Type.CONTACTS;
const request = new Proto.SyncMessage.Request();
request.type = Proto.SyncMessage.Request.Type.CONTACTS;
const syncMessage = this.createSyncMessage();
syncMessage.request = request;
const contentMessage = new window.textsecure.protobuf.Content();
const contentMessage = new Proto.Content();
contentMessage.syncMessage = syncMessage;
const {
ContentHint,
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
return this.sendIndividualProto({
identifier: myUuid || myNumber,
@ -1229,18 +1209,15 @@ export default class MessageSender {
return;
}
const fetchLatest = new window.textsecure.protobuf.SyncMessage.FetchLatest();
fetchLatest.type =
window.textsecure.protobuf.SyncMessage.FetchLatest.Type.STORAGE_MANIFEST;
const fetchLatest = new Proto.SyncMessage.FetchLatest();
fetchLatest.type = Proto.SyncMessage.FetchLatest.Type.STORAGE_MANIFEST;
const syncMessage = this.createSyncMessage();
syncMessage.fetchLatest = fetchLatest;
const contentMessage = new window.textsecure.protobuf.Content();
const contentMessage = new Proto.Content();
contentMessage.syncMessage = syncMessage;
const {
ContentHint,
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
await this.sendIndividualProto({
identifier: myUuid || myNumber,
@ -1262,17 +1239,15 @@ export default class MessageSender {
return;
}
const request = new window.textsecure.protobuf.SyncMessage.Request();
request.type = window.textsecure.protobuf.SyncMessage.Request.Type.KEYS;
const request = new Proto.SyncMessage.Request();
request.type = Proto.SyncMessage.Request.Type.KEYS;
const syncMessage = this.createSyncMessage();
syncMessage.request = request;
const contentMessage = new window.textsecure.protobuf.Content();
const contentMessage = new Proto.Content();
contentMessage.syncMessage = syncMessage;
const {
ContentHint,
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
await this.sendIndividualProto({
identifier: myUuid || myNumber,
@ -1295,25 +1270,19 @@ export default class MessageSender {
const myUuid = window.textsecure.storage.user.getUuid();
const myDevice = window.textsecure.storage.user.getDeviceId();
if (myDevice === 1) {
return Promise.resolve();
return;
}
const syncMessage = this.createSyncMessage();
syncMessage.read = [];
for (let i = 0; i < reads.length; i += 1) {
const read = new window.textsecure.protobuf.SyncMessage.Read();
read.timestamp = reads[i].timestamp;
read.sender = reads[i].senderE164 || null;
read.senderUuid = reads[i].senderUuid || null;
const proto = new Proto.SyncMessage.Read(reads[i]);
syncMessage.read.push(read);
syncMessage.read.push(proto);
}
const contentMessage = new window.textsecure.protobuf.Content();
const contentMessage = new Proto.Content();
contentMessage.syncMessage = syncMessage;
const {
ContentHint,
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
return this.sendIndividualProto({
identifier: myUuid || myNumber,
@ -1339,18 +1308,18 @@ export default class MessageSender {
const syncMessage = this.createSyncMessage();
const viewOnceOpen = new window.textsecure.protobuf.SyncMessage.ViewOnceOpen();
viewOnceOpen.sender = sender || null;
viewOnceOpen.senderUuid = senderUuid || null;
viewOnceOpen.timestamp = timestamp || null;
const viewOnceOpen = new Proto.SyncMessage.ViewOnceOpen();
if (sender !== undefined) {
viewOnceOpen.sender = sender;
}
viewOnceOpen.senderUuid = senderUuid;
viewOnceOpen.timestamp = timestamp;
syncMessage.viewOnceOpen = viewOnceOpen;
const contentMessage = new window.textsecure.protobuf.Content();
const contentMessage = new Proto.Content();
contentMessage.syncMessage = syncMessage;
const {
ContentHint,
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
return this.sendIndividualProto({
identifier: myUuid || myNumber,
@ -1379,19 +1348,23 @@ export default class MessageSender {
const syncMessage = this.createSyncMessage();
const response = new window.textsecure.protobuf.SyncMessage.MessageRequestResponse();
response.threadE164 = responseArgs.threadE164 || null;
response.threadUuid = responseArgs.threadUuid || null;
response.groupId = responseArgs.groupId || null;
const response = new Proto.SyncMessage.MessageRequestResponse();
if (responseArgs.threadE164 !== undefined) {
response.threadE164 = responseArgs.threadE164;
}
if (responseArgs.threadUuid !== undefined) {
response.threadUuid = responseArgs.threadUuid;
}
if (responseArgs.groupId) {
response.groupId = new FIXMEU8(responseArgs.groupId);
}
response.type = responseArgs.type;
syncMessage.messageRequestResponse = response;
const contentMessage = new window.textsecure.protobuf.Content();
const contentMessage = new Proto.Content();
contentMessage.syncMessage = syncMessage;
const {
ContentHint,
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
return this.sendIndividualProto({
identifier: myUuid || myNumber,
@ -1417,15 +1390,14 @@ export default class MessageSender {
const myNumber = window.textsecure.storage.user.getNumber();
const myUuid = window.textsecure.storage.user.getUuid();
const ENUM =
window.textsecure.protobuf.SyncMessage.StickerPackOperation.Type;
const ENUM = Proto.SyncMessage.StickerPackOperation.Type;
const packOperations = operations.map(item => {
const { packId, packKey, installed } = item;
const operation = new window.textsecure.protobuf.SyncMessage.StickerPackOperation();
operation.packId = hexToArrayBuffer(packId);
operation.packKey = base64ToArrayBuffer(packKey);
const operation = new Proto.SyncMessage.StickerPackOperation();
operation.packId = Bytes.fromHex(packId);
operation.packKey = Bytes.fromBase64(packKey);
operation.type = installed ? ENUM.INSTALL : ENUM.REMOVE;
return operation;
@ -1434,12 +1406,10 @@ export default class MessageSender {
const syncMessage = this.createSyncMessage();
syncMessage.stickerPackOperation = packOperations;
const contentMessage = new window.textsecure.protobuf.Content();
const contentMessage = new Proto.Content();
contentMessage.syncMessage = syncMessage;
const {
ContentHint,
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
return this.sendIndividualProto({
identifier: myUuid || myNumber,
@ -1476,7 +1446,7 @@ export default class MessageSender {
);
return promise.then(async () => {
const verified = new window.textsecure.protobuf.Verified();
const verified = new Proto.Verified();
verified.state = state;
if (destinationE164) {
verified.destination = destinationE164;
@ -1484,18 +1454,16 @@ export default class MessageSender {
if (destinationUuid) {
verified.destinationUuid = destinationUuid;
}
verified.identityKey = identityKey;
verified.identityKey = new FIXMEU8(identityKey);
verified.nullMessage = padding;
const syncMessage = this.createSyncMessage();
syncMessage.verified = verified;
const secondMessage = new window.textsecure.protobuf.Content();
const secondMessage = new Proto.Content();
secondMessage.syncMessage = syncMessage;
const {
ContentHint,
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
await this.sendIndividualProto({
identifier: myUuid || myNumber,
@ -1515,21 +1483,19 @@ export default class MessageSender {
options: SendOptionsType,
groupId?: string
): Promise<CallbackResultType> {
const {
ContentHint,
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
return this.sendMessage({
messageOptions: {
recipients,
timestamp: Date.now(),
profileKey,
flags: window.textsecure.protobuf.DataMessage.Flags.PROFILE_KEY_UPDATE,
flags: Proto.DataMessage.Flags.PROFILE_KEY_UPDATE,
...(groupId
? {
group: {
id: groupId,
type: window.textsecure.protobuf.GroupContext.Type.DELIVER,
type: Proto.GroupContext.Type.DELIVER,
},
}
: {}),
@ -1542,18 +1508,16 @@ export default class MessageSender {
async sendCallingMessage(
recipientId: string,
callingMessage: CallingMessageClass,
callingMessage: Proto.ICallingMessage,
options?: SendOptionsType
): Promise<void> {
const recipients = [recipientId];
const finalTimestamp = Date.now();
const contentMessage = new window.textsecure.protobuf.Content();
const contentMessage = new Proto.Content();
contentMessage.callingMessage = callingMessage;
const {
ContentHint,
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
await this.sendMessageProtoAndWait({
timestamp: finalTimestamp,
@ -1583,17 +1547,14 @@ export default class MessageSender {
return Promise.resolve();
}
const receiptMessage = new window.textsecure.protobuf.ReceiptMessage();
receiptMessage.type =
window.textsecure.protobuf.ReceiptMessage.Type.DELIVERY;
const receiptMessage = new Proto.ReceiptMessage();
receiptMessage.type = Proto.ReceiptMessage.Type.DELIVERY;
receiptMessage.timestamp = timestamps;
const contentMessage = new window.textsecure.protobuf.Content();
const contentMessage = new Proto.Content();
contentMessage.receiptMessage = receiptMessage;
const {
ContentHint,
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
return this.sendIndividualProto({
identifier: uuid || e164,
@ -1615,16 +1576,14 @@ export default class MessageSender {
timestamps: Array<number>;
options?: SendOptionsType;
}): Promise<CallbackResultType> {
const receiptMessage = new window.textsecure.protobuf.ReceiptMessage();
receiptMessage.type = window.textsecure.protobuf.ReceiptMessage.Type.READ;
const receiptMessage = new Proto.ReceiptMessage();
receiptMessage.type = Proto.ReceiptMessage.Type.READ;
receiptMessage.timestamp = timestamps;
const contentMessage = new window.textsecure.protobuf.Content();
const contentMessage = new Proto.Content();
contentMessage.receiptMessage = receiptMessage;
const {
ContentHint,
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
return this.sendIndividualProto({
identifier: senderUuid || senderE164,
@ -1640,10 +1599,10 @@ export default class MessageSender {
uuid,
e164,
padding,
}: { uuid?: string; e164?: string; padding?: ArrayBuffer },
}: { uuid?: string; e164?: string; padding?: Uint8Array },
options?: SendOptionsType
): Promise<CallbackResultType> {
const nullMessage = new window.textsecure.protobuf.NullMessage();
const nullMessage = new Proto.NullMessage();
const identifier = uuid || e164;
if (!identifier) {
@ -1652,12 +1611,10 @@ export default class MessageSender {
nullMessage.padding = padding || this.getRandomPadding();
const contentMessage = new window.textsecure.protobuf.Content();
const contentMessage = new Proto.Content();
contentMessage.nullMessage = nullMessage;
const {
ContentHint,
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
// We want the NullMessage to look like a normal outgoing message
const timestamp = Date.now();
@ -1679,9 +1636,9 @@ export default class MessageSender {
CallbackResultType | void | Array<CallbackResultType | void | Array<void>>
> {
window.log.info('resetSession: start');
const proto = new window.textsecure.protobuf.DataMessage();
const proto = new Proto.DataMessage();
proto.body = 'TERMINATE';
proto.flags = window.textsecure.protobuf.DataMessage.Flags.END_SESSION;
proto.flags = Proto.DataMessage.Flags.END_SESSION;
proto.timestamp = timestamp;
const identifier = uuid || e164;
@ -1691,9 +1648,7 @@ export default class MessageSender {
throw error;
};
const {
ContentHint,
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
const sendToContactPromise = window.textsecure.storage.protocol
.archiveAllSessions(identifier)
@ -1723,7 +1678,9 @@ export default class MessageSender {
return sendToContactPromise;
}
const buffer = proto.toArrayBuffer();
const buffer = typedArrayToArrayBuffer(
Proto.DataMessage.encode(proto).finish()
);
const sendSyncPromise = this.sendSyncMessage({
encodedDataMessage: buffer,
timestamp,
@ -1745,9 +1702,7 @@ export default class MessageSender {
profileKey?: ArrayBuffer,
options?: SendOptionsType
): Promise<CallbackResultType> {
const {
ContentHint,
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
return this.sendMessage({
messageOptions: {
@ -1755,8 +1710,7 @@ export default class MessageSender {
timestamp,
expireTimer,
profileKey,
flags:
window.textsecure.protobuf.DataMessage.Flags.EXPIRATION_TIMER_UPDATE,
flags: Proto.DataMessage.Flags.EXPIRATION_TIMER_UPDATE,
},
contentHint: ContentHint.DEFAULT,
groupId: undefined,
@ -1773,9 +1727,7 @@ export default class MessageSender {
plaintext: PlaintextContent;
uuid: string;
}): Promise<CallbackResultType> {
const {
ContentHint,
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
return this.sendMessageProtoAndWait({
timestamp: Date.now(),
@ -1799,13 +1751,17 @@ export default class MessageSender {
options,
}: {
recipients: Array<string>;
proto: ContentClass;
proto: Proto.Content;
timestamp: number;
contentHint: number;
groupId: string | undefined;
options?: SendOptionsType;
}): Promise<CallbackResultType> {
const dataMessage = proto.dataMessage?.toArrayBuffer();
const dataMessage = proto.dataMessage
? typedArrayToArrayBuffer(
Proto.DataMessage.encode(proto.dataMessage).finish()
)
: undefined;
const myE164 = window.textsecure.storage.user.getNumber();
const myUuid = window.textsecure.storage.user.getUuid();
@ -1887,14 +1843,12 @@ export default class MessageSender {
},
options?: SendOptionsType
): Promise<CallbackResultType> {
const contentMessage = new window.textsecure.protobuf.Content();
const contentMessage = new Proto.Content();
const senderKeyDistributionMessage = await this.getSenderKeyDistributionMessage(
distributionId
);
contentMessage.senderKeyDistributionMessage = window.dcodeIO.ByteBuffer.wrap(
typedArrayToArrayBuffer(senderKeyDistributionMessage.serialize())
);
contentMessage.senderKeyDistributionMessage = senderKeyDistributionMessage.serialize();
return this.sendGroupProto({
recipients: identifiers,
@ -1913,14 +1867,16 @@ export default class MessageSender {
groupIdentifiers: Array<string>,
options?: SendOptionsType
): Promise<CallbackResultType> {
const proto = new window.textsecure.protobuf.DataMessage();
proto.group = new window.textsecure.protobuf.GroupContext();
proto.group.id = stringToArrayBuffer(groupId);
proto.group.type = window.textsecure.protobuf.GroupContext.Type.QUIT;
const proto = new Proto.Content({
dataMessage: {
group: {
id: Bytes.fromString(groupId),
type: Proto.GroupContext.Type.QUIT,
},
},
});
const {
ContentHint,
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
return this.sendGroupProto({
recipients: groupIdentifiers,
proto,
@ -1949,11 +1905,10 @@ export default class MessageSender {
timestamp,
expireTimer,
profileKey,
flags:
window.textsecure.protobuf.DataMessage.Flags.EXPIRATION_TIMER_UPDATE,
flags: Proto.DataMessage.Flags.EXPIRATION_TIMER_UPDATE,
group: {
id: groupId,
type: window.textsecure.protobuf.GroupContext.Type.DELIVER,
type: Proto.GroupContext.Type.DELIVER,
},
};
@ -1967,9 +1922,7 @@ export default class MessageSender {
});
}
const {
ContentHint,
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
return this.sendMessage({
messageOptions,
contentHint: ContentHint.DEFAULT,

View file

@ -6,8 +6,9 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable max-classes-per-file */
import EventTarget from './EventTarget';
import EventTarget, { EventHandler } from './EventTarget';
import MessageReceiver from './MessageReceiver';
import { ContactSyncEvent, GroupSyncEvent } from './messageReceiverEvents';
import MessageSender from './SendMessage';
import { assert } from '../util/assert';
@ -20,9 +21,9 @@ class SyncRequestInner extends EventTarget {
timeout: any;
oncontact: Function;
oncontact: (event: ContactSyncEvent) => void;
ongroup: Function;
ongroup: (event: GroupSyncEvent) => void;
timeoutMillis: number;
@ -43,10 +44,10 @@ class SyncRequestInner extends EventTarget {
}
this.oncontact = this.onContactSyncComplete.bind(this);
receiver.addEventListener('contactsync', this.oncontact);
receiver.addEventListener('contactSync', this.oncontact);
this.ongroup = this.onGroupSyncComplete.bind(this);
receiver.addEventListener('groupsync', this.ongroup);
receiver.addEventListener('groupSync', this.ongroup);
this.timeoutMillis = timeoutMillis || 60000;
}
@ -126,9 +127,15 @@ class SyncRequestInner extends EventTarget {
export default class SyncRequest {
private inner: SyncRequestInner;
addEventListener: (name: string, handler: Function) => void;
addEventListener: (
name: 'success' | 'timeout',
handler: EventHandler
) => void;
removeEventListener: (name: string, handler: Function) => void;
removeEventListener: (
name: 'success' | 'timeout',
handler: EventHandler
) => void;
constructor(
sender: MessageSender,

View file

@ -1,6 +1,8 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { SignalService as Proto } from '../protobuf';
export {
IdentityKeyType,
PreKeyType,
@ -56,3 +58,152 @@ export type OuterSignedPrekeyType = {
};
export type SessionResetsType = Record<string, number>;
export type ProcessedEnvelope = Readonly<{
id: string;
receivedAtCounter: number;
receivedAtDate: number;
messageAgeSec: number;
// Mostly from Proto.Envelope except for null/undefined
type: Proto.Envelope.Type;
source?: string;
sourceUuid?: string;
sourceDevice?: number;
timestamp: number;
legacyMessage?: Uint8Array;
content?: Uint8Array;
serverGuid: string;
serverTimestamp: number;
}>;
export type ProcessedAttachment = {
cdnId?: string;
cdnKey?: string;
digest?: string;
contentType?: string;
key?: string;
size?: number;
fileName?: string;
flags?: number;
width?: number;
height?: number;
caption?: string;
blurHash?: string;
cdnNumber?: number;
};
export type ProcessedGroupContext = {
id: string;
type: Proto.GroupContext.Type;
name?: string;
membersE164: ReadonlyArray<string>;
avatar?: ProcessedAttachment;
// Computed fields
derivedGroupV2Id: string;
};
export type ProcessedGroupV2Context = {
masterKey: string;
revision?: number;
groupChange?: string;
// Computed fields
id: string;
secretParams: string;
publicParams: string;
};
export type ProcessedQuoteAttachment = {
contentType?: string;
fileName?: string;
thumbnail?: ProcessedAttachment;
};
export type ProcessedQuote = {
id?: number;
authorUuid?: string;
text?: string;
attachments: ReadonlyArray<ProcessedQuoteAttachment>;
bodyRanges: ReadonlyArray<Proto.DataMessage.IBodyRange>;
};
export type ProcessedAvatar = {
avatar?: ProcessedAttachment;
isProfile: boolean;
};
export type ProcessedContact = Omit<Proto.DataMessage.IContact, 'avatar'> & {
avatar?: ProcessedAvatar;
};
export type ProcessedPreview = {
url?: string;
title?: string;
image?: ProcessedAttachment;
description?: string;
date?: number;
};
export type ProcessedSticker = {
packId?: string;
packKey?: string;
stickerId?: number;
data?: ProcessedAttachment;
};
export type ProcessedReaction = {
emoji?: string;
remove: boolean;
targetAuthorUuid?: string;
targetTimestamp?: number;
};
export type ProcessedDelete = {
targetSentTimestamp?: number;
};
export type ProcessedBodyRange = Proto.DataMessage.IBodyRange;
export type ProcessedGroupCallUpdate = Proto.DataMessage.IGroupCallUpdate;
export type ProcessedDataMessage = {
body?: string;
attachments: ReadonlyArray<ProcessedAttachment>;
group?: ProcessedGroupContext;
groupV2?: ProcessedGroupV2Context;
flags: number;
expireTimer: number;
profileKey?: string;
timestamp: number;
quote?: ProcessedQuote;
contact?: ReadonlyArray<ProcessedContact>;
preview?: ReadonlyArray<ProcessedPreview>;
sticker?: ProcessedSticker;
requiredProtocolVersion?: number;
isViewOnce: boolean;
reaction?: ProcessedReaction;
delete?: ProcessedDelete;
bodyRanges?: ReadonlyArray<ProcessedBodyRange>;
groupCallUpdate?: ProcessedGroupCallUpdate;
};
export type ProcessedUnidentifiedDeliveryStatus = Omit<
Proto.SyncMessage.Sent.IUnidentifiedDeliveryStatus,
'destinationUuid'
> & {
destinationUuid?: string;
};
export type ProcessedSent = Omit<
Proto.SyncMessage.ISent,
'destinationId' | 'unidentifiedStatus'
> & {
destinationId?: string;
unidentifiedStatus?: Array<ProcessedUnidentifiedDeliveryStatus>;
};
export type ProcessedSyncMessage = Omit<Proto.ISyncMessage, 'sent'> & {
sent?: ProcessedSent;
};

View file

@ -33,7 +33,7 @@ import { Long } from '../window.d';
import { assert } from '../util/assert';
import { getUserAgent } from '../util/getUserAgent';
import { toWebSafeBase64 } from '../util/webSafeBase64';
import { isPackIdValid, redactPackId } from '../../js/modules/stickers';
import { isPackIdValid, redactPackId } from '../types/Stickers';
import {
arrayBufferToBase64,
base64ToArrayBuffer,
@ -53,7 +53,6 @@ import { calculateAgreement, generateKeyPair } from '../Curve';
import * as linkPreviewFetch from '../linkPreviews/linkPreviewFetch';
import {
AvatarUploadAttributesClass,
StorageServiceCallOptionsType,
StorageServiceCredentials,
} from '../textsecure.d';
@ -2161,7 +2160,7 @@ export function initialize({
return Proto.GroupExternalCredential.decode(new FIXMEU8(response));
}
function verifyAttributes(attributes: AvatarUploadAttributesClass) {
function verifyAttributes(attributes: Proto.IAvatarUploadAttributes) {
const {
key,
credential,
@ -2213,8 +2212,8 @@ export function initialize({
responseType: 'arraybuffer',
host: storageUrl,
});
const attributes = window.textsecure.protobuf.AvatarUploadAttributes.decode(
response
const attributes = Proto.AvatarUploadAttributes.decode(
new FIXMEU8(response)
);
const verified = verifyAttributes(attributes);

View file

@ -26,7 +26,7 @@
import { connection as WebSocket, IMessage } from 'websocket';
import EventTarget from './EventTarget';
import EventTarget, { EventHandler } from './EventTarget';
import { dropNull } from '../util/dropNull';
import { isOlderThan } from '../util/timestamp';
@ -120,6 +120,12 @@ export type WebSocketResourceOptions = {
keepalive?: KeepAliveOptionsType | true;
};
export class CloseEvent extends Event {
constructor(public readonly code: number, public readonly reason: string) {
super('close');
}
}
export default class WebSocketResource extends EventTarget {
private outgoingId = 1;
@ -159,6 +165,15 @@ export default class WebSocketResource extends EventTarget {
});
}
public addEventListener(
name: 'close',
handler: (ev: CloseEvent) => void
): void;
public addEventListener(name: string, handler: EventHandler): void {
return super.addEventListener(name, handler);
}
public sendRequest(
options: OutgoingWebSocketRequestOptions
): OutgoingWebSocketRequest {
@ -204,10 +219,7 @@ export default class WebSocketResource extends EventTarget {
}
window.log.warn('Dispatching our own socket close event');
const ev = new Event('close');
ev.code = code;
ev.reason = reason;
this.dispatchEvent(ev);
this.dispatchEvent(new CloseEvent(code, reason || 'normal'));
}, 5000);
}

View file

@ -0,0 +1,370 @@
// Copyright 2020-2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
/* eslint-disable max-classes-per-file */
import { SignalService as Proto } from '../protobuf';
import { ProcessedDataMessage, ProcessedSent } from './Types.d';
import type {
ModifiedContactDetails,
ModifiedGroupDetails,
} from './ContactsParser';
export class ReconnectEvent extends Event {
constructor() {
super('reconnect');
}
}
export class EmptyEvent extends Event {
constructor() {
super('empty');
}
}
export class ProgressEvent extends Event {
public readonly count: number;
constructor({ count }: { count: number }) {
super('progress');
this.count = count;
}
}
export type TypingEventData = Readonly<{
typingMessage: Proto.ITypingMessage;
timestamp: number;
started: boolean;
stopped: boolean;
groupId?: string;
groupV2Id?: string;
}>;
export type TypingEventConfig = {
sender?: string;
senderUuid?: string;
senderDevice: number;
typing: TypingEventData;
};
export class TypingEvent extends Event {
public readonly sender?: string;
public readonly senderUuid?: string;
public readonly senderDevice: number;
public readonly typing: TypingEventData;
constructor({ sender, senderUuid, senderDevice, typing }: TypingEventConfig) {
super('typing');
this.sender = sender;
this.senderUuid = senderUuid;
this.senderDevice = senderDevice;
this.typing = typing;
}
}
export class ErrorEvent extends Event {
constructor(public readonly error: Error) {
super('error');
}
}
export type DecryptionErrorEventData = Readonly<{
cipherTextBytes?: ArrayBuffer;
cipherTextType?: number;
contentHint?: number;
groupId?: string;
receivedAtCounter: number;
receivedAtDate: number;
senderDevice: number;
senderUuid: string;
timestamp: number;
}>;
export class DecryptionErrorEvent extends Event {
constructor(public readonly decryptionError: DecryptionErrorEventData) {
super('decryption-error');
}
}
export type RetryRequestEventData = Readonly<{
groupId?: string;
requesterUuid: string;
requesterDevice: number;
senderDevice: number;
sentAt: number;
}>;
export class RetryRequestEvent extends Event {
constructor(public readonly retryRequest: RetryRequestEventData) {
super('retry-request');
}
}
export class ContactEvent extends Event {
constructor(public readonly contactDetails: ModifiedContactDetails) {
super('contact');
}
}
export class ContactSyncEvent extends Event {
constructor() {
super('contactSync');
}
}
export class GroupEvent extends Event {
constructor(public readonly groupDetails: ModifiedGroupDetails) {
super('group');
}
}
export class GroupSyncEvent extends Event {
constructor() {
super('groupSync');
}
}
//
// Confirmable events below
//
export type ConfirmCallback = () => void;
export class ConfirmableEvent extends Event {
constructor(type: string, public readonly confirm: ConfirmCallback) {
super(type);
}
}
export type DeliveryEventData = Readonly<{
timestamp: number;
envelopeTimestamp?: number;
source?: string;
sourceUuid?: string;
sourceDevice?: number;
}>;
export class DeliveryEvent extends ConfirmableEvent {
constructor(
public readonly deliveryReceipt: DeliveryEventData,
confirm: ConfirmCallback
) {
super('delivery', confirm);
}
}
export type SentEventData = Readonly<{
destination?: string;
destinationUuid?: string;
timestamp?: number;
serverTimestamp?: number;
device?: number;
unidentifiedStatus: ProcessedSent['unidentifiedStatus'];
message: ProcessedDataMessage;
isRecipientUpdate: boolean;
receivedAtCounter: number;
receivedAtDate: number;
expirationStartTimestamp?: number;
}>;
export class SentEvent extends ConfirmableEvent {
constructor(public readonly data: SentEventData, confirm: ConfirmCallback) {
super('sent', confirm);
}
}
export type ProfileKeyUpdateData = Readonly<{
source?: string;
sourceUuid?: string;
profileKey: string;
}>;
export class ProfileKeyUpdateEvent extends ConfirmableEvent {
constructor(
public readonly data: ProfileKeyUpdateData,
confirm: ConfirmCallback
) {
super('profileKeyUpdate', confirm);
}
}
export type MessageEventData = Readonly<{
source?: string;
sourceUuid?: string;
sourceDevice?: number;
timestamp: number;
serverGuid?: string;
serverTimestamp?: number;
unidentifiedDeliveryReceived: boolean;
message: ProcessedDataMessage;
receivedAtCounter: number;
receivedAtDate: number;
}>;
export class MessageEvent extends ConfirmableEvent {
constructor(
public readonly data: MessageEventData,
confirm: ConfirmCallback
) {
super('message', confirm);
}
}
export type ReadEventData = Readonly<{
timestamp: number;
envelopeTimestamp: number;
source?: string;
sourceUuid?: string;
}>;
export class ReadEvent extends ConfirmableEvent {
constructor(public readonly read: ReadEventData, confirm: ConfirmCallback) {
super('read', confirm);
}
}
export class ConfigurationEvent extends ConfirmableEvent {
constructor(
public readonly configuration: Proto.SyncMessage.IConfiguration,
confirm: ConfirmCallback
) {
super('configuration', confirm);
}
}
export type ViewSyncOptions = {
source?: string;
sourceUuid?: string;
timestamp?: number;
};
export class ViewSyncEvent extends ConfirmableEvent {
public readonly source?: string;
public readonly sourceUuid?: string;
public readonly timestamp?: number;
constructor(
{ source, sourceUuid, timestamp }: ViewSyncOptions,
confirm: ConfirmCallback
) {
super('viewSync', confirm);
this.source = source;
this.sourceUuid = sourceUuid;
this.timestamp = timestamp;
}
}
export type MessageRequestResponseOptions = {
threadE164?: string;
threadUuid?: string;
messageRequestResponseType: Proto.SyncMessage.IMessageRequestResponse['type'];
groupId?: string;
groupV2Id?: string;
};
export class MessageRequestResponseEvent extends ConfirmableEvent {
public readonly threadE164?: string;
public readonly threadUuid?: string;
public readonly messageRequestResponseType?: MessageRequestResponseOptions['messageRequestResponseType'];
public readonly groupId?: string;
public readonly groupV2Id?: string;
constructor(
{
threadE164,
threadUuid,
messageRequestResponseType,
groupId,
groupV2Id,
}: MessageRequestResponseOptions,
confirm: ConfirmCallback
) {
super('messageRequestResponse', confirm);
this.threadE164 = threadE164;
this.threadUuid = threadUuid;
this.messageRequestResponseType = messageRequestResponseType;
this.groupId = groupId;
this.groupV2Id = groupV2Id;
}
}
export class FetchLatestEvent extends ConfirmableEvent {
constructor(
public readonly eventType: Proto.SyncMessage.IFetchLatest['type'],
confirm: ConfirmCallback
) {
super('fetchLatest', confirm);
}
}
export class KeysEvent extends ConfirmableEvent {
constructor(
public readonly storageServiceKey: ArrayBuffer,
confirm: ConfirmCallback
) {
super('keys', confirm);
}
}
export type StickerPackEventData = Readonly<{
id?: string;
key?: string;
isInstall: boolean;
isRemove: boolean;
}>;
export class StickerPackEvent extends ConfirmableEvent {
constructor(
public readonly stickerPacks: ReadonlyArray<StickerPackEventData>,
confirm: ConfirmCallback
) {
super('sticker-pack', confirm);
}
}
export type VerifiedEventData = Readonly<{
state: Proto.IVerified['state'];
destination?: string;
destinationUuid?: string;
identityKey?: ArrayBuffer;
// Used in `ts/background.ts`
viaContactSync?: boolean;
}>;
export class VerifiedEvent extends ConfirmableEvent {
constructor(
public readonly verified: VerifiedEventData,
confirm: ConfirmCallback
) {
super('verified', confirm);
}
}
export type ReadSyncEventData = Readonly<{
timestamp?: number;
envelopeTimestamp: number;
sender?: string;
senderUuid?: string;
}>;
export class ReadSyncEvent extends ConfirmableEvent {
constructor(
public readonly read: ReadSyncEventData,
confirm: ConfirmCallback
) {
super('readSync', confirm);
}
}

View file

@ -0,0 +1,352 @@
// Copyright 2020-2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import Long from 'long';
import { assert, strictAssert } from '../util/assert';
import { dropNull, shallowDropNull } from '../util/dropNull';
import { normalizeNumber } from '../util/normalizeNumber';
import { SignalService as Proto } from '../protobuf';
import { deriveGroupFields } from '../groups';
import * as Bytes from '../Bytes';
import { deriveMasterKeyFromGroupV1, typedArrayToArrayBuffer } from '../Crypto';
import {
ProcessedAttachment,
ProcessedDataMessage,
ProcessedGroupContext,
ProcessedGroupV2Context,
ProcessedQuote,
ProcessedContact,
ProcessedPreview,
ProcessedSticker,
ProcessedReaction,
ProcessedDelete,
} from './Types.d';
// TODO: remove once we move away from ArrayBuffers
const FIXMEU8 = Uint8Array;
const FLAGS = Proto.DataMessage.Flags;
export const ATTACHMENT_MAX = 32;
export function processAttachment(
attachment: Proto.IAttachmentPointer
): ProcessedAttachment;
export function processAttachment(
attachment?: Proto.IAttachmentPointer | null
): ProcessedAttachment | undefined;
export function processAttachment(
attachment?: Proto.IAttachmentPointer | null
): ProcessedAttachment | undefined {
if (!attachment) {
return undefined;
}
return {
...shallowDropNull(attachment),
cdnId: attachment.cdnId ? attachment.cdnId.toString() : undefined,
key: attachment.key ? Bytes.toBase64(attachment.key) : undefined,
digest: attachment.digest ? Bytes.toBase64(attachment.digest) : undefined,
};
}
async function processGroupContext(
group?: Proto.IGroupContext | null
): Promise<ProcessedGroupContext | undefined> {
if (!group) {
return undefined;
}
strictAssert(group.id, 'group context without id');
strictAssert(
group.type !== undefined && group.type !== null,
'group context without type'
);
const masterKey = await deriveMasterKeyFromGroupV1(
typedArrayToArrayBuffer(group.id)
);
const data = deriveGroupFields(new FIXMEU8(masterKey));
const derivedGroupV2Id = Bytes.toBase64(data.id);
const result: ProcessedGroupContext = {
id: Bytes.toBinary(group.id),
type: group.type,
name: dropNull(group.name),
membersE164: group.membersE164 ?? [],
avatar: processAttachment(group.avatar),
derivedGroupV2Id,
};
if (result.type === Proto.GroupContext.Type.DELIVER) {
result.name = undefined;
result.membersE164 = [];
result.avatar = undefined;
}
return result;
}
export function processGroupV2Context(
groupV2?: Proto.IGroupContextV2 | null
): ProcessedGroupV2Context | undefined {
if (!groupV2) {
return undefined;
}
strictAssert(groupV2.masterKey, 'groupV2 context without masterKey');
const data = deriveGroupFields(groupV2.masterKey);
return {
masterKey: Bytes.toBase64(groupV2.masterKey),
revision: dropNull(groupV2.revision),
groupChange: groupV2.groupChange
? Bytes.toBase64(groupV2.groupChange)
: undefined,
id: Bytes.toBase64(data.id),
secretParams: Bytes.toBase64(data.secretParams),
publicParams: Bytes.toBase64(data.publicParams),
};
}
export function processQuote(
quote?: Proto.DataMessage.IQuote | null
): ProcessedQuote | undefined {
if (!quote) {
return undefined;
}
return {
id: normalizeNumber(dropNull(quote.id)),
authorUuid: dropNull(quote.authorUuid),
text: dropNull(quote.text),
attachments: (quote.attachments ?? []).map(attachment => {
return {
contentType: dropNull(attachment.contentType),
fileName: dropNull(attachment.fileName),
thumbnail: processAttachment(attachment.thumbnail),
};
}),
bodyRanges: quote.bodyRanges ?? [],
};
}
export function processContact(
contact?: ReadonlyArray<Proto.DataMessage.IContact> | null
): ReadonlyArray<ProcessedContact> | undefined {
if (!contact) {
return undefined;
}
return contact.map(item => {
return {
...item,
avatar: item.avatar
? {
avatar: processAttachment(item.avatar.avatar),
isProfile: Boolean(item.avatar.isProfile),
}
: undefined,
};
});
}
function isLinkPreviewDateValid(value: unknown): value is number {
return (
typeof value === 'number' &&
!Number.isNaN(value) &&
Number.isFinite(value) &&
value > 0
);
}
function cleanLinkPreviewDate(
value?: Long | number | null
): number | undefined {
const result = normalizeNumber(value ?? undefined);
return isLinkPreviewDateValid(result) ? result : undefined;
}
export function processPreview(
preview?: ReadonlyArray<Proto.DataMessage.IPreview> | null
): ReadonlyArray<ProcessedPreview> | undefined {
if (!preview) {
return undefined;
}
return preview.map(item => {
return {
url: dropNull(item.url),
title: dropNull(item.title),
image: item.image ? processAttachment(item.image) : undefined,
description: dropNull(item.description),
date: cleanLinkPreviewDate(item.date),
};
});
}
export function processSticker(
sticker?: Proto.DataMessage.ISticker | null
): ProcessedSticker | undefined {
if (!sticker) {
return undefined;
}
return {
packId: sticker.packId ? Bytes.toHex(sticker.packId) : undefined,
packKey: sticker.packKey ? Bytes.toBase64(sticker.packKey) : undefined,
stickerId: normalizeNumber(dropNull(sticker.stickerId)),
data: processAttachment(sticker.data),
};
}
export function processReaction(
reaction?: Proto.DataMessage.IReaction | null
): ProcessedReaction | undefined {
if (!reaction) {
return undefined;
}
return {
emoji: dropNull(reaction.emoji),
remove: Boolean(reaction.remove),
targetAuthorUuid: dropNull(reaction.targetAuthorUuid),
targetTimestamp: normalizeNumber(dropNull(reaction.targetTimestamp)),
};
}
export function processDelete(
del?: Proto.DataMessage.IDelete | null
): ProcessedDelete | undefined {
if (!del) {
return undefined;
}
return {
targetSentTimestamp: normalizeNumber(dropNull(del.targetSentTimestamp)),
};
}
export async function processDataMessage(
message: Proto.IDataMessage,
envelopeTimestamp: number
): Promise<ProcessedDataMessage> {
/* eslint-disable no-bitwise */
// Now that its decrypted, validate the message and clean it up for consumer
// processing
// Note that messages may (generally) only perform one action and we ignore remaining
// fields after the first action.
if (!message.timestamp) {
throw new Error('Missing timestamp on dataMessage');
}
const timestamp = normalizeNumber(message.timestamp);
if (envelopeTimestamp !== timestamp) {
throw new Error(
`Timestamp ${timestamp} in DataMessage did not ` +
`match envelope timestamp ${envelopeTimestamp}`
);
}
const result: ProcessedDataMessage = {
body: dropNull(message.body),
attachments: (
message.attachments ?? []
).map((attachment: Proto.IAttachmentPointer) =>
processAttachment(attachment)
),
group: await processGroupContext(message.group),
groupV2: processGroupV2Context(message.groupV2),
flags: message.flags ?? 0,
expireTimer: message.expireTimer ?? 0,
profileKey: message.profileKey
? Bytes.toBase64(message.profileKey)
: undefined,
timestamp,
quote: processQuote(message.quote),
contact: processContact(message.contact),
preview: processPreview(message.preview),
sticker: processSticker(message.sticker),
requiredProtocolVersion: normalizeNumber(
dropNull(message.requiredProtocolVersion)
),
isViewOnce: Boolean(message.isViewOnce),
reaction: processReaction(message.reaction),
delete: processDelete(message.delete),
bodyRanges: message.bodyRanges ?? [],
groupCallUpdate: dropNull(message.groupCallUpdate),
};
const isEndSession = Boolean(result.flags & FLAGS.END_SESSION);
const isExpirationTimerUpdate = Boolean(
result.flags & FLAGS.EXPIRATION_TIMER_UPDATE
);
const isProfileKeyUpdate = Boolean(result.flags & FLAGS.PROFILE_KEY_UPDATE);
// The following assertion codifies an assumption: 0 or 1 flags are set, but never
// more. This assumption is fine as of this writing, but may not always be.
const flagCount = [
isEndSession,
isExpirationTimerUpdate,
isProfileKeyUpdate,
].filter(Boolean).length;
assert(
flagCount <= 1,
`Expected exactly <=1 flags to be set, but got ${flagCount}`
);
if (isEndSession) {
result.body = undefined;
result.attachments = [];
result.group = undefined;
return result;
}
if (isExpirationTimerUpdate) {
result.body = undefined;
result.attachments = [];
} else if (isProfileKeyUpdate) {
result.body = undefined;
result.attachments = [];
} else if (result.flags !== 0) {
throw new Error(`Unknown flags in message: ${result.flags}`);
}
if (result.group) {
switch (result.group.type) {
case Proto.GroupContext.Type.UPDATE:
result.body = undefined;
result.attachments = [];
break;
case Proto.GroupContext.Type.QUIT:
result.body = undefined;
result.attachments = [];
break;
case Proto.GroupContext.Type.DELIVER:
// Cleaned up in `processGroupContext`
break;
default: {
const err = new Error(
`Unknown group message type: ${result.group.type}`
);
err.warn = true;
throw err;
}
}
}
const attachmentCount = result.attachments.length;
if (attachmentCount > ATTACHMENT_MAX) {
throw new Error(
`Too many attachments: ${attachmentCount} included in one message, ` +
`max is ${ATTACHMENT_MAX}`
);
}
return result;
}

View file

@ -0,0 +1,60 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { SignalService as Proto } from '../protobuf';
import { normalizeUuid } from '../util/normalizeUuid';
import {
ProcessedUnidentifiedDeliveryStatus,
ProcessedSent,
ProcessedSyncMessage,
} from './Types.d';
import UnidentifiedDeliveryStatus = Proto.SyncMessage.Sent.IUnidentifiedDeliveryStatus;
function processUnidentifiedDeliveryStatus(
status: UnidentifiedDeliveryStatus
): ProcessedUnidentifiedDeliveryStatus {
const { destinationUuid } = status;
return {
...status,
destinationUuid: destinationUuid
? normalizeUuid(
destinationUuid,
'syncMessage.sent.unidentifiedStatus.destinationUuid'
)
: undefined,
};
}
function processSent(
sent?: Proto.SyncMessage.ISent | null
): ProcessedSent | undefined {
if (!sent) {
return undefined;
}
const { destinationUuid, unidentifiedStatus } = sent;
return {
...sent,
destinationUuid: destinationUuid
? normalizeUuid(destinationUuid, 'syncMessage.sent.destinationUuid')
: undefined,
unidentifiedStatus: unidentifiedStatus
? unidentifiedStatus.map(processUnidentifiedDeliveryStatus)
: undefined,
};
}
export function processSyncMessage(
syncMessage: Proto.ISyncMessage
): ProcessedSyncMessage {
return {
...syncMessage,
sent: processSent(syncMessage.sent),
};
}