mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2026-05-02 14:21:05 +01:00
Use protopiler for protocol buffers
Co-authored-by: Jamie Kyle <jamie@signal.org>
This commit is contained in:
@@ -3,7 +3,6 @@
|
||||
|
||||
import lodash from 'lodash';
|
||||
|
||||
import type { SignalService as Proto } from '../protobuf/index.std.js';
|
||||
import {
|
||||
BodyRange,
|
||||
type RawBodyRange,
|
||||
@@ -26,7 +25,7 @@ const MAX_PER_TYPE = 250;
|
||||
|
||||
// We drop unknown bodyRanges and remove extra stuff so they serialize properly
|
||||
export function filterAndClean(
|
||||
ranges: ReadonlyArray<Proto.IBodyRange | RawBodyRange> | undefined | null
|
||||
ranges: ReadonlyArray<RawBodyRange> | undefined | null
|
||||
): ReadonlyArray<RawBodyRange> | undefined {
|
||||
if (!ranges) {
|
||||
return undefined;
|
||||
@@ -108,7 +107,7 @@ export function filterAndClean(
|
||||
}
|
||||
|
||||
export function hydrateRanges(
|
||||
ranges: ReadonlyArray<BodyRange<object>> | undefined,
|
||||
ranges: ReadonlyArray<RawBodyRange> | undefined,
|
||||
conversationSelector: (id: string) => { id: string; title: string }
|
||||
): Array<HydratedBodyRangeType> | undefined {
|
||||
if (!ranges) {
|
||||
|
||||
@@ -78,6 +78,12 @@ export function fromAciUuidBytesOrString(
|
||||
context: string
|
||||
): AciString;
|
||||
|
||||
export function fromAciUuidBytesOrString(
|
||||
bytes: Uint8Array | undefined | null,
|
||||
fallback: string,
|
||||
context: string
|
||||
): AciString;
|
||||
|
||||
export function fromAciUuidBytesOrString(
|
||||
bytes: Uint8Array | undefined | null,
|
||||
fallback: string | undefined | null,
|
||||
|
||||
@@ -6,7 +6,7 @@ import * as Bytes from '../Bytes.std.js';
|
||||
import { SignalService as Proto } from '../protobuf/index.std.js';
|
||||
import { fromServiceIdBinaryOrString } from './ServiceId.node.js';
|
||||
|
||||
import PinnedConversation = Proto.AccountRecord.IPinnedConversation;
|
||||
import PinnedConversation = Proto.AccountRecord.PinnedConversation.Params;
|
||||
|
||||
export function arePinnedConversationsEqual(
|
||||
localValue: Array<PinnedConversation>,
|
||||
@@ -17,10 +17,17 @@ export function arePinnedConversationsEqual(
|
||||
}
|
||||
return localValue.every(
|
||||
(localPinnedConversation: PinnedConversation, index: number) => {
|
||||
const remotePinnedConversation = remoteValue[index];
|
||||
const remotePinnedConversation = remoteValue[index].identifier;
|
||||
if (!remotePinnedConversation) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!localPinnedConversation.identifier) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { contact, groupMasterKey, legacyGroupId } =
|
||||
localPinnedConversation;
|
||||
localPinnedConversation.identifier;
|
||||
|
||||
if (contact) {
|
||||
const { contact: remoteContact } = remotePinnedConversation;
|
||||
|
||||
@@ -15,6 +15,8 @@ import { canBeTranscoded } from './Attachment.std.js';
|
||||
import * as Errors from '../types/errors.std.js';
|
||||
import * as Bytes from '../Bytes.std.js';
|
||||
|
||||
import { toNumber } from './toNumber.std.js';
|
||||
|
||||
const log = createLogger('attachments');
|
||||
|
||||
// All outgoing images go through handleImageAttachment before being sent and thus have
|
||||
@@ -100,8 +102,8 @@ export function copyCdnFields(
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
cdnId: dropNull(uploaded.cdnId)?.toString(),
|
||||
cdnKey: uploaded.cdnKey,
|
||||
cdnId: undefined,
|
||||
cdnKey: uploaded.attachmentIdentifier.cdnKey,
|
||||
cdnNumber: dropNull(uploaded.cdnNumber),
|
||||
digest: Bytes.toBase64(uploaded.digest),
|
||||
incrementalMac: uploaded.incrementalMac
|
||||
@@ -110,6 +112,6 @@ export function copyCdnFields(
|
||||
chunkSize: dropNull(uploaded.chunkSize),
|
||||
key: Bytes.toBase64(uploaded.key),
|
||||
plaintextHash: uploaded.plaintextHash,
|
||||
uploadTimestamp: uploaded.uploadTimestamp?.toNumber(),
|
||||
uploadTimestamp: toNumber(uploaded.uploadTimestamp) ?? undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2024 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import Long from 'long';
|
||||
import type { Backups, SignalService } from '../protobuf/index.std.js';
|
||||
import * as Bytes from '../Bytes.std.js';
|
||||
import { backupsService } from '../services/backups/index.preload.js';
|
||||
@@ -17,8 +16,8 @@ const log = createLogger('BackupSubscriptionData');
|
||||
// we'll need separate logic for each
|
||||
export async function saveBackupsSubscriberData(
|
||||
backupsSubscriberData:
|
||||
| Backups.AccountData.IIAPSubscriberData
|
||||
| SignalService.AccountRecord.IIAPSubscriberData
|
||||
| Backups.AccountData.IAPSubscriberData.Params
|
||||
| SignalService.AccountRecord.IAPSubscriberData.Params
|
||||
| null
|
||||
| undefined
|
||||
): Promise<void> {
|
||||
@@ -35,8 +34,7 @@ export async function saveBackupsSubscriberData(
|
||||
return;
|
||||
}
|
||||
|
||||
const { subscriberId, purchaseToken, originalTransactionId } =
|
||||
backupsSubscriberData;
|
||||
const { subscriberId, iapSubscriptionId } = backupsSubscriberData;
|
||||
|
||||
if (Bytes.isNotEmpty(subscriberId)) {
|
||||
await itemStorage.put('backupsSubscriberId', subscriberId);
|
||||
@@ -44,16 +42,19 @@ export async function saveBackupsSubscriberData(
|
||||
await itemStorage.remove('backupsSubscriberId');
|
||||
}
|
||||
|
||||
if (purchaseToken) {
|
||||
await itemStorage.put('backupsSubscriberPurchaseToken', purchaseToken);
|
||||
if (iapSubscriptionId?.purchaseToken != null) {
|
||||
await itemStorage.put(
|
||||
'backupsSubscriberPurchaseToken',
|
||||
iapSubscriptionId.purchaseToken
|
||||
);
|
||||
} else {
|
||||
await itemStorage.remove('backupsSubscriberPurchaseToken');
|
||||
}
|
||||
|
||||
if (originalTransactionId) {
|
||||
if (iapSubscriptionId?.originalTransactionId != null) {
|
||||
await itemStorage.put(
|
||||
'backupsSubscriberOriginalTransactionId',
|
||||
originalTransactionId.toString()
|
||||
iapSubscriptionId.originalTransactionId.toString()
|
||||
);
|
||||
} else {
|
||||
await itemStorage.remove('backupsSubscriberOriginalTransactionId');
|
||||
@@ -72,27 +73,38 @@ export async function saveBackupTier(
|
||||
}
|
||||
}
|
||||
|
||||
export function generateBackupsSubscriberData(): Backups.AccountData.IIAPSubscriberData | null {
|
||||
const backupsSubscriberId = itemStorage.get('backupsSubscriberId');
|
||||
export function generateBackupsSubscriberData(): Backups.AccountData.IAPSubscriberData.Params | null {
|
||||
const subscriberId = itemStorage.get('backupsSubscriberId') ?? null;
|
||||
|
||||
if (Bytes.isEmpty(backupsSubscriberId)) {
|
||||
if (Bytes.isEmpty(subscriberId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const backupsSubscriberData: Backups.AccountData.IIAPSubscriberData = {
|
||||
subscriberId: backupsSubscriberId,
|
||||
};
|
||||
let backupsSubscriberData: Backups.AccountData.IAPSubscriberData.Params;
|
||||
const purchaseToken = itemStorage.get('backupsSubscriberPurchaseToken');
|
||||
if (purchaseToken) {
|
||||
backupsSubscriberData.purchaseToken = purchaseToken;
|
||||
backupsSubscriberData = {
|
||||
subscriberId,
|
||||
iapSubscriptionId: {
|
||||
purchaseToken,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
const originalTransactionId = itemStorage.get(
|
||||
'backupsSubscriberOriginalTransactionId'
|
||||
);
|
||||
if (originalTransactionId) {
|
||||
backupsSubscriberData.originalTransactionId = Long.fromString(
|
||||
originalTransactionId
|
||||
);
|
||||
if (originalTransactionId != null) {
|
||||
backupsSubscriberData = {
|
||||
subscriberId,
|
||||
iapSubscriptionId: {
|
||||
originalTransactionId: BigInt(originalTransactionId),
|
||||
},
|
||||
};
|
||||
} else {
|
||||
backupsSubscriberData = {
|
||||
subscriberId,
|
||||
iapSubscriptionId: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import Long from 'long';
|
||||
import type { Call, PeekInfo, LocalDeviceState } from '@signalapp/ringrtc';
|
||||
import {
|
||||
CallState,
|
||||
@@ -67,7 +66,7 @@ import { drop } from './drop.std.js';
|
||||
import { sendCallLinkUpdateSync } from './sendCallLinkUpdateSync.preload.js';
|
||||
import { storageServiceUploadJob } from '../services/storage.preload.js';
|
||||
import { CallLinkFinalizeDeleteManager } from '../jobs/CallLinkFinalizeDeleteManager.preload.js';
|
||||
import { parsePartial, parseStrict } from './schemas.std.js';
|
||||
import { parseLoose, parseStrict } from './schemas.std.js';
|
||||
import { calling } from '../services/calling.preload.js';
|
||||
import { cleanupMessages } from './cleanup.preload.js';
|
||||
import { MessageModel } from '../models/messages.preload.js';
|
||||
@@ -141,11 +140,11 @@ export function formatLocalDeviceState(
|
||||
}
|
||||
|
||||
export function getCallIdFromRing(ringId: bigint): string {
|
||||
return Long.fromValue(callIdFromRingId(ringId)).toString();
|
||||
return BigInt(callIdFromRingId(ringId)).toString();
|
||||
}
|
||||
|
||||
export function getCallIdFromEra(eraId: string): string {
|
||||
return Long.fromValue(callIdFromEra(eraId)).toString();
|
||||
return BigInt(callIdFromEra(eraId)).toString();
|
||||
}
|
||||
|
||||
export function getCreatorAci(creator: Uint8Array): AciString {
|
||||
@@ -205,11 +204,11 @@ export function convertJoinState(joinState: JoinState): GroupCallJoinState {
|
||||
// ----------------------
|
||||
|
||||
export function getCallEventForProto(
|
||||
callEventProto: Proto.SyncMessage.ICallEvent,
|
||||
callEventProto: Proto.SyncMessage.CallEvent.Params,
|
||||
eventSource: string
|
||||
): CallEventDetails {
|
||||
const callEvent = parsePartial(callEventNormalizeSchema, callEventProto);
|
||||
const { callId, peerId, timestamp } = callEvent;
|
||||
const callEvent = parseLoose(callEventNormalizeSchema, callEventProto);
|
||||
const { callId, conversationId: peerId, timestamp } = callEvent;
|
||||
|
||||
let type: CallType;
|
||||
if (callEvent.type === Proto.SyncMessage.CallEvent.Type.GROUP_CALL) {
|
||||
@@ -234,10 +233,12 @@ export function getCallEventForProto(
|
||||
}
|
||||
|
||||
let direction: CallDirection;
|
||||
if (callEvent.direction === Proto.SyncMessage.CallEvent.Direction.INCOMING) {
|
||||
if (
|
||||
callEventProto.direction === Proto.SyncMessage.CallEvent.Direction.INCOMING
|
||||
) {
|
||||
direction = CallDirection.Incoming;
|
||||
} else if (
|
||||
callEvent.direction === Proto.SyncMessage.CallEvent.Direction.OUTGOING
|
||||
callEventProto.direction === Proto.SyncMessage.CallEvent.Direction.OUTGOING
|
||||
) {
|
||||
direction = CallDirection.Outgoing;
|
||||
} else {
|
||||
@@ -285,12 +286,13 @@ const callLogEventFromProto: Partial<
|
||||
};
|
||||
|
||||
export function getCallLogEventForProto(
|
||||
callLogEventProto: Proto.SyncMessage.ICallLogEvent
|
||||
callLogEventProto: Proto.SyncMessage.CallLogEvent.Params
|
||||
): CallLogEventDetails {
|
||||
// CallLogEvent peerId is ambiguous whether it's a conversationId (direct, or groupId)
|
||||
// or roomId so handle both cases
|
||||
const { peerId: peerIdBytes } = callLogEventProto;
|
||||
const callLogEvent = parsePartial(callLogEventNormalizeSchema, {
|
||||
const { conversationId: peerIdBytes } = callLogEventProto;
|
||||
|
||||
const callLogEvent = parseLoose(callLogEventNormalizeSchema, {
|
||||
...callLogEventProto,
|
||||
peerIdAsConversationId: peerIdBytes,
|
||||
peerIdAsRoomId: peerIdBytes,
|
||||
@@ -365,24 +367,24 @@ export function getBytesForPeerId(callHistory: CallHistoryDetails): Uint8Array {
|
||||
|
||||
export function getCallIdForProto(
|
||||
callHistory: CallHistoryDetails
|
||||
): Long | undefined {
|
||||
): bigint | null {
|
||||
try {
|
||||
return Long.fromString(callHistory.callId);
|
||||
} catch (error) {
|
||||
return BigInt(callHistory.callId);
|
||||
} catch {
|
||||
// When CallHistory is a placeholder record for call links, then the history item's
|
||||
// callId is invalid. We will ignore it and only send the timestamp.
|
||||
if (callHistory.mode === CallMode.Adhoc) {
|
||||
return undefined;
|
||||
return null;
|
||||
}
|
||||
|
||||
// For other calls, we expect a valid callId.
|
||||
throw error;
|
||||
throw new Error(`Invalid callId: ${callHistory.callId}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function getProtoForCallHistory(
|
||||
callHistory: CallHistoryDetails
|
||||
): Proto.SyncMessage.ICallEvent | null {
|
||||
): Proto.SyncMessage.CallEvent.Params {
|
||||
const event = statusToProto[callHistory.status];
|
||||
|
||||
strictAssert(
|
||||
@@ -392,14 +394,14 @@ export function getProtoForCallHistory(
|
||||
)}`
|
||||
);
|
||||
|
||||
return new Proto.SyncMessage.CallEvent({
|
||||
peerId: getBytesForPeerId(callHistory),
|
||||
return {
|
||||
conversationId: getBytesForPeerId(callHistory),
|
||||
callId: getCallIdForProto(callHistory),
|
||||
type: typeToProto[callHistory.type],
|
||||
direction: directionToProto[callHistory.direction],
|
||||
event,
|
||||
timestamp: Long.fromNumber(callHistory.timestamp),
|
||||
});
|
||||
timestamp: BigInt(callHistory.timestamp),
|
||||
};
|
||||
}
|
||||
|
||||
// Local Events
|
||||
@@ -536,7 +538,7 @@ export function getCallDetailsFromDirectCall(
|
||||
): CallDetails {
|
||||
const ringerId = call.isIncoming ? call.remoteUserId : null;
|
||||
return parseStrict(callDetailsSchema, {
|
||||
callId: Long.fromValue(call.callId).toString(),
|
||||
callId: (call.callId satisfies bigint).toString(),
|
||||
peerId,
|
||||
ringerId,
|
||||
startedById: ringerId,
|
||||
@@ -1304,19 +1306,24 @@ async function updateRemoteCallHistory(
|
||||
|
||||
try {
|
||||
const ourAci = itemStorage.user.getCheckedAci();
|
||||
const syncMessage = MessageSender.createSyncMessage();
|
||||
|
||||
syncMessage.callEvent = getProtoForCallHistory(callHistory);
|
||||
|
||||
const contentMessage = new Proto.Content();
|
||||
contentMessage.syncMessage = syncMessage;
|
||||
const syncMessage = MessageSender.padSyncMessage({
|
||||
content: {
|
||||
callEvent: getProtoForCallHistory(callHistory),
|
||||
},
|
||||
});
|
||||
|
||||
await singleProtoJobQueue.add({
|
||||
contentHint: ContentHint.Resendable,
|
||||
serviceId: ourAci,
|
||||
isSyncMessage: true,
|
||||
protoBase64: Bytes.toBase64(
|
||||
Proto.Content.encode(contentMessage).finish()
|
||||
Proto.Content.encode({
|
||||
content: {
|
||||
syncMessage,
|
||||
},
|
||||
senderKeyDistributionMessage: null,
|
||||
pniSignatureMessage: null,
|
||||
})
|
||||
),
|
||||
type: 'callEventSync',
|
||||
urgent: false,
|
||||
@@ -1457,28 +1464,34 @@ export async function markAllCallHistoryReadAndSync(
|
||||
|
||||
const ourAci = itemStorage.user.getCheckedAci();
|
||||
|
||||
const callLogEvent = new Proto.SyncMessage.CallLogEvent({
|
||||
const callLogEvent: Proto.SyncMessage.CallLogEvent.Params = {
|
||||
type: inConversation
|
||||
? Proto.SyncMessage.CallLogEvent.Type.MARKED_AS_READ_IN_CONVERSATION
|
||||
: Proto.SyncMessage.CallLogEvent.Type.MARKED_AS_READ,
|
||||
timestamp: Long.fromNumber(latestCall.timestamp),
|
||||
peerId: getBytesForPeerId(latestCall),
|
||||
timestamp: BigInt(latestCall.timestamp),
|
||||
conversationId: getBytesForPeerId(latestCall),
|
||||
callId: getCallIdForProto(latestCall),
|
||||
};
|
||||
|
||||
const syncMessage = MessageSender.padSyncMessage({
|
||||
content: {
|
||||
callLogEvent,
|
||||
},
|
||||
});
|
||||
|
||||
const syncMessage = MessageSender.createSyncMessage();
|
||||
syncMessage.callLogEvent = callLogEvent;
|
||||
|
||||
const contentMessage = new Proto.Content();
|
||||
contentMessage.syncMessage = syncMessage;
|
||||
|
||||
log.info('markAllCallHistoryReadAndSync: Queueing sync message');
|
||||
await singleProtoJobQueue.add({
|
||||
contentHint: ContentHint.Resendable,
|
||||
serviceId: ourAci,
|
||||
isSyncMessage: true,
|
||||
protoBase64: Bytes.toBase64(
|
||||
Proto.Content.encode(contentMessage).finish()
|
||||
Proto.Content.encode({
|
||||
content: {
|
||||
syncMessage,
|
||||
},
|
||||
senderKeyDistributionMessage: null,
|
||||
pniSignatureMessage: null,
|
||||
})
|
||||
),
|
||||
type: 'callLogEventSync',
|
||||
urgent: false,
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
import type { CallingMessage } from '@signalapp/ringrtc';
|
||||
import { CallMessageUrgency } from '@signalapp/ringrtc';
|
||||
import Long from 'long';
|
||||
import { SignalService as Proto } from '../protobuf/index.std.js';
|
||||
import { createLogger } from '../logging/log.std.js';
|
||||
import { toLogFormat } from '../types/errors.std.js';
|
||||
@@ -22,17 +21,18 @@ export function callingMessageToProto(
|
||||
opaque,
|
||||
}: CallingMessage,
|
||||
urgency?: CallMessageUrgency
|
||||
): Proto.ICallMessage {
|
||||
let opaqueField: undefined | Proto.CallMessage.IOpaque;
|
||||
): Proto.CallMessage.Params {
|
||||
let opaqueField: undefined | Proto.CallMessage.Opaque.Params;
|
||||
if (opaque) {
|
||||
opaqueField = {
|
||||
...opaque,
|
||||
data: opaque.data,
|
||||
urgency: null,
|
||||
data: opaque.data ?? null,
|
||||
};
|
||||
}
|
||||
if (urgency !== undefined) {
|
||||
opaqueField = {
|
||||
...(opaqueField ?? {}),
|
||||
...(opaqueField ?? { data: null, urgency: null }),
|
||||
urgency: urgencyToProto(urgency),
|
||||
};
|
||||
}
|
||||
@@ -41,42 +41,42 @@ export function callingMessageToProto(
|
||||
offer: offer
|
||||
? {
|
||||
...offer,
|
||||
id: Long.fromValue(offer.callId),
|
||||
id: offer.callId,
|
||||
type: offer.type as number,
|
||||
opaque: offer.opaque,
|
||||
}
|
||||
: undefined,
|
||||
: null,
|
||||
answer: answer
|
||||
? {
|
||||
...answer,
|
||||
id: Long.fromValue(answer.callId),
|
||||
id: answer.callId,
|
||||
opaque: answer.opaque,
|
||||
}
|
||||
: undefined,
|
||||
: null,
|
||||
iceUpdate: iceCandidates
|
||||
? iceCandidates.map((candidate): Proto.CallMessage.IIceUpdate => {
|
||||
? iceCandidates.map((candidate): Proto.CallMessage.IceUpdate.Params => {
|
||||
return {
|
||||
...candidate,
|
||||
id: Long.fromValue(candidate.callId),
|
||||
id: candidate.callId,
|
||||
opaque: candidate.opaque,
|
||||
};
|
||||
})
|
||||
: undefined,
|
||||
: null,
|
||||
busy: busy
|
||||
? {
|
||||
...busy,
|
||||
id: Long.fromValue(busy.callId),
|
||||
id: busy.callId,
|
||||
}
|
||||
: undefined,
|
||||
: null,
|
||||
hangup: hangup
|
||||
? {
|
||||
...hangup,
|
||||
id: Long.fromValue(hangup.callId),
|
||||
id: hangup.callId,
|
||||
type: hangup.type as number,
|
||||
}
|
||||
: undefined,
|
||||
destinationDeviceId,
|
||||
opaque: opaqueField,
|
||||
: null,
|
||||
destinationDeviceId: destinationDeviceId ?? null,
|
||||
opaque: opaqueField ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ import { isProduction } from './version.std.js';
|
||||
|
||||
import type { IncomingWebSocketRequest } from '../textsecure/WebsocketResources.preload.js';
|
||||
|
||||
import { toNumber } from './toNumber.std.js';
|
||||
|
||||
const { isNumber } = lodash;
|
||||
|
||||
const log = createLogger('checkFirstEnvelope');
|
||||
@@ -33,7 +35,7 @@ export function checkFirstEnvelope(incoming: IncomingWebSocketRequest): void {
|
||||
}
|
||||
|
||||
const decoded = Proto.Envelope.decode(plaintext);
|
||||
const newEnvelopeTimestamp = decoded.clientTimestamp?.toNumber();
|
||||
const newEnvelopeTimestamp = toNumber(decoded.clientTimestamp);
|
||||
if (!isNumber(newEnvelopeTimestamp)) {
|
||||
log.warn('timestamp is not a number!');
|
||||
return;
|
||||
|
||||
@@ -65,7 +65,8 @@ export function computeBlurHashUrl(
|
||||
desiredWidth = 1,
|
||||
desiredHeight = 1
|
||||
): string {
|
||||
const invAspect = Math.abs(desiredHeight) / (Math.abs(desiredWidth) + 1e-23);
|
||||
const invAspect =
|
||||
(Math.abs(desiredHeight) + 1e-23) / (Math.abs(desiredWidth) + 1e-23);
|
||||
|
||||
// Calculate width and height that roughly satisfy the desired PIXEL_COUNT
|
||||
//
|
||||
|
||||
@@ -207,7 +207,12 @@ export async function deleteStoryForEveryone(
|
||||
{
|
||||
destinationServiceId,
|
||||
timestamp: story.timestamp,
|
||||
storyMessageRecipients: newStoryMessageRecipients,
|
||||
storyMessageRecipients: newStoryMessageRecipients.map(recipient => {
|
||||
return {
|
||||
...recipient,
|
||||
destinationServiceId: recipient.destinationServiceId ?? undefined,
|
||||
};
|
||||
}),
|
||||
},
|
||||
noop
|
||||
);
|
||||
|
||||
@@ -15,7 +15,7 @@ const log = createLogger('denyPendingApprovalRequest');
|
||||
export async function denyPendingApprovalRequest(
|
||||
conversationAttributes: ConversationAttributesType,
|
||||
aci: AciString
|
||||
): Promise<Proto.GroupChange.Actions | undefined> {
|
||||
): Promise<Proto.GroupChange.Actions.Params | undefined> {
|
||||
const idLog = getConversationIdForLogging(conversationAttributes);
|
||||
|
||||
// This user's pending state may have changed in the time between the user's
|
||||
|
||||
36
ts/util/encodeDelimited.std.ts
Normal file
36
ts/util/encodeDelimited.std.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright 2026 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
export function encodeDelimited(buf: Uint8Array): [Uint8Array, Uint8Array] {
|
||||
const len = buf.byteLength;
|
||||
let prefix: Uint8Array;
|
||||
/* eslint-disable no-bitwise */
|
||||
if (len < 0x80) {
|
||||
prefix = new Uint8Array(1);
|
||||
prefix[0] = len;
|
||||
} else if (len < 0x4000) {
|
||||
prefix = new Uint8Array(2);
|
||||
prefix[0] = 0x80 | (len & 0x7f);
|
||||
prefix[1] = len >>> 7;
|
||||
} else if (len < 0x200000) {
|
||||
prefix = new Uint8Array(3);
|
||||
prefix[0] = 0x80 | (len & 0x7f);
|
||||
prefix[1] = 0x80 | ((len >>> 7) & 0x7f);
|
||||
prefix[2] = len >>> 14;
|
||||
} else if (len < 0x10000000) {
|
||||
prefix = new Uint8Array(4);
|
||||
prefix[0] = 0x80 | (len & 0x7f);
|
||||
prefix[1] = 0x80 | ((len >>> 7) & 0x7f);
|
||||
prefix[2] = 0x80 | ((len >>> 14) & 0x7f);
|
||||
prefix[3] = len >>> 21;
|
||||
} else {
|
||||
prefix = new Uint8Array(5);
|
||||
prefix[0] = 0x80 | (len & 0x7f);
|
||||
prefix[1] = 0x80 | ((len >>> 7) & 0x7f);
|
||||
prefix[2] = 0x80 | ((len >>> 14) & 0x7f);
|
||||
prefix[3] = 0x80 | ((len >>> 21) & 0x7f);
|
||||
prefix[4] = len >>> 28;
|
||||
}
|
||||
/* eslint-enable no-bitwise */
|
||||
return [prefix, buf];
|
||||
}
|
||||
@@ -224,13 +224,13 @@ export async function onRetryRequest(event: RetryRequestEvent): Promise<void> {
|
||||
timestamp,
|
||||
});
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
let contentProto: Proto.IContent | undefined =
|
||||
let contentProto: Proto.Content.Params | undefined =
|
||||
addSenderKeyResult.contentProto;
|
||||
const { groupId } = addSenderKeyResult;
|
||||
|
||||
// Assert that the requesting UUID is still part of a story distribution list that
|
||||
// the message was sent to, and add its sender key distribution message (SKDM).
|
||||
if (contentProto.storyMessage && !groupId) {
|
||||
if (contentProto.content?.storyMessage && !groupId) {
|
||||
contentProto = await checkDistributionListAndAddSKDM({
|
||||
confirm,
|
||||
contentProto,
|
||||
@@ -243,20 +243,19 @@ export async function onRetryRequest(event: RetryRequestEvent): Promise<void> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const story = Boolean(contentProto.storyMessage);
|
||||
const story = Boolean(contentProto.content?.storyMessage);
|
||||
|
||||
const recipientConversation = window.ConversationController.getOrCreate(
|
||||
requesterAci,
|
||||
'private'
|
||||
);
|
||||
const protoToSend = new Proto.Content(contentProto);
|
||||
|
||||
await conversationJobQueue.add({
|
||||
type: 'SavedProto',
|
||||
conversationId: recipientConversation.id,
|
||||
contentHint,
|
||||
groupId,
|
||||
protoBase64: Bytes.toBase64(Proto.Content.encode(protoToSend).finish()),
|
||||
protoBase64: Bytes.toBase64(Proto.Content.encode(contentProto)),
|
||||
story,
|
||||
timestamp,
|
||||
urgent,
|
||||
@@ -481,13 +480,13 @@ async function checkDistributionListAndAddSKDM({
|
||||
requesterAci,
|
||||
messaging,
|
||||
}: {
|
||||
contentProto: Proto.IContent;
|
||||
contentProto: Proto.Content.Params;
|
||||
timestamp: number;
|
||||
confirm: () => void;
|
||||
requesterAci: AciString;
|
||||
logId: string;
|
||||
messaging: MessageSender;
|
||||
}): Promise<Proto.IContent | undefined> {
|
||||
}): Promise<Proto.Content.Params | undefined> {
|
||||
let distributionList: StoryDistributionListDataType | undefined;
|
||||
const { storyDistributionLists } = window.reduxStore.getState();
|
||||
const membersByListId = new Map<string, Set<ServiceIdString>>();
|
||||
@@ -562,14 +561,14 @@ async function maybeAddSenderKeyDistributionMessage({
|
||||
requesterAci,
|
||||
timestamp,
|
||||
}: {
|
||||
contentProto: Proto.IContent;
|
||||
contentProto: Proto.Content.Params;
|
||||
logId: string;
|
||||
messageIds: Array<string>;
|
||||
requestGroupId?: string;
|
||||
requesterAci: AciString;
|
||||
timestamp: number;
|
||||
}): Promise<{
|
||||
contentProto: Proto.IContent;
|
||||
contentProto: Proto.Content.Params;
|
||||
groupId?: string;
|
||||
}> {
|
||||
const conversation = await getRetryConversation({
|
||||
|
||||
@@ -6,7 +6,7 @@ import protobufjs from 'protobufjs';
|
||||
const { Reader } = protobufjs;
|
||||
|
||||
type MessageWithUnknownFields = {
|
||||
$unknownFields?: ReadonlyArray<Uint8Array>;
|
||||
$unknown?: ReadonlyArray<Uint8Array>;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -38,7 +38,7 @@ export function inspectUnknownFieldTags(
|
||||
message: MessageWithUnknownFields
|
||||
): Array<number> {
|
||||
return (
|
||||
message.$unknownFields?.map(field => {
|
||||
message.$unknown?.map(field => {
|
||||
// https://protobuf.dev/programming-guides/encoding/
|
||||
// The first byte of a field is a varint encoding the tag bit-shifted << 3
|
||||
// eslint-disable-next-line no-bitwise
|
||||
|
||||
9
ts/util/isKnownProtoEnumMember.std.ts
Normal file
9
ts/util/isKnownProtoEnumMember.std.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright 2026 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
export function isKnownProtoEnumMember<E extends number>(
|
||||
enum_: Record<string | `${E}`, E | string>,
|
||||
value: unknown
|
||||
): value is E {
|
||||
return typeof value === 'number' && Object.hasOwn(enum_, value);
|
||||
}
|
||||
4
ts/util/long.std.ts
Normal file
4
ts/util/long.std.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
// Copyright 2026 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
export const MAX_VALUE = 0xffffffff_ffffffffn;
|
||||
@@ -12,7 +12,6 @@ import { isStory } from '../state/selectors/message.preload.js';
|
||||
import { queueUpdateMessage } from './messageBatcher.preload.js';
|
||||
import { isMe } from './whatTypeOfConversation.dom.js';
|
||||
import { drop } from './drop.std.js';
|
||||
import { fromServiceIdBinaryOrString } from './ServiceId.node.js';
|
||||
import { applyDeleteForEveryone } from './deleteForEveryone.preload.js';
|
||||
import { itemStorage } from '../textsecure/Storage.preload.js';
|
||||
import { MessageModel } from '../models/messages.preload.js';
|
||||
@@ -64,16 +63,7 @@ export async function onStoryRecipientUpdate(
|
||||
Set<string>
|
||||
>();
|
||||
data.storyMessageRecipients.forEach(item => {
|
||||
const {
|
||||
destinationServiceId: rawDestinationServiceId,
|
||||
destinationServiceIdBinary,
|
||||
} = item;
|
||||
|
||||
const recipientServiceId = fromServiceIdBinaryOrString(
|
||||
destinationServiceIdBinary,
|
||||
rawDestinationServiceId,
|
||||
`${logId}.recipientServiceId`
|
||||
);
|
||||
const { destinationServiceId: recipientServiceId } = item;
|
||||
|
||||
if (recipientServiceId == null) {
|
||||
return;
|
||||
|
||||
@@ -15,7 +15,7 @@ const log = createLogger('removePendingMember');
|
||||
export async function removePendingMember(
|
||||
conversationAttributes: ConversationAttributesType,
|
||||
serviceIds: ReadonlyArray<ServiceIdString>
|
||||
): Promise<Proto.GroupChange.Actions | undefined> {
|
||||
): Promise<Proto.GroupChange.Actions.Params | undefined> {
|
||||
const idLog = getConversationIdForLogging(conversationAttributes);
|
||||
|
||||
const pendingServiceIds = serviceIds
|
||||
|
||||
@@ -43,26 +43,32 @@ async function _sendCallLinkUpdateSync(
|
||||
try {
|
||||
const ourAci = itemStorage.user.getCheckedAci();
|
||||
|
||||
const callLinkUpdate = new Proto.SyncMessage.CallLinkUpdate({
|
||||
const callLinkUpdate: Proto.SyncMessage.CallLinkUpdate.Params = {
|
||||
type: protoType,
|
||||
rootKey: toRootKeyBytes(callLink.rootKey),
|
||||
adminPasskey: callLink.adminKey
|
||||
? toAdminKeyBytes(callLink.adminKey)
|
||||
: null,
|
||||
};
|
||||
|
||||
const syncMessage = MessageSender.padSyncMessage({
|
||||
content: {
|
||||
callLinkUpdate,
|
||||
},
|
||||
});
|
||||
|
||||
const syncMessage = MessageSender.createSyncMessage();
|
||||
syncMessage.callLinkUpdate = callLinkUpdate;
|
||||
|
||||
const contentMessage = new Proto.Content();
|
||||
contentMessage.syncMessage = syncMessage;
|
||||
|
||||
await singleProtoJobQueue.add({
|
||||
contentHint: ContentHint.Resendable,
|
||||
serviceId: ourAci,
|
||||
isSyncMessage: true,
|
||||
protoBase64: Bytes.toBase64(
|
||||
Proto.Content.encode(contentMessage).finish()
|
||||
Proto.Content.encode({
|
||||
content: {
|
||||
syncMessage,
|
||||
},
|
||||
senderKeyDistributionMessage: null,
|
||||
pniSignatureMessage: null,
|
||||
})
|
||||
),
|
||||
type: 'callLinkUpdateSync',
|
||||
urgent: false,
|
||||
|
||||
@@ -183,7 +183,7 @@ export async function sendToGroup({
|
||||
|
||||
type SendToGroupOptions = Readonly<{
|
||||
contentHint: number;
|
||||
contentMessage: Proto.Content;
|
||||
contentMessage: Proto.Content.Params;
|
||||
isPartialSend?: boolean;
|
||||
messageId: string | undefined;
|
||||
online?: boolean;
|
||||
@@ -249,7 +249,7 @@ export async function sendContentMessageToGroup(
|
||||
const sendLogCallback = messageSender.makeSendLogCallback({
|
||||
contentHint,
|
||||
messageId,
|
||||
proto: Proto.Content.encode(contentMessage).finish(),
|
||||
proto: Proto.Content.encode(contentMessage),
|
||||
sendType,
|
||||
timestamp,
|
||||
urgent,
|
||||
@@ -563,7 +563,7 @@ export async function sendToGroupViaSenderKey(
|
||||
contentHint,
|
||||
devices: devicesForSenderKey,
|
||||
distributionId,
|
||||
contentMessage: Proto.Content.encode(contentMessage).finish(),
|
||||
contentMessage: Proto.Content.encode(contentMessage),
|
||||
groupId,
|
||||
});
|
||||
|
||||
@@ -617,7 +617,7 @@ export async function sendToGroupViaSenderKey(
|
||||
sendLogId = await DataWriter.insertSentProto(
|
||||
{
|
||||
contentHint,
|
||||
proto: Proto.Content.encode(contentMessage).finish(),
|
||||
proto: Proto.Content.encode(contentMessage),
|
||||
timestamp,
|
||||
urgent,
|
||||
hasPniSignatureMessage: false,
|
||||
@@ -810,18 +810,18 @@ export async function sendToGroupViaSenderKey(
|
||||
// 11. Return early if there are no normal send recipients
|
||||
if (normalSendRecipients.length === 0) {
|
||||
return {
|
||||
dataMessage: contentMessage.dataMessage
|
||||
? Proto.DataMessage.encode(contentMessage.dataMessage).finish()
|
||||
dataMessage: contentMessage.content?.dataMessage
|
||||
? Proto.DataMessage.encode(contentMessage.content.dataMessage)
|
||||
: undefined,
|
||||
editMessage: contentMessage.editMessage
|
||||
? Proto.EditMessage.encode(contentMessage.editMessage).finish()
|
||||
editMessage: contentMessage.content?.editMessage
|
||||
? Proto.EditMessage.encode(contentMessage.content.editMessage)
|
||||
: undefined,
|
||||
successfulServiceIds: senderKeyRecipients,
|
||||
unidentifiedDeliveries: senderKeyRecipients,
|
||||
|
||||
contentHint,
|
||||
timestamp,
|
||||
contentProto: Proto.Content.encode(contentMessage).finish(),
|
||||
contentProto: Proto.Content.encode(contentMessage),
|
||||
recipients: senderKeyRecipientsWithDevices,
|
||||
urgent,
|
||||
};
|
||||
|
||||
@@ -9,9 +9,6 @@ import { deriveSecrets } from '../Crypto.node.js';
|
||||
|
||||
const { get, isFinite, isInteger, isString } = lodash;
|
||||
|
||||
const { RecordStructure, SessionStructure } = signal.proto.storage;
|
||||
const { Chain } = SessionStructure;
|
||||
|
||||
type KeyPairType = {
|
||||
privKey?: string;
|
||||
pubKey?: string;
|
||||
@@ -78,19 +75,15 @@ export type LocalUserDataType = {
|
||||
};
|
||||
|
||||
export function sessionStructureToBytes(
|
||||
recordStructure: signal.proto.storage.RecordStructure
|
||||
recordStructure: signal.proto.storage.RecordStructure.Params
|
||||
): Uint8Array {
|
||||
return signal.proto.storage.RecordStructure.encode(recordStructure).finish();
|
||||
return signal.proto.storage.RecordStructure.encode(recordStructure);
|
||||
}
|
||||
|
||||
export function sessionRecordToProtobuf(
|
||||
record: SessionRecordType,
|
||||
ourData: LocalUserDataType
|
||||
): signal.proto.storage.RecordStructure {
|
||||
const proto = new RecordStructure();
|
||||
|
||||
proto.previousSessions = [];
|
||||
|
||||
): signal.proto.storage.RecordStructure.Params {
|
||||
const sessionGroup = record.sessions || {};
|
||||
const sessions = Object.values(sessionGroup);
|
||||
|
||||
@@ -98,8 +91,11 @@ export function sessionRecordToProtobuf(
|
||||
return session?.indexInfo?.closed === -1;
|
||||
});
|
||||
|
||||
let currentSession: signal.proto.storage.SessionStructure.Params | null;
|
||||
if (first) {
|
||||
proto.currentSession = toProtobufSession(first, ourData);
|
||||
currentSession = toProtobufSession(first, ourData);
|
||||
} else {
|
||||
currentSession = null;
|
||||
}
|
||||
|
||||
sessions.sort((left, right) => {
|
||||
@@ -114,69 +110,26 @@ export function sessionRecordToProtobuf(
|
||||
throw new Error('toProtobuf: More than one open session!');
|
||||
}
|
||||
|
||||
proto.previousSessions = [];
|
||||
const previousSessions =
|
||||
new Array<signal.proto.storage.SessionStructure.Params>();
|
||||
onlyClosed.forEach(session => {
|
||||
proto.previousSessions.push(toProtobufSession(session, ourData));
|
||||
previousSessions.push(toProtobufSession(session, ourData));
|
||||
});
|
||||
|
||||
if (!proto.currentSession && proto.previousSessions.length === 0) {
|
||||
if (!currentSession && previousSessions.length === 0) {
|
||||
throw new Error('toProtobuf: Record had no sessions!');
|
||||
}
|
||||
|
||||
return proto;
|
||||
return {
|
||||
currentSession,
|
||||
previousSessions,
|
||||
};
|
||||
}
|
||||
|
||||
function toProtobufSession(
|
||||
session: SessionType,
|
||||
ourData: LocalUserDataType
|
||||
): signal.proto.storage.SessionStructure {
|
||||
const proto = new SessionStructure();
|
||||
|
||||
// Core Fields
|
||||
|
||||
proto.aliceBaseKey = binaryToUint8Array(session, 'indexInfo.baseKey', 33);
|
||||
proto.localIdentityPublic = ourData.identityKeyPublic;
|
||||
proto.localRegistrationId = ourData.registrationId;
|
||||
|
||||
proto.previousCounter =
|
||||
getInteger(session, 'currentRatchet.previousCounter') + 1;
|
||||
proto.remoteIdentityPublic = binaryToUint8Array(
|
||||
session,
|
||||
'indexInfo.remoteIdentityKey',
|
||||
33
|
||||
);
|
||||
proto.remoteRegistrationId = getInteger(session, 'registrationId');
|
||||
proto.rootKey = binaryToUint8Array(session, 'currentRatchet.rootKey', 32);
|
||||
proto.sessionVersion = 3;
|
||||
|
||||
// Note: currently unused
|
||||
// proto.needsRefresh = null;
|
||||
|
||||
// Pending PreKey
|
||||
|
||||
if (session.pendingPreKey) {
|
||||
proto.pendingPreKey =
|
||||
new signal.proto.storage.SessionStructure.PendingPreKey();
|
||||
proto.pendingPreKey.baseKey = binaryToUint8Array(
|
||||
session,
|
||||
'pendingPreKey.baseKey',
|
||||
33
|
||||
);
|
||||
proto.pendingPreKey.signedPreKeyId = getInteger(
|
||||
session,
|
||||
'pendingPreKey.signedKeyId'
|
||||
);
|
||||
|
||||
if (session.pendingPreKey.preKeyId !== undefined) {
|
||||
proto.pendingPreKey.preKeyId = getInteger(
|
||||
session,
|
||||
'pendingPreKey.preKeyId'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Sender Chain
|
||||
|
||||
): signal.proto.storage.SessionStructure.Params {
|
||||
const senderBaseKey = session.currentRatchet?.ephemeralKeyPair?.pubKey;
|
||||
if (!senderBaseKey) {
|
||||
throw new Error('toProtobufSession: No sender base key!');
|
||||
@@ -208,12 +161,8 @@ function toProtobufSession(
|
||||
32
|
||||
);
|
||||
|
||||
proto.senderChain = protoSenderChain;
|
||||
|
||||
// First Receiver Chain
|
||||
|
||||
proto.receiverChains = [];
|
||||
|
||||
const firstReceiverChainBaseKey =
|
||||
session.currentRatchet?.lastRemoteEphemeralKey;
|
||||
if (!firstReceiverChainBaseKey) {
|
||||
@@ -225,6 +174,9 @@ function toProtobufSession(
|
||||
| ChainType
|
||||
| undefined;
|
||||
|
||||
const receiverChains =
|
||||
new Array<signal.proto.storage.SessionStructure.Chain.Params>();
|
||||
|
||||
// If the session was just initialized, then there will be no receiver chain
|
||||
if (firstReceiverChain) {
|
||||
const protoFirstReceiverChain = toProtobufChain(firstReceiverChain);
|
||||
@@ -241,7 +193,7 @@ function toProtobufSession(
|
||||
33
|
||||
);
|
||||
|
||||
proto.receiverChains.push(protoFirstReceiverChain);
|
||||
receiverChains.push(protoFirstReceiverChain);
|
||||
}
|
||||
|
||||
// Old Receiver Chains
|
||||
@@ -277,40 +229,79 @@ function toProtobufSession(
|
||||
33
|
||||
);
|
||||
|
||||
proto.receiverChains.push(protoChain);
|
||||
receiverChains.push(protoChain);
|
||||
});
|
||||
|
||||
return proto;
|
||||
return {
|
||||
// Core Fields
|
||||
aliceBaseKey: binaryToUint8Array(session, 'indexInfo.baseKey', 33),
|
||||
localIdentityPublic: ourData.identityKeyPublic,
|
||||
localRegistrationId: ourData.registrationId,
|
||||
|
||||
previousCounter: getInteger(session, 'currentRatchet.previousCounter') + 1,
|
||||
remoteIdentityPublic: binaryToUint8Array(
|
||||
session,
|
||||
'indexInfo.remoteIdentityKey',
|
||||
33
|
||||
),
|
||||
remoteRegistrationId: getInteger(session, 'registrationId'),
|
||||
rootKey: binaryToUint8Array(session, 'currentRatchet.rootKey', 32),
|
||||
sessionVersion: 3,
|
||||
|
||||
// Note: currently unused
|
||||
needsRefresh: null,
|
||||
|
||||
// Pending PreKey
|
||||
pendingPreKey: session.pendingPreKey
|
||||
? {
|
||||
baseKey: binaryToUint8Array(session, 'pendingPreKey.baseKey', 33),
|
||||
signedPreKeyId: getInteger(session, 'pendingPreKey.signedKeyId'),
|
||||
preKeyId:
|
||||
session.pendingPreKey.preKeyId !== undefined
|
||||
? getInteger(session, 'pendingPreKey.preKeyId')
|
||||
: null,
|
||||
}
|
||||
: null,
|
||||
|
||||
// Sender Chain
|
||||
|
||||
senderChain: protoSenderChain,
|
||||
|
||||
receiverChains,
|
||||
};
|
||||
}
|
||||
|
||||
function toProtobufChain(
|
||||
chain: ChainType
|
||||
): signal.proto.storage.SessionStructure.Chain {
|
||||
const proto = new Chain();
|
||||
|
||||
const protoChainKey = new Chain.ChainKey();
|
||||
protoChainKey.index = getInteger(chain, 'chainKey.counter') + 1;
|
||||
if (chain.chainKey?.key !== undefined) {
|
||||
protoChainKey.key = binaryToUint8Array(chain, 'chainKey.key', 32);
|
||||
}
|
||||
proto.chainKey = protoChainKey;
|
||||
|
||||
): signal.proto.storage.SessionStructure.Chain.Params {
|
||||
const messageKeys = Object.entries(chain.messageKeys || {});
|
||||
proto.messageKeys = messageKeys.map(entry => {
|
||||
const protoMessageKey = new SessionStructure.Chain.MessageKey();
|
||||
protoMessageKey.index = getInteger(entry, '0') + 1;
|
||||
const key = binaryToUint8Array(entry, '1', 32);
|
||||
|
||||
const { cipherKey, macKey, iv } = translateMessageKey(key);
|
||||
return {
|
||||
chainKey: {
|
||||
index: getInteger(chain, 'chainKey.counter') + 1,
|
||||
key:
|
||||
chain.chainKey?.key !== undefined
|
||||
? binaryToUint8Array(chain, 'chainKey.key', 32)
|
||||
: null,
|
||||
},
|
||||
messageKeys: messageKeys.map(
|
||||
(
|
||||
entry
|
||||
): signal.proto.storage.SessionStructure.Chain.MessageKey.Params => {
|
||||
const key = binaryToUint8Array(entry, '1', 32);
|
||||
|
||||
protoMessageKey.cipherKey = cipherKey;
|
||||
protoMessageKey.macKey = macKey;
|
||||
protoMessageKey.iv = iv;
|
||||
|
||||
return protoMessageKey;
|
||||
});
|
||||
|
||||
return proto;
|
||||
const { cipherKey, macKey, iv } = translateMessageKey(key);
|
||||
return {
|
||||
index: getInteger(entry, '0') + 1,
|
||||
cipherKey,
|
||||
macKey,
|
||||
iv,
|
||||
};
|
||||
}
|
||||
),
|
||||
senderRatchetKey: null,
|
||||
senderRatchetKeyPrivate: null,
|
||||
};
|
||||
}
|
||||
|
||||
// Utility functions
|
||||
|
||||
@@ -1,33 +1,32 @@
|
||||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import Long from 'long';
|
||||
|
||||
import { MAX_SAFE_DATE } from './timestamp.std.js';
|
||||
import { toNumber } from './toNumber.std.js';
|
||||
|
||||
export function getSafeLongFromTimestamp(
|
||||
timestamp = 0,
|
||||
maxValue: Long | number = MAX_SAFE_DATE
|
||||
): Long {
|
||||
maxValue: bigint | number = MAX_SAFE_DATE
|
||||
): bigint {
|
||||
if (timestamp >= MAX_SAFE_DATE) {
|
||||
if (typeof maxValue === 'number') {
|
||||
return Long.fromNumber(maxValue);
|
||||
return BigInt(maxValue);
|
||||
}
|
||||
return maxValue;
|
||||
}
|
||||
|
||||
return Long.fromNumber(timestamp);
|
||||
return BigInt(timestamp);
|
||||
}
|
||||
|
||||
export function getTimestampFromLong(
|
||||
value?: Long | null,
|
||||
value?: bigint | null,
|
||||
maxValue = MAX_SAFE_DATE
|
||||
): number {
|
||||
if (!value || value.isNegative()) {
|
||||
if (!value || value < 0n) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const num = value.toNumber();
|
||||
const num = toNumber(value);
|
||||
|
||||
if (num > MAX_SAFE_DATE) {
|
||||
return maxValue;
|
||||
@@ -42,12 +41,12 @@ export class InvalidTimestampError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
export function getCheckedTimestampFromLong(value?: Long | null): number {
|
||||
export function getCheckedTimestampFromLong(value?: bigint | null): number {
|
||||
if (value == null) {
|
||||
throw new InvalidTimestampError('No number');
|
||||
}
|
||||
|
||||
const num = value.toNumber();
|
||||
const num = toNumber(value);
|
||||
|
||||
if (num < 0) {
|
||||
throw new InvalidTimestampError('Underflow');
|
||||
@@ -61,9 +60,9 @@ export function getCheckedTimestampFromLong(value?: Long | null): number {
|
||||
}
|
||||
|
||||
export function getTimestampOrUndefinedFromLong(
|
||||
value?: Long | null
|
||||
value?: bigint | null
|
||||
): number | undefined {
|
||||
if (!value || value.isZero()) {
|
||||
if (!value || value === 0n) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -71,9 +70,9 @@ export function getTimestampOrUndefinedFromLong(
|
||||
}
|
||||
|
||||
export function getCheckedTimestampOrUndefinedFromLong(
|
||||
value?: Long | null
|
||||
value?: bigint | null
|
||||
): number | undefined {
|
||||
if (!value || value.isZero()) {
|
||||
if (!value || value === 0n) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
import Long from 'long';
|
||||
import { createReadStream } from 'node:fs';
|
||||
import type {
|
||||
AttachmentType,
|
||||
@@ -100,24 +99,28 @@ export async function uploadAttachment(
|
||||
const fileName = shouldStripFilename ? undefined : attachment.fileName;
|
||||
|
||||
return {
|
||||
cdnKey,
|
||||
attachmentIdentifier: {
|
||||
cdnKey,
|
||||
},
|
||||
cdnNumber,
|
||||
clientUuid: clientUuid ? uuidToBytes(clientUuid) : undefined,
|
||||
clientUuid: clientUuid ? uuidToBytes(clientUuid) : null,
|
||||
key: keys,
|
||||
size: attachment.data.byteLength,
|
||||
digest,
|
||||
plaintextHash,
|
||||
incrementalMac,
|
||||
chunkSize,
|
||||
uploadTimestamp: Long.fromNumber(uploadTimestamp),
|
||||
incrementalMac: incrementalMac ?? null,
|
||||
chunkSize: chunkSize ?? null,
|
||||
uploadTimestamp: BigInt(uploadTimestamp),
|
||||
|
||||
contentType: MIMETypeToString(attachment.contentType),
|
||||
fileName,
|
||||
flags,
|
||||
width,
|
||||
height,
|
||||
caption,
|
||||
blurHash,
|
||||
fileName: fileName ?? null,
|
||||
flags: flags ?? null,
|
||||
width: width ?? null,
|
||||
height: height ?? null,
|
||||
caption: caption ?? null,
|
||||
blurHash: blurHash ?? null,
|
||||
|
||||
thumbnail: null,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user