You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-12-02 17:02:31 +03:00
Merge branch 'develop' into bwindels/verification-right-panel
This commit is contained in:
6
.babelrc
6
.babelrc
@@ -1,11 +1,9 @@
|
|||||||
{
|
{
|
||||||
|
"sourceMaps": true,
|
||||||
"presets": [
|
"presets": [
|
||||||
["@babel/preset-env", {
|
["@babel/preset-env", {
|
||||||
"targets": {
|
"targets": {
|
||||||
"browsers": [
|
"node": 10
|
||||||
"last 2 versions"
|
|
||||||
],
|
|
||||||
"node": 12
|
|
||||||
},
|
},
|
||||||
"modules": "commonjs"
|
"modules": "commonjs"
|
||||||
}],
|
}],
|
||||||
|
|||||||
@@ -1,11 +1,35 @@
|
|||||||
steps:
|
steps:
|
||||||
- label: ":eslint: Lint"
|
- label: ":eslint: JS Lint"
|
||||||
command:
|
command:
|
||||||
- "yarn install"
|
- "yarn install"
|
||||||
- "yarn lint"
|
- "yarn lint:js"
|
||||||
plugins:
|
plugins:
|
||||||
- docker#v3.0.1:
|
- docker#v3.0.1:
|
||||||
image: "node:10"
|
image: "node:12"
|
||||||
|
|
||||||
|
- label: ":tslint: TS Lint"
|
||||||
|
command:
|
||||||
|
- "yarn install"
|
||||||
|
- "yarn lint:ts"
|
||||||
|
plugins:
|
||||||
|
- docker#v3.0.1:
|
||||||
|
image: "node:12"
|
||||||
|
|
||||||
|
- label: ":typescript: Types Lint"
|
||||||
|
command:
|
||||||
|
- "yarn install"
|
||||||
|
- "yarn lint:types"
|
||||||
|
plugins:
|
||||||
|
- docker#v3.0.1:
|
||||||
|
image: "node:12"
|
||||||
|
|
||||||
|
- label: "🛠 Build"
|
||||||
|
command:
|
||||||
|
- "yarn install"
|
||||||
|
- "yarn build"
|
||||||
|
plugins:
|
||||||
|
- docker#v3.0.1:
|
||||||
|
image: "node:12"
|
||||||
|
|
||||||
- label: ":jest: Tests"
|
- label: ":jest: Tests"
|
||||||
command:
|
command:
|
||||||
@@ -13,7 +37,7 @@ steps:
|
|||||||
- "yarn test"
|
- "yarn test"
|
||||||
plugins:
|
plugins:
|
||||||
- docker#v3.0.1:
|
- docker#v3.0.1:
|
||||||
image: "node:10"
|
image: "node:12"
|
||||||
|
|
||||||
- label: "📃 Docs"
|
- label: "📃 Docs"
|
||||||
command:
|
command:
|
||||||
@@ -21,7 +45,7 @@ steps:
|
|||||||
- "yarn gendoc"
|
- "yarn gendoc"
|
||||||
plugins:
|
plugins:
|
||||||
- docker#v3.0.1:
|
- docker#v3.0.1:
|
||||||
image: "node:10"
|
image: "node:12"
|
||||||
|
|
||||||
- wait
|
- wait
|
||||||
|
|
||||||
|
|||||||
58
CHANGELOG.md
58
CHANGELOG.md
@@ -1,3 +1,61 @@
|
|||||||
|
Changes in [3.0.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v3.0.0) (2020-01-13)
|
||||||
|
================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v3.0.0-rc.1...v3.0.0)
|
||||||
|
|
||||||
|
* No changes from rc.1
|
||||||
|
|
||||||
|
Changes in [3.0.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v3.0.0-rc.1) (2020-01-06)
|
||||||
|
==========================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v2.4.6...v3.0.0-rc.1)
|
||||||
|
|
||||||
|
BREAKING CHANGES
|
||||||
|
================
|
||||||
|
* matrix-js-sdk no longer uses bluebird promises, so promises returned
|
||||||
|
by the js-sdk no longer support the done() method. Code that calls
|
||||||
|
done() on promises returned by the js-sdk will break and will need
|
||||||
|
to be updated to remove the done() call.
|
||||||
|
|
||||||
|
All Changes
|
||||||
|
===========
|
||||||
|
* Make displayName disambiguation more fuzzy especially against RTL/LTR
|
||||||
|
content
|
||||||
|
[\#1141](https://github.com/matrix-org/matrix-js-sdk/pull/1141)
|
||||||
|
* stop trying to resend event if we get M_TOO_LARGE
|
||||||
|
[\#1129](https://github.com/matrix-org/matrix-js-sdk/pull/1129)
|
||||||
|
* Fix creating a key backup with cross signing diabled
|
||||||
|
[\#1139](https://github.com/matrix-org/matrix-js-sdk/pull/1139)
|
||||||
|
* Use checkDeviceTrust with key backup
|
||||||
|
[\#1138](https://github.com/matrix-org/matrix-js-sdk/pull/1138)
|
||||||
|
* Add support for passthrough SSSS secrets
|
||||||
|
[\#1128](https://github.com/matrix-org/matrix-js-sdk/pull/1128)
|
||||||
|
* Add support for key backups using secret storage
|
||||||
|
[\#1118](https://github.com/matrix-org/matrix-js-sdk/pull/1118)
|
||||||
|
* Remove unused user verification event
|
||||||
|
[\#1117](https://github.com/matrix-org/matrix-js-sdk/pull/1117)
|
||||||
|
* Fix check for private keys
|
||||||
|
[\#1116](https://github.com/matrix-org/matrix-js-sdk/pull/1116)
|
||||||
|
* Restore watching mode for `start:watch`
|
||||||
|
[\#1115](https://github.com/matrix-org/matrix-js-sdk/pull/1115)
|
||||||
|
* Add secret storage bootstrap flow
|
||||||
|
[\#1079](https://github.com/matrix-org/matrix-js-sdk/pull/1079)
|
||||||
|
* Part 1 of many: Upgrade to babel@7 and TypeScript
|
||||||
|
[\#1112](https://github.com/matrix-org/matrix-js-sdk/pull/1112)
|
||||||
|
* Remove Bluebird: phase 2.5
|
||||||
|
[\#1100](https://github.com/matrix-org/matrix-js-sdk/pull/1100)
|
||||||
|
* Remove Bluebird: phase 3
|
||||||
|
[\#1088](https://github.com/matrix-org/matrix-js-sdk/pull/1088)
|
||||||
|
* ignore m.key.verification.done messages when we don't expect any more
|
||||||
|
messages
|
||||||
|
[\#1104](https://github.com/matrix-org/matrix-js-sdk/pull/1104)
|
||||||
|
* dont cancel on remote echo of own .request event
|
||||||
|
[\#1111](https://github.com/matrix-org/matrix-js-sdk/pull/1111)
|
||||||
|
* Refactor verification request code
|
||||||
|
[\#1109](https://github.com/matrix-org/matrix-js-sdk/pull/1109)
|
||||||
|
* Fix device list's cross-signing storage path
|
||||||
|
[\#1105](https://github.com/matrix-org/matrix-js-sdk/pull/1105)
|
||||||
|
* yarn upgrade
|
||||||
|
[\#1103](https://github.com/matrix-org/matrix-js-sdk/pull/1103)
|
||||||
|
|
||||||
Changes in [2.4.6](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v2.4.6) (2019-12-09)
|
Changes in [2.4.6](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v2.4.6) (2019-12-09)
|
||||||
================================================================================================
|
================================================================================================
|
||||||
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v2.4.6-rc.1...v2.4.6)
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v2.4.6-rc.1...v2.4.6)
|
||||||
|
|||||||
@@ -36,8 +36,16 @@ minutes.
|
|||||||
Code style
|
Code style
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
|
||||||
The code-style for matrix-js-sdk is not formally documented, but contributors
|
The js-sdk aims to target TypeScript/ES6. All new files should be written in
|
||||||
are encouraged to read the code style document for matrix-react-sdk
|
TypeScript and existing files should use ES6 principles where possible.
|
||||||
|
|
||||||
|
Members should not be exported as a default export in general - it causes problems
|
||||||
|
with the architecture of the SDK (index file becomes less clear) and could
|
||||||
|
introduce naming problems (as default exports get aliased upon import). In
|
||||||
|
general, avoid using `export default`.
|
||||||
|
|
||||||
|
The remaining code-style for matrix-js-sdk is not formally documented, but
|
||||||
|
contributors are encouraged to read the code style document for matrix-react-sdk
|
||||||
(`<https://github.com/matrix-org/matrix-react-sdk/blob/master/code_style.md>`_)
|
(`<https://github.com/matrix-org/matrix-react-sdk/blob/master/code_style.md>`_)
|
||||||
and follow the principles set out there.
|
and follow the principles set out there.
|
||||||
|
|
||||||
|
|||||||
36
README.md
36
README.md
@@ -9,12 +9,16 @@ Quickstart
|
|||||||
|
|
||||||
In a browser
|
In a browser
|
||||||
------------
|
------------
|
||||||
Download either the full or minified version from
|
Download the browser version from
|
||||||
https://github.com/matrix-org/matrix-js-sdk/releases/latest and add that as a
|
https://github.com/matrix-org/matrix-js-sdk/releases/latest and add that as a
|
||||||
``<script>`` to your page. There will be a global variable ``matrixcs``
|
``<script>`` to your page. There will be a global variable ``matrixcs``
|
||||||
attached to ``window`` through which you can access the SDK. See below for how to
|
attached to ``window`` through which you can access the SDK. See below for how to
|
||||||
include libolm to enable end-to-end-encryption.
|
include libolm to enable end-to-end-encryption.
|
||||||
|
|
||||||
|
The browser bundle supports recent versions of browsers. Typically this is ES2015
|
||||||
|
or `> 0.5%, last 2 versions, Firefox ESR, not dead` if using
|
||||||
|
[browserlists](https://github.com/browserslist/browserslist).
|
||||||
|
|
||||||
Please check [the working browser example](examples/browser) for more information.
|
Please check [the working browser example](examples/browser) for more information.
|
||||||
|
|
||||||
In Node.js
|
In Node.js
|
||||||
@@ -22,13 +26,18 @@ In Node.js
|
|||||||
|
|
||||||
Ensure you have the latest LTS version of Node.js installed.
|
Ensure you have the latest LTS version of Node.js installed.
|
||||||
|
|
||||||
Using `yarn` instead of `npm` is recommended. Please see the Yarn [install guide](https://yarnpkg.com/docs/install/) if you do not have it already.
|
This SDK targets Node 10 for compatibility, which translates to ES6. If you're using
|
||||||
|
a bundler like webpack you'll likely have to transpile dependencies, including this
|
||||||
|
SDK, to match your target browsers.
|
||||||
|
|
||||||
|
Using `yarn` instead of `npm` is recommended. Please see the Yarn [install guide](https://yarnpkg.com/docs/install/)
|
||||||
|
if you do not have it already.
|
||||||
|
|
||||||
``yarn add matrix-js-sdk``
|
``yarn add matrix-js-sdk``
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
var sdk = require("matrix-js-sdk");
|
import * as sdk from "matrix-js-sdk";
|
||||||
var client = sdk.createClient("https://matrix.org");
|
const client = sdk.createClient("https://matrix.org");
|
||||||
client.publicRooms(function(err, data) {
|
client.publicRooms(function(err, data) {
|
||||||
console.log("Public Rooms: %s", JSON.stringify(data));
|
console.log("Public Rooms: %s", JSON.stringify(data));
|
||||||
});
|
});
|
||||||
@@ -59,7 +68,7 @@ client.once('sync', function(state, prevState, res) {
|
|||||||
To send a message:
|
To send a message:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
var content = {
|
const content = {
|
||||||
"body": "message text",
|
"body": "message text",
|
||||||
"msgtype": "m.text"
|
"msgtype": "m.text"
|
||||||
};
|
};
|
||||||
@@ -191,10 +200,10 @@ This section provides some useful code snippets which demonstrate the
|
|||||||
core functionality of the SDK. These examples assume the SDK is setup like this:
|
core functionality of the SDK. These examples assume the SDK is setup like this:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
var sdk = require("matrix-js-sdk");
|
import * as sdk from "matrix-js-sdk";
|
||||||
var myUserId = "@example:localhost";
|
const myUserId = "@example:localhost";
|
||||||
var myAccessToken = "QGV4YW1wbGU6bG9jYWxob3N0.qPEvLuYfNBjxikiCjP";
|
const myAccessToken = "QGV4YW1wbGU6bG9jYWxob3N0.qPEvLuYfNBjxikiCjP";
|
||||||
var matrixClient = sdk.createClient({
|
const matrixClient = sdk.createClient({
|
||||||
baseUrl: "http://localhost:8008",
|
baseUrl: "http://localhost:8008",
|
||||||
accessToken: myAccessToken,
|
accessToken: myAccessToken,
|
||||||
userId: myUserId
|
userId: myUserId
|
||||||
@@ -247,11 +256,11 @@ Output:
|
|||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
matrixClient.on("RoomState.members", function(event, state, member) {
|
matrixClient.on("RoomState.members", function(event, state, member) {
|
||||||
var room = matrixClient.getRoom(state.roomId);
|
const room = matrixClient.getRoom(state.roomId);
|
||||||
if (!room) {
|
if (!room) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var memberList = state.getMembers();
|
const memberList = state.getMembers();
|
||||||
console.log(room.name);
|
console.log(room.name);
|
||||||
console.log(Array(room.name.length + 1).join("=")); // underline
|
console.log(Array(room.name.length + 1).join("=")); // underline
|
||||||
for (var i = 0; i < memberList.length; i++) {
|
for (var i = 0; i < memberList.length; i++) {
|
||||||
@@ -351,11 +360,6 @@ To build a browser version from scratch when developing::
|
|||||||
$ yarn build
|
$ yarn build
|
||||||
```
|
```
|
||||||
|
|
||||||
To constantly do builds when files are modified (using ``watchify``)::
|
|
||||||
```
|
|
||||||
$ yarn watch
|
|
||||||
```
|
|
||||||
|
|
||||||
To run tests (Jasmine)::
|
To run tests (Jasmine)::
|
||||||
```
|
```
|
||||||
$ yarn test
|
$ yarn test
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
var matrixcs = require("./lib/matrix");
|
|
||||||
const request = require('browser-request');
|
|
||||||
const queryString = require('qs');
|
|
||||||
|
|
||||||
matrixcs.request(function(opts, fn) {
|
|
||||||
// We manually fix the query string for browser-request because
|
|
||||||
// it doesn't correctly handle cases like ?via=one&via=two. Instead
|
|
||||||
// we mimic `request`'s query string interface to make it all work
|
|
||||||
// as expected.
|
|
||||||
// browser-request will happily take the constructed string as the
|
|
||||||
// query string without trying to modify it further.
|
|
||||||
opts.qs = queryString.stringify(opts.qs || {}, opts.qsStringifyOptions);
|
|
||||||
return request(opts, fn);
|
|
||||||
});
|
|
||||||
|
|
||||||
// just *accessing* indexedDB throws an exception in firefox with
|
|
||||||
// indexeddb disabled.
|
|
||||||
var indexedDB;
|
|
||||||
try {
|
|
||||||
indexedDB = global.indexedDB;
|
|
||||||
} catch(e) {}
|
|
||||||
|
|
||||||
// if our browser (appears to) support indexeddb, use an indexeddb crypto store.
|
|
||||||
if (indexedDB) {
|
|
||||||
matrixcs.setCryptoStoreFactory(
|
|
||||||
function() {
|
|
||||||
return new matrixcs.IndexedDBCryptoStore(
|
|
||||||
indexedDB, "matrix-js-sdk:crypto"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = matrixcs; // keep export for browserify package deps
|
|
||||||
global.matrixcs = matrixcs;
|
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
"use strict";
|
|
||||||
console.log("Loading browser sdk");
|
console.log("Loading browser sdk");
|
||||||
|
|
||||||
var client = matrixcs.createClient("http://matrix.org");
|
var client = matrixcs.createClient("http://matrix.org");
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
"use strict";
|
|
||||||
|
|
||||||
var myUserId = "@example:localhost";
|
var myUserId = "@example:localhost";
|
||||||
var myAccessToken = "QGV4YW1wbGU6bG9jYWxob3N0.qPEvLuYfNBjxikiCjP";
|
var myAccessToken = "QGV4YW1wbGU6bG9jYWxob3N0.qPEvLuYfNBjxikiCjP";
|
||||||
var sdk = require("matrix-js-sdk");
|
var sdk = require("matrix-js-sdk");
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
"use strict";
|
|
||||||
console.log("Loading browser sdk");
|
console.log("Loading browser sdk");
|
||||||
var BASE_URL = "https://matrix.org";
|
var BASE_URL = "https://matrix.org";
|
||||||
var TOKEN = "accesstokengoeshere";
|
var TOKEN = "accesstokengoeshere";
|
||||||
|
|||||||
6
index.js
6
index.js
@@ -1,6 +0,0 @@
|
|||||||
var matrixcs = require("./lib/matrix");
|
|
||||||
matrixcs.request(require("request"));
|
|
||||||
module.exports = matrixcs;
|
|
||||||
|
|
||||||
var utils = require("./lib/utils");
|
|
||||||
utils.runPolyfills();
|
|
||||||
71
package.json
71
package.json
@@ -1,21 +1,23 @@
|
|||||||
{
|
{
|
||||||
"name": "matrix-js-sdk",
|
"name": "matrix-js-sdk",
|
||||||
"version": "2.4.6",
|
"version": "3.0.0",
|
||||||
"description": "Matrix Client-Server SDK for Javascript",
|
"description": "Matrix Client-Server SDK for Javascript",
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test:watch": "jest spec/ --coverage --testEnvironment node --watch",
|
"start": "echo THIS IS FOR LEGACY PURPOSES ONLY. && babel src -w -s -d lib --verbose --extensions \".ts,.js\"",
|
||||||
"test": "jest spec/ --coverage --testEnvironment node",
|
"dist": "echo 'This is for the release script so it can make assets (browser bundle).' && yarn build",
|
||||||
"gendoc": "jsdoc -c jsdoc.json -P package.json",
|
|
||||||
"start": "yarn start:init && yarn start:watch",
|
|
||||||
"start:watch": "babel src -s -w --skip-initial-build -d lib --verbose --extensions \".ts,.js\"",
|
|
||||||
"start:init": "babel src -s -d lib --verbose --extensions \".ts,.js\"",
|
|
||||||
"clean": "rimraf lib dist",
|
"clean": "rimraf lib dist",
|
||||||
"build": "babel src -s -d lib --verbose --extensions \".ts,.js\" && rimraf dist && mkdir dist && browserify -d browser-index.js | exorcist dist/browser-matrix.js.map > dist/browser-matrix.js && terser -c -m -o dist/browser-matrix.min.js --source-map \"content='dist/browser-matrix.js.map'\" dist/browser-matrix.js",
|
"build": "yarn clean && git rev-parse HEAD > git-revision.txt && yarn build:compile && yarn build:compile-browser && yarn build:minify-browser && yarn build:types",
|
||||||
"dist": "yarn build",
|
"build:types": "tsc --emitDeclarationOnly",
|
||||||
"watch": "watchify -d browser-index.js -o 'exorcist dist/browser-matrix.js.map > dist/browser-matrix.js' -v",
|
"build:compile": "babel -d lib --verbose --extensions \".ts,.js\" src",
|
||||||
"lint": "eslint --max-warnings 93 src spec",
|
"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",
|
||||||
"prepare": "yarn clean && yarn build && git rev-parse HEAD > git-revision.txt"
|
"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 93 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"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -24,28 +26,23 @@
|
|||||||
"keywords": [
|
"keywords": [
|
||||||
"matrix-org"
|
"matrix-org"
|
||||||
],
|
],
|
||||||
"browser": "browser-index.js",
|
"main": "./lib/index.js",
|
||||||
|
"typings": "./lib/index.d.ts",
|
||||||
|
"browser": "./lib/browser-index.js",
|
||||||
|
"matrix_src_main": "./src/index.ts",
|
||||||
|
"matrix_src_browser": "./src/browser-index.js",
|
||||||
"author": "matrix.org",
|
"author": "matrix.org",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"files": [
|
"files": [
|
||||||
".babelrc",
|
"lib",
|
||||||
".eslintrc.js",
|
"src",
|
||||||
"spec/.eslintrc.js",
|
"git-revision.txt",
|
||||||
"CHANGELOG.md",
|
"CHANGELOG.md",
|
||||||
"CONTRIBUTING.rst",
|
"CONTRIBUTING.rst",
|
||||||
"LICENSE",
|
"LICENSE",
|
||||||
"README.md",
|
"README.md",
|
||||||
"RELEASING.md",
|
|
||||||
"examples",
|
|
||||||
"git-hooks",
|
|
||||||
"git-revision.txt",
|
|
||||||
"index.js",
|
|
||||||
"browser-index.js",
|
|
||||||
"lib",
|
|
||||||
"package.json",
|
"package.json",
|
||||||
"release.sh",
|
"release.sh"
|
||||||
"spec",
|
|
||||||
"src"
|
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"another-json": "^0.2.0",
|
"another-json": "^0.2.0",
|
||||||
@@ -69,11 +66,12 @@
|
|||||||
"@babel/preset-typescript": "^7.7.4",
|
"@babel/preset-typescript": "^7.7.4",
|
||||||
"@babel/register": "^7.7.4",
|
"@babel/register": "^7.7.4",
|
||||||
"@babel/runtime": "^7.7.6",
|
"@babel/runtime": "^7.7.6",
|
||||||
|
"@types/node": "12",
|
||||||
"babel-eslint": "^10.0.3",
|
"babel-eslint": "^10.0.3",
|
||||||
"babel-jest": "^24.9.0",
|
"babel-jest": "^24.9.0",
|
||||||
|
"babelify": "^10.0.0",
|
||||||
"better-docs": "^1.4.7",
|
"better-docs": "^1.4.7",
|
||||||
"browserify": "^16.2.3",
|
"browserify": "^16.5.0",
|
||||||
"browserify-shim": "^3.8.13",
|
|
||||||
"eslint": "^5.12.0",
|
"eslint": "^5.12.0",
|
||||||
"eslint-config-google": "^0.7.1",
|
"eslint-config-google": "^0.7.1",
|
||||||
"eslint-plugin-babel": "^5.3.0",
|
"eslint-plugin-babel": "^5.3.0",
|
||||||
@@ -84,15 +82,12 @@
|
|||||||
"matrix-mock-request": "^1.2.3",
|
"matrix-mock-request": "^1.2.3",
|
||||||
"olm": "https://packages.matrix.org/npm/olm/olm-3.1.4.tgz",
|
"olm": "https://packages.matrix.org/npm/olm/olm-3.1.4.tgz",
|
||||||
"rimraf": "^3.0.0",
|
"rimraf": "^3.0.0",
|
||||||
"source-map-support": "^0.5.13",
|
"terser": "^4.4.3",
|
||||||
"sourceify": "^1.0.0",
|
"tsify": "^4.0.1",
|
||||||
"terser": "^4.3.8",
|
"tslint": "^5.20.1",
|
||||||
"typescript": "^3.7.3",
|
"typescript": "^3.7.3"
|
||||||
"watchify": "^3.11.1"
|
|
||||||
},
|
},
|
||||||
"browserify": {
|
"jest": {
|
||||||
"transform": [
|
"testEnvironment": "node"
|
||||||
"sourceify"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
#
|
#
|
||||||
# Script to perform a release of matrix-js-sdk.
|
# Script to perform a release of matrix-js-sdk and downstream projects.
|
||||||
#
|
#
|
||||||
# Requires:
|
# Requires:
|
||||||
# github-changelog-generator; install via:
|
# github-changelog-generator; install via:
|
||||||
@@ -9,6 +9,8 @@
|
|||||||
# hub; install via brew (macOS) or source/pre-compiled binaries (debian) (https://github.com/github/hub) - Tested on v2.2.9
|
# hub; install via brew (macOS) or source/pre-compiled binaries (debian) (https://github.com/github/hub) - Tested on v2.2.9
|
||||||
# npm; typically installed by Node.js
|
# npm; typically installed by Node.js
|
||||||
# yarn; install via brew (macOS) or similar (https://yarnpkg.com/docs/install/)
|
# 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.
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -18,7 +19,7 @@ limitations under the License.
|
|||||||
* A mock implementation of the webstorage api
|
* A mock implementation of the webstorage api
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function MockStorageApi() {
|
export function MockStorageApi() {
|
||||||
this.data = {};
|
this.data = {};
|
||||||
this.keys = [];
|
this.keys = [];
|
||||||
this.length = 0;
|
this.length = 0;
|
||||||
@@ -52,5 +53,3 @@ MockStorageApi.prototype = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/** */
|
|
||||||
module.exports = MockStorageApi;
|
|
||||||
|
|||||||
@@ -16,16 +16,16 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
// load olm before the sdk if possible
|
// load olm before the sdk if possible
|
||||||
import './olm-loader';
|
import './olm-loader';
|
||||||
|
|
||||||
import sdk from '..';
|
|
||||||
import testUtils from './test-utils';
|
|
||||||
import MockHttpBackend from 'matrix-mock-request';
|
import MockHttpBackend from 'matrix-mock-request';
|
||||||
import LocalStorageCryptoStore from '../lib/crypto/store/localStorage-crypto-store';
|
import {LocalStorageCryptoStore} from '../src/crypto/store/localStorage-crypto-store';
|
||||||
import logger from '../lib/logger';
|
import {logger} from '../src/logger';
|
||||||
|
import {WebStorageSessionStore} from "../src/store/session/webstorage";
|
||||||
|
import {syncPromise} from "./test-utils";
|
||||||
|
import {createClient} from "../src/matrix";
|
||||||
|
import {MockStorageApi} from "./MockStorageApi";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper for a MockStorageApi, MockHttpBackend and MatrixClient
|
* Wrapper for a MockStorageApi, MockHttpBackend and MatrixClient
|
||||||
@@ -39,16 +39,16 @@ import logger from '../lib/logger';
|
|||||||
* session store. If undefined, we will create a MockStorageApi.
|
* session store. If undefined, we will create a MockStorageApi.
|
||||||
* @param {object} options additional options to pass to the client
|
* @param {object} options additional options to pass to the client
|
||||||
*/
|
*/
|
||||||
export default function TestClient(
|
export function TestClient(
|
||||||
userId, deviceId, accessToken, sessionStoreBackend, options,
|
userId, deviceId, accessToken, sessionStoreBackend, options,
|
||||||
) {
|
) {
|
||||||
this.userId = userId;
|
this.userId = userId;
|
||||||
this.deviceId = deviceId;
|
this.deviceId = deviceId;
|
||||||
|
|
||||||
if (sessionStoreBackend === undefined) {
|
if (sessionStoreBackend === undefined) {
|
||||||
sessionStoreBackend = new testUtils.MockStorageApi();
|
sessionStoreBackend = new MockStorageApi();
|
||||||
}
|
}
|
||||||
const sessionStore = new sdk.WebStorageSessionStore(sessionStoreBackend);
|
const sessionStore = new WebStorageSessionStore(sessionStoreBackend);
|
||||||
|
|
||||||
this.httpBackend = new MockHttpBackend();
|
this.httpBackend = new MockHttpBackend();
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ export default function TestClient(
|
|||||||
this.cryptoStore = new LocalStorageCryptoStore(sessionStoreBackend);
|
this.cryptoStore = new LocalStorageCryptoStore(sessionStoreBackend);
|
||||||
options.cryptoStore = this.cryptoStore;
|
options.cryptoStore = this.cryptoStore;
|
||||||
}
|
}
|
||||||
this.client = sdk.createClient(options);
|
this.client = createClient(options);
|
||||||
|
|
||||||
this.deviceKeys = null;
|
this.deviceKeys = null;
|
||||||
this.oneTimeKeys = {};
|
this.oneTimeKeys = {};
|
||||||
@@ -97,7 +97,7 @@ TestClient.prototype.start = function() {
|
|||||||
|
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
this.httpBackend.flushAllExpected(),
|
this.httpBackend.flushAllExpected(),
|
||||||
testUtils.syncPromise(this.client),
|
syncPromise(this.client),
|
||||||
]).then(() => {
|
]).then(() => {
|
||||||
logger.log(this + ': started');
|
logger.log(this + ': started');
|
||||||
});
|
});
|
||||||
@@ -225,7 +225,7 @@ TestClient.prototype.flushSync = function() {
|
|||||||
logger.log(`${this}: flushSync`);
|
logger.log(`${this}: flushSync`);
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
this.httpBackend.flush('/sync', 1),
|
this.httpBackend.flush('/sync', 1),
|
||||||
testUtils.syncPromise(this.client),
|
syncPromise(this.client),
|
||||||
]).then(() => {
|
]).then(() => {
|
||||||
logger.log(`${this}: flushSync completed`);
|
logger.log(`${this}: flushSync completed`);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2017 Vector Creations Ltd
|
Copyright 2017 Vector Creations Ltd
|
||||||
Copyright 2018 New Vector Ltd
|
Copyright 2018 New Vector Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -15,9 +16,9 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import TestClient from '../TestClient';
|
import {TestClient} from '../TestClient';
|
||||||
import testUtils from '../test-utils';
|
import * as testUtils from '../test-utils';
|
||||||
import logger from '../../lib/logger';
|
import {logger} from '../../src/logger';
|
||||||
|
|
||||||
const ROOM_ID = "!room:id";
|
const ROOM_ID = "!room:id";
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
Copyright 2016 OpenMarket Ltd
|
Copyright 2016 OpenMarket Ltd
|
||||||
Copyright 2017 Vector Creations Ltd
|
Copyright 2017 Vector Creations Ltd
|
||||||
Copyright 2018 New Vector Ltd
|
Copyright 2018 New Vector Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -24,17 +25,14 @@ limitations under the License.
|
|||||||
* See also `megolm.spec.js`.
|
* See also `megolm.spec.js`.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
"use strict";
|
|
||||||
import 'source-map-support/register';
|
|
||||||
|
|
||||||
// load olm before the sdk if possible
|
// load olm before the sdk if possible
|
||||||
import '../olm-loader';
|
import '../olm-loader';
|
||||||
|
|
||||||
const sdk = require("../..");
|
import {logger} from '../../src/logger';
|
||||||
const utils = require("../../lib/utils");
|
import * as testUtils from "../test-utils";
|
||||||
const testUtils = require("../test-utils");
|
import * as utils from "../../src/utils";
|
||||||
const TestClient = require('../TestClient').default;
|
import {TestClient} from "../TestClient";
|
||||||
import logger from '../../lib/logger';
|
import {CRYPTO_ENABLED} from "../../src/client";
|
||||||
|
|
||||||
let aliTestClient;
|
let aliTestClient;
|
||||||
const roomId = "!room:localhost";
|
const roomId = "!room:localhost";
|
||||||
@@ -400,7 +398,7 @@ function firstSync(testClient) {
|
|||||||
|
|
||||||
|
|
||||||
describe("MatrixClient crypto", function() {
|
describe("MatrixClient crypto", function() {
|
||||||
if (!sdk.CRYPTO_ENABLED) {
|
if (!CRYPTO_ENABLED) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,16 @@
|
|||||||
"use strict";
|
import * as utils from "../test-utils";
|
||||||
import 'source-map-support/register';
|
import {TestClient} from "../TestClient";
|
||||||
const sdk = require("../..");
|
|
||||||
const HttpBackend = require("matrix-mock-request");
|
|
||||||
const utils = require("../test-utils");
|
|
||||||
|
|
||||||
describe("MatrixClient events", function() {
|
describe("MatrixClient events", function() {
|
||||||
const baseUrl = "http://localhost.or.something";
|
|
||||||
let client;
|
let client;
|
||||||
let httpBackend;
|
let httpBackend;
|
||||||
const selfUserId = "@alice:localhost";
|
const selfUserId = "@alice:localhost";
|
||||||
const selfAccessToken = "aseukfgwef";
|
const selfAccessToken = "aseukfgwef";
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
httpBackend = new HttpBackend();
|
const testClient = new TestClient(selfUserId, "DEVICE", selfAccessToken);
|
||||||
sdk.request(httpBackend.requestFn);
|
client = testClient.client;
|
||||||
client = sdk.createClient({
|
httpBackend = testClient.httpBackend;
|
||||||
baseUrl: baseUrl,
|
|
||||||
userId: selfUserId,
|
|
||||||
accessToken: selfAccessToken,
|
|
||||||
});
|
|
||||||
httpBackend.when("GET", "/pushrules").respond(200, {});
|
httpBackend.when("GET", "/pushrules").respond(200, {});
|
||||||
httpBackend.when("POST", "/filter").respond(200, { filter_id: "a filter id" });
|
httpBackend.when("POST", "/filter").respond(200, { filter_id: "a filter id" });
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
"use strict";
|
import * as utils from "../test-utils";
|
||||||
import 'source-map-support/register';
|
import {EventTimeline} from "../../src/matrix";
|
||||||
const sdk = require("../..");
|
import {logger} from "../../src/logger";
|
||||||
const HttpBackend = require("matrix-mock-request");
|
import {TestClient} from "../TestClient";
|
||||||
const utils = require("../test-utils");
|
|
||||||
const EventTimeline = sdk.EventTimeline;
|
|
||||||
import logger from '../../lib/logger';
|
|
||||||
|
|
||||||
const baseUrl = "http://localhost.or.something";
|
|
||||||
const userId = "@alice:localhost";
|
const userId = "@alice:localhost";
|
||||||
const userName = "Alice";
|
const userName = "Alice";
|
||||||
const accessToken = "aseukfgwef";
|
const accessToken = "aseukfgwef";
|
||||||
@@ -103,8 +99,9 @@ describe("getEventTimeline support", function() {
|
|||||||
let client;
|
let client;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
httpBackend = new HttpBackend();
|
const testClient = new TestClient(userId, "DEVICE", accessToken);
|
||||||
sdk.request(httpBackend.requestFn);
|
client = testClient.client;
|
||||||
|
httpBackend = testClient.httpBackend;
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function() {
|
||||||
@@ -115,12 +112,6 @@ describe("getEventTimeline support", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("timeline support must be enabled to work", function() {
|
it("timeline support must be enabled to work", function() {
|
||||||
client = sdk.createClient({
|
|
||||||
baseUrl: baseUrl,
|
|
||||||
userId: userId,
|
|
||||||
accessToken: accessToken,
|
|
||||||
});
|
|
||||||
|
|
||||||
return startClient(httpBackend, client).then(function() {
|
return startClient(httpBackend, client).then(function() {
|
||||||
const room = client.getRoom(roomId);
|
const room = client.getRoom(roomId);
|
||||||
const timelineSet = room.getTimelineSets()[0];
|
const timelineSet = room.getTimelineSets()[0];
|
||||||
@@ -131,12 +122,15 @@ describe("getEventTimeline support", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("timeline support works when enabled", function() {
|
it("timeline support works when enabled", function() {
|
||||||
client = sdk.createClient({
|
const testClient = new TestClient(
|
||||||
baseUrl: baseUrl,
|
userId,
|
||||||
userId: userId,
|
"DEVICE",
|
||||||
accessToken: accessToken,
|
accessToken,
|
||||||
timelineSupport: true,
|
undefined,
|
||||||
});
|
{timelineSupport: true},
|
||||||
|
);
|
||||||
|
client = testClient.client;
|
||||||
|
httpBackend = testClient.httpBackend;
|
||||||
|
|
||||||
return startClient(httpBackend, client).then(() => {
|
return startClient(httpBackend, client).then(() => {
|
||||||
const room = client.getRoom(roomId);
|
const room = client.getRoom(roomId);
|
||||||
@@ -151,11 +145,7 @@ 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
|
||||||
client = sdk.createClient({
|
|
||||||
baseUrl: baseUrl,
|
|
||||||
userId: userId,
|
|
||||||
accessToken: accessToken,
|
|
||||||
});
|
|
||||||
let room;
|
let room;
|
||||||
|
|
||||||
return startClient(httpBackend, client).then(function() {
|
return startClient(httpBackend, client).then(function() {
|
||||||
@@ -223,15 +213,15 @@ describe("MatrixClient event timelines", function() {
|
|||||||
let httpBackend = null;
|
let httpBackend = null;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
httpBackend = new HttpBackend();
|
const testClient = new TestClient(
|
||||||
sdk.request(httpBackend.requestFn);
|
userId,
|
||||||
|
"DEVICE",
|
||||||
client = sdk.createClient({
|
accessToken,
|
||||||
baseUrl: baseUrl,
|
undefined,
|
||||||
userId: userId,
|
{timelineSupport: true},
|
||||||
accessToken: accessToken,
|
);
|
||||||
timelineSupport: true,
|
client = testClient.client;
|
||||||
});
|
httpBackend = testClient.httpBackend;
|
||||||
|
|
||||||
return startClient(httpBackend, client);
|
return startClient(httpBackend, client);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,39 +1,23 @@
|
|||||||
"use strict";
|
import * as utils from "../test-utils";
|
||||||
import 'source-map-support/register';
|
import {CRYPTO_ENABLED} from "../../src/client";
|
||||||
const sdk = require("../..");
|
import {Filter, MemoryStore, Room} from "../../src/matrix";
|
||||||
const HttpBackend = require("matrix-mock-request");
|
import {TestClient} from "../TestClient";
|
||||||
const publicGlobals = require("../../lib/matrix");
|
|
||||||
const Room = publicGlobals.Room;
|
|
||||||
const MemoryStore = publicGlobals.MemoryStore;
|
|
||||||
const Filter = publicGlobals.Filter;
|
|
||||||
const utils = require("../test-utils");
|
|
||||||
const MockStorageApi = require("../MockStorageApi");
|
|
||||||
|
|
||||||
describe("MatrixClient", function() {
|
describe("MatrixClient", function() {
|
||||||
const baseUrl = "http://localhost.or.something";
|
|
||||||
let client = null;
|
let client = null;
|
||||||
let httpBackend = null;
|
let httpBackend = null;
|
||||||
let store = null;
|
let store = null;
|
||||||
let sessionStore = null;
|
|
||||||
const userId = "@alice:localhost";
|
const userId = "@alice:localhost";
|
||||||
const accessToken = "aseukfgwef";
|
const accessToken = "aseukfgwef";
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
httpBackend = new HttpBackend();
|
|
||||||
store = new MemoryStore();
|
store = new MemoryStore();
|
||||||
|
|
||||||
const mockStorage = new MockStorageApi();
|
const testClient = new TestClient(userId, "aliceDevice", accessToken, undefined, {
|
||||||
sessionStore = new sdk.WebStorageSessionStore(mockStorage);
|
|
||||||
|
|
||||||
sdk.request(httpBackend.requestFn);
|
|
||||||
client = sdk.createClient({
|
|
||||||
baseUrl: baseUrl,
|
|
||||||
userId: userId,
|
|
||||||
deviceId: "aliceDevice",
|
|
||||||
accessToken: accessToken,
|
|
||||||
store: store,
|
store: store,
|
||||||
sessionStore: sessionStore,
|
|
||||||
});
|
});
|
||||||
|
httpBackend = testClient.httpBackend;
|
||||||
|
client = testClient.client;
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function() {
|
||||||
@@ -303,7 +287,7 @@ describe("MatrixClient", function() {
|
|||||||
|
|
||||||
|
|
||||||
describe("downloadKeys", function() {
|
describe("downloadKeys", function() {
|
||||||
if (!sdk.CRYPTO_ENABLED) {
|
if (!CRYPTO_ENABLED) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
"use strict";
|
import * as utils from "../test-utils";
|
||||||
import 'source-map-support/register';
|
import HttpBackend from "matrix-mock-request";
|
||||||
const sdk = require("../..");
|
import {MatrixClient} from "../../src/matrix";
|
||||||
const MatrixClient = sdk.MatrixClient;
|
import {MatrixScheduler} from "../../src/scheduler";
|
||||||
const HttpBackend = require("matrix-mock-request");
|
import {MemoryStore} from "../../src/store/memory";
|
||||||
const utils = require("../test-utils");
|
|
||||||
|
|
||||||
describe("MatrixClient opts", function() {
|
describe("MatrixClient opts", function() {
|
||||||
const baseUrl = "http://localhost.or.something";
|
const baseUrl = "http://localhost.or.something";
|
||||||
@@ -71,7 +70,7 @@ describe("MatrixClient opts", function() {
|
|||||||
baseUrl: baseUrl,
|
baseUrl: baseUrl,
|
||||||
userId: userId,
|
userId: userId,
|
||||||
accessToken: accessToken,
|
accessToken: accessToken,
|
||||||
scheduler: new sdk.MatrixScheduler(),
|
scheduler: new MatrixScheduler(),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -124,7 +123,7 @@ describe("MatrixClient opts", function() {
|
|||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
client = new MatrixClient({
|
client = new MatrixClient({
|
||||||
request: httpBackend.requestFn,
|
request: httpBackend.requestFn,
|
||||||
store: new sdk.MemoryStore(),
|
store: new MemoryStore(),
|
||||||
baseUrl: baseUrl,
|
baseUrl: baseUrl,
|
||||||
userId: userId,
|
userId: userId,
|
||||||
accessToken: accessToken,
|
accessToken: accessToken,
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
"use strict";
|
import {EventStatus} from "../../src/matrix";
|
||||||
import 'source-map-support/register';
|
import {MatrixScheduler} from "../../src/scheduler";
|
||||||
|
import {Room} from "../../src/models/room";
|
||||||
const sdk = require("../..");
|
import {TestClient} from "../TestClient";
|
||||||
const HttpBackend = require("matrix-mock-request");
|
|
||||||
const EventStatus = sdk.EventStatus;
|
|
||||||
|
|
||||||
describe("MatrixClient retrying", function() {
|
describe("MatrixClient retrying", function() {
|
||||||
const baseUrl = "http://localhost.or.something";
|
|
||||||
let client = null;
|
let client = null;
|
||||||
let httpBackend = null;
|
let httpBackend = null;
|
||||||
let scheduler;
|
let scheduler;
|
||||||
@@ -16,16 +13,17 @@ describe("MatrixClient retrying", function() {
|
|||||||
let room;
|
let room;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
httpBackend = new HttpBackend();
|
scheduler = new MatrixScheduler();
|
||||||
sdk.request(httpBackend.requestFn);
|
const testClient = new TestClient(
|
||||||
scheduler = new sdk.MatrixScheduler();
|
userId,
|
||||||
client = sdk.createClient({
|
"DEVICE",
|
||||||
baseUrl: baseUrl,
|
accessToken,
|
||||||
userId: userId,
|
undefined,
|
||||||
accessToken: accessToken,
|
{scheduler},
|
||||||
scheduler: scheduler,
|
);
|
||||||
});
|
httpBackend = testClient.httpBackend;
|
||||||
room = new sdk.Room(roomId);
|
client = testClient.client;
|
||||||
|
room = new Room(roomId);
|
||||||
client.store.storeRoom(room);
|
client.store.storeRoom(room);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
"use strict";
|
import * as utils from "../test-utils";
|
||||||
import 'source-map-support/register';
|
import {EventStatus} from "../../src/models/event";
|
||||||
const sdk = require("../..");
|
import {TestClient} from "../TestClient";
|
||||||
const EventStatus = sdk.EventStatus;
|
|
||||||
const HttpBackend = require("matrix-mock-request");
|
|
||||||
const utils = require("../test-utils");
|
|
||||||
|
|
||||||
describe("MatrixClient room timelines", function() {
|
describe("MatrixClient room timelines", function() {
|
||||||
const baseUrl = "http://localhost.or.something";
|
|
||||||
let client = null;
|
let client = null;
|
||||||
let httpBackend = null;
|
let httpBackend = null;
|
||||||
const userId = "@alice:localhost";
|
const userId = "@alice:localhost";
|
||||||
@@ -101,15 +98,17 @@ describe("MatrixClient room timelines", function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
httpBackend = new HttpBackend();
|
|
||||||
sdk.request(httpBackend.requestFn);
|
|
||||||
client = sdk.createClient({
|
|
||||||
baseUrl: baseUrl,
|
|
||||||
userId: userId,
|
|
||||||
accessToken: accessToken,
|
|
||||||
// these tests should work with or without timelineSupport
|
// these tests should work with or without timelineSupport
|
||||||
timelineSupport: true,
|
const testClient = new TestClient(
|
||||||
});
|
userId,
|
||||||
|
"DEVICE",
|
||||||
|
accessToken,
|
||||||
|
undefined,
|
||||||
|
{timelineSupport: true},
|
||||||
|
);
|
||||||
|
httpBackend = testClient.httpBackend;
|
||||||
|
client = testClient.client;
|
||||||
|
|
||||||
setNextSyncData();
|
setNextSyncData();
|
||||||
httpBackend.when("GET", "/pushrules").respond(200, {});
|
httpBackend.when("GET", "/pushrules").respond(200, {});
|
||||||
httpBackend.when("POST", "/filter").respond(200, { filter_id: "fid" });
|
httpBackend.when("POST", "/filter").respond(200, { filter_id: "fid" });
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
"use strict";
|
import {MatrixEvent} from "../../src/models/event";
|
||||||
import 'source-map-support/register';
|
import {EventTimeline} from "../../src/models/event-timeline";
|
||||||
const sdk = require("../..");
|
import * as utils from "../test-utils";
|
||||||
const HttpBackend = require("matrix-mock-request");
|
import {TestClient} from "../TestClient";
|
||||||
const utils = require("../test-utils");
|
|
||||||
const MatrixEvent = sdk.MatrixEvent;
|
|
||||||
const EventTimeline = sdk.EventTimeline;
|
|
||||||
|
|
||||||
describe("MatrixClient syncing", function() {
|
describe("MatrixClient syncing", function() {
|
||||||
const baseUrl = "http://localhost.or.something";
|
|
||||||
let client = null;
|
let client = null;
|
||||||
let httpBackend = null;
|
let httpBackend = null;
|
||||||
const selfUserId = "@alice:localhost";
|
const selfUserId = "@alice:localhost";
|
||||||
@@ -20,13 +16,9 @@ describe("MatrixClient syncing", function() {
|
|||||||
const roomTwo = "!bar:localhost";
|
const roomTwo = "!bar:localhost";
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
httpBackend = new HttpBackend();
|
const testClient = new TestClient(selfUserId, "DEVICE", selfAccessToken);
|
||||||
sdk.request(httpBackend.requestFn);
|
httpBackend = testClient.httpBackend;
|
||||||
client = sdk.createClient({
|
client = testClient.client;
|
||||||
baseUrl: baseUrl,
|
|
||||||
userId: selfUserId,
|
|
||||||
accessToken: selfAccessToken,
|
|
||||||
});
|
|
||||||
httpBackend.when("GET", "/pushrules").respond(200, {});
|
httpBackend.when("GET", "/pushrules").respond(200, {});
|
||||||
httpBackend.when("POST", "/filter").respond(200, { filter_id: "a filter id" });
|
httpBackend.when("POST", "/filter").respond(200, { filter_id: "a filter id" });
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2016 OpenMarket Ltd
|
Copyright 2016 OpenMarket Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -14,14 +15,11 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
"use strict";
|
import anotherjson from "another-json";
|
||||||
|
import * as utils from "../../src/utils";
|
||||||
const anotherjson = require('another-json');
|
import * as testUtils from "../test-utils";
|
||||||
|
import {TestClient} from "../TestClient";
|
||||||
const utils = require('../../lib/utils');
|
import {logger} from "../../src/logger";
|
||||||
const testUtils = require('../test-utils');
|
|
||||||
const TestClient = require('../TestClient').default;
|
|
||||||
import logger from '../../lib/logger';
|
|
||||||
|
|
||||||
const ROOM_ID = "!room:id";
|
const ROOM_ID = "!room:id";
|
||||||
|
|
||||||
@@ -617,6 +615,9 @@ describe("megolm", function() {
|
|||||||
).respond(200, {
|
).respond(200, {
|
||||||
event_id: '$event_id',
|
event_id: '$event_id',
|
||||||
});
|
});
|
||||||
|
aliceTestClient.httpBackend.when(
|
||||||
|
'PUT', '/sendToDevice/org.matrix.room_key.withheld/',
|
||||||
|
).respond(200, {});
|
||||||
|
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
aliceTestClient.client.sendTextMessage(ROOM_ID, 'test'),
|
aliceTestClient.client.sendTextMessage(ROOM_ID, 'test'),
|
||||||
@@ -714,6 +715,9 @@ describe("megolm", function() {
|
|||||||
event_id: '$event_id',
|
event_id: '$event_id',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
aliceTestClient.httpBackend.when(
|
||||||
|
'PUT', '/sendToDevice/org.matrix.room_key.withheld/',
|
||||||
|
).respond(200, {});
|
||||||
|
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
aliceTestClient.client.sendTextMessage(ROOM_ID, 'test2'),
|
aliceTestClient.client.sendTextMessage(ROOM_ID, 'test2'),
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2017 Vector creations Ltd
|
Copyright 2017 Vector creations Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -14,7 +15,7 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import logger from '../lib/logger';
|
import {logger} from '../src/logger';
|
||||||
|
|
||||||
// try to load the olm library.
|
// try to load the olm library.
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
"use strict";
|
|
||||||
// load olm before the sdk if possible
|
// load olm before the sdk if possible
|
||||||
import './olm-loader';
|
import './olm-loader';
|
||||||
|
|
||||||
import logger from '../lib/logger';
|
import {logger} from '../src/logger';
|
||||||
import sdk from '..';
|
import {MatrixEvent} from "../src/models/event";
|
||||||
const MatrixEvent = sdk.MatrixEvent;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a promise that is resolved when the client next emits a
|
* Return a promise that is resolved when the client next emits a
|
||||||
@@ -13,7 +11,7 @@ const MatrixEvent = sdk.MatrixEvent;
|
|||||||
* @param {Number=} count Number of syncs to wait for (default 1)
|
* @param {Number=} count Number of syncs to wait for (default 1)
|
||||||
* @return {Promise} Resolves once the client has emitted a SYNCING event
|
* @return {Promise} Resolves once the client has emitted a SYNCING event
|
||||||
*/
|
*/
|
||||||
module.exports.syncPromise = function(client, count) {
|
export function syncPromise(client, count) {
|
||||||
if (count === undefined) {
|
if (count === undefined) {
|
||||||
count = 1;
|
count = 1;
|
||||||
}
|
}
|
||||||
@@ -24,7 +22,7 @@ module.exports.syncPromise = function(client, count) {
|
|||||||
const p = new Promise((resolve, reject) => {
|
const p = new Promise((resolve, reject) => {
|
||||||
const cb = (state) => {
|
const cb = (state) => {
|
||||||
logger.log(`${Date.now()} syncPromise(${count}): ${state}`);
|
logger.log(`${Date.now()} syncPromise(${count}): ${state}`);
|
||||||
if (state == 'SYNCING') {
|
if (state === 'SYNCING') {
|
||||||
resolve();
|
resolve();
|
||||||
} else {
|
} else {
|
||||||
client.once('sync', cb);
|
client.once('sync', cb);
|
||||||
@@ -34,9 +32,9 @@ module.exports.syncPromise = function(client, count) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return p.then(() => {
|
return p.then(() => {
|
||||||
return module.exports.syncPromise(client, count-1);
|
return syncPromise(client, count-1);
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a spy for an object and automatically spy its methods.
|
* Create a spy for an object and automatically spy its methods.
|
||||||
@@ -44,7 +42,7 @@ module.exports.syncPromise = function(client, count) {
|
|||||||
* @param {string} name The name of the class
|
* @param {string} name The name of the class
|
||||||
* @return {Object} An instantiated object with spied methods/properties.
|
* @return {Object} An instantiated object with spied methods/properties.
|
||||||
*/
|
*/
|
||||||
module.exports.mock = function(constr, name) {
|
export function mock(constr, name) {
|
||||||
// Based on
|
// Based on
|
||||||
// http://eclipsesource.com/blogs/2014/03/27/mocks-in-jasmine-tests/
|
// http://eclipsesource.com/blogs/2014/03/27/mocks-in-jasmine-tests/
|
||||||
const HelperConstr = new Function(); // jshint ignore:line
|
const HelperConstr = new Function(); // jshint ignore:line
|
||||||
@@ -65,7 +63,7 @@ module.exports.mock = function(constr, name) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an Event.
|
* Create an Event.
|
||||||
@@ -78,7 +76,7 @@ module.exports.mock = function(constr, name) {
|
|||||||
* @param {boolean} opts.event True to make a MatrixEvent.
|
* @param {boolean} opts.event True to make a MatrixEvent.
|
||||||
* @return {Object} a JSON object representing this event.
|
* @return {Object} a JSON object representing this event.
|
||||||
*/
|
*/
|
||||||
module.exports.mkEvent = function(opts) {
|
export function mkEvent(opts) {
|
||||||
if (!opts.type || !opts.content) {
|
if (!opts.type || !opts.content) {
|
||||||
throw new Error("Missing .type or .content =>" + JSON.stringify(opts));
|
throw new Error("Missing .type or .content =>" + JSON.stringify(opts));
|
||||||
}
|
}
|
||||||
@@ -97,14 +95,14 @@ module.exports.mkEvent = function(opts) {
|
|||||||
event.state_key = "";
|
event.state_key = "";
|
||||||
}
|
}
|
||||||
return opts.event ? new MatrixEvent(event) : event;
|
return opts.event ? new MatrixEvent(event) : event;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an m.presence event.
|
* Create an m.presence event.
|
||||||
* @param {Object} opts Values for the presence.
|
* @param {Object} opts Values for the presence.
|
||||||
* @return {Object|MatrixEvent} The event
|
* @return {Object|MatrixEvent} The event
|
||||||
*/
|
*/
|
||||||
module.exports.mkPresence = function(opts) {
|
export function mkPresence(opts) {
|
||||||
if (!opts.user) {
|
if (!opts.user) {
|
||||||
throw new Error("Missing user");
|
throw new Error("Missing user");
|
||||||
}
|
}
|
||||||
@@ -120,7 +118,7 @@ module.exports.mkPresence = function(opts) {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
return opts.event ? new MatrixEvent(event) : event;
|
return opts.event ? new MatrixEvent(event) : event;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an m.room.member event.
|
* Create an m.room.member event.
|
||||||
@@ -135,7 +133,7 @@ module.exports.mkPresence = function(opts) {
|
|||||||
* @param {boolean} opts.event True to make a MatrixEvent.
|
* @param {boolean} opts.event True to make a MatrixEvent.
|
||||||
* @return {Object|MatrixEvent} The event
|
* @return {Object|MatrixEvent} The event
|
||||||
*/
|
*/
|
||||||
module.exports.mkMembership = function(opts) {
|
export function mkMembership(opts) {
|
||||||
opts.type = "m.room.member";
|
opts.type = "m.room.member";
|
||||||
if (!opts.skey) {
|
if (!opts.skey) {
|
||||||
opts.skey = opts.sender || opts.user;
|
opts.skey = opts.sender || opts.user;
|
||||||
@@ -152,8 +150,8 @@ module.exports.mkMembership = function(opts) {
|
|||||||
if (opts.url) {
|
if (opts.url) {
|
||||||
opts.content.avatar_url = opts.url;
|
opts.content.avatar_url = opts.url;
|
||||||
}
|
}
|
||||||
return module.exports.mkEvent(opts);
|
return mkEvent(opts);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an m.room.message event.
|
* Create an m.room.message event.
|
||||||
@@ -164,7 +162,7 @@ module.exports.mkMembership = function(opts) {
|
|||||||
* @param {boolean} opts.event True to make a MatrixEvent.
|
* @param {boolean} opts.event True to make a MatrixEvent.
|
||||||
* @return {Object|MatrixEvent} The event
|
* @return {Object|MatrixEvent} The event
|
||||||
*/
|
*/
|
||||||
module.exports.mkMessage = function(opts) {
|
export function mkMessage(opts) {
|
||||||
opts.type = "m.room.message";
|
opts.type = "m.room.message";
|
||||||
if (!opts.msg) {
|
if (!opts.msg) {
|
||||||
opts.msg = "Random->" + Math.random();
|
opts.msg = "Random->" + Math.random();
|
||||||
@@ -176,8 +174,8 @@ module.exports.mkMessage = function(opts) {
|
|||||||
msgtype: "m.text",
|
msgtype: "m.text",
|
||||||
body: opts.msg,
|
body: opts.msg,
|
||||||
};
|
};
|
||||||
return module.exports.mkEvent(opts);
|
return mkEvent(opts);
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -185,10 +183,10 @@ module.exports.mkMessage = function(opts) {
|
|||||||
*
|
*
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
module.exports.MockStorageApi = function() {
|
export function MockStorageApi() {
|
||||||
this.data = {};
|
this.data = {};
|
||||||
};
|
}
|
||||||
module.exports.MockStorageApi.prototype = {
|
MockStorageApi.prototype = {
|
||||||
get length() {
|
get length() {
|
||||||
return Object.keys(this.data).length;
|
return Object.keys(this.data).length;
|
||||||
},
|
},
|
||||||
@@ -213,7 +211,7 @@ module.exports.MockStorageApi.prototype = {
|
|||||||
* @param {MatrixEvent} event
|
* @param {MatrixEvent} event
|
||||||
* @returns {Promise} promise which resolves (to `event`) when the event has been decrypted
|
* @returns {Promise} promise which resolves (to `event`) when the event has been decrypted
|
||||||
*/
|
*/
|
||||||
module.exports.awaitDecryption = function(event) {
|
export function awaitDecryption(event) {
|
||||||
if (!event.isBeingDecrypted()) {
|
if (!event.isBeingDecrypted()) {
|
||||||
return Promise.resolve(event);
|
return Promise.resolve(event);
|
||||||
}
|
}
|
||||||
@@ -226,19 +224,19 @@ module.exports.awaitDecryption = function(event) {
|
|||||||
resolve(ev);
|
resolve(ev);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
const HttpResponse = module.exports.HttpResponse = function(
|
export function HttpResponse(
|
||||||
httpLookups, acceptKeepalives, ignoreUnhandledSync,
|
httpLookups, acceptKeepalives, ignoreUnhandledSync,
|
||||||
) {
|
) {
|
||||||
this.httpLookups = httpLookups;
|
this.httpLookups = httpLookups;
|
||||||
this.acceptKeepalives = acceptKeepalives === undefined ? true : acceptKeepalives;
|
this.acceptKeepalives = acceptKeepalives === undefined ? true : acceptKeepalives;
|
||||||
this.ignoreUnhandledSync = ignoreUnhandledSync;
|
this.ignoreUnhandledSync = ignoreUnhandledSync;
|
||||||
this.pendingLookup = null;
|
this.pendingLookup = null;
|
||||||
};
|
}
|
||||||
|
|
||||||
HttpResponse.prototype.request = function HttpResponse(
|
HttpResponse.prototype.request = function(
|
||||||
cb, method, path, qp, data, prefix,
|
cb, method, path, qp, data, prefix,
|
||||||
) {
|
) {
|
||||||
if (path === HttpResponse.KEEP_ALIVE_PATH && this.acceptKeepalives) {
|
if (path === HttpResponse.KEEP_ALIVE_PATH && this.acceptKeepalives) {
|
||||||
@@ -351,7 +349,7 @@ HttpResponse.defaultResponses = function(userId) {
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.setHttpResponses = function setHttpResponses(
|
export function setHttpResponses(
|
||||||
client, responses, acceptKeepalives, ignoreUnhandledSyncs,
|
client, responses, acceptKeepalives, ignoreUnhandledSyncs,
|
||||||
) {
|
) {
|
||||||
const httpResponseObj = new HttpResponse(
|
const httpResponseObj = new HttpResponse(
|
||||||
@@ -367,4 +365,4 @@ module.exports.setHttpResponses = function setHttpResponses(
|
|||||||
client._http.authedRequestWithPrefix.mockImplementation(httpReq);
|
client._http.authedRequestWithPrefix.mockImplementation(httpReq);
|
||||||
client._http.requestWithPrefix.mockImplementation(httpReq);
|
client._http.requestWithPrefix.mockImplementation(httpReq);
|
||||||
client._http.request.mockImplementation(httpReq);
|
client._http.request.mockImplementation(httpReq);
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2018 New Vector Ltd
|
Copyright 2018 New Vector Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -13,15 +14,10 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
"use strict";
|
|
||||||
|
|
||||||
import 'source-map-support/register';
|
|
||||||
const sdk = require("../..");
|
|
||||||
|
|
||||||
const AutoDiscovery = sdk.AutoDiscovery;
|
|
||||||
|
|
||||||
import MockHttpBackend from "matrix-mock-request";
|
import MockHttpBackend from "matrix-mock-request";
|
||||||
|
import * as sdk from "../../src";
|
||||||
|
import {AutoDiscovery} from "../../src/autodiscovery";
|
||||||
|
|
||||||
describe("AutoDiscovery", function() {
|
describe("AutoDiscovery", function() {
|
||||||
let httpBackend = null;
|
let httpBackend = null;
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
"use strict";
|
import {getHttpUriForMxc, getIdenticonUri} from "../../src/content-repo";
|
||||||
import 'source-map-support/register';
|
|
||||||
const ContentRepo = require("../../lib/content-repo");
|
|
||||||
|
|
||||||
describe("ContentRepo", function() {
|
describe("ContentRepo", function() {
|
||||||
const baseUrl = "https://my.home.server";
|
const baseUrl = "https://my.home.server";
|
||||||
@@ -9,7 +7,7 @@ describe("ContentRepo", function() {
|
|||||||
it("should do nothing to HTTP URLs when allowing direct links", function() {
|
it("should do nothing to HTTP URLs when allowing direct links", function() {
|
||||||
const httpUrl = "http://example.com/image.jpeg";
|
const httpUrl = "http://example.com/image.jpeg";
|
||||||
expect(
|
expect(
|
||||||
ContentRepo.getHttpUriForMxc(
|
getHttpUriForMxc(
|
||||||
baseUrl, httpUrl, undefined, undefined, undefined, true,
|
baseUrl, httpUrl, undefined, undefined, undefined, true,
|
||||||
),
|
),
|
||||||
).toEqual(httpUrl);
|
).toEqual(httpUrl);
|
||||||
@@ -17,25 +15,25 @@ describe("ContentRepo", function() {
|
|||||||
|
|
||||||
it("should return the empty string HTTP URLs by default", function() {
|
it("should return the empty string HTTP URLs by default", function() {
|
||||||
const httpUrl = "http://example.com/image.jpeg";
|
const httpUrl = "http://example.com/image.jpeg";
|
||||||
expect(ContentRepo.getHttpUriForMxc(baseUrl, httpUrl)).toEqual("");
|
expect(getHttpUriForMxc(baseUrl, httpUrl)).toEqual("");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return a download URL if no width/height/resize are specified",
|
it("should return a download URL if no width/height/resize are specified",
|
||||||
function() {
|
function() {
|
||||||
const mxcUri = "mxc://server.name/resourceid";
|
const mxcUri = "mxc://server.name/resourceid";
|
||||||
expect(ContentRepo.getHttpUriForMxc(baseUrl, mxcUri)).toEqual(
|
expect(getHttpUriForMxc(baseUrl, mxcUri)).toEqual(
|
||||||
baseUrl + "/_matrix/media/r0/download/server.name/resourceid",
|
baseUrl + "/_matrix/media/r0/download/server.name/resourceid",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return the empty string for null input", function() {
|
it("should return the empty string for null input", function() {
|
||||||
expect(ContentRepo.getHttpUriForMxc(null)).toEqual("");
|
expect(getHttpUriForMxc(null)).toEqual("");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return a thumbnail URL if a width/height/resize is specified",
|
it("should return a thumbnail URL if a width/height/resize is specified",
|
||||||
function() {
|
function() {
|
||||||
const mxcUri = "mxc://server.name/resourceid";
|
const mxcUri = "mxc://server.name/resourceid";
|
||||||
expect(ContentRepo.getHttpUriForMxc(baseUrl, mxcUri, 32, 64, "crop")).toEqual(
|
expect(getHttpUriForMxc(baseUrl, mxcUri, 32, 64, "crop")).toEqual(
|
||||||
baseUrl + "/_matrix/media/r0/thumbnail/server.name/resourceid" +
|
baseUrl + "/_matrix/media/r0/thumbnail/server.name/resourceid" +
|
||||||
"?width=32&height=64&method=crop",
|
"?width=32&height=64&method=crop",
|
||||||
);
|
);
|
||||||
@@ -44,7 +42,7 @@ describe("ContentRepo", function() {
|
|||||||
it("should put fragments from mxc:// URIs after any query parameters",
|
it("should put fragments from mxc:// URIs after any query parameters",
|
||||||
function() {
|
function() {
|
||||||
const mxcUri = "mxc://server.name/resourceid#automade";
|
const mxcUri = "mxc://server.name/resourceid#automade";
|
||||||
expect(ContentRepo.getHttpUriForMxc(baseUrl, mxcUri, 32)).toEqual(
|
expect(getHttpUriForMxc(baseUrl, mxcUri, 32)).toEqual(
|
||||||
baseUrl + "/_matrix/media/r0/thumbnail/server.name/resourceid" +
|
baseUrl + "/_matrix/media/r0/thumbnail/server.name/resourceid" +
|
||||||
"?width=32#automade",
|
"?width=32#automade",
|
||||||
);
|
);
|
||||||
@@ -53,7 +51,7 @@ describe("ContentRepo", function() {
|
|||||||
it("should put fragments from mxc:// URIs at the end of the HTTP URI",
|
it("should put fragments from mxc:// URIs at the end of the HTTP URI",
|
||||||
function() {
|
function() {
|
||||||
const mxcUri = "mxc://server.name/resourceid#automade";
|
const mxcUri = "mxc://server.name/resourceid#automade";
|
||||||
expect(ContentRepo.getHttpUriForMxc(baseUrl, mxcUri)).toEqual(
|
expect(getHttpUriForMxc(baseUrl, mxcUri)).toEqual(
|
||||||
baseUrl + "/_matrix/media/r0/download/server.name/resourceid#automade",
|
baseUrl + "/_matrix/media/r0/download/server.name/resourceid#automade",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -61,25 +59,25 @@ describe("ContentRepo", function() {
|
|||||||
|
|
||||||
describe("getIdenticonUri", function() {
|
describe("getIdenticonUri", function() {
|
||||||
it("should do nothing for null input", function() {
|
it("should do nothing for null input", function() {
|
||||||
expect(ContentRepo.getIdenticonUri(null)).toEqual(null);
|
expect(getIdenticonUri(null)).toEqual(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should set w/h by default to 96", function() {
|
it("should set w/h by default to 96", function() {
|
||||||
expect(ContentRepo.getIdenticonUri(baseUrl, "foobar")).toEqual(
|
expect(getIdenticonUri(baseUrl, "foobar")).toEqual(
|
||||||
baseUrl + "/_matrix/media/unstable/identicon/foobar" +
|
baseUrl + "/_matrix/media/unstable/identicon/foobar" +
|
||||||
"?width=96&height=96",
|
"?width=96&height=96",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be able to set custom w/h", function() {
|
it("should be able to set custom w/h", function() {
|
||||||
expect(ContentRepo.getIdenticonUri(baseUrl, "foobar", 32, 64)).toEqual(
|
expect(getIdenticonUri(baseUrl, "foobar", 32, 64)).toEqual(
|
||||||
baseUrl + "/_matrix/media/unstable/identicon/foobar" +
|
baseUrl + "/_matrix/media/unstable/identicon/foobar" +
|
||||||
"?width=32&height=64",
|
"?width=32&height=64",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should URL encode the identicon string", function() {
|
it("should URL encode the identicon string", function() {
|
||||||
expect(ContentRepo.getIdenticonUri(baseUrl, "foo#bar", 32, 64)).toEqual(
|
expect(getIdenticonUri(baseUrl, "foo#bar", 32, 64)).toEqual(
|
||||||
baseUrl + "/_matrix/media/unstable/identicon/foo%23bar" +
|
baseUrl + "/_matrix/media/unstable/identicon/foo%23bar" +
|
||||||
"?width=32&height=64",
|
"?width=32&height=64",
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,26 +1,20 @@
|
|||||||
import 'source-map-support/register';
|
|
||||||
|
|
||||||
import '../olm-loader';
|
import '../olm-loader';
|
||||||
|
import {Crypto} from "../../src/crypto";
|
||||||
import Crypto from '../../lib/crypto';
|
import {WebStorageSessionStore} from "../../src/store/session/webstorage";
|
||||||
|
import {MemoryCryptoStore} from "../../src/crypto/store/memory-crypto-store";
|
||||||
import WebStorageSessionStore from '../../lib/store/session/webstorage';
|
import {MockStorageApi} from "../MockStorageApi";
|
||||||
import MemoryCryptoStore from '../../lib/crypto/store/memory-crypto-store.js';
|
import {TestClient} from "../TestClient";
|
||||||
import MockStorageApi from '../MockStorageApi';
|
import {MatrixEvent} from "../../src/models/event";
|
||||||
import TestClient from '../TestClient';
|
import {Room} from "../../src/models/room";
|
||||||
import {MatrixEvent} from '../../lib/models/event';
|
import * as olmlib from "../../src/crypto/olmlib";
|
||||||
import Room from '../../lib/models/room';
|
|
||||||
import olmlib from '../../lib/crypto/olmlib';
|
|
||||||
import {sleep} from "../../src/utils";
|
import {sleep} from "../../src/utils";
|
||||||
|
import {EventEmitter} from "events";
|
||||||
const EventEmitter = require("events").EventEmitter;
|
import {CRYPTO_ENABLED} from "../../src/client";
|
||||||
|
|
||||||
const sdk = require("../..");
|
|
||||||
|
|
||||||
const Olm = global.Olm;
|
const Olm = global.Olm;
|
||||||
|
|
||||||
describe("Crypto", function() {
|
describe("Crypto", function() {
|
||||||
if (!sdk.CRYPTO_ENABLED) {
|
if (!CRYPTO_ENABLED) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2017 Vector Creations Ltd
|
Copyright 2017 Vector Creations Ltd
|
||||||
Copyright 2018, 2019 New Vector Ltd
|
Copyright 2018, 2019 New Vector Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -15,10 +16,10 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import DeviceList from '../../../lib/crypto/DeviceList';
|
import {logger} from "../../../src/logger";
|
||||||
import MemoryCryptoStore from '../../../lib/crypto/store/memory-crypto-store.js';
|
import * as utils from "../../../src/utils";
|
||||||
import utils from '../../../lib/utils';
|
import {MemoryCryptoStore} from "../../../src/crypto/store/memory-crypto-store";
|
||||||
import logger from '../../../lib/logger';
|
import {DeviceList} from "../../../src/crypto/DeviceList";
|
||||||
|
|
||||||
const signedDeviceList = {
|
const signedDeviceList = {
|
||||||
"failures": {},
|
"failures": {},
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
import '../../../olm-loader';
|
import '../../../olm-loader';
|
||||||
|
import * as algorithms from "../../../../src/crypto/algorithms";
|
||||||
|
import {MemoryCryptoStore} from "../../../../src/crypto/store/memory-crypto-store";
|
||||||
|
import {MockStorageApi} from "../../../MockStorageApi";
|
||||||
|
import * as testUtils from "../../../test-utils";
|
||||||
|
import {OlmDevice} from "../../../../src/crypto/OlmDevice";
|
||||||
|
import {Crypto} from "../../../../src/crypto";
|
||||||
|
import {logger} from "../../../../src/logger";
|
||||||
|
import {MatrixEvent} from "../../../../src/models/event";
|
||||||
|
import {TestClient} from "../../../TestClient";
|
||||||
|
import {Room} from "../../../../src/models/room";
|
||||||
|
import * as olmlib from "../../../../src/crypto/olmlib";
|
||||||
|
|
||||||
import sdk from '../../../..';
|
|
||||||
import algorithms from '../../../../lib/crypto/algorithms';
|
|
||||||
import MemoryCryptoStore from '../../../../lib/crypto/store/memory-crypto-store.js';
|
|
||||||
import MockStorageApi from '../../../MockStorageApi';
|
|
||||||
import testUtils from '../../../test-utils';
|
|
||||||
import OlmDevice from '../../../../lib/crypto/OlmDevice';
|
|
||||||
import Crypto from '../../../../lib/crypto';
|
|
||||||
import logger from '../../../../lib/logger';
|
|
||||||
|
|
||||||
const MatrixEvent = sdk.MatrixEvent;
|
|
||||||
const MegolmDecryption = algorithms.DECRYPTION_CLASSES['m.megolm.v1.aes-sha2'];
|
const MegolmDecryption = algorithms.DECRYPTION_CLASSES['m.megolm.v1.aes-sha2'];
|
||||||
const MegolmEncryption = algorithms.ENCRYPTION_CLASSES['m.megolm.v1.aes-sha2'];
|
const MegolmEncryption = algorithms.ENCRYPTION_CLASSES['m.megolm.v1.aes-sha2'];
|
||||||
|
|
||||||
@@ -342,4 +343,346 @@ describe("MegolmDecryption", function() {
|
|||||||
expect(ct2.session_id).toEqual(ct1.session_id);
|
expect(ct2.session_id).toEqual(ct1.session_id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("notifies devices that have been blocked", async function() {
|
||||||
|
const aliceClient = (new TestClient(
|
||||||
|
"@alice:example.com", "alicedevice",
|
||||||
|
)).client;
|
||||||
|
const bobClient1 = (new TestClient(
|
||||||
|
"@bob:example.com", "bobdevice1",
|
||||||
|
)).client;
|
||||||
|
const bobClient2 = (new TestClient(
|
||||||
|
"@bob:example.com", "bobdevice2",
|
||||||
|
)).client;
|
||||||
|
await Promise.all([
|
||||||
|
aliceClient.initCrypto(),
|
||||||
|
bobClient1.initCrypto(),
|
||||||
|
bobClient2.initCrypto(),
|
||||||
|
]);
|
||||||
|
const aliceDevice = aliceClient._crypto._olmDevice;
|
||||||
|
const bobDevice1 = bobClient1._crypto._olmDevice;
|
||||||
|
const bobDevice2 = bobClient2._crypto._olmDevice;
|
||||||
|
|
||||||
|
const encryptionCfg = {
|
||||||
|
"algorithm": "m.megolm.v1.aes-sha2",
|
||||||
|
};
|
||||||
|
const roomId = "!someroom";
|
||||||
|
const room = new Room(roomId, aliceClient, "@alice:example.com", {});
|
||||||
|
room.getEncryptionTargetMembers = async function() {
|
||||||
|
return [{userId: "@bob:example.com"}];
|
||||||
|
};
|
||||||
|
room.setBlacklistUnverifiedDevices(true);
|
||||||
|
aliceClient.store.storeRoom(room);
|
||||||
|
await aliceClient.setRoomEncryption(roomId, encryptionCfg);
|
||||||
|
|
||||||
|
const BOB_DEVICES = {
|
||||||
|
bobdevice1: {
|
||||||
|
user_id: "@bob:example.com",
|
||||||
|
device_id: "bobdevice1",
|
||||||
|
algorithms: [olmlib.OLM_ALGORITHM, olmlib.MEGOLM_ALGORITHM],
|
||||||
|
keys: {
|
||||||
|
"ed25519:Dynabook": bobDevice1.deviceEd25519Key,
|
||||||
|
"curve25519:Dynabook": bobDevice1.deviceCurve25519Key,
|
||||||
|
},
|
||||||
|
verified: 0,
|
||||||
|
},
|
||||||
|
bobdevice2: {
|
||||||
|
user_id: "@bob:example.com",
|
||||||
|
device_id: "bobdevice2",
|
||||||
|
algorithms: [olmlib.OLM_ALGORITHM, olmlib.MEGOLM_ALGORITHM],
|
||||||
|
keys: {
|
||||||
|
"ed25519:Dynabook": bobDevice2.deviceEd25519Key,
|
||||||
|
"curve25519:Dynabook": bobDevice2.deviceCurve25519Key,
|
||||||
|
},
|
||||||
|
verified: -1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
aliceClient._crypto._deviceList.storeDevicesForUser(
|
||||||
|
"@bob:example.com", BOB_DEVICES,
|
||||||
|
);
|
||||||
|
aliceClient._crypto._deviceList.downloadKeys = async function(userIds) {
|
||||||
|
return this._getDevicesFromStore(userIds);
|
||||||
|
};
|
||||||
|
|
||||||
|
let run = false;
|
||||||
|
aliceClient.sendToDevice = async (msgtype, contentMap) => {
|
||||||
|
run = true;
|
||||||
|
expect(msgtype).toBe("org.matrix.room_key.withheld");
|
||||||
|
delete contentMap["@bob:example.com"].bobdevice1.session_id;
|
||||||
|
delete contentMap["@bob:example.com"].bobdevice2.session_id;
|
||||||
|
expect(contentMap).toStrictEqual({
|
||||||
|
'@bob:example.com': {
|
||||||
|
bobdevice1: {
|
||||||
|
algorithm: "m.megolm.v1.aes-sha2",
|
||||||
|
room_id: roomId,
|
||||||
|
code: 'm.unverified',
|
||||||
|
reason:
|
||||||
|
'The sender has disabled encrypting to unverified devices.',
|
||||||
|
sender_key: aliceDevice.deviceCurve25519Key,
|
||||||
|
},
|
||||||
|
bobdevice2: {
|
||||||
|
algorithm: "m.megolm.v1.aes-sha2",
|
||||||
|
room_id: roomId,
|
||||||
|
code: 'm.blacklisted',
|
||||||
|
reason: 'The sender has blocked you.',
|
||||||
|
sender_key: aliceDevice.deviceCurve25519Key,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const event = new MatrixEvent({
|
||||||
|
type: "m.room.message",
|
||||||
|
sender: "@alice:example.com",
|
||||||
|
room_id: roomId,
|
||||||
|
event_id: "$event",
|
||||||
|
content: {
|
||||||
|
msgtype: "m.text",
|
||||||
|
body: "secret",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await aliceClient._crypto.encryptEvent(event, room);
|
||||||
|
|
||||||
|
expect(run).toBe(true);
|
||||||
|
|
||||||
|
aliceClient.stopClient();
|
||||||
|
bobClient1.stopClient();
|
||||||
|
bobClient2.stopClient();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("notifies devices when unable to create olm session", async function() {
|
||||||
|
const aliceClient = (new TestClient(
|
||||||
|
"@alice:example.com", "alicedevice",
|
||||||
|
)).client;
|
||||||
|
const bobClient = (new TestClient(
|
||||||
|
"@bob:example.com", "bobdevice",
|
||||||
|
)).client;
|
||||||
|
await Promise.all([
|
||||||
|
aliceClient.initCrypto(),
|
||||||
|
bobClient.initCrypto(),
|
||||||
|
]);
|
||||||
|
const aliceDevice = aliceClient._crypto._olmDevice;
|
||||||
|
const bobDevice = bobClient._crypto._olmDevice;
|
||||||
|
|
||||||
|
const encryptionCfg = {
|
||||||
|
"algorithm": "m.megolm.v1.aes-sha2",
|
||||||
|
};
|
||||||
|
const roomId = "!someroom";
|
||||||
|
const aliceRoom = new Room(roomId, aliceClient, "@alice:example.com", {});
|
||||||
|
const bobRoom = new Room(roomId, bobClient, "@bob:example.com", {});
|
||||||
|
aliceClient.store.storeRoom(aliceRoom);
|
||||||
|
bobClient.store.storeRoom(bobRoom);
|
||||||
|
await aliceClient.setRoomEncryption(roomId, encryptionCfg);
|
||||||
|
await bobClient.setRoomEncryption(roomId, encryptionCfg);
|
||||||
|
|
||||||
|
aliceRoom.getEncryptionTargetMembers = async () => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
userId: "@alice:example.com",
|
||||||
|
membership: "join",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userId: "@bob:example.com",
|
||||||
|
membership: "join",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
const BOB_DEVICES = {
|
||||||
|
bobdevice: {
|
||||||
|
user_id: "@bob:example.com",
|
||||||
|
device_id: "bobdevice",
|
||||||
|
algorithms: [olmlib.OLM_ALGORITHM, olmlib.MEGOLM_ALGORITHM],
|
||||||
|
keys: {
|
||||||
|
"ed25519:bobdevice": bobDevice.deviceEd25519Key,
|
||||||
|
"curve25519:bobdevice": bobDevice.deviceCurve25519Key,
|
||||||
|
},
|
||||||
|
known: true,
|
||||||
|
verified: 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
aliceClient._crypto._deviceList.storeDevicesForUser(
|
||||||
|
"@bob:example.com", BOB_DEVICES,
|
||||||
|
);
|
||||||
|
aliceClient._crypto._deviceList.downloadKeys = async function(userIds) {
|
||||||
|
return this._getDevicesFromStore(userIds);
|
||||||
|
};
|
||||||
|
|
||||||
|
aliceClient.claimOneTimeKeys = async () => {
|
||||||
|
// Bob has no one-time keys
|
||||||
|
return {
|
||||||
|
one_time_keys: {},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
let run = false;
|
||||||
|
aliceClient.sendToDevice = async (msgtype, contentMap) => {
|
||||||
|
run = true;
|
||||||
|
expect(msgtype).toBe("org.matrix.room_key.withheld");
|
||||||
|
expect(contentMap).toStrictEqual({
|
||||||
|
'@bob:example.com': {
|
||||||
|
bobdevice: {
|
||||||
|
algorithm: "m.megolm.v1.aes-sha2",
|
||||||
|
code: 'm.no_olm',
|
||||||
|
reason: 'Unable to establish a secure channel.',
|
||||||
|
sender_key: aliceDevice.deviceCurve25519Key,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const event = new MatrixEvent({
|
||||||
|
type: "m.room.message",
|
||||||
|
sender: "@alice:example.com",
|
||||||
|
room_id: roomId,
|
||||||
|
event_id: "$event",
|
||||||
|
content: {},
|
||||||
|
});
|
||||||
|
await aliceClient._crypto.encryptEvent(event, aliceRoom);
|
||||||
|
|
||||||
|
expect(run).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws an error describing why it doesn't have a key", async function() {
|
||||||
|
const aliceClient = (new TestClient(
|
||||||
|
"@alice:example.com", "alicedevice",
|
||||||
|
)).client;
|
||||||
|
const bobClient = (new TestClient(
|
||||||
|
"@bob:example.com", "bobdevice",
|
||||||
|
)).client;
|
||||||
|
await Promise.all([
|
||||||
|
aliceClient.initCrypto(),
|
||||||
|
bobClient.initCrypto(),
|
||||||
|
]);
|
||||||
|
const bobDevice = bobClient._crypto._olmDevice;
|
||||||
|
|
||||||
|
const roomId = "!someroom";
|
||||||
|
|
||||||
|
aliceClient._crypto._onToDeviceEvent(new MatrixEvent({
|
||||||
|
type: "org.matrix.room_key.withheld",
|
||||||
|
sender: "@bob:example.com",
|
||||||
|
content: {
|
||||||
|
algorithm: "m.megolm.v1.aes-sha2",
|
||||||
|
room_id: roomId,
|
||||||
|
session_id: "session_id",
|
||||||
|
sender_key: bobDevice.deviceCurve25519Key,
|
||||||
|
code: "m.blacklisted",
|
||||||
|
reason: "You have been blocked",
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
await expect(aliceClient._crypto.decryptEvent(new MatrixEvent({
|
||||||
|
type: "m.room.encrypted",
|
||||||
|
sender: "@bob:example.com",
|
||||||
|
event_id: "$event",
|
||||||
|
room_id: roomId,
|
||||||
|
content: {
|
||||||
|
algorithm: "m.megolm.v1.aes-sha2",
|
||||||
|
ciphertext: "blablabla",
|
||||||
|
device_id: "bobdevice",
|
||||||
|
sender_key: bobDevice.deviceCurve25519Key,
|
||||||
|
session_id: "session_id",
|
||||||
|
},
|
||||||
|
}))).rejects.toThrow("The sender has blocked you.");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws an error describing the lack of an olm session", async function() {
|
||||||
|
const aliceClient = (new TestClient(
|
||||||
|
"@alice:example.com", "alicedevice",
|
||||||
|
)).client;
|
||||||
|
const bobClient = (new TestClient(
|
||||||
|
"@bob:example.com", "bobdevice",
|
||||||
|
)).client;
|
||||||
|
await Promise.all([
|
||||||
|
aliceClient.initCrypto(),
|
||||||
|
bobClient.initCrypto(),
|
||||||
|
]);
|
||||||
|
const bobDevice = bobClient._crypto._olmDevice;
|
||||||
|
|
||||||
|
const roomId = "!someroom";
|
||||||
|
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
aliceClient._crypto._onToDeviceEvent(new MatrixEvent({
|
||||||
|
type: "org.matrix.room_key.withheld",
|
||||||
|
sender: "@bob:example.com",
|
||||||
|
content: {
|
||||||
|
algorithm: "m.megolm.v1.aes-sha2",
|
||||||
|
room_id: roomId,
|
||||||
|
session_id: "session_id",
|
||||||
|
sender_key: bobDevice.deviceCurve25519Key,
|
||||||
|
code: "m.no_olm",
|
||||||
|
reason: "Unable to establish a secure channel.",
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
setTimeout(resolve, 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(aliceClient._crypto.decryptEvent(new MatrixEvent({
|
||||||
|
type: "m.room.encrypted",
|
||||||
|
sender: "@bob:example.com",
|
||||||
|
event_id: "$event",
|
||||||
|
room_id: roomId,
|
||||||
|
content: {
|
||||||
|
algorithm: "m.megolm.v1.aes-sha2",
|
||||||
|
ciphertext: "blablabla",
|
||||||
|
device_id: "bobdevice",
|
||||||
|
sender_key: bobDevice.deviceCurve25519Key,
|
||||||
|
session_id: "session_id",
|
||||||
|
},
|
||||||
|
origin_server_ts: now,
|
||||||
|
}))).rejects.toThrow("The sender was unable to establish a secure channel.");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws an error to indicate a wedged olm session", async function() {
|
||||||
|
const aliceClient = (new TestClient(
|
||||||
|
"@alice:example.com", "alicedevice",
|
||||||
|
)).client;
|
||||||
|
const bobClient = (new TestClient(
|
||||||
|
"@bob:example.com", "bobdevice",
|
||||||
|
)).client;
|
||||||
|
await Promise.all([
|
||||||
|
aliceClient.initCrypto(),
|
||||||
|
bobClient.initCrypto(),
|
||||||
|
]);
|
||||||
|
const bobDevice = bobClient._crypto._olmDevice;
|
||||||
|
|
||||||
|
const roomId = "!someroom";
|
||||||
|
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
// pretend we got an event that we can't decrypt
|
||||||
|
aliceClient._crypto._onToDeviceEvent(new MatrixEvent({
|
||||||
|
type: "m.room.encrypted",
|
||||||
|
sender: "@bob:example.com",
|
||||||
|
content: {
|
||||||
|
msgtype: "m.bad.encrypted",
|
||||||
|
algorithm: "m.megolm.v1.aes-sha2",
|
||||||
|
session_id: "session_id",
|
||||||
|
sender_key: bobDevice.deviceCurve25519Key,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
setTimeout(resolve, 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(aliceClient._crypto.decryptEvent(new MatrixEvent({
|
||||||
|
type: "m.room.encrypted",
|
||||||
|
sender: "@bob:example.com",
|
||||||
|
event_id: "$event",
|
||||||
|
room_id: roomId,
|
||||||
|
content: {
|
||||||
|
algorithm: "m.megolm.v1.aes-sha2",
|
||||||
|
ciphertext: "blablabla",
|
||||||
|
device_id: "bobdevice",
|
||||||
|
sender_key: bobDevice.deviceCurve25519Key,
|
||||||
|
session_id: "session_id",
|
||||||
|
},
|
||||||
|
origin_server_ts: now,
|
||||||
|
}))).rejects.toThrow("The secure channel with the sender was corrupted.");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2018,2019 New Vector Ltd
|
Copyright 2018,2019 New Vector Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -15,14 +16,12 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import '../../../olm-loader';
|
import '../../../olm-loader';
|
||||||
|
import {MemoryCryptoStore} from "../../../../src/crypto/store/memory-crypto-store";
|
||||||
import MemoryCryptoStore from '../../../../lib/crypto/store/memory-crypto-store.js';
|
import {MockStorageApi} from "../../../MockStorageApi";
|
||||||
import MockStorageApi from '../../../MockStorageApi';
|
import {logger} from "../../../../src/logger";
|
||||||
import logger from '../../../../lib/logger';
|
import {OlmDevice} from "../../../../src/crypto/OlmDevice";
|
||||||
|
import * as olmlib from "../../../../src/crypto/olmlib";
|
||||||
import OlmDevice from '../../../../lib/crypto/OlmDevice';
|
import {DeviceInfo} from "../../../../src/crypto/deviceinfo";
|
||||||
import olmlib from '../../../../lib/crypto/olmlib';
|
|
||||||
import DeviceInfo from '../../../../lib/crypto/deviceinfo';
|
|
||||||
|
|
||||||
function makeOlmDevice() {
|
function makeOlmDevice() {
|
||||||
const mockStorage = new MockStorageApi();
|
const mockStorage = new MockStorageApi();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2018 New Vector Ltd
|
Copyright 2018 New Vector Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -15,23 +16,20 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import '../../olm-loader';
|
import '../../olm-loader';
|
||||||
|
import {logger} from "../../../src/logger";
|
||||||
import sdk from '../../..';
|
import * as olmlib from "../../../src/crypto/olmlib";
|
||||||
import algorithms from '../../../lib/crypto/algorithms';
|
import {MatrixClient} from "../../../src/client";
|
||||||
import WebStorageSessionStore from '../../../lib/store/session/webstorage';
|
import {MatrixEvent} from "../../../src/models/event";
|
||||||
import MemoryCryptoStore from '../../../lib/crypto/store/memory-crypto-store.js';
|
import * as algorithms from "../../../src/crypto/algorithms";
|
||||||
import MockStorageApi from '../../MockStorageApi';
|
import {WebStorageSessionStore} from "../../../src/store/session/webstorage";
|
||||||
import testUtils from '../../test-utils';
|
import {MemoryCryptoStore} from "../../../src/crypto/store/memory-crypto-store";
|
||||||
|
import {MockStorageApi} from "../../MockStorageApi";
|
||||||
import OlmDevice from '../../../lib/crypto/OlmDevice';
|
import * as testUtils from "../../test-utils";
|
||||||
import Crypto from '../../../lib/crypto';
|
import {OlmDevice} from "../../../src/crypto/OlmDevice";
|
||||||
import logger from '../../../lib/logger';
|
import {Crypto} from "../../../src/crypto";
|
||||||
import olmlib from '../../../lib/crypto/olmlib';
|
|
||||||
|
|
||||||
const Olm = global.Olm;
|
const Olm = global.Olm;
|
||||||
|
|
||||||
const MatrixClient = sdk.MatrixClient;
|
|
||||||
const MatrixEvent = sdk.MatrixEvent;
|
|
||||||
const MegolmDecryption = algorithms.DECRYPTION_CLASSES['m.megolm.v1.aes-sha2'];
|
const MegolmDecryption = algorithms.DECRYPTION_CLASSES['m.megolm.v1.aes-sha2'];
|
||||||
|
|
||||||
const ROOM_ID = '!ROOM:ID';
|
const ROOM_ID = '!ROOM:ID';
|
||||||
|
|||||||
@@ -16,13 +16,9 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import '../../olm-loader';
|
import '../../olm-loader';
|
||||||
|
|
||||||
import anotherjson from 'another-json';
|
import anotherjson from 'another-json';
|
||||||
|
import * as olmlib from "../../../src/crypto/olmlib";
|
||||||
import olmlib from '../../../lib/crypto/olmlib';
|
import {TestClient} from '../../TestClient';
|
||||||
|
|
||||||
import TestClient from '../../TestClient';
|
|
||||||
|
|
||||||
import {HttpResponse, setHttpResponses} from '../../test-utils';
|
import {HttpResponse, setHttpResponses} from '../../test-utils';
|
||||||
|
|
||||||
async function makeTestClient(userInfo, options, keys) {
|
async function makeTestClient(userInfo, options, keys) {
|
||||||
|
|||||||
@@ -15,13 +15,10 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import '../../olm-loader';
|
import '../../olm-loader';
|
||||||
|
import * as olmlib from "../../../src/crypto/olmlib";
|
||||||
import { MatrixEvent } from '../../../lib/models/event';
|
import {SECRET_STORAGE_ALGORITHM_V1} from "../../../src/crypto/SecretStorage";
|
||||||
import { SECRET_STORAGE_ALGORITHM_V1 } from '../../../lib/crypto/SecretStorage';
|
import {MatrixEvent} from "../../../src/models/event";
|
||||||
|
import {TestClient} from '../../TestClient';
|
||||||
import olmlib from '../../../lib/crypto/olmlib';
|
|
||||||
|
|
||||||
import TestClient from '../../TestClient';
|
|
||||||
import {makeTestClients} from './verification/util';
|
import {makeTestClients} from './verification/util';
|
||||||
|
|
||||||
async function makeTestClient(userInfo, options) {
|
async function makeTestClient(userInfo, options) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2018-2019 New Vector Ltd
|
Copyright 2018-2019 New Vector Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -13,17 +14,10 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
import logger from '../../../../lib/logger';
|
import "../../../olm-loader";
|
||||||
|
import {logger} from "../../../../src/logger";
|
||||||
try {
|
import {DeviceInfo} from "../../../../src/crypto/deviceinfo";
|
||||||
global.Olm = require('olm');
|
import {ScanQRCode, ShowQRCode} from "../../../../src/crypto/verification/QRCode";
|
||||||
} catch (e) {
|
|
||||||
logger.warn("unable to run device verification tests: libolm not available");
|
|
||||||
}
|
|
||||||
|
|
||||||
import DeviceInfo from '../../../../lib/crypto/deviceinfo';
|
|
||||||
|
|
||||||
import {ShowQRCode, ScanQRCode} from '../../../../lib/crypto/verification/QRCode';
|
|
||||||
|
|
||||||
const Olm = global.Olm;
|
const Olm = global.Olm;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2019 New Vector Ltd
|
Copyright 2019 New Vector Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -13,22 +14,14 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
import logger from '../../../../lib/logger';
|
import "../../../olm-loader";
|
||||||
|
import {verificationMethods} from "../../../../src/crypto";
|
||||||
try {
|
import {logger} from "../../../../src/logger";
|
||||||
global.Olm = require('olm');
|
import {SAS} from "../../../../src/crypto/verification/SAS";
|
||||||
} catch (e) {
|
import {makeTestClients} from './util';
|
||||||
logger.warn("unable to run device verification tests: libolm not available");
|
|
||||||
}
|
|
||||||
|
|
||||||
import {verificationMethods} from '../../../../lib/crypto';
|
|
||||||
|
|
||||||
import SAS from '../../../../lib/crypto/verification/SAS';
|
|
||||||
|
|
||||||
const Olm = global.Olm;
|
const Olm = global.Olm;
|
||||||
|
|
||||||
import {makeTestClients} from './util';
|
|
||||||
|
|
||||||
describe("verification request", function() {
|
describe("verification request", function() {
|
||||||
if (!global.Olm) {
|
if (!global.Olm) {
|
||||||
logger.warn('Not running device verification unit tests: libolm not present');
|
logger.warn('Not running device verification unit tests: libolm not present');
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2018-2019 New Vector Ltd
|
Copyright 2018-2019 New Vector Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -13,29 +14,17 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
import logger from '../../../../lib/logger';
|
import "../../../olm-loader";
|
||||||
|
import {makeTestClients} from './util';
|
||||||
try {
|
import {MatrixEvent} from "../../../../src/models/event";
|
||||||
global.Olm = require('olm');
|
import {SAS} from "../../../../src/crypto/verification/SAS";
|
||||||
} catch (e) {
|
import {DeviceInfo} from "../../../../src/crypto/deviceinfo";
|
||||||
logger.warn("unable to run device verification tests: libolm not available");
|
import {verificationMethods} from "../../../../src/crypto";
|
||||||
}
|
import * as olmlib from "../../../../src/crypto/olmlib";
|
||||||
|
import {logger} from "../../../../src/logger";
|
||||||
import olmlib from '../../../../lib/crypto/olmlib';
|
|
||||||
|
|
||||||
import sdk from '../../../..';
|
|
||||||
|
|
||||||
import {verificationMethods} from '../../../../lib/crypto';
|
|
||||||
import DeviceInfo from '../../../../lib/crypto/deviceinfo';
|
|
||||||
|
|
||||||
import SAS from '../../../../lib/crypto/verification/SAS';
|
|
||||||
|
|
||||||
const Olm = global.Olm;
|
const Olm = global.Olm;
|
||||||
|
|
||||||
const MatrixEvent = sdk.MatrixEvent;
|
|
||||||
|
|
||||||
import {makeTestClients} from './util';
|
|
||||||
|
|
||||||
let ALICE_DEVICES;
|
let ALICE_DEVICES;
|
||||||
let BOB_DEVICES;
|
let BOB_DEVICES;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2019 New Vector Ltd
|
Copyright 2019 New Vector Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -14,10 +15,8 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import TestClient from '../../../TestClient';
|
import {TestClient} from '../../../TestClient';
|
||||||
|
import {MatrixEvent} from "../../../../src/models/event";
|
||||||
import sdk from '../../../..';
|
|
||||||
const MatrixEvent = sdk.MatrixEvent;
|
|
||||||
|
|
||||||
export async function makeTestClients(userInfos, options) {
|
export async function makeTestClients(userInfos, options) {
|
||||||
const clients = [];
|
const clients = [];
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
"use strict";
|
import * as utils from "../test-utils";
|
||||||
import 'source-map-support/register';
|
import {EventTimeline} from "../../src/models/event-timeline";
|
||||||
const sdk = require("../..");
|
import {RoomState} from "../../src/models/room-state";
|
||||||
const EventTimeline = sdk.EventTimeline;
|
|
||||||
const utils = require("../test-utils");
|
|
||||||
|
|
||||||
function mockRoomStates(timeline) {
|
function mockRoomStates(timeline) {
|
||||||
timeline._startState = utils.mock(sdk.RoomState, "startState");
|
timeline._startState = utils.mock(RoomState, "startState");
|
||||||
timeline._endState = utils.mock(sdk.RoomState, "endState");
|
timeline._endState = utils.mock(RoomState, "endState");
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("EventTimeline", function() {
|
describe("EventTimeline", function() {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2017 New Vector Ltd
|
Copyright 2017 New Vector Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundaction C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -14,10 +15,8 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import sdk from '../..';
|
import {logger} from "../../src/logger";
|
||||||
const MatrixEvent = sdk.MatrixEvent;
|
import {MatrixEvent} from "../../src/models/event";
|
||||||
|
|
||||||
import logger from '../../lib/logger';
|
|
||||||
|
|
||||||
describe("MatrixEvent", () => {
|
describe("MatrixEvent", () => {
|
||||||
describe(".attemptDecryption", () => {
|
describe(".attemptDecryption", () => {
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
"use strict";
|
import {Filter} from "../../src/filter";
|
||||||
import 'source-map-support/register';
|
|
||||||
const sdk = require("../..");
|
|
||||||
const Filter = sdk.Filter;
|
|
||||||
|
|
||||||
describe("Filter", function() {
|
describe("Filter", function() {
|
||||||
const filterId = "f1lt3ring15g00d4ursoul";
|
const filterId = "f1lt3ring15g00d4ursoul";
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2016 OpenMarket Ltd
|
Copyright 2016 OpenMarket Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -13,15 +14,10 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
"use strict";
|
|
||||||
|
|
||||||
import 'source-map-support/register';
|
import {logger} from "../../src/logger";
|
||||||
const sdk = require("../..");
|
import {InteractiveAuth} from "../../src/interactive-auth";
|
||||||
|
import {MatrixError} from "../../src/http-api";
|
||||||
const InteractiveAuth = sdk.InteractiveAuth;
|
|
||||||
const MatrixError = sdk.MatrixError;
|
|
||||||
|
|
||||||
import logger from '../../lib/logger';
|
|
||||||
|
|
||||||
// Trivial client object to test interactive auth
|
// Trivial client object to test interactive auth
|
||||||
// (we do not need TestClient here)
|
// (we do not need TestClient here)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import TestClient from '../TestClient';
|
import {TestClient} from '../TestClient';
|
||||||
|
|
||||||
describe('Login request', function() {
|
describe('Login request', function() {
|
||||||
let client;
|
let client;
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
"use strict";
|
import {logger} from "../../src/logger";
|
||||||
import 'source-map-support/register';
|
import {MatrixClient} from "../../src/client";
|
||||||
const sdk = require("../..");
|
import {Filter} from "../../src/filter";
|
||||||
const MatrixClient = sdk.MatrixClient;
|
|
||||||
|
|
||||||
import logger from '../../lib/logger';
|
|
||||||
|
|
||||||
jest.useFakeTimers();
|
jest.useFakeTimers();
|
||||||
|
|
||||||
@@ -180,7 +177,7 @@ describe("MatrixClient", function() {
|
|||||||
httpLookups.push(SYNC_RESPONSE);
|
httpLookups.push(SYNC_RESPONSE);
|
||||||
const filterId = "ehfewf";
|
const filterId = "ehfewf";
|
||||||
store.getFilterIdByName.mockReturnValue(filterId);
|
store.getFilterIdByName.mockReturnValue(filterId);
|
||||||
const filter = new sdk.Filter(0, filterId);
|
const filter = new Filter(0, filterId);
|
||||||
filter.setDefinition({"room": {"timeline": {"limit": 8}}});
|
filter.setDefinition({"room": {"timeline": {"limit": 8}}});
|
||||||
store.getFilter.mockReturnValue(filter);
|
store.getFilter.mockReturnValue(filter);
|
||||||
const syncPromise = new Promise((resolve, reject) => {
|
const syncPromise = new Promise((resolve, reject) => {
|
||||||
@@ -247,7 +244,7 @@ describe("MatrixClient", function() {
|
|||||||
|
|
||||||
const filterName = getFilterName(client.credentials.userId);
|
const filterName = getFilterName(client.credentials.userId);
|
||||||
client.store.setFilterIdByName(filterName, invalidFilterId);
|
client.store.setFilterIdByName(filterName, invalidFilterId);
|
||||||
const filter = new sdk.Filter(client.credentials.userId);
|
const filter = new Filter(client.credentials.userId);
|
||||||
|
|
||||||
client.getOrCreateFilter(filterName, filter).then(function(filterId) {
|
client.getOrCreateFilter(filterName, filter).then(function(filterId) {
|
||||||
expect(filterId).toEqual(FILTER_RESPONSE.data.filter_id);
|
expect(filterId).toEqual(FILTER_RESPONSE.data.filter_id);
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
"use strict";
|
import * as utils from "../test-utils";
|
||||||
import 'source-map-support/register';
|
import {PushProcessor} from "../../src/pushprocessor";
|
||||||
const PushProcessor = require("../../lib/pushprocessor");
|
|
||||||
const utils = require("../test-utils");
|
|
||||||
|
|
||||||
describe('NotificationService', function() {
|
describe('NotificationService', function() {
|
||||||
const testUserId = "@ali:matrix.org";
|
const testUserId = "@ali:matrix.org";
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
"use strict";
|
import * as callbacks from "../../src/realtime-callbacks";
|
||||||
|
|
||||||
import 'source-map-support/register';
|
|
||||||
const callbacks = require("../../src/realtime-callbacks");
|
|
||||||
|
|
||||||
let wallTime = 1234567890;
|
let wallTime = 1234567890;
|
||||||
jest.useFakeTimers();
|
jest.useFakeTimers();
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
"use strict";
|
import * as utils from "../test-utils";
|
||||||
import 'source-map-support/register';
|
import {RoomMember} from "../../src/models/room-member";
|
||||||
const sdk = require("../..");
|
|
||||||
const RoomMember = sdk.RoomMember;
|
|
||||||
const utils = require("../test-utils");
|
|
||||||
|
|
||||||
describe("RoomMember", function() {
|
describe("RoomMember", function() {
|
||||||
const roomId = "!foo:bar";
|
const roomId = "!foo:bar";
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
"use strict";
|
import * as utils from "../test-utils";
|
||||||
import 'source-map-support/register';
|
import {RoomState} from "../../src/models/room-state";
|
||||||
const sdk = require("../..");
|
import {RoomMember} from "../../src/models/room-member";
|
||||||
const RoomState = sdk.RoomState;
|
|
||||||
const RoomMember = sdk.RoomMember;
|
|
||||||
const utils = require("../test-utils");
|
|
||||||
|
|
||||||
describe("RoomState", function() {
|
describe("RoomState", function() {
|
||||||
const roomId = "!foo:bar";
|
const roomId = "!foo:bar";
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
"use strict";
|
import * as utils from "../test-utils";
|
||||||
import 'source-map-support/register';
|
import {EventStatus, MatrixEvent} from "../../src/models/event";
|
||||||
const sdk = require("../..");
|
import {EventTimeline} from "../../src/models/event-timeline";
|
||||||
const Room = sdk.Room;
|
import {RoomState} from "../../src/models/room-state";
|
||||||
const RoomState = sdk.RoomState;
|
import {Room} from "../../src/models/room";
|
||||||
const MatrixEvent = sdk.MatrixEvent;
|
|
||||||
const EventStatus = sdk.EventStatus;
|
|
||||||
const EventTimeline = sdk.EventTimeline;
|
|
||||||
const utils = require("../test-utils");
|
|
||||||
|
|
||||||
describe("Room", function() {
|
describe("Room", function() {
|
||||||
const roomId = "!foo:bar";
|
const roomId = "!foo:bar";
|
||||||
@@ -20,9 +16,9 @@ describe("Room", function() {
|
|||||||
room = new Room(roomId);
|
room = new Room(roomId);
|
||||||
// mock RoomStates
|
// mock RoomStates
|
||||||
room.oldState = room.getLiveTimeline()._startState =
|
room.oldState = room.getLiveTimeline()._startState =
|
||||||
utils.mock(sdk.RoomState, "oldState");
|
utils.mock(RoomState, "oldState");
|
||||||
room.currentState = room.getLiveTimeline()._endState =
|
room.currentState = room.getLiveTimeline()._endState =
|
||||||
utils.mock(sdk.RoomState, "currentState");
|
utils.mock(RoomState, "currentState");
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("getAvatarUrl", function() {
|
describe("getAvatarUrl", function() {
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
// This file had a function whose name is all caps, which displeases eslint
|
// This file had a function whose name is all caps, which displeases eslint
|
||||||
/* eslint new-cap: "off" */
|
/* eslint new-cap: "off" */
|
||||||
|
|
||||||
import 'source-map-support/register';
|
|
||||||
import {defer} from '../../src/utils';
|
import {defer} from '../../src/utils';
|
||||||
const sdk = require("../..");
|
import {MatrixError} from "../../src/http-api";
|
||||||
const MatrixScheduler = sdk.MatrixScheduler;
|
import {MatrixScheduler} from "../../src/scheduler";
|
||||||
const MatrixError = sdk.MatrixError;
|
import * as utils from "../test-utils";
|
||||||
const utils = require("../test-utils");
|
|
||||||
|
|
||||||
jest.useFakeTimers();
|
jest.useFakeTimers();
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2017 Vector Creations Ltd
|
Copyright 2017 Vector Creations Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -14,11 +15,7 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
"use strict";
|
import {SyncAccumulator} from "../../src/sync-accumulator";
|
||||||
import 'source-map-support/register';
|
|
||||||
import sdk from "../..";
|
|
||||||
|
|
||||||
const SyncAccumulator = sdk.SyncAccumulator;
|
|
||||||
|
|
||||||
describe("SyncAccumulator", function() {
|
describe("SyncAccumulator", function() {
|
||||||
let sa;
|
let sa;
|
||||||
|
|||||||
@@ -1,11 +1,6 @@
|
|||||||
"use strict";
|
import {EventTimeline} from "../../src/models/event-timeline";
|
||||||
import 'source-map-support/register';
|
import {TimelineIndex, TimelineWindow} from "../../src/timeline-window";
|
||||||
const sdk = require("../..");
|
import * as utils from "../test-utils";
|
||||||
const EventTimeline = sdk.EventTimeline;
|
|
||||||
const TimelineWindow = sdk.TimelineWindow;
|
|
||||||
const TimelineIndex = require("../../lib/timeline-window").TimelineIndex;
|
|
||||||
|
|
||||||
const utils = require("../test-utils");
|
|
||||||
|
|
||||||
const ROOM_ID = "roomId";
|
const ROOM_ID = "roomId";
|
||||||
const USER_ID = "userId";
|
const USER_ID = "userId";
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
"use strict";
|
import {User} from "../../src/models/user";
|
||||||
import 'source-map-support/register';
|
import * as utils from "../test-utils";
|
||||||
const sdk = require("../..");
|
|
||||||
const User = sdk.User;
|
|
||||||
const utils = require("../test-utils");
|
|
||||||
|
|
||||||
describe("User", function() {
|
describe("User", function() {
|
||||||
const userId = "@alice:bar";
|
const userId = "@alice:bar";
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
"use strict";
|
import * as utils from "../../src/utils";
|
||||||
import 'source-map-support/register';
|
|
||||||
const utils = require("../../lib/utils");
|
|
||||||
|
|
||||||
describe("utils", function() {
|
describe("utils", function() {
|
||||||
describe("encodeParams", function() {
|
describe("encodeParams", function() {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ limitations under the License.
|
|||||||
* @module
|
* @module
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default class Reemitter {
|
export class ReEmitter {
|
||||||
constructor(target) {
|
constructor(target) {
|
||||||
this.target = target;
|
this.target = target;
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ limitations under the License.
|
|||||||
|
|
||||||
/** @module auto-discovery */
|
/** @module auto-discovery */
|
||||||
|
|
||||||
import logger from './logger';
|
import {logger} from './logger';
|
||||||
import {URL as NodeURL} from "url";
|
import {URL as NodeURL} from "url";
|
||||||
|
|
||||||
// Dev note: Auto discovery is part of the spec.
|
// Dev note: Auto discovery is part of the spec.
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
"use strict";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is an internal module. MatrixBaseApis is currently only meant to be used
|
* This is an internal module. MatrixBaseApis is currently only meant to be used
|
||||||
@@ -26,16 +25,21 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {SERVICE_TYPES} from './service-types';
|
import {SERVICE_TYPES} from './service-types';
|
||||||
import logger from './logger';
|
import {logger} from './logger';
|
||||||
|
import {PushProcessor} from "./pushprocessor";
|
||||||
const httpApi = require("./http-api");
|
import * as utils from "./utils";
|
||||||
const utils = require("./utils");
|
import {
|
||||||
const PushProcessor = require("./pushprocessor");
|
MatrixHttpApi,
|
||||||
|
PREFIX_IDENTITY_V1,
|
||||||
|
PREFIX_IDENTITY_V2,
|
||||||
|
PREFIX_R0,
|
||||||
|
PREFIX_UNSTABLE,
|
||||||
|
} from "./http-api";
|
||||||
|
|
||||||
function termsUrlForService(serviceType, baseUrl) {
|
function termsUrlForService(serviceType, baseUrl) {
|
||||||
switch (serviceType) {
|
switch (serviceType) {
|
||||||
case SERVICE_TYPES.IS:
|
case SERVICE_TYPES.IS:
|
||||||
return baseUrl + httpApi.PREFIX_IDENTITY_V2 + '/terms';
|
return baseUrl + PREFIX_IDENTITY_V2 + '/terms';
|
||||||
case SERVICE_TYPES.IM:
|
case SERVICE_TYPES.IM:
|
||||||
return baseUrl + '/_matrix/integrations/v1/terms';
|
return baseUrl + '/_matrix/integrations/v1/terms';
|
||||||
default:
|
default:
|
||||||
@@ -83,7 +87,7 @@ function termsUrlForService(serviceType, baseUrl) {
|
|||||||
* @param {boolean} [opts.useAuthorizationHeader = false] Set to true to use
|
* @param {boolean} [opts.useAuthorizationHeader = false] Set to true to use
|
||||||
* Authorization header instead of query param to send the access token to the server.
|
* Authorization header instead of query param to send the access token to the server.
|
||||||
*/
|
*/
|
||||||
function MatrixBaseApis(opts) {
|
export function MatrixBaseApis(opts) {
|
||||||
utils.checkObjectHasKeys(opts, ["baseUrl", "request"]);
|
utils.checkObjectHasKeys(opts, ["baseUrl", "request"]);
|
||||||
|
|
||||||
this.baseUrl = opts.baseUrl;
|
this.baseUrl = opts.baseUrl;
|
||||||
@@ -95,13 +99,13 @@ function MatrixBaseApis(opts) {
|
|||||||
idBaseUrl: opts.idBaseUrl,
|
idBaseUrl: opts.idBaseUrl,
|
||||||
accessToken: opts.accessToken,
|
accessToken: opts.accessToken,
|
||||||
request: opts.request,
|
request: opts.request,
|
||||||
prefix: httpApi.PREFIX_R0,
|
prefix: PREFIX_R0,
|
||||||
onlyData: true,
|
onlyData: true,
|
||||||
extraParams: opts.queryParams,
|
extraParams: opts.queryParams,
|
||||||
localTimeoutMs: opts.localTimeoutMs,
|
localTimeoutMs: opts.localTimeoutMs,
|
||||||
useAuthorizationHeader: opts.useAuthorizationHeader,
|
useAuthorizationHeader: opts.useAuthorizationHeader,
|
||||||
};
|
};
|
||||||
this._http = new httpApi.MatrixHttpApi(this, httpOpts);
|
this._http = new MatrixHttpApi(this, httpOpts);
|
||||||
|
|
||||||
this._txnCtr = 0;
|
this._txnCtr = 0;
|
||||||
}
|
}
|
||||||
@@ -369,7 +373,7 @@ MatrixBaseApis.prototype.getSsoLoginUrl = function(redirectUrl, loginType) {
|
|||||||
}
|
}
|
||||||
return this._http.getUrl("/login/"+loginType+"/redirect", {
|
return this._http.getUrl("/login/"+loginType+"/redirect", {
|
||||||
"redirectUrl": redirectUrl,
|
"redirectUrl": redirectUrl,
|
||||||
}, httpApi.PREFIX_R0);
|
}, PREFIX_R0);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -447,7 +451,7 @@ MatrixBaseApis.prototype.getFallbackAuthUrl = function(loginType, authSessionId)
|
|||||||
|
|
||||||
return this._http.getUrl(path, {
|
return this._http.getUrl(path, {
|
||||||
session: authSessionId,
|
session: authSessionId,
|
||||||
}, httpApi.PREFIX_R0);
|
}, PREFIX_R0);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Room operations
|
// Room operations
|
||||||
@@ -499,7 +503,7 @@ MatrixBaseApis.prototype.fetchRelations =
|
|||||||
});
|
});
|
||||||
const response = await this._http.authedRequest(
|
const response = await this._http.authedRequest(
|
||||||
undefined, "GET", path, null, null, {
|
undefined, "GET", path, null, null, {
|
||||||
prefix: httpApi.PREFIX_UNSTABLE,
|
prefix: PREFIX_UNSTABLE,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return response;
|
return response;
|
||||||
@@ -1377,7 +1381,7 @@ MatrixBaseApis.prototype.addThreePid = function(creds, bind, callback) {
|
|||||||
MatrixBaseApis.prototype.addThreePidOnly = async function(data) {
|
MatrixBaseApis.prototype.addThreePidOnly = async function(data) {
|
||||||
const path = "/account/3pid/add";
|
const path = "/account/3pid/add";
|
||||||
const prefix = await this.isVersionSupported("r0.6.0") ?
|
const prefix = await this.isVersionSupported("r0.6.0") ?
|
||||||
httpApi.PREFIX_R0 : httpApi.PREFIX_UNSTABLE;
|
PREFIX_R0 : PREFIX_UNSTABLE;
|
||||||
return this._http.authedRequest(
|
return this._http.authedRequest(
|
||||||
undefined, "POST", path, null, data, { prefix },
|
undefined, "POST", path, null, data, { prefix },
|
||||||
);
|
);
|
||||||
@@ -1400,7 +1404,7 @@ MatrixBaseApis.prototype.addThreePidOnly = async function(data) {
|
|||||||
MatrixBaseApis.prototype.bindThreePid = async function(data) {
|
MatrixBaseApis.prototype.bindThreePid = async function(data) {
|
||||||
const path = "/account/3pid/bind";
|
const path = "/account/3pid/bind";
|
||||||
const prefix = await this.isVersionSupported("r0.6.0") ?
|
const prefix = await this.isVersionSupported("r0.6.0") ?
|
||||||
httpApi.PREFIX_R0 : httpApi.PREFIX_UNSTABLE;
|
PREFIX_R0 : PREFIX_UNSTABLE;
|
||||||
return this._http.authedRequest(
|
return this._http.authedRequest(
|
||||||
undefined, "POST", path, null, data, { prefix },
|
undefined, "POST", path, null, data, { prefix },
|
||||||
);
|
);
|
||||||
@@ -1425,7 +1429,7 @@ MatrixBaseApis.prototype.unbindThreePid = async function(medium, address) {
|
|||||||
id_server: this.getIdentityServerUrl(true),
|
id_server: this.getIdentityServerUrl(true),
|
||||||
};
|
};
|
||||||
const prefix = await this.isVersionSupported("r0.6.0") ?
|
const prefix = await this.isVersionSupported("r0.6.0") ?
|
||||||
httpApi.PREFIX_R0 : httpApi.PREFIX_UNSTABLE;
|
PREFIX_R0 : PREFIX_UNSTABLE;
|
||||||
return this._http.authedRequest(
|
return this._http.authedRequest(
|
||||||
undefined, "POST", path, null, data, { prefix },
|
undefined, "POST", path, null, data, { prefix },
|
||||||
);
|
);
|
||||||
@@ -1722,7 +1726,7 @@ MatrixBaseApis.prototype.uploadKeySignatures = function(content) {
|
|||||||
return this._http.authedRequest(
|
return this._http.authedRequest(
|
||||||
undefined, "POST", '/keys/signatures/upload', undefined,
|
undefined, "POST", '/keys/signatures/upload', undefined,
|
||||||
content, {
|
content, {
|
||||||
prefix: httpApi.PREFIX_UNSTABLE,
|
prefix: PREFIX_UNSTABLE,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -1815,7 +1819,7 @@ MatrixBaseApis.prototype.uploadDeviceSigningKeys = function(auth, keys) {
|
|||||||
const data = Object.assign({}, keys, {auth});
|
const data = Object.assign({}, keys, {auth});
|
||||||
return this._http.authedRequest(
|
return this._http.authedRequest(
|
||||||
undefined, "POST", "/keys/device_signing/upload", undefined, data, {
|
undefined, "POST", "/keys/device_signing/upload", undefined, data, {
|
||||||
prefix: httpApi.PREFIX_UNSTABLE,
|
prefix: PREFIX_UNSTABLE,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -1841,7 +1845,7 @@ MatrixBaseApis.prototype.registerWithIdentityServer = function(hsOpenIdToken) {
|
|||||||
throw new Error("No Identity Server base URL set");
|
throw new Error("No Identity Server base URL set");
|
||||||
}
|
}
|
||||||
|
|
||||||
const uri = this.idBaseUrl + httpApi.PREFIX_IDENTITY_V2 + "/account/register";
|
const uri = this.idBaseUrl + PREFIX_IDENTITY_V2 + "/account/register";
|
||||||
return this._http.requestOtherUrl(
|
return this._http.requestOtherUrl(
|
||||||
undefined, "POST", uri,
|
undefined, "POST", uri,
|
||||||
null, hsOpenIdToken,
|
null, hsOpenIdToken,
|
||||||
@@ -1890,7 +1894,7 @@ MatrixBaseApis.prototype.requestEmailToken = async function(
|
|||||||
try {
|
try {
|
||||||
const response = await this._http.idServerRequest(
|
const response = await this._http.idServerRequest(
|
||||||
undefined, "POST", "/validate/email/requestToken",
|
undefined, "POST", "/validate/email/requestToken",
|
||||||
params, httpApi.PREFIX_IDENTITY_V2, identityAccessToken,
|
params, PREFIX_IDENTITY_V2, identityAccessToken,
|
||||||
);
|
);
|
||||||
// TODO: Fold callback into above call once v1 path below is removed
|
// TODO: Fold callback into above call once v1 path below is removed
|
||||||
if (callback) callback(null, response);
|
if (callback) callback(null, response);
|
||||||
@@ -1903,7 +1907,7 @@ MatrixBaseApis.prototype.requestEmailToken = async function(
|
|||||||
logger.warn("IS doesn't support v2, falling back to deprecated v1");
|
logger.warn("IS doesn't support v2, falling back to deprecated v1");
|
||||||
return await this._http.idServerRequest(
|
return await this._http.idServerRequest(
|
||||||
callback, "POST", "/validate/email/requestToken",
|
callback, "POST", "/validate/email/requestToken",
|
||||||
params, httpApi.PREFIX_IDENTITY_V1,
|
params, PREFIX_IDENTITY_V1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (callback) callback(err);
|
if (callback) callback(err);
|
||||||
@@ -1958,7 +1962,7 @@ MatrixBaseApis.prototype.requestMsisdnToken = async function(
|
|||||||
try {
|
try {
|
||||||
const response = await this._http.idServerRequest(
|
const response = await this._http.idServerRequest(
|
||||||
undefined, "POST", "/validate/msisdn/requestToken",
|
undefined, "POST", "/validate/msisdn/requestToken",
|
||||||
params, httpApi.PREFIX_IDENTITY_V2, identityAccessToken,
|
params, PREFIX_IDENTITY_V2, identityAccessToken,
|
||||||
);
|
);
|
||||||
// TODO: Fold callback into above call once v1 path below is removed
|
// TODO: Fold callback into above call once v1 path below is removed
|
||||||
if (callback) callback(null, response);
|
if (callback) callback(null, response);
|
||||||
@@ -1971,7 +1975,7 @@ MatrixBaseApis.prototype.requestMsisdnToken = async function(
|
|||||||
logger.warn("IS doesn't support v2, falling back to deprecated v1");
|
logger.warn("IS doesn't support v2, falling back to deprecated v1");
|
||||||
return await this._http.idServerRequest(
|
return await this._http.idServerRequest(
|
||||||
callback, "POST", "/validate/msisdn/requestToken",
|
callback, "POST", "/validate/msisdn/requestToken",
|
||||||
params, httpApi.PREFIX_IDENTITY_V1,
|
params, PREFIX_IDENTITY_V1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (callback) callback(err);
|
if (callback) callback(err);
|
||||||
@@ -2013,7 +2017,7 @@ MatrixBaseApis.prototype.submitMsisdnToken = async function(
|
|||||||
try {
|
try {
|
||||||
return await this._http.idServerRequest(
|
return await this._http.idServerRequest(
|
||||||
undefined, "POST", "/validate/msisdn/submitToken",
|
undefined, "POST", "/validate/msisdn/submitToken",
|
||||||
params, httpApi.PREFIX_IDENTITY_V2, identityAccessToken,
|
params, PREFIX_IDENTITY_V2, identityAccessToken,
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.cors === "rejected" || err.httpStatus === 404) {
|
if (err.cors === "rejected" || err.httpStatus === 404) {
|
||||||
@@ -2023,7 +2027,7 @@ MatrixBaseApis.prototype.submitMsisdnToken = async function(
|
|||||||
logger.warn("IS doesn't support v2, falling back to deprecated v1");
|
logger.warn("IS doesn't support v2, falling back to deprecated v1");
|
||||||
return await this._http.idServerRequest(
|
return await this._http.idServerRequest(
|
||||||
undefined, "POST", "/validate/msisdn/submitToken",
|
undefined, "POST", "/validate/msisdn/submitToken",
|
||||||
params, httpApi.PREFIX_IDENTITY_V1,
|
params, PREFIX_IDENTITY_V1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
throw err;
|
throw err;
|
||||||
@@ -2074,7 +2078,7 @@ MatrixBaseApis.prototype.submitMsisdnTokenOtherUrl = function(
|
|||||||
MatrixBaseApis.prototype.getIdentityHashDetails = function(identityAccessToken) {
|
MatrixBaseApis.prototype.getIdentityHashDetails = function(identityAccessToken) {
|
||||||
return this._http.idServerRequest(
|
return this._http.idServerRequest(
|
||||||
undefined, "GET", "/hash_details",
|
undefined, "GET", "/hash_details",
|
||||||
null, httpApi.PREFIX_IDENTITY_V2, identityAccessToken,
|
null, PREFIX_IDENTITY_V2, identityAccessToken,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2143,7 +2147,7 @@ MatrixBaseApis.prototype.identityHashedLookup = async function(
|
|||||||
|
|
||||||
const response = await this._http.idServerRequest(
|
const response = await this._http.idServerRequest(
|
||||||
undefined, "POST", "/lookup",
|
undefined, "POST", "/lookup",
|
||||||
params, httpApi.PREFIX_IDENTITY_V2, identityAccessToken,
|
params, PREFIX_IDENTITY_V2, identityAccessToken,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!response || !response['mappings']) return []; // no results
|
if (!response || !response['mappings']) return []; // no results
|
||||||
@@ -2223,7 +2227,7 @@ MatrixBaseApis.prototype.lookupThreePid = async function(
|
|||||||
logger.warn("IS doesn't support v2, falling back to deprecated v1");
|
logger.warn("IS doesn't support v2, falling back to deprecated v1");
|
||||||
return await this._http.idServerRequest(
|
return await this._http.idServerRequest(
|
||||||
callback, "GET", "/lookup",
|
callback, "GET", "/lookup",
|
||||||
params, httpApi.PREFIX_IDENTITY_V1,
|
params, PREFIX_IDENTITY_V1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (callback) callback(err, undefined);
|
if (callback) callback(err, undefined);
|
||||||
@@ -2281,7 +2285,7 @@ MatrixBaseApis.prototype.bulkLookupThreePids = async function(
|
|||||||
logger.warn("IS doesn't support v2, falling back to deprecated v1");
|
logger.warn("IS doesn't support v2, falling back to deprecated v1");
|
||||||
return await this._http.idServerRequest(
|
return await this._http.idServerRequest(
|
||||||
undefined, "POST", "/bulk_lookup", params,
|
undefined, "POST", "/bulk_lookup", params,
|
||||||
httpApi.PREFIX_IDENTITY_V1, identityAccessToken,
|
PREFIX_IDENTITY_V1, identityAccessToken,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
throw err;
|
throw err;
|
||||||
@@ -2304,7 +2308,7 @@ MatrixBaseApis.prototype.getIdentityAccount = function(
|
|||||||
) {
|
) {
|
||||||
return this._http.idServerRequest(
|
return this._http.idServerRequest(
|
||||||
undefined, "GET", "/account",
|
undefined, "GET", "/account",
|
||||||
undefined, httpApi.PREFIX_IDENTITY_V2, identityAccessToken,
|
undefined, PREFIX_IDENTITY_V2, identityAccessToken,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2432,7 +2436,3 @@ MatrixBaseApis.prototype.reportEvent = function(roomId, eventId, score, reason)
|
|||||||
return this._http.authedRequest(undefined, "POST", path, null, {score, reason});
|
return this._http.authedRequest(undefined, "POST", path, null, {score, reason});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* MatrixBaseApis object
|
|
||||||
*/
|
|
||||||
module.exports = MatrixBaseApis;
|
|
||||||
|
|||||||
54
src/browser-index.js
Normal file
54
src/browser-index.js
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2019 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 * as matrixcs from "./matrix";
|
||||||
|
import request from "browser-request";
|
||||||
|
import queryString from "qs";
|
||||||
|
|
||||||
|
matrixcs.request(function(opts, fn) {
|
||||||
|
// We manually fix the query string for browser-request because
|
||||||
|
// it doesn't correctly handle cases like ?via=one&via=two. Instead
|
||||||
|
// we mimic `request`'s query string interface to make it all work
|
||||||
|
// as expected.
|
||||||
|
// browser-request will happily take the constructed string as the
|
||||||
|
// query string without trying to modify it further.
|
||||||
|
opts.qs = queryString.stringify(opts.qs || {}, opts.qsStringifyOptions);
|
||||||
|
return request(opts, fn);
|
||||||
|
});
|
||||||
|
|
||||||
|
// just *accessing* indexedDB throws an exception in firefox with
|
||||||
|
// indexeddb disabled.
|
||||||
|
let indexedDB;
|
||||||
|
try {
|
||||||
|
indexedDB = global.indexedDB;
|
||||||
|
} catch(e) {}
|
||||||
|
|
||||||
|
// if our browser (appears to) support indexeddb, use an indexeddb crypto store.
|
||||||
|
if (indexedDB) {
|
||||||
|
matrixcs.setCryptoStoreFactory(
|
||||||
|
function() {
|
||||||
|
return new matrixcs.IndexedDBCryptoStore(
|
||||||
|
indexedDB, "matrix-js-sdk:crypto",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We export 3 things to make browserify happy as well as downstream projects.
|
||||||
|
// It's awkward, but required.
|
||||||
|
export * from "./matrix";
|
||||||
|
export default matrixcs; // keep export for browserify package deps
|
||||||
|
global.matrixcs = matrixcs;
|
||||||
141
src/client.js
141
src/client.js
@@ -16,47 +16,41 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const PushProcessor = require('./pushprocessor');
|
|
||||||
import {sleep} from './utils';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
*/
|
*/
|
||||||
const EventEmitter = require("events").EventEmitter;
|
|
||||||
const url = require('url');
|
|
||||||
|
|
||||||
const httpApi = require("./http-api");
|
import url from "url";
|
||||||
const MatrixEvent = require("./models/event").MatrixEvent;
|
import {EventEmitter} from "events";
|
||||||
const EventStatus = require("./models/event").EventStatus;
|
import {MatrixBaseApis} from "./base-apis";
|
||||||
const EventTimeline = require("./models/event-timeline");
|
import {Filter} from "./filter";
|
||||||
const SearchResult = require("./models/search-result");
|
import {SyncApi} from "./sync";
|
||||||
const StubStore = require("./store/stub");
|
import {EventStatus, MatrixEvent} from "./models/event";
|
||||||
const webRtcCall = require("./webrtc/call");
|
import {EventTimeline} from "./models/event-timeline";
|
||||||
const utils = require("./utils");
|
import {SearchResult} from "./models/search-result";
|
||||||
const contentRepo = require("./content-repo");
|
import {StubStore} from "./store/stub";
|
||||||
const Filter = require("./filter");
|
import {createNewMatrixCall} from "./webrtc/call";
|
||||||
const SyncApi = require("./sync");
|
import * as utils from './utils';
|
||||||
const MatrixBaseApis = require("./base-apis");
|
import {sleep} from './utils';
|
||||||
const MatrixError = httpApi.MatrixError;
|
import {MatrixError, PREFIX_MEDIA_R0, PREFIX_UNSTABLE} from "./http-api";
|
||||||
const ContentHelpers = require("./content-helpers");
|
import {getHttpUriForMxc} from "./content-repo";
|
||||||
const olmlib = require("./crypto/olmlib");
|
import * as ContentHelpers from "./content-helpers";
|
||||||
|
import * as olmlib from "./crypto/olmlib";
|
||||||
import ReEmitter from './ReEmitter';
|
import {ReEmitter} from './ReEmitter';
|
||||||
import RoomList from './crypto/RoomList';
|
import {RoomList} from './crypto/RoomList';
|
||||||
import logger from './logger';
|
import {logger} from './logger';
|
||||||
|
import {Crypto, isCryptoAvailable} from './crypto';
|
||||||
import Crypto from './crypto';
|
|
||||||
import { isCryptoAvailable } from './crypto';
|
|
||||||
import {decodeRecoveryKey} from './crypto/recoverykey';
|
import {decodeRecoveryKey} from './crypto/recoverykey';
|
||||||
import {keyFromAuthData} from './crypto/key_passphrase';
|
import {keyFromAuthData} from './crypto/key_passphrase';
|
||||||
import {randomString} from './randomstring';
|
import {randomString} from './randomstring';
|
||||||
import { encodeBase64, decodeBase64 } from '../lib/crypto/olmlib';
|
import {PushProcessor} from "./pushprocessor";
|
||||||
|
import {encodeBase64, decodeBase64} from "./crypto/olmlib";
|
||||||
|
|
||||||
const SCROLLBACK_DELAY_MS = 3000;
|
const SCROLLBACK_DELAY_MS = 3000;
|
||||||
const CRYPTO_ENABLED = isCryptoAvailable();
|
export const CRYPTO_ENABLED = isCryptoAvailable();
|
||||||
const CAPABILITIES_CACHE_MS = 21600000; // 6 hours - an arbitrary value
|
const CAPABILITIES_CACHE_MS = 21600000; // 6 hours - an arbitrary value
|
||||||
|
|
||||||
function keysFromRecoverySession(sessions, decryptionKey, roomId) {
|
function keysFromRecoverySession(sessions, decryptionKey, roomId) {
|
||||||
@@ -235,7 +229,7 @@ function keyFromRecoverySession(session, decryptionKey) {
|
|||||||
* {DeviceTrustLevel} device_trust: The trust status of the device requesting
|
* {DeviceTrustLevel} device_trust: The trust status of the device requesting
|
||||||
* the secret as returned by {@link module:client~MatrixClient#checkDeviceTrust}.
|
* the secret as returned by {@link module:client~MatrixClient#checkDeviceTrust}.
|
||||||
*/
|
*/
|
||||||
function MatrixClient(opts) {
|
export function MatrixClient(opts) {
|
||||||
opts.baseUrl = utils.ensureNoTrailingSlash(opts.baseUrl);
|
opts.baseUrl = utils.ensureNoTrailingSlash(opts.baseUrl);
|
||||||
opts.idBaseUrl = utils.ensureNoTrailingSlash(opts.idBaseUrl);
|
opts.idBaseUrl = utils.ensureNoTrailingSlash(opts.idBaseUrl);
|
||||||
|
|
||||||
@@ -274,7 +268,7 @@ function MatrixClient(opts) {
|
|||||||
|
|
||||||
// try constructing a MatrixCall to see if we are running in an environment
|
// try constructing a MatrixCall to see if we are running in an environment
|
||||||
// which has WebRTC. If we are, listen for and handle m.call.* events.
|
// which has WebRTC. If we are, listen for and handle m.call.* events.
|
||||||
const call = webRtcCall.createNewMatrixCall(this);
|
const call = createNewMatrixCall(this);
|
||||||
this._supportsVoip = false;
|
this._supportsVoip = false;
|
||||||
if (call) {
|
if (call) {
|
||||||
setupCallEventHandler(this);
|
setupCallEventHandler(this);
|
||||||
@@ -946,6 +940,35 @@ MatrixClient.prototype.getGlobalBlacklistUnverifiedDevices = function() {
|
|||||||
return this._crypto.getGlobalBlacklistUnverifiedDevices();
|
return this._crypto.getGlobalBlacklistUnverifiedDevices();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether sendMessage in a room with unknown and unverified devices
|
||||||
|
* should throw an error and not send them message. This has 'Global' for
|
||||||
|
* symmetry with setGlobalBlacklistUnverifiedDevices but there is currently
|
||||||
|
* no room-level equivalent for this setting.
|
||||||
|
*
|
||||||
|
* This API is currently UNSTABLE and may change or be removed without notice.
|
||||||
|
*
|
||||||
|
* @param {boolean} value whether error on unknown devices
|
||||||
|
*/
|
||||||
|
MatrixClient.prototype.setGlobalErrorOnUnknownDevices = function(value) {
|
||||||
|
if (this._crypto === null) {
|
||||||
|
throw new Error("End-to-end encryption disabled");
|
||||||
|
}
|
||||||
|
this._crypto.setGlobalErrorOnUnknownDevices(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {boolean} whether to error on unknown devices
|
||||||
|
*
|
||||||
|
* This API is currently UNSTABLE and may change or be removed without notice.
|
||||||
|
*/
|
||||||
|
MatrixClient.prototype.getGlobalErrorOnUnknownDevices = function() {
|
||||||
|
if (this._crypto === null) {
|
||||||
|
throw new Error("End-to-end encryption disabled");
|
||||||
|
}
|
||||||
|
return this._crypto.getGlobalErrorOnUnknownDevices();
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add methods that call the corresponding method in this._crypto
|
* Add methods that call the corresponding method in this._crypto
|
||||||
*
|
*
|
||||||
@@ -1346,7 +1369,7 @@ MatrixClient.prototype.checkKeyBackup = function() {
|
|||||||
MatrixClient.prototype.getKeyBackupVersion = function() {
|
MatrixClient.prototype.getKeyBackupVersion = function() {
|
||||||
return this._http.authedRequest(
|
return this._http.authedRequest(
|
||||||
undefined, "GET", "/room_keys/version", undefined, undefined,
|
undefined, "GET", "/room_keys/version", undefined, undefined,
|
||||||
{prefix: httpApi.PREFIX_UNSTABLE},
|
{prefix: PREFIX_UNSTABLE},
|
||||||
).then((res) => {
|
).then((res) => {
|
||||||
if (res.algorithm !== olmlib.MEGOLM_BACKUP_ALGORITHM) {
|
if (res.algorithm !== olmlib.MEGOLM_BACKUP_ALGORITHM) {
|
||||||
const err = "Unknown backup algorithm: " + res.algorithm;
|
const err = "Unknown backup algorithm: " + res.algorithm;
|
||||||
@@ -1505,14 +1528,20 @@ MatrixClient.prototype.createKeyBackupVersion = async function(info) {
|
|||||||
// favour of just signing with the cross-singing master key.
|
// favour of just signing with the cross-singing master key.
|
||||||
await this._crypto._signObject(data.auth_data);
|
await this._crypto._signObject(data.auth_data);
|
||||||
|
|
||||||
if (this._crypto._crossSigningInfo.getId()) {
|
if (
|
||||||
|
this._cryptoCallbacks.getCrossSigningKey &&
|
||||||
|
this._crypto._crossSigningInfo.getId()
|
||||||
|
) {
|
||||||
// now also sign the auth data with the cross-signing master key
|
// now also sign the auth data with the cross-signing master key
|
||||||
|
// we check for the callback explicitly here because we still want to be able
|
||||||
|
// to create an un-cross-signed key backup if there is a cross-signing key but
|
||||||
|
// no callback supplied.
|
||||||
await this._crypto._crossSigningInfo.signObject(data.auth_data, "master");
|
await this._crypto._crossSigningInfo.signObject(data.auth_data, "master");
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await this._http.authedRequest(
|
const res = await this._http.authedRequest(
|
||||||
undefined, "POST", "/room_keys/version", undefined, data,
|
undefined, "POST", "/room_keys/version", undefined, data,
|
||||||
{prefix: httpApi.PREFIX_UNSTABLE},
|
{prefix: PREFIX_UNSTABLE},
|
||||||
);
|
);
|
||||||
|
|
||||||
// We could assume everything's okay and enable directly, but this ensures
|
// We could assume everything's okay and enable directly, but this ensures
|
||||||
@@ -1544,7 +1573,7 @@ MatrixClient.prototype.deleteKeyBackupVersion = function(version) {
|
|||||||
|
|
||||||
return this._http.authedRequest(
|
return this._http.authedRequest(
|
||||||
undefined, "DELETE", path, undefined, undefined,
|
undefined, "DELETE", path, undefined, undefined,
|
||||||
{prefix: httpApi.PREFIX_UNSTABLE},
|
{prefix: PREFIX_UNSTABLE},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1586,7 +1615,7 @@ MatrixClient.prototype.sendKeyBackup = function(roomId, sessionId, version, data
|
|||||||
const path = this._makeKeyBackupPath(roomId, sessionId, version);
|
const path = this._makeKeyBackupPath(roomId, sessionId, version);
|
||||||
return this._http.authedRequest(
|
return this._http.authedRequest(
|
||||||
undefined, "PUT", path.path, path.queryData, data,
|
undefined, "PUT", path.path, path.queryData, data,
|
||||||
{prefix: httpApi.PREFIX_UNSTABLE},
|
{prefix: PREFIX_UNSTABLE},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1720,7 +1749,7 @@ MatrixClient.prototype._restoreKeyBackup = function(
|
|||||||
|
|
||||||
return this._http.authedRequest(
|
return this._http.authedRequest(
|
||||||
undefined, "GET", path.path, path.queryData, undefined,
|
undefined, "GET", path.path, path.queryData, undefined,
|
||||||
{prefix: httpApi.PREFIX_UNSTABLE},
|
{prefix: PREFIX_UNSTABLE},
|
||||||
).then((res) => {
|
).then((res) => {
|
||||||
if (res.rooms) {
|
if (res.rooms) {
|
||||||
for (const [roomId, roomData] of Object.entries(res.rooms)) {
|
for (const [roomId, roomData] of Object.entries(res.rooms)) {
|
||||||
@@ -1728,7 +1757,7 @@ MatrixClient.prototype._restoreKeyBackup = function(
|
|||||||
|
|
||||||
totalKeyCount += Object.keys(roomData.sessions).length;
|
totalKeyCount += Object.keys(roomData.sessions).length;
|
||||||
const roomKeys = keysFromRecoverySession(
|
const roomKeys = keysFromRecoverySession(
|
||||||
roomData.sessions, decryption, roomId, roomKeys,
|
roomData.sessions, decryption, roomId,
|
||||||
);
|
);
|
||||||
for (const k of roomKeys) {
|
for (const k of roomKeys) {
|
||||||
k.room_id = roomId;
|
k.room_id = roomId;
|
||||||
@@ -1770,7 +1799,7 @@ MatrixClient.prototype.deleteKeysFromBackup = function(roomId, sessionId, versio
|
|||||||
const path = this._makeKeyBackupPath(roomId, sessionId, version);
|
const path = this._makeKeyBackupPath(roomId, sessionId, version);
|
||||||
return this._http.authedRequest(
|
return this._http.authedRequest(
|
||||||
undefined, "DELETE", path.path, path.queryData, undefined,
|
undefined, "DELETE", path.path, path.queryData, undefined,
|
||||||
{prefix: httpApi.PREFIX_UNSTABLE},
|
{prefix: PREFIX_UNSTABLE},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1806,7 +1835,7 @@ MatrixClient.prototype.getGroups = function() {
|
|||||||
MatrixClient.prototype.getMediaConfig = function(callback) {
|
MatrixClient.prototype.getMediaConfig = function(callback) {
|
||||||
return this._http.authedRequest(
|
return this._http.authedRequest(
|
||||||
callback, "GET", "/config", undefined, undefined, {
|
callback, "GET", "/config", undefined, undefined, {
|
||||||
prefix: httpApi.PREFIX_MEDIA_R0,
|
prefix: PREFIX_MEDIA_R0,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -1913,6 +1942,25 @@ MatrixClient.prototype.getAccountData = function(eventType) {
|
|||||||
return this.store.getAccountData(eventType);
|
return this.store.getAccountData(eventType);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get account data event of given type for the current user. This variant
|
||||||
|
* bypasses the local store and gets account data directly from the homeserver,
|
||||||
|
* which can be useful very early in startup before the initial sync.
|
||||||
|
* @param {string} eventType The event type
|
||||||
|
* @return {module:client.Promise} Resolves: The contents of the given account
|
||||||
|
* data event.
|
||||||
|
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||||
|
*/
|
||||||
|
MatrixClient.prototype.getAccountDataFromServer = function(eventType) {
|
||||||
|
const path = utils.encodeUri("/user/$userId/account_data/$type", {
|
||||||
|
$userId: this.credentials.userId,
|
||||||
|
$type: eventType,
|
||||||
|
});
|
||||||
|
return this._http.authedRequest(
|
||||||
|
undefined, "GET", path, undefined,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the users that are ignored by this client
|
* Gets the users that are ignored by this client
|
||||||
* @returns {string[]} The array of users that are ignored (empty if none)
|
* @returns {string[]} The array of users that are ignored (empty if none)
|
||||||
@@ -2743,7 +2791,7 @@ MatrixClient.prototype.getUrlPreview = function(url, ts, callback) {
|
|||||||
url: url,
|
url: url,
|
||||||
ts: ts,
|
ts: ts,
|
||||||
}, undefined, {
|
}, undefined, {
|
||||||
prefix: httpApi.PREFIX_MEDIA_R0,
|
prefix: PREFIX_MEDIA_R0,
|
||||||
},
|
},
|
||||||
).then(function(response) {
|
).then(function(response) {
|
||||||
// TODO: expire cache occasionally
|
// TODO: expire cache occasionally
|
||||||
@@ -3193,7 +3241,7 @@ MatrixClient.prototype.setAvatarUrl = function(url, callback) {
|
|||||||
*/
|
*/
|
||||||
MatrixClient.prototype.mxcUrlToHttp =
|
MatrixClient.prototype.mxcUrlToHttp =
|
||||||
function(mxcUrl, width, height, resizeMethod, allowDirectLinks) {
|
function(mxcUrl, width, height, resizeMethod, allowDirectLinks) {
|
||||||
return contentRepo.getHttpUriForMxc(
|
return getHttpUriForMxc(
|
||||||
this.baseUrl, mxcUrl, width, height, resizeMethod, allowDirectLinks,
|
this.baseUrl, mxcUrl, width, height, resizeMethod, allowDirectLinks,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -4852,7 +4900,7 @@ function setupCallEventHandler(client) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
call = webRtcCall.createNewMatrixCall(client, event.getRoomId(), {
|
call = createNewMatrixCall(client, event.getRoomId(), {
|
||||||
forceTURN: client._forceTURN,
|
forceTURN: client._forceTURN,
|
||||||
});
|
});
|
||||||
if (!call) {
|
if (!call) {
|
||||||
@@ -4952,7 +5000,7 @@ function setupCallEventHandler(client) {
|
|||||||
// if not live, store the fact that the call has ended because
|
// if not live, store the fact that the call has ended because
|
||||||
// we're probably getting events backwards so
|
// we're probably getting events backwards so
|
||||||
// the hangup will come before the invite
|
// the hangup will come before the invite
|
||||||
call = webRtcCall.createNewMatrixCall(client, event.getRoomId());
|
call = createNewMatrixCall(client, event.getRoomId());
|
||||||
if (call) {
|
if (call) {
|
||||||
call.callId = content.call_id;
|
call.callId = content.call_id;
|
||||||
call._initWithHangup(event);
|
call._initWithHangup(event);
|
||||||
@@ -5052,11 +5100,6 @@ MatrixClient.prototype.generateClientSecret = function() {
|
|||||||
return randomString(32);
|
return randomString(32);
|
||||||
};
|
};
|
||||||
|
|
||||||
/** */
|
|
||||||
module.exports.MatrixClient = MatrixClient;
|
|
||||||
/** */
|
|
||||||
module.exports.CRYPTO_ENABLED = CRYPTO_ENABLED;
|
|
||||||
|
|
||||||
// MatrixClient Event JSDocs
|
// MatrixClient Event JSDocs
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2018 New Vector Ltd
|
Copyright 2018 New Vector Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -13,24 +14,23 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
"use strict";
|
|
||||||
|
|
||||||
/** @module ContentHelpers */
|
/** @module ContentHelpers */
|
||||||
module.exports = {
|
|
||||||
/**
|
/**
|
||||||
* Generates the content for a HTML Message event
|
* Generates the content for a HTML Message event
|
||||||
* @param {string} body the plaintext body of the message
|
* @param {string} body the plaintext body of the message
|
||||||
* @param {string} htmlBody the HTML representation of the message
|
* @param {string} htmlBody the HTML representation of the message
|
||||||
* @returns {{msgtype: string, format: string, body: string, formatted_body: string}}
|
* @returns {{msgtype: string, format: string, body: string, formatted_body: string}}
|
||||||
*/
|
*/
|
||||||
makeHtmlMessage: function(body, htmlBody) {
|
export function makeHtmlMessage(body, htmlBody) {
|
||||||
return {
|
return {
|
||||||
msgtype: "m.text",
|
msgtype: "m.text",
|
||||||
format: "org.matrix.custom.html",
|
format: "org.matrix.custom.html",
|
||||||
body: body,
|
body: body,
|
||||||
formatted_body: htmlBody,
|
formatted_body: htmlBody,
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the content for a HTML Notice event
|
* Generates the content for a HTML Notice event
|
||||||
@@ -38,14 +38,14 @@ module.exports = {
|
|||||||
* @param {string} htmlBody the HTML representation of the notice
|
* @param {string} htmlBody the HTML representation of the notice
|
||||||
* @returns {{msgtype: string, format: string, body: string, formatted_body: string}}
|
* @returns {{msgtype: string, format: string, body: string, formatted_body: string}}
|
||||||
*/
|
*/
|
||||||
makeHtmlNotice: function(body, htmlBody) {
|
export function makeHtmlNotice(body, htmlBody) {
|
||||||
return {
|
return {
|
||||||
msgtype: "m.notice",
|
msgtype: "m.notice",
|
||||||
format: "org.matrix.custom.html",
|
format: "org.matrix.custom.html",
|
||||||
body: body,
|
body: body,
|
||||||
formatted_body: htmlBody,
|
formatted_body: htmlBody,
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the content for a HTML Emote event
|
* Generates the content for a HTML Emote event
|
||||||
@@ -53,48 +53,47 @@ module.exports = {
|
|||||||
* @param {string} htmlBody the HTML representation of the emote
|
* @param {string} htmlBody the HTML representation of the emote
|
||||||
* @returns {{msgtype: string, format: string, body: string, formatted_body: string}}
|
* @returns {{msgtype: string, format: string, body: string, formatted_body: string}}
|
||||||
*/
|
*/
|
||||||
makeHtmlEmote: function(body, htmlBody) {
|
export function makeHtmlEmote(body, htmlBody) {
|
||||||
return {
|
return {
|
||||||
msgtype: "m.emote",
|
msgtype: "m.emote",
|
||||||
format: "org.matrix.custom.html",
|
format: "org.matrix.custom.html",
|
||||||
body: body,
|
body: body,
|
||||||
formatted_body: htmlBody,
|
formatted_body: htmlBody,
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the content for a Plaintext Message event
|
* Generates the content for a Plaintext Message event
|
||||||
* @param {string} body the plaintext body of the emote
|
* @param {string} body the plaintext body of the emote
|
||||||
* @returns {{msgtype: string, body: string}}
|
* @returns {{msgtype: string, body: string}}
|
||||||
*/
|
*/
|
||||||
makeTextMessage: function(body) {
|
export function makeTextMessage(body) {
|
||||||
return {
|
return {
|
||||||
msgtype: "m.text",
|
msgtype: "m.text",
|
||||||
body: body,
|
body: body,
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the content for a Plaintext Notice event
|
* Generates the content for a Plaintext Notice event
|
||||||
* @param {string} body the plaintext body of the notice
|
* @param {string} body the plaintext body of the notice
|
||||||
* @returns {{msgtype: string, body: string}}
|
* @returns {{msgtype: string, body: string}}
|
||||||
*/
|
*/
|
||||||
makeNotice: function(body) {
|
export function makeNotice(body) {
|
||||||
return {
|
return {
|
||||||
msgtype: "m.notice",
|
msgtype: "m.notice",
|
||||||
body: body,
|
body: body,
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the content for a Plaintext Emote event
|
* Generates the content for a Plaintext Emote event
|
||||||
* @param {string} body the plaintext body of the emote
|
* @param {string} body the plaintext body of the emote
|
||||||
* @returns {{msgtype: string, body: string}}
|
* @returns {{msgtype: string, body: string}}
|
||||||
*/
|
*/
|
||||||
makeEmoteMessage: function(body) {
|
export function makeEmoteMessage(body) {
|
||||||
return {
|
return {
|
||||||
msgtype: "m.emote",
|
msgtype: "m.emote",
|
||||||
body: body,
|
body: body,
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -16,10 +17,9 @@ limitations under the License.
|
|||||||
/**
|
/**
|
||||||
* @module content-repo
|
* @module content-repo
|
||||||
*/
|
*/
|
||||||
const utils = require("./utils");
|
|
||||||
|
|
||||||
/** Content Repo utility functions */
|
import * as utils from "./utils";
|
||||||
module.exports = {
|
|
||||||
/**
|
/**
|
||||||
* Get the HTTP URL for an MXC URI.
|
* Get the HTTP URL for an MXC URI.
|
||||||
* @param {string} baseUrl The base homeserver url which has a content repo.
|
* @param {string} baseUrl The base homeserver url which has a content repo.
|
||||||
@@ -34,7 +34,7 @@ module.exports = {
|
|||||||
* for such URLs.
|
* for such URLs.
|
||||||
* @return {string} The complete URL to the content.
|
* @return {string} The complete URL to the content.
|
||||||
*/
|
*/
|
||||||
getHttpUriForMxc: function(baseUrl, mxc, width, height,
|
export function getHttpUriForMxc(baseUrl, mxc, width, height,
|
||||||
resizeMethod, allowDirectLinks) {
|
resizeMethod, allowDirectLinks) {
|
||||||
if (typeof mxc !== "string" || !mxc) {
|
if (typeof mxc !== "string" || !mxc) {
|
||||||
return '';
|
return '';
|
||||||
@@ -74,7 +74,7 @@ module.exports = {
|
|||||||
return baseUrl + prefix + serverAndMediaId +
|
return baseUrl + prefix + serverAndMediaId +
|
||||||
(utils.keys(params).length === 0 ? "" :
|
(utils.keys(params).length === 0 ? "" :
|
||||||
("?" + utils.encodeParams(params))) + fragment;
|
("?" + utils.encodeParams(params))) + fragment;
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an identicon URL from an arbitrary string.
|
* Get an identicon URL from an arbitrary string.
|
||||||
@@ -85,7 +85,7 @@ module.exports = {
|
|||||||
* @return {string} The complete URL to the identicon.
|
* @return {string} The complete URL to the identicon.
|
||||||
* @deprecated This is no longer in the specification.
|
* @deprecated This is no longer in the specification.
|
||||||
*/
|
*/
|
||||||
getIdenticonUri: function(baseUrl, identiconString, width, height) {
|
export function getIdenticonUri(baseUrl, identiconString, width, height) {
|
||||||
if (!identiconString) {
|
if (!identiconString) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -106,5 +106,4 @@ module.exports = {
|
|||||||
return baseUrl + path +
|
return baseUrl + path +
|
||||||
(utils.keys(params).length === 0 ? "" :
|
(utils.keys(params).length === 0 ? "" :
|
||||||
("?" + utils.encodeParams(params)));
|
("?" + utils.encodeParams(params)));
|
||||||
},
|
}
|
||||||
};
|
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ limitations under the License.
|
|||||||
* @module crypto/CrossSigning
|
* @module crypto/CrossSigning
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {pkSign, pkVerify, encodeBase64, decodeBase64} from './olmlib';
|
import {decodeBase64, encodeBase64, pkSign, pkVerify} from './olmlib';
|
||||||
import {EventEmitter} from 'events';
|
import {EventEmitter} from 'events';
|
||||||
import logger from '../logger';
|
import {logger} from '../logger';
|
||||||
|
|
||||||
function publicKeyFromKeyInfo(keyInfo) {
|
function publicKeyFromKeyInfo(keyInfo) {
|
||||||
// `keys` is an object with { [`ed25519:${pubKey}`]: pubKey }
|
// `keys` is an object with { [`ed25519:${pubKey}`]: pubKey }
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
"use strict";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @module crypto/DeviceList
|
* @module crypto/DeviceList
|
||||||
@@ -24,12 +23,11 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {EventEmitter} from 'events';
|
import {EventEmitter} from 'events';
|
||||||
|
import {logger} from '../logger';
|
||||||
import logger from '../logger';
|
import {DeviceInfo} from './deviceinfo';
|
||||||
import DeviceInfo from './deviceinfo';
|
|
||||||
import {CrossSigningInfo} from './CrossSigning';
|
import {CrossSigningInfo} from './CrossSigning';
|
||||||
import olmlib from './olmlib';
|
import * as olmlib from './olmlib';
|
||||||
import IndexedDBCryptoStore from './store/indexeddb-crypto-store';
|
import {IndexedDBCryptoStore} from './store/indexeddb-crypto-store';
|
||||||
import {defer, sleep} from '../utils';
|
import {defer, sleep} from '../utils';
|
||||||
|
|
||||||
|
|
||||||
@@ -63,7 +61,7 @@ const TRACKING_STATUS_UP_TO_DATE = 3;
|
|||||||
/**
|
/**
|
||||||
* @alias module:crypto/DeviceList
|
* @alias module:crypto/DeviceList
|
||||||
*/
|
*/
|
||||||
export default class DeviceList extends EventEmitter {
|
export class DeviceList extends EventEmitter {
|
||||||
constructor(baseApis, cryptoStore, olmDevice) {
|
constructor(baseApis, cryptoStore, olmDevice) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2016 OpenMarket Ltd
|
Copyright 2016 OpenMarket Ltd
|
||||||
Copyright 2017, 2019 New Vector Ltd
|
Copyright 2017, 2019 New Vector Ltd
|
||||||
|
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -15,8 +16,9 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import logger from '../logger';
|
import {logger} from '../logger';
|
||||||
import IndexedDBCryptoStore from './store/indexeddb-crypto-store';
|
import {IndexedDBCryptoStore} from './store/indexeddb-crypto-store';
|
||||||
|
import * as algorithms from './algorithms';
|
||||||
|
|
||||||
// The maximum size of an event is 65K, and we base64 the content, so this is a
|
// The maximum size of an event is 65K, and we base64 the content, so this is a
|
||||||
// reasonable approximation to the biggest plaintext we can encrypt.
|
// reasonable approximation to the biggest plaintext we can encrypt.
|
||||||
@@ -69,7 +71,7 @@ function checkPayloadLength(payloadString) {
|
|||||||
* @property {string} deviceCurve25519Key Curve25519 key for the account
|
* @property {string} deviceCurve25519Key Curve25519 key for the account
|
||||||
* @property {string} deviceEd25519Key Ed25519 key for the account
|
* @property {string} deviceEd25519Key Ed25519 key for the account
|
||||||
*/
|
*/
|
||||||
function OlmDevice(cryptoStore) {
|
export function OlmDevice(cryptoStore) {
|
||||||
this._cryptoStore = cryptoStore;
|
this._cryptoStore = cryptoStore;
|
||||||
this._pickleKey = "DEFAULT_KEY";
|
this._pickleKey = "DEFAULT_KEY";
|
||||||
|
|
||||||
@@ -671,6 +673,18 @@ OlmDevice.prototype.matchesSession = async function(
|
|||||||
return matches;
|
return matches;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
OlmDevice.prototype.recordSessionProblem = async function(deviceKey, type, fixed) {
|
||||||
|
await this._cryptoStore.storeEndToEndSessionProblem(deviceKey, type, fixed);
|
||||||
|
};
|
||||||
|
|
||||||
|
OlmDevice.prototype.sessionMayHaveProblems = async function(deviceKey, timestamp) {
|
||||||
|
return await this._cryptoStore.getEndToEndSessionProblem(deviceKey, timestamp);
|
||||||
|
};
|
||||||
|
|
||||||
|
OlmDevice.prototype.filterOutNotifiedErrorDevices = async function(devices) {
|
||||||
|
return await this._cryptoStore.filterOutNotifiedErrorDevices(devices);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// Outbound group session
|
// Outbound group session
|
||||||
// ======================
|
// ======================
|
||||||
@@ -818,9 +832,9 @@ OlmDevice.prototype._getInboundGroupSession = function(
|
|||||||
roomId, senderKey, sessionId, txn, func,
|
roomId, senderKey, sessionId, txn, func,
|
||||||
) {
|
) {
|
||||||
this._cryptoStore.getEndToEndInboundGroupSession(
|
this._cryptoStore.getEndToEndInboundGroupSession(
|
||||||
senderKey, sessionId, txn, (sessionData) => {
|
senderKey, sessionId, txn, (sessionData, withheld) => {
|
||||||
if (sessionData === null) {
|
if (sessionData === null) {
|
||||||
func(null);
|
func(null, null, withheld);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -834,7 +848,7 @@ OlmDevice.prototype._getInboundGroupSession = function(
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._unpickleInboundGroupSession(sessionData, (session) => {
|
this._unpickleInboundGroupSession(sessionData, (session) => {
|
||||||
func(session, sessionData);
|
func(session, sessionData, withheld);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -859,7 +873,10 @@ OlmDevice.prototype.addInboundGroupSession = async function(
|
|||||||
exportFormat,
|
exportFormat,
|
||||||
) {
|
) {
|
||||||
await this._cryptoStore.doTxn(
|
await this._cryptoStore.doTxn(
|
||||||
'readwrite', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => {
|
'readwrite', [
|
||||||
|
IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS,
|
||||||
|
IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS_WITHHELD,
|
||||||
|
], (txn) => {
|
||||||
/* if we already have this session, consider updating it */
|
/* if we already have this session, consider updating it */
|
||||||
this._getInboundGroupSession(
|
this._getInboundGroupSession(
|
||||||
roomId, senderKey, sessionId, txn,
|
roomId, senderKey, sessionId, txn,
|
||||||
@@ -914,6 +931,60 @@ OlmDevice.prototype.addInboundGroupSession = async function(
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record in the data store why an inbound group session was withheld.
|
||||||
|
*
|
||||||
|
* @param {string} roomId room that the session belongs to
|
||||||
|
* @param {string} senderKey base64-encoded curve25519 key of the sender
|
||||||
|
* @param {string} sessionId session identifier
|
||||||
|
* @param {string} code reason code
|
||||||
|
* @param {string} reason human-readable version of `code`
|
||||||
|
*/
|
||||||
|
OlmDevice.prototype.addInboundGroupSessionWithheld = async function(
|
||||||
|
roomId, senderKey, sessionId, code, reason,
|
||||||
|
) {
|
||||||
|
await this._cryptoStore.doTxn(
|
||||||
|
'readwrite', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS_WITHHELD],
|
||||||
|
(txn) => {
|
||||||
|
this._cryptoStore.storeEndToEndInboundGroupSessionWithheld(
|
||||||
|
senderKey, sessionId,
|
||||||
|
{
|
||||||
|
room_id: roomId,
|
||||||
|
code: code,
|
||||||
|
reason: reason,
|
||||||
|
},
|
||||||
|
txn,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WITHHELD_MESSAGES = {
|
||||||
|
"m.unverified": "The sender has disabled encrypting to unverified devices.",
|
||||||
|
"m.blacklisted": "The sender has blocked you.",
|
||||||
|
"m.unauthorised": "You are not authorised to read the message.",
|
||||||
|
"m.no_olm": "Unable to establish a secure channel.",
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the message to use for the exception when a session key is withheld.
|
||||||
|
*
|
||||||
|
* @param {object} withheld An object that describes why the key was withheld.
|
||||||
|
*
|
||||||
|
* @return {string} the message
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function _calculateWithheldMessage(withheld) {
|
||||||
|
if (withheld.code && withheld.code in WITHHELD_MESSAGES) {
|
||||||
|
return WITHHELD_MESSAGES[withheld.code];
|
||||||
|
} else if (withheld.reason) {
|
||||||
|
return withheld.reason;
|
||||||
|
} else {
|
||||||
|
return "decryption key withheld";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decrypt a received message with an inbound group session
|
* Decrypt a received message with an inbound group session
|
||||||
*
|
*
|
||||||
@@ -934,16 +1005,49 @@ OlmDevice.prototype.decryptGroupMessage = async function(
|
|||||||
roomId, senderKey, sessionId, body, eventId, timestamp,
|
roomId, senderKey, sessionId, body, eventId, timestamp,
|
||||||
) {
|
) {
|
||||||
let result;
|
let result;
|
||||||
|
// when the localstorage crypto store is used as an indexeddb backend,
|
||||||
|
// exceptions thrown from within the inner function are not passed through
|
||||||
|
// to the top level, so we store exceptions in a variable and raise them at
|
||||||
|
// the end
|
||||||
|
let error;
|
||||||
|
|
||||||
await this._cryptoStore.doTxn(
|
await this._cryptoStore.doTxn(
|
||||||
'readwrite', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => {
|
'readwrite', [
|
||||||
|
IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS,
|
||||||
|
IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS_WITHHELD,
|
||||||
|
], (txn) => {
|
||||||
this._getInboundGroupSession(
|
this._getInboundGroupSession(
|
||||||
roomId, senderKey, sessionId, txn, (session, sessionData) => {
|
roomId, senderKey, sessionId, txn, (session, sessionData, withheld) => {
|
||||||
if (session === null) {
|
if (session === null) {
|
||||||
|
if (withheld) {
|
||||||
|
error = new algorithms.DecryptionError(
|
||||||
|
"MEGOLM_UNKNOWN_INBOUND_SESSION_ID",
|
||||||
|
_calculateWithheldMessage(withheld),
|
||||||
|
{
|
||||||
|
session: senderKey + '|' + sessionId,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
result = null;
|
result = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const res = session.decrypt(body);
|
let res;
|
||||||
|
try {
|
||||||
|
res = session.decrypt(body);
|
||||||
|
} catch (e) {
|
||||||
|
if (e && e.message === 'OLM.UNKNOWN_MESSAGE_INDEX' && withheld) {
|
||||||
|
error = new algorithms.DecryptionError(
|
||||||
|
"MEGOLM_UNKNOWN_INBOUND_SESSION_ID",
|
||||||
|
_calculateWithheldMessage(withheld),
|
||||||
|
{
|
||||||
|
session: senderKey + '|' + sessionId,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let plaintext = res.plaintext;
|
let plaintext = res.plaintext;
|
||||||
if (plaintext === undefined) {
|
if (plaintext === undefined) {
|
||||||
@@ -965,10 +1069,11 @@ OlmDevice.prototype.decryptGroupMessage = async function(
|
|||||||
msgInfo.id !== eventId ||
|
msgInfo.id !== eventId ||
|
||||||
msgInfo.timestamp !== timestamp
|
msgInfo.timestamp !== timestamp
|
||||||
) {
|
) {
|
||||||
throw new Error(
|
error = new Error(
|
||||||
"Duplicate message index, possible replay attack: " +
|
"Duplicate message index, possible replay attack: " +
|
||||||
messageIndexKey,
|
messageIndexKey,
|
||||||
);
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this._inboundGroupSessionMessageIndexes[messageIndexKey] = {
|
this._inboundGroupSessionMessageIndexes[messageIndexKey] = {
|
||||||
@@ -994,6 +1099,9 @@ OlmDevice.prototype.decryptGroupMessage = async function(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1009,7 +1117,10 @@ OlmDevice.prototype.decryptGroupMessage = async function(
|
|||||||
OlmDevice.prototype.hasInboundSessionKeys = async function(roomId, senderKey, sessionId) {
|
OlmDevice.prototype.hasInboundSessionKeys = async function(roomId, senderKey, sessionId) {
|
||||||
let result;
|
let result;
|
||||||
await this._cryptoStore.doTxn(
|
await this._cryptoStore.doTxn(
|
||||||
'readonly', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => {
|
'readonly', [
|
||||||
|
IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS,
|
||||||
|
IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS_WITHHELD,
|
||||||
|
], (txn) => {
|
||||||
this._cryptoStore.getEndToEndInboundGroupSession(
|
this._cryptoStore.getEndToEndInboundGroupSession(
|
||||||
senderKey, sessionId, txn, (sessionData) => {
|
senderKey, sessionId, txn, (sessionData) => {
|
||||||
if (sessionData === null) {
|
if (sessionData === null) {
|
||||||
@@ -1060,7 +1171,10 @@ OlmDevice.prototype.getInboundGroupSessionKey = async function(
|
|||||||
) {
|
) {
|
||||||
let result;
|
let result;
|
||||||
await this._cryptoStore.doTxn(
|
await this._cryptoStore.doTxn(
|
||||||
'readonly', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => {
|
'readonly', [
|
||||||
|
IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS,
|
||||||
|
IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS_WITHHELD,
|
||||||
|
], (txn) => {
|
||||||
this._getInboundGroupSession(
|
this._getInboundGroupSession(
|
||||||
roomId, senderKey, sessionId, txn, (session, sessionData) => {
|
roomId, senderKey, sessionId, txn, (session, sessionData) => {
|
||||||
if (session === null) {
|
if (session === null) {
|
||||||
@@ -1139,6 +1253,3 @@ OlmDevice.prototype.verifySignature = function(
|
|||||||
util.ed25519_verify(key, message, signature);
|
util.ed25519_verify(key, message, signature);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/** */
|
|
||||||
module.exports = OlmDevice;
|
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import logger from '../logger';
|
import {logger} from '../logger';
|
||||||
import utils from '../utils';
|
import * as utils from '../utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal module. Management of outgoing room key requests.
|
* Internal module. Management of outgoing room key requests.
|
||||||
@@ -75,7 +75,7 @@ const ROOM_KEY_REQUEST_STATES = {
|
|||||||
CANCELLATION_PENDING_AND_WILL_RESEND: 3,
|
CANCELLATION_PENDING_AND_WILL_RESEND: 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class OutgoingRoomKeyRequestManager {
|
export class OutgoingRoomKeyRequestManager {
|
||||||
constructor(baseApis, deviceId, cryptoStore) {
|
constructor(baseApis, deviceId, cryptoStore) {
|
||||||
this._baseApis = baseApis;
|
this._baseApis = baseApis;
|
||||||
this._deviceId = deviceId;
|
this._deviceId = deviceId;
|
||||||
|
|||||||
@@ -20,12 +20,12 @@ limitations under the License.
|
|||||||
* Manages the list of encrypted rooms
|
* Manages the list of encrypted rooms
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import IndexedDBCryptoStore from './store/indexeddb-crypto-store';
|
import {IndexedDBCryptoStore} from './store/indexeddb-crypto-store';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @alias module:crypto/RoomList
|
* @alias module:crypto/RoomList
|
||||||
*/
|
*/
|
||||||
export default class RoomList {
|
export class RoomList {
|
||||||
constructor(cryptoStore) {
|
constructor(cryptoStore) {
|
||||||
this._cryptoStore = cryptoStore;
|
this._cryptoStore = cryptoStore;
|
||||||
|
|
||||||
|
|||||||
@@ -15,10 +15,10 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {EventEmitter} from 'events';
|
import {EventEmitter} from 'events';
|
||||||
import logger from '../logger';
|
import {logger} from '../logger';
|
||||||
import olmlib from './olmlib';
|
import * as olmlib from './olmlib';
|
||||||
import { randomString } from '../randomstring';
|
|
||||||
import {pkVerify} from './olmlib';
|
import {pkVerify} from './olmlib';
|
||||||
|
import {randomString} from '../randomstring';
|
||||||
|
|
||||||
export const SECRET_STORAGE_ALGORITHM_V1 = "m.secret_storage.v1.curve25519-aes-sha2";
|
export const SECRET_STORAGE_ALGORITHM_V1 = "m.secret_storage.v1.curve25519-aes-sha2";
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ export const SECRET_STORAGE_ALGORITHM_V1 = "m.secret_storage.v1.curve25519-aes-s
|
|||||||
* Implements Secure Secret Storage and Sharing (MSC1946)
|
* Implements Secure Secret Storage and Sharing (MSC1946)
|
||||||
* @module crypto/SecretStorage
|
* @module crypto/SecretStorage
|
||||||
*/
|
*/
|
||||||
export default class SecretStorage extends EventEmitter {
|
export class SecretStorage extends EventEmitter {
|
||||||
constructor(baseApis, cryptoCallbacks, crossSigningInfo) {
|
constructor(baseApis, cryptoCallbacks, crossSigningInfo) {
|
||||||
super();
|
super();
|
||||||
this._baseApis = baseApis;
|
this._baseApis = baseApis;
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ export const DECRYPTION_CLASSES = {};
|
|||||||
* @param {string} params.roomId The ID of the room we will be sending to
|
* @param {string} params.roomId The ID of the room we will be sending to
|
||||||
* @param {object} params.config The body of the m.room.encryption event
|
* @param {object} params.config The body of the m.room.encryption event
|
||||||
*/
|
*/
|
||||||
class EncryptionAlgorithm {
|
export class EncryptionAlgorithm {
|
||||||
constructor(params) {
|
constructor(params) {
|
||||||
this._userId = params.userId;
|
this._userId = params.userId;
|
||||||
this._deviceId = params.deviceId;
|
this._deviceId = params.deviceId;
|
||||||
@@ -84,7 +84,6 @@ class EncryptionAlgorithm {
|
|||||||
onRoomMembership(event, member, oldMembership) {
|
onRoomMembership(event, member, oldMembership) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export {EncryptionAlgorithm}; // https://github.com/jsdoc3/jsdoc/issues/1272
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* base type for decryption implementations
|
* base type for decryption implementations
|
||||||
@@ -98,7 +97,7 @@ export {EncryptionAlgorithm}; // https://github.com/jsdoc3/jsdoc/issues/1272
|
|||||||
* @param {string=} params.roomId The ID of the room we will be receiving
|
* @param {string=} params.roomId The ID of the room we will be receiving
|
||||||
* from. Null for to-device events.
|
* from. Null for to-device events.
|
||||||
*/
|
*/
|
||||||
class DecryptionAlgorithm {
|
export class DecryptionAlgorithm {
|
||||||
constructor(params) {
|
constructor(params) {
|
||||||
this._userId = params.userId;
|
this._userId = params.userId;
|
||||||
this._crypto = params.crypto;
|
this._crypto = params.crypto;
|
||||||
@@ -159,8 +158,17 @@ class DecryptionAlgorithm {
|
|||||||
shareKeysWithDevice(keyRequest) {
|
shareKeysWithDevice(keyRequest) {
|
||||||
throw new Error("shareKeysWithDevice not supported for this DecryptionAlgorithm");
|
throw new Error("shareKeysWithDevice not supported for this DecryptionAlgorithm");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retry decrypting all the events from a sender that haven't been
|
||||||
|
* decrypted yet.
|
||||||
|
*
|
||||||
|
* @param {string} senderKey the sender's key
|
||||||
|
*/
|
||||||
|
async retryDecryptionFromSender(senderKey) {
|
||||||
|
// ignore by default
|
||||||
|
}
|
||||||
}
|
}
|
||||||
export {DecryptionAlgorithm}; // https://github.com/jsdoc3/jsdoc/issues/1272
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception thrown when decryption fails
|
* Exception thrown when decryption fails
|
||||||
@@ -173,7 +181,7 @@ export {DecryptionAlgorithm}; // https://github.com/jsdoc3/jsdoc/issues/1272
|
|||||||
*
|
*
|
||||||
* @extends Error
|
* @extends Error
|
||||||
*/
|
*/
|
||||||
class DecryptionError extends Error {
|
export class DecryptionError extends Error {
|
||||||
constructor(code, msg, details) {
|
constructor(code, msg, details) {
|
||||||
super(msg);
|
super(msg);
|
||||||
this.code = code;
|
this.code = code;
|
||||||
@@ -181,7 +189,6 @@ class DecryptionError extends Error {
|
|||||||
this.detailedString = _detailedStringForDecryptionError(this, details);
|
this.detailedString = _detailedStringForDecryptionError(this, details);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export {DecryptionError}; // https://github.com/jsdoc3/jsdoc/issues/1272
|
|
||||||
|
|
||||||
function _detailedStringForDecryptionError(err, details) {
|
function _detailedStringForDecryptionError(err, details) {
|
||||||
let result = err.name + '[msg: ' + err.message;
|
let result = err.name + '[msg: ' + err.message;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2016 OpenMarket Ltd
|
Copyright 2016 OpenMarket Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -13,28 +14,12 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
"use strict";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @module crypto/algorithms
|
* @module crypto/algorithms
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const base = require("./base");
|
import "./olm";
|
||||||
|
import "./megolm";
|
||||||
|
|
||||||
require("./olm");
|
export * from "./base";
|
||||||
require("./megolm");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see module:crypto/algorithms/base.ENCRYPTION_CLASSES
|
|
||||||
*/
|
|
||||||
module.exports.ENCRYPTION_CLASSES = base.ENCRYPTION_CLASSES;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see module:crypto/algorithms/base.DECRYPTION_CLASSES
|
|
||||||
*/
|
|
||||||
module.exports.DECRYPTION_CLASSES = base.DECRYPTION_CLASSES;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see module:crypto/algorithms/base.DecryptionError
|
|
||||||
*/
|
|
||||||
module.exports.DecryptionError = base.DecryptionError;
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
Copyright 2018 New Vector Ltd
|
Copyright 2018 New Vector Ltd
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -14,7 +15,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
"use strict";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines m.olm encryption/decryption
|
* Defines m.olm encryption/decryption
|
||||||
@@ -22,11 +22,19 @@ limitations under the License.
|
|||||||
* @module crypto/algorithms/megolm
|
* @module crypto/algorithms/megolm
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import logger from '../../logger';
|
import {logger} from '../../logger';
|
||||||
|
import * as utils from "../../utils";
|
||||||
|
import {polyfillSuper} from "../../utils";
|
||||||
|
import * as olmlib from "../olmlib";
|
||||||
|
import {
|
||||||
|
DecryptionAlgorithm,
|
||||||
|
DecryptionError,
|
||||||
|
EncryptionAlgorithm,
|
||||||
|
registerAlgorithm,
|
||||||
|
UnknownDeviceError,
|
||||||
|
} from "./base";
|
||||||
|
|
||||||
const utils = require("../../utils");
|
import {WITHHELD_MESSAGES} from '../OlmDevice';
|
||||||
const olmlib = require("../olmlib");
|
|
||||||
const base = require("./base");
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
@@ -47,6 +55,7 @@ function OutboundSessionInfo(sessionId) {
|
|||||||
this.useCount = 0;
|
this.useCount = 0;
|
||||||
this.creationTime = new Date().getTime();
|
this.creationTime = new Date().getTime();
|
||||||
this.sharedWithDevices = {};
|
this.sharedWithDevices = {};
|
||||||
|
this.blockedDevicesNotified = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -84,6 +93,15 @@ OutboundSessionInfo.prototype.markSharedWithDevice = function(
|
|||||||
this.sharedWithDevices[userId][deviceId] = chainIndex;
|
this.sharedWithDevices[userId][deviceId] = chainIndex;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
OutboundSessionInfo.prototype.markNotifiedBlockedDevice = function(
|
||||||
|
userId, deviceId,
|
||||||
|
) {
|
||||||
|
if (!this.blockedDevicesNotified[userId]) {
|
||||||
|
this.blockedDevicesNotified[userId] = {};
|
||||||
|
}
|
||||||
|
this.blockedDevicesNotified[userId][deviceId] = true;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if this session has been shared with devices which it shouldn't
|
* Determine if this session has been shared with devices which it shouldn't
|
||||||
* have been.
|
* have been.
|
||||||
@@ -128,13 +146,13 @@ OutboundSessionInfo.prototype.sharedWithTooManyDevices = function(
|
|||||||
* Megolm encryption implementation
|
* Megolm encryption implementation
|
||||||
*
|
*
|
||||||
* @constructor
|
* @constructor
|
||||||
* @extends {module:crypto/algorithms/base.EncryptionAlgorithm}
|
* @extends {module:crypto/algorithms/EncryptionAlgorithm}
|
||||||
*
|
*
|
||||||
* @param {object} params parameters, as per
|
* @param {object} params parameters, as per
|
||||||
* {@link module:crypto/algorithms/base.EncryptionAlgorithm}
|
* {@link module:crypto/algorithms/EncryptionAlgorithm}
|
||||||
*/
|
*/
|
||||||
function MegolmEncryption(params) {
|
function MegolmEncryption(params) {
|
||||||
base.EncryptionAlgorithm.call(this, params);
|
polyfillSuper(this, EncryptionAlgorithm, params);
|
||||||
|
|
||||||
// the most recent attempt to set up a session. This is used to serialise
|
// the most recent attempt to set up a session. This is used to serialise
|
||||||
// the session setups, so that we have a race-free view of which session we
|
// the session setups, so that we have a race-free view of which session we
|
||||||
@@ -160,17 +178,20 @@ function MegolmEncryption(params) {
|
|||||||
this._sessionRotationPeriodMsgs = params.config.rotation_period_msgs;
|
this._sessionRotationPeriodMsgs = params.config.rotation_period_msgs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
utils.inherits(MegolmEncryption, base.EncryptionAlgorithm);
|
utils.inherits(MegolmEncryption, EncryptionAlgorithm);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*
|
*
|
||||||
* @param {Object} devicesInRoom The devices in this room, indexed by user ID
|
* @param {Object} devicesInRoom The devices in this room, indexed by user ID
|
||||||
|
* @param {Object} blocked The devices that are blocked, indexed by user ID
|
||||||
*
|
*
|
||||||
* @return {module:client.Promise} Promise which resolves to the
|
* @return {module:client.Promise} Promise which resolves to the
|
||||||
* OutboundSessionInfo when setup is complete.
|
* OutboundSessionInfo when setup is complete.
|
||||||
*/
|
*/
|
||||||
MegolmEncryption.prototype._ensureOutboundSession = function(devicesInRoom) {
|
MegolmEncryption.prototype._ensureOutboundSession = async function(
|
||||||
|
devicesInRoom, blocked,
|
||||||
|
) {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
let session;
|
let session;
|
||||||
@@ -237,9 +258,50 @@ MegolmEncryption.prototype._ensureOutboundSession = function(devicesInRoom) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return self._shareKeyWithDevices(
|
const errorDevices = [];
|
||||||
session, shareMap,
|
|
||||||
|
await self._shareKeyWithDevices(
|
||||||
|
session, shareMap, errorDevices,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// are there any new blocked devices that we need to notify?
|
||||||
|
const blockedMap = {};
|
||||||
|
for (const userId in blocked) {
|
||||||
|
if (!blocked.hasOwnProperty(userId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userBlockedDevices = blocked[userId];
|
||||||
|
|
||||||
|
for (const deviceId in userBlockedDevices) {
|
||||||
|
if (!userBlockedDevices.hasOwnProperty(deviceId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!session.blockedDevicesNotified[userId] ||
|
||||||
|
session.blockedDevicesNotified[userId][deviceId] === undefined
|
||||||
|
) {
|
||||||
|
blockedMap[userId] = blockedMap[userId] || [];
|
||||||
|
blockedMap[userId].push(userBlockedDevices[deviceId]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const filteredErrorDevices =
|
||||||
|
await self._olmDevice.filterOutNotifiedErrorDevices(errorDevices);
|
||||||
|
for (const {userId, deviceInfo} of filteredErrorDevices) {
|
||||||
|
blockedMap[userId] = blockedMap[userId] || [];
|
||||||
|
blockedMap[userId].push({
|
||||||
|
code: "m.no_olm",
|
||||||
|
reason: WITHHELD_MESSAGES["m.no_olm"],
|
||||||
|
deviceInfo,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// notify blocked devices that they're blocked
|
||||||
|
await self._notifyBlockedDevices(session, blockedMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
// helper which returns the session prepared by prepareSession
|
// helper which returns the session prepared by prepareSession
|
||||||
@@ -287,6 +349,10 @@ MegolmEncryption.prototype._prepareNewSession = async function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Splits the user device map into multiple chunks to reduce the number of
|
||||||
|
* devices we encrypt to per API call. Also filters out devices we don't have
|
||||||
|
* a session with.
|
||||||
|
*
|
||||||
* @private
|
* @private
|
||||||
*
|
*
|
||||||
* @param {module:crypto/algorithms/megolm.OutboundSessionInfo} session
|
* @param {module:crypto/algorithms/megolm.OutboundSessionInfo} session
|
||||||
@@ -299,12 +365,16 @@ MegolmEncryption.prototype._prepareNewSession = async function() {
|
|||||||
* @param {object<string, module:crypto/deviceinfo[]>} devicesByUser
|
* @param {object<string, module:crypto/deviceinfo[]>} devicesByUser
|
||||||
* map from userid to list of devices
|
* map from userid to list of devices
|
||||||
*
|
*
|
||||||
|
* @param {array<object>} errorDevices
|
||||||
|
* array that will be populated with the devices that can't get an
|
||||||
|
* olm session for
|
||||||
|
*
|
||||||
* @return {array<object<userid, deviceInfo>>}
|
* @return {array<object<userid, deviceInfo>>}
|
||||||
*/
|
*/
|
||||||
MegolmEncryption.prototype._splitUserDeviceMap = function(
|
MegolmEncryption.prototype._splitUserDeviceMap = function(
|
||||||
session, chainIndex, devicemap, devicesByUser,
|
session, chainIndex, devicemap, devicesByUser, errorDevices,
|
||||||
) {
|
) {
|
||||||
const maxToDeviceMessagesPerRequest = 20;
|
const maxUsersPerRequest = 20;
|
||||||
|
|
||||||
// use an array where the slices of a content map gets stored
|
// use an array where the slices of a content map gets stored
|
||||||
const mapSlices = [];
|
const mapSlices = [];
|
||||||
@@ -334,6 +404,8 @@ MegolmEncryption.prototype._splitUserDeviceMap = function(
|
|||||||
// to claim a one-time-key for dead devices on every message.
|
// to claim a one-time-key for dead devices on every message.
|
||||||
session.markSharedWithDevice(userId, deviceId, chainIndex);
|
session.markSharedWithDevice(userId, deviceId, chainIndex);
|
||||||
|
|
||||||
|
errorDevices.push({userId, deviceInfo});
|
||||||
|
|
||||||
// ensureOlmSessionsForUsers has already done the logging,
|
// ensureOlmSessionsForUsers has already done the logging,
|
||||||
// so just skip it.
|
// so just skip it.
|
||||||
continue;
|
continue;
|
||||||
@@ -343,11 +415,6 @@ MegolmEncryption.prototype._splitUserDeviceMap = function(
|
|||||||
"share keys with device " + userId + ":" + deviceId,
|
"share keys with device " + userId + ":" + deviceId,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (entriesInCurrentSlice > maxToDeviceMessagesPerRequest) {
|
|
||||||
// the current slice is filled up. Start inserting into the next slice
|
|
||||||
entriesInCurrentSlice = 0;
|
|
||||||
currentSliceId++;
|
|
||||||
}
|
|
||||||
if (!mapSlices[currentSliceId]) {
|
if (!mapSlices[currentSliceId]) {
|
||||||
mapSlices[currentSliceId] = [];
|
mapSlices[currentSliceId] = [];
|
||||||
}
|
}
|
||||||
@@ -359,6 +426,61 @@ MegolmEncryption.prototype._splitUserDeviceMap = function(
|
|||||||
|
|
||||||
entriesInCurrentSlice++;
|
entriesInCurrentSlice++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We do this in the per-user loop as we prefer that all messages to the
|
||||||
|
// same user end up in the same API call to make it easier for the
|
||||||
|
// server (e.g. only have to send one EDU if a remote user, etc). This
|
||||||
|
// does mean that if a user has many devices we may go over the desired
|
||||||
|
// limit, but its not a hard limit so that is fine.
|
||||||
|
if (entriesInCurrentSlice > maxUsersPerRequest) {
|
||||||
|
// the current slice is filled up. Start inserting into the next slice
|
||||||
|
entriesInCurrentSlice = 0;
|
||||||
|
currentSliceId++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mapSlices;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits the user device map into multiple chunks to reduce the number of
|
||||||
|
* devices we encrypt to per API call.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*
|
||||||
|
* @param {object} devicesByUser map from userid to list of devices
|
||||||
|
*
|
||||||
|
* @return {array<array<object>>} the blocked devices, split into chunks
|
||||||
|
*/
|
||||||
|
MegolmEncryption.prototype._splitBlockedDevices = function(devicesByUser) {
|
||||||
|
const maxUsersPerRequest = 20;
|
||||||
|
|
||||||
|
// use an array where the slices of a content map gets stored
|
||||||
|
let currentSlice = [];
|
||||||
|
const mapSlices = [currentSlice];
|
||||||
|
|
||||||
|
for (const userId of Object.keys(devicesByUser)) {
|
||||||
|
const userBlockedDevicesToShareWith = devicesByUser[userId];
|
||||||
|
|
||||||
|
for (const blockedInfo of userBlockedDevicesToShareWith) {
|
||||||
|
currentSlice.push({
|
||||||
|
userId: userId,
|
||||||
|
blockedInfo: blockedInfo,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// We do this in the per-user loop as we prefer that all messages to the
|
||||||
|
// same user end up in the same API call to make it easier for the
|
||||||
|
// server (e.g. only have to send one EDU if a remote user, etc). This
|
||||||
|
// does mean that if a user has many devices we may go over the desired
|
||||||
|
// limit, but its not a hard limit so that is fine.
|
||||||
|
if (currentSlice.length > maxUsersPerRequest) {
|
||||||
|
// the current slice is filled up. Start inserting into the next slice
|
||||||
|
currentSlice = [];
|
||||||
|
mapSlices.push(currentSlice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (currentSlice.length === 0) {
|
||||||
|
mapSlices.pop();
|
||||||
}
|
}
|
||||||
return mapSlices;
|
return mapSlices;
|
||||||
};
|
};
|
||||||
@@ -381,15 +503,15 @@ MegolmEncryption.prototype._splitUserDeviceMap = function(
|
|||||||
MegolmEncryption.prototype._encryptAndSendKeysToDevices = function(
|
MegolmEncryption.prototype._encryptAndSendKeysToDevices = function(
|
||||||
session, chainIndex, userDeviceMap, payload,
|
session, chainIndex, userDeviceMap, payload,
|
||||||
) {
|
) {
|
||||||
|
const contentMap = {};
|
||||||
|
|
||||||
|
const promises = [];
|
||||||
|
for (let i = 0; i < userDeviceMap.length; i++) {
|
||||||
const encryptedContent = {
|
const encryptedContent = {
|
||||||
algorithm: olmlib.OLM_ALGORITHM,
|
algorithm: olmlib.OLM_ALGORITHM,
|
||||||
sender_key: this._olmDevice.deviceCurve25519Key,
|
sender_key: this._olmDevice.deviceCurve25519Key,
|
||||||
ciphertext: {},
|
ciphertext: {},
|
||||||
};
|
};
|
||||||
const contentMap = {};
|
|
||||||
|
|
||||||
const promises = [];
|
|
||||||
for (let i = 0; i < userDeviceMap.length; i++) {
|
|
||||||
const val = userDeviceMap[i];
|
const val = userDeviceMap[i];
|
||||||
const userId = val.userId;
|
const userId = val.userId;
|
||||||
const deviceInfo = val.deviceInfo;
|
const deviceInfo = val.deviceInfo;
|
||||||
@@ -427,6 +549,53 @@ MegolmEncryption.prototype._encryptAndSendKeysToDevices = function(
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*
|
||||||
|
* @param {module:crypto/algorithms/megolm.OutboundSessionInfo} session
|
||||||
|
*
|
||||||
|
* @param {array<object>} userDeviceMap list of blocked devices to notify
|
||||||
|
*
|
||||||
|
* @param {object} payload fields to include in the notification payload
|
||||||
|
*
|
||||||
|
* @return {module:client.Promise} Promise which resolves once the notifications
|
||||||
|
* for the given userDeviceMap is generated and has been sent.
|
||||||
|
*/
|
||||||
|
MegolmEncryption.prototype._sendBlockedNotificationsToDevices = async function(
|
||||||
|
session, userDeviceMap, payload,
|
||||||
|
) {
|
||||||
|
const contentMap = {};
|
||||||
|
|
||||||
|
for (const val of userDeviceMap) {
|
||||||
|
const userId = val.userId;
|
||||||
|
const blockedInfo = val.blockedInfo;
|
||||||
|
const deviceInfo = blockedInfo.deviceInfo;
|
||||||
|
const deviceId = deviceInfo.deviceId;
|
||||||
|
|
||||||
|
const message = Object.assign({}, payload);
|
||||||
|
message.code = blockedInfo.code;
|
||||||
|
message.reason = blockedInfo.reason;
|
||||||
|
if (message.code === "m.no_olm") {
|
||||||
|
delete message.room_id;
|
||||||
|
delete message.session_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!contentMap[userId]) {
|
||||||
|
contentMap[userId] = {};
|
||||||
|
}
|
||||||
|
contentMap[userId][deviceId] = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this._baseApis.sendToDevice("org.matrix.room_key.withheld", contentMap);
|
||||||
|
|
||||||
|
// store that we successfully uploaded the keys of the current slice
|
||||||
|
for (const userId of Object.keys(contentMap)) {
|
||||||
|
for (const deviceId of Object.keys(contentMap[userId])) {
|
||||||
|
session.markNotifiedBlockedDevice(userId, deviceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Re-shares a megolm session key with devices if the key has already been
|
* Re-shares a megolm session key with devices if the key has already been
|
||||||
* sent to them.
|
* sent to them.
|
||||||
@@ -523,8 +692,14 @@ MegolmEncryption.prototype.reshareKeyWithDevice = async function(
|
|||||||
*
|
*
|
||||||
* @param {object<string, module:crypto/deviceinfo[]>} devicesByUser
|
* @param {object<string, module:crypto/deviceinfo[]>} devicesByUser
|
||||||
* map from userid to list of devices
|
* map from userid to list of devices
|
||||||
|
*
|
||||||
|
* @param {array<object>} errorDevices
|
||||||
|
* array that will be populated with the devices that we can't get an
|
||||||
|
* olm session for
|
||||||
*/
|
*/
|
||||||
MegolmEncryption.prototype._shareKeyWithDevices = async function(session, devicesByUser) {
|
MegolmEncryption.prototype._shareKeyWithDevices = async function(
|
||||||
|
session, devicesByUser, errorDevices,
|
||||||
|
) {
|
||||||
const key = this._olmDevice.getOutboundGroupSessionKey(session.sessionId);
|
const key = this._olmDevice.getOutboundGroupSessionKey(session.sessionId);
|
||||||
const payload = {
|
const payload = {
|
||||||
type: "m.room_key",
|
type: "m.room_key",
|
||||||
@@ -542,7 +717,7 @@ MegolmEncryption.prototype._shareKeyWithDevices = async function(session, device
|
|||||||
);
|
);
|
||||||
|
|
||||||
const userDeviceMaps = this._splitUserDeviceMap(
|
const userDeviceMaps = this._splitUserDeviceMap(
|
||||||
session, key.chain_index, devicemap, devicesByUser,
|
session, key.chain_index, devicemap, devicesByUser, errorDevices,
|
||||||
);
|
);
|
||||||
|
|
||||||
for (let i = 0; i < userDeviceMaps.length; i++) {
|
for (let i = 0; i < userDeviceMaps.length; i++) {
|
||||||
@@ -561,6 +736,42 @@ MegolmEncryption.prototype._shareKeyWithDevices = async function(session, device
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify blocked devices that they have been blocked.
|
||||||
|
*
|
||||||
|
* @param {module:crypto/algorithms/megolm.OutboundSessionInfo} session
|
||||||
|
*
|
||||||
|
* @param {object<string, object>} devicesByUser
|
||||||
|
* map from userid to device ID to blocked data
|
||||||
|
*/
|
||||||
|
MegolmEncryption.prototype._notifyBlockedDevices = async function(
|
||||||
|
session, devicesByUser,
|
||||||
|
) {
|
||||||
|
const payload = {
|
||||||
|
room_id: this._roomId,
|
||||||
|
session_id: session.sessionId,
|
||||||
|
algorithm: olmlib.MEGOLM_ALGORITHM,
|
||||||
|
sender_key: this._olmDevice.deviceCurve25519Key,
|
||||||
|
};
|
||||||
|
|
||||||
|
const userDeviceMaps = this._splitBlockedDevices(devicesByUser);
|
||||||
|
|
||||||
|
for (let i = 0; i < userDeviceMaps.length; i++) {
|
||||||
|
try {
|
||||||
|
await this._sendBlockedNotificationsToDevices(
|
||||||
|
session, userDeviceMaps[i], payload,
|
||||||
|
);
|
||||||
|
logger.log(`Completed blacklist notification for ${session.sessionId} `
|
||||||
|
+ `in ${this._roomId} (slice ${i + 1}/${userDeviceMaps.length})`);
|
||||||
|
} catch (e) {
|
||||||
|
logger.log(`blacklist notification for ${session.sessionId} in `
|
||||||
|
+ `${this._roomId} (slice ${i + 1}/${userDeviceMaps.length}) failed`);
|
||||||
|
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*
|
*
|
||||||
@@ -570,17 +781,19 @@ MegolmEncryption.prototype._shareKeyWithDevices = async function(session, device
|
|||||||
*
|
*
|
||||||
* @return {module:client.Promise} Promise which resolves to the new event body
|
* @return {module:client.Promise} Promise which resolves to the new event body
|
||||||
*/
|
*/
|
||||||
MegolmEncryption.prototype.encryptMessage = function(room, eventType, content) {
|
MegolmEncryption.prototype.encryptMessage = async function(room, eventType, content) {
|
||||||
const self = this;
|
const self = this;
|
||||||
logger.log(`Starting to encrypt event for ${this._roomId}`);
|
logger.log(`Starting to encrypt event for ${this._roomId}`);
|
||||||
|
|
||||||
return this._getDevicesInRoom(room).then(function(devicesInRoom) {
|
const [devicesInRoom, blocked] = await this._getDevicesInRoom(room);
|
||||||
|
|
||||||
// check if any of these devices are not yet known to the user.
|
// check if any of these devices are not yet known to the user.
|
||||||
// if so, warn the user so they can verify or ignore.
|
// if so, warn the user so they can verify or ignore.
|
||||||
|
if (this._crypto.getGlobalErrorOnUnknownDevices()) {
|
||||||
self._checkForUnknownDevices(devicesInRoom);
|
self._checkForUnknownDevices(devicesInRoom);
|
||||||
|
}
|
||||||
|
|
||||||
return self._ensureOutboundSession(devicesInRoom);
|
const session = await self._ensureOutboundSession(devicesInRoom, blocked);
|
||||||
}).then(function(session) {
|
|
||||||
const payloadJson = {
|
const payloadJson = {
|
||||||
room_id: self._roomId,
|
room_id: self._roomId,
|
||||||
type: eventType,
|
type: eventType,
|
||||||
@@ -605,7 +818,6 @@ MegolmEncryption.prototype.encryptMessage = function(room, eventType, content) {
|
|||||||
|
|
||||||
session.useCount++;
|
session.useCount++;
|
||||||
return encryptedContent;
|
return encryptedContent;
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -643,7 +855,7 @@ MegolmEncryption.prototype._checkForUnknownDevices = function(devicesInRoom) {
|
|||||||
|
|
||||||
if (Object.keys(unknownDevices).length) {
|
if (Object.keys(unknownDevices).length) {
|
||||||
// it'd be kind to pass unknownDevices up to the user in this error
|
// it'd be kind to pass unknownDevices up to the user in this error
|
||||||
throw new base.UnknownDeviceError(
|
throw new UnknownDeviceError(
|
||||||
"This room contains unknown devices which have not been verified. " +
|
"This room contains unknown devices which have not been verified. " +
|
||||||
"We strongly recommend you verify them before continuing.", unknownDevices);
|
"We strongly recommend you verify them before continuing.", unknownDevices);
|
||||||
}
|
}
|
||||||
@@ -654,8 +866,11 @@ MegolmEncryption.prototype._checkForUnknownDevices = function(devicesInRoom) {
|
|||||||
*
|
*
|
||||||
* @param {module:models/room} room
|
* @param {module:models/room} room
|
||||||
*
|
*
|
||||||
* @return {module:client.Promise} Promise which resolves to a map
|
* @return {module:client.Promise} Promise which resolves to an array whose
|
||||||
* from userId to deviceId to deviceInfo
|
* first element is a map from userId to deviceId to deviceInfo indicating
|
||||||
|
* the devices that messages should be encrypted to, and whose second
|
||||||
|
* element is a map from userId to deviceId to data indicating the devices
|
||||||
|
* that are in the room but that have been blocked
|
||||||
*/
|
*/
|
||||||
MegolmEncryption.prototype._getDevicesInRoom = async function(room) {
|
MegolmEncryption.prototype._getDevicesInRoom = async function(room) {
|
||||||
const members = await room.getEncryptionTargetMembers();
|
const members = await room.getEncryptionTargetMembers();
|
||||||
@@ -676,6 +891,7 @@ MegolmEncryption.prototype._getDevicesInRoom = async function(room) {
|
|||||||
// using all the device_lists changes and left fields.
|
// 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/riot-web/issues/2305 for details.
|
||||||
const devices = await this._crypto.downloadKeys(roomMembers, false);
|
const devices = await this._crypto.downloadKeys(roomMembers, false);
|
||||||
|
const blocked = {};
|
||||||
// remove any blocked devices
|
// remove any blocked devices
|
||||||
for (const userId in devices) {
|
for (const userId in devices) {
|
||||||
if (!devices.hasOwnProperty(userId)) {
|
if (!devices.hasOwnProperty(userId)) {
|
||||||
@@ -691,25 +907,39 @@ MegolmEncryption.prototype._getDevicesInRoom = async function(room) {
|
|||||||
if (userDevices[deviceId].isBlocked() ||
|
if (userDevices[deviceId].isBlocked() ||
|
||||||
(userDevices[deviceId].isUnverified() && isBlacklisting)
|
(userDevices[deviceId].isUnverified() && isBlacklisting)
|
||||||
) {
|
) {
|
||||||
|
if (!blocked[userId]) {
|
||||||
|
blocked[userId] = {};
|
||||||
|
}
|
||||||
|
const blockedInfo = userDevices[deviceId].isBlocked()
|
||||||
|
? {
|
||||||
|
code: "m.blacklisted",
|
||||||
|
reason: WITHHELD_MESSAGES["m.blacklisted"],
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
code: "m.unverified",
|
||||||
|
reason: WITHHELD_MESSAGES["m.unverified"],
|
||||||
|
};
|
||||||
|
blockedInfo.deviceInfo = userDevices[deviceId];
|
||||||
|
blocked[userId][deviceId] = blockedInfo;
|
||||||
delete userDevices[deviceId];
|
delete userDevices[deviceId];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return devices;
|
return [devices, blocked];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Megolm decryption implementation
|
* Megolm decryption implementation
|
||||||
*
|
*
|
||||||
* @constructor
|
* @constructor
|
||||||
* @extends {module:crypto/algorithms/base.DecryptionAlgorithm}
|
* @extends {module:crypto/algorithms/DecryptionAlgorithm}
|
||||||
*
|
*
|
||||||
* @param {object} params parameters, as per
|
* @param {object} params parameters, as per
|
||||||
* {@link module:crypto/algorithms/base.DecryptionAlgorithm}
|
* {@link module:crypto/algorithms/DecryptionAlgorithm}
|
||||||
*/
|
*/
|
||||||
function MegolmDecryption(params) {
|
function MegolmDecryption(params) {
|
||||||
base.DecryptionAlgorithm.call(this, params);
|
polyfillSuper(this, DecryptionAlgorithm, params);
|
||||||
|
|
||||||
// events which we couldn't decrypt due to unknown sessions / indexes: map from
|
// events which we couldn't decrypt due to unknown sessions / indexes: map from
|
||||||
// senderKey|sessionId to Set of MatrixEvents
|
// senderKey|sessionId to Set of MatrixEvents
|
||||||
@@ -718,7 +948,12 @@ function MegolmDecryption(params) {
|
|||||||
// this gets stubbed out by the unit tests.
|
// this gets stubbed out by the unit tests.
|
||||||
this.olmlib = olmlib;
|
this.olmlib = olmlib;
|
||||||
}
|
}
|
||||||
utils.inherits(MegolmDecryption, base.DecryptionAlgorithm);
|
utils.inherits(MegolmDecryption, DecryptionAlgorithm);
|
||||||
|
|
||||||
|
const PROBLEM_DESCRIPTIONS = {
|
||||||
|
no_olm: "The sender was unable to establish a secure channel.",
|
||||||
|
unknown: "The secure channel with the sender was corrupted.",
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
@@ -736,7 +971,7 @@ MegolmDecryption.prototype.decryptEvent = async function(event) {
|
|||||||
if (!content.sender_key || !content.session_id ||
|
if (!content.sender_key || !content.session_id ||
|
||||||
!content.ciphertext
|
!content.ciphertext
|
||||||
) {
|
) {
|
||||||
throw new base.DecryptionError(
|
throw new DecryptionError(
|
||||||
"MEGOLM_MISSING_FIELDS",
|
"MEGOLM_MISSING_FIELDS",
|
||||||
"Missing fields in input",
|
"Missing fields in input",
|
||||||
);
|
);
|
||||||
@@ -756,6 +991,11 @@ MegolmDecryption.prototype.decryptEvent = async function(event) {
|
|||||||
event.getId(), event.getTs(),
|
event.getId(), event.getTs(),
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
if (e.name === "DecryptionError") {
|
||||||
|
// re-throw decryption errors as-is
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
let errorCode = "OLM_DECRYPT_GROUP_MESSAGE_ERROR";
|
let errorCode = "OLM_DECRYPT_GROUP_MESSAGE_ERROR";
|
||||||
|
|
||||||
if (e && e.message === 'OLM.UNKNOWN_MESSAGE_INDEX') {
|
if (e && e.message === 'OLM.UNKNOWN_MESSAGE_INDEX') {
|
||||||
@@ -764,7 +1004,7 @@ MegolmDecryption.prototype.decryptEvent = async function(event) {
|
|||||||
errorCode = 'OLM_UNKNOWN_MESSAGE_INDEX';
|
errorCode = 'OLM_UNKNOWN_MESSAGE_INDEX';
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new base.DecryptionError(
|
throw new DecryptionError(
|
||||||
errorCode,
|
errorCode,
|
||||||
e ? e.toString() : "Unknown Error: Error is undefined", {
|
e ? e.toString() : "Unknown Error: Error is undefined", {
|
||||||
session: content.sender_key + '|' + content.session_id,
|
session: content.sender_key + '|' + content.session_id,
|
||||||
@@ -781,7 +1021,29 @@ MegolmDecryption.prototype.decryptEvent = async function(event) {
|
|||||||
// event is still in the pending list; if not, a retry will have been
|
// event is still in the pending list; if not, a retry will have been
|
||||||
// scheduled, so we needn't send out the request here.)
|
// scheduled, so we needn't send out the request here.)
|
||||||
this._requestKeysForEvent(event);
|
this._requestKeysForEvent(event);
|
||||||
throw new base.DecryptionError(
|
|
||||||
|
// See if there was a problem with the olm session at the time the
|
||||||
|
// event was sent. Use a fuzz factor of 2 minutes.
|
||||||
|
const problem = await this._olmDevice.sessionMayHaveProblems(
|
||||||
|
content.sender_key, event.getTs() - 120000,
|
||||||
|
);
|
||||||
|
if (problem) {
|
||||||
|
let problemDescription = PROBLEM_DESCRIPTIONS[problem.type]
|
||||||
|
|| PROBLEM_DESCRIPTIONS.unknown;
|
||||||
|
if (problem.fixed) {
|
||||||
|
problemDescription +=
|
||||||
|
" Trying to create a new secure channel and re-requesting the keys.";
|
||||||
|
}
|
||||||
|
throw new DecryptionError(
|
||||||
|
"MEGOLM_UNKNOWN_INBOUND_SESSION_ID",
|
||||||
|
problemDescription,
|
||||||
|
{
|
||||||
|
session: content.sender_key + '|' + content.session_id,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new DecryptionError(
|
||||||
"MEGOLM_UNKNOWN_INBOUND_SESSION_ID",
|
"MEGOLM_UNKNOWN_INBOUND_SESSION_ID",
|
||||||
"The sender's device has not sent us the keys for this message.",
|
"The sender's device has not sent us the keys for this message.",
|
||||||
{
|
{
|
||||||
@@ -800,7 +1062,7 @@ MegolmDecryption.prototype.decryptEvent = async function(event) {
|
|||||||
// (this is somewhat redundant, since the megolm session is scoped to the
|
// (this is somewhat redundant, since the megolm session is scoped to the
|
||||||
// room, so neither the sender nor a MITM can lie about the room_id).
|
// room, so neither the sender nor a MITM can lie about the room_id).
|
||||||
if (payload.room_id !== event.getRoomId()) {
|
if (payload.room_id !== event.getRoomId()) {
|
||||||
throw new base.DecryptionError(
|
throw new DecryptionError(
|
||||||
"MEGOLM_BAD_ROOM",
|
"MEGOLM_BAD_ROOM",
|
||||||
"Message intended for room " + payload.room_id,
|
"Message intended for room " + payload.room_id,
|
||||||
);
|
);
|
||||||
@@ -836,11 +1098,16 @@ MegolmDecryption.prototype._requestKeysForEvent = function(event) {
|
|||||||
*/
|
*/
|
||||||
MegolmDecryption.prototype._addEventToPendingList = function(event) {
|
MegolmDecryption.prototype._addEventToPendingList = function(event) {
|
||||||
const content = event.getWireContent();
|
const content = event.getWireContent();
|
||||||
const k = content.sender_key + "|" + content.session_id;
|
const senderKey = content.sender_key;
|
||||||
if (!this._pendingEvents[k]) {
|
const sessionId = content.session_id;
|
||||||
this._pendingEvents[k] = new Set();
|
if (!this._pendingEvents[senderKey]) {
|
||||||
|
this._pendingEvents[senderKey] = new Map();
|
||||||
}
|
}
|
||||||
this._pendingEvents[k].add(event);
|
const senderPendingEvents = this._pendingEvents[senderKey];
|
||||||
|
if (!senderPendingEvents.has(sessionId)) {
|
||||||
|
senderPendingEvents.set(sessionId, new Set());
|
||||||
|
}
|
||||||
|
senderPendingEvents.get(sessionId).add(event);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -852,14 +1119,20 @@ MegolmDecryption.prototype._addEventToPendingList = function(event) {
|
|||||||
*/
|
*/
|
||||||
MegolmDecryption.prototype._removeEventFromPendingList = function(event) {
|
MegolmDecryption.prototype._removeEventFromPendingList = function(event) {
|
||||||
const content = event.getWireContent();
|
const content = event.getWireContent();
|
||||||
const k = content.sender_key + "|" + content.session_id;
|
const senderKey = content.sender_key;
|
||||||
if (!this._pendingEvents[k]) {
|
const sessionId = content.session_id;
|
||||||
|
const senderPendingEvents = this._pendingEvents[senderKey];
|
||||||
|
const pendingEvents = senderPendingEvents && senderPendingEvents.get(sessionId);
|
||||||
|
if (!pendingEvents) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._pendingEvents[k].delete(event);
|
pendingEvents.delete(event);
|
||||||
if (this._pendingEvents[k].size === 0) {
|
if (pendingEvents.size === 0) {
|
||||||
delete this._pendingEvents[k];
|
senderPendingEvents.delete(senderKey);
|
||||||
|
}
|
||||||
|
if (senderPendingEvents.size === 0) {
|
||||||
|
delete this._pendingEvents[senderKey];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -963,6 +1236,78 @@ MegolmDecryption.prototype.onRoomKeyEvent = function(event) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*
|
||||||
|
* @param {module:models/event.MatrixEvent} event key event
|
||||||
|
*/
|
||||||
|
MegolmDecryption.prototype.onRoomKeyWithheldEvent = async function(event) {
|
||||||
|
const content = event.getContent();
|
||||||
|
const senderKey = content.sender_key;
|
||||||
|
|
||||||
|
if (content.code === "m.no_olm") {
|
||||||
|
const sender = event.getSender();
|
||||||
|
// if the sender says that they haven't been able to establish an olm
|
||||||
|
// session, let's proactively establish one
|
||||||
|
|
||||||
|
// Note: after we record that the olm session has had a problem, we
|
||||||
|
// trigger retrying decryption for all the messages from the sender's
|
||||||
|
// key, so that we can update the error message to indicate the olm
|
||||||
|
// session problem.
|
||||||
|
|
||||||
|
if (await this._olmDevice.getSessionIdForDevice(senderKey)) {
|
||||||
|
// a session has already been established, so we don't need to
|
||||||
|
// create a new one.
|
||||||
|
await this._olmDevice.recordSessionProblem(senderKey, "no_olm", true);
|
||||||
|
this.retryDecryptionFromSender(senderKey);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const device = this._crypto._deviceList.getDeviceByIdentityKey(
|
||||||
|
content.algorithm, senderKey,
|
||||||
|
);
|
||||||
|
if (!device) {
|
||||||
|
logger.info(
|
||||||
|
"Couldn't find device for identity key " + senderKey +
|
||||||
|
": not establishing session",
|
||||||
|
);
|
||||||
|
await this._olmDevice.recordSessionProblem(senderKey, "no_olm", false);
|
||||||
|
this.retryDecryptionFromSender(senderKey);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await olmlib.ensureOlmSessionsForDevices(
|
||||||
|
this._olmDevice, this._baseApis, {[sender]: [device]}, false,
|
||||||
|
);
|
||||||
|
const encryptedContent = {
|
||||||
|
algorithm: olmlib.OLM_ALGORITHM,
|
||||||
|
sender_key: this._olmDevice.deviceCurve25519Key,
|
||||||
|
ciphertext: {},
|
||||||
|
};
|
||||||
|
await olmlib.encryptMessageForDevice(
|
||||||
|
encryptedContent.ciphertext,
|
||||||
|
this._userId,
|
||||||
|
this._deviceId,
|
||||||
|
this._olmDevice,
|
||||||
|
sender,
|
||||||
|
device,
|
||||||
|
{type: "m.dummy"},
|
||||||
|
);
|
||||||
|
|
||||||
|
await this._olmDevice.recordSessionProblem(senderKey, "no_olm", true);
|
||||||
|
this.retryDecryptionFromSender(senderKey);
|
||||||
|
|
||||||
|
await this._baseApis.sendToDevice("m.room.encrypted", {
|
||||||
|
[sender]: {
|
||||||
|
[device.deviceId]: encryptedContent,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await this._olmDevice.addInboundGroupSessionWithheld(
|
||||||
|
content.room_id, senderKey, content.session_id, content.code,
|
||||||
|
content.reason,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
@@ -1106,13 +1451,20 @@ MegolmDecryption.prototype.importRoomKey = function(session) {
|
|||||||
* @return {Boolean} whether all messages were successfully decrypted
|
* @return {Boolean} whether all messages were successfully decrypted
|
||||||
*/
|
*/
|
||||||
MegolmDecryption.prototype._retryDecryption = async function(senderKey, sessionId) {
|
MegolmDecryption.prototype._retryDecryption = async function(senderKey, sessionId) {
|
||||||
const k = senderKey + "|" + sessionId;
|
const senderPendingEvents = this._pendingEvents[senderKey];
|
||||||
const pending = this._pendingEvents[k];
|
if (!senderPendingEvents) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pending = senderPendingEvents.get(sessionId);
|
||||||
if (!pending) {
|
if (!pending) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
delete this._pendingEvents[k];
|
pending.delete(sessionId);
|
||||||
|
if (pending.size === 0) {
|
||||||
|
this._pendingEvents[senderKey];
|
||||||
|
}
|
||||||
|
|
||||||
await Promise.all([...pending].map(async (ev) => {
|
await Promise.all([...pending].map(async (ev) => {
|
||||||
try {
|
try {
|
||||||
@@ -1122,9 +1474,34 @@ MegolmDecryption.prototype._retryDecryption = async function(senderKey, sessionI
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return !this._pendingEvents[k];
|
// ev.attemptDecryption will re-add to this._pendingEvents if an event
|
||||||
|
// couldn't be decrypted
|
||||||
|
return !((this._pendingEvents[senderKey] || {})[sessionId]);
|
||||||
};
|
};
|
||||||
|
|
||||||
base.registerAlgorithm(
|
MegolmDecryption.prototype.retryDecryptionFromSender = async function(senderKey) {
|
||||||
|
const senderPendingEvents = this._pendingEvents[senderKey];
|
||||||
|
logger.warn(senderPendingEvents);
|
||||||
|
if (!senderPendingEvents) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete this._pendingEvents[senderKey];
|
||||||
|
|
||||||
|
await Promise.all([...senderPendingEvents].map(async ([_sessionId, pending]) => {
|
||||||
|
await Promise.all([...pending].map(async (ev) => {
|
||||||
|
try {
|
||||||
|
logger.warn(ev.getId());
|
||||||
|
await ev.attemptDecryption(this._crypto);
|
||||||
|
} catch (e) {
|
||||||
|
// don't die if something goes wrong
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}));
|
||||||
|
|
||||||
|
return !this._pendingEvents[senderKey];
|
||||||
|
};
|
||||||
|
|
||||||
|
registerAlgorithm(
|
||||||
olmlib.MEGOLM_ALGORITHM, MegolmEncryption, MegolmDecryption,
|
olmlib.MEGOLM_ALGORITHM, MegolmEncryption, MegolmDecryption,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -13,36 +13,42 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
"use strict";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines m.olm encryption/decryption
|
* Defines m.olm encryption/decryption
|
||||||
*
|
*
|
||||||
* @module crypto/algorithms/olm
|
* @module crypto/algorithms/olm
|
||||||
*/
|
*/
|
||||||
import logger from '../../logger';
|
|
||||||
const utils = require("../../utils");
|
|
||||||
const olmlib = require("../olmlib");
|
|
||||||
const DeviceInfo = require("../deviceinfo");
|
|
||||||
const DeviceVerification = DeviceInfo.DeviceVerification;
|
|
||||||
|
|
||||||
const base = require("./base");
|
import {logger} from '../../logger';
|
||||||
|
import * as utils from "../../utils";
|
||||||
|
import {polyfillSuper} from "../../utils";
|
||||||
|
import * as olmlib from "../olmlib";
|
||||||
|
import {DeviceInfo} from "../deviceinfo";
|
||||||
|
import {
|
||||||
|
DecryptionAlgorithm,
|
||||||
|
DecryptionError,
|
||||||
|
EncryptionAlgorithm,
|
||||||
|
registerAlgorithm,
|
||||||
|
} from "./base";
|
||||||
|
|
||||||
|
const DeviceVerification = DeviceInfo.DeviceVerification;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Olm encryption implementation
|
* Olm encryption implementation
|
||||||
*
|
*
|
||||||
* @constructor
|
* @constructor
|
||||||
* @extends {module:crypto/algorithms/base.EncryptionAlgorithm}
|
* @extends {module:crypto/algorithms/EncryptionAlgorithm}
|
||||||
*
|
*
|
||||||
* @param {object} params parameters, as per
|
* @param {object} params parameters, as per
|
||||||
* {@link module:crypto/algorithms/base.EncryptionAlgorithm}
|
* {@link module:crypto/algorithms/EncryptionAlgorithm}
|
||||||
*/
|
*/
|
||||||
function OlmEncryption(params) {
|
function OlmEncryption(params) {
|
||||||
base.EncryptionAlgorithm.call(this, params);
|
polyfillSuper(this, EncryptionAlgorithm, params);
|
||||||
this._sessionPrepared = false;
|
this._sessionPrepared = false;
|
||||||
this._prepPromise = null;
|
this._prepPromise = null;
|
||||||
}
|
}
|
||||||
utils.inherits(OlmEncryption, base.EncryptionAlgorithm);
|
utils.inherits(OlmEncryption, EncryptionAlgorithm);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
@@ -143,14 +149,14 @@ OlmEncryption.prototype.encryptMessage = async function(room, eventType, content
|
|||||||
* Olm decryption implementation
|
* Olm decryption implementation
|
||||||
*
|
*
|
||||||
* @constructor
|
* @constructor
|
||||||
* @extends {module:crypto/algorithms/base.DecryptionAlgorithm}
|
* @extends {module:crypto/algorithms/DecryptionAlgorithm}
|
||||||
* @param {object} params parameters, as per
|
* @param {object} params parameters, as per
|
||||||
* {@link module:crypto/algorithms/base.DecryptionAlgorithm}
|
* {@link module:crypto/algorithms/DecryptionAlgorithm}
|
||||||
*/
|
*/
|
||||||
function OlmDecryption(params) {
|
function OlmDecryption(params) {
|
||||||
base.DecryptionAlgorithm.call(this, params);
|
polyfillSuper(this, DecryptionAlgorithm, params);
|
||||||
}
|
}
|
||||||
utils.inherits(OlmDecryption, base.DecryptionAlgorithm);
|
utils.inherits(OlmDecryption, DecryptionAlgorithm);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
@@ -168,14 +174,14 @@ OlmDecryption.prototype.decryptEvent = async function(event) {
|
|||||||
const ciphertext = content.ciphertext;
|
const ciphertext = content.ciphertext;
|
||||||
|
|
||||||
if (!ciphertext) {
|
if (!ciphertext) {
|
||||||
throw new base.DecryptionError(
|
throw new DecryptionError(
|
||||||
"OLM_MISSING_CIPHERTEXT",
|
"OLM_MISSING_CIPHERTEXT",
|
||||||
"Missing ciphertext",
|
"Missing ciphertext",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(this._olmDevice.deviceCurve25519Key in ciphertext)) {
|
if (!(this._olmDevice.deviceCurve25519Key in ciphertext)) {
|
||||||
throw new base.DecryptionError(
|
throw new DecryptionError(
|
||||||
"OLM_NOT_INCLUDED_IN_RECIPIENTS",
|
"OLM_NOT_INCLUDED_IN_RECIPIENTS",
|
||||||
"Not included in recipients",
|
"Not included in recipients",
|
||||||
);
|
);
|
||||||
@@ -186,7 +192,7 @@ OlmDecryption.prototype.decryptEvent = async function(event) {
|
|||||||
try {
|
try {
|
||||||
payloadString = await this._decryptMessage(deviceKey, message);
|
payloadString = await this._decryptMessage(deviceKey, message);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new base.DecryptionError(
|
throw new DecryptionError(
|
||||||
"OLM_BAD_ENCRYPTED_MESSAGE",
|
"OLM_BAD_ENCRYPTED_MESSAGE",
|
||||||
"Bad Encrypted Message", {
|
"Bad Encrypted Message", {
|
||||||
sender: deviceKey,
|
sender: deviceKey,
|
||||||
@@ -200,14 +206,14 @@ OlmDecryption.prototype.decryptEvent = async function(event) {
|
|||||||
// check that we were the intended recipient, to avoid unknown-key attack
|
// check that we were the intended recipient, to avoid unknown-key attack
|
||||||
// https://github.com/vector-im/vector-web/issues/2483
|
// https://github.com/vector-im/vector-web/issues/2483
|
||||||
if (payload.recipient != this._userId) {
|
if (payload.recipient != this._userId) {
|
||||||
throw new base.DecryptionError(
|
throw new DecryptionError(
|
||||||
"OLM_BAD_RECIPIENT",
|
"OLM_BAD_RECIPIENT",
|
||||||
"Message was intented for " + payload.recipient,
|
"Message was intented for " + payload.recipient,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payload.recipient_keys.ed25519 != this._olmDevice.deviceEd25519Key) {
|
if (payload.recipient_keys.ed25519 != this._olmDevice.deviceEd25519Key) {
|
||||||
throw new base.DecryptionError(
|
throw new DecryptionError(
|
||||||
"OLM_BAD_RECIPIENT_KEY",
|
"OLM_BAD_RECIPIENT_KEY",
|
||||||
"Message not intended for this device", {
|
"Message not intended for this device", {
|
||||||
intended: payload.recipient_keys.ed25519,
|
intended: payload.recipient_keys.ed25519,
|
||||||
@@ -221,7 +227,7 @@ OlmDecryption.prototype.decryptEvent = async function(event) {
|
|||||||
// (this check is also provided via the sender's embedded ed25519 key,
|
// (this check is also provided via the sender's embedded ed25519 key,
|
||||||
// which is checked elsewhere).
|
// which is checked elsewhere).
|
||||||
if (payload.sender != event.getSender()) {
|
if (payload.sender != event.getSender()) {
|
||||||
throw new base.DecryptionError(
|
throw new DecryptionError(
|
||||||
"OLM_FORWARDED_MESSAGE",
|
"OLM_FORWARDED_MESSAGE",
|
||||||
"Message forwarded from " + payload.sender, {
|
"Message forwarded from " + payload.sender, {
|
||||||
reported_sender: event.getSender(),
|
reported_sender: event.getSender(),
|
||||||
@@ -231,7 +237,7 @@ OlmDecryption.prototype.decryptEvent = async function(event) {
|
|||||||
|
|
||||||
// Olm events intended for a room have a room_id.
|
// Olm events intended for a room have a room_id.
|
||||||
if (payload.room_id !== event.getRoomId()) {
|
if (payload.room_id !== event.getRoomId()) {
|
||||||
throw new base.DecryptionError(
|
throw new DecryptionError(
|
||||||
"OLM_BAD_ROOM",
|
"OLM_BAD_ROOM",
|
||||||
"Message intended for room " + payload.room_id, {
|
"Message intended for room " + payload.room_id, {
|
||||||
reported_room: event.room_id,
|
reported_room: event.room_id,
|
||||||
@@ -334,4 +340,4 @@ OlmDecryption.prototype._decryptMessage = async function(
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
base.registerAlgorithm(olmlib.OLM_ALGORITHM, OlmEncryption, OlmDecryption);
|
registerAlgorithm(olmlib.OLM_ALGORITHM, OlmEncryption, OlmDecryption);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2016 OpenMarket Ltd
|
Copyright 2016 OpenMarket Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -13,8 +14,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
"use strict";
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @module crypto/deviceinfo
|
* @module crypto/deviceinfo
|
||||||
@@ -44,7 +43,7 @@ limitations under the License.
|
|||||||
*
|
*
|
||||||
* @param {string} deviceId id of the device
|
* @param {string} deviceId id of the device
|
||||||
*/
|
*/
|
||||||
function DeviceInfo(deviceId) {
|
export function DeviceInfo(deviceId) {
|
||||||
// you can't change the deviceId
|
// you can't change the deviceId
|
||||||
Object.defineProperty(this, 'deviceId', {
|
Object.defineProperty(this, 'deviceId', {
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
@@ -167,5 +166,3 @@ DeviceInfo.DeviceVerification = {
|
|||||||
|
|
||||||
const DeviceVerification = DeviceInfo.DeviceVerification;
|
const DeviceVerification = DeviceInfo.DeviceVerification;
|
||||||
|
|
||||||
/** */
|
|
||||||
module.exports = DeviceInfo;
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Copyright 2016 OpenMarket Ltd
|
Copyright 2016 OpenMarket Ltd
|
||||||
Copyright 2017 Vector Creations Ltd
|
Copyright 2017 Vector Creations Ltd
|
||||||
Copyright 2018-2019 New Vector Ltd
|
Copyright 2018-2019 New Vector Ltd
|
||||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
Copyright 2019-2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -16,44 +16,41 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
"use strict";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @module crypto
|
* @module crypto
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const anotherjson = require('another-json');
|
import anotherjson from "another-json";
|
||||||
import {EventEmitter} from 'events';
|
import {EventEmitter} from 'events';
|
||||||
import ReEmitter from '../ReEmitter';
|
import {ReEmitter} from '../ReEmitter';
|
||||||
|
import {logger} from '../logger';
|
||||||
import logger from '../logger';
|
import * as utils from "../utils";
|
||||||
const utils = require("../utils");
|
import {sleep} from "../utils";
|
||||||
const OlmDevice = require("./OlmDevice");
|
import {OlmDevice} from "./OlmDevice";
|
||||||
const olmlib = require("./olmlib");
|
import * as olmlib from "./olmlib";
|
||||||
const algorithms = require("./algorithms");
|
import {DeviceList} from "./DeviceList";
|
||||||
const DeviceInfo = require("./deviceinfo");
|
import {DeviceInfo} from "./deviceinfo";
|
||||||
const DeviceVerification = DeviceInfo.DeviceVerification;
|
import * as algorithms from "./algorithms";
|
||||||
const DeviceList = require('./DeviceList').default;
|
|
||||||
import {
|
import {
|
||||||
CrossSigningInfo,
|
CrossSigningInfo,
|
||||||
UserTrustLevel,
|
|
||||||
DeviceTrustLevel,
|
|
||||||
CrossSigningLevel,
|
CrossSigningLevel,
|
||||||
|
DeviceTrustLevel,
|
||||||
|
UserTrustLevel,
|
||||||
} from './CrossSigning';
|
} from './CrossSigning';
|
||||||
import SecretStorage, { SECRET_STORAGE_ALGORITHM_V1 } from './SecretStorage';
|
import {SECRET_STORAGE_ALGORITHM_V1, SecretStorage} from './SecretStorage';
|
||||||
|
import {OutgoingRoomKeyRequestManager} from './OutgoingRoomKeyRequestManager';
|
||||||
import OutgoingRoomKeyRequestManager from './OutgoingRoomKeyRequestManager';
|
import {IndexedDBCryptoStore} from './store/indexeddb-crypto-store';
|
||||||
import IndexedDBCryptoStore from './store/indexeddb-crypto-store';
|
import {ScanQRCode, ShowQRCode} from './verification/QRCode';
|
||||||
|
import {SAS} from './verification/SAS';
|
||||||
import {ShowQRCode, ScanQRCode} from './verification/QRCode';
|
|
||||||
import SAS from './verification/SAS';
|
|
||||||
import {sleep} from '../utils';
|
|
||||||
import {keyFromPassphrase} from './key_passphrase';
|
import {keyFromPassphrase} from './key_passphrase';
|
||||||
import {encodeRecoveryKey} from './recoverykey';
|
import {encodeRecoveryKey} from './recoverykey';
|
||||||
|
import {VerificationRequest} from "./verification/request/VerificationRequest";
|
||||||
import VerificationRequest from "./verification/request/VerificationRequest";
|
|
||||||
import {InRoomChannel, InRoomRequests} from "./verification/request/InRoomChannel";
|
import {InRoomChannel, InRoomRequests} from "./verification/request/InRoomChannel";
|
||||||
import {ToDeviceChannel, ToDeviceRequests} from "./verification/request/ToDeviceChannel";
|
import {ToDeviceChannel, ToDeviceRequests} from "./verification/request/ToDeviceChannel";
|
||||||
|
import * as httpApi from "../http-api";
|
||||||
|
|
||||||
|
const DeviceVerification = DeviceInfo.DeviceVerification;
|
||||||
|
|
||||||
const defaultVerificationMethods = {
|
const defaultVerificationMethods = {
|
||||||
[ScanQRCode.NAME]: ScanQRCode,
|
[ScanQRCode.NAME]: ScanQRCode,
|
||||||
@@ -107,7 +104,7 @@ const KEY_BACKUP_KEYS_PER_REQUEST = 200;
|
|||||||
* Each element can either be a string from MatrixClient.verificationMethods
|
* Each element can either be a string from MatrixClient.verificationMethods
|
||||||
* or a class that implements a verification method.
|
* or a class that implements a verification method.
|
||||||
*/
|
*/
|
||||||
export default function Crypto(baseApis, sessionStore, userId, deviceId,
|
export function Crypto(baseApis, sessionStore, userId, deviceId,
|
||||||
clientStore, cryptoStore, roomList, verificationMethods) {
|
clientStore, cryptoStore, roomList, verificationMethods) {
|
||||||
this._onDeviceListUserCrossSigningUpdated =
|
this._onDeviceListUserCrossSigningUpdated =
|
||||||
this._onDeviceListUserCrossSigningUpdated.bind(this);
|
this._onDeviceListUserCrossSigningUpdated.bind(this);
|
||||||
@@ -176,6 +173,7 @@ export default function Crypto(baseApis, sessionStore, userId, deviceId,
|
|||||||
this._deviceKeys = {};
|
this._deviceKeys = {};
|
||||||
|
|
||||||
this._globalBlacklistUnverifiedDevices = false;
|
this._globalBlacklistUnverifiedDevices = false;
|
||||||
|
this._globalErrorOnUnknownDevices = true;
|
||||||
|
|
||||||
this._outgoingRoomKeyRequestManager = new OutgoingRoomKeyRequestManager(
|
this._outgoingRoomKeyRequestManager = new OutgoingRoomKeyRequestManager(
|
||||||
baseApis, this._deviceId, this._cryptoStore,
|
baseApis, this._deviceId, this._cryptoStore,
|
||||||
@@ -218,7 +216,7 @@ export default function Crypto(baseApis, sessionStore, userId, deviceId,
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Assuming no app-supplied callback, default to getting from SSSS.
|
// Assuming no app-supplied callback, default to getting from SSSS.
|
||||||
if (!cryptoCallbacks.getCrossSigningKey) {
|
if (!cryptoCallbacks.getCrossSigningKey && cryptoCallbacks.getSecretStorageKey) {
|
||||||
cryptoCallbacks.getCrossSigningKey = async (type) => {
|
cryptoCallbacks.getCrossSigningKey = async (type) => {
|
||||||
return CrossSigningInfo.getFromSecretStorage(type, this._secretStorage);
|
return CrossSigningInfo.getFromSecretStorage(type, this._secretStorage);
|
||||||
};
|
};
|
||||||
@@ -418,6 +416,25 @@ Crypto.prototype.bootstrapSecretStorage = async function({
|
|||||||
// Add an entry for the backup key in SSSS as a 'passthrough' key
|
// Add an entry for the backup key in SSSS as a 'passthrough' key
|
||||||
// (ie. the secret is the key itself).
|
// (ie. the secret is the key itself).
|
||||||
this._secretStorage.storePassthrough('m.megolm_backup.v1', newKeyId);
|
this._secretStorage.storePassthrough('m.megolm_backup.v1', newKeyId);
|
||||||
|
|
||||||
|
// if this key backup is trusted, sign it with the cross signing key
|
||||||
|
// so the key backup can be trusted via cross-signing.
|
||||||
|
const backupSigStatus = await this.checkKeyBackup(keyBackupInfo);
|
||||||
|
if (backupSigStatus.trustInfo.usable) {
|
||||||
|
console.log("Adding cross signing signature to key backup");
|
||||||
|
await this._crossSigningInfo.signObject(
|
||||||
|
keyBackupInfo.auth_data, "master",
|
||||||
|
);
|
||||||
|
await this._baseApis._http.authedRequest(
|
||||||
|
undefined, "PUT", "/room_keys/version/" + keyBackupInfo.version,
|
||||||
|
undefined, keyBackupInfo,
|
||||||
|
{prefix: httpApi.PREFIX_UNSTABLE},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
"Key backup is NOT TRUSTED: NOT adding cross signing signature",
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.log("Secret storage default key not found, creating new key");
|
logger.log("Secret storage default key not found, creating new key");
|
||||||
const keyOptions = await createSecretStorageKey();
|
const keyOptions = await createSecretStorageKey();
|
||||||
@@ -588,8 +605,8 @@ Crypto.prototype.resetCrossSigningKeys = async function(level, {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Run various follow-up actions after cross-signing keys have changed locally
|
* Run various follow-up actions after cross-signing keys have changed locally
|
||||||
* (either by resetting the keys for the account or bye getting them from secret
|
* (either by resetting the keys for the account or by getting them from secret
|
||||||
* storaoge), such as signing the current device, upgrading device
|
* storage), such as signing the current device, upgrading device
|
||||||
* verifications, etc.
|
* verifications, etc.
|
||||||
*/
|
*/
|
||||||
Crypto.prototype._afterCrossSigningLocalKeyChange = async function() {
|
Crypto.prototype._afterCrossSigningLocalKeyChange = async function() {
|
||||||
@@ -1068,6 +1085,9 @@ Crypto.prototype.isKeyBackupTrusted = async function(backupInfo) {
|
|||||||
);
|
);
|
||||||
if (device) {
|
if (device) {
|
||||||
sigInfo.device = device;
|
sigInfo.device = device;
|
||||||
|
sigInfo.deviceTrust = await this.checkDeviceTrust(
|
||||||
|
this._userId, sigInfo.deviceId,
|
||||||
|
);
|
||||||
try {
|
try {
|
||||||
await olmlib.verifySignature(
|
await olmlib.verifySignature(
|
||||||
this._olmDevice,
|
this._olmDevice,
|
||||||
@@ -1095,7 +1115,7 @@ Crypto.prototype.isKeyBackupTrusted = async function(backupInfo) {
|
|||||||
ret.usable = ret.sigs.some((s) => {
|
ret.usable = ret.sigs.some((s) => {
|
||||||
return (
|
return (
|
||||||
s.valid && (
|
s.valid && (
|
||||||
(s.device && s.device.isVerified()) ||
|
(s.device && s.deviceTrust.isVerified()) ||
|
||||||
(s.crossSigningId)
|
(s.crossSigningId)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -1183,6 +1203,29 @@ Crypto.prototype.getGlobalBlacklistUnverifiedDevices = function() {
|
|||||||
return this._globalBlacklistUnverifiedDevices;
|
return this._globalBlacklistUnverifiedDevices;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether sendMessage in a room with unknown and unverified devices
|
||||||
|
* should throw an error and not send them message. This has 'Global' for
|
||||||
|
* symmertry with setGlobalBlacklistUnverifiedDevices but there is currently
|
||||||
|
* no room-level equivalent for this setting.
|
||||||
|
*
|
||||||
|
* This API is currently UNSTABLE and may change or be removed without notice.
|
||||||
|
*
|
||||||
|
* @param {boolean} value whether error on unknown devices
|
||||||
|
*/
|
||||||
|
Crypto.prototype.setGlobalErrorOnUnknownDevices = function(value) {
|
||||||
|
this._globalErrorOnUnknownDevices = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {boolean} whether to error on unknown devices
|
||||||
|
*
|
||||||
|
* This API is currently UNSTABLE and may change or be removed without notice.
|
||||||
|
*/
|
||||||
|
Crypto.prototype.getGlobalErrorOnUnknownDevices = function() {
|
||||||
|
return this._globalErrorOnUnknownDevices;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upload the device keys to the homeserver.
|
* Upload the device keys to the homeserver.
|
||||||
* @return {object} A promise that will resolve when the keys are uploaded.
|
* @return {object} A promise that will resolve when the keys are uploaded.
|
||||||
@@ -2381,6 +2424,8 @@ Crypto.prototype._onToDeviceEvent = function(event) {
|
|||||||
this._secretStorage._onRequestReceived(event);
|
this._secretStorage._onRequestReceived(event);
|
||||||
} else if (event.getType() === "m.secret.send") {
|
} else if (event.getType() === "m.secret.send") {
|
||||||
this._secretStorage._onSecretReceived(event);
|
this._secretStorage._onSecretReceived(event);
|
||||||
|
} else if (event.getType() === "org.matrix.room_key.withheld") {
|
||||||
|
this._onRoomKeyWithheldEvent(event);
|
||||||
} else if (event.getContent().transaction_id) {
|
} else if (event.getContent().transaction_id) {
|
||||||
this._onKeyVerificationMessage(event);
|
this._onKeyVerificationMessage(event);
|
||||||
} else if (event.getContent().msgtype === "m.bad.encrypted") {
|
} else if (event.getContent().msgtype === "m.bad.encrypted") {
|
||||||
@@ -2420,6 +2465,42 @@ Crypto.prototype._onRoomKeyEvent = function(event) {
|
|||||||
alg.onRoomKeyEvent(event);
|
alg.onRoomKeyEvent(event);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle a key withheld event
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {module:models/event.MatrixEvent} event key withheld event
|
||||||
|
*/
|
||||||
|
Crypto.prototype._onRoomKeyWithheldEvent = function(event) {
|
||||||
|
const content = event.getContent();
|
||||||
|
|
||||||
|
if ((content.code !== "m.no_olm" && (!content.room_id || !content.session_id))
|
||||||
|
|| !content.algorithm || !content.sender_key) {
|
||||||
|
logger.error("key withheld event is missing fields");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
`Got room key withheld event from ${event.getSender()} (${content.sender_key}) `
|
||||||
|
+ `for ${content.algorithm}/${content.room_id}/${content.session_id} `
|
||||||
|
+ `with reason ${content.code} (${content.reason})`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const alg = this._getRoomDecryptor(content.room_id, content.algorithm);
|
||||||
|
if (alg.onRoomKeyWithheldEvent) {
|
||||||
|
alg.onRoomKeyWithheldEvent(event);
|
||||||
|
}
|
||||||
|
if (!content.room_id) {
|
||||||
|
// retry decryption for all events sent by the sender_key. This will
|
||||||
|
// update the events to show a message indicating that the olm session was
|
||||||
|
// wedged.
|
||||||
|
const roomDecryptors = this._getRoomDecryptors(content.algorithm);
|
||||||
|
for (const decryptor of roomDecryptors) {
|
||||||
|
decryptor.retryDecryptionFromSender(content.sender_key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle a general key verification event.
|
* Handle a general key verification event.
|
||||||
*
|
*
|
||||||
@@ -2536,6 +2617,16 @@ Crypto.prototype._onToDeviceBadEncrypted = async function(event) {
|
|||||||
const algorithm = content.algorithm;
|
const algorithm = content.algorithm;
|
||||||
const deviceKey = content.sender_key;
|
const deviceKey = content.sender_key;
|
||||||
|
|
||||||
|
// retry decryption for all events sent by the sender_key. This will
|
||||||
|
// update the events to show a message indicating that the olm session was
|
||||||
|
// wedged.
|
||||||
|
const retryDecryption = () => {
|
||||||
|
const roomDecryptors = this._getRoomDecryptors(olmlib.MEGOLM_ALGORITHM);
|
||||||
|
for (const decryptor of roomDecryptors) {
|
||||||
|
decryptor.retryDecryptionFromSender(deviceKey);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (sender === undefined || deviceKey === undefined || deviceKey === undefined) {
|
if (sender === undefined || deviceKey === undefined || deviceKey === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -2549,6 +2640,8 @@ Crypto.prototype._onToDeviceBadEncrypted = async function(event) {
|
|||||||
"New session already forced with device " + sender + ":" + deviceKey +
|
"New session already forced with device " + sender + ":" + deviceKey +
|
||||||
" at " + lastNewSessionForced + ": not forcing another",
|
" at " + lastNewSessionForced + ": not forcing another",
|
||||||
);
|
);
|
||||||
|
await this._olmDevice.recordSessionProblem(deviceKey, "wedged", true);
|
||||||
|
retryDecryption();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2562,6 +2655,8 @@ Crypto.prototype._onToDeviceBadEncrypted = async function(event) {
|
|||||||
"Couldn't find device for identity key " + deviceKey +
|
"Couldn't find device for identity key " + deviceKey +
|
||||||
": not re-establishing session",
|
": not re-establishing session",
|
||||||
);
|
);
|
||||||
|
await this._olmDevice.recordSessionProblem(deviceKey, "wedged", false);
|
||||||
|
retryDecryption();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const devicesByUser = {};
|
const devicesByUser = {};
|
||||||
@@ -2593,6 +2688,9 @@ Crypto.prototype._onToDeviceBadEncrypted = async function(event) {
|
|||||||
{type: "m.dummy"},
|
{type: "m.dummy"},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await this._olmDevice.recordSessionProblem(deviceKey, "wedged", true);
|
||||||
|
retryDecryption();
|
||||||
|
|
||||||
await this._baseApis.sendToDevice("m.room.encrypted", {
|
await this._baseApis.sendToDevice("m.room.encrypted", {
|
||||||
[sender]: {
|
[sender]: {
|
||||||
[device.deviceId]: encryptedContent,
|
[device.deviceId]: encryptedContent,
|
||||||
@@ -2874,6 +2972,24 @@ Crypto.prototype._getRoomDecryptor = function(roomId, algorithm) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all the room decryptors for a given encryption algorithm.
|
||||||
|
*
|
||||||
|
* @param {string} algorithm The encryption algorithm
|
||||||
|
*
|
||||||
|
* @return {array} An array of room decryptors
|
||||||
|
*/
|
||||||
|
Crypto.prototype._getRoomDecryptors = function(algorithm) {
|
||||||
|
const decryptors = [];
|
||||||
|
for (const d of Object.values(this._roomDecryptors)) {
|
||||||
|
if (algorithm in d) {
|
||||||
|
decryptors.push(d[algorithm]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return decryptors;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sign the given object with our ed25519 key
|
* sign the given object with our ed25519 key
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -22,25 +22,24 @@ limitations under the License.
|
|||||||
* Utilities common to olm encryption algorithms
|
* Utilities common to olm encryption algorithms
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const anotherjson = require('another-json');
|
import {logger} from '../logger';
|
||||||
|
import * as utils from "../utils";
|
||||||
import logger from '../logger';
|
import anotherjson from "another-json";
|
||||||
const utils = require("../utils");
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* matrix algorithm tag for olm
|
* matrix algorithm tag for olm
|
||||||
*/
|
*/
|
||||||
module.exports.OLM_ALGORITHM = "m.olm.v1.curve25519-aes-sha2";
|
export const OLM_ALGORITHM = "m.olm.v1.curve25519-aes-sha2";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* matrix algorithm tag for megolm
|
* matrix algorithm tag for megolm
|
||||||
*/
|
*/
|
||||||
module.exports.MEGOLM_ALGORITHM = "m.megolm.v1.aes-sha2";
|
export const MEGOLM_ALGORITHM = "m.megolm.v1.aes-sha2";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* matrix algorithm tag for megolm backups
|
* matrix algorithm tag for megolm backups
|
||||||
*/
|
*/
|
||||||
module.exports.MEGOLM_BACKUP_ALGORITHM = "m.megolm_backup.v1.curve25519-aes-sha2";
|
export const MEGOLM_BACKUP_ALGORITHM = "m.megolm_backup.v1.curve25519-aes-sha2";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -59,7 +58,7 @@ module.exports.MEGOLM_BACKUP_ALGORITHM = "m.megolm_backup.v1.curve25519-aes-sha2
|
|||||||
* Returns a promise which resolves (to undefined) when the payload
|
* Returns a promise which resolves (to undefined) when the payload
|
||||||
* has been encrypted into `resultsObject`
|
* has been encrypted into `resultsObject`
|
||||||
*/
|
*/
|
||||||
module.exports.encryptMessageForDevice = async function(
|
export async function encryptMessageForDevice(
|
||||||
resultsObject,
|
resultsObject,
|
||||||
ourUserId, ourDeviceId, olmDevice, recipientUserId, recipientDevice,
|
ourUserId, ourDeviceId, olmDevice, recipientUserId, recipientDevice,
|
||||||
payloadFields,
|
payloadFields,
|
||||||
@@ -112,7 +111,7 @@ module.exports.encryptMessageForDevice = async function(
|
|||||||
resultsObject[deviceKey] = await olmDevice.encryptMessage(
|
resultsObject[deviceKey] = await olmDevice.encryptMessage(
|
||||||
deviceKey, sessionId, JSON.stringify(payload),
|
deviceKey, sessionId, JSON.stringify(payload),
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to make sure we have established olm sessions for the given devices.
|
* Try to make sure we have established olm sessions for the given devices.
|
||||||
@@ -131,7 +130,7 @@ module.exports.encryptMessageForDevice = async function(
|
|||||||
* an Object mapping from userId to deviceId to
|
* an Object mapping from userId to deviceId to
|
||||||
* {@link module:crypto~OlmSessionResult}
|
* {@link module:crypto~OlmSessionResult}
|
||||||
*/
|
*/
|
||||||
module.exports.ensureOlmSessionsForDevices = async function(
|
export async function ensureOlmSessionsForDevices(
|
||||||
olmDevice, baseApis, devicesByUser, force,
|
olmDevice, baseApis, devicesByUser, force,
|
||||||
) {
|
) {
|
||||||
const devicesWithoutSession = [
|
const devicesWithoutSession = [
|
||||||
@@ -263,12 +262,12 @@ module.exports.ensureOlmSessionsForDevices = async function(
|
|||||||
|
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
return result;
|
return result;
|
||||||
};
|
}
|
||||||
|
|
||||||
async function _verifyKeyAndStartSession(olmDevice, oneTimeKey, userId, deviceInfo) {
|
async function _verifyKeyAndStartSession(olmDevice, oneTimeKey, userId, deviceInfo) {
|
||||||
const deviceId = deviceInfo.deviceId;
|
const deviceId = deviceInfo.deviceId;
|
||||||
try {
|
try {
|
||||||
await _verifySignature(
|
await verifySignature(
|
||||||
olmDevice, oneTimeKey, userId, deviceId,
|
olmDevice, oneTimeKey, userId, deviceId,
|
||||||
deviceInfo.getFingerprint(),
|
deviceInfo.getFingerprint(),
|
||||||
);
|
);
|
||||||
@@ -314,7 +313,7 @@ async function _verifyKeyAndStartSession(olmDevice, oneTimeKey, userId, deviceIn
|
|||||||
* Returns a promise which resolves (to undefined) if the the signature is good,
|
* Returns a promise which resolves (to undefined) if the the signature is good,
|
||||||
* or rejects with an Error if it is bad.
|
* or rejects with an Error if it is bad.
|
||||||
*/
|
*/
|
||||||
const _verifySignature = module.exports.verifySignature = async function(
|
export async function verifySignature(
|
||||||
olmDevice, obj, signingUserId, signingDeviceId, signingKey,
|
olmDevice, obj, signingUserId, signingDeviceId, signingKey,
|
||||||
) {
|
) {
|
||||||
const signKeyId = "ed25519:" + signingDeviceId;
|
const signKeyId = "ed25519:" + signingDeviceId;
|
||||||
@@ -335,7 +334,7 @@ const _verifySignature = module.exports.verifySignature = async function(
|
|||||||
olmDevice.verifySignature(
|
olmDevice.verifySignature(
|
||||||
signingKey, json, signature,
|
signingKey, json, signature,
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sign a JSON object using public key cryptography
|
* Sign a JSON object using public key cryptography
|
||||||
@@ -347,7 +346,7 @@ const _verifySignature = module.exports.verifySignature = async function(
|
|||||||
* @param {string} pubkey The public key (ignored if key is a seed)
|
* @param {string} pubkey The public key (ignored if key is a seed)
|
||||||
* @returns {string} the signature for the object
|
* @returns {string} the signature for the object
|
||||||
*/
|
*/
|
||||||
module.exports.pkSign = function(obj, key, userId, pubkey) {
|
export function pkSign(obj, key, userId, pubkey) {
|
||||||
let createdKey = false;
|
let createdKey = false;
|
||||||
if (key instanceof Uint8Array) {
|
if (key instanceof Uint8Array) {
|
||||||
const keyObj = new global.Olm.PkSigning();
|
const keyObj = new global.Olm.PkSigning();
|
||||||
@@ -371,7 +370,7 @@ module.exports.pkSign = function(obj, key, userId, pubkey) {
|
|||||||
key.free();
|
key.free();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify a signed JSON object
|
* Verify a signed JSON object
|
||||||
@@ -379,7 +378,7 @@ module.exports.pkSign = function(obj, key, userId, pubkey) {
|
|||||||
* @param {string} pubkey The public key to use to verify
|
* @param {string} pubkey The public key to use to verify
|
||||||
* @param {string} userId The user ID who signed the object
|
* @param {string} userId The user ID who signed the object
|
||||||
*/
|
*/
|
||||||
module.exports.pkVerify = function(obj, pubkey, userId) {
|
export function pkVerify(obj, pubkey, userId) {
|
||||||
const keyId = "ed25519:" + pubkey;
|
const keyId = "ed25519:" + pubkey;
|
||||||
if (!(obj.signatures && obj.signatures[userId] && obj.signatures[userId][keyId])) {
|
if (!(obj.signatures && obj.signatures[userId] && obj.signatures[userId][keyId])) {
|
||||||
throw new Error("No signature");
|
throw new Error("No signature");
|
||||||
@@ -397,22 +396,22 @@ module.exports.pkVerify = function(obj, pubkey, userId) {
|
|||||||
if (unsigned) obj.unsigned = unsigned;
|
if (unsigned) obj.unsigned = unsigned;
|
||||||
util.free();
|
util.free();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encode a typed array of uint8 as base64.
|
* Encode a typed array of uint8 as base64.
|
||||||
* @param {Uint8Array} uint8Array The data to encode.
|
* @param {Uint8Array} uint8Array The data to encode.
|
||||||
* @return {string} The base64.
|
* @return {string} The base64.
|
||||||
*/
|
*/
|
||||||
module.exports.encodeBase64 = function(uint8Array) {
|
export function encodeBase64(uint8Array) {
|
||||||
return Buffer.from(uint8Array).toString("base64");
|
return Buffer.from(uint8Array).toString("base64");
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decode a base64 string to a typed array of uint8.
|
* Decode a base64 string to a typed array of uint8.
|
||||||
* @param {string} base64 The base64 to decode.
|
* @param {string} base64 The base64 to decode.
|
||||||
* @return {Uint8Array} The decoded data.
|
* @return {Uint8Array} The decoded data.
|
||||||
*/
|
*/
|
||||||
module.exports.decodeBase64 = function(base64) {
|
export function decodeBase64(base64) {
|
||||||
return Buffer.from(base64, "base64");
|
return Buffer.from(base64, "base64");
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2017 Vector Creations Ltd
|
Copyright 2017 Vector Creations Ltd
|
||||||
Copyright 2018 New Vector Ltd
|
Copyright 2018 New Vector Ltd
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -15,10 +16,10 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import logger from '../../logger';
|
import {logger} from '../../logger';
|
||||||
import utils from '../../utils';
|
import * as utils from "../../utils";
|
||||||
|
|
||||||
export const VERSION = 7;
|
export const VERSION = 9;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of a CryptoStore which is backed by an existing
|
* Implementation of a CryptoStore which is backed by an existing
|
||||||
@@ -425,17 +426,107 @@ export class Backend {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async storeEndToEndSessionProblem(deviceKey, type, fixed) {
|
||||||
|
const txn = this._db.transaction("session_problems", "readwrite");
|
||||||
|
const objectStore = txn.objectStore("session_problems");
|
||||||
|
objectStore.put({
|
||||||
|
deviceKey,
|
||||||
|
type,
|
||||||
|
fixed,
|
||||||
|
time: Date.now(),
|
||||||
|
});
|
||||||
|
return promiseifyTxn(txn);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getEndToEndSessionProblem(deviceKey, timestamp) {
|
||||||
|
let result;
|
||||||
|
const txn = this._db.transaction("session_problems", "readwrite");
|
||||||
|
const objectStore = txn.objectStore("session_problems");
|
||||||
|
const index = objectStore.index("deviceKey");
|
||||||
|
const req = index.getAll(deviceKey);
|
||||||
|
req.onsuccess = (event) => {
|
||||||
|
const problems = req.result;
|
||||||
|
if (!problems.length) {
|
||||||
|
result = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
problems.sort((a, b) => {
|
||||||
|
return a.time - b.time;
|
||||||
|
});
|
||||||
|
const lastProblem = problems[problems.length - 1];
|
||||||
|
for (const problem of problems) {
|
||||||
|
if (problem.time > timestamp) {
|
||||||
|
result = Object.assign({}, problem, {fixed: lastProblem.fixed});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lastProblem.fixed) {
|
||||||
|
result = null;
|
||||||
|
} else {
|
||||||
|
result = lastProblem;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
await promiseifyTxn(txn);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: we should probably prune this when devices get deleted
|
||||||
|
async filterOutNotifiedErrorDevices(devices) {
|
||||||
|
const txn = this._db.transaction("notified_error_devices", "readwrite");
|
||||||
|
const objectStore = txn.objectStore("notified_error_devices");
|
||||||
|
|
||||||
|
const ret = [];
|
||||||
|
|
||||||
|
await Promise.all(devices.map((device) => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const {userId, deviceInfo} = device;
|
||||||
|
const getReq = objectStore.get([userId, deviceInfo.deviceId]);
|
||||||
|
getReq.onsuccess = function() {
|
||||||
|
if (!getReq.result) {
|
||||||
|
objectStore.put({userId, deviceId: deviceInfo.deviceId});
|
||||||
|
ret.push(device);
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
// Inbound group sessions
|
// Inbound group sessions
|
||||||
|
|
||||||
getEndToEndInboundGroupSession(senderCurve25519Key, sessionId, txn, func) {
|
getEndToEndInboundGroupSession(senderCurve25519Key, sessionId, txn, func) {
|
||||||
|
let session = false;
|
||||||
|
let withheld = false;
|
||||||
const objectStore = txn.objectStore("inbound_group_sessions");
|
const objectStore = txn.objectStore("inbound_group_sessions");
|
||||||
const getReq = objectStore.get([senderCurve25519Key, sessionId]);
|
const getReq = objectStore.get([senderCurve25519Key, sessionId]);
|
||||||
getReq.onsuccess = function() {
|
getReq.onsuccess = function() {
|
||||||
try {
|
try {
|
||||||
if (getReq.result) {
|
if (getReq.result) {
|
||||||
func(getReq.result.session);
|
session = getReq.result.session;
|
||||||
} else {
|
} else {
|
||||||
func(null);
|
session = null;
|
||||||
|
}
|
||||||
|
if (withheld !== false) {
|
||||||
|
func(session, withheld);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
abortWithException(txn, e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const withheldObjectStore = txn.objectStore("inbound_group_sessions_withheld");
|
||||||
|
const withheldGetReq = withheldObjectStore.get([senderCurve25519Key, sessionId]);
|
||||||
|
withheldGetReq.onsuccess = function() {
|
||||||
|
try {
|
||||||
|
if (withheldGetReq.result) {
|
||||||
|
withheld = withheldGetReq.result.session;
|
||||||
|
} else {
|
||||||
|
withheld = null;
|
||||||
|
}
|
||||||
|
if (session !== false) {
|
||||||
|
func(session, withheld);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
abortWithException(txn, e);
|
abortWithException(txn, e);
|
||||||
@@ -499,6 +590,15 @@ export class Backend {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
storeEndToEndInboundGroupSessionWithheld(
|
||||||
|
senderCurve25519Key, sessionId, sessionData, txn,
|
||||||
|
) {
|
||||||
|
const objectStore = txn.objectStore("inbound_group_sessions_withheld");
|
||||||
|
objectStore.put({
|
||||||
|
senderCurve25519Key, sessionId, session: sessionData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
getEndToEndDeviceData(txn, func) {
|
getEndToEndDeviceData(txn, func) {
|
||||||
const objectStore = txn.objectStore("device_data");
|
const objectStore = txn.objectStore("device_data");
|
||||||
const getReq = objectStore.get("-");
|
const getReq = objectStore.get("-");
|
||||||
@@ -662,6 +762,21 @@ export function upgradeDatabase(db, oldVersion) {
|
|||||||
keyPath: ["senderCurve25519Key", "sessionId"],
|
keyPath: ["senderCurve25519Key", "sessionId"],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (oldVersion < 8) {
|
||||||
|
db.createObjectStore("inbound_group_sessions_withheld", {
|
||||||
|
keyPath: ["senderCurve25519Key", "sessionId"],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (oldVersion < 9) {
|
||||||
|
const problemsStore = db.createObjectStore("session_problems", {
|
||||||
|
keyPath: ["deviceKey", "time"],
|
||||||
|
});
|
||||||
|
problemsStore.createIndex("deviceKey", "deviceKey");
|
||||||
|
|
||||||
|
db.createObjectStore("notified_error_devices", {
|
||||||
|
keyPath: ["userId", "deviceId"],
|
||||||
|
});
|
||||||
|
}
|
||||||
// Expand as needed.
|
// Expand as needed.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2017 Vector Creations Ltd
|
Copyright 2017 Vector Creations Ltd
|
||||||
Copyright 2018 New Vector Ltd
|
Copyright 2018 New Vector Ltd
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -15,9 +16,9 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import logger from '../../logger';
|
import {logger} from '../../logger';
|
||||||
import LocalStorageCryptoStore from './localStorage-crypto-store';
|
import {LocalStorageCryptoStore} from './localStorage-crypto-store';
|
||||||
import MemoryCryptoStore from './memory-crypto-store';
|
import {MemoryCryptoStore} from './memory-crypto-store';
|
||||||
import * as IndexedDBCryptoStoreBackend from './indexeddb-crypto-store-backend';
|
import * as IndexedDBCryptoStoreBackend from './indexeddb-crypto-store-backend';
|
||||||
import {InvalidCryptoStoreError} from '../../errors';
|
import {InvalidCryptoStoreError} from '../../errors';
|
||||||
import * as IndexedDBHelpers from "../../indexeddb-helpers";
|
import * as IndexedDBHelpers from "../../indexeddb-helpers";
|
||||||
@@ -34,7 +35,7 @@ import * as IndexedDBHelpers from "../../indexeddb-helpers";
|
|||||||
*
|
*
|
||||||
* @implements {module:crypto/store/base~CryptoStore}
|
* @implements {module:crypto/store/base~CryptoStore}
|
||||||
*/
|
*/
|
||||||
export default class IndexedDBCryptoStore {
|
export class IndexedDBCryptoStore {
|
||||||
/**
|
/**
|
||||||
* Create a new IndexedDBCryptoStore
|
* Create a new IndexedDBCryptoStore
|
||||||
*
|
*
|
||||||
@@ -104,7 +105,10 @@ export default class IndexedDBCryptoStore {
|
|||||||
// we can fall back to a different backend.
|
// we can fall back to a different backend.
|
||||||
return backend.doTxn(
|
return backend.doTxn(
|
||||||
'readonly',
|
'readonly',
|
||||||
[IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS],
|
[
|
||||||
|
IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS,
|
||||||
|
IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS_WITHHELD,
|
||||||
|
],
|
||||||
(txn) => {
|
(txn) => {
|
||||||
backend.getEndToEndInboundGroupSession('', '', txn, () => {});
|
backend.getEndToEndInboundGroupSession('', '', txn, () => {});
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
@@ -405,6 +409,24 @@ export default class IndexedDBCryptoStore {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
storeEndToEndSessionProblem(deviceKey, type, fixed) {
|
||||||
|
return this._backendPromise.then(async (backend) => {
|
||||||
|
await backend.storeEndToEndSessionProblem(deviceKey, type, fixed);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getEndToEndSessionProblem(deviceKey, timestamp) {
|
||||||
|
return this._backendPromise.then(async (backend) => {
|
||||||
|
return await backend.getEndToEndSessionProblem(deviceKey, timestamp);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
filterOutNotifiedErrorDevices(devices) {
|
||||||
|
return this._backendPromise.then(async (backend) => {
|
||||||
|
return await backend.filterOutNotifiedErrorDevices(devices);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Inbound group sessions
|
// Inbound group sessions
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -471,6 +493,16 @@ export default class IndexedDBCryptoStore {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
storeEndToEndInboundGroupSessionWithheld(
|
||||||
|
senderCurve25519Key, sessionId, sessionData, txn,
|
||||||
|
) {
|
||||||
|
this._backendPromise.then(backend => {
|
||||||
|
backend.storeEndToEndInboundGroupSessionWithheld(
|
||||||
|
senderCurve25519Key, sessionId, sessionData, txn,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// End-to-end device tracking
|
// End-to-end device tracking
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -607,6 +639,8 @@ export default class IndexedDBCryptoStore {
|
|||||||
IndexedDBCryptoStore.STORE_ACCOUNT = 'account';
|
IndexedDBCryptoStore.STORE_ACCOUNT = 'account';
|
||||||
IndexedDBCryptoStore.STORE_SESSIONS = 'sessions';
|
IndexedDBCryptoStore.STORE_SESSIONS = 'sessions';
|
||||||
IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS = 'inbound_group_sessions';
|
IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS = 'inbound_group_sessions';
|
||||||
|
IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS_WITHHELD
|
||||||
|
= 'inbound_group_sessions_withheld';
|
||||||
IndexedDBCryptoStore.STORE_DEVICE_DATA = 'device_data';
|
IndexedDBCryptoStore.STORE_DEVICE_DATA = 'device_data';
|
||||||
IndexedDBCryptoStore.STORE_ROOMS = 'rooms';
|
IndexedDBCryptoStore.STORE_ROOMS = 'rooms';
|
||||||
IndexedDBCryptoStore.STORE_BACKUP = 'sessions_needing_backup';
|
IndexedDBCryptoStore.STORE_BACKUP = 'sessions_needing_backup';
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2017, 2018 New Vector Ltd
|
Copyright 2017, 2018 New Vector Ltd
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -14,8 +15,8 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import logger from '../../logger';
|
import {logger} from '../../logger';
|
||||||
import MemoryCryptoStore from './memory-crypto-store';
|
import {MemoryCryptoStore} from './memory-crypto-store';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal module. Partial localStorage backed storage for e2e.
|
* Internal module. Partial localStorage backed storage for e2e.
|
||||||
@@ -30,8 +31,10 @@ import MemoryCryptoStore from './memory-crypto-store';
|
|||||||
const E2E_PREFIX = "crypto.";
|
const E2E_PREFIX = "crypto.";
|
||||||
const KEY_END_TO_END_ACCOUNT = E2E_PREFIX + "account";
|
const KEY_END_TO_END_ACCOUNT = E2E_PREFIX + "account";
|
||||||
const KEY_CROSS_SIGNING_KEYS = E2E_PREFIX + "cross_signing_keys";
|
const KEY_CROSS_SIGNING_KEYS = E2E_PREFIX + "cross_signing_keys";
|
||||||
|
const KEY_NOTIFIED_ERROR_DEVICES = E2E_PREFIX + "notified_error_devices";
|
||||||
const KEY_DEVICE_DATA = E2E_PREFIX + "device_data";
|
const KEY_DEVICE_DATA = E2E_PREFIX + "device_data";
|
||||||
const KEY_INBOUND_SESSION_PREFIX = E2E_PREFIX + "inboundgroupsessions/";
|
const KEY_INBOUND_SESSION_PREFIX = E2E_PREFIX + "inboundgroupsessions/";
|
||||||
|
const KEY_INBOUND_SESSION_WITHHELD_PREFIX = E2E_PREFIX + "inboundgroupsessions.withheld/";
|
||||||
const KEY_ROOMS_PREFIX = E2E_PREFIX + "rooms/";
|
const KEY_ROOMS_PREFIX = E2E_PREFIX + "rooms/";
|
||||||
const KEY_SESSIONS_NEEDING_BACKUP = E2E_PREFIX + "sessionsneedingbackup";
|
const KEY_SESSIONS_NEEDING_BACKUP = E2E_PREFIX + "sessionsneedingbackup";
|
||||||
|
|
||||||
@@ -39,10 +42,18 @@ function keyEndToEndSessions(deviceKey) {
|
|||||||
return E2E_PREFIX + "sessions/" + deviceKey;
|
return E2E_PREFIX + "sessions/" + deviceKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function keyEndToEndSessionProblems(deviceKey) {
|
||||||
|
return E2E_PREFIX + "session.problems/" + deviceKey;
|
||||||
|
}
|
||||||
|
|
||||||
function keyEndToEndInboundGroupSession(senderKey, sessionId) {
|
function keyEndToEndInboundGroupSession(senderKey, sessionId) {
|
||||||
return KEY_INBOUND_SESSION_PREFIX + senderKey + "/" + sessionId;
|
return KEY_INBOUND_SESSION_PREFIX + senderKey + "/" + sessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function keyEndToEndInboundGroupSessionWithheld(senderKey, sessionId) {
|
||||||
|
return KEY_INBOUND_SESSION_WITHHELD_PREFIX + senderKey + "/" + sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
function keyEndToEndRoomsPrefix(roomId) {
|
function keyEndToEndRoomsPrefix(roomId) {
|
||||||
return KEY_ROOMS_PREFIX + roomId;
|
return KEY_ROOMS_PREFIX + roomId;
|
||||||
}
|
}
|
||||||
@@ -50,7 +61,7 @@ function keyEndToEndRoomsPrefix(roomId) {
|
|||||||
/**
|
/**
|
||||||
* @implements {module:crypto/store/base~CryptoStore}
|
* @implements {module:crypto/store/base~CryptoStore}
|
||||||
*/
|
*/
|
||||||
export default class LocalStorageCryptoStore extends MemoryCryptoStore {
|
export class LocalStorageCryptoStore extends MemoryCryptoStore {
|
||||||
constructor(webStore) {
|
constructor(webStore) {
|
||||||
super();
|
super();
|
||||||
this.store = webStore;
|
this.store = webStore;
|
||||||
@@ -122,13 +133,71 @@ export default class LocalStorageCryptoStore extends MemoryCryptoStore {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async storeEndToEndSessionProblem(deviceKey, type, fixed) {
|
||||||
|
const key = keyEndToEndSessionProblems(deviceKey);
|
||||||
|
const problems = getJsonItem(this.store, key) || [];
|
||||||
|
problems.push({type, fixed, time: Date.now()});
|
||||||
|
problems.sort((a, b) => {
|
||||||
|
return a.time - b.time;
|
||||||
|
});
|
||||||
|
setJsonItem(this.store, key, problems);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getEndToEndSessionProblem(deviceKey, timestamp) {
|
||||||
|
const key = keyEndToEndSessionProblems(deviceKey);
|
||||||
|
const problems = getJsonItem(this.store, key) || [];
|
||||||
|
if (!problems.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const lastProblem = problems[problems.length - 1];
|
||||||
|
for (const problem of problems) {
|
||||||
|
if (problem.time > timestamp) {
|
||||||
|
return Object.assign({}, problem, {fixed: lastProblem.fixed});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lastProblem.fixed) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return lastProblem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async filterOutNotifiedErrorDevices(devices) {
|
||||||
|
const notifiedErrorDevices =
|
||||||
|
getJsonItem(this.store, KEY_NOTIFIED_ERROR_DEVICES) || {};
|
||||||
|
const ret = [];
|
||||||
|
|
||||||
|
for (const device of devices) {
|
||||||
|
const {userId, deviceInfo} = device;
|
||||||
|
if (userId in notifiedErrorDevices) {
|
||||||
|
if (!(deviceInfo.deviceId in notifiedErrorDevices[userId])) {
|
||||||
|
ret.push(device);
|
||||||
|
notifiedErrorDevices[userId][deviceInfo.deviceId] = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret.push(device);
|
||||||
|
notifiedErrorDevices[userId] = {[deviceInfo.deviceId]: true };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setJsonItem(this.store, KEY_NOTIFIED_ERROR_DEVICES, notifiedErrorDevices);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
// Inbound Group Sessions
|
// Inbound Group Sessions
|
||||||
|
|
||||||
getEndToEndInboundGroupSession(senderCurve25519Key, sessionId, txn, func) {
|
getEndToEndInboundGroupSession(senderCurve25519Key, sessionId, txn, func) {
|
||||||
func(getJsonItem(
|
func(
|
||||||
|
getJsonItem(
|
||||||
this.store,
|
this.store,
|
||||||
keyEndToEndInboundGroupSession(senderCurve25519Key, sessionId),
|
keyEndToEndInboundGroupSession(senderCurve25519Key, sessionId),
|
||||||
));
|
),
|
||||||
|
getJsonItem(
|
||||||
|
this.store,
|
||||||
|
keyEndToEndInboundGroupSessionWithheld(senderCurve25519Key, sessionId),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllEndToEndInboundGroupSessions(txn, func) {
|
getAllEndToEndInboundGroupSessions(txn, func) {
|
||||||
@@ -170,6 +239,16 @@ export default class LocalStorageCryptoStore extends MemoryCryptoStore {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
storeEndToEndInboundGroupSessionWithheld(
|
||||||
|
senderCurve25519Key, sessionId, sessionData, txn,
|
||||||
|
) {
|
||||||
|
setJsonItem(
|
||||||
|
this.store,
|
||||||
|
keyEndToEndInboundGroupSessionWithheld(senderCurve25519Key, sessionId),
|
||||||
|
sessionData,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
getEndToEndDeviceData(txn, func) {
|
getEndToEndDeviceData(txn, func) {
|
||||||
func(getJsonItem(
|
func(getJsonItem(
|
||||||
this.store, KEY_DEVICE_DATA,
|
this.store, KEY_DEVICE_DATA,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2017 Vector Creations Ltd
|
Copyright 2017 Vector Creations Ltd
|
||||||
Copyright 2018 New Vector Ltd
|
Copyright 2018 New Vector Ltd
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -15,8 +16,8 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import logger from '../../logger';
|
import {logger} from '../../logger';
|
||||||
import utils from '../../utils';
|
import * as utils from "../../utils";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal module. in-memory storage for e2e.
|
* Internal module. in-memory storage for e2e.
|
||||||
@@ -27,7 +28,7 @@ import utils from '../../utils';
|
|||||||
/**
|
/**
|
||||||
* @implements {module:crypto/store/base~CryptoStore}
|
* @implements {module:crypto/store/base~CryptoStore}
|
||||||
*/
|
*/
|
||||||
export default class MemoryCryptoStore {
|
export class MemoryCryptoStore {
|
||||||
constructor() {
|
constructor() {
|
||||||
this._outgoingRoomKeyRequests = [];
|
this._outgoingRoomKeyRequests = [];
|
||||||
this._account = null;
|
this._account = null;
|
||||||
@@ -35,8 +36,13 @@ export default class MemoryCryptoStore {
|
|||||||
|
|
||||||
// Map of {devicekey -> {sessionId -> session pickle}}
|
// Map of {devicekey -> {sessionId -> session pickle}}
|
||||||
this._sessions = {};
|
this._sessions = {};
|
||||||
|
// Map of {devicekey -> array of problems}
|
||||||
|
this._sessionProblems = {};
|
||||||
|
// Map of {userId -> deviceId -> true}
|
||||||
|
this._notifiedErrorDevices = {};
|
||||||
// Map of {senderCurve25519Key+'/'+sessionId -> session data object}
|
// Map of {senderCurve25519Key+'/'+sessionId -> session data object}
|
||||||
this._inboundGroupSessions = {};
|
this._inboundGroupSessions = {};
|
||||||
|
this._inboundGroupSessionsWithheld = {};
|
||||||
// Opaque device data object
|
// Opaque device data object
|
||||||
this._deviceData = null;
|
this._deviceData = null;
|
||||||
// roomId -> Opaque roomInfo object
|
// roomId -> Opaque roomInfo object
|
||||||
@@ -273,10 +279,61 @@ export default class MemoryCryptoStore {
|
|||||||
deviceSessions[sessionId] = sessionInfo;
|
deviceSessions[sessionId] = sessionInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async storeEndToEndSessionProblem(deviceKey, type, fixed) {
|
||||||
|
const problems = this._sessionProblems[deviceKey]
|
||||||
|
= this._sessionProblems[deviceKey] || [];
|
||||||
|
problems.push({type, fixed, time: Date.now()});
|
||||||
|
problems.sort((a, b) => {
|
||||||
|
return a.time - b.time;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getEndToEndSessionProblem(deviceKey, timestamp) {
|
||||||
|
const problems = this._sessionProblems[deviceKey] || [];
|
||||||
|
if (!problems.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const lastProblem = problems[problems.length - 1];
|
||||||
|
for (const problem of problems) {
|
||||||
|
if (problem.time > timestamp) {
|
||||||
|
return Object.assign({}, problem, {fixed: lastProblem.fixed});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lastProblem.fixed) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return lastProblem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async filterOutNotifiedErrorDevices(devices) {
|
||||||
|
const notifiedErrorDevices = this._notifiedErrorDevices;
|
||||||
|
const ret = [];
|
||||||
|
|
||||||
|
for (const device of devices) {
|
||||||
|
const {userId, deviceInfo} = device;
|
||||||
|
if (userId in notifiedErrorDevices) {
|
||||||
|
if (!(deviceInfo.deviceId in notifiedErrorDevices[userId])) {
|
||||||
|
ret.push(device);
|
||||||
|
notifiedErrorDevices[userId][deviceInfo.deviceId] = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret.push(device);
|
||||||
|
notifiedErrorDevices[userId] = {[deviceInfo.deviceId]: true };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
// Inbound Group Sessions
|
// Inbound Group Sessions
|
||||||
|
|
||||||
getEndToEndInboundGroupSession(senderCurve25519Key, sessionId, txn, func) {
|
getEndToEndInboundGroupSession(senderCurve25519Key, sessionId, txn, func) {
|
||||||
func(this._inboundGroupSessions[senderCurve25519Key+'/'+sessionId] || null);
|
const k = senderCurve25519Key+'/'+sessionId;
|
||||||
|
func(
|
||||||
|
this._inboundGroupSessions[k] || null,
|
||||||
|
this._inboundGroupSessionsWithheld[k] || null,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllEndToEndInboundGroupSessions(txn, func) {
|
getAllEndToEndInboundGroupSessions(txn, func) {
|
||||||
@@ -306,6 +363,13 @@ export default class MemoryCryptoStore {
|
|||||||
this._inboundGroupSessions[senderCurve25519Key+'/'+sessionId] = sessionData;
|
this._inboundGroupSessions[senderCurve25519Key+'/'+sessionId] = sessionData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
storeEndToEndInboundGroupSessionWithheld(
|
||||||
|
senderCurve25519Key, sessionId, sessionData, txn,
|
||||||
|
) {
|
||||||
|
const k = senderCurve25519Key+'/'+sessionId;
|
||||||
|
this._inboundGroupSessionsWithheld[k] = sessionData;
|
||||||
|
}
|
||||||
|
|
||||||
// Device Data
|
// Device Data
|
||||||
|
|
||||||
getEndToEndDeviceData(txn, func) {
|
getEndToEndDeviceData(txn, func) {
|
||||||
|
|||||||
@@ -21,13 +21,13 @@ limitations under the License.
|
|||||||
|
|
||||||
import {MatrixEvent} from '../../models/event';
|
import {MatrixEvent} from '../../models/event';
|
||||||
import {EventEmitter} from 'events';
|
import {EventEmitter} from 'events';
|
||||||
import logger from '../../logger';
|
import {logger} from '../../logger';
|
||||||
import DeviceInfo from '../deviceinfo';
|
import {DeviceInfo} from '../deviceinfo';
|
||||||
import {newTimeoutError} from "./Error";
|
import {newTimeoutError} from "./Error";
|
||||||
|
|
||||||
const timeoutException = new Error("Verification timed out");
|
const timeoutException = new Error("Verification timed out");
|
||||||
|
|
||||||
export default class VerificationBase extends EventEmitter {
|
export class VerificationBase extends EventEmitter {
|
||||||
/**
|
/**
|
||||||
* Base class for verification methods.
|
* Base class for verification methods.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -19,11 +19,11 @@ limitations under the License.
|
|||||||
* @module crypto/verification/QRCode
|
* @module crypto/verification/QRCode
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Base from "./Base";
|
import {VerificationBase as Base} from "./Base";
|
||||||
import {
|
import {
|
||||||
errorFactory,
|
errorFactory,
|
||||||
newUserCancelledError,
|
|
||||||
newKeyMismatchError,
|
newKeyMismatchError,
|
||||||
|
newUserCancelledError,
|
||||||
newUserMismatchError,
|
newUserMismatchError,
|
||||||
} from './Error';
|
} from './Error';
|
||||||
|
|
||||||
|
|||||||
@@ -19,14 +19,14 @@ limitations under the License.
|
|||||||
* @module crypto/verification/SAS
|
* @module crypto/verification/SAS
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Base from "./Base";
|
import {VerificationBase as Base} from "./Base";
|
||||||
import anotherjson from 'another-json';
|
import anotherjson from 'another-json';
|
||||||
import {
|
import {
|
||||||
errorFactory,
|
errorFactory,
|
||||||
newUserCancelledError,
|
|
||||||
newUnknownMethodError,
|
|
||||||
newKeyMismatchError,
|
|
||||||
newInvalidMessageError,
|
newInvalidMessageError,
|
||||||
|
newKeyMismatchError,
|
||||||
|
newUnknownMethodError,
|
||||||
|
newUserCancelledError,
|
||||||
} from './Error';
|
} from './Error';
|
||||||
|
|
||||||
const EVENTS = [
|
const EVENTS = [
|
||||||
@@ -108,7 +108,7 @@ const emojiMapping = [
|
|||||||
["✏️", "pencil"], // 43
|
["✏️", "pencil"], // 43
|
||||||
["📎", "paperclip"], // 44
|
["📎", "paperclip"], // 44
|
||||||
["✂️", "scissors"], // 45
|
["✂️", "scissors"], // 45
|
||||||
["🔒", "padlock"], // 46
|
["🔒", "lock"], // 46
|
||||||
["🔑", "key"], // 47
|
["🔑", "key"], // 47
|
||||||
["🔨", "hammer"], // 48
|
["🔨", "hammer"], // 48
|
||||||
["☎️", "telephone"], // 49
|
["☎️", "telephone"], // 49
|
||||||
@@ -185,7 +185,11 @@ function intersection(anArray, aSet) {
|
|||||||
* @alias module:crypto/verification/SAS
|
* @alias module:crypto/verification/SAS
|
||||||
* @extends {module:crypto/verification/Base}
|
* @extends {module:crypto/verification/Base}
|
||||||
*/
|
*/
|
||||||
export default class SAS extends Base {
|
export class SAS extends Base {
|
||||||
|
static get NAME() {
|
||||||
|
return "m.sas.v1";
|
||||||
|
}
|
||||||
|
|
||||||
get events() {
|
get events() {
|
||||||
return EVENTS;
|
return EVENTS;
|
||||||
}
|
}
|
||||||
@@ -426,5 +430,3 @@ export default class SAS extends Base {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SAS.NAME = "m.sas.v1";
|
|
||||||
|
|||||||
@@ -15,11 +15,13 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import VerificationRequest, {
|
import {
|
||||||
|
VerificationRequest,
|
||||||
REQUEST_TYPE,
|
REQUEST_TYPE,
|
||||||
READY_TYPE,
|
READY_TYPE,
|
||||||
START_TYPE,
|
START_TYPE,
|
||||||
} from "./VerificationRequest";
|
} from "./VerificationRequest";
|
||||||
|
|
||||||
const MESSAGE_TYPE = "m.room.message";
|
const MESSAGE_TYPE = "m.room.message";
|
||||||
const M_REFERENCE = "m.reference";
|
const M_REFERENCE = "m.reference";
|
||||||
const M_RELATES_TO = "m.relates_to";
|
const M_RELATES_TO = "m.relates_to";
|
||||||
|
|||||||
@@ -16,23 +16,18 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {randomString} from '../../../randomstring';
|
import {randomString} from '../../../randomstring';
|
||||||
import logger from '../../../logger';
|
import {logger} from '../../../logger';
|
||||||
import VerificationRequest, {
|
import {
|
||||||
|
CANCEL_TYPE,
|
||||||
PHASE_STARTED,
|
PHASE_STARTED,
|
||||||
PHASE_READY,
|
PHASE_READY,
|
||||||
REQUEST_TYPE,
|
REQUEST_TYPE,
|
||||||
READY_TYPE,
|
READY_TYPE,
|
||||||
START_TYPE,
|
START_TYPE,
|
||||||
CANCEL_TYPE,
|
VerificationRequest,
|
||||||
} from "./VerificationRequest";
|
} from "./VerificationRequest";
|
||||||
|
import {errorFromEvent, newUnexpectedMessageError} from "../Error";
|
||||||
import {
|
import {MatrixEvent} from "../../../models/event";
|
||||||
newUnexpectedMessageError,
|
|
||||||
errorFromEvent,
|
|
||||||
} from "../Error";
|
|
||||||
|
|
||||||
const MatrixEvent = require("../../../models/event").MatrixEvent;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A key verification channel that sends verification events over to_device messages.
|
* A key verification channel that sends verification events over to_device messages.
|
||||||
|
|||||||
@@ -15,13 +15,13 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import logger from '../../../logger';
|
import {logger} from '../../../logger';
|
||||||
import {EventEmitter} from 'events';
|
import {EventEmitter} from 'events';
|
||||||
import {
|
import {
|
||||||
newUnknownMethodError,
|
|
||||||
newUnexpectedMessageError,
|
|
||||||
errorFromEvent,
|
|
||||||
errorFactory,
|
errorFactory,
|
||||||
|
errorFromEvent,
|
||||||
|
newUnexpectedMessageError,
|
||||||
|
newUnknownMethodError,
|
||||||
} from "../Error";
|
} from "../Error";
|
||||||
|
|
||||||
// the recommended amount of time before a verification request
|
// the recommended amount of time before a verification request
|
||||||
@@ -56,7 +56,7 @@ export const PHASE_DONE = 6;
|
|||||||
* send and receive verification events are put in `InRoomChannel` or `ToDeviceChannel`.
|
* send and receive verification events are put in `InRoomChannel` or `ToDeviceChannel`.
|
||||||
* @event "change" whenever the state of the request object has changed.
|
* @event "change" whenever the state of the request object has changed.
|
||||||
*/
|
*/
|
||||||
export default class VerificationRequest extends EventEmitter {
|
export class VerificationRequest extends EventEmitter {
|
||||||
constructor(channel, verificationMethods, client) {
|
constructor(channel, verificationMethods, client) {
|
||||||
super();
|
super();
|
||||||
this.channel = channel;
|
this.channel = channel;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2016 OpenMarket Ltd
|
Copyright 2016 OpenMarket Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -13,7 +14,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
"use strict";
|
|
||||||
/**
|
/**
|
||||||
* @module filter-component
|
* @module filter-component
|
||||||
*/
|
*/
|
||||||
@@ -45,7 +46,7 @@ function _matches_wildcard(actual_value, filter_value) {
|
|||||||
* @constructor
|
* @constructor
|
||||||
* @param {Object} filter_json the definition of this filter JSON, e.g. { 'contains_url': true }
|
* @param {Object} filter_json the definition of this filter JSON, e.g. { 'contains_url': true }
|
||||||
*/
|
*/
|
||||||
function FilterComponent(filter_json) {
|
export function FilterComponent(filter_json) {
|
||||||
this.filter_json = filter_json;
|
this.filter_json = filter_json;
|
||||||
|
|
||||||
this.types = filter_json.types || null;
|
this.types = filter_json.types || null;
|
||||||
@@ -141,6 +142,3 @@ FilterComponent.prototype.filter = function(events) {
|
|||||||
FilterComponent.prototype.limit = function() {
|
FilterComponent.prototype.limit = function() {
|
||||||
return this.filter_json.limit !== undefined ? this.filter_json.limit : 10;
|
return this.filter_json.limit !== undefined ? this.filter_json.limit : 10;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** The FilterComponent class */
|
|
||||||
module.exports = FilterComponent;
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -13,12 +14,12 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
"use strict";
|
|
||||||
/**
|
/**
|
||||||
* @module filter
|
* @module filter
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const FilterComponent = require("./filter-component");
|
import {FilterComponent} from "./filter-component";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} obj
|
* @param {Object} obj
|
||||||
@@ -45,7 +46,7 @@ function setProp(obj, keyNesting, val) {
|
|||||||
* @prop {string} userId The user ID of the filter
|
* @prop {string} userId The user ID of the filter
|
||||||
* @prop {?string} filterId The filter ID
|
* @prop {?string} filterId The filter ID
|
||||||
*/
|
*/
|
||||||
function Filter(userId, filterId) {
|
export function Filter(userId, filterId) {
|
||||||
this.userId = userId;
|
this.userId = userId;
|
||||||
this.filterId = filterId;
|
this.filterId = filterId;
|
||||||
this.definition = {};
|
this.definition = {};
|
||||||
@@ -198,6 +199,3 @@ Filter.fromJson = function(userId, filterId, jsonObj) {
|
|||||||
filter.setDefinition(jsonObj);
|
filter.setDefinition(jsonObj);
|
||||||
return filter;
|
return filter;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** The Filter class */
|
|
||||||
module.exports = Filter;
|
|
||||||
|
|||||||
@@ -14,20 +14,20 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
"use strict";
|
|
||||||
/**
|
/**
|
||||||
* This is an internal module. See {@link MatrixHttpApi} for the public class.
|
* This is an internal module. See {@link MatrixHttpApi} for the public class.
|
||||||
* @module http-api
|
* @module http-api
|
||||||
*/
|
*/
|
||||||
const parseContentType = require('content-type').parse;
|
|
||||||
|
|
||||||
const utils = require("./utils");
|
import {parse as parseContentType} from "content-type";
|
||||||
import logger from './logger';
|
import * as utils from "./utils";
|
||||||
|
import {logger} from './logger';
|
||||||
|
|
||||||
// we use our own implementation of setTimeout, so that if we get suspended in
|
// we use our own implementation of setTimeout, so that if we get suspended in
|
||||||
// the middle of a /sync, we cancel the sync as soon as we awake, rather than
|
// the middle of a /sync, we cancel the sync as soon as we awake, rather than
|
||||||
// waiting for the delay to elapse.
|
// waiting for the delay to elapse.
|
||||||
const callbacks = require("./realtime-callbacks");
|
import * as callbacks from "./realtime-callbacks";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TODO:
|
TODO:
|
||||||
@@ -38,27 +38,27 @@ TODO:
|
|||||||
/**
|
/**
|
||||||
* A constant representing the URI path for release 0 of the Client-Server HTTP API.
|
* A constant representing the URI path for release 0 of the Client-Server HTTP API.
|
||||||
*/
|
*/
|
||||||
module.exports.PREFIX_R0 = "/_matrix/client/r0";
|
export const PREFIX_R0 = "/_matrix/client/r0";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A constant representing the URI path for as-yet unspecified Client-Server HTTP APIs.
|
* A constant representing the URI path for as-yet unspecified Client-Server HTTP APIs.
|
||||||
*/
|
*/
|
||||||
module.exports.PREFIX_UNSTABLE = "/_matrix/client/unstable";
|
export const PREFIX_UNSTABLE = "/_matrix/client/unstable";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* URI path for v1 of the the identity API
|
* URI path for v1 of the the identity API
|
||||||
*/
|
*/
|
||||||
module.exports.PREFIX_IDENTITY_V1 = "/_matrix/identity/api/v1";
|
export const PREFIX_IDENTITY_V1 = "/_matrix/identity/api/v1";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* URI path for the v2 identity API
|
* URI path for the v2 identity API
|
||||||
*/
|
*/
|
||||||
module.exports.PREFIX_IDENTITY_V2 = "/_matrix/identity/v2";
|
export const PREFIX_IDENTITY_V2 = "/_matrix/identity/v2";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* URI path for the media repo API
|
* URI path for the media repo API
|
||||||
*/
|
*/
|
||||||
module.exports.PREFIX_MEDIA_R0 = "/_matrix/media/r0";
|
export const PREFIX_MEDIA_R0 = "/_matrix/media/r0";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a MatrixHttpApi.
|
* Construct a MatrixHttpApi.
|
||||||
@@ -85,16 +85,16 @@ module.exports.PREFIX_MEDIA_R0 = "/_matrix/media/r0";
|
|||||||
* @param {boolean} [opts.useAuthorizationHeader = false] Set to true to use
|
* @param {boolean} [opts.useAuthorizationHeader = false] Set to true to use
|
||||||
* Authorization header instead of query param to send the access token to the server.
|
* Authorization header instead of query param to send the access token to the server.
|
||||||
*/
|
*/
|
||||||
module.exports.MatrixHttpApi = function MatrixHttpApi(event_emitter, opts) {
|
export function MatrixHttpApi(event_emitter, opts) {
|
||||||
utils.checkObjectHasKeys(opts, ["baseUrl", "request", "prefix"]);
|
utils.checkObjectHasKeys(opts, ["baseUrl", "request", "prefix"]);
|
||||||
opts.onlyData = opts.onlyData || false;
|
opts.onlyData = opts.onlyData || false;
|
||||||
this.event_emitter = event_emitter;
|
this.event_emitter = event_emitter;
|
||||||
this.opts = opts;
|
this.opts = opts;
|
||||||
this.useAuthorizationHeader = Boolean(opts.useAuthorizationHeader);
|
this.useAuthorizationHeader = Boolean(opts.useAuthorizationHeader);
|
||||||
this.uploads = [];
|
this.uploads = [];
|
||||||
};
|
}
|
||||||
|
|
||||||
module.exports.MatrixHttpApi.prototype = {
|
MatrixHttpApi.prototype = {
|
||||||
/**
|
/**
|
||||||
* Sets the baase URL for the identity server
|
* Sets the baase URL for the identity server
|
||||||
* @param {string} url The new base url
|
* @param {string} url The new base url
|
||||||
@@ -698,7 +698,7 @@ module.exports.MatrixHttpApi.prototype = {
|
|||||||
if (req && req.abort) {
|
if (req && req.abort) {
|
||||||
req.abort();
|
req.abort();
|
||||||
}
|
}
|
||||||
defer.reject(new module.exports.MatrixError({
|
defer.reject(new MatrixError({
|
||||||
error: "Locally timed out waiting for a response",
|
error: "Locally timed out waiting for a response",
|
||||||
errcode: "ORG.MATRIX.JSSDK_TIMEOUT",
|
errcode: "ORG.MATRIX.JSSDK_TIMEOUT",
|
||||||
timeout: localTimeoutMs,
|
timeout: localTimeoutMs,
|
||||||
@@ -836,7 +836,7 @@ function parseErrorResponse(response, body) {
|
|||||||
if (contentType) {
|
if (contentType) {
|
||||||
if (contentType.type === 'application/json') {
|
if (contentType.type === 'application/json') {
|
||||||
const jsonBody = typeof(body) === 'object' ? body : JSON.parse(body);
|
const jsonBody = typeof(body) === 'object' ? body : JSON.parse(body);
|
||||||
err = new module.exports.MatrixError(jsonBody);
|
err = new MatrixError(jsonBody);
|
||||||
} else if (contentType.type === 'text/plain') {
|
} else if (contentType.type === 'text/plain') {
|
||||||
err = new Error(`Server returned ${httpStatus} error: ${body}`);
|
err = new Error(`Server returned ${httpStatus} error: ${body}`);
|
||||||
}
|
}
|
||||||
@@ -891,13 +891,12 @@ function getResponseContentType(response) {
|
|||||||
* @prop {Object} data The raw Matrix error JSON used to construct this object.
|
* @prop {Object} data The raw Matrix error JSON used to construct this object.
|
||||||
* @prop {integer} httpStatus The numeric HTTP status code given
|
* @prop {integer} httpStatus The numeric HTTP status code given
|
||||||
*/
|
*/
|
||||||
module.exports.MatrixError = function MatrixError(errorJson) {
|
export function MatrixError(errorJson) {
|
||||||
errorJson = errorJson || {};
|
errorJson = errorJson || {};
|
||||||
this.errcode = errorJson.errcode;
|
this.errcode = errorJson.errcode;
|
||||||
this.name = errorJson.errcode || "Unknown error code";
|
this.name = errorJson.errcode || "Unknown error code";
|
||||||
this.message = errorJson.error || "Unknown message";
|
this.message = errorJson.error || "Unknown message";
|
||||||
this.data = errorJson;
|
this.data = errorJson;
|
||||||
};
|
}
|
||||||
module.exports.MatrixError.prototype = Object.create(Error.prototype);
|
MatrixError.prototype = Object.create(Error.prototype);
|
||||||
/** */
|
MatrixError.prototype.constructor = MatrixError;
|
||||||
module.exports.MatrixError.prototype.constructor = module.exports.MatrixError;
|
|
||||||
|
|||||||
24
src/index.ts
Normal file
24
src/index.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2019 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 * as matrixcs from "./matrix";
|
||||||
|
import * as utils from "./utils";
|
||||||
|
|
||||||
|
matrixcs.request(import("request"));
|
||||||
|
utils.runPolyfills();
|
||||||
|
|
||||||
|
export * from "./matrix";
|
||||||
|
export default matrixcs;
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2017 Vector Creations Ltd
|
Copyright 2017 Vector Creations Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -20,5 +21,5 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/** The {@link module:indexeddb-store-worker~IndexedDBStoreWorker} class. */
|
/** The {@link module:indexeddb-store-worker~IndexedDBStoreWorker} class. */
|
||||||
module.exports.IndexedDBStoreWorker = require("./store/indexeddb-store-worker.js");
|
export {IndexedDBStoreWorker} from "./store/indexeddb-store-worker";
|
||||||
|
|
||||||
|
|||||||
@@ -15,13 +15,12 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
"use strict";
|
|
||||||
|
|
||||||
/** @module interactive-auth */
|
/** @module interactive-auth */
|
||||||
const url = require("url");
|
|
||||||
|
|
||||||
const utils = require("./utils");
|
import url from "url";
|
||||||
import logger from './logger';
|
import * as utils from "./utils";
|
||||||
|
import {logger} from './logger';
|
||||||
|
|
||||||
const EMAIL_STAGE_TYPE = "m.login.email.identity";
|
const EMAIL_STAGE_TYPE = "m.login.email.identity";
|
||||||
const MSISDN_STAGE_TYPE = "m.login.msisdn";
|
const MSISDN_STAGE_TYPE = "m.login.msisdn";
|
||||||
@@ -103,7 +102,7 @@ const MSISDN_STAGE_TYPE = "m.login.msisdn";
|
|||||||
* attemptAuth promise.
|
* attemptAuth promise.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
function InteractiveAuth(opts) {
|
export function InteractiveAuth(opts) {
|
||||||
this._matrixClient = opts.matrixClient;
|
this._matrixClient = opts.matrixClient;
|
||||||
this._data = opts.authData || {};
|
this._data = opts.authData || {};
|
||||||
this._requestCallback = opts.doRequest;
|
this._requestCallback = opts.doRequest;
|
||||||
@@ -510,6 +509,3 @@ InteractiveAuth.prototype = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/** */
|
|
||||||
module.exports = InteractiveAuth;
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2018 André Jaenisch
|
Copyright 2018 André Jaenisch
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -17,7 +18,8 @@ limitations under the License.
|
|||||||
/**
|
/**
|
||||||
* @module logger
|
* @module logger
|
||||||
*/
|
*/
|
||||||
const log = require("loglevel");
|
|
||||||
|
import log from "loglevel";
|
||||||
|
|
||||||
// This is to demonstrate, that you can use any namespace you want.
|
// This is to demonstrate, that you can use any namespace you want.
|
||||||
// Namespaces allow you to turn on/off the logging for specific parts of the
|
// Namespaces allow you to turn on/off the logging for specific parts of the
|
||||||
@@ -25,12 +27,12 @@ const log = require("loglevel");
|
|||||||
// An idea would be to control this via an environment variable (on Node.js).
|
// An idea would be to control this via an environment variable (on Node.js).
|
||||||
// See https://www.npmjs.com/package/debug to see how this could be implemented
|
// See https://www.npmjs.com/package/debug to see how this could be implemented
|
||||||
// Part of #332 is introducing a logging library in the first place.
|
// Part of #332 is introducing a logging library in the first place.
|
||||||
const DEFAULT_NAME_SPACE = "matrix";
|
const DEFAULT_NAMESPACE = "matrix";
|
||||||
const logger = log.getLogger(DEFAULT_NAME_SPACE);
|
|
||||||
logger.setLevel(log.levels.DEBUG);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Drop-in replacement for <code>console</code> using {@link https://www.npmjs.com/package/loglevel|loglevel}.
|
* Drop-in replacement for <code>console</code> using {@link https://www.npmjs.com/package/loglevel|loglevel}.
|
||||||
* Can be tailored down to specific use cases if needed.
|
* Can be tailored down to specific use cases if needed.
|
||||||
*/
|
*/
|
||||||
module.exports = logger;
|
export const logger = log.getLogger(DEFAULT_NAMESPACE);
|
||||||
|
logger.setLevel(log.levels.DEBUG);
|
||||||
|
|
||||||
|
|||||||
175
src/matrix.js
175
src/matrix.js
@@ -15,129 +15,66 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
"use strict";
|
|
||||||
|
|
||||||
/** The {@link module:ContentHelpers} object */
|
import {MemoryCryptoStore} from "./crypto/store/memory-crypto-store";
|
||||||
module.exports.ContentHelpers = require("./content-helpers");
|
import {MemoryStore} from "./store/memory";
|
||||||
/** The {@link module:models/event.MatrixEvent|MatrixEvent} class. */
|
import {MatrixScheduler} from "./scheduler";
|
||||||
module.exports.MatrixEvent = require("./models/event").MatrixEvent;
|
import {MatrixClient} from "./client";
|
||||||
/** The {@link module:models/event.EventStatus|EventStatus} enum. */
|
|
||||||
module.exports.EventStatus = require("./models/event").EventStatus;
|
|
||||||
/** The {@link module:store/memory.MemoryStore|MemoryStore} class. */
|
|
||||||
module.exports.MemoryStore = require("./store/memory").MemoryStore;
|
|
||||||
/**
|
|
||||||
* The {@link module:store/memory.MemoryStore|MemoryStore} class was previously
|
|
||||||
* exported as `MatrixInMemoryStore`, so this is preserved for SDK consumers.
|
|
||||||
* @deprecated Prefer `MemoryStore` going forward.
|
|
||||||
*/
|
|
||||||
module.exports.MatrixInMemoryStore = module.exports.MemoryStore;
|
|
||||||
/** The {@link module:store/indexeddb.IndexedDBStore|IndexedDBStore} class. */
|
|
||||||
module.exports.IndexedDBStore = require("./store/indexeddb").IndexedDBStore;
|
|
||||||
/** The {@link module:store/indexeddb.IndexedDBStoreBackend|IndexedDBStoreBackend} class. */
|
|
||||||
module.exports.IndexedDBStoreBackend = require("./store/indexeddb").IndexedDBStoreBackend;
|
|
||||||
/** The {@link module:sync-accumulator.SyncAccumulator|SyncAccumulator} class. */
|
|
||||||
module.exports.SyncAccumulator = require("./sync-accumulator");
|
|
||||||
/** The {@link module:http-api.MatrixHttpApi|MatrixHttpApi} class. */
|
|
||||||
module.exports.MatrixHttpApi = require("./http-api").MatrixHttpApi;
|
|
||||||
/** The {@link module:http-api.MatrixError|MatrixError} class. */
|
|
||||||
module.exports.MatrixError = require("./http-api").MatrixError;
|
|
||||||
/** The {@link module:errors.InvalidStoreError|InvalidStoreError} class. */
|
|
||||||
module.exports.InvalidStoreError = require("./errors").InvalidStoreError;
|
|
||||||
/** The {@link module:client.MatrixClient|MatrixClient} class. */
|
|
||||||
module.exports.MatrixClient = require("./client").MatrixClient;
|
|
||||||
/** The {@link module:models/room|Room} class. */
|
|
||||||
module.exports.Room = require("./models/room");
|
|
||||||
/** The {@link module:models/group|Group} class. */
|
|
||||||
module.exports.Group = require("./models/group");
|
|
||||||
/** The {@link module:models/event-timeline~EventTimeline} class. */
|
|
||||||
module.exports.EventTimeline = require("./models/event-timeline");
|
|
||||||
/** The {@link module:models/event-timeline-set~EventTimelineSet} class. */
|
|
||||||
module.exports.EventTimelineSet = require("./models/event-timeline-set");
|
|
||||||
/** The {@link module:models/room-member|RoomMember} class. */
|
|
||||||
module.exports.RoomMember = require("./models/room-member");
|
|
||||||
/** The {@link module:models/room-state~RoomState|RoomState} class. */
|
|
||||||
module.exports.RoomState = require("./models/room-state");
|
|
||||||
/** The {@link module:models/user~User|User} class. */
|
|
||||||
module.exports.User = require("./models/user");
|
|
||||||
/** The {@link module:scheduler~MatrixScheduler|MatrixScheduler} class. */
|
|
||||||
module.exports.MatrixScheduler = require("./scheduler");
|
|
||||||
/** The {@link module:store/session/webstorage~WebStorageSessionStore|
|
|
||||||
* WebStorageSessionStore} class. <strong>Work in progress; unstable.</strong> */
|
|
||||||
module.exports.WebStorageSessionStore = require("./store/session/webstorage");
|
|
||||||
/** True if crypto libraries are being used on this client. */
|
|
||||||
module.exports.CRYPTO_ENABLED = require("./client").CRYPTO_ENABLED;
|
|
||||||
/** {@link module:content-repo|ContentRepo} utility functions. */
|
|
||||||
module.exports.ContentRepo = require("./content-repo");
|
|
||||||
/** The {@link module:filter~Filter|Filter} class. */
|
|
||||||
module.exports.Filter = require("./filter");
|
|
||||||
/** The {@link module:timeline-window~TimelineWindow} class. */
|
|
||||||
module.exports.TimelineWindow = require("./timeline-window").TimelineWindow;
|
|
||||||
/** The {@link module:interactive-auth} class. */
|
|
||||||
module.exports.InteractiveAuth = require("./interactive-auth");
|
|
||||||
/** The {@link module:auto-discovery|AutoDiscovery} class. */
|
|
||||||
module.exports.AutoDiscovery = require("./autodiscovery").AutoDiscovery;
|
|
||||||
|
|
||||||
module.exports.SERVICE_TYPES = require('./service-types').SERVICE_TYPES;
|
export * from "./client";
|
||||||
|
export * from "./http-api";
|
||||||
module.exports.MemoryCryptoStore =
|
export * from "./autodiscovery";
|
||||||
require("./crypto/store/memory-crypto-store").default;
|
export * from "./sync-accumulator";
|
||||||
module.exports.IndexedDBCryptoStore =
|
export * from "./errors";
|
||||||
require("./crypto/store/indexeddb-crypto-store").default;
|
export * from "./models/event";
|
||||||
|
export * from "./models/room";
|
||||||
/**
|
export * from "./models/group";
|
||||||
* Create a new Matrix Call.
|
export * from "./models/event-timeline";
|
||||||
* @function
|
export * from "./models/event-timeline-set";
|
||||||
* @param {module:client.MatrixClient} client The MatrixClient instance to use.
|
export * from "./models/room-member";
|
||||||
* @param {string} roomId The room the call is in.
|
export * from "./models/room-state";
|
||||||
* @return {module:webrtc/call~MatrixCall} The Matrix call or null if the browser
|
export * from "./models/user";
|
||||||
* does not support WebRTC.
|
export * from "./scheduler";
|
||||||
*/
|
export * from "./filter";
|
||||||
module.exports.createNewMatrixCall = require("./webrtc/call").createNewMatrixCall;
|
export * from "./timeline-window";
|
||||||
|
export * from "./interactive-auth";
|
||||||
|
export * from "./service-types";
|
||||||
/**
|
export * from "./store/memory";
|
||||||
* Set a preferred audio output device to use for MatrixCalls
|
export * from "./store/indexeddb";
|
||||||
* @function
|
export * from "./store/session/webstorage";
|
||||||
* @param {string=} deviceId the identifier for the device
|
export * from "./crypto/store/memory-crypto-store";
|
||||||
* undefined treated as unset
|
export * from "./crypto/store/indexeddb-crypto-store";
|
||||||
*/
|
export * from "./content-repo";
|
||||||
module.exports.setMatrixCallAudioOutput = require('./webrtc/call').setAudioOutput;
|
export const ContentHelpers = import("./content-helpers");
|
||||||
/**
|
export {
|
||||||
* Set a preferred audio input device to use for MatrixCalls
|
createNewMatrixCall,
|
||||||
* @function
|
setAudioOutput as setMatrixCallAudioOutput,
|
||||||
* @param {string=} deviceId the identifier for the device
|
setAudioInput as setMatrixCallAudioInput,
|
||||||
* undefined treated as unset
|
setVideoInput as setMatrixCallVideoInput,
|
||||||
*/
|
} from "./webrtc/call";
|
||||||
module.exports.setMatrixCallAudioInput = require('./webrtc/call').setAudioInput;
|
|
||||||
/**
|
|
||||||
* Set a preferred video input device to use for MatrixCalls
|
|
||||||
* @function
|
|
||||||
* @param {string=} deviceId the identifier for the device
|
|
||||||
* undefined treated as unset
|
|
||||||
*/
|
|
||||||
module.exports.setMatrixCallVideoInput = require('./webrtc/call').setVideoInput;
|
|
||||||
|
|
||||||
|
|
||||||
// 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 request;
|
let requestInstance;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The function used to perform HTTP requests. Only use this if you want to
|
* The function used to perform HTTP requests. Only use this if you want to
|
||||||
* use a different HTTP library, e.g. Angular's <code>$http</code>. This should
|
* use a different HTTP library, e.g. Angular's <code>$http</code>. This should
|
||||||
* be set prior to calling {@link createClient}.
|
* be set prior to calling {@link createClient}.
|
||||||
* @param {requestFunction} r The request function to use.
|
* @param {requestFunction} r The request function to use.
|
||||||
*/
|
*/
|
||||||
module.exports.request = function(r) {
|
export function request(r) {
|
||||||
request = r;
|
requestInstance = r;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the currently-set request function.
|
* Return the currently-set request function.
|
||||||
* @return {requestFunction} The current request function.
|
* @return {requestFunction} The current request function.
|
||||||
*/
|
*/
|
||||||
module.exports.getRequest = function() {
|
export function getRequest() {
|
||||||
return request;
|
return requestInstance;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply wrapping code around the request function. The wrapper function is
|
* Apply wrapping code around the request function. The wrapper function is
|
||||||
@@ -145,15 +82,15 @@ module.exports.getRequest = function() {
|
|||||||
* previous value, along with the options and callback arguments.
|
* previous value, along with the options and callback arguments.
|
||||||
* @param {requestWrapperFunction} wrapper The wrapping function.
|
* @param {requestWrapperFunction} wrapper The wrapping function.
|
||||||
*/
|
*/
|
||||||
module.exports.wrapRequest = function(wrapper) {
|
export function wrapRequest(wrapper) {
|
||||||
const origRequest = request;
|
const origRequest = requestInstance;
|
||||||
request = function(options, callback) {
|
requestInstance = function(options, callback) {
|
||||||
return wrapper(origRequest, options, callback);
|
return wrapper(origRequest, options, callback);
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
let cryptoStoreFactory = () => new module.exports.MemoryCryptoStore;
|
let cryptoStoreFactory = () => new MemoryCryptoStore;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure a different factory to be used for creating crypto stores
|
* Configure a different factory to be used for creating crypto stores
|
||||||
@@ -161,9 +98,9 @@ let cryptoStoreFactory = () => new module.exports.MemoryCryptoStore;
|
|||||||
* @param {Function} fac a function which will return a new
|
* @param {Function} fac a function which will return a new
|
||||||
* {@link module:crypto.store.base~CryptoStore}.
|
* {@link module:crypto.store.base~CryptoStore}.
|
||||||
*/
|
*/
|
||||||
module.exports.setCryptoStoreFactory = function(fac) {
|
export function setCryptoStoreFactory(fac) {
|
||||||
cryptoStoreFactory = fac;
|
cryptoStoreFactory = fac;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a Matrix Client. Similar to {@link module:client~MatrixClient}
|
* Construct a Matrix Client. Similar to {@link module:client~MatrixClient}
|
||||||
@@ -188,20 +125,20 @@ module.exports.setCryptoStoreFactory = function(fac) {
|
|||||||
* @see {@link module:client~MatrixClient} for the full list of options for
|
* @see {@link module:client~MatrixClient} for the full list of options for
|
||||||
* <code>opts</code>.
|
* <code>opts</code>.
|
||||||
*/
|
*/
|
||||||
module.exports.createClient = function(opts) {
|
export function createClient(opts) {
|
||||||
if (typeof opts === "string") {
|
if (typeof opts === "string") {
|
||||||
opts = {
|
opts = {
|
||||||
"baseUrl": opts,
|
"baseUrl": opts,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
opts.request = opts.request || request;
|
opts.request = opts.request || requestInstance;
|
||||||
opts.store = opts.store || new module.exports.MemoryStore({
|
opts.store = opts.store || new MemoryStore({
|
||||||
localStorage: global.localStorage,
|
localStorage: global.localStorage,
|
||||||
});
|
});
|
||||||
opts.scheduler = opts.scheduler || new module.exports.MatrixScheduler();
|
opts.scheduler = opts.scheduler || new MatrixScheduler();
|
||||||
opts.cryptoStore = opts.cryptoStore || cryptoStoreFactory();
|
opts.cryptoStore = opts.cryptoStore || cryptoStoreFactory();
|
||||||
return new module.exports.MatrixClient(opts);
|
return new MatrixClient(opts);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The request function interface for performing HTTP requests. This matches the
|
* The request function interface for performing HTTP requests. This matches the
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -13,7 +14,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
"use strict";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @module models/event-context
|
* @module models/event-context
|
||||||
@@ -33,7 +33,7 @@ limitations under the License.
|
|||||||
*
|
*
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function EventContext(ourEvent) {
|
export function EventContext(ourEvent) {
|
||||||
this._timeline = [ourEvent];
|
this._timeline = [ourEvent];
|
||||||
this._ourEventIndex = 0;
|
this._ourEventIndex = 0;
|
||||||
this._paginateTokens = {b: null, f: null};
|
this._paginateTokens = {b: null, f: null};
|
||||||
@@ -113,7 +113,3 @@ EventContext.prototype.addEvents = function(events, atStart) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* The EventContext class
|
|
||||||
*/
|
|
||||||
module.exports = EventContext;
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2016 OpenMarket Ltd
|
Copyright 2016 OpenMarket Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -13,16 +14,17 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
"use strict";
|
|
||||||
/**
|
/**
|
||||||
* @module models/event-timeline-set
|
* @module models/event-timeline-set
|
||||||
*/
|
*/
|
||||||
const EventEmitter = require("events").EventEmitter;
|
|
||||||
const utils = require("../utils");
|
import {EventEmitter} from "events";
|
||||||
const EventTimeline = require("./event-timeline");
|
import {EventTimeline} from "./event-timeline";
|
||||||
import {EventStatus} from "./event";
|
import {EventStatus} from "./event";
|
||||||
import logger from '../logger';
|
import * as utils from "../utils";
|
||||||
import Relations from './relations';
|
import {logger} from '../logger';
|
||||||
|
import {Relations} from './relations';
|
||||||
|
|
||||||
// var DEBUG = false;
|
// var DEBUG = false;
|
||||||
const DEBUG = true;
|
const DEBUG = true;
|
||||||
@@ -71,7 +73,7 @@ if (DEBUG) {
|
|||||||
* via `getRelationsForEvent`.
|
* via `getRelationsForEvent`.
|
||||||
* This feature is currently unstable and the API may change without notice.
|
* This feature is currently unstable and the API may change without notice.
|
||||||
*/
|
*/
|
||||||
function EventTimelineSet(room, opts) {
|
export function EventTimelineSet(room, opts) {
|
||||||
this.room = room;
|
this.room = room;
|
||||||
|
|
||||||
this._timelineSupport = Boolean(opts.timelineSupport);
|
this._timelineSupport = Boolean(opts.timelineSupport);
|
||||||
@@ -817,11 +819,6 @@ EventTimelineSet.prototype.aggregateRelations = function(event) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* The EventTimelineSet class.
|
|
||||||
*/
|
|
||||||
module.exports = EventTimelineSet;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fires whenever the timeline in a room is updated.
|
* Fires whenever the timeline in a room is updated.
|
||||||
* @event module:client~MatrixClient#"Room.timeline"
|
* @event module:client~MatrixClient#"Room.timeline"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2016, 2017 OpenMarket Ltd
|
Copyright 2016, 2017 OpenMarket Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -13,13 +14,12 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
"use strict";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @module models/event-timeline
|
* @module models/event-timeline
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const RoomState = require("./room-state");
|
import {RoomState} from "./room-state";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new EventTimeline
|
* Construct a new EventTimeline
|
||||||
@@ -41,7 +41,7 @@ const RoomState = require("./room-state");
|
|||||||
* @param {EventTimelineSet} eventTimelineSet the set of timelines this is part of
|
* @param {EventTimelineSet} eventTimelineSet the set of timelines this is part of
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function EventTimeline(eventTimelineSet) {
|
export function EventTimeline(eventTimelineSet) {
|
||||||
this._eventTimelineSet = eventTimelineSet;
|
this._eventTimelineSet = eventTimelineSet;
|
||||||
this._roomId = eventTimelineSet.room ? eventTimelineSet.room.roomId : null;
|
this._roomId = eventTimelineSet.room ? eventTimelineSet.room.roomId : null;
|
||||||
this._events = [];
|
this._events = [];
|
||||||
@@ -396,8 +396,3 @@ EventTimeline.prototype.toString = function() {
|
|||||||
return this._name;
|
return this._name;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The EventTimeline class
|
|
||||||
*/
|
|
||||||
module.exports = EventTimeline;
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -13,7 +14,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
"use strict";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is an internal module. See {@link MatrixEvent} and {@link RoomEvent} for
|
* This is an internal module. See {@link MatrixEvent} and {@link RoomEvent} for
|
||||||
@@ -22,15 +22,15 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {EventEmitter} from 'events';
|
import {EventEmitter} from 'events';
|
||||||
import utils from '../utils.js';
|
import * as utils from '../utils.js';
|
||||||
import logger from '../logger';
|
import {logger} from '../logger';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enum for event statuses.
|
* Enum for event statuses.
|
||||||
* @readonly
|
* @readonly
|
||||||
* @enum {string}
|
* @enum {string}
|
||||||
*/
|
*/
|
||||||
const EventStatus = {
|
export const EventStatus = {
|
||||||
/** The event was not sent and will no longer be retried. */
|
/** The event was not sent and will no longer be retried. */
|
||||||
NOT_SENT: "not_sent",
|
NOT_SENT: "not_sent",
|
||||||
|
|
||||||
@@ -48,7 +48,6 @@ const EventStatus = {
|
|||||||
/** The event was cancelled before it was successfully sent. */
|
/** The event was cancelled before it was successfully sent. */
|
||||||
CANCELLED: "cancelled",
|
CANCELLED: "cancelled",
|
||||||
};
|
};
|
||||||
module.exports.EventStatus = EventStatus;
|
|
||||||
|
|
||||||
const interns = {};
|
const interns = {};
|
||||||
function intern(str) {
|
function intern(str) {
|
||||||
@@ -81,7 +80,7 @@ function intern(str) {
|
|||||||
* that getDirectionalContent() will return event.content and not event.prev_content.
|
* that getDirectionalContent() will return event.content and not event.prev_content.
|
||||||
* Default: true. <strong>This property is experimental and may change.</strong>
|
* Default: true. <strong>This property is experimental and may change.</strong>
|
||||||
*/
|
*/
|
||||||
module.exports.MatrixEvent = function MatrixEvent(
|
export const MatrixEvent = function(
|
||||||
event,
|
event,
|
||||||
) {
|
) {
|
||||||
// intern the values of matrix events to force share strings and reduce the
|
// intern the values of matrix events to force share strings and reduce the
|
||||||
@@ -162,10 +161,10 @@ module.exports.MatrixEvent = function MatrixEvent(
|
|||||||
*/
|
*/
|
||||||
this.verificationRequest = null;
|
this.verificationRequest = null;
|
||||||
};
|
};
|
||||||
utils.inherits(module.exports.MatrixEvent, EventEmitter);
|
utils.inherits(MatrixEvent, EventEmitter);
|
||||||
|
|
||||||
|
|
||||||
utils.extend(module.exports.MatrixEvent.prototype, {
|
utils.extend(MatrixEvent.prototype, {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the event_id for this event.
|
* Get the event_id for this event.
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2017 New Vector Ltd
|
Copyright 2017 New Vector Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -17,9 +18,9 @@ limitations under the License.
|
|||||||
/**
|
/**
|
||||||
* @module models/group
|
* @module models/group
|
||||||
*/
|
*/
|
||||||
const EventEmitter = require("events").EventEmitter;
|
|
||||||
|
|
||||||
const utils = require("../utils");
|
import * as utils from "../utils";
|
||||||
|
import {EventEmitter} from "events";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new Group.
|
* Construct a new Group.
|
||||||
@@ -34,7 +35,7 @@ const utils = require("../utils");
|
|||||||
* to the group, if myMembership is 'invite'.
|
* to the group, if myMembership is 'invite'.
|
||||||
* @prop {string} inviter.userId The user ID of the inviter
|
* @prop {string} inviter.userId The user ID of the inviter
|
||||||
*/
|
*/
|
||||||
function Group(groupId) {
|
export function Group(groupId) {
|
||||||
this.groupId = groupId;
|
this.groupId = groupId;
|
||||||
this.name = null;
|
this.name = null;
|
||||||
this.avatarUrl = null;
|
this.avatarUrl = null;
|
||||||
@@ -70,8 +71,6 @@ Group.prototype.setInviter = function(inviter) {
|
|||||||
this.inviter = inviter;
|
this.inviter = inviter;
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = Group;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fires whenever a group's profile information is updated.
|
* Fires whenever a group's profile information is updated.
|
||||||
* This means the 'name' and 'avatarUrl' properties.
|
* This means the 'name' and 'avatarUrl' properties.
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import EventEmitter from 'events';
|
import {EventEmitter} from 'events';
|
||||||
import {EventStatus} from '../models/event';
|
import {EventStatus} from '../models/event';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -25,7 +25,7 @@ import { EventStatus } from '../models/event';
|
|||||||
* The typical way to get one of these containers is via
|
* The typical way to get one of these containers is via
|
||||||
* EventTimelineSet#getRelationsForEvent.
|
* EventTimelineSet#getRelationsForEvent.
|
||||||
*/
|
*/
|
||||||
export default class Relations extends EventEmitter {
|
export class Relations extends EventEmitter {
|
||||||
/**
|
/**
|
||||||
* @param {String} relationType
|
* @param {String} relationType
|
||||||
* The type of relation involved, such as "m.annotation", "m.reference",
|
* The type of relation involved, such as "m.annotation", "m.reference",
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -13,14 +14,14 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
"use strict";
|
|
||||||
/**
|
/**
|
||||||
* @module models/room-member
|
* @module models/room-member
|
||||||
*/
|
*/
|
||||||
const EventEmitter = require("events").EventEmitter;
|
|
||||||
const ContentRepo = require("../content-repo");
|
|
||||||
|
|
||||||
const utils = require("../utils");
|
import {EventEmitter} from "events";
|
||||||
|
import {getHttpUriForMxc, getIdenticonUri} from "../content-repo";
|
||||||
|
import * as utils from "../utils";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new room member.
|
* Construct a new room member.
|
||||||
@@ -45,7 +46,7 @@ const utils = require("../utils");
|
|||||||
* @prop {Object} events The events describing this RoomMember.
|
* @prop {Object} events The events describing this RoomMember.
|
||||||
* @prop {MatrixEvent} events.member The m.room.member event for this RoomMember.
|
* @prop {MatrixEvent} events.member The m.room.member event for this RoomMember.
|
||||||
*/
|
*/
|
||||||
function RoomMember(roomId, userId) {
|
export function RoomMember(roomId, userId) {
|
||||||
this.roomId = roomId;
|
this.roomId = roomId;
|
||||||
this.userId = userId;
|
this.userId = userId;
|
||||||
this.typing = false;
|
this.typing = false;
|
||||||
@@ -268,13 +269,13 @@ RoomMember.prototype.getAvatarUrl =
|
|||||||
if (!rawUrl && !allowDefault) {
|
if (!rawUrl && !allowDefault) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const httpUrl = ContentRepo.getHttpUriForMxc(
|
const httpUrl = getHttpUriForMxc(
|
||||||
baseUrl, rawUrl, width, height, resizeMethod, allowDirectLinks,
|
baseUrl, rawUrl, width, height, resizeMethod, allowDirectLinks,
|
||||||
);
|
);
|
||||||
if (httpUrl) {
|
if (httpUrl) {
|
||||||
return httpUrl;
|
return httpUrl;
|
||||||
} else if (allowDefault) {
|
} else if (allowDefault) {
|
||||||
return ContentRepo.getIdenticonUri(
|
return getIdenticonUri(
|
||||||
baseUrl, this.userId, width, height,
|
baseUrl, this.userId, width, height,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -311,10 +312,18 @@ function calculateDisplayName(selfUserId, displayName, roomState) {
|
|||||||
// Next check if the name contains something that look like a mxid
|
// Next check if the name contains something that look like a mxid
|
||||||
// If it does, it may be someone trying to impersonate someone else
|
// If it does, it may be someone trying to impersonate someone else
|
||||||
// Show full mxid in this case
|
// Show full mxid in this case
|
||||||
|
let disambiguate = /@.+:.+/.test(displayName);
|
||||||
|
|
||||||
|
if (!disambiguate) {
|
||||||
|
// Also show mxid if the display name contains any LTR/RTL characters as these
|
||||||
|
// make it very difficult for us to find similar *looking* display names
|
||||||
|
// E.g "Mark" could be cloned by writing "kraM" but in RTL.
|
||||||
|
disambiguate = /[\u200E\u200F\u202A-\u202F]/.test(displayName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!disambiguate) {
|
||||||
// Also show mxid if there are other people with the same or similar
|
// Also show mxid if there are other people with the same or similar
|
||||||
// displayname, after hidden character removal.
|
// displayname, after hidden character removal.
|
||||||
let disambiguate = /@.+:.+/.test(displayName);
|
|
||||||
if (!disambiguate) {
|
|
||||||
const userIds = roomState.getUserIdsWithDisplayName(displayName);
|
const userIds = roomState.getUserIdsWithDisplayName(displayName);
|
||||||
disambiguate = userIds.some((u) => u !== selfUserId);
|
disambiguate = userIds.some((u) => u !== selfUserId);
|
||||||
}
|
}
|
||||||
@@ -325,11 +334,6 @@ function calculateDisplayName(selfUserId, displayName, roomState) {
|
|||||||
return displayName;
|
return displayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The RoomMember class.
|
|
||||||
*/
|
|
||||||
module.exports = RoomMember;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fires whenever any room member's name changes.
|
* Fires whenever any room member's name changes.
|
||||||
* @event module:client~MatrixClient#"RoomMember.name"
|
* @event module:client~MatrixClient#"RoomMember.name"
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user