You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-11-28 05:03:59 +03:00
Merge remote-tracking branch 'upstream/dbkr/e2e_backups' into e2e_backups
This commit is contained in:
35
CHANGELOG.md
35
CHANGELOG.md
@@ -1,3 +1,38 @@
|
|||||||
|
Changes in [0.11.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.11.1) (2018-10-01)
|
||||||
|
==================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.11.1-rc.1...v0.11.1)
|
||||||
|
|
||||||
|
* No changes since rc.1
|
||||||
|
|
||||||
|
Changes in [0.11.1-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.11.1-rc.1) (2018-09-27)
|
||||||
|
============================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.11.0...v0.11.1-rc.1)
|
||||||
|
|
||||||
|
* make usage of hub compatible with latest version (2.5)
|
||||||
|
[\#747](https://github.com/matrix-org/matrix-js-sdk/pull/747)
|
||||||
|
* Detect when lazy loading has been toggled in client.startClient
|
||||||
|
[\#746](https://github.com/matrix-org/matrix-js-sdk/pull/746)
|
||||||
|
* Add getMediaLimits to client
|
||||||
|
[\#644](https://github.com/matrix-org/matrix-js-sdk/pull/644)
|
||||||
|
* Split npm start into an init and watch script
|
||||||
|
[\#742](https://github.com/matrix-org/matrix-js-sdk/pull/742)
|
||||||
|
* Revert "room name should only take canonical alias into account"
|
||||||
|
[\#738](https://github.com/matrix-org/matrix-js-sdk/pull/738)
|
||||||
|
* fix display name disambiguation with LL
|
||||||
|
[\#737](https://github.com/matrix-org/matrix-js-sdk/pull/737)
|
||||||
|
* Introduce Room.myMembership event
|
||||||
|
[\#735](https://github.com/matrix-org/matrix-js-sdk/pull/735)
|
||||||
|
* room name should only take canonical alias into account
|
||||||
|
[\#733](https://github.com/matrix-org/matrix-js-sdk/pull/733)
|
||||||
|
* state events from context response were not wrapped in a MatrixEvent
|
||||||
|
[\#732](https://github.com/matrix-org/matrix-js-sdk/pull/732)
|
||||||
|
* Reduce amount of promises created when inserting members
|
||||||
|
[\#724](https://github.com/matrix-org/matrix-js-sdk/pull/724)
|
||||||
|
* dont wait for LL members to be stored to resolve the members
|
||||||
|
[\#726](https://github.com/matrix-org/matrix-js-sdk/pull/726)
|
||||||
|
* RoomState.members emitted with wrong argument order for OOB members
|
||||||
|
[\#728](https://github.com/matrix-org/matrix-js-sdk/pull/728)
|
||||||
|
|
||||||
Changes in [0.11.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.11.0) (2018-09-10)
|
Changes in [0.11.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.11.0) (2018-09-10)
|
||||||
==================================================================================================
|
==================================================================================================
|
||||||
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.11.0-rc.1...v0.11.0)
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.11.0-rc.1...v0.11.0)
|
||||||
|
|||||||
11
package.json
11
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "matrix-js-sdk",
|
"name": "matrix-js-sdk",
|
||||||
"version": "0.11.0",
|
"version": "0.11.1",
|
||||||
"description": "Matrix Client-Server SDK for Javascript",
|
"description": "Matrix Client-Server SDK for Javascript",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -10,7 +10,9 @@
|
|||||||
"test": "npm run test:build && npm run test:run",
|
"test": "npm run test:build && npm run test:run",
|
||||||
"check": "npm run test:build && _mocha --recursive specbuild --colors",
|
"check": "npm run test:build && _mocha --recursive specbuild --colors",
|
||||||
"gendoc": "babel --no-babelrc -d .jsdocbuild src && jsdoc -r .jsdocbuild -P package.json -R README.md -d .jsdoc",
|
"gendoc": "babel --no-babelrc -d .jsdocbuild src && jsdoc -r .jsdocbuild -P package.json -R README.md -d .jsdoc",
|
||||||
"start": "babel -s -w -d lib src",
|
"start": "npm run start:init && npm run start:watch",
|
||||||
|
"start:watch": "babel -s -w --skip-initial-build -d lib src",
|
||||||
|
"start:init": "babel -s -d lib src",
|
||||||
"clean": "rimraf lib dist",
|
"clean": "rimraf lib dist",
|
||||||
"build": "babel -s -d lib src && rimraf dist && mkdir dist && browserify -d browser-index.js | exorcist dist/browser-matrix.js.map > dist/browser-matrix.js && uglifyjs -c -m -o dist/browser-matrix.min.js --source-map dist/browser-matrix.min.js.map --in-source-map dist/browser-matrix.js.map dist/browser-matrix.js",
|
"build": "babel -s -d lib src && rimraf dist && mkdir dist && browserify -d browser-index.js | exorcist dist/browser-matrix.js.map > dist/browser-matrix.js && uglifyjs -c -m -o dist/browser-matrix.min.js --source-map dist/browser-matrix.min.js.map --in-source-map dist/browser-matrix.js.map dist/browser-matrix.js",
|
||||||
"dist": "npm run build",
|
"dist": "npm run build",
|
||||||
@@ -53,6 +55,7 @@
|
|||||||
"babel-runtime": "^6.26.0",
|
"babel-runtime": "^6.26.0",
|
||||||
"bluebird": "^3.5.0",
|
"bluebird": "^3.5.0",
|
||||||
"browser-request": "^0.3.3",
|
"browser-request": "^0.3.3",
|
||||||
|
"bs58": "^4.0.1",
|
||||||
"content-type": "^1.0.2",
|
"content-type": "^1.0.2",
|
||||||
"request": "^2.53.0"
|
"request": "^2.53.0"
|
||||||
},
|
},
|
||||||
@@ -72,8 +75,8 @@
|
|||||||
"jsdoc": "^3.5.5",
|
"jsdoc": "^3.5.5",
|
||||||
"lolex": "^1.5.2",
|
"lolex": "^1.5.2",
|
||||||
"matrix-mock-request": "^1.2.0",
|
"matrix-mock-request": "^1.2.0",
|
||||||
"mocha": "^3.2.0",
|
"mocha": "^5.2.0",
|
||||||
"mocha-jenkins-reporter": "^0.3.6",
|
"mocha-jenkins-reporter": "^0.4.0",
|
||||||
"rimraf": "^2.5.4",
|
"rimraf": "^2.5.4",
|
||||||
"source-map-support": "^0.4.11",
|
"source-map-support": "^0.4.11",
|
||||||
"sourceify": "^0.1.0",
|
"sourceify": "^0.1.0",
|
||||||
|
|||||||
@@ -245,7 +245,7 @@ release_text=`mktemp`
|
|||||||
echo "$tag" > "${release_text}"
|
echo "$tag" > "${release_text}"
|
||||||
echo >> "${release_text}"
|
echo >> "${release_text}"
|
||||||
cat "${latest_changes}" >> "${release_text}"
|
cat "${latest_changes}" >> "${release_text}"
|
||||||
hub release create $hubflags $assets -f "${release_text}" "$tag"
|
hub release create $hubflags $assets -F "${release_text}" "$tag"
|
||||||
|
|
||||||
if [ $dodist -eq 0 ]; then
|
if [ $dodist -eq 0 ]; then
|
||||||
rm -rf "$builddir"
|
rm -rf "$builddir"
|
||||||
@@ -281,7 +281,7 @@ fi
|
|||||||
echo "updating master branch"
|
echo "updating master branch"
|
||||||
git checkout master
|
git checkout master
|
||||||
git pull
|
git pull
|
||||||
git merge --ff-only "$rel_branch"
|
git merge "$rel_branch"
|
||||||
|
|
||||||
# push master and docs (if generated) to github
|
# push master and docs (if generated) to github
|
||||||
git push origin master
|
git push origin master
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ describe("MatrixClient opts", function() {
|
|||||||
httpBackend.flush("/txn1", 1);
|
httpBackend.flush("/txn1", 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be able to sync / get new events", function(done) {
|
it("should be able to sync / get new events", async function() {
|
||||||
const expectedEventTypes = [ // from /initialSync
|
const expectedEventTypes = [ // from /initialSync
|
||||||
"m.room.message", "m.room.name", "m.room.member", "m.room.member",
|
"m.room.message", "m.room.name", "m.room.member", "m.room.member",
|
||||||
"m.room.create",
|
"m.room.create",
|
||||||
@@ -110,20 +110,16 @@ describe("MatrixClient opts", function() {
|
|||||||
httpBackend.when("GET", "/pushrules").respond(200, {});
|
httpBackend.when("GET", "/pushrules").respond(200, {});
|
||||||
httpBackend.when("POST", "/filter").respond(200, { filter_id: "foo" });
|
httpBackend.when("POST", "/filter").respond(200, { filter_id: "foo" });
|
||||||
httpBackend.when("GET", "/sync").respond(200, syncData);
|
httpBackend.when("GET", "/sync").respond(200, syncData);
|
||||||
client.startClient();
|
await client.startClient();
|
||||||
httpBackend.flush("/pushrules", 1).then(function() {
|
await httpBackend.flush("/pushrules", 1);
|
||||||
return httpBackend.flush("/filter", 1);
|
await httpBackend.flush("/filter", 1);
|
||||||
}).then(function() {
|
await Promise.all([
|
||||||
return Promise.all([
|
httpBackend.flush("/sync", 1),
|
||||||
httpBackend.flush("/sync", 1),
|
utils.syncPromise(client),
|
||||||
utils.syncPromise(client),
|
]);
|
||||||
]);
|
expect(expectedEventTypes.length).toEqual(
|
||||||
}).done(function() {
|
0, "Expected to see event types: " + expectedEventTypes,
|
||||||
expect(expectedEventTypes.length).toEqual(
|
);
|
||||||
0, "Expected to see event types: " + expectedEventTypes,
|
|
||||||
);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,24 @@
|
|||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
import 'source-map-support/register';
|
import 'source-map-support/register';
|
||||||
|
import Crypto from '../../lib/crypto';
|
||||||
|
import expect from 'expect';
|
||||||
|
|
||||||
const sdk = require("../..");
|
const sdk = require("../..");
|
||||||
let Crypto;
|
|
||||||
if (sdk.CRYPTO_ENABLED) {
|
|
||||||
Crypto = require("../../lib/crypto");
|
|
||||||
}
|
|
||||||
|
|
||||||
import expect from 'expect';
|
const Olm = global.Olm;
|
||||||
|
|
||||||
describe("Crypto", function() {
|
describe("Crypto", function() {
|
||||||
if (!sdk.CRYPTO_ENABLED) {
|
if (!sdk.CRYPTO_ENABLED) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
beforeEach(function(done) {
|
||||||
|
Olm.init().then(done);
|
||||||
|
});
|
||||||
|
|
||||||
it("Crypto exposes the correct olm library version", function() {
|
it("Crypto exposes the correct olm library version", function() {
|
||||||
|
console.log(Crypto);
|
||||||
expect(Crypto.getOlmVersion()[0]).toEqual(2);
|
expect(Crypto.getOlmVersion()[0]).toEqual(2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,20 +13,16 @@ import WebStorageSessionStore from '../../../../lib/store/session/webstorage';
|
|||||||
import MemoryCryptoStore from '../../../../lib/crypto/store/memory-crypto-store.js';
|
import MemoryCryptoStore from '../../../../lib/crypto/store/memory-crypto-store.js';
|
||||||
import MockStorageApi from '../../../MockStorageApi';
|
import MockStorageApi from '../../../MockStorageApi';
|
||||||
import testUtils from '../../../test-utils';
|
import testUtils from '../../../test-utils';
|
||||||
|
import OlmDevice from '../../../../lib/crypto/OlmDevice';
|
||||||
// Crypto and OlmDevice won't import unless we have global.Olm
|
import Crypto from '../../../../lib/crypto';
|
||||||
let OlmDevice;
|
|
||||||
let Crypto;
|
|
||||||
if (global.Olm) {
|
|
||||||
OlmDevice = require('../../../../lib/crypto/OlmDevice');
|
|
||||||
Crypto = require('../../../../lib/crypto');
|
|
||||||
}
|
|
||||||
|
|
||||||
const MatrixEvent = sdk.MatrixEvent;
|
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';
|
||||||
|
|
||||||
|
const Olm = global.Olm;
|
||||||
|
|
||||||
describe("MegolmDecryption", function() {
|
describe("MegolmDecryption", function() {
|
||||||
if (!global.Olm) {
|
if (!global.Olm) {
|
||||||
console.warn('Not running megolm unit tests: libolm not present');
|
console.warn('Not running megolm unit tests: libolm not present');
|
||||||
@@ -69,7 +65,8 @@ describe("MegolmDecryption", function() {
|
|||||||
|
|
||||||
describe('receives some keys:', function() {
|
describe('receives some keys:', function() {
|
||||||
let groupSession;
|
let groupSession;
|
||||||
beforeEach(function() {
|
beforeEach(async function() {
|
||||||
|
await Olm.init();
|
||||||
groupSession = new global.Olm.OutboundGroupSession();
|
groupSession = new global.Olm.OutboundGroupSession();
|
||||||
groupSession.create();
|
groupSession.create();
|
||||||
|
|
||||||
@@ -98,7 +95,7 @@ describe("MegolmDecryption", function() {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return event.attemptDecryption(mockCrypto).then(() => {
|
await event.attemptDecryption(mockCrypto).then(() => {
|
||||||
megolmDecryption.onRoomKeyEvent(event);
|
megolmDecryption.onRoomKeyEvent(event);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -43,13 +43,14 @@ const MegolmDecryption = algorithms.DECRYPTION_CLASSES['m.megolm.v1.aes-sha2'];
|
|||||||
|
|
||||||
const ROOM_ID = '!ROOM:ID';
|
const ROOM_ID = '!ROOM:ID';
|
||||||
|
|
||||||
|
const SESSION_ID = 'o+21hSjP+mgEmcfdslPsQdvzWnkdt0Wyo00Kp++R8Kc';
|
||||||
const ENCRYPTED_EVENT = new MatrixEvent({
|
const ENCRYPTED_EVENT = new MatrixEvent({
|
||||||
type: 'm.room.encrypted',
|
type: 'm.room.encrypted',
|
||||||
room_id: '!ROOM:ID',
|
room_id: '!ROOM:ID',
|
||||||
content: {
|
content: {
|
||||||
algorithm: 'm.megolm.v1.aes-sha2',
|
algorithm: 'm.megolm.v1.aes-sha2',
|
||||||
sender_key: 'SENDER_CURVE25519',
|
sender_key: 'SENDER_CURVE25519',
|
||||||
session_id: 'o+21hSjP+mgEmcfdslPsQdvzWnkdt0Wyo00Kp++R8Kc',
|
session_id: SESSION_ID,
|
||||||
ciphertext: 'AwgAEjD+VwXZ7PoGPRS/H4kwpAsMp/g+WPvJVtPEKE8fmM9IcT/N'
|
ciphertext: 'AwgAEjD+VwXZ7PoGPRS/H4kwpAsMp/g+WPvJVtPEKE8fmM9IcT/N'
|
||||||
+ 'CiwPb8PehecDKP0cjm1XO88k6Bw3D17aGiBHr5iBoP7oSw8CXULXAMTkBl'
|
+ 'CiwPb8PehecDKP0cjm1XO88k6Bw3D17aGiBHr5iBoP7oSw8CXULXAMTkBl'
|
||||||
+ 'mkufRQq2+d0Giy1s4/Cg5n13jSVrSb2q7VTSv1ZHAFjUCsLSfR0gxqcQs',
|
+ 'mkufRQq2+d0Giy1s4/Cg5n13jSVrSb2q7VTSv1ZHAFjUCsLSfR0gxqcQs',
|
||||||
@@ -412,7 +413,7 @@ describe("MegolmBackup", function() {
|
|||||||
"qx37WTQrjZLz5tId/uBX9B3/okqAbV1ofl9UnHKno1eipByCpXleAAlAZoJgYnCDOQZD"
|
"qx37WTQrjZLz5tId/uBX9B3/okqAbV1ofl9UnHKno1eipByCpXleAAlAZoJgYnCDOQZD"
|
||||||
+ "QWzo3luTSfkF9pU1mOILCbbouubs6TVeDyPfgGD9i86J8irHjA",
|
+ "QWzo3luTSfkF9pU1mOILCbbouubs6TVeDyPfgGD9i86J8irHjA",
|
||||||
ROOM_ID,
|
ROOM_ID,
|
||||||
'o+21hSjP+mgEmcfdslPsQdvzWnkdt0Wyo00Kp++R8Kc',
|
SESSION_ID,
|
||||||
).then(() => {
|
).then(() => {
|
||||||
return megolmDecryption.decryptEvent(ENCRYPTED_EVENT);
|
return megolmDecryption.decryptEvent(ENCRYPTED_EVENT);
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
@@ -426,10 +427,10 @@ describe("MegolmBackup", function() {
|
|||||||
rooms: {
|
rooms: {
|
||||||
[ROOM_ID]: {
|
[ROOM_ID]: {
|
||||||
sessions: {
|
sessions: {
|
||||||
'o+21hSjP+mgEmcfdslPsQdvzWnkdt0Wyo00Kp++R8Kc': KEY_BACKUP_DATA,
|
SESSION_ID: KEY_BACKUP_DATA,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
return client.restoreKeyBackups(
|
return client.restoreKeyBackups(
|
||||||
|
|||||||
@@ -139,6 +139,9 @@ describe("MatrixClient", function() {
|
|||||||
store.getSavedSync = expect.createSpy().andReturn(Promise.resolve(null));
|
store.getSavedSync = expect.createSpy().andReturn(Promise.resolve(null));
|
||||||
store.getSavedSyncToken = expect.createSpy().andReturn(Promise.resolve(null));
|
store.getSavedSyncToken = expect.createSpy().andReturn(Promise.resolve(null));
|
||||||
store.setSyncData = expect.createSpy().andReturn(Promise.resolve(null));
|
store.setSyncData = expect.createSpy().andReturn(Promise.resolve(null));
|
||||||
|
store.getClientOptions = expect.createSpy().andReturn(Promise.resolve(null));
|
||||||
|
store.storeClientOptions = expect.createSpy().andReturn(Promise.resolve(null));
|
||||||
|
store.isNewlyCreated = expect.createSpy().andReturn(Promise.resolve(true));
|
||||||
client = new MatrixClient({
|
client = new MatrixClient({
|
||||||
baseUrl: "https://my.home.server",
|
baseUrl: "https://my.home.server",
|
||||||
idBaseUrl: identityServerUrl,
|
idBaseUrl: identityServerUrl,
|
||||||
@@ -182,7 +185,7 @@ describe("MatrixClient", function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not POST /filter if a matching filter already exists", function(done) {
|
it("should not POST /filter if a matching filter already exists", async function() {
|
||||||
httpLookups = [];
|
httpLookups = [];
|
||||||
httpLookups.push(PUSH_RULES_RESPONSE);
|
httpLookups.push(PUSH_RULES_RESPONSE);
|
||||||
httpLookups.push(SYNC_RESPONSE);
|
httpLookups.push(SYNC_RESPONSE);
|
||||||
@@ -191,15 +194,19 @@ describe("MatrixClient", function() {
|
|||||||
const filter = new sdk.Filter(0, filterId);
|
const filter = new sdk.Filter(0, filterId);
|
||||||
filter.setDefinition({"room": {"timeline": {"limit": 8}}});
|
filter.setDefinition({"room": {"timeline": {"limit": 8}}});
|
||||||
store.getFilter.andReturn(filter);
|
store.getFilter.andReturn(filter);
|
||||||
client.startClient();
|
const syncPromise = new Promise((resolve, reject) => {
|
||||||
|
client.on("sync", function syncListener(state) {
|
||||||
client.on("sync", function syncListener(state) {
|
if (state === "SYNCING") {
|
||||||
if (state === "SYNCING") {
|
expect(httpLookups.length).toEqual(0);
|
||||||
expect(httpLookups.length).toEqual(0);
|
client.removeListener("sync", syncListener);
|
||||||
client.removeListener("sync", syncListener);
|
resolve();
|
||||||
done();
|
} else if (state === "ERROR") {
|
||||||
}
|
reject(new Error("sync error"));
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
await client.startClient();
|
||||||
|
await syncPromise;
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("getSyncState", function() {
|
describe("getSyncState", function() {
|
||||||
@@ -207,15 +214,18 @@ describe("MatrixClient", function() {
|
|||||||
expect(client.getSyncState()).toBe(null);
|
expect(client.getSyncState()).toBe(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return the same sync state as emitted sync events", function(done) {
|
it("should return the same sync state as emitted sync events", async function() {
|
||||||
client.on("sync", function syncListener(state) {
|
const syncingPromise = new Promise((resolve) => {
|
||||||
expect(state).toEqual(client.getSyncState());
|
client.on("sync", function syncListener(state) {
|
||||||
if (state === "SYNCING") {
|
expect(state).toEqual(client.getSyncState());
|
||||||
client.removeListener("sync", syncListener);
|
if (state === "SYNCING") {
|
||||||
done();
|
client.removeListener("sync", syncListener);
|
||||||
}
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
client.startClient();
|
await client.startClient();
|
||||||
|
await syncingPromise;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -258,8 +268,8 @@ describe("MatrixClient", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("retryImmediately", function() {
|
describe("retryImmediately", function() {
|
||||||
it("should return false if there is no request waiting", function() {
|
it("should return false if there is no request waiting", async function() {
|
||||||
client.startClient();
|
await client.startClient();
|
||||||
expect(client.retryImmediately()).toBe(false);
|
expect(client.retryImmediately()).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1402,13 +1402,25 @@ describe("Room", function() {
|
|||||||
it("should return synced membership if membership isn't available yet",
|
it("should return synced membership if membership isn't available yet",
|
||||||
function() {
|
function() {
|
||||||
const room = new Room(roomId, null, userA);
|
const room = new Room(roomId, null, userA);
|
||||||
room.setSyncedMembership("invite");
|
room.updateMyMembership("invite");
|
||||||
expect(room.getMyMembership()).toEqual("invite");
|
expect(room.getMyMembership()).toEqual("invite");
|
||||||
room.addLiveEvents([utils.mkMembership({
|
});
|
||||||
user: userA, mship: "join",
|
it("should emit a Room.myMembership event on a change",
|
||||||
room: roomId, event: true,
|
function() {
|
||||||
})]);
|
const room = new Room(roomId, null, userA);
|
||||||
|
const events = [];
|
||||||
|
room.on("Room.myMembership", (_room, membership, oldMembership) => {
|
||||||
|
events.push({membership, oldMembership});
|
||||||
|
});
|
||||||
|
room.updateMyMembership("invite");
|
||||||
|
expect(room.getMyMembership()).toEqual("invite");
|
||||||
|
expect(events[0]).toEqual({membership: "invite", oldMembership: null});
|
||||||
|
events.splice(0); //clear
|
||||||
|
room.updateMyMembership("invite");
|
||||||
|
expect(events.length).toEqual(0);
|
||||||
|
room.updateMyMembership("join");
|
||||||
expect(room.getMyMembership()).toEqual("join");
|
expect(room.getMyMembership()).toEqual("join");
|
||||||
|
expect(events[0]).toEqual({membership: "join", oldMembership: "invite"});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1439,11 +1451,11 @@ describe("Room", function() {
|
|||||||
it("should return false if synced membership not join",
|
it("should return false if synced membership not join",
|
||||||
function() {
|
function() {
|
||||||
const room = new Room(roomId, null, userA);
|
const room = new Room(roomId, null, userA);
|
||||||
room.setSyncedMembership("invite");
|
room.updateMyMembership("invite");
|
||||||
expect(room.maySendMessage()).toEqual(false);
|
expect(room.maySendMessage()).toEqual(false);
|
||||||
room.setSyncedMembership("leave");
|
room.updateMyMembership("leave");
|
||||||
expect(room.maySendMessage()).toEqual(false);
|
expect(room.maySendMessage()).toEqual(false);
|
||||||
room.setSyncedMembership("join");
|
room.updateMyMembership("join");
|
||||||
expect(room.maySendMessage()).toEqual(true);
|
expect(room.maySendMessage()).toEqual(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
120
src/client.js
120
src/client.js
@@ -45,7 +45,15 @@ const olmlib = require("./crypto/olmlib");
|
|||||||
|
|
||||||
import ReEmitter from './ReEmitter';
|
import ReEmitter from './ReEmitter';
|
||||||
import RoomList from './crypto/RoomList';
|
import RoomList from './crypto/RoomList';
|
||||||
|
import {InvalidStoreError} from './errors';
|
||||||
|
|
||||||
|
import Crypto from './crypto';
|
||||||
|
import { isCryptoAvailable } from './crypto';
|
||||||
|
import { encodeRecoveryKey, decodeRecoveryKey } from './crypto/recoverykey';
|
||||||
|
|
||||||
|
// Disable warnings for now: we use deprecated bluebird functions
|
||||||
|
// and need to migrate, but they spam the console with warnings.
|
||||||
|
Promise.config({warnings: false});
|
||||||
|
|
||||||
const LAZY_LOADING_MESSAGES_FILTER = {
|
const LAZY_LOADING_MESSAGES_FILTER = {
|
||||||
lazy_load_members: true,
|
lazy_load_members: true,
|
||||||
@@ -59,14 +67,7 @@ const LAZY_LOADING_SYNC_FILTER = {
|
|||||||
|
|
||||||
|
|
||||||
const SCROLLBACK_DELAY_MS = 3000;
|
const SCROLLBACK_DELAY_MS = 3000;
|
||||||
let CRYPTO_ENABLED = false;
|
const CRYPTO_ENABLED = isCryptoAvailable();
|
||||||
|
|
||||||
try {
|
|
||||||
var Crypto = require("./crypto");
|
|
||||||
CRYPTO_ENABLED = true;
|
|
||||||
} catch (e) {
|
|
||||||
console.warn("Unable to load crypto module: crypto will be disabled: " + e);
|
|
||||||
}
|
|
||||||
|
|
||||||
function keysFromRecoverySession(sessions, decryptionKey, roomId) {
|
function keysFromRecoverySession(sessions, decryptionKey, roomId) {
|
||||||
const keys = [];
|
const keys = [];
|
||||||
@@ -164,6 +165,8 @@ function MatrixClient(opts) {
|
|||||||
|
|
||||||
MatrixBaseApis.call(this, opts);
|
MatrixBaseApis.call(this, opts);
|
||||||
|
|
||||||
|
this.olmVersion = null; // Populated after initCrypto is done
|
||||||
|
|
||||||
this.reEmitter = new ReEmitter(this);
|
this.reEmitter = new ReEmitter(this);
|
||||||
|
|
||||||
this.store = opts.store || new StubStore();
|
this.store = opts.store || new StubStore();
|
||||||
@@ -216,10 +219,6 @@ function MatrixClient(opts) {
|
|||||||
|
|
||||||
this._forceTURN = opts.forceTURN || false;
|
this._forceTURN = opts.forceTURN || false;
|
||||||
|
|
||||||
if (CRYPTO_ENABLED) {
|
|
||||||
this.olmVersion = Crypto.getOlmVersion();
|
|
||||||
}
|
|
||||||
|
|
||||||
// List of which rooms have encryption enabled: separate from crypto because
|
// List of which rooms have encryption enabled: separate from crypto because
|
||||||
// we still want to know which rooms are encrypted even if crypto is disabled:
|
// we still want to know which rooms are encrypted even if crypto is disabled:
|
||||||
// we don't want to start sending unencrypted events to them.
|
// we don't want to start sending unencrypted events to them.
|
||||||
@@ -409,6 +408,13 @@ MatrixClient.prototype.setNotifTimelineSet = function(notifTimelineSet) {
|
|||||||
* successfully initialised.
|
* successfully initialised.
|
||||||
*/
|
*/
|
||||||
MatrixClient.prototype.initCrypto = async function() {
|
MatrixClient.prototype.initCrypto = async function() {
|
||||||
|
if (!isCryptoAvailable()) {
|
||||||
|
throw new Error(
|
||||||
|
`End-to-end encryption not supported in this js-sdk build: did ` +
|
||||||
|
`you remember to load the olm library?`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (this._crypto) {
|
if (this._crypto) {
|
||||||
console.warn("Attempt to re-initialise e2e encryption on MatrixClient");
|
console.warn("Attempt to re-initialise e2e encryption on MatrixClient");
|
||||||
return;
|
return;
|
||||||
@@ -426,13 +432,6 @@ MatrixClient.prototype.initCrypto = async function() {
|
|||||||
// initialise the list of encrypted rooms (whether or not crypto is enabled)
|
// initialise the list of encrypted rooms (whether or not crypto is enabled)
|
||||||
await this._roomList.init();
|
await this._roomList.init();
|
||||||
|
|
||||||
if (!CRYPTO_ENABLED) {
|
|
||||||
throw new Error(
|
|
||||||
`End-to-end encryption not supported in this js-sdk build: did ` +
|
|
||||||
`you remember to load the olm library?`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const userId = this.getUserId();
|
const userId = this.getUserId();
|
||||||
if (userId === null) {
|
if (userId === null) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@@ -464,6 +463,9 @@ MatrixClient.prototype.initCrypto = async function() {
|
|||||||
|
|
||||||
await crypto.init();
|
await crypto.init();
|
||||||
|
|
||||||
|
this.olmVersion = Crypto.getOlmVersion();
|
||||||
|
|
||||||
|
|
||||||
// if crypto initialisation was successful, tell it to attach its event
|
// if crypto initialisation was successful, tell it to attach its event
|
||||||
// handlers.
|
// handlers.
|
||||||
crypto.registerEventHandlers(this);
|
crypto.registerEventHandlers(this);
|
||||||
@@ -887,9 +889,7 @@ MatrixClient.prototype.prepareKeyBackupVersion = function() {
|
|||||||
auth_data: {
|
auth_data: {
|
||||||
public_key: publicKey,
|
public_key: publicKey,
|
||||||
},
|
},
|
||||||
// FIXME: pickle isn't the right thing to use, but we don't have
|
recovery_key: encodeRecoveryKey(decryption.get_private_key()),
|
||||||
// anything else yet, so use it for now
|
|
||||||
recovery_key: decryption.pickle("secret_key"),
|
|
||||||
};
|
};
|
||||||
} finally {
|
} finally {
|
||||||
decryption.free();
|
decryption.free();
|
||||||
@@ -996,26 +996,17 @@ MatrixClient.prototype.backupAllGroupSessions = function(version) {
|
|||||||
return this._crypto.backupAllGroupSessions(version);
|
return this._crypto.backupAllGroupSessions(version);
|
||||||
};
|
};
|
||||||
|
|
||||||
MatrixClient.prototype.isValidRecoveryKey = function(decryptionKey) {
|
MatrixClient.prototype.isValidRecoveryKey = function(recoveryKey) {
|
||||||
if (this._crypto === null) {
|
|
||||||
throw new Error("End-to-end encryption disabled");
|
|
||||||
}
|
|
||||||
|
|
||||||
const decryption = new global.Olm.PkDecryption();
|
|
||||||
try {
|
try {
|
||||||
// FIXME: see the FIXME in createKeyBackupVersion
|
decodeRecoveryKey(recoveryKey);
|
||||||
decryption.unpickle("secret_key", decryptionKey);
|
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
|
||||||
return false;
|
return false;
|
||||||
} finally {
|
|
||||||
decryption.free();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
MatrixClient.prototype.restoreKeyBackups = function(
|
MatrixClient.prototype.restoreKeyBackups = function(
|
||||||
decryptionKey, targetRoomId, targetSessionId, version,
|
recoveryKey, targetRoomId, targetSessionId, version,
|
||||||
) {
|
) {
|
||||||
if (this._crypto === null) {
|
if (this._crypto === null) {
|
||||||
throw new Error("End-to-end encryption disabled");
|
throw new Error("End-to-end encryption disabled");
|
||||||
@@ -1026,9 +1017,10 @@ MatrixClient.prototype.restoreKeyBackups = function(
|
|||||||
const path = this._makeKeyBackupPath(targetRoomId, targetSessionId, version);
|
const path = this._makeKeyBackupPath(targetRoomId, targetSessionId, version);
|
||||||
|
|
||||||
// FIXME: see the FIXME in createKeyBackupVersion
|
// FIXME: see the FIXME in createKeyBackupVersion
|
||||||
|
const privkey = decodeRecoveryKey(recoveryKey);
|
||||||
const decryption = new global.Olm.PkDecryption();
|
const decryption = new global.Olm.PkDecryption();
|
||||||
try {
|
try {
|
||||||
decryption.unpickle("secret_key", decryptionKey);
|
decryption.init_with_private_key(privkey);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
decryption.free();
|
decryption.free();
|
||||||
throw e;
|
throw e;
|
||||||
@@ -1042,7 +1034,9 @@ MatrixClient.prototype.restoreKeyBackups = function(
|
|||||||
if (!roomData.sessions) continue;
|
if (!roomData.sessions) continue;
|
||||||
|
|
||||||
totalKeyCount += Object.keys(roomData.sessions).length;
|
totalKeyCount += Object.keys(roomData.sessions).length;
|
||||||
const roomKeys = keysFromRecoverySession(roomData.sessions, decryption, roomId, roomKeys);
|
const roomKeys = keysFromRecoverySession(
|
||||||
|
roomData.sessions, decryption, roomId, roomKeys,
|
||||||
|
);
|
||||||
for (const k of roomKeys) {
|
for (const k of roomKeys) {
|
||||||
k.room_id = roomId;
|
k.room_id = roomId;
|
||||||
keys.push(k);
|
keys.push(k);
|
||||||
@@ -2439,7 +2433,8 @@ MatrixClient.prototype.getEventTimeline = function(timelineSet, eventId) {
|
|||||||
self.getEventMapper()));
|
self.getEventMapper()));
|
||||||
timeline.getState(EventTimeline.FORWARDS).paginationToken = res.end;
|
timeline.getState(EventTimeline.FORWARDS).paginationToken = res.end;
|
||||||
} else {
|
} else {
|
||||||
timeline.getState(EventTimeline.BACKWARDS).setUnknownStateEvents(res.state);
|
const stateEvents = utils.map(res.state, self.getEventMapper());
|
||||||
|
timeline.getState(EventTimeline.BACKWARDS).setUnknownStateEvents(stateEvents);
|
||||||
}
|
}
|
||||||
timelineSet.addEventsToTimeline(matrixEvents, true, timeline, res.start);
|
timelineSet.addEventsToTimeline(matrixEvents, true, timeline, res.start);
|
||||||
|
|
||||||
@@ -3441,6 +3436,9 @@ MatrixClient.prototype.startClient = async function(opts) {
|
|||||||
// shallow-copy the opts dict before modifying and storing it
|
// shallow-copy the opts dict before modifying and storing it
|
||||||
opts = Object.assign({}, opts);
|
opts = Object.assign({}, opts);
|
||||||
|
|
||||||
|
if (opts.lazyLoadMembers && this.isGuest()) {
|
||||||
|
opts.lazyLoadMembers = false;
|
||||||
|
}
|
||||||
if (opts.lazyLoadMembers) {
|
if (opts.lazyLoadMembers) {
|
||||||
const supported = await this.doesServerSupportLazyLoading();
|
const supported = await this.doesServerSupportLazyLoading();
|
||||||
if (supported) {
|
if (supported) {
|
||||||
@@ -3451,7 +3449,12 @@ MatrixClient.prototype.startClient = async function(opts) {
|
|||||||
opts.lazyLoadMembers = false;
|
opts.lazyLoadMembers = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// need to vape the store when enabling LL and wasn't enabled before
|
||||||
|
const shouldClear = await this._wasLazyLoadingToggled(opts.lazyLoadMembers);
|
||||||
|
if (shouldClear) {
|
||||||
|
const reason = InvalidStoreError.TOGGLED_LAZY_LOADING;
|
||||||
|
throw new InvalidStoreError(reason, !!opts.lazyLoadMembers);
|
||||||
|
}
|
||||||
if (opts.lazyLoadMembers && this._crypto) {
|
if (opts.lazyLoadMembers && this._crypto) {
|
||||||
this._crypto.enableLazyLoading();
|
this._crypto.enableLazyLoading();
|
||||||
}
|
}
|
||||||
@@ -3464,11 +3467,50 @@ MatrixClient.prototype.startClient = async function(opts) {
|
|||||||
return this._canResetTimelineCallback(roomId);
|
return this._canResetTimelineCallback(roomId);
|
||||||
};
|
};
|
||||||
this._clientOpts = opts;
|
this._clientOpts = opts;
|
||||||
|
await this._storeClientOptions(this._clientOpts);
|
||||||
this._syncApi = new SyncApi(this, opts);
|
this._syncApi = new SyncApi(this, opts);
|
||||||
this._syncApi.sync();
|
this._syncApi.sync();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the lazy loading option different than in previous session?
|
||||||
|
* @param {bool} lazyLoadMembers current options for lazy loading
|
||||||
|
* @return {bool} whether or not the option has changed compared to the previous session */
|
||||||
|
MatrixClient.prototype._wasLazyLoadingToggled = async function(lazyLoadMembers) {
|
||||||
|
lazyLoadMembers = !!lazyLoadMembers;
|
||||||
|
// assume it was turned off before
|
||||||
|
// if we don't know any better
|
||||||
|
let lazyLoadMembersBefore = false;
|
||||||
|
const isStoreNewlyCreated = await this.store.isNewlyCreated();
|
||||||
|
if (!isStoreNewlyCreated) {
|
||||||
|
const prevClientOptions = await this.store.getClientOptions();
|
||||||
|
if (prevClientOptions) {
|
||||||
|
lazyLoadMembersBefore = !!prevClientOptions.lazyLoadMembers;
|
||||||
|
}
|
||||||
|
return lazyLoadMembersBefore !== lazyLoadMembers;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* store client options with boolean/string/numeric values
|
||||||
|
* to know in the next session what flags the sync data was
|
||||||
|
* created with (e.g. lazy loading)
|
||||||
|
* @param {object} opts the complete set of client options
|
||||||
|
* @return {Promise} for store operation */
|
||||||
|
MatrixClient.prototype._storeClientOptions = function(opts) {
|
||||||
|
const primTypes = ["boolean", "string", "number"];
|
||||||
|
const serializableOpts = Object.entries(opts)
|
||||||
|
.filter(([key, value]) => {
|
||||||
|
return primTypes.includes(typeof value);
|
||||||
|
})
|
||||||
|
.reduce((obj, [key, value]) => {
|
||||||
|
obj[key] = value;
|
||||||
|
return obj;
|
||||||
|
}, {});
|
||||||
|
return this.store.storeClientOptions(serializableOpts);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* High level helper method to stop the client from polling and allow a
|
* High level helper method to stop the client from polling and allow a
|
||||||
* clean shutdown.
|
* clean shutdown.
|
||||||
|
|||||||
@@ -17,17 +17,6 @@ limitations under the License.
|
|||||||
|
|
||||||
import IndexedDBCryptoStore from './store/indexeddb-crypto-store';
|
import IndexedDBCryptoStore from './store/indexeddb-crypto-store';
|
||||||
|
|
||||||
/**
|
|
||||||
* olm.js wrapper
|
|
||||||
*
|
|
||||||
* @module crypto/OlmDevice
|
|
||||||
*/
|
|
||||||
const Olm = global.Olm;
|
|
||||||
if (!Olm) {
|
|
||||||
throw new Error("global.Olm is not defined");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 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.
|
||||||
const MAX_PLAINTEXT_LENGTH = 65536 * 3 / 4;
|
const MAX_PLAINTEXT_LENGTH = 65536 * 3 / 4;
|
||||||
@@ -138,7 +127,7 @@ OlmDevice.prototype.init = async function() {
|
|||||||
await this._migrateFromSessionStore();
|
await this._migrateFromSessionStore();
|
||||||
|
|
||||||
let e2eKeys;
|
let e2eKeys;
|
||||||
const account = new Olm.Account();
|
const account = new global.Olm.Account();
|
||||||
try {
|
try {
|
||||||
await _initialiseAccount(
|
await _initialiseAccount(
|
||||||
this._sessionStore, this._cryptoStore, this._pickleKey, account,
|
this._sessionStore, this._cryptoStore, this._pickleKey, account,
|
||||||
@@ -172,7 +161,7 @@ async function _initialiseAccount(sessionStore, cryptoStore, pickleKey, account)
|
|||||||
* @return {array} The version of Olm.
|
* @return {array} The version of Olm.
|
||||||
*/
|
*/
|
||||||
OlmDevice.getOlmVersion = function() {
|
OlmDevice.getOlmVersion = function() {
|
||||||
return Olm.get_library_version();
|
return global.Olm.get_library_version();
|
||||||
};
|
};
|
||||||
|
|
||||||
OlmDevice.prototype._migrateFromSessionStore = async function() {
|
OlmDevice.prototype._migrateFromSessionStore = async function() {
|
||||||
@@ -279,7 +268,7 @@ OlmDevice.prototype._migrateFromSessionStore = async function() {
|
|||||||
*/
|
*/
|
||||||
OlmDevice.prototype._getAccount = function(txn, func) {
|
OlmDevice.prototype._getAccount = function(txn, func) {
|
||||||
this._cryptoStore.getAccount(txn, (pickledAccount) => {
|
this._cryptoStore.getAccount(txn, (pickledAccount) => {
|
||||||
const account = new Olm.Account();
|
const account = new global.Olm.Account();
|
||||||
try {
|
try {
|
||||||
account.unpickle(this._pickleKey, pickledAccount);
|
account.unpickle(this._pickleKey, pickledAccount);
|
||||||
func(account);
|
func(account);
|
||||||
@@ -332,7 +321,7 @@ OlmDevice.prototype._getSession = function(deviceKey, sessionId, txn, func) {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
OlmDevice.prototype._unpickleSession = function(pickledSession, func) {
|
OlmDevice.prototype._unpickleSession = function(pickledSession, func) {
|
||||||
const session = new Olm.Session();
|
const session = new global.Olm.Session();
|
||||||
try {
|
try {
|
||||||
session.unpickle(this._pickleKey, pickledSession);
|
session.unpickle(this._pickleKey, pickledSession);
|
||||||
func(session);
|
func(session);
|
||||||
@@ -365,7 +354,7 @@ OlmDevice.prototype._saveSession = function(deviceKey, session, txn) {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
OlmDevice.prototype._getUtility = function(func) {
|
OlmDevice.prototype._getUtility = function(func) {
|
||||||
const utility = new Olm.Utility();
|
const utility = new global.Olm.Utility();
|
||||||
try {
|
try {
|
||||||
return func(utility);
|
return func(utility);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -477,7 +466,7 @@ OlmDevice.prototype.createOutboundSession = async function(
|
|||||||
],
|
],
|
||||||
(txn) => {
|
(txn) => {
|
||||||
this._getAccount(txn, (account) => {
|
this._getAccount(txn, (account) => {
|
||||||
const session = new Olm.Session();
|
const session = new global.Olm.Session();
|
||||||
try {
|
try {
|
||||||
session.create_outbound(account, theirIdentityKey, theirOneTimeKey);
|
session.create_outbound(account, theirIdentityKey, theirOneTimeKey);
|
||||||
newSessionId = session.session_id();
|
newSessionId = session.session_id();
|
||||||
@@ -521,7 +510,7 @@ OlmDevice.prototype.createInboundSession = async function(
|
|||||||
],
|
],
|
||||||
(txn) => {
|
(txn) => {
|
||||||
this._getAccount(txn, (account) => {
|
this._getAccount(txn, (account) => {
|
||||||
const session = new Olm.Session();
|
const session = new global.Olm.Session();
|
||||||
try {
|
try {
|
||||||
session.create_inbound_from(
|
session.create_inbound_from(
|
||||||
account, theirDeviceIdentityKey, ciphertext,
|
account, theirDeviceIdentityKey, ciphertext,
|
||||||
@@ -739,7 +728,7 @@ OlmDevice.prototype._getOutboundGroupSession = function(sessionId, func) {
|
|||||||
throw new Error("Unknown outbound group session " + sessionId);
|
throw new Error("Unknown outbound group session " + sessionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
const session = new Olm.OutboundGroupSession();
|
const session = new global.Olm.OutboundGroupSession();
|
||||||
try {
|
try {
|
||||||
session.unpickle(this._pickleKey, pickled);
|
session.unpickle(this._pickleKey, pickled);
|
||||||
return func(session);
|
return func(session);
|
||||||
@@ -755,7 +744,7 @@ OlmDevice.prototype._getOutboundGroupSession = function(sessionId, func) {
|
|||||||
* @return {string} sessionId for the outbound session.
|
* @return {string} sessionId for the outbound session.
|
||||||
*/
|
*/
|
||||||
OlmDevice.prototype.createOutboundGroupSession = function() {
|
OlmDevice.prototype.createOutboundGroupSession = function() {
|
||||||
const session = new Olm.OutboundGroupSession();
|
const session = new global.Olm.OutboundGroupSession();
|
||||||
try {
|
try {
|
||||||
session.create();
|
session.create();
|
||||||
this._saveOutboundGroupSession(session);
|
this._saveOutboundGroupSession(session);
|
||||||
@@ -827,7 +816,7 @@ OlmDevice.prototype.getOutboundGroupSessionKey = function(sessionId) {
|
|||||||
* @return {*} result of func
|
* @return {*} result of func
|
||||||
*/
|
*/
|
||||||
OlmDevice.prototype._unpickleInboundGroupSession = function(sessionData, func) {
|
OlmDevice.prototype._unpickleInboundGroupSession = function(sessionData, func) {
|
||||||
const session = new Olm.InboundGroupSession();
|
const session = new global.Olm.InboundGroupSession();
|
||||||
try {
|
try {
|
||||||
session.unpickle(this._pickleKey, sessionData.session);
|
session.unpickle(this._pickleKey, sessionData.session);
|
||||||
return func(session);
|
return func(session);
|
||||||
@@ -908,7 +897,7 @@ OlmDevice.prototype.addInboundGroupSession = async function(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// new session.
|
// new session.
|
||||||
const session = new Olm.InboundGroupSession();
|
const session = new global.Olm.InboundGroupSession();
|
||||||
try {
|
try {
|
||||||
if (exportFormat) {
|
if (exportFormat) {
|
||||||
session.import_session(sessionKey);
|
session.import_session(sessionKey);
|
||||||
|
|||||||
@@ -36,9 +36,8 @@ const DeviceList = require('./DeviceList').default;
|
|||||||
import OutgoingRoomKeyRequestManager from './OutgoingRoomKeyRequestManager';
|
import OutgoingRoomKeyRequestManager from './OutgoingRoomKeyRequestManager';
|
||||||
import IndexedDBCryptoStore from './store/indexeddb-crypto-store';
|
import IndexedDBCryptoStore from './store/indexeddb-crypto-store';
|
||||||
|
|
||||||
const Olm = global.Olm;
|
export function isCryptoAvailable() {
|
||||||
if (!Olm) {
|
return Boolean(global.Olm);
|
||||||
throw new Error("global.Olm is not defined");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -67,7 +66,7 @@ if (!Olm) {
|
|||||||
*
|
*
|
||||||
* @param {RoomList} roomList An initialised RoomList object
|
* @param {RoomList} roomList An initialised RoomList object
|
||||||
*/
|
*/
|
||||||
function Crypto(baseApis, sessionStore, userId, deviceId,
|
export default function Crypto(baseApis, sessionStore, userId, deviceId,
|
||||||
clientStore, cryptoStore, roomList) {
|
clientStore, cryptoStore, roomList) {
|
||||||
this._baseApis = baseApis;
|
this._baseApis = baseApis;
|
||||||
this._sessionStore = sessionStore;
|
this._sessionStore = sessionStore;
|
||||||
@@ -137,6 +136,10 @@ utils.inherits(Crypto, EventEmitter);
|
|||||||
* Returns a promise which resolves once the crypto module is ready for use.
|
* Returns a promise which resolves once the crypto module is ready for use.
|
||||||
*/
|
*/
|
||||||
Crypto.prototype.init = async function() {
|
Crypto.prototype.init = async function() {
|
||||||
|
// Olm is just an object with a .then, not a fully-fledged promise, so
|
||||||
|
// pass it into bluebird to make it a proper promise.
|
||||||
|
await global.Olm.init();
|
||||||
|
|
||||||
const sessionStoreHasAccount = Boolean(this._sessionStore.getEndToEndAccount());
|
const sessionStoreHasAccount = Boolean(this._sessionStore.getEndToEndAccount());
|
||||||
let cryptoStoreHasAccount;
|
let cryptoStoreHasAccount;
|
||||||
await this._cryptoStore.doTxn(
|
await this._cryptoStore.doTxn(
|
||||||
@@ -1747,6 +1750,3 @@ class IncomingRoomKeyRequestCancellation {
|
|||||||
* @event module:client~MatrixClient#"crypto.warning"
|
* @event module:client~MatrixClient#"crypto.warning"
|
||||||
* @param {string} type One of the strings listed above
|
* @param {string} type One of the strings listed above
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** */
|
|
||||||
module.exports = Crypto;
|
|
||||||
|
|||||||
67
src/crypto/recoverykey.js
Normal file
67
src/crypto/recoverykey.js
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2018 New Vector Ltd
|
||||||
|
|
||||||
|
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 bs58 from 'bs58';
|
||||||
|
|
||||||
|
// picked arbitrarily but to try & avoid clashing with any bitcoin ones
|
||||||
|
// (also base58 encoded, albeit with a lot of hashing)
|
||||||
|
const OLM_RECOVERY_KEY_PREFIX = [0x8B, 0x01];
|
||||||
|
|
||||||
|
export function encodeRecoveryKey(key) {
|
||||||
|
const buf = new Uint8Array(OLM_RECOVERY_KEY_PREFIX.length + key.length + 1);
|
||||||
|
buf.set(OLM_RECOVERY_KEY_PREFIX, 0);
|
||||||
|
buf.set(key, OLM_RECOVERY_KEY_PREFIX.length);
|
||||||
|
|
||||||
|
let parity = 0;
|
||||||
|
for (let i = 0; i < buf.length - 1; ++i) {
|
||||||
|
parity ^= buf[i];
|
||||||
|
}
|
||||||
|
buf[buf.length - 1] = parity;
|
||||||
|
const base58key = bs58.encode(buf);
|
||||||
|
|
||||||
|
|
||||||
|
return base58key.match(/.{1,4}/g).join(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function decodeRecoveryKey(recoverykey) {
|
||||||
|
const result = bs58.decode(recoverykey.replace(/ /g, ''));
|
||||||
|
|
||||||
|
let parity = 0;
|
||||||
|
for (const b of result) {
|
||||||
|
parity ^= b;
|
||||||
|
}
|
||||||
|
if (parity !== 0) {
|
||||||
|
throw new Error("Incorrect parity");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < OLM_RECOVERY_KEY_PREFIX.length; ++i) {
|
||||||
|
if (result[i] !== OLM_RECOVERY_KEY_PREFIX[i]) {
|
||||||
|
throw new Error("Incorrect prefix");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
result.length !==
|
||||||
|
OLM_RECOVERY_KEY_PREFIX.length + global.Olm.PRIVATE_KEY_LENGTH + 1
|
||||||
|
) {
|
||||||
|
throw new Error("Incorrect length");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.slice(
|
||||||
|
OLM_RECOVERY_KEY_PREFIX.length,
|
||||||
|
OLM_RECOVERY_KEY_PREFIX.length + global.Olm.PRIVATE_KEY_LENGTH,
|
||||||
|
);
|
||||||
|
}
|
||||||
25
src/errors.js
Normal file
25
src/errors.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
// can't just do InvalidStoreError extends Error
|
||||||
|
// because of http://babeljs.io/docs/usage/caveats/#classes
|
||||||
|
function InvalidStoreError(reason, value) {
|
||||||
|
const message = `Store is invalid because ${reason}, ` +
|
||||||
|
`please delete all data and retry`;
|
||||||
|
const instance = Reflect.construct(Error, [message]);
|
||||||
|
Reflect.setPrototypeOf(instance, Reflect.getPrototypeOf(this));
|
||||||
|
instance.reason = reason;
|
||||||
|
instance.value = value;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
InvalidStoreError.TOGGLED_LAZY_LOADING = "TOGGLED_LAZY_LOADING";
|
||||||
|
|
||||||
|
InvalidStoreError.prototype = Object.create(Error.prototype, {
|
||||||
|
constructor: {
|
||||||
|
value: Error,
|
||||||
|
enumerable: false,
|
||||||
|
writable: true,
|
||||||
|
configurable: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
Reflect.setPrototypeOf(InvalidStoreError, Error);
|
||||||
|
|
||||||
|
module.exports.InvalidStoreError = InvalidStoreError;
|
||||||
@@ -34,6 +34,8 @@ module.exports.SyncAccumulator = require("./sync-accumulator");
|
|||||||
module.exports.MatrixHttpApi = require("./http-api").MatrixHttpApi;
|
module.exports.MatrixHttpApi = require("./http-api").MatrixHttpApi;
|
||||||
/** The {@link module:http-api.MatrixError|MatrixError} class. */
|
/** The {@link module:http-api.MatrixError|MatrixError} class. */
|
||||||
module.exports.MatrixError = require("./http-api").MatrixError;
|
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. */
|
/** The {@link module:client.MatrixClient|MatrixClient} class. */
|
||||||
module.exports.MatrixClient = require("./client").MatrixClient;
|
module.exports.MatrixClient = require("./client").MatrixClient;
|
||||||
/** The {@link module:models/room|Room} class. */
|
/** The {@link module:models/room|Room} class. */
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ RoomState.prototype.getSentinelMember = function(userId) {
|
|||||||
sentinel = new RoomMember(this.roomId, userId);
|
sentinel = new RoomMember(this.roomId, userId);
|
||||||
const member = this.members[userId];
|
const member = this.members[userId];
|
||||||
if (member) {
|
if (member) {
|
||||||
sentinel.setMembershipEvent(member.events.member);
|
sentinel.setMembershipEvent(member.events.member, this);
|
||||||
}
|
}
|
||||||
this._sentinels[userId] = sentinel;
|
this._sentinels[userId] = sentinel;
|
||||||
}
|
}
|
||||||
@@ -501,7 +501,7 @@ RoomState.prototype._setOutOfBandMember = function(stateEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const member = this._getOrCreateMember(userId, stateEvent);
|
const member = this._getOrCreateMember(userId, stateEvent);
|
||||||
member.setMembershipEvent(stateEvent);
|
member.setMembershipEvent(stateEvent, this);
|
||||||
// needed to know which members need to be stored seperately
|
// needed to know which members need to be stored seperately
|
||||||
// as the are not part of the sync accumulator
|
// as the are not part of the sync accumulator
|
||||||
// this is cleared by setMembershipEvent so when it's updated through /sync
|
// this is cleared by setMembershipEvent so when it's updated through /sync
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ function Room(roomId, client, myUserId, opts) {
|
|||||||
|
|
||||||
// read by megolm; boolean value - null indicates "use global value"
|
// read by megolm; boolean value - null indicates "use global value"
|
||||||
this._blacklistUnverifiedDevices = null;
|
this._blacklistUnverifiedDevices = null;
|
||||||
this._syncedMembership = null;
|
this._selfMembership = null;
|
||||||
this._summaryHeroes = null;
|
this._summaryHeroes = null;
|
||||||
// awaited by getEncryptionTargetMembers while room members are loading
|
// awaited by getEncryptionTargetMembers while room members are loading
|
||||||
|
|
||||||
@@ -198,7 +198,10 @@ utils.inherits(Room, EventEmitter);
|
|||||||
*/
|
*/
|
||||||
Room.prototype.getVersion = function() {
|
Room.prototype.getVersion = function() {
|
||||||
const createEvent = this.currentState.getStateEvents("m.room.create", "");
|
const createEvent = this.currentState.getStateEvents("m.room.create", "");
|
||||||
if (!createEvent) return null;
|
if (!createEvent) {
|
||||||
|
console.warn("Room " + this.room_id + " does not have an m.room.create event");
|
||||||
|
return '1';
|
||||||
|
}
|
||||||
const ver = createEvent.getContent()['room_version'];
|
const ver = createEvent.getContent()['room_version'];
|
||||||
if (ver === undefined) return '1';
|
if (ver === undefined) return '1';
|
||||||
return ver;
|
return ver;
|
||||||
@@ -257,13 +260,7 @@ Room.prototype.getLiveTimeline = function() {
|
|||||||
* @return {string} the membership type (join | leave | invite) for the logged in user
|
* @return {string} the membership type (join | leave | invite) for the logged in user
|
||||||
*/
|
*/
|
||||||
Room.prototype.getMyMembership = function() {
|
Room.prototype.getMyMembership = function() {
|
||||||
if (this.myUserId) {
|
return this._selfMembership;
|
||||||
const me = this.getMember(this.myUserId);
|
|
||||||
if (me) {
|
|
||||||
return me.membership;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this._syncedMembership;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -278,7 +275,7 @@ Room.prototype.getDMInviter = function() {
|
|||||||
return me.getDMInviter();
|
return me.getDMInviter();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this._syncedMembership === "invite") {
|
if (this._selfMembership === "invite") {
|
||||||
// fall back to summary information
|
// fall back to summary information
|
||||||
const memberCount = this.getInvitedAndJoinedMemberCount();
|
const memberCount = this.getInvitedAndJoinedMemberCount();
|
||||||
if (memberCount == 2 && this._summaryHeroes.length) {
|
if (memberCount == 2 && this._summaryHeroes.length) {
|
||||||
@@ -362,8 +359,15 @@ Room.prototype.getAvatarFallbackMember = function() {
|
|||||||
* Sets the membership this room was received as during sync
|
* Sets the membership this room was received as during sync
|
||||||
* @param {string} membership join | leave | invite
|
* @param {string} membership join | leave | invite
|
||||||
*/
|
*/
|
||||||
Room.prototype.setSyncedMembership = function(membership) {
|
Room.prototype.updateMyMembership = function(membership) {
|
||||||
this._syncedMembership = membership;
|
const prevMembership = this._selfMembership;
|
||||||
|
this._selfMembership = membership;
|
||||||
|
if (prevMembership !== membership) {
|
||||||
|
if (membership === "leave") {
|
||||||
|
this._cleanupAfterLeaving();
|
||||||
|
}
|
||||||
|
this.emit("Room.myMembership", this, membership, prevMembership);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Room.prototype._loadMembersFromServer = async function() {
|
Room.prototype._loadMembersFromServer = async function() {
|
||||||
@@ -470,7 +474,7 @@ Room.prototype.clearLoadedMembersIfNeeded = async function() {
|
|||||||
* called when sync receives this room in the leave section
|
* called when sync receives this room in the leave section
|
||||||
* to do cleanup after leaving a room. Possibly called multiple times.
|
* to do cleanup after leaving a room. Possibly called multiple times.
|
||||||
*/
|
*/
|
||||||
Room.prototype.onLeft = function() {
|
Room.prototype._cleanupAfterLeaving = function() {
|
||||||
this.clearLoadedMembersIfNeeded().catch((err) => {
|
this.clearLoadedMembersIfNeeded().catch((err) => {
|
||||||
console.error(`error after clearing loaded members from ` +
|
console.error(`error after clearing loaded members from ` +
|
||||||
`room ${this.roomId} after leaving`);
|
`room ${this.roomId} after leaving`);
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import Promise from 'bluebird';
|
|||||||
import SyncAccumulator from "../sync-accumulator";
|
import SyncAccumulator from "../sync-accumulator";
|
||||||
import utils from "../utils";
|
import utils from "../utils";
|
||||||
|
|
||||||
const VERSION = 2;
|
const VERSION = 3;
|
||||||
|
|
||||||
function createDatabase(db) {
|
function createDatabase(db) {
|
||||||
// Make user store, clobber based on user ID. (userId property of User objects)
|
// Make user store, clobber based on user ID. (userId property of User objects)
|
||||||
@@ -41,6 +41,12 @@ function upgradeSchemaV2(db) {
|
|||||||
oobMembersStore.createIndex("room", "room_id");
|
oobMembersStore.createIndex("room", "room_id");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function upgradeSchemaV3(db) {
|
||||||
|
db.createObjectStore("client_options",
|
||||||
|
{ keyPath: ["clobber"]});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper method to collect results from a Cursor and promiseify it.
|
* Helper method to collect results from a Cursor and promiseify it.
|
||||||
* @param {ObjectStore|Index} store The store to perform openCursor on.
|
* @param {ObjectStore|Index} store The store to perform openCursor on.
|
||||||
@@ -77,7 +83,7 @@ function txnAsPromise(txn) {
|
|||||||
resolve(event);
|
resolve(event);
|
||||||
};
|
};
|
||||||
txn.onerror = function(event) {
|
txn.onerror = function(event) {
|
||||||
reject(event);
|
reject(event.target.error);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -88,7 +94,7 @@ function reqAsEventPromise(req) {
|
|||||||
resolve(event);
|
resolve(event);
|
||||||
};
|
};
|
||||||
req.onerror = function(event) {
|
req.onerror = function(event) {
|
||||||
reject(event);
|
reject(event.target.error);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -123,6 +129,7 @@ const LocalIndexedDBStoreBackend = function LocalIndexedDBStoreBackend(
|
|||||||
this.db = null;
|
this.db = null;
|
||||||
this._disconnected = true;
|
this._disconnected = true;
|
||||||
this._syncAccumulator = new SyncAccumulator();
|
this._syncAccumulator = new SyncAccumulator();
|
||||||
|
this._isNewlyCreated = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -153,11 +160,15 @@ LocalIndexedDBStoreBackend.prototype = {
|
|||||||
`LocalIndexedDBStoreBackend.connect: upgrading from ${oldVersion}`,
|
`LocalIndexedDBStoreBackend.connect: upgrading from ${oldVersion}`,
|
||||||
);
|
);
|
||||||
if (oldVersion < 1) { // The database did not previously exist.
|
if (oldVersion < 1) { // The database did not previously exist.
|
||||||
|
this._isNewlyCreated = true;
|
||||||
createDatabase(db);
|
createDatabase(db);
|
||||||
}
|
}
|
||||||
if (oldVersion < 2) {
|
if (oldVersion < 2) {
|
||||||
upgradeSchemaV2(db);
|
upgradeSchemaV2(db);
|
||||||
}
|
}
|
||||||
|
if (oldVersion < 3) {
|
||||||
|
upgradeSchemaV3(db);
|
||||||
|
}
|
||||||
// Expand as needed.
|
// Expand as needed.
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -185,6 +196,10 @@ LocalIndexedDBStoreBackend.prototype = {
|
|||||||
return this._init();
|
return this._init();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
/** @return {bool} whether or not the database was newly created in this session. */
|
||||||
|
isNewlyCreated: function() {
|
||||||
|
return Promise.resolve(this._isNewlyCreated);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Having connected, load initial data from the database and prepare for use
|
* Having connected, load initial data from the database and prepare for use
|
||||||
@@ -529,6 +544,28 @@ LocalIndexedDBStoreBackend.prototype = {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getClientOptions: function() {
|
||||||
|
return Promise.resolve().then(() => {
|
||||||
|
const txn = this.db.transaction(["client_options"], "readonly");
|
||||||
|
const store = txn.objectStore("client_options");
|
||||||
|
return selectQuery(store, undefined, (cursor) => {
|
||||||
|
if (cursor.value && cursor.value && cursor.value.options) {
|
||||||
|
return cursor.value.options;
|
||||||
|
}
|
||||||
|
}).then((results) => results[0]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
storeClientOptions: async function(options) {
|
||||||
|
const txn = this.db.transaction(["client_options"], "readwrite");
|
||||||
|
const store = txn.objectStore("client_options");
|
||||||
|
store.put({
|
||||||
|
clobber: "-", // constant key so will always clobber
|
||||||
|
options: options,
|
||||||
|
}); // put == UPSERT
|
||||||
|
await txnAsPromise(txn);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default LocalIndexedDBStoreBackend;
|
export default LocalIndexedDBStoreBackend;
|
||||||
|
|||||||
@@ -65,7 +65,10 @@ RemoteIndexedDBStoreBackend.prototype = {
|
|||||||
clearDatabase: function() {
|
clearDatabase: function() {
|
||||||
return this._ensureStarted().then(() => this._doCmd('clearDatabase'));
|
return this._ensureStarted().then(() => this._doCmd('clearDatabase'));
|
||||||
},
|
},
|
||||||
|
/** @return {Promise<bool>} whether or not the database was newly created in this session. */
|
||||||
|
isNewlyCreated: function() {
|
||||||
|
return this._doCmd('isNewlyCreated');
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* @return {Promise} Resolves with a sync response to restore the
|
* @return {Promise} Resolves with a sync response to restore the
|
||||||
* client state to where it was at the last save, or null if there
|
* client state to where it was at the last save, or null if there
|
||||||
@@ -114,6 +117,14 @@ RemoteIndexedDBStoreBackend.prototype = {
|
|||||||
return this._doCmd('clearOutOfBandMembers', [roomId]);
|
return this._doCmd('clearOutOfBandMembers', [roomId]);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getClientOptions: function() {
|
||||||
|
return this._doCmd('getClientOptions');
|
||||||
|
},
|
||||||
|
|
||||||
|
storeClientOptions: function(options) {
|
||||||
|
return this._doCmd('storeClientOptions', [options]);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load all user presence events from the database. This is not cached.
|
* Load all user presence events from the database. This is not cached.
|
||||||
* @return {Promise<Object[]>} A list of presence events in their raw form.
|
* @return {Promise<Object[]>} A list of presence events in their raw form.
|
||||||
@@ -173,7 +184,9 @@ RemoteIndexedDBStoreBackend.prototype = {
|
|||||||
if (msg.command == 'cmd_success') {
|
if (msg.command == 'cmd_success') {
|
||||||
def.resolve(msg.result);
|
def.resolve(msg.result);
|
||||||
} else {
|
} else {
|
||||||
def.reject(msg.error);
|
const error = new Error(msg.error.message);
|
||||||
|
error.name = msg.error.name;
|
||||||
|
def.reject(error);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.warn("Unrecognised message from worker: " + msg);
|
console.warn("Unrecognised message from worker: " + msg);
|
||||||
|
|||||||
@@ -67,6 +67,9 @@ class IndexedDBStoreWorker {
|
|||||||
case 'connect':
|
case 'connect':
|
||||||
prom = this.backend.connect();
|
prom = this.backend.connect();
|
||||||
break;
|
break;
|
||||||
|
case 'isNewlyCreated':
|
||||||
|
prom = this.backend.isNewlyCreated();
|
||||||
|
break;
|
||||||
case 'clearDatabase':
|
case 'clearDatabase':
|
||||||
prom = this.backend.clearDatabase().then((result) => {
|
prom = this.backend.clearDatabase().then((result) => {
|
||||||
// This returns special classes which can't be cloned
|
// This returns special classes which can't be cloned
|
||||||
@@ -101,10 +104,16 @@ class IndexedDBStoreWorker {
|
|||||||
case 'setOutOfBandMembers':
|
case 'setOutOfBandMembers':
|
||||||
prom = this.backend.setOutOfBandMembers(msg.args[0], msg.args[1]);
|
prom = this.backend.setOutOfBandMembers(msg.args[0], msg.args[1]);
|
||||||
break;
|
break;
|
||||||
|
case 'getClientOptions':
|
||||||
|
prom = this.backend.getClientOptions();
|
||||||
|
break;
|
||||||
|
case 'storeClientOptions':
|
||||||
|
prom = this.backend.storeClientOptions(msg.args[0]);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prom === undefined) {
|
if (prom === undefined) {
|
||||||
postMessage({
|
this.postMessage({
|
||||||
command: 'cmd_fail',
|
command: 'cmd_fail',
|
||||||
seq: msg.seq,
|
seq: msg.seq,
|
||||||
// Can't be an Error because they're not structured cloneable
|
// Can't be an Error because they're not structured cloneable
|
||||||
@@ -126,7 +135,10 @@ class IndexedDBStoreWorker {
|
|||||||
command: 'cmd_fail',
|
command: 'cmd_fail',
|
||||||
seq: msg.seq,
|
seq: msg.seq,
|
||||||
// Just send a string because Error objects aren't cloneable
|
// Just send a string because Error objects aren't cloneable
|
||||||
error: "Error running command",
|
error: {
|
||||||
|
message: err.message,
|
||||||
|
name: err.name,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,6 +146,11 @@ IndexedDBStore.prototype.getSavedSync = function() {
|
|||||||
return this.backend.getSavedSync();
|
return this.backend.getSavedSync();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** @return {Promise<bool>} whether or not the database was newly created in this session. */
|
||||||
|
IndexedDBStore.prototype.isNewlyCreated = function() {
|
||||||
|
return this.backend.isNewlyCreated();
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {Promise} If there is a saved sync, the nextBatch token
|
* @return {Promise} If there is a saved sync, the nextBatch token
|
||||||
* for this sync, otherwise null.
|
* for this sync, otherwise null.
|
||||||
@@ -246,4 +251,12 @@ IndexedDBStore.prototype.clearOutOfBandMembers = function(roomId) {
|
|||||||
return this.backend.clearOutOfBandMembers(roomId);
|
return this.backend.clearOutOfBandMembers(roomId);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
IndexedDBStore.prototype.getClientOptions = function() {
|
||||||
|
return this.backend.getClientOptions();
|
||||||
|
};
|
||||||
|
|
||||||
|
IndexedDBStore.prototype.storeClientOptions = function(options) {
|
||||||
|
return this.backend.storeClientOptions(options);
|
||||||
|
};
|
||||||
|
|
||||||
module.exports.IndexedDBStore = IndexedDBStore;
|
module.exports.IndexedDBStore = IndexedDBStore;
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ module.exports.MatrixInMemoryStore = function MatrixInMemoryStore(opts) {
|
|||||||
this._oobMembers = {
|
this._oobMembers = {
|
||||||
// roomId: [member events]
|
// roomId: [member events]
|
||||||
};
|
};
|
||||||
|
this._clientOptions = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.MatrixInMemoryStore.prototype = {
|
module.exports.MatrixInMemoryStore.prototype = {
|
||||||
@@ -67,6 +68,10 @@ module.exports.MatrixInMemoryStore.prototype = {
|
|||||||
return this.syncToken;
|
return this.syncToken;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** @return {Promise<bool>} whether or not the database was newly created in this session. */
|
||||||
|
isNewlyCreated: function() {
|
||||||
|
return Promise.resolve(true);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the token to stream from.
|
* Set the token to stream from.
|
||||||
@@ -402,4 +407,13 @@ module.exports.MatrixInMemoryStore.prototype = {
|
|||||||
this._oobMembers[roomId] = membershipEvents;
|
this._oobMembers[roomId] = membershipEvents;
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getClientOptions: function() {
|
||||||
|
return Promise.resolve(this._clientOptions);
|
||||||
|
},
|
||||||
|
|
||||||
|
storeClientOptions: function(options) {
|
||||||
|
this._clientOptions = Object.assign({}, options);
|
||||||
|
return Promise.resolve();
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -32,6 +32,11 @@ function StubStore() {
|
|||||||
|
|
||||||
StubStore.prototype = {
|
StubStore.prototype = {
|
||||||
|
|
||||||
|
/** @return {Promise<bool>} whether or not the database was newly created in this session. */
|
||||||
|
isNewlyCreated: function() {
|
||||||
|
return Promise.resolve(true);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the sync token.
|
* Get the sync token.
|
||||||
* @return {string}
|
* @return {string}
|
||||||
@@ -276,6 +281,14 @@ StubStore.prototype = {
|
|||||||
clearOutOfBandMembers: function() {
|
clearOutOfBandMembers: function() {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getClientOptions: function() {
|
||||||
|
return Promise.resolve();
|
||||||
|
},
|
||||||
|
|
||||||
|
storeClientOptions: function() {
|
||||||
|
return Promise.resolve();
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Stub Store class. */
|
/** Stub Store class. */
|
||||||
|
|||||||
13
src/sync.js
13
src/sync.js
@@ -123,6 +123,7 @@ SyncApi.prototype.createRoom = function(roomId) {
|
|||||||
"Room.timelineReset",
|
"Room.timelineReset",
|
||||||
"Room.localEchoUpdated",
|
"Room.localEchoUpdated",
|
||||||
"Room.accountData",
|
"Room.accountData",
|
||||||
|
"Room.myMembership",
|
||||||
]);
|
]);
|
||||||
this._registerStateListeners(room);
|
this._registerStateListeners(room);
|
||||||
return room;
|
return room;
|
||||||
@@ -976,9 +977,10 @@ SyncApi.prototype._processSyncResponse = async function(
|
|||||||
// Handle invites
|
// Handle invites
|
||||||
inviteRooms.forEach(function(inviteObj) {
|
inviteRooms.forEach(function(inviteObj) {
|
||||||
const room = inviteObj.room;
|
const room = inviteObj.room;
|
||||||
room.setSyncedMembership("invite");
|
|
||||||
const stateEvents =
|
const stateEvents =
|
||||||
self._mapSyncEventsFormat(inviteObj.invite_state, room);
|
self._mapSyncEventsFormat(inviteObj.invite_state, room);
|
||||||
|
|
||||||
|
room.updateMyMembership("invite");
|
||||||
self._processRoomEvents(room, stateEvents);
|
self._processRoomEvents(room, stateEvents);
|
||||||
if (inviteObj.isBrandNewRoom) {
|
if (inviteObj.isBrandNewRoom) {
|
||||||
room.recalculate();
|
room.recalculate();
|
||||||
@@ -993,7 +995,6 @@ SyncApi.prototype._processSyncResponse = async function(
|
|||||||
// Handle joins
|
// Handle joins
|
||||||
await Promise.mapSeries(joinRooms, async function(joinObj) {
|
await Promise.mapSeries(joinRooms, async function(joinObj) {
|
||||||
const room = joinObj.room;
|
const room = joinObj.room;
|
||||||
room.setSyncedMembership("join");
|
|
||||||
const stateEvents = self._mapSyncEventsFormat(joinObj.state, room);
|
const stateEvents = self._mapSyncEventsFormat(joinObj.state, room);
|
||||||
const timelineEvents = self._mapSyncEventsFormat(joinObj.timeline, room);
|
const timelineEvents = self._mapSyncEventsFormat(joinObj.timeline, room);
|
||||||
const ephemeralEvents = self._mapSyncEventsFormat(joinObj.ephemeral);
|
const ephemeralEvents = self._mapSyncEventsFormat(joinObj.ephemeral);
|
||||||
@@ -1009,6 +1010,8 @@ SyncApi.prototype._processSyncResponse = async function(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
room.updateMyMembership("join");
|
||||||
|
|
||||||
joinObj.timeline = joinObj.timeline || {};
|
joinObj.timeline = joinObj.timeline || {};
|
||||||
|
|
||||||
if (joinObj.isBrandNewRoom) {
|
if (joinObj.isBrandNewRoom) {
|
||||||
@@ -1116,8 +1119,6 @@ SyncApi.prototype._processSyncResponse = async function(
|
|||||||
// Handle leaves (e.g. kicked rooms)
|
// Handle leaves (e.g. kicked rooms)
|
||||||
leaveRooms.forEach(function(leaveObj) {
|
leaveRooms.forEach(function(leaveObj) {
|
||||||
const room = leaveObj.room;
|
const room = leaveObj.room;
|
||||||
room.setSyncedMembership("leave");
|
|
||||||
|
|
||||||
const stateEvents =
|
const stateEvents =
|
||||||
self._mapSyncEventsFormat(leaveObj.state, room);
|
self._mapSyncEventsFormat(leaveObj.state, room);
|
||||||
const timelineEvents =
|
const timelineEvents =
|
||||||
@@ -1125,6 +1126,8 @@ SyncApi.prototype._processSyncResponse = async function(
|
|||||||
const accountDataEvents =
|
const accountDataEvents =
|
||||||
self._mapSyncEventsFormat(leaveObj.account_data);
|
self._mapSyncEventsFormat(leaveObj.account_data);
|
||||||
|
|
||||||
|
room.updateMyMembership("leave");
|
||||||
|
|
||||||
self._processRoomEvents(room, stateEvents, timelineEvents);
|
self._processRoomEvents(room, stateEvents, timelineEvents);
|
||||||
room.addAccountData(accountDataEvents);
|
room.addAccountData(accountDataEvents);
|
||||||
|
|
||||||
@@ -1145,8 +1148,6 @@ SyncApi.prototype._processSyncResponse = async function(
|
|||||||
accountDataEvents.forEach(function(e) {
|
accountDataEvents.forEach(function(e) {
|
||||||
client.emit("event", e);
|
client.emit("event", e);
|
||||||
});
|
});
|
||||||
|
|
||||||
room.onLeft();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// update the notification timeline, if appropriate.
|
// update the notification timeline, if appropriate.
|
||||||
|
|||||||
Reference in New Issue
Block a user