1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-11-28 05:03:59 +03:00

Merge branch 'develop' into backup_refactor

This commit is contained in:
Hubert Chathi
2021-06-03 18:43:46 -04:00
120 changed files with 1045 additions and 911 deletions

View File

@@ -2,7 +2,9 @@ module.exports = {
plugins: [ plugins: [
"matrix-org", "matrix-org",
], ],
extends: ["plugin:matrix-org/javascript"], extends: [
"plugin:matrix-org/babel",
],
env: { env: {
browser: true, browser: true,
node: true, node: true,
@@ -31,14 +33,26 @@ module.exports = {
"no-console": "error", "no-console": "error",
}, },
overrides: [{ overrides: [{
"files": ["src/**/*.ts"], files: [
"extends": ["plugin:matrix-org/typescript"], "**/*.ts",
"rules": { ],
extends: [
"plugin:matrix-org/typescript",
],
rules: {
// TypeScript has its own version of this
"@babel/no-invalid-this": "off",
// We're okay being explicit at the moment // We're okay being explicit at the moment
"@typescript-eslint/no-empty-interface": "off", "@typescript-eslint/no-empty-interface": "off",
// While we're converting to ts we make heavy use of this // We disable this while we're transitioning
"@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-explicit-any": "off",
// We'd rather not do this but we do
"@typescript-eslint/ban-ts-comment": "off",
"quotes": "off", "quotes": "off",
// We use a `logger` intermediary module
"no-console": "error",
}, },
}], }],
}; };

View File

@@ -1,3 +1,26 @@
Changes in [11.1.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v11.1.0) (2021-05-24)
==================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v11.1.0-rc.1...v11.1.0)
* [Release] Bump libolm version and update package name
[\#1707](https://github.com/matrix-org/matrix-js-sdk/pull/1707)
* [Release] Change call event handlers to adapt to undecrypted events
[\#1699](https://github.com/matrix-org/matrix-js-sdk/pull/1699)
Changes in [11.1.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v11.1.0-rc.1) (2021-05-19)
============================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v11.0.0...v11.1.0-rc.1)
* Fix regressed glare
[\#1690](https://github.com/matrix-org/matrix-js-sdk/pull/1690)
* Add m.reaction to EventType enum
[\#1692](https://github.com/matrix-org/matrix-js-sdk/pull/1692)
* Prioritise and reduce the amount of events decrypted on application startup
[\#1684](https://github.com/matrix-org/matrix-js-sdk/pull/1684)
* Decrypt relations before applying them to target event
[\#1696](https://github.com/matrix-org/matrix-js-sdk/pull/1696)
* Guard against duplicates in `Relations` model
Changes in [11.0.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v11.0.0) (2021-05-17) Changes in [11.0.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v11.0.0) (2021-05-17)
================================================================================================== ==================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v11.0.0-rc.1...v11.0.0) [Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v11.0.0-rc.1...v11.0.0)

View File

@@ -1,6 +1,6 @@
{ {
"name": "matrix-js-sdk", "name": "matrix-js-sdk",
"version": "11.0.0", "version": "11.1.0",
"description": "Matrix Client-Server SDK for Javascript", "description": "Matrix Client-Server SDK for Javascript",
"scripts": { "scripts": {
"prepublishOnly": "yarn build", "prepublishOnly": "yarn build",
@@ -72,6 +72,7 @@
"@babel/preset-env": "^7.12.11", "@babel/preset-env": "^7.12.11",
"@babel/preset-typescript": "^7.12.7", "@babel/preset-typescript": "^7.12.7",
"@babel/register": "^7.12.10", "@babel/register": "^7.12.10",
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz",
"@types/jest": "^26.0.20", "@types/jest": "^26.0.20",
"@types/node": "12", "@types/node": "12",
"@types/request": "^2.48.5", "@types/request": "^2.48.5",
@@ -91,7 +92,6 @@
"jest-localstorage-mock": "^2.4.6", "jest-localstorage-mock": "^2.4.6",
"jsdoc": "^3.6.6", "jsdoc": "^3.6.6",
"matrix-mock-request": "^1.2.3", "matrix-mock-request": "^1.2.3",
"olm": "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"terser": "^5.5.1", "terser": "^5.5.1",
"tsify": "^5.0.2", "tsify": "^5.0.2",

View File

@@ -133,7 +133,6 @@ TestClient.prototype.expectDeviceKeyUpload = function() {
}); });
}; };
/** /**
* If one-time keys have already been uploaded, return them. Otherwise, * If one-time keys have already been uploaded, return them. Otherwise,
* set up an expectation that the keys will be uploaded, and wait for * set up an expectation that the keys will be uploaded, and wait for
@@ -197,7 +196,6 @@ TestClient.prototype.expectKeyQuery = function(response) {
}); });
}; };
/** /**
* get the uploaded curve25519 device key * get the uploaded curve25519 device key
* *
@@ -208,7 +206,6 @@ TestClient.prototype.getDeviceKey = function() {
return this.deviceKeys.keys[keyId]; return this.deviceKeys.keys[keyId];
}; };
/** /**
* get the uploaded ed25519 device key * get the uploaded ed25519 device key
* *

View File

@@ -67,7 +67,6 @@ function getSyncResponse(roomMembers) {
return syncResponse; return syncResponse;
} }
describe("DeviceList management:", function() { describe("DeviceList management:", function() {
if (!global.Olm) { if (!global.Olm) {
logger.warn('not running deviceList tests: Olm not present'); logger.warn('not running deviceList tests: Olm not present');
@@ -137,7 +136,6 @@ describe("DeviceList management:", function() {
}); });
}); });
it("We should not get confused by out-of-order device query responses", it("We should not get confused by out-of-order device query responses",
() => { () => {
// https://github.com/vector-im/element-web/issues/3126 // https://github.com/vector-im/element-web/issues/3126
@@ -323,7 +321,6 @@ describe("DeviceList management:", function() {
}, },
); );
await aliceTestClient.flushSync(); await aliceTestClient.flushSync();
await aliceTestClient.client._crypto._deviceList.saveIfDirty(); await aliceTestClient.client._crypto._deviceList.saveIfDirty();

View File

@@ -144,7 +144,6 @@ function expectAliClaimKeys() {
}); });
} }
function aliDownloadsKeys() { function aliDownloadsKeys() {
// can't query keys before bob has uploaded them // can't query keys before bob has uploaded them
expect(bobTestClient.getSigningKey()).toBeTruthy(); expect(bobTestClient.getSigningKey()).toBeTruthy();
@@ -357,7 +356,6 @@ function recvMessage(httpBackend, client, sender, message) {
}); });
} }
/** /**
* Send an initial sync response to the client (which just includes the member * Send an initial sync response to the client (which just includes the member
* list for our test room). * list for our test room).
@@ -395,7 +393,6 @@ function firstSync(testClient) {
return testClient.flushSync(); return testClient.flushSync();
} }
describe("MatrixClient crypto", function() { describe("MatrixClient crypto", function() {
if (!CRYPTO_ENABLED) { if (!CRYPTO_ENABLED) {
return; return;
@@ -533,7 +530,6 @@ describe("MatrixClient crypto", function() {
}); });
}); });
it("Bob starts his client and uploads device keys and one-time keys", function() { it("Bob starts his client and uploads device keys and one-time keys", function() {
return Promise.resolve() return Promise.resolve()
.then(() => bobTestClient.start()) .then(() => bobTestClient.start())

View File

@@ -141,7 +141,6 @@ describe("getEventTimeline support", function() {
}); });
}); });
it("scrollback should be able to scroll back to before a gappy /sync", it("scrollback should be able to scroll back to before a gappy /sync",
function() { function() {
// need a client with timelineSupport disabled to make this work // need a client with timelineSupport disabled to make this work
@@ -532,7 +531,6 @@ describe("MatrixClient event timelines", function() {
]); ]);
}); });
it("should allow you to paginate forwards", function() { it("should allow you to paginate forwards", function() {
const room = client.getRoom(roomId); const room = client.getRoom(roomId);
const timelineSet = room.getTimelineSets()[0]; const timelineSet = room.getTimelineSets()[0];
@@ -680,7 +678,6 @@ describe("MatrixClient event timelines", function() {
}); });
}); });
it("should handle gappy syncs after redactions", function() { it("should handle gappy syncs after redactions", function() {
// https://github.com/vector-im/vector-web/issues/1389 // https://github.com/vector-im/vector-web/issues/1389

View File

@@ -285,7 +285,6 @@ describe("MatrixClient", function() {
}); });
}); });
describe("downloadKeys", function() { describe("downloadKeys", function() {
if (!CRYPTO_ENABLED) { if (!CRYPTO_ENABLED) {
return; return;

View File

@@ -2,7 +2,6 @@ import * as utils from "../test-utils";
import { EventStatus } from "../../src/models/event"; import { EventStatus } from "../../src/models/event";
import { TestClient } from "../TestClient"; import { TestClient } from "../TestClient";
describe("MatrixClient room timelines", function() { describe("MatrixClient room timelines", function() {
let client = null; let client = null;
let httpBackend = null; let httpBackend = null;

View File

@@ -122,7 +122,6 @@ describe("MatrixClient syncing", function() {
resolveInvitesToProfiles: true, resolveInvitesToProfiles: true,
}); });
return Promise.all([ return Promise.all([
httpBackend.flushAllExpected(), httpBackend.flushAllExpected(),
awaitSyncEvent(), awaitSyncEvent(),

View File

@@ -196,7 +196,6 @@ function getSyncResponse(roomMembers) {
return syncResponse; return syncResponse;
} }
describe("megolm", function() { describe("megolm", function() {
if (!global.Olm) { if (!global.Olm) {
logger.warn('not running megolm tests: Olm not present'); logger.warn('not running megolm tests: Olm not present');
@@ -841,7 +840,6 @@ describe("megolm", function() {
}); });
}); });
it('Alice should wait for device list to complete when sending a megolm message', it('Alice should wait for device list to complete when sending a megolm message',
function() { function() {
let downloadPromise; let downloadPromise;
@@ -887,7 +885,6 @@ describe("megolm", function() {
}); });
}); });
it("Alice exports megolm keys and imports them to a new device", function() { it("Alice exports megolm keys and imports them to a new device", function() {
let messageEncrypted; let messageEncrypted;

View File

@@ -20,7 +20,7 @@ import * as utils from "../src/utils";
// try to load the olm library. // try to load the olm library.
try { try {
global.Olm = require('olm'); global.Olm = require('@matrix-org/olm');
logger.log('loaded libolm'); logger.log('loaded libolm');
} catch (e) { } catch (e) {
logger.warn("unable to run crypto tests: libolm not available"); logger.warn("unable to run crypto tests: libolm not available");

View File

@@ -177,7 +177,6 @@ export function mkMessage(opts) {
return mkEvent(opts); return mkEvent(opts);
} }
/** /**
* A mock implementation of webstorage * A mock implementation of webstorage
* *
@@ -204,7 +203,6 @@ MockStorageApi.prototype = {
}, },
}; };
/** /**
* If an event is being decrypted, wait for it to finish being decrypted. * If an event is being decrypted, wait for it to finish being decrypted.
* *
@@ -229,7 +227,6 @@ export function awaitDecryption(event) {
} }
} }
export function HttpResponse( export function HttpResponse(
httpLookups, acceptKeepalives, ignoreUnhandledSync, httpLookups, acceptKeepalives, ignoreUnhandledSync,
) { ) {

View File

@@ -50,7 +50,6 @@ describe("MegolmDecryption", function() {
roomId: ROOM_ID, roomId: ROOM_ID,
}); });
// we stub out the olm encryption bits // we stub out the olm encryption bits
mockOlmLib = {}; mockOlmLib = {};
mockOlmLib.ensureOlmSessionsForDevices = jest.fn(); mockOlmLib.ensureOlmSessionsForDevices = jest.fn();

View File

@@ -1,6 +1,5 @@
import { IndexedDBCryptoStore } from '../../../src/crypto/store/indexeddb-crypto-store'; import { IndexedDBCryptoStore } from '../../../src/crypto/store/indexeddb-crypto-store';
// needs to be phased out and replaced with bootstrapSecretStorage, // needs to be phased out and replaced with bootstrapSecretStorage,
// but that is doing too much extra stuff for it to be an easy transition. // but that is doing too much extra stuff for it to be an easy transition.
export async function resetCrossSigningKeys(client, { export async function resetCrossSigningKeys(client, {

View File

@@ -94,7 +94,6 @@ describe("EventTimeline", function() {
}); });
}); });
describe("neighbouringTimelines", function() { describe("neighbouringTimelines", function() {
it("neighbouring timelines should start null", function() { it("neighbouring timelines should start null", function() {
expect(timeline.getNeighbouringTimeline(EventTimeline.BACKWARDS)).toBe(null); expect(timeline.getNeighbouringTimeline(EventTimeline.BACKWARDS)).toBe(null);
@@ -278,7 +277,6 @@ describe("EventTimeline", function() {
not.toHaveBeenCalled(); not.toHaveBeenCalled();
}); });
it("should call setStateEvents on the right RoomState with the right " + it("should call setStateEvents on the right RoomState with the right " +
"forwardLooking value for old events", function() { "forwardLooking value for old events", function() {
const events = [ const events = [

133
spec/unit/relations.spec.ts Normal file
View File

@@ -0,0 +1,133 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { EventTimelineSet } from "../../src/models/event-timeline-set";
import { MatrixEvent } from "../../src/models/event";
import { Relations } from "../../src/models/relations";
describe("Relations", function() {
it("should deduplicate annotations", function() {
const relations = new Relations("m.annotation", "m.reaction");
// Create an instance of an annotation
const eventData = {
"sender": "@bob:example.com",
"type": "m.reaction",
"event_id": "$cZ1biX33ENJqIm00ks0W_hgiO_6CHrsAc3ZQrnLeNTw",
"room_id": "!pzVjCQSoQPpXQeHpmK:example.com",
"content": {
"m.relates_to": {
"event_id": "$2s4yYpEkVQrPglSCSqB_m6E8vDhWsg0yFNyOJdVIb_o",
"key": "👍️",
"rel_type": "m.annotation",
},
},
};
const eventA = new MatrixEvent(eventData);
// Add the event once and check results
{
relations.addEvent(eventA);
const annotationsByKey = relations.getSortedAnnotationsByKey();
expect(annotationsByKey.length).toEqual(1);
const [key, events] = annotationsByKey[0];
expect(key).toEqual("👍️");
expect(events.size).toEqual(1);
}
// Add the event again and expect the same
{
relations.addEvent(eventA);
const annotationsByKey = relations.getSortedAnnotationsByKey();
expect(annotationsByKey.length).toEqual(1);
const [key, events] = annotationsByKey[0];
expect(key).toEqual("👍️");
expect(events.size).toEqual(1);
}
// Create a fresh object with the same event content
const eventB = new MatrixEvent(eventData);
// Add the event again and expect the same
{
relations.addEvent(eventB);
const annotationsByKey = relations.getSortedAnnotationsByKey();
expect(annotationsByKey.length).toEqual(1);
const [key, events] = annotationsByKey[0];
expect(key).toEqual("👍️");
expect(events.size).toEqual(1);
}
});
it("should emit created regardless of ordering", async function() {
const targetEvent = new MatrixEvent({
"sender": "@bob:example.com",
"type": "m.room.message",
"event_id": "$2s4yYpEkVQrPglSCSqB_m6E8vDhWsg0yFNyOJdVIb_o",
"room_id": "!pzVjCQSoQPpXQeHpmK:example.com",
"content": {},
});
const relationEvent = new MatrixEvent({
"sender": "@bob:example.com",
"type": "m.reaction",
"event_id": "$cZ1biX33ENJqIm00ks0W_hgiO_6CHrsAc3ZQrnLeNTw",
"room_id": "!pzVjCQSoQPpXQeHpmK:example.com",
"content": {
"m.relates_to": {
"event_id": "$2s4yYpEkVQrPglSCSqB_m6E8vDhWsg0yFNyOJdVIb_o",
"key": "👍️",
"rel_type": "m.annotation",
},
},
});
// Stub the room
const room = {
getPendingEvent() { return null; },
getUnfilteredTimelineSet() { return null; },
};
// Add the target event first, then the relation event
{
const relationsCreated = new Promise(resolve => {
targetEvent.once("Event.relationsCreated", resolve);
})
const timelineSet = new EventTimelineSet(room, {
unstableClientRelationAggregation: true,
});
timelineSet.addLiveEvent(targetEvent);
timelineSet.addLiveEvent(relationEvent);
await relationsCreated;
}
// Add the relation event first, then the target event
{
const relationsCreated = new Promise(resolve => {
targetEvent.once("Event.relationsCreated", resolve);
})
const timelineSet = new EventTimelineSet(room, {
unstableClientRelationAggregation: true,
});
timelineSet.addLiveEvent(relationEvent);
timelineSet.addLiveEvent(targetEvent);
await relationsCreated;
}
});
});

View File

@@ -1267,7 +1267,6 @@ describe("Room", function() {
expect(callCount).toEqual(1); expect(callCount).toEqual(1);
}); });
it("should remove cancelled events from the timeline", function() { it("should remove cancelled events from the timeline", function() {
const room = new Room(roomId, null, userA); const room = new Room(roomId, null, userA);
const eventA = utils.mkMessage({ const eventA = utils.mkMessage({

View File

@@ -46,7 +46,6 @@ function addEventsToTimeline(timeline, numEvents, atStart) {
} }
} }
/* /*
* create a pair of linked timelines * create a pair of linked timelines
*/ */
@@ -58,7 +57,6 @@ function createLinkedTimelines() {
return [tl1, tl2]; return [tl1, tl2];
} }
describe("TimelineIndex", function() { describe("TimelineIndex", function() {
describe("minIndex", function() { describe("minIndex", function() {
it("should return the min index relative to BaseIndex", function() { it("should return the min index relative to BaseIndex", function() {
@@ -133,7 +131,6 @@ describe("TimelineIndex", function() {
}); });
}); });
describe("TimelineWindow", function() { describe("TimelineWindow", function() {
/** /**
* create a dummy eventTimelineSet and client, and a TimelineWindow * create a dummy eventTimelineSet and client, and a TimelineWindow
@@ -385,7 +382,6 @@ describe("TimelineWindow", function() {
}); });
}); });
it("should make backward pagination requests", function() { it("should make backward pagination requests", function() {
const timeline = createTimeline(); const timeline = createTimeline();
timeline.setPaginationToken("toktok", EventTimeline.BACKWARDS); timeline.setPaginationToken("toktok", EventTimeline.BACKWARDS);

View File

@@ -175,7 +175,6 @@ describe("utils", function() {
}); });
}); });
describe("extend", function() { describe("extend", function() {
const SOURCE = { "prop2": 1, "string2": "x", "newprop": "new" }; const SOURCE = { "prop2": 1, "string2": "x", "newprop": "new" };

View File

@@ -15,7 +15,7 @@ limitations under the License.
*/ */
// this is needed to tell TS about global.Olm // this is needed to tell TS about global.Olm
import * as Olm from "olm"; // eslint-disable-line @typescript-eslint/no-unused-vars import * as Olm from "@matrix-org/olm"; // eslint-disable-line @typescript-eslint/no-unused-vars
export {}; export {};

View File

@@ -158,7 +158,6 @@ MatrixBaseApis.prototype.makeTxnId = function() {
return "m" + new Date().getTime() + "." + (this._txnCtr++); return "m" + new Date().getTime() + "." + (this._txnCtr++);
}; };
// Registration/Login operations // Registration/Login operations
// ============================= // =============================
@@ -379,14 +378,12 @@ MatrixBaseApis.prototype.getSsoLoginUrl = function(redirectUrl, loginType, idpId
loginType = "sso"; loginType = "sso";
} }
let prefix = PREFIX_R0;
let url = "/login/" + loginType + "/redirect"; let url = "/login/" + loginType + "/redirect";
if (idpId) { if (idpId) {
url += "/" + idpId; url += "/" + idpId;
prefix = "/_matrix/client/unstable/org.matrix.msc2858";
} }
return this._http.getUrl(url, { redirectUrl }, prefix); return this._http.getUrl(url, { redirectUrl }, PREFIX_R0);
}; };
/** /**
@@ -401,7 +398,6 @@ MatrixBaseApis.prototype.loginWithToken = function(token, callback) {
}, callback); }, callback);
}; };
/** /**
* Logs out the current session. * Logs out the current session.
* Obviously, further calls that require authorisation should fail after this * Obviously, further calls that require authorisation should fail after this
@@ -613,7 +609,6 @@ MatrixBaseApis.prototype.upgradeRoom = function(roomId, newVersion) {
); );
}; };
/** /**
* @param {string} groupId * @param {string} groupId
* @return {Promise} Resolves: Group summary object * @return {Promise} Resolves: Group summary object
@@ -1278,7 +1273,6 @@ MatrixBaseApis.prototype.searchUserDirectory = function(opts) {
); );
}; };
// Media operations // Media operations
// ================ // ================
@@ -1347,7 +1341,6 @@ MatrixBaseApis.prototype.getCurrentUploads = function() {
return this._http.getCurrentUploads(); return this._http.getCurrentUploads();
}; };
// Profile operations // Profile operations
// ================== // ==================
@@ -1372,7 +1365,6 @@ MatrixBaseApis.prototype.getProfileInfo = function(userId, info, callback) {
return this._http.authedRequest(callback, "GET", path); return this._http.authedRequest(callback, "GET", path);
}; };
// Account operations // Account operations
// ================== // ==================
@@ -1518,7 +1510,6 @@ MatrixBaseApis.prototype.setPassword = function(authDict, newPassword, callback)
); );
}; };
// Device operations // Device operations
// ================= // =================
@@ -1605,7 +1596,6 @@ MatrixBaseApis.prototype.deleteMultipleDevices = function(devices, auth) {
return this._http.authedRequest(undefined, "POST", path, undefined, body); return this._http.authedRequest(undefined, "POST", path, undefined, body);
}; };
// Push operations // Push operations
// =============== // ===============
@@ -1728,7 +1718,6 @@ MatrixBaseApis.prototype.setPushRuleActions = function(scope, kind,
); );
}; };
// Search // Search
// ====== // ======

View File

@@ -17,7 +17,6 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
/** /**
* This is an internal module. See {@link MatrixClient} for the public class. * This is an internal module. See {@link MatrixClient} for the public class.
* @module client * @module client
@@ -335,6 +334,10 @@ export function MatrixClient(opts) {
if (call) { if (call) {
this._callEventHandler = new CallEventHandler(this); this._callEventHandler = new CallEventHandler(this);
this._supportsVoip = true; this._supportsVoip = true;
// Start listening for calls after the initial sync is done
// We do not need to backfill the call event buffer
// with encrypted events that might never get decrypted
this.on("sync", this._startCallEventHandler);
} else { } else {
this._callEventHandler = null; this._callEventHandler = null;
} }
@@ -684,7 +687,6 @@ MatrixClient.prototype.getDeviceId = function() {
return this.deviceId; return this.deviceId;
}; };
/** /**
* Check if the runtime environment supports VoIP calling. * Check if the runtime environment supports VoIP calling.
* @return {boolean} True if VoIP is supported. * @return {boolean} True if VoIP is supported.
@@ -947,14 +949,12 @@ MatrixClient.prototype.initCrypto = async function() {
this.olmVersion = Crypto.getOlmVersion(); this.olmVersion = Crypto.getOlmVersion();
// if crypto initialisation was successful, tell it to attach its event // if crypto initialisation was successful, tell it to attach its event
// handlers. // handlers.
crypto.registerEventHandlers(this); crypto.registerEventHandlers(this);
this._crypto = crypto; this._crypto = crypto;
}; };
/** /**
* Is end-to-end crypto enabled for this client. * Is end-to-end crypto enabled for this client.
* @return {boolean} True if end-to-end is enabled. * @return {boolean} True if end-to-end is enabled.
@@ -2842,7 +2842,6 @@ MatrixClient.prototype._sendCompleteEvent = function(roomId, eventObject, txnId,
return _sendEvent(this, room, localEvent, callback); return _sendEvent(this, room, localEvent, callback);
}; };
// encrypts the event if necessary // encrypts the event if necessary
// adds the event to the queue, or sends it // adds the event to the queue, or sends it
// marks the event as sent/unsent // marks the event as sent/unsent
@@ -4526,7 +4525,6 @@ MatrixClient.prototype._requestTokenFromEndpoint = async function(endpoint, para
); );
}; };
// Push operations // Push operations
// =============== // ===============
@@ -4782,7 +4780,6 @@ MatrixClient.prototype._processRoomEventsSearch = function(searchResults, respon
return searchResults; return searchResults;
}; };
/** /**
* Populate the store with rooms the user has left. * Populate the store with rooms the user has left.
* @return {Promise} Resolves: TODO - Resolved when the rooms have * @return {Promise} Resolves: TODO - Resolved when the rooms have
@@ -4928,7 +4925,6 @@ MatrixClient.prototype.getOrCreateFilter = async function(filterName, filter) {
return createdFilter.filterId; return createdFilter.filterId;
}; };
/** /**
* Gets a bearer token from the Home Server that the user can * Gets a bearer token from the Home Server that the user can
* present to a third party in order to prove their ownership * present to a third party in order to prove their ownership
@@ -4946,10 +4942,16 @@ MatrixClient.prototype.getOpenIdToken = function() {
); );
}; };
// VoIP operations // VoIP operations
// =============== // ===============
MatrixClient.prototype._startCallEventHandler = function() {
if (this.isInitialSyncComplete()) {
this._callEventHandler.start();
this.off("sync", this._startCallEventHandler);
}
};
/** /**
* @param {module:client.callback} callback Optional. * @param {module:client.callback} callback Optional.
* @return {Promise} Resolves: TODO * @return {Promise} Resolves: TODO
@@ -5107,7 +5109,6 @@ MatrixClient.prototype.deactivateSynapseUser = function(userId) {
// due to ambiguity (or should this be on a chat-specific layer)? // due to ambiguity (or should this be on a chat-specific layer)?
// reconnect after connectivity outages // reconnect after connectivity outages
/** /**
* High level helper method to begin syncing and poll for new events. To listen for these * High level helper method to begin syncing and poll for new events. To listen for these
* events, add a listener for {@link module:client~MatrixClient#event:"event"} * events, add a listener for {@link module:client~MatrixClient#event:"event"}
@@ -5530,7 +5531,7 @@ function _PojoToMatrixEventMapper(client, options = {}) {
]); ]);
} }
if (decrypt) { if (decrypt) {
event.attemptDecryption(client._crypto); client.decryptEventIfNeeded(event);
} }
} }
if (!preventReEmit) { if (!preventReEmit) {
@@ -5572,6 +5573,26 @@ MatrixClient.prototype.generateClientSecret = function() {
return randomString(32); return randomString(32);
}; };
/**
* Attempts to decrypt an event
* @param {MatrixEvent} event The event to decrypt
* @returns {Promise<void>} A decryption promise
* @param {object} options
* @param {bool} options.isRetry True if this is a retry (enables more logging)
* @param {bool} options.emit Emits "event.decrypted" if set to true
*/
MatrixClient.prototype.decryptEventIfNeeded = function(event, options) {
if (event.shouldAttemptDecryption()) {
event.attemptDecryption(this._crypto, options);
}
if (event.isBeingDecrypted()) {
return event._decryptionPromise;
} else {
return Promise.resolve();
}
};
// MatrixClient Event JSDocs // MatrixClient Event JSDocs
/** /**

View File

@@ -30,7 +30,6 @@ import * as olmlib from './olmlib';
import { IndexedDBCryptoStore } from './store/indexeddb-crypto-store'; import { IndexedDBCryptoStore } from './store/indexeddb-crypto-store';
import { chunkPromises, defer, sleep } from '../utils'; import { chunkPromises, defer, sleep } from '../utils';
/* State transition diagram for DeviceList._deviceTrackingStatus /* State transition diagram for DeviceList._deviceTrackingStatus
* *
* | * |
@@ -51,7 +50,6 @@ import {chunkPromises, defer, sleep} from '../utils';
* +----------------------- UP_TO_DATE ------------------------+ * +----------------------- UP_TO_DATE ------------------------+
*/ */
// constants for DeviceList._deviceTrackingStatus // constants for DeviceList._deviceTrackingStatus
const TRACKING_STATUS_NOT_TRACKED = 0; const TRACKING_STATUS_NOT_TRACKED = 0;
const TRACKING_STATUS_PENDING_DOWNLOAD = 1; const TRACKING_STATUS_PENDING_DOWNLOAD = 1;
@@ -892,7 +890,6 @@ class DeviceListUpdateSerialiser {
} }
} }
async function _updateStoredDeviceKeysForUser( async function _updateStoredDeviceKeysForUser(
_olmDevice, userId, userStore, userResult, localUserId, localDeviceId, _olmDevice, userId, userStore, userResult, localUserId, localDeviceId,
) { ) {

View File

@@ -86,7 +86,6 @@ export class EncryptionSetupBuilder {
userSignatures[deviceId] = signature; userSignatures[deviceId] = signature;
} }
/** /**
* @param {String} type * @param {String} type
* @param {Object} content * @param {Object} content
@@ -225,7 +224,6 @@ export class EncryptionSetupOperation {
} }
} }
/** /**
* Catches account data set by SecretStorage during bootstrapping by * Catches account data set by SecretStorage during bootstrapping by
* implementing the methods related to account data in MatrixClient * implementing the methods related to account data in MatrixClient

View File

@@ -48,7 +48,6 @@ function checkPayloadLength(payloadString) {
} }
} }
/** /**
* The type of object we use for importing and exporting megolm session data. * The type of object we use for importing and exporting megolm session data.
* *
@@ -62,7 +61,6 @@ function checkPayloadLength(payloadString) {
* @property {String} session_key Base64'ed key data * @property {String} session_key Base64'ed key data
*/ */
/** /**
* Manages the olm cryptography functions. Each OlmDevice has a single * Manages the olm cryptography functions. Each OlmDevice has a single
* OlmAccount and a number of OlmSessions. * OlmAccount and a number of OlmSessions.
@@ -376,7 +374,6 @@ OlmDevice.prototype._saveSession = function(deviceKey, sessionInfo, txn) {
); );
}; };
/** /**
* get an OlmUtility and call the given function * get an OlmUtility and call the given function
* *
@@ -393,7 +390,6 @@ OlmDevice.prototype._getUtility = function(func) {
} }
}; };
/** /**
* Signs a message with the ed25519 key for this account. * Signs a message with the ed25519 key for this account.
* *
@@ -434,7 +430,6 @@ OlmDevice.prototype.getOneTimeKeys = async function() {
return result; return result;
}; };
/** /**
* Get the maximum number of one-time keys we can store. * Get the maximum number of one-time keys we can store.
* *
@@ -550,7 +545,6 @@ OlmDevice.prototype.createOutboundSession = async function(
return newSessionId; return newSessionId;
}; };
/** /**
* Generate a new inbound session, given an incoming message * Generate a new inbound session, given an incoming message
* *
@@ -612,7 +606,6 @@ OlmDevice.prototype.createInboundSession = async function(
return result; return result;
}; };
/** /**
* Get a list of known session IDs for the given device * Get a list of known session IDs for the given device
* *
@@ -856,7 +849,6 @@ OlmDevice.prototype.filterOutNotifiedErrorDevices = async function(devices) {
return await this._cryptoStore.filterOutNotifiedErrorDevices(devices); return await this._cryptoStore.filterOutNotifiedErrorDevices(devices);
}; };
// Outbound group session // Outbound group session
// ====================== // ======================
@@ -871,7 +863,6 @@ OlmDevice.prototype._saveOutboundGroupSession = function(session) {
this._outboundGroupSessionStore[session.session_id()] = pickledSession; this._outboundGroupSessionStore[session.session_id()] = pickledSession;
}; };
/** /**
* extract an OutboundGroupSession from _outboundGroupSessionStore and call the * extract an OutboundGroupSession from _outboundGroupSessionStore and call the
* given function * given function
@@ -896,7 +887,6 @@ OlmDevice.prototype._getOutboundGroupSession = function(sessionId, func) {
} }
}; };
/** /**
* Generate a new outbound group session * Generate a new outbound group session
* *
@@ -913,7 +903,6 @@ OlmDevice.prototype.createOutboundGroupSession = function() {
} }
}; };
/** /**
* Encrypt an outgoing message with an outbound group session * Encrypt an outgoing message with an outbound group session
* *
@@ -953,7 +942,6 @@ OlmDevice.prototype.getOutboundGroupSessionKey = function(sessionId) {
}); });
}; };
// Inbound group session // Inbound group session
// ===================== // =====================

View File

@@ -75,7 +75,6 @@ function OutboundSessionInfo(sessionId, sharedHistory = false) {
this.sharedHistory = sharedHistory; this.sharedHistory = sharedHistory;
} }
/** /**
* Check if it's time to rotate the session * Check if it's time to rotate the session
* *
@@ -158,7 +157,6 @@ OutboundSessionInfo.prototype.sharedWithTooManyDevices = function(
} }
}; };
/** /**
* Megolm encryption implementation * Megolm encryption implementation
* *
@@ -1340,7 +1338,6 @@ MegolmDecryption.prototype._removeEventFromPendingList = function(event) {
} }
}; };
/** /**
* @inheritdoc * @inheritdoc
* *

View File

@@ -358,5 +358,4 @@ OlmDecryption.prototype._reallyDecryptMessage = async function(
return res.payload; return res.payload;
}; };
registerAlgorithm(olmlib.OLM_ALGORITHM, OlmEncryption, OlmDecryption); registerAlgorithm(olmlib.OLM_ALGORITHM, OlmEncryption, OlmDecryption);

View File

@@ -1604,7 +1604,6 @@ Crypto.prototype.registerEventHandlers = function(eventEmitter) {
eventEmitter.on("Event.decrypted", timelineHandler); eventEmitter.on("Event.decrypted", timelineHandler);
}; };
/** Start background processes related to crypto */ /** Start background processes related to crypto */
Crypto.prototype.start = function() { Crypto.prototype.start = function() {
this._outgoingRoomKeyRequestManager.start(); this._outgoingRoomKeyRequestManager.start();
@@ -2198,7 +2197,6 @@ Crypto.prototype.legacyDeviceVerification = async function(
return request; return request;
}; };
/** /**
* Get information on the active olm sessions with a user * Get information on the active olm sessions with a user
* <p> * <p>
@@ -2229,7 +2227,6 @@ Crypto.prototype.getOlmSessionsForUser = async function(userId) {
return result; return result;
}; };
/** /**
* Get the device which sent an event * Get the device which sent an event
* *
@@ -2474,7 +2471,6 @@ Crypto.prototype.setRoomEncryption = async function(roomId, config, inhibitDevic
} }
}; };
/** /**
* Make sure we are tracking the device lists for all users in this room. * Make sure we are tracking the device lists for all users in this room.
* *
@@ -2952,7 +2948,6 @@ Crypto.prototype._getTrackedE2eRooms = function() {
}); });
}; };
Crypto.prototype._onToDeviceEvent = function(event) { Crypto.prototype._onToDeviceEvent = function(event) {
try { try {
logger.log(`received to_device ${event.getType()} from: ` + logger.log(`received to_device ${event.getType()} from: ` +
@@ -2973,7 +2968,10 @@ Crypto.prototype._onToDeviceEvent = function(event) {
this._onKeyVerificationMessage(event); this._onKeyVerificationMessage(event);
} else if (event.getContent().msgtype === "m.bad.encrypted") { } else if (event.getContent().msgtype === "m.bad.encrypted") {
this._onToDeviceBadEncrypted(event); this._onToDeviceBadEncrypted(event);
} else if (event.isBeingDecrypted()) { } else if (event.isBeingDecrypted() || event.shouldAttemptDecryption()) {
if (!event.isBeingDecrypted()) {
event.attemptDecryption(this);
}
// once the event has been decrypted, try again // once the event has been decrypted, try again
event.once('Event.decrypted', (ev) => { event.once('Event.decrypted', (ev) => {
this._onToDeviceEvent(ev); this._onToDeviceEvent(ev);
@@ -3240,7 +3238,6 @@ Crypto.prototype._onToDeviceBadEncrypted = async function(event) {
}, },
}); });
// Most of the time this probably won't be necessary since we'll have queued up a key request when // Most of the time this probably won't be necessary since we'll have queued up a key request when
// we failed to decrypt the message and will be waiting a bit for the key to arrive before sending // we failed to decrypt the message and will be waiting a bit for the key to arrive before sending
// it. This won't always be the case though so we need to re-send any that have already been sent // it. This won't always be the case though so we need to re-send any that have already been sent
@@ -3297,7 +3294,6 @@ Crypto.prototype._onRoomMembership = function(event, member, oldMembership) {
alg.onRoomMembership(event, member, oldMembership); alg.onRoomMembership(event, member, oldMembership);
}; };
/** /**
* Called when we get an m.room_key_request event. * Called when we get an m.room_key_request event.
* *
@@ -3450,7 +3446,6 @@ Crypto.prototype._processReceivedRoomKeyRequest = async function(req) {
this.emit("crypto.roomKeyRequest", req); this.emit("crypto.roomKeyRequest", req);
}; };
/** /**
* Helper for processReceivedRoomKeyRequests * Helper for processReceivedRoomKeyRequests
* *
@@ -3526,7 +3521,6 @@ Crypto.prototype._getRoomDecryptor = function(roomId, algorithm) {
return alg; return alg;
}; };
/** /**
* Get all the room decryptors for a given encryption algorithm. * Get all the room decryptors for a given encryption algorithm.
* *
@@ -3544,7 +3538,6 @@ Crypto.prototype._getRoomDecryptors = function(algorithm) {
return decryptors; return decryptors;
}; };
/** /**
* sign the given object with our ed25519 key * sign the given object with our ed25519 key
* *
@@ -3564,7 +3557,6 @@ Crypto.prototype._signObject = async function(obj) {
if (unsigned !== undefined) obj.unsigned = unsigned; if (unsigned !== undefined) obj.unsigned = unsigned;
}; };
/** /**
* The parameters of a room key request. The details of the request may * The parameters of a room key request. The details of the request may
* vary with the crypto algorithm, but the management and storage layers for * vary with the crypto algorithm, but the management and storage layers for

View File

@@ -41,7 +41,6 @@ export const MEGOLM_ALGORITHM = "m.megolm.v1.aes-sha2";
*/ */
export const MEGOLM_BACKUP_ALGORITHM = "m.megolm_backup.v1.curve25519-aes-sha2"; export const MEGOLM_BACKUP_ALGORITHM = "m.megolm_backup.v1.curve25519-aes-sha2";
/** /**
* Encrypt an event payload for an Olm device * Encrypt an event payload for an Olm device
* *
@@ -408,7 +407,6 @@ async function _verifyKeyAndStartSession(olmDevice, oneTimeKey, userId, deviceIn
return sid; return sid;
} }
/** /**
* Verify the signature on an object * Verify the signature on an object
* *

View File

@@ -323,7 +323,6 @@ export class SAS extends Base {
key: this.ourSASPubKey, key: this.ourSASPubKey,
}); });
e = await this._waitForEvent("m.key.verification.key"); e = await this._waitForEvent("m.key.verification.key");
// FIXME: make sure event is properly formed // FIXME: make sure event is properly formed
content = e.getContent(); content = e.getContent();
@@ -353,7 +352,6 @@ export class SAS extends Base {
this.emit("show_sas", this.sasEvent); this.emit("show_sas", this.sasEvent);
}); });
[e] = await Promise.all([ [e] = await Promise.all([
this._waitForEvent("m.key.verification.mac") this._waitForEvent("m.key.verification.mac")
.then((e) => { .then((e) => {
@@ -411,7 +409,6 @@ export class SAS extends Base {
commitment: olmutil.sha256(commitmentStr), commitment: olmutil.sha256(commitmentStr),
}); });
let e = await this._waitForEvent("m.key.verification.key"); let e = await this._waitForEvent("m.key.verification.key");
// FIXME: make sure event is properly formed // FIXME: make sure event is properly formed
content = e.getContent(); content = e.getContent();
@@ -440,7 +437,6 @@ export class SAS extends Base {
this.emit("show_sas", this.sasEvent); this.emit("show_sas", this.sasEvent);
}); });
[e] = await Promise.all([ [e] = await Promise.all([
this._waitForEvent("m.key.verification.mac") this._waitForEvent("m.key.verification.mac")
.then((e) => { .then((e) => {

View File

@@ -292,7 +292,6 @@ export class ToDeviceChannel {
} }
} }
export class ToDeviceRequests { export class ToDeviceRequests {
constructor() { constructor() {
this._requestsByUserId = new Map(); this._requestsByUserId = new Map();

View File

@@ -37,7 +37,6 @@ const TIMEOUT_FROM_EVENT_RECEIPT = 2 * 60 * 1000; // 2 minutes
// are this amount of time away from expiring. // are this amount of time away from expiring.
const VERIFICATION_REQUEST_MARGIN = 3 * 1000; // 3 seconds const VERIFICATION_REQUEST_MARGIN = 3 * 1000; // 3 seconds
export const EVENT_PREFIX = "m.key.verification."; export const EVENT_PREFIX = "m.key.verification.";
export const REQUEST_TYPE = EVENT_PREFIX + "request"; export const REQUEST_TYPE = EVENT_PREFIX + "request";
export const START_TYPE = EVENT_PREFIX + "start"; export const START_TYPE = EVENT_PREFIX + "start";
@@ -52,7 +51,6 @@ export const PHASE_STARTED = 4;
export const PHASE_CANCELLED = 5; export const PHASE_CANCELLED = 5;
export const PHASE_DONE = 6; export const PHASE_DONE = 6;
/** /**
* State machine for verification requests. * State machine for verification requests.
* Things that differ based on what channel is used to * Things that differ based on what channel is used to
@@ -98,7 +96,6 @@ export class VerificationRequest extends EventEmitter {
static validateEvent(type, event, client) { static validateEvent(type, event, client) {
const content = event.getContent(); const content = event.getContent();
if (!type || !type.startsWith(EVENT_PREFIX)) { if (!type || !type.startsWith(EVENT_PREFIX)) {
return false; return false;
} }
@@ -355,7 +352,6 @@ export class VerificationRequest extends EventEmitter {
return this._observeOnly; return this._observeOnly;
} }
/** /**
* Gets which device the verification should be started with * Gets which device the verification should be started with
* given the events sent so far in the verification. This is the * given the events sent so far in the verification. This is the

View File

@@ -22,7 +22,6 @@ InvalidStoreError.prototype = Object.create(Error.prototype, {
}); });
Reflect.setPrototypeOf(InvalidStoreError, Error); Reflect.setPrototypeOf(InvalidStoreError, Error);
export function InvalidCryptoStoreError(reason) { export function InvalidCryptoStoreError(reason) {
const message = `Crypto store is invalid because ${reason}, ` + const message = `Crypto store is invalid because ${reason}, ` +
`please stop the client, delete all data and start the client again`; `please stop the client, delete all data and start the client again`;

View File

@@ -803,7 +803,8 @@ const requestCallback = function(
} }
if (!err) { if (!err) {
try { try {
if (response.statusCode >= 400) { const httpStatus = response.status || response.statusCode; // XMLHttpRequest vs http.IncomingMessage
if (httpStatus >= 400) {
err = parseErrorResponse(response, body); err = parseErrorResponse(response, body);
} else if (bodyParser) { } else if (bodyParser) {
body = bodyParser(body); body = bodyParser(body);
@@ -818,7 +819,7 @@ const requestCallback = function(
userDefinedCallback(err); userDefinedCallback(err);
} else { } else {
const res = { const res = {
code: response.statusCode, code: response.status || response.statusCode, // XMLHttpRequest vs http.IncomingMessage
// XXX: why do we bother with this? it doesn't work for // XXX: why do we bother with this? it doesn't work for
// XMLHttpRequest, so clearly we don't use it. // XMLHttpRequest, so clearly we don't use it.
@@ -842,7 +843,7 @@ const requestCallback = function(
* @returns {Error} * @returns {Error}
*/ */
function parseErrorResponse(response, body) { function parseErrorResponse(response, body) {
const httpStatus = response.statusCode; const httpStatus = response.status || response.statusCode; // XMLHttpRequest vs http.IncomingMessage
const contentType = getResponseContentType(response); const contentType = getResponseContentType(response);
let err; let err;
@@ -862,7 +863,6 @@ function parseErrorResponse(response, body) {
return err; return err;
} }
/** /**
* extract the Content-Type header from the response object, and * extract the Content-Type header from the response object, and
* parse it to a `{type, parameters}` object. * parse it to a `{type, parameters}` object.

View File

@@ -16,6 +16,7 @@ limitations under the License.
import * as matrixcs from "./matrix"; import * as matrixcs from "./matrix";
import * as utils from "./utils"; import * as utils from "./utils";
import { logger } from './logger';
import request from "request"; import request from "request";
matrixcs.request(request); matrixcs.request(request);
@@ -25,7 +26,7 @@ try {
const crypto = require('crypto'); const crypto = require('crypto');
utils.setCrypto(crypto); utils.setCrypto(crypto);
} catch (err) { } catch (err) {
console.log('nodejs was compiled without crypto support'); logger.log('nodejs was compiled without crypto support');
} }
export * from "./matrix"; export * from "./matrix";

View File

@@ -36,11 +36,11 @@ const DEFAULT_NAMESPACE = "matrix";
// when logging so we always get the current value of console methods. // when logging so we always get the current value of console methods.
log.methodFactory = function(methodName, logLevel, loggerName) { log.methodFactory = function(methodName, logLevel, loggerName) {
return function(...args) { return function(...args) {
/* eslint-disable @babel/no-invalid-this */ /* eslint-disable @typescript-eslint/no-invalid-this */
if (this.prefix) { if (this.prefix) {
args.unshift(this.prefix); args.unshift(this.prefix);
} }
/* eslint-enable @babel/no-invalid-this */ /* eslint-enable @typescript-eslint/no-invalid-this */
const supportedByConsole = methodName === "error" || const supportedByConsole = methodName === "error" ||
methodName === "warn" || methodName === "warn" ||
methodName === "trace" || methodName === "trace" ||

View File

@@ -59,7 +59,6 @@ export {
setVideoInput as setMatrixCallVideoInput, setVideoInput as setMatrixCallVideoInput,
} from "./webrtc/call"; } from "./webrtc/call";
// expose the underlying request object so different environments can use // expose the underlying request object so different environments can use
// different request libs (e.g. request or browser-request) // different request libs (e.g. request or browser-request)
let requestInstance; let requestInstance;

View File

@@ -271,7 +271,6 @@ EventTimelineSet.prototype.addTimeline = function() {
return timeline; return timeline;
}; };
/** /**
* Add events to a timeline * Add events to a timeline
* *
@@ -740,17 +739,12 @@ EventTimelineSet.prototype.setRelationsTarget = function(event) {
if (!relationsForEvent) { if (!relationsForEvent) {
return; return;
} }
// don't need it for non m.replace relations for now
const relationsWithRelType = relationsForEvent["m.replace"];
if (!relationsWithRelType) {
return;
}
// only doing replacements for messages for now (e.g. edits)
const relationsWithEventType = relationsWithRelType["m.room.message"];
if (relationsWithEventType) { for (const relationsWithRelType of Object.values(relationsForEvent)) {
for (const relationsWithEventType of Object.values(relationsWithRelType)) {
relationsWithEventType.setTargetEvent(event); relationsWithEventType.setTargetEvent(event);
} }
}
}; };
/** /**
@@ -769,7 +763,7 @@ EventTimelineSet.prototype.aggregateRelations = function(event) {
} }
// If the event is currently encrypted, wait until it has been decrypted. // If the event is currently encrypted, wait until it has been decrypted.
if (event.isBeingDecrypted()) { if (event.isBeingDecrypted() || event.shouldAttemptDecryption()) {
event.once("Event.decrypted", () => { event.once("Event.decrypted", () => {
this.aggregateRelations(event); this.aggregateRelations(event);
}); });
@@ -797,7 +791,6 @@ EventTimelineSet.prototype.aggregateRelations = function(event) {
} }
let relationsWithEventType = relationsWithRelType[eventType]; let relationsWithEventType = relationsWithRelType[eventType];
let isNewRelations = false;
let relatesToEvent; let relatesToEvent;
if (!relationsWithEventType) { if (!relationsWithEventType) {
relationsWithEventType = relationsWithRelType[eventType] = new Relations( relationsWithEventType = relationsWithRelType[eventType] = new Relations(
@@ -805,7 +798,6 @@ EventTimelineSet.prototype.aggregateRelations = function(event) {
eventType, eventType,
this.room, this.room,
); );
isNewRelations = true;
relatesToEvent = this.findEventById(relatesToEventId) || this.room.getPendingEvent(relatesToEventId); relatesToEvent = this.findEventById(relatesToEventId) || this.room.getPendingEvent(relatesToEventId);
if (relatesToEvent) { if (relatesToEvent) {
relationsWithEventType.setTargetEvent(relatesToEvent); relationsWithEventType.setTargetEvent(relatesToEvent);
@@ -813,11 +805,6 @@ EventTimelineSet.prototype.aggregateRelations = function(event) {
} }
relationsWithEventType.addEvent(event); relationsWithEventType.addEvent(event);
// only emit once event has been added to relations
if (isNewRelations && relatesToEvent) {
relatesToEvent.emit("Event.relationsCreated", relationType, eventType);
}
}; };
/** /**

View File

@@ -180,7 +180,6 @@ export const MatrixEvent = function(
}; };
utils.inherits(MatrixEvent, EventEmitter); utils.inherits(MatrixEvent, EventEmitter);
utils.extend(MatrixEvent.prototype, { utils.extend(MatrixEvent.prototype, {
/** /**
* Get the event_id for this event. * Get the event_id for this event.
@@ -1146,7 +1145,6 @@ utils.extend(MatrixEvent.prototype, {
}, },
}); });
/* _REDACT_KEEP_KEY_MAP gives the keys we keep when an event is redacted /* _REDACT_KEEP_KEY_MAP gives the keys we keep when an event is redacted
* *
* This is specified here: * This is specified here:
@@ -1175,7 +1173,6 @@ const _REDACT_KEEP_CONTENT_MAP = {
'm.room.aliases': { 'aliases': 1 }, 'm.room.aliases': { 'aliases': 1 },
}; };
/** /**
* Fires when an event is decrypted * Fires when an event is decrypted
* *

View File

@@ -41,11 +41,14 @@ export class Relations extends EventEmitter {
super(); super();
this.relationType = relationType; this.relationType = relationType;
this.eventType = eventType; this.eventType = eventType;
this._relationEventIds = new Set();
this._relations = new Set(); this._relations = new Set();
this._annotationsByKey = {}; this._annotationsByKey = {};
this._annotationsBySender = {}; this._annotationsBySender = {};
this._sortedAnnotationsByKey = []; this._sortedAnnotationsByKey = [];
this._targetEvent = null; this._targetEvent = null;
this._room = room;
this._creationEmitted = false;
} }
/** /**
@@ -54,8 +57,8 @@ export class Relations extends EventEmitter {
* @param {MatrixEvent} event * @param {MatrixEvent} event
* The new relation event to be added. * The new relation event to be added.
*/ */
addEvent(event) { async addEvent(event) {
if (this._relations.has(event)) { if (this._relationEventIds.has(event.getId())) {
return; return;
} }
@@ -80,16 +83,20 @@ export class Relations extends EventEmitter {
} }
this._relations.add(event); this._relations.add(event);
this._relationEventIds.add(event.getId());
if (this.relationType === "m.annotation") { if (this.relationType === "m.annotation") {
this._addAnnotationToAggregation(event); this._addAnnotationToAggregation(event);
} else if (this.relationType === "m.replace" && this._targetEvent) { } else if (this.relationType === "m.replace" && this._targetEvent) {
this._targetEvent.makeReplaced(this.getLastReplacement()); const lastReplacement = await this.getLastReplacement();
this._targetEvent.makeReplaced(lastReplacement);
} }
event.on("Event.beforeRedaction", this._onBeforeRedaction); event.on("Event.beforeRedaction", this._onBeforeRedaction);
this.emit("Relations.add", event); this.emit("Relations.add", event);
this._maybeEmitCreated();
} }
/** /**
@@ -98,7 +105,7 @@ export class Relations extends EventEmitter {
* @param {MatrixEvent} event * @param {MatrixEvent} event
* The relation event to remove. * The relation event to remove.
*/ */
_removeEvent(event) { async _removeEvent(event) {
if (!this._relations.has(event)) { if (!this._relations.has(event)) {
return; return;
} }
@@ -122,7 +129,8 @@ export class Relations extends EventEmitter {
if (this.relationType === "m.annotation") { if (this.relationType === "m.annotation") {
this._removeAnnotationFromAggregation(event); this._removeAnnotationFromAggregation(event);
} else if (this.relationType === "m.replace" && this._targetEvent) { } else if (this.relationType === "m.replace" && this._targetEvent) {
this._targetEvent.makeReplaced(this.getLastReplacement()); const lastReplacement = await this.getLastReplacement();
this._targetEvent.makeReplaced(lastReplacement);
} }
this.emit("Relations.remove", event); this.emit("Relations.remove", event);
@@ -227,7 +235,7 @@ export class Relations extends EventEmitter {
* @param {MatrixEvent} redactedEvent * @param {MatrixEvent} redactedEvent
* The original relation event that is about to be redacted. * The original relation event that is about to be redacted.
*/ */
_onBeforeRedaction = (redactedEvent) => { _onBeforeRedaction = async (redactedEvent) => {
if (!this._relations.has(redactedEvent)) { if (!this._relations.has(redactedEvent)) {
return; return;
} }
@@ -238,7 +246,8 @@ export class Relations extends EventEmitter {
// Remove the redacted annotation from aggregation by key // Remove the redacted annotation from aggregation by key
this._removeAnnotationFromAggregation(redactedEvent); this._removeAnnotationFromAggregation(redactedEvent);
} else if (this.relationType === "m.replace" && this._targetEvent) { } else if (this.relationType === "m.replace" && this._targetEvent) {
this._targetEvent.makeReplaced(this.getLastReplacement()); const lastReplacement = await this.getLastReplacement();
this._targetEvent.makeReplaced(lastReplacement);
} }
redactedEvent.removeListener("Event.beforeRedaction", this._onBeforeRedaction); redactedEvent.removeListener("Event.beforeRedaction", this._onBeforeRedaction);
@@ -291,7 +300,7 @@ export class Relations extends EventEmitter {
* *
* @return {MatrixEvent?} * @return {MatrixEvent?}
*/ */
getLastReplacement() { async getLastReplacement() {
if (this.relationType !== "m.replace") { if (this.relationType !== "m.replace") {
// Aggregating on last only makes sense for this relation type // Aggregating on last only makes sense for this relation type
return null; return null;
@@ -309,7 +318,7 @@ export class Relations extends EventEmitter {
this._targetEvent.getServerAggregatedRelation("m.replace"); this._targetEvent.getServerAggregatedRelation("m.replace");
const minTs = replaceRelation && replaceRelation.origin_server_ts; const minTs = replaceRelation && replaceRelation.origin_server_ts;
return this.getRelations().reduce((last, event) => { const lastReplacement = this.getRelations().reduce((last, event) => {
if (event.getSender() !== this._targetEvent.getSender()) { if (event.getSender() !== this._targetEvent.getSender()) {
return last; return last;
} }
@@ -321,23 +330,51 @@ export class Relations extends EventEmitter {
} }
return event; return event;
}, null); }, null);
if (lastReplacement?.shouldAttemptDecryption()) {
await lastReplacement.attemptDecryption(this._room._client._crypto);
} else if (lastReplacement?.isBeingDecrypted()) {
await lastReplacement._decryptionPromise;
}
return lastReplacement;
} }
/* /*
* @param {MatrixEvent} targetEvent the event the relations are related to. * @param {MatrixEvent} targetEvent the event the relations are related to.
*/ */
setTargetEvent(event) { async setTargetEvent(event) {
if (this._targetEvent) { if (this._targetEvent) {
return; return;
} }
this._targetEvent = event; this._targetEvent = event;
if (this.relationType === "m.replace") { if (this.relationType === "m.replace") {
const replacement = this.getLastReplacement(); const replacement = await this.getLastReplacement();
// this is the initial update, so only call it if we already have something // this is the initial update, so only call it if we already have something
// to not emit Event.replaced needlessly // to not emit Event.replaced needlessly
if (replacement) { if (replacement) {
this._targetEvent.makeReplaced(replacement); this._targetEvent.makeReplaced(replacement);
} }
} }
this._maybeEmitCreated();
}
_maybeEmitCreated() {
if (this._creationEmitted) {
return;
}
// Only emit we're "created" once we have a target event instance _and_
// at least one related event.
if (!this._targetEvent || !this._relations.size) {
return;
}
this._creationEmitted = true;
this._targetEvent.emit(
"Event.relationsCreated",
this.relationType,
this.eventType,
);
} }
} }

View File

@@ -203,7 +203,6 @@ RoomMember.prototype.getLastModifiedTime = function() {
return this._modified; return this._modified;
}; };
RoomMember.prototype.isKicked = function() { RoomMember.prototype.isKicked = function() {
return this.membership === "leave" && return this.membership === "leave" &&
this.events.member.getSender() !== this.events.member.getStateKey(); this.events.member.getSender() !== this.events.member.getStateKey();
@@ -240,7 +239,6 @@ RoomMember.prototype.getDMInviter = function() {
} }
}; };
/** /**
* Get the avatar URL for a room member. * Get the avatar URL for a room member.
* @param {string} baseUrl The base homeserver URL See * @param {string} baseUrl The base homeserver URL See

View File

@@ -349,6 +349,11 @@ RoomState.prototype.setStateEvents = function(stateEvents) {
self._updateMember(member); self._updateMember(member);
self.emit("RoomState.members", event, self, member); self.emit("RoomState.members", event, self, member);
} else if (event.getType() === "m.room.power_levels") { } else if (event.getType() === "m.room.power_levels") {
// events with unknown state keys should be ignored
// and should not aggregate onto members power levels
if (event.getStateKey() !== "") {
return;
}
const members = Object.values(self.members); const members = Object.values(self.members);
members.forEach(function(member) { members.forEach(function(member) {
// We only propagate `RoomState.members` event if the // We only propagate `RoomState.members` event if the
@@ -735,7 +740,6 @@ RoomState.prototype.getJoinRule = function() {
return joinRuleContent["join_rule"] || "invite"; return joinRuleContent["join_rule"] || "invite";
}; };
function _updateThirdPartyTokenCache(roomState, memberEvent) { function _updateThirdPartyTokenCache(roomState, memberEvent) {
if (!memberEvent.getContent().third_party_invite) { if (!memberEvent.getContent().third_party_invite) {
return; return;

View File

@@ -31,6 +31,7 @@ import {RoomSummary} from "./room-summary";
import { logger } from '../logger'; import { logger } from '../logger';
import { ReEmitter } from '../ReEmitter'; import { ReEmitter } from '../ReEmitter';
import { EventType, RoomCreateTypeField, RoomType } from "../@types/event"; import { EventType, RoomCreateTypeField, RoomType } from "../@types/event";
import { normalize } from "../utils";
// These constants are used as sane defaults when the homeserver doesn't support // These constants are used as sane defaults when the homeserver doesn't support
// the m.room_versions capability. In practice, KNOWN_SAFE_ROOM_VERSION should be // the m.room_versions capability. In practice, KNOWN_SAFE_ROOM_VERSION should be
@@ -58,7 +59,6 @@ function synthesizeReceipt(userId, event, receiptType) {
return new MatrixEvent(fakeReceipt); return new MatrixEvent(fakeReceipt);
} }
/** /**
* Construct a new Room. * Construct a new Room.
* *
@@ -102,6 +102,7 @@ function synthesizeReceipt(userId, event, receiptType) {
* *
* @prop {string} roomId The ID of this room. * @prop {string} roomId The ID of this room.
* @prop {string} name The human-readable display name for this room. * @prop {string} name The human-readable display name for this room.
* @prop {string} normalizedName The unhomoglyphed name for this room.
* @prop {Array<MatrixEvent>} timeline The live event timeline for this room, * @prop {Array<MatrixEvent>} timeline The live event timeline for this room,
* with the oldest event at index 0. Present for backwards compatibility - * with the oldest event at index 0. Present for backwards compatibility -
* prefer getLiveTimeline().getEvents(). * prefer getLiveTimeline().getEvents().
@@ -216,6 +217,10 @@ export function Room(roomId, client, myUserId, opts) {
} else { } else {
this._membersPromise = null; this._membersPromise = null;
} }
// flags to stop logspam about missing m.room.create events
this.getTypeWarning = false;
this.getVersionWarning = false;
} }
/** /**
@@ -228,7 +233,6 @@ function pendingEventsKey(roomId) {
utils.inherits(Room, EventEmitter); utils.inherits(Room, EventEmitter);
/** /**
* Bulk decrypt critical events in a room * Bulk decrypt critical events in a room
* *
@@ -280,7 +284,10 @@ Room.prototype.decryptAllEvents = function() {
Room.prototype.getVersion = function() { Room.prototype.getVersion = function() {
const createEvent = this.currentState.getStateEvents("m.room.create", ""); const createEvent = this.currentState.getStateEvents("m.room.create", "");
if (!createEvent) { if (!createEvent) {
if (!this.getVersionWarning) {
logger.warn("[getVersion] Room " + this.roomId + " does not have an m.room.create event"); logger.warn("[getVersion] Room " + this.roomId + " does not have an m.room.create event");
this.getVersionWarning = true;
}
return '1'; return '1';
} }
const ver = createEvent.getContent()['room_version']; const ver = createEvent.getContent()['room_version'];
@@ -486,7 +493,6 @@ Room.prototype.getLiveTimeline = function() {
return this.getUnfilteredTimelineSet().getLiveTimeline(); return this.getUnfilteredTimelineSet().getLiveTimeline();
}; };
/** /**
* Get the timestamp of the last message in the room * Get the timestamp of the last message in the room
* *
@@ -631,7 +637,6 @@ Room.prototype._loadMembersFromServer = async function() {
return response.chunk; return response.chunk;
}; };
Room.prototype._loadMembers = async function() { Room.prototype._loadMembers = async function() {
// were the members loaded from the server? // were the members loaded from the server?
let fromServer = false; let fromServer = false;
@@ -1116,7 +1121,6 @@ Room.prototype.getInvitedAndJoinedMemberCount = function() {
return calculateRoomName(this, userId, true); return calculateRoomName(this, userId, true);
}; };
/** /**
* Check if the given user_id has the given membership state. * Check if the given user_id has the given membership state.
* @param {string} userId The user ID to check. * @param {string} userId The user ID to check.
@@ -1273,7 +1277,6 @@ Room.prototype._addLiveEvent = function(event, duplicateStrategy, fromCache) {
} }
}; };
/** /**
* Add a pending outgoing event to this room. * Add a pending outgoing event to this room.
* *
@@ -1684,7 +1687,6 @@ Room.prototype.removeEvent = function(eventId) {
return removedAny; return removedAny;
}; };
/** /**
* Recalculate various aspects of the room, including the room name and * Recalculate various aspects of the room, including the room name and
* room summary. Call this any time the room's current state is modified. * room summary. Call this any time the room's current state is modified.
@@ -1720,6 +1722,7 @@ Room.prototype.recalculate = function() {
const oldName = this.name; const oldName = this.name;
this.name = calculateRoomName(this, this.myUserId); this.name = calculateRoomName(this, this.myUserId);
this.normalizedName = normalize(this.name);
this.summary = new RoomSummary(this.roomId, { this.summary = new RoomSummary(this.roomId, {
title: this.name, title: this.name,
}); });
@@ -1908,7 +1911,6 @@ Room.prototype._buildReceiptCache = function(receipts) {
return receiptCacheByEventId; return receiptCacheByEventId;
}; };
/** /**
* Add a temporary local-echo receipt to the room to reflect in the * Add a temporary local-echo receipt to the room to reflect in the
* client the fact that we've sent one. * client the fact that we've sent one.
@@ -1966,7 +1968,6 @@ Room.prototype.getAccountData = function(type) {
return this.accountData[type]; return this.accountData[type];
}; };
/** /**
* Returns whether the syncing user has permission to send a message in the room * Returns whether the syncing user has permission to send a message in the room
* @return {boolean} true if the user should be permitted to send * @return {boolean} true if the user should be permitted to send
@@ -2008,7 +2009,10 @@ Room.prototype.getJoinRule = function() {
Room.prototype.getType = function() { Room.prototype.getType = function() {
const createEvent = this.currentState.getStateEvents("m.room.create", ""); const createEvent = this.currentState.getStateEvents("m.room.create", "");
if (!createEvent) { if (!createEvent) {
if (!this.getTypeWarning) {
logger.warn("[getType] Room " + this.roomId + " does not have an m.room.create event"); logger.warn("[getType] Room " + this.roomId + " does not have an m.room.create event");
this.getTypeWarning = true;
}
return undefined; return undefined;
} }
return createEvent.getContent()[RoomCreateTypeField]; return createEvent.getContent()[RoomCreateTypeField];

View File

@@ -139,7 +139,6 @@ User.prototype.setDisplayName = function(name) {
} }
}; };
/** /**
* Manually set this user's non-disambiguated display name. No event is emitted * Manually set this user's non-disambiguated display name. No event is emitted
* in response to this as there is no underlying MatrixEvent to emit with. * in response to this as there is no underlying MatrixEvent to emit with.
@@ -153,7 +152,6 @@ User.prototype.setRawDisplayName = function(name) {
} }
}; };
/** /**
* Manually set this user's avatar URL. No event is emitted in response to this * Manually set this user's avatar URL. No event is emitted in response to this
* as there is no underlying MatrixEvent to emit with. * as there is no underlying MatrixEvent to emit with.

View File

@@ -360,7 +360,6 @@ export function PushProcessor(client) {
return ret; return ret;
}; };
/** /**
* Get the user's push actions for the given event * Get the user's push actions for the given event
* *
@@ -468,4 +467,3 @@ PushProcessor.rewriteDefaultRules = function(incomingRules) {
* noise. * noise.
*/ */

View File

@@ -177,7 +177,6 @@ function _runCallbacks() {
} }
} }
/* search in a sorted array. /* search in a sorted array.
* *
* returns the index of the last element for which func returns * returns the index of the last element for which func returns

View File

@@ -93,7 +93,6 @@ MatrixScheduler.prototype.removeEventFromQueue = function(event) {
return removed; return removed;
}; };
/** /**
* Set the process function. Required for events in the queue to be processed. * Set the process function. Required for events in the queue to be processed.
* If set after events have been added to the queue, this will immediately start * If set after events have been added to the queue, this will immediately start

View File

@@ -48,7 +48,6 @@ function upgradeSchemaV3(db) {
{ keyPath: ["clobber"] }); { keyPath: ["clobber"] });
} }
/** /**
* Helper method to collect results from a Cursor and promiseify it. * Helper method to collect results from a Cursor and promiseify it.
* @param {ObjectStore|Index} store The store to perform openCursor on. * @param {ObjectStore|Index} store The store to perform openCursor on.

View File

@@ -48,7 +48,6 @@ export function RemoteIndexedDBStoreBackend(
this._startPromise = null; this._startPromise = null;
} }
RemoteIndexedDBStoreBackend.prototype = { RemoteIndexedDBStoreBackend.prototype = {
/** /**
* Attempt to connect to the database. This can fail if the user does not * Attempt to connect to the database. This can fail if the user does not

View File

@@ -39,7 +39,6 @@ import {logger} from '../logger';
// response is persisted each time. // response is persisted each time.
const WRITE_DELAY_MS = 1000 * 60 * 5; // once every 5 minutes const WRITE_DELAY_MS = 1000 * 60 * 5; // once every 5 minutes
/** /**
* Construct a new Indexed Database store, which extends MemoryStore. * Construct a new Indexed Database store, which extends MemoryStore.
* *

View File

@@ -62,7 +62,6 @@ function debuglog(...params) {
logger.log(...params); logger.log(...params);
} }
/** /**
* <b>Internal class - unstable.</b> * <b>Internal class - unstable.</b>
* Construct an entity which is able to sync with a homeserver. * Construct an entity which is able to sync with a homeserver.
@@ -188,7 +187,6 @@ SyncApi.prototype._deregisterStateListeners = function(room) {
room.currentState.removeAllListeners("RoomState.newMember"); room.currentState.removeAllListeners("RoomState.newMember");
}; };
/** /**
* Sync rooms the user has left. * Sync rooms the user has left.
* @return {Promise} Resolved when they've been added to the store. * @return {Promise} Resolved when they've been added to the store.

View File

@@ -335,7 +335,6 @@ TimelineWindow.prototype.paginate = function(direction, size, makeRequest,
return prom; return prom;
}; };
/** /**
* Remove `delta` events from the start or end of the timeline. * Remove `delta` events from the start or end of the timeline.
* *
@@ -368,7 +367,6 @@ TimelineWindow.prototype.unpaginate = function(delta, startOfTimeline) {
} }
}; };
/** /**
* Get a list of the events currently in the window * Get a list of the events currently in the window
* *
@@ -420,7 +418,6 @@ TimelineWindow.prototype.getEvents = function() {
return result; return result;
}; };
/** /**
* a thing which contains a timeline reference, and an index into it. * a thing which contains a timeline reference, and an index into it.
* *

View File

@@ -346,6 +346,16 @@ export function removeHiddenChars(str: string): string {
return ""; return "";
} }
export function normalize(str: string): string {
// Note: we have to match the filter with the removeHiddenChars() because the
// function strips spaces and other characters (M becomes RN for example, in lowercase).
return removeHiddenChars(str.toLowerCase())
// Strip all punctuation
.replace(/[\\'!"#$%&()*+,\-./:;<=>?@[\]^_`{|}~\u2000-\u206f\u2e00-\u2e7f]/g, "")
// We also doubly convert to lowercase to work around oddities of the library.
.toLowerCase();
}
// Regex matching bunch of unicode control characters and otherwise misleading/invisible characters. // Regex matching bunch of unicode control characters and otherwise misleading/invisible characters.
// Includes: // Includes:
// various width spaces U+2000 - U+200D // various width spaces U+2000 - U+200D

View File

@@ -37,7 +37,6 @@ import {
} from './callEventTypes'; } from './callEventTypes';
import { CallFeed } from './callFeed'; import { CallFeed } from './callFeed';
// events: hangup, error(err), replaced(call), state(state, oldState) // events: hangup, error(err), replaced(call), state(state, oldState)
/** /**
@@ -189,6 +188,11 @@ export enum CallErrorCode {
* Signalling for the call could not be sent (other than the initial invite) * Signalling for the call could not be sent (other than the initial invite)
*/ */
SignallingFailed = 'signalling_timeout', SignallingFailed = 'signalling_timeout',
/**
* The remote party is busy
*/
UserBusy = 'user_busy'
} }
enum ConstraintsType { enum ConstraintsType {
@@ -643,7 +647,7 @@ export class MatrixCall extends EventEmitter {
// Continue to send no reason for user hangups temporarily, until // Continue to send no reason for user hangups temporarily, until
// clients understand the user_hangup reason (voip v1) // clients understand the user_hangup reason (voip v1)
if (reason !== CallErrorCode.UserHangup) content['reason'] = reason; if (reason !== CallErrorCode.UserHangup) content['reason'] = reason;
this.sendVoipEvent(EventType.CallHangup, {}); this.sendVoipEvent(EventType.CallHangup, content);
} }
/** /**
@@ -1102,21 +1106,8 @@ export class MatrixCall extends EventEmitter {
await this.peerConn.setRemoteDescription(description); await this.peerConn.setRemoteDescription(description);
if (description.type === 'offer') { if (description.type === 'offer') {
// First we sent the direction of the tranciever to what we'd like it to be,
// irresepective of whether the other side has us on hold - so just whether we
// want the call to be on hold or not. This is necessary because in a few lines,
// we'll adjust the direction and unless we do this too, we'll never come off hold.
for (const tranceiver of this.peerConn.getTransceivers()) {
tranceiver.direction = this.isRemoteOnHold() ? 'inactive' : 'sendrecv';
}
const localDescription = await this.peerConn.createAnswer(); const localDescription = await this.peerConn.createAnswer();
await this.peerConn.setLocalDescription(localDescription); await this.peerConn.setLocalDescription(localDescription);
// Now we've got our answer, set the direction to the outcome of the negotiation.
// We need to do this otherwise Firefox will notice that the direction is not the
// currentDirection and try to negotiate itself off hold again.
for (const tranceiver of this.peerConn.getTransceivers()) {
tranceiver.direction = tranceiver.currentDirection;
}
this.sendVoipEvent(EventType.CallNegotiate, { this.sendVoipEvent(EventType.CallNegotiate, {
description: this.peerConn.localDescription, description: this.peerConn.localDescription,
@@ -1375,7 +1366,7 @@ export class MatrixCall extends EventEmitter {
); );
if (shouldTerminate) { if (shouldTerminate) {
this.terminate(CallParty.Remote, CallErrorCode.UserHangup, true); this.terminate(CallParty.Remote, msg.reason || CallErrorCode.UserHangup, true);
} else { } else {
logger.debug(`Call is in state: ${this.state}: ignoring reject`); logger.debug(`Call is in state: ${this.state}: ignoring reject`);
} }
@@ -1804,7 +1795,10 @@ export function createNewMatrixCall(client: any, roomId: string, options?: CallO
window.RTCIceCandidate || navigator.mediaDevices, window.RTCIceCandidate || navigator.mediaDevices,
); );
if (!supported) { if (!supported) {
// Adds a lot of noise to test runs, so disable logging there.
if (process.env.NODE_ENV !== "test") {
logger.error("WebRTC is not supported in this browser / environment"); logger.error("WebRTC is not supported in this browser / environment");
}
return null; return null;
} }
} catch (e) { } catch (e) {

View File

@@ -43,6 +43,9 @@ export class CallEventHandler {
// after loading and after we've been offline for a bit. // after loading and after we've been offline for a bit.
this.callEventBuffer = []; this.callEventBuffer = [];
this.candidateEventsByCall = new Map<string, Array<MatrixEvent>>(); this.candidateEventsByCall = new Map<string, Array<MatrixEvent>>();
}
public start() {
this.client.on("sync", this.evaluateEventBuffer); this.client.on("sync", this.evaluateEventBuffer);
this.client.on("event", this.onEvent); this.client.on("event", this.onEvent);
} }
@@ -52,10 +55,11 @@ export class CallEventHandler {
this.client.removeListener("event", this.onEvent); this.client.removeListener("event", this.onEvent);
} }
private evaluateEventBuffer = () => { private evaluateEventBuffer = async () => {
if (this.client.getSyncState() === "SYNCING") { if (this.client.getSyncState() === "SYNCING") {
// don't process any events until they are all decrypted await Promise.all(this.callEventBuffer.map(event => {
if (this.callEventBuffer.some((e) => e.isBeingDecrypted())) return; this.client.decryptEventIfNeeded(event);
}));
const ignoreCallIds = new Set<String>(); const ignoreCallIds = new Set<String>();
// inspect the buffer and mark all calls which have been answered // inspect the buffer and mark all calls which have been answered
@@ -86,12 +90,9 @@ export class CallEventHandler {
} }
private onEvent = (event: MatrixEvent) => { private onEvent = (event: MatrixEvent) => {
this.client.decryptEventIfNeeded(event);
// any call events or ones that might be once they're decrypted // any call events or ones that might be once they're decrypted
if ( if (this.eventIsACall(event) || event.isBeingDecrypted()) {
event.getType().indexOf("m.call.") === 0 ||
event.getType().indexOf("org.matrix.call.") === 0
|| event.isBeingDecrypted()
) {
// queue up for processing once all events from this sync have been // queue up for processing once all events from this sync have been
// processed (see above). // processed (see above).
this.callEventBuffer.push(event); this.callEventBuffer.push(event);
@@ -100,7 +101,7 @@ export class CallEventHandler {
if (event.isBeingDecrypted() || event.isDecryptionFailure()) { if (event.isBeingDecrypted() || event.isDecryptionFailure()) {
// add an event listener for once the event is decrypted. // add an event listener for once the event is decrypted.
event.once("Event.decrypted", () => { event.once("Event.decrypted", () => {
if (event.getType().indexOf("m.call.") === -1) return; if (!this.eventIsACall(event)) return;
if (this.callEventBuffer.includes(event)) { if (this.callEventBuffer.includes(event)) {
// we were waiting for that event to decrypt, so recheck the buffer // we were waiting for that event to decrypt, so recheck the buffer
@@ -118,6 +119,15 @@ export class CallEventHandler {
} }
} }
private eventIsACall(event: MatrixEvent): boolean {
const type = event.getType();
/**
* Unstable prefixes:
* - org.matrix.call. : MSC3086 https://github.com/matrix-org/matrix-doc/pull/3086
*/
return type.startsWith("m.call.") || type.startsWith("org.matrix.call.");
}
private handleCallEvent(event: MatrixEvent) { private handleCallEvent(event: MatrixEvent) {
const content = event.getContent(); const content = event.getContent();
let call = content.call_id ? this.calls.get(content.call_id) : undefined; let call = content.call_id ? this.calls.get(content.call_id) : undefined;

View File

@@ -1130,6 +1130,10 @@
"@types/yargs" "^15.0.0" "@types/yargs" "^15.0.0"
chalk "^4.0.0" chalk "^4.0.0"
"@matrix-org/olm@https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz":
version "3.2.3"
resolved "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz#cc332fdd25c08ef0e40f4d33fc3f822a0f98b6f4"
"@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents": "@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents":
version "2.1.8-no-fsevents" version "2.1.8-no-fsevents"
resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.tgz#da7c3996b8e6e19ebd14d82eaced2313e7769f9b" resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.tgz#da7c3996b8e6e19ebd14d82eaced2313e7769f9b"
@@ -2030,15 +2034,15 @@ browserify@^17.0.0:
xtend "^4.0.0" xtend "^4.0.0"
browserslist@^4.14.5, browserslist@^4.16.1: browserslist@^4.14.5, browserslist@^4.16.1:
version "4.16.1" version "4.16.6"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.1.tgz#bf757a2da376b3447b800a16f0f1c96358138766" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2"
integrity sha512-UXhDrwqsNcpTYJBTZsbGATDxZbiVDsx6UjpmRUmtnP10pr8wAYr5LgFoEFw9ixriQH2mv/NX2SfGzE/o8GndLA== integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==
dependencies: dependencies:
caniuse-lite "^1.0.30001173" caniuse-lite "^1.0.30001219"
colorette "^1.2.1" colorette "^1.2.2"
electron-to-chromium "^1.3.634" electron-to-chromium "^1.3.723"
escalade "^3.1.1" escalade "^3.1.1"
node-releases "^1.1.69" node-releases "^1.1.71"
bs58@^4.0.1: bs58@^4.0.1:
version "4.0.1" version "4.0.1"
@@ -2125,10 +2129,10 @@ camelcase@^6.0.0:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809"
integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==
caniuse-lite@^1.0.30001173: caniuse-lite@^1.0.30001219:
version "1.0.30001178" version "1.0.30001230"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001178.tgz#3ad813b2b2c7d585b0be0a2440e1e233c6eabdbc" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001230.tgz#8135c57459854b2240b57a4a6786044bdc5a9f71"
integrity sha512-VtdZLC0vsXykKni8Uztx45xynytOi71Ufx9T8kHptSw9AL4dpqailUJJHavttuzUe1KYuBYtChiWv+BAb7mPmQ== integrity sha512-5yBd5nWCBS+jWKTcHOzXwo5xzcj4ePE/yjtkZyUV1BTUmrBaA9MRGC+e7mxnqXSA90CmCA8L3eKLaSUkt099IQ==
capture-exit@^2.0.0: capture-exit@^2.0.0:
version "2.0.0" version "2.0.0"
@@ -2296,10 +2300,10 @@ color-name@~1.1.4:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
colorette@^1.2.1: colorette@^1.2.2:
version "1.2.1" version "1.2.2"
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b" resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94"
integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw== integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==
combine-source-map@^0.8.0, combine-source-map@~0.8.0: combine-source-map@^0.8.0, combine-source-map@~0.8.0:
version "0.8.0" version "0.8.0"
@@ -2711,10 +2715,10 @@ ecc-jsbn@~0.1.1:
jsbn "~0.1.0" jsbn "~0.1.0"
safer-buffer "^2.1.0" safer-buffer "^2.1.0"
electron-to-chromium@^1.3.634: electron-to-chromium@^1.3.723:
version "1.3.642" version "1.3.738"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.642.tgz#8b884f50296c2ae2a9997f024d0e3e57facc2b94" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.738.tgz#aec24b091c82acbfabbdcce08076a703941d17ca"
integrity sha512-cev+jOrz/Zm1i+Yh334Hed6lQVOkkemk2wRozfMF4MtTR7pxf3r3L5Rbd7uX1zMcEqVJ7alJBnJL7+JffkC6FQ== integrity sha512-vCMf4gDOpEylPSLPLSwAEsz+R3ShP02Y3cAKMZvTqule3XcPp7tgc/0ESI7IS6ZeyBlGClE50N53fIOkcIVnpw==
elliptic@^6.5.3: elliptic@^6.5.3:
version "6.5.4" version "6.5.4"
@@ -2841,8 +2845,8 @@ eslint-config-google@^0.14.0:
integrity sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw== integrity sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw==
"eslint-plugin-matrix-org@github:matrix-org/eslint-plugin-matrix-org#main": "eslint-plugin-matrix-org@github:matrix-org/eslint-plugin-matrix-org#main":
version "0.2.0" version "0.3.1"
resolved "https://codeload.github.com/matrix-org/eslint-plugin-matrix-org/tar.gz/0ae103fe9af97655be6039fc1e7ad6ea95da310b" resolved "https://codeload.github.com/matrix-org/eslint-plugin-matrix-org/tar.gz/b55649a0f48ee27155c1968ed5050b6ddc5afdbe"
eslint-rule-composer@^0.3.0: eslint-rule-composer@^0.3.0:
version "0.3.0" version "0.3.0"
@@ -5033,10 +5037,10 @@ node-notifier@^8.0.0:
uuid "^8.3.0" uuid "^8.3.0"
which "^2.0.2" which "^2.0.2"
node-releases@^1.1.69: node-releases@^1.1.71:
version "1.1.70" version "1.1.72"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.70.tgz#66e0ed0273aa65666d7fe78febe7634875426a08" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.72.tgz#14802ab6b1039a79a0c7d662b610a5bbd76eacbe"
integrity sha512-Slf2s69+2/uAD79pVVQo8uSiC34+g8GWY8UH2Qtqv34ZfhYrxpYpfzs9Js9d6O0mbDmALuxaTlplnBTnSELcrw== integrity sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==
normalize-package-data@^2.5.0: normalize-package-data@^2.5.0:
version "2.5.0" version "2.5.0"
@@ -5142,10 +5146,6 @@ object.pick@^1.3.0:
dependencies: dependencies:
isobject "^3.0.1" isobject "^3.0.1"
"olm@https://packages.matrix.org/npm/olm/olm-3.2.1.tgz":
version "3.2.1"
resolved "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz#d623d76f99c3518dde68be8c86618d68bc7b004a"
once@^1.3.0, once@^1.3.1, once@^1.4.0: once@^1.3.0, once@^1.3.1, once@^1.4.0:
version "1.4.0" version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
@@ -7135,9 +7135,9 @@ write-file-atomic@^3.0.0:
typedarray-to-buffer "^3.1.5" typedarray-to-buffer "^3.1.5"
ws@^7.2.3: ws@^7.2.3:
version "7.4.2" version "7.4.6"
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.2.tgz#782100048e54eb36fe9843363ab1c68672b261dd" resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c"
integrity sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA== integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==
xml-name-validator@^3.0.0: xml-name-validator@^3.0.0:
version "3.0.0" version "3.0.0"