Compare commits

...

56 Commits

Author SHA1 Message Date
RiotRobot 8b3126e9d8 v8.1.0-rc.1 2020-08-13 12:05:35 +01:00
RiotRobot 74d497cd2d Prepare changelog for v8.1.0-rc.1 2020-08-13 12:05:35 +01:00
Michael Telatynski 5070a5c598 Merge pull request #1438 from brettz9/patch-1
Update on Promises
2020-08-09 13:11:51 +01:00
Brett Zamir 2e30b08e74 Update on Promises
It seems that with ES6 Promises being apparently used, the reference to `done()` is outdated.

Also clarifies that it is "problematic" to discard the results of promises rather than it being an "error" .
2020-08-09 17:39:53 +08:00
J. Ryan Stinnett 8bf63f5f0b Merge pull request #1437 from matrix-org/jryans/defer-cross-signing-setup
Store and request master cross-signing key
2020-08-07 16:06:28 +01:00
J. Ryan Stinnett 11665d18ee Cache all cross-signing private keys when checking trust
This ensures we try to get all private keys when e.g. logging in and using the
passphrase to unlock 4S.

Part of https://github.com/vector-im/element-web/issues/13896
2020-08-06 16:43:56 +01:00
J. Ryan Stinnett a8a9fc0c9d Log secret name received via sharing 2020-08-06 16:43:56 +01:00
J. Ryan Stinnett 098cd1b8d4 Request master cross-signing private key during verification
This change adds a request for the master cross-signing private key, in case the
other device is willing to share it.

Part of https://github.com/vector-im/element-web/issues/13896
2020-08-06 16:43:56 +01:00
J. Ryan Stinnett 3166a4880d Move cross-signing key request path out of verification
This was always quite awkwardly placed in verification, so this moves it to
somewhere more expected. No changes are made to the logic or features in this
commit.
2020-08-06 16:39:56 +01:00
J. Ryan Stinnett 9d1c7136cc Store master cross-signing private key in local cache
We now want to store all private keys in the local cache, including the master
key if available.

Part of https://github.com/vector-im/element-web/issues/13896
2020-08-06 16:39:56 +01:00
J. Ryan Stinnett e100943edf Fix typo in comments 2020-08-06 16:24:49 +01:00
RiotRobot a6fe4cdf1c Merge branch 'master' into develop 2020-08-05 15:30:42 +01:00
RiotRobot 8b5213c09a v8.0.1 2020-08-05 15:27:22 +01:00
RiotRobot 23a133c825 Prepare changelog for v8.0.1 2020-08-05 15:27:22 +01:00
J. Ryan Stinnett 69c4496dfe Merge pull request #1433 from matrix-org/bwindels/fixinvaliddisplaynames
Filter out non-string display names
2020-08-05 15:03:53 +01:00
Bruno Windels 4d74cca206 Merge pull request #1434 from matrix-org/bwindels/fixinvaliddisplaynames-release
Filter out non-string display names
2020-08-04 13:55:40 +00:00
Bruno Windels 955e081699 respect signature 2020-08-04 15:48:48 +02:00
Bruno Windels b7e73422ab filter out non-string display names 2020-08-04 15:48:48 +02:00
Bruno Windels a9a4ba33aa respect signature 2020-08-04 15:43:57 +02:00
Bruno Windels 7116ad9f58 filter out non-string display names 2020-08-04 15:30:32 +02:00
J. Ryan Stinnett 3d18bdb2aa Merge pull request #1427 from matrix-org/dependabot/npm_and_yarn/elliptic-6.5.3
Bump elliptic from 6.5.2 to 6.5.3
2020-08-04 10:38:10 +01:00
J. Ryan Stinnett 4c14581606 Merge pull request #1431 from matrix-org/jryans/riot-to-element
Replace Riot with Element in docs and comments
2020-08-04 10:27:32 +01:00
J. Ryan Stinnett a9c9ec3977 Replace Riot with Element in docs and comments
This only covers the simple cases of references to issues and repos. More
complex areas, such as deployment scripts, will be handled separately.

Part of https://github.com/vector-im/element-web/issues/14864
2020-08-03 18:32:52 +01:00
J. Ryan Stinnett 694d1f9631 Merge pull request #1430 from matrix-org/jryans/rm-tslint
Remove leftover bits of TSLint
2020-07-31 20:30:44 +01:00
J. Ryan Stinnett 2b9cfae18a Remove leftover bits of TSLint 2020-07-31 14:31:19 +01:00
RiotRobot 800d8380ce v8.0.1-rc.1 2020-07-31 13:18:04 +01:00
RiotRobot 621ca28f68 Prepare changelog for v8.0.1-rc.1 2020-07-31 13:18:03 +01:00
J. Ryan Stinnett 4792241ff6 Merge pull request #1426 from matrix-org/jryans/clean-lint-deps
Remove redundant lint dependencies
2020-07-31 11:42:21 +01:00
David Baker 0b984df5f9 Merge pull request #1428 from matrix-org/dbkr/upload_keys_when_new_key_backup_seen
Upload all keys when we start using a new key backup version
2020-07-30 22:46:39 +01:00
David Baker 2e260155ea Merge pull request #1429 from matrix-org/dbkr/countSessionsNeedingBackup
Expose countSessionsNeedingBackup
2020-07-30 22:46:22 +01:00
David Baker b15c8a2d1c Expose countSessionsNeedingBackup
Useful to see the number of keys waiting for backup (it's exposed
via events but you couldn;t get it directly). Also clarify doc on
the return value of `flagAllGroupSessionsForBackup` which was not
technically incorrect...
2020-07-30 19:05:45 +01:00
David Baker 94ab317f23 Upload all keys when we start using a new key backup version
Although see comment and other bug created

Fixes https://github.com/vector-im/riot-web/issues/14828
2020-07-30 18:08:48 +01:00
dependabot[bot] 02b37e1219 Bump elliptic from 6.5.2 to 6.5.3
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.2 to 6.5.3.
- [Release notes](https://github.com/indutny/elliptic/releases)
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.2...v6.5.3)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-30 12:41:01 +00:00
J. Ryan Stinnett 9d25848a21 Remove unused Jest lint plugin 2020-07-29 11:55:50 +01:00
J. Ryan Stinnett 3958768e1f Remove redundant lint dependencies
These are no longer needed with the new standard lint repo.
2020-07-29 11:36:57 +01:00
Jorik Schellekens cec00cd303 Merge pull request #1422 from matrix-org/joriks/conigure-eslint
Configure and use new eslint package
2020-07-28 19:53:54 +01:00
Jorik Schellekens 45cfef4294 New lines for let statements 2020-07-28 14:31:21 +01:00
Jorik Schellekens 2a01e99635 Merge branch 'develop' of github.com:matrix-org/matrix-js-sdk into joriks/conigure-eslint 2020-07-28 14:17:02 +01:00
Jorik Schellekens a9c3aee447 Revert quotes change in ts 2020-07-28 14:16:46 +01:00
RiotRobot c669382e12 v8.0.0 2020-07-27 20:50:09 +01:00
RiotRobot 19251c427a Prepare changelog for v8.0.0 2020-07-27 20:50:08 +01:00
Michael Telatynski a7aec9e2a4 Merge pull request #1424 from matrix-org/t3chguy/txnId
Properly support txnId
2020-07-24 00:01:31 +01:00
Michael Telatynski 99d7622b42 Properly support txnId
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2020-07-22 11:09:07 +01:00
Michael Telatynski 8728619d2c Merge pull request #1423 from matrix-org/t3chguy/depr/1
[BREAKING] Remove deprecated getIdenticonUri
2020-07-21 21:35:30 +01:00
Michael Telatynski f6d51fdfb8 remove outdated identicon tests
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2020-07-21 21:30:25 +01:00
Michael Telatynski 0ffaa8d617 Remove deprecated getIdenticonUri
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2020-07-21 21:18:17 +01:00
Jorik Schellekens 8a974172ab Fix babel 2020-07-21 10:40:02 +01:00
Jorik Schellekens 72675f7266 Lint ts 2020-07-21 10:33:16 +01:00
Jorik Schellekens 0f559050d8 Fix whitespace issues 2020-07-21 10:00:16 +01:00
Jorik Schellekens 58084774bf fix lock 2020-07-21 09:56:17 +01:00
Travis Ralston c7aee7cebd Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/lodash-4.17.19' into develop 2020-07-20 15:47:57 -06:00
Jorik Schellekens c68d135ae8 Use new eslint 2020-07-20 21:48:28 +01:00
dependabot[bot] 9ed6c99ec8 Bump lodash from 4.17.15 to 4.17.19
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-16 19:28:57 +00:00
Travis Ralston 7d398d41d0 Merge pull request #1419 from matrix-org/travis/general/perf/js-roomstate-map
[BREAKING] Convert RoomState's stored state map to a real map
2020-07-16 12:42:31 -06:00
Travis Ralston 0af763252c Have the comment represent reality 2020-07-16 12:42:17 -06:00
Travis Ralston 1c9dbbbb19 [BREAKING] Convert RoomState's stored state map to a real map
Though the dictionary format works fine, it's slow. Access times are around the 1ms range for rooms like HQ, which doesn't seem like much, but when compared to the Map's access time of 0.05ms it's slow.

Converting things to a map means we lose index access and have to instead use `.keys()` and `.values()`, both of which return iterables and not arrays. Even doing the `Array.from()` conversion we see times in the 0.05ms range. This is also what makes this a breaking change.

Memory-wise there does not appear to be any measurable impact for a large account.
2020-07-08 22:28:14 -06:00
44 changed files with 1274 additions and 749 deletions
+21 -68
View File
@@ -1,71 +1,15 @@
module.exports = {
parser: "babel-eslint", // now needed for class properties
parserOptions: {
sourceType: "module",
ecmaFeatures: {
}
},
extends: ["matrix-org"],
plugins: [
"babel",
],
env: {
browser: true,
node: true,
// babel's transform-runtime converts references to ES6 globals such as
// Promise and Map to core-js polyfills, so we can use ES6 globals.
es6: true,
jest: true,
},
extends: ["eslint:recommended", "google"],
plugins: [
"babel",
"jest",
],
rules: {
// rules we've always adhered to or now do
"max-len": ["error", {
code: 90,
ignoreComments: true,
}],
curly: ["error", "multi-line"],
"prefer-const": ["error"],
"comma-dangle": ["error", {
arrays: "always-multiline",
objects: "always-multiline",
imports: "always-multiline",
exports: "always-multiline",
functions: "always-multiline",
}],
// loosen jsdoc requirements a little
"require-jsdoc": ["error", {
require: {
FunctionDeclaration: false,
}
}],
"valid-jsdoc": ["error", {
requireParamDescription: false,
requireReturn: false,
requireReturnDescription: false,
}],
// rules we do not want from eslint-recommended
"no-console": ["off"],
"no-constant-condition": ["off"],
"no-empty": ["error", { "allowEmptyCatch": true }],
// rules we do not want from the google styleguide
"object-curly-spacing": ["off"],
"spaced-comment": ["off"],
"guard-for-in": ["off"],
// in principle we prefer single quotes, but life is too short
quotes: ["off"],
// rules we'd ideally like to adhere to, but the current
// code does not (in most cases because it's still ES5)
// we set these to warnings, and assert that the number
// of warnings doesn't exceed a given threshold
"no-var": ["warn"],
"brace-style": ["warn", "1tbs", {"allowSingleLine": true}],
"prefer-rest-params": ["warn"],
"prefer-spread": ["warn"],
"one-var": ["warn"],
@@ -79,10 +23,19 @@ module.exports = {
"asyncArrow": "always",
}],
"arrow-parens": "off",
// eslint's built in no-invalid-this rule breaks with class properties
"no-invalid-this": "off",
// so we replace it with a version that is class property aware
"babel/no-invalid-this": "error",
}
}
"prefer-promise-reject-errors": "off",
"quotes": "off",
"indent": "off",
"no-constant-condition": "off",
"no-async-promise-executor": "off",
},
overrides: [{
"files": ["src/**/*.{ts, tsx}"],
"extends": ["matrix-org/ts"],
"rules": {
// While we're converting to ts we make heavy use of this
"@typescript-eslint/no-explicit-any": "off",
"quotes": "off",
},
}],
};
+58
View File
@@ -1,3 +1,61 @@
Changes in [8.1.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v8.1.0-rc.1) (2020-08-13)
==========================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v8.0.1...v8.1.0-rc.1)
* Update on Promises
[\#1438](https://github.com/matrix-org/matrix-js-sdk/pull/1438)
* Store and request master cross-signing key
[\#1437](https://github.com/matrix-org/matrix-js-sdk/pull/1437)
* Filter out non-string display names
[\#1433](https://github.com/matrix-org/matrix-js-sdk/pull/1433)
* Bump elliptic from 6.5.2 to 6.5.3
[\#1427](https://github.com/matrix-org/matrix-js-sdk/pull/1427)
* Replace Riot with Element in docs and comments
[\#1431](https://github.com/matrix-org/matrix-js-sdk/pull/1431)
* Remove leftover bits of TSLint
[\#1430](https://github.com/matrix-org/matrix-js-sdk/pull/1430)
Changes in [8.0.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v8.0.1) (2020-08-05)
================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v8.0.1-rc.1...v8.0.1)
* Filter out non-string display names
[\#1434](https://github.com/matrix-org/matrix-js-sdk/pull/1434)
Changes in [8.0.1-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v8.0.1-rc.1) (2020-07-31)
==========================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v8.0.0...v8.0.1-rc.1)
* Remove redundant lint dependencies
[\#1426](https://github.com/matrix-org/matrix-js-sdk/pull/1426)
* Upload all keys when we start using a new key backup version
[\#1428](https://github.com/matrix-org/matrix-js-sdk/pull/1428)
* Expose countSessionsNeedingBackup
[\#1429](https://github.com/matrix-org/matrix-js-sdk/pull/1429)
* Configure and use new eslint package
[\#1422](https://github.com/matrix-org/matrix-js-sdk/pull/1422)
Changes in [8.0.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v8.0.0) (2020-07-27)
================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v7.1.0...v8.0.0)
BREAKING CHANGES
---
* `RoomState` events changed to use a Map instead of an object, which changes the collection APIs available to access them.
All Changes
---
* Properly support txnId
[\#1424](https://github.com/matrix-org/matrix-js-sdk/pull/1424)
* [BREAKING] Remove deprecated getIdenticonUri
[\#1423](https://github.com/matrix-org/matrix-js-sdk/pull/1423)
* Bump lodash from 4.17.15 to 4.17.19
[\#1421](https://github.com/matrix-org/matrix-js-sdk/pull/1421)
* [BREAKING] Convert RoomState's stored state map to a real map
[\#1419](https://github.com/matrix-org/matrix-js-sdk/pull/1419)
Changes in [7.1.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v7.1.0) (2020-07-03)
================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v7.1.0-rc.1...v7.1.0)
+2 -4
View File
@@ -182,10 +182,8 @@ you can pass the result of the promise into it with something like:
matrixClient.someMethod(arg1, arg2).nodeify(callback);
```
The main thing to note is that it is an error to discard the result of a
promise-returning function, as that will cause exceptions to go unobserved. If
you have nothing better to do with the result, just call ``.done()`` on it. See
http://documentup.com/kriskowal/q/#the-end for more information.
The main thing to note is that it is problematic to discard the result of a
promise-returning function, as that will cause exceptions to go unobserved.
Methods which return a promise show this in their documentation.
+5 -4
View File
@@ -288,7 +288,7 @@ function printMemberList(room) {
}
function printRoomInfo(room) {
var eventDict = room.currentState.events;
var eventMap = room.currentState.events;
var eTypeHeader = " Event Type(state_key) ";
var sendHeader = " Sender ";
// pad content to 100
@@ -300,14 +300,15 @@ function printRoomInfo(room) {
var contentHeader = padSide + "Content" + padSide;
print(eTypeHeader+sendHeader+contentHeader);
print(new Array(100).join("-"));
Object.keys(eventDict).forEach(function(eventType) {
eventMap.keys().forEach(function(eventType) {
if (eventType === "m.room.member") { return; } // use /members instead.
Object.keys(eventDict[eventType]).forEach(function(stateKey) {
var eventEventMap = eventMap.get(eventType);
eventEventMap.keys().forEach(function(stateKey) {
var typeAndKey = eventType + (
stateKey.length > 0 ? "("+stateKey+")" : ""
);
var typeStr = fixWidth(typeAndKey, eTypeHeader.length);
var event = eventDict[eventType][stateKey];
var event = eventEventMap.get(stateKey);
var sendStr = fixWidth(event.getSender(), sendHeader.length);
var contentStr = fixWidth(
JSON.stringify(event.getContent()), contentHeader.length
+5 -8
View File
@@ -1,6 +1,6 @@
{
"name": "matrix-js-sdk",
"version": "7.1.0",
"version": "8.1.0-rc.1",
"description": "Matrix Client-Server SDK for Javascript",
"scripts": {
"prepare": "yarn build",
@@ -13,10 +13,9 @@
"build:compile-browser": "mkdirp dist && browserify -d src/browser-index.js -p [ tsify -p ./tsconfig.json ] -t [ babelify --sourceMaps=inline --presets [ @babel/preset-env @babel/preset-typescript ] ] | exorcist dist/browser-matrix.js.map > dist/browser-matrix.js",
"build:minify-browser": "terser dist/browser-matrix.js --compress --mangle --source-map --output dist/browser-matrix.min.js",
"gendoc": "jsdoc -c jsdoc.json -P package.json",
"lint": "yarn lint:types && yarn lint:ts && yarn lint:js",
"lint:js": "eslint --max-warnings 81 src spec",
"lint": "yarn lint:types && yarn lint:js",
"lint:js": "eslint --max-warnings 76 src spec",
"lint:types": "tsc --noEmit",
"lint:ts": "tslint --project ./tsconfig.json -t stylish",
"test": "jest spec/ --coverage --testEnvironment node",
"test:watch": "jest spec/ --coverage --testEnvironment node --watch"
},
@@ -75,10 +74,9 @@
"babelify": "^10.0.0",
"better-docs": "^1.4.7",
"browserify": "^16.5.0",
"eslint": "^5.12.0",
"eslint-config-google": "^0.7.1",
"eslint": "7.3.1",
"eslint-config-matrix-org": "^0.1.2",
"eslint-plugin-babel": "^5.3.0",
"eslint-plugin-jest": "^23.0.4",
"exorcist": "^1.0.1",
"fake-indexeddb": "^3.0.0",
"jest": "^24.9.0",
@@ -89,7 +87,6 @@
"rimraf": "^3.0.0",
"terser": "^4.4.3",
"tsify": "^4.0.1",
"tslint": "^5.20.1",
"typescript": "^3.7.3"
},
"jest": {
+1 -1
View File
@@ -10,7 +10,7 @@
# npm; typically installed by Node.js
# yarn; install via brew (macOS) or similar (https://yarnpkg.com/docs/install/)
#
# Note: this script is also used to release matrix-react-sdk and riot-web.
# Note: this script is also used to release matrix-react-sdk and element-web.
set -e
+2 -2
View File
@@ -140,7 +140,7 @@ describe("DeviceList management:", function() {
it("We should not get confused by out-of-order device query responses",
() => {
// https://github.com/vector-im/riot-web/issues/3126
// https://github.com/vector-im/element-web/issues/3126
aliceTestClient.expectKeyQuery({device_keys: {'@alice:localhost': {}}});
return aliceTestClient.start().then(() => {
aliceTestClient.httpBackend.when('GET', '/sync').respond(
@@ -271,7 +271,7 @@ describe("DeviceList management:", function() {
});
}).timeout(3000);
// https://github.com/vector-im/riot-web/issues/4983
// https://github.com/vector-im/element-web/issues/4983
describe("Alice should know she has stale device lists", () => {
beforeEach(async function() {
await aliceTestClient.start();
+1 -1
View File
@@ -90,7 +90,7 @@ describe("MatrixClient retrying", function() {
// wait for the localecho of ev1 to be updated
const p3 = new Promise((resolve, reject) => {
room.on("Room.localEchoUpdated", (ev0) => {
if(ev0 === ev1) {
if (ev0 === ev1) {
resolve();
}
});
+2 -2
View File
@@ -346,7 +346,7 @@ describe("megolm", function() {
});
it("Alice receives a megolm message before the session keys", function() {
// https://github.com/vector-im/riot-web/issues/2273
// https://github.com/vector-im/element-web/issues/2273
let roomKeyEncrypted;
return aliceTestClient.start().then(() => {
@@ -726,7 +726,7 @@ describe("megolm", function() {
});
});
// https://github.com/vector-im/riot-web/issues/2676
// https://github.com/vector-im/element-web/issues/2676
it("Alice should send to her other devices", function() {
// for this test, we make the testOlmAccount be another of Alice's devices.
// it ought to get included in messages Alice sends.
+1 -28
View File
@@ -1,4 +1,4 @@
import {getHttpUriForMxc, getIdenticonUri} from "../../src/content-repo";
import {getHttpUriForMxc} from "../../src/content-repo";
describe("ContentRepo", function() {
const baseUrl = "https://my.home.server";
@@ -56,31 +56,4 @@ describe("ContentRepo", function() {
);
});
});
describe("getIdenticonUri", function() {
it("should do nothing for null input", function() {
expect(getIdenticonUri(null)).toEqual(null);
});
it("should set w/h by default to 96", function() {
expect(getIdenticonUri(baseUrl, "foobar")).toEqual(
baseUrl + "/_matrix/media/unstable/identicon/foobar" +
"?width=96&height=96",
);
});
it("should be able to set custom w/h", function() {
expect(getIdenticonUri(baseUrl, "foobar", 32, 64)).toEqual(
baseUrl + "/_matrix/media/unstable/identicon/foobar" +
"?width=32&height=64",
);
});
it("should URL encode the identicon string", function() {
expect(getIdenticonUri(baseUrl, "foo#bar", 32, 64)).toEqual(
baseUrl + "/_matrix/media/unstable/identicon/foo%23bar" +
"?width=32&height=64",
);
});
});
});
+1 -1
View File
@@ -38,7 +38,7 @@ const testKey = new Uint8Array([
]);
const types = [
{ type: "master", shouldCache: false },
{ type: "master", shouldCache: true },
{ type: "self_signing", shouldCache: true },
{ type: "user_signing", shouldCache: true },
{ type: "invalid", shouldCache: false },
@@ -54,6 +54,7 @@ describe("self-verifications", () => {
cacheCallbacks,
);
_crossSigningInfo.keys = {
master: { keys: { X: testKeyPub } },
self_signing: { keys: { X: testKeyPub } },
user_signing: { keys: { X: testKeyPub } },
};
@@ -96,9 +97,9 @@ describe("self-verifications", () => {
const result = await verification.done();
/* We should request, and store, two cross signing key and the key backup key */
expect(cacheCallbacks.storeCrossSigningKeyCache.mock.calls.length).toBe(2);
expect(_secretStorage.request.mock.calls.length).toBe(3);
/* We should request, and store, 3 cross signing keys and the key backup key */
expect(cacheCallbacks.storeCrossSigningKeyCache.mock.calls.length).toBe(3);
expect(_secretStorage.request.mock.calls.length).toBe(4);
expect(cacheCallbacks.storeCrossSigningKeyCache.mock.calls[0][1])
.toEqual(testKey);
-6
View File
@@ -33,12 +33,6 @@ describe("RoomMember", function() {
expect(url.indexOf("flibble/wibble")).not.toEqual(-1);
});
it("should return an identicon HTTP URL if allowDefault was set and there " +
"was no m.room.member event", function() {
const url = member.getAvatarUrl(hsUrl, 64, 64, "crop", true);
expect(url.indexOf("http")).toEqual(0); // don't care about form
});
it("should return nothing if there is no m.room.member and allowDefault=false",
function() {
const url = member.getAvatarUrl(hsUrl, 64, 64, "crop", false);
+1 -7
View File
@@ -45,12 +45,6 @@ describe("Room", function() {
expect(url.indexOf("flibble/wibble")).not.toEqual(-1);
});
it("should return an identicon HTTP URL if allowDefault was set and there " +
"was no m.room.avatar event", function() {
const url = room.getAvatarUrl(hsUrl, 64, 64, "crop", true);
expect(url.indexOf("http")).toEqual(0); // don't care about form
});
it("should return nothing if there is no m.room.avatar and allowDefault=false",
function() {
const url = room.getAvatarUrl(hsUrl, 64, 64, "crop", false);
@@ -1379,7 +1373,7 @@ describe("Room", function() {
let hasThrown = false;
try {
await room.loadMembersIfNeeded();
} catch(err) {
} catch (err) {
hasThrown = true;
}
expect(hasThrown).toEqual(true);
+1 -1
View File
@@ -143,7 +143,7 @@ describe("MatrixScheduler", function() {
deferA.reject({});
try {
await globalA;
} catch(err) {
} catch (err) {
await Promise.resolve();
expect(procCount).toEqual(2);
}
+1 -1
View File
@@ -66,7 +66,7 @@ function termsUrlForService(serviceType, baseUrl) {
* callback that returns a Promise<String> of an identity access token to supply
* with identity requests. If the object is unset, no access token will be
* supplied.
* See also https://github.com/vector-im/riot-web/issues/10615 which seeks to
* See also https://github.com/vector-im/element-web/issues/10615 which seeks to
* replace the previous approach of manual access tokens params with this
* callback throughout the SDK.
*
+1 -1
View File
@@ -34,7 +34,7 @@ matrixcs.request(function(opts, fn) {
let indexedDB;
try {
indexedDB = global.indexedDB;
} catch(e) {}
} catch (e) {}
// if our browser (appears to) support indexeddb, use an indexeddb crypto store.
if (indexedDB) {
+16 -8
View File
@@ -119,7 +119,7 @@ function keyFromRecoverySession(session, decryptionKey) {
* callback that returns a Promise<String> of an identity access token to supply
* with identity requests. If the object is unset, no access token will be
* supplied.
* See also https://github.com/vector-im/riot-web/issues/10615 which seeks to
* See also https://github.com/vector-im/element-web/issues/10615 which seeks to
* replace the previous approach of manual access tokens params with this
* callback throughout the SDK.
*
@@ -380,7 +380,7 @@ export function MatrixClient(opts) {
? !!actions.tweaks.highlight : false;
if (oldHighlight !== newHighlight || currentCount > 0) {
// TODO: Handle mentions received while the client is offline
// See also https://github.com/vector-im/riot-web/issues/9069
// See also https://github.com/vector-im/element-web/issues/9069
if (!room.hasUserReadEvent(this.getUserId(), event.getId())) {
let newCount = currentCount;
if (newHighlight && !oldHighlight) newCount++;
@@ -398,7 +398,7 @@ export function MatrixClient(opts) {
// Like above, we have to listen for read receipts from ourselves in order to
// correctly handle notification counts on encrypted rooms.
// This fixes https://github.com/vector-im/riot-web/issues/9421
// This fixes https://github.com/vector-im/element-web/issues/9421
this.on("Room.receipt", (event, room) => {
if (room && this.isRoomEncrypted(room.roomId)) {
// Figure out if we've read something or if it's just informational
@@ -1169,6 +1169,7 @@ wrapCryptoFuncs(MatrixClient, [
"isCrossSigningReady",
"getCryptoTrustCrossSignedDevices",
"setCryptoTrustCrossSignedDevices",
"countSessionsNeedingBackup",
]);
/**
@@ -1925,7 +1926,7 @@ MatrixClient.prototype._restoreKeyBackup = function(
let backupPubKey;
try {
backupPubKey = decryption.init_with_private_key(privKey);
} catch(e) {
} catch (e) {
decryption.free();
throw e;
}
@@ -2523,7 +2524,7 @@ MatrixClient.prototype._sendCompleteEvent = function(roomId, eventObject, txnId,
const type = localEvent.getType();
logger.log(`sendEvent of type ${type} in ${roomId} with txnId ${txnId}`);
localEvent._txnId = txnId;
localEvent.setTxnId(txnId);
localEvent.setStatus(EventStatus.SENDING);
// add this event immediately to the local store as 'sending'.
@@ -2691,7 +2692,11 @@ function _updatePendingEventStatus(room, event, newStatus) {
}
function _sendEventHttpRequest(client, event) {
const txnId = event._txnId ? event._txnId : client.makeTxnId();
let txnId = event.getTxnId();
if (!txnId) {
txnId = client.makeTxnId();
event.setTxnId(txnId);
}
const pathParams = {
$roomId: event.getRoomId(),
@@ -3797,7 +3802,9 @@ MatrixClient.prototype.paginateEventTimeline = function(eventTimeline, opts) {
return pendingRequest;
}
let path, params, promise;
let path;
let params;
let promise;
const self = this;
if (isNotifTimeline) {
@@ -4217,7 +4224,8 @@ MatrixClient.prototype.getRoomPushRule = function(scope, roomId) {
*/
MatrixClient.prototype.setRoomMutePushRule = function(scope, roomId, mute) {
const self = this;
let deferred, hasDontNotifyRule;
let deferred;
let hasDontNotifyRule;
// Get the existing room-kind push rule if any
const roomPushRule = this.getRoomPushRule(scope, roomId);
-32
View File
@@ -75,35 +75,3 @@ export function getHttpUriForMxc(baseUrl, mxc, width, height,
(utils.keys(params).length === 0 ? "" :
("?" + utils.encodeParams(params))) + fragment;
}
/**
* Get an identicon URL from an arbitrary string.
* @param {string} baseUrl The base homeserver url which has a content repo.
* @param {string} identiconString The string to create an identicon for.
* @param {Number} width The desired width of the image in pixels. Default: 96.
* @param {Number} height The desired height of the image in pixels. Default: 96.
* @return {string} The complete URL to the identicon.
* @deprecated This is no longer in the specification.
*/
export function getIdenticonUri(baseUrl, identiconString, width, height) {
if (!identiconString) {
return null;
}
if (!width) {
width = 96;
}
if (!height) {
height = 96;
}
const params = {
width: width,
height: height,
};
const path = utils.encodeUri("/_matrix/media/unstable/identicon/$ident", {
$ident: identiconString,
});
return baseUrl + path +
(utils.keys(params).length === 0 ? "" :
("?" + utils.encodeParams(params)));
}
+96 -1
View File
@@ -26,6 +26,8 @@ import {logger} from '../logger';
import {IndexedDBCryptoStore} from '../crypto/store/indexeddb-crypto-store';
import {decryptAES, encryptAES} from './aes';
const KEY_REQUEST_TIMEOUT_MS = 1000 * 60;
function publicKeyFromKeyInfo(keyInfo) {
// `keys` is an object with { [`ed25519:${pubKey}`]: pubKey }
// We assume only a single key, and we want the bare form without type
@@ -72,7 +74,7 @@ export class CrossSigningInfo extends EventEmitter {
* @returns {Array} An array with [ public key, Olm.PkSigning ]
*/
async getCrossSigningKey(type, expectedPubkey) {
const shouldCache = ["self_signing", "user_signing"].indexOf(type) >= 0;
const shouldCache = ["master", "self_signing", "user_signing"].indexOf(type) >= 0;
if (!this._callbacks.getCrossSigningKey) {
throw new Error("No getCrossSigningKey callback supplied");
@@ -677,3 +679,96 @@ export function createCryptoStoreCacheCallbacks(store, olmdevice) {
},
};
}
/**
* Request cross-signing keys from another device during verification.
*
* @param {module:base-apis~MatrixBaseApis} baseApis base Matrix API interface
* @param {string} userId The user ID being verified
* @param {string} deviceId The device ID being verified
*/
export async function requestKeysDuringVerification(baseApis, userId, deviceId) {
// If this is a self-verification, ask the other party for keys
if (baseApis.getUserId() !== userId) {
return;
}
console.log("Cross-signing: Self-verification done; requesting keys");
// This happens asynchronously, and we're not concerned about waiting for
// it. We return here in order to test.
return new Promise((resolve, reject) => {
const client = baseApis;
const original = client._crypto._crossSigningInfo;
// We already have all of the infrastructure we need to validate and
// cache cross-signing keys, so instead of replicating that, here we set
// up callbacks that request them from the other device and call
// CrossSigningInfo.getCrossSigningKey() to validate/cache
const crossSigning = new CrossSigningInfo(
original.userId,
{ getCrossSigningKey: async (type) => {
console.debug("Cross-signing: requesting secret",
type, deviceId);
const { promise } = client.requestSecret(
`m.cross_signing.${type}`, [deviceId],
);
const result = await promise;
const decoded = decodeBase64(result);
return Uint8Array.from(decoded);
} },
original._cacheCallbacks,
);
crossSigning.keys = original.keys;
// XXX: get all keys out if we get one key out
// https://github.com/vector-im/element-web/issues/12604
// then change here to reject on the timeout
// Requests can be ignored, so don't wait around forever
const timeout = new Promise((resolve, reject) => {
setTimeout(
resolve,
KEY_REQUEST_TIMEOUT_MS,
new Error("Timeout"),
);
});
// also request and cache the key backup key
const backupKeyPromise = new Promise(async resolve => {
const cachedKey = await client._crypto.getSessionBackupPrivateKey();
if (!cachedKey) {
logger.info("No cached backup key found. Requesting...");
const secretReq = client.requestSecret(
'm.megolm_backup.v1', [deviceId],
);
const base64Key = await secretReq.promise;
logger.info("Got key backup key, decoding...");
const decodedKey = decodeBase64(base64Key);
logger.info("Decoded backup key, storing...");
client._crypto.storeSessionBackupPrivateKey(
Uint8Array.from(decodedKey),
);
logger.info("Backup key stored. Starting backup restore...");
const backupInfo = await client.getKeyBackupVersion();
// no need to await for this - just let it go in the bg
client.restoreKeyBackupWithCache(
undefined, undefined, backupInfo,
).then(() => {
logger.info("Backup restored.");
});
}
resolve();
});
// We call getCrossSigningKey() for its side-effects
return Promise.race([
Promise.all([
crossSigning.getCrossSigningKey("master"),
crossSigning.getCrossSigningKey("self_signing"),
crossSigning.getCrossSigningKey("user_signing"),
backupKeyPromise,
]),
timeout,
]).then(resolve, reject);
}).catch((e) => {
console.warn("Cross-signing: failure while requesting keys:", e);
});
}
+1 -1
View File
@@ -790,7 +790,7 @@ class DeviceListUpdateSerialiser {
// yield to other things that want to execute in between users, to
// avoid wedging the CPU
// (https://github.com/vector-im/riot-web/issues/3158)
// (https://github.com/vector-im/element-web/issues/3158)
//
// of course we ought to do this in a web worker or similar, but
// this serves as an easy solution for now.
+4 -3
View File
@@ -1,3 +1,4 @@
import { logger } from "../logger";
import {MatrixEvent} from "../models/event";
import {EventEmitter} from "events";
import {createCryptoStoreCacheCallbacks} from "./CrossSigning";
@@ -113,12 +114,12 @@ export class EncryptionSetupBuilder {
* @return {Promise}
*/
async persist(crypto) {
// store self_signing and user_signing private key in cache
// store private keys in cache
if (this._crossSigningKeys) {
const cacheCallbacks = createCryptoStoreCacheCallbacks(
crypto._cryptoStore, crypto._olmDevice);
for (const type of ["self_signing", "user_signing"]) {
// logger.log(`Cache ${type} cross-signing private key locally`);
for (const type of ["master", "self_signing", "user_signing"]) {
logger.log(`Cache ${type} cross-signing private key locally`);
const privateKey = this.crossSigningCallbacks.privateKeys.get(type);
await cacheCallbacks.storeCrossSigningKeyCache(type, privateKey);
}
+5
View File
@@ -373,6 +373,7 @@ export class SecretStorage extends EventEmitter {
const requestId = this._baseApis.makeTxnId();
const requestControl = this._requests[requestId] = {
name,
devices,
};
const promise = new Promise((resolve, reject) => {
@@ -536,6 +537,10 @@ export class SecretStorage extends EventEmitter {
return;
}
logger.log(
`Successfully received secret ${requestControl.name} ` +
`from ${deviceInfo.deviceId}`,
);
requestControl.resolve(content.secret);
}
}
+2 -2
View File
@@ -1015,7 +1015,7 @@ MegolmEncryption.prototype._getDevicesInRoom = async function(room) {
// with them, which means that they will have announced any new devices via
// device_lists in their /sync response. This cache should then be maintained
// using all the device_lists changes and left fields.
// See https://github.com/vector-im/riot-web/issues/2305 for details.
// See https://github.com/vector-im/element-web/issues/2305 for details.
const devices = await this._crypto.downloadKeys(roomMembers, false);
const blocked = {};
// remove any blocked devices
@@ -1109,7 +1109,7 @@ MegolmDecryption.prototype.decryptEvent = async function(event) {
//
// then, if the key turns up while decryption is in progress (and
// decryption fails), we will schedule a retry.
// (fixes https://github.com/vector-im/riot-web/issues/5001)
// (fixes https://github.com/vector-im/element-web/issues/5001)
this._addEventToPendingList(event);
let res;
+49 -6
View File
@@ -413,7 +413,7 @@ Crypto.prototype.createRecoveryKeyFromPassphrase = async function(password) {
*
* If this function returns false, bootstrapSecretStorage() can be used
* to fix things such that it returns true. That is to say, after
* bootstrapSecretStorage() completes sucessfully, this function should
* bootstrapSecretStorage() completes successfully, this function should
* return true.
*
* The cross-signing API is currently UNSTABLE and may change without notice.
@@ -1221,6 +1221,20 @@ Crypto.prototype.checkOwnCrossSigningTrust = async function() {
if (oldSelfSigningId !== newCrossSigning.getId("self_signing")) {
logger.info("Got new self-signing key", newCrossSigning.getId("self_signing"));
// Try to cache the self-signing private key as a side-effect
let signing = null;
try {
const ret = await this._crossSigningInfo.getCrossSigningKey(
"self_signing", newCrossSigning.getId("self_signing"),
);
signing = ret[1];
logger.info(
"Got matching private key from callback for new public self-signing key",
);
} finally {
if (signing) signing.free();
}
const device = this._deviceList.getStoredDevice(this._userId, this._deviceId);
const signedDevice = await this._crossSigningInfo.signDevice(
this._userId, device,
@@ -1229,6 +1243,20 @@ Crypto.prototype.checkOwnCrossSigningTrust = async function() {
}
if (oldUserSigningId !== newCrossSigning.getId("user_signing")) {
logger.info("Got new user-signing key", newCrossSigning.getId("user_signing"));
// Try to cache the user-signing private key as a side-effect
let signing = null;
try {
const ret = await this._crossSigningInfo.getCrossSigningKey(
"user_signing", newCrossSigning.getId("user_signing"),
);
signing = ret[1];
logger.info(
"Got matching private key from callback for new public user-signing key",
);
} finally {
if (signing) signing.free();
}
}
if (masterChanged) {
@@ -1399,6 +1427,12 @@ Crypto.prototype._checkAndStartKeyBackup = async function() {
);
this._baseApis.disableKeyBackup();
this._baseApis.enableKeyBackup(backupInfo);
// We're now using a new backup, so schedule all the keys we have to be
// uploaded to the new backup. This is a bit of a workaround to upload
// keys to a new backup in *most* cases, but it won't cover all cases
// because we don't remember what backup version we uploaded keys to:
// see https://github.com/vector-im/element-web/issues/14833
await this.scheduleAllGroupSessionsForBackup();
} else {
logger.log("Backup version " + backupInfo.version + " still current");
}
@@ -2374,7 +2408,7 @@ Crypto.prototype.setRoomEncryption = async function(roomId, config, inhibitDevic
// after all the in-memory state (_roomEncryptors and _roomList) has been updated
// to avoid races when calling this method multiple times. Hence keep a hold of the promise.
let storeConfigPromise = null;
if(!existingConfig) {
if (!existingConfig) {
storeConfigPromise = this._roomList.setRoomEncryption(roomId, config);
}
@@ -2404,10 +2438,10 @@ Crypto.prototype.setRoomEncryption = async function(roomId, config, inhibitDevic
await this.trackRoomDevices(roomId);
// TODO: this flag is only not used from MatrixClient::setRoomEncryption
// which is never used (inside riot at least)
// which is never used (inside Element at least)
// but didn't want to remove it as it technically would
// be a breaking change.
if(!this.inhibitDeviceQuery) {
if (!this.inhibitDeviceQuery) {
this._deviceList.refreshOutdatedDeviceLists();
}
} else {
@@ -2712,7 +2746,8 @@ Crypto.prototype.scheduleAllGroupSessionsForBackup = async function() {
/**
* Marks all group sessions as needing to be backed up without scheduling
* them to upload in the background.
* @returns {Promise<int>} Resolves to the number of sessions requiring a backup.
* @returns {Promise<int>} Resolves to the number of sessions now requiring a backup
* (which will be equal to the number of sessions in the store).
*/
Crypto.prototype.flagAllGroupSessionsForBackup = async function() {
await this._cryptoStore.doTxn(
@@ -2735,6 +2770,14 @@ Crypto.prototype.flagAllGroupSessionsForBackup = async function() {
return remaining;
};
/**
* Counts the number of end to end session keys that are waiting to be backed up
* @returns {Promise<int>} Resolves to the number of sessions requiring backup
*/
Crypto.prototype.countSessionsNeedingBackup = function() {
return this._cryptoStore.countSessionsNeedingBackup();
};
/**
* Perform any background tasks that can be done before a message is ready to
* send, in order to speed up sending of the message.
@@ -2969,7 +3012,7 @@ Crypto.prototype.onSyncCompleted = async function(syncData) {
// we don't start uploading one-time keys until we've caught up with
// to-device messages, to help us avoid throwing away one-time-keys that we
// are about to receive messages for
// (https://github.com/vector-im/riot-web/issues/2782).
// (https://github.com/vector-im/element-web/issues/2782).
if (!syncData.catchingUp) {
_maybeUploadOneTimeKeys(this);
this._processReceivedRoomKeyRequests();
@@ -131,7 +131,7 @@ export class Backend {
cursorReq.onsuccess = (ev) => {
const cursor = ev.target.result;
if(!cursor) {
if (!cursor) {
// no match found
callback(null);
return;
+2 -91
View File
@@ -25,8 +25,7 @@ import {EventEmitter} from 'events';
import {logger} from '../../logger';
import {DeviceInfo} from '../deviceinfo';
import {newTimeoutError} from "./Error";
import {CrossSigningInfo} from "../CrossSigning";
import {decodeBase64} from "../olmlib";
import {requestKeysDuringVerification} from "../CrossSigning";
const timeoutException = new Error("Verification timed out");
@@ -79,8 +78,6 @@ export class VerificationBase extends EventEmitter {
this._transactionTimeoutTimer = null;
}
static keyRequestTimeoutMs = 1000 * 60;
get initiatedByMe() {
// if there is no start event yet,
// we probably want to send it,
@@ -198,93 +195,7 @@ export class VerificationBase extends EventEmitter {
if (!this._done) {
this.request.onVerifierFinished();
this._resolve();
//#region Cross-signing keys request
// If this is a self-verification, ask the other party for keys
if (this._baseApis.getUserId() !== this.userId) {
return;
}
// FIXME: This is a lot of logic that isn't anything to do with verification
// and probably ought to be somewhere else.
console.log("VerificationBase.done: Self-verification done; requesting keys");
/* This happens asynchronously, and we're not concerned about
* waiting for it. We return here in order to test. */
return new Promise((resolve, reject) => {
const client = this._baseApis;
const original = client._crypto._crossSigningInfo;
/* We already have all of the infrastructure we need to validate and
* cache cross-signing keys, so instead of replicating that, here we
* set up callbacks that request them from the other device and call
* CrossSigningInfo.getCrossSigningKey() to validate/cache */
const crossSigning = new CrossSigningInfo(
original.userId,
{ getCrossSigningKey: async (type) => {
console.debug("VerificationBase.done: requesting secret",
type, this.deviceId);
const { promise } = client.requestSecret(
`m.cross_signing.${type}`, [this.deviceId],
);
const result = await promise;
const decoded = decodeBase64(result);
return Uint8Array.from(decoded);
} },
original._cacheCallbacks,
);
crossSigning.keys = original.keys;
// XXX: get all keys out if we get one key out
// https://github.com/vector-im/riot-web/issues/12604
// then change here to reject on the timeout
/* Requests can be ignored, so don't wait around forever */
const timeout = new Promise((resolve, reject) => {
setTimeout(
resolve,
VerificationBase.keyRequestTimeoutMs,
new Error("Timeout"),
);
});
// also request and cache the key backup key
const backupKeyPromise = new Promise(async resolve => {
const cachedKey = await client._crypto.getSessionBackupPrivateKey();
if (!cachedKey) {
logger.info("No cached backup key found. Requesting...");
const secretReq = client.requestSecret(
'm.megolm_backup.v1', [this.deviceId],
);
const base64Key = await secretReq.promise;
logger.info("Got key backup key, decoding...");
const decodedKey = decodeBase64(base64Key);
logger.info("Decoded backup key, storing...");
client._crypto.storeSessionBackupPrivateKey(
Uint8Array.from(decodedKey),
);
logger.info("Backup key stored. Starting backup restore...");
const backupInfo = await client.getKeyBackupVersion();
// no need to await for this - just let it go in the bg
client.restoreKeyBackupWithCache(
undefined, undefined, backupInfo,
).then(() => {
logger.info("Backup restored.");
});
}
resolve();
});
/* We call getCrossSigningKey() for its side-effects */
return Promise.race([
Promise.all([
crossSigning.getCrossSigningKey("self_signing"),
crossSigning.getCrossSigningKey("user_signing"),
backupKeyPromise,
]),
timeout,
]).then(resolve, reject);
}).catch((e) => {
console.warn("VerificationBase: failure while requesting keys:", e);
});
//#endregion
return requestKeysDuringVerification(this._baseApis, this.userId, this.deviceId);
}
}
+1 -1
View File
@@ -430,7 +430,7 @@ export class SAS extends Base {
try {
await this._sendMAC(olmSAS, macMethod);
resolve();
} catch(err) {
} catch (err) {
reject(err);
}
},
+1 -1
View File
@@ -890,7 +890,7 @@ function getResponseContentType(response) {
try {
return parseContentType(contentType);
} catch(e) {
} catch (e) {
throw new Error(`Error parsing Content-Type '${contentType}': ${e}`);
}
}
+1
View File
@@ -22,6 +22,7 @@ matrixcs.request(request);
utils.runPolyfills();
try {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const crypto = require('crypto');
utils.setCrypto(crypto);
} catch (err) {
+1 -1
View File
@@ -187,7 +187,7 @@ export function createClient(opts: ICreateClientOpts | string) {
* @param {requestCallback} callback The request callback.
*/
/**
/**
* The request callback interface for performing HTTP requests. This matches the
* API for the {@link https://github.com/request/request#requestoptions-callback|
* request NPM module}. The SDK will implement a callback which meets this
+2 -1
View File
@@ -647,7 +647,8 @@ EventTimelineSet.prototype.compareEventOrdering = function(eventId1, eventId2) {
if (timeline1 === timeline2) {
// both events are in the same timeline - figure out their
// relative indices
let idx1, idx2;
let idx1;
let idx2;
const events = timeline1.getEvents();
for (let idx = 0; idx < events.length &&
(idx1 === undefined || idx2 === undefined); idx++) {
+14 -2
View File
@@ -87,7 +87,7 @@ export const MatrixEvent = function(
// amount of needless string duplication. This can save moderate amounts of
// memory (~10% on a 350MB heap).
// 'membership' at the event level (rather than the content level) is a legacy
// field that Riot never otherwise looks at, but it will still take up a lot
// field that Element never otherwise looks at, but it will still take up a lot
// of space if we don't intern it.
["state_key", "type", "sender", "room_id", "membership"].forEach((prop) => {
if (!event[prop]) {
@@ -164,12 +164,16 @@ export const MatrixEvent = function(
* so it can be easily accessed from the timeline.
*/
this.verificationRequest = null;
/* The txnId with which this event was sent if it was during this session,
allows for a unique ID which does not change when the event comes back down sync.
*/
this._txnId = null;
};
utils.inherits(MatrixEvent, EventEmitter);
utils.extend(MatrixEvent.prototype, {
/**
* Get the event_id for this event.
* @return {string} The event ID, e.g. <code>$143350589368169JsLZx:localhost
@@ -1085,6 +1089,14 @@ utils.extend(MatrixEvent.prototype, {
setVerificationRequest: function(request) {
this.verificationRequest = request;
},
setTxnId(txnId) {
this._txnId = txnId;
},
getTxnId() {
return this._txnId;
},
});
+3 -7
View File
@@ -20,7 +20,7 @@ limitations under the License.
*/
import {EventEmitter} from "events";
import {getHttpUriForMxc, getIdenticonUri} from "../content-repo";
import {getHttpUriForMxc} from "../content-repo";
import * as utils from "../utils";
/**
@@ -274,10 +274,6 @@ RoomMember.prototype.getAvatarUrl =
);
if (httpUrl) {
return httpUrl;
} else if (allowDefault) {
return getIdenticonUri(
baseUrl, this.userId, width, height,
);
}
return null;
};
@@ -286,9 +282,9 @@ RoomMember.prototype.getAvatarUrl =
* @return {string} the mxc avatar url
*/
RoomMember.prototype.getMxcAvatarUrl = function() {
if(this.events.member) {
if (this.events.member) {
return this.events.member.getDirectionalContent().avatar_url;
} else if(this.user) {
} else if (this.user) {
return this.user.avatarUrl;
}
return null;
+14 -17
View File
@@ -68,9 +68,7 @@ export function RoomState(roomId, oobMemberFlags = undefined) {
this.members = {
// userId: RoomMember
};
this.events = {
// eventType: { stateKey: MatrixEvent }
};
this.events = new Map(); // Map<eventType, Map<stateKey, MatrixEvent>>
this.paginationToken = null;
this._sentinels = {
@@ -211,14 +209,14 @@ RoomState.prototype.getSentinelMember = function(userId) {
* <code>undefined</code>, else a single event (or null if no match found).
*/
RoomState.prototype.getStateEvents = function(eventType, stateKey) {
if (!this.events[eventType]) {
if (!this.events.has(eventType)) {
// no match
return stateKey === undefined ? [] : null;
}
if (stateKey === undefined) { // return all values
return utils.values(this.events[eventType]);
return Array.from(this.events.get(eventType).values());
}
const event = this.events[eventType][stateKey];
const event = this.events.get(eventType).get(stateKey);
return event ? event : null;
};
@@ -238,9 +236,8 @@ RoomState.prototype.clone = function() {
const status = this._oobMemberFlags.status;
this._oobMemberFlags.status = OOB_STATUS_NOTSTARTED;
Object.values(this.events).forEach((eventsByStateKey) => {
const eventsForType = Object.values(eventsByStateKey);
copy.setStateEvents(eventsForType);
Array.from(this.events.values()).forEach((eventsByStateKey) => {
copy.setStateEvents(Array.from(eventsByStateKey.values()));
});
// Ugly hack: see above
@@ -276,8 +273,8 @@ RoomState.prototype.clone = function() {
*/
RoomState.prototype.setUnknownStateEvents = function(events) {
const unknownStateEvents = events.filter((event) => {
return this.events[event.getType()] === undefined ||
this.events[event.getType()][event.getStateKey()] === undefined;
return !this.events.has(event.getType()) ||
!this.events.get(event.getType()).has(event.getStateKey());
});
this.setStateEvents(unknownStateEvents);
@@ -386,15 +383,15 @@ RoomState.prototype._getOrCreateMember = function(userId, event) {
};
RoomState.prototype._setStateEvent = function(event) {
if (this.events[event.getType()] === undefined) {
this.events[event.getType()] = {};
if (!this.events.has(event.getType())) {
this.events.set(event.getType(), new Map());
}
this.events[event.getType()][event.getStateKey()] = event;
this.events.get(event.getType()).set(event.getStateKey(), event);
};
RoomState.prototype._getStateEventMatching = function(event) {
if (!this.events[event.getType()]) return null;
return this.events[event.getType()][event.getStateKey()];
if (!this.events.has(event.getType())) return null;
return this.events.get(event.getType()).get(event.getStateKey());
};
RoomState.prototype._updateMember = function(member) {
@@ -676,7 +673,7 @@ RoomState.prototype._maySendEventOfType = function(eventType, userId, state) {
const userPowerLevel = power_levels.users && power_levels.users[userId];
if (Number.isFinite(userPowerLevel)) {
powerLevel = userPowerLevel;
} else if(Number.isFinite(power_levels.users_default)) {
} else if (Number.isFinite(power_levels.users_default)) {
powerLevel = power_levels.users_default;
}
+3 -7
View File
@@ -23,7 +23,7 @@ limitations under the License.
import {EventEmitter} from "events";
import {EventTimelineSet} from "./event-timeline-set";
import {EventTimeline} from "./event-timeline";
import {getHttpUriForMxc, getIdenticonUri} from "../content-repo";
import {getHttpUriForMxc} from "../content-repo";
import * as utils from "../utils";
import {EventStatus, MatrixEvent} from "./event";
import {RoomMember} from "./room-member";
@@ -818,10 +818,6 @@ Room.prototype.getAvatarUrl = function(baseUrl, width, height, resizeMethod,
return getHttpUriForMxc(
baseUrl, mainUrl, width, height, resizeMethod,
);
} else if (allowDefault) {
return getIdenticonUri(
baseUrl, this.roomId, width, height,
);
}
return null;
@@ -1904,14 +1900,14 @@ function calculateRoomName(room, userId, ignoreRoomNameEvent) {
// let's try to figure out who was here before
let leftNames = otherNames;
// if we didn't have heroes, try finding them in the room state
if(!leftNames.length) {
if (!leftNames.length) {
leftNames = room.currentState.getMembers().filter((m) => {
return m.userId !== userId &&
m.membership !== "invite" &&
m.membership !== "join";
}).map((m) => m.name);
}
if(leftNames.length) {
if (leftNames.length) {
return `Empty room (was ${memberNamesToRoomName(leftNames)})`;
} else {
return "Empty room";
+10 -2
View File
@@ -129,7 +129,11 @@ User.prototype.setPresenceEvent = function(event) {
*/
User.prototype.setDisplayName = function(name) {
const oldName = this.displayName;
this.displayName = name;
if (typeof name === "string") {
this.displayName = name;
} else {
this.displayName = undefined;
}
if (name !== oldName) {
this._updateModifiedTime();
}
@@ -142,7 +146,11 @@ User.prototype.setDisplayName = function(name) {
* @param {string} name The new display name.
*/
User.prototype.setRawDisplayName = function(name) {
this.rawDisplayName = name;
if (typeof name === "string") {
this.rawDisplayName = name;
} else {
this.rawDisplayName = undefined;
}
};
+2 -2
View File
@@ -185,8 +185,8 @@ function _runCallbacks() {
*/
function binarySearch(array, func) {
// min is inclusive, max exclusive.
let min = 0,
max = array.length;
let min = 0;
let max = array.length;
while (min < max) {
const mid = (min + max) >> 1;
+3 -3
View File
@@ -701,7 +701,7 @@ SyncApi.prototype._syncFromCache = async function(savedSync) {
try {
await this._processSyncResponse(syncEventData, data);
} catch(e) {
} catch (e) {
logger.error("Error processing cached sync", e.stack || e);
}
@@ -774,7 +774,7 @@ SyncApi.prototype._sync = async function(syncOptions) {
try {
await this._processSyncResponse(syncEventData, data);
} catch(e) {
} catch (e) {
// log the exception with stack if we have it, else fall back
// to the plain description
logger.error("Caught /sync error", e.stack || e);
@@ -894,7 +894,7 @@ SyncApi.prototype._onSyncError = function(err, syncOptions) {
logger.error("/sync error %s", err);
logger.error(err);
if(this._shouldAbortSync(err)) {
if (this._shouldAbortSync(err)) {
return;
}
+2 -1
View File
@@ -396,7 +396,8 @@ TimelineWindow.prototype.getEvents = function() {
// (Note that both this._start.index and this._end.index are relative
// to their respective timelines' BaseIndex).
//
let startIndex = 0, endIndex = events.length;
let startIndex = 0;
let endIndex = events.length;
if (timeline === this._start.timeline) {
startIndex = this._start.index + timeline.getBaseIndex();
}
+169 -161
View File
@@ -21,7 +21,6 @@ limitations under the License.
*/
import unhomoglyph from 'unhomoglyph';
import {ConnectionError} from "./http-api";
/**
* Encode a dictionary of query parameters.
@@ -50,7 +49,7 @@ export function encodeParams(params: Record<string, string>): string {
* @return {string} The result of replacing all template variables e.g. '/foo/baz'.
*/
export function encodeUri(pathTemplate: string,
variables: Record<string, string>): string {
variables: Record<string, string>): string {
for (const key in variables) {
if (!variables.hasOwnProperty(key)) {
continue;
@@ -86,7 +85,7 @@ export function map<T, S>(array: T[], fn: (t: T) => S): S[] {
* @return {Array} A new array with the results of the function.
*/
export function filter<T>(array: T[],
fn: (t: T, i?: number, a?: T[]) => boolean): T[] {
fn: (t: T, i?: number, a?: T[]) => boolean): T[] {
const results: T[] = [];
for (let i = 0; i < array.length; i++) {
if (fn(array[i], i, array)) {
@@ -152,10 +151,10 @@ export function forEach<T>(array: T[], fn: (t: T, i: number) => void) {
* the given function.
*/
export function findElement<T>(
array: T[],
fn: (t: T, i?: number, a?: T[]) => boolean,
reverse?: boolean
) {
array: T[],
fn: (t: T, i?: number, a?: T[]) => boolean,
reverse?: boolean,
) {
let i;
if (reverse) {
for (i = array.length - 1; i >= 0; i--) {
@@ -183,10 +182,10 @@ export function findElement<T>(
* @return {boolean} True if an element was removed.
*/
export function removeElement<T>(
array: T[],
fn: (t: T, i?: number, a?: T[]) => boolean,
reverse?: boolean
) {
array: T[],
fn: (t: T, i?: number, a?: T[]) => boolean,
reverse?: boolean,
) {
let i;
let removed;
if (reverse) {
@@ -294,7 +293,7 @@ export function deepCompare(x: any, y: any): boolean {
// special-case NaN (since NaN !== NaN)
if (typeof x === 'number' && isNaN(x) && isNaN(y)) {
return true;
return true;
}
// special-case null (since typeof null == 'object', but null.constructor
@@ -369,10 +368,10 @@ export function deepCompare(x: any, y: any): boolean {
*
* @return {Object} target
*/
export function extend() {
const target = arguments[0] || {};
for (let i = 1; i < arguments.length; i++) {
const source = arguments[i];
export function extend(...restParams) {
const target = restParams[0] || {};
for (let i = 1; i < restParams.length; i++) {
const source = restParams[i];
if (!source) continue;
for (const propName in source) { // eslint-disable-line guard-for-in
target[propName] = source[propName];
@@ -390,36 +389,36 @@ export function runPolyfills() {
// SOURCE:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
if (!Array.prototype.filter) {
Array.prototype.filter = function(fun: Function/*, thisArg*/) {
if (this === void 0 || this === null) {
throw new TypeError();
}
const t = Object(this);
const len = t.length >>> 0;
if (typeof fun !== 'function') {
throw new TypeError();
}
const res = [];
const thisArg = arguments.length >= 2 ? arguments[1] : void 0;
for (let i = 0; i < len; i++) {
if (i in t) {
const val = t[i];
// NOTE: Technically this should Object.defineProperty at
// the next index, as push can be affected by
// properties on Object.prototype and Array.prototype.
// But that method's new, and collisions should be
// rare, so use the more-compatible alternative.
if (fun.call(thisArg, val, i, t)) {
res.push(val);
// eslint-disable-next-line no-extend-native
Array.prototype.filter = function(fun: Function/*, thisArg*/, ...restProps) {
if (this === void 0 || this === null) {
throw new TypeError();
}
}
}
return res;
};
const t = Object(this);
const len = t.length >>> 0;
if (typeof fun !== 'function') {
throw new TypeError();
}
const res = [];
const thisArg = restProps ? restProps[0] : void 0;
for (let i = 0; i < len; i++) {
if (i in t) {
const val = t[i];
// NOTE: Technically this should Object.defineProperty at
// the next index, as push can be affected by
// properties on Object.prototype and Array.prototype.
// But that method's new, and collisions should be
// rare, so use the more-compatible alternative.
if (fun.call(thisArg, val, i, t)) {
res.push(val);
}
}
}
return res;
};
}
// Array.prototype.map
@@ -429,151 +428,156 @@ export function runPolyfills() {
// Production steps of ECMA-262, Edition 5, 15.4.4.19
// Reference: http://es5.github.io/#x15.4.4.19
if (!Array.prototype.map) {
Array.prototype.map = function(callback, thisArg) {
let T, k;
// eslint-disable-next-line no-extend-native
Array.prototype.map = function(callback, thisArg) {
let T;
let k;
if (this === null || this === undefined) {
throw new TypeError(' this is null or not defined');
}
if (this === null || this === undefined) {
throw new TypeError(' this is null or not defined');
}
// 1. Let O be the result of calling ToObject passing the |this|
// value as the argument.
const O = Object(this);
// 1. Let O be the result of calling ToObject passing the |this|
// value as the argument.
const O = Object(this);
// 2. Let lenValue be the result of calling the Get internal
// method of O with the argument "length".
// 3. Let len be ToUint32(lenValue).
const len = O.length >>> 0;
// 2. Let lenValue be the result of calling the Get internal
// method of O with the argument "length".
// 3. Let len be ToUint32(lenValue).
const len = O.length >>> 0;
// 4. If IsCallable(callback) is false, throw a TypeError exception.
// See: http://es5.github.com/#x9.11
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
// 4. If IsCallable(callback) is false, throw a TypeError exception.
// See: http://es5.github.com/#x9.11
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
if (arguments.length > 1) {
T = thisArg;
}
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
if (arguments.length > 1) {
T = thisArg;
}
// 6. Let A be a new array created as if by the expression new Array(len)
// where Array is the standard built-in constructor with that name and
// len is the value of len.
const A = new Array(len);
// 6. Let A be a new array created as if by the expression new Array(len)
// where Array is the standard built-in constructor with that name and
// len is the value of len.
const A = new Array(len);
// 7. Let k be 0
k = 0;
// 7. Let k be 0
k = 0;
// 8. Repeat, while k < len
while (k < len) {
let kValue, mappedValue;
// 8. Repeat, while k < len
while (k < len) {
let kValue;
let mappedValue;
// a. Let Pk be ToString(k).
// This is implicit for LHS operands of the in operator
// b. Let kPresent be the result of calling the HasProperty internal
// method of O with argument Pk.
// This step can be combined with c
// c. If kPresent is true, then
if (k in O) {
// i. Let kValue be the result of calling the Get internal
// method of O with argument Pk.
kValue = O[k];
// a. Let Pk be ToString(k).
// This is implicit for LHS operands of the in operator
// b. Let kPresent be the result of calling the HasProperty internal
// method of O with argument Pk.
// This step can be combined with c
// c. If kPresent is true, then
if (k in O) {
// i. Let kValue be the result of calling the Get internal
// method of O with argument Pk.
kValue = O[k];
// ii. Let mappedValue be the result of calling the Call internal
// method of callback with T as the this value and argument
// list containing kValue, k, and O.
mappedValue = callback.call(T, kValue, k, O);
// ii. Let mappedValue be the result of calling the Call internal
// method of callback with T as the this value and argument
// list containing kValue, k, and O.
mappedValue = callback.call(T, kValue, k, O);
// iii. Call the DefineOwnProperty internal method of A with arguments
// Pk, Property Descriptor
// { Value: mappedValue,
// Writable: true,
// Enumerable: true,
// Configurable: true },
// and false.
// iii. Call the DefineOwnProperty internal method of A with arguments
// Pk, Property Descriptor
// { Value: mappedValue,
// Writable: true,
// Enumerable: true,
// Configurable: true },
// and false.
// In browsers that support Object.defineProperty, use the following:
// Object.defineProperty(A, k, {
// value: mappedValue,
// writable: true,
// enumerable: true,
// configurable: true
// });
// In browsers that support Object.defineProperty, use the following:
// Object.defineProperty(A, k, {
// value: mappedValue,
// writable: true,
// enumerable: true,
// configurable: true
// });
// For best browser support, use the following:
A[k] = mappedValue;
}
// d. Increase k by 1.
k++;
}
// For best browser support, use the following:
A[k] = mappedValue;
}
// d. Increase k by 1.
k++;
}
// 9. return A
return A;
};
// 9. return A
return A;
};
}
// Array.prototype.forEach
// ========================================================
// SOURCE:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
// Production steps of ECMA-262, Edition 5, 15.4.4.18
// Reference: http://es5.github.io/#x15.4.4.18
if (!Array.prototype.forEach) {
Array.prototype.forEach = function(callback, thisArg) {
let T, k;
// eslint-disable-next-line no-extend-native
Array.prototype.forEach = function(callback, thisArg) {
let T;
let k;
if (this === null || this === undefined) {
throw new TypeError(' this is null or not defined');
}
if (this === null || this === undefined) {
throw new TypeError(' this is null or not defined');
}
// 1. Let O be the result of calling ToObject passing the |this| value as the
// argument.
const O = Object(this);
// 1. Let O be the result of calling ToObject passing the |this| value as the
// argument.
const O = Object(this);
// 2. Let lenValue be the result of calling the Get internal method of O with the
// argument "length".
// 3. Let len be ToUint32(lenValue).
const len = O.length >>> 0;
// 2. Let lenValue be the result of calling the Get internal method of O with the
// argument "length".
// 3. Let len be ToUint32(lenValue).
const len = O.length >>> 0;
// 4. If IsCallable(callback) is false, throw a TypeError exception.
// See: http://es5.github.com/#x9.11
if (typeof callback !== "function") {
throw new TypeError(callback + ' is not a function');
}
// 4. If IsCallable(callback) is false, throw a TypeError exception.
// See: http://es5.github.com/#x9.11
if (typeof callback !== "function") {
throw new TypeError(callback + ' is not a function');
}
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
if (arguments.length > 1) {
T = thisArg;
}
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
if (arguments.length > 1) {
T = thisArg;
}
// 6. Let k be 0
k = 0;
// 6. Let k be 0
k = 0;
// 7. Repeat, while k < len
while (k < len) {
let kValue;
// 7. Repeat, while k < len
while (k < len) {
let kValue;
// a. Let Pk be ToString(k).
// This is implicit for LHS operands of the in operator
// b. Let kPresent be the result of calling the HasProperty internal
// method of O with
// argument Pk.
// This step can be combined with c
// c. If kPresent is true, then
if (k in O) {
// i. Let kValue be the result of calling the Get internal method of O with
// argument Pk
kValue = O[k];
// a. Let Pk be ToString(k).
// This is implicit for LHS operands of the in operator
// b. Let kPresent be the result of calling the HasProperty internal
// method of O with
// argument Pk.
// This step can be combined with c
// c. If kPresent is true, then
if (k in O) {
// i. Let kValue be the result of calling the Get internal method of O with
// argument Pk
kValue = O[k];
// ii. Call the Call internal method of callback with T as the this value and
// argument list containing kValue, k, and O.
callback.call(T, kValue, k, O);
}
// d. Increase k by 1.
k++;
}
// ii. Call the Call internal method of callback with T as the this value and
// argument list containing kValue, k, and O.
callback.call(T, kValue, k, O);
}
// d. Increase k by 1.
k++;
}
// 8. return undefined
};
};
}
}
@@ -657,7 +661,10 @@ export function isNumber(value: any): boolean {
* @return {string} a string with the hidden characters removed
*/
export function removeHiddenChars(str: string): string {
return unhomoglyph(str.normalize('NFD').replace(removeHiddenCharsRegex, ''));
if (typeof str === "string") {
return unhomoglyph(str.normalize('NFD').replace(removeHiddenCharsRegex, ''));
}
return "";
}
// Regex matching bunch of unicode control characters and otherwise misleading/invisible characters.
@@ -667,6 +674,7 @@ export function removeHiddenChars(str: string): string {
// LTR/RTL and other directional formatting marks U+202A - U+202F
// Combining characters U+0300 - U+036F
// Zero width no-break space (BOM) U+FEFF
// eslint-disable-next-line no-misleading-character-class
const removeHiddenCharsRegex = /[\u2000-\u200F\u202A-\u202F\u0300-\u036f\uFEFF\s]/g;
export function escapeRegExp(string: string): string {
@@ -725,9 +733,9 @@ export function defer() {
}
export async function promiseMapSeries<T>(
promises: Promise<T>[],
fn: (t: T) => void
): Promise<void> {
promises: Promise<T>[],
fn: (t: T) => void,
): Promise<void> {
for (const o of await promises) {
await fn(await o);
}
+1 -1
View File
@@ -176,7 +176,7 @@ MatrixCall.prototype.placeScreenSharingCall =
debuglog("Got screen stream, requesting audio stream...");
const audioConstraints = _getUserMediaVideoContraints('voice');
_placeCallWithConstraints(self, audioConstraints);
} catch(err) {
} catch (err) {
self.emit("error",
callError(
MatrixCall.ERR_NO_USER_MEDIA,
-72
View File
@@ -1,72 +0,0 @@
{
"rules": {
"class-name": false,
"comment-format": [
true
],
"curly": false,
"eofline": false,
"forin": false,
"indent": [
true,
"spaces"
],
"label-position": true,
"max-line-length": false,
"member-access": false,
"member-ordering": [
true,
"static-after-instance",
"variables-before-functions"
],
"no-arg": true,
"no-bitwise": false,
"no-console": false,
"no-construct": true,
"no-debugger": true,
"no-duplicate-variable": true,
"no-empty": false,
"no-eval": true,
"no-inferrable-types": true,
"no-shadowed-variable": true,
"no-string-literal": false,
"no-switch-case-fall-through": true,
"no-trailing-whitespace": true,
"no-unused-expression": true,
"no-use-before-declare": false,
"no-var-keyword": true,
"object-literal-sort-keys": false,
"one-line": [
true,
"check-open-brace",
"check-catch",
"check-else",
"check-whitespace"
],
"quotemark": false,
"radix": true,
"semicolon": [
"always"
],
"triple-equals": [],
"typedef-whitespace": [
true,
{
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
}
],
"variable-name": false,
"whitespace": [
true,
"check-branch",
"check-decl",
"check-operator",
"check-separator",
"check-type"
]
}
}
+764 -187
View File
File diff suppressed because it is too large Load Diff