Use --force-long with pbjs

This commit is contained in:
Fedor Indutny 2022-03-23 13:49:27 -07:00 committed by GitHub
parent bb066d4a84
commit 2eaacac151
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 99 additions and 131 deletions

View File

@ -23,7 +23,7 @@
"get-expire-time": "node ts/scripts/get-expire-time.js", "get-expire-time": "node ts/scripts/get-expire-time.js",
"copy-and-concat": "node ts/scripts/copy-and-concat.js", "copy-and-concat": "node ts/scripts/copy-and-concat.js",
"sass": "sass stylesheets/manifest.scss:stylesheets/manifest.css stylesheets/manifest_bridge.scss:stylesheets/manifest_bridge.css", "sass": "sass stylesheets/manifest.scss:stylesheets/manifest.css stylesheets/manifest_bridge.scss:stylesheets/manifest_bridge.css",
"build-module-protobuf": "pbjs --target static-module --no-verify --no-create --wrap commonjs --out ts/protobuf/compiled.js protos/*.proto && pbts --out ts/protobuf/compiled.d.ts ts/protobuf/compiled.js", "build-module-protobuf": "pbjs --target static-module --force-long --no-verify --no-create --wrap commonjs --out ts/protobuf/compiled.js protos/*.proto && pbts --out ts/protobuf/compiled.d.ts ts/protobuf/compiled.js",
"clean-module-protobuf": "rm -f ts/protobuf/compiled.d.ts ts/protobuf/compiled.js", "clean-module-protobuf": "rm -f ts/protobuf/compiled.d.ts ts/protobuf/compiled.js",
"build-protobuf": "yarn build-module-protobuf", "build-protobuf": "yarn build-module-protobuf",
"clean-protobuf": "yarn clean-module-protobuf", "clean-protobuf": "yarn clean-module-protobuf",

View File

@ -142,7 +142,7 @@ export function decryptDeviceName(
export function deriveStorageManifestKey( export function deriveStorageManifestKey(
storageServiceKey: Uint8Array, storageServiceKey: Uint8Array,
version: number version: Long = Long.fromNumber(0)
): Uint8Array { ): Uint8Array {
return hmacSha256(storageServiceKey, Bytes.fromString(`Manifest_${version}`)); return hmacSha256(storageServiceKey, Bytes.fromString(`Manifest_${version}`));
} }

View File

@ -10,6 +10,7 @@ import {
last, last,
values, values,
} from 'lodash'; } from 'lodash';
import Long from 'long';
import type { ClientZkGroupCipher } from '@signalapp/signal-client/zkgroup'; import type { ClientZkGroupCipher } from '@signalapp/signal-client/zkgroup';
import { v4 as getGuid } from 'uuid'; import { v4 as getGuid } from 'uuid';
import LRU from 'lru-cache'; import LRU from 'lru-cache';
@ -614,7 +615,7 @@ function buildGroupProto(
member.role = item.role || MEMBER_ROLE_ENUM.DEFAULT; member.role = item.role || MEMBER_ROLE_ENUM.DEFAULT;
pendingMember.member = member; pendingMember.member = member;
pendingMember.timestamp = item.timestamp; pendingMember.timestamp = Long.fromNumber(item.timestamp);
pendingMember.addedByUserId = ourUuidCipherTextBuffer; pendingMember.addedByUserId = ourUuidCipherTextBuffer;
return pendingMember; return pendingMember;
@ -717,7 +718,7 @@ export async function buildAddMembersChange(
const memberPendingProfileKey = new Proto.MemberPendingProfileKey(); const memberPendingProfileKey = new Proto.MemberPendingProfileKey();
memberPendingProfileKey.member = member; memberPendingProfileKey.member = member;
memberPendingProfileKey.addedByUserId = ourUuidCipherTextBuffer; memberPendingProfileKey.addedByUserId = ourUuidCipherTextBuffer;
memberPendingProfileKey.timestamp = now; memberPendingProfileKey.timestamp = Long.fromNumber(now);
const addPendingMemberAction = const addPendingMemberAction =
new Proto.GroupChange.Actions.AddMemberPendingProfileKeyAction(); new Proto.GroupChange.Actions.AddMemberPendingProfileKeyAction();
@ -5112,17 +5113,11 @@ function isValidProfileKey(buffer?: Uint8Array): boolean {
return Boolean(buffer && buffer.length === 32); return Boolean(buffer && buffer.length === 32);
} }
function normalizeTimestamp( function normalizeTimestamp(timestamp: Long | null | undefined): number {
timestamp: number | Long | null | undefined
): number {
if (!timestamp) { if (!timestamp) {
return 0; return 0;
} }
if (typeof timestamp === 'number') {
return timestamp;
}
const asNumber = timestamp.toNumber(); const asNumber = timestamp.toNumber();
const now = Date.now(); const now = Date.now();

View File

@ -9,7 +9,6 @@ import {
import * as Bytes from '../Bytes'; import * as Bytes from '../Bytes';
import { assert } from '../util/assert'; import { assert } from '../util/assert';
import { missingCaseError } from '../util/missingCaseError'; import { missingCaseError } from '../util/missingCaseError';
import { normalizeNumber } from '../util/normalizeNumber';
import { waitForOnline } from '../util/waitForOnline'; import { waitForOnline } from '../util/waitForOnline';
import * as log from '../logging/log'; import * as log from '../logging/log';
import type { StorageInterface } from '../types/Storage.d'; import type { StorageInterface } from '../types/Storage.d';
@ -181,7 +180,7 @@ export class SenderCertificateService {
const decodedCert = decodedContainer.certificate const decodedCert = decodedContainer.certificate
? SenderCertificate.Certificate.decode(decodedContainer.certificate) ? SenderCertificate.Certificate.decode(decodedContainer.certificate)
: undefined; : undefined;
const expires = normalizeNumber(decodedCert?.expires); const expires = decodedCert?.expires?.toNumber();
if (!isExpirationValid(expires)) { if (!isExpirationValid(expires)) {
log.warn( log.warn(

View File

@ -3,6 +3,7 @@
import { debounce, isNumber } from 'lodash'; import { debounce, isNumber } from 'lodash';
import pMap from 'p-map'; import pMap from 'p-map';
import Long from 'long';
import dataInterface from '../sql/Client'; import dataInterface from '../sql/Client';
import * as Bytes from '../Bytes'; import * as Bytes from '../Bytes';
@ -27,12 +28,12 @@ import {
import type { MergeResultType } from './storageRecordOps'; import type { MergeResultType } from './storageRecordOps';
import type { ConversationModel } from '../models/conversations'; import type { ConversationModel } from '../models/conversations';
import { strictAssert } from '../util/assert'; import { strictAssert } from '../util/assert';
import { dropNull } from '../util/dropNull';
import * as durations from '../util/durations'; import * as durations from '../util/durations';
import { BackOff } from '../util/BackOff'; import { BackOff } from '../util/BackOff';
import { storageJobQueue } from '../util/JobQueue'; import { storageJobQueue } from '../util/JobQueue';
import { sleep } from '../util/sleep'; import { sleep } from '../util/sleep';
import { isMoreRecentThan } from '../util/timestamp'; import { isMoreRecentThan } from '../util/timestamp';
import { normalizeNumber } from '../util/normalizeNumber';
import { ourProfileKeyService } from './ourProfileKey'; import { ourProfileKeyService } from './ourProfileKey';
import { import {
ConversationTypes, ConversationTypes,
@ -495,7 +496,7 @@ async function generateManifest(
} }
const manifestRecord = new Proto.ManifestRecord(); const manifestRecord = new Proto.ManifestRecord();
manifestRecord.version = version; manifestRecord.version = Long.fromNumber(version);
manifestRecord.keys = Array.from(manifestRecordKeys); manifestRecord.keys = Array.from(manifestRecordKeys);
const storageKeyBase64 = window.storage.get('storageKey'); const storageKeyBase64 = window.storage.get('storageKey');
@ -503,14 +504,17 @@ async function generateManifest(
throw new Error('No storage key'); throw new Error('No storage key');
} }
const storageKey = Bytes.fromBase64(storageKeyBase64); const storageKey = Bytes.fromBase64(storageKeyBase64);
const storageManifestKey = deriveStorageManifestKey(storageKey, version); const storageManifestKey = deriveStorageManifestKey(
storageKey,
Long.fromNumber(version)
);
const encryptedManifest = encryptProfile( const encryptedManifest = encryptProfile(
Proto.ManifestRecord.encode(manifestRecord).finish(), Proto.ManifestRecord.encode(manifestRecord).finish(),
storageManifestKey storageManifestKey
); );
const storageManifest = new Proto.StorageManifest(); const storageManifest = new Proto.StorageManifest();
storageManifest.version = version; storageManifest.version = manifestRecord.version;
storageManifest.value = encryptedManifest; storageManifest.value = encryptedManifest;
return { return {
@ -676,7 +680,7 @@ async function decryptManifest(
const storageKey = Bytes.fromBase64(storageKeyBase64); const storageKey = Bytes.fromBase64(storageKeyBase64);
const storageManifestKey = deriveStorageManifestKey( const storageManifestKey = deriveStorageManifestKey(
storageKey, storageKey,
normalizeNumber(version ?? 0) dropNull(version)
); );
strictAssert(value, 'StorageManifest has no value field'); strictAssert(value, 'StorageManifest has no value field');
@ -1317,7 +1321,7 @@ async function sync(
manifest.version !== undefined && manifest.version !== null, manifest.version !== undefined && manifest.version !== null,
'Manifest without version' 'Manifest without version'
); );
const version = normalizeNumber(manifest.version); const version = manifest.version?.toNumber() ?? 0;
log.info( log.info(
`storageService.sync: updating to remoteVersion=${version} from ` + `storageService.sync: updating to remoteVersion=${version} from ` +

View File

@ -16,7 +16,7 @@ const FLAGS = Proto.DataMessage.Flags;
const TIMESTAMP = Date.now(); const TIMESTAMP = Date.now();
const UNPROCESSED_ATTACHMENT: Proto.IAttachmentPointer = { const UNPROCESSED_ATTACHMENT: Proto.IAttachmentPointer = {
cdnId: 123, cdnId: Long.fromNumber(123),
key: new Uint8Array([1, 2, 3]), key: new Uint8Array([1, 2, 3]),
digest: new Uint8Array([4, 5, 6]), digest: new Uint8Array([4, 5, 6]),
}; };
@ -35,7 +35,7 @@ describe('processDataMessage', () => {
const check = (message: Proto.IDataMessage) => const check = (message: Proto.IDataMessage) =>
processDataMessage( processDataMessage(
{ {
timestamp: TIMESTAMP, timestamp: Long.fromNumber(TIMESTAMP),
...message, ...message,
}, },
TIMESTAMP TIMESTAMP
@ -175,7 +175,7 @@ describe('processDataMessage', () => {
it('should process quote', async () => { it('should process quote', async () => {
const out = await check({ const out = await check({
quote: { quote: {
id: 1, id: Long.fromNumber(1),
authorUuid: 'author', authorUuid: 'author',
text: 'text', text: 'text',
attachments: [ attachments: [
@ -236,7 +236,7 @@ describe('processDataMessage', () => {
await check({ await check({
reaction: { reaction: {
emoji: '😎', emoji: '😎',
targetTimestamp: TIMESTAMP, targetTimestamp: Long.fromNumber(TIMESTAMP),
}, },
}) })
).reaction, ).reaction,
@ -254,7 +254,7 @@ describe('processDataMessage', () => {
reaction: { reaction: {
emoji: '😎', emoji: '😎',
remove: true, remove: true,
targetTimestamp: TIMESTAMP, targetTimestamp: Long.fromNumber(TIMESTAMP),
}, },
}) })
).reaction, ).reaction,
@ -271,7 +271,7 @@ describe('processDataMessage', () => {
const out = await check({ const out = await check({
preview: [ preview: [
{ {
date: TIMESTAMP, date: Long.fromNumber(TIMESTAMP),
image: UNPROCESSED_ATTACHMENT, image: UNPROCESSED_ATTACHMENT,
}, },
], ],

View File

@ -1,21 +0,0 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { assert } from 'chai';
import Long from 'long';
import { normalizeNumber } from '../../util/normalizeNumber';
describe('normalizeNumber', () => {
it('returns undefined when input is undefined', () => {
assert.isUndefined(normalizeNumber(undefined));
});
it('returns number when input is number', () => {
assert.strictEqual(normalizeNumber(123), 123);
});
it('returns number when input is Long', () => {
assert.strictEqual(normalizeNumber(new Long(123)), 123);
});
});

View File

@ -5,6 +5,7 @@
import { assert } from 'chai'; import { assert } from 'chai';
import { v4 as getGuid } from 'uuid'; import { v4 as getGuid } from 'uuid';
import Long from 'long';
import MessageReceiver from '../textsecure/MessageReceiver'; import MessageReceiver from '../textsecure/MessageReceiver';
import { IncomingWebSocketRequest } from '../textsecure/WebsocketResources'; import { IncomingWebSocketRequest } from '../textsecure/WebsocketResources';
@ -48,14 +49,14 @@ describe('MessageReceiver', () => {
source: number, source: number,
sourceUuid: uuid, sourceUuid: uuid,
sourceDevice: deviceId, sourceDevice: deviceId,
timestamp: Date.now(), timestamp: Long.fromNumber(Date.now()),
content: Crypto.getRandomBytes(200), content: Crypto.getRandomBytes(200),
}).finish(); }).finish();
messageReceiver.handleRequest( messageReceiver.handleRequest(
new IncomingWebSocketRequest( new IncomingWebSocketRequest(
{ {
id: 1, id: Long.fromNumber(1),
verb: 'PUT', verb: 'PUT',
path: '/api/v1/message', path: '/api/v1/message',
body, body,

View File

@ -93,7 +93,7 @@ describe('WebSocket-Resource', () => {
it('sends requests and receives responses', async () => { it('sends requests and receives responses', async () => {
// mock socket and request handler // mock socket and request handler
let requestId: number | Long | undefined; let requestId: Long | undefined;
const socket = new FakeSocket(); const socket = new FakeSocket();
sinon.stub(socket, 'sendBytes').callsFake((data: Uint8Array) => { sinon.stub(socket, 'sendBytes').callsFake((data: Uint8Array) => {

View File

@ -43,7 +43,6 @@ import type { BatcherType } from '../util/batcher';
import { createBatcher } from '../util/batcher'; import { createBatcher } from '../util/batcher';
import { dropNull } from '../util/dropNull'; import { dropNull } from '../util/dropNull';
import { normalizeUuid } from '../util/normalizeUuid'; import { normalizeUuid } from '../util/normalizeUuid';
import { normalizeNumber } from '../util/normalizeNumber';
import { parseIntOrThrow } from '../util/parseIntOrThrow'; import { parseIntOrThrow } from '../util/parseIntOrThrow';
import { clearTimeoutIfNecessary } from '../util/clearTimeoutIfNecessary'; import { clearTimeoutIfNecessary } from '../util/clearTimeoutIfNecessary';
import { Zone } from '../util/Zone'; import { Zone } from '../util/Zone';
@ -279,7 +278,7 @@ export default class MessageReceiver
try { try {
const decoded = Proto.Envelope.decode(plaintext); const decoded = Proto.Envelope.decode(plaintext);
const serverTimestamp = normalizeNumber(decoded.serverTimestamp); const serverTimestamp = decoded.serverTimestamp?.toNumber();
const ourUuid = this.storage.user.getCheckedUuid(); const ourUuid = this.storage.user.getCheckedUuid();
@ -310,7 +309,7 @@ export default class MessageReceiver
) )
) )
: ourUuid, : ourUuid,
timestamp: normalizeNumber(decoded.timestamp), timestamp: decoded.timestamp?.toNumber(),
legacyMessage: dropNull(decoded.legacyMessage), legacyMessage: dropNull(decoded.legacyMessage),
content: dropNull(decoded.content), content: dropNull(decoded.content),
serverGuid: decoded.serverGuid, serverGuid: decoded.serverGuid,
@ -690,13 +689,12 @@ export default class MessageReceiver
destinationUuid: new UUID( destinationUuid: new UUID(
decoded.destinationUuid || item.destinationUuid || ourUuid.toString() decoded.destinationUuid || item.destinationUuid || ourUuid.toString()
), ),
timestamp: normalizeNumber(decoded.timestamp), timestamp: decoded.timestamp?.toNumber(),
legacyMessage: dropNull(decoded.legacyMessage), legacyMessage: dropNull(decoded.legacyMessage),
content: dropNull(decoded.content), content: dropNull(decoded.content),
serverGuid: decoded.serverGuid, serverGuid: decoded.serverGuid,
serverTimestamp: normalizeNumber( serverTimestamp:
item.serverTimestamp || decoded.serverTimestamp item.serverTimestamp || decoded.serverTimestamp?.toNumber(),
),
}; };
const { decrypted } = item; const { decrypted } = item;
@ -1773,7 +1771,7 @@ export default class MessageReceiver
{ {
destination: dropNull(destination), destination: dropNull(destination),
destinationUuid: dropNull(destinationUuid), destinationUuid: dropNull(destinationUuid),
timestamp: timestamp ? normalizeNumber(timestamp) : undefined, timestamp: timestamp?.toNumber(),
serverTimestamp: envelope.serverTimestamp, serverTimestamp: envelope.serverTimestamp,
device: envelope.sourceDevice, device: envelope.sourceDevice,
unidentifiedStatus, unidentifiedStatus,
@ -1781,9 +1779,7 @@ export default class MessageReceiver
isRecipientUpdate: Boolean(isRecipientUpdate), isRecipientUpdate: Boolean(isRecipientUpdate),
receivedAtCounter: envelope.receivedAtCounter, receivedAtCounter: envelope.receivedAtCounter,
receivedAtDate: envelope.receivedAtDate, receivedAtDate: envelope.receivedAtDate,
expirationStartTimestamp: expirationStartTimestamp expirationStartTimestamp: expirationStartTimestamp?.toNumber(),
? normalizeNumber(expirationStartTimestamp)
: undefined,
}, },
this.removeFromCache.bind(this, envelope) this.removeFromCache.bind(this, envelope)
); );
@ -2200,7 +2196,7 @@ export default class MessageReceiver
receiptMessage.timestamp.map(async rawTimestamp => { receiptMessage.timestamp.map(async rawTimestamp => {
const ev = new EventClass( const ev = new EventClass(
{ {
timestamp: normalizeNumber(rawTimestamp), timestamp: rawTimestamp?.toNumber(),
envelopeTimestamp: envelope.timestamp, envelopeTimestamp: envelope.timestamp,
source: envelope.source, source: envelope.source,
sourceUuid: envelope.sourceUuid, sourceUuid: envelope.sourceUuid,
@ -2221,7 +2217,7 @@ export default class MessageReceiver
if (envelope.timestamp && typingMessage.timestamp) { if (envelope.timestamp && typingMessage.timestamp) {
const envelopeTimestamp = envelope.timestamp; const envelopeTimestamp = envelope.timestamp;
const typingTimestamp = normalizeNumber(typingMessage.timestamp); const typingTimestamp = typingMessage.timestamp?.toNumber();
if (typingTimestamp !== envelopeTimestamp) { if (typingTimestamp !== envelopeTimestamp) {
log.warn( log.warn(
@ -2258,7 +2254,7 @@ export default class MessageReceiver
senderDevice: envelope.sourceDevice, senderDevice: envelope.sourceDevice,
typing: { typing: {
typingMessage, typingMessage,
timestamp: timestamp ? normalizeNumber(timestamp) : Date.now(), timestamp: timestamp?.toNumber() ?? Date.now(),
started: action === Proto.TypingMessage.Action.STARTED, started: action === Proto.TypingMessage.Action.STARTED,
stopped: action === Proto.TypingMessage.Action.STOPPED, stopped: action === Proto.TypingMessage.Action.STOPPED,
@ -2421,7 +2417,7 @@ export default class MessageReceiver
log.info( log.info(
'sent message to', 'sent message to',
this.getDestination(sentMessage), this.getDestination(sentMessage),
normalizeNumber(sentMessage.timestamp), sentMessage.timestamp?.toNumber(),
'from', 'from',
this.getEnvelopeId(envelope) this.getEnvelopeId(envelope)
); );
@ -2511,7 +2507,7 @@ export default class MessageReceiver
sourceUuid: sync.senderUuid sourceUuid: sync.senderUuid
? normalizeUuid(sync.senderUuid, 'handleViewOnceOpen.senderUuid') ? normalizeUuid(sync.senderUuid, 'handleViewOnceOpen.senderUuid')
: undefined, : undefined,
timestamp: sync.timestamp ? normalizeNumber(sync.timestamp) : undefined, timestamp: sync.timestamp?.toNumber(),
}, },
this.removeFromCache.bind(this, envelope) this.removeFromCache.bind(this, envelope)
); );
@ -2646,7 +2642,7 @@ export default class MessageReceiver
const ev = new ReadSyncEvent( const ev = new ReadSyncEvent(
{ {
envelopeTimestamp: envelope.timestamp, envelopeTimestamp: envelope.timestamp,
timestamp: normalizeNumber(dropNull(timestamp)), timestamp: timestamp?.toNumber(),
sender: dropNull(sender), sender: dropNull(sender),
senderUuid: senderUuid senderUuid: senderUuid
? normalizeUuid(senderUuid, 'handleRead.senderUuid') ? normalizeUuid(senderUuid, 'handleRead.senderUuid')
@ -2669,7 +2665,7 @@ export default class MessageReceiver
const ev = new ViewSyncEvent( const ev = new ViewSyncEvent(
{ {
envelopeTimestamp: envelope.timestamp, envelopeTimestamp: envelope.timestamp,
timestamp: normalizeNumber(dropNull(timestamp)), timestamp: timestamp?.toNumber(),
senderE164: dropNull(senderE164), senderE164: dropNull(senderE164),
senderUuid: senderUuid senderUuid: senderUuid
? normalizeUuid(senderUuid, 'handleViewed.senderUuid') ? normalizeUuid(senderUuid, 'handleViewed.senderUuid')

View File

@ -342,7 +342,7 @@ class Message {
} }
const proto = new Proto.DataMessage(); const proto = new Proto.DataMessage();
proto.timestamp = this.timestamp; proto.timestamp = Long.fromNumber(this.timestamp);
proto.attachments = this.attachmentPointers; proto.attachments = this.attachmentPointers;
if (this.body) { if (this.body) {
@ -384,7 +384,10 @@ class Message {
proto.reaction.emoji = this.reaction.emoji || null; proto.reaction.emoji = this.reaction.emoji || null;
proto.reaction.remove = this.reaction.remove || false; proto.reaction.remove = this.reaction.remove || false;
proto.reaction.targetAuthorUuid = this.reaction.targetAuthorUuid || null; proto.reaction.targetAuthorUuid = this.reaction.targetAuthorUuid || null;
proto.reaction.targetTimestamp = this.reaction.targetTimestamp || null; proto.reaction.targetTimestamp =
this.reaction.targetTimestamp === undefined
? null
: Long.fromNumber(this.reaction.targetTimestamp);
} }
if (Array.isArray(this.preview)) { if (Array.isArray(this.preview)) {
@ -407,7 +410,8 @@ class Message {
proto.quote = new Quote(); proto.quote = new Quote();
const { quote } = proto; const { quote } = proto;
quote.id = this.quote.id || null; quote.id =
this.quote.id === undefined ? null : Long.fromNumber(this.quote.id);
quote.authorUuid = this.quote.authorUuid || null; quote.authorUuid = this.quote.authorUuid || null;
quote.text = this.quote.text || null; quote.text = this.quote.text || null;
quote.attachments = (this.quote.attachments || []).map( quote.attachments = (this.quote.attachments || []).map(
@ -453,7 +457,7 @@ class Message {
} }
if (this.deletedForEveryoneTimestamp) { if (this.deletedForEveryoneTimestamp) {
proto.delete = { proto.delete = {
targetSentTimestamp: this.deletedForEveryoneTimestamp, targetSentTimestamp: Long.fromNumber(this.deletedForEveryoneTimestamp),
}; };
} }
if (this.mentions) { if (this.mentions) {
@ -484,7 +488,7 @@ class Message {
if (this.storyContext.authorUuid) { if (this.storyContext.authorUuid) {
storyContext.authorUuid = this.storyContext.authorUuid; storyContext.authorUuid = this.storyContext.authorUuid;
} }
storyContext.sentTimestamp = this.storyContext.timestamp; storyContext.sentTimestamp = Long.fromNumber(this.storyContext.timestamp);
proto.storyContext = storyContext; proto.storyContext = storyContext;
} }
@ -772,7 +776,7 @@ export default class MessageSender {
typingMessage.groupId = groupId; typingMessage.groupId = groupId;
} }
typingMessage.action = action; typingMessage.action = action;
typingMessage.timestamp = finalTimestamp; typingMessage.timestamp = Long.fromNumber(finalTimestamp);
const contentMessage = new Proto.Content(); const contentMessage = new Proto.Content();
contentMessage.typingMessage = typingMessage; contentMessage.typingMessage = typingMessage;
@ -1111,7 +1115,7 @@ export default class MessageSender {
const dataMessage = Proto.DataMessage.decode(encodedDataMessage); const dataMessage = Proto.DataMessage.decode(encodedDataMessage);
const sentMessage = new Proto.SyncMessage.Sent(); const sentMessage = new Proto.SyncMessage.Sent();
sentMessage.timestamp = timestamp; sentMessage.timestamp = Long.fromNumber(timestamp);
sentMessage.message = dataMessage; sentMessage.message = dataMessage;
if (destination) { if (destination) {
sentMessage.destination = destination; sentMessage.destination = destination;
@ -1120,7 +1124,9 @@ export default class MessageSender {
sentMessage.destinationUuid = destinationUuid; sentMessage.destinationUuid = destinationUuid;
} }
if (expirationStartTimestamp) { if (expirationStartTimestamp) {
sentMessage.expirationStartTimestamp = expirationStartTimestamp; sentMessage.expirationStartTimestamp = Long.fromNumber(
expirationStartTimestamp
);
} }
if (isUpdate) { if (isUpdate) {
@ -1345,7 +1351,10 @@ export default class MessageSender {
const syncMessage = this.createSyncMessage(); const syncMessage = this.createSyncMessage();
syncMessage.read = []; syncMessage.read = [];
for (let i = 0; i < reads.length; i += 1) { for (let i = 0; i < reads.length; i += 1) {
const proto = new Proto.SyncMessage.Read(reads[i]); const proto = new Proto.SyncMessage.Read({
...reads[i],
timestamp: Long.fromNumber(reads[i].timestamp),
});
syncMessage.read.push(proto); syncMessage.read.push(proto);
} }
@ -1374,7 +1383,13 @@ export default class MessageSender {
const myUuid = window.textsecure.storage.user.getCheckedUuid(); const myUuid = window.textsecure.storage.user.getCheckedUuid();
const syncMessage = this.createSyncMessage(); const syncMessage = this.createSyncMessage();
syncMessage.viewed = views.map(view => new Proto.SyncMessage.Viewed(view)); syncMessage.viewed = views.map(
view =>
new Proto.SyncMessage.Viewed({
...view,
timestamp: Long.fromNumber(view.timestamp),
})
);
const contentMessage = new Proto.Content(); const contentMessage = new Proto.Content();
contentMessage.syncMessage = syncMessage; contentMessage.syncMessage = syncMessage;
@ -1417,7 +1432,7 @@ export default class MessageSender {
viewOnceOpen.sender = senderE164; viewOnceOpen.sender = senderE164;
} }
viewOnceOpen.senderUuid = senderUuid; viewOnceOpen.senderUuid = senderUuid;
viewOnceOpen.timestamp = timestamp; viewOnceOpen.timestamp = Long.fromNumber(timestamp);
syncMessage.viewOnceOpen = viewOnceOpen; syncMessage.viewOnceOpen = viewOnceOpen;
const contentMessage = new Proto.Content(); const contentMessage = new Proto.Content();
@ -1647,7 +1662,9 @@ export default class MessageSender {
const receiptMessage = new Proto.ReceiptMessage(); const receiptMessage = new Proto.ReceiptMessage();
receiptMessage.type = type; receiptMessage.type = type;
receiptMessage.timestamp = timestamps; receiptMessage.timestamp = timestamps.map(timestamp =>
Long.fromNumber(timestamp)
);
const contentMessage = new Proto.Content(); const contentMessage = new Proto.Content();
contentMessage.receiptMessage = receiptMessage; contentMessage.receiptMessage = receiptMessage;

View File

@ -24,6 +24,7 @@
*/ */
import type { connection as WebSocket, IMessage } from 'websocket'; import type { connection as WebSocket, IMessage } from 'websocket';
import Long from 'long';
import type { EventHandler } from './EventTarget'; import type { EventHandler } from './EventTarget';
import EventTarget from './EventTarget'; import EventTarget from './EventTarget';
@ -32,7 +33,6 @@ import * as durations from '../util/durations';
import { dropNull } from '../util/dropNull'; import { dropNull } from '../util/dropNull';
import { isOlderThan } from '../util/timestamp'; import { isOlderThan } from '../util/timestamp';
import { strictAssert } from '../util/assert'; import { strictAssert } from '../util/assert';
import { normalizeNumber } from '../util/normalizeNumber';
import * as Errors from '../types/errors'; import * as Errors from '../types/errors';
import { SignalService as Proto } from '../protobuf'; import { SignalService as Proto } from '../protobuf';
import * as log from '../logging/log'; import * as log from '../logging/log';
@ -43,7 +43,7 @@ const THIRTY_SECONDS = 30 * durations.SECOND;
const MAX_MESSAGE_SIZE = 256 * 1024; const MAX_MESSAGE_SIZE = 256 * 1024;
export class IncomingWebSocketRequest { export class IncomingWebSocketRequest {
private readonly id: Long | number; private readonly id: Long;
public readonly verb: string; public readonly verb: string;
@ -105,18 +105,18 @@ export class CloseEvent extends Event {
} }
export default class WebSocketResource extends EventTarget { export default class WebSocketResource extends EventTarget {
private outgoingId = 1; private outgoingId = Long.fromNumber(1, true);
private closed = false; private closed = false;
private readonly outgoingMap = new Map< private readonly outgoingMap = new Map<
number, string,
(result: SendRequestResult) => void (result: SendRequestResult) => void
>(); >();
private readonly boundOnMessage: (message: IMessage) => void; private readonly boundOnMessage: (message: IMessage) => void;
private activeRequests = new Set<IncomingWebSocketRequest | number>(); private activeRequests = new Set<IncomingWebSocketRequest | string>();
private shuttingDown = false; private shuttingDown = false;
@ -176,10 +176,11 @@ export default class WebSocketResource extends EventTarget {
options: SendRequestOptions options: SendRequestOptions
): Promise<SendRequestResult> { ): Promise<SendRequestResult> {
const id = this.outgoingId; const id = this.outgoingId;
strictAssert(!this.outgoingMap.has(id), 'Duplicate outgoing request'); const idString = id.toString();
strictAssert(!this.outgoingMap.has(idString), 'Duplicate outgoing request');
// eslint-disable-next-line no-bitwise // Note that this automatically wraps
this.outgoingId = Math.max(1, (this.outgoingId + 1) & 0x7fffffff); this.outgoingId = this.outgoingId.add(1);
const bytes = Proto.WebSocketMessage.encode({ const bytes = Proto.WebSocketMessage.encode({
type: Proto.WebSocketMessage.Type.REQUEST, type: Proto.WebSocketMessage.Type.REQUEST,
@ -197,22 +198,22 @@ export default class WebSocketResource extends EventTarget {
); );
strictAssert(!this.shuttingDown, 'Cannot send request, shutting down'); strictAssert(!this.shuttingDown, 'Cannot send request, shutting down');
this.addActive(id); this.addActive(idString);
const promise = new Promise<SendRequestResult>((resolve, reject) => { const promise = new Promise<SendRequestResult>((resolve, reject) => {
let timer = options.timeout let timer = options.timeout
? Timers.setTimeout(() => { ? Timers.setTimeout(() => {
this.removeActive(id); this.removeActive(idString);
reject(new Error('Request timed out')); reject(new Error('Request timed out'));
}, options.timeout) }, options.timeout)
: undefined; : undefined;
this.outgoingMap.set(id, result => { this.outgoingMap.set(idString, result => {
if (timer !== undefined) { if (timer !== undefined) {
Timers.clearTimeout(timer); Timers.clearTimeout(timer);
timer = undefined; timer = undefined;
} }
this.removeActive(id); this.removeActive(idString);
resolve(result); resolve(result);
}); });
}); });
@ -321,12 +322,12 @@ export default class WebSocketResource extends EventTarget {
const { response } = message; const { response } = message;
strictAssert(response.id, 'response without id'); strictAssert(response.id, 'response without id');
const responseId = normalizeNumber(response.id); const responseIdString = response.id.toString();
const resolve = this.outgoingMap.get(responseId); const resolve = this.outgoingMap.get(responseIdString);
this.outgoingMap.delete(responseId); this.outgoingMap.delete(responseIdString);
if (!resolve) { if (!resolve) {
throw new Error(`Received response for unknown request ${responseId}`); throw new Error(`Received response for unknown request ${response.id}`);
} }
resolve({ resolve({
@ -352,11 +353,11 @@ export default class WebSocketResource extends EventTarget {
} }
} }
private addActive(request: IncomingWebSocketRequest | number): void { private addActive(request: IncomingWebSocketRequest | string): void {
this.activeRequests.add(request); this.activeRequests.add(request);
} }
private removeActive(request: IncomingWebSocketRequest | number): void { private removeActive(request: IncomingWebSocketRequest | string): void {
if (!this.activeRequests.has(request)) { if (!this.activeRequests.has(request)) {
log.warn('WebSocketResource: removing unknown request'); log.warn('WebSocketResource: removing unknown request');
return; return;

View File

@ -5,7 +5,6 @@ import Long from 'long';
import { assert, strictAssert } from '../util/assert'; import { assert, strictAssert } from '../util/assert';
import { dropNull, shallowDropNull } from '../util/dropNull'; import { dropNull, shallowDropNull } from '../util/dropNull';
import { normalizeNumber } from '../util/normalizeNumber';
import { SignalService as Proto } from '../protobuf'; import { SignalService as Proto } from '../protobuf';
import { deriveGroupFields } from '../groups'; import { deriveGroupFields } from '../groups';
import * as Bytes from '../Bytes'; import * as Bytes from '../Bytes';
@ -120,7 +119,7 @@ export function processQuote(
} }
return { return {
id: normalizeNumber(dropNull(quote.id)), id: quote.id?.toNumber(),
authorUuid: dropNull(quote.authorUuid), authorUuid: dropNull(quote.authorUuid),
text: dropNull(quote.text), text: dropNull(quote.text),
attachments: (quote.attachments ?? []).map(attachment => { attachments: (quote.attachments ?? []).map(attachment => {
@ -163,10 +162,8 @@ function isLinkPreviewDateValid(value: unknown): value is number {
); );
} }
function cleanLinkPreviewDate( function cleanLinkPreviewDate(value?: Long | null): number | undefined {
value?: Long | number | null const result = value?.toNumber();
): number | undefined {
const result = normalizeNumber(value ?? undefined);
return isLinkPreviewDateValid(result) ? result : undefined; return isLinkPreviewDateValid(result) ? result : undefined;
} }
@ -198,7 +195,7 @@ export function processSticker(
return { return {
packId: sticker.packId ? Bytes.toHex(sticker.packId) : undefined, packId: sticker.packId ? Bytes.toHex(sticker.packId) : undefined,
packKey: sticker.packKey ? Bytes.toBase64(sticker.packKey) : undefined, packKey: sticker.packKey ? Bytes.toBase64(sticker.packKey) : undefined,
stickerId: normalizeNumber(dropNull(sticker.stickerId)), stickerId: dropNull(sticker.stickerId),
data: processAttachment(sticker.data), data: processAttachment(sticker.data),
}; };
} }
@ -214,7 +211,7 @@ export function processReaction(
emoji: dropNull(reaction.emoji), emoji: dropNull(reaction.emoji),
remove: Boolean(reaction.remove), remove: Boolean(reaction.remove),
targetAuthorUuid: dropNull(reaction.targetAuthorUuid), targetAuthorUuid: dropNull(reaction.targetAuthorUuid),
targetTimestamp: normalizeNumber(dropNull(reaction.targetTimestamp)), targetTimestamp: reaction.targetTimestamp?.toNumber(),
}; };
} }
@ -226,7 +223,7 @@ export function processDelete(
} }
return { return {
targetSentTimestamp: normalizeNumber(dropNull(del.targetSentTimestamp)), targetSentTimestamp: del.targetSentTimestamp?.toNumber(),
}; };
} }
@ -245,7 +242,7 @@ export async function processDataMessage(
throw new Error('Missing timestamp on dataMessage'); throw new Error('Missing timestamp on dataMessage');
} }
const timestamp = normalizeNumber(message.timestamp); const timestamp = message.timestamp?.toNumber();
if (envelopeTimestamp !== timestamp) { if (envelopeTimestamp !== timestamp) {
throw new Error( throw new Error(
@ -272,9 +269,7 @@ export async function processDataMessage(
contact: processContact(message.contact), contact: processContact(message.contact),
preview: processPreview(message.preview), preview: processPreview(message.preview),
sticker: processSticker(message.sticker), sticker: processSticker(message.sticker),
requiredProtocolVersion: normalizeNumber( requiredProtocolVersion: dropNull(message.requiredProtocolVersion),
dropNull(message.requiredProtocolVersion)
),
isViewOnce: Boolean(message.isViewOnce), isViewOnce: Boolean(message.isViewOnce),
reaction: processReaction(message.reaction), reaction: processReaction(message.reaction),
delete: processDelete(message.delete), delete: processDelete(message.delete),

View File

@ -1,17 +0,0 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
export function normalizeNumber(value: number | Long): number;
export function normalizeNumber(value?: number | Long): number | undefined;
export function normalizeNumber(value?: number | Long): number | undefined {
if (value === undefined) {
return undefined;
}
if (typeof value === 'number') {
return value;
}
return value.toNumber();
}

View File

@ -3,8 +3,6 @@
import Long from 'long'; import Long from 'long';
import { normalizeNumber } from './normalizeNumber';
export function getSafeLongFromTimestamp(timestamp = 0): Long { export function getSafeLongFromTimestamp(timestamp = 0): Long {
if (timestamp >= Number.MAX_SAFE_INTEGER) { if (timestamp >= Number.MAX_SAFE_INTEGER) {
return Long.MAX_VALUE; return Long.MAX_VALUE;
@ -13,12 +11,12 @@ export function getSafeLongFromTimestamp(timestamp = 0): Long {
return Long.fromNumber(timestamp); return Long.fromNumber(timestamp);
} }
export function getTimestampFromLong(value?: Long | number | null): number { export function getTimestampFromLong(value?: Long | null): number {
if (!value) { if (!value) {
return 0; return 0;
} }
const num = normalizeNumber(value); const num = value.toNumber();
if (num >= Number.MAX_SAFE_INTEGER) { if (num >= Number.MAX_SAFE_INTEGER) {
return Number.MAX_SAFE_INTEGER; return Number.MAX_SAFE_INTEGER;