Compare commits
89 Commits
v34.1.0
...
v34.5.0-rc.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 5a1c70ad19 | |||
| 27cb16ffe4 | |||
| 9be0b3e701 | |||
| 05ba27f36b | |||
| e6acfdf275 | |||
| 2a6612c73a | |||
| 2f8b05b0da | |||
| fe984ede6e | |||
| 3f74b9a0cc | |||
| 802b996b10 | |||
| 8d44f9d665 | |||
| a72a1b294a | |||
| ab5f32f984 | |||
| 6a21d812ab | |||
| ee94e93354 | |||
| 31c4786a96 | |||
| 17b6e59819 | |||
| 42510022a1 | |||
| 5f0978ac3f | |||
| cd6787e0ac | |||
| 03baa3e358 | |||
| 658563e2a7 | |||
| 26d3033b17 | |||
| a4bd7dc7d7 | |||
| 1f48544b38 | |||
| 7ef4062f59 | |||
| 968bc51a35 | |||
| d413f5042e | |||
| b8e8b14375 | |||
| 43e58871de | |||
| 2544c14032 | |||
| 8d19782c57 | |||
| 340bbe1a8f | |||
| 8214fd7156 | |||
| a0efed8b88 | |||
| c408c0d1d5 | |||
| d6080398db | |||
| 467908703b | |||
| 87eddaf51a | |||
| c65ef03567 | |||
| 78cbf7cd28 | |||
| dc7c1a4fef | |||
| 1ae0c2f3ee | |||
| affaa95fb4 | |||
| 9176d3a671 | |||
| de50129a53 | |||
| 5568dfdd41 | |||
| 39216d44ed | |||
| 8c3b249567 | |||
| b8e40ad2a8 | |||
| 4e2831764d | |||
| 09780672aa | |||
| 0fe53876ec | |||
| dfec3dc33c | |||
| fbdd78b428 | |||
| e10c362ef0 | |||
| 89a9a7fa38 | |||
| 687d08dc9d | |||
| 7f91db83d0 | |||
| 0300d6343f | |||
| e0ef467d7d | |||
| dc1cccfecc | |||
| 79299891fd | |||
| d32f398345 | |||
| 0f08c00c07 | |||
| 6b261b98c9 | |||
| 99f157a0f1 | |||
| f9f6d81346 | |||
| 46604abe7b | |||
| 553758e713 | |||
| 509e64cfc1 | |||
| 60c2e9b3ed | |||
| cfb21fa80a | |||
| c3d7f4e730 | |||
| aa97beae44 | |||
| 5feab37166 | |||
| 1a02835ab2 | |||
| 6f63ff1711 | |||
| 30a26813ec | |||
| f17a4fedb9 | |||
| 94e393c9a6 | |||
| 53201688a6 | |||
| 996663bf64 | |||
| d6e4338a37 | |||
| b2665f2128 | |||
| af4b6bc126 | |||
| 565bb0ef7c | |||
| fe0edcd081 | |||
| 51544f25a7 |
@@ -2,6 +2,7 @@
|
||||
/.github/workflows/** @matrix-org/element-web-team
|
||||
/package.json @matrix-org/element-web-team
|
||||
/yarn.lock @matrix-org/element-web-team
|
||||
/scripts/** @matrix-org/element-web-team
|
||||
/src/webrtc @matrix-org/element-call-reviewers
|
||||
/src/matrixrtc @matrix-org/element-call-reviewers
|
||||
/spec/*/webrtc @matrix-org/element-call-reviewers
|
||||
|
||||
@@ -22,7 +22,7 @@ runs:
|
||||
|
||||
- name: Upload tarball signature
|
||||
if: ${{ inputs.upload-url }}
|
||||
uses: shogo82148/actions-upload-release-asset@8f032eff0255912cc9c8455797fd6d72f25c7ab7 # v1
|
||||
uses: shogo82148/actions-upload-release-asset@6d4fd50e333ee797e83ae380e6bc55b048baec41 # v1
|
||||
with:
|
||||
upload_url: ${{ inputs.upload-url }}
|
||||
asset_path: ${{ env.VERSION }}.tar.gz.asc
|
||||
|
||||
@@ -29,13 +29,13 @@ runs:
|
||||
|
||||
- name: Upload asset signatures
|
||||
if: inputs.gpg-fingerprint
|
||||
uses: shogo82148/actions-upload-release-asset@8f032eff0255912cc9c8455797fd6d72f25c7ab7 # v1
|
||||
uses: shogo82148/actions-upload-release-asset@6d4fd50e333ee797e83ae380e6bc55b048baec41 # v1
|
||||
with:
|
||||
upload_url: ${{ inputs.upload-url }}
|
||||
asset_path: ${{ inputs.asset-path }}.asc
|
||||
|
||||
- name: Upload assets
|
||||
uses: shogo82148/actions-upload-release-asset@8f032eff0255912cc9c8455797fd6d72f25c7ab7 # v1
|
||||
uses: shogo82148/actions-upload-release-asset@6d4fd50e333ee797e83ae380e6bc55b048baec41 # v1
|
||||
with:
|
||||
upload_url: ${{ inputs.upload-url }}
|
||||
asset_path: ${{ inputs.asset-path }}
|
||||
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
script: |
|
||||
const { RELEASE_ID: releaseId, DEPENDENCY, VERSION } = process.env;
|
||||
const { owner, repo } = context.repo;
|
||||
const script = require("./.action-repo/scripts/release/merge-release-notes.js");
|
||||
const script = require("./.action-repo/scripts/release/merge-release-notes.cjs");
|
||||
|
||||
let deps = [];
|
||||
if (DEPENDENCY.includes("/")) {
|
||||
|
||||
@@ -34,6 +34,7 @@ jobs:
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
cache: "yarn"
|
||||
node-version-file: package.json
|
||||
|
||||
- name: Install Deps
|
||||
run: "yarn install --frozen-lockfile"
|
||||
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
specs: [integ, unit]
|
||||
node: ["lts/*", 21, 22]
|
||||
node: ["lts/*", 22]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
@@ -63,6 +63,16 @@ jobs:
|
||||
coverage
|
||||
!coverage/lcov-report
|
||||
|
||||
# Dummy completion job to simplify branch protections
|
||||
jest-complete:
|
||||
name: Jest tests
|
||||
needs: jest
|
||||
if: always()
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- if: needs.jest.result != 'skipped' && needs.jest.result != 'success'
|
||||
run: exit 1
|
||||
|
||||
matrix-react-sdk:
|
||||
name: Downstream test matrix-react-sdk
|
||||
if: github.event_name == 'merge_group'
|
||||
|
||||
@@ -1,3 +1,46 @@
|
||||
Changes in [34.4.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v34.4.0) (2024-08-27)
|
||||
==================================================================================================
|
||||
## ✨ Features
|
||||
|
||||
* Use non-legacy calls if any are found ([#4337](https://github.com/matrix-org/matrix-js-sdk/pull/4337)). Contributed by @AndrewFerr.
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
|
||||
* Retry event decryption failures on first failure ([#4346](https://github.com/matrix-org/matrix-js-sdk/pull/4346)). Contributed by @hughns.
|
||||
* Ensure "type" = "module" ES declaration in pre-release.sh ([#4350](https://github.com/matrix-org/matrix-js-sdk/pull/4350)). Contributed by @BLCK-B.
|
||||
* Handle MatrixRTC encryption keys arriving out of order ([#4345](https://github.com/matrix-org/matrix-js-sdk/pull/4345)). Contributed by @hughns.
|
||||
* Resend MatrixRTC encryption keys if a membership has changed ([#4343](https://github.com/matrix-org/matrix-js-sdk/pull/4343)). Contributed by @hughns.
|
||||
|
||||
|
||||
Changes in [34.3.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v34.3.1) (2024-08-20)
|
||||
==================================================================================================
|
||||
# Security
|
||||
- Fixes for [CVE-2024-42369](https://nvd.nist.gov/vuln/detail/CVE-2024-42369) / [GHSA-vhr5-g3pm-49fm](https://github.com/matrix-org/matrix-js-sdk/security/advisories/GHSA-vhr5-g3pm-49fm).
|
||||
|
||||
Changes in [34.3.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v34.3.0) (2024-08-13)
|
||||
==================================================================================================
|
||||
## ✨ Features
|
||||
|
||||
* Bump matrix-widget-api ([#4336](https://github.com/matrix-org/matrix-js-sdk/pull/4336)). Contributed by @AndrewFerr.
|
||||
* Also check for MSC3757 for session state keys ([#4334](https://github.com/matrix-org/matrix-js-sdk/pull/4334)). Contributed by @AndrewFerr.
|
||||
* Support Futures via widgets ([#4311](https://github.com/matrix-org/matrix-js-sdk/pull/4311)). Contributed by @AndrewFerr.
|
||||
* Support MSC4140: Delayed events (Futures) ([#4294](https://github.com/matrix-org/matrix-js-sdk/pull/4294)). Contributed by @AndrewFerr.
|
||||
* Handle late-arriving `m.room_key.withheld` messages ([#4310](https://github.com/matrix-org/matrix-js-sdk/pull/4310)). Contributed by @richvdh.
|
||||
* Be specific about what is considered a MSC4143 call member event. ([#4328](https://github.com/matrix-org/matrix-js-sdk/pull/4328)). Contributed by @toger5.
|
||||
* Add index.ts for matrixrtc module ([#4314](https://github.com/matrix-org/matrix-js-sdk/pull/4314)). Contributed by @toger5.
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
|
||||
* Fix hashed ID server lookups with no Olm ([#4333](https://github.com/matrix-org/matrix-js-sdk/pull/4333)). Contributed by @dbkr.
|
||||
|
||||
|
||||
Changes in [34.2.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v34.2.0) (2024-07-30)
|
||||
==================================================================================================
|
||||
## 🐛 Bug Fixes
|
||||
|
||||
* Element-R: detect "withheld key" UTD errors, and mark them as such ([#4302](https://github.com/matrix-org/matrix-js-sdk/pull/4302)). Contributed by @richvdh.
|
||||
|
||||
|
||||
Changes in [34.1.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v34.1.0) (2024-07-16)
|
||||
==================================================================================================
|
||||
## ✨ Features
|
||||
|
||||
@@ -39,7 +39,7 @@ client.publicRooms(function (err, data) {
|
||||
```
|
||||
|
||||
See below for how to include libolm to enable end-to-end-encryption. Please check
|
||||
[the Node.js terminal app](examples/node) for a more complex example.
|
||||
[the Node.js terminal app](examples/node/README.md) for a more complex example.
|
||||
|
||||
To start the client:
|
||||
|
||||
|
||||
+10
-1
@@ -14,7 +14,16 @@ module.exports = {
|
||||
modules: process.env.NODE_ENV === "test" ? "commonjs" : false,
|
||||
},
|
||||
],
|
||||
"@babel/preset-typescript",
|
||||
[
|
||||
"@babel/preset-typescript",
|
||||
{
|
||||
// When using the transpiled javascript in `lib`, Node.js requires `.js` extensions on any `import`
|
||||
// specifiers. However, Jest uses the TS source (via babel) and fails to resolve the `.js` names.
|
||||
// To resolve this,we use the `.ts` names in the source, and rewrite the `import` specifiers to use
|
||||
// `.js` during transpilation, *except* when we are targetting Jest.
|
||||
rewriteImportExtensions: process.env.NODE_ENV !== "test",
|
||||
},
|
||||
],
|
||||
],
|
||||
plugins: [
|
||||
"@babel/plugin-transform-numeric-separator",
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ if (env["GITHUB_ACTIONS"] !== undefined) {
|
||||
|
||||
// if we're running against the develop branch, also enable the slow test reporter
|
||||
if (env["GITHUB_REF"] == "refs/heads/develop") {
|
||||
reporters.push("<rootDir>/spec/slowReporter.js");
|
||||
reporters.push("<rootDir>/spec/slowReporter.cjs");
|
||||
}
|
||||
config.reporters = reporters;
|
||||
}
|
||||
|
||||
+11
-15
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "matrix-js-sdk",
|
||||
"version": "34.1.0",
|
||||
"version": "34.5.0-rc.0",
|
||||
"description": "Matrix Client-Server SDK for Javascript",
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
@@ -31,13 +31,10 @@
|
||||
"keywords": [
|
||||
"matrix-org"
|
||||
],
|
||||
"type": "module",
|
||||
"main": "./lib/index.js",
|
||||
"browser": "./lib/browser-index.js",
|
||||
"matrix_src_main": "./src/index.ts",
|
||||
"matrix_src_browser": "./src/browser-index.ts",
|
||||
"matrix_lib_main": "./lib/index.js",
|
||||
"matrix_lib_browser": "./lib/browser-index.js",
|
||||
"matrix_lib_typings": "./lib/index.d.ts",
|
||||
"typings": "./lib/index.d.ts",
|
||||
"author": "matrix.org",
|
||||
"license": "Apache-2.0",
|
||||
"files": [
|
||||
@@ -53,7 +50,7 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@matrix-org/matrix-sdk-crypto-wasm": "^6.0.0",
|
||||
"@matrix-org/matrix-sdk-crypto-wasm": "^7.0.0",
|
||||
"@matrix-org/olm": "3.2.15",
|
||||
"another-json": "^0.2.0",
|
||||
"bs58": "^6.0.0",
|
||||
@@ -61,7 +58,7 @@
|
||||
"jwt-decode": "^4.0.0",
|
||||
"loglevel": "^1.7.1",
|
||||
"matrix-events-sdk": "0.0.1",
|
||||
"matrix-widget-api": "^1.6.0",
|
||||
"matrix-widget-api": "^1.8.2",
|
||||
"oidc-client-ts": "^3.0.1",
|
||||
"p-retry": "4",
|
||||
"sdp-transform": "^2.14.1",
|
||||
@@ -101,12 +98,12 @@
|
||||
"eslint-import-resolver-typescript": "^3.5.1",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-jest": "^28.0.0",
|
||||
"eslint-plugin-jsdoc": "^48.0.0",
|
||||
"eslint-plugin-jsdoc": "^50.0.0",
|
||||
"eslint-plugin-matrix-org": "^1.0.0",
|
||||
"eslint-plugin-tsdoc": "^0.3.0",
|
||||
"eslint-plugin-unicorn": "^54.0.0",
|
||||
"eslint-plugin-unicorn": "^55.0.0",
|
||||
"fake-indexeddb": "^5.0.2",
|
||||
"fetch-mock": "10.0.7",
|
||||
"fetch-mock": "11.1.1",
|
||||
"fetch-mock-jest": "^1.5.1",
|
||||
"husky": "^9.0.0",
|
||||
"jest": "^29.0.0",
|
||||
@@ -117,8 +114,8 @@
|
||||
"lint-staged": "^15.0.2",
|
||||
"matrix-mock-request": "^2.5.0",
|
||||
"node-fetch": "^2.7.0",
|
||||
"prettier": "3.3.2",
|
||||
"rimraf": "^5.0.0",
|
||||
"prettier": "3.3.3",
|
||||
"rimraf": "^6.0.0",
|
||||
"ts-node": "^10.9.2",
|
||||
"typedoc": "^0.26.0",
|
||||
"typedoc-plugin-coverage": "^3.0.0",
|
||||
@@ -130,6 +127,5 @@
|
||||
"outputDirectory": "coverage",
|
||||
"outputName": "jest-sonar-report.xml",
|
||||
"relativePaths": true
|
||||
},
|
||||
"typings": "./lib/index.d.ts"
|
||||
}
|
||||
}
|
||||
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fsProm = require("fs/promises");
|
||||
|
||||
const PKGJSON = "package.json";
|
||||
|
||||
async function main() {
|
||||
const pkgJson = JSON.parse(await fsProm.readFile(PKGJSON, "utf8"));
|
||||
for (const field of ["main", "typings"]) {
|
||||
if (pkgJson["matrix_lib_" + field] !== undefined) {
|
||||
pkgJson[field] = pkgJson["matrix_lib_" + field];
|
||||
}
|
||||
}
|
||||
|
||||
await fsProm.writeFile(PKGJSON, JSON.stringify(pkgJson, null, 2));
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -1,22 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fsProm = require("fs/promises");
|
||||
|
||||
const PKGJSON = "package.json";
|
||||
|
||||
async function main() {
|
||||
const pkgJson = JSON.parse(await fsProm.readFile(PKGJSON, "utf8"));
|
||||
for (const field of ["main", "typings"]) {
|
||||
if (pkgJson["matrix_lib_" + field] !== undefined) {
|
||||
pkgJson[field] = pkgJson["matrix_lib_" + field];
|
||||
}
|
||||
}
|
||||
|
||||
// matrix-js-sdk is built into ECMAScript modules. Make sure we declare it as such.
|
||||
// See https://nodejs.org/api/packages.html#type.
|
||||
pkgJson["type"] = "module";
|
||||
|
||||
await fsProm.writeFile(PKGJSON, JSON.stringify(pkgJson, null, 2));
|
||||
}
|
||||
|
||||
main();
|
||||
+1
@@ -0,0 +1 @@
|
||||
switch_package_to_release.cjs
|
||||
@@ -2333,8 +2333,84 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,
|
||||
});
|
||||
|
||||
describe("m.room_key.withheld handling", () => {
|
||||
// TODO: there are a bunch more tests for this sort of thing in spec/unit/crypto/algorithms/megolm.spec.ts.
|
||||
// They should be converted to integ tests and moved.
|
||||
describe.each([
|
||||
["m.blacklisted", "The sender has blocked you.", DecryptionFailureCode.MEGOLM_KEY_WITHHELD],
|
||||
[
|
||||
"m.unverified",
|
||||
"The sender has disabled encrypting to unverified devices.",
|
||||
DecryptionFailureCode.MEGOLM_KEY_WITHHELD_FOR_UNVERIFIED_DEVICE,
|
||||
],
|
||||
])(
|
||||
"Decryption fails with withheld error if a withheld notice with code '%s' is received",
|
||||
(withheldCode, expectedMessage, expectedErrorCode) => {
|
||||
it.each(["before", "after"])("%s the event", async (when) => {
|
||||
expectAliceKeyQuery({ device_keys: { "@alice:localhost": {} }, failures: {} });
|
||||
await startClientAndAwaitFirstSync();
|
||||
|
||||
// A promise which resolves, with the MatrixEvent which wraps the event, once the decryption fails.
|
||||
let awaitDecryption = emitPromise(aliceClient, MatrixEventEvent.Decrypted);
|
||||
|
||||
// Send Alice an encrypted room event which looks like it was encrypted with a megolm session
|
||||
async function sendEncryptedEvent() {
|
||||
const event = {
|
||||
...testData.ENCRYPTED_EVENT,
|
||||
origin_server_ts: Date.now(),
|
||||
};
|
||||
const syncResponse = {
|
||||
next_batch: 1,
|
||||
rooms: { join: { [ROOM_ID]: { timeline: { events: [event] } } } },
|
||||
};
|
||||
|
||||
syncResponder.sendOrQueueSyncResponse(syncResponse);
|
||||
await syncPromise(aliceClient);
|
||||
}
|
||||
|
||||
// Send Alice a withheld notice
|
||||
async function sendWithheldMessage() {
|
||||
const withheldMessage = {
|
||||
type: "m.room_key.withheld",
|
||||
sender: "@bob:example.com",
|
||||
content: {
|
||||
algorithm: "m.megolm.v1.aes-sha2",
|
||||
room_id: ROOM_ID,
|
||||
sender_key: testData.ENCRYPTED_EVENT.content!.sender_key,
|
||||
session_id: testData.ENCRYPTED_EVENT.content!.session_id,
|
||||
code: withheldCode,
|
||||
reason: "zzz",
|
||||
},
|
||||
};
|
||||
|
||||
syncResponder.sendOrQueueSyncResponse({
|
||||
next_batch: 1,
|
||||
to_device: { events: [withheldMessage] },
|
||||
});
|
||||
await syncPromise(aliceClient);
|
||||
}
|
||||
|
||||
if (when === "before") {
|
||||
await sendWithheldMessage();
|
||||
await sendEncryptedEvent();
|
||||
} else {
|
||||
await sendEncryptedEvent();
|
||||
// Make sure that the first attempt to decrypt has happened before the withheld arrives
|
||||
await awaitDecryption;
|
||||
awaitDecryption = emitPromise(aliceClient, MatrixEventEvent.Decrypted);
|
||||
await sendWithheldMessage();
|
||||
}
|
||||
|
||||
const ev = await awaitDecryption;
|
||||
expect(ev.getContent()).toEqual({
|
||||
body: `** Unable to decrypt: DecryptionError: ${expectedMessage} **`,
|
||||
msgtype: "m.bad.encrypted",
|
||||
});
|
||||
|
||||
expect(ev.decryptionFailureReason).toEqual(expectedErrorCode);
|
||||
|
||||
// `isEncryptedDisabledForUnverifiedDevices` should be true for `m.unverified` and false for other errors.
|
||||
expect(ev.isEncryptedDisabledForUnverifiedDevices).toEqual(withheldCode === "m.unverified");
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
oldBackendOnly("does not block decryption on an 'm.unavailable' report", async function () {
|
||||
// there may be a key downloads for alice
|
||||
|
||||
@@ -268,7 +268,8 @@ describe("MSC4108SignInWithQR", () => {
|
||||
it("should abort if device doesn't come up by timeout", async () => {
|
||||
jest.spyOn(global, "setTimeout").mockImplementation((fn) => {
|
||||
(<Function>fn)();
|
||||
return -1;
|
||||
// TODO: mock timers properly
|
||||
return -1 as any;
|
||||
});
|
||||
jest.spyOn(Date, "now").mockImplementation(() => {
|
||||
return 12345678 + mocked(setTimeout).mock.calls.length * 1000;
|
||||
@@ -320,7 +321,8 @@ describe("MSC4108SignInWithQR", () => {
|
||||
it("should not send secrets if user cancels", async () => {
|
||||
jest.spyOn(global, "setTimeout").mockImplementation((fn) => {
|
||||
(<Function>fn)();
|
||||
return -1;
|
||||
// TODO: mock timers properly
|
||||
return -1 as any;
|
||||
});
|
||||
|
||||
await Promise.all([ourLogin.negotiateProtocols(), opponentLogin.negotiateProtocols()]);
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
Copyright 2024 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 { encodeUnpaddedBase64Url } from "../../src";
|
||||
import { sha256 } from "../../src/digest";
|
||||
|
||||
describe("sha256", () => {
|
||||
it("should hash a string", async () => {
|
||||
const hash = await sha256("test");
|
||||
expect(encodeUnpaddedBase64Url(hash)).toBe("n4bQgYhMfWWaL-qgxVrQFaO_TxsrC4Is0V1sFbDwCgg");
|
||||
});
|
||||
|
||||
it("should hash a string with emoji", async () => {
|
||||
const hash = await sha256("test 🍱");
|
||||
expect(encodeUnpaddedBase64Url(hash)).toBe("X2aDNrrwfq3nCTOl90R9qg9ynxhHnSzsMqtrdYX-SGw");
|
||||
});
|
||||
|
||||
it("throws if webcrypto is not available", async () => {
|
||||
const oldCrypto = global.crypto;
|
||||
try {
|
||||
global.crypto = {} as any;
|
||||
await expect(sha256("test")).rejects.toThrow();
|
||||
} finally {
|
||||
global.crypto = oldCrypto;
|
||||
}
|
||||
});
|
||||
});
|
||||
+236
-3
@@ -32,7 +32,7 @@ import {
|
||||
IOpenIDCredentials,
|
||||
} from "matrix-widget-api";
|
||||
|
||||
import { createRoomWidgetClient, MsgType } from "../../src/matrix";
|
||||
import { createRoomWidgetClient, MsgType, UpdateDelayedEventAction } from "../../src/matrix";
|
||||
import { MatrixClient, ClientEvent, ITurnServer as IClientTurnServer } from "../../src/client";
|
||||
import { SyncState } from "../../src/sync";
|
||||
import { ICapabilities } from "../../src/embedded";
|
||||
@@ -59,8 +59,26 @@ class MockWidgetApi extends EventEmitter {
|
||||
public requestCapabilityToReceiveState = jest.fn();
|
||||
public requestCapabilityToSendToDevice = jest.fn();
|
||||
public requestCapabilityToReceiveToDevice = jest.fn();
|
||||
public sendRoomEvent = jest.fn(() => ({ event_id: `$${Math.random()}` }));
|
||||
public sendStateEvent = jest.fn();
|
||||
public sendRoomEvent = jest.fn(
|
||||
(eventType: string, content: unknown, roomId?: string, delay?: number, parentDelayId?: string) =>
|
||||
delay === undefined && parentDelayId === undefined
|
||||
? { event_id: `$${Math.random()}` }
|
||||
: { delay_id: `id-${Math.random()}` },
|
||||
);
|
||||
public sendStateEvent = jest.fn(
|
||||
(
|
||||
eventType: string,
|
||||
stateKey: string,
|
||||
content: unknown,
|
||||
roomId?: string,
|
||||
delay?: number,
|
||||
parentDelayId?: string,
|
||||
) =>
|
||||
delay === undefined && parentDelayId === undefined
|
||||
? { event_id: `$${Math.random()}` }
|
||||
: { delay_id: `id-${Math.random()}` },
|
||||
);
|
||||
public updateDelayedEvent = jest.fn();
|
||||
public sendToDevice = jest.fn();
|
||||
public requestOpenIDConnectToken = jest.fn(() => {
|
||||
return testOIDCToken;
|
||||
@@ -125,6 +143,17 @@ describe("RoomWidgetClient", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("send handles wrong field in response", async () => {
|
||||
await makeClient({ sendEvent: ["org.matrix.rageshake_request"] });
|
||||
widgetApi.sendRoomEvent.mockResolvedValueOnce({
|
||||
room_id: "!1:example.org",
|
||||
delay_id: `id-${Math.random}`,
|
||||
});
|
||||
await expect(
|
||||
client.sendEvent("!1:example.org", "org.matrix.rageshake_request", { request_id: 123 }),
|
||||
).rejects.toThrow();
|
||||
});
|
||||
|
||||
it("receives", async () => {
|
||||
const event = new MatrixEvent({
|
||||
type: "org.matrix.rageshake_request",
|
||||
@@ -160,6 +189,199 @@ describe("RoomWidgetClient", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("delayed events", () => {
|
||||
describe("when supported", () => {
|
||||
const doesServerSupportUnstableFeatureMock = jest.fn((feature) =>
|
||||
Promise.resolve(feature === "org.matrix.msc4140"),
|
||||
);
|
||||
|
||||
beforeAll(() => {
|
||||
MatrixClient.prototype.doesServerSupportUnstableFeature = doesServerSupportUnstableFeatureMock;
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
doesServerSupportUnstableFeatureMock.mockReset();
|
||||
});
|
||||
|
||||
it("sends delayed message events", async () => {
|
||||
await makeClient({ sendDelayedEvents: true, sendEvent: ["org.matrix.rageshake_request"] });
|
||||
expect(widgetApi.requestCapability).toHaveBeenCalledWith(MatrixCapabilities.MSC4157SendDelayedEvent);
|
||||
await client._unstable_sendDelayedEvent(
|
||||
"!1:example.org",
|
||||
{ delay: 2000 },
|
||||
null,
|
||||
"org.matrix.rageshake_request",
|
||||
{ request_id: 123 },
|
||||
);
|
||||
expect(widgetApi.sendRoomEvent).toHaveBeenCalledWith(
|
||||
"org.matrix.rageshake_request",
|
||||
{ request_id: 123 },
|
||||
"!1:example.org",
|
||||
2000,
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
it("sends child action delayed message events", async () => {
|
||||
await makeClient({ sendDelayedEvents: true, sendEvent: ["org.matrix.rageshake_request"] });
|
||||
expect(widgetApi.requestCapability).toHaveBeenCalledWith(MatrixCapabilities.MSC4157SendDelayedEvent);
|
||||
const parentDelayId = `id-${Math.random()}`;
|
||||
await client._unstable_sendDelayedEvent(
|
||||
"!1:example.org",
|
||||
{ parent_delay_id: parentDelayId },
|
||||
null,
|
||||
"org.matrix.rageshake_request",
|
||||
{ request_id: 123 },
|
||||
);
|
||||
expect(widgetApi.sendRoomEvent).toHaveBeenCalledWith(
|
||||
"org.matrix.rageshake_request",
|
||||
{ request_id: 123 },
|
||||
"!1:example.org",
|
||||
undefined,
|
||||
parentDelayId,
|
||||
);
|
||||
});
|
||||
|
||||
it("sends delayed state events", async () => {
|
||||
await makeClient({
|
||||
sendDelayedEvents: true,
|
||||
sendState: [{ eventType: "org.example.foo", stateKey: "bar" }],
|
||||
});
|
||||
expect(widgetApi.requestCapability).toHaveBeenCalledWith(MatrixCapabilities.MSC4157SendDelayedEvent);
|
||||
await client._unstable_sendDelayedStateEvent(
|
||||
"!1:example.org",
|
||||
{ delay: 2000 },
|
||||
"org.example.foo",
|
||||
{ hello: "world" },
|
||||
"bar",
|
||||
);
|
||||
expect(widgetApi.sendStateEvent).toHaveBeenCalledWith(
|
||||
"org.example.foo",
|
||||
"bar",
|
||||
{ hello: "world" },
|
||||
"!1:example.org",
|
||||
2000,
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
it("sends child action delayed state events", async () => {
|
||||
await makeClient({
|
||||
sendDelayedEvents: true,
|
||||
sendState: [{ eventType: "org.example.foo", stateKey: "bar" }],
|
||||
});
|
||||
expect(widgetApi.requestCapability).toHaveBeenCalledWith(MatrixCapabilities.MSC4157SendDelayedEvent);
|
||||
const parentDelayId = `fg-${Math.random()}`;
|
||||
await client._unstable_sendDelayedStateEvent(
|
||||
"!1:example.org",
|
||||
{ parent_delay_id: parentDelayId },
|
||||
"org.example.foo",
|
||||
{ hello: "world" },
|
||||
"bar",
|
||||
);
|
||||
expect(widgetApi.sendStateEvent).toHaveBeenCalledWith(
|
||||
"org.example.foo",
|
||||
"bar",
|
||||
{ hello: "world" },
|
||||
"!1:example.org",
|
||||
undefined,
|
||||
parentDelayId,
|
||||
);
|
||||
});
|
||||
|
||||
it("send delayed message events handles wrong field in response", async () => {
|
||||
await makeClient({ sendDelayedEvents: true, sendEvent: ["org.matrix.rageshake_request"] });
|
||||
widgetApi.sendRoomEvent.mockResolvedValueOnce({
|
||||
room_id: "!1:example.org",
|
||||
event_id: `$${Math.random()}`,
|
||||
});
|
||||
await expect(
|
||||
client._unstable_sendDelayedEvent(
|
||||
"!1:example.org",
|
||||
{ delay: 2000 },
|
||||
null,
|
||||
"org.matrix.rageshake_request",
|
||||
{ request_id: 123 },
|
||||
),
|
||||
).rejects.toThrow();
|
||||
});
|
||||
|
||||
it("send delayed state events handles wrong field in response", async () => {
|
||||
await makeClient({
|
||||
sendDelayedEvents: true,
|
||||
sendState: [{ eventType: "org.example.foo", stateKey: "bar" }],
|
||||
});
|
||||
widgetApi.sendStateEvent.mockResolvedValueOnce({
|
||||
room_id: "!1:example.org",
|
||||
event_id: `$${Math.random()}`,
|
||||
});
|
||||
await expect(
|
||||
client._unstable_sendDelayedStateEvent(
|
||||
"!1:example.org",
|
||||
{ delay: 2000 },
|
||||
"org.example.foo",
|
||||
{ hello: "world" },
|
||||
"bar",
|
||||
),
|
||||
).rejects.toThrow();
|
||||
});
|
||||
|
||||
it("updates delayed events", async () => {
|
||||
await makeClient({ updateDelayedEvents: true, sendEvent: ["org.matrix.rageshake_request"] });
|
||||
expect(widgetApi.requestCapability).toHaveBeenCalledWith(MatrixCapabilities.MSC4157UpdateDelayedEvent);
|
||||
for (const action of [
|
||||
UpdateDelayedEventAction.Cancel,
|
||||
UpdateDelayedEventAction.Restart,
|
||||
UpdateDelayedEventAction.Send,
|
||||
]) {
|
||||
await client._unstable_updateDelayedEvent("id", action);
|
||||
expect(widgetApi.updateDelayedEvent).toHaveBeenCalledWith("id", action);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("when unsupported", () => {
|
||||
it("fails to send delayed message events", async () => {
|
||||
await makeClient({ sendEvent: ["org.matrix.rageshake_request"] });
|
||||
await expect(
|
||||
client._unstable_sendDelayedEvent(
|
||||
"!1:example.org",
|
||||
{ delay: 2000 },
|
||||
null,
|
||||
"org.matrix.rageshake_request",
|
||||
{ request_id: 123 },
|
||||
),
|
||||
).rejects.toThrow("Server does not support");
|
||||
});
|
||||
|
||||
it("fails to send delayed state events", async () => {
|
||||
await makeClient({ sendState: [{ eventType: "org.example.foo", stateKey: "bar" }] });
|
||||
await expect(
|
||||
client._unstable_sendDelayedStateEvent(
|
||||
"!1:example.org",
|
||||
{ delay: 2000 },
|
||||
"org.example.foo",
|
||||
{ hello: "world" },
|
||||
"bar",
|
||||
),
|
||||
).rejects.toThrow("Server does not support");
|
||||
});
|
||||
|
||||
it("fails to update delayed state events", async () => {
|
||||
await makeClient({});
|
||||
for (const action of [
|
||||
UpdateDelayedEventAction.Cancel,
|
||||
UpdateDelayedEventAction.Restart,
|
||||
UpdateDelayedEventAction.Send,
|
||||
]) {
|
||||
await expect(client._unstable_updateDelayedEvent("id", action)).rejects.toThrow(
|
||||
"Server does not support",
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("initialization", () => {
|
||||
it("requests permissions for specific message types", async () => {
|
||||
await makeClient({ sendMessage: [MsgType.Text], receiveMessage: [MsgType.Text] });
|
||||
@@ -211,6 +433,17 @@ describe("RoomWidgetClient", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("send handles incorrect response", async () => {
|
||||
await makeClient({ sendState: [{ eventType: "org.example.foo", stateKey: "bar" }] });
|
||||
widgetApi.sendStateEvent.mockResolvedValueOnce({
|
||||
room_id: "!1:example.org",
|
||||
delay_id: `id-${Math.random}`,
|
||||
});
|
||||
await expect(
|
||||
client.sendStateEvent("!1:example.org", "org.example.foo", { hello: "world" }, "bar"),
|
||||
).rejects.toThrow();
|
||||
});
|
||||
|
||||
it("receives", async () => {
|
||||
await makeClient({ receiveState: [{ eventType: "org.example.foo", stateKey: "bar" }] });
|
||||
expect(widgetApi.requestCapabilityForRoomTimeline).toHaveBeenCalledWith("!1:example.org");
|
||||
|
||||
@@ -57,6 +57,7 @@ import {
|
||||
Room,
|
||||
RuleId,
|
||||
TweakName,
|
||||
UpdateDelayedEventAction,
|
||||
} from "../../src";
|
||||
import { supportsMatrixCall } from "../../src/webrtc/call";
|
||||
import { makeBeaconEvent } from "../test-utils/beacon";
|
||||
@@ -97,7 +98,7 @@ type HttpLookup = {
|
||||
method: string;
|
||||
path: string;
|
||||
prefix?: string;
|
||||
data?: Record<string, any>;
|
||||
data?: Record<string, any> | Record<string, any>[];
|
||||
error?: object;
|
||||
expectBody?: Record<string, any>;
|
||||
expectQueryParams?: QueryDict;
|
||||
@@ -298,7 +299,9 @@ describe("MatrixClient", function () {
|
||||
...(opts || {}),
|
||||
});
|
||||
// FIXME: We shouldn't be yanking http like this.
|
||||
client.http = (["authedRequest", "getContentUri", "request", "uploadContent"] as const).reduce((r, k) => {
|
||||
client.http = (
|
||||
["authedRequest", "getContentUri", "request", "uploadContent", "idServerRequest"] as const
|
||||
).reduce((r, k) => {
|
||||
r[k] = jest.fn();
|
||||
return r;
|
||||
}, {} as MatrixHttpApi<any>);
|
||||
@@ -704,6 +707,328 @@ describe("MatrixClient", function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe("_unstable_sendDelayedEvent", () => {
|
||||
const unstableMSC4140Prefix = `${ClientPrefix.Unstable}/org.matrix.msc4140`;
|
||||
|
||||
const roomId = "!room:example.org";
|
||||
const body = "This is the body";
|
||||
const content = { body, msgtype: MsgType.Text } satisfies RoomMessageEventContent;
|
||||
const timeoutDelayOpts = { delay: 2000 };
|
||||
const realTimeoutDelayOpts = { "org.matrix.msc4140.delay": 2000 };
|
||||
|
||||
beforeEach(() => {
|
||||
unstableFeatures["org.matrix.msc4140"] = true;
|
||||
});
|
||||
|
||||
it("throws when unsupported by server", async () => {
|
||||
unstableFeatures["org.matrix.msc4140"] = false;
|
||||
const errorMessage = "Server does not support";
|
||||
|
||||
await expect(
|
||||
client._unstable_sendDelayedEvent(
|
||||
roomId,
|
||||
timeoutDelayOpts,
|
||||
null,
|
||||
EventType.RoomMessage,
|
||||
{ ...content },
|
||||
client.makeTxnId(),
|
||||
),
|
||||
).rejects.toThrow(errorMessage);
|
||||
|
||||
await expect(
|
||||
client._unstable_sendDelayedStateEvent(roomId, timeoutDelayOpts, EventType.RoomTopic, {
|
||||
topic: "topic",
|
||||
}),
|
||||
).rejects.toThrow(errorMessage);
|
||||
|
||||
await expect(client._unstable_getDelayedEvents()).rejects.toThrow(errorMessage);
|
||||
|
||||
await expect(
|
||||
client._unstable_updateDelayedEvent("anyDelayId", UpdateDelayedEventAction.Send),
|
||||
).rejects.toThrow(errorMessage);
|
||||
});
|
||||
|
||||
it("works with null threadId", async () => {
|
||||
httpLookups = [];
|
||||
|
||||
const timeoutDelayTxnId = client.makeTxnId();
|
||||
httpLookups.push({
|
||||
method: "PUT",
|
||||
path: `/rooms/${encodeURIComponent(roomId)}/send/m.room.message/${timeoutDelayTxnId}`,
|
||||
expectQueryParams: realTimeoutDelayOpts,
|
||||
data: { delay_id: "id1" },
|
||||
expectBody: content,
|
||||
});
|
||||
|
||||
const { delay_id: timeoutDelayId } = await client._unstable_sendDelayedEvent(
|
||||
roomId,
|
||||
timeoutDelayOpts,
|
||||
null,
|
||||
EventType.RoomMessage,
|
||||
{ ...content },
|
||||
timeoutDelayTxnId,
|
||||
);
|
||||
|
||||
const actionDelayTxnId = client.makeTxnId();
|
||||
httpLookups.push({
|
||||
method: "PUT",
|
||||
path: `/rooms/${encodeURIComponent(roomId)}/send/m.room.message/${actionDelayTxnId}`,
|
||||
expectQueryParams: { "org.matrix.msc4140.parent_delay_id": timeoutDelayId },
|
||||
data: { delay_id: "id2" },
|
||||
expectBody: content,
|
||||
});
|
||||
|
||||
await client._unstable_sendDelayedEvent(
|
||||
roomId,
|
||||
{ parent_delay_id: timeoutDelayId },
|
||||
null,
|
||||
EventType.RoomMessage,
|
||||
{ ...content },
|
||||
actionDelayTxnId,
|
||||
);
|
||||
});
|
||||
|
||||
it("works with non-null threadId", async () => {
|
||||
httpLookups = [];
|
||||
const threadId = "$threadId:server";
|
||||
const expectBody = {
|
||||
...content,
|
||||
"m.relates_to": {
|
||||
event_id: threadId,
|
||||
is_falling_back: true,
|
||||
rel_type: "m.thread",
|
||||
},
|
||||
};
|
||||
|
||||
const timeoutDelayTxnId = client.makeTxnId();
|
||||
httpLookups.push({
|
||||
method: "PUT",
|
||||
path: `/rooms/${encodeURIComponent(roomId)}/send/m.room.message/${timeoutDelayTxnId}`,
|
||||
expectQueryParams: realTimeoutDelayOpts,
|
||||
data: { delay_id: "id1" },
|
||||
expectBody,
|
||||
});
|
||||
|
||||
const { delay_id: timeoutDelayId } = await client._unstable_sendDelayedEvent(
|
||||
roomId,
|
||||
timeoutDelayOpts,
|
||||
threadId,
|
||||
EventType.RoomMessage,
|
||||
{ ...content },
|
||||
timeoutDelayTxnId,
|
||||
);
|
||||
|
||||
const actionDelayTxnId = client.makeTxnId();
|
||||
httpLookups.push({
|
||||
method: "PUT",
|
||||
path: `/rooms/${encodeURIComponent(roomId)}/send/m.room.message/${actionDelayTxnId}`,
|
||||
expectQueryParams: { "org.matrix.msc4140.parent_delay_id": timeoutDelayId },
|
||||
data: { delay_id: "id2" },
|
||||
expectBody,
|
||||
});
|
||||
|
||||
await client._unstable_sendDelayedEvent(
|
||||
roomId,
|
||||
{ parent_delay_id: timeoutDelayId },
|
||||
threadId,
|
||||
EventType.RoomMessage,
|
||||
{ ...content },
|
||||
actionDelayTxnId,
|
||||
);
|
||||
});
|
||||
|
||||
it("should add thread relation if threadId is passed and the relation is missing", async () => {
|
||||
httpLookups = [];
|
||||
const threadId = "$threadId:server";
|
||||
const expectBody = {
|
||||
...content,
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
event_id: threadId,
|
||||
},
|
||||
"event_id": threadId,
|
||||
"is_falling_back": true,
|
||||
"rel_type": "m.thread",
|
||||
},
|
||||
};
|
||||
|
||||
const room = new Room(roomId, client, userId);
|
||||
mocked(store.getRoom).mockReturnValue(room);
|
||||
|
||||
const rootEvent = new MatrixEvent({ event_id: threadId });
|
||||
room.createThread(threadId, rootEvent, [rootEvent], false);
|
||||
|
||||
const timeoutDelayTxnId = client.makeTxnId();
|
||||
httpLookups.push({
|
||||
method: "PUT",
|
||||
path: `/rooms/${encodeURIComponent(roomId)}/send/m.room.message/${timeoutDelayTxnId}`,
|
||||
expectQueryParams: realTimeoutDelayOpts,
|
||||
data: { delay_id: "id1" },
|
||||
expectBody,
|
||||
});
|
||||
|
||||
const { delay_id: timeoutDelayId } = await client._unstable_sendDelayedEvent(
|
||||
roomId,
|
||||
timeoutDelayOpts,
|
||||
threadId,
|
||||
EventType.RoomMessage,
|
||||
{ ...content },
|
||||
timeoutDelayTxnId,
|
||||
);
|
||||
|
||||
const actionDelayTxnId = client.makeTxnId();
|
||||
httpLookups.push({
|
||||
method: "PUT",
|
||||
path: `/rooms/${encodeURIComponent(roomId)}/send/m.room.message/${actionDelayTxnId}`,
|
||||
expectQueryParams: { "org.matrix.msc4140.parent_delay_id": timeoutDelayId },
|
||||
data: { delay_id: "id2" },
|
||||
expectBody,
|
||||
});
|
||||
|
||||
await client._unstable_sendDelayedEvent(
|
||||
roomId,
|
||||
{ parent_delay_id: timeoutDelayId },
|
||||
threadId,
|
||||
EventType.RoomMessage,
|
||||
{ ...content },
|
||||
actionDelayTxnId,
|
||||
);
|
||||
});
|
||||
|
||||
it("should add thread relation if threadId is passed and the relation is missing with reply", async () => {
|
||||
httpLookups = [];
|
||||
const threadId = "$threadId:server";
|
||||
|
||||
const content = {
|
||||
body,
|
||||
"msgtype": MsgType.Text,
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
event_id: "$other:event",
|
||||
},
|
||||
},
|
||||
} satisfies RoomMessageEventContent;
|
||||
const expectBody = {
|
||||
...content,
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
event_id: "$other:event",
|
||||
},
|
||||
"event_id": threadId,
|
||||
"is_falling_back": false,
|
||||
"rel_type": "m.thread",
|
||||
},
|
||||
};
|
||||
|
||||
const room = new Room(roomId, client, userId);
|
||||
mocked(store.getRoom).mockReturnValue(room);
|
||||
|
||||
const rootEvent = new MatrixEvent({ event_id: threadId });
|
||||
room.createThread(threadId, rootEvent, [rootEvent], false);
|
||||
|
||||
const timeoutDelayTxnId = client.makeTxnId();
|
||||
httpLookups.push({
|
||||
method: "PUT",
|
||||
path: `/rooms/${encodeURIComponent(roomId)}/send/m.room.message/${timeoutDelayTxnId}`,
|
||||
expectQueryParams: realTimeoutDelayOpts,
|
||||
data: { delay_id: "id1" },
|
||||
expectBody,
|
||||
});
|
||||
|
||||
const { delay_id: timeoutDelayId } = await client._unstable_sendDelayedEvent(
|
||||
roomId,
|
||||
timeoutDelayOpts,
|
||||
threadId,
|
||||
EventType.RoomMessage,
|
||||
{ ...content },
|
||||
timeoutDelayTxnId,
|
||||
);
|
||||
|
||||
const actionDelayTxnId = client.makeTxnId();
|
||||
httpLookups.push({
|
||||
method: "PUT",
|
||||
path: `/rooms/${encodeURIComponent(roomId)}/send/m.room.message/${actionDelayTxnId}`,
|
||||
expectQueryParams: { "org.matrix.msc4140.parent_delay_id": timeoutDelayId },
|
||||
data: { delay_id: "id2" },
|
||||
expectBody,
|
||||
});
|
||||
|
||||
await client._unstable_sendDelayedEvent(
|
||||
roomId,
|
||||
{ parent_delay_id: timeoutDelayId },
|
||||
threadId,
|
||||
EventType.RoomMessage,
|
||||
{ ...content },
|
||||
actionDelayTxnId,
|
||||
);
|
||||
});
|
||||
|
||||
it("can send a delayed state event", async () => {
|
||||
httpLookups = [];
|
||||
const content = { topic: "The year 2000" };
|
||||
|
||||
httpLookups.push({
|
||||
method: "PUT",
|
||||
path: `/rooms/${encodeURIComponent(roomId)}/state/m.room.topic/`,
|
||||
expectQueryParams: realTimeoutDelayOpts,
|
||||
data: { delay_id: "id1" },
|
||||
expectBody: content,
|
||||
});
|
||||
|
||||
const { delay_id: timeoutDelayId } = await client._unstable_sendDelayedStateEvent(
|
||||
roomId,
|
||||
timeoutDelayOpts,
|
||||
EventType.RoomTopic,
|
||||
{ ...content },
|
||||
);
|
||||
|
||||
httpLookups.push({
|
||||
method: "PUT",
|
||||
path: `/rooms/${encodeURIComponent(roomId)}/state/m.room.topic/`,
|
||||
expectQueryParams: { "org.matrix.msc4140.parent_delay_id": timeoutDelayId },
|
||||
data: { delay_id: "id2" },
|
||||
expectBody: content,
|
||||
});
|
||||
|
||||
await client._unstable_sendDelayedStateEvent(
|
||||
roomId,
|
||||
{ parent_delay_id: timeoutDelayId },
|
||||
EventType.RoomTopic,
|
||||
{ ...content },
|
||||
);
|
||||
});
|
||||
|
||||
it("can look up delayed events", async () => {
|
||||
httpLookups = [
|
||||
{
|
||||
method: "GET",
|
||||
prefix: unstableMSC4140Prefix,
|
||||
path: "/delayed_events",
|
||||
data: [],
|
||||
},
|
||||
];
|
||||
|
||||
await client._unstable_getDelayedEvents();
|
||||
});
|
||||
|
||||
it("can update delayed events", async () => {
|
||||
const delayId = "id";
|
||||
const action = UpdateDelayedEventAction.Restart;
|
||||
httpLookups = [
|
||||
{
|
||||
method: "POST",
|
||||
prefix: unstableMSC4140Prefix,
|
||||
path: `/delayed_events/${encodeURIComponent(delayId)}`,
|
||||
data: {
|
||||
action,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
await client._unstable_updateDelayedEvent(delayId, action);
|
||||
});
|
||||
});
|
||||
|
||||
it("should create (unstable) file trees", async () => {
|
||||
const userId = "@test:example.org";
|
||||
const roomId = "!room:example.org";
|
||||
@@ -963,7 +1288,7 @@ describe("MatrixClient", function () {
|
||||
const filter = new Filter(client.credentials.userId);
|
||||
|
||||
const filterId = await client.getOrCreateFilter(filterName, filter);
|
||||
expect(filterId).toEqual(FILTER_RESPONSE.data?.filter_id);
|
||||
expect(filterId).toEqual(!Array.isArray(FILTER_RESPONSE.data) && FILTER_RESPONSE.data?.filter_id);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3035,4 +3360,45 @@ describe("MatrixClient", function () {
|
||||
expect(httpLookups.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("identityHashedLookup", () => {
|
||||
it("should return hashed lookup results", async () => {
|
||||
const ID_ACCESS_TOKEN = "hello_id_server_please_let_me_make_a_request";
|
||||
|
||||
client.http.idServerRequest = jest.fn().mockImplementation((method, path, params) => {
|
||||
if (method === "GET" && path === "/hash_details") {
|
||||
return { algorithms: ["sha256"], lookup_pepper: "carrot" };
|
||||
} else if (method === "POST" && path === "/lookup") {
|
||||
return {
|
||||
mappings: {
|
||||
"WHA-MgrrsZACDI9F8OaVagpiyiV2sjZylGHJteT4OMU": "@bob:homeserver.dummy",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
throw new Error("Test impl doesn't know about this request");
|
||||
});
|
||||
|
||||
const lookupResult = await client.identityHashedLookup([["bob@email.dummy", "email"]], ID_ACCESS_TOKEN);
|
||||
|
||||
expect(client.http.idServerRequest).toHaveBeenCalledWith(
|
||||
"GET",
|
||||
"/hash_details",
|
||||
undefined,
|
||||
"/_matrix/identity/v2",
|
||||
ID_ACCESS_TOKEN,
|
||||
);
|
||||
|
||||
expect(client.http.idServerRequest).toHaveBeenCalledWith(
|
||||
"POST",
|
||||
"/lookup",
|
||||
{ pepper: "carrot", algorithm: "sha256", addresses: ["WHA-MgrrsZACDI9F8OaVagpiyiV2sjZylGHJteT4OMU"] },
|
||||
"/_matrix/identity/v2",
|
||||
ID_ACCESS_TOKEN,
|
||||
);
|
||||
|
||||
expect(lookupResult).toHaveLength(1);
|
||||
expect(lookupResult[0]).toEqual({ address: "bob@email.dummy", mxid: "@bob:homeserver.dummy" });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -98,20 +98,6 @@ describe("CallMembership", () => {
|
||||
expect(membership.getAbsoluteExpiry()).toEqual(5000 + 1000);
|
||||
});
|
||||
|
||||
it("considers memberships unexpired if local age low enough", () => {
|
||||
const fakeEvent = makeMockEvent(1000);
|
||||
fakeEvent.getLocalAge = jest.fn().mockReturnValue(3000);
|
||||
const membership = new CallMembership(fakeEvent, membershipTemplate);
|
||||
expect(membership.isExpired()).toEqual(false);
|
||||
});
|
||||
|
||||
it("considers memberships expired when local age large", () => {
|
||||
const fakeEvent = makeMockEvent(1000);
|
||||
fakeEvent.localTimestamp = Date.now() - 6000;
|
||||
const membership = new CallMembership(fakeEvent, membershipTemplate);
|
||||
expect(membership.isExpired()).toEqual(true);
|
||||
});
|
||||
|
||||
it("returns preferred foci", () => {
|
||||
const fakeEvent = makeMockEvent();
|
||||
const mockFocus = { type: "this_is_a_mock_focus" };
|
||||
@@ -198,13 +184,6 @@ describe("CallMembership", () => {
|
||||
beforeEach(() => {
|
||||
// server origin timestamp for this event is 1000
|
||||
fakeEvent = makeMockEvent(1000);
|
||||
// our clock would have been at 2000 at the creation time (our clock at event receive time - age)
|
||||
// (ie. the local clock is 1 second ahead of the servers' clocks)
|
||||
fakeEvent.localTimestamp = 2000;
|
||||
|
||||
// for simplicity's sake, we say that the event's age is zero
|
||||
fakeEvent.getLocalAge = jest.fn().mockReturnValue(0);
|
||||
|
||||
membership = new CallMembership(fakeEvent!, membershipTemplate);
|
||||
|
||||
jest.useFakeTimers();
|
||||
@@ -215,6 +194,13 @@ describe("CallMembership", () => {
|
||||
});
|
||||
|
||||
it("converts expiry time into local clock", () => {
|
||||
// our clock would have been at 2000 at the creation time (our clock at event receive time - age)
|
||||
// (ie. the local clock is 1 second ahead of the servers' clocks)
|
||||
fakeEvent.localTimestamp = 2000;
|
||||
|
||||
// for simplicity's sake, we say that the event's age is zero
|
||||
fakeEvent.getLocalAge = jest.fn().mockReturnValue(0);
|
||||
|
||||
// for sanity's sake, make sure the server-relative expiry time is what we expect
|
||||
expect(membership.getAbsoluteExpiry()).toEqual(6000);
|
||||
// therefore the expiry time converted to our clock should be 1 second later
|
||||
@@ -223,7 +209,8 @@ describe("CallMembership", () => {
|
||||
|
||||
it("calculates time until expiry", () => {
|
||||
jest.setSystemTime(2000);
|
||||
expect(membership.getMsUntilExpiry()).toEqual(5000);
|
||||
// should be using absolute expiry time
|
||||
expect(membership.getMsUntilExpiry()).toEqual(4000);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { EventTimeline, EventType, MatrixClient, MatrixError, MatrixEvent, Room } from "../../../src";
|
||||
import { encodeBase64, EventTimeline, EventType, MatrixClient, MatrixError, MatrixEvent, Room } from "../../../src";
|
||||
import { KnownMembership } from "../../../src/@types/membership";
|
||||
import {
|
||||
CallMembershipData,
|
||||
@@ -46,6 +46,9 @@ describe("MatrixRTCSession", () => {
|
||||
client = new MatrixClient({ baseUrl: "base_url" });
|
||||
client.getUserId = jest.fn().mockReturnValue("@alice:example.org");
|
||||
client.getDeviceId = jest.fn().mockReturnValue("AAAAAAA");
|
||||
client.doesServerSupportUnstableFeature = jest.fn((feature) =>
|
||||
Promise.resolve(feature === "org.matrix.msc4140"),
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -70,14 +73,17 @@ describe("MatrixRTCSession", () => {
|
||||
});
|
||||
|
||||
it("ignores expired memberships events", () => {
|
||||
jest.useFakeTimers();
|
||||
const expiredMembership = Object.assign({}, membershipTemplate);
|
||||
expiredMembership.expires = 1000;
|
||||
expiredMembership.device_id = "EXPIRED";
|
||||
const mockRoom = makeMockRoom([membershipTemplate, expiredMembership], 10000);
|
||||
const mockRoom = makeMockRoom([membershipTemplate, expiredMembership]);
|
||||
|
||||
jest.advanceTimersByTime(2000);
|
||||
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
|
||||
expect(sess?.memberships.length).toEqual(1);
|
||||
expect(sess?.memberships[0].deviceId).toEqual("AAAAAAA");
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it("ignores memberships events of members not in the room", () => {
|
||||
@@ -88,12 +94,15 @@ describe("MatrixRTCSession", () => {
|
||||
});
|
||||
|
||||
it("honours created_ts", () => {
|
||||
jest.useFakeTimers();
|
||||
jest.setSystemTime(500);
|
||||
const expiredMembership = Object.assign({}, membershipTemplate);
|
||||
expiredMembership.created_ts = 500;
|
||||
expiredMembership.expires = 1000;
|
||||
const mockRoom = makeMockRoom([expiredMembership]);
|
||||
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
|
||||
expect(sess?.memberships[0].getAbsoluteExpiry()).toEqual(1500);
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it("returns empty session if no membership events are present", () => {
|
||||
@@ -241,40 +250,68 @@ describe("MatrixRTCSession", () => {
|
||||
foci_preferred: [mockFocus],
|
||||
};
|
||||
|
||||
function testSession(
|
||||
let sendStateEventMock: jest.Mock;
|
||||
let sendDelayedStateMock: jest.Mock;
|
||||
|
||||
let sentStateEvent: Promise<void>;
|
||||
let sentDelayedState: Promise<void>;
|
||||
|
||||
beforeEach(() => {
|
||||
sentStateEvent = new Promise((resolve) => {
|
||||
sendStateEventMock = jest.fn(resolve);
|
||||
});
|
||||
sentDelayedState = new Promise((resolve) => {
|
||||
sendDelayedStateMock = jest.fn(() => {
|
||||
resolve();
|
||||
return {
|
||||
delay_id: "id",
|
||||
};
|
||||
});
|
||||
});
|
||||
client.sendStateEvent = sendStateEventMock;
|
||||
client._unstable_sendDelayedStateEvent = sendDelayedStateMock;
|
||||
});
|
||||
|
||||
async function testSession(
|
||||
membershipData: CallMembershipData[] | SessionMembershipData,
|
||||
shouldUseLegacy: boolean,
|
||||
): void {
|
||||
): Promise<void> {
|
||||
sess = MatrixRTCSession.roomSessionForRoom(client, makeMockRoom(membershipData));
|
||||
|
||||
const makeNewLegacyMembershipsMock = jest.spyOn(sess as any, "makeNewLegacyMemberships");
|
||||
const makeNewMembershipMock = jest.spyOn(sess as any, "makeNewMembership");
|
||||
|
||||
sess.joinRoomSession([mockFocus], mockFocus, joinSessionConfig);
|
||||
await Promise.race([sentStateEvent, new Promise((resolve) => setTimeout(resolve, 500))]);
|
||||
|
||||
expect(makeNewLegacyMembershipsMock).toHaveBeenCalledTimes(shouldUseLegacy ? 1 : 0);
|
||||
expect(makeNewMembershipMock).toHaveBeenCalledTimes(shouldUseLegacy ? 0 : 1);
|
||||
|
||||
await Promise.race([sentDelayedState, new Promise((resolve) => setTimeout(resolve, 500))]);
|
||||
expect(client._unstable_sendDelayedStateEvent).toHaveBeenCalledTimes(shouldUseLegacy ? 0 : 1);
|
||||
}
|
||||
|
||||
it("uses legacy events if there are any active legacy calls", () => {
|
||||
testSession([expiredLegacyMembershipData, legacyMembershipData, sessionMembershipData], true);
|
||||
it("uses legacy events if there are any active legacy calls", async () => {
|
||||
await testSession([expiredLegacyMembershipData, legacyMembershipData, sessionMembershipData], true);
|
||||
});
|
||||
|
||||
it('uses legacy events if a non-legacy call is in a "memberships" array', () => {
|
||||
testSession([sessionMembershipData], true);
|
||||
it('uses legacy events if a non-legacy call is in a "memberships" array', async () => {
|
||||
await testSession([sessionMembershipData], true);
|
||||
});
|
||||
|
||||
it("uses non-legacy events if all legacy calls are expired", () => {
|
||||
testSession([expiredLegacyMembershipData], false);
|
||||
it("uses non-legacy events if all legacy calls are expired", async () => {
|
||||
await testSession([expiredLegacyMembershipData], false);
|
||||
});
|
||||
|
||||
it("uses non-legacy events if there are only non-legacy calls", () => {
|
||||
testSession(sessionMembershipData, false);
|
||||
it("uses non-legacy events if there are only non-legacy calls", async () => {
|
||||
await testSession(sessionMembershipData, false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getOldestMembership", () => {
|
||||
it("returns the oldest membership event", () => {
|
||||
jest.useFakeTimers();
|
||||
jest.setSystemTime(4000);
|
||||
const mockRoom = makeMockRoom([
|
||||
Object.assign({}, membershipTemplate, { device_id: "foo", created_ts: 3000 }),
|
||||
Object.assign({}, membershipTemplate, { device_id: "old", created_ts: 1000 }),
|
||||
@@ -283,12 +320,15 @@ describe("MatrixRTCSession", () => {
|
||||
|
||||
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
|
||||
expect(sess.getOldestMembership()!.deviceId).toEqual("old");
|
||||
jest.useRealTimers();
|
||||
});
|
||||
});
|
||||
|
||||
describe("getsActiveFocus", () => {
|
||||
const activeFociConfig = { type: "livekit", livekit_service_url: "https://active.url" };
|
||||
it("gets the correct active focus with oldest_membership", () => {
|
||||
jest.useFakeTimers();
|
||||
jest.setSystemTime(3000);
|
||||
const mockRoom = makeMockRoom([
|
||||
Object.assign({}, membershipTemplate, {
|
||||
device_id: "foo",
|
||||
@@ -306,6 +346,7 @@ describe("MatrixRTCSession", () => {
|
||||
focus_selection: "oldest_membership",
|
||||
});
|
||||
expect(sess.getActiveFocus()).toBe(activeFociConfig);
|
||||
jest.useRealTimers();
|
||||
});
|
||||
it("does not provide focus if the selction method is unknown", () => {
|
||||
const mockRoom = makeMockRoom([
|
||||
@@ -327,6 +368,8 @@ describe("MatrixRTCSession", () => {
|
||||
expect(sess.getActiveFocus()).toBe(undefined);
|
||||
});
|
||||
it("gets the correct active focus legacy", () => {
|
||||
jest.useFakeTimers();
|
||||
jest.setSystemTime(3000);
|
||||
const mockRoom = makeMockRoom([
|
||||
Object.assign({}, membershipTemplate, {
|
||||
device_id: "foo",
|
||||
@@ -341,18 +384,34 @@ describe("MatrixRTCSession", () => {
|
||||
|
||||
sess.joinRoomSession([{ type: "livekit", livekit_service_url: "htts://test.org" }]);
|
||||
expect(sess.getActiveFocus()).toBe(activeFociConfig);
|
||||
jest.useRealTimers();
|
||||
});
|
||||
});
|
||||
|
||||
describe("joining", () => {
|
||||
let mockRoom: Room;
|
||||
let sendStateEventMock: jest.Mock;
|
||||
let sendDelayedStateMock: jest.Mock;
|
||||
let sendEventMock: jest.Mock;
|
||||
|
||||
let sentStateEvent: Promise<void>;
|
||||
let sentDelayedState: Promise<void>;
|
||||
|
||||
beforeEach(() => {
|
||||
sendStateEventMock = jest.fn();
|
||||
sentStateEvent = new Promise((resolve) => {
|
||||
sendStateEventMock = jest.fn(resolve);
|
||||
});
|
||||
sentDelayedState = new Promise((resolve) => {
|
||||
sendDelayedStateMock = jest.fn(() => {
|
||||
resolve();
|
||||
return {
|
||||
delay_id: "id",
|
||||
};
|
||||
});
|
||||
});
|
||||
sendEventMock = jest.fn();
|
||||
client.sendStateEvent = sendStateEventMock;
|
||||
client._unstable_sendDelayedStateEvent = sendDelayedStateMock;
|
||||
client.sendEvent = sendEventMock;
|
||||
|
||||
mockRoom = makeMockRoom([]);
|
||||
@@ -373,9 +432,11 @@ describe("MatrixRTCSession", () => {
|
||||
expect(sess!.isJoined()).toEqual(true);
|
||||
});
|
||||
|
||||
it("sends a membership event when joining a call", () => {
|
||||
it("sends a membership event when joining a call", async () => {
|
||||
const realSetTimeout = setTimeout;
|
||||
jest.useFakeTimers();
|
||||
sess!.joinRoomSession([mockFocus], mockFocus);
|
||||
await Promise.race([sentStateEvent, new Promise((resolve) => realSetTimeout(resolve, 500))]);
|
||||
expect(client.sendStateEvent).toHaveBeenCalledWith(
|
||||
mockRoom!.roomId,
|
||||
EventType.GroupCallMemberPrefix,
|
||||
@@ -396,6 +457,8 @@ describe("MatrixRTCSession", () => {
|
||||
},
|
||||
"@alice:example.org",
|
||||
);
|
||||
await Promise.race([sentDelayedState, new Promise((resolve) => realSetTimeout(resolve, 500))]);
|
||||
expect(client._unstable_sendDelayedStateEvent).toHaveBeenCalledTimes(0);
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
@@ -403,13 +466,15 @@ describe("MatrixRTCSession", () => {
|
||||
const activeFocusConfig = { type: "livekit", livekit_service_url: "https://active.url" };
|
||||
const activeFocus = { type: "livekit", focus_selection: "oldest_membership" };
|
||||
|
||||
function testJoin(useOwnedStateEvents: boolean): void {
|
||||
async function testJoin(useOwnedStateEvents: boolean): Promise<void> {
|
||||
const realSetTimeout = setTimeout;
|
||||
if (useOwnedStateEvents) {
|
||||
mockRoom.getVersion = jest.fn().mockReturnValue("org.matrix.msc3779.default");
|
||||
}
|
||||
|
||||
jest.useFakeTimers();
|
||||
sess!.joinRoomSession([activeFocusConfig], activeFocus, { useLegacyMemberEvents: false });
|
||||
await Promise.race([sentStateEvent, new Promise((resolve) => realSetTimeout(resolve, 500))]);
|
||||
expect(client.sendStateEvent).toHaveBeenCalledWith(
|
||||
mockRoom!.roomId,
|
||||
EventType.GroupCallMemberPrefix,
|
||||
@@ -423,15 +488,17 @@ describe("MatrixRTCSession", () => {
|
||||
} satisfies SessionMembershipData,
|
||||
`${!useOwnedStateEvents ? "_" : ""}@alice:example.org_AAAAAAA`,
|
||||
);
|
||||
await Promise.race([sentDelayedState, new Promise((resolve) => realSetTimeout(resolve, 500))]);
|
||||
expect(client._unstable_sendDelayedStateEvent).toHaveBeenCalledTimes(1);
|
||||
jest.useRealTimers();
|
||||
}
|
||||
|
||||
it("sends a membership event with session payload when joining a non-legacy call", () => {
|
||||
testJoin(false);
|
||||
it("sends a membership event with session payload when joining a non-legacy call", async () => {
|
||||
await testJoin(false);
|
||||
});
|
||||
|
||||
it("does not prefix the state key with _ for rooms that support user-owned state events", () => {
|
||||
testJoin(true);
|
||||
it("does not prefix the state key with _ for rooms that support user-owned state events", async () => {
|
||||
await testJoin(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -461,9 +528,8 @@ describe("MatrixRTCSession", () => {
|
||||
|
||||
const eventContent = await eventSentPromise;
|
||||
|
||||
// definitely should have renewed by 1 second before the expiry!
|
||||
const timeElapsed = 60 * 60 * 1000 - 1000;
|
||||
const event = mockRTCEvent(eventContent.memberships, mockRoom.roomId, timeElapsed);
|
||||
jest.setSystemTime(1000);
|
||||
const event = mockRTCEvent(eventContent.memberships, mockRoom.roomId);
|
||||
const getState = mockRoom.getLiveTimeline().getState(EventTimeline.FORWARDS)!;
|
||||
getState.getStateEvents = jest.fn().mockReturnValue(event);
|
||||
getState.events = new Map([
|
||||
@@ -486,6 +552,8 @@ describe("MatrixRTCSession", () => {
|
||||
|
||||
sendStateEventMock.mockReset().mockImplementation(resolveFn);
|
||||
|
||||
// definitely should have renewed by 1 second before the expiry!
|
||||
const timeElapsed = 60 * 60 * 1000 - 1000;
|
||||
jest.setSystemTime(Date.now() + timeElapsed);
|
||||
jest.advanceTimersByTime(timeElapsed);
|
||||
await eventReSentPromise;
|
||||
@@ -546,6 +614,17 @@ describe("MatrixRTCSession", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("does not send key if join called when already joined", () => {
|
||||
sess!.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true });
|
||||
|
||||
expect(client.sendStateEvent).toHaveBeenCalledTimes(1);
|
||||
expect(client.sendEvent).toHaveBeenCalledTimes(1);
|
||||
|
||||
sess!.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true });
|
||||
expect(client.sendStateEvent).toHaveBeenCalledTimes(1);
|
||||
expect(client.sendEvent).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("retries key sends", async () => {
|
||||
jest.useFakeTimers();
|
||||
let firstEventSent = false;
|
||||
@@ -622,7 +701,7 @@ describe("MatrixRTCSession", () => {
|
||||
|
||||
mockRoom.getLiveTimeline().getState = jest
|
||||
.fn()
|
||||
.mockReturnValue(makeMockRoomState([membershipTemplate, member2], mockRoom.roomId, undefined));
|
||||
.mockReturnValue(makeMockRoomState([membershipTemplate, member2], mockRoom.roomId));
|
||||
sess.onMembershipUpdate();
|
||||
|
||||
await keysSentPromise2;
|
||||
@@ -633,6 +712,214 @@ describe("MatrixRTCSession", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("Does not re-send key if memberships stays same", async () => {
|
||||
jest.useFakeTimers();
|
||||
try {
|
||||
const keysSentPromise1 = new Promise((resolve) => {
|
||||
sendEventMock.mockImplementation(resolve);
|
||||
});
|
||||
|
||||
const member1 = membershipTemplate;
|
||||
const member2 = Object.assign({}, membershipTemplate, {
|
||||
device_id: "BBBBBBB",
|
||||
});
|
||||
|
||||
const mockRoom = makeMockRoom([member1, member2]);
|
||||
mockRoom.getLiveTimeline().getState = jest
|
||||
.fn()
|
||||
.mockReturnValue(makeMockRoomState([member1, member2], mockRoom.roomId));
|
||||
|
||||
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
|
||||
sess.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true });
|
||||
|
||||
await keysSentPromise1;
|
||||
|
||||
// make sure an encryption key was sent
|
||||
expect(sendEventMock).toHaveBeenCalledWith(
|
||||
expect.stringMatching(".*"),
|
||||
"io.element.call.encryption_keys",
|
||||
{
|
||||
call_id: "",
|
||||
device_id: "AAAAAAA",
|
||||
keys: [
|
||||
{
|
||||
index: 0,
|
||||
key: expect.stringMatching(".*"),
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
sendEventMock.mockClear();
|
||||
|
||||
// these should be a no-op:
|
||||
sess.onMembershipUpdate();
|
||||
expect(sendEventMock).toHaveBeenCalledTimes(0);
|
||||
} finally {
|
||||
jest.useRealTimers();
|
||||
}
|
||||
});
|
||||
|
||||
it("Re-sends key if a member changes membership ID", async () => {
|
||||
jest.useFakeTimers();
|
||||
try {
|
||||
const keysSentPromise1 = new Promise((resolve) => {
|
||||
sendEventMock.mockImplementation(resolve);
|
||||
});
|
||||
|
||||
const member1 = membershipTemplate;
|
||||
const member2 = {
|
||||
...membershipTemplate,
|
||||
device_id: "BBBBBBB",
|
||||
};
|
||||
|
||||
const mockRoom = makeMockRoom([member1, member2]);
|
||||
mockRoom.getLiveTimeline().getState = jest
|
||||
.fn()
|
||||
.mockReturnValue(makeMockRoomState([member1, member2], mockRoom.roomId));
|
||||
|
||||
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
|
||||
sess.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true });
|
||||
|
||||
await keysSentPromise1;
|
||||
|
||||
// make sure an encryption key was sent
|
||||
expect(sendEventMock).toHaveBeenCalledWith(
|
||||
expect.stringMatching(".*"),
|
||||
"io.element.call.encryption_keys",
|
||||
{
|
||||
call_id: "",
|
||||
device_id: "AAAAAAA",
|
||||
keys: [
|
||||
{
|
||||
index: 0,
|
||||
key: expect.stringMatching(".*"),
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
sendEventMock.mockClear();
|
||||
|
||||
// this should be a no-op:
|
||||
sess.onMembershipUpdate();
|
||||
expect(sendEventMock).toHaveBeenCalledTimes(0);
|
||||
|
||||
// advance time to avoid key throttling
|
||||
jest.advanceTimersByTime(10000);
|
||||
|
||||
// update membership ID
|
||||
member2.membershipID = "newID";
|
||||
|
||||
const keysSentPromise2 = new Promise((resolve) => {
|
||||
sendEventMock.mockImplementation(resolve);
|
||||
});
|
||||
|
||||
// this should re-send the key
|
||||
sess.onMembershipUpdate();
|
||||
|
||||
await keysSentPromise2;
|
||||
|
||||
expect(sendEventMock).toHaveBeenCalledWith(
|
||||
expect.stringMatching(".*"),
|
||||
"io.element.call.encryption_keys",
|
||||
{
|
||||
call_id: "",
|
||||
device_id: "AAAAAAA",
|
||||
keys: [
|
||||
{
|
||||
index: 0,
|
||||
key: expect.stringMatching(".*"),
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
} finally {
|
||||
jest.useRealTimers();
|
||||
}
|
||||
});
|
||||
|
||||
it("Re-sends key if a member changes created_ts", async () => {
|
||||
jest.useFakeTimers();
|
||||
jest.setSystemTime(1000);
|
||||
try {
|
||||
const keysSentPromise1 = new Promise((resolve) => {
|
||||
sendEventMock.mockImplementation(resolve);
|
||||
});
|
||||
|
||||
const member1 = { ...membershipTemplate, created_ts: 1000 };
|
||||
const member2 = {
|
||||
...membershipTemplate,
|
||||
created_ts: 1000,
|
||||
device_id: "BBBBBBB",
|
||||
};
|
||||
|
||||
const mockRoom = makeMockRoom([member1, member2]);
|
||||
mockRoom.getLiveTimeline().getState = jest
|
||||
.fn()
|
||||
.mockReturnValue(makeMockRoomState([member1, member2], mockRoom.roomId));
|
||||
|
||||
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
|
||||
sess.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true });
|
||||
|
||||
await keysSentPromise1;
|
||||
|
||||
// make sure an encryption key was sent
|
||||
expect(sendEventMock).toHaveBeenCalledWith(
|
||||
expect.stringMatching(".*"),
|
||||
"io.element.call.encryption_keys",
|
||||
{
|
||||
call_id: "",
|
||||
device_id: "AAAAAAA",
|
||||
keys: [
|
||||
{
|
||||
index: 0,
|
||||
key: expect.stringMatching(".*"),
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
sendEventMock.mockClear();
|
||||
|
||||
// this should be a no-op:
|
||||
sess.onMembershipUpdate();
|
||||
expect(sendEventMock).toHaveBeenCalledTimes(0);
|
||||
|
||||
// advance time to avoid key throttling
|
||||
jest.advanceTimersByTime(10000);
|
||||
|
||||
// update created_ts
|
||||
member2.created_ts = 5000;
|
||||
|
||||
const keysSentPromise2 = new Promise((resolve) => {
|
||||
sendEventMock.mockImplementation(resolve);
|
||||
});
|
||||
|
||||
// this should re-send the key
|
||||
sess.onMembershipUpdate();
|
||||
|
||||
await keysSentPromise2;
|
||||
|
||||
expect(sendEventMock).toHaveBeenCalledWith(
|
||||
expect.stringMatching(".*"),
|
||||
"io.element.call.encryption_keys",
|
||||
{
|
||||
call_id: "",
|
||||
device_id: "AAAAAAA",
|
||||
keys: [
|
||||
{
|
||||
index: 0,
|
||||
key: expect.stringMatching(".*"),
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
} finally {
|
||||
jest.useRealTimers();
|
||||
}
|
||||
});
|
||||
|
||||
it("Rotates key if a member leaves", async () => {
|
||||
jest.useFakeTimers();
|
||||
try {
|
||||
@@ -668,7 +955,7 @@ describe("MatrixRTCSession", () => {
|
||||
|
||||
mockRoom.getLiveTimeline().getState = jest
|
||||
.fn()
|
||||
.mockReturnValue(makeMockRoomState([membershipTemplate], mockRoom.roomId, undefined));
|
||||
.mockReturnValue(makeMockRoomState([membershipTemplate], mockRoom.roomId));
|
||||
sess.onMembershipUpdate();
|
||||
|
||||
jest.advanceTimersByTime(10000);
|
||||
@@ -707,7 +994,7 @@ describe("MatrixRTCSession", () => {
|
||||
|
||||
mockRoom.getLiveTimeline().getState = jest
|
||||
.fn()
|
||||
.mockReturnValue(makeMockRoomState([membershipTemplate, member2], mockRoom.roomId, undefined));
|
||||
.mockReturnValue(makeMockRoomState([membershipTemplate, member2], mockRoom.roomId));
|
||||
sess.onMembershipUpdate();
|
||||
|
||||
await new Promise((resolve) => {
|
||||
@@ -739,9 +1026,7 @@ describe("MatrixRTCSession", () => {
|
||||
const onMembershipsChanged = jest.fn();
|
||||
sess.on(MatrixRTCSessionEvent.MembershipsChanged, onMembershipsChanged);
|
||||
|
||||
mockRoom.getLiveTimeline().getState = jest
|
||||
.fn()
|
||||
.mockReturnValue(makeMockRoomState([], mockRoom.roomId, undefined));
|
||||
mockRoom.getLiveTimeline().getState = jest.fn().mockReturnValue(makeMockRoomState([], mockRoom.roomId));
|
||||
sess.onMembershipUpdate();
|
||||
|
||||
expect(onMembershipsChanged).toHaveBeenCalled();
|
||||
@@ -751,7 +1036,7 @@ describe("MatrixRTCSession", () => {
|
||||
jest.useFakeTimers();
|
||||
try {
|
||||
const membership = Object.assign({}, membershipTemplate);
|
||||
const mockRoom = makeMockRoom([membership], 0);
|
||||
const mockRoom = makeMockRoom([membership]);
|
||||
|
||||
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
|
||||
const membershipObject = sess.memberships[0];
|
||||
@@ -779,7 +1064,7 @@ describe("MatrixRTCSession", () => {
|
||||
expires: 1000,
|
||||
}),
|
||||
];
|
||||
const mockRoomNoExpired = makeMockRoom(mockMemberships, 0);
|
||||
const mockRoomNoExpired = makeMockRoom(mockMemberships);
|
||||
|
||||
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoomNoExpired);
|
||||
|
||||
@@ -818,6 +1103,7 @@ describe("MatrixRTCSession", () => {
|
||||
it("fills in created_ts for other memberships on update", () => {
|
||||
client.sendStateEvent = jest.fn();
|
||||
jest.useFakeTimers();
|
||||
jest.setSystemTime(1000);
|
||||
const mockRoom = makeMockRoom([
|
||||
Object.assign({}, membershipTemplate, {
|
||||
device_id: "OTHERDEVICE",
|
||||
@@ -875,6 +1161,7 @@ describe("MatrixRTCSession", () => {
|
||||
],
|
||||
}),
|
||||
getSender: jest.fn().mockReturnValue("@bob:example.org"),
|
||||
getTs: jest.fn().mockReturnValue(Date.now()),
|
||||
} as unknown as MatrixEvent);
|
||||
|
||||
const bobKeys = sess.getKeysForParticipant("@bob:example.org", "bobsphone")!;
|
||||
@@ -898,6 +1185,7 @@ describe("MatrixRTCSession", () => {
|
||||
],
|
||||
}),
|
||||
getSender: jest.fn().mockReturnValue("@bob:example.org"),
|
||||
getTs: jest.fn().mockReturnValue(Date.now()),
|
||||
} as unknown as MatrixEvent);
|
||||
|
||||
const bobKeys = sess.getKeysForParticipant("@bob:example.org", "bobsphone")!;
|
||||
@@ -909,6 +1197,130 @@ describe("MatrixRTCSession", () => {
|
||||
expect(bobKeys[4]).toEqual(Buffer.from("this is the key", "utf-8"));
|
||||
});
|
||||
|
||||
it("collects keys by merging", () => {
|
||||
const mockRoom = makeMockRoom([membershipTemplate]);
|
||||
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
|
||||
sess.onCallEncryption({
|
||||
getType: jest.fn().mockReturnValue("io.element.call.encryption_keys"),
|
||||
getContent: jest.fn().mockReturnValue({
|
||||
device_id: "bobsphone",
|
||||
call_id: "",
|
||||
keys: [
|
||||
{
|
||||
index: 0,
|
||||
key: "dGhpcyBpcyB0aGUga2V5",
|
||||
},
|
||||
],
|
||||
}),
|
||||
getSender: jest.fn().mockReturnValue("@bob:example.org"),
|
||||
getTs: jest.fn().mockReturnValue(Date.now()),
|
||||
} as unknown as MatrixEvent);
|
||||
|
||||
let bobKeys = sess.getKeysForParticipant("@bob:example.org", "bobsphone")!;
|
||||
expect(bobKeys).toHaveLength(1);
|
||||
expect(bobKeys[0]).toEqual(Buffer.from("this is the key", "utf-8"));
|
||||
|
||||
sess.onCallEncryption({
|
||||
getType: jest.fn().mockReturnValue("io.element.call.encryption_keys"),
|
||||
getContent: jest.fn().mockReturnValue({
|
||||
device_id: "bobsphone",
|
||||
call_id: "",
|
||||
keys: [
|
||||
{
|
||||
index: 4,
|
||||
key: "dGhpcyBpcyB0aGUga2V5",
|
||||
},
|
||||
],
|
||||
}),
|
||||
getSender: jest.fn().mockReturnValue("@bob:example.org"),
|
||||
getTs: jest.fn().mockReturnValue(Date.now()),
|
||||
} as unknown as MatrixEvent);
|
||||
|
||||
bobKeys = sess.getKeysForParticipant("@bob:example.org", "bobsphone")!;
|
||||
expect(bobKeys).toHaveLength(5);
|
||||
expect(bobKeys[4]).toEqual(Buffer.from("this is the key", "utf-8"));
|
||||
});
|
||||
|
||||
it("ignores older keys at same index", () => {
|
||||
const mockRoom = makeMockRoom([membershipTemplate]);
|
||||
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
|
||||
sess.onCallEncryption({
|
||||
getType: jest.fn().mockReturnValue("io.element.call.encryption_keys"),
|
||||
getContent: jest.fn().mockReturnValue({
|
||||
device_id: "bobsphone",
|
||||
call_id: "",
|
||||
keys: [
|
||||
{
|
||||
index: 0,
|
||||
key: encodeBase64(Buffer.from("newer key", "utf-8")),
|
||||
},
|
||||
],
|
||||
}),
|
||||
getSender: jest.fn().mockReturnValue("@bob:example.org"),
|
||||
getTs: jest.fn().mockReturnValue(2000),
|
||||
} as unknown as MatrixEvent);
|
||||
|
||||
sess.onCallEncryption({
|
||||
getType: jest.fn().mockReturnValue("io.element.call.encryption_keys"),
|
||||
getContent: jest.fn().mockReturnValue({
|
||||
device_id: "bobsphone",
|
||||
call_id: "",
|
||||
keys: [
|
||||
{
|
||||
index: 0,
|
||||
key: encodeBase64(Buffer.from("older key", "utf-8")),
|
||||
},
|
||||
],
|
||||
}),
|
||||
getSender: jest.fn().mockReturnValue("@bob:example.org"),
|
||||
getTs: jest.fn().mockReturnValue(1000), // earlier timestamp than the newer key
|
||||
} as unknown as MatrixEvent);
|
||||
|
||||
const bobKeys = sess.getKeysForParticipant("@bob:example.org", "bobsphone")!;
|
||||
expect(bobKeys).toHaveLength(1);
|
||||
expect(bobKeys[0]).toEqual(Buffer.from("newer key", "utf-8"));
|
||||
});
|
||||
|
||||
it("key timestamps are treated as monotonic", () => {
|
||||
const mockRoom = makeMockRoom([membershipTemplate]);
|
||||
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
|
||||
sess.onCallEncryption({
|
||||
getType: jest.fn().mockReturnValue("io.element.call.encryption_keys"),
|
||||
getContent: jest.fn().mockReturnValue({
|
||||
device_id: "bobsphone",
|
||||
call_id: "",
|
||||
keys: [
|
||||
{
|
||||
index: 0,
|
||||
key: encodeBase64(Buffer.from("first key", "utf-8")),
|
||||
},
|
||||
],
|
||||
}),
|
||||
getSender: jest.fn().mockReturnValue("@bob:example.org"),
|
||||
getTs: jest.fn().mockReturnValue(1000),
|
||||
} as unknown as MatrixEvent);
|
||||
|
||||
sess.onCallEncryption({
|
||||
getType: jest.fn().mockReturnValue("io.element.call.encryption_keys"),
|
||||
getContent: jest.fn().mockReturnValue({
|
||||
device_id: "bobsphone",
|
||||
call_id: "",
|
||||
keys: [
|
||||
{
|
||||
index: 0,
|
||||
key: encodeBase64(Buffer.from("second key", "utf-8")),
|
||||
},
|
||||
],
|
||||
}),
|
||||
getSender: jest.fn().mockReturnValue("@bob:example.org"),
|
||||
getTs: jest.fn().mockReturnValue(1000), // same timestamp as the first key
|
||||
} as unknown as MatrixEvent);
|
||||
|
||||
const bobKeys = sess.getKeysForParticipant("@bob:example.org", "bobsphone")!;
|
||||
expect(bobKeys).toHaveLength(1);
|
||||
expect(bobKeys[0]).toEqual(Buffer.from("second key", "utf-8"));
|
||||
});
|
||||
|
||||
it("ignores keys event for the local participant", () => {
|
||||
const mockRoom = makeMockRoom([membershipTemplate]);
|
||||
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
|
||||
@@ -925,6 +1337,7 @@ describe("MatrixRTCSession", () => {
|
||||
],
|
||||
}),
|
||||
getSender: jest.fn().mockReturnValue(client.getUserId()),
|
||||
getTs: jest.fn().mockReturnValue(Date.now()),
|
||||
} as unknown as MatrixEvent);
|
||||
|
||||
const myKeys = sess.getKeysForParticipant(client.getUserId()!, client.getDeviceId()!)!;
|
||||
|
||||
@@ -102,6 +102,7 @@ describe("MatrixRTCSessionManager", () => {
|
||||
getContent: jest.fn().mockReturnValue({}),
|
||||
getSender: jest.fn().mockReturnValue("@mock:user.example"),
|
||||
getRoomId: jest.fn().mockReturnValue("!room:id"),
|
||||
isDecryptionFailure: jest.fn().mockReturnValue(false),
|
||||
sender: {
|
||||
userId: "@mock:user.example",
|
||||
},
|
||||
@@ -110,4 +111,93 @@ describe("MatrixRTCSessionManager", () => {
|
||||
await new Promise(process.nextTick);
|
||||
expect(onCallEncryptionMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe("event decryption", () => {
|
||||
it("Retries decryption and processes success", async () => {
|
||||
try {
|
||||
jest.useFakeTimers();
|
||||
const room1 = makeMockRoom([membershipTemplate]);
|
||||
jest.spyOn(client, "getRooms").mockReturnValue([room1]);
|
||||
jest.spyOn(client, "getRoom").mockReturnValue(room1);
|
||||
|
||||
client.emit(ClientEvent.Room, room1);
|
||||
const onCallEncryptionMock = jest.fn();
|
||||
client.matrixRTC.getRoomSession(room1).onCallEncryption = onCallEncryptionMock;
|
||||
let isDecryptionFailure = true;
|
||||
client.decryptEventIfNeeded = jest
|
||||
.fn()
|
||||
.mockReturnValueOnce(Promise.resolve())
|
||||
.mockImplementation(() => {
|
||||
isDecryptionFailure = false;
|
||||
return Promise.resolve();
|
||||
});
|
||||
const timelineEvent = {
|
||||
getType: jest.fn().mockReturnValue(EventType.CallEncryptionKeysPrefix),
|
||||
getContent: jest.fn().mockReturnValue({}),
|
||||
getSender: jest.fn().mockReturnValue("@mock:user.example"),
|
||||
getRoomId: jest.fn().mockReturnValue("!room:id"),
|
||||
isDecryptionFailure: jest.fn().mockImplementation(() => isDecryptionFailure),
|
||||
getId: jest.fn().mockReturnValue("event_id"),
|
||||
sender: {
|
||||
userId: "@mock:user.example",
|
||||
},
|
||||
} as unknown as MatrixEvent;
|
||||
client.emit(RoomEvent.Timeline, timelineEvent, undefined, undefined, false, {} as IRoomTimelineData);
|
||||
|
||||
expect(client.decryptEventIfNeeded).toHaveBeenCalledTimes(1);
|
||||
expect(onCallEncryptionMock).toHaveBeenCalledTimes(0);
|
||||
|
||||
// should retry after one second:
|
||||
await jest.advanceTimersByTimeAsync(1500);
|
||||
|
||||
expect(client.decryptEventIfNeeded).toHaveBeenCalledTimes(2);
|
||||
expect(onCallEncryptionMock).toHaveBeenCalledTimes(1);
|
||||
} finally {
|
||||
jest.useRealTimers();
|
||||
}
|
||||
});
|
||||
|
||||
it("Retries decryption and processes failure", async () => {
|
||||
try {
|
||||
jest.useFakeTimers();
|
||||
const room1 = makeMockRoom([membershipTemplate]);
|
||||
jest.spyOn(client, "getRooms").mockReturnValue([room1]);
|
||||
jest.spyOn(client, "getRoom").mockReturnValue(room1);
|
||||
|
||||
client.emit(ClientEvent.Room, room1);
|
||||
const onCallEncryptionMock = jest.fn();
|
||||
client.matrixRTC.getRoomSession(room1).onCallEncryption = onCallEncryptionMock;
|
||||
client.decryptEventIfNeeded = jest.fn().mockReturnValue(Promise.resolve());
|
||||
const timelineEvent = {
|
||||
getType: jest.fn().mockReturnValue(EventType.CallEncryptionKeysPrefix),
|
||||
getContent: jest.fn().mockReturnValue({}),
|
||||
getSender: jest.fn().mockReturnValue("@mock:user.example"),
|
||||
getRoomId: jest.fn().mockReturnValue("!room:id"),
|
||||
isDecryptionFailure: jest.fn().mockReturnValue(true), // always fail
|
||||
getId: jest.fn().mockReturnValue("event_id"),
|
||||
sender: {
|
||||
userId: "@mock:user.example",
|
||||
},
|
||||
} as unknown as MatrixEvent;
|
||||
client.emit(RoomEvent.Timeline, timelineEvent, undefined, undefined, false, {} as IRoomTimelineData);
|
||||
|
||||
expect(client.decryptEventIfNeeded).toHaveBeenCalledTimes(1);
|
||||
expect(onCallEncryptionMock).toHaveBeenCalledTimes(0);
|
||||
|
||||
// should retry after one second:
|
||||
await jest.advanceTimersByTimeAsync(1500);
|
||||
|
||||
expect(client.decryptEventIfNeeded).toHaveBeenCalledTimes(2);
|
||||
expect(onCallEncryptionMock).toHaveBeenCalledTimes(0);
|
||||
|
||||
// doesn't retry again:
|
||||
await jest.advanceTimersByTimeAsync(1500);
|
||||
|
||||
expect(client.decryptEventIfNeeded).toHaveBeenCalledTimes(2);
|
||||
expect(onCallEncryptionMock).toHaveBeenCalledTimes(0);
|
||||
} finally {
|
||||
jest.useRealTimers();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,10 +20,10 @@ import { randomString } from "../../../src/randomstring";
|
||||
|
||||
type MembershipData = CallMembershipData[] | SessionMembershipData;
|
||||
|
||||
export function makeMockRoom(membershipData: MembershipData, localAge: number | null = null): Room {
|
||||
export function makeMockRoom(membershipData: MembershipData): Room {
|
||||
const roomId = randomString(8);
|
||||
// Caching roomState here so it does not get recreated when calling `getLiveTimeline.getState()`
|
||||
const roomState = makeMockRoomState(membershipData, roomId, localAge);
|
||||
const roomState = makeMockRoomState(membershipData, roomId);
|
||||
return {
|
||||
roomId: roomId,
|
||||
hasMembershipState: jest.fn().mockReturnValue(true),
|
||||
@@ -34,8 +34,8 @@ export function makeMockRoom(membershipData: MembershipData, localAge: number |
|
||||
} as unknown as Room;
|
||||
}
|
||||
|
||||
export function makeMockRoomState(membershipData: MembershipData, roomId: string, localAge: number | null = null) {
|
||||
const event = mockRTCEvent(membershipData, roomId, localAge);
|
||||
export function makeMockRoomState(membershipData: MembershipData, roomId: string) {
|
||||
const event = mockRTCEvent(membershipData, roomId);
|
||||
return {
|
||||
on: jest.fn(),
|
||||
off: jest.fn(),
|
||||
@@ -57,7 +57,7 @@ export function makeMockRoomState(membershipData: MembershipData, roomId: string
|
||||
};
|
||||
}
|
||||
|
||||
export function mockRTCEvent(membershipData: MembershipData, roomId: string, localAge: number | null): MatrixEvent {
|
||||
export function mockRTCEvent(membershipData: MembershipData, roomId: string): MatrixEvent {
|
||||
return {
|
||||
getType: jest.fn().mockReturnValue(EventType.GroupCallMemberPrefix),
|
||||
getContent: jest.fn().mockReturnValue(
|
||||
@@ -68,11 +68,11 @@ export function mockRTCEvent(membershipData: MembershipData, roomId: string, loc
|
||||
},
|
||||
),
|
||||
getSender: jest.fn().mockReturnValue("@mock:user.example"),
|
||||
getTs: jest.fn().mockReturnValue(1000),
|
||||
localTimestamp: Date.now() - (localAge ?? 10),
|
||||
getTs: jest.fn().mockReturnValue(Date.now()),
|
||||
getRoomId: jest.fn().mockReturnValue(roomId),
|
||||
sender: {
|
||||
userId: "@mock:user.example",
|
||||
},
|
||||
isDecryptionFailure: jest.fn().mockReturnValue(false),
|
||||
} as unknown as MatrixEvent;
|
||||
}
|
||||
|
||||
@@ -412,7 +412,12 @@ describe("MatrixEvent", () => {
|
||||
const crypto = {
|
||||
decryptEvent: jest
|
||||
.fn()
|
||||
.mockRejectedValue("DecryptionError: The sender has disabled encrypting to unverified devices."),
|
||||
.mockRejectedValue(
|
||||
new DecryptionError(
|
||||
DecryptionFailureCode.MEGOLM_KEY_WITHHELD_FOR_UNVERIFIED_DEVICE,
|
||||
"The sender has disabled encrypting to unverified devices.",
|
||||
),
|
||||
),
|
||||
} as unknown as Crypto;
|
||||
|
||||
await encryptedEvent.attemptDecryption(crypto);
|
||||
|
||||
@@ -89,11 +89,8 @@ describe("oidc authorization", () => {
|
||||
|
||||
describe("generateAuthorizationUrl()", () => {
|
||||
it("should generate url with correct parameters", async () => {
|
||||
// test the no crypto case here
|
||||
// @ts-ignore mocking
|
||||
globalThis.crypto.subtle = undefined;
|
||||
|
||||
const authorizationParams = generateAuthorizationParams({ redirectUri: baseUrl });
|
||||
authorizationParams.codeVerifier = "test-code-verifier";
|
||||
const authUrl = new URL(
|
||||
await generateAuthorizationUrl(authorizationEndpoint, clientId, authorizationParams),
|
||||
);
|
||||
@@ -105,6 +102,18 @@ describe("oidc authorization", () => {
|
||||
expect(authUrl.searchParams.get("scope")).toEqual(authorizationParams.scope);
|
||||
expect(authUrl.searchParams.get("state")).toEqual(authorizationParams.state);
|
||||
expect(authUrl.searchParams.get("nonce")).toEqual(authorizationParams.nonce);
|
||||
expect(authUrl.searchParams.get("code_challenge")).toEqual("0FLIKahrX7kqxncwhV5WD82lu_wi5GA8FsRSLubaOpU");
|
||||
});
|
||||
|
||||
it("should log a warning if crypto is not available", async () => {
|
||||
// test the no crypto case here
|
||||
// @ts-ignore mocking
|
||||
globalThis.crypto.subtle = undefined;
|
||||
|
||||
const authorizationParams = generateAuthorizationParams({ redirectUri: baseUrl });
|
||||
const authUrl = new URL(
|
||||
await generateAuthorizationUrl(authorizationEndpoint, clientId, authorizationParams),
|
||||
);
|
||||
|
||||
// crypto not available, plain text code_challenge is used
|
||||
expect(authUrl.searchParams.get("code_challenge")).toEqual(authorizationParams.codeVerifier);
|
||||
|
||||
@@ -95,6 +95,7 @@ describe("initRustCrypto", () => {
|
||||
deleteSecretsFromInbox: jest.fn(),
|
||||
registerReceiveSecretCallback: jest.fn(),
|
||||
registerDevicesUpdatedCallback: jest.fn(),
|
||||
registerRoomKeysWithheldCallback: jest.fn(),
|
||||
outgoingRequests: jest.fn(),
|
||||
isBackupEnabled: jest.fn().mockResolvedValue(false),
|
||||
verifyBackup: jest.fn().mockResolvedValue({ trusted: jest.fn().mockReturnValue(false) }),
|
||||
|
||||
@@ -434,7 +434,7 @@ describe("TimelineWindow", function () {
|
||||
});
|
||||
|
||||
function idsOf(events: Array<MatrixEvent>): Array<string> {
|
||||
return events.map((e) => (e ? e.getId() ?? "MISSING_ID" : "MISSING_EVENT"));
|
||||
return events.map((e) => (e ? (e.getId() ?? "MISSING_ID") : "MISSING_EVENT"));
|
||||
}
|
||||
|
||||
describe("removing events", () => {
|
||||
|
||||
@@ -105,7 +105,7 @@ const mockGetStateEvents =
|
||||
(events: MatrixEvent[] = FAKE_STATE_EVENTS as MatrixEvent[]) =>
|
||||
(type: EventType, userId?: string): MatrixEvent[] | MatrixEvent | null => {
|
||||
if (type === EventType.GroupCallMemberPrefix) {
|
||||
return userId === undefined ? events : events.find((e) => e.getStateKey() === userId) ?? null;
|
||||
return userId === undefined ? events : (events.find((e) => e.getStateKey() === userId) ?? null);
|
||||
} else {
|
||||
const fakeEvent = { getContent: () => ({}), getTs: () => 0 } as MatrixEvent;
|
||||
return userId === undefined ? [fakeEvent] : fakeEvent;
|
||||
|
||||
+2
-2
@@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { UnstableValue } from "../NamespacedValue";
|
||||
import { IClientWellKnown } from "../client";
|
||||
import { UnstableValue } from "../NamespacedValue.ts";
|
||||
import { IClientWellKnown } from "../client.ts";
|
||||
|
||||
// disable lint because these are wire responses
|
||||
/* eslint-disable camelcase */
|
||||
|
||||
@@ -14,9 +14,9 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { RelatesToRelationship, REFERENCE_RELATION } from "./extensible_events";
|
||||
import { UnstableValue } from "../NamespacedValue";
|
||||
import { MAssetEvent, MLocationEvent, MTimestampEvent } from "./location";
|
||||
import { RelatesToRelationship, REFERENCE_RELATION } from "./extensible_events.ts";
|
||||
import { UnstableValue } from "../NamespacedValue.ts";
|
||||
import { MAssetEvent, MLocationEvent, MTimestampEvent } from "./location.ts";
|
||||
|
||||
/**
|
||||
* Beacon info and beacon event types as described in MSC3672
|
||||
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import type { ISignatures } from "./signed";
|
||||
import type { ISignatures } from "./signed.ts";
|
||||
|
||||
export type OlmGroupSessionExtraData = {
|
||||
untrusted?: boolean;
|
||||
@@ -22,7 +22,7 @@ export type OlmGroupSessionExtraData = {
|
||||
};
|
||||
|
||||
// Backwards compatible re-export
|
||||
export type { EventDecryptionResult as IEventDecryptionResult } from "../common-crypto/CryptoBackend";
|
||||
export type { EventDecryptionResult as IEventDecryptionResult } from "../common-crypto/CryptoBackend.ts";
|
||||
|
||||
interface Extensible {
|
||||
[key: string]: any;
|
||||
|
||||
+11
-11
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { NamespacedValue, UnstableValue } from "../NamespacedValue";
|
||||
import { NamespacedValue, UnstableValue } from "../NamespacedValue.ts";
|
||||
import {
|
||||
PolicyRuleEventContent,
|
||||
RoomAvatarEventContent,
|
||||
@@ -34,16 +34,16 @@ import {
|
||||
RoomTopicEventContent,
|
||||
SpaceChildEventContent,
|
||||
SpaceParentEventContent,
|
||||
} from "./state_events";
|
||||
} from "./state_events.ts";
|
||||
import {
|
||||
ExperimentalGroupCallRoomMemberState,
|
||||
IGroupCallRoomMemberState,
|
||||
IGroupCallRoomState,
|
||||
} from "../webrtc/groupCall";
|
||||
import { MSC3089EventContent } from "../models/MSC3089Branch";
|
||||
import { M_BEACON, M_BEACON_INFO, MBeaconEventContent, MBeaconInfoEventContent } from "./beacon";
|
||||
import { XOR } from "./common";
|
||||
import { ReactionEventContent, RoomMessageEventContent, StickerEventContent } from "./events";
|
||||
} from "../webrtc/groupCall.ts";
|
||||
import { MSC3089EventContent } from "../models/MSC3089Branch.ts";
|
||||
import { M_BEACON, M_BEACON_INFO, MBeaconEventContent, MBeaconInfoEventContent } from "./beacon.ts";
|
||||
import { XOR } from "./common.ts";
|
||||
import { ReactionEventContent, RoomMessageEventContent, StickerEventContent } from "./events.ts";
|
||||
import {
|
||||
MCallAnswer,
|
||||
MCallBase,
|
||||
@@ -54,10 +54,10 @@ import {
|
||||
MCallSelectAnswer,
|
||||
SDPStreamMetadata,
|
||||
SDPStreamMetadataKey,
|
||||
} from "../webrtc/callEventTypes";
|
||||
import { EncryptionKeysEventContent, ICallNotifyContent } from "../matrixrtc/types";
|
||||
import { M_POLL_END, M_POLL_START, PollEndEventContent, PollStartEventContent } from "./polls";
|
||||
import { SessionMembershipData } from "../matrixrtc/CallMembership";
|
||||
} from "../webrtc/callEventTypes.ts";
|
||||
import { EncryptionKeysEventContent, ICallNotifyContent } from "../matrixrtc/types.ts";
|
||||
import { M_POLL_END, M_POLL_START, PollEndEventContent, PollStartEventContent } from "./polls.ts";
|
||||
import { SessionMembershipData } from "../matrixrtc/CallMembership.ts";
|
||||
|
||||
export enum EventType {
|
||||
// Room state events
|
||||
|
||||
@@ -14,9 +14,9 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { MsgType, RelationType } from "./event";
|
||||
import { FileInfo, ImageInfo, MediaEventContent } from "./media";
|
||||
import { XOR } from "./common";
|
||||
import { MsgType, RelationType } from "./event.ts";
|
||||
import { FileInfo, ImageInfo, MediaEventContent } from "./media.ts";
|
||||
import { XOR } from "./common.ts";
|
||||
|
||||
interface BaseTimelineEvent {
|
||||
"body": string;
|
||||
|
||||
@@ -16,7 +16,7 @@ limitations under the License.
|
||||
|
||||
import { EitherAnd, NamespacedValue, Optional, UnstableValue } from "matrix-events-sdk";
|
||||
|
||||
import { isProvided } from "../extensible_events_v1/utilities";
|
||||
import { isProvided } from "../extensible_events_v1/utilities.ts";
|
||||
|
||||
// Types and utilities for MSC1767: Extensible events (version 1) in Matrix
|
||||
|
||||
|
||||
Vendored
-45
@@ -29,20 +29,11 @@ declare global {
|
||||
|
||||
namespace NodeJS {
|
||||
interface Global {
|
||||
localStorage: Storage;
|
||||
// marker variable used to detect both the browser & node entrypoints being used at once
|
||||
__js_sdk_entrypoint: unknown;
|
||||
}
|
||||
}
|
||||
|
||||
interface Window {
|
||||
webkitAudioContext: typeof AudioContext;
|
||||
}
|
||||
|
||||
interface Crypto {
|
||||
webkitSubtle?: Window["crypto"]["subtle"];
|
||||
}
|
||||
|
||||
interface MediaDevices {
|
||||
// This is experimental and types don't know about it yet
|
||||
// https://github.com/microsoft/TypeScript/issues/33232
|
||||
@@ -76,40 +67,4 @@ declare global {
|
||||
// on webkit: we should check if we still need to do this
|
||||
webkitGetUserMedia: DummyInterfaceWeShouldntBeUsingThis;
|
||||
}
|
||||
|
||||
export interface ISettledFulfilled<T> {
|
||||
status: "fulfilled";
|
||||
value: T;
|
||||
}
|
||||
export interface ISettledRejected {
|
||||
status: "rejected";
|
||||
reason: any;
|
||||
}
|
||||
|
||||
interface PromiseConstructor {
|
||||
allSettled<T>(promises: Promise<T>[]): Promise<Array<ISettledFulfilled<T> | ISettledRejected>>;
|
||||
}
|
||||
|
||||
interface RTCRtpTransceiver {
|
||||
// This has been removed from TS
|
||||
// (https://github.com/microsoft/TypeScript-DOM-lib-generator/issues/1029),
|
||||
// but we still need this for MatrixCall::getRidOfRTXCodecs()
|
||||
setCodecPreferences(codecs: RTCRtpCodecCapability[]): void;
|
||||
}
|
||||
|
||||
interface RequestInit {
|
||||
/**
|
||||
* Specifies the priority of the fetch request relative to other requests of the same type.
|
||||
* Must be one of the following strings:
|
||||
* high: A high priority fetch request relative to other requests of the same type.
|
||||
* low: A low priority fetch request relative to other requests of the same type.
|
||||
* auto: Automatically determine the priority of the fetch request relative to other requests of the same type (default).
|
||||
*
|
||||
* @see https://html.spec.whatwg.org/multipage/urls-and-fetching.html#fetch-priority-attribute
|
||||
* @see https://github.com/microsoft/TypeScript/issues/54472
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API#browser_compatibility
|
||||
* Not yet supported in Safari or Firefox
|
||||
*/
|
||||
priority?: "high" | "low" | "auto";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@ limitations under the License.
|
||||
// Types for MSC3488 - m.location: Extending events with location data
|
||||
import { EitherAnd } from "matrix-events-sdk";
|
||||
|
||||
import { UnstableValue } from "../NamespacedValue";
|
||||
import { M_TEXT } from "./extensible_events";
|
||||
import { UnstableValue } from "../NamespacedValue.ts";
|
||||
import { M_TEXT } from "./extensible_events.ts";
|
||||
|
||||
export enum LocationAssetType {
|
||||
Self = "m.self",
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { MsgType } from "../@types/event";
|
||||
import { MsgType } from "../@types/event.ts";
|
||||
|
||||
/**
|
||||
* Information on encrypted media attachments.
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ import {
|
||||
REFERENCE_RELATION,
|
||||
RelatesToRelationship,
|
||||
TSNamespace,
|
||||
} from "./extensible_events";
|
||||
} from "./extensible_events.ts";
|
||||
|
||||
/**
|
||||
* Identifier for a disclosed poll.
|
||||
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { AuthDict } from "../interactive-auth";
|
||||
import { AuthDict } from "../interactive-auth.ts";
|
||||
|
||||
/**
|
||||
* The request body of a call to `POST /_matrix/client/v3/register`.
|
||||
|
||||
+58
-8
@@ -14,14 +14,14 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { IContent, IEvent } from "../models/event";
|
||||
import { Preset, Visibility } from "./partials";
|
||||
import { IEventWithRoomId, SearchKey } from "./search";
|
||||
import { IRoomEventFilter } from "../filter";
|
||||
import { Direction } from "../models/event-timeline";
|
||||
import { PushRuleAction } from "./PushRules";
|
||||
import { IRoomEvent } from "../sync-accumulator";
|
||||
import { EventType, RelationType, RoomType } from "./event";
|
||||
import { IContent, IEvent } from "../models/event.ts";
|
||||
import { Preset, Visibility } from "./partials.ts";
|
||||
import { IEventWithRoomId, SearchKey } from "./search.ts";
|
||||
import { IRoomEventFilter } from "../filter.ts";
|
||||
import { Direction } from "../models/event-timeline.ts";
|
||||
import { PushRuleAction } from "./PushRules.ts";
|
||||
import { IRoomEvent } from "../sync-accumulator.ts";
|
||||
import { EventType, RelationType, RoomType } from "./event.ts";
|
||||
|
||||
// allow camelcase as these are things that go onto the wire
|
||||
/* eslint-disable camelcase */
|
||||
@@ -76,6 +76,56 @@ export interface ISendEventResponse {
|
||||
event_id: string;
|
||||
}
|
||||
|
||||
export type TimeoutDelay = {
|
||||
delay: number;
|
||||
};
|
||||
|
||||
export type ParentDelayId = {
|
||||
parent_delay_id: string;
|
||||
};
|
||||
|
||||
export type SendTimeoutDelayedEventRequestOpts = TimeoutDelay & Partial<ParentDelayId>;
|
||||
export type SendActionDelayedEventRequestOpts = ParentDelayId;
|
||||
|
||||
export type SendDelayedEventRequestOpts = SendTimeoutDelayedEventRequestOpts | SendActionDelayedEventRequestOpts;
|
||||
|
||||
export type SendDelayedEventResponse = {
|
||||
delay_id: string;
|
||||
};
|
||||
|
||||
export enum UpdateDelayedEventAction {
|
||||
Cancel = "cancel",
|
||||
Restart = "restart",
|
||||
Send = "send",
|
||||
}
|
||||
|
||||
export type UpdateDelayedEventRequestOpts = SendDelayedEventResponse & {
|
||||
action: UpdateDelayedEventAction;
|
||||
};
|
||||
|
||||
type DelayedPartialTimelineEvent = {
|
||||
room_id: string;
|
||||
type: string;
|
||||
content: IContent;
|
||||
};
|
||||
|
||||
type DelayedPartialStateEvent = DelayedPartialTimelineEvent & {
|
||||
state_key: string;
|
||||
transaction_id: string;
|
||||
};
|
||||
|
||||
type DelayedPartialEvent = DelayedPartialTimelineEvent | DelayedPartialStateEvent;
|
||||
|
||||
export type DelayedEventInfo = {
|
||||
delayed_events: DelayedPartialEvent &
|
||||
SendDelayedEventResponse &
|
||||
SendDelayedEventRequestOpts &
|
||||
{
|
||||
running_since: number;
|
||||
}[];
|
||||
next_batch?: string;
|
||||
};
|
||||
|
||||
export interface IPresenceOpts {
|
||||
// One of "online", "offline" or "unavailable"
|
||||
presence: "online" | "offline" | "unavailable";
|
||||
|
||||
@@ -16,9 +16,9 @@ limitations under the License.
|
||||
|
||||
// Types relating to the /search API
|
||||
|
||||
import { IRoomEvent, IStateEvent } from "../sync-accumulator";
|
||||
import { IRoomEventFilter } from "../filter";
|
||||
import { SearchResult } from "../models/search-result";
|
||||
import { IRoomEvent, IStateEvent } from "../sync-accumulator.ts";
|
||||
import { IRoomEventFilter } from "../filter.ts";
|
||||
import { SearchResult } from "../models/search-result.ts";
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
export interface IEventWithRoomId extends IRoomEvent {
|
||||
|
||||
@@ -14,9 +14,9 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { IPublicRoomsChunkRoom } from "../client";
|
||||
import { RoomType } from "./event";
|
||||
import { IStrippedState } from "../sync-accumulator";
|
||||
import { IPublicRoomsChunkRoom } from "../client.ts";
|
||||
import { RoomType } from "./event.ts";
|
||||
import { IStrippedState } from "../sync-accumulator.ts";
|
||||
|
||||
// Types relating to Rooms of type `m.space` and related APIs
|
||||
|
||||
|
||||
@@ -14,10 +14,10 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { RoomType } from "./event";
|
||||
import { GuestAccess, HistoryVisibility, JoinRule, RestrictedAllowType } from "./partials";
|
||||
import { ImageInfo } from "./media";
|
||||
import { PolicyRecommendation } from "../models/invites-ignorer";
|
||||
import { RoomType } from "./event.ts";
|
||||
import { GuestAccess, HistoryVisibility, JoinRule, RestrictedAllowType } from "./partials.ts";
|
||||
import { ImageInfo } from "./media.ts";
|
||||
import { PolicyRecommendation } from "../models/invites-ignorer.ts";
|
||||
|
||||
export interface RoomCanonicalAliasEventContent {
|
||||
alias?: string;
|
||||
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { IdServerUnbindResult } from "./partials";
|
||||
import { IdServerUnbindResult } from "./partials.ts";
|
||||
|
||||
// Types relating to Synapse Admin APIs
|
||||
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { ServerControlledNamespacedValue } from "../NamespacedValue";
|
||||
import { ServerControlledNamespacedValue } from "../NamespacedValue.ts";
|
||||
|
||||
/**
|
||||
* https://github.com/matrix-org/matrix-doc/pull/3773
|
||||
|
||||
+2
-2
@@ -16,8 +16,8 @@ limitations under the License.
|
||||
|
||||
import { EitherAnd } from "matrix-events-sdk";
|
||||
|
||||
import { UnstableValue } from "../NamespacedValue";
|
||||
import { IMessageRendering } from "./extensible_events";
|
||||
import { UnstableValue } from "../NamespacedValue.ts";
|
||||
import { IMessageRendering } from "./extensible_events.ts";
|
||||
|
||||
/**
|
||||
* Extensible topic event type based on MSC3765
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { AuthDict, IAuthData } from "../interactive-auth";
|
||||
import { AuthDict, IAuthData } from "../interactive-auth.ts";
|
||||
|
||||
/**
|
||||
* Helper type to represent HTTP request body for a UIA enabled endpoint
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ limitations under the License.
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { EventEmitter } from "events";
|
||||
|
||||
import { ListenerMap, TypedEventEmitter } from "./models/typed-event-emitter";
|
||||
import { ListenerMap, TypedEventEmitter } from "./models/typed-event-emitter.ts";
|
||||
|
||||
export class ReEmitter {
|
||||
public constructor(private readonly target: EventEmitter) {}
|
||||
|
||||
@@ -14,14 +14,19 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { ToDeviceMessageId } from "./@types/event";
|
||||
import { logger } from "./logger";
|
||||
import { MatrixClient, ClientEvent } from "./client";
|
||||
import { MatrixError } from "./http-api";
|
||||
import { IndexedToDeviceBatch, ToDeviceBatch, ToDeviceBatchWithTxnId, ToDevicePayload } from "./models/ToDeviceMessage";
|
||||
import { MatrixScheduler } from "./scheduler";
|
||||
import { SyncState } from "./sync";
|
||||
import { MapWithDefault } from "./utils";
|
||||
import { ToDeviceMessageId } from "./@types/event.ts";
|
||||
import { logger } from "./logger.ts";
|
||||
import { MatrixClient, ClientEvent } from "./client.ts";
|
||||
import { MatrixError } from "./http-api/index.ts";
|
||||
import {
|
||||
IndexedToDeviceBatch,
|
||||
ToDeviceBatch,
|
||||
ToDeviceBatchWithTxnId,
|
||||
ToDevicePayload,
|
||||
} from "./models/ToDeviceMessage.ts";
|
||||
import { MatrixScheduler } from "./scheduler.ts";
|
||||
import { SyncState } from "./sync.ts";
|
||||
import { MapWithDefault } from "./utils.ts";
|
||||
|
||||
const MAX_BATCH_SIZE = 20;
|
||||
|
||||
|
||||
@@ -15,10 +15,10 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { IClientWellKnown, IWellKnownConfig, IServerVersions } from "./client";
|
||||
import { logger } from "./logger";
|
||||
import { MatrixError, Method, timeoutSignal } from "./http-api";
|
||||
import { SUPPORTED_MATRIX_VERSIONS } from "./version-support";
|
||||
import { IClientWellKnown, IWellKnownConfig, IServerVersions } from "./client.ts";
|
||||
import { logger } from "./logger.ts";
|
||||
import { MatrixError, Method, timeoutSignal } from "./http-api/index.ts";
|
||||
import { SUPPORTED_MATRIX_VERSIONS } from "./version-support.ts";
|
||||
|
||||
// Dev note: Auto discovery is part of the spec.
|
||||
// See: https://matrix.org/docs/spec/client_server/r0.4.0.html#server-discovery
|
||||
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import * as matrixcs from "./matrix";
|
||||
import * as matrixcs from "./matrix.ts";
|
||||
|
||||
type BrowserMatrix = typeof matrixcs;
|
||||
declare global {
|
||||
@@ -40,5 +40,5 @@ if (indexedDB) {
|
||||
matrixcs.setCryptoStoreFactory(() => new matrixcs.IndexedDBCryptoStore(indexedDB!, "matrix-js-sdk:crypto"));
|
||||
}
|
||||
|
||||
export * from "./matrix";
|
||||
export * from "./matrix.ts";
|
||||
globalThis.matrixcs = matrixcs;
|
||||
|
||||
+337
-134
@@ -20,8 +20,8 @@ limitations under the License.
|
||||
|
||||
import { Optional } from "matrix-events-sdk";
|
||||
|
||||
import type { IDeviceKeys, IMegolmSessionData, IOneTimeKey } from "./@types/crypto";
|
||||
import { ISyncStateData, SetPresence, SyncApi, SyncApiOptions, SyncState } from "./sync";
|
||||
import type { IDeviceKeys, IMegolmSessionData, IOneTimeKey } from "./@types/crypto.ts";
|
||||
import { ISyncStateData, SetPresence, SyncApi, SyncApiOptions, SyncState } from "./sync.ts";
|
||||
import {
|
||||
EventStatus,
|
||||
IContent,
|
||||
@@ -31,29 +31,29 @@ import {
|
||||
MatrixEventEvent,
|
||||
MatrixEventHandlerMap,
|
||||
PushDetails,
|
||||
} from "./models/event";
|
||||
import { StubStore } from "./store/stub";
|
||||
import { CallEvent, CallEventHandlerMap, createNewMatrixCall, MatrixCall, supportsMatrixCall } from "./webrtc/call";
|
||||
import { Filter, IFilterDefinition, IRoomEventFilter } from "./filter";
|
||||
import { CallEventHandler, CallEventHandlerEvent, CallEventHandlerEventHandlerMap } from "./webrtc/callEventHandler";
|
||||
} from "./models/event.ts";
|
||||
import { StubStore } from "./store/stub.ts";
|
||||
import { CallEvent, CallEventHandlerMap, createNewMatrixCall, MatrixCall, supportsMatrixCall } from "./webrtc/call.ts";
|
||||
import { Filter, IFilterDefinition, IRoomEventFilter } from "./filter.ts";
|
||||
import { CallEventHandler, CallEventHandlerEvent, CallEventHandlerEventHandlerMap } from "./webrtc/callEventHandler.ts";
|
||||
import {
|
||||
GroupCallEventHandler,
|
||||
GroupCallEventHandlerEvent,
|
||||
GroupCallEventHandlerEventHandlerMap,
|
||||
} from "./webrtc/groupCallEventHandler";
|
||||
import * as utils from "./utils";
|
||||
import { noUnsafeEventProps, QueryDict, replaceParam, safeSet, sleep } from "./utils";
|
||||
import { Direction, EventTimeline } from "./models/event-timeline";
|
||||
import { IActionsObject, PushProcessor } from "./pushprocessor";
|
||||
import { AutoDiscovery, AutoDiscoveryAction } from "./autodiscovery";
|
||||
import * as olmlib from "./crypto/olmlib";
|
||||
import { decodeBase64, encodeBase64 } from "./base64";
|
||||
import { IExportedDevice as IExportedOlmDevice } from "./crypto/OlmDevice";
|
||||
import { IOlmDevice } from "./crypto/algorithms/megolm";
|
||||
import { TypedReEmitter } from "./ReEmitter";
|
||||
import { IRoomEncryption } from "./crypto/RoomList";
|
||||
import { logger, Logger } from "./logger";
|
||||
import { SERVICE_TYPES } from "./service-types";
|
||||
} from "./webrtc/groupCallEventHandler.ts";
|
||||
import * as utils from "./utils.ts";
|
||||
import { noUnsafeEventProps, QueryDict, replaceParam, safeSet, sleep } from "./utils.ts";
|
||||
import { Direction, EventTimeline } from "./models/event-timeline.ts";
|
||||
import { IActionsObject, PushProcessor } from "./pushprocessor.ts";
|
||||
import { AutoDiscovery, AutoDiscoveryAction } from "./autodiscovery.ts";
|
||||
import * as olmlib from "./crypto/olmlib.ts";
|
||||
import { decodeBase64, encodeBase64, encodeUnpaddedBase64Url } from "./base64.ts";
|
||||
import { IExportedDevice as IExportedOlmDevice } from "./crypto/OlmDevice.ts";
|
||||
import { IOlmDevice } from "./crypto/algorithms/megolm.ts";
|
||||
import { TypedReEmitter } from "./ReEmitter.ts";
|
||||
import { IRoomEncryption } from "./crypto/RoomList.ts";
|
||||
import { logger, Logger } from "./logger.ts";
|
||||
import { SERVICE_TYPES } from "./service-types.ts";
|
||||
import {
|
||||
Body,
|
||||
ClientPrefix,
|
||||
@@ -73,7 +73,7 @@ import {
|
||||
Upload,
|
||||
UploadOpts,
|
||||
UploadResponse,
|
||||
} from "./http-api";
|
||||
} from "./http-api/index.ts";
|
||||
import {
|
||||
Crypto,
|
||||
CryptoEvent,
|
||||
@@ -83,14 +83,14 @@ import {
|
||||
ICryptoCallbacks,
|
||||
IRoomKeyRequestBody,
|
||||
isCryptoAvailable,
|
||||
} from "./crypto";
|
||||
import { DeviceInfo } from "./crypto/deviceinfo";
|
||||
import { decodeRecoveryKey } from "./crypto/recoverykey";
|
||||
import { keyFromAuthData } from "./crypto/key_passphrase";
|
||||
import { User, UserEvent, UserEventHandlerMap } from "./models/user";
|
||||
import { getHttpUriForMxc } from "./content-repo";
|
||||
import { SearchResult } from "./models/search-result";
|
||||
import { DEHYDRATION_ALGORITHM, IDehydratedDevice, IDehydratedDeviceKeyInfo } from "./crypto/dehydration";
|
||||
} from "./crypto/index.ts";
|
||||
import { DeviceInfo } from "./crypto/deviceinfo.ts";
|
||||
import { decodeRecoveryKey } from "./crypto/recoverykey.ts";
|
||||
import { keyFromAuthData } from "./crypto/key_passphrase.ts";
|
||||
import { User, UserEvent, UserEventHandlerMap } from "./models/user.ts";
|
||||
import { getHttpUriForMxc } from "./content-repo.ts";
|
||||
import { SearchResult } from "./models/search-result.ts";
|
||||
import { DEHYDRATION_ALGORITHM, IDehydratedDevice, IDehydratedDeviceKeyInfo } from "./crypto/dehydration.ts";
|
||||
import {
|
||||
IKeyBackupInfo,
|
||||
IKeyBackupPrepareOpts,
|
||||
@@ -98,22 +98,23 @@ import {
|
||||
IKeyBackupRestoreResult,
|
||||
IKeyBackupRoomSessions,
|
||||
IKeyBackupSession,
|
||||
} from "./crypto/keybackup";
|
||||
import { IIdentityServerProvider } from "./@types/IIdentityServerProvider";
|
||||
import { MatrixScheduler } from "./scheduler";
|
||||
import { BeaconEvent, BeaconEventHandlerMap } from "./models/beacon";
|
||||
import { AuthDict } from "./interactive-auth";
|
||||
import { IMinimalEvent, IRoomEvent, IStateEvent } from "./sync-accumulator";
|
||||
import { CrossSigningKey, ICreateSecretStorageOpts, IEncryptedEventInfo, IRecoveryKey } from "./crypto/api";
|
||||
import { EventTimelineSet } from "./models/event-timeline-set";
|
||||
import { VerificationRequest } from "./crypto/verification/request/VerificationRequest";
|
||||
import { VerificationBase as Verification } from "./crypto/verification/Base";
|
||||
import * as ContentHelpers from "./content-helpers";
|
||||
import { CrossSigningInfo, DeviceTrustLevel, ICacheCallbacks, UserTrustLevel } from "./crypto/CrossSigning";
|
||||
import { NotificationCountType, Room, RoomEvent, RoomEventHandlerMap, RoomNameState } from "./models/room";
|
||||
import { RoomMemberEvent, RoomMemberEventHandlerMap } from "./models/room-member";
|
||||
import { IPowerLevelsContent, RoomStateEvent, RoomStateEventHandlerMap } from "./models/room-state";
|
||||
} from "./crypto/keybackup.ts";
|
||||
import { IIdentityServerProvider } from "./@types/IIdentityServerProvider.ts";
|
||||
import { MatrixScheduler } from "./scheduler.ts";
|
||||
import { BeaconEvent, BeaconEventHandlerMap } from "./models/beacon.ts";
|
||||
import { AuthDict } from "./interactive-auth.ts";
|
||||
import { IMinimalEvent, IRoomEvent, IStateEvent } from "./sync-accumulator.ts";
|
||||
import { CrossSigningKey, ICreateSecretStorageOpts, IEncryptedEventInfo, IRecoveryKey } from "./crypto/api.ts";
|
||||
import { EventTimelineSet } from "./models/event-timeline-set.ts";
|
||||
import { VerificationRequest } from "./crypto/verification/request/VerificationRequest.ts";
|
||||
import { VerificationBase as Verification } from "./crypto/verification/Base.ts";
|
||||
import * as ContentHelpers from "./content-helpers.ts";
|
||||
import { CrossSigningInfo, DeviceTrustLevel, ICacheCallbacks, UserTrustLevel } from "./crypto/CrossSigning.ts";
|
||||
import { NotificationCountType, Room, RoomEvent, RoomEventHandlerMap, RoomNameState } from "./models/room.ts";
|
||||
import { RoomMemberEvent, RoomMemberEventHandlerMap } from "./models/room-member.ts";
|
||||
import { IPowerLevelsContent, RoomStateEvent, RoomStateEventHandlerMap } from "./models/room-state.ts";
|
||||
import {
|
||||
DelayedEventInfo,
|
||||
IAddThreePidOnlyBody,
|
||||
IBindThreePidBody,
|
||||
IContextResponse,
|
||||
@@ -134,7 +135,10 @@ import {
|
||||
IStatusResponse,
|
||||
ITagsResponse,
|
||||
KnockRoomOpts,
|
||||
} from "./@types/requests";
|
||||
SendDelayedEventRequestOpts,
|
||||
SendDelayedEventResponse,
|
||||
UpdateDelayedEventAction,
|
||||
} from "./@types/requests.ts";
|
||||
import {
|
||||
EventType,
|
||||
LOCAL_NOTIFICATION_SETTINGS_PREFIX,
|
||||
@@ -149,15 +153,22 @@ import {
|
||||
UNSTABLE_MSC3088_ENABLED,
|
||||
UNSTABLE_MSC3088_PURPOSE,
|
||||
UNSTABLE_MSC3089_TREE_SUBTYPE,
|
||||
} from "./@types/event";
|
||||
import { GuestAccess, HistoryVisibility, IdServerUnbindResult, JoinRule, Preset, Visibility } from "./@types/partials";
|
||||
import { EventMapper, eventMapperFor, MapperOpts } from "./event-mapper";
|
||||
import { randomString } from "./randomstring";
|
||||
import { BackupManager, IKeyBackup, IKeyBackupCheck, IPreparedKeyBackupVersion, TrustInfo } from "./crypto/backup";
|
||||
import { DEFAULT_TREE_POWER_LEVELS_TEMPLATE, MSC3089TreeSpace } from "./models/MSC3089TreeSpace";
|
||||
import { ISignatures } from "./@types/signed";
|
||||
import { IStore } from "./store";
|
||||
import { ISecretRequest } from "./crypto/SecretStorage";
|
||||
} from "./@types/event.ts";
|
||||
import {
|
||||
GuestAccess,
|
||||
HistoryVisibility,
|
||||
IdServerUnbindResult,
|
||||
JoinRule,
|
||||
Preset,
|
||||
Visibility,
|
||||
} from "./@types/partials.ts";
|
||||
import { EventMapper, eventMapperFor, MapperOpts } from "./event-mapper.ts";
|
||||
import { randomString } from "./randomstring.ts";
|
||||
import { BackupManager, IKeyBackup, IKeyBackupCheck, IPreparedKeyBackupVersion, TrustInfo } from "./crypto/backup.ts";
|
||||
import { DEFAULT_TREE_POWER_LEVELS_TEMPLATE, MSC3089TreeSpace } from "./models/MSC3089TreeSpace.ts";
|
||||
import { ISignatures } from "./@types/signed.ts";
|
||||
import { IStore } from "./store/index.ts";
|
||||
import { ISecretRequest } from "./crypto/SecretStorage.ts";
|
||||
import {
|
||||
IEventWithRoomId,
|
||||
ISearchRequestBody,
|
||||
@@ -165,9 +176,9 @@ import {
|
||||
ISearchResults,
|
||||
IStateEventWithRoomId,
|
||||
SearchOrderBy,
|
||||
} from "./@types/search";
|
||||
import { ISynapseAdminDeactivateResponse, ISynapseAdminWhoisResponse } from "./@types/synapse";
|
||||
import { IHierarchyRoom } from "./@types/spaces";
|
||||
} from "./@types/search.ts";
|
||||
import { ISynapseAdminDeactivateResponse, ISynapseAdminWhoisResponse } from "./@types/synapse.ts";
|
||||
import { IHierarchyRoom } from "./@types/spaces.ts";
|
||||
import {
|
||||
IPusher,
|
||||
IPusherRequest,
|
||||
@@ -177,11 +188,11 @@ import {
|
||||
PushRuleActionName,
|
||||
PushRuleKind,
|
||||
RuleId,
|
||||
} from "./@types/PushRules";
|
||||
import { IThreepid } from "./@types/threepids";
|
||||
import { CryptoStore, OutgoingRoomKeyRequest } from "./crypto/store/base";
|
||||
import { GroupCall, GroupCallIntent, GroupCallType, IGroupCallDataChannelOptions } from "./webrtc/groupCall";
|
||||
import { MediaHandler } from "./webrtc/mediaHandler";
|
||||
} from "./@types/PushRules.ts";
|
||||
import { IThreepid } from "./@types/threepids.ts";
|
||||
import { CryptoStore, OutgoingRoomKeyRequest } from "./crypto/store/base.ts";
|
||||
import { GroupCall, GroupCallIntent, GroupCallType, IGroupCallDataChannelOptions } from "./webrtc/groupCall.ts";
|
||||
import { MediaHandler } from "./webrtc/mediaHandler.ts";
|
||||
import {
|
||||
ILoginFlowsResponse,
|
||||
IRefreshTokenResponse,
|
||||
@@ -189,11 +200,11 @@ import {
|
||||
LoginResponse,
|
||||
LoginTokenPostResponse,
|
||||
SSOAction,
|
||||
} from "./@types/auth";
|
||||
import { TypedEventEmitter } from "./models/typed-event-emitter";
|
||||
import { MAIN_ROOM_TIMELINE, ReceiptType } from "./@types/read_receipts";
|
||||
import { MSC3575SlidingSyncRequest, MSC3575SlidingSyncResponse, SlidingSync } from "./sliding-sync";
|
||||
import { SlidingSyncSdk } from "./sliding-sync-sdk";
|
||||
} from "./@types/auth.ts";
|
||||
import { TypedEventEmitter } from "./models/typed-event-emitter.ts";
|
||||
import { MAIN_ROOM_TIMELINE, ReceiptType } from "./@types/read_receipts.ts";
|
||||
import { MSC3575SlidingSyncRequest, MSC3575SlidingSyncResponse, SlidingSync } from "./sliding-sync.ts";
|
||||
import { SlidingSyncSdk } from "./sliding-sync-sdk.ts";
|
||||
import {
|
||||
determineFeatureSupport,
|
||||
FeatureSupport,
|
||||
@@ -201,32 +212,33 @@ import {
|
||||
THREAD_RELATION_TYPE,
|
||||
ThreadFilterType,
|
||||
threadFilterTypeToFilter,
|
||||
} from "./models/thread";
|
||||
import { M_BEACON_INFO, MBeaconInfoEventContent } from "./@types/beacon";
|
||||
import { NamespacedValue, UnstableValue } from "./NamespacedValue";
|
||||
import { ToDeviceMessageQueue } from "./ToDeviceMessageQueue";
|
||||
import { ToDeviceBatch } from "./models/ToDeviceMessage";
|
||||
import { IgnoredInvites } from "./models/invites-ignorer";
|
||||
import { UIARequest, UIAResponse } from "./@types/uia";
|
||||
import { LocalNotificationSettings } from "./@types/local_notifications";
|
||||
import { buildFeatureSupportMap, Feature, ServerSupport } from "./feature";
|
||||
import { BackupDecryptor, CryptoBackend } from "./common-crypto/CryptoBackend";
|
||||
import { RUST_SDK_STORE_PREFIX } from "./rust-crypto/constants";
|
||||
import { BootstrapCrossSigningOpts, CrossSigningKeyInfo, CryptoApi, ImportRoomKeysOpts } from "./crypto-api";
|
||||
import { DeviceInfoMap } from "./crypto/DeviceList";
|
||||
} from "./models/thread.ts";
|
||||
import { M_BEACON_INFO, MBeaconInfoEventContent } from "./@types/beacon.ts";
|
||||
import { NamespacedValue, UnstableValue } from "./NamespacedValue.ts";
|
||||
import { ToDeviceMessageQueue } from "./ToDeviceMessageQueue.ts";
|
||||
import { ToDeviceBatch } from "./models/ToDeviceMessage.ts";
|
||||
import { IgnoredInvites } from "./models/invites-ignorer.ts";
|
||||
import { UIARequest, UIAResponse } from "./@types/uia.ts";
|
||||
import { LocalNotificationSettings } from "./@types/local_notifications.ts";
|
||||
import { buildFeatureSupportMap, Feature, ServerSupport } from "./feature.ts";
|
||||
import { BackupDecryptor, CryptoBackend } from "./common-crypto/CryptoBackend.ts";
|
||||
import { RUST_SDK_STORE_PREFIX } from "./rust-crypto/constants.ts";
|
||||
import { BootstrapCrossSigningOpts, CrossSigningKeyInfo, CryptoApi, ImportRoomKeysOpts } from "./crypto-api/index.ts";
|
||||
import { DeviceInfoMap } from "./crypto/DeviceList.ts";
|
||||
import {
|
||||
AddSecretStorageKeyOpts,
|
||||
SecretStorageKeyDescription,
|
||||
ServerSideSecretStorage,
|
||||
ServerSideSecretStorageImpl,
|
||||
} from "./secret-storage";
|
||||
import { RegisterRequest, RegisterResponse } from "./@types/registration";
|
||||
import { MatrixRTCSessionManager } from "./matrixrtc/MatrixRTCSessionManager";
|
||||
import { getRelationsThreadFilter } from "./thread-utils";
|
||||
import { KnownMembership, Membership } from "./@types/membership";
|
||||
import { RoomMessageEventContent, StickerEventContent } from "./@types/events";
|
||||
import { ImageInfo } from "./@types/media";
|
||||
import { Capabilities, ServerCapabilities } from "./serverCapabilities";
|
||||
} from "./secret-storage.ts";
|
||||
import { RegisterRequest, RegisterResponse } from "./@types/registration.ts";
|
||||
import { MatrixRTCSessionManager } from "./matrixrtc/MatrixRTCSessionManager.ts";
|
||||
import { getRelationsThreadFilter } from "./thread-utils.ts";
|
||||
import { KnownMembership, Membership } from "./@types/membership.ts";
|
||||
import { RoomMessageEventContent, StickerEventContent } from "./@types/events.ts";
|
||||
import { ImageInfo } from "./@types/media.ts";
|
||||
import { Capabilities, ServerCapabilities } from "./serverCapabilities.ts";
|
||||
import { sha256 } from "./digest.ts";
|
||||
|
||||
export type Store = IStore;
|
||||
|
||||
@@ -324,10 +336,13 @@ export interface ICreateClientOpts {
|
||||
localTimeoutMs?: number;
|
||||
|
||||
/**
|
||||
* Set to true to use
|
||||
* Authorization header instead of query param to send the access token to the server.
|
||||
* Set to false to send the access token to the server via a query parameter rather
|
||||
* than the Authorization HTTP header.
|
||||
*
|
||||
* Default false.
|
||||
* Note that as of v1.11 of the Matrix spec, sending the access token via a query
|
||||
* is deprecated.
|
||||
*
|
||||
* Default true.
|
||||
*/
|
||||
useAuthorizationHeader?: boolean;
|
||||
|
||||
@@ -527,6 +542,8 @@ export const UNSTABLE_MSC2666_SHARED_ROOMS = "uk.half-shot.msc2666";
|
||||
export const UNSTABLE_MSC2666_MUTUAL_ROOMS = "uk.half-shot.msc2666.mutual_rooms";
|
||||
export const UNSTABLE_MSC2666_QUERY_MUTUAL_ROOMS = "uk.half-shot.msc2666.query_mutual_rooms";
|
||||
|
||||
export const UNSTABLE_MSC4140_DELAYED_EVENTS = "org.matrix.msc4140";
|
||||
|
||||
enum CrossSigningKeyType {
|
||||
MasterKey = "master_key",
|
||||
SelfSigningKey = "self_signing_key",
|
||||
@@ -4570,12 +4587,19 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
threadId = threadIdOrEventType;
|
||||
}
|
||||
|
||||
// If we expect that an event is part of a thread but is missing the relation
|
||||
// we need to add it manually, as well as the reply fallback
|
||||
if (threadId && !content!["m.relates_to"]?.rel_type) {
|
||||
const isReply = !!content!["m.relates_to"]?.["m.in_reply_to"];
|
||||
content!["m.relates_to"] = {
|
||||
...content!["m.relates_to"],
|
||||
this.addThreadRelationIfNeeded(content, threadId, roomId);
|
||||
return this.sendCompleteEvent(roomId, threadId, { type: eventType, content }, txnId);
|
||||
}
|
||||
|
||||
/**
|
||||
* If we expect that an event is part of a thread but is missing the relation
|
||||
* we need to add it manually, as well as the reply fallback
|
||||
*/
|
||||
private addThreadRelationIfNeeded(content: IContent, threadId: string | null, roomId: string): void {
|
||||
if (threadId && !content["m.relates_to"]?.rel_type) {
|
||||
const isReply = !!content["m.relates_to"]?.["m.in_reply_to"];
|
||||
content["m.relates_to"] = {
|
||||
...content["m.relates_to"],
|
||||
rel_type: THREAD_RELATION_TYPE.name,
|
||||
event_id: threadId,
|
||||
// Set is_falling_back to true unless this is actually intended to be a reply
|
||||
@@ -4583,7 +4607,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
};
|
||||
const thread = this.getRoom(roomId)?.getThread(threadId);
|
||||
if (thread && !isReply) {
|
||||
content!["m.relates_to"]["m.in_reply_to"] = {
|
||||
content["m.relates_to"]["m.in_reply_to"] = {
|
||||
event_id:
|
||||
thread
|
||||
.lastReply((ev: MatrixEvent) => {
|
||||
@@ -4593,8 +4617,6 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return this.sendCompleteEvent(roomId, threadId, { type: eventType, content }, txnId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4608,7 +4630,38 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
threadId: string | null,
|
||||
eventObject: Partial<IEvent>,
|
||||
txnId?: string,
|
||||
): Promise<ISendEventResponse> {
|
||||
): Promise<ISendEventResponse>;
|
||||
/**
|
||||
* Sends a delayed event (MSC4140).
|
||||
* @param eventObject - An object with the partial structure of an event, to which event_id, user_id, room_id and origin_server_ts will be added.
|
||||
* @param delayOpts - Properties of the delay for this event.
|
||||
* @param txnId - Optional.
|
||||
* @returns Promise which resolves: to an empty object `{}`
|
||||
* @returns Rejects: with an error response.
|
||||
*/
|
||||
private sendCompleteEvent(
|
||||
roomId: string,
|
||||
threadId: string | null,
|
||||
eventObject: Partial<IEvent>,
|
||||
delayOpts: SendDelayedEventRequestOpts,
|
||||
txnId?: string,
|
||||
): Promise<SendDelayedEventResponse>;
|
||||
private sendCompleteEvent(
|
||||
roomId: string,
|
||||
threadId: string | null,
|
||||
eventObject: Partial<IEvent>,
|
||||
delayOptsOrTxnId?: SendDelayedEventRequestOpts | string,
|
||||
txnIdOrVoid?: string,
|
||||
): Promise<ISendEventResponse | SendDelayedEventResponse> {
|
||||
let delayOpts: SendDelayedEventRequestOpts | undefined;
|
||||
let txnId: string | undefined;
|
||||
if (typeof delayOptsOrTxnId === "string") {
|
||||
txnId = delayOptsOrTxnId;
|
||||
} else {
|
||||
delayOpts = delayOptsOrTxnId;
|
||||
txnId = txnIdOrVoid;
|
||||
}
|
||||
|
||||
if (!txnId) {
|
||||
txnId = this.makeTxnId();
|
||||
}
|
||||
@@ -4631,9 +4684,11 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
localEvent.setThread(thread);
|
||||
}
|
||||
|
||||
// set up re-emitter for this new event - this is normally the job of EventMapper but we don't use it here
|
||||
this.reEmitter.reEmit(localEvent, [MatrixEventEvent.Replaced, MatrixEventEvent.VisibilityChange]);
|
||||
room?.reEmitter.reEmit(localEvent, [MatrixEventEvent.BeforeRedaction]);
|
||||
if (!delayOpts) {
|
||||
// set up re-emitter for this new event - this is normally the job of EventMapper but we don't use it here
|
||||
this.reEmitter.reEmit(localEvent, [MatrixEventEvent.Replaced, MatrixEventEvent.VisibilityChange]);
|
||||
room?.reEmitter.reEmit(localEvent, [MatrixEventEvent.BeforeRedaction]);
|
||||
}
|
||||
|
||||
// if this is a relation or redaction of an event
|
||||
// that hasn't been sent yet (e.g. with a local id starting with a ~)
|
||||
@@ -4648,29 +4703,56 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
}
|
||||
|
||||
const type = localEvent.getType();
|
||||
this.logger.debug(`sendEvent of type ${type} in ${roomId} with txnId ${txnId}`);
|
||||
this.logger.debug(
|
||||
`sendEvent of type ${type} in ${roomId} with txnId ${txnId}${delayOpts ? " (delayed event)" : ""}`,
|
||||
);
|
||||
|
||||
localEvent.setTxnId(txnId);
|
||||
localEvent.setStatus(EventStatus.SENDING);
|
||||
|
||||
// add this event immediately to the local store as 'sending'.
|
||||
room?.addPendingEvent(localEvent, txnId);
|
||||
// TODO: separate store for delayed events?
|
||||
if (!delayOpts) {
|
||||
// add this event immediately to the local store as 'sending'.
|
||||
room?.addPendingEvent(localEvent, txnId);
|
||||
|
||||
// addPendingEvent can change the state to NOT_SENT if it believes
|
||||
// that there's other events that have failed. We won't bother to
|
||||
// try sending the event if the state has changed as such.
|
||||
if (localEvent.status === EventStatus.NOT_SENT) {
|
||||
return Promise.reject(new Error("Event blocked by other events not yet sent"));
|
||||
// addPendingEvent can change the state to NOT_SENT if it believes
|
||||
// that there's other events that have failed. We won't bother to
|
||||
// try sending the event if the state has changed as such.
|
||||
if (localEvent.status === EventStatus.NOT_SENT) {
|
||||
return Promise.reject(new Error("Event blocked by other events not yet sent"));
|
||||
}
|
||||
|
||||
return this.encryptAndSendEvent(room, localEvent);
|
||||
} else {
|
||||
return this.encryptAndSendEvent(room, localEvent, delayOpts);
|
||||
}
|
||||
|
||||
return this.encryptAndSendEvent(room, localEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* encrypts the event if necessary; adds the event to the queue, or sends it; marks the event as sent/unsent
|
||||
* @returns returns a promise which resolves with the result of the send request
|
||||
*/
|
||||
protected async encryptAndSendEvent(room: Room | null, event: MatrixEvent): Promise<ISendEventResponse> {
|
||||
protected async encryptAndSendEvent(room: Room | null, event: MatrixEvent): Promise<ISendEventResponse>;
|
||||
/**
|
||||
* Simply sends a delayed event without encrypting it.
|
||||
* TODO: Allow encrypted delayed events, and encrypt them properly
|
||||
* @param delayOpts - Properties of the delay for this event.
|
||||
* @returns returns a promise which resolves with the result of the delayed send request
|
||||
*/
|
||||
protected async encryptAndSendEvent(
|
||||
room: Room | null,
|
||||
event: MatrixEvent,
|
||||
delayOpts: SendDelayedEventRequestOpts,
|
||||
): Promise<SendDelayedEventResponse>;
|
||||
protected async encryptAndSendEvent(
|
||||
room: Room | null,
|
||||
event: MatrixEvent,
|
||||
delayOpts?: SendDelayedEventRequestOpts,
|
||||
): Promise<ISendEventResponse | SendDelayedEventResponse> {
|
||||
if (delayOpts) {
|
||||
return this.sendEventHttpRequest(event, delayOpts);
|
||||
}
|
||||
|
||||
try {
|
||||
let cancelled: boolean;
|
||||
this.eventsBeingEncrypted.add(event.getId()!);
|
||||
@@ -4821,7 +4903,15 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
}
|
||||
}
|
||||
|
||||
private sendEventHttpRequest(event: MatrixEvent): Promise<ISendEventResponse> {
|
||||
private sendEventHttpRequest(event: MatrixEvent): Promise<ISendEventResponse>;
|
||||
private sendEventHttpRequest(
|
||||
event: MatrixEvent,
|
||||
delayOpts: SendDelayedEventRequestOpts,
|
||||
): Promise<SendDelayedEventResponse>;
|
||||
private sendEventHttpRequest(
|
||||
event: MatrixEvent,
|
||||
delayOpts?: SendDelayedEventRequestOpts,
|
||||
): Promise<ISendEventResponse | SendDelayedEventResponse> {
|
||||
let txnId = event.getTxnId();
|
||||
if (!txnId) {
|
||||
txnId = this.makeTxnId();
|
||||
@@ -4853,12 +4943,20 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
path = utils.encodeUri("/rooms/$roomId/send/$eventType/$txnId", pathParams);
|
||||
}
|
||||
|
||||
return this.http
|
||||
.authedRequest<ISendEventResponse>(Method.Put, path, undefined, event.getWireContent())
|
||||
.then((res) => {
|
||||
const content = event.getWireContent();
|
||||
if (!delayOpts) {
|
||||
return this.http.authedRequest<ISendEventResponse>(Method.Put, path, undefined, content).then((res) => {
|
||||
this.logger.debug(`Event sent to ${event.getRoomId()} with event id ${res.event_id}`);
|
||||
return res;
|
||||
});
|
||||
} else {
|
||||
return this.http.authedRequest<SendDelayedEventResponse>(
|
||||
Method.Put,
|
||||
path,
|
||||
getUnstableDelayQueryOpts(delayOpts),
|
||||
content,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -5188,6 +5286,101 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
return this.sendMessage(roomId, threadId, content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a delayed timeline event.
|
||||
*
|
||||
* Note: This endpoint is unstable, and can throw an `Error`.
|
||||
* Check progress on [MSC4140](https://github.com/matrix-org/matrix-spec-proposals/pull/4140) for more details.
|
||||
*/
|
||||
// eslint-disable-next-line
|
||||
public async _unstable_sendDelayedEvent<K extends keyof TimelineEvents>(
|
||||
roomId: string,
|
||||
delayOpts: SendDelayedEventRequestOpts,
|
||||
threadId: string | null,
|
||||
eventType: K,
|
||||
content: TimelineEvents[K],
|
||||
txnId?: string,
|
||||
): Promise<SendDelayedEventResponse> {
|
||||
if (!(await this.doesServerSupportUnstableFeature(UNSTABLE_MSC4140_DELAYED_EVENTS))) {
|
||||
throw Error("Server does not support the delayed events API");
|
||||
}
|
||||
|
||||
this.addThreadRelationIfNeeded(content, threadId, roomId);
|
||||
return this.sendCompleteEvent(roomId, threadId, { type: eventType, content }, delayOpts, txnId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a delayed state event.
|
||||
*
|
||||
* Note: This endpoint is unstable, and can throw an `Error`.
|
||||
* Check progress on [MSC4140](https://github.com/matrix-org/matrix-spec-proposals/pull/4140) for more details.
|
||||
*/
|
||||
// eslint-disable-next-line
|
||||
public async _unstable_sendDelayedStateEvent<K extends keyof StateEvents>(
|
||||
roomId: string,
|
||||
delayOpts: SendDelayedEventRequestOpts,
|
||||
eventType: K,
|
||||
content: StateEvents[K],
|
||||
stateKey = "",
|
||||
opts: IRequestOpts = {},
|
||||
): Promise<SendDelayedEventResponse> {
|
||||
if (!(await this.doesServerSupportUnstableFeature(UNSTABLE_MSC4140_DELAYED_EVENTS))) {
|
||||
throw Error("Server does not support the delayed events API");
|
||||
}
|
||||
|
||||
const pathParams = {
|
||||
$roomId: roomId,
|
||||
$eventType: eventType,
|
||||
$stateKey: stateKey,
|
||||
};
|
||||
let path = utils.encodeUri("/rooms/$roomId/state/$eventType", pathParams);
|
||||
if (stateKey !== undefined) {
|
||||
path = utils.encodeUri(path + "/$stateKey", pathParams);
|
||||
}
|
||||
return this.http.authedRequest(Method.Put, path, getUnstableDelayQueryOpts(delayOpts), content as Body, opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all pending delayed events for the calling user.
|
||||
*
|
||||
* Note: This endpoint is unstable, and can throw an `Error`.
|
||||
* Check progress on [MSC4140](https://github.com/matrix-org/matrix-spec-proposals/pull/4140) for more details.
|
||||
*/
|
||||
// eslint-disable-next-line
|
||||
public async _unstable_getDelayedEvents(fromToken?: string): Promise<DelayedEventInfo> {
|
||||
if (!(await this.doesServerSupportUnstableFeature(UNSTABLE_MSC4140_DELAYED_EVENTS))) {
|
||||
throw Error("Server does not support the delayed events API");
|
||||
}
|
||||
|
||||
const queryDict = fromToken ? { from: fromToken } : undefined;
|
||||
return await this.http.authedRequest(Method.Get, "/delayed_events", queryDict, undefined, {
|
||||
prefix: `${ClientPrefix.Unstable}/${UNSTABLE_MSC4140_DELAYED_EVENTS}`,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage a delayed event associated with the given delay_id.
|
||||
*
|
||||
* Note: This endpoint is unstable, and can throw an `Error`.
|
||||
* Check progress on [MSC4140](https://github.com/matrix-org/matrix-spec-proposals/pull/4140) for more details.
|
||||
*/
|
||||
// eslint-disable-next-line
|
||||
public async _unstable_updateDelayedEvent(delayId: string, action: UpdateDelayedEventAction): Promise<{}> {
|
||||
if (!(await this.doesServerSupportUnstableFeature(UNSTABLE_MSC4140_DELAYED_EVENTS))) {
|
||||
throw Error("Server does not support the delayed events API");
|
||||
}
|
||||
|
||||
const path = utils.encodeUri("/delayed_events/$delayId", {
|
||||
$delayId: delayId,
|
||||
});
|
||||
const data = {
|
||||
action,
|
||||
};
|
||||
return await this.http.authedRequest(Method.Post, path, undefined, data, {
|
||||
prefix: `${ClientPrefix.Unstable}/${UNSTABLE_MSC4140_DELAYED_EVENTS}`,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a receipt.
|
||||
* @param event - The event being acknowledged
|
||||
@@ -5401,10 +5594,15 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
|
||||
private findPredecessorRooms(room: Room, verifyLinks: boolean, msc3946ProcessDynamicPredecessor: boolean): Room[] {
|
||||
const ret: Room[] = [];
|
||||
const seenRoomIDs = new Set<string>([room.roomId]);
|
||||
|
||||
// Work backwards from newer to older rooms
|
||||
let predecessorRoomId = room.findPredecessor(msc3946ProcessDynamicPredecessor)?.roomId;
|
||||
while (predecessorRoomId !== null) {
|
||||
if (predecessorRoomId) {
|
||||
if (seenRoomIDs.has(predecessorRoomId)) break;
|
||||
seenRoomIDs.add(predecessorRoomId);
|
||||
}
|
||||
const predecessorRoom = this.getRoom(predecessorRoomId);
|
||||
if (predecessorRoom === null) {
|
||||
break;
|
||||
@@ -9299,20 +9497,19 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
|
||||
// When picking an algorithm, we pick the hashed over no hashes
|
||||
if (hashes["algorithms"].includes("sha256")) {
|
||||
// Abuse the olm hashing
|
||||
const olmutil = new global.Olm.Utility();
|
||||
params["addresses"] = addressPairs.map((p) => {
|
||||
const addr = p[0].toLowerCase(); // lowercase to get consistent hashes
|
||||
const med = p[1].toLowerCase();
|
||||
const hashed = olmutil
|
||||
.sha256(`${addr} ${med} ${params["pepper"]}`)
|
||||
.replace(/\+/g, "-")
|
||||
.replace(/\//g, "_"); // URL-safe base64
|
||||
// Map the hash to a known (case-sensitive) address. We use the case
|
||||
// sensitive version because the caller might be expecting that.
|
||||
localMapping[hashed] = p[0];
|
||||
return hashed;
|
||||
});
|
||||
params["addresses"] = await Promise.all(
|
||||
addressPairs.map(async (p) => {
|
||||
const addr = p[0].toLowerCase(); // lowercase to get consistent hashes
|
||||
const med = p[1].toLowerCase();
|
||||
const hashBuffer = await sha256(`${addr} ${med} ${params["pepper"]}`);
|
||||
const hashed = encodeUnpaddedBase64Url(hashBuffer);
|
||||
|
||||
// Map the hash to a known (case-sensitive) address. We use the case
|
||||
// sensitive version because the caller might be expecting that.
|
||||
localMapping[hashed] = p[0];
|
||||
return hashed;
|
||||
}),
|
||||
);
|
||||
params["algorithm"] = "sha256";
|
||||
} else if (hashes["algorithms"].includes("none")) {
|
||||
params["addresses"] = addressPairs.map((p) => {
|
||||
@@ -9889,6 +10086,12 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
}
|
||||
}
|
||||
|
||||
function getUnstableDelayQueryOpts(delayOpts: SendDelayedEventRequestOpts): QueryDict {
|
||||
return Object.fromEntries(
|
||||
Object.entries(delayOpts).map(([k, v]) => [`${UNSTABLE_MSC4140_DELAYED_EVENTS}.${k}`, v]),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* recalculates an accurate notifications count on event decryption.
|
||||
* Servers do not have enough knowledge about encrypted events to calculate an
|
||||
|
||||
@@ -14,14 +14,14 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import type { IDeviceLists, IToDeviceEvent } from "../sync-accumulator";
|
||||
import { IClearEvent, MatrixEvent } from "../models/event";
|
||||
import { Room } from "../models/room";
|
||||
import { CryptoApi, DecryptionFailureCode, ImportRoomKeysOpts } from "../crypto-api";
|
||||
import { CrossSigningInfo, UserTrustLevel } from "../crypto/CrossSigning";
|
||||
import { IEncryptedEventInfo } from "../crypto/api";
|
||||
import { KeyBackupInfo, KeyBackupSession } from "../crypto-api/keybackup";
|
||||
import { IMegolmSessionData } from "../@types/crypto";
|
||||
import type { IDeviceLists, IToDeviceEvent } from "../sync-accumulator.ts";
|
||||
import { IClearEvent, MatrixEvent } from "../models/event.ts";
|
||||
import { Room } from "../models/room.ts";
|
||||
import { CryptoApi, DecryptionFailureCode, ImportRoomKeysOpts } from "../crypto-api/index.ts";
|
||||
import { CrossSigningInfo, UserTrustLevel } from "../crypto/CrossSigning.ts";
|
||||
import { IEncryptedEventInfo } from "../crypto/api.ts";
|
||||
import { KeyBackupInfo, KeyBackupSession } from "../crypto-api/keybackup.ts";
|
||||
import { IMegolmSessionData } from "../@types/crypto.ts";
|
||||
|
||||
/**
|
||||
* Common interface for the crypto implementations
|
||||
|
||||
@@ -14,10 +14,10 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { MBeaconEventContent, MBeaconInfoContent, MBeaconInfoEventContent } from "./@types/beacon";
|
||||
import { MsgType } from "./@types/event";
|
||||
import { M_TEXT, REFERENCE_RELATION } from "./@types/extensible_events";
|
||||
import { isProvided } from "./extensible_events_v1/utilities";
|
||||
import { MBeaconEventContent, MBeaconInfoContent, MBeaconInfoEventContent } from "./@types/beacon.ts";
|
||||
import { MsgType } from "./@types/event.ts";
|
||||
import { M_TEXT, REFERENCE_RELATION } from "./@types/extensible_events.ts";
|
||||
import { isProvided } from "./extensible_events_v1/utilities.ts";
|
||||
import {
|
||||
M_ASSET,
|
||||
LocationAssetType,
|
||||
@@ -28,9 +28,9 @@ import {
|
||||
MLocationContent,
|
||||
MAssetContent,
|
||||
LegacyLocationEventContent,
|
||||
} from "./@types/location";
|
||||
import { MRoomTopicEventContent, MTopicContent, M_TOPIC } from "./@types/topic";
|
||||
import { RoomMessageEventContent } from "./@types/events";
|
||||
} from "./@types/location.ts";
|
||||
import { MRoomTopicEventContent, MTopicContent, M_TOPIC } from "./@types/topic.ts";
|
||||
import { RoomMessageEventContent } from "./@types/events.ts";
|
||||
|
||||
/**
|
||||
* Generates the content for a HTML Message event
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { encodeParams } from "./utils";
|
||||
import { encodeParams } from "./utils.ts";
|
||||
|
||||
/**
|
||||
* Get the HTTP URL for an MXC URI.
|
||||
|
||||
+29
-13
@@ -15,15 +15,15 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import type { SecretsBundle } from "@matrix-org/matrix-sdk-crypto-wasm";
|
||||
import type { IMegolmSessionData } from "../@types/crypto";
|
||||
import { Room } from "../models/room";
|
||||
import { DeviceMap } from "../models/device";
|
||||
import { UIAuthCallback } from "../interactive-auth";
|
||||
import { PassphraseInfo, SecretStorageCallbacks, SecretStorageKeyDescription } from "../secret-storage";
|
||||
import { VerificationRequest } from "./verification";
|
||||
import { BackupTrustInfo, KeyBackupCheck, KeyBackupInfo } from "./keybackup";
|
||||
import { ISignatures } from "../@types/signed";
|
||||
import { MatrixEvent } from "../models/event";
|
||||
import type { IMegolmSessionData } from "../@types/crypto.ts";
|
||||
import { Room } from "../models/room.ts";
|
||||
import { DeviceMap } from "../models/device.ts";
|
||||
import { UIAuthCallback } from "../interactive-auth.ts";
|
||||
import { PassphraseInfo, SecretStorageCallbacks, SecretStorageKeyDescription } from "../secret-storage.ts";
|
||||
import { VerificationRequest } from "./verification.ts";
|
||||
import { BackupTrustInfo, KeyBackupCheck, KeyBackupInfo } from "./keybackup.ts";
|
||||
import { ISignatures } from "../@types/signed.ts";
|
||||
import { MatrixEvent } from "../models/event.ts";
|
||||
|
||||
/**
|
||||
* Public interface to the cryptography parts of the js-sdk
|
||||
@@ -557,6 +557,12 @@ export enum DecryptionFailureCode {
|
||||
/** Message was encrypted with a Megolm session whose keys have not been shared with us. */
|
||||
MEGOLM_UNKNOWN_INBOUND_SESSION_ID = "MEGOLM_UNKNOWN_INBOUND_SESSION_ID",
|
||||
|
||||
/** A special case of {@link MEGOLM_UNKNOWN_INBOUND_SESSION_ID}: the sender has told us it is withholding the key. */
|
||||
MEGOLM_KEY_WITHHELD = "MEGOLM_KEY_WITHHELD",
|
||||
|
||||
/** A special case of {@link MEGOLM_KEY_WITHHELD}: the sender has told us it is withholding the key, because the current device is unverified. */
|
||||
MEGOLM_KEY_WITHHELD_FOR_UNVERIFIED_DEVICE = "MEGOLM_KEY_WITHHELD_FOR_UNVERIFIED_DEVICE",
|
||||
|
||||
/** Message was encrypted with a Megolm session which has been shared with us, but in a later ratchet state. */
|
||||
OLM_UNKNOWN_MESSAGE_INDEX = "OLM_UNKNOWN_MESSAGE_INDEX",
|
||||
|
||||
@@ -803,6 +809,8 @@ export interface CryptoCallbacks extends SecretStorageCallbacks {
|
||||
* @param key - private key to store
|
||||
*/
|
||||
cacheSecretStorageKey?: (keyId: string, keyInfo: SecretStorageKeyDescription, key: Uint8Array) => void;
|
||||
|
||||
/** @deprecated: unused with the Rust crypto stack. */
|
||||
onSecretRequested?: (
|
||||
userId: string,
|
||||
deviceId: string,
|
||||
@@ -810,10 +818,13 @@ export interface CryptoCallbacks extends SecretStorageCallbacks {
|
||||
secretName: string,
|
||||
deviceTrust: DeviceVerificationStatus,
|
||||
) => Promise<string | undefined>;
|
||||
|
||||
/** @deprecated: unused with the Rust crypto stack. */
|
||||
getDehydrationKey?: (
|
||||
keyInfo: SecretStorageKeyDescription,
|
||||
checkFunc: (key: Uint8Array) => void,
|
||||
) => Promise<Uint8Array>;
|
||||
|
||||
getBackupKey?: () => Promise<Uint8Array>;
|
||||
}
|
||||
|
||||
@@ -848,9 +859,14 @@ export interface CreateSecretStorageOpts {
|
||||
setupNewSecretStorage?: boolean;
|
||||
|
||||
/**
|
||||
* Function called to get the user's
|
||||
* current key backup passphrase. Should return a promise that resolves with a Uint8Array
|
||||
* Function called to get the user's current key backup passphrase.
|
||||
*
|
||||
* Should return a promise that resolves with a Uint8Array
|
||||
* containing the key, or rejects if the key cannot be obtained.
|
||||
*
|
||||
* Only used when the client has existing key backup, but no secret storage.
|
||||
*
|
||||
* @deprecated Not used by the Rust crypto stack.
|
||||
*/
|
||||
getKeyBackupPassphrase?: () => Promise<Uint8Array>;
|
||||
}
|
||||
@@ -949,5 +965,5 @@ export interface OwnDeviceKeys {
|
||||
curve25519: string;
|
||||
}
|
||||
|
||||
export * from "./verification";
|
||||
export * from "./keybackup";
|
||||
export * from "./verification.ts";
|
||||
export * from "./keybackup.ts";
|
||||
|
||||
@@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { ISigned } from "../@types/signed";
|
||||
import { IEncryptedPayload } from "../crypto/aes";
|
||||
import { ISigned } from "../@types/signed.ts";
|
||||
import { IEncryptedPayload } from "../crypto/aes.ts";
|
||||
|
||||
export interface Curve25519AuthData {
|
||||
public_key: string;
|
||||
|
||||
@@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { MatrixEvent } from "../models/event";
|
||||
import { TypedEventEmitter } from "../models/typed-event-emitter";
|
||||
import { MatrixEvent } from "../models/event.ts";
|
||||
import { TypedEventEmitter } from "../models/typed-event-emitter.ts";
|
||||
|
||||
/**
|
||||
* An incoming, or outgoing, request to verify a user or a device via cross-signing.
|
||||
|
||||
+17
-13
@@ -19,19 +19,23 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import type { PkSigning } from "@matrix-org/olm";
|
||||
import { IObject, pkSign, pkVerify } from "./olmlib";
|
||||
import { logger } from "../logger";
|
||||
import { IndexedDBCryptoStore } from "../crypto/store/indexeddb-crypto-store";
|
||||
import { decryptAES, encryptAES } from "./aes";
|
||||
import { DeviceInfo } from "./deviceinfo";
|
||||
import { ISignedKey, MatrixClient } from "../client";
|
||||
import { OlmDevice } from "./OlmDevice";
|
||||
import { ICryptoCallbacks } from ".";
|
||||
import { ISignatures } from "../@types/signed";
|
||||
import { CryptoStore, SecretStorePrivateKeys } from "./store/base";
|
||||
import { ServerSideSecretStorage, SecretStorageKeyDescription } from "../secret-storage";
|
||||
import { CrossSigningKeyInfo, DeviceVerificationStatus, UserVerificationStatus as UserTrustLevel } from "../crypto-api";
|
||||
import { decodeBase64, encodeBase64 } from "../base64";
|
||||
import { IObject, pkSign, pkVerify } from "./olmlib.ts";
|
||||
import { logger } from "../logger.ts";
|
||||
import { IndexedDBCryptoStore } from "../crypto/store/indexeddb-crypto-store.ts";
|
||||
import { decryptAES, encryptAES } from "./aes.ts";
|
||||
import { DeviceInfo } from "./deviceinfo.ts";
|
||||
import { ISignedKey, MatrixClient } from "../client.ts";
|
||||
import { OlmDevice } from "./OlmDevice.ts";
|
||||
import { ICryptoCallbacks } from "./index.ts";
|
||||
import { ISignatures } from "../@types/signed.ts";
|
||||
import { CryptoStore, SecretStorePrivateKeys } from "./store/base.ts";
|
||||
import { ServerSideSecretStorage, SecretStorageKeyDescription } from "../secret-storage.ts";
|
||||
import {
|
||||
CrossSigningKeyInfo,
|
||||
DeviceVerificationStatus,
|
||||
UserVerificationStatus as UserTrustLevel,
|
||||
} from "../crypto-api/index.ts";
|
||||
import { decodeBase64, encodeBase64 } from "../base64.ts";
|
||||
|
||||
// backwards-compatibility re-exports
|
||||
export { UserTrustLevel };
|
||||
|
||||
+11
-11
@@ -18,17 +18,17 @@ limitations under the License.
|
||||
* Manages the list of other users' devices
|
||||
*/
|
||||
|
||||
import { logger } from "../logger";
|
||||
import { DeviceInfo, IDevice } from "./deviceinfo";
|
||||
import { CrossSigningInfo, ICrossSigningInfo } from "./CrossSigning";
|
||||
import * as olmlib from "./olmlib";
|
||||
import { IndexedDBCryptoStore } from "./store/indexeddb-crypto-store";
|
||||
import { chunkPromises, defer, IDeferred, sleep } from "../utils";
|
||||
import { DeviceKeys, IDownloadKeyResult, Keys, MatrixClient, SigningKeys } from "../client";
|
||||
import { OlmDevice } from "./OlmDevice";
|
||||
import { CryptoStore } from "./store/base";
|
||||
import { TypedEventEmitter } from "../models/typed-event-emitter";
|
||||
import { CryptoEvent, CryptoEventHandlerMap } from "./index";
|
||||
import { logger } from "../logger.ts";
|
||||
import { DeviceInfo, IDevice } from "./deviceinfo.ts";
|
||||
import { CrossSigningInfo, ICrossSigningInfo } from "./CrossSigning.ts";
|
||||
import * as olmlib from "./olmlib.ts";
|
||||
import { IndexedDBCryptoStore } from "./store/indexeddb-crypto-store.ts";
|
||||
import { chunkPromises, defer, IDeferred, sleep } from "../utils.ts";
|
||||
import { DeviceKeys, IDownloadKeyResult, Keys, MatrixClient, SigningKeys } from "../client.ts";
|
||||
import { OlmDevice } from "./OlmDevice.ts";
|
||||
import { CryptoStore } from "./store/base.ts";
|
||||
import { TypedEventEmitter } from "../models/typed-event-emitter.ts";
|
||||
import { CryptoEvent, CryptoEventHandlerMap } from "./index.ts";
|
||||
|
||||
/* State transition diagram for DeviceList.deviceTrackingStatus
|
||||
*
|
||||
|
||||
@@ -14,17 +14,17 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { logger } from "../logger";
|
||||
import { MatrixEvent } from "../models/event";
|
||||
import { createCryptoStoreCacheCallbacks, ICacheCallbacks } from "./CrossSigning";
|
||||
import { IndexedDBCryptoStore } from "./store/indexeddb-crypto-store";
|
||||
import { Method, ClientPrefix } from "../http-api";
|
||||
import { Crypto, ICryptoCallbacks } from "./index";
|
||||
import { ClientEvent, ClientEventHandlerMap, CrossSigningKeys, ISignedKey, KeySignatures } from "../client";
|
||||
import { IKeyBackupInfo } from "./keybackup";
|
||||
import { TypedEventEmitter } from "../models/typed-event-emitter";
|
||||
import { AccountDataClient, SecretStorageKeyDescription } from "../secret-storage";
|
||||
import { BootstrapCrossSigningOpts, CrossSigningKeyInfo } from "../crypto-api";
|
||||
import { logger } from "../logger.ts";
|
||||
import { MatrixEvent } from "../models/event.ts";
|
||||
import { createCryptoStoreCacheCallbacks, ICacheCallbacks } from "./CrossSigning.ts";
|
||||
import { IndexedDBCryptoStore } from "./store/indexeddb-crypto-store.ts";
|
||||
import { Method, ClientPrefix } from "../http-api/index.ts";
|
||||
import { Crypto, ICryptoCallbacks } from "./index.ts";
|
||||
import { ClientEvent, ClientEventHandlerMap, CrossSigningKeys, ISignedKey, KeySignatures } from "../client.ts";
|
||||
import { IKeyBackupInfo } from "./keybackup.ts";
|
||||
import { TypedEventEmitter } from "../models/typed-event-emitter.ts";
|
||||
import { AccountDataClient, SecretStorageKeyDescription } from "../secret-storage.ts";
|
||||
import { BootstrapCrossSigningOpts, CrossSigningKeyInfo } from "../crypto-api/index.ts";
|
||||
|
||||
interface ICrossSigningKeys {
|
||||
authUpload: BootstrapCrossSigningOpts["authUploadDeviceSigningKeys"];
|
||||
|
||||
+22
-22
@@ -16,14 +16,14 @@ limitations under the License.
|
||||
|
||||
import { Account, InboundGroupSession, OutboundGroupSession, Session, Utility } from "@matrix-org/olm";
|
||||
|
||||
import { logger, Logger } from "../logger";
|
||||
import { IndexedDBCryptoStore } from "./store/indexeddb-crypto-store";
|
||||
import { CryptoStore, IProblem, ISessionInfo, IWithheld } from "./store/base";
|
||||
import { IOlmDevice, IOutboundGroupSessionKey } from "./algorithms/megolm";
|
||||
import { IMegolmSessionData, OlmGroupSessionExtraData } from "../@types/crypto";
|
||||
import { IMessage } from "./algorithms/olm";
|
||||
import { DecryptionFailureCode } from "../crypto-api";
|
||||
import { DecryptionError } from "../common-crypto/CryptoBackend";
|
||||
import { logger, Logger } from "../logger.ts";
|
||||
import { IndexedDBCryptoStore } from "./store/indexeddb-crypto-store.ts";
|
||||
import { CryptoStore, IProblem, ISessionInfo, IWithheld } from "./store/base.ts";
|
||||
import { IOlmDevice, IOutboundGroupSessionKey } from "./algorithms/megolm.ts";
|
||||
import { IMegolmSessionData, OlmGroupSessionExtraData } from "../@types/crypto.ts";
|
||||
import { IMessage } from "./algorithms/olm.ts";
|
||||
import { DecryptionFailureCode } from "../crypto-api/index.ts";
|
||||
import { DecryptionError } from "../common-crypto/CryptoBackend.ts";
|
||||
|
||||
// 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.
|
||||
@@ -1221,13 +1221,13 @@ export class OlmDevice {
|
||||
this.getInboundGroupSession(roomId, senderKey, sessionId, txn, (session, sessionData, withheld) => {
|
||||
if (session === null || sessionData === null) {
|
||||
if (withheld) {
|
||||
error = new DecryptionError(
|
||||
DecryptionFailureCode.MEGOLM_UNKNOWN_INBOUND_SESSION_ID,
|
||||
calculateWithheldMessage(withheld),
|
||||
{
|
||||
session: senderKey + "|" + sessionId,
|
||||
},
|
||||
);
|
||||
const failureCode =
|
||||
withheld.code === "m.unverified"
|
||||
? DecryptionFailureCode.MEGOLM_KEY_WITHHELD_FOR_UNVERIFIED_DEVICE
|
||||
: DecryptionFailureCode.MEGOLM_KEY_WITHHELD;
|
||||
error = new DecryptionError(failureCode, calculateWithheldMessage(withheld), {
|
||||
session: senderKey + "|" + sessionId,
|
||||
});
|
||||
}
|
||||
result = null;
|
||||
return;
|
||||
@@ -1237,13 +1237,13 @@ export class OlmDevice {
|
||||
res = session.decrypt(body);
|
||||
} catch (e) {
|
||||
if ((<Error>e)?.message === "OLM.UNKNOWN_MESSAGE_INDEX" && withheld) {
|
||||
error = new DecryptionError(
|
||||
DecryptionFailureCode.MEGOLM_UNKNOWN_INBOUND_SESSION_ID,
|
||||
calculateWithheldMessage(withheld),
|
||||
{
|
||||
session: senderKey + "|" + sessionId,
|
||||
},
|
||||
);
|
||||
const failureCode =
|
||||
withheld.code === "m.unverified"
|
||||
? DecryptionFailureCode.MEGOLM_KEY_WITHHELD_FOR_UNVERIFIED_DEVICE
|
||||
: DecryptionFailureCode.MEGOLM_KEY_WITHHELD;
|
||||
error = new DecryptionError(failureCode, calculateWithheldMessage(withheld), {
|
||||
session: senderKey + "|" + sessionId,
|
||||
});
|
||||
} else {
|
||||
error = <Error>e;
|
||||
}
|
||||
|
||||
@@ -16,12 +16,12 @@ limitations under the License.
|
||||
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
import { logger } from "../logger";
|
||||
import { MatrixClient } from "../client";
|
||||
import { IRoomKeyRequestBody, IRoomKeyRequestRecipient } from "./index";
|
||||
import { CryptoStore, OutgoingRoomKeyRequest } from "./store/base";
|
||||
import { EventType, ToDeviceMessageId } from "../@types/event";
|
||||
import { MapWithDefault } from "../utils";
|
||||
import { logger } from "../logger.ts";
|
||||
import { MatrixClient } from "../client.ts";
|
||||
import { IRoomKeyRequestBody, IRoomKeyRequestRecipient } from "./index.ts";
|
||||
import { CryptoStore, OutgoingRoomKeyRequest } from "./store/base.ts";
|
||||
import { EventType, ToDeviceMessageId } from "../@types/event.ts";
|
||||
import { MapWithDefault } from "../utils.ts";
|
||||
|
||||
/**
|
||||
* Internal module. Management of outgoing room key requests.
|
||||
|
||||
@@ -18,8 +18,8 @@ limitations under the License.
|
||||
* Manages the list of encrypted rooms
|
||||
*/
|
||||
|
||||
import { CryptoStore } from "./store/base";
|
||||
import { IndexedDBCryptoStore } from "./store/indexeddb-crypto-store";
|
||||
import { CryptoStore } from "./store/base.ts";
|
||||
import { IndexedDBCryptoStore } from "./store/indexeddb-crypto-store.ts";
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
export interface IRoomEncryption {
|
||||
|
||||
@@ -15,13 +15,13 @@ limitations under the License.
|
||||
*/
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
import { MatrixClient } from "../client";
|
||||
import { ICryptoCallbacks, IEncryptedContent } from "./index";
|
||||
import { defer, IDeferred } from "../utils";
|
||||
import { ToDeviceMessageId } from "../@types/event";
|
||||
import { logger } from "../logger";
|
||||
import { MatrixEvent } from "../models/event";
|
||||
import * as olmlib from "./olmlib";
|
||||
import { MatrixClient } from "../client.ts";
|
||||
import { ICryptoCallbacks, IEncryptedContent } from "./index.ts";
|
||||
import { defer, IDeferred } from "../utils.ts";
|
||||
import { ToDeviceMessageId } from "../@types/event.ts";
|
||||
import { logger } from "../logger.ts";
|
||||
import { MatrixEvent } from "../models/event.ts";
|
||||
import * as olmlib from "./olmlib.ts";
|
||||
|
||||
export interface ISecretRequest {
|
||||
requestId: string;
|
||||
|
||||
@@ -14,9 +14,9 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { ICryptoCallbacks } from ".";
|
||||
import { MatrixEvent } from "../models/event";
|
||||
import { MatrixClient } from "../client";
|
||||
import { ICryptoCallbacks } from "./index.ts";
|
||||
import { MatrixEvent } from "../models/event.ts";
|
||||
import { MatrixClient } from "../client.ts";
|
||||
import {
|
||||
SecretStorageKeyDescription,
|
||||
SecretStorageKeyTuple,
|
||||
@@ -25,8 +25,8 @@ import {
|
||||
AccountDataClient,
|
||||
ServerSideSecretStorage,
|
||||
ServerSideSecretStorageImpl,
|
||||
} from "../secret-storage";
|
||||
import { ISecretRequest, SecretSharing } from "./SecretSharing";
|
||||
} from "../secret-storage.ts";
|
||||
import { ISecretRequest, SecretSharing } from "./SecretSharing.ts";
|
||||
|
||||
/* re-exports for backwards compatibility */
|
||||
export type {
|
||||
@@ -34,9 +34,9 @@ export type {
|
||||
SecretStorageKeyTuple,
|
||||
SecretStorageKeyObject,
|
||||
SECRET_STORAGE_ALGORITHM_V1_AES,
|
||||
} from "../secret-storage";
|
||||
} from "../secret-storage.ts";
|
||||
|
||||
export type { ISecretRequest } from "./SecretSharing";
|
||||
export type { ISecretRequest } from "./SecretSharing.ts";
|
||||
|
||||
/**
|
||||
* Implements Secure Secret Storage and Sharing (MSC1946)
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { decodeBase64, encodeBase64 } from "../base64";
|
||||
import { decodeBase64, encodeBase64 } from "../base64.ts";
|
||||
|
||||
// salt for HKDF, with 8 bytes of zeros
|
||||
const zeroSalt = new Uint8Array(8);
|
||||
|
||||
@@ -18,15 +18,15 @@ limitations under the License.
|
||||
* Internal module. Defines the base classes of the encryption implementations
|
||||
*/
|
||||
|
||||
import type { IMegolmSessionData } from "../../@types/crypto";
|
||||
import { MatrixClient } from "../../client";
|
||||
import { Room } from "../../models/room";
|
||||
import { OlmDevice } from "../OlmDevice";
|
||||
import { IContent, MatrixEvent, RoomMember } from "../../matrix";
|
||||
import { Crypto, IEncryptedContent, IEventDecryptionResult, IncomingRoomKeyRequest } from "..";
|
||||
import { DeviceInfo } from "../deviceinfo";
|
||||
import { IRoomEncryption } from "../RoomList";
|
||||
import { DeviceInfoMap } from "../DeviceList";
|
||||
import type { IMegolmSessionData } from "../../@types/crypto.ts";
|
||||
import { MatrixClient } from "../../client.ts";
|
||||
import { Room } from "../../models/room.ts";
|
||||
import { OlmDevice } from "../OlmDevice.ts";
|
||||
import { IContent, MatrixEvent, RoomMember } from "../../matrix.ts";
|
||||
import { Crypto, IEncryptedContent, IEventDecryptionResult, IncomingRoomKeyRequest } from "../index.ts";
|
||||
import { DeviceInfo } from "../deviceinfo.ts";
|
||||
import { IRoomEncryption } from "../RoomList.ts";
|
||||
import { DeviceInfoMap } from "../DeviceList.ts";
|
||||
|
||||
/**
|
||||
* Map of registered encryption algorithm classes. A map from string to {@link EncryptionAlgorithm} class
|
||||
@@ -66,7 +66,6 @@ export abstract class EncryptionAlgorithm {
|
||||
protected readonly crypto: Crypto;
|
||||
protected readonly olmDevice: OlmDevice;
|
||||
protected readonly baseApis: MatrixClient;
|
||||
protected readonly roomId?: string;
|
||||
|
||||
/**
|
||||
* @param params - parameters
|
||||
@@ -77,7 +76,6 @@ export abstract class EncryptionAlgorithm {
|
||||
this.crypto = params.crypto;
|
||||
this.olmDevice = params.olmDevice;
|
||||
this.baseApis = params.baseApis;
|
||||
this.roomId = params.roomId;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -127,14 +125,12 @@ export abstract class DecryptionAlgorithm {
|
||||
protected readonly crypto: Crypto;
|
||||
protected readonly olmDevice: OlmDevice;
|
||||
protected readonly baseApis: MatrixClient;
|
||||
protected readonly roomId?: string;
|
||||
|
||||
public constructor(params: DecryptionClassParams) {
|
||||
this.userId = params.userId;
|
||||
this.crypto = params.crypto;
|
||||
this.olmDevice = params.olmDevice;
|
||||
this.baseApis = params.baseApis;
|
||||
this.roomId = params.roomId;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -237,4 +233,4 @@ export function registerAlgorithm<P extends IParams = IParams>(
|
||||
}
|
||||
|
||||
/* Re-export for backwards compatibility. Deprecated: this is an internal class. */
|
||||
export { DecryptionError } from "../../common-crypto/CryptoBackend";
|
||||
export { DecryptionError } from "../../common-crypto/CryptoBackend.ts";
|
||||
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import "./olm";
|
||||
import "./megolm";
|
||||
import "./olm.ts";
|
||||
import "./megolm.ts";
|
||||
|
||||
export * from "./base";
|
||||
export * from "./base.ts";
|
||||
|
||||
@@ -20,9 +20,9 @@ limitations under the License.
|
||||
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
import type { IEventDecryptionResult, IMegolmSessionData } from "../../@types/crypto";
|
||||
import { logger, Logger } from "../../logger";
|
||||
import * as olmlib from "../olmlib";
|
||||
import type { IEventDecryptionResult, IMegolmSessionData } from "../../@types/crypto.ts";
|
||||
import { logger, Logger } from "../../logger.ts";
|
||||
import * as olmlib from "../olmlib.ts";
|
||||
import {
|
||||
DecryptionAlgorithm,
|
||||
DecryptionClassParams,
|
||||
@@ -30,22 +30,22 @@ import {
|
||||
IParams,
|
||||
registerAlgorithm,
|
||||
UnknownDeviceError,
|
||||
} from "./base";
|
||||
import { IDecryptedGroupMessage, WITHHELD_MESSAGES } from "../OlmDevice";
|
||||
import { Room } from "../../models/room";
|
||||
import { DeviceInfo } from "../deviceinfo";
|
||||
import { IOlmSessionResult } from "../olmlib";
|
||||
import { DeviceInfoMap } from "../DeviceList";
|
||||
import { IContent, MatrixEvent } from "../../models/event";
|
||||
import { EventType, MsgType, ToDeviceMessageId } from "../../@types/event";
|
||||
import { IMegolmEncryptedContent, IncomingRoomKeyRequest, IEncryptedContent } from "../index";
|
||||
import { RoomKeyRequestState } from "../OutgoingRoomKeyRequestManager";
|
||||
import { OlmGroupSessionExtraData } from "../../@types/crypto";
|
||||
import { MatrixError } from "../../http-api";
|
||||
import { immediate, MapWithDefault } from "../../utils";
|
||||
import { KnownMembership } from "../../@types/membership";
|
||||
import { DecryptionFailureCode } from "../../crypto-api";
|
||||
import { DecryptionError } from "../../common-crypto/CryptoBackend";
|
||||
} from "./base.ts";
|
||||
import { IDecryptedGroupMessage, WITHHELD_MESSAGES } from "../OlmDevice.ts";
|
||||
import { Room } from "../../models/room.ts";
|
||||
import { DeviceInfo } from "../deviceinfo.ts";
|
||||
import { IOlmSessionResult } from "../olmlib.ts";
|
||||
import { DeviceInfoMap } from "../DeviceList.ts";
|
||||
import { IContent, MatrixEvent } from "../../models/event.ts";
|
||||
import { EventType, MsgType, ToDeviceMessageId } from "../../@types/event.ts";
|
||||
import { IMegolmEncryptedContent, IncomingRoomKeyRequest, IEncryptedContent } from "../index.ts";
|
||||
import { RoomKeyRequestState } from "../OutgoingRoomKeyRequestManager.ts";
|
||||
import { OlmGroupSessionExtraData } from "../../@types/crypto.ts";
|
||||
import { MatrixError } from "../../http-api/index.ts";
|
||||
import { immediate, MapWithDefault } from "../../utils.ts";
|
||||
import { KnownMembership } from "../../@types/membership.ts";
|
||||
import { DecryptionFailureCode } from "../../crypto-api/index.ts";
|
||||
import { DecryptionError } from "../../common-crypto/CryptoBackend.ts";
|
||||
|
||||
// determine whether the key can be shared with invitees
|
||||
export function isRoomSharedHistory(room: Room): boolean {
|
||||
|
||||
@@ -18,17 +18,17 @@ limitations under the License.
|
||||
* Defines m.olm encryption/decryption
|
||||
*/
|
||||
|
||||
import type { IEventDecryptionResult } from "../../@types/crypto";
|
||||
import { logger } from "../../logger";
|
||||
import * as olmlib from "../olmlib";
|
||||
import { DeviceInfo } from "../deviceinfo";
|
||||
import { DecryptionAlgorithm, EncryptionAlgorithm, registerAlgorithm } from "./base";
|
||||
import { Room } from "../../models/room";
|
||||
import { IContent, MatrixEvent } from "../../models/event";
|
||||
import { IEncryptedContent, IOlmEncryptedContent } from "../index";
|
||||
import { IInboundSession } from "../OlmDevice";
|
||||
import { DecryptionFailureCode } from "../../crypto-api";
|
||||
import { DecryptionError } from "../../common-crypto/CryptoBackend";
|
||||
import type { IEventDecryptionResult } from "../../@types/crypto.ts";
|
||||
import { logger } from "../../logger.ts";
|
||||
import * as olmlib from "../olmlib.ts";
|
||||
import { DeviceInfo } from "../deviceinfo.ts";
|
||||
import { DecryptionAlgorithm, EncryptionAlgorithm, registerAlgorithm } from "./base.ts";
|
||||
import { Room } from "../../models/room.ts";
|
||||
import { IContent, MatrixEvent } from "../../models/event.ts";
|
||||
import { IEncryptedContent, IOlmEncryptedContent } from "../index.ts";
|
||||
import { IInboundSession } from "../OlmDevice.ts";
|
||||
import { DecryptionFailureCode } from "../../crypto-api/index.ts";
|
||||
import { DecryptionError } from "../../common-crypto/CryptoBackend.ts";
|
||||
|
||||
const DeviceVerification = DeviceInfo.DeviceVerification;
|
||||
|
||||
|
||||
+5
-5
@@ -14,26 +14,26 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { DeviceInfo } from "./deviceinfo";
|
||||
import { DeviceInfo } from "./deviceinfo.ts";
|
||||
|
||||
/* re-exports for backwards compatibility. */
|
||||
// CrossSigningKey is used as a value in `client.ts`, we can't export it as a type
|
||||
export { CrossSigningKey } from "../crypto-api";
|
||||
export { CrossSigningKey } from "../crypto-api/index.ts";
|
||||
export type {
|
||||
GeneratedSecretStorageKey as IRecoveryKey,
|
||||
CreateSecretStorageOpts as ICreateSecretStorageOpts,
|
||||
} from "../crypto-api";
|
||||
} from "../crypto-api/index.ts";
|
||||
|
||||
export type {
|
||||
ImportRoomKeyProgressData as IImportOpts,
|
||||
ImportRoomKeysOpts as IImportRoomKeysOpts,
|
||||
} from "../crypto-api";
|
||||
} from "../crypto-api/index.ts";
|
||||
|
||||
export type {
|
||||
AddSecretStorageKeyOpts as IAddSecretStorageKeyOpts,
|
||||
PassphraseInfo as IPassphraseInfo,
|
||||
SecretStorageKeyDescription as ISecretStorageKeyInfo,
|
||||
} from "../secret-storage";
|
||||
} from "../secret-storage.ts";
|
||||
|
||||
// TODO: Merge this with crypto.js once converted
|
||||
|
||||
|
||||
+17
-17
@@ -18,29 +18,29 @@ limitations under the License.
|
||||
* Classes for dealing with key backup.
|
||||
*/
|
||||
|
||||
import type { IMegolmSessionData } from "../@types/crypto";
|
||||
import { MatrixClient } from "../client";
|
||||
import { logger } from "../logger";
|
||||
import { MEGOLM_ALGORITHM, verifySignature } from "./olmlib";
|
||||
import { DeviceInfo } from "./deviceinfo";
|
||||
import { DeviceTrustLevel } from "./CrossSigning";
|
||||
import { keyFromPassphrase } from "./key_passphrase";
|
||||
import { encodeUri, safeSet, sleep } from "../utils";
|
||||
import { IndexedDBCryptoStore } from "./store/indexeddb-crypto-store";
|
||||
import { encodeRecoveryKey } from "./recoverykey";
|
||||
import { calculateKeyCheck, decryptAES, encryptAES, IEncryptedPayload } from "./aes";
|
||||
import type { IMegolmSessionData } from "../@types/crypto.ts";
|
||||
import { MatrixClient } from "../client.ts";
|
||||
import { logger } from "../logger.ts";
|
||||
import { MEGOLM_ALGORITHM, verifySignature } from "./olmlib.ts";
|
||||
import { DeviceInfo } from "./deviceinfo.ts";
|
||||
import { DeviceTrustLevel } from "./CrossSigning.ts";
|
||||
import { keyFromPassphrase } from "./key_passphrase.ts";
|
||||
import { encodeUri, safeSet, sleep } from "../utils.ts";
|
||||
import { IndexedDBCryptoStore } from "./store/indexeddb-crypto-store.ts";
|
||||
import { encodeRecoveryKey } from "./recoverykey.ts";
|
||||
import { calculateKeyCheck, decryptAES, encryptAES, IEncryptedPayload } from "./aes.ts";
|
||||
import {
|
||||
Curve25519SessionData,
|
||||
IAes256AuthData,
|
||||
ICurve25519AuthData,
|
||||
IKeyBackupInfo,
|
||||
IKeyBackupSession,
|
||||
} from "./keybackup";
|
||||
import { UnstableValue } from "../NamespacedValue";
|
||||
import { CryptoEvent } from "./index";
|
||||
import { ClientPrefix, HTTPError, MatrixError, Method } from "../http-api";
|
||||
import { BackupTrustInfo } from "../crypto-api/keybackup";
|
||||
import { BackupDecryptor } from "../common-crypto/CryptoBackend";
|
||||
} from "./keybackup.ts";
|
||||
import { UnstableValue } from "../NamespacedValue.ts";
|
||||
import { CryptoEvent } from "./index.ts";
|
||||
import { ClientPrefix, HTTPError, MatrixError, Method } from "../http-api/index.ts";
|
||||
import { BackupTrustInfo } from "../crypto-api/keybackup.ts";
|
||||
import { BackupDecryptor } from "../common-crypto/CryptoBackend.ts";
|
||||
|
||||
const KEY_BACKUP_KEYS_PER_REQUEST = 200;
|
||||
const KEY_BACKUP_CHECK_RATE_LIMIT = 5000; // ms
|
||||
|
||||
@@ -16,14 +16,14 @@ limitations under the License.
|
||||
|
||||
import anotherjson from "another-json";
|
||||
|
||||
import type { IDeviceKeys, IOneTimeKey } from "../@types/crypto";
|
||||
import { decodeBase64, encodeBase64 } from "../base64";
|
||||
import { IndexedDBCryptoStore } from "../crypto/store/indexeddb-crypto-store";
|
||||
import { decryptAES, encryptAES } from "./aes";
|
||||
import { logger } from "../logger";
|
||||
import { Crypto } from "./index";
|
||||
import { Method } from "../http-api";
|
||||
import { SecretStorageKeyDescription } from "../secret-storage";
|
||||
import type { IDeviceKeys, IOneTimeKey } from "../@types/crypto.ts";
|
||||
import { decodeBase64, encodeBase64 } from "../base64.ts";
|
||||
import { IndexedDBCryptoStore } from "../crypto/store/indexeddb-crypto-store.ts";
|
||||
import { decryptAES, encryptAES } from "./aes.ts";
|
||||
import { logger } from "../logger.ts";
|
||||
import { Crypto } from "./index.ts";
|
||||
import { Method } from "../http-api/index.ts";
|
||||
import { SecretStorageKeyDescription } from "../secret-storage.ts";
|
||||
|
||||
export interface IDehydratedDevice {
|
||||
device_id: string; // eslint-disable-line camelcase
|
||||
|
||||
@@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { Device } from "../models/device";
|
||||
import { DeviceInfo } from "./deviceinfo";
|
||||
import { Device } from "../models/device.ts";
|
||||
import { DeviceInfo } from "./deviceinfo.ts";
|
||||
|
||||
/**
|
||||
* Convert a {@link DeviceInfo} to a {@link Device}.
|
||||
|
||||
@@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { ISignatures } from "../@types/signed";
|
||||
import { DeviceVerification } from "../models/device";
|
||||
import { ISignatures } from "../@types/signed.ts";
|
||||
import { DeviceVerification } from "../models/device.ts";
|
||||
|
||||
export interface IDevice {
|
||||
keys: Record<string, string>;
|
||||
|
||||
+63
-58
@@ -20,55 +20,60 @@ limitations under the License.
|
||||
import anotherjson from "another-json";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
import type { IDeviceKeys, IEventDecryptionResult, IMegolmSessionData, IOneTimeKey } from "../@types/crypto";
|
||||
import type { IDeviceKeys, IEventDecryptionResult, IMegolmSessionData, IOneTimeKey } from "../@types/crypto.ts";
|
||||
import type { PkDecryption, PkSigning } from "@matrix-org/olm";
|
||||
import { EventType, ToDeviceMessageId } from "../@types/event";
|
||||
import { TypedReEmitter } from "../ReEmitter";
|
||||
import { logger } from "../logger";
|
||||
import { IExportedDevice, OlmDevice } from "./OlmDevice";
|
||||
import { IOlmDevice } from "./algorithms/megolm";
|
||||
import * as olmlib from "./olmlib";
|
||||
import { DeviceInfoMap, DeviceList } from "./DeviceList";
|
||||
import { DeviceInfo, IDevice } from "./deviceinfo";
|
||||
import type { DecryptionAlgorithm, EncryptionAlgorithm } from "./algorithms";
|
||||
import * as algorithms from "./algorithms";
|
||||
import { createCryptoStoreCacheCallbacks, CrossSigningInfo, DeviceTrustLevel, UserTrustLevel } from "./CrossSigning";
|
||||
import { EncryptionSetupBuilder } from "./EncryptionSetup";
|
||||
import { SecretStorage as LegacySecretStorage } from "./SecretStorage";
|
||||
import { CrossSigningKey, ICreateSecretStorageOpts, IEncryptedEventInfo, IRecoveryKey } from "./api";
|
||||
import { OutgoingRoomKeyRequestManager } from "./OutgoingRoomKeyRequestManager";
|
||||
import { IndexedDBCryptoStore } from "./store/indexeddb-crypto-store";
|
||||
import { VerificationBase } from "./verification/Base";
|
||||
import { ReciprocateQRCode, SCAN_QR_CODE_METHOD, SHOW_QR_CODE_METHOD } from "./verification/QRCode";
|
||||
import { SAS as SASVerification } from "./verification/SAS";
|
||||
import { keyFromPassphrase } from "./key_passphrase";
|
||||
import { decodeRecoveryKey, encodeRecoveryKey } from "./recoverykey";
|
||||
import { VerificationRequest } from "./verification/request/VerificationRequest";
|
||||
import { InRoomChannel, InRoomRequests } from "./verification/request/InRoomChannel";
|
||||
import { Request, ToDeviceChannel, ToDeviceRequests } from "./verification/request/ToDeviceChannel";
|
||||
import { IllegalMethod } from "./verification/IllegalMethod";
|
||||
import { KeySignatureUploadError } from "../errors";
|
||||
import { calculateKeyCheck, decryptAES, encryptAES, IEncryptedPayload } from "./aes";
|
||||
import { DehydrationManager } from "./dehydration";
|
||||
import { BackupManager, LibOlmBackupDecryptor, backupTrustInfoFromLegacyTrustInfo } from "./backup";
|
||||
import { IStore } from "../store";
|
||||
import { Room, RoomEvent } from "../models/room";
|
||||
import { RoomMember, RoomMemberEvent } from "../models/room-member";
|
||||
import { EventStatus, IContent, IEvent, MatrixEvent, MatrixEventEvent } from "../models/event";
|
||||
import { ToDeviceBatch } from "../models/ToDeviceMessage";
|
||||
import { ClientEvent, IKeysUploadResponse, ISignedKey, IUploadKeySignaturesResponse, MatrixClient } from "../client";
|
||||
import { IRoomEncryption, RoomList } from "./RoomList";
|
||||
import { IKeyBackupInfo } from "./keybackup";
|
||||
import { ISyncStateData } from "../sync";
|
||||
import { CryptoStore } from "./store/base";
|
||||
import { IVerificationChannel } from "./verification/request/Channel";
|
||||
import { TypedEventEmitter } from "../models/typed-event-emitter";
|
||||
import { IDeviceLists, ISyncResponse, IToDeviceEvent } from "../sync-accumulator";
|
||||
import { ISignatures } from "../@types/signed";
|
||||
import { IMessage } from "./algorithms/olm";
|
||||
import { BackupDecryptor, CryptoBackend, DecryptionError, OnSyncCompletedData } from "../common-crypto/CryptoBackend";
|
||||
import { RoomState, RoomStateEvent } from "../models/room-state";
|
||||
import { MapWithDefault, recursiveMapToObject } from "../utils";
|
||||
import { EventType, ToDeviceMessageId } from "../@types/event.ts";
|
||||
import { TypedReEmitter } from "../ReEmitter.ts";
|
||||
import { logger } from "../logger.ts";
|
||||
import { IExportedDevice, OlmDevice } from "./OlmDevice.ts";
|
||||
import { IOlmDevice } from "./algorithms/megolm.ts";
|
||||
import * as olmlib from "./olmlib.ts";
|
||||
import { DeviceInfoMap, DeviceList } from "./DeviceList.ts";
|
||||
import { DeviceInfo, IDevice } from "./deviceinfo.ts";
|
||||
import type { DecryptionAlgorithm, EncryptionAlgorithm } from "./algorithms/index.ts";
|
||||
import * as algorithms from "./algorithms/index.ts";
|
||||
import { createCryptoStoreCacheCallbacks, CrossSigningInfo, DeviceTrustLevel, UserTrustLevel } from "./CrossSigning.ts";
|
||||
import { EncryptionSetupBuilder } from "./EncryptionSetup.ts";
|
||||
import { SecretStorage as LegacySecretStorage } from "./SecretStorage.ts";
|
||||
import { CrossSigningKey, ICreateSecretStorageOpts, IEncryptedEventInfo, IRecoveryKey } from "./api.ts";
|
||||
import { OutgoingRoomKeyRequestManager } from "./OutgoingRoomKeyRequestManager.ts";
|
||||
import { IndexedDBCryptoStore } from "./store/indexeddb-crypto-store.ts";
|
||||
import { VerificationBase } from "./verification/Base.ts";
|
||||
import { ReciprocateQRCode, SCAN_QR_CODE_METHOD, SHOW_QR_CODE_METHOD } from "./verification/QRCode.ts";
|
||||
import { SAS as SASVerification } from "./verification/SAS.ts";
|
||||
import { keyFromPassphrase } from "./key_passphrase.ts";
|
||||
import { decodeRecoveryKey, encodeRecoveryKey } from "./recoverykey.ts";
|
||||
import { VerificationRequest } from "./verification/request/VerificationRequest.ts";
|
||||
import { InRoomChannel, InRoomRequests } from "./verification/request/InRoomChannel.ts";
|
||||
import { Request, ToDeviceChannel, ToDeviceRequests } from "./verification/request/ToDeviceChannel.ts";
|
||||
import { IllegalMethod } from "./verification/IllegalMethod.ts";
|
||||
import { KeySignatureUploadError } from "../errors.ts";
|
||||
import { calculateKeyCheck, decryptAES, encryptAES, IEncryptedPayload } from "./aes.ts";
|
||||
import { DehydrationManager } from "./dehydration.ts";
|
||||
import { BackupManager, LibOlmBackupDecryptor, backupTrustInfoFromLegacyTrustInfo } from "./backup.ts";
|
||||
import { IStore } from "../store/index.ts";
|
||||
import { Room, RoomEvent } from "../models/room.ts";
|
||||
import { RoomMember, RoomMemberEvent } from "../models/room-member.ts";
|
||||
import { EventStatus, IContent, IEvent, MatrixEvent, MatrixEventEvent } from "../models/event.ts";
|
||||
import { ToDeviceBatch } from "../models/ToDeviceMessage.ts";
|
||||
import { ClientEvent, IKeysUploadResponse, ISignedKey, IUploadKeySignaturesResponse, MatrixClient } from "../client.ts";
|
||||
import { IRoomEncryption, RoomList } from "./RoomList.ts";
|
||||
import { IKeyBackupInfo } from "./keybackup.ts";
|
||||
import { ISyncStateData } from "../sync.ts";
|
||||
import { CryptoStore } from "./store/base.ts";
|
||||
import { IVerificationChannel } from "./verification/request/Channel.ts";
|
||||
import { TypedEventEmitter } from "../models/typed-event-emitter.ts";
|
||||
import { IDeviceLists, ISyncResponse, IToDeviceEvent } from "../sync-accumulator.ts";
|
||||
import { ISignatures } from "../@types/signed.ts";
|
||||
import { IMessage } from "./algorithms/olm.ts";
|
||||
import {
|
||||
BackupDecryptor,
|
||||
CryptoBackend,
|
||||
DecryptionError,
|
||||
OnSyncCompletedData,
|
||||
} from "../common-crypto/CryptoBackend.ts";
|
||||
import { RoomState, RoomStateEvent } from "../models/room-state.ts";
|
||||
import { MapWithDefault, recursiveMapToObject } from "../utils.ts";
|
||||
import {
|
||||
AccountDataClient,
|
||||
AddSecretStorageKeyOpts,
|
||||
@@ -77,8 +82,8 @@ import {
|
||||
SecretStorageKeyObject,
|
||||
SecretStorageKeyTuple,
|
||||
ServerSideSecretStorageImpl,
|
||||
} from "../secret-storage";
|
||||
import { ISecretRequest } from "./SecretSharing";
|
||||
} from "../secret-storage.ts";
|
||||
import { ISecretRequest } from "./SecretSharing.ts";
|
||||
import {
|
||||
BackupTrustInfo,
|
||||
BootstrapCrossSigningOpts,
|
||||
@@ -94,18 +99,18 @@ import {
|
||||
KeyBackupInfo,
|
||||
OwnDeviceKeys,
|
||||
VerificationRequest as CryptoApiVerificationRequest,
|
||||
} from "../crypto-api";
|
||||
import { Device, DeviceMap } from "../models/device";
|
||||
import { deviceInfoToDevice } from "./device-converter";
|
||||
import { ClientPrefix, MatrixError, Method } from "../http-api";
|
||||
import { decodeBase64, encodeBase64 } from "../base64";
|
||||
import { KnownMembership } from "../@types/membership";
|
||||
} from "../crypto-api/index.ts";
|
||||
import { Device, DeviceMap } from "../models/device.ts";
|
||||
import { deviceInfoToDevice } from "./device-converter.ts";
|
||||
import { ClientPrefix, MatrixError, Method } from "../http-api/index.ts";
|
||||
import { decodeBase64, encodeBase64 } from "../base64.ts";
|
||||
import { KnownMembership } from "../@types/membership.ts";
|
||||
|
||||
/* re-exports for backwards compatibility */
|
||||
export type {
|
||||
BootstrapCrossSigningOpts as IBootstrapCrossSigningOpts,
|
||||
CryptoCallbacks as ICryptoCallbacks,
|
||||
} from "../crypto-api";
|
||||
} from "../crypto-api/index.ts";
|
||||
|
||||
const DeviceVerification = DeviceInfo.DeviceVerification;
|
||||
|
||||
@@ -4374,4 +4379,4 @@ class IncomingRoomKeyRequestCancellation {
|
||||
}
|
||||
|
||||
// a number of types are re-exported for backwards compatibility, in case any applications are referencing it.
|
||||
export type { IEventDecryptionResult, IMegolmSessionData } from "../@types/crypto";
|
||||
export type { IEventDecryptionResult, IMegolmSessionData } from "../@types/crypto.ts";
|
||||
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { randomString } from "../randomstring";
|
||||
import { randomString } from "../randomstring.ts";
|
||||
|
||||
const DEFAULT_ITERATIONS = 500000;
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
// Export for backward compatibility
|
||||
import { ImportRoomKeyProgressData } from "../crypto-api";
|
||||
import { ImportRoomKeyProgressData } from "../crypto-api/index.ts";
|
||||
|
||||
export type {
|
||||
Curve25519AuthData as ICurve25519AuthData,
|
||||
@@ -24,7 +24,7 @@ export type {
|
||||
Curve25519SessionData,
|
||||
KeyBackupSession as IKeyBackupSession,
|
||||
KeyBackupRoomSessions as IKeyBackupRoomSessions,
|
||||
} from "../crypto-api/keybackup";
|
||||
} from "../crypto-api/keybackup.ts";
|
||||
|
||||
/* eslint-enable camelcase */
|
||||
|
||||
|
||||
+10
-10
@@ -21,16 +21,16 @@ limitations under the License.
|
||||
import anotherjson from "another-json";
|
||||
|
||||
import type { PkSigning } from "@matrix-org/olm";
|
||||
import type { IOneTimeKey } from "../@types/crypto";
|
||||
import { OlmDevice } from "./OlmDevice";
|
||||
import { DeviceInfo } from "./deviceinfo";
|
||||
import { Logger, logger } from "../logger";
|
||||
import { IClaimOTKsResult, MatrixClient } from "../client";
|
||||
import { ISignatures } from "../@types/signed";
|
||||
import { MatrixEvent } from "../models/event";
|
||||
import { EventType } from "../@types/event";
|
||||
import { IMessage } from "./algorithms/olm";
|
||||
import { MapWithDefault } from "../utils";
|
||||
import type { IOneTimeKey } from "../@types/crypto.ts";
|
||||
import { OlmDevice } from "./OlmDevice.ts";
|
||||
import { DeviceInfo } from "./deviceinfo.ts";
|
||||
import { Logger, logger } from "../logger.ts";
|
||||
import { IClaimOTKsResult, MatrixClient } from "../client.ts";
|
||||
import { ISignatures } from "../@types/signed.ts";
|
||||
import { MatrixEvent } from "../models/event.ts";
|
||||
import { EventType } from "../@types/event.ts";
|
||||
import { IMessage } from "./algorithms/olm.ts";
|
||||
import { MapWithDefault } from "../utils.ts";
|
||||
|
||||
enum Algorithm {
|
||||
Olm = "m.olm.v1.curve25519-aes-sha2",
|
||||
|
||||
+13
-13
@@ -14,19 +14,19 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { IRoomKeyRequestBody, IRoomKeyRequestRecipient } from "../index";
|
||||
import { RoomKeyRequestState } from "../OutgoingRoomKeyRequestManager";
|
||||
import { IOlmDevice } from "../algorithms/megolm";
|
||||
import { TrackingStatus } from "../DeviceList";
|
||||
import { IRoomEncryption } from "../RoomList";
|
||||
import { IDevice } from "../deviceinfo";
|
||||
import { ICrossSigningInfo } from "../CrossSigning";
|
||||
import { Logger } from "../../logger";
|
||||
import { InboundGroupSessionData } from "../OlmDevice";
|
||||
import { MatrixEvent } from "../../models/event";
|
||||
import { DehydrationManager } from "../dehydration";
|
||||
import { IEncryptedPayload } from "../aes";
|
||||
import { CrossSigningKeyInfo } from "../../crypto-api";
|
||||
import { IRoomKeyRequestBody, IRoomKeyRequestRecipient } from "../index.ts";
|
||||
import { RoomKeyRequestState } from "../OutgoingRoomKeyRequestManager.ts";
|
||||
import { IOlmDevice } from "../algorithms/megolm.ts";
|
||||
import { TrackingStatus } from "../DeviceList.ts";
|
||||
import { IRoomEncryption } from "../RoomList.ts";
|
||||
import { IDevice } from "../deviceinfo.ts";
|
||||
import { ICrossSigningInfo } from "../CrossSigning.ts";
|
||||
import { Logger } from "../../logger.ts";
|
||||
import { InboundGroupSessionData } from "../OlmDevice.ts";
|
||||
import { MatrixEvent } from "../../models/event.ts";
|
||||
import { DehydrationManager } from "../dehydration.ts";
|
||||
import { IEncryptedPayload } from "../aes.ts";
|
||||
import { CrossSigningKeyInfo } from "../../crypto-api/index.ts";
|
||||
|
||||
/**
|
||||
* Internal module. Definitions for storage for the crypto module
|
||||
|
||||
@@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { Logger, logger } from "../../logger";
|
||||
import { deepCompare } from "../../utils";
|
||||
import { Logger, logger } from "../../logger.ts";
|
||||
import { deepCompare } from "../../utils.ts";
|
||||
import {
|
||||
CryptoStore,
|
||||
IDeviceData,
|
||||
@@ -31,13 +31,13 @@ import {
|
||||
SecretStorePrivateKeys,
|
||||
SESSION_BATCH_SIZE,
|
||||
ACCOUNT_OBJECT_KEY_MIGRATION_STATE,
|
||||
} from "./base";
|
||||
import { IRoomKeyRequestBody, IRoomKeyRequestRecipient } from "../index";
|
||||
import { IOlmDevice } from "../algorithms/megolm";
|
||||
import { IRoomEncryption } from "../RoomList";
|
||||
import { InboundGroupSessionData } from "../OlmDevice";
|
||||
import { IndexedDBCryptoStore } from "./indexeddb-crypto-store";
|
||||
import { CrossSigningKeyInfo } from "../../crypto-api";
|
||||
} from "./base.ts";
|
||||
import { IRoomKeyRequestBody, IRoomKeyRequestRecipient } from "../index.ts";
|
||||
import { IOlmDevice } from "../algorithms/megolm.ts";
|
||||
import { IRoomEncryption } from "../RoomList.ts";
|
||||
import { InboundGroupSessionData } from "../OlmDevice.ts";
|
||||
import { IndexedDBCryptoStore } from "./indexeddb-crypto-store.ts";
|
||||
import { CrossSigningKeyInfo } from "../../crypto-api/index.ts";
|
||||
|
||||
const PROFILE_TRANSACTIONS = false;
|
||||
|
||||
|
||||
@@ -14,12 +14,12 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { logger, Logger } from "../../logger";
|
||||
import { LocalStorageCryptoStore } from "./localStorage-crypto-store";
|
||||
import { MemoryCryptoStore } from "./memory-crypto-store";
|
||||
import * as IndexedDBCryptoStoreBackend from "./indexeddb-crypto-store-backend";
|
||||
import { InvalidCryptoStoreError, InvalidCryptoStoreState } from "../../errors";
|
||||
import * as IndexedDBHelpers from "../../indexeddb-helpers";
|
||||
import { logger, Logger } from "../../logger.ts";
|
||||
import { LocalStorageCryptoStore } from "./localStorage-crypto-store.ts";
|
||||
import { MemoryCryptoStore } from "./memory-crypto-store.ts";
|
||||
import * as IndexedDBCryptoStoreBackend from "./indexeddb-crypto-store-backend.ts";
|
||||
import { InvalidCryptoStoreError, InvalidCryptoStoreState } from "../../errors.ts";
|
||||
import * as IndexedDBHelpers from "../../indexeddb-helpers.ts";
|
||||
import {
|
||||
CryptoStore,
|
||||
IDeviceData,
|
||||
@@ -34,12 +34,12 @@ import {
|
||||
ParkedSharedHistory,
|
||||
SecretStorePrivateKeys,
|
||||
ACCOUNT_OBJECT_KEY_MIGRATION_STATE,
|
||||
} from "./base";
|
||||
import { IRoomKeyRequestBody } from "../index";
|
||||
import { IOlmDevice } from "../algorithms/megolm";
|
||||
import { IRoomEncryption } from "../RoomList";
|
||||
import { InboundGroupSessionData } from "../OlmDevice";
|
||||
import { CrossSigningKeyInfo } from "../../crypto-api";
|
||||
} from "./base.ts";
|
||||
import { IRoomKeyRequestBody } from "../index.ts";
|
||||
import { IOlmDevice } from "../algorithms/megolm.ts";
|
||||
import { IRoomEncryption } from "../RoomList.ts";
|
||||
import { InboundGroupSessionData } from "../OlmDevice.ts";
|
||||
import { CrossSigningKeyInfo } from "../../crypto-api/index.ts";
|
||||
|
||||
/*
|
||||
* Internal module. indexeddb storage for e2e.
|
||||
|
||||
@@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { logger } from "../../logger";
|
||||
import { MemoryCryptoStore } from "./memory-crypto-store";
|
||||
import { logger } from "../../logger.ts";
|
||||
import { MemoryCryptoStore } from "./memory-crypto-store.ts";
|
||||
import {
|
||||
CryptoStore,
|
||||
IDeviceData,
|
||||
@@ -28,12 +28,12 @@ import {
|
||||
Mode,
|
||||
SecretStorePrivateKeys,
|
||||
SESSION_BATCH_SIZE,
|
||||
} from "./base";
|
||||
import { IOlmDevice } from "../algorithms/megolm";
|
||||
import { IRoomEncryption } from "../RoomList";
|
||||
import { InboundGroupSessionData } from "../OlmDevice";
|
||||
import { safeSet } from "../../utils";
|
||||
import { CrossSigningKeyInfo } from "../../crypto-api";
|
||||
} from "./base.ts";
|
||||
import { IOlmDevice } from "../algorithms/megolm.ts";
|
||||
import { IRoomEncryption } from "../RoomList.ts";
|
||||
import { InboundGroupSessionData } from "../OlmDevice.ts";
|
||||
import { safeSet } from "../../utils.ts";
|
||||
import { CrossSigningKeyInfo } from "../../crypto-api/index.ts";
|
||||
|
||||
/**
|
||||
* Internal module. Partial localStorage backed storage for e2e.
|
||||
|
||||
@@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { logger } from "../../logger";
|
||||
import { deepCompare, promiseTry, safeSet } from "../../utils";
|
||||
import { logger } from "../../logger.ts";
|
||||
import { deepCompare, promiseTry, safeSet } from "../../utils.ts";
|
||||
import {
|
||||
CryptoStore,
|
||||
IDeviceData,
|
||||
@@ -30,12 +30,12 @@ import {
|
||||
ParkedSharedHistory,
|
||||
SecretStorePrivateKeys,
|
||||
SESSION_BATCH_SIZE,
|
||||
} from "./base";
|
||||
import { IRoomKeyRequestBody } from "../index";
|
||||
import { IOlmDevice } from "../algorithms/megolm";
|
||||
import { IRoomEncryption } from "../RoomList";
|
||||
import { InboundGroupSessionData } from "../OlmDevice";
|
||||
import { CrossSigningKeyInfo } from "../../crypto-api";
|
||||
} from "./base.ts";
|
||||
import { IRoomKeyRequestBody } from "../index.ts";
|
||||
import { IOlmDevice } from "../algorithms/megolm.ts";
|
||||
import { IRoomEncryption } from "../RoomList.ts";
|
||||
import { InboundGroupSessionData } from "../OlmDevice.ts";
|
||||
import { CrossSigningKeyInfo } from "../../crypto-api/index.ts";
|
||||
|
||||
function encodeSessionKey(senderCurve25519Key: string, sessionId: string): string {
|
||||
return encodeURIComponent(senderCurve25519Key) + "/" + encodeURIComponent(sessionId);
|
||||
|
||||
@@ -19,23 +19,23 @@ limitations under the License.
|
||||
* Base class for verification methods.
|
||||
*/
|
||||
|
||||
import { MatrixEvent } from "../../models/event";
|
||||
import { EventType } from "../../@types/event";
|
||||
import { logger } from "../../logger";
|
||||
import { DeviceInfo } from "../deviceinfo";
|
||||
import { newTimeoutError } from "./Error";
|
||||
import { KeysDuringVerification, requestKeysDuringVerification } from "../CrossSigning";
|
||||
import { IVerificationChannel } from "./request/Channel";
|
||||
import { MatrixClient } from "../../client";
|
||||
import { VerificationRequest } from "./request/VerificationRequest";
|
||||
import { TypedEventEmitter } from "../../models/typed-event-emitter";
|
||||
import { MatrixEvent } from "../../models/event.ts";
|
||||
import { EventType } from "../../@types/event.ts";
|
||||
import { logger } from "../../logger.ts";
|
||||
import { DeviceInfo } from "../deviceinfo.ts";
|
||||
import { newTimeoutError } from "./Error.ts";
|
||||
import { KeysDuringVerification, requestKeysDuringVerification } from "../CrossSigning.ts";
|
||||
import { IVerificationChannel } from "./request/Channel.ts";
|
||||
import { MatrixClient } from "../../client.ts";
|
||||
import { VerificationRequest } from "./request/VerificationRequest.ts";
|
||||
import { TypedEventEmitter } from "../../models/typed-event-emitter.ts";
|
||||
import {
|
||||
ShowQrCodeCallbacks,
|
||||
ShowSasCallbacks,
|
||||
Verifier,
|
||||
VerifierEvent,
|
||||
VerifierEventHandlerMap,
|
||||
} from "../../crypto-api/verification";
|
||||
} from "../../crypto-api/verification.ts";
|
||||
|
||||
const timeoutException = new Error("Verification timed out");
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ limitations under the License.
|
||||
* Error messages.
|
||||
*/
|
||||
|
||||
import { MatrixEvent } from "../../models/event";
|
||||
import { EventType } from "../../@types/event";
|
||||
import { MatrixEvent } from "../../models/event.ts";
|
||||
import { EventType } from "../../@types/event.ts";
|
||||
|
||||
export function newVerificationError(code: string, reason: string, extraData?: Record<string, any>): MatrixEvent {
|
||||
const content = Object.assign({}, { code, reason }, extraData);
|
||||
|
||||
@@ -19,11 +19,11 @@ limitations under the License.
|
||||
* do verification with this method).
|
||||
*/
|
||||
|
||||
import { VerificationBase as Base, VerificationEvent, VerificationEventHandlerMap } from "./Base";
|
||||
import { IVerificationChannel } from "./request/Channel";
|
||||
import { MatrixClient } from "../../client";
|
||||
import { MatrixEvent } from "../../models/event";
|
||||
import { VerificationRequest } from "./request/VerificationRequest";
|
||||
import { VerificationBase as Base, VerificationEvent, VerificationEventHandlerMap } from "./Base.ts";
|
||||
import { IVerificationChannel } from "./request/Channel.ts";
|
||||
import { MatrixClient } from "../../client.ts";
|
||||
import { MatrixEvent } from "../../models/event.ts";
|
||||
import { VerificationRequest } from "./request/VerificationRequest.ts";
|
||||
|
||||
export class IllegalMethod extends Base<VerificationEvent, VerificationEventHandlerMap> {
|
||||
public static factory(
|
||||
|
||||
@@ -18,16 +18,16 @@ limitations under the License.
|
||||
* QR code key verification.
|
||||
*/
|
||||
|
||||
import { VerificationBase as Base } from "./Base";
|
||||
import { newKeyMismatchError, newUserCancelledError } from "./Error";
|
||||
import { decodeBase64, encodeUnpaddedBase64 } from "../../base64";
|
||||
import { logger } from "../../logger";
|
||||
import { VerificationRequest } from "./request/VerificationRequest";
|
||||
import { MatrixClient } from "../../client";
|
||||
import { IVerificationChannel } from "./request/Channel";
|
||||
import { MatrixEvent } from "../../models/event";
|
||||
import { ShowQrCodeCallbacks, VerifierEvent } from "../../crypto-api/verification";
|
||||
import { VerificationMethod } from "../../types";
|
||||
import { VerificationBase as Base } from "./Base.ts";
|
||||
import { newKeyMismatchError, newUserCancelledError } from "./Error.ts";
|
||||
import { decodeBase64, encodeUnpaddedBase64 } from "../../base64.ts";
|
||||
import { logger } from "../../logger.ts";
|
||||
import { VerificationRequest } from "./request/VerificationRequest.ts";
|
||||
import { MatrixClient } from "../../client.ts";
|
||||
import { IVerificationChannel } from "./request/Channel.ts";
|
||||
import { MatrixEvent } from "../../models/event.ts";
|
||||
import { ShowQrCodeCallbacks, VerifierEvent } from "../../crypto-api/verification.ts";
|
||||
import { VerificationMethod } from "../../types.ts";
|
||||
|
||||
export const SHOW_QR_CODE_METHOD = VerificationMethod.ShowQrCode;
|
||||
export const SCAN_QR_CODE_METHOD = VerificationMethod.ScanQrCode;
|
||||
|
||||
@@ -21,27 +21,27 @@ limitations under the License.
|
||||
import anotherjson from "another-json";
|
||||
import { Utility, SAS as OlmSAS } from "@matrix-org/olm";
|
||||
|
||||
import { VerificationBase as Base, SwitchStartEventError } from "./Base";
|
||||
import { VerificationBase as Base, SwitchStartEventError } from "./Base.ts";
|
||||
import {
|
||||
errorFactory,
|
||||
newInvalidMessageError,
|
||||
newKeyMismatchError,
|
||||
newUnknownMethodError,
|
||||
newUserCancelledError,
|
||||
} from "./Error";
|
||||
import { logger } from "../../logger";
|
||||
import { IContent, MatrixEvent } from "../../models/event";
|
||||
import { generateDecimalSas } from "./SASDecimal";
|
||||
import { EventType } from "../../@types/event";
|
||||
import { EmojiMapping, GeneratedSas, ShowSasCallbacks, VerifierEvent } from "../../crypto-api/verification";
|
||||
import { VerificationMethod } from "../../types";
|
||||
} from "./Error.ts";
|
||||
import { logger } from "../../logger.ts";
|
||||
import { IContent, MatrixEvent } from "../../models/event.ts";
|
||||
import { generateDecimalSas } from "./SASDecimal.ts";
|
||||
import { EventType } from "../../@types/event.ts";
|
||||
import { EmojiMapping, GeneratedSas, ShowSasCallbacks, VerifierEvent } from "../../crypto-api/verification.ts";
|
||||
import { VerificationMethod } from "../../types.ts";
|
||||
|
||||
// backwards-compatibility exports
|
||||
export type {
|
||||
ShowSasCallbacks as ISasEvent,
|
||||
GeneratedSas as IGeneratedSas,
|
||||
EmojiMapping,
|
||||
} from "../../crypto-api/verification";
|
||||
} from "../../crypto-api/verification.ts";
|
||||
|
||||
const START_TYPE = EventType.KeyVerificationStart;
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { MatrixEvent } from "../../../models/event";
|
||||
import { VerificationRequest } from "./VerificationRequest";
|
||||
import { MatrixEvent } from "../../../models/event.ts";
|
||||
import { VerificationRequest } from "./VerificationRequest.ts";
|
||||
|
||||
export interface IVerificationChannel {
|
||||
request?: VerificationRequest;
|
||||
|
||||
@@ -15,13 +15,13 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { VerificationRequest, REQUEST_TYPE, READY_TYPE, START_TYPE } from "./VerificationRequest";
|
||||
import { logger } from "../../../logger";
|
||||
import { IVerificationChannel } from "./Channel";
|
||||
import { EventType, TimelineEvents } from "../../../@types/event";
|
||||
import { MatrixClient } from "../../../client";
|
||||
import { MatrixEvent } from "../../../models/event";
|
||||
import { IRequestsMap } from "../..";
|
||||
import { VerificationRequest, REQUEST_TYPE, READY_TYPE, START_TYPE } from "./VerificationRequest.ts";
|
||||
import { logger } from "../../../logger.ts";
|
||||
import { IVerificationChannel } from "./Channel.ts";
|
||||
import { EventType, TimelineEvents } from "../../../@types/event.ts";
|
||||
import { MatrixClient } from "../../../client.ts";
|
||||
import { MatrixEvent } from "../../../models/event.ts";
|
||||
import { IRequestsMap } from "../../index.ts";
|
||||
|
||||
const MESSAGE_TYPE = EventType.RoomMessage;
|
||||
const M_REFERENCE = "m.reference";
|
||||
|
||||
@@ -15,8 +15,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { randomString } from "../../../randomstring";
|
||||
import { logger } from "../../../logger";
|
||||
import { randomString } from "../../../randomstring.ts";
|
||||
import { logger } from "../../../logger.ts";
|
||||
import {
|
||||
CANCEL_TYPE,
|
||||
PHASE_STARTED,
|
||||
@@ -25,12 +25,12 @@ import {
|
||||
READY_TYPE,
|
||||
START_TYPE,
|
||||
VerificationRequest,
|
||||
} from "./VerificationRequest";
|
||||
import { errorFromEvent, newUnexpectedMessageError } from "../Error";
|
||||
import { MatrixEvent } from "../../../models/event";
|
||||
import { IVerificationChannel } from "./Channel";
|
||||
import { MatrixClient } from "../../../client";
|
||||
import { IRequestsMap } from "../..";
|
||||
} from "./VerificationRequest.ts";
|
||||
import { errorFromEvent, newUnexpectedMessageError } from "../Error.ts";
|
||||
import { MatrixEvent } from "../../../models/event.ts";
|
||||
import { IVerificationChannel } from "./Channel.ts";
|
||||
import { MatrixClient } from "../../../client.ts";
|
||||
import { IRequestsMap } from "../../index.ts";
|
||||
|
||||
export type Request = VerificationRequest<ToDeviceChannel>;
|
||||
|
||||
|
||||
@@ -14,16 +14,16 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { logger } from "../../../logger";
|
||||
import { errorFactory, errorFromEvent, newUnexpectedMessageError, newUnknownMethodError } from "../Error";
|
||||
import { QRCodeData, SCAN_QR_CODE_METHOD } from "../QRCode";
|
||||
import { IVerificationChannel } from "./Channel";
|
||||
import { MatrixClient } from "../../../client";
|
||||
import { MatrixEvent } from "../../../models/event";
|
||||
import { EventType } from "../../../@types/event";
|
||||
import { VerificationBase } from "../Base";
|
||||
import { VerificationMethod } from "../../index";
|
||||
import { TypedEventEmitter } from "../../../models/typed-event-emitter";
|
||||
import { logger } from "../../../logger.ts";
|
||||
import { errorFactory, errorFromEvent, newUnexpectedMessageError, newUnknownMethodError } from "../Error.ts";
|
||||
import { QRCodeData, SCAN_QR_CODE_METHOD } from "../QRCode.ts";
|
||||
import { IVerificationChannel } from "./Channel.ts";
|
||||
import { MatrixClient } from "../../../client.ts";
|
||||
import { MatrixEvent } from "../../../models/event.ts";
|
||||
import { EventType } from "../../../@types/event.ts";
|
||||
import { VerificationBase } from "../Base.ts";
|
||||
import { VerificationMethod } from "../../index.ts";
|
||||
import { TypedEventEmitter } from "../../../models/typed-event-emitter.ts";
|
||||
import {
|
||||
canAcceptVerificationRequest,
|
||||
VerificationPhase as Phase,
|
||||
@@ -31,10 +31,10 @@ import {
|
||||
VerificationRequestEvent,
|
||||
VerificationRequestEventHandlerMap,
|
||||
Verifier,
|
||||
} from "../../../crypto-api/verification";
|
||||
} from "../../../crypto-api/verification.ts";
|
||||
|
||||
// backwards-compatibility exports
|
||||
export { VerificationPhase as Phase, VerificationRequestEvent } from "../../../crypto-api/verification";
|
||||
export { VerificationPhase as Phase, VerificationRequestEvent } from "../../../crypto-api/verification.ts";
|
||||
|
||||
// How long after the event's timestamp that the request times out
|
||||
const TIMEOUT_FROM_EVENT_TS = 10 * 60 * 1000; // 10 minutes
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
Copyright 2024 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Computes a SHA-256 hash of a string (after utf-8 encoding) and returns it as an ArrayBuffer.
|
||||
*
|
||||
* @param plaintext The string to hash
|
||||
* @returns An ArrayBuffer containing the SHA-256 hash of the input string
|
||||
* @throws If the subtle crypto API is not available, for example if the code is running
|
||||
* in a web page with an insecure context (eg. served over plain HTTP).
|
||||
*/
|
||||
export async function sha256(plaintext: string): Promise<ArrayBuffer> {
|
||||
if (!globalThis.crypto.subtle) {
|
||||
throw new Error("Crypto.subtle is not available: insecure context?");
|
||||
}
|
||||
const utf8 = new TextEncoder().encode(plaintext);
|
||||
|
||||
const digest = await globalThis.crypto.subtle.digest("SHA-256", utf8);
|
||||
|
||||
return digest;
|
||||
}
|
||||
+120
-16
@@ -25,10 +25,15 @@ import {
|
||||
ISendEventFromWidgetResponseData,
|
||||
} from "matrix-widget-api";
|
||||
|
||||
import { MatrixEvent, IEvent, IContent, EventStatus } from "./models/event";
|
||||
import { ISendEventResponse } from "./@types/requests";
|
||||
import { EventType } from "./@types/event";
|
||||
import { logger } from "./logger";
|
||||
import { MatrixEvent, IEvent, IContent, EventStatus } from "./models/event.ts";
|
||||
import {
|
||||
ISendEventResponse,
|
||||
SendDelayedEventRequestOpts,
|
||||
SendDelayedEventResponse,
|
||||
UpdateDelayedEventAction,
|
||||
} from "./@types/requests.ts";
|
||||
import { EventType, StateEvents } from "./@types/event.ts";
|
||||
import { logger } from "./logger.ts";
|
||||
import {
|
||||
MatrixClient,
|
||||
ClientEvent,
|
||||
@@ -36,15 +41,16 @@ import {
|
||||
IStartClientOpts,
|
||||
SendToDeviceContentMap,
|
||||
IOpenIDToken,
|
||||
} from "./client";
|
||||
import { SyncApi, SyncState } from "./sync";
|
||||
import { SlidingSyncSdk } from "./sliding-sync-sdk";
|
||||
import { User } from "./models/user";
|
||||
import { Room } from "./models/room";
|
||||
import { ToDeviceBatch, ToDevicePayload } from "./models/ToDeviceMessage";
|
||||
import { DeviceInfo } from "./crypto/deviceinfo";
|
||||
import { IOlmDevice } from "./crypto/algorithms/megolm";
|
||||
import { MapWithDefault, recursiveMapToObject } from "./utils";
|
||||
UNSTABLE_MSC4140_DELAYED_EVENTS,
|
||||
} from "./client.ts";
|
||||
import { SyncApi, SyncState } from "./sync.ts";
|
||||
import { SlidingSyncSdk } from "./sliding-sync-sdk.ts";
|
||||
import { User } from "./models/user.ts";
|
||||
import { Room } from "./models/room.ts";
|
||||
import { ToDeviceBatch, ToDevicePayload } from "./models/ToDeviceMessage.ts";
|
||||
import { DeviceInfo } from "./crypto/deviceinfo.ts";
|
||||
import { IOlmDevice } from "./crypto/algorithms/megolm.ts";
|
||||
import { MapWithDefault, recursiveMapToObject } from "./utils.ts";
|
||||
|
||||
interface IStateEventRequest {
|
||||
eventType: string;
|
||||
@@ -95,6 +101,20 @@ export interface ICapabilities {
|
||||
* @defaultValue false
|
||||
*/
|
||||
turnServers?: boolean;
|
||||
|
||||
/**
|
||||
* Whether this client needs to be able to send delayed events.
|
||||
* @experimental Part of MSC4140 & MSC4157
|
||||
* @defaultValue false
|
||||
*/
|
||||
sendDelayedEvents?: boolean;
|
||||
|
||||
/**
|
||||
* Whether this client needs to be able to update delayed events.
|
||||
* @experimental Part of MSC4140 & MSC4157
|
||||
* @defaultValue false
|
||||
*/
|
||||
updateDelayedEvents?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,6 +182,18 @@ export class RoomWidgetClient extends MatrixClient {
|
||||
);
|
||||
capabilities.sendToDevice?.forEach((eventType) => widgetApi.requestCapabilityToSendToDevice(eventType));
|
||||
capabilities.receiveToDevice?.forEach((eventType) => widgetApi.requestCapabilityToReceiveToDevice(eventType));
|
||||
if (
|
||||
capabilities.sendDelayedEvents &&
|
||||
(capabilities.sendEvent?.length ||
|
||||
capabilities.sendMessage === true ||
|
||||
(Array.isArray(capabilities.sendMessage) && capabilities.sendMessage.length) ||
|
||||
capabilities.sendState?.length)
|
||||
) {
|
||||
widgetApi.requestCapability(MatrixCapabilities.MSC4157SendDelayedEvent);
|
||||
}
|
||||
if (capabilities.updateDelayedEvents) {
|
||||
widgetApi.requestCapability(MatrixCapabilities.MSC4157UpdateDelayedEvent);
|
||||
}
|
||||
if (capabilities.turnServers) {
|
||||
widgetApi.requestCapability(MatrixCapabilities.MSC3846TurnServers);
|
||||
}
|
||||
@@ -248,7 +280,29 @@ export class RoomWidgetClient extends MatrixClient {
|
||||
throw new Error(`Unknown room: ${roomIdOrAlias}`);
|
||||
}
|
||||
|
||||
protected async encryptAndSendEvent(room: Room, event: MatrixEvent): Promise<ISendEventResponse> {
|
||||
protected async encryptAndSendEvent(room: Room, event: MatrixEvent): Promise<ISendEventResponse>;
|
||||
protected async encryptAndSendEvent(
|
||||
room: Room,
|
||||
event: MatrixEvent,
|
||||
delayOpts: SendDelayedEventRequestOpts,
|
||||
): Promise<SendDelayedEventResponse>;
|
||||
protected async encryptAndSendEvent(
|
||||
room: Room,
|
||||
event: MatrixEvent,
|
||||
delayOpts?: SendDelayedEventRequestOpts,
|
||||
): Promise<ISendEventResponse | SendDelayedEventResponse> {
|
||||
if (delayOpts) {
|
||||
// TODO: updatePendingEvent for delayed events?
|
||||
const response = await this.widgetApi.sendRoomEvent(
|
||||
event.getType(),
|
||||
event.getContent(),
|
||||
room.roomId,
|
||||
"delay" in delayOpts ? delayOpts.delay : undefined,
|
||||
"parent_delay_id" in delayOpts ? delayOpts.parent_delay_id : undefined,
|
||||
);
|
||||
return this.validateSendDelayedEventResponse(response);
|
||||
}
|
||||
|
||||
let response: ISendEventFromWidgetResponseData;
|
||||
try {
|
||||
response = await this.widgetApi.sendRoomEvent(event.getType(), event.getContent(), room.roomId);
|
||||
@@ -257,8 +311,9 @@ export class RoomWidgetClient extends MatrixClient {
|
||||
throw e;
|
||||
}
|
||||
|
||||
// This also checks for an event id on the response
|
||||
room.updatePendingEvent(event, EventStatus.SENT, response.event_id);
|
||||
return { event_id: response.event_id };
|
||||
return { event_id: response.event_id! };
|
||||
}
|
||||
|
||||
public async sendStateEvent(
|
||||
@@ -267,7 +322,56 @@ export class RoomWidgetClient extends MatrixClient {
|
||||
content: any,
|
||||
stateKey = "",
|
||||
): Promise<ISendEventResponse> {
|
||||
return await this.widgetApi.sendStateEvent(eventType, stateKey, content, roomId);
|
||||
const response = await this.widgetApi.sendStateEvent(eventType, stateKey, content, roomId);
|
||||
if (response.event_id === undefined) {
|
||||
throw new Error("'event_id' absent from response to an event request");
|
||||
}
|
||||
return { event_id: response.event_id };
|
||||
}
|
||||
|
||||
/**
|
||||
* @experimental This currently relies on an unstable MSC (MSC4140).
|
||||
*/
|
||||
// eslint-disable-next-line
|
||||
public async _unstable_sendDelayedStateEvent<K extends keyof StateEvents>(
|
||||
roomId: string,
|
||||
delayOpts: SendDelayedEventRequestOpts,
|
||||
eventType: K,
|
||||
content: StateEvents[K],
|
||||
stateKey = "",
|
||||
): Promise<SendDelayedEventResponse> {
|
||||
if (!(await this.doesServerSupportUnstableFeature(UNSTABLE_MSC4140_DELAYED_EVENTS))) {
|
||||
throw Error("Server does not support the delayed events API");
|
||||
}
|
||||
|
||||
const response = await this.widgetApi.sendStateEvent(
|
||||
eventType,
|
||||
stateKey,
|
||||
content,
|
||||
roomId,
|
||||
"delay" in delayOpts ? delayOpts.delay : undefined,
|
||||
"parent_delay_id" in delayOpts ? delayOpts.parent_delay_id : undefined,
|
||||
);
|
||||
return this.validateSendDelayedEventResponse(response);
|
||||
}
|
||||
|
||||
private validateSendDelayedEventResponse(response: ISendEventFromWidgetResponseData): SendDelayedEventResponse {
|
||||
if (response.delay_id === undefined) {
|
||||
throw new Error("'delay_id' absent from response to a delayed event request");
|
||||
}
|
||||
return { delay_id: response.delay_id };
|
||||
}
|
||||
|
||||
/**
|
||||
* @experimental This currently relies on an unstable MSC (MSC4140).
|
||||
*/
|
||||
// eslint-disable-next-line
|
||||
public async _unstable_updateDelayedEvent(delayId: string, action: UpdateDelayedEventAction): Promise<{}> {
|
||||
if (!(await this.doesServerSupportUnstableFeature(UNSTABLE_MSC4140_DELAYED_EVENTS))) {
|
||||
throw Error("Server does not support the delayed events API");
|
||||
}
|
||||
|
||||
return await this.widgetApi.updateDelayedEvent(delayId, action);
|
||||
}
|
||||
|
||||
public async sendToDevice(eventType: string, contentMap: SendToDeviceContentMap): Promise<{}> {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user