Migrate textsecure to eslint
Co-authored-by: Chris Svenningsen <chris@carbonfive.com>
This commit is contained in:
parent
b5df9b4067
commit
7b6d8f55d6
24 changed files with 706 additions and 299 deletions
|
@ -28,8 +28,3 @@ sticker-creator/**/*.js
|
|||
|
||||
**/*.d.ts
|
||||
webpack.config.ts
|
||||
|
||||
# Temporarily ignored during TSLint transition
|
||||
# JIRA: DESKTOP-304
|
||||
ts/sql/**
|
||||
ts/textsecure/**
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
// tslint:disable no-default-export no-unnecessary-local-variable
|
||||
|
||||
/* eslint-disable no-await-in-loop */
|
||||
/* eslint-disable camelcase */
|
||||
/* eslint-disable no-param-reassign */
|
||||
/* eslint-disable no-continue */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
import { ipcRenderer } from 'electron';
|
||||
|
||||
import {
|
||||
|
@ -241,7 +246,7 @@ const channels: ServerInterface = channelsAsUnknown;
|
|||
// When IPC arguments are prepared for the cross-process send, they are JSON.stringified.
|
||||
// We can't send ArrayBuffers or BigNumbers (what we get from proto library for dates),
|
||||
// We also cannot send objects with function-value keys, like what protobufjs gives us.
|
||||
function _cleanData(data: any, path: string = 'root') {
|
||||
function _cleanData(data: any, path = 'root') {
|
||||
if (data === null || data === undefined) {
|
||||
window.log.warn(`_cleanData: null or undefined value at path ${path}`);
|
||||
|
||||
|
@ -321,8 +326,6 @@ async function _shutdown() {
|
|||
}
|
||||
|
||||
resolve();
|
||||
|
||||
return;
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -1032,7 +1035,7 @@ async function getLastConversationActivity(
|
|||
if (result) {
|
||||
return new Message(result);
|
||||
}
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
async function getLastConversationPreview(
|
||||
conversationId: string,
|
||||
|
@ -1045,7 +1048,7 @@ async function getLastConversationPreview(
|
|||
if (result) {
|
||||
return new Message(result);
|
||||
}
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
async function getMessageMetricsForConversation(conversationId: string) {
|
||||
const result = await channels.getMessageMetricsForConversation(
|
||||
|
@ -1292,7 +1295,7 @@ async function getRecentStickers() {
|
|||
async function updateEmojiUsage(shortName: string) {
|
||||
await channels.updateEmojiUsage(shortName);
|
||||
}
|
||||
async function getRecentEmojis(limit: number = 32) {
|
||||
async function getRecentEmojis(limit = 32) {
|
||||
return channels.getRecentEmojis(limit);
|
||||
}
|
||||
|
||||
|
@ -1336,8 +1339,6 @@ async function callChannel(name: string) {
|
|||
}
|
||||
|
||||
resolve();
|
||||
|
||||
return;
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
|
|
|
@ -1,5 +1,15 @@
|
|||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
/* eslint-disable camelcase */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { LocaleMessagesType } from '../types/I18N';
|
||||
|
||||
import {
|
||||
ConversationModelCollectionType,
|
||||
MessageModelCollectionType,
|
||||
} from '../model-types.d';
|
||||
import { MessageModel } from '../models/messages';
|
||||
import { ConversationModel } from '../models/conversations';
|
||||
|
||||
export type AttachmentDownloadJobType = any;
|
||||
export type ConverationMetricsType = any;
|
||||
export type ConversationType = any;
|
||||
|
@ -17,13 +27,6 @@ export type StickerPackType = any;
|
|||
export type StickerType = any;
|
||||
export type UnprocessedType = any;
|
||||
|
||||
import {
|
||||
ConversationModelCollectionType,
|
||||
MessageModelCollectionType,
|
||||
} from '../model-types.d';
|
||||
import { MessageModel } from '../models/messages';
|
||||
import { ConversationModel } from '../models/conversations';
|
||||
|
||||
export interface DataInterface {
|
||||
close: () => Promise<void>;
|
||||
removeDB: () => Promise<void>;
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
/* eslint-disable no-nested-ternary */
|
||||
/* eslint-disable camelcase */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
/* eslint-disable no-await-in-loop */
|
||||
/* eslint-disable no-restricted-syntax */
|
||||
/* eslint-disable no-console */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// tslint:disable no-console no-default-export no-unnecessary-local-variable
|
||||
|
||||
import { join } from 'path';
|
||||
|
@ -6,12 +13,6 @@ import rimraf from 'rimraf';
|
|||
import PQueue from 'p-queue';
|
||||
import sql from '@journeyapps/sqlcipher';
|
||||
import { app, clipboard, dialog } from 'electron';
|
||||
import { redactAll } from '../../js/modules/privacy';
|
||||
import { remove as removeUserConfig } from '../../app/user_config';
|
||||
import { combineNames } from '../util/combineNames';
|
||||
|
||||
import { GroupV2MemberType } from '../model-types.d';
|
||||
import { LocaleMessagesType } from '../types/I18N';
|
||||
|
||||
import pify from 'pify';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
|
@ -28,6 +29,13 @@ import {
|
|||
pick,
|
||||
} from 'lodash';
|
||||
|
||||
import { redactAll } from '../../js/modules/privacy';
|
||||
import { remove as removeUserConfig } from '../../app/user_config';
|
||||
import { combineNames } from '../util/combineNames';
|
||||
|
||||
import { GroupV2MemberType } from '../model-types.d';
|
||||
import { LocaleMessagesType } from '../types/I18N';
|
||||
|
||||
import {
|
||||
AttachmentDownloadJobType,
|
||||
ConversationType,
|
||||
|
@ -211,8 +219,6 @@ async function openDatabase(filePath: string): Promise<sql.Database> {
|
|||
}
|
||||
|
||||
resolve(instance);
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
instance = new sql.Database(filePath, callback);
|
||||
|
@ -3700,7 +3706,7 @@ async function updateEmojiUsage(
|
|||
}
|
||||
updateEmojiUsage.needsSerial = true;
|
||||
|
||||
async function getRecentEmojis(limit: number = 32) {
|
||||
async function getRecentEmojis(limit = 32) {
|
||||
const db = getInstance();
|
||||
const rows = await db.all(
|
||||
'SELECT * FROM emojis ORDER BY lastUsage DESC LIMIT $limit;',
|
||||
|
|
2
ts/textsecure.d.ts
vendored
2
ts/textsecure.d.ts
vendored
|
@ -808,7 +808,7 @@ declare class ProvisioningUuidClass {
|
|||
uuid?: string;
|
||||
}
|
||||
|
||||
declare class ProvisionEnvelopeClass {
|
||||
export declare class ProvisionEnvelopeClass {
|
||||
static decode: (
|
||||
data: ArrayBuffer | ByteBufferClass,
|
||||
encoding?: string
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
// tslint:disable no-default-export no-unnecessary-local-variable
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
/* eslint-disable more/no-then */
|
||||
/* eslint-disable class-methods-use-this */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import PQueue from 'p-queue';
|
||||
|
||||
import EventTarget from './EventTarget';
|
||||
import { WebAPIType } from './WebAPI';
|
||||
import MessageReceiver from './MessageReceiver';
|
||||
import { KeyPairType, SignedPreKeyType } from '../libsignal.d';
|
||||
import utils from './Helpers';
|
||||
import PQueue from 'p-queue';
|
||||
import ProvisioningCipher from './ProvisioningCipher';
|
||||
import WebSocketResource, {
|
||||
IncomingWebSocketRequest,
|
||||
|
@ -42,7 +46,9 @@ type GeneratedKeysType = {
|
|||
|
||||
export default class AccountManager extends EventTarget {
|
||||
server: WebAPIType;
|
||||
|
||||
pending: Promise<void>;
|
||||
|
||||
pendingQueue?: PQueue;
|
||||
|
||||
constructor(username: string, password: string) {
|
||||
|
@ -55,9 +61,11 @@ export default class AccountManager extends EventTarget {
|
|||
async requestVoiceVerification(number: string) {
|
||||
return this.server.requestVerificationVoice(number);
|
||||
}
|
||||
|
||||
async requestSMSVerification(number: string) {
|
||||
return this.server.requestVerificationSMS(number);
|
||||
}
|
||||
|
||||
async encryptDeviceName(name: string, providedIdentityKey?: KeyPairType) {
|
||||
if (!name) {
|
||||
return null;
|
||||
|
@ -81,6 +89,7 @@ export default class AccountManager extends EventTarget {
|
|||
const arrayBuffer = proto.encode().toArrayBuffer();
|
||||
return MessageReceiver.arrayBufferToStringBase64(arrayBuffer);
|
||||
}
|
||||
|
||||
async decryptDeviceName(base64: string) {
|
||||
const identityKey = await window.textsecure.storage.protocol.getIdentityKeyPair();
|
||||
|
||||
|
@ -99,6 +108,7 @@ export default class AccountManager extends EventTarget {
|
|||
|
||||
return name;
|
||||
}
|
||||
|
||||
async maybeUpdateDeviceName() {
|
||||
const isNameEncrypted = window.textsecure.storage.user.getDeviceNameEncrypted();
|
||||
if (isNameEncrypted) {
|
||||
|
@ -111,15 +121,18 @@ export default class AccountManager extends EventTarget {
|
|||
await this.server.updateDeviceName(base64);
|
||||
}
|
||||
}
|
||||
|
||||
async deviceNameIsEncrypted() {
|
||||
await window.textsecure.storage.user.setDeviceNameEncrypted();
|
||||
}
|
||||
|
||||
async maybeDeleteSignalingKey() {
|
||||
const key = window.textsecure.storage.user.getSignalingKey();
|
||||
if (key) {
|
||||
await this.server.removeSignalingKey();
|
||||
}
|
||||
}
|
||||
|
||||
async registerSingleDevice(number: string, verificationCode: string) {
|
||||
const registerKeys = this.server.registerKeys.bind(this.server);
|
||||
const createAccount = this.createAccount.bind(this);
|
||||
|
@ -275,6 +288,7 @@ export default class AccountManager extends EventTarget {
|
|||
})
|
||||
);
|
||||
}
|
||||
|
||||
async refreshPreKeys() {
|
||||
const generateKeys = this.generateKeys.bind(this, 100);
|
||||
const registerKeys = this.server.registerKeys.bind(this.server);
|
||||
|
@ -289,6 +303,7 @@ export default class AccountManager extends EventTarget {
|
|||
})
|
||||
);
|
||||
}
|
||||
|
||||
async rotateSignedPreKey() {
|
||||
return this.queueTask(async () => {
|
||||
const signedKeyId = window.textsecure.storage.get('signedKeyId', 1);
|
||||
|
@ -314,6 +329,7 @@ export default class AccountManager extends EventTarget {
|
|||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line consistent-return
|
||||
return store
|
||||
.getIdentityKeyPair()
|
||||
.then(
|
||||
|
@ -382,12 +398,14 @@ export default class AccountManager extends EventTarget {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
async queueTask(task: () => Promise<any>) {
|
||||
this.pendingQueue = this.pendingQueue || new PQueue({ concurrency: 1 });
|
||||
const taskWithTimeout = window.textsecure.createTaskWithTimeout(task);
|
||||
|
||||
return this.pendingQueue.add(taskWithTimeout);
|
||||
}
|
||||
|
||||
async cleanSignedPreKeys() {
|
||||
const MINIMUM_KEYS = 3;
|
||||
const store = window.textsecure.storage.protocol;
|
||||
|
@ -600,6 +618,7 @@ export default class AccountManager extends EventTarget {
|
|||
await window.textsecure.storage.put('regionCode', regionCode);
|
||||
await window.textsecure.storage.protocol.hydrateCaches();
|
||||
}
|
||||
|
||||
async clearSessionsAndPreKeys() {
|
||||
const store = window.textsecure.storage.protocol;
|
||||
|
||||
|
@ -628,6 +647,7 @@ export default class AccountManager extends EventTarget {
|
|||
window.log.info('confirmKeys: confirming key', key.keyId);
|
||||
await store.storeSignedPreKey(key.keyId, key.keyPair, confirmed);
|
||||
}
|
||||
|
||||
async generateKeys(count: number, providedProgressCallback?: Function) {
|
||||
const progressCallback =
|
||||
typeof providedProgressCallback === 'function'
|
||||
|
@ -695,6 +715,7 @@ export default class AccountManager extends EventTarget {
|
|||
);
|
||||
});
|
||||
}
|
||||
|
||||
async registrationDone({ uuid, number }: { uuid?: string; number?: string }) {
|
||||
window.log.info('registration done');
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable max-classes-per-file */
|
||||
|
||||
import { ByteBufferClass } from '../window.d';
|
||||
import { AttachmentType } from './SendMessage';
|
||||
|
||||
|
@ -18,6 +21,7 @@ export type PackedAttachmentType = AttachmentType & {
|
|||
|
||||
export class ProtoParser {
|
||||
buffer: ByteBufferClass;
|
||||
|
||||
protobuf: ProtobufConstructorType;
|
||||
|
||||
constructor(arrayBuffer: ArrayBuffer, protobuf: ProtobufConstructorType) {
|
||||
|
@ -28,7 +32,7 @@ export class ProtoParser {
|
|||
this.buffer.limit = arrayBuffer.byteLength;
|
||||
}
|
||||
|
||||
next() {
|
||||
next(): ProtobufType | undefined | null {
|
||||
try {
|
||||
if (this.buffer.limit === this.buffer.offset) {
|
||||
return undefined; // eof
|
||||
|
|
|
@ -1,13 +1,119 @@
|
|||
// tslint:disable no-bitwise no-default-export
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable no-bitwise */
|
||||
/* eslint-disable more/no-then */
|
||||
import { ByteBufferClass } from '../window.d';
|
||||
|
||||
declare global {
|
||||
// this is fixed in already, and won't be necessary when the new definitions
|
||||
// files are used: https://github.com/microsoft/TSJS-lib-generator/pull/843
|
||||
export interface SubtleCrypto {
|
||||
decrypt(
|
||||
algorithm:
|
||||
| string
|
||||
| RsaOaepParams
|
||||
| AesCtrParams
|
||||
| AesCbcParams
|
||||
| AesCmacParams
|
||||
| AesGcmParams
|
||||
| AesCfbParams,
|
||||
key: CryptoKey,
|
||||
data:
|
||||
| Int8Array
|
||||
| Int16Array
|
||||
| Int32Array
|
||||
| Uint8Array
|
||||
| Uint16Array
|
||||
| Uint32Array
|
||||
| Uint8ClampedArray
|
||||
| Float32Array
|
||||
| Float64Array
|
||||
| DataView
|
||||
| ArrayBuffer
|
||||
): Promise<ArrayBuffer>;
|
||||
|
||||
digest(
|
||||
algorithm: string | Algorithm,
|
||||
data:
|
||||
| Int8Array
|
||||
| Int16Array
|
||||
| Int32Array
|
||||
| Uint8Array
|
||||
| Uint16Array
|
||||
| Uint32Array
|
||||
| Uint8ClampedArray
|
||||
| Float32Array
|
||||
| Float64Array
|
||||
| DataView
|
||||
| ArrayBuffer
|
||||
): Promise<ArrayBuffer>;
|
||||
|
||||
importKey(
|
||||
format: 'raw' | 'pkcs8' | 'spki',
|
||||
keyData:
|
||||
| Int8Array
|
||||
| Int16Array
|
||||
| Int32Array
|
||||
| Uint8Array
|
||||
| Uint16Array
|
||||
| Uint32Array
|
||||
| Uint8ClampedArray
|
||||
| Float32Array
|
||||
| Float64Array
|
||||
| DataView
|
||||
| ArrayBuffer,
|
||||
algorithm:
|
||||
| string
|
||||
| RsaHashedImportParams
|
||||
| EcKeyImportParams
|
||||
| HmacImportParams
|
||||
| DhImportKeyParams
|
||||
| AesKeyAlgorithm,
|
||||
extractable: boolean,
|
||||
keyUsages: string[]
|
||||
): Promise<CryptoKey>;
|
||||
|
||||
importKey(
|
||||
format: string,
|
||||
keyData:
|
||||
| JsonWebKey
|
||||
| Int8Array
|
||||
| Int16Array
|
||||
| Int32Array
|
||||
| Uint8Array
|
||||
| Uint16Array
|
||||
| Uint32Array
|
||||
| Uint8ClampedArray
|
||||
| Float32Array
|
||||
| Float64Array
|
||||
| DataView
|
||||
| ArrayBuffer,
|
||||
algorithm:
|
||||
| string
|
||||
| RsaHashedImportParams
|
||||
| EcKeyImportParams
|
||||
| HmacImportParams
|
||||
| DhImportKeyParams
|
||||
| AesKeyAlgorithm,
|
||||
extractable: boolean,
|
||||
keyUsages: string[]
|
||||
): Promise<CryptoKey>;
|
||||
}
|
||||
}
|
||||
|
||||
const PROFILE_IV_LENGTH = 12; // bytes
|
||||
const PROFILE_KEY_LENGTH = 32; // bytes
|
||||
const PROFILE_TAG_LENGTH = 128; // bits
|
||||
const PROFILE_NAME_PADDED_LENGTH = 53; // bytes
|
||||
|
||||
function verifyDigest(data: ArrayBuffer, theirDigest: ArrayBuffer) {
|
||||
interface EncryptedAttachment {
|
||||
ciphertext: ArrayBuffer;
|
||||
digest: ArrayBuffer;
|
||||
}
|
||||
|
||||
async function verifyDigest(
|
||||
data: ArrayBuffer,
|
||||
theirDigest: ArrayBuffer
|
||||
): Promise<void> {
|
||||
return window.crypto.subtle
|
||||
.digest({ name: 'SHA-256' }, data)
|
||||
.then(ourDigest => {
|
||||
|
@ -32,7 +138,7 @@ const Crypto = {
|
|||
async decryptWebsocketMessage(
|
||||
message: ByteBufferClass,
|
||||
signalingKey: ArrayBuffer
|
||||
) {
|
||||
): Promise<ArrayBuffer> {
|
||||
const decodedMessage = message.toArrayBuffer();
|
||||
|
||||
if (signalingKey.byteLength !== 52) {
|
||||
|
@ -75,7 +181,7 @@ const Crypto = {
|
|||
encryptedBin: ArrayBuffer,
|
||||
keys: ArrayBuffer,
|
||||
theirDigest: ArrayBuffer
|
||||
) {
|
||||
): Promise<ArrayBuffer> {
|
||||
if (keys.byteLength !== 64) {
|
||||
throw new Error('Got invalid length attachment keys');
|
||||
}
|
||||
|
@ -112,7 +218,7 @@ const Crypto = {
|
|||
plaintext: ArrayBuffer,
|
||||
keys: ArrayBuffer,
|
||||
iv: ArrayBuffer
|
||||
) {
|
||||
): Promise<EncryptedAttachment> {
|
||||
if (!(plaintext instanceof ArrayBuffer) && !ArrayBuffer.isView(plaintext)) {
|
||||
throw new TypeError(
|
||||
`\`plaintext\` must be an \`ArrayBuffer\` or \`ArrayBufferView\`; got: ${typeof plaintext}`
|
||||
|
@ -152,7 +258,11 @@ const Crypto = {
|
|||
});
|
||||
});
|
||||
},
|
||||
async encryptProfile(data: ArrayBuffer, key: ArrayBuffer) {
|
||||
|
||||
async encryptProfile(
|
||||
data: ArrayBuffer,
|
||||
key: ArrayBuffer
|
||||
): Promise<ArrayBuffer> {
|
||||
const iv = window.libsignal.crypto.getRandomBytes(PROFILE_IV_LENGTH);
|
||||
if (key.byteLength !== PROFILE_KEY_LENGTH) {
|
||||
throw new Error('Got invalid length profile key');
|
||||
|
@ -179,7 +289,11 @@ const Crypto = {
|
|||
})
|
||||
);
|
||||
},
|
||||
async decryptProfile(data: ArrayBuffer, key: ArrayBuffer) {
|
||||
|
||||
async decryptProfile(
|
||||
data: ArrayBuffer,
|
||||
key: ArrayBuffer
|
||||
): Promise<ArrayBuffer> {
|
||||
if (data.byteLength < 12 + 16 + 1) {
|
||||
throw new Error(`Got too short input: ${data.byteLength}`);
|
||||
}
|
||||
|
@ -201,8 +315,6 @@ const Crypto = {
|
|||
keyForEncryption,
|
||||
ciphertext
|
||||
)
|
||||
// Typescript says that there's no .catch() available here
|
||||
// @ts-ignore
|
||||
.catch((e: Error) => {
|
||||
if (e.name === 'OperationError') {
|
||||
// bad mac, basically.
|
||||
|
@ -211,15 +323,25 @@ const Crypto = {
|
|||
error.name = 'ProfileDecryptError';
|
||||
throw error;
|
||||
}
|
||||
|
||||
return (undefined as unknown) as ArrayBuffer; // uses of this function are not guarded
|
||||
})
|
||||
);
|
||||
},
|
||||
async encryptProfileName(name: ArrayBuffer, key: ArrayBuffer) {
|
||||
|
||||
async encryptProfileName(
|
||||
name: ArrayBuffer,
|
||||
key: ArrayBuffer
|
||||
): Promise<ArrayBuffer> {
|
||||
const padded = new Uint8Array(PROFILE_NAME_PADDED_LENGTH);
|
||||
padded.set(new Uint8Array(name));
|
||||
return Crypto.encryptProfile(padded.buffer as ArrayBuffer, key);
|
||||
},
|
||||
async decryptProfileName(encryptedProfileName: string, key: ArrayBuffer) {
|
||||
|
||||
async decryptProfileName(
|
||||
encryptedProfileName: string,
|
||||
key: ArrayBuffer
|
||||
): Promise<{ given: ArrayBuffer; family: ArrayBuffer | null }> {
|
||||
const data = window.dcodeIO.ByteBuffer.wrap(
|
||||
encryptedProfileName,
|
||||
'base64'
|
||||
|
@ -261,7 +383,7 @@ const Crypto = {
|
|||
});
|
||||
},
|
||||
|
||||
getRandomBytes(size: number) {
|
||||
getRandomBytes(size: number): ArrayBuffer {
|
||||
return window.libsignal.crypto.getRandomBytes(size);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// tslint:disable max-classes-per-file
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable max-classes-per-file */
|
||||
|
||||
function appendStack(newError: Error, originalError: Error) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
|
@ -7,7 +8,9 @@ function appendStack(newError: Error, originalError: Error) {
|
|||
|
||||
export class ReplayableError extends Error {
|
||||
name: string;
|
||||
|
||||
message: string;
|
||||
|
||||
functionCode?: number;
|
||||
|
||||
constructor(options: {
|
||||
|
@ -32,6 +35,7 @@ export class ReplayableError extends Error {
|
|||
|
||||
export class IncomingIdentityKeyError extends ReplayableError {
|
||||
identifier: string;
|
||||
|
||||
identityKey: ArrayBuffer;
|
||||
|
||||
// Note: Data to resend message is no longer captured
|
||||
|
@ -50,6 +54,7 @@ export class IncomingIdentityKeyError extends ReplayableError {
|
|||
|
||||
export class OutgoingIdentityKeyError extends ReplayableError {
|
||||
identifier: string;
|
||||
|
||||
identityKey: ArrayBuffer;
|
||||
|
||||
// Note: Data to resend message is no longer captured
|
||||
|
@ -73,6 +78,7 @@ export class OutgoingIdentityKeyError extends ReplayableError {
|
|||
|
||||
export class OutgoingMessageError extends ReplayableError {
|
||||
identifier: string;
|
||||
|
||||
code?: any;
|
||||
|
||||
// Note: Data to resend message is no longer captured
|
||||
|
@ -101,13 +107,13 @@ export class OutgoingMessageError extends ReplayableError {
|
|||
export class SendMessageNetworkError extends ReplayableError {
|
||||
identifier: string;
|
||||
|
||||
constructor(identifier: string, _m: any, httpError: Error) {
|
||||
constructor(identifier: string, _m: unknown, httpError: Error) {
|
||||
super({
|
||||
name: 'SendMessageNetworkError',
|
||||
message: httpError.message,
|
||||
});
|
||||
|
||||
this.identifier = identifier.split('.')[0];
|
||||
[this.identifier] = identifier.split('.');
|
||||
this.code = httpError.code;
|
||||
|
||||
appendStack(this, httpError);
|
||||
|
@ -126,7 +132,7 @@ export class SignedPreKeyRotationError extends ReplayableError {
|
|||
export class MessageError extends ReplayableError {
|
||||
code?: any;
|
||||
|
||||
constructor(_m: any, httpError: Error) {
|
||||
constructor(_m: unknown, httpError: Error) {
|
||||
super({
|
||||
name: 'MessageError',
|
||||
message: httpError.message,
|
||||
|
@ -140,10 +146,11 @@ export class MessageError extends ReplayableError {
|
|||
|
||||
export class UnregisteredUserError extends Error {
|
||||
identifier: string;
|
||||
|
||||
code?: any;
|
||||
|
||||
constructor(identifier: string, httpError: Error) {
|
||||
const message = httpError.message;
|
||||
const { message } = httpError;
|
||||
|
||||
super(message);
|
||||
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
// tslint:disable no-default-export
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable guard-for-in */
|
||||
/* eslint-disable no-restricted-syntax */
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
|
||||
/*
|
||||
* Implements EventTarget
|
||||
|
@ -8,7 +12,7 @@
|
|||
export default class EventTarget {
|
||||
listeners?: { [type: string]: Array<Function> };
|
||||
|
||||
dispatchEvent(ev: Event) {
|
||||
dispatchEvent(ev: Event): Array<unknown> {
|
||||
if (!(ev instanceof Event)) {
|
||||
throw new Error('Expects an event');
|
||||
}
|
||||
|
@ -29,7 +33,7 @@ export default class EventTarget {
|
|||
return results;
|
||||
}
|
||||
|
||||
addEventListener(eventName: string, callback: Function) {
|
||||
addEventListener(eventName: string, callback: Function): void {
|
||||
if (typeof eventName !== 'string') {
|
||||
throw new Error('First argument expects a string');
|
||||
}
|
||||
|
@ -47,7 +51,7 @@ export default class EventTarget {
|
|||
this.listeners[eventName] = listeners;
|
||||
}
|
||||
|
||||
removeEventListener(eventName: string, callback: Function) {
|
||||
removeEventListener(eventName: string, callback: Function): void {
|
||||
if (typeof eventName !== 'string') {
|
||||
throw new Error('First argument expects a string');
|
||||
}
|
||||
|
@ -69,7 +73,7 @@ export default class EventTarget {
|
|||
this.listeners[eventName] = listeners;
|
||||
}
|
||||
|
||||
extend(source: any) {
|
||||
extend(source: any): any {
|
||||
const target = this as any;
|
||||
|
||||
// tslint:disable-next-line forin no-for-in no-default-export
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
/* eslint-disable guard-for-in */
|
||||
/* eslint-disable no-restricted-syntax */
|
||||
/* eslint-disable no-proto */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// tslint:disable no-default-export
|
||||
|
||||
import { ByteBufferClass } from '../window.d';
|
||||
|
@ -7,11 +11,10 @@ const arrayBuffer = new ArrayBuffer(0);
|
|||
const uint8Array = new Uint8Array();
|
||||
|
||||
let StaticByteBufferProto: any;
|
||||
// @ts-ignore
|
||||
const StaticArrayBufferProto = arrayBuffer.__proto__;
|
||||
// @ts-ignore
|
||||
const StaticUint8ArrayProto = uint8Array.__proto__;
|
||||
const StaticArrayBufferProto = (arrayBuffer as any).__proto__;
|
||||
const StaticUint8ArrayProto = (uint8Array as any).__proto__;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
function getString(thing: any): string {
|
||||
// Note: we must make this at runtime because it's loaded in the browser context
|
||||
if (!ByteBuffer) {
|
||||
|
@ -19,8 +22,7 @@ function getString(thing: any): string {
|
|||
}
|
||||
|
||||
if (!StaticByteBufferProto) {
|
||||
// @ts-ignore
|
||||
StaticByteBufferProto = ByteBuffer.__proto__;
|
||||
StaticByteBufferProto = (ByteBuffer as any).__proto__;
|
||||
}
|
||||
|
||||
if (thing === Object(thing)) {
|
||||
|
@ -52,28 +54,30 @@ function getStringable(thing: any): boolean {
|
|||
function ensureStringed(thing: any): any {
|
||||
if (getStringable(thing)) {
|
||||
return getString(thing);
|
||||
} else if (thing instanceof Array) {
|
||||
}
|
||||
if (thing instanceof Array) {
|
||||
const res = [];
|
||||
for (let i = 0; i < thing.length; i += 1) {
|
||||
res[i] = ensureStringed(thing[i]);
|
||||
}
|
||||
|
||||
return res;
|
||||
} else if (thing === Object(thing)) {
|
||||
}
|
||||
if (thing === Object(thing)) {
|
||||
const res: any = {};
|
||||
// tslint:disable-next-line forin no-for-in no-default-export
|
||||
for (const key in thing) {
|
||||
res[key] = ensureStringed(thing[key]);
|
||||
}
|
||||
|
||||
return res;
|
||||
} else if (thing === null) {
|
||||
}
|
||||
if (thing === null) {
|
||||
return null;
|
||||
}
|
||||
throw new Error(`unsure of how to jsonify object of type ${typeof thing}`);
|
||||
}
|
||||
|
||||
function stringToArrayBuffer(string: string) {
|
||||
function stringToArrayBuffer(string: string): ArrayBuffer {
|
||||
if (typeof string !== 'string') {
|
||||
throw new TypeError("'string' must be a string");
|
||||
}
|
||||
|
@ -88,11 +92,12 @@ function stringToArrayBuffer(string: string) {
|
|||
// Number formatting utils
|
||||
const utils = {
|
||||
getString,
|
||||
isNumberSane: (number: string) =>
|
||||
isNumberSane: (number: string): boolean =>
|
||||
number[0] === '+' && /^[0-9]+$/.test(number.substring(1)),
|
||||
jsonThing: (thing: any) => JSON.stringify(ensureStringed(thing)),
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
jsonThing: (thing: unknown) => JSON.stringify(ensureStringed(thing)),
|
||||
stringToArrayBuffer,
|
||||
unencodeNumber: (number: string) => number.split('.'),
|
||||
unencodeNumber: (number: string): Array<string> => number.split('.'),
|
||||
};
|
||||
|
||||
export default utils;
|
||||
|
|
|
@ -1,19 +1,25 @@
|
|||
// tslint:disable no-bitwise no-default-export
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
/* eslint-disable no-bitwise */
|
||||
/* eslint-disable class-methods-use-this */
|
||||
/* eslint-disable more/no-then */
|
||||
/* eslint-disable camelcase */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable max-classes-per-file */
|
||||
|
||||
import { isNumber, map, omit } from 'lodash';
|
||||
import { w3cwebsocket as WebSocket } from 'websocket';
|
||||
import PQueue from 'p-queue';
|
||||
import { v4 as getGuid } from 'uuid';
|
||||
|
||||
import { SessionCipherClass, SignalProtocolAddressClass } from '../libsignal.d';
|
||||
import { BatcherType, createBatcher } from '../util/batcher';
|
||||
|
||||
import EventTarget from './EventTarget';
|
||||
import { WebAPIType } from './WebAPI';
|
||||
import { BatcherType, createBatcher } from '../util/batcher';
|
||||
import utils from './Helpers';
|
||||
import WebSocketResource, {
|
||||
IncomingWebSocketRequest,
|
||||
} from './WebsocketResources';
|
||||
import Crypto from './Crypto';
|
||||
import { SessionCipherClass, SignalProtocolAddressClass } from '../libsignal.d';
|
||||
import { ContactBuffer, GroupBuffer } from './ContactsParser';
|
||||
import { IncomingIdentityKeyError } from './Errors';
|
||||
|
||||
|
@ -30,6 +36,8 @@ import {
|
|||
VerifiedClass,
|
||||
} from '../textsecure.d';
|
||||
|
||||
import { WebSocket } from './WebSocket';
|
||||
|
||||
import { deriveGroupFields, MASTER_KEY_LENGTH } from '../groups';
|
||||
|
||||
const RETRY_TIMEOUT = 2 * 60 * 1000;
|
||||
|
@ -84,30 +92,51 @@ type CacheUpdateItemType = {
|
|||
|
||||
class MessageReceiverInner extends EventTarget {
|
||||
_onClose?: (ev: any) => Promise<void>;
|
||||
|
||||
appQueue: PQueue;
|
||||
|
||||
cacheAddBatcher: BatcherType<CacheAddItemType>;
|
||||
|
||||
cacheRemoveBatcher: BatcherType<string>;
|
||||
|
||||
cacheUpdateBatcher: BatcherType<CacheUpdateItemType>;
|
||||
|
||||
calledClose?: boolean;
|
||||
|
||||
count: number;
|
||||
|
||||
deviceId: number;
|
||||
|
||||
hasConnected?: boolean;
|
||||
|
||||
incomingQueue: PQueue;
|
||||
|
||||
isEmptied?: boolean;
|
||||
// tslint:disable-next-line variable-name
|
||||
|
||||
number_id: string | null;
|
||||
|
||||
password: string;
|
||||
|
||||
pendingQueue: PQueue;
|
||||
|
||||
retryCachedTimeout: any;
|
||||
|
||||
server: WebAPIType;
|
||||
|
||||
serverTrustRoot: ArrayBuffer;
|
||||
|
||||
signalingKey: ArrayBuffer;
|
||||
|
||||
socket?: WebSocket;
|
||||
|
||||
stoppingProcessing?: boolean;
|
||||
|
||||
username: string;
|
||||
|
||||
uuid: string;
|
||||
// tslint:disable-next-line variable-name
|
||||
|
||||
uuid_id: string | null;
|
||||
|
||||
wsr?: WebSocketResource;
|
||||
|
||||
constructor(
|
||||
|
@ -175,10 +204,13 @@ class MessageReceiverInner extends EventTarget {
|
|||
|
||||
static stringToArrayBuffer = (string: string): ArrayBuffer =>
|
||||
window.dcodeIO.ByteBuffer.wrap(string, 'binary').toArrayBuffer();
|
||||
|
||||
static arrayBufferToString = (arrayBuffer: ArrayBuffer): string =>
|
||||
window.dcodeIO.ByteBuffer.wrap(arrayBuffer).toString('binary');
|
||||
|
||||
static stringToArrayBufferBase64 = (string: string): ArrayBuffer =>
|
||||
window.dcodeIO.ByteBuffer.wrap(string, 'base64').toArrayBuffer();
|
||||
|
||||
static arrayBufferToStringBase64 = (arrayBuffer: ArrayBuffer): string =>
|
||||
window.dcodeIO.ByteBuffer.wrap(arrayBuffer).toString('base64');
|
||||
|
||||
|
@ -237,12 +269,9 @@ class MessageReceiverInner extends EventTarget {
|
|||
|
||||
shutdown() {
|
||||
if (this.socket) {
|
||||
// @ts-ignore
|
||||
this.socket.onclose = null;
|
||||
// @ts-ignore
|
||||
this.socket.onerror = null;
|
||||
// @ts-ignore
|
||||
this.socket.onopen = null;
|
||||
delete this.socket.onclose;
|
||||
delete this.socket.onerror;
|
||||
delete this.socket.onopen;
|
||||
this.socket = undefined;
|
||||
}
|
||||
|
||||
|
@ -253,6 +282,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
this.wsr = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
async close() {
|
||||
window.log.info('MessageReceiver.close()');
|
||||
this.calledClose = true;
|
||||
|
@ -267,18 +297,22 @@ class MessageReceiverInner extends EventTarget {
|
|||
|
||||
return this.drain();
|
||||
}
|
||||
|
||||
onopen() {
|
||||
window.log.info('websocket open');
|
||||
}
|
||||
|
||||
onerror() {
|
||||
window.log.error('websocket error');
|
||||
}
|
||||
|
||||
async dispatchAndWait(event: Event) {
|
||||
// tslint:disable-next-line no-floating-promises
|
||||
this.appQueue.add(async () => Promise.all(this.dispatchEvent(event)));
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
async onclose(ev: any) {
|
||||
window.log.info(
|
||||
'websocket closed',
|
||||
|
@ -309,6 +343,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
return this.dispatchAndWait(event);
|
||||
});
|
||||
}
|
||||
|
||||
handleRequest(request: IncomingWebSocketRequest) {
|
||||
// We do the message decryption here, instead of in the ordered pending queue,
|
||||
// to avoid exposing the time it took us to process messages through the time-to-ack.
|
||||
|
@ -395,6 +430,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
// tslint:disable-next-line no-floating-promises
|
||||
this.incomingQueue.add(job);
|
||||
}
|
||||
|
||||
calculateMessageAge(
|
||||
headers: Array<string>,
|
||||
serverTimestamp?: number
|
||||
|
@ -404,6 +440,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
if (serverTimestamp) {
|
||||
// The 'X-Signal-Timestamp' is usually the last item, so start there.
|
||||
let it = headers.length;
|
||||
// eslint-disable-next-line no-plusplus
|
||||
while (--it >= 0) {
|
||||
const match = headers[it].match(/^X-Signal-Timestamp:\s*(\d+)\s*$/);
|
||||
if (match && match.length === 2) {
|
||||
|
@ -422,6 +459,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
|
||||
return messageAgeSec;
|
||||
}
|
||||
|
||||
async addToQueue(task: () => Promise<void>) {
|
||||
this.count += 1;
|
||||
|
||||
|
@ -437,9 +475,11 @@ class MessageReceiverInner extends EventTarget {
|
|||
|
||||
return promise;
|
||||
}
|
||||
|
||||
hasEmptied(): boolean {
|
||||
return Boolean(this.isEmptied);
|
||||
}
|
||||
|
||||
onEmpty() {
|
||||
const emitEmpty = () => {
|
||||
window.log.info("MessageReceiver: emitting 'empty' event");
|
||||
|
@ -478,6 +518,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
// tslint:disable-next-line no-floating-promises
|
||||
waitForCacheAddBatcher();
|
||||
}
|
||||
|
||||
async drain() {
|
||||
const waitForIncomingQueue = async () =>
|
||||
this.addToQueue(async () => {
|
||||
|
@ -486,6 +527,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
|
||||
return this.incomingQueue.add(waitForIncomingQueue);
|
||||
}
|
||||
|
||||
updateProgress(count: number) {
|
||||
// count by 10s
|
||||
if (count % 10 !== 0) {
|
||||
|
@ -495,6 +537,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
ev.count = count;
|
||||
this.dispatchEvent(ev);
|
||||
}
|
||||
|
||||
async queueAllCached() {
|
||||
const items = await this.getAllFromCache();
|
||||
const max = items.length;
|
||||
|
@ -503,6 +546,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
await this.queueCached(items[i]);
|
||||
}
|
||||
}
|
||||
|
||||
async queueCached(item: UnprocessedType) {
|
||||
try {
|
||||
let envelopePlaintext: ArrayBuffer;
|
||||
|
@ -573,6 +617,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
getEnvelopeId(envelope: EnvelopeClass) {
|
||||
if (envelope.sourceUuid || envelope.source) {
|
||||
return `${envelope.sourceUuid || envelope.source}.${
|
||||
|
@ -582,12 +627,14 @@ class MessageReceiverInner extends EventTarget {
|
|||
|
||||
return envelope.id;
|
||||
}
|
||||
|
||||
clearRetryTimeout() {
|
||||
if (this.retryCachedTimeout) {
|
||||
clearInterval(this.retryCachedTimeout);
|
||||
this.retryCachedTimeout = null;
|
||||
}
|
||||
}
|
||||
|
||||
maybeScheduleRetryTimeout() {
|
||||
if (this.isEmptied) {
|
||||
this.clearRetryTimeout();
|
||||
|
@ -597,6 +644,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
}, RETRY_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
async getAllFromCache() {
|
||||
window.log.info('getAllFromCache');
|
||||
const count = await window.textsecure.storage.unprocessed.getCount();
|
||||
|
@ -640,6 +688,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
})
|
||||
);
|
||||
}
|
||||
|
||||
async cacheAndQueueBatch(items: Array<CacheAddItemType>) {
|
||||
const dataArray = items.map(item => item.data);
|
||||
try {
|
||||
|
@ -661,6 +710,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
cacheAndQueue(
|
||||
envelope: EnvelopeClass,
|
||||
plaintext: ArrayBuffer,
|
||||
|
@ -680,9 +730,11 @@ class MessageReceiverInner extends EventTarget {
|
|||
data,
|
||||
});
|
||||
}
|
||||
|
||||
async cacheUpdateBatch(items: Array<Partial<UnprocessedType>>) {
|
||||
await window.textsecure.storage.unprocessed.addDecryptedDataToList(items);
|
||||
}
|
||||
|
||||
updateCache(envelope: EnvelopeClass, plaintext: ArrayBuffer) {
|
||||
const { id } = envelope;
|
||||
const data = {
|
||||
|
@ -694,13 +746,16 @@ class MessageReceiverInner extends EventTarget {
|
|||
};
|
||||
this.cacheUpdateBatcher.add({ id, data });
|
||||
}
|
||||
|
||||
async cacheRemoveBatch(items: Array<string>) {
|
||||
await window.textsecure.storage.unprocessed.remove(items);
|
||||
}
|
||||
|
||||
removeFromCache(envelope: EnvelopeClass) {
|
||||
const { id } = envelope;
|
||||
this.cacheRemoveBatcher.add(id);
|
||||
}
|
||||
|
||||
async queueDecryptedEnvelope(
|
||||
envelope: EnvelopeClass,
|
||||
plaintext: ArrayBuffer
|
||||
|
@ -722,6 +777,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
);
|
||||
});
|
||||
}
|
||||
|
||||
async queueEnvelope(envelope: EnvelopeClass) {
|
||||
const id = this.getEnvelopeId(envelope);
|
||||
window.log.info('queueing envelope', id);
|
||||
|
@ -747,6 +803,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Same as handleEnvelope, just without the decryption step. Necessary for handling
|
||||
// messages which were successfully decrypted, but application logic didn't finish
|
||||
// processing.
|
||||
|
@ -764,7 +821,8 @@ class MessageReceiverInner extends EventTarget {
|
|||
await this.innerHandleContentMessage(envelope, plaintext);
|
||||
|
||||
return;
|
||||
} else if (envelope.legacyMessage) {
|
||||
}
|
||||
if (envelope.legacyMessage) {
|
||||
await this.innerHandleLegacyMessage(envelope, plaintext);
|
||||
|
||||
return;
|
||||
|
@ -773,6 +831,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
this.removeFromCache(envelope);
|
||||
throw new Error('Received message with no content and no legacyMessage');
|
||||
}
|
||||
|
||||
async handleEnvelope(envelope: EnvelopeClass) {
|
||||
if (this.stoppingProcessing) {
|
||||
return Promise.resolve();
|
||||
|
@ -784,20 +843,24 @@ class MessageReceiverInner extends EventTarget {
|
|||
|
||||
if (envelope.content) {
|
||||
return this.handleContentMessage(envelope);
|
||||
} else if (envelope.legacyMessage) {
|
||||
}
|
||||
if (envelope.legacyMessage) {
|
||||
return this.handleLegacyMessage(envelope);
|
||||
}
|
||||
this.removeFromCache(envelope);
|
||||
throw new Error('Received message with no content and no legacyMessage');
|
||||
}
|
||||
|
||||
getStatus() {
|
||||
if (this.socket) {
|
||||
return this.socket.readyState;
|
||||
} else if (this.hasConnected) {
|
||||
}
|
||||
if (this.hasConnected) {
|
||||
return WebSocket.CLOSED;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
async onDeliveryReceipt(envelope: EnvelopeClass) {
|
||||
// tslint:disable-next-line promise-must-complete
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@ -812,6 +875,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
this.dispatchAndWait(ev).then(resolve as any, reject as any);
|
||||
});
|
||||
}
|
||||
|
||||
unpad(paddedData: ArrayBuffer) {
|
||||
const paddedPlaintext = new Uint8Array(paddedData);
|
||||
let plaintext;
|
||||
|
@ -837,11 +901,10 @@ class MessageReceiverInner extends EventTarget {
|
|||
): Promise<ArrayBuffer> {
|
||||
const { serverTrustRoot } = this;
|
||||
|
||||
let address: SignalProtocolAddressClass;
|
||||
let promise;
|
||||
const identifier = envelope.sourceUuid || envelope.source;
|
||||
|
||||
address = new window.libsignal.SignalProtocolAddress(
|
||||
const address = new window.libsignal.SignalProtocolAddress(
|
||||
// Using source as opposed to sourceUuid allows us to get the existing
|
||||
// session if we haven't yet harvested the incoming uuid
|
||||
identifier as any,
|
||||
|
@ -1034,6 +1097,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
return this.dispatchAndWait(ev).then(returnError, returnError);
|
||||
});
|
||||
}
|
||||
|
||||
async decryptPreKeyWhisperMessage(
|
||||
ciphertext: ArrayBuffer,
|
||||
sessionCipher: SessionCipherClass,
|
||||
|
@ -1057,6 +1121,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async handleSentMessage(
|
||||
envelope: EnvelopeClass,
|
||||
sentContainer: SyncMessageClass.Sent
|
||||
|
@ -1114,7 +1179,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
)} ignored; destined for blocked group`
|
||||
);
|
||||
this.removeFromCache(envelope);
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const ev = new Event('sent');
|
||||
|
@ -1136,6 +1201,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
})
|
||||
);
|
||||
}
|
||||
|
||||
async handleDataMessage(envelope: EnvelopeClass, msg: DataMessageClass) {
|
||||
window.log.info('data message from', this.getEnvelopeId(envelope));
|
||||
let p: Promise<any> = Promise.resolve();
|
||||
|
@ -1152,7 +1218,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
window.log.info(
|
||||
'MessageReceiver.handleDataMessage: dropping GroupV2 message'
|
||||
);
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
this.deriveGroupsV2Data(msg);
|
||||
|
@ -1204,7 +1270,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
)} ignored; destined for blocked group`
|
||||
);
|
||||
this.removeFromCache(envelope);
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const ev = new Event('message');
|
||||
|
@ -1222,6 +1288,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
})
|
||||
);
|
||||
}
|
||||
|
||||
async handleLegacyMessage(envelope: EnvelopeClass) {
|
||||
return this.decrypt(envelope, envelope.legacyMessage).then(plaintext => {
|
||||
if (!plaintext) {
|
||||
|
@ -1231,6 +1298,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
return this.innerHandleLegacyMessage(envelope, plaintext);
|
||||
});
|
||||
}
|
||||
|
||||
async innerHandleLegacyMessage(
|
||||
envelope: EnvelopeClass,
|
||||
plaintext: ArrayBuffer
|
||||
|
@ -1238,6 +1306,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
const message = window.textsecure.protobuf.DataMessage.decode(plaintext);
|
||||
return this.handleDataMessage(envelope, message);
|
||||
}
|
||||
|
||||
async handleContentMessage(envelope: EnvelopeClass) {
|
||||
return this.decrypt(envelope, envelope.content).then(plaintext => {
|
||||
if (!plaintext) {
|
||||
|
@ -1247,6 +1316,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
return this.innerHandleContentMessage(envelope, plaintext);
|
||||
});
|
||||
}
|
||||
|
||||
async innerHandleContentMessage(
|
||||
envelope: EnvelopeClass,
|
||||
plaintext: ArrayBuffer
|
||||
|
@ -1254,21 +1324,27 @@ class MessageReceiverInner extends EventTarget {
|
|||
const content = window.textsecure.protobuf.Content.decode(plaintext);
|
||||
if (content.syncMessage) {
|
||||
return this.handleSyncMessage(envelope, content.syncMessage);
|
||||
} else if (content.dataMessage) {
|
||||
}
|
||||
if (content.dataMessage) {
|
||||
return this.handleDataMessage(envelope, content.dataMessage);
|
||||
} else if (content.nullMessage) {
|
||||
}
|
||||
if (content.nullMessage) {
|
||||
this.handleNullMessage(envelope);
|
||||
return;
|
||||
} else if (content.callingMessage) {
|
||||
return undefined;
|
||||
}
|
||||
if (content.callingMessage) {
|
||||
return this.handleCallingMessage(envelope, content.callingMessage);
|
||||
} else if (content.receiptMessage) {
|
||||
}
|
||||
if (content.receiptMessage) {
|
||||
return this.handleReceiptMessage(envelope, content.receiptMessage);
|
||||
} else if (content.typingMessage) {
|
||||
}
|
||||
if (content.typingMessage) {
|
||||
return this.handleTypingMessage(envelope, content.typingMessage);
|
||||
}
|
||||
this.removeFromCache(envelope);
|
||||
throw new Error('Unsupported content message');
|
||||
}
|
||||
|
||||
async handleCallingMessage(
|
||||
envelope: EnvelopeClass,
|
||||
callingMessage: CallingMessageClass
|
||||
|
@ -1279,6 +1355,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
callingMessage
|
||||
);
|
||||
}
|
||||
|
||||
async handleReceiptMessage(
|
||||
envelope: EnvelopeClass,
|
||||
receiptMessage: ReceiptMessageClass
|
||||
|
@ -1319,6 +1396,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
}
|
||||
return Promise.all(results);
|
||||
}
|
||||
|
||||
handleTypingMessage(
|
||||
envelope: EnvelopeClass,
|
||||
typingMessage: TypingMessageClass
|
||||
|
@ -1366,6 +1444,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
|
||||
return this.dispatchEvent(ev);
|
||||
}
|
||||
|
||||
handleNullMessage(envelope: EnvelopeClass) {
|
||||
window.log.info('null message from', this.getEnvelopeId(envelope));
|
||||
this.removeFromCache(envelope);
|
||||
|
@ -1404,6 +1483,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
groupV2.groupChange = groupV2.groupChange.toString('base64');
|
||||
}
|
||||
}
|
||||
|
||||
getGroupId(message: DataMessageClass) {
|
||||
if (message.groupV2) {
|
||||
return message.groupV2.id;
|
||||
|
@ -1418,11 +1498,11 @@ class MessageReceiverInner extends EventTarget {
|
|||
getDestination(sentMessage: SyncMessageClass.Sent) {
|
||||
if (sentMessage.message && sentMessage.message.groupV2) {
|
||||
return `groupv2(${sentMessage.message.groupV2.id})`;
|
||||
} else if (sentMessage.message && sentMessage.message.group) {
|
||||
return `group(${sentMessage.message.group.id.toBinary()})`;
|
||||
} else {
|
||||
return sentMessage.destination || sentMessage.destinationUuid;
|
||||
}
|
||||
if (sentMessage.message && sentMessage.message.group) {
|
||||
return `group(${sentMessage.message.group.id.toBinary()})`;
|
||||
}
|
||||
return sentMessage.destination || sentMessage.destinationUuid;
|
||||
}
|
||||
|
||||
// tslint:disable-next-line cyclomatic-complexity
|
||||
|
@ -1450,7 +1530,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
if (!fromSelfSource && !fromSelfSourceUuid) {
|
||||
throw new Error('Received sync message from another number');
|
||||
}
|
||||
// tslint:disable-next-line triple-equals
|
||||
// eslint-disable-next-line eqeqeq
|
||||
if (envelope.sourceDevice == this.deviceId) {
|
||||
throw new Error('Received sync message from our own device');
|
||||
}
|
||||
|
@ -1468,7 +1548,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
window.log.info(
|
||||
'MessageReceiver.handleSyncMessage: dropping GroupV2 message'
|
||||
);
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
this.deriveGroupsV2Data(sentMessage.message);
|
||||
|
@ -1481,26 +1561,34 @@ class MessageReceiverInner extends EventTarget {
|
|||
this.getEnvelopeId(envelope)
|
||||
);
|
||||
return this.handleSentMessage(envelope, sentMessage);
|
||||
} else if (syncMessage.contacts) {
|
||||
}
|
||||
if (syncMessage.contacts) {
|
||||
this.handleContacts(envelope, syncMessage.contacts);
|
||||
return;
|
||||
} else if (syncMessage.groups) {
|
||||
return undefined;
|
||||
}
|
||||
if (syncMessage.groups) {
|
||||
this.handleGroups(envelope, syncMessage.groups);
|
||||
return;
|
||||
} else if (syncMessage.blocked) {
|
||||
return undefined;
|
||||
}
|
||||
if (syncMessage.blocked) {
|
||||
return this.handleBlocked(envelope, syncMessage.blocked);
|
||||
} else if (syncMessage.request) {
|
||||
}
|
||||
if (syncMessage.request) {
|
||||
window.log.info('Got SyncMessage Request');
|
||||
this.removeFromCache(envelope);
|
||||
return;
|
||||
} else if (syncMessage.read && syncMessage.read.length) {
|
||||
return undefined;
|
||||
}
|
||||
if (syncMessage.read && syncMessage.read.length) {
|
||||
window.log.info('read messages from', this.getEnvelopeId(envelope));
|
||||
return this.handleRead(envelope, syncMessage.read);
|
||||
} else if (syncMessage.verified) {
|
||||
}
|
||||
if (syncMessage.verified) {
|
||||
return this.handleVerified(envelope, syncMessage.verified);
|
||||
} else if (syncMessage.configuration) {
|
||||
}
|
||||
if (syncMessage.configuration) {
|
||||
return this.handleConfiguration(envelope, syncMessage.configuration);
|
||||
} else if (
|
||||
}
|
||||
if (
|
||||
syncMessage.stickerPackOperation &&
|
||||
syncMessage.stickerPackOperation.length > 0
|
||||
) {
|
||||
|
@ -1508,22 +1596,27 @@ class MessageReceiverInner extends EventTarget {
|
|||
envelope,
|
||||
syncMessage.stickerPackOperation
|
||||
);
|
||||
} else if (syncMessage.viewOnceOpen) {
|
||||
}
|
||||
if (syncMessage.viewOnceOpen) {
|
||||
return this.handleViewOnceOpen(envelope, syncMessage.viewOnceOpen);
|
||||
} else if (syncMessage.messageRequestResponse) {
|
||||
}
|
||||
if (syncMessage.messageRequestResponse) {
|
||||
return this.handleMessageRequestResponse(
|
||||
envelope,
|
||||
syncMessage.messageRequestResponse
|
||||
);
|
||||
} else if (syncMessage.fetchLatest) {
|
||||
}
|
||||
if (syncMessage.fetchLatest) {
|
||||
return this.handleFetchLatest(envelope, syncMessage.fetchLatest);
|
||||
} else if (syncMessage.keys) {
|
||||
}
|
||||
if (syncMessage.keys) {
|
||||
return this.handleKeys(envelope, syncMessage.keys);
|
||||
}
|
||||
|
||||
this.removeFromCache(envelope);
|
||||
throw new Error('Got empty SyncMessage');
|
||||
}
|
||||
|
||||
async handleConfiguration(
|
||||
envelope: EnvelopeClass,
|
||||
configuration: SyncMessageClass.Configuration
|
||||
|
@ -1534,6 +1627,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
ev.configuration = configuration;
|
||||
return this.dispatchAndWait(ev);
|
||||
}
|
||||
|
||||
async handleViewOnceOpen(
|
||||
envelope: EnvelopeClass,
|
||||
sync: SyncMessageClass.ViewOnceOpen
|
||||
|
@ -1554,6 +1648,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
|
||||
return this.dispatchAndWait(ev);
|
||||
}
|
||||
|
||||
async handleMessageRequestResponse(
|
||||
envelope: EnvelopeClass,
|
||||
sync: SyncMessageClass.MessageRequestResponse
|
||||
|
@ -1573,6 +1668,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
'MessageReceiver::handleMessageRequestResponse'
|
||||
);
|
||||
}
|
||||
|
||||
async handleFetchLatest(
|
||||
envelope: EnvelopeClass,
|
||||
sync: SyncMessageClass.FetchLatest
|
||||
|
@ -1585,11 +1681,12 @@ class MessageReceiverInner extends EventTarget {
|
|||
|
||||
return this.dispatchAndWait(ev);
|
||||
}
|
||||
|
||||
async handleKeys(envelope: EnvelopeClass, sync: SyncMessageClass.Keys) {
|
||||
window.log.info('got keys sync message');
|
||||
|
||||
if (!sync.storageService) {
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const ev = new Event('keys');
|
||||
|
@ -1598,6 +1695,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
|
||||
return this.dispatchAndWait(ev);
|
||||
}
|
||||
|
||||
async handleStickerPackOperation(
|
||||
envelope: EnvelopeClass,
|
||||
operations: Array<SyncMessageClass.StickerPackOperation>
|
||||
|
@ -1615,6 +1713,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
}));
|
||||
return this.dispatchAndWait(ev);
|
||||
}
|
||||
|
||||
async handleVerified(envelope: EnvelopeClass, verified: VerifiedClass) {
|
||||
const ev = new Event('verified');
|
||||
ev.confirm = this.removeFromCache.bind(this, envelope);
|
||||
|
@ -1631,6 +1730,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
);
|
||||
return this.dispatchAndWait(ev);
|
||||
}
|
||||
|
||||
async handleRead(
|
||||
envelope: EnvelopeClass,
|
||||
read: Array<SyncMessageClass.Read>
|
||||
|
@ -1655,6 +1755,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
}
|
||||
return Promise.all(results);
|
||||
}
|
||||
|
||||
handleContacts(envelope: EnvelopeClass, contacts: SyncMessageClass.Contacts) {
|
||||
window.log.info('contact sync');
|
||||
const { blob } = contacts;
|
||||
|
@ -1692,6 +1793,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
handleGroups(envelope: EnvelopeClass, groups: SyncMessageClass.Groups) {
|
||||
window.log.info('group sync');
|
||||
const { blob } = groups;
|
||||
|
@ -1726,6 +1828,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
async handleBlocked(
|
||||
envelope: EnvelopeClass,
|
||||
blocked: SyncMessageClass.Blocked
|
||||
|
@ -1750,19 +1853,22 @@ class MessageReceiverInner extends EventTarget {
|
|||
await window.textsecure.storage.put('blocked-groups', groupIds);
|
||||
|
||||
this.removeFromCache(envelope);
|
||||
return;
|
||||
}
|
||||
|
||||
isBlocked(number: string) {
|
||||
return window.textsecure.storage.get('blocked', []).includes(number);
|
||||
}
|
||||
|
||||
isUuidBlocked(uuid: string) {
|
||||
return window.textsecure.storage.get('blocked-uuids', []).includes(uuid);
|
||||
}
|
||||
|
||||
isGroupBlocked(groupId: string) {
|
||||
return window.textsecure.storage
|
||||
.get('blocked-groups', [])
|
||||
.includes(groupId);
|
||||
}
|
||||
|
||||
cleanAttachment(attachment: AttachmentPointerClass) {
|
||||
return {
|
||||
...omit(attachment, 'thumbnail'),
|
||||
|
@ -1771,6 +1877,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
digest: attachment.digest ? attachment.digest.toString('base64') : null,
|
||||
};
|
||||
}
|
||||
|
||||
private isLinkPreviewDateValid(value: unknown): value is number {
|
||||
return (
|
||||
typeof value === 'number' &&
|
||||
|
@ -1779,6 +1886,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
value > 0
|
||||
);
|
||||
}
|
||||
|
||||
private cleanLinkPreviewDate(value: unknown): number | null {
|
||||
if (this.isLinkPreviewDateValid(value)) {
|
||||
return value;
|
||||
|
@ -1794,6 +1902,7 @@ class MessageReceiverInner extends EventTarget {
|
|||
}
|
||||
return this.isLinkPreviewDateValid(result) ? result : null;
|
||||
}
|
||||
|
||||
async downloadAttachment(
|
||||
attachment: AttachmentPointerClass
|
||||
): Promise<DownloadAttachmentType> {
|
||||
|
@ -1826,12 +1935,14 @@ class MessageReceiverInner extends EventTarget {
|
|||
data,
|
||||
};
|
||||
}
|
||||
|
||||
async handleAttachment(
|
||||
attachment: AttachmentPointerClass
|
||||
): Promise<DownloadAttachmentType> {
|
||||
const cleaned = this.cleanAttachment(attachment);
|
||||
return this.downloadAttachment(cleaned);
|
||||
}
|
||||
|
||||
async handleEndSession(identifier: string) {
|
||||
window.log.info('got end session');
|
||||
const deviceIds = await window.textsecure.storage.protocol.getDeviceIds(
|
||||
|
@ -1890,7 +2001,8 @@ class MessageReceiverInner extends EventTarget {
|
|||
decrypted.attachments = [];
|
||||
decrypted.group = null;
|
||||
return Promise.resolve(decrypted);
|
||||
} else if (decrypted.flags & FLAGS.EXPIRATION_TIMER_UPDATE) {
|
||||
}
|
||||
if (decrypted.flags & FLAGS.EXPIRATION_TIMER_UPDATE) {
|
||||
decrypted.body = null;
|
||||
decrypted.attachments = [];
|
||||
} else if (decrypted.flags & FLAGS.PROFILE_KEY_UPDATE) {
|
||||
|
@ -2053,20 +2165,30 @@ export default class MessageReceiver {
|
|||
}
|
||||
|
||||
addEventListener: (name: string, handler: Function) => void;
|
||||
|
||||
close: () => Promise<void>;
|
||||
|
||||
downloadAttachment: (
|
||||
attachment: AttachmentPointerClass
|
||||
) => Promise<DownloadAttachmentType>;
|
||||
|
||||
getStatus: () => number;
|
||||
|
||||
hasEmptied: () => boolean;
|
||||
|
||||
removeEventListener: (name: string, handler: Function) => void;
|
||||
|
||||
stopProcessing: () => Promise<void>;
|
||||
|
||||
unregisterBatchers: () => void;
|
||||
|
||||
static stringToArrayBuffer = MessageReceiverInner.stringToArrayBuffer;
|
||||
|
||||
static arrayBufferToString = MessageReceiverInner.arrayBufferToString;
|
||||
|
||||
static stringToArrayBufferBase64 =
|
||||
MessageReceiverInner.stringToArrayBufferBase64;
|
||||
|
||||
static arrayBufferToStringBase64 =
|
||||
MessageReceiverInner.arrayBufferToStringBase64;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
// tslint:disable no-default-export
|
||||
/* eslint-disable guard-for-in */
|
||||
/* eslint-disable no-restricted-syntax */
|
||||
/* eslint-disable class-methods-use-this */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable more/no-then */
|
||||
/* eslint-disable no-param-reassign */
|
||||
|
||||
import { reject } from 'lodash';
|
||||
import { ServerKeysType, WebAPIType } from './WebAPI';
|
||||
|
@ -24,25 +29,38 @@ type OutgoingMessageOptionsType = SendOptionsType & {
|
|||
|
||||
export default class OutgoingMessage {
|
||||
server: WebAPIType;
|
||||
|
||||
timestamp: number;
|
||||
|
||||
identifiers: Array<string>;
|
||||
|
||||
message: ContentClass;
|
||||
|
||||
callback: (result: CallbackResultType) => void;
|
||||
|
||||
silent?: boolean;
|
||||
|
||||
plaintext?: Uint8Array;
|
||||
|
||||
identifiersCompleted: number;
|
||||
errors: Array<any>;
|
||||
successfulIdentifiers: Array<any>;
|
||||
failoverIdentifiers: Array<any>;
|
||||
unidentifiedDeliveries: Array<any>;
|
||||
|
||||
errors: Array<unknown>;
|
||||
|
||||
successfulIdentifiers: Array<unknown>;
|
||||
|
||||
failoverIdentifiers: Array<unknown>;
|
||||
|
||||
unidentifiedDeliveries: Array<unknown>;
|
||||
|
||||
discoveredIdentifierPairs: Array<{
|
||||
e164: string;
|
||||
uuid: string;
|
||||
}>;
|
||||
|
||||
sendMetadata?: SendMetadataType;
|
||||
|
||||
senderCertificate?: ArrayBuffer;
|
||||
|
||||
online?: boolean;
|
||||
|
||||
constructor(
|
||||
|
@ -76,12 +94,13 @@ export default class OutgoingMessage {
|
|||
this.unidentifiedDeliveries = [];
|
||||
this.discoveredIdentifierPairs = [];
|
||||
|
||||
const { sendMetadata, senderCertificate, online } = options || ({} as any);
|
||||
const { sendMetadata, senderCertificate, online } = options;
|
||||
this.sendMetadata = sendMetadata;
|
||||
this.senderCertificate = senderCertificate;
|
||||
this.online = online;
|
||||
}
|
||||
numberCompleted() {
|
||||
|
||||
numberCompleted(): void {
|
||||
this.identifiersCompleted += 1;
|
||||
if (this.identifiersCompleted >= this.identifiers.length) {
|
||||
this.callback({
|
||||
|
@ -93,9 +112,9 @@ export default class OutgoingMessage {
|
|||
});
|
||||
}
|
||||
}
|
||||
registerError(identifier: string, reason: string, error?: Error) {
|
||||
|
||||
registerError(identifier: string, reason: string, error?: Error): void {
|
||||
if (!error || (error.name === 'HTTPError' && error.code !== 404)) {
|
||||
// tslint:disable-next-line no-parameter-reassignment
|
||||
error = new OutgoingMessageError(
|
||||
identifier,
|
||||
this.message.toArrayBuffer(),
|
||||
|
@ -104,11 +123,11 @@ export default class OutgoingMessage {
|
|||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
error.reason = reason;
|
||||
this.errors[this.errors.length] = error;
|
||||
this.numberCompleted();
|
||||
}
|
||||
|
||||
reloadDevicesAndSend(
|
||||
identifier: string,
|
||||
recurse?: boolean
|
||||
|
@ -123,14 +142,17 @@ export default class OutgoingMessage {
|
|||
'Got empty device list when loading device keys',
|
||||
undefined
|
||||
);
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
return this.doSendMessage(identifier, deviceIds, recurse);
|
||||
});
|
||||
}
|
||||
|
||||
// tslint:disable-next-line max-func-body-length
|
||||
async getKeysForIdentifier(identifier: string, updateDevices: Array<number>) {
|
||||
async getKeysForIdentifier(
|
||||
identifier: string,
|
||||
updateDevices: Array<number>
|
||||
): Promise<void | Array<void | null>> {
|
||||
const handleResult = async (response: ServerKeysType) =>
|
||||
Promise.all(
|
||||
response.devices.map(async device => {
|
||||
|
@ -194,7 +216,7 @@ export default class OutgoingMessage {
|
|||
return this.server.getKeysForIdentifier(identifier).then(handleResult);
|
||||
}
|
||||
|
||||
let promise: Promise<any> = Promise.resolve();
|
||||
let promise: Promise<void | Array<void | null>> = Promise.resolve();
|
||||
updateDevices.forEach(deviceId => {
|
||||
promise = promise.then(async () => {
|
||||
let innerPromise;
|
||||
|
@ -238,10 +260,10 @@ export default class OutgoingMessage {
|
|||
|
||||
async transmitMessage(
|
||||
identifier: string,
|
||||
jsonData: Array<any>,
|
||||
jsonData: Array<unknown>,
|
||||
timestamp: number,
|
||||
{ accessKey }: { accessKey?: string } = {}
|
||||
) {
|
||||
): Promise<void> {
|
||||
let promise;
|
||||
|
||||
if (accessKey) {
|
||||
|
@ -277,7 +299,7 @@ export default class OutgoingMessage {
|
|||
});
|
||||
}
|
||||
|
||||
getPaddedMessageLength(messageLength: number) {
|
||||
getPaddedMessageLength(messageLength: number): number {
|
||||
const messageLengthWithTerminator = messageLength + 1;
|
||||
let messagePartCount = Math.floor(messageLengthWithTerminator / 160);
|
||||
|
||||
|
@ -288,7 +310,7 @@ export default class OutgoingMessage {
|
|||
return messagePartCount * 160;
|
||||
}
|
||||
|
||||
getPlaintext() {
|
||||
getPlaintext(): ArrayBuffer {
|
||||
if (!this.plaintext) {
|
||||
const messageBuffer = this.message.toArrayBuffer();
|
||||
this.plaintext = new Uint8Array(
|
||||
|
@ -380,22 +402,21 @@ export default class OutgoingMessage {
|
|||
),
|
||||
content: window.Signal.Crypto.arrayBufferToBase64(ciphertext),
|
||||
};
|
||||
} else {
|
||||
const sessionCipher = new window.libsignal.SessionCipher(
|
||||
window.textsecure.storage.protocol,
|
||||
address,
|
||||
options
|
||||
);
|
||||
ciphers[address.getDeviceId()] = sessionCipher;
|
||||
|
||||
const ciphertext = await sessionCipher.encrypt(plaintext);
|
||||
return {
|
||||
type: ciphertext.type,
|
||||
destinationDeviceId: address.getDeviceId(),
|
||||
destinationRegistrationId: ciphertext.registrationId,
|
||||
content: btoa(ciphertext.body),
|
||||
};
|
||||
}
|
||||
const sessionCipher = new window.libsignal.SessionCipher(
|
||||
window.textsecure.storage.protocol,
|
||||
address,
|
||||
options
|
||||
);
|
||||
ciphers[address.getDeviceId()] = sessionCipher;
|
||||
|
||||
const ciphertext = await sessionCipher.encrypt(plaintext);
|
||||
return {
|
||||
type: ciphertext.type,
|
||||
destinationDeviceId: address.getDeviceId(),
|
||||
destinationRegistrationId: ciphertext.registrationId,
|
||||
content: btoa(ciphertext.body),
|
||||
};
|
||||
})
|
||||
)
|
||||
.then(async jsonData => {
|
||||
|
@ -446,7 +467,7 @@ export default class OutgoingMessage {
|
|||
'Hit retry limit attempting to reload device list',
|
||||
error
|
||||
);
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let p: Promise<any> = Promise.resolve();
|
||||
|
@ -479,7 +500,8 @@ export default class OutgoingMessage {
|
|||
this.reloadDevicesAndSend(identifier, error.code === 409)
|
||||
);
|
||||
});
|
||||
} else if (error.message === 'Identity key changed') {
|
||||
}
|
||||
if (error.message === 'Identity key changed') {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
error.timestamp = this.timestamp;
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
|
@ -526,10 +548,14 @@ export default class OutgoingMessage {
|
|||
'Failed to create or send message',
|
||||
error
|
||||
);
|
||||
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
async getStaleDeviceIdsForIdentifier(identifier: string) {
|
||||
async getStaleDeviceIdsForIdentifier(
|
||||
identifier: string
|
||||
): Promise<Array<number>> {
|
||||
return window.textsecure.storage.protocol
|
||||
.getDeviceIds(identifier)
|
||||
.then(async deviceIds => {
|
||||
|
@ -560,9 +586,8 @@ export default class OutgoingMessage {
|
|||
async removeDeviceIdsForIdentifier(
|
||||
identifier: string,
|
||||
deviceIdsToRemove: Array<number>
|
||||
) {
|
||||
): Promise<void> {
|
||||
let promise = Promise.resolve();
|
||||
// tslint:disable-next-line forin no-for-in no-for-in-array
|
||||
for (const j in deviceIdsToRemove) {
|
||||
promise = promise.then(async () => {
|
||||
const encodedAddress = `${identifier}.${deviceIdsToRemove[j]}`;
|
||||
|
@ -572,7 +597,7 @@ export default class OutgoingMessage {
|
|||
return promise;
|
||||
}
|
||||
|
||||
async sendToIdentifier(providedIdentifier: string) {
|
||||
async sendToIdentifier(providedIdentifier: string): Promise<void> {
|
||||
let identifier = providedIdentifier;
|
||||
try {
|
||||
if (isRemoteFlagEnabled('desktop.cds')) {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// tslint:disable no-default-export
|
||||
/* eslint-disable more/no-then */
|
||||
/* eslint-disable max-classes-per-file */
|
||||
|
||||
import { KeyPairType } from '../libsignal.d';
|
||||
import { ProvisionEnvelopeClass } from '../textsecure.d';
|
||||
|
@ -73,6 +74,7 @@ class ProvisioningCipherInner {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
async getPublicKey(): Promise<ArrayBuffer> {
|
||||
return Promise.resolve()
|
||||
.then(async () => {
|
||||
|
@ -107,5 +109,6 @@ export default class ProvisioningCipher {
|
|||
decrypt: (
|
||||
provisionEnvelope: ProvisionEnvelopeClass
|
||||
) => Promise<ProvisionDecryptResult>;
|
||||
|
||||
getPublicKey: () => Promise<ArrayBuffer>;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
// tslint:disable no-bitwise no-default-export
|
||||
/* eslint-disable no-nested-ternary */
|
||||
/* eslint-disable class-methods-use-this */
|
||||
/* eslint-disable more/no-then */
|
||||
/* eslint-disable no-bitwise */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable max-classes-per-file */
|
||||
|
||||
import { Dictionary, without } from 'lodash';
|
||||
import PQueue from 'p-queue';
|
||||
|
@ -28,6 +33,7 @@ import {
|
|||
GroupClass,
|
||||
StorageServiceCallOptionsType,
|
||||
StorageServiceCredentials,
|
||||
SyncMessageClass,
|
||||
} from '../textsecure.d';
|
||||
import { MessageError, SignedPreKeyRotationError } from './Errors';
|
||||
import { BodyRangesType } from '../types/Util';
|
||||
|
@ -112,24 +118,38 @@ type MessageOptionsType = {
|
|||
|
||||
class Message {
|
||||
attachments: Array<any>;
|
||||
|
||||
body?: string;
|
||||
|
||||
expireTimer?: number;
|
||||
|
||||
flags?: number;
|
||||
|
||||
group?: {
|
||||
id: string;
|
||||
type: number;
|
||||
};
|
||||
|
||||
groupV2?: GroupV2InfoType;
|
||||
|
||||
needsSync?: boolean;
|
||||
|
||||
preview: any;
|
||||
|
||||
profileKey?: ArrayBuffer;
|
||||
|
||||
quote?: any;
|
||||
|
||||
recipients: Array<string>;
|
||||
|
||||
sticker?: any;
|
||||
|
||||
reaction?: any;
|
||||
|
||||
timestamp: number;
|
||||
|
||||
dataMessage: any;
|
||||
|
||||
attachmentPointers?: Array<any>;
|
||||
|
||||
// tslint:disable cyclomatic-complexity
|
||||
|
@ -332,6 +352,7 @@ export type AttachmentType = {
|
|||
|
||||
export default class MessageSender {
|
||||
server: WebAPIType;
|
||||
|
||||
pendingMessages: {
|
||||
[id: string]: PQueue;
|
||||
};
|
||||
|
@ -341,14 +362,14 @@ export default class MessageSender {
|
|||
this.pendingMessages = {};
|
||||
}
|
||||
|
||||
_getAttachmentSizeBucket(size: number) {
|
||||
_getAttachmentSizeBucket(size: number): number {
|
||||
return Math.max(
|
||||
541,
|
||||
Math.floor(1.05 ** Math.ceil(Math.log(size) / Math.log(1.05)))
|
||||
);
|
||||
}
|
||||
|
||||
getPaddedAttachment(data: ArrayBuffer) {
|
||||
getPaddedAttachment(data: ArrayBuffer): ArrayBuffer {
|
||||
const size = data.byteLength;
|
||||
const paddedSize = this._getAttachmentSizeBucket(size);
|
||||
const padding = getZeroes(paddedSize - size);
|
||||
|
@ -356,7 +377,9 @@ export default class MessageSender {
|
|||
return concatenateBytes(data, padding);
|
||||
}
|
||||
|
||||
async makeAttachmentPointer(attachment: AttachmentType) {
|
||||
async makeAttachmentPointer(
|
||||
attachment: AttachmentType
|
||||
): Promise<AttachmentPointerClass | undefined> {
|
||||
if (typeof attachment !== 'object' || attachment == null) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
@ -409,7 +432,10 @@ export default class MessageSender {
|
|||
return proto;
|
||||
}
|
||||
|
||||
async queueJobForIdentifier(identifier: string, runJob: () => Promise<any>) {
|
||||
async queueJobForIdentifier(
|
||||
identifier: string,
|
||||
runJob: () => Promise<any>
|
||||
): Promise<void> {
|
||||
const { id } = await window.ConversationController.getOrCreateAndWait(
|
||||
identifier,
|
||||
'private'
|
||||
|
@ -427,7 +453,7 @@ export default class MessageSender {
|
|||
return queue.add(taskWithTimeout);
|
||||
}
|
||||
|
||||
async uploadAttachments(message: Message) {
|
||||
async uploadAttachments(message: Message): Promise<void> {
|
||||
return Promise.all(
|
||||
message.attachments.map(this.makeAttachmentPointer.bind(this))
|
||||
)
|
||||
|
@ -444,7 +470,7 @@ export default class MessageSender {
|
|||
});
|
||||
}
|
||||
|
||||
async uploadLinkPreviews(message: Message) {
|
||||
async uploadLinkPreviews(message: Message): Promise<void> {
|
||||
try {
|
||||
const preview = await Promise.all(
|
||||
(message.preview || []).map(async (item: PreviewType) => ({
|
||||
|
@ -463,7 +489,7 @@ export default class MessageSender {
|
|||
}
|
||||
}
|
||||
|
||||
async uploadSticker(message: Message) {
|
||||
async uploadSticker(message: Message): Promise<void> {
|
||||
try {
|
||||
const { sticker } = message;
|
||||
|
||||
|
@ -490,7 +516,7 @@ export default class MessageSender {
|
|||
const { quote } = message;
|
||||
|
||||
if (!quote || !quote.attachments || quote.attachments.length === 0) {
|
||||
return Promise.resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
|
@ -546,6 +572,7 @@ export default class MessageSender {
|
|||
})
|
||||
);
|
||||
}
|
||||
|
||||
sendMessageProto(
|
||||
timestamp: number,
|
||||
recipients: Array<string>,
|
||||
|
@ -553,7 +580,7 @@ export default class MessageSender {
|
|||
callback: (result: CallbackResultType) => void,
|
||||
silent?: boolean,
|
||||
options?: SendOptionsType
|
||||
) {
|
||||
): void {
|
||||
const rejections = window.textsecure.storage.get(
|
||||
'signedKeyRotationRejected',
|
||||
0
|
||||
|
@ -595,7 +622,6 @@ export default class MessageSender {
|
|||
}
|
||||
|
||||
resolve(result);
|
||||
return;
|
||||
};
|
||||
|
||||
this.sendMessageProto(
|
||||
|
@ -635,7 +661,7 @@ export default class MessageSender {
|
|||
});
|
||||
}
|
||||
|
||||
createSyncMessage() {
|
||||
createSyncMessage(): SyncMessageClass {
|
||||
const syncMessage = new window.textsecure.protobuf.SyncMessage();
|
||||
|
||||
// Generate a random int from 1 and 512
|
||||
|
@ -656,9 +682,9 @@ export default class MessageSender {
|
|||
expirationStartTimestamp: number | null,
|
||||
sentTo: Array<string> = [],
|
||||
unidentifiedDeliveries: Array<string> = [],
|
||||
isUpdate: boolean = false,
|
||||
isUpdate = false,
|
||||
options?: SendOptionsType
|
||||
) {
|
||||
): Promise<CallbackResultType | void> {
|
||||
const myNumber = window.textsecure.storage.user.getNumber();
|
||||
const myUuid = window.textsecure.storage.user.getUuid();
|
||||
const myDevice = window.textsecure.storage.user.getDeviceId();
|
||||
|
@ -735,7 +761,7 @@ export default class MessageSender {
|
|||
profileKeyVersion?: string;
|
||||
profileKeyCredentialRequest?: string;
|
||||
} = {}
|
||||
) {
|
||||
): Promise<any> {
|
||||
const { accessKey } = options;
|
||||
|
||||
if (accessKey) {
|
||||
|
@ -755,18 +781,21 @@ export default class MessageSender {
|
|||
return this.server.getUuidsForE164s(numbers);
|
||||
}
|
||||
|
||||
async getAvatar(path: string) {
|
||||
async getAvatar(path: string): Promise<any> {
|
||||
return this.server.getAvatar(path);
|
||||
}
|
||||
|
||||
async getSticker(packId: string, stickerId: string) {
|
||||
async getSticker(packId: string, stickerId: number): Promise<any> {
|
||||
return this.server.getSticker(packId, stickerId);
|
||||
}
|
||||
async getStickerPackManifest(packId: string) {
|
||||
|
||||
async getStickerPackManifest(packId: string): Promise<any> {
|
||||
return this.server.getStickerPackManifest(packId);
|
||||
}
|
||||
|
||||
async sendRequestBlockSyncMessage(options?: SendOptionsType) {
|
||||
async sendRequestBlockSyncMessage(
|
||||
options?: SendOptionsType
|
||||
): Promise<CallbackResultType | void> {
|
||||
const myNumber = window.textsecure.storage.user.getNumber();
|
||||
const myUuid = window.textsecure.storage.user.getUuid();
|
||||
const myDevice = window.textsecure.storage.user.getDeviceId();
|
||||
|
@ -792,7 +821,9 @@ export default class MessageSender {
|
|||
return Promise.resolve();
|
||||
}
|
||||
|
||||
async sendRequestConfigurationSyncMessage(options?: SendOptionsType) {
|
||||
async sendRequestConfigurationSyncMessage(
|
||||
options?: SendOptionsType
|
||||
): Promise<CallbackResultType | void> {
|
||||
const myNumber = window.textsecure.storage.user.getNumber();
|
||||
const myUuid = window.textsecure.storage.user.getUuid();
|
||||
const myDevice = window.textsecure.storage.user.getDeviceId();
|
||||
|
@ -818,7 +849,9 @@ export default class MessageSender {
|
|||
return Promise.resolve();
|
||||
}
|
||||
|
||||
async sendRequestGroupSyncMessage(options?: SendOptionsType) {
|
||||
async sendRequestGroupSyncMessage(
|
||||
options?: SendOptionsType
|
||||
): Promise<CallbackResultType | void> {
|
||||
const myNumber = window.textsecure.storage.user.getNumber();
|
||||
const myUuid = window.textsecure.storage.user.getUuid();
|
||||
const myDevice = window.textsecure.storage.user.getDeviceId();
|
||||
|
@ -843,7 +876,9 @@ export default class MessageSender {
|
|||
return Promise.resolve();
|
||||
}
|
||||
|
||||
async sendRequestContactSyncMessage(options?: SendOptionsType) {
|
||||
async sendRequestContactSyncMessage(
|
||||
options?: SendOptionsType
|
||||
): Promise<CallbackResultType | void> {
|
||||
const myNumber = window.textsecure.storage.user.getNumber();
|
||||
const myUuid = window.textsecure.storage.user.getUuid();
|
||||
|
||||
|
@ -870,7 +905,9 @@ export default class MessageSender {
|
|||
return Promise.resolve();
|
||||
}
|
||||
|
||||
async sendFetchManifestSyncMessage(options?: SendOptionsType) {
|
||||
async sendFetchManifestSyncMessage(
|
||||
options?: SendOptionsType
|
||||
): Promise<CallbackResultType | void> {
|
||||
const myUuid = window.textsecure.storage.user.getUuid();
|
||||
const myNumber = window.textsecure.storage.user.getNumber();
|
||||
const myDevice = window.textsecure.storage.user.getDeviceId();
|
||||
|
@ -898,7 +935,9 @@ export default class MessageSender {
|
|||
);
|
||||
}
|
||||
|
||||
async sendRequestKeySyncMessage(options?: SendOptionsType) {
|
||||
async sendRequestKeySyncMessage(
|
||||
options?: SendOptionsType
|
||||
): Promise<CallbackResultType | void> {
|
||||
const myUuid = window.textsecure.storage.user.getUuid();
|
||||
const myNumber = window.textsecure.storage.user.getNumber();
|
||||
const myDevice = window.textsecure.storage.user.getDeviceId();
|
||||
|
@ -934,7 +973,7 @@ export default class MessageSender {
|
|||
timestamp?: number;
|
||||
},
|
||||
sendOptions: SendOptionsType = {}
|
||||
) {
|
||||
): Promise<CallbackResultType | null> {
|
||||
const ACTION_ENUM = window.textsecure.protobuf.TypingMessage.Action;
|
||||
const { recipientId, groupId, groupMembers, isTyping, timestamp } = options;
|
||||
|
||||
|
@ -988,7 +1027,7 @@ export default class MessageSender {
|
|||
recipients: Array<string>,
|
||||
sendOptions: SendOptionsType,
|
||||
groupId?: string
|
||||
) {
|
||||
): Promise<CallbackResultType> {
|
||||
return this.sendMessage(
|
||||
{
|
||||
recipients,
|
||||
|
@ -1012,7 +1051,7 @@ export default class MessageSender {
|
|||
recipientId: string,
|
||||
callingMessage: CallingMessageClass,
|
||||
sendOptions?: SendOptionsType
|
||||
) {
|
||||
): Promise<void> {
|
||||
const recipients = [recipientId];
|
||||
const finalTimestamp = Date.now();
|
||||
|
||||
|
@ -1069,7 +1108,7 @@ export default class MessageSender {
|
|||
senderUuid: string,
|
||||
timestamps: Array<number>,
|
||||
options?: SendOptionsType
|
||||
) {
|
||||
): Promise<CallbackResultType> {
|
||||
const receiptMessage = new window.textsecure.protobuf.ReceiptMessage();
|
||||
receiptMessage.type = window.textsecure.protobuf.ReceiptMessage.Type.READ;
|
||||
receiptMessage.timestamp = timestamps;
|
||||
|
@ -1086,6 +1125,7 @@ export default class MessageSender {
|
|||
options
|
||||
);
|
||||
}
|
||||
|
||||
async syncReadMessages(
|
||||
reads: Array<{
|
||||
senderUuid?: string;
|
||||
|
@ -1093,7 +1133,7 @@ export default class MessageSender {
|
|||
timestamp: number;
|
||||
}>,
|
||||
options?: SendOptionsType
|
||||
) {
|
||||
): Promise<CallbackResultType | void> {
|
||||
const myNumber = window.textsecure.storage.user.getNumber();
|
||||
const myUuid = window.textsecure.storage.user.getUuid();
|
||||
const myDevice = window.textsecure.storage.user.getDeviceId();
|
||||
|
@ -1166,7 +1206,7 @@ export default class MessageSender {
|
|||
type: number;
|
||||
},
|
||||
sendOptions?: SendOptionsType
|
||||
) {
|
||||
): Promise<CallbackResultType | null> {
|
||||
const myNumber = window.textsecure.storage.user.getNumber();
|
||||
const myUuid = window.textsecure.storage.user.getUuid();
|
||||
const myDevice = window.textsecure.storage.user.getDeviceId();
|
||||
|
@ -1251,7 +1291,7 @@ export default class MessageSender {
|
|||
state: number,
|
||||
identityKey: ArrayBuffer,
|
||||
options?: SendOptionsType
|
||||
) {
|
||||
): Promise<CallbackResultType | void> {
|
||||
const myNumber = window.textsecure.storage.user.getNumber();
|
||||
const myUuid = window.textsecure.storage.user.getUuid();
|
||||
const myDevice = window.textsecure.storage.user.getDeviceId();
|
||||
|
@ -1318,7 +1358,9 @@ export default class MessageSender {
|
|||
proto: DataMessageClass,
|
||||
timestamp = Date.now(),
|
||||
options = {}
|
||||
) {
|
||||
): Promise<
|
||||
CallbackResultType | Omit<CallbackResultType, 'discoveredIdentifierPairs'>
|
||||
> {
|
||||
const myE164 = window.textsecure.storage.user.getNumber();
|
||||
const myUuid = window.textsecure.storage.user.getUuid();
|
||||
const identifiers = providedIdentifiers.filter(
|
||||
|
@ -1361,15 +1403,15 @@ export default class MessageSender {
|
|||
destination: string,
|
||||
body: string | undefined,
|
||||
attachments: Array<AttachmentType>,
|
||||
quote: any,
|
||||
quote: unknown,
|
||||
preview: Array<PreviewType>,
|
||||
sticker: any,
|
||||
reaction: any,
|
||||
sticker: unknown,
|
||||
reaction: unknown,
|
||||
timestamp: number,
|
||||
expireTimer: number | undefined,
|
||||
profileKey?: ArrayBuffer,
|
||||
flags?: number
|
||||
) {
|
||||
): Promise<ArrayBuffer> {
|
||||
const attributes = {
|
||||
recipients: [destination],
|
||||
destination,
|
||||
|
@ -1388,7 +1430,9 @@ export default class MessageSender {
|
|||
return this.getMessageProtoObj(attributes);
|
||||
}
|
||||
|
||||
async getMessageProtoObj(attributes: MessageOptionsType) {
|
||||
async getMessageProtoObj(
|
||||
attributes: MessageOptionsType
|
||||
): Promise<ArrayBuffer> {
|
||||
const message = new Message(attributes);
|
||||
await Promise.all([
|
||||
this.uploadAttachments(message),
|
||||
|
@ -1404,15 +1448,15 @@ export default class MessageSender {
|
|||
identifier: string,
|
||||
messageText: string | undefined,
|
||||
attachments: Array<AttachmentType> | undefined,
|
||||
quote: any,
|
||||
quote: unknown,
|
||||
preview: Array<PreviewType> | undefined,
|
||||
sticker: any,
|
||||
reaction: any,
|
||||
sticker: unknown,
|
||||
reaction: unknown,
|
||||
timestamp: number,
|
||||
expireTimer: number | undefined,
|
||||
profileKey?: ArrayBuffer,
|
||||
options?: SendOptionsType
|
||||
) {
|
||||
): Promise<CallbackResultType> {
|
||||
return this.sendMessage(
|
||||
{
|
||||
recipients: [identifier],
|
||||
|
@ -1435,7 +1479,9 @@ export default class MessageSender {
|
|||
e164: string,
|
||||
timestamp: number,
|
||||
options?: SendOptionsType
|
||||
) {
|
||||
): Promise<
|
||||
CallbackResultType | void | Array<CallbackResultType | void | Array<void>>
|
||||
> {
|
||||
window.log.info('resetting secure session');
|
||||
const silent = false;
|
||||
const proto = new window.textsecure.protobuf.DataMessage();
|
||||
|
@ -1592,15 +1638,18 @@ export default class MessageSender {
|
|||
async getGroup(options: GroupCredentialsType): Promise<GroupClass> {
|
||||
return this.server.getGroup(options);
|
||||
}
|
||||
|
||||
async getGroupLog(
|
||||
startVersion: number,
|
||||
options: GroupCredentialsType
|
||||
): Promise<GroupLogResponseType> {
|
||||
return this.server.getGroupLog(startVersion, options);
|
||||
}
|
||||
|
||||
async getGroupAvatar(key: string): Promise<ArrayBuffer> {
|
||||
return this.server.getGroupAvatar(key);
|
||||
}
|
||||
|
||||
async modifyGroup(
|
||||
changes: GroupChangeClass.Actions,
|
||||
options: GroupCredentialsType
|
||||
|
@ -1654,7 +1703,7 @@ export default class MessageSender {
|
|||
timestamp: number,
|
||||
profileKey?: ArrayBuffer,
|
||||
options?: SendOptionsType
|
||||
) {
|
||||
): Promise<CallbackResultType> {
|
||||
return this.sendMessage(
|
||||
{
|
||||
recipients: [identifier],
|
||||
|
@ -1667,7 +1716,11 @@ export default class MessageSender {
|
|||
options
|
||||
);
|
||||
}
|
||||
async makeProxiedRequest(url: string, options?: ProxiedRequestOptionsType) {
|
||||
|
||||
async makeProxiedRequest(
|
||||
url: string,
|
||||
options?: ProxiedRequestOptionsType
|
||||
): Promise<any> {
|
||||
return this.server.makeProxiedRequest(url, options);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
// tslint:disable no-default-export
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import utils from './Helpers';
|
||||
|
||||
// Default implmentation working with localStorage
|
||||
|
@ -33,15 +32,15 @@ export interface StorageInterface {
|
|||
const Storage = {
|
||||
impl: localStorageImpl as StorageInterface,
|
||||
|
||||
put(key: string, value: any) {
|
||||
put(key: string, value: unknown): Promise<void> | void {
|
||||
return Storage.impl.put(key, value);
|
||||
},
|
||||
|
||||
get(key: string, defaultValue: any) {
|
||||
get(key: string, defaultValue: unknown): Promise<unknown> {
|
||||
return Storage.impl.get(key, defaultValue);
|
||||
},
|
||||
|
||||
remove(key: string) {
|
||||
remove(key: string): Promise<void> | void {
|
||||
return Storage.impl.remove(key);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// tslint:disable binary-expression-operand-order no-bitwise no-default-export
|
||||
/* eslint-disable no-bitwise */
|
||||
/* eslint-disable no-nested-ternary */
|
||||
|
||||
const StringView = {
|
||||
/*
|
||||
|
@ -9,7 +10,7 @@ const StringView = {
|
|||
*/
|
||||
|
||||
// prettier-ignore
|
||||
b64ToUint6(nChr: number) {
|
||||
b64ToUint6(nChr: number): number {
|
||||
return nChr > 64 && nChr < 91
|
||||
? nChr - 65
|
||||
: nChr > 96 && nChr < 123
|
||||
|
@ -23,7 +24,7 @@ const StringView = {
|
|||
: 0;
|
||||
},
|
||||
|
||||
base64ToBytes(sBase64: string, nBlocksSize: number) {
|
||||
base64ToBytes(sBase64: string, nBlocksSize: number): ArrayBuffer {
|
||||
const sB64Enc = sBase64.replace(/[^A-Za-z0-9+/]/g, '');
|
||||
const nInLen = sB64Enc.length;
|
||||
const nOutLen = nBlocksSize
|
||||
|
@ -55,7 +56,7 @@ const StringView = {
|
|||
},
|
||||
|
||||
// prettier-ignore
|
||||
uint6ToB64(nUint6: number) {
|
||||
uint6ToB64(nUint6: number): number {
|
||||
return nUint6 < 26
|
||||
? nUint6 + 65
|
||||
: nUint6 < 52
|
||||
|
@ -69,7 +70,7 @@ const StringView = {
|
|||
: 65;
|
||||
},
|
||||
|
||||
bytesToBase64(aBytes: Uint8Array) {
|
||||
bytesToBase64(aBytes: Uint8Array): string {
|
||||
let nMod3;
|
||||
let sB64Enc = '';
|
||||
let nUint24 = 0;
|
||||
|
|
|
@ -1,13 +1,23 @@
|
|||
/* eslint-disable more/no-then */
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable max-classes-per-file */
|
||||
|
||||
import EventTarget from './EventTarget';
|
||||
import MessageReceiver from './MessageReceiver';
|
||||
import MessageSender from './SendMessage';
|
||||
|
||||
class SyncRequestInner extends EventTarget {
|
||||
receiver: MessageReceiver;
|
||||
|
||||
contactSync?: boolean;
|
||||
|
||||
groupSync?: boolean;
|
||||
|
||||
timeout: any;
|
||||
|
||||
oncontact: Function;
|
||||
|
||||
ongroup: Function;
|
||||
|
||||
constructor(sender: MessageSender, receiver: MessageReceiver) {
|
||||
|
@ -38,7 +48,6 @@ class SyncRequestInner extends EventTarget {
|
|||
);
|
||||
|
||||
window.log.info('SyncRequest created. Sending config sync request...');
|
||||
// tslint:disable
|
||||
wrap(sender.sendRequestConfigurationSyncMessage(sendOptions));
|
||||
|
||||
window.log.info('SyncRequest now sending block sync request...');
|
||||
|
@ -58,20 +67,24 @@ class SyncRequestInner extends EventTarget {
|
|||
});
|
||||
this.timeout = setTimeout(this.onTimeout.bind(this), 60000);
|
||||
}
|
||||
|
||||
onContactSyncComplete() {
|
||||
this.contactSync = true;
|
||||
this.update();
|
||||
}
|
||||
|
||||
onGroupSyncComplete() {
|
||||
this.groupSync = true;
|
||||
this.update();
|
||||
}
|
||||
|
||||
update() {
|
||||
if (this.contactSync && this.groupSync) {
|
||||
this.dispatchEvent(new Event('success'));
|
||||
this.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
onTimeout() {
|
||||
if (this.contactSync || this.groupSync) {
|
||||
this.dispatchEvent(new Event('success'));
|
||||
|
@ -80,6 +93,7 @@ class SyncRequestInner extends EventTarget {
|
|||
}
|
||||
this.cleanup();
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
clearTimeout(this.timeout);
|
||||
this.receiver.removeEventListener('contactsync', this.oncontact);
|
||||
|
@ -96,5 +110,6 @@ export default class SyncRequest {
|
|||
}
|
||||
|
||||
addEventListener: (name: string, handler: Function) => void;
|
||||
|
||||
removeEventListener: (name: string, handler: Function) => void;
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ export default function createTaskWithTimeout<T>(
|
|||
window.log.error(message);
|
||||
reject(new Error(message));
|
||||
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -47,15 +47,11 @@ export default function createTaskWithTimeout<T>(
|
|||
clearTimer();
|
||||
complete = true;
|
||||
resolve(result);
|
||||
|
||||
return;
|
||||
};
|
||||
const failure = (error: Error) => {
|
||||
clearTimer();
|
||||
complete = true;
|
||||
reject(error);
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
let promise;
|
||||
|
@ -70,9 +66,10 @@ export default function createTaskWithTimeout<T>(
|
|||
complete = true;
|
||||
resolve(promise);
|
||||
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line more/no-then
|
||||
return promise.then(success, failure);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
import { w3cwebsocket as WebSocket } from 'websocket';
|
||||
/* eslint-disable no-param-reassign */
|
||||
/* eslint-disable more/no-then */
|
||||
/* eslint-disable no-bitwise */
|
||||
/* eslint-disable guard-for-in */
|
||||
/* eslint-disable no-restricted-syntax */
|
||||
/* eslint-disable no-nested-ternary */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
import fetch, { Response } from 'node-fetch';
|
||||
import ProxyAgent from 'proxy-agent';
|
||||
import { Agent } from 'https';
|
||||
|
@ -11,12 +18,14 @@ import {
|
|||
zipObject,
|
||||
} from 'lodash';
|
||||
import { createVerify } from 'crypto';
|
||||
import { Long } from '../window.d';
|
||||
import { pki } from 'node-forge';
|
||||
|
||||
import is from '@sindresorhus/is';
|
||||
import PQueue from 'p-queue';
|
||||
import { v4 as getGuid } from 'uuid';
|
||||
|
||||
import { Long } from '../window.d';
|
||||
import { getUserAgent } from '../util/getUserAgent';
|
||||
import { isPackIdValid, redactPackId } from '../../js/modules/stickers';
|
||||
import MessageSender from './SendMessage';
|
||||
import {
|
||||
arrayBufferToBase64,
|
||||
base64ToArrayBuffer,
|
||||
|
@ -30,11 +39,6 @@ import {
|
|||
getRandomValue,
|
||||
splitUuids,
|
||||
} from '../Crypto';
|
||||
import { getUserAgent } from '../util/getUserAgent';
|
||||
|
||||
import PQueue from 'p-queue';
|
||||
import { v4 as getGuid } from 'uuid';
|
||||
|
||||
import {
|
||||
AvatarUploadAttributesClass,
|
||||
GroupChangeClass,
|
||||
|
@ -44,6 +48,9 @@ import {
|
|||
StorageServiceCredentials,
|
||||
} from '../textsecure.d';
|
||||
|
||||
import { WebSocket } from './WebSocket';
|
||||
import MessageSender from './SendMessage';
|
||||
|
||||
type SgxConstantsType = {
|
||||
SGX_FLAGS_INITTED: Long;
|
||||
SGX_FLAGS_DEBUG: Long;
|
||||
|
@ -81,8 +88,6 @@ function getSgxConstants() {
|
|||
return sgxConstantCache;
|
||||
}
|
||||
|
||||
// tslint:disable no-bitwise
|
||||
|
||||
function _btoa(str: any) {
|
||||
let buffer;
|
||||
|
||||
|
@ -142,24 +147,27 @@ function _getStringable(thing: any) {
|
|||
function _ensureStringed(thing: any): any {
|
||||
if (_getStringable(thing)) {
|
||||
return _getString(thing);
|
||||
} else if (thing instanceof Array) {
|
||||
}
|
||||
if (thing instanceof Array) {
|
||||
const res = [];
|
||||
for (let i = 0; i < thing.length; i += 1) {
|
||||
res[i] = _ensureStringed(thing[i]);
|
||||
}
|
||||
|
||||
return res;
|
||||
} else if (thing === Object(thing)) {
|
||||
}
|
||||
if (thing === Object(thing)) {
|
||||
const res: any = {};
|
||||
// tslint:disable-next-line forin no-for-in
|
||||
for (const key in thing) {
|
||||
res[key] = _ensureStringed(thing[key]);
|
||||
}
|
||||
|
||||
return res;
|
||||
} else if (thing === null) {
|
||||
}
|
||||
if (thing === null) {
|
||||
return null;
|
||||
} else if (thing === undefined) {
|
||||
}
|
||||
if (thing === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
throw new Error(`unsure of how to jsonify object of type ${typeof thing}`);
|
||||
|
@ -185,7 +193,6 @@ function _base64ToBytes(sBase64: string, nBlocksSize?: number) {
|
|||
|
||||
for (let nInIdx = 0; nInIdx < nInLen; nInIdx += 1) {
|
||||
nMod4 = nInIdx & 3;
|
||||
// tslint:disable-next-line binary-expression-operand-order
|
||||
nUint24 |= _b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << (18 - 6 * nMod4);
|
||||
if (nMod4 === 3 || nInLen - nInIdx === 1) {
|
||||
for (
|
||||
|
@ -219,7 +226,6 @@ function _createRedactor(
|
|||
|
||||
function _validateResponse(response: any, schema: any) {
|
||||
try {
|
||||
// tslint:disable-next-line forin no-for-in
|
||||
for (const i in schema) {
|
||||
switch (schema[i]) {
|
||||
case 'object':
|
||||
|
@ -327,12 +333,10 @@ type ArrayBufferWithDetailsType = {
|
|||
response: Response;
|
||||
};
|
||||
|
||||
// tslint:disable-next-line max-func-body-length
|
||||
async function _promiseAjax(
|
||||
providedUrl: string | null,
|
||||
options: PromiseAjaxOptionsType
|
||||
): Promise<any> {
|
||||
// tslint:disable-next-line max-func-body-length
|
||||
return new Promise((resolve, reject) => {
|
||||
const url = providedUrl || `${options.host}/${options.path}`;
|
||||
|
||||
|
@ -376,8 +380,6 @@ async function _promiseAjax(
|
|||
} as HeaderListType,
|
||||
redirect: options.redirect,
|
||||
agent,
|
||||
// We patched node-fetch to add the ca param; its type definitions don't have it
|
||||
// @ts-ignore
|
||||
ca: options.certificateAuthority,
|
||||
timeout,
|
||||
};
|
||||
|
@ -414,7 +416,6 @@ async function _promiseAjax(
|
|||
}
|
||||
|
||||
fetch(url, fetchOptions)
|
||||
// tslint:disable-next-line max-func-body-length
|
||||
.then(async response => {
|
||||
// Build expired!
|
||||
if (response.status === 499) {
|
||||
|
@ -439,16 +440,13 @@ async function _promiseAjax(
|
|||
resultPromise = response.textConverted();
|
||||
}
|
||||
|
||||
// tslint:disable-next-line max-func-body-length
|
||||
return resultPromise.then(result => {
|
||||
if (
|
||||
options.responseType === 'arraybuffer' ||
|
||||
options.responseType === 'arraybufferwithdetails'
|
||||
) {
|
||||
// tslint:disable-next-line no-parameter-reassignment
|
||||
result = result.buffer.slice(
|
||||
result.byteOffset,
|
||||
// tslint:disable-next-line: restrict-plus-operands
|
||||
result.byteOffset + result.byteLength
|
||||
);
|
||||
}
|
||||
|
@ -539,8 +537,6 @@ async function _promiseAjax(
|
|||
options.stack
|
||||
)
|
||||
);
|
||||
|
||||
return;
|
||||
});
|
||||
})
|
||||
.catch(e => {
|
||||
|
@ -757,7 +753,7 @@ export type WebAPIType = {
|
|||
) => Promise<any>;
|
||||
getProvisioningSocket: () => WebSocket;
|
||||
getSenderCertificate: (withUuid?: boolean) => Promise<any>;
|
||||
getSticker: (packId: string, stickerId: string) => Promise<any>;
|
||||
getSticker: (packId: string, stickerId: number) => Promise<any>;
|
||||
getStickerPackManifest: (packId: string) => Promise<StickerPackManifestType>;
|
||||
getStorageCredentials: MessageSender['getStorageCredentials'];
|
||||
getStorageManifest: MessageSender['getStorageManifest'];
|
||||
|
@ -852,7 +848,6 @@ export type ProxiedRequestOptionsType = {
|
|||
};
|
||||
|
||||
// We first set up the data that won't change during this session of the app
|
||||
// tslint:disable-next-line max-func-body-length
|
||||
export function initialize({
|
||||
url,
|
||||
storageUrl,
|
||||
|
@ -911,7 +906,6 @@ export function initialize({
|
|||
// Then we connect to the server with user-specific information. This is the only API
|
||||
// exposed to the browser context, ensuring that it can't connect to arbitrary
|
||||
// locations.
|
||||
// tslint:disable-next-line max-func-body-length
|
||||
function connect({
|
||||
username: initialUsername,
|
||||
password: initialPassword,
|
||||
|
@ -1263,7 +1257,7 @@ export function initialize({
|
|||
'gv2-3': true,
|
||||
},
|
||||
fetchesMessages: true,
|
||||
name: deviceName ? deviceName : undefined,
|
||||
name: deviceName || undefined,
|
||||
registrationId,
|
||||
supportsSms: false,
|
||||
unidentifiedAccessKey: accessKey
|
||||
|
@ -1551,7 +1545,7 @@ export function initialize({
|
|||
);
|
||||
}
|
||||
|
||||
async function getSticker(packId: string, stickerId: string) {
|
||||
async function getSticker(packId: string, stickerId: number) {
|
||||
if (!isPackIdValid(packId)) {
|
||||
throw new Error('getSticker: pack ID was invalid');
|
||||
}
|
||||
|
@ -2031,7 +2025,6 @@ export function initialize({
|
|||
window.log.info('opening message socket', url);
|
||||
const fixedScheme = url
|
||||
.replace('https://', 'wss://')
|
||||
// tslint:disable-next-line no-http-string
|
||||
.replace('http://', 'ws://');
|
||||
const login = encodeURIComponent(username);
|
||||
const pass = encodeURIComponent(password);
|
||||
|
@ -2047,7 +2040,6 @@ export function initialize({
|
|||
window.log.info('opening provisioning socket', url);
|
||||
const fixedScheme = url
|
||||
.replace('https://', 'wss://')
|
||||
// tslint:disable-next-line no-http-string
|
||||
.replace('http://', 'ws://');
|
||||
const clientVersion = encodeURIComponent(version);
|
||||
|
||||
|
@ -2247,7 +2239,6 @@ export function initialize({
|
|||
}
|
||||
}
|
||||
|
||||
// tslint:disable-next-line max-func-body-length
|
||||
async function putRemoteAttestation(auth: {
|
||||
username: string;
|
||||
password: string;
|
||||
|
|
17
ts/textsecure/WebSocket.ts
Normal file
17
ts/textsecure/WebSocket.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { w3cwebsocket } from 'websocket';
|
||||
|
||||
type ModifiedEventSource = Omit<EventSource, 'onerror'>;
|
||||
|
||||
declare class ModifiedWebSocket extends w3cwebsocket
|
||||
implements ModifiedEventSource {
|
||||
withCredentials: boolean;
|
||||
|
||||
addEventListener: EventSource['addEventListener'];
|
||||
|
||||
removeEventListener: EventSource['removeEventListener'];
|
||||
|
||||
dispatchEvent: EventSource['dispatchEvent'];
|
||||
}
|
||||
|
||||
export type WebSocket = ModifiedWebSocket;
|
||||
export const WebSocket = w3cwebsocket as typeof ModifiedWebSocket;
|
|
@ -1,3 +1,7 @@
|
|||
/* eslint-disable no-param-reassign */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
/* eslint-disable max-classes-per-file */
|
||||
/*
|
||||
* WebSocket-Resources
|
||||
*
|
||||
|
@ -20,21 +24,27 @@
|
|||
*
|
||||
*/
|
||||
|
||||
// tslint:disable max-classes-per-file no-default-export no-unnecessary-class
|
||||
|
||||
import { w3cwebsocket as WebSocket } from 'websocket';
|
||||
import { ByteBufferClass } from '../window.d';
|
||||
|
||||
import EventTarget from './EventTarget';
|
||||
|
||||
import { WebSocket } from './WebSocket';
|
||||
|
||||
class Request {
|
||||
verb: string;
|
||||
|
||||
path: string;
|
||||
|
||||
headers: Array<string>;
|
||||
|
||||
body: ByteBufferClass | null;
|
||||
|
||||
success: Function;
|
||||
|
||||
error: Function;
|
||||
|
||||
id: number;
|
||||
|
||||
response?: any;
|
||||
|
||||
constructor(options: any) {
|
||||
|
@ -60,14 +70,18 @@ class Request {
|
|||
|
||||
export class IncomingWebSocketRequest {
|
||||
verb: string;
|
||||
|
||||
path: string;
|
||||
|
||||
body: ByteBufferClass | null;
|
||||
|
||||
headers: Array<string>;
|
||||
|
||||
respond: (status: number, message: string) => void;
|
||||
|
||||
constructor(options: any) {
|
||||
constructor(options: unknown) {
|
||||
const request = new Request(options);
|
||||
const { socket } = options;
|
||||
const { socket } = options as { socket: WebSocket };
|
||||
|
||||
this.verb = request.verb;
|
||||
this.path = request.path;
|
||||
|
@ -113,8 +127,11 @@ class OutgoingWebSocketRequest {
|
|||
|
||||
export default class WebSocketResource extends EventTarget {
|
||||
closed?: boolean;
|
||||
|
||||
close: (code?: number, reason?: string) => void;
|
||||
|
||||
sendRequest: (options: any) => OutgoingWebSocketRequest;
|
||||
|
||||
keepalive?: KeepAlive;
|
||||
|
||||
// tslint:disable-next-line max-func-body-length
|
||||
|
@ -198,21 +215,14 @@ export default class WebSocketResource extends EventTarget {
|
|||
});
|
||||
const resetKeepAliveTimer = this.keepalive.reset.bind(this.keepalive);
|
||||
|
||||
// websocket type definitions don't include an addEventListener, but it's there. And
|
||||
// We can't use declaration merging on classes:
|
||||
// https://www.typescriptlang.org/docs/handbook/declaration-merging.html#disallowed-merges)
|
||||
// @ts-ignore
|
||||
socket.addEventListener('open', resetKeepAliveTimer);
|
||||
// @ts-ignore
|
||||
socket.addEventListener('message', resetKeepAliveTimer);
|
||||
// @ts-ignore
|
||||
socket.addEventListener(
|
||||
'close',
|
||||
this.keepalive.stop.bind(this.keepalive)
|
||||
);
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
socket.addEventListener('close', () => {
|
||||
this.closed = true;
|
||||
});
|
||||
|
@ -228,8 +238,9 @@ export default class WebSocketResource extends EventTarget {
|
|||
}
|
||||
|
||||
socket.close(code, reason);
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
socket.onmessage = null;
|
||||
socket.onmessage = undefined;
|
||||
|
||||
// On linux the socket can wait a long time to emit its close event if we've
|
||||
// lost the internet connection. On the order of minutes. This speeds that
|
||||
|
@ -257,9 +268,13 @@ type KeepAliveOptionsType = {
|
|||
|
||||
class KeepAlive {
|
||||
keepAliveTimer: any;
|
||||
|
||||
disconnectTimer: any;
|
||||
|
||||
path: string;
|
||||
|
||||
disconnect: boolean;
|
||||
|
||||
wsr: WebSocketResource;
|
||||
|
||||
constructor(
|
||||
|
|
|
@ -12848,9 +12848,8 @@
|
|||
"path": "ts/components/CallScreen.js",
|
||||
"line": " this.localVideoRef = react_1.default.createRef();",
|
||||
"lineNumber": 98,
|
||||
"reasonCategory": "falseMatch|testCode|exampleCode|otherUtilityCode|regexMatchedSafeCode|notExercisedByOurApp|ruleNeeded|usageTrusted",
|
||||
"updated": "2020-09-14T23:03:44.863Z",
|
||||
"reasonDetail": "<optional>"
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2020-09-14T23:03:44.863Z"
|
||||
},
|
||||
{
|
||||
"rule": "React-createRef",
|
||||
|
@ -12935,8 +12934,8 @@
|
|||
"line": " this.listRef = react_1.default.createRef();",
|
||||
"lineNumber": 16,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2019-11-05T01:14:21.081Z",
|
||||
"reasonDetail": "Used for focus management"
|
||||
"updated": "2020-09-11T17:24:56.124Z",
|
||||
"reasonDetail": "Used for scroll calculations"
|
||||
},
|
||||
{
|
||||
"rule": "React-createRef",
|
||||
|
@ -13015,8 +13014,8 @@
|
|||
"line": " this.containerRef = react_1.default.createRef();",
|
||||
"lineNumber": 25,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2020-09-11T17:24:56.124Z",
|
||||
"reasonDetail": "Used for scroll calculations"
|
||||
"updated": "2019-08-09T00:44:31.008Z",
|
||||
"reasonDetail": "SearchResults needs to interact with its child List directly"
|
||||
},
|
||||
{
|
||||
"rule": "React-createRef",
|
||||
|
@ -13086,7 +13085,7 @@
|
|||
"line": " public audioRef: React.RefObject<HTMLAudioElement> = React.createRef();",
|
||||
"lineNumber": 213,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2020-09-14T23:03:44.863Z"
|
||||
"updated": "2020-09-08T20:19:01.913Z"
|
||||
},
|
||||
{
|
||||
"rule": "React-createRef",
|
||||
|
@ -13094,7 +13093,7 @@
|
|||
"line": " public focusRef: React.RefObject<HTMLDivElement> = React.createRef();",
|
||||
"lineNumber": 215,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2020-09-14T23:03:44.863Z"
|
||||
"updated": "2020-09-08T20:19:01.913Z"
|
||||
},
|
||||
{
|
||||
"rule": "React-createRef",
|
||||
|
@ -13187,7 +13186,7 @@
|
|||
"rule": "jQuery-append(",
|
||||
"path": "ts/textsecure/ContactsParser.js",
|
||||
"line": " this.buffer.append(arrayBuffer);",
|
||||
"lineNumber": 7,
|
||||
"lineNumber": 9,
|
||||
"reasonCategory": "falseMatch",
|
||||
"updated": "2020-04-05T23:45:16.746Z"
|
||||
},
|
||||
|
@ -13195,7 +13194,7 @@
|
|||
"rule": "jQuery-append(",
|
||||
"path": "ts/textsecure/ContactsParser.ts",
|
||||
"line": " this.buffer.append(arrayBuffer);",
|
||||
"lineNumber": 26,
|
||||
"lineNumber": 30,
|
||||
"reasonCategory": "falseMatch",
|
||||
"updated": "2020-04-05T23:45:16.746Z"
|
||||
},
|
||||
|
@ -13203,7 +13202,7 @@
|
|||
"rule": "jQuery-wrap(",
|
||||
"path": "ts/textsecure/Crypto.js",
|
||||
"line": " const data = window.dcodeIO.ByteBuffer.wrap(encryptedProfileName, 'base64').toArrayBuffer();",
|
||||
"lineNumber": 157,
|
||||
"lineNumber": 155,
|
||||
"reasonCategory": "falseMatch",
|
||||
"updated": "2020-04-05T23:45:16.746Z"
|
||||
},
|
||||
|
@ -13211,7 +13210,7 @@
|
|||
"rule": "jQuery-wrap(",
|
||||
"path": "ts/textsecure/Crypto.js",
|
||||
"line": " given: window.dcodeIO.ByteBuffer.wrap(padded)",
|
||||
"lineNumber": 176,
|
||||
"lineNumber": 174,
|
||||
"reasonCategory": "falseMatch",
|
||||
"updated": "2020-04-05T23:45:16.746Z"
|
||||
},
|
||||
|
@ -13219,7 +13218,7 @@
|
|||
"rule": "jQuery-wrap(",
|
||||
"path": "ts/textsecure/Crypto.js",
|
||||
"line": " ? window.dcodeIO.ByteBuffer.wrap(padded)",
|
||||
"lineNumber": 180,
|
||||
"lineNumber": 178,
|
||||
"reasonCategory": "falseMatch",
|
||||
"updated": "2020-04-05T23:45:16.746Z"
|
||||
},
|
||||
|
@ -13227,7 +13226,7 @@
|
|||
"rule": "jQuery-wrap(",
|
||||
"path": "ts/textsecure/Crypto.ts",
|
||||
"line": " const data = window.dcodeIO.ByteBuffer.wrap(",
|
||||
"lineNumber": 223,
|
||||
"lineNumber": 345,
|
||||
"reasonCategory": "falseMatch",
|
||||
"updated": "2020-04-05T23:45:16.746Z"
|
||||
},
|
||||
|
@ -13235,7 +13234,7 @@
|
|||
"rule": "jQuery-wrap(",
|
||||
"path": "ts/textsecure/Crypto.ts",
|
||||
"line": " given: window.dcodeIO.ByteBuffer.wrap(padded)",
|
||||
"lineNumber": 252,
|
||||
"lineNumber": 374,
|
||||
"reasonCategory": "falseMatch",
|
||||
"updated": "2020-04-05T23:45:16.746Z"
|
||||
},
|
||||
|
@ -13243,7 +13242,7 @@
|
|||
"rule": "jQuery-wrap(",
|
||||
"path": "ts/textsecure/Crypto.ts",
|
||||
"line": " ? window.dcodeIO.ByteBuffer.wrap(padded)",
|
||||
"lineNumber": 256,
|
||||
"lineNumber": 378,
|
||||
"reasonCategory": "falseMatch",
|
||||
"updated": "2020-04-05T23:45:16.746Z"
|
||||
},
|
||||
|
@ -13251,7 +13250,7 @@
|
|||
"rule": "jQuery-wrap(",
|
||||
"path": "ts/textsecure/SyncRequest.js",
|
||||
"line": " wrap(sender.sendRequestConfigurationSyncMessage(sendOptions));",
|
||||
"lineNumber": 27,
|
||||
"lineNumber": 30,
|
||||
"reasonCategory": "falseMatch",
|
||||
"updated": "2020-04-05T23:45:16.746Z"
|
||||
},
|
||||
|
@ -13259,7 +13258,7 @@
|
|||
"rule": "jQuery-wrap(",
|
||||
"path": "ts/textsecure/SyncRequest.js",
|
||||
"line": " wrap(sender.sendRequestBlockSyncMessage(sendOptions));",
|
||||
"lineNumber": 29,
|
||||
"lineNumber": 32,
|
||||
"reasonCategory": "falseMatch",
|
||||
"updated": "2020-04-05T23:45:16.746Z"
|
||||
},
|
||||
|
@ -13267,7 +13266,7 @@
|
|||
"rule": "jQuery-wrap(",
|
||||
"path": "ts/textsecure/SyncRequest.js",
|
||||
"line": " wrap(sender.sendRequestContactSyncMessage(sendOptions))",
|
||||
"lineNumber": 31,
|
||||
"lineNumber": 34,
|
||||
"reasonCategory": "falseMatch",
|
||||
"updated": "2020-04-05T23:45:16.746Z"
|
||||
},
|
||||
|
@ -13275,7 +13274,7 @@
|
|||
"rule": "jQuery-wrap(",
|
||||
"path": "ts/textsecure/SyncRequest.js",
|
||||
"line": " return wrap(sender.sendRequestGroupSyncMessage(sendOptions));",
|
||||
"lineNumber": 34,
|
||||
"lineNumber": 37,
|
||||
"reasonCategory": "falseMatch",
|
||||
"updated": "2020-04-05T23:45:16.746Z"
|
||||
},
|
||||
|
@ -13283,7 +13282,7 @@
|
|||
"rule": "jQuery-wrap(",
|
||||
"path": "ts/textsecure/SyncRequest.ts",
|
||||
"line": " wrap(sender.sendRequestConfigurationSyncMessage(sendOptions));",
|
||||
"lineNumber": 42,
|
||||
"lineNumber": 51,
|
||||
"reasonCategory": "falseMatch",
|
||||
"updated": "2020-04-05T23:45:16.746Z"
|
||||
},
|
||||
|
@ -13291,7 +13290,7 @@
|
|||
"rule": "jQuery-wrap(",
|
||||
"path": "ts/textsecure/SyncRequest.ts",
|
||||
"line": " wrap(sender.sendRequestBlockSyncMessage(sendOptions));",
|
||||
"lineNumber": 45,
|
||||
"lineNumber": 54,
|
||||
"reasonCategory": "falseMatch",
|
||||
"updated": "2020-04-05T23:45:16.746Z"
|
||||
},
|
||||
|
@ -13299,7 +13298,7 @@
|
|||
"rule": "jQuery-wrap(",
|
||||
"path": "ts/textsecure/SyncRequest.ts",
|
||||
"line": " wrap(sender.sendRequestContactSyncMessage(sendOptions))",
|
||||
"lineNumber": 48,
|
||||
"lineNumber": 57,
|
||||
"reasonCategory": "falseMatch",
|
||||
"updated": "2020-04-05T23:45:16.746Z"
|
||||
},
|
||||
|
@ -13307,7 +13306,7 @@
|
|||
"rule": "jQuery-wrap(",
|
||||
"path": "ts/textsecure/SyncRequest.ts",
|
||||
"line": " return wrap(sender.sendRequestGroupSyncMessage(sendOptions));",
|
||||
"lineNumber": 51,
|
||||
"lineNumber": 60,
|
||||
"reasonCategory": "falseMatch",
|
||||
"updated": "2020-04-05T23:45:16.746Z"
|
||||
},
|
||||
|
@ -13315,7 +13314,7 @@
|
|||
"rule": "jQuery-wrap(",
|
||||
"path": "ts/textsecure/WebAPI.js",
|
||||
"line": " const byteBuffer = window.dcodeIO.ByteBuffer.wrap(quote, 'binary', window.dcodeIO.ByteBuffer.LITTLE_ENDIAN);",
|
||||
"lineNumber": 1223,
|
||||
"lineNumber": 1212,
|
||||
"reasonCategory": "falseMatch",
|
||||
"updated": "2020-09-08T23:07:22.682Z"
|
||||
},
|
||||
|
@ -13323,7 +13322,7 @@
|
|||
"rule": "jQuery-wrap(",
|
||||
"path": "ts/textsecure/WebAPI.ts",
|
||||
"line": " const byteBuffer = window.dcodeIO.ByteBuffer.wrap(",
|
||||
"lineNumber": 2076,
|
||||
"lineNumber": 2068,
|
||||
"reasonCategory": "falseMatch",
|
||||
"updated": "2020-09-08T23:07:22.682Z"
|
||||
}
|
||||
|
|
|
@ -192,9 +192,11 @@
|
|||
"ts/scripts/**",
|
||||
"ts/services/**",
|
||||
"ts/shims/**",
|
||||
"ts/sql/**",
|
||||
"ts/state/**",
|
||||
"ts/storybook/**",
|
||||
"ts/test/**",
|
||||
"ts/textsecure/**",
|
||||
"ts/types/**",
|
||||
"ts/updater/**",
|
||||
"ts/util/**",
|
||||
|
|
Loading…
Reference in a new issue