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